Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Webpack 2: -p option does not set NODE_ENV to "production" in webpack config #2537

Closed
aight8 opened this issue May 23, 2016 · 49 comments
Closed
Labels

Comments

@aight8
Copy link

aight8 commented May 23, 2016

Is that an upcoming feature or why isn't the variable set when I try to access it in the webpack config with process.env.NODE_ENV ? I use webpack ^2.1

Furthermore I recognized that when using -p option the UglifyJsPlugin is applied? Don't see that in the documentation anywhere.

@sokra
Copy link
Member

sokra commented May 23, 2016

NODE_ENV is set in the compiled code, not in the webpack.config.js file. You should not use enviroment variables in your configuration. Pass options via --env.option abc and export a function from the webpack.config.js.

@fab1an
Copy link

fab1an commented May 29, 2016

Why should you not use env-variables in the config?

I have a webpack config, which changes according to NODE_ENV. It's awesome because it's in one file and

  • for developing I just run webpack-dev-server
  • for production I run NODE_ENV='production' webpack -p
const webpack = require('webpack'),
    path = require('path'),
    fs = require('fs')

const SRC = path.resolve(__dirname, "src"),
    NODE_MODULES = path.resolve(__dirname, "node_modules")

/* babel */
const babelSettings = JSON.parse(fs.readFileSync(".babelrc"))

const config = {
    entry: [
        './src/index.js'
    ],
    module: {
        loaders: [
            {
                test: /\.jsx?$/,
                include: SRC,
                loader: "babel-loader",
                query: babelSettings
            },
            {
                test: [
                    /\.png$/,
                    /\.jpg$/
                ],
                loader: "url-loader"
            },
            {
                test: /\.css$/,
                include: [
                    NODE_MODULES,
                    SRC
                ],
                loader: 'style-loader!css-loader!postcss-loader',
            }
        ]
    },
    resolve: {
        root: SRC,
        extensions: [
            '',
            '.js',
            '.jsx'
        ]
    },
    output: {
        path: path.resolve(__dirname, 'public', 'assets'),
        publicPath: '/assets',
        filename: 'bundle.js'
    },
    postcss: [
        require('autoprefixer')
    ],
    plugins: [
        new webpack.DefinePlugin({
            'process.env': {
                'NODE_ENV': JSON.stringify(process.env.NODE_ENV)
            }
        }),
    ]
};

if (process.env.NODE_ENV === 'production') {
    config.plugins.push(
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                screw_ie8: true
            }
        })
    )
    babelSettings.plugins.push("transform-react-inline-elements");
    babelSettings.plugins.push("transform-react-constant-elements");

} else {
    config.devtool = "#cheap-module-source-map"
    config.devServer = {
        contentBase: './public',
        hot: true,
        inline: true,
        host: "0.0.0.0",
        port: 2708
    }
    config.plugins.push(
        new webpack.HotModuleReplacementPlugin()
    );
}

module.exports = config

I noticed too that for some reason the output of "webpack -p" is different than just running webpack with the Uglify plugin applied.

@Goblinlordx
Copy link

Goblinlordx commented Oct 2, 2016

Personally, I am not quite getting what you mean by -env.option abcd but I tried this and it doesn't seem to help with anything. I do believe that doing this automatically based on the -p flag would be reasonable. For the loads of other people that have also run into this behavior wondering "wtf!?" below is dead simple example of what I am currently using to deal with the issue:

_package.json_

{
  "name": "webpack_test",
  "version": "1.0.0",
  "description": "",
  "main": "build/bundle.js",
  "scripts": {
    "build": "webpack",
    "build:prod": "webpack -p",
  },
  "author": "Benjamin Baldivia",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^1.13.2"
  }
}

_webpack.config.js_

const webpack = require('webpack');
const prod = process.argv.indexOf('-p') !== -1;

const config = {
  entry: './src/main.js',
  output: {
    path: 'build',
    filename: 'bundle.js'
  }
};


/*
This code was seperated from the config for multiple reasons.
Other conditional things can be added very simply.
Also, the check for config.plugins is so it is not dependent on the structure above.
*/

config.plugins = config.plugins||[];
if (prod) {
  config.plugins.push(new webpack.DefinePlugin({
      'process.env': {
          'NODE_ENV': `"production"`
      }
  }));
} else {
  config.plugins.push(new webpack.DefinePlugin({
      'process.env': {
          'NODE_ENV': `""`
      }
  }));
}

module.exports = config;

_src/main.js_

if (process.env.NODE_ENV === 'production') {
  module.exports = require('./prod.js')
} else {
  module.exports = require('./dev.js')
}

_src/dev.js_

console.log("I am in dev");

src/prod.js

console.log("I am in production");

The key here is really only the webpack.config.js I only included the rest for it to be a complete example of it's usage. Below we can see the results:

npm run build

# OUTPUT

#> webpack_test@1.0.0 build C:\Users\Goblin\dev\webpack_test
#> webpack
#
#Hash: 133cddf132a8bd0ff743
#Version: webpack 1.13.2
#Time: 42ms
#    Asset     Size  Chunks             Chunk Names
#bundle.js  1.61 kB       0  [emitted]  main
#   [0] ./src/main.js 140 bytes {0} [built]
#   [1] ./src/dev.js 29 bytes {0} [built]
npm run build:prod

#OUTPUT

#> webpack_test@1.0.0 build:prod C:\Users\Goblin\dev\webpack_test
#> webpack -p
#Hash: e5112caeb4609ddc76a9
#Version: webpack 1.13.2
#Time: 79ms
#    Asset       Size  Chunks             Chunk Names
#bundle.js  283 bytes       0  [emitted]  main
#   [0] ./src/main.js 140 bytes {0} [built]
#   [1] ./src/prod.js 38 bytes {0} [built]
#
#WARNING in bundle.js from UglifyJs
#Condition always true [./src/main.js:1,0]
#Dropping unreachable code [./src/main.js:4,0]

This is much mode in-line with what I believe users expect when using the -p flag. It would be REALLY nice if this was the default behavior when using the flag. I would be more than willing to submit a PR if this is deemed acceptable default behavior. I know I ran into this issue and ran around in circles working around it and while I was I know I looked up quite a few "solutions" (some blogged about) meaning there are quite a few people with this specific problem.

@marcofugaro
Copy link

marcofugaro commented Oct 3, 2016

Agree, it's really confusing that is says this in the changelog:

-p sets NODE_ENV = “production”

and then process.env.NODE_ENV is undefined in the webpack config.

I usually call webpack setting NODE_ENV from the command line (NODE_ENV=production webpack -p), and then add or remove some plugins based on that.

EDIT: here is an example of what I'm doing, webpack-dashboard has an issue and so it needs to be included only in development

@klinki
Copy link

klinki commented Oct 21, 2016

I agree this is an issue. I spent several hour on it before I found out what is causing my problems :(
There are many articles and repositories which depend on having NODE_ENV set in config file already.

@ghost
Copy link

ghost commented Nov 3, 2016

Agreed, this seems like a departure from previous behaviour? Migration guides could alert to this also.

@mattapet
Copy link

mattapet commented Nov 8, 2016

A great workaround for me was using aliases as described in this StackOverflow post.

@okbel
Copy link

okbel commented Nov 24, 2016

What's the extra optimisation that -p performs?

@antoine-lizee
Copy link

antoine-lizee commented Nov 29, 2016

The key part for me was to understand that environment variables are not available in the webpack build process unless specified. That's why we need the following line from @fab1an's great example :

...
        new webpack.DefinePlugin({
            'process.env': {
                'NODE_ENV': JSON.stringify(process.env.NODE_ENV)
            }
        }),
...

....which can be stated more clearly using the EnvironmentPlugin, which directly passes environment variables to the build:

...
        new webpack.EnvironmentPlugin(['NODE_ENV']),
...

simon04 added a commit to simon04/webpack.js.org that referenced this issue Jan 3, 2017
`-p` does not set `NODE_ENV` in the build script,
see webpack/webpack#2537
simon04 added a commit to simon04/webpack.js.org that referenced this issue Jan 3, 2017
`-p` does not set `NODE_ENV` in the build script,
see webpack/webpack#2537
@rohmanhm
Copy link

is webpack.DefinePlugin still work in webpack@2.2.0-rc.7?
I used it but I think doesn't work.

@marcofugaro
Copy link

could this simply be implemented just by adding --env=production to the arguments baked into webpack -p?

Here are the relevant docs

@jakewies
Copy link

Currently I'm handling this issue with webpack -p --env production.

And then in webpack.config.js:

module.exports = function(env) {
    // return env-specific configuration
}

I do agree the -p and --env production is a bit repetitive.

@phun-ky
Copy link

phun-ky commented Feb 15, 2017

It seems that most people here thinks that webpack -p is mandatory to do a production build, but am I the only one thinking it should not be this way?

Not all are running webpack for production via the CLI, but programmatically via node. Shouldnt webpack -p be the same as running webpack with NODE_ENV=production?

@jacobmischka
Copy link

-p automatically adds

        new webpack.DefinePlugin({
            'process.env': {
                'NODE_ENV': JSON.stringify(process.env.NODE_ENV)
            }
        }),
       new webpack.optimize.UglifyJsPlugin()

as plugins. It is not mandatory, it just enables those plugins for you for convenience.

Because of the DefinePlugin, process.env.NODE_ENV will be replaced in your source files, just like if you had used DefinePlugin in your config by hand. It will not set the node environment variable process.env.NODE_ENV in your webpack config, as the webpack config is run directly by node.

Basically, what you want to do is NODE_ENV=production webpack -p all the time. https://github.com/jacobmischka/coyote-grill/blob/master/package.json#L10

@jacobmischka
Copy link

I don't think it's even possible to have a regular command line flag like -p directly influence the environment variables that node sees.

@alaaalii
Copy link

alaaalii commented Feb 25, 2017

I'm pretty new to web development and man this is confusing.

Shouldn't webpack's default behavior be to simply pass along the NODE_ENV environment variable to the build? using its own plugin this way: webpack.EnvironmentPlugin(['NODE_ENV'])?
I think webpack's CLI options shortcuts should never set the environment variable to a specific value, the NODE_ENV variable should be explicitly set and visibly identifiable.

Maybe I'm missing something but what scenarios require you to not pass along the NODE_ENV variable to source files when using webpack?

If that becomes the default behavior:

  • If I want to invoke webpack and tell my source files 'hey this is a production build', I would invoke it with NODE_ENV=production webpack.
  • If I want to invoke webpack and tell my source files 'hey this is a production build' and utilize webpack's optimization and minification for a 'production bundle', I would do NODE_ENV=production webpack --optimize-minimize (which is the option that -p does).
  • If I want to have different webpack config items based on that environment variable, I would simply access process.env.NODE_ENV in the webpack config.
  • (bonus) Then if you want to, instead of using NODE_ENV=production with --optimize-minimize every time, you can just automatically add the UglifyPlugin in the webpack config if process.env.NODE_ENV==production.

Anyways, this is what I'm currently doing to completely avoid the confusing -p option, basically same as fab1an's:

// package.json
{
  "scripts": {
    "build:dev-server": "webpack-dev-server --inline --progress --colors",
    "build:dev": "webpack --progress --colors",
    "build:dist": "NODE_ENV=production webpack --optimize-minimize --progress --colors",
}
// webpack.config.js
var webpack = require('webpack');
var path = require('path');
var CopyWebpackPlugin = require('copy-webpack-plugin');

var config = {
    // ...
    plugins: [
        new webpack.EnvironmentPlugin(['NODE_ENV']),
    ],
};

if (process.env.NODE_ENV == 'production') {
    config.plugins.push(
        new CopyWebpackPlugin([{
            from: 'src/templates/index_prod.html',
            to: 'templates/index.html'
        }])
    );
} else {
    // ...
    config.devtool = 'inline-source-map';
    config.plugins.push(
        new CopyWebpackPlugin([{
            from: 'src/templates/index_dev.html',
            to: 'templates/index.html'
        }]),
    );
}

module.exports = config;

@linaspasv
Copy link

linaspasv commented Mar 30, 2017

In order, to determine if I am in development or production mode I use the following code in my webpack.config.js:

// detect if webpack bundle is being processed in a production or development env
let isDevelopmentMode = !(require('yargs').argv.p || false);

then I can use it like:

    plugins: [
        new webpack.DefinePlugin({
            __DEBUG__: JSON.stringify(isDevelopmentMode)
        }),
    ]

I hope it helps for someone looking for an alternative.

@selfup
Copy link

selfup commented Apr 16, 2017

I do the following:

"build": "webpack --env production",

and then in the webpack config:

const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const BabiliPlugin = require('babili-webpack-plugin');

const plugins = [
  new ExtractTextPlugin({
    filename: './bundle.css',
    allChunks: true,
  }),
];

module.exports = function webpackStuff(env) {
  if (env === 'production') plugins.push(new BabiliPlugin());

  return { /* the rest of your config goes here */ };
};

Now I just do npm run build and everything is minified! 🎉

@dizel3d
Copy link

dizel3d commented Apr 26, 2017

The second argument of your config function contains webpack calling options. This is undocumented feature, but might be useful, anyway.

module.exports = (env = {}, args = {}) => {
    console.log(args);
    // E.g. args.bail and args.p are true for webpack -p --bail
    ...
};

@HoraceShmorace
Copy link

Wanting -p to set NODE_ENV to "production" is a bizarre request. -p indicates a production build, while NODE_ENV === "production" indicates a [runtime] production environment. I've never understood why devs tie these things together. I might want to test my production build in a local dev environment, in which case I now have to spoof NODE_ENV at build time. Unless you're actually using Webpack in a runtime production environment, I'm not sure why you'd want your config to rely on NODE_ENV at all. It seems like it would be mixing two entirely unrelated concerns.

@jacobmischka
Copy link

I think the usual reason is that people specify different configs based on the NODE_ENV env var. So something like:

filename: process.env.NODE_ENV === 'production'
			? '[name]-[chunkhash].js'
			: '[name].js',

I do that as well, but I agree that it's a separate thing and it should be kept separate mentally. I just think people get confused when it says that -p sets NODE_ENV for you but don't realize that refers to in the source files, not in the config files.

@pblackwood
Copy link

I finally settled on the EnvironmentConfig's defaulting behavior as in:

    plugins: [
        new webpack.EnvironmentPlugin({
            NODE_ENV: 'development'
        })
    ]

This just sets the default NODE_ENV value if not already defined.

If you also set it on the command line, as in NODE_ENV=production webpack ..., the command line wins. To me, that is intuitive and desirable.

Yes it's confusing to write NODE_ENV=production webpack -p ... as many have pointed out, but when the light bulb finally went on I realized this is really two different concepts.

@jacobmischka
Copy link

jacobmischka commented Sep 1, 2017

As it says, process.env.NODE_ENV is defined in your source files that webpack bundles. That is what the --define flag does. Not in the webpack.config.js itself.

That is a separate thing, which is managed by setting the environment variable ie

$ NODE_ENV=production webpack

@fgaleano
Copy link

fgaleano commented Sep 5, 2017

The solution is simple: rename -p to -o for optimize, which is exactly what this does. That way we can disassociate this command with the concept of production/environment variables. It should never have been called -p anyway.

@axdemelas
Copy link

Why not using:

"scripts": {
  "build": "webpack --env development",
  "build:production": "webpack --env production"
}

And inside webpack.config.js:

module.exports = env => ({
   /* ... */
   plugins: [
     new webpack.DefinePlugin({
       'process.env': {
         NODE_ENV: JSON.stringify(env),
       },
     }),
   ],
   /* ... */
})

@SamanthaAdrichem
Copy link

SamanthaAdrichem commented Dec 4, 2017

Please clarify your documentation then. (Add an additional mark, stating that it does not set it for the config itself)

https://webpack.js.org/guides/production/#cli-alternatives

states that -p sets both flags. But in webpack 3.9.1 it still only sets this variable for packages run by webpack and not the webpack config itself.

Why use it?

See my repo: https://github.com/SamanthaAdrichem/webpack-3.9.1-splitted-config-angular/

i'm using it to load separate development and production configs -p now feels like a useless argument

@gankhe
Copy link

gankhe commented Dec 20, 2017

const webpack = require('webpack');
  const merge = require('webpack-merge');
  const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
  const common = require('./webpack.common.js');

  module.exports = merge(common, {
    devtool: 'cheap-module-source-map',
    plugins: [
      new UglifyJSPlugin({
        sourceMap: true
      }),
     new webpack.DefinePlugin({
       'process.env': {
         'NODE_ENV': JSON.stringify('production')
       }
    })
    ]
  })

Technically, NODE_ENV is a system environment variable that Node.js exposes into running scripts. It is used by convention to determine dev-vs-prod behavior by server tools, build scripts, and client-side libraries. Contrary to expectations, process.env.NODE_ENV is not set to "production" within the build script webpack.config.js, see #2537. Thus, conditionals like process.env.NODE_ENV === 'production' ? '[name].[hash].bundle.js' : '[name].bundle.js' within webpack configurations do not work as expected.

@jefer94
Copy link

jefer94 commented Dec 26, 2017

@sindresorhus
Copy link

I've made a webpack plugin, node-env-webpack-plugin, that works around this by setting process.env.NODE_ENV to production in the Node.js process when you run webpack -p.

@mittalyashu
Copy link

What is the difference between webpack -p and NODE_ENV=production.

@sindresorhus
Copy link

@mittalyashu It's in webpack --help:

-p           shortcut for --optimize-minimize --define
               process.env.NODE_ENV="production"
  --optimize-minimize        Minimize javascript and switches loaders to
                             minimizing

@AlexMcDee
Copy link

AlexMcDee commented Feb 24, 2018

So. After testing this way best for me.

Doc: https://webpack.js.org/guides/environment-variables/

package.json
"scripts": {
"watch": "webpack --watch --env.watch",
"build:dev": "webpack --env.development --progress",
"build:prod": "webpack --env.production --progress"
}

webpack.config.js

module.exports = env => {
   const IS_DEV = !env.production;

   return {
        entry: {
            'app': './app.js',
        },
        output: {
            filename: IS_DEV ? '[name].bundle.js' : '[name].[hash].bundle.js',
        },
    }
};

@EspenLarod
Copy link

After 16 months the hackaton ended. They had finally found a way to set the environment in webpack.

@jefer94
Copy link

jefer94 commented Feb 27, 2018

@EspenLarod hahahahahahaha.

@whosesmile
Copy link

@EspenLarod @jefer94 So, are you ready to update your webpack.config.js according to the latest webpack4 rfc? OMG, it's a big project...

@TheLarkInn
Copy link
Member

This is now fixed in webpack 4. Setting mode: production or development also sets the proper NODE_ENV

@yordis
Copy link

yordis commented Mar 5, 2018

@TheLarkInn I keep getting this


WARNING in configuration
The 'mode' option has not been set. Set 'mode' option to 'development' or 'production' to enable defaults for this environment.

even when I am setting up NODE_ENV

"start": "NODE_ENV=development webpack --config ./webpack/webpack.dev.js --watch",
"build": "NODE_ENV=production webpack --config ./webpack/webpack.prod.js"

@montogeek
Copy link
Member

montogeek commented Mar 5, 2018

@yordis From the warning:

Set 'mode' option to 'development' or 'production'...

It means: webpack --mode=production, webpack doesn't know about your NODE_ENV variable.

Also from @TheLarkInn comment, webpack sets NODE_ENV according to mode, not the other way around

@blinkylights23
Copy link

blinkylights23 commented Mar 6, 2018

I sometimes use NODE_ENV outside the context of bundling. Is there a command-line flag or configuration setting to prevent --mode from changing NODE_ENV?

const fooConfig = {
  test: 'foo',
  local: 'Foo',
  sandbox: 'fooo',
  dev: 'FoOoOo',
  prod: 'FOO'
}

console.log(fooConfig[process.env['NODE_ENV']])
$ NODE_ENV=dev node foo.js 
FoOoOo

$ webpack --mode development --entry ./foo.js -o ./fooBundle.js
Hash: 82c0f8c800e30479245d
Version: webpack 4.1.0
...
$ NODE_ENV=dev node fooBundle.js
undefined

Update: yes there is. 🎉 😄

{
  ...
  optimization: {
    nodeEnv: process.env.NODE_ENV
  }
  ...
}

Update 2: this is still bad, do this:

This should make it so webpack leaves process.env.NODE in your bundled code rather than resolving it to whatever NODE_ENV is at compile-time, which is what you'd want in the dumb example above.

{
  ...
  optimization: {
    nodeEnv: false
  }
  ...
}

@zaclummys
Copy link

Webpack doesn't handle Dev. vs Prod. well. So I just created a package called Environmentally to handle it. 👍

@xgqfrms
Copy link

xgqfrms commented Nov 9, 2018

fullstack412 added a commit to fullstack412/React-redux-node-slydv that referenced this issue Dec 28, 2019
I originally tried to do a positive environment check:
`if (process.env.NODE_ENV === 'development')`
Because Webpack, however, `process.env.NODE_ENV` was coming up undefined.
There's lots of chatter about this on SO and GitHub, but the couple of
solutions I tried didn't work. See, for example,
[Webpack 2: -p option does not set NODE_ENV to "production" in webpack
config #2537](webpack/webpack#2537)
Maybe this `NODE_ENV` issue is only a problem when you're *not* building
for production? If not, I would imagine this issue affects ALL projects
based on Boilerplate, since all of them use `process.env` in server/index.js.

For now, I've made the environment check a negative—
`if (process.env.NODE_ENV !== 'production')`
so if the variable actually _is_ successfully set on production,
react-a11y won't run.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests