Passing variables from NPM Scripts to Webpack - node.js

I have a production build with Webpack that uses node's process.env to set environment variables:
webpack.prod.babel.js:
const DefinePlugin = new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
API_URL: JSON.stringify('https://myprodurl.com'),
},
});
packge.json:
"scripts: {
"build:prod": "webpack"
}
It's working fine, but I need something different.
I need to set the production url as variable in the NPM Script.
So, instead of this:
npm run build:prod
I need this:
npm run build:prod --URL https://myprodurl.com

How about defining your environment variable in the command line, like:
URL=https://myprodurl.com npm run build:prod
I tested this with a simple script and was able to print out the URL.
"scripts": {
"test": "./myTest.js"
},
myTest.js:
#!/usr/local/bin/node
'use strict'
console.log(process.env.URL);
console.log('Done!');
then:
$ URL=https://whatever.com npm run test
> my-test#1.0.0 test /Test/my-test
> ./myTest.js
https://whatever.com
Done!
EDIT: As mentioned by #RyanZim, see the following for Windows: https://github.com/kentcdodds/cross-env
(disclaimer: I don't use Windows and have never tried this lib)

Related

How to use command line params while running npm for 2 scripts

I have the following scripts in the package.json:
"scripts": {
"test" : "npm run module1 || npm run posttest",
"createenv": "node cliTest.js && npm run test"
}
cliTest.js has the following:
console.log(process.argv);
I need to run createenv first to create an env file that will be used by the script 'test'. The problem is that the arguments in the CLI are not made available.
So, if I run the following:
npm run createenv foobar
I get the following and I do not see 'foobar'
[
'/Users/xxxxx/.nvm/versions/node/v16.13.2/bin/node',
'/Users/xxxxx/Documents/core/cliTest.js'
]
How can I retrieve the value foobar from the CLI in my cliTest.js file?
If you really need this &&-chain you can try npm_config_, something like this:
"createenv": "node cliTest.js ${npm_config_name} && npm run test"
then run the command:
npm run createenv --name=foobar
the last argument in console.log should be foobar.

How do you work with dev, pre and prod with Snowpack?

I want to define environment variables in my package.json where I'm using Snowpack, but as far as I know Snowpack sets the NODE_ENV automatically based on dev vs build.
Is it possible to define variables for 3 modes instead of 2, I mean:
development
preproduction
production
These are my scripts in my package.json:
"scripts": {
"start": "snowpack dev",
"build": "snowpack build NODE_ENV=pre",
"build:production": "snowpack build NODE_ENV=pro"
}
However, import.meta.env.MODE returns production for the 2 types of build.
I couldn't make it work, maybe there is another way of doing this.
My use case was not exactly the same, but similar, and you should be able to generate as many different environments as you want with this technique.
I was able to do this by writing a custom snowpack plugin to use .env files with the dotenv npm package, and two separate snowpack.config.js files; one for dev and one for prod. Here's how...
Add dotenv to your project: npm i -D dotenv
Create this new file. This will be our custom snowpack plugin
// env-loader.js
const dotenv = require('dotenv');
module.exports = function plugin(snowpackConfig, { path }) {
dotenv.config({ path: path });
return { name: 'Custom plugin from StackOverflow' };
};
Create your .env files; .env.develop and .env.production
Create your snowpack.config files; snowpack-develop.config.js and snowpack-production.config.js
Add your custom plugin to both snowpack.config files. Be sure to give it the correct path to your custom plugin, and the correct path to your .env files.
// snowpack-develop.config.js
const path = require('path');
module.exports = {
plugins: [
['./path/to/env-loader', { path: path.resolve(process.cwd(), '.develop.env') }],
],
};
Finally, add your npm scripts. Point your dev scripts to the snowpack-develop file, and prod to the snowpack-production file.
"scripts": {
"develop": "snowpack dev --config ./snowpack-develop.config.js",
"build:dev": "snowpack build --config ./snowpack-develop.config.js",
"build:prod": "snowpack build --config ./snowpack-production.config.js"
},
In snowpack, environment variables must include the prefix SNOWPACK_PUBLIC_, and to use a variable in your code you would access it like this: import.meta.env.SNOWPACK_PUBLIC_MY_VARIABLE. It runs a find-and-replace at build time.
Snowpack Config Docs
Snowpack Plugin Docs
Snowpack Env Docs

Run npm script with nodemon command

I am testing GraphQL Server from Apollo and what to integrate nodemon to it. Here's sample file structure :
build/
src/
server.js
Here my npm scripts looks like this
"scripts": {
"start": "babel --presets es2015,stage-2 server.js -d build/ && node build/server.js",
"dev": "nodemon server.js" // Sample code here
}
What npm run start would do is to convert ES6 code into build/server.js using babel and execute it. This would start the server correctly.
What I want is to watch for changes in server.js or in src/ and restart the server if changes occur. Here I want to execute npm run start command if any changes occur. What is the correct 'nodemon' command to my need. Its better if I could use npm run dev like command to start development using nodemon.
you can use gulpjs to watch any changes in specific folders and then command it to do something.
With your sample, you want to convert the code to es6 too. so it also need gulp-bable and . you can include babel-preset-stage-2 if you want. So you can simply put the code below in gulpfile.js
gulp.task('build-es2015', () => {
return gulp.src('server.js')
.pipe(babel({
presets: ['es2015']
}))
.pipe(gulp.dest('build'));
});
gulp.task('watch', () => {
gulp.watch(['./app/*.js'], ['build-es2015'])
})
Basically, the task 'watch' will keep watching the specific files. When they are saved then it will to execute the task 'build-es2015' to convert to es6.
And then nodemon, it needs gulp-nodemon then you can do on gulpfile.js
gulp.task('server', () => {
nodemon({
script: 'build/server.js',
ext: 'js',
ignore: [
'server.js',
'node_modules/**',
'test/**',
'build/**'
]
})
.on('restart', () => { console.log(`Server restarted!`) })
})
The above will keep watching build/server.js'. whenever it is changed, nodemon will automatically restart the server.
And the last piece for gulpfile.js
gulp.task('dev', ['server', 'watch'])
Include the tasks that need to be executed for gulp command.
$ gulp dev
or with npm command
"scripts": {
"start": "gulp dev"
}
so you can npm run start as well.
And dont forget to require all packages in gulpfile.js
const gulp = require('gulp')
const babel = require('gulp-babel')
const nodemon = require('gulp-nodemon')

How to set environment variables from within package.json?

How to set some environment variables from within package.json to be used with npm start like commands?
Here's what I currently have in my package.json:
{
...
"scripts": {
"help": "tagove help",
"start": "tagove start"
}
...
}
I want to set environment variables (like NODE_ENV) in the start script while still being able to start the app with just one command, npm start.
Set the environment variable in the script command:
...
"scripts": {
"start": "node app.js",
"test": "NODE_ENV=test mocha --reporter spec"
},
...
Then use process.env.NODE_ENV in your app.
Note: This is for Mac & Linux only. For Windows refer to the comments.
Just use NPM package cross-env. Super easy. Works on Windows, Linux, and all environments. Notice that you don't use && to move to the next task. You just set the env and then start the next task. Credit to #mikekidder for the suggestion in one of the comments here.
From documentation:
{
"scripts": {
"build": "cross-env NODE_ENV=production OTHERFLAG=myValue webpack --config build/webpack.config.js"
}
}
Notice that if you want to set multiple global vars, you just state them in succession, followed by your command to be executed.
Ultimately, the command that is executed (using spawn) is:
webpack --config build/webpack.config.js
The NODE_ENV environment variable will be set by cross-env
I just wanted to add my two cents here for future Node-explorers. On my Ubuntu 14.04 the NODE_ENV=test didn't work, I had to use export NODE_ENV=test after which NODE_ENV=test started working too, weird.
On Windows as have been said you have to use set NODE_ENV=test but for a cross-platform solution the cross-env library didn't seem to do the trick and do you really need a library to do this:
export NODE_ENV=test || set NODE_ENV=test&& yadda yadda
The vertical bars are needed as otherwise Windows would crash on the unrecognized export NODE_ENV command. I don't know about the trailing space, but just to be sure I removed them too.
Because I often find myself working with multiple environment variables, I find it useful to keep them in a separate .env file (make sure to ignore this from your source control). Then (in Linux) prepend export $(cat .env | xargs) && in your script command before starting your app.
Example .env file:
VAR_A=Hello World
VAR_B=format the .env file like this with new vars separated by a line break
Example index.js:
console.log('Test', process.env.VAR_A, process.env.VAR_B);
Example package.json:
{
...
"scripts": {
"start": "node index.js",
"env-linux": "export $(cat .env | xargs) && env",
"start-linux": "export $(cat .env | xargs) && npm start",
"env-windows": "(for /F \"tokens=*\" %i in (.env) do set %i)",
"start-windows": "(for /F \"tokens=*\" %i in (.env) do set %i) && npm start",
}
...
}
Unfortunately I can't seem to set the environment variables by calling a script from a script -- like "start-windows": "npm run env-windows && npm start" -- so there is some redundancy in the scripts.
For a test you can see the env variables by running npm run env-linux or npm run env-windows, and test that they make it into your app by running npm run start-linux or npm run start-windows.
Try this on Windows by replacing YOURENV:
{
...
"scripts": {
"help": "set NODE_ENV=YOURENV && tagove help",
"start": "set NODE_ENV=YOURENV && tagove start"
}
...
}
#luke's answer was almost the one I needed! Thanks.
As the selected answer is very straightforward (and correct), but old, I would like to offer an alternative for importing variables from a .env separate file when running your scripts and fixing some limitations to Luke's answer.
Try this:
::: .env file :::
# This way, you CAN use comments in your .env files
NODE_PATH="src/"
# You can also have extra/empty lines in it
SASS_PATH="node_modules:src/styles"
Then, in your package json, you will create a script that will set the variables and run it before the scripts you need them:
::: package.json :::
scripts: {
"set-env": "export $(cat .env | grep \"^[^#;]\" |xargs)",
"storybook": "npm run set-env && start-storybook -s public"
}
Some observations:
The regular expression in the grep'ed cat command will clear the comments and empty lines.
The && don't need to be "glued" to npm run set-env, as it would be required if you were setting the variables in the same command.
If you are using yarn, you may see a warning, you can either change it to yarn set-env or use npm run set-env --scripts-prepend-node-path && instead.
Different environments
Another advantage when using it is that you can have different environment variables.
scripts: {
"set-env:production": "export $(cat .production.env | grep \"^[^#;]\" |xargs)",
"set-env:development": "export $(cat .env | grep \"^[^#;]\" |xargs)",
}
Please, remember not to add .env files to your git repository when you have keys, passwords or sensitive/personal data in them!
UPDATE: This solution may break in npm v7 due to npm RFC 21
CAVEAT: no idea if this works with yarn
npm (and yarn) passes a lot of data from package.json into scripts as environment variables. Use npm run env to see them all. This is documented in https://docs.npmjs.com/misc/scripts#environment and is not only for "lifecycle" scripts like prepublish but also any script executed by npm run.
You can access these inside code (e.g. process.env.npm_package_config_port in JS) but they're already available to the shell running the scripts so you can also access them as $npm_... expansions in the "scripts" (unix syntax, might not work on windows?).
The "config" section seems intended for this use:
"name": "myproject",
...
"config": {
"port": "8010"
},
"scripts": {
"start": "node server.js $npm_package_config_port",
"test": "wait-on http://localhost:$npm_package_config_port/ && node test.js http://localhost:$npm_package_config_port/"
}
An important quality of these "config" fields is that users can override them without modifying package.json!
$ npm run start
> myproject#0.0.0 start /home/cben/mydir
> node server.js $npm_package_config_port
Serving on localhost:8010
$ npm config set myproject:port 8020
$ git diff package.json # no change!
$ cat ~/.npmrc
myproject:port=8020
$ npm run start
> myproject#0.0.0 start /home/cben/mydir
> node server.js $npm_package_config_port
Serving on localhost:8020
See npm config and yarn config docs.
It appears that yarn reads ~/.npmrc so npm config set affects both, but yarn config set writes to ~/.yarnrc, so only yarn will see it :-(
For a larger set of environment variables or when you want to reuse them you can use env-cmd.
As a plus, the .env file would also work with direnv.
./.env file:
# This is a comment
ENV1=THANKS
ENV2=FOR ALL
ENV3=THE FISH
./package.json:
{
"scripts": {
"test": "env-cmd mocha -R spec"
}
}
This will work in Windows console:
"scripts": {
"setAndStart": "set TMP=test&& node index.js",
"otherScriptCmd": "echo %TMP%"
}
npm run aaa
output:
test
See this answer for details.
suddenly i found that actionhero is using following code, that solved my problem by just passing --NODE_ENV=production in start script command option.
if(argv['NODE_ENV'] != null){
api.env = argv['NODE_ENV'];
} else if(process.env.NODE_ENV != null){
api.env = process.env.NODE_ENV;
}
i would really appreciate to accept answer of someone else who know more better way to set environment variables in package.json or init script or something like, where app bootstrapped by someone else.
use git bash in windows. Git Bash processes commands differently than cmd.
Most Windows command prompts will choke when you set environment variables with NODE_ENV=production like that. (The exception is Bash on Windows, which uses native Bash.) Similarly, there's a difference in how windows and POSIX commands utilize environment variables. With POSIX, you use: $ENV_VAR and on windows you use %ENV_VAR%. - cross-env doc
{
...
"scripts": {
"help": "tagove help",
"start": "env NODE_ENV=production tagove start"
}
...
}
use dotenv package to declare the env variables
For single environment variable
"scripts": {
"start": "set NODE_ENV=production&& node server.js"
}
For multiple environment variables
"scripts": {
"start": "set NODE_ENV=production&& set PORT=8000&& node server.js"
}
When the NODE_ENV environment variable is set to 'production' all devDependencies in your package.json file will be completely ignored when running npm install. You can also enforce this with a --production flag:
npm install --production
For setting NODE_ENV you can use any of these methods
method 1: set NODE_ENV for all node apps
Windows :
set NODE_ENV=production
Linux, macOS or other unix based system :
export NODE_ENV=production
This sets NODE_ENV for current bash session thus any apps started after this statement will have NODE_ENV set to production.
method 2: set NODE_ENV for current app
NODE_ENV=production node app.js
This will set NODE_ENV for the current app only. This helps when we want to test our apps on different environments.
method 3: create .env file and use it
This uses the idea explained here. Refer this post for more detailed explanation.
Basically, you create a .env file and run some bash scripts to set them on the environment.
To avoid writing a bash script, the env-cmd package can be used to load the environment variables defined in the .env file.
env-cmd .env node app.js
method 4: Use cross-env package
This package allows environment variables to be set in one way for every platform.
After installing it with npm, you can just add it to your deployment script in package.json as follows:
"build:deploy": "cross-env NODE_ENV=production webpack"
{
...
"scripts": {
"start": "ENV NODE_ENV=production someapp --options"
}
...
}
Most elegant and portable solution:
package.json:
"scripts": {
"serve": "export NODE_PRESERVE_SYMLINKS_MAIN=1 && vue-cli-service serve"
},
Under windows create export.cmd and put it somewhere to your %PATH%:
#echo off
set %*
If you:
Are currently using Windows;
Have git bash installed;
Don't want to use set ENV in your package.json which makes it only runnable for Windows dev machines;
Then you can set the script shell of node from cmd to git bash and write linux-style env setting statements in package.json for it to work on both Windows/Linux/Mac.
$ npm config set script-shell "C:\\Program Files\\git\\bin\\bash.exe"
Although not directly answering the question I´d like to share an idea on top of the other answers. From what I got each of these would offer some level of complexity to achieve cross platform independency.
On my scenario all I wanted, originally, to set a variable to control whether or not to secure the server with JWT authentication (for development purposes)
After reading the answers I decided simply to create 2 different files, with authentication turned on and off respectively.
"scripts": {
"dev": "nodemon --debug index_auth.js",
"devna": "nodemon --debug index_no_auth.js",
}
The files are simply wrappers that call the original index.js file (which I renamed to appbootstrapper.js):
//index_no_auth.js authentication turned off
const bootstrapper = require('./appbootstrapper');
bootstrapper(false);
//index_auth.js authentication turned on
const bootstrapper = require('./appbootstrapper');
bootstrapper(true);
class AppBootStrapper {
init(useauth) {
//real initialization
}
}
Perhaps this can help someone else
Running a node.js script from package.json with multiple environment variables:
package.json file:
"scripts": {
"do-nothing": "set NODE_ENV=prod4 && set LOCAL_RUN=true && node ./x.js",
},
x.js file can be as:
let env = process.env.NODE_ENV;
let isLocal = process.env.LOCAL_RUN;
console.log("ENV" , env);
console.log("isLocal", isLocal);
You should not set ENV variables in package.json. actionhero uses NODE_ENV to allow you to change configuration options which are loaded from the files in ./config. Check out the redis config file, and see how NODE_ENV is uses to change database options in NODE_ENV=test
If you want to use other ENV variables to set things (perhaps the HTTP port), you still don't need to change anything in package.json. For example, if you set PORT=1234 in ENV and want to use that as the HTTP port in NODE_ENV=production, just reference that in the relevant config file, IE:
# in config/servers/web.js
exports.production = {
servers: {
web: function(api){
return {
port: process.env.PORT
}
}
}
}
In addition to use of cross-env as documented above, for setting a few environment variables within a package.json 'run script', if your script involves running NodeJS, then you can set Node to pre-require dotenv/config:
{
scripts: {
"eg:js": "node -r dotenv/config your-script.js",
"eg:ts": "ts-node -r dotenv/config your-script.ts",
"test": "ts-node -r dotenv/config -C 'console.log(process.env.PATH)'",
}
}
This will cause your node interpreter to require dotenv/config, which will itself read the .env file in the present working directory from which node was called.
The .env format is lax or liberal:
# Comments are permitted
FOO=123
BAR=${FOO}
BAZ=Basingstoke Round About
#Blank lines are no problem
Note : In order to set multiple environment variable, script should goes like this
"scripts": {
"start": "set NODE_ENV=production&& set MONGO_USER=your_DB_USER_NAME&& set MONGO_PASSWORD=DB_PASSWORD&& set MONGO_DEFAULT_DATABASE=DB_NAME&& node app.js",
},

How to deploy node that uses Gulp to heroku

I'm using gulp and also gulp plugins like gulp-minify-css, gulp-uglify etc (that listed as npm dependencies for my application).
Also I don't commit npm_modules folder and public folder, where all generated files are. And I can't figure out how to build my app (I have gulp build command) after deploy and setup my server (it's already looking for public folder).
It seems me a bad idea to commit before upload. Maybe there are some gentle decisions... Any thoughts?
Forked from: How to deploy node app that uses grunt to heroku
I was able to get this to work by adding this into my "package.json" file:
"scripts": {
"start": "node app",
"postinstall": "gulp default"
}
The postinstall script is run after the build pack. Check this for more information. The only annoying thing is that all of your dependencies have to live under "dependencies" instead of having separate "devDependencies"
I didn't need to do anything else with buildpacks or configuration. This seems like the simplest way to do it.
I wrote about the process I used here
You can do it!
There were a few key measures that helped me along the way:
heroku config:set NODE_ENV=production - to set your environment to 'production'
heroku config:set BUILDPACK_URL=https://github.com/krry/heroku-buildpack-nodejs-gulp-bower - to enable a customised Heroku buildpack. I incorporated elements of a few to make one that worked for me.
A gulp task entitled heroku:production that performs the build tasks that need to happen on the heroku server when NODE_ENV===production. Here's mine:
var gulp = require('gulp')
var runSeq = require('run-sequence')
gulp.task('heroku:production', function(){
runSeq('clean', 'build', 'minify')
})
clean, build, and minify are, of course separate gulp tasks that do the magic gulpage
If your application lives in /app.js, either:
(A) make a Procfile in the project root that contains only: web: node app.js, or
(B) add a start script to your package.json:
"name": "gulp-node-app-name",
"version": "10.0.4",
"scripts": {
"start": "node app.js"
},
And like #Zero21xxx says, put your gulp modules in your normal dependencies list in package.json, not in the devDependencies, which get overlooked by the buildpack, which runs npm install --production
The easiest way I found was:
Setup gulp on package.json scripts area:
"scripts": {
"build": "gulp",
"start": "node app.js"
}
Heroku will run build before starting the app.
Include gulp on dependencies instead of devDevependencies, otherwise Heroku won't be able to find it.
There is more relevant info about it on Heroku Dev Center: Best Practices for Node.js Development
How to deploy to Heroku (or Azure) with git-push
// gulpfile.js
var gulp = require('gulp');
var del = require('del');
var push = require('git-push');
var argv = require('minimist')(process.argv.slice(2));
gulp.task('clean', del.bind(null, ['build/*', '!build/.git'], {dot: true}));
gulp.task('build', ['clean'], function() {
// TODO: Build website from source files into the `./build` folder
});
gulp.task('deploy', function(cb) {
var remote = argv.production ?
{name: 'production', url: 'https://github.com/<org>/site.com', branch: 'gh-pages'},
{name: 'test', url: 'https://github.com/<org>/test.site.com', branch: 'gh-pages'};
push('./build', remote, cb);
});
Then
$ gulp build --release
$ gulp deploy --production
See also
https://github.com/koistya/git-push
https://github.com/kriasoft/react-starter-kit (tools/deploy.js)
There's a specific startup script that Heroku provides;
"scripts": {
"start": "nodemon app.js",
"heroku-postbuild": "gulp"
}
note that in your gulpfile.js (gulpfile.babel.js if you es6-ifed your gulp build process), you should have a task name default which will be automatically run after the dependencies are installed via Heroku.
https://devcenter.heroku.com/articles/nodejs-support#heroku-specific-build-steps
Heroku finds that there is a gulpfile in your project and expects there to be a heroku:production task (in the gulpfile). So all you need to do is register a task that matches that name:
gulp.task("heroku:production", function(){
console.log('hello'); // the task does not need to do anything.
});
This is enough for heroku to not reject your app.
I had to take a slightly different to get this working because I'm using browsersync:
package.json
"scripts": {
"start": "gulp serve"
}
gulp.js
gulp.task('serve', function() {
browserSync({
server: {
baseDir: './'
},
port: process.env.PORT || 5000
});
gulp.watch(['*.html', 'css/*.css', 'js/*.js', 'views/*.html', 'template/*.html', './*.html'], {cwd: 'app'}, reload);
});
Setting the port to be environment port is important to prevent error when deploying in Heroku. I did not need to set a postinstall script.
It's possible to piggyback any command you want over the top of npm install. Much like the linked question in your post, you can add an install directive in scripts within package.json that will run after all the node deps have been installed that does the build.
Your main issue will be sorting out the correct relative paths for everything.
{
...
scripts:{
install: "YOUR GULP BUILD COMMAND"
}
...
}

Resources