Detecting node-webkit during npm install - node.js

I'm working on a library for node.js that is build primarily as a native module. As such, when people try to include it in node-webkit projects, they have to recompile it using nw-gyp instead of node-gyp. I know we can detect node-webkit specifically when our built code runs using something like this:
try {
isNodeWebkit = (typeof require('nw.gui') !== "undefined");
} catch(e) {
isNodeWebkit = false;
}
However, I would like to detect this inside of our install script (run by npm install). Alternatively, we look in our own package.json, but is there maybe a way to look at the root project's package.json? That way we could at least look at some property, maybe engine or something?

In order to look into your own package.json you can do something like this
gui = require(nw.gui);
myPackageJSONFile = gui.App.manifest; // this will get the package.json file
I hope this helps.

I ended up writing a module to do this: https://github.com/maxkorp/which-native-nodish (also supports atom-shell and the renamed nw.js)
The gist is that you start at the parent directory to the module, and keep going up as long as you are a child of a node_modules folder which is a child of a folder with a package.json. Once at the root level, check the engines property in the package.json for an atom-shell, node-webkit or nwjs property. Not guaranteed to work (The farthest ancestor project must specify if its using a node-ish engine in this way), but it's better than nothing, and the only out of the box solution I've seen.

Simply as this:
isNodeWebkit = (typeof process == "object");

Related

If an npm package ships with multiple dist folders, how do I know which one is used when my App is built?

I am using an npm package (aurelia-google-maps) in my application. The package ships with AMD, System, CommonJS, Native Modules, and ES2015 dist folders like this:
/node_modules/
/aurelia-google-maps/
/dist/
/amd
/system
/native-modules
/es2015
/commonjs
In my typescript app I am simply importing all the classes and functions as:
import {Configure} from "aurelia-google-maps"
Is there a way that I can find out which distribution is used when I build my application?
I don't think you can determine it without the help of your build tool. It can be done in 2 steps:
using type of to check the availability of a variable in runtime code:
const distType = typeof DIST_TYPE !== 'undefined' ? DIST_TYPE : void 0;
configure the build tool to replace DIST_TYPE with the distribution target
And then you can use it in normal code, via distType variable.
For typescript, you just need an extra declaration
declare const DIST_TYPE: string | undefined;
I have figured it out. It seems like Aurelia automatically registers it's own plugin called DistPlugin that resolves dependencies using the native-modules folder. See this wiki for an explanation on how to change this behaviour.

Loading Nodejs Module at runtime in electron app

Currently I am playing around with electron using vue-cli-plugin-electron-builder along side a simple vue project. This is the project https://github.com/nklayman/vue-cli-plugin-electron-builder .
vue create my-project
cd my-project
vue add electron-builder
npm run electron:serve
My goal is to add a simple plugin-like architecture. The app serves only base functionality but can be extended with "plugins". Those plugins therefore are not included in the built, but will be loaded at runtime by electron. I would prefere when those plugins just behave like node modules ( module.exports = ) with its own dependencies ( probably with a package.json file inside ). I would locate those plugins at app.getPath('userData') + '/Plugins.
I looked at a few approaches on how to tackle this problem :
1. Using Nodejs vm module
First, I tried using Nodejs vm module to read and execute a script from an external file, all at runtime. It works great so far, although I would not be able to use external dependencies inside those loaded scripts. If I want to use external dependencies inside the plugin scripts, those dependencies must have been included in the electron build beforehand. Somehow defeats the whole purpose of having plugins ... only vanilla js + nodejs base modules would be possible .
2. using global.require
I saw this solution in another SO answer.
Using node require with Electron and Webpack
Webpack/electron require dynamic module
They say to use global.require but it throws an error saying global.require is not a function. The solution looked promising first, but somehow I can't get it to work.
3. simply use require
Of course I had to try it. When I try to require an external module from a non-project location it won't find the module, even if the path is correct. Again, the path I am trying to locate the module should be at app.getPath("userData"), not in the projects root directory. When however, I locate the plugins inside the root directory of the project it gets included in the built. This again defeats the purpose of having plugins.
Goal
So far, I haven't found a viable solution to this. I simply want my electron app to be extendible with basic node modules at runtime ( following a pre-defined schema to simplify ) . Of course there is atom, made with electron, using their own apm manager to install and load plugins, but this seems way to overpowered. Its enough for me to only have plugin files located locally, to have a public "marketplace" is no goal. Also, it's ok if the app has to reload / restart to load plugins.
Any ideas ?
After more and more research I stumbled over 2 packages :
https://www.npmjs.com/package/live-plugin-manager
https://github.com/getstation/electron-package-manager
both integrating npm to programmatically handle package installation at runtime. I settled for live-plugin-manager for now since its better documented and even allow package installation from local file system.
Pro
I was able to integrate the system out-of-the-box into a vanilla electron app. Works like a charm.
Cons
.I was not able to use it inside a vue electron boilerplate (like the one I said I was using in OP), since webpack is interferring with the require environment. But there sure is a solution to this.
Update : I was able to get it to work eventually inside a webpack bundled electron vue boilerplate. I accidentally mixed import and require . The following code works for me using live-plugin-manager
// plugin-loader.js
const path = require('path');
const { PluginManager } = require('live-plugin-manager');
const pluginInstallFolder = path.resolve(app.getPath('userData'), '.plugins');
const pluginManager = new PluginManager();
module.exports = async (pkg) => {
// installs pkg from npm
await pluginManager.install(pkg);
const package = pluginManager.require(pkg);
return package
}
// main.js
const pluginLoader = require('./plugin-loader');
pluginLoader("moment").then((moment) => {
console.log(moment().format());
})
This will install "moment" package from npm during runtime into a local directory and load it into the app, without bundling it into the executable files.

Managing NPM packages via a script

Is there a library or is it perhaps built into NPM itself to manage packages and install them within a script? I'm writing a process that checks if a local package exists and installs if it doesn't. Then I'd like to be able to dynamically require it in the same process.
This is definitely possible, but probably inadvisable.
I found the npm module "npm-programmatic" which lets you npm install. Once you have that, all you need to do is wrap your require with a try catch so that you can handle when a require fails.
const npm = require('npm-programmatic')
let myPackage
try {
myPackage = require('my-package')
} catch(err) {
npm.install(['my-package']).then(function() {
myPackage = require('my-package')
console.log(myPackage)
})
}
The biggest problem you're probably going to face here is that the script will need to be running with more than standard privileges. You will likely need to sudo run this script which is very inadvisable.
No, I think that there is no way to accomplish your aim, and in fact, it is a "bad" idea, I think.
Under Node.js, we always use package.json to manage all dependencies, and when we want to deploy them, we only use to run
$npm install
it is very easy and effective, but based on your mind, we need write a new function instead of require function, for example, require2, and while we use it to load the module, it always check the module exists or not at first, it is not effective, I think.

Do all npm packages need an export?

I am new to nodejs packages and what I understood was that to share code you had to do a module.export (in addition to adding a package.json)
For example bootstrap-select does not have an export function but is available on npm.
So my question is do all modules require an export and also can I do a require('bootstrap-select') in my code?
no, all npm modules do not require an export. npm is now being used more generally for not only javascript packages intended for use under node.js but front end code for browsers, CSS libraries, etc. At the very least, an npm package could just deliver a payload of files not even including any javascript, such as some images, some CSS, some HTML, etc.
So you can only do require('some-module') if that package has either an index.js file or has set the main property in it's package.json file properly.
However if you are authoring a javascript module for node.js, then yes you'll need to export something in order for your module to load properly.
No, npm modules do not require doing something with module.exports. If you do not touch that object, requireing your module will return an empty object (since that is the default for module.exports. However, this can be useful if your module is only intended to be required for side effects, rather than a return value.
For example, the module you linked to modifies global state by attaching a jQuery event handler.
As per i know ,
1.All npm modules are not required to build an app.
2.If we use var bootStrap = require('bootstrap-select'); using bootStrap variable you can access bootStrap module.
so we can pass that object in anywhere of your code
3.To install a dependency modules,
In package.json give dependency block as like this
"dependencies": {
"express": "2.3.12",
"jade": "latest",
"redis": "0.6.0"
}
you can change and edit your packages. then enter a command npm install in command prompt it will install only dependency modules.
If i made any mistakes please correct me Thanks.

Determine NPM modules used from a running node.js application

Other than grabbing the package.json file at the project root is there a way to determine the list of dependencies of a running node.js application? Does node keep this meta information available as some var in the global namespace?
If you are just looking for the currently installed npm packages in the application directory, then you can install the npm package (npm install -g npm) and programatically invoke ls to list the installed packages and the dependency trees.
Obviously, this has no bearing on whether the installed packages are actually require'd in the application or not.
Usage is not that well documented but this should get you started.
var npm = require('npm');
npm.load(function(err, npm) {
npm.commands.ls([], true, function(err, data, lite) {
console.log(data); //or lite for simplified output
});
});
e.g.:
{ dependencies:
{ npm: { version: '1.1.18', dependencies: [Object] },
request: { version: '2.9.202' } } }
Otherwise, I believe the only other option is to introspect the module module to get information pertaining to the currently loaded/cached module paths. However this definitely does not look to have been developed as a public API. I'm not sure if there are any alternatives so would be keen to hear if there are e.g.
var req = require('request'); // require some module for demo purposes
var m = require('module');
// properties of m contain current loaded module info, e.g. m._cache
I believe you could use require-analyzer, which sort of works according to Isaacs(could miss some). You could hear this in Nodeup's first podcast from 11:55.
Or you could try James node-detective which probably will find your dependencies better(but not by running code), but because of Javascript dynamic nature(12:46).
detective
Find all calls to require() no matter how crazily nested using a
proper walk of the AST.
P.S: to expose those package.json variables to node.js you could use node-pkginfo

Resources