How to set React to production mode when using Gulp

19,404

Solution 1

2017 - Edit: anyone trying to set up React in Gulp for a new project: Just use create-react-app


Step I: Add the following to your gulpfile.js somewhere

gulp.task('apply-prod-environment', function() {
    process.env.NODE_ENV = 'production';
});

Step II: Add it to your default task (or whichever task you use to serve/build your app)

// before: 
// gulp.task('default',['browsersync','watch'], function() {});
// after:
   gulp.task('default',['apply-prod-environment', 'browsersync','watch'], function() {});

OPTIONAL: If you want to be ABSOLUTELY CERTAIN that you are in prod mode, you can create the following slightly enhanced task instead of the one in Step I:

gulp.task('apply-prod-environment', function() {
    process.stdout.write("Setting NODE_ENV to 'production'" + "\n");
    process.env.NODE_ENV = 'production';
    if (process.env.NODE_ENV != 'production') {
        throw new Error("Failed to set NODE_ENV to production!!!!");
    } else {
        process.stdout.write("Successfully set NODE_ENV to production" + "\n");
    }
});

Which will throw the following error if NODE_ENV is ever not set to 'production'

[13:55:24] Starting 'apply-prod-environment'...
[13:55:24] 'apply-prod-environment' errored after 77 μs
[13:55:24] Error: Failed to set NODE_ENV to production!!!!

Solution 2

Similar to the other answers, but hopefully gives someone a starting point:

var vendorList = ['react', 'react-dom'];

gulp.task('vendor-dev', function() {
    browserify()
        .require(vendorList)
        .bundle()
        .on('error', handleErrors)
        .pipe(source('vendor.js'))
        .pipe(gulp.dest('./build/dev/js'));
});

gulp.task('vendor-production', function() {
    process.env.NODE_ENV = 'production';
    browserify()
        .require(vendorList)
        .bundle()
        .on('error', handleErrors)
        .pipe(source('vendor.js'))
        .pipe(buffer())
        .pipe(uglify({ mangle: false }))
        .pipe(gulp.dest('./build/production/js'));
});

The main difference is I am explicitly setting the NODE_ENV prior to bundling the vendor libraries. Gulp tasks aren't guaranteed to run in order.

Am I running in production mode?

If you remove the uglify line (and prior buffer) you will notice that both the dev and production builds are near identical in size - and match in line count.

The difference is the production version will be littered with:

"production" !== "production" ? [show dev error] : [no nothing]

Most reputable minify'ers (I believe) will strip out deadend code, such as the above, which will always result in false.

But really how do I tell?

Easiest method to be sure, would be goto the console of your running application and type:

React.createClass.toString();

The output should be:

"function (e){var t=function(e,t,n){this.__reactAutoBindMap&&c(this),"[....and more and more]

If you find the createClass in the react source, you will see:

createClass: function (spec) {
    var Constructor = function (props, context, updater) {
      // This constructor is overridden by mocks. The argument is used
      // by mocks to assert on what gets mounted.

      if ("production" !== 'production') {
        "production" !== 'production' ? warning(this instanceof Constructor, 'Something is calling a React component directly. Use a factory or ' + 'JSX instead. See: react-legacyfactory') : undefined;
      }

      // Wire up auto-binding
      if (this.__reactAutoBindMap) {
        bindAutoBindMethods(this);
      }

Notice how the console output skips straight through to this.__reactAutobind, because you are running in production mode, and using an minify'er, all the !== 'production' warngins and checks have been skipped over.

Solution 3

Unfortunately none of the above answers work, because setting process.env.NODE_ENV has no effect in Browserify. The resulting bundle still has process.env.NODE_ENV references in it and hence

  • Browserify will not require() the React production version modules,
  • the minifier will not be able to remove dead code, and
  • the application will still be running in debug mode.

This is unfortunately not the only place where this approach is offered as the correct answer :-(


The correct approach can be found in e.g.

You need to switch the envify transform to be a global one, e.g.

# note the "-g" instead of the usual "-t"
$ browserify ... -g [ envify --NODE_ENV production ] ....

or in gulpfile.js

browserify(...)
    ...
    .transform('envify', {
        global:   true, // also apply to node_modules
        NODE_ENV: debug ? 'development' : 'production',
    })
    ...
    .bundle()
    ...
    .pipe(gulpif(!debug, babelMinify())) // my example uses gulp-babel-minify
    ...

Solution 4

To set React in production mode you need to set your NODE_ENV variable to production and uglify your JS as an extra step.

You're already taking care of the uglification, for setting your NODE_ENV variable :

  • Set the variable while running the gulp task :

NODE_ENV='production' gulp

  • OR set it from inside your gulpfile by doing something like this :

gulp.task('set-production-env', function() { return process.env.NODE_ENV = 'production'; });

Share:
19,404
vgoklani
Author by

vgoklani

Updated on June 03, 2022

Comments

  • vgoklani
    vgoklani almost 2 years

    I need to run React in production mode, which presumably entails defining the following somewhere in the enviornment:

    process.env.NODE_ENV = 'production';
    

    The issue is that I'm running this behind Tornado (a python web-server), not Node.js. I also use Supervisord to manage the tornado instances, so it's not abundantly clear how to set this in the running environment.

    I do however use Gulp to build my jsx files to javascript.

    Is it possible to somehow set this inside Gulp? And if so, how do I check that React is running in production mode?

    Here is my Gulpfile.js:

    'use strict';
    
    var gulp = require('gulp'),
            babelify = require('babelify'),
            browserify = require('browserify'),
            browserSync = require('browser-sync'),
            source = require('vinyl-source-stream'),
            uglify = require('gulp-uglify'),
            buffer = require('vinyl-buffer');
    
    var vendors = [
        'react',
        'react-bootstrap',
        'jquery',
    ];
    
    gulp.task('vendors', function () {
            var stream = browserify({
                            debug: false,
                            require: vendors
                    });
    
            stream.bundle()
                        .pipe(source('vendors.min.js'))
                        .pipe(buffer())
                        .pipe(uglify())
                        .pipe(gulp.dest('build/js'));
    
            return stream;
    });
    
    gulp.task('app', function () {
            var stream = browserify({
                            entries: ['./app/app.jsx'],
                            transform: [babelify],
                            debug: false,
                            extensions: ['.jsx'],
                            fullPaths: false
                    });
    
            vendors.forEach(function(vendor) {
                    stream.external(vendor);
            });
    
            return stream.bundle()
                                     .pipe(source('build.min.js'))
                                     .pipe(buffer())
                                     .pipe(uglify())
                                     .pipe(gulp.dest('build/js'));
    
    });
    
    gulp.task('watch', [], function () {
        // gulp.watch(['./app/**/*.jsx'], ['app', browserSync.reload]);
        gulp.watch(['./app/**/*.jsx'], ['app']);
    });
    
    gulp.task('browsersync',['vendors','app'], function () {
            browserSync({
                server: {
                    baseDir: './',
                },
                notify: false,
                browser: ["google chrome"]
        });
    });
    
    gulp.task('default',['browsersync','watch'], function() {});