Skip to content

Instantly share code, notes, and snippets.

@ncochard
Last active September 29, 2023 05:15
  • Star 87 You must be signed in to star a gist
  • Fork 17 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save ncochard/6cce17272a069fdb4ac92569d85508f4 to your computer and use it in GitHub Desktop.
The correct way to compile ES6 using babel...

When you create a npm package, remember it might be used in a browser or a server, or even a command line utility… For each package you create, please pay attention at what it will be used for:

  1. Is it going to be used as a dependency to a nodejs application that is not bundled? (e.g. command line utilities)
  2. Is it going to be used as a dependency to a nodejs application that is bundled? (e.g. AWS Lambdas)
  3. Is it going to be used as a dependency to a browser application (always bundled)?.
  • In cases 2) and 3) you want to allow for tree shaking.
  • In cases 1) and 2) you want to benefit from the "ES6"/"ES next" features supported natively by nodejs.
  • In case 3) you also want to benefit from the native support of "ES6" from your browser.

Note: The examples below will assume that you do not care about people using old browsers or a version of nodejs prior to 6.10.

Dependencies of a nodejs application that is not bundled...

For a package that might be used as a dependency to a nodejs application that is not bundled...

  1. You need to compile your code to the folder ./dist.

  2. You need to define the following entry point in package.json: "main": "./dist/index.js".

  3. And you need to use the preset below.

     "presets": [
     	["babel-preset-env", {
     		"targets": {
     			"node": "6.10"
     		}
     	}]
     ]
    

Dependencies of a nodejs application that is bundled...

For a package that might be used as a dependency to a nodejs application that is bundled...

  1. You need to compile your code to the folder ./module.

  2. You need to define the following entry point in package.json: "module": "./module/index.js".

  3. And you need to use the preset below.

     "presets": [
     	["babel-preset-env", {
     		"targets": {
     			"node": "6.10"
     		},
     		"modules": false
     	}]
     ]
    

Dependencies of a browser application that is bundled...

For a package that might be used as a dependency to a browser application that would obviously be bundled...

  1. You need to compile your code to the folder ./lib.

  2. You need to define the following entry point in package.json: "browser": "./lib/index.js".

  3. And you need to use the preset below.

     "presets": [
     	["babel-preset-env", {
     		"targets": {
     			"browsers": "last 2 versions, ie 10-11"
     		},
     		"modules": false
     	}]
     ]
    

The complete .babelrc

This is an example of how the .babelrc file might look like for an npm package that might be used in any application.

{
    "env": {
        "development": {
	    "presets": [
		["babel-preset-env", {
		    "targets": {
			"node": "6.10"
		    }
		}]
	    ],
	    "plugins": [
		"transform-object-rest-spread",
		"transform-class-properties",
		...
	    ]
	},
        "browser": {
            "presets": [
                ["babel-preset-env", {
                    "targets": {
                        "browsers": "last 2 versions, ie 10-11"
                    },
                    "modules": false
                }]
            ],
	    "plugins": [
		"transform-object-rest-spread",
		"transform-class-properties",
		...
	    ]
        },
        "module": {
            "presets": [
                ["babel-preset-env", {
                    "targets": {
                        "node": "6.10"
                    },
                    "modules": false
                }]
            ],
	    "plugins": [
		"transform-object-rest-spread",
		"transform-class-properties",
		...
	    ]
        }
    },
    "sourceMaps": true
}

package.json

This is an example of how the package.json file might look like for an npm package that might be used in any application.

{
  ...
  "main": "./dist/index.js",
  "browser": "./lib/index.js",
  "module": "./module/index.js",
  "scripts": {
    ...
    "build-browser": "cross-env BABEL_ENV=browser babel ./src --out-dir ./lib --source-maps --copy-files",
    "build-module": "cross-env BABEL_ENV=module babel ./src --out-dir ./module --source-maps --copy-files",
    "build-node": "babel ./src --out-dir ./dist --source-maps --copy-files",
    "build ": "npm run build-node && npm run build-browser && npm run build-module"
  },
  ...
}

Bundling an application for the browser

Finally in the consuming application, you should configure webpack.config.js as followed. If the application runs in the browser, use the following. (Obviously this file would also need to contain configuration for CSS, etc.)

Note that webpack will assume target: 'web'. This is what tells webpack that this bundle is expected to be used in the browser. And webpack will automatically look for the entry point browser rather than main in each dependency.

const webpack = require('webpack');
...
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
    entry: {
        ...
    },
    output: {
        filename: '[name].js',
        path: ...
    },
    ...,
    module: {
        rules: [
            ...
        ]
    },
    plugins: [
        ...,
        new webpack.LoaderOptionsPlugin({
            minimize: true,
            debug: false
        }),
        new UglifyJSPlugin({
            uglifyOptions: {
                beautify: false,
                ecma: 6,
                compress: true,
                comments: false
            }
        }),
        …
    ]
};

Bundling an application for the server

If the application runs on a server, use the following. Note the property target: 'node'. This is what tells webpack that this bundle is expected to be used on the server rather than the browser. And webpack will automatically look for the entry point module rather than main in each dependency.

const webpack = require('webpack');
...
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
    target: 'node',
    entry: {
        app: …
    },
    output: {
        filename: '[name].js',
        path: ...,
        library: '???',
        libraryTarget: 'commonjs'
    },
    ...,
    plugins: [
        new webpack.LoaderOptionsPlugin({
            minimize: true,
            debug: false
        }),
        new UglifyJSPlugin({
            uglifyOptions: {
                beautify: false,
                ecma: 6,
                compress: true,
                comments: false
            }
        }),
        ...
    ]
};
@KarmaBlackshaw
Copy link

thanks! Helped a lot 😁😁😁

@DougAnderson444
Copy link

This is awesome, super useful 🍾 🎆

Curious as to why you used modules: false for node instead of any of the other options usch as auto or cjs?

"targets": {
      "node": "6.10"
},
	"modules": false

The docs say false is set for browsers:

Setting this to false will preserve ES modules. Use this only if you intend to ship native ES Modules to browsers.

@Izhaki
Copy link

Izhaki commented Jun 11, 2021

Curious as to why you used modules: false for node instead of any of the other options usch as auto or cjs?

"modules": false:

  • Instructs babel not to transform modules (to, say cjs).
  • This will keep import and export statement.
  • This esm format is only natively supported on Node 14 (or 13 with a flag) and when the module has an .mjs extension.
  • So "normal" node scripts with old-school require won't be able to read these.
  • But any code that is bundled (say with webpack) will have the bundler understand these and it can transform these to whatever.
  • So this is useful when the code is to be bundled - the bundler will do the transformations, not babel.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment