{"id":18020682,"url":"https://github.com/danielschaffer/webpack-babel-multi-target-plugin","last_synced_at":"2025-04-06T02:09:28.459Z","repository":{"id":56088539,"uuid":"114177870","full_name":"DanielSchaffer/webpack-babel-multi-target-plugin","owner":"DanielSchaffer","description":"A Webpack plugin that works with Babel to allow differential loading - production deployment of ES2015 builds targeted to modern browsers, with an ES5 fallback for legacy browsers.","archived":false,"fork":false,"pushed_at":"2021-01-16T15:00:11.000Z","size":164737,"stargazers_count":152,"open_issues_count":19,"forks_count":14,"subscribers_count":5,"default_branch":"develop","last_synced_at":"2025-04-06T02:09:01.514Z","etag":null,"topics":["babel","babel-loader","differential-loading","es2015","es6","esmodules","esnext","webpack","webpack-angular2","webpack-plugin","webpack4"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DanielSchaffer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-12-13T23:03:39.000Z","updated_at":"2025-01-10T08:57:30.000Z","dependencies_parsed_at":"2022-08-15T13:00:18.573Z","dependency_job_id":null,"html_url":"https://github.com/DanielSchaffer/webpack-babel-multi-target-plugin","commit_stats":null,"previous_names":[],"tags_count":95,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DanielSchaffer%2Fwebpack-babel-multi-target-plugin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DanielSchaffer%2Fwebpack-babel-multi-target-plugin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DanielSchaffer%2Fwebpack-babel-multi-target-plugin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DanielSchaffer%2Fwebpack-babel-multi-target-plugin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DanielSchaffer","download_url":"https://codeload.github.com/DanielSchaffer/webpack-babel-multi-target-plugin/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247423515,"owners_count":20936626,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["babel","babel-loader","differential-loading","es2015","es6","esmodules","esnext","webpack","webpack-angular2","webpack-plugin","webpack4"],"created_at":"2024-10-30T06:07:09.692Z","updated_at":"2025-04-06T02:09:28.439Z","avatar_url":"https://github.com/DanielSchaffer.png","language":"TypeScript","readme":"# webpack-babel-multi-target-plugin\n\n[![](https://img.shields.io/npm/v/webpack-babel-multi-target-plugin.svg)](https://www.npmjs.com/package/webpack-babel-multi-target-plugin)\n[![](https://img.shields.io/npm/dm/webpack-babel-multi-target-plugin.svg)](https://www.npmjs.com/package/webpack-babel-multi-target-plugin)\n[![BrowserStack Status](https://www.browserstack.com/automate/badge.svg?badge_key=UHB0dnF0cUoyaDJZeVJqOTJDWk1EQjY1NC93d29zaTZEYytJZEt1THhsbz0tLU5EcHhtQzJtaGFUbno3aGd3d1pKN2c9PQ==--7f5b762117052ec52c9b04edff86c01266da5dd0)](https://www.browserstack.com/automate/public-build/UHB0dnF0cUoyaDJZeVJqOTJDWk1EQjY1NC93d29zaTZEYytJZEt1THhsbz0tLU5EcHhtQzJtaGFUbno3aGd3d1pKN2c9PQ==--7f5b762117052ec52c9b04edff86c01266da5dd0)\n[![Build Status](https://travis-ci.org/DanielSchaffer/webpack-babel-multi-target-plugin.svg?branch=master)](https://travis-ci.org/DanielSchaffer/webpack-babel-multi-target-plugin)\n\nThis project, inspired by Phil Walton's article\n[Deploying es2015 Code in Production Today](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/),\nadds tooling to simplify the additional configuration with a\nWebpack plugin, `BabelMultiTargetPlugin`.\n\n# Setup and Configuration\n\n[![NPM](https://nodei.co/npm/webpack-babel-multi-target-plugin.png)](https://npmjs.org/package/webpack-babel-multi-target-plugin)\n\nUsing the plugin requires making a few small changes to your existing webpack configuration:\n\n* Replace any instances of `'babel-loader'` with `BabelMultiTargetPlugin.loader()`\n  * Do not use a `Loader` configuration object here - see [Options Reference](#options-reference)\n  below for information on customizing options for `'babel-loader'`\n  \n* Add a loader rule for `.js` files if there isn't one already:\n```javascript\n{\n  test: /\\.js$/,\n  use: [\n    BabelMultiTargetPlugin.loader(),\n  ],\n},\n```\n_Note:_ The above example intentionally does not exclude _node\\_modules_.\n\n* Set `resolve.mainFields` to favor modern ES modules, which allows webpack to load the most modern source possible.\nThere are several intersecting de-facto standards flying around, so this should cover as much as possible:\n```javascript\nmainFields: [\n\n  // rxjs and Angular Package Format\n  // these are generally shipped as a higher ES language level than `module`\n  'es2015',\n  'esm2015',\n  'fesm2015',\n\n  // current leading de-facto standard - see https://github.com/rollup/rollup/wiki/pkg.module\n  'module',\n\n  // previous de-facto standard, superceded by `module`, but still in use by some packages\n  'jsnext:main',\n\n  // Angular Package Format - lower ES level\n  'esm5',\n  'fesm5',\n\n  // standard package.json fields\n  'browser',\n  'main',\n],\n```\n\n* Add an instance of `BabelMultiTargetPlugin` to the webpack\n configuration's `plugins` property\n\n* `BabelMultiTargetPlugin` does not require any configuration - but can\nbe customized (see [Options Reference](#options-reference) below)\n\n* Remove any `.babelrc` - see [Options Reference](#options-reference) below for setting preset options\n\n* Remove any references to `babel-loader` from your `package.json` - it is a direct dependency of\n  `webpack-babel-multi-target-plugin`, and may cause unexpected issues if there are duplicate instances due to\n  a version mismatch\n  \n* Remove any path or pattern matching _node\\_modules_ from the `exclude`\n  property of any rules using `BabelMultiTargetPlugin.loader()`\n\n* TypeScript\n  * Loader rules must use `BabelMultiTargetPlugin.loader()` after your compiler loader (remember, loaders are run bottom to top)\n  * Set `tsconfig` to `target` es6 or higher\n\n* Vue\n  * Replace `'vue-loader'` with `BabelMultiTargetPlugin.loader('vue-loader')`\n  \n* `expose-loader`\n  * Rules using `expose-loader` must be defined _before_ rules using `BabelMultiTargetPlugin.loader()`\n  * Do not `import`/`require` libraries exposed with `expose-loader` - either reference them from the global scope,\n    or do not use `expose-loader`. You may also need to use Webpack's `ProvidePlugin`.\n\n## Upgrading from v1.x\n\n* Change usages of `BabelMultiTargetPlugin.loader` to `BabelMultiTargetPlugin.loader()`\n\n## Usage with ES6 Dynamic Imports\n\nWhen using ES6's `import(...)` syntax, you may use Webpack's built-in chunk naming syntax to control the naming\nof the resulting chunk:\n\n```typescript\nimport(/* webpackChunkName: \"my-dynamic-import\" */'./some-other-module')\n``` \n\nWhen working with imports that use an expression within the import syntax, `BabelMultiTargetPlugin` adds the `[resource]`\ntag to allow better control over the naming of the resulting chunk. The `[resource]` tag will be replaced by the\nrelative path of the imported module, minus the file extension.\n\n```typescript\n/*\n * ./src/\n *   - plugins\n *     - a\n *       plugin.js\n*      - b\n*        plugin.js\n *  \n */\n\n// ./src/loader.js\nimport(/* webpackChunkName: \"[resource]\" */`./plugins/${plugin}/plugin.js`)\n```\n\nIn the above example, the resulting chunks for the plugin files would be (depending on the target configuration):\n* `a-plugin.js` (legacy bundle for `./src/plugins/a/plugin.js`)\n* `a-plugin.modern.js` (modern bundle for `./src/plugins/a/plugin.js`)\n* `b-plugin.js` (legacy bundle for `./src/plugins/b/plugin.js`)\n* `b-plugin.modern.js` (modern bundle for `./src/plugins/b/plugin.js`)\n\n```javascript\n// webpack.config.js\n\nconst BabelMultiTargetPlugin = require('webpack-babel-multi-target-plugin').BabelMultiTargetPlugin\nconst NamedLazyChunksPlugin =  require('webpack-babel-multi-target-plugin').NamedLazyChunksPlugin\n\nmodule.exports = {\n  ...\n  \n  plugins: [\n    new BabelMultiTargetPlugin(),\n    new NamedLazyChunksPlugin(),\n  ],\n}\n\n```\n\n`NamedLazyChunkPlugin` can also be used with plain ES6 Dynamic Imports as an alternative to Webpack's chunk naming\nsyntax.\n\n## Configuration Defaults\n\n`BabelMultiTargetPlugin` does not require any options to be set. The\ndefault behavior is:\n\n* Generate \"modern\" and \"legacy\" bundles.\n\n* The \"modern\" bundle assets will have their filenames appended with\n`.modern`, while the \"legacy\" bundle assets will remain the same. This\nenables these assets to be deployed without breaking anything since it\nwill still have the required polyfills.\n\n* \"modern\" browsers are the last 2 versions of each browser, excluding\nversions that don't support `\u003cscript type=\"module\"\u003e`\n\n### Options Reference\n\n* **`babel.plugins`** (`string[]`) - a list of Babel plugins to use. `@babel/plugin-syntax-dynamic-import` is included automatically.\n* **`babel.presets`** (`string[]`) - a list of Babel presets to use. `@babel/preset-env` is included automatically.\n* **`babel.presetOptions`** (`BabelPresetOptions`) - options passed to `@babel/preset-env`. See Babel's preset-env [options](https://babeljs.io/docs/en/babel-preset-env#options) documentation for more info.\n  * Default: `{ modules: false, useBuiltIns: 'usage' }`\n  * **IMPORTANT:** `modules` is forced to `false` to avoid problems with transformed commonjs modules\n* **`doNotTarget`** (`RegExp[]`) - an array of `RegExp` patterns for modules which\n will be excluded from targeting (see [How It Works](#how-it-works) below)\n* **`exclude`** (`RegExp[]`) - an array of `RegExp` patterns for modules which will\n be excluded from transpiling\n* **`targets`** (`{ [browserProfile: string]: BabelTargetOptions }`) - a\n map of browser profiles to target definitions. This is used to control\n the transpilation for each browser target. See [Configuration Defaults](#configuration-defaults)\n above for default values.\n  * **`targets[browserProfile].key`** (`string`) - Used internally to\n  identify the target, and is appended to the filename of an asset if\n  `tagAssetsWithKey` is set to `true`. Defaults to `browserProfile` if\n  not set.\n  * **`targets[browserProfile].tagAssetsWithKey`** (`boolean`) - Determines whether the\n  `key` is appended to the filename of the target's assets. Defaults to\n  `true` for the \"modern\" target, and `false` for the \"legacy\" target.\n  Only one target can have this property set to `false`.\n  * **`targets[browserProfile].browsers`** Defines the\n   [browserslist](https://babeljs.io/docs/en/babel-preset-env#options) used\n  by `@babel/preset-env` for this target.\n  * **`targets[browserProfile].esModule`** (`boolean`) - Determines whether\n  this target can be referenced by a `\u003cscript type=\"module\"\u003e` tag. Only\n  one target may have this property set to `true`.\n  * **`targets[browserProfile].noModule`** (`boolean`) - Determines whether\n    this target can be referenced by a `\u003cscript nomodule\u003e` tag. Only\n    one target may have this property set to `true`.\n  * **`targets[browserProfile].additionalModules`** (`string[]`) - An optional\n  array of modules that will be prepended to the entry module for the target.\n* **`safari10NoModuleFix` | `safari10NoModuleFix.mode`** (`boolean` | `'external'`, `'inline'` | `'inline-data'` | `'inline-data-base64'` ) - Embeds a polyfill/workaround\nto allow the `nomodule` attribute to function correctly in Safari 10.1.\nSee #9 for more information.\n  * `false` - disabled (default)\n  * `true` | `'inline'` - adds the nomodule fix in an inline script (`HtmlWebpackPlugin` only)\n  * `'inline-data'` - adds the nomodule fix using a script tag with a data url (`HtmlWebpackPlugin` only)\n  * `'inline-data-base64'` - adds the nomodule fix using a script tag with a base64-encoded data url (`HtmlWebpackPlugin` only)\n  * `'external'` - adds the nomodule fix as a separate file linked with a `\u003cscript src\u003e` tag\n* **`safari10NoModuleFix.inject`** (`'head'` | `'body'`) - element to inject the script tag into (`HtmlWebpackPlugin` only)\n  * Default: `'head'`\n  * When using `'body'` the tag will be inserted before other script tags.\n* **`safari10NoModuleFix.minify`** (`boolean`) - minify the fix (uses [terser](https://github.com/terser/terser) with default settings)\n  * Default: `false` (to maintain compatibility with older versions of the plugin without this option)\n\n* **`normalizeModuleIds`**: (`boolean`) - **EXPERIMENTAL**. Removes the babel targeting query from module ids so they\n use what the module id would be without using `BabelMultiTargetPlugin`, and adds a check to webpack's bootstrapping\n code that stops bundle code from executing if it detects that webpack has already been bootstrapped elsewhere.\n This has the effect of preventing duplicate modules from loading in instances where the browser loads both bundles\n (e.g. Safari 10.1).\n\n## Configuration Examples\n\n### Basic Usage\n\n```javascript\n\n// webpack.config.js\n\nconst BabelMultiTargetPlugin = require('webpack-babel-multi-target-plugin').BabelMultiTargetPlugin;\n\nmodule.exports = {\n\n    entry: 'src/main.js',\n\n    resolve: {\n        mainFields: [\n            'es2015',\n            'module',\n            'main',\n        ],\n    },\n\n    module: {\n        rules: [\n            {\n                test: /\\.js$/,\n                use: [\n                    BabelMultiTargetPlugin.loader(),\n                ],\n            },\n        ],\n    },\n\n    plugins: [\n        new BabelMultiTargetPlugin(),\n    ],\n\n};\n```\n\n### TypeScript\n\n```javascript\n\n// webpack.config.js\n\nconst BabelMultiTargetPlugin = require('webpack-babel-multi-target-plugin').BabelMultiTargetPlugin;\n\nmodule.exports = {\n\n    entry: 'src/main.ts',\n\n    resolve: {\n        mainFields: [\n            'es2015',\n            'module',\n            'main',\n        ],\n    },\n\n    module: {\n        rules: [\n            {\n                test: /\\.js$/,\n                use: [\n                    BabelMultiTargetPlugin.loader(),\n                ],\n            },\n            {\n                test: /\\.ts$/,\n                use: [\n                    BabelMultiTargetPlugin.loader(),\n                    'ts-loader'\n                ],\n                options: {\n                    useCache: true,\n                    cacheDirectory: 'node_modules/.cache/ts-loader',\n                },\n            },\n        ],\n    },\n\n    plugins: [\n        new BabelMultiTargetPlugin(),\n    ],\n\n};\n```\n\n### With Options\n\n```javascript\n\n// webpack.config.js\n\nconst BabelMultiTargetPlugin = require('webpack-babel-multi-target-plugin').BabelMultiTargetPlugin;\n\nmodule.exports = {\n\n    entry: 'src/main.js',\n\n    resolve: {\n        mainFields: [\n            'es2015',\n            'module',\n            'main',\n        ],\n    },\n\n    module: {\n        rules: [\n            {\n                test: /\\.js$/,\n                use: [\n                    BabelMultiTargetPlugin.loader(),\n                ],\n            },\n        ],\n    },\n\n    plugins: [\n        new BabelMultiTargetPlugin({\n\n            babel: {\n                // babel preset-env plugin options go here\n            },\n\n            // excludes the untargetable-library module from being targeted\n            doNotTarget: [\n                /node_modules\\/untargetable-library/,\n            ],\n\n            // excludes the transpiling-trouble module from being transpiled\n            exclude: [\n                /node_modules\\/transpiling-trouble/\n            ],\n\n            // swap which target gets the name appended\n            targets: {\n\n                // results in the \"modern\" bundle being output as main.js\n                // the default is main.modern.js\n                modern: {\n                    tagAssetsWithKey: false,\n                },\n\n                // results in the \"legacy\" bundle being output as main.old-and-broke.js\n                // the default is main.js\n                legacy: {\n                    key: 'old-and-broke',\n                    tagAssetsWithKey: true,\n                },\n            },\n        }),\n    ],\n\n};\n```\n\n### Don't Transpile ES5-only Libraries\n\nSome libraries may cause runtime errors if they are transpiled - often,\nthey will already have been transpiled by Babel as part of the author's\npublishing process. These errors may look like:\n\n\u003e `Cannot assign to read only property 'exports' of object '\\#\\\u003cObject\\\u003e'`\n\nor\n\n\u003e `__webpack_require__(...) is not a function`\n\nThese libraries most likely need to be excluded from Babel's\ntranspilation. While the plugin will automatically attempt to filter out\nCommonJs modules, you can also specify libraries to be excluded in the\n`BabelMultiTargetPlugin` constructor:\n\n```javascript\n\nnew BabelMultiTargetPlugin({\n    exclude: [\n        /node_modules\\/some-es5-library/,\n        /node_modules\\/another-es5-library/,\n    ],\n});\n```\n\n## Example Projects\nSeveral simple use cases are provided to show how the plugin works.\n\n### Install Example Project Dependencies\n```bash\n# installs dependencies for all example projects; requires bash and yarn\nyarn setup\n```\n\n### Build the Example Projects\n```bash\n# builds all example projects\nyarn examples\n\n# build just the specified example projects\nyarn es6-plain typescript-plain\n```\n\n### Example Project Dev Server\n```bash\n# builds and serves all example projects\nyarn start\n\n# builds and serves just the specified example projects\nyarn start es6-plain typescript-plain\n```\n\nNote that when running all example projects concurrently, you may need to increase\nNode's memory limit:\n```\nNODE_OPTIONS=\"--max-old-space-size=8192\" yarn start\n```\n\nExamples will be available at `http://HOST:PORT/examples/EXAMPLE_NAME`.\n\n## How It Works\n\nThis plugin works by effectively duplicating each entry point, and giving it\na target. Each target corresponds to a browser definition that is passed\nto Babel. As the compilation processes each entry point, the target filters\ndown from the entry point through each of its dependencies. Once the\ncompilation is complete, any CSS outputs are merged into a single\nmodule so they are not duplicated (since CSS will be the same regardless\nof ES supported level). If [HtmlWebpackPlugin](https://github.com/jantimon/html-webpack-plugin)\nis being used, the script tags are updated to use the appropriate\n`type=\"module\"` and `nomodule` attributes.\n\n### Transpiling _node\\_modules_\nIn order to have the greatest possible positive effect, the compilation must\nbe able to start with the high possible ES level of source code. This is why\nthe extra entries were added to the `mainFields` array, and why\n_node\\_modules_ is not excluded from loader rules. This ensures that even\ndependencies can take advantage of being able to be bundled with ES6\nfeatures and syntax, and the more verbose syntax and polyfill-laden\nonly included for legacy browsers.\n\n### Blind Targeting\nIn some circumstances, such as lazy-loaded routes and modules with\nAngular, Vue, and ES6 dynamic imports, it may not be possible to\ndetermine the entry point of a module. In these cases, the plugin will\nassign the module a target on its own. It does this by creating an array\nof the targets, and removing and assigning one target each time it\nencounters a given resource.\n\nIf you encounter a `BlindTargetingError` while attempting to use this\nplugin, please create an issue with a simple reproduction.\n\n## Benefits\n\n* Automatically sets up your index HTML files with both \"modern\" and\n \"legacy\" bundles\n\n* Uses ES2015 source when available, and attempts to automatically avoid\nre-transpiling ES5/CommonJs code\n\n* Avoid using between 30-70 KB of polyfill code on browsers that don't\nneed them (depends on project size and features used)\n\n## Caveats\n* Increased build time - since the plugin duplicates entry points, everything\nhas to be done twice. This can be helped with appropriate cache\nconfigurations where they are available (Babel, TypeScript, etc), but\nit may make sense to avoid using this plugin during development.\n\n* May not play nice with [hard-source-webpack-plugin](https://github.com/mzgoddard/hard-source-webpack-plugin)\n\n* Code Splitting - Since CommonJs dependencies can be shared between\n \"modern\" and \"legacy\" bundles, apps with multiple entries or\n lazy-loaded modules may end up with a large number of \"vendor\" chunks.\n\n## Testing\nThe output generated by this plugin is tested on the following browsers courtesy of BrowserStack:\n\n* Chrome\n* Firefox\n* Edge\n* Safari (including 10.1 on Mac OS and 10.3 on iOS)\n* IE 11\n\n\u003ca href=\"https://www.browserstack.com\" target=\"_blank\"\u003e\u003cimg src=\"./doc/browserstack-logo.svg\" width=\"25%\"\u003e\u003c/a\u003e\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielschaffer%2Fwebpack-babel-multi-target-plugin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielschaffer%2Fwebpack-babel-multi-target-plugin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielschaffer%2Fwebpack-babel-multi-target-plugin/lists"}