Search code examples
javascriptnode.jsbrowserify

Create vendor.js bundle with pure npm script


I am trying out the modern approach to build javascript applications without Grunt or Gulp. I am creating my build utilities by just using the scripts key word in package.json.

It works great, but I ran into a challenge. Is there a good way to create separate vendor.js and app.js bundles without making every dependency explicit in the browserify command (or alternatively passing a list of deps to the browserify command)?

Something better than:

"dependencies": {
   "react": "latest",
   "react-dom": "latest",
   "redux": "latest",
   "d3": "latest"
},
"devDependencies": {
   "browserify": "latest" 
},
"scripts": {
   "vendor": "browserify -r react -r react-dom -r redux -r d3 > vendor.js",
   "app": "browserify -x react -x react-dom -x redux -x d3 ./app/main.js > app.js"
}

Preferable, I would recycle the information stored in the dependencies keyword. Obviously, I do not want bundle devDependencies or dependencies not used in my code (even though the latter can be prevented by good maintenance of the requirements).


Solution

  • Yes, it is possible. Whether the solution is very elegant, I'll leave up to you to decide. Basically it boils down to something like the following (incomplete, browserify not yet called) snippet:

      "scripts": {
          "init": "npm ls -json --depth 0 | jq .dependencies | jq keys[]",
          "vendor": "npm run --silent init | sed 's/\\(.*\\)/-r \\1/g' | xargs"
      },
    

    The init script is used to extract the dependencies. The vendor script calls this script, and converts it to the input parameters for browserify.

    Note 1: I'm using jq to extract information from the dependencies tree. Note 2: construction of the argument list can also be done in the init script. You will have to provide an environment variable to distinguish between the -r or -x options.