Overview

Exactly.

Well, lately I've been researching CommonJS and ES Modules...

...So, next let's talk about bundling them with webpack for ease of use in various environments.

Historically, JavaScript has had the problem of "wanting to use a module system but not being able to use modules in the browser." To overcome this, webpack, an amazing tool, was born in 2012. This tool bundles code split by a module system, making it usable in a browser. It also offers ES2015 -> ES5 transpiling, TypeScript transpiling, tree shaking, and minification features, making it a truly remarkable tool. Let's see the power of webpack this time.

 

Bundling CommonJS with webpack

First, let's run CommonJS as it is

// This is index.js. It uses require, so it's CommonJS.
const dayjs = require('dayjs');

const today = dayjs();
console.log('Today:', today.format('YYYY-MM-DD'));

const tomorrow = today.add(1, 'day');
console.log('Tomorrow:', tomorrow.format('YYYY-MM-DD'));
# Preparing an npm project.
yarn init -y

# Installing dayjs.
yarn add dayjs

node -v
# v20.3.0

# Node.js can normally execute CommonJS.
node index.js
# Today: 2023-07-05
# Tomorrow: 2023-07-06
<!--
  index.html for running index.js in the browser.
  Running index.js in the browser will result in 
  Uncaught ReferenceError: require is not defined.
-->
<script src="node_modules/dayjs/dayjs.min.js"></script>
<script src="index.js"></script>

 

Let's bundle CommonJS with webpack

// webpack configuration for creating a bundle that can be executed in Node.js.
// webpack.config-for-node.js
module.exports = {
  target: 'node',
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  },
  output: {
    filename: 'main-for-node.js',
    libraryTarget: 'commonjs2'
  }
};
// webpack configuration for creating a bundle that can be executed in the browser.
// webpack.config-for-browser.js
module.exports = {
  target: 'web',
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  },
  output: {
    filename: 'main-for-browser.js',
    libraryTarget: 'umd'
  }
};
# Installing webpack.
yarn add --dev webpack webpack-cli babel-loader @babel/core

# Bundling for Node.js!
# Using production mode will reduce the file size.
yarn webpack --mode development --entry ./index.js --config ./webpack.config-for-node.js

# Node.js can still normally execute it.
node ./dist/main-for-node.js
# Today: 2023-07-05
# Tomorrow: 2023-07-06

# Bundling for the browser!
yarn webpack --mode development --entry ./index.js --config ./webpack.config-for-browser.js
<!--
  index-with-webpack.html for running the bundle in the browser.
  Running the bundle in the browser will result in
  Today: 2023-07-05
  Tomorrow: 2023-07-06
-->
<script src="dist/main-for-browser.js"></script>

 

Bundling ES Modules with webpack

First, let's run ES Modules as they are

// This is index.mjs. It uses import, so it's ES Modules.
import dayjs from 'dayjs';

const today = dayjs();
const tomorrow = today.add(1, 'day');

console.log(`Today: ${today.format('YYYY-MM-DD')}`);
console.log(`Tomorrow: ${tomorrow.format('YYYY-MM-DD')}`);
yarn init -y

yarn add dayjs

node -v
# v20.3.0

# Node.js can normally execute ES Modules.
node index.mjs
# Today: 2023-07-05
# Tomorrow: 2023-07-06
<!--
  Running in the browser results in
  GET http://127.0.../node_modules/dayjs/esm/constant net::ERR_ABORTED 404 (Not Found)
  GET http://127.0.../node_modules/dayjs/esm/locale/en 404 (Not Found)
  GET http://127.0.../node_modules/dayjs/esm/utils net::ERR_ABORTED 404 (Not Found)
  I'm not sure, but it doesn't work well.
  Maybe it doesn't work when npm packages are involved?
-->
<script type="module">
  import dayjs from './node_modules/dayjs/esm/index.js';

  const today = dayjs();
  const tomorrow = today.add(1, 'day');

  console.log(`Today: ${today.format('YYYY-MM-DD')}`);
  console.log(`Tomorrow: ${tomorrow.format('YYYY-MM-DD')}`);
</script>

 

Let's bundle ES Modules with webpack

// webpack configuration for creating a bundle that can be run in Node.js.
// Let's stick to the ES Modules format for fun.
// webpack.config-for-node.mjs
export default {
  // The contents are the same as the CommonJS configuration.
};
// webpack configuration for creating a bundle that can be run in the browser.
// Let's stick to the ES Modules format for fun.
// webpack.config-for-browser.mjs
export default {
  // The contents are the same as the CommonJS configuration.
};
# Install webpack.
yarn add --dev webpack webpack-cli babel-loader @babel/core

# Let's bundle for Node.js!
# Using production mode will reduce the file size.
yarn webpack --mode development --entry ./index.mjs --config ./webpack.config-for-node.mjs

# Node.js can still run it as usual.
node ./dist/main-for-node.js
# Today: 2023-07-05
# Tomorrow: 2023-07-06

# Let's bundle for the browser!
yarn webpack --mode development --entry ./index.mjs --config ./webpack.config-for-browser.mjs
<!--
  index-with-webpack.html for running the bundle in the browser.
  Running the bundle in the browser will display
  Today: 2023-07-05
  Tomorrow: 2023-07-06
-->
<script src="dist/main-for-browser.js"></script>

 

The End

No matter how modern the version of JavaScript we write, no matter which module system we use, we have made it work in both Node.js and the browser! No matter how cool the script we write, it's meaningless if it can't run. By getting our hands dirty, we've really felt the power of webpack.