Using subdependencies in npm script - node.js

Say I have a private npm package, #myprivate/repo which has the following contents in its package.json:
"scripts": {
"example": "db-migrate"
},
"bin": {
"foo": "bin/foo"
}
Where bin/foo is:
#!/bin/bash
npm run example
I now pull this into a parent repo with npm install:
package.json:
{
"dependencies": {
"#myprivate/repo": "*"
},
"scripts": {
"example": "unrelated command",
"useful": "foo"
}
}
Then running npm run useful results in the foo bin script getting called, which then attemps to call unrelated command. How do I scope the invocation to the dependency? How can I force a bin script to package its own npm dependency and rely on that? Is nested scripts in nested dependency package.json the best way, or is there a better more canonical solution?

I ended up being able to get the behaviour I want by changing the last line of bin/foo to:
npm explore #myprivate/repo npm run example
This does feel a little bit happy (I'm referencing the repo using npm explore from within itself), but it does get the job done. Would love to hear of a better solution

Related

How to disable local package dependency for `npm install --prefix`?

I have a project with at its root the following simplified package.json file:
package.json
{
"name": "parent-project",
"dependencies": {
...
}
}
In a subfolder that we'll call child-project, another package.json resides:
child-project / package.json
{
"name": "child-project",
"dependencies": {
...
}
}
Some code that I depend on uses the command npm --prefix ./child-project install to install dependencies in child-project. However, this always has the undesirable side-effect of altering child-project/package.json like so:
{
"name": "child-project",
"dependencies": {
...
"parent-project": "file:.." // <- I don't want this!
}
}
When I execute cd ./child-project && npm install all is fine and child-project/package.json remains untouched, so my hunch is that it has to do with --prefix but documentation on --prefix is very obscure.
Is there a way to disable this behaviour and prevent NPM from altering the child-project/package.json?
Seems like there are some issues with the --prefix.
However, I couldn't find any issue while I was testing with different modules which have sub-packages.
Hence, according to one of the npm contributors, it is going to be refactored or removed in a future release, which means it is not the best choice to use it at least for now. So I recommend you go through the traditional way and avoid using --prefix.
I think we're gonna close this as a wontfix as --prefix is not the best flag to be promoting. We do eventually want to refactor or remove it but for now we should at least not be promoting it.

Can I run a command from package.json bin script?

Is it possible to run a command from a bin script in a package.json ? I know it expects a path to file and trying to run a command there results in an error upon installation (after publishing to npm). Is it possible to run a command like it is in an npm start ?
Examples:
{
"name": "myscript",
"version": "1.0.3",
"bin": {
"myscript": "app/main.js"
}
}
This will create a symlink from the app/main.js script to /usr/local/bin/myscript
Instead, this is what I want to achieve:
{
"name": "myscript",
"version": "1.0.3",
"bin": {
"myscript": "echo hello world"
}
}
Possible workarounds are also appreciated.
This answer is updated since the old answer was a bit dated and ultimately incorrect. You can now do:
npx myscript
Have you tried running npm link https://docs.npmjs.com/cli/link which would create a link to your binary, then you can just run your script on the command line as myscript.

NPM - package.json#engines | How to specify Python?

I need to specify a python version on my package.json.
Can I simple do:
{ engines: { "python": "2.7.11" } } ?
Putting "engines": { "python": "2.7.11" } won't cause any issues (as far as I am aware) in your package.json, but it won't actually do anything either.
Determining an appropriate way to do this depends on particulars of your project. If it's about Python code you've written, you can check the version within the Python script itself. If it's about a build step in package.json, you can perhaps test as part of the build step.
As of NPM 7.x (same is valid for legacy NPM 6.x), the only valid entries for "engines" field in package.json, are the "node" version, and the "npm" version.
Furthermore, this is not a hard constraint unless you use "engine-strict" as well, as stated by the NPM docs:
Unless the user has set the engine-strict config flag, this field is advisory only and will only produce warnings when your package is installed as a dependency.
Your requirement, expecting a specific python version, is more related to an environment requirement than to your Node/NPM environment.
You can achieve this by implementing a "postinstall" NPM script that can result an error if the desired version is not found:
{
"scripts": {
"postinstall": "node ./check-python.js"
}
}
This script will be executed by NPM automatically after npm install. You could also use "preinstall" instead.
Consider using it on your "build" or "prebuild" scripts as well, based on your requirements. See more details on NPM pre- and post- scripts in the docs.
Then, your check-python.js script could be something like:
const { exec } = require('child_process');
const EXPECTED_PYTHON_VERSION = "2.7.11";
exec('python -c "import platform; print(platform.python_version())"',
function(err, stdout, stderr) {
const currentPythonVersion = stdout.toString();
if(currentPythonVersion !== EXPECTED_PYTHON_VERSION) {
throw new Error(`Expected Python version '${EXPECTED_PYTHON_VERSION}' but found '${currentPythonVersion}'. Please fix your Python installation.`);
}
});

Is it possible to consume environment variables inside of npm / package.json?

I'm attempting to build a package.json so that when running a NodeJS app on Heroku it will run the scripts.postinstall step using an environment variable. For example:
...
"scripts": {
"postinstall": "command $ENV_VAR"}
},
...
I've looked at the docs and wasn't able to find something saying I can.
Is this even possible? Is this even desirable and "I'm Doing It Wrong"™?
Ignore the nay-sayers. You can do this in a cross-platform manner using cross-var:
"scripts": {
"postinstall": "cross-var command $ENV_VAR"
}
Updated answer due to new packages having been written
You can use the cross-var package to do this in a clean way:
...
"scripts": {
...
"postinstall": "cross-var command $ENV_VAR",
...
},
"dependencies": {
...
"cross-var": "^1.1.0",
...
}
...
Original answer
To answer the last questions, because they're the most important one: yes, no, and absolutely, because you've just broken cross-platform compatibility. There is no guarantee your environment syntax works for all shells on all operating systems, so don't do this.
We have a guaranteed cross-platform technology available to us already: Node. So, create a file called something like bootstrap.js, and then make npm run node bootstrap as your postinstall script. Since the code inside bootstrap.js will run like any other node script, it'll have access to process.env in a fully cross-platform compatible way, and everyone will be happy.
And many, many, many things that use common utils have node equivalents, so you can npm install them, locally rather than globally, and then call them in an npm script. For instance mkdir -p is not cross-platform, but installing the mkdirp module is, and then an npm script like "ensuredirs": "mkdirp dist/assets" works fine everywhere when run as npm run ensuredirs
And for convenience, the most common unix utilities have their own runner package, shx, which is fully cross-platform and makes the lives of devs even easier, with the "if you're writing code" equivalent being fs-extra.

package.json isn't installing dependencies when running npm install

I've created a package.json file for my private app. In it, I have my dependencies listed, as expected. Some of those dependencies have dependencies of their own. When running npm install on my app, it's not installing the dependencies of my dependencies. Is there something wrong with my package.json file which is preventing this? ("winston" is one of my dependencies which isn't properly installing)
{
"name": "my app",
"version": "0.0.1",
"dependencies" : {
"connect" : "1.8.5",
"express" : "2.5.8",
"socket.io" : "0.8.7",
"winston" : "0.5.9"
},
"engine": {
"node": ">=0.6"
}
}
Reponse to comments: NPM installs the top level deps, fine, no errors, it looks like it works. It just never downloads the deps of the deps. Will try the -d option.
Spaces are not allowed in the name option for package.json files.
The name ends up being part of a URL, an argument on the command line, and a folder name. Therefore, the name can't contain any non-URL-safe characters.
https://docs.npmjs.com/files/package.json#name
I had the same issue and with some googling, it seems that this is a problem in node.js: https://github.com/isaacs/npm/issues/1341
I noticed the winston line is ended with ,
This is not a valid JSON.

Resources