Merge Multiple Javascript Into Single File (Global Scope)

August 10, 2017

Note: Webpack by default is a module bundler, wrapping each javascript file as a module (cannot be accessed globally by default, resolve modules through import or require statement).

Do refer to Webpack: Merge Multiple Javascript Into Single File.

This article shall focus on how to enable the merged javascripts to be accessible in global scope, especially from HTML which include the javascript.

expose-loader

You can combine javascripts using the standard way, but expose the javascript into global scope using expose-loader.

npm install --save-dev expose-loader
npm install --save-dev jquery

Edit webpack.config.js. We shall merge npm jquery, src/util.js and src/index.js into dist/bundle.js.

const path = require('path');

module.exports = {
  entry: {
    'bundle.js': [
      "jquery",
      path.resolve(__dirname, 'src/util.js'),
      path.resolve(__dirname, 'src/index.js')
    ]
  },
  output: {
    filename: '[name]',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      /*
      // somehow the following expose-loader declaration doesn't work
      // https://github.com/webpack-contrib/expose-loader/issues/51
      {
        test: require.resolve('jquery'),
        use: [{
            loader: 'expose-loader',
            options: '$'
        }]
      }
       */
    ],
  }

Edit src/utils.js. Attributes which is to be exposed globally must be declared in module.exports.

var today = "Monday";

module.exports = {
  getName: function () {
    return "Desmond Lua";
  },
  today: today
};

Edit src/index.js. Add the following code to expose jquery as $ in global scope, util.js as util.

// expose jquery globally as $
require("expose-loader?$!jquery");
// epose util.js globally as util
require("expose-loader?util!./util.js");

Build.

./node_modules/.bin/webpack

Edit dist/index.html to test if $ and util is explosed in global scope. Preview dist/index.html in browser.

<html>
  <head>
    <title>Hello</title>
  </head>
  <body>
    <script src="bundle.js"></script>
    <script>
      // test if $jquery and util accessible in global scope
      $("body").text(util.getName() + ", today is " + util.today);
    </script>
    <div id="output"></name>
  </body>
</html>

Project structure.

├── dist
│   ├── bundle.js
│   ├── index.html
├── src
│   ├── index.js
│   └── util.js
└── webpack.config.js

Manually expose to global scope

You can also manually expose modules into global scope without using expose-loader.

Edit src/index.js.

var jquery = require('jquery');
import util from './util.js';

window.$ = jquery;
window.util = util;

ProvidePlugin

ProvidePlugin can automatically load modules instead of having to import or require them everywhere. This global scope only apply within the module bundle, not accessible from HTML file.

Edit webpack.config.js.

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

module.exports = {
  entry: {
    'bundle.js': [
      "jquery",
      path.resolve(__dirname, 'src/util.js'),
      path.resolve(__dirname, 'src/index.js')
    ]
  },
  output: {
    filename: '[name]',
    path: path.resolve(__dirname, 'dist'),
  },
  plugins: [
    new webpack.ProvidePlugin({
      $: "jquery",
      util: path.resolve(__dirname, 'src/util.js')
    })
  ]
};

Edit src/index.js.

// $ and util is automatically require/import using ProvidePlugin
window.$ = $;
window.util = util;

output.library

You can use output.library to export your module bundle as variable.

Edit webpack.config.js. We name our module bundle library as bundle.

const path = require('path');

module.exports = {
  entry: {
    'bundle.js': [
      "jquery",
      path.resolve(__dirname, 'src/util.js'),
      path.resolve(__dirname, 'src/index.js')
    ]
  },
  output: {
    filename: '[name]',
    path: path.resolve(__dirname, 'dist'),
    libraryTarget: 'var',
    library: 'bundle'    
  }
};

Edit src/index.js.

var jquery = require('jquery');
var util = require('./util.js');

module.exports = {
  $: jquery,
  util: util
};

Edit dist/index.html.

<html>
  <head>
    <title>Hello</title>
  </head>
  <body>
    <script src="bundle.js"></script>
    <script>
      bundle.$("body").text(bundle.util.getName() + ", today is " + bundle.util.today);
    </script>
    <div id="output"></name>
  </body>
</html>

webpack-merge-and-include-globally

There easiet way is to utlize webpack-merge-and-include-globally plugin, which will generate pure javascript file without module bundling. It should be able to combine any file without modification, including .css and .js.

Sadly, this plugin doesn’t support webpack optimization such as minify.

npm install --save-dev webpack-merge-and-include-globally

Edit webpack.config.js.

const path = require('path');
const MergeIntoSingleFilePlugin = require('webpack-merge-and-include-globally');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: '[name]',
    path: path.resolve(__dirname, 'dist'),
  },
  plugins: [
    new MergeIntoSingleFilePlugin({
      "bundle.js": [
        path.resolve(__dirname, 'src/util.js'),
        path.resolve(__dirname, 'src/index.js')
      ],
      "bundle.css": [
        path.resolve(__dirname, 'src/css/main.css'),
        path.resolve(__dirname, 'src/css/local.css')
      ]
    })
  ]
};

Build.

./node_modules/.bin/webpack
# production parameter doesn't have minify effect on webpack-merge-and-include-globally
# ./node_modules/.bin/webpack -p
This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.