Electron js: use npm internally/programmatically - node.js

I wrote an app with Electron js that encapsulate Ionic apps for editing them live.
So i use a child process to cd {ionic app path} && ionic serve/npm install/npm update for serving and updating packages of the live Ionic app in my Electron container.
No problems with this technique on my side. But when i package my app and use an installer to test it on a clean machine, npm cannot be executed because of nodejs that is not installed on it.
First I was thinking of including a nodejs installer into my main app installer but this does not seem to me that is the good way of doing it.
And after digging on stackoverflow I've found this thread: Install programmatically a NPM package providing its version
that explain how to use npm directly in my code with require("npm"); and that's worked but i was not able to tell npm.install() in which folder i want to run this command what was possible with child process.
I has tried to read the lib but this not seams to be possible: https://github.com/npm/npm/blob/latest/lib/install.js
Do you have any idea what I can do to solve this problem ?

So I've found the answer after digging into this code https://github.com/npm/npm/blob/latest/lib/install.js
Simply use npm like this :
npm.load({}, function (err) {
npm.commands.install(HERE_A_PATH, [], function(er, data){
//callback here
});
npm.on("log", function (msg) {
console.log(msg + '');
});
});

Related

Problem when using process.cwd() in published npm package

I'm dipping my toes into cli tooling by building a simple program for automating Gerrit commits. Everything works locally, but after publishing the package to npm and installing it globally it looks like process.cwd() behaves differently. The program exits, but no console.log(). Even a simple console.log(process.cwd()) is ignored (again works locally). Is it possible to use process.cwd() when running a globally installed npm package?
console.log(process.cwd());
const getCurrentBranchName = (p = process.cwd()) => {
const gitHeadPath = `${p}/.git/HEAD`;
return fs.existsSync(gitHeadPath)
? fs.readFileSync(gitHeadPath, "utf-8").trim().split("/").pop()
: (console.log("not a git repo"), process.exit(0));
}
const currentBranch = getCurrentBranchName();
When ran locally (with node index):
$ /Users/jpap/.npm-packages/lib/node_modules/gittest
$ not a git repo
You haven't proved the problem is with process.cwd()
The code in your question and the results your describe only indicate that the console.log() calls aren't executing.
You could easily test this by adding the following to the top of your code:
console.log('My name is Inigo Montoya. You killed my father. Prepare to die!')
What you are publishing is likely not the same as what you are running locally
For example, you could be using a bundler (e.g. Rollup) configured to strip console.log calls.
This is easy to confirm. Simple look at the code of the npm installed version:
Use npm root -g to find out where your global packages are installed. For non-global packages, look in node_modules.
Find your package's subdir.
Look at the code, or diff it with the source code.
I suspect you will see all console.log statements removed.

Using npm as custom plugin manager?

Think of sublime text, where you can install or uninstall plugins. I want that for my app, and I want to use npm/github to do it.
Maybe I'll require that your package starts with myapp- to be considered a plugin for my app. How can I search npm based on that, and also install/update packages into the folder I want (not node_modules) and ideally it should work even if the person doesn't have npm installed (using an http api?).
Plugins for my app go into plugins/plugin-name folder, all I need to do is download their git source into that folder
I have created a project to solve similar problems. See live-plugin-manager.
You can install, uninstall and load plugins from npm at runtime.
import {PluginManager} from "live-plugin-manager";
import * as path from "path";
const manager = new PluginManager({
pluginsPath: path.join(__dirname, "plugins")
});
async function run() {
await manager.installFromNpm("moment");
const moment = manager.require("moment");
console.log(moment().format());
await manager.uninstall("moment");
}
run();
In the above code I install moment package at runtime, load and execute it. Here I have used typescript, but the same can be written with plain javascript.
Plugins are installed inside the directory specified in the PluginManager constructor or in the plugins directory if not specified.
I just created a great module, for enhancements like your proposal:
https://www.npmjs.com/package/#kawix/core
You can read the README.md, for try understand the usage, i will add a basic example:
> npm install -g #kawix/core
> kwcore "https://raw.githubusercontent.com/voxsoftware/kawix-core/master/example/npmrequire/express.js"
And this is the content of file https://raw.githubusercontent.com/voxsoftware/kawix-core/master/example/npmrequire/express.js
// this will download the npm module and make a local cache
import express from 'npm://express#^4.16.4'
var app = express()
app.get('/', function (req, res) {
res.send('Hello World')
})
app.listen(3000)
console.log("Listening on 3000")
https://api-docs.npms.io/ has an http API for searching npm packages.
Which can be used like this: https://api.npms.io/v2/search?q=keywords:myapp to get all plugins for myapp
To actually download/install an npm package into any folder, I found an npm package called download-npm-package that lets me do it in code

Tutorials for writing plugin for nodejs app?

I am seeing difficulty to find a good tutorial about writing plugins for nodejs app(not for nodejs itself!).
What I want to do is, for some nodejs app, I want to extend its functionalities with few js files I wrote.
For example, I am using karma runner to run JS unit tests, but the existing reporters don't meet our logging requirement very well. So I wrote my own reporter and put that in a separate js file, e.g. myreporter.js. To use it now, I have to add the myreporter.js into the folder of karma code and add some line like this into reporter.js of karma:
exports.myreporter = require('./reporters/myreporter');
That works nicely on my machine, however, it could be an issue for the CI server. I don't want to include all karma code in our own project. I want to install the karma from the public repo on the server and only include myreporter.js in our project, so that we don't have to install the modified karma for every build. And I only want to install the myreporter.js as a plugin for karma for the builds.
I am not sure whether that is clear enough. So what I need is to write a plugin for karma, or for any existing nodejs app. I should still be able to use the app in the same way as before, while I could use the new functionalities brought by the plugin.
I have see this page about peerdependency. Does that mean if I add peerdependency or karma into the package.json file for myreporter.js, once I installed the myreporter package and run karma, karma will pick up the new reporter?
Thanks for any help regarding this long question.
Updates:
Tried peerdependency, it seems karma 0.8.6 doesn't pick my plugin up. Looks like starts from 0.9 karma is using di, I guess that is when it starts to support plugins. However, 0.9.3 is still not a stable version.
Found the answer by myself. There is PROBABLY no way to write a plugin for a general nodejs app if the app itself doesn't explicitly support plugins.
However, it is extremely easy to change an app to support plugins. The only thing to do is using the powerful "require" to obtain the plugin module with its name. For example, in Karma 0.9.4 (latest version by now), it has plugin.js which loads all plugin modules:
...
var requirePlugin = function(name) {
log.debug('Loading plugin %s.', name);
try {
modules.push(require(name)); // Call require here!
} catch (e) {
if (e.code === 'MODULE_NOT_FOUND' && e.message.indexOf(name) !== -1) {
log.warn('Cannot find plugin "%s".\n Did you forget to install it ?\n' +
' npm install %s --save-dev', name, name);
} else {
log.warn('Error during loading "%s" plugin:\n %s', name, e.message);
}
}
};
And all the custom plugins could be set in the conf.js file like this:
...
plugins: ['you-own-plugin'],
...
To write your own plugins, have a look at the Karma github, there are lots of examples of plugins for Karma.
Unfortunately, the latest stable version of Karma (0.8.6) doesn't support external plugins. I had to modify the preprocessor.js and reporter.js myself to support custom preprocessors and reporters. I provide the change below, it is simple change but not really recommended.
//preprocessor.js line 26
processor = exports[processorName] || require(processorName.toLowerCase())
//reporter.js line 48
var Reporter = exports[helper.ucFirst(name) + (config.colors ? 'Color' : '')] || require(name.toLowerCase());

How to install npm package from nodejs script?

How to install npm package from nodejs script?
Question is not about simple installation npm packages via terminal,
it is about installation via nodejs script:
Not about this: npm install express, but about having install.js file with content npm install express, which I will execute like node install.js and after this it will locally install express module in this folder.
Sorry, but Google and DuckDuckGo are not my friends today(
The main problem is in automatic local installation required packages for my small utility, because global packages are not working in windows.
Check out commander.js it allows you to write command line apps using node.
Then you can use the exec module.
Assuming you put the following in install.js, you just have to do: ./install.js and it will run npm install for you.
#!/usr/bin/env node
var program = require('commander');
var exec = require('child_process').exec;
var run = function(cmd){
var child = exec(cmd, function (error, stdout, stderr) {
if (stderr !== null) {
console.log('' + stderr);
}
if (stdout !== null) {
console.log('' + stdout);
}
if (error !== null) {
console.log('' + error);
}
});
};
program
.version('0.1.3')
.option('i, --install ', 'install packages')
.parse(process.argv);
if (program.install) {
run('npm install');
}
var count = 0;
// If parameter is missing or not supported, display help
program.options.filter(function (option) {
if(!(option.short == process.argv[2]))
count++
});
if(count == program.options.length)
program.help();
Hope this helps!
NOTE: I don't think this fulfills all the requirements of your question, because at the end you state that you can't find npm...so maybe your question would be better titled "How to install npm package without npm?"--yikes! But it addresses the title, "How to install npm package from nodejs script?"
I've just been shown another alternative to do this: the module npmi. While this is still another module dependency, it does at least work without a *nix shell script environment, which I think the other answer here (about commander.js) does. And, if you look inside the code for npmi.js, you'll find it's very short and merely uses the npm module directly in the node script--which you can do yourself if you don't want to add the npmi module.
So in our case we needed a way to install modules without requiring a *nix shell script (to support Windows users), and this fits the bill nicely.
That still doesn't help you if you can't require('npm'). Only thing I can think of there is trying likely absolute paths...you can require('C:\Program Files\Node\packages\x)`, I think--or wherever node's global packages are stored (per user?). Wrap a couple of attempts in try/catch or test for the file's existence first and try to require the npm module whenever you find where the global packages are actually installed? You might tick off a malware scanner :-), but it might work.

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