Declaring global commands inside a NPM package - node.js

I'm try to understand how to declare a command inside a NPM package accessible through the main application.
For instance, I released a project inside the NPM registry, with a couple of commands inside the package.json like:
"scripts": {
"somestuff": "node index.js"
},
Let say now I want to use this command somestuff inside the main app. When I try to run this I receive: missing script: somestuff.
Because is not defined inside the package.json of the main app but it's inside the package.json of the module inside the node_modules folder.
How can I declare a command inside a node module accessible from the main app?

I haven't tried it myself, but I think you can do something like this. In your package.json:
{
"name": "my-package",
...
"main": "index.js",
"bin": "index.js",
...
}
Then invoke it from wherever you installed it as a dependency:
npx my-package
You would probably want to install my-package with --save-dev.
Also, have a look at npx-run project, it looks exactly as what you want.

Related

Npm install from repo not running `prepare`

I have an npm package for common components hosted on an internal git server. For some reason when I call npm install in another project I want to consume this in it will not run the prepare hook. Obviously, this does not work since the npm package needs a /dist folder in node_modules to be able to consume the package.
I have already tried things such as using the deprecated prepublish hook and even that does not get called. I also tried to do postinstall to see if I could build after install, while that hook did get called it failed because the devDependencies were not installed
package.json
{
"name": "common-components",
"version": "0.1.0",
"scripts": {
"prepare": "npm run build",
"build": "ng build",
...
},
"private": true,
"dependencies": {
...
},
"devDependencies": {
...
},
}
command being used for install
npm install --save git+ssh://{URL-to-common-components-repo}}
I have read through the npm-scripts documentation https://docs.npmjs.com/misc/scripts thoroughly and it seems like they insist that prepare hook should always be called for this exact use-case
Updated 5/6/2019
Just as a note I found this bug on NPM community https://npm.community/t/using-npm-ci-does-not-run-prepare-script-for-git-modules/632/4.
I am using npm 6.4.1 which should work according to the bug
One thing to check that hit me on a package recently - if there is a .gitignore and not .npmignore npm may be ignoring your /dist folder. Adding an empty .npmignore worked in this case.
"If there’s no .npmignore file, but there is a .gitignore file, then
npm will ignore the stuff matched by the .gitignore file. If you want
to include something that is excluded by your .gitignore file, you can
create an empty .npmignore file to override it."
from https://docs.npmjs.com/misc/developers
For those that are wondering the status of this. I was unable to ever get it to work. What I ended up doing was hosting the components on a private npm registry and that works fine since the npm publish command will do the build and only publish the dist folder
If adding an empty .npmignore does not help, you can try specifying all files in dist explicitly in package.json#files. If this works you might want to consider using a wildcard pattern that matches the files in dist to simplify the package.json.
package.json
...
"files": [
"source",
"dist/cjs/main.js",
"dist/es/main.js"
]
}
see this comment to a similar issue in the npm/cli repository https://github.com/npm/cli/issues/1287#issuecomment-635021757
It's very likely that your dist/ folder is in your .gitignore file. According to this response from an npm-cli maintainer:
In order to be able to properly prepare a git repo npm will run the
extracted files through npm-packlist in order to get the expected
files that are going to be placed in your node_modules folder.
Further checking the documentation of npm-packlist, we find that npm-packlist will respect the .gitignore file if it has nothing else to go off of:
If there's no package.json with a files list, and there's no
.npmignore file, but there is a .gitignore file, then ignore all the
files in the .gitignore file.
This article further expands on the idea.
It seems to me that the best fix is to explicitly declare the files that your package needs (including dist/) in the files section of your package.json file. Then you have complete control over what's included and the package size is minimized.
If you are using root user to npm install the package then the prepare script might not be triggered. The reason was the prepare child process has no permission to run (user account was set to default of 'nobody' when using npm with root). You can read more here: https://github.com/npm/npm/issues/17346
To fix this, in the lib package, create an .npmrc file and add:
unsafe-perm: true
Adding main in the package.json fixed this issue for me.
"main": "./dist/index.js",
"scripts": {
"build": "babel src --out-dir dist",
"prepare": "npm run build",
"lint": "eslint ."
},
node v14.15.4 npm 6.14.11

Managing Node JS dependencies

I am learning node js and I have put together a small website which is using a few dependencies like jquery and a couple of other js files. When it comes to moving the site to a live server how do I migrate the dependencies that I used in production? Would I have to download the jquery files and other js library files then add the link into my script tags like you normally would as I guess everything in production points at the node_modules folder?
Sorry for sounding a little daft but I’m still learning my way with npm.
Appreciate any advice
Rufus
In Node.js, server side dependencies are saved in the package.json file and these describe any dependencies you'll need.
You create this file by typing
npm init
This will prompt you for some details (which you can skip with -y flag.)
To install your server side dependencies, I presume you've used:
npm install <dependency>
To ensure this is saved you can type
npm install <dependency> --save
e.g.
npm install lodash --save
This will update your package.json.
package.json should be stored in source control and your dependencies can be restored using:
npm install.
An example of a package.json file would look like this:
{
"name": "node_test_project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"lodash": "^4.17.11"
}
}
If you see a package-lock.json file (later versions of NPM), this should be added to source control too.
Personally I just use npm install lodash
Then
app.use('/scripts/lodash', express.static(__dirname + '/node_modules/lodash/'));
that way I can use this as (in node.js)
const _ = require('lodash');
or in an html file as
<script src='/scripts/lodash/lodash.js'></script>
Preventing the whole "copying to public/js"...
Or if you're just talking about a build process, you just run npm install on your productions server and it installs all the packages from packages.json again.

node local dependency installs as shortcut and nested instead of flat

It seems this started when I updated node/npm but I didn't realized until now when I had to delete and re create my node_modules folder.
I have a React Native project which has the core module and one Examples project to showcase the module. The examples project references the module like this in my package.json:
"dependencies": {
"module-core": "file:../core"
},
When I run npm install in the Examples project I was getting this nodule_module structure:
node_modules
core
core_dependency_1
core_dependency_2
Now, I get this:
node_modules
core
node_modules
core_dependency_1
core_dependency_2
At first I thought it had to do with peerDepencies and how npm handled flat/nested dependencies but I have checked and it seems now the core folder is now a shortcut (I am using Windows).
This is breaking my gradle scripts because some are referenced like this:
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
I could fix this by renaming the links but that would make the build platform/environment dependent.
Also it breaks some other scripts like this one:
apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"
That is because the project() resolves to my root folder and now I cannot use that either.
I am using npm 5.4.2 and node 8.8.1. Previously I had node 7.4.0.
Is there any flag or way to make npm install the localDependency and not treat it as a shortcut?
I finally found the answer. Npm Version 5 indeed changed the way the local dependencies are handled and it just makes npm link, which creates symbolic links or shortcuts in windows.
You can accomplish the same behavior as before with this:
npm install $(npm pack <folder> | tail -1)
Working for me in Windows 10 with git-bash
My final solution was having this package.json in the Example project that used the core:
{
"name": "core-examples",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"preinstall": "npm pack ../Core | tail -1",
},
"dependencies": {
"core": "file:core-0.0.0.tgz"
},
"jest": {
"preset": "react-native"
}
}
The preinstall script will generate the tgz file and then you can install as usual. This will avoid having to commit the tgz file to the repository.

How to save the installation of global package using package.json?

How to save the installation of global package using package.json?
I couldn't find any good solutions but I did this like below:
In package.json I added the following:
"scripts": {
"preinstall": "npm install babel babel-cli -g"
},
It would run and install the above packages globally before installing all the dependencies and devDependencies in package.json.
But the problem would be that I would not be able to find out, are those packages installed globally in any of the machines.
Please help, if anyone have any better solution of this.
It's a bad practice to force global install of a module. You can put babel and babel-cli in your devDependencies and then use them in your npm scripts :
{
"build" : "babel src -d build"
},
"devDependencies": {
"babel-cli": "^6.18.0"
}
If you are looking to run your package via CLI, you need to set up using bin approach.
Here's one of my libraries for example, fol. It is meant to be ran via command line only.
At the most minimal setup, you would wire both main and bin to the same JS file, for example, adding the following in package.json:
"main": "./bin/fol.js",
"bin": {
"fol": "./bin/fol.js"
},
Then, put the files into bin folder for the same of consistency, everybody will recognise that's CLI app stuff if it's in /bin/.

Find the package.json file from within an npm script that runs on preinstall

So I need to read the package.json before installing a new package via npm.
Why reading package.json in the first place?
I am using npm for CSS components that are individually versioned and can have inter dependencies. (No javascript is delivered)
Looking for version conflicts for a bunch of dependencies I need to detect when package A requires package C#1.0.0 and package B requires package C#2.0.0 and deal with it.
Npm (as of version 3) deals with these issues by nesting a conflicting module deeper inside the tree. You now end up with both versions of the same module. CSS has a global namespace and a mixin (in Sasss case) would then overwrite each other and break your CSS.
This flat dependency issue is perfectly outlined in the npm blog: http://blog.npmjs.org/post/101775448305/npm-and-front-end-packaging
Even not considering our specific use case it strikes me as odd that you don't have access to the package.json in preinstall and postinstall scripts. They seem to be just for that use case.
What I tried
My package.json of the package I'm installing looks like this:
{
"name": "testmodule",
"version": "0.3.6",
"description": "TODO",
"scripts": {
"preinstall": "npm i some-script && some-script",
},
"author": "TODO",
"license": "MIT"
}
Inside that some-script package I run:
console.log( process.cwd() );
console.log( __dirname );
I then run:
~/path/to/folder $ npm i testmodule
This will result in:
$ npm i testmodule
> testmodule#0.3.6 preinstall /path/to/folder/node_modules/.staging/testmodule-5cc9d333
> some-script
/path/to/folder/node_modules/.staging/test-module-5cc9d333
/path/to/folder/node_modules/.staging/test-module-5cc9d333/node_modules/some-script
Now I totally get that I can't really access the root of where npm i was ran because my script was run by a subprocess of npm and has an entirely different root.
I then thought npm root should keep track where the actual root was for me and passed that as a parameter to my script from inside the testmodule package.json:
{
"name": "testmodule",
"version": "0.3.6",
"description": "TODO",
"scripts": {
"preinstall": "npm i some-script && some-script \"$(npm root)\"",
},
"author": "TODO",
"license": "MIT"
}
Unfortunately that also defaults back to a staging path:
/path/to/folder/node_modules/.staging/testmodule-5cc9d333/node_modules
I filed an issue with the registry but not holding my hopes up for them to get to that in time. Also my script needs to work on older npm installations.
In the meantime I came up with something like that inside my some-script:
let pgkPath = process.cwd().split('/node_modules/')[0];
That will return /path/to/folder/ which is correct but it makes the assumption no-one runs an npm i inside a folder incidentally named node_modules... Seems hacky.
Question
How can I access the path to the package.json from inside an npm script that is run via preinstall? To me that seems like something not too outrageous to ask for?
I don't understand your use-case entirely, but to answer your specific question of finding a parent package.json from a preinstall script:
Pass $(cd .. && npm prefix) as an argument to your script, then load ./package.json.
npm prefix will return the closest parent directory to contain a package.json file, which when invoked from the .. directory, should return the parent npm package's path.
{
"name": "testmodule",
"version": "0.3.6",
"description": "TODO",
"scripts": {
"preinstall": "npm i some-script && some-script \"$(cd .. && npm prefix)\"",
},
"author": "TODO",
"license": "MIT"
}

Resources