javascriptbuildrequirebrunch

Brunch: separating vendor and app javascript


I have made two bundles of javascript from our project- vendor and app. I do this in the manner suggested by the documentation, as seen in this snippet from my brunch-config.js:

files: {
  javascripts: {
    joinTo: {
      'js/vendor.js': /^(?!source\/)/,
      'js/app.js': /^source\//
    },
    entryPoints: {
      'source/scripts/app.jsx': 'js/app.js'
    }
  }
}

And I end up with a vendor.js and an app.js. But check out the file sizes:files view

Note how app.js is larger than vendor.js! This large size makes watching slower than it needs to be. Upon inspecting the contents of app.js, it seemed to contain lodash, React, and other libraries, which I expected it to get from vendor.js. And vendor.js seems to contain the same libraries, which I do expect.

My question: Why are the libraries present in app.js? Why does app.js not reference them from vendor.js?

It is possible I missing some piece of configuration. Here is my full brunch-config.js for your examination:

module.exports = {

  files: {
    javascripts: {
      joinTo: {
        'js/vendor.js': /^(?!source\/)/,
        'js/app.js': /^source\//
      },
      entryPoints: {
        'source/scripts/app.jsx': 'js/app.js'
      }
    },
    stylesheets: {joinTo: 'css/core.css'},
  },

  paths: {
    watched: ['source']
  },

  modules: {
    autoRequire: {
      'js/app.js': ['source/scripts/app']
    }
  },

  plugins: {
    babel: {presets: ['latest', 'react']},
    assetsmanager: {
      copyTo: {
        'assets': ['source/resources/*']
      }
    },
    static: {
      processors: [
        require('html-brunch-static')({
          processors: [
            require('pug-brunch-static')({
              fileMatch: 'source/views/home.pug',
              fileTransform: (filename) => {
                filename = filename.replace(/\.pug$/, '.html');
                filename = filename.replace('views/', '');
                return filename;
              }
            })
          ]
        })
      ]
    }
  },

  server: {
    run: true,
    port: 9005
  }

};

and in HTML I require these files like this:

<script type="text/javascript" src="js/vendor.js" defer></script>
<script type="text/javascript" src="js/app.js" defer></script>

I tried setting the order object, but to no avail:

files:
  javascripts: {
    joinTo: {
      'js/vendor.js': /^(?!source\/)/,
      'js/app.js': /^source\//
    },
    entryPoints: {
      'source/scripts/app.jsx': 'js/app.js'
    },
    order: {
      before: /^(?!source)/,
      after: /^source\//
    }
  }
}

Here's my package.json:

{
  "version": "0.0.1",
  "devDependencies": {
    "assetsmanager-brunch": "^1.8.1",
    "babel-brunch": "^6.1.1",
    "babel-plugin-add-module-exports": "^0.2.1",
    "babel-plugin-rewire": "^1.0.0-rc-5",
    "babel-plugin-transform-es2015-modules-commonjs": "^6.10.3",
    "babel-plugin-transform-object-rest-spread": "^6.8.0",
    "babel-preset-react": "^6.3.13",
    "babel-register": "^6.11.6",
    "browser-sync-brunch": "^0.0.9",
    "brunch": "^2.10.9",
    "brunch-static": "^1.2.1",
    "chai": "^3.5.0",
    "es6-promise": "^3.2.1",
    "eslint-plugin-react": "^5.1.1",
    "expect": "^1.20.2",
    "html-brunch-static": "^1.3.2",
    "jquery": "~2.1.4",
    "jquery-mousewheel": "^3.1.13",
    "mocha": "^3.0.0",
    "nib": "^1.1.0",
    "nock": "^8.0.0",
    "oboe": "~2.1.2",
    "paper": "0.9.25",
    "path": "^0.12.7",
    "pug": "^2.0.0-beta10",
    "pug-brunch-static": "^2.0.1",
    "react": "^15.2.1",
    "react-dom": "^15.2.1",
    "react-redux": "^4.4.5",
    "redux": "^3.5.2",
    "redux-logger": "^2.6.1",
    "redux-mock-store": "^1.1.2",
    "redux-promise": "^0.5.3",
    "redux-thunk": "^2.1.0",
    "reselect": "^2.5.3",
    "spectrum-colorpicker": "~1.8.0",
    "stylus-brunch": "^2.10.0",
    "uglify-js-brunch": "^2.10.0",
    "unibabel": "~2.1.0",
    "when": "~3.4.5"
  },
  "dependencies": {
    "jwt-decode": "^2.1.0",
    "lodash": "^4.17.4",
    "postal": "^2.0.5",
    "rc-tree": "^1.3.9"
  },
  "scripts": {
    "test": "mocha --compilers js:babel-register"
  }
}

Another thought, could this have to do with using require instead of import?

If there's any other information I can provide that would be helpful please let me know. Thanks for your help.

UPDATE

Here's my folder structure, simplified:

node_modules
source
|---resources
|---scripts
|---styles
|---views

Here's the output structure produced by brunch build:

assets
css
|---core.css
js
|---app.js
|---app.js.map
|---vendor.js
|---vendor.js.map
home.html

Debug it for yourself! MVCE available. Follow these instructions:

  1. Clone this example repository
  2. npm install
  3. brunch build (make sure it is installed globally with npm install brunch -g)
  4. Compare the sizes of app.js and vendor.js in public/js. They should be 744 KB and 737 KB respectively. Examine the contents of app.js and note the library stuff. How is my files.javascripts.joinTo['js/app.js'] including this with regex /^source\//?

Solution

  • The problem is caused by the mixture of joinTo and entryPoints. I assume that with your config, you first split your code in app.js and vendor.js but then the app.js gets overridden by the output of the entryPoints.

    In order to solve it, you have to choose one of the options:

    Option 1

    Remove the entryPoints declaration. This will just split your code along the provided RegEx.

    Option 2

    Remove the joinTo declaration and change the entryPoints to:

        entryPoints: {
          'source/scripts/app.jsx': {
            'js/vendor.js': /^(?!source\/)/,
            'js/app.js': /^source\//
          },
        }
    

    Conclusion

    In this very case, the output of both options is the same. But with entryPoints the code get's analyzed and only needed modules get bundled. Because there aren't any unnecessary modules, the size is the same. See this issue for more information.