Custom NPM package - Custom command line command - javascript

I recently created an NPM package called Project_1 (which I installed in another of my Node.js projects called Project_2) using the command:
npm install --save ./path/to/Project_1
(Is there a better method to install a local package inside one other locally?)
So the packaje.json of Project_2 is as follows:
{
"name": "Project_2",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
},
"dependencies": {
"Project_1": "file:../Project_1",
}
}
In the npm package I created (Project_1), the JSON package is as follows:
{
"name": "Project_1",
"scripts": {
"custom-serve": "http-server ./website --port 8888 -o"
}
}
When I am in the root of Project_1 I can launch from the terminal:
npm run custom-serve
And in this way I can execute my custom command.
I need to know how to call npm run custom-serve inside Project_2 (also from the command line) after installing the Project_1 package as an npm dependency in Project_2.
So, in the root of Project_2 I would like to be able to run npm run custom-serve so that the command written in the Project_1 library is triggered.
How can I do this? How should I set the JSON packages? Is there a best practice for doing this? Do I need to add special scripts in .js?
This is because I have noticed that always when installing npm packages they provide commands that can be launched from the root of the project in which they are installed.
NB: The command custom-serve is just an example. I would like to create some custom commands inside the Project_1 and i want to be able to call them inside the Project_2 after the npm install of the Project_1 package.
I have already tried to create a script inside the Project_2 like so:
"scripts": {
"custom-command": "cd ./node_modules/Project_1 && npm run custom-serve",
}
But it doesn't works.

Firstly, to answer your question if there's a better way to install a local dependency, there is via npm link.
Assuming the name in Project 1's package file is project-1, you can link it in Project 2 as follows (obviously using the paths that correspond to your setup):
cd ~/projects/project-1
npm link
cd ~/projects/project-2
npm link project-1
Secondly, if you want your package to expose a runnable command, you should configure this in the bin section of its package.json. The path should point to an executable file, so to reuse the npm script, you could use execute as follows
custom-command.js
const {exec} = require('child_process');
exec('npm run custom-serve');
And then add the bin section to the package.json of Project 1:
"bin": {
"custom-command": "./custom-command.js",
}

If you have an index file inside your Project_1, and you had Project1 as a dependency for Project2, you can just call the file and it will run the default start command:
In Project_1 package.json file:
{
"name": "Project_1",
"scripts": {
"custom-serve": "http-server ./website --port 8888 -o",
"start": "npm run custom-serve"
}
}
In Project_2 package.json file:
{
"name": "Project_2",
"scripts": {
"custom-command": "node Project_1"
}
}

Related

Can my NPM CLI package be executed on CMD without installing globally?

I have written an NPM package which has its own CLI commands.
Let's name that package as xyz and imagine it's now avaialable on npmjs.com
So, let's say a user installs this package in his project by running npm install xyz.
And now he wants to run a CLI command provided by xyz package on his terminal in his project.
xyz do_this
Can this be done without installing this package globally by user ? Or without any further configuration for the user ?
Here's some part of the package.json of the xyz package.
{
"name": "xyz",
"version": "1.0.0",
"description": "Description",
"main": "index.js",
"preferGlobal": true,
"bin": "./index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
........
Here's how npm works. When you install a package in the local directory, it installs the executable dependencies files inside node_modules inside the folder with package.json, but if you use --global, it places it globally where the user has set their path.
For example, when I run npm install http-server, my executable ends up as ./node_modules/http-server/bin/http-server but when i install it globally, I have it as node_modules/http-server/bin
The work around for this is, if you want to just run the executable, just execute it inside like so ./node_modules/http-server/bin/http-server. If you want it as a command, you'll need too have the user add the directory to their Path so when the user enters the command, the computer looks for the command inside that folder.
Here's a guide to adding PATH directories in Linux, you'll just add the directory /pathtofolder/node_modules/http-server/bin/ or whatever your folder is. https://linuxize.com/post/how-to-add-directory-to-path-in-linux/
For example, if I wanted to add http-server from my local folder to my path, I would run
export PATH="/pathtofolder/node_modules/http-server/bin/:$PATH"
Good luck! Let me know how I can help you!

Apply changes from linked npm modules without rebuilding

My webapp consists of two modules from separate git repos, with the following directory structure:
webapp/module1
webapp/module2
module1 depends on module2, so I've added the link:
cd webapp/module1
npm link ../module2
The module1 is main module, so I'm running webapp using npm start from there:
cd webapp/module1
npm start
start is configured in module1's package.json as follows:
{
"scripts": {
...
"start": "webpack-dev-server --open --progress --colors & npm run build:css -- -w",
"build:css": "node-sass src/style/main.scss dist/webpage.min.css --output-style compressed"
}
}
The problem is when I make a change to module2 source code editing its javascript sources - the change is not applied immediately to the running webapp instance. I need to execute npm run build manually:
cd webapp/module2
npm run build
build is configured in module2's package.json as follows:
"build": "cross-env WEBPACK_ENV=prod && npm run v:patch && webpack"
Only after this step the changes are applied to the webapp. Is there a way to get such changes to be applied automatically? I'm using npm version 5.6.0.

Using babel-cli locally

Is there a way to use the babel client without installing it globally?
So rather than this
npm install -g babel-cli
I'd like to do this
npm install babel-cli --save-dev
Any local package's binary can be accessed inside npm scripts as if it was installed globally:
// package.json
{
"scripts": {
"build": "babel ..."
}
}
If you want to execute the binary on the command line, you can use a relative path to node_modules/.bin/:
$ node_modules/.bin/babel ...
This is related to first example: node_modules/.bin/ is simple added to the PATH of the environment the npm scripts are executed in.
you can put something like this:
{
"scripts": {
"start": "babel-node test.js"
}
}
in your package.json where test.js is a script which you want to run. Now you can run it with npm start command
Yes, you could install locally and run from node_modules:
./node_modules/.bin/babel
If you have a local package.json you could add an NPM script to simplify the command, since NPM scripts run with ./node_modules/.bin on the PATH:
"scripts": {
"babel": "babel ...",
}
To run from any directory under package.json:
$ npm run babel
If you just want to run test with command "npm test testFile.js". This is my package.json:
"scripts": {
"build": "babel-node",
"test": "node_modules/.bin/babel-node"
}

Nodejs - npm start & npm build both do not work?

this is a snippet from my package.json
"scripts": {
"start": "mkdir BigDirectory",
"build": "mkdir BigDirectory"
},
I've noticed that npm build simply goes to the next new line in terminal (without creating a directory)
While npm start works and creates the "BigDirectory"
Why is npm build non-responsive? Am i perhaps allowed only one single script in my package.json?

Npm postinstall only on development

I have npm module with following package.json
{
"name": "my-app",
"version": "0.0.0",
"scripts": {
"prepublish": "bower install",
"build": "gulp"
},
"dependencies": {
"express": "~4.0.0",
"body-parser": "~1.0.1"
},
"devDependencies": {
"gulp": "~3.6.0",
"bower": "~1.3.2"
}
}
When I deploy my app to production, I don't want install devDependecies, so, I run npm install --production. But in this case, prepublish script is called, but it doesn't need to, because I use CDN links in production.
How to call postinstall script only after npm install but not after npm install --production?
Newer npm (& Yarn) versions include support for the prepare script that is run after each install run but only in development mode. Also, the prepublish is deprecated. This should be enough:
{
scripts: {
"prepare": "bower install"
}
}
Docs: https://docs.npmjs.com/misc/scripts
I think you cannot choose what scripts are run based on the --production argument. What you can do, however, is supply a script which tests the NODE_ENV variable and only runs bower install if it's not "production".
If you are always in a unix-y environment, you can do it like this:
{
scripts: {
"prepublish": "[ \"$NODE_ENV\" = production ] && exit 0; bower install"
}
}
This only works if you're on a unix-like environment:
NPM sets an environment variable to "true" when install is run with --production. To only run the postinstall script if npm install was not run with --production, use the following code.
"postinstall": "if [ -z \"$npm_config_production\" ]; then node_modules/gulp/bin/gulp.js first-run; fi",
Solution that is less dependent on unix nature of your shell:
"scripts": {
"postinstall": "node -e \"process.env.NODE_ENV != 'production' && process.exit(1)\" || echo do dev stuff"
},
I work with windows, osx and linux so I use a NON environment specific solution to solve this problem:
In the postinstall handler i execute a js script that checks process.env.NODE_ENV variable and does the work.
in my specific case I have to execute a gulp task only in development env:
part of package.json
"scripts": {
"postinstall": "node postinstall"
}
all postinstall.js script
if (process.env.NODE_ENV === 'development') {
const gulp = require('./gulpfile');
gulp.start('taskname');
}
last row of gulpfile.js
module.exports = gulp;
it's important to export gulp from the gulpfile.js because all tasks are in that specific gulp instance.
I have a more general problem - where I want to skip running the postinstall script on local (direct) installs - like when I'm developing the package and run yarn add --dev my-new-dependency.
This is what I came up with. It works with both npm and yarn.
postinstall.js:
const env = process.env;
if (
// if INIT_CWD (yarn/npm install invocation path) and PWD
// are the same, then local (dev) install/add is taking place
env.INIT_CWD === env.PWD ||
// local (dev) yarn install may have been run
// from a project subfolder
env.INIT_CWD.indexOf(env.PWD) === 0
) {
console.info('Skipping `postinstall` script on local installs');
}
else {
// do post-installation things
// ...
}
package.json:
"script": {
"postinstall": "node postinstall.js",
...
Landed here because I had the same issue. Ended up with a solution that tests for the existence of a package under node_modules that I know should only be available in development.
{
"scripts": {
"postinstall": "bash -c '[ -d ./node_modules/#types ] && lerna run prepare || echo No postinstall without type info'"
}
}
This works fine for me conceptually, as the prepare scripts here called by lerna are mainly to ts-to-js compilations.
I'm using if-env module. It's less verbose.
PS: I didn't test it on windows yet.
Install with:
npm i if-env
than in package.json scripts:
"postinstall-production": "echo \"production, skipping...\"",
"postinstall-dev": "echo \"doing dev exclusive stuff\"",
"postinstall": "if-env NODE_ENV=production && npm run postinstall-production || npm run postinstall-dev"

Categories