Set NODE_ENV from Grunt task

2.2k views Asked by At

I would like to set the NODE_ENV variable at the beginning of a Grunt task, to development or production, but it looks it's not as simple as I thought.

The reason, why I would like this is that I use grunt-webpack, which expects NODE_ENV to be set correctly to "development" or "production". But I also would like to initialize my tasks exclusively from grunt, if possible.

I created the following test Gruntfile, using the grunt-shell and cross-env modules:

function log(err, stdout, stderr, cb, e) {
    if (err) {
        cb(err);
        return;
    }

    console.log(process.env.NODE_ENV);
    console.log(stdout);
    cb();
}

module.exports = function(grunt) {

    grunt.initConfig({
        shell: {
            dev: {
                command : 'cross-env NODE_ENV="development"',
                options: {
                    callback: log
                }
            },
            dist: {
                command : 'cross-env NODE_ENV="production"',
                options: {
                    callback: log
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-shell');

};

Line 6 of log() should echo the actual value of process.env.NODE_ENV, but it constantly says undefined, even if I check it manually in the node console.

If I set it manually from the terminal, like set NODE_ENV=production (set is for Windows), everywhere echoes the value production, as I would like it to.

2

There are 2 answers

3
LifeQuery On BEST ANSWER

Your test won't work because grunt-shell runs a child_process and your callback runs after it ends and under the main process.
Same thing would happen with cross-env.

If you want to pass an environment variable to grunt-shell, you should use the options configuration according to the documentation.
For example:

grunt.initConfig({
    shell: {
        dev: {
            command : 'echo %NODE_ENV%', //windows syntax
            options: {
                execOptions: {
                    env: {
                        'NODE_ENV': 'dev'
                    }
                },
                callback: log
            }
        }
    }
});

This will still print undefined for process.env.NODE_ENV, but the value of NODE_ENV will be available in the stdout because of the echo.

On a side note, it sounds like you're trying to run a process (grunt-shell), which runs a process (cross-env), which runs a process (webpack or grunt-webpack).
Why not just use the cross-env example usage? It looks pretty close to what you need.
Or you can just define the variable in the task config itself and lose all of these wrappers.

0
bencergazda On

LifeQuery's answer helped me a lot to find out what the problem actually was. I first realized that webpack.DefinePlugin() actually doesn't change anything on process.env.NODE_ENV (and it would be too late anyway, as it transforms code parsed by webpack after all loaders).

After this I created a solution, which does what I want. This is how my customized Gruntfile.js begins:

'use strict';

const path = require('path');
const webpack = require('webpack');

module.exports = function (grunt) {

    // Setting the node environment based on the tasks's name or target
    let set_NODE_ENV = function () {
        const devTasks = ['webpack-dev-server', 'dev', 'hmr', 'watch'],
            devTargets = [':dev'],
            task = grunt.cli.tasks[0], // The name of the (first) task we initialized grunt with ('webpack-dev-server' if started 'grunt webpack-dev-server)
            target = ':'+grunt.option('target'),
            devEnv = (devTasks.indexOf(task) > -1 || devTargets.indexOf(target) > -1);

        process.env.NODE_ENV = devEnv ? 'development' : 'production';
    }();

    const webpackConfig = require('../assets/webpack.config');

    grunt.initConfig({
        // ...usual Gruntfile content
    });

};

I created a whitelist of grunt task names and targets when I set the process.env.NODE_ENV. As it is placed before the grunt.initConfig(), the configuration object can use process.env.NODE_ENV with the desired state.

It will set NODE_ENV to "development" if starting definitely webpack-dev-server, dev, hmr or watch tasks, or any other tasks with the :dev target.