In package.json scripts key: npm run or not? - node.js

In an online tutorial, the package.json said (simplified):
"scripts": {
"dev": "npm run webpack -- --mode development"
},
I usually write, and it works:
"scripts": {
"dev": "webpack --mode development"
},
Is the first version in any way preferable? I understand the meaning of --, but I do not know whether encapsulating the command in another npm run call provides any benefits.

The first call allows you to change the webpack command used, or add additional arguments, or even run an entirely different command at all.
For example, npm run tsc on my repos runs npm run delDist && tsc.

Related

package.json: what is the difference between & and &&?

Title pretty much says it all, but I'd also like to know if these commands work or behave differently depending upon the OS.
example1:
"scripts": {
"build": "babel -d serverbuild ./server",
"exe": "node ./serverbuild/index.js",
"start": "npm run build && npm run exe"
}
example2:
"scripts": {
"build": "babel -d serverbuild ./server",
"exe": "node ./serverbuild/index.js",
"start": "npm run build & npm run exe"
}
Given these examples portions of a package.json, what would be the difference between npm run start?
When using &&, the first command will run, and if it does not error, the second command will run. It's like a logical AND.
Using &, however, will run a command in the background. So in your second package.json, npm run build will start running in the background and then npm run exe will run as well regardless of what happens to the first command.

Syntax (windows and linux) for multiple commands using Node cross-env

I'm taking a look at a project that has this line in it's package.json to run Karma tests
"scripts": {
"test": "NODE_ENV=test karma start karma.conf.js",
This doesn't work in Windows when I try "npm test".
I gather this is because this is a *nix syntax. And, in fact, if I change it to
"scripts": {
"test": "set NODE_ENV=test && karma start karma.conf.js",
the tests start when I run npm test.
Looking around, the optimal solution appears to be to use the cross-env package and rewrite it like
"scripts": {
"test": "cross-env NODE_ENV=test && karma start karma.conf.js",
So I get the cross-env will take care of the "set NODE_ENV" part to work on multiple OSes, but it's the "&&" part I'm questioning.
Do I leave the "&&" between the commands when using cross-env? Will that work in windows and linux?
Your cross-env example won't work, it should just be:
"scripts": {
"test": "cross-env NODE_ENV=test karma start karma.conf.js",
without the &&. The command to run comes immediately after you're done setting the variables.
I found your question by searching for how to set multiple variables with cross-env and also how to run multiple scripts/commands with cross-env. So to address the "multiple commands" part of your question:
Given these 2 test scripts:
a.js
console.log('a', process.env.TEST_VAR, process.env.TEST_VAR_2);
b.js
console.log('b', process.env.TEST_VAR, process.env.TEST_VAR_2);
You'll find if you have an && in the script in your package.json the subsequent scripts don't receive the variables. For example:
"scripts": {
"check": "cross-env TEST_VAR=hello TEST_VAR_2=world node a.js && node b.js",
Running npm run check gives:
a hello world
b undefined undefined
You can solve this by having an extra script which runs the multiple commands and running that with cross-env:
"scripts": {
"check": "cross-env TEST_VAR=hello TEST_VAR_2=world npm run check-real",
"check-real": "node a.js && node b.js",
Now npm run check gives:
a hello world
b hello world

How do I deploy my Typescript Node.js app to Heroku?

When testing locally I was previously running:
"build-live": "nodemon --exec ./node_modules/.bin/ts-node -r dotenv/config -- ./index.ts"
I then figured my Procfile should be something like:
web: ./node_modules/.bin/ts-node -- ./index.ts
But it says module 'typescript' not found, even when it is in package.json. I read in a few places that ts-node is not the way to go to deploy to Heroku, so I am not sure what to do.
UPDATE: I think I am supposed to compile it, so I tried:
web: ./node_modules/.bin/tsc --module commonjs --allowJs --outDir build/ --sourceMap --target es6 index.ts && node build/index.js
This succeeds, however when actually running it, a bunch of the libs I'm using get "Cannot find module '...'".
Alternatively you can have the TypeScript compile as a postinstall hook and run node build/index.js as the only Procfile command:
Your package.json should contain a postinstall hint that gets executed after npm install and before the node process launches:
"scripts": {
"start": "node build/index.js",
"build": "tsc",
"postinstall": "npm run build"
}
You can then leave your Procfile as is:
web: npm start
This 'build on deploy' approach is documented by Heroku here.
The command you've given Heroku is to launch the web "process" by compiling index.ts and dependencies and starting node at index.js. Depending on how things are timed, index.js might or might not exist at the time node starts.
You need to already have your sources compiled by the time you want to start your app. For example, web should just be web: node index.js or similar.
Each build process is different, so you need to figure that out for your own setup. But, suppose you have a classical setup where you push to git and then Heroku picks up that change and updates the app with the new slug. You could just compile things locally and include index.js and any other build output in the repository, for it to be available in the slug for Heroku to use.
A better approach is to use a build server which has an integration with Heroku. After you do the build there, configure it to send the build results to Heroku. Travis has a straighforward setup like this. This way you don't need to include build outputs in your repository, which is considered an anti-pattern.
On a sidenode, try using a tsconfig.json to keep the tsc configuration. It will save you from having to write such long command lines all over the place.
Fabian said that we could do something like:
"scripts": {
"start": "node build/index.js",
"build": "tsc",
"postinstall": "npm run build"
}
As of me writing this, I tested this and can state: postinstall is not required since build script is ran by Heroku. If you want to do it without build script, then you can use heroku-postbuild which will run after dependencies are installed there you run tsc to compile.
My problem was about missing Typescript npm modules. The Typescript compiler tsc was not found when deployed the app to Heroku.
The Heroku deploy process (rightly) does not install development dependencies, in my case the Typescript module was part of devDependencies and thus the tsc command was not running on the Heroku platform.
Solution 1
Add typescript to dependencies: npm i typescript -s
Solution 2
Open Heroku console:
Select console type:
Run the command npm i typescript && npm run tsc
Install typescript as a dev dependency (cf. https://www.typescriptlang.org/download). Once built, your app does not need typescript anymore!
npm install -D typescript
Then in your package.json:
{
"main": "index.js", // <- file will be generated at build time with `tsc`
"scripts": {
"build": "tsc",
"start": "node ."
"start:dev": "ts-node index.ts" // idem, install ts-node as a dev dependency
}
}
The key point here is "build": "tsc".
Why?
Heroku does install all dependencies during build and remove the dev dependencies before the app is deployed (source here).
Node.js deployments will automatically execute an app’s build script during build (since March 11. 2019 source here)
In package.json
"scripts": {
"tsc": "./node_modules/typescript/bin/tsc",
"postinstall": "npm run tsc"
},
Works for me for Heroku deployment.
Installing typescript npm install -D typescript and writing tsc in the build script "build": "tsc", does not work for me. Also, try to run npm i typescript && npm run tsc in the Heroku console which also does not work.
In my case, I remove some dependencies from "devDependencies" to "dependencies", so it goes like this:
"dependencies": {
// The other dependencies goes here, I don't touch them.
// But all TS dependencies I remove to here.
"ts-node": "^9.1.1",
"tsconfig-paths": "^3.9.0",
"typescript": "^4.2.3",
"ts-loader": "^8.0.18"
},

How do I execute typescript watch and running server at the same time?

I was developing my project in nodejs. I found if I need to code and test api, I will run two console, one is to execute typescript watch, another is to execute server.
I think it's so troublesome. I find other developers on github have written scripts in package.json. It's easy to call any commands. It attracts how to write the scripts and simply my development workflow.
In short, the comand of typescript watch is tsc -w and the comand of running server is node app.js. My idea is merge the commands as tsc -w & node app.js but I can't work the two commands at the same time. How do I do? Thanks.
My idea is merge the commands as tsc -w & node app.js but I can't work the two commands at the same time. How do I do
You have a few options. Simplest is to use ts-node-dev : https://github.com/whitecolor/ts-node-dev
Option 1
Step 1
install concurrently, use npm, pnpm or yarn
pnpm i concurrently -D
Step 2
create a script with this command
"scripts": {
"run": "tsc && concurrently \"tsc -w\" \"nodemon dist/app.js\"",
}
Option 2
without install anything (mac or Linux)
"scripts": {
"run": "tsc -w & nodemon dist/app.js",
}
run tsc first so that your directory has something at the time of running node
And with that you will have running your Typescript application 🚀
Another option can be to use nodemon:
tsc -w & nodemon app.js
Since Typescript 3.4 the compilation is faster because you can use the incremental compiler option and they keep improving (including interesting changes for large projects in 3.8).
Update:
I also moved to use concurrently as HerberthObregon says in his answer
TLDR, If you like nodemon this is a straight forward way to get file watch, compilation and execution:
nodemon --ext ts --exec 'tsc && node dist/index.js'
Optionally replace tsc with babel for faster compilation.
Here is a more complete example, in package.json (with source maps):
"scripts": {
"develop": "nodemon --ext ts --exec 'yarn build --incremental && yarn serve'",
"build": "tsc",
"serve": "node --require source-map-support/register dist/index.js",
...
},
Install source-map-support as a dependency if you want, ahem... source map support! Otherwise, remove --require source-map-support/register from the serve script above.
tsconfig.json
{
"compilerOptions": {
...
"sourceMap": true,
"outDir": "dist",
}
}
Building on herberthObregon's answer
Step 1: Install packages
npm install concurrently typescript nodemon --save-dev
Step 2: Create these scripts in package.json
"scripts": {
"build": "tsc",
"build:watch": "tsc -w",
"dev": "npm run build && concurrently \"npm run build:watch\" \"npm run serve:watch\"",
"serve": "node dist/index.js",
"serve:watch": "nodemon dist/index.js"
},
build runs a standard typescript build
build:watch runs typescript build in watch mode
serve serves up your node project (assuming your tsconfig outputs to dest/index/js)
serve:watch uses nodemon to restart the node server whenever the js output changes
dev puts them all together
Just going to throw my hat in here, here's a solution using ts-node-dev and concurrently, similar to the one provided by #HerberthObregon but using ts-node-dev instead of nodemon:
"scripts": {
"start": "npm run build && concurrently \"npm run build:watch\" \"npm run dev\"",
"dev": "tsnd --respawn src/main.ts",
"build": "tsc -p tsconfig.release.json",
"build:watch": "tsc -w -p tsconfig.release.json"
}
Bonus: If you need help with your figuring out tscand your tsconfig.json, I use the sensible defaults from this node typescript starter.
Here's a solution that works for me
1. Install ts-node and nodemon as dev dependencies
2. Create a script : "dev" : "nodemon app.ts"

NODE_PATH not recognized

Here is my package.json script:
"scripts": {
"start": "NODE_PATH=$NODE_PATH:./shared node",
"dev": "npm run start & webpack-dev-server --progress --color"
},
When I run npm start in Windows 8 it shows the below error:
node_path is not recognized as a internal or external command, operable program or batch file
I had the same problem when I wanted to set the environment variable in a browserify script:
"scripts": {
"build:symlinked": "NODE_PATH=./node_modules browserify src/index.js > dist/build.js"
}
To be able to use linked node modules that are requiring peer-dependencies.
As mentioned above, you can try to set the environment variable manually or by script where it seems you have to use different commands depending on what command line tool you use.
For not having to do this every time, I found that npm package: cross-env.
By installing it and applying the script like this
"scripts": {
"build:symlinked": "cross-env NODE_PATH=./node_modules browserify src/index.js > dist/build.js"
}
I was able to solve that problem. This is mainly useful, if you work in a team with mixed MAC/Linux and Windows users, so you don't have to to take care about applying the Environment variables in such scripts anymore.
You don't need to define environment variable in package.json just use this
{
"scripts" : "node server.js"
}
or define what you want, here is the reference link.

Resources