Coder Social home page Coder Social logo

danielschaffer / webpack-babel-multi-target-plugin Goto Github PK

View Code? Open in Web Editor NEW
153.0 6.0 14.0 160.88 MB

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.

JavaScript 2.74% TypeScript 96.46% Shell 0.80%
esnext es6 babel webpack-plugin es2015 esmodules babel-loader webpack webpack4 webpack-angular2 differential-loading

webpack-babel-multi-target-plugin's Introduction

webpack-babel-multi-target-plugin

BrowserStack Status Build Status

This project, inspired by Phil Walton's article Deploying es2015 Code in Production Today, adds tooling to simplify the additional configuration with a Webpack plugin, BabelMultiTargetPlugin.

Setup and Configuration

NPM

Using the plugin requires making a few small changes to your existing webpack configuration:

  • Replace any instances of 'babel-loader' with BabelMultiTargetPlugin.loader()

    • Do not use a Loader configuration object here - see Options Reference below for information on customizing options for 'babel-loader'
  • Add a loader rule for .js files if there isn't one already:

{
  test: /\.js$/,
  use: [
    BabelMultiTargetPlugin.loader(),
  ],
},

Note: The above example intentionally does not exclude node_modules.

  • Set resolve.mainFields to favor modern ES modules, which allows webpack to load the most modern source possible. There are several intersecting de-facto standards flying around, so this should cover as much as possible:
mainFields: [

  // rxjs and Angular Package Format
  // these are generally shipped as a higher ES language level than `module`
  'es2015',
  'esm2015',
  'fesm2015',

  // current leading de-facto standard - see https://github.com/rollup/rollup/wiki/pkg.module
  'module',

  // previous de-facto standard, superceded by `module`, but still in use by some packages
  'jsnext:main',

  // Angular Package Format - lower ES level
  'esm5',
  'fesm5',

  // standard package.json fields
  'browser',
  'main',
],
  • Add an instance of BabelMultiTargetPlugin to the webpack configuration's plugins property

  • BabelMultiTargetPlugin does not require any configuration - but can be customized (see Options Reference below)

  • Remove any .babelrc - see Options Reference below for setting preset options

  • Remove any references to babel-loader from your package.json - it is a direct dependency of webpack-babel-multi-target-plugin, and may cause unexpected issues if there are duplicate instances due to a version mismatch

  • Remove any path or pattern matching node_modules from the exclude property of any rules using BabelMultiTargetPlugin.loader()

  • TypeScript

    • Loader rules must use BabelMultiTargetPlugin.loader() after your compiler loader (remember, loaders are run bottom to top)
    • Set tsconfig to target es6 or higher
  • Vue

    • Replace 'vue-loader' with BabelMultiTargetPlugin.loader('vue-loader')
  • expose-loader

    • Rules using expose-loader must be defined before rules using BabelMultiTargetPlugin.loader()
    • Do not import/require libraries exposed with expose-loader - either reference them from the global scope, or do not use expose-loader. You may also need to use Webpack's ProvidePlugin.

Upgrading from v1.x

  • Change usages of BabelMultiTargetPlugin.loader to BabelMultiTargetPlugin.loader()

Usage with ES6 Dynamic Imports

When using ES6's import(...) syntax, you may use Webpack's built-in chunk naming syntax to control the naming of the resulting chunk:

import(/* webpackChunkName: "my-dynamic-import" */'./some-other-module')

When working with imports that use an expression within the import syntax, BabelMultiTargetPlugin adds the [resource] tag to allow better control over the naming of the resulting chunk. The [resource] tag will be replaced by the relative path of the imported module, minus the file extension.

/*
 * ./src/
 *   - plugins
 *     - a
 *       plugin.js
*      - b
*        plugin.js
 *  
 */

// ./src/loader.js
import(/* webpackChunkName: "[resource]" */`./plugins/${plugin}/plugin.js`)

In the above example, the resulting chunks for the plugin files would be (depending on the target configuration):

  • a-plugin.js (legacy bundle for ./src/plugins/a/plugin.js)
  • a-plugin.modern.js (modern bundle for ./src/plugins/a/plugin.js)
  • b-plugin.js (legacy bundle for ./src/plugins/b/plugin.js)
  • b-plugin.modern.js (modern bundle for ./src/plugins/b/plugin.js)
// webpack.config.js

const BabelMultiTargetPlugin = require('webpack-babel-multi-target-plugin').BabelMultiTargetPlugin
const NamedLazyChunksPlugin =  require('webpack-babel-multi-target-plugin').NamedLazyChunksPlugin

module.exports = {
  ...
  
  plugins: [
    new BabelMultiTargetPlugin(),
    new NamedLazyChunksPlugin(),
  ],
}

NamedLazyChunkPlugin can also be used with plain ES6 Dynamic Imports as an alternative to Webpack's chunk naming syntax.

Configuration Defaults

BabelMultiTargetPlugin does not require any options to be set. The default behavior is:

  • Generate "modern" and "legacy" bundles.

  • The "modern" bundle assets will have their filenames appended with .modern, while the "legacy" bundle assets will remain the same. This enables these assets to be deployed without breaking anything since it will still have the required polyfills.

  • "modern" browsers are the last 2 versions of each browser, excluding versions that don't support <script type="module">

Options Reference

  • babel.plugins (string[]) - a list of Babel plugins to use. @babel/plugin-syntax-dynamic-import is included automatically.

  • babel.presets (string[]) - a list of Babel presets to use. @babel/preset-env is included automatically.

  • babel.presetOptions (BabelPresetOptions) - options passed to @babel/preset-env. See Babel's preset-env options documentation for more info.

    • Default: { modules: false, useBuiltIns: 'usage' }
    • IMPORTANT: modules is forced to false to avoid problems with transformed commonjs modules
  • doNotTarget (RegExp[]) - an array of RegExp patterns for modules which will be excluded from targeting (see How It Works below)

  • exclude (RegExp[]) - an array of RegExp patterns for modules which will be excluded from transpiling

  • targets ({ [browserProfile: string]: BabelTargetOptions }) - a map of browser profiles to target definitions. This is used to control the transpilation for each browser target. See Configuration Defaults above for default values.

    • targets[browserProfile].key (string) - Used internally to identify the target, and is appended to the filename of an asset if tagAssetsWithKey is set to true. Defaults to browserProfile if not set.
    • targets[browserProfile].tagAssetsWithKey (boolean) - Determines whether the key is appended to the filename of the target's assets. Defaults to true for the "modern" target, and false for the "legacy" target. Only one target can have this property set to false.
    • targets[browserProfile].browsers Defines the browserslist used by @babel/preset-env for this target.
    • targets[browserProfile].esModule (boolean) - Determines whether this target can be referenced by a <script type="module"> tag. Only one target may have this property set to true.
    • targets[browserProfile].noModule (boolean) - Determines whether this target can be referenced by a <script nomodule> tag. Only one target may have this property set to true.
    • targets[browserProfile].additionalModules (string[]) - An optional array of modules that will be prepended to the entry module for the target.
  • safari10NoModuleFix | safari10NoModuleFix.mode (boolean | 'external', 'inline' | 'inline-data' | 'inline-data-base64' ) - Embeds a polyfill/workaround to allow the nomodule attribute to function correctly in Safari 10.1. See #9 for more information.

    • false - disabled (default)
    • true | 'inline' - adds the nomodule fix in an inline script (HtmlWebpackPlugin only)
    • 'inline-data' - adds the nomodule fix using a script tag with a data url (HtmlWebpackPlugin only)
    • 'inline-data-base64' - adds the nomodule fix using a script tag with a base64-encoded data url (HtmlWebpackPlugin only)
    • 'external' - adds the nomodule fix as a separate file linked with a <script src> tag
  • safari10NoModuleFix.inject ('head' | 'body') - element to inject the script tag into (HtmlWebpackPlugin only)

    • Default: 'head'
    • When using 'body' the tag will be inserted before other script tags.
  • safari10NoModuleFix.minify (boolean) - minify the fix (uses terser with default settings)

    • Default: false (to maintain compatibility with older versions of the plugin without this option)
  • normalizeModuleIds: (boolean) - EXPERIMENTAL. Removes the babel targeting query from module ids so they use what the module id would be without using BabelMultiTargetPlugin, and adds a check to webpack's bootstrapping code that stops bundle code from executing if it detects that webpack has already been bootstrapped elsewhere. This has the effect of preventing duplicate modules from loading in instances where the browser loads both bundles (e.g. Safari 10.1).

Configuration Examples

Basic Usage

// webpack.config.js

const BabelMultiTargetPlugin = require('webpack-babel-multi-target-plugin').BabelMultiTargetPlugin;

module.exports = {

    entry: 'src/main.js',

    resolve: {
        mainFields: [
            'es2015',
            'module',
            'main',
        ],
    },

    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    BabelMultiTargetPlugin.loader(),
                ],
            },
        ],
    },

    plugins: [
        new BabelMultiTargetPlugin(),
    ],

};

TypeScript

// webpack.config.js

const BabelMultiTargetPlugin = require('webpack-babel-multi-target-plugin').BabelMultiTargetPlugin;

module.exports = {

    entry: 'src/main.ts',

    resolve: {
        mainFields: [
            'es2015',
            'module',
            'main',
        ],
    },

    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    BabelMultiTargetPlugin.loader(),
                ],
            },
            {
                test: /\.ts$/,
                use: [
                    BabelMultiTargetPlugin.loader(),
                    'ts-loader'
                ],
                options: {
                    useCache: true,
                    cacheDirectory: 'node_modules/.cache/ts-loader',
                },
            },
        ],
    },

    plugins: [
        new BabelMultiTargetPlugin(),
    ],

};

With Options

// webpack.config.js

const BabelMultiTargetPlugin = require('webpack-babel-multi-target-plugin').BabelMultiTargetPlugin;

module.exports = {

    entry: 'src/main.js',

    resolve: {
        mainFields: [
            'es2015',
            'module',
            'main',
        ],
    },

    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    BabelMultiTargetPlugin.loader(),
                ],
            },
        ],
    },

    plugins: [
        new BabelMultiTargetPlugin({

            babel: {
                // babel preset-env plugin options go here
            },

            // excludes the untargetable-library module from being targeted
            doNotTarget: [
                /node_modules\/untargetable-library/,
            ],

            // excludes the transpiling-trouble module from being transpiled
            exclude: [
                /node_modules\/transpiling-trouble/
            ],

            // swap which target gets the name appended
            targets: {

                // results in the "modern" bundle being output as main.js
                // the default is main.modern.js
                modern: {
                    tagAssetsWithKey: false,
                },

                // results in the "legacy" bundle being output as main.old-and-broke.js
                // the default is main.js
                legacy: {
                    key: 'old-and-broke',
                    tagAssetsWithKey: true,
                },
            },
        }),
    ],

};

Don't Transpile ES5-only Libraries

Some libraries may cause runtime errors if they are transpiled - often, they will already have been transpiled by Babel as part of the author's publishing process. These errors may look like:

Cannot assign to read only property 'exports' of object '\#\<Object\>'

or

__webpack_require__(...) is not a function

These libraries most likely need to be excluded from Babel's transpilation. While the plugin will automatically attempt to filter out CommonJs modules, you can also specify libraries to be excluded in the BabelMultiTargetPlugin constructor:

new BabelMultiTargetPlugin({
    exclude: [
        /node_modules\/some-es5-library/,
        /node_modules\/another-es5-library/,
    ],
});

Example Projects

Several simple use cases are provided to show how the plugin works.

Install Example Project Dependencies

# installs dependencies for all example projects; requires bash and yarn
yarn setup

Build the Example Projects

# builds all example projects
yarn examples

# build just the specified example projects
yarn es6-plain typescript-plain

Example Project Dev Server

# builds and serves all example projects
yarn start

# builds and serves just the specified example projects
yarn start es6-plain typescript-plain

Note that when running all example projects concurrently, you may need to increase Node's memory limit:

NODE_OPTIONS="--max-old-space-size=8192" yarn start

Examples will be available at http://HOST:PORT/examples/EXAMPLE_NAME.

How It Works

This plugin works by effectively duplicating each entry point, and giving it a target. Each target corresponds to a browser definition that is passed to Babel. As the compilation processes each entry point, the target filters down from the entry point through each of its dependencies. Once the compilation is complete, any CSS outputs are merged into a single module so they are not duplicated (since CSS will be the same regardless of ES supported level). If HtmlWebpackPlugin is being used, the script tags are updated to use the appropriate type="module" and nomodule attributes.

Transpiling node_modules

In order to have the greatest possible positive effect, the compilation must be able to start with the high possible ES level of source code. This is why the extra entries were added to the mainFields array, and why node_modules is not excluded from loader rules. This ensures that even dependencies can take advantage of being able to be bundled with ES6 features and syntax, and the more verbose syntax and polyfill-laden only included for legacy browsers.

Blind Targeting

In some circumstances, such as lazy-loaded routes and modules with Angular, Vue, and ES6 dynamic imports, it may not be possible to determine the entry point of a module. In these cases, the plugin will assign the module a target on its own. It does this by creating an array of the targets, and removing and assigning one target each time it encounters a given resource.

If you encounter a BlindTargetingError while attempting to use this plugin, please create an issue with a simple reproduction.

Benefits

  • Automatically sets up your index HTML files with both "modern" and "legacy" bundles

  • Uses ES2015 source when available, and attempts to automatically avoid re-transpiling ES5/CommonJs code

  • Avoid using between 30-70 KB of polyfill code on browsers that don't need them (depends on project size and features used)

Caveats

  • Increased build time - since the plugin duplicates entry points, everything has to be done twice. This can be helped with appropriate cache configurations where they are available (Babel, TypeScript, etc), but it may make sense to avoid using this plugin during development.

  • May not play nice with hard-source-webpack-plugin

  • Code Splitting - Since CommonJs dependencies can be shared between "modern" and "legacy" bundles, apps with multiple entries or lazy-loaded modules may end up with a large number of "vendor" chunks.

Testing

The output generated by this plugin is tested on the following browsers courtesy of BrowserStack:

  • Chrome
  • Firefox
  • Edge
  • Safari (including 10.1 on Mac OS and 10.3 on iOS)
  • IE 11

webpack-babel-multi-target-plugin's People

Contributors

austaras avatar danielschaffer avatar despian avatar foglerek avatar georgegriff avatar rangermeier avatar saumitrab avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

webpack-babel-multi-target-plugin's Issues

Support dynamic imports for Vue components

Following up to the discussion in #4 I open a new issue for this.

v1.0.1-alpha.2 works fine for examples/es6-dynamic-import but for examples/vue-cssextract (the HelloWorld.vue component is loaded async) the output still looks like this:

out/examples/vue-cssextract/0.js  <--- imported by both main.js and main.modern.js, sometimes using the modern target, somtimes the legacy target
out/examples/vue-cssextract/main.css
out/examples/vue-cssextract/main.js
out/examples/vue-cssextract/main.modern.js
out/examples/vue-cssextract/vendors~main.js
out/examples/vue-cssextract/vendors~main.modern.js

When I import a plain js file dynamically from within a Vue component, webpack will however emit both a modern and a legacy chunk for it. E.g. adding another dependency.js and altering the component config of examples/vue-cssextract/App.vue like this:

export default {
  name: 'app',
  components: {
    HelloWorld: () => import('./components/HelloWorld.vue')
  },
  mounted() {
    import('./dependency').then(dep => {
      console.log(dep('hello'))
    })
  }
}

will build the following files:

out/examples/vue-cssextract/0.js  <--- this is HelloWorld.vue, imported by both main.modern.js and main.js
out/examples/vue-cssextract/1.js  <--- this is modern dependency.js, imported by main.modern.js
out/examples/vue-cssextract/2.js  <--- this is legacy dependency.js, imported by main.js
out/examples/vue-cssextract/main.js
out/examples/vue-cssextract/main.modern.js
out/examples/vue-cssextract/vendors~main.js
out/examples/vue-cssextract/vendors~main.modern.js

Use Browserslist config

It will be nice to respect the user’s Browserslist config.

We can do it with code like:

let browserslist = require('browserslist')

const baseBrowsers = browserslist()
const es6 = browserslist('supports es6-module')

const modernBrowsers = baseBrowsers.filter(i => es6.includes(i))
const legacyBrowsers = baseBrowsers.filter(i => !es6.includes(i))

add travis-ci integration

  • lint, test, and build on all branches
  • prerelease deployments to npm on release/* branches
  • non-prerelease deployments to npm on master
  • add appropriate dist-tags depending on release/prerelease
  • determine versioning strategy (e.g. make travis auto version based on release branch? or only version on tagged releases?)

Encountered unexpected blind targeting request on Windows 10

The following error happens on Windows 10 when using a prepackaged Juicy Ace Editor npm dependency that has been transpiled and minified to ES5 already:

ERROR in ./node_modules/ace-builds/src-noconflict/ace.js?babel-target=es5
Module not found: Error: Encountered unexpected blind targeting request for C:\Users\user\Downloads\webpack-wc-demo-ace\webpack-wc-demo-ace\node_modules\babel-loader\lib\index.js??ref--4-0!C:\Users\user\Downloads\webpack-wc-demo-ace\webpack-wc-demo-ace\node_modules\webpack\buildin\harmony-module.js.Please see http://github.com/DanielSchaffer/webpack-babel-multi-target-plugin#blind-targeting for more information.
 @ ./node_modules/ace-builds/src-noconflict/ace.js?babel-target=es5 1:0-52
 @ ./src/juicy-ace-editor-npm.min.js?babel-target=es5
 @ ./src/hello-world.js?babel-target=es5
 @ ./src?babel-target=es5

This error does not happen on MacOS.

Minimally reproducible project.

Having another `babel-loader` in the project’s package.json results in silently broken targeting

While trying several different versions of webpack-babel-multi-target-plugin, I have noticed, that some of them result in broken targeting behaviour. Symptoms include:

  • the webpack build finishes successfully, no errors or warnings,
  • both modern and legacy bundles contain the same code,
  • Babel options do not apply, in particular, presetOptions: {debug: true} does not do anything useful.

I had babel-loader dependency in the project’s package.json. When a version mismatch happens, there were two babel-loader installations in the project node_modules/babel-loader and in the plugin node_modules/webpack-babel-multi-target-plugin/node_modules/babel-loader, which seems to be the cause of the issue.

Removing babel-loader from package.json helped. My webpack.config.js was not using babel-loader directly, so removing the dependency didn’t break anything.

Perhaps this is worth documenting somewhere. When someone upgrades a project already using babel-loader to webpack-babel-multi-target-plugin, the old babel-loader dependency could be forgotten in the package.json even though it is not used.

Upcoming Webpack5 compatibility

Hi @DanielSchaffer ,

with webpack5 at it's doorstep, I noticed that with webpack@next (v5.0.0-beta7) the following line

import MultiModuleFactory = require('webpack/lib/MultiModuleFactory')
throws:

⬢ webpack:  { Error: Cannot find module 'webpack/lib/MultiModuleFactory'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:581:15)
    at Function.Module._load (internal/modules/cjs/loader.js:507:25)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (node_modules/webpack-cli/node_modules/v8-compile-cache/v8-compile-cache.js:161:20)
    at Object.<anonymous> (node_modules/webpack-babel-multi-target-plugin/dist/src/babel.target.multi.entry.plugin.js:11:28)
    at Module._compile (node_modules/webpack-cli/node_modules/v8-compile-cache/v8-compile-cache.js:194:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3) code: 'MODULE_NOT_FOUND' }

It looks like v5 changed their /lib structure

Babel seems to have an issue with expose-loader files

When exposing JQuery in Webpack, The Plugin encounters a unexpected blind targeting request.

ERROR in ../node_modules/jquery/dist/jquery.js-exposed
Module not found: Error: Encountered unexpected blind targeting request for C:\Users\Sebas\IdeaProjects\rpanel\node_modules\babel-loader\lib\index.js??ref--4-0!C:\Users\Sebas\IdeaProjects\rpanel\node_modules\webpack\buildin\global.js.Please see http://github.com/DanielSchaffer/webpack-babel-multi-target-plugin#blind-targeting for more information.
 @ ../node_modules/jquery/dist/jquery.js-exposed 1:0-44
 @ ./custom.js?babel-target=es6
 @ ../target/frontend/generated-flow-imports.js?babel-target=es6

ERROR in ../node_modules/jquery/dist/jquery.js-exposed (../node_modules/expose-loader?$!../node_modules/jquery/dist/jquery.js-exposed)
Module not found: Error: Encountered unexpected blind targeting request for C:\Users\Sebas\IdeaProjects\rpanel\node_modules\babel-loader\lib\index.js??ref--4-0!C:\Users\Sebas\IdeaProjects\rpanel\node_modules\webpack\buildin\global.js.Please see http://github.com/DanielSchaffer/webpack-babel-multi-target-plugin#blind-targeting for more information.
 @ ../node_modules/jquery/dist/jquery.js-exposed (../node_modules/expose-loader?$!../node_modules/jquery/dist/jquery.js-exposed) 1:0-44
 @ ../node_modules/jquery/dist/jquery.js-exposed
 @ ./custom.js?babel-target=es6
 @ ../target/frontend/generated-flow-imports.js?babel-target=es6

ERROR in ../node_modules/jquery/dist/jquery.js?babel-target=es6-exposed (../node_modules/expose-loader?$!../node_modules/jquery/dist/jquery.js?babel-target=es6-exposed)
Module not found: Error: Encountered unexpected blind targeting request for C:\Users\Sebas\IdeaProjects\rpanel\node_modules\babel-loader\lib\index.js??ref--4-0!C:\Users\Sebas\IdeaProjects\rpanel\node_modules\webpack\buildin\global.js.Please see http://github.com/DanielSchaffer/webpack-babel-multi-target-plugin#blind-targeting for more information.
 @ ../node_modules/jquery/dist/jquery.js?babel-target=es6-exposed (../node_modules/expose-loader?$!../node_modules/jquery/dist/jquery.js?babel-target=es6-exposed) 1:0-44
 @ ../node_modules/jquery/dist/jquery.js?babel-target=es6-exposed
 @ ./horizontal/js/sidebarmenu.js?babel-target=es6
 @ ../target/frontend/generated-flow-imports.js?babel-target=es6

ERROR in ../node_modules/jquery/dist/jquery.js?babel-target=es5-exposed (../node_modules/expose-loader?$!../node_modules/jquery/dist/jquery.js?babel-target=es5-exposed)
Module not found: Error: Encountered unexpected blind targeting request for C:\Users\Sebas\IdeaProjects\rpanel\node_modules\babel-loader\lib\index.js??ref--4-0!C:\Users\Sebas\IdeaProjects\rpanel\node_modules\webpack\buildin\global.js.Please see http://github.com/DanielSchaffer/webpack-babel-multi-target-plugin#blind-targeting for more information.
 @ ../node_modules/jquery/dist/jquery.js?babel-target=es5-exposed (../node_modules/expose-loader?$!../node_modules/jquery/dist/jquery.js?babel-target=es5-exposed) 1:0-44
 @ ../node_modules/jquery/dist/jquery.js?babel-target=es5-exposed
 @ ./horizontal/js/sidebarmenu.js?babel-target=es5
 @ ../target/frontend/generated-flow-imports.js?babel-target=es5

Webpack part that causes the error:

        rules: [{
            test: require.resolve('jquery'),
            use: [{
                loader: 'expose-loader',
                options: 'jQuery'
            }, {
                loader: 'expose-loader',
                options: '$'
            }
            ]
        }]

TypeError: "exports" is read-only

After I added your awesome plugin to my project I have the error in browser, when I try to open my webapp:

Uncaught TypeError: "exports" is read-only
    <anonymous> http://localhost:5000/app.modern.05404073.js:2
    <anonymous> http://localhost:5000/app.modern.05404073.js:2
    n http://localhost:5000/app.modern.05404073.js:2
    <anonymous> http://localhost:5000/app.modern.05404073.js:2
    n http://localhost:5000/app.modern.05404073.js:2
    <anonymous> http://localhost:5000/app.modern.05404073.js:2
    n http://localhost:5000/app.modern.05404073.js:2
    <anonymous> http://localhost:5000/app.modern.05404073.js:2
    n http://localhost:5000/app.modern.05404073.js:2
    <anonymous> http://localhost:5000/app.modern.05404073.js:2
    <anonymous> http://localhost:5000/app.modern.05404073.js:2
app.modern.05404073.js:2:3531

Versions:

  • webpack: 4.44.2
  • webpack-babel-multi-target-plugin: 2.5.0

Webpack config:

let { BabelMultiTargetPlugin } = require('webpack-babel-multi-target-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin')
let CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
let HtmlWebpackPlugin = require('html-webpack-plugin')
let browserslist = require('browserslist')
let { join, resolve } = require('path')
let TerserPlugin = require('terser-webpack-plugin')

const IS_PRODUCTION = process.env.NODE_ENV === 'production'

const BABEL = {
  presets: [
    '@babel/preset-env',
    [
      '@babel/preset-typescript',
      {
        isTSX: true,
        allExtensions: true
      }
    ],
    [
      '@babel/preset-react',
      {
        runtime: 'automatic'
      }
    ]
  ]
}

let plugins = [
  new HtmlWebpackPlugin({ template: './src/index.html' })
]
let optimization = {}

if (IS_PRODUCTION) {
  let browsers = browserslist()
  let esmBrowsers = browserslist('supports es6-module')

  plugins.push(
    new MiniCssExtractPlugin({
      filename: '[name].[hash:8].css',
      ignoreOrder: true
    }),
    new BabelMultiTargetPlugin({
      babel: {
        presets: BABEL.presets.filter(i => i !== '@babel/preset-env')
      },
      targets: {
        modern: {
          browsers: browsers.filter(i => esmBrowsers.includes(i))
        },
        legacy: {
          browsers: browsers.filter(i => !esmBrowsers.includes(i))
        }
      }
    })
  )
  cssClasses = '[hash:base64:12]'
  optimization = {
    minimize: true,
    minimizer: [
      new TerserPlugin({ terserOptions: { format: { comments: false } } }),
      new CssMinimizerPlugin()
    ]
  }
}

module.exports = {
  mode: IS_PRODUCTION ? 'production' : 'development',
  devtool: IS_PRODUCTION ? false : 'eval-cheap-module-source-map',
  entry: {
    app: join(__dirname, 'src', 'index.tsx')
  },
  output: {
    publicPath: '/',
  },
  plugins,
  module: {
    rules: [
      {
        test: /\.(js|mjs|cjs|jsx)?$/,
        use: [IS_PRODUCTION ? BabelMultiTargetPlugin.loader() : 'babel-loader']
      },
      {
        test: /\.tsx?$/,
        use: [
          IS_PRODUCTION ? BabelMultiTargetPlugin.loader() : 'babel-loader'
        ]
      },
      {
        test: /\.pcss$/,
        use: [
          IS_PRODUCTION ? MiniCssExtractPlugin.loader : 'style-loader',
          'css-loader',
          'postcss-loader'
        ]
      }
    ]
  },
  optimization,
  resolve: {
    alias: { src: join(__dirname, 'src') }
  }
}

Error when using with html-webpack-include-assets-plugin

While using webpack-babel-multi-target-plugin, I also want webpack to append a script into index.html, which is not being compiled with webpack here, but copied from elsewhere. For that, I append html-webpack-include-assets-plugin in my webpack configuration like this:

module.exports = {
  /* ... */,
  plugins: [
    new BabelMultiTargetPlugin(/*...*/),
    new HtmlWebpackPlugin({template: 'index.html'}),
    new HtmlWebpackIncludeAssetsPlugin({
      assets: ['copied_script.js'],
      append: true
    })
  ]
};

I’ve got this error when running webpack:

ERROR in   TypeError: Cannot read property 'map' of undefined

  - babel.multi.target.html.updater.js:30 tags.forEach
    [project]/[webpack-babel-multi-target-plugin]/src/babel.multi.target.html.updater.js:30:44

Thrown from these lines in babel.multi.target.html.updater.js:

            const targetedChunks = chunkMap.get(tag.attributes.src);
            const targets = targetedChunks.map(targetedChunk => targetedChunk.target);
            if (!targets || !targets.length) {
                return;
            }

The reason is that html-webpack-include-assets-plugin appends a script tag for the copied_script.js, which is not emitted from any entrypoint, and therefore not found in the chunkMap, so the targetedChunks const is undefined for this case.

Deprecate use of configuration options to be removed in 4.0.0

  • deprecate options.babel.plugins - add @deprecated jsdoc and a console warning when using it, direct users to use options.babel.loaderOptions (added in #37)
  • deprecate options.babel.cacheDirectory - add @deprecated jsdoc and a console warning when using it, direct users to use options.babel.loaderOptions (added in #37)

preparation for 4.0.0 (see #21)

Angular: The AngularCompilerPlugin was not found

Hi, I'm trying to setup this plugin on my app.
It's an hybrid Angular 10 / AngularJS with a custom Webpack config.

I followed all the instructions but when I try to start the app using webpack-dev-server I have the following trace:

ERROR in Module build failed (from ./node_modules/@ngtools/webpack/src/index.js):
Error: The AngularCompilerPlugin was not found. The @ngtools/webpack loader requires the plugin.
    at Object.ngcLoader (/Users/wmarques/workspace/artel/telematics-services/telematics-fo-web/node_modules/@ngtools/webpack/src/loader.js:28:15)

Here is a snippet of my configuration:

  resolve: {
    extensions: ['.ts', '.js'],
    alias: {
      '../assets': resolve('src/assets'),
      // Use the same moment package for each lib (remove duplicates)
      moment: resolve('node_modules/moment')
    }
  },

  module: {
    rules: [
      {
        test: /\.([tj])s$/,
        use: [BabelMultiTargetPlugin.loader(), '@ngtools/webpack']
      },
      {
        test: /\.html$/,
        loader: 'html-loader',
        options: {
          minimize: false
        },
        exclude: /index.html/
      },
      {
        test: /\.yml/,
        loader: 'yaml-import-loader'
      },
      {
        test: /\.(png|jpg|gif|ico|ttf|otf|eot|svg|woff(2)?)$/,
        loader: 'file-loader',
        options: {
          esModule: false
        }
      },
      {
        test: /\.(scss|css)$/,
        use: [
          'to-string-loader',
          'css-loader',
          'sass-loader',
          {
            loader: 'sass-resources-loader',
            options: {
              // Or array of paths
              resources: [
                resolve('node_modules/@artel/commons/variables.scss'),
                resolve('src/sass/bourbon/dist/settings/*.scss'),
                resolve('src/sass/bourbon/dist/functions/*.scss'),
                resolve('src/sass/bourbon/dist/addons/_font-family.scss'),
                resolve('src/sass/auto-generated/_style-vars.scss'),
                resolve('src/sass/base/_variables.scss')
              ]
            }
          }
        ],
        exclude: /(styles|vendors)\.scss/
      },
      { test: /[/\\]@angular[/\\].+\.js$/, parser: { system: true } }
    ]
  },

  plugins: [
    new BabelMultiTargetPlugin({
      babel: {
        presetOptions: {
          modules: false,
          useBuiltIns: 'entry',
          corejs: 3
        },
        plugins: [
          'angularjs-annotate',
          '@babel/plugin-proposal-object-rest-spread'
        ]
      }
    }),
    new AngularCompilerPlugin({
      tsConfigPath: resolve('tsconfig.app.json'),
      mainPath: resolve('src/app/main.ts')
    }),

Thanks for your help 😄

Ability to disable babel cache

Babel's cacheDirectory option is hardcoded to a non-undefined value at babel.target.ts:createTransformOptions(...), which can cause some pain when using debug: true to debug babel's plugins, etc.

It would be nice if that were in some way configurable.

fork-ts-checker emits error

bug reproduction repo

when use this plugin and fork-ts-checker together, fork-ts-checker will complain about

ERROR in /home/chla/Code/template/node_modules/webpack-babel-multi-target-plugin/dist/src/defaults.d.ts(1,36):
TS7016: Could not find a declaration file for module 'babel-loader'. '/home/chla/Code/template/node_modules/babel-loader/lib/index.js' implicitly has an 'any' type.
  Try `npm install @types/babel-loader` if it exists or add a new declaration (.d.ts) file containing `declare module 'babel-loader';`

which should be resolved by add your custom babel-loader typing to build result

Aurelia: A typeError 'Cannot read property 'options' of undefined' in babel-target.js:92:20

Hi!

I don't know if this is the right place and this the right way to ask for help, but...

I tried to set up this plugin and have errors that seem to have to do something with the Babel version I'm using, but I'm unable to sort it out nor ascertain myself it's not something in my setup. The errors look like as follows

ERROR in TypeError: Cannot read property 'options' of undefined

  • babel-target.js:92 Function.getTargetFromModule
    [MultioutputWebpack]/[webpack-babel-multi-target-plugin]/dist/src/babel-target.js:92:20

  • babel-target.js:112 Function.getTargetFromEntrypoint
    [MultioutputWebpack]/[webpack-babel-multi-target-plugin]/dist/src/babel-target.js:112:28

  • babel-target.js:129 Function.findTarget
    [MultioutputWebpack]/[webpack-babel-multi-target-plugin]/dist/src/babel-target.js:129:32

  • targeted.chunk.js:7 TargetedChunk.get target [as target]
    [MultioutputWebpack]/[webpack-babel-multi-target-plugin]/dist/src/targeted.chunk.js:7:55

The project is located at https://github.com/veikkoeeva/MultioutputWebpack and should build with au build (and run with au run).

Maybe, if nothing else, this serves as a food for thoughts for the next version or help someone else too.

Add Safari 10.1 nomodule fix

First of all, thanks for your work! I have implemented the build using BabelMultiTargetPlugin and received a feedback about the code broken in Safari 10.1 and iOS 10.3.

The issue is that Safari 10.1+ does not support nomodule attribute (fixed in 11). It is a well known issue mentioned e.g. in blog post by Jake Archibald. So, the code is getting executed twice

This issue especially breaks Custom Elements registration: 1) they cannot be redeclared, and 2) they use ES2015 classes, and when transpiled to ES5, they need a special script, otherwise the error TypeError: Constructor requires 'new' operator will be thrown.

Known workaround: https://gist.github.com/samthor/64b114e4a4f539915a95b91ffd340acc

Here is how it is injected by ModernModePlugin used in Vue CLI.

Please consider adding an option to include this fix, e.g. based on presence of Safari 10.1 and iOS 10.1 - 10.3 in the list of targeted browsers. From my experience, some users still support Safari 10.x.

Original issue reported here: web-padawan/polymer3-webpack-starter@fb82b28#r31858089
I confirmed the issue myself using Simulator: works in iOS 9 (ES5) and broken in 10.3.

presets for babel

Since this plugin requires us to stop using babelrc (and I assume babel.config.js), how can we list additionnal presets to be used (such as @babel/typescript, @babel/preset-react ?

Thanks !

Support Angular CLI-based projects

  • Angular has its own differential loading solution that uses a different approach
  • It does not use HtmlWebpackPlugin, but has its own "index" page generating logic
  • Angular's diff loading can be disabled by setting browserslist to >5% and setting the tsconfig target to es6
  • HtmlWebpackPlugin can be used with the Angular CLI by pointing its "index" configuration to an empty HTML file (e.g. index-placeholder.html), and then adding HtmlWebpackPlugin using a custom webpack config
  • Is it possible to use a plugin to accomplish these workarounds? Could make sense as a separate project
  • Is it possible to hook in to Angular's index page generator the same way this plugin does with HtmlWebpackPlugin?

'babel-loader' cannot be found when using npm 7.20.3+

With a simple package.json: https://github.com/Artur-/webpack-babel-test/blob/main/package.json

npm install with npm 7.20.2 and earlier will install babel-loader as a top level module in node_modules
npm install with npm 7.30.2 and newer will install babel-loader as a webpack-babel-multi-target-plugin dependency in node_modules/webpack-babel-multi-target-plugin/node_modules

Because of this, a simple webpack configuration like https://github.com/Artur-/webpack-babel-test/blob/main/webpack.config.js will work fine with npm < 7.20.3 and fail with npm >= 7.20.3:

ERROR in Entry module not found: Error: Can't resolve 'babel-loader' in '/.../webpack-babel-test'

To reproduce the problem:

git clone https://github.com/Artur-/webpack-babel-test.git
cd webpack-babel-test
npx [email protected] install
node_modules/.bin/webpack

Works fine if you change the npm command to

npx [email protected] install

Imports with expression cause infinite loop when called inside a dynamic import

With Webpack you can define an import with any expression in the name e.g.

import(`./plugins/${pluginName}/plugin.js`);

image

I've run into an issue with this plugin (via the configuration from open-wc/building-webpack), where I find that imports like the one above do not work if they are themselves defined inside of a file which is imported dynamically.

I receive the following error:

(node:18564) UnhandledPromiseRejectionWarning: RangeError: Maximum call stack size exceeded
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:105:23)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)
    at NormalizeCssChunksPlugin.getEntryFromModule (C:\EWB\Code\dynamic_bug\node_modules\webpack-babel-multi-target-plugin\dist\src\normalize.css.chunks.plugin.js:110:25)

I've tried to debug the issue in your code but don't understand webpack plugins enough.

I have however created a simple case which reproduces the problem:
https://gist.github.com/ggriffithsIDBS/f61f475831f4beef02e1d58f5f960e2d
or see zip
dynamic_bug.zip

Please let me know if you need any more information, i'll let the people whose config i've been using know i've run into this issue

htmlWebpackPluginAlterAssetTags is undefined

I can't figure out what's wrong, but I have an error:

htmlWebpackPluginAlterAssetTags is undefined

here:

compiler.hooks.compilation.tap(plugin_name_1.PLUGIN_NAME, (compilation) => {
    if (compilation.name) {
        return;
    }
    compilation.hooks.htmlWebpackPluginAlterAssetTags.tapPromise(`${plugin_name_1.PLUGIN_NAME} add safari nomodule fix tags`, (htmlPluginData) => __awaiter(this, void 0, void 0, function* () {
        const element = this.inject === babel_multi_target_options_1.SafariNoModuleFixInject.body ? htmlPluginData.body : htmlPluginData.head;
        element.unshift(this.createSafariNoModuleFixTag());
        return htmlPluginData;
    }));
});

Trace:

TypeError: Cannot read property 'tapPromise' of undefined
    at C:\Repo\org\test\node_modules\@mntm\scripts\node_modules\webpack-babel-multi-target-plugin\dist\src\safari-nomodule-fix\safari.nomodule.fix.plugin.js:107:63
    at SyncHook.eval [as call] (eval at create (C:\Repo\org\test\node_modules\@mntm\scripts\node_modules\tapable\lib\HookCodeFactory.js:19:10), <anonymous>:99:1)

discussion: plugin options revamp

The flurry of activity on this project over the last couple weeks has made me realize that the current options structure isn't terribly forgiving with regards to providing the kind of flexibility that is required in customizing options for Babel. As part of the 3.0.0 release, I'd like to change the format of the options to make this a little more straightforward and manageable. Here are the goals for this change:

  • allow complete "pass-through" of settings for babel-loader and presets
  • allow separate definitions of above for global/shared and per-target settings
  • properly merge default, shared, and per-target settings (currently there is a weird combination of manual object construction and Object.assign)
  • ensure that settings for Babel and settings specific to the plugin are clearly separated

Fortunately, I don't think this requires a huge number of breaking changes. Here are my proposed changes:

  • options.babel.loaderOptions will be added to provide shared/global babel-loader options
  • BREAKING: plugins and cacheDirectory will be removed from the options.babel object in favor of using the corresponding properties in options.babel.loaderOptions
  • options.babel.presetOptions will remain as a shortcut to customize @babel/preset-env, and the modules key will continue to be forced to false
  • options.targets[key].babel will be added to provide babel-loader options on a per-target basis
  • options.targets[key].browsers will remain as a shortcut to customizing the @babel/preset-env's targets.browsers option
  • Babel preset and plugin ordering: Since ordering of both presets and plugins matters, wherever presets and plugins are defined, the most specific array will be used. This means that a target-specific configuration could remove plugins or presets defined as defaults or at the global level. Options defined for presets will be merged by preset name (so for example, options for @babel/preset-env at the global level will be merged with those set in the presets array at the target level, regardless of the order of the respective presets arrays.
  • Upcoming breaking changes will be deprecated in v2.1.0, and console warnings will be added to notify users on usage

@rangermeier, @platosha, @daKmoR, @web-padawan, @adamdoyle, @LarsDenBakker - I'd love to hear your input on this.

Vue scoped css only work in nomodule

Steps to reproduce:
Add following code to vue-cssextract example

<style scoped>
.foobar {
  font-family: mono
}
</style>

and add foobar class to some html element
reason of bug:
the above css is compiled to .foobar[data-v-xxxxxx], however for different target vue generate different hashed id, so only in legacy mode would scoped css work

Request for evaluation/feedback

hey,
we are thinking about using your plugin as part of our open-wc recommended bundling configuration for webpack.

We are focusing on webcomponents and how to facilitate them. This still requires quite some polyfills.
So having different builds is absolutely vital.

I think we have a pretty good starting point but we are still struggling with the bundle size.

In the spirit of open source I was wondering if you could take a look do a review and give us some hints? Maybe we are running into #11 and it seems there is a workaround? care to share?

This is the related pull request: open-wc/open-wc#122

This is how you could check it out

git clone [email protected]:open-wc/open-wc.git
cd open-wc
git checkout feat/webpack
cd packages/building-webpack
npm i
npm run build
npx http-server ./dist/

If you open that page in the latest chrome the download is still >50KB even though it's a tiny tiny tiny demo page. I think we should be able to make it smaller - and yes that is our work as we need to find a way to handle all these polyfills with potentially only 2 versions? (as there is only one nomodule trick)?

or do you know any more?

I'm looking forward to your feedback :)

moment.js locale not loaded

Steps to reproduce:
Add following code to typescript-moment example

moment.locale('fr')
console.log(moment.locales())

expected:
console should print ["en", "fr"]
current:
only ["en"] is printed. Thus formating to certain locale doesn't work

support html-webpack-plugin@4

(node:13342) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'tapPromise' of undefined
    at compiler.hooks.compilation.tap (node_modules/webpack-babel-multi-target-plugin/src/babel.multi.target.html.updater.ts:112:59)

BlindTargetErrors when building @tanker and subscriptions-transport-ws

Hello !

First of all thanks a lot for your efforts on this particular matter that is differential loading,
the ecosystem shall be thankful !

I'm trying to convert a webpack configuration from a project using react / Apollo and a bunch of other tools to use this plugin. I've been in luck so far but just came across an issue that I do not know how to solve.

plugin revision: 2.5.0-next.2

webpack configuration:

// The base webpack config
const config = {
  mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
  entry: path.resolve(__dirname, '../src/index.js'),
  output: {
    path: path.resolve(__dirname, '../dist/'),
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [BabelMultiTargetPlugin.loader(), 'eslint-loader'],
      },
    ],
  },
  resolve: {
    mainFields: ['es2015', 'module', 'main'],
  },
  plugins: [
    new BabelMultiTargetPlugin({
      doNotTarget: [
        /node_modules\/@tanker/,
        /node_modules\/subscriptions-transport-ws/,
      ],
      babel: {
        presets: ['@babel/preset-react'],
      },
    }),
  ],
  node: {
    fs: 'empty',
  },
};

Error output (stripped warnings for readability but there's a lot of Can't resolve 'xxx'):

ERROR in ./node_modules/subscriptions-transport-ws/node_modules/ws/lib/websocket.js
Module not found: Error: Can't resolve 'net' in '/Users/qpre/connected_physics/platform-web-stack/app/client-web/node_modules/subscriptions-transport-ws/node_modules/ws/lib'
 @ ./node_modules/subscriptions-transport-ws/node_modules/ws/lib/websocket.js 7:12-26
 @ ./node_modules/subscriptions-transport-ws/node_modules/ws/index.js
 @ ./node_modules/subscriptions-transport-ws/dist/server.js
 @ ./node_modules/subscriptions-transport-ws/dist?babel-target=legacy
 @ ./node_modules/apollo-link-ws/lib/bundle.esm.js?babel-target=legacy
 @ ./src/services/apollo-graphql.js?babel-target=legacy
 @ ./src/pages/_app/App.js?babel-target=legacy
 @ ./src?babel-target=legacy

ERROR in ./node_modules/subscriptions-transport-ws/node_modules/ws/lib/websocket.js
Module not found: Error: Can't resolve 'tls' in '/Users/qpre/connected_physics/platform-web-stack/app/client-web/node_modules/subscriptions-transport-ws/node_modules/ws/lib'
 @ ./node_modules/subscriptions-transport-ws/node_modules/ws/lib/websocket.js 8:12-26
 @ ./node_modules/subscriptions-transport-ws/node_modules/ws/index.js
 @ ./node_modules/subscriptions-transport-ws/dist/server.js
 @ ./node_modules/subscriptions-transport-ws/dist?babel-target=legacy
 @ ./node_modules/apollo-link-ws/lib/bundle.esm.js?babel-target=legacy
 @ ./src/services/apollo-graphql.js?babel-target=legacy
 @ ./src/pages/_app/App.js?babel-target=legacy
 @ ./src?babel-target=legacy

ERROR in ./node_modules/subscriptions-transport-ws/node_modules/ws/lib/websocket.js
Module not found: Error: Encountered unexpected blind targeting request for /Users/qpre/connected_physics/platform-web-stack/app/client-web/node_modules/babel-loader/lib/index.js??ref--4-0!/Users/qpre/connected_physics/platform-web-stack/app/client-web/node_modules/eslint-loader/dist/cjs.js!/Users/qpre/connected_physics/platform-web-stack/app/client-web/node_modules/crypto-browserify/index.js.Please see http://github.com/DanielSchaffer/webpack-babel-multi-target-plugin#blind-targeting for more information.
 @ ./node_modules/subscriptions-transport-ws/node_modules/ws/lib/websocket.js 4:15-32
 @ ./node_modules/subscriptions-transport-ws/node_modules/ws/index.js
 @ ./node_modules/subscriptions-transport-ws/dist/server.js
 @ ./node_modules/subscriptions-transport-ws/dist?babel-target=legacy
 @ ./node_modules/apollo-link-ws/lib/bundle.esm.js?babel-target=legacy
 @ ./src/services/apollo-graphql.js?babel-target=legacy
 @ ./src/pages/_app/App.js?babel-target=legacy
 @ ./src?babel-target=legacy

How can I move forward from this point of how could I help solving this ?

Thanks !

Unable to import 'crypto' when plugin is used

Trying to use

const c = require("crypto");

when the plugin is included in a webpack build results in

ERROR in .?babel-target=modern
Module not found: TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type boolean
 @ .?babel-target=modern 1:10-27

ERROR in .?babel-target=legacy
Module not found: TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type boolean
 @ .?babel-target=legacy 1:10-27

To reproduce:

npm init -y
npm i --save-dev webpack webpack-cli webpack-babel-multi-target-plugin
echo 'const c = require("crypto");console.log(c);' > index.js
cat > webpack.config.js << EOF
const { BabelMultiTargetPlugin } = require("webpack-babel-multi-target-plugin");

module.exports = {
  mode: "production",
  entry: {
    bundle: "./index.js"
  },

  plugins: [
    new BabelMultiTargetPlugin({})
  ]
};
EOF
node_modules/.bin/webpack

If you remove the BabelMultiTargetPlugin, webpack finishes successfully

Runtime error when using webpack-dev-server

When running my project with webpack-dev-server I get the following runtime error:

TypeError: Cannot assign to read only property 'exports' of object '#<Object>'
    at Module.<anonymous> (/Users/matt/temp/test/node_modules/querystring-es3/encode.js:40:1)
    at Module../node_modules/querystring-es3/encode.js?babel-target=modern (http://localhost:8080/main.modern.js:3590:30)
    at __webpack_require__ (http://localhost:8080/main.modern.js:20:30)
    at Object../node_modules/querystring-es3/index.js (/Users/matt/temp/test/node_modules/querystring-es3/index.js:4:38)
    at __webpack_require__ (http://localhost:8080/main.modern.js:20:30)
    at Object../node_modules/url/url.js (/Users/matt/temp/test/node_modules/url/url.js:100:19)
    at __webpack_require__ (http://localhost:8080/main.modern.js:20:30)
    at Object../node_modules/webpack-dev-server/client/utils/createSocketUrl.js (http://localhost:8080/main.modern.js:10747:11)
    at __webpack_require__ (http://localhost:8080/main.modern.js:20:30)
    at Object.<anonymous> (http://localhost:8080/main.modern.js:10380:23)

This can be resolved by adding:

doNotTarget: [ /querystring-es3/ ]

or

babel.presetOptions.useBuiltIns: false

I'm using:

"webpack": "^4.41.5"
"webpack-babel-multi-target-plugin": "^2.3.3"
"webpack-dev-server": "^3.10.2"

I was surprised that I wasn't able to find anybody mentioning this same exact issue here or elsewhere online (maybe my google-foo is just bad). There must be many people using this plugin with webpack-dev-server which makes me think there is maybe something odd with my config.

If my config is okay then maybe I am just the first to report the issue. In which case maybe the auto filtering needs to be improved to catch querystring-es3.

My full config:

package.json

{
  "name": "test-app",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "webpack-dev-server --env development",
    "build": "webpack --env production"
  },
  "devDependencies": {
    "@webcomponents/webcomponentsjs": "^2.4.1",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.4.2",
    "ejs-loader": "^0.3.5",
    "html-loader": "^0.5.5",
    "html-webpack-deploy-plugin": "^2.0.6",
    "html-webpack-plugin": "^3.2.0",
    "to-string-loader": "^1.1.6",
    "webpack": "^4.41.5",
    "webpack-babel-multi-target-plugin": "^2.3.3",
    "webpack-cli": "^3.3.10",
    "webpack-dev-server": "^3.10.2",
    "webpack-merge": "^4.2.2"
  }
}

webpack.config.js

const path = require('path');
const merge = require('webpack-merge');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackDeployPlugin = require('html-webpack-deploy-plugin');
const BabelMultiTargetPlugin = require('webpack-babel-multi-target-plugin').BabelMultiTargetPlugin;
const package = require('./package.json');

const ENV = process.argv.find(arg => arg.includes('production'))
  ? 'production'
  : 'development';
const OUTPUT_PATH = path.resolve(__dirname, 'dist');

const webcomponentsjs = [
  {
    from: 'webcomponents-loader.js',
    to: 'webcomponents-loader.js'
  },
  {
    from: 'bundles/*.js',
    to: 'bundles',
    flatten: true
  }
];

const commonConfig = {
  entry: './src/index.js',
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackDeployPlugin({
      packages: {
        '@webcomponents/webcomponentsjs': {
          copy: ENV === 'development' ? webcomponentsjs : [],
          scripts: {
            useCdn: ENV === 'production',
            path: 'webcomponents-loader.js',
            append: false
          },
        }
      },
      getCdnPath: (packageName, packageVersion, packagePath) => `https://unpkg.com/${packageName}@${packageVersion}/${packagePath}`
    }),
    new BabelMultiTargetPlugin({
      babel: {
        presetOptions: {
          // Don’t add polyfills, they are provided from webcomponents-loader.js
          useBuiltIns: false
        }
      }
    })
  ],
  output: {
    path: OUTPUT_PATH
  },
  resolve: {
    mainFields: [
      'es2015',
      'module',
      'main'
    ]
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          BabelMultiTargetPlugin.loader()
        ]
      },
      {
        test: /\.css$/,
        use: [
          'to-string-loader',
          'css-loader'
        ]
      },
      {
        test: /\.html$/i,
        loader: 'html-loader'
      },
      {
        test: /\.ejs$/,
        use: [
          'ejs-loader'
        ]
      }
    ]
  }
};

const developmentConfig = {
  output: {
    filename: '[name].js'
  },
  devtool: 'inline-source-map',
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/nly-dev-wrapper.ejs',
      templateParameters: {
        appSelector: package.name
      }
    })
  ],
  devServer: {
    contentBase: './dist',
    host: '0.0.0.0',
    disableHostCheck: true
  }
};

const productionConfig = {
  output: {
    filename: '[name].[chunkhash:8].js'
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.ejs',
      templateParameters: {
        appSelector: package.name
      },
      minify: {
        collapseWhitespace: true,
        removeComments: true,
        minifyCSS: true,
        minifyJS: true
      }
    })
  ]
};

module.exports = () => {
  if (ENV === 'production') {
    return merge(productionConfig, commonConfig, { mode: ENV });
  }
  return merge(developmentConfig, commonConfig, { mode: ENV });
};

Update:

So I don't think it's anything funky with my config. I was able to reproduce the issue in this minimal setup:

https://codesandbox.io/s/webpack-babel-multi-target-pluginwebpack-dev-servertest-vq79w

You need to open the server output in its own browser tab/window in order to see the error in the console for some reason. A bug with the embedded browser window I guess.

v2.5.0

@Austaras @despian @semoal @erezvish

Hey folks!

My apologies (again, as ever) for the delay in following up . You all have PRs or otherwise interest in the upcoming 2.5.0 release. I've just published a new prerelease, v2.5.0-next.2, which includes the following PRs:

  • #55 - support htmlwebpack v4
  • #59 - add preset configuration to babel options
  • #62 - add exclude for 'querystring-es3'
  • #63 - add inject (head|body) and minify options to safari10NoModuleFix

Please take a few moments to make sure everything is working as intended, and once I hear back from everyone I'll do the final release.

Thanks again for all of your contributions, I truly appreciate the help!

TypeError: Cannot read property 'descriptionFileRoot' of undefined

Hi! Thank you for the great plugin letting us make (IT, world) more progressive! But. I came across the next problems:

  1. ERROR in .?babel-target=legacy Module not found: TypeError: Cannot read property 'descriptionFileRoot' of undefined @ .?babel-target=legacy 8:14-39

Reproducable repo: https://github.com/shurygindv/reproducable-babel-multi-target

  1. I can't find the bottleneck of this problem, but I have only such message (uses awesome-ts-loader, without options,tsconfig has target esnext)

ERROR in ./src/components/A.tsx?babel-target=legacy Module not found: TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type boolean

p.s I'm sure module presents there and it pretty works without plugin

Need example on how to exclude a polyfill from the modern bundle

Some DOM APIs are present in all browsers that support ES modules, while could be absent in others, e. g., URLSearchParams and fetch. When I use polyfills for such APIs in my app, I want to exclude them from the modern bundle to save on the size.

Babel env does not help, because Babel does not care about the DOM APIs, it only polyfills the JS features themselves.

From README I cannot figure out, what could be done for this use case, if anything at all. The target-specific options seem not to mention anything related.

As a workaround, I add a separate entrypoint for polyfills.js in webpack, skip it in the HTML plugin and add only the legacy polyfills.js right in the HTML template file instead. This approach works, but could be improved in some ways:

  • I have the polyfills.modern.js file built, but never used
  • I wish I could include the polyfills in the main legacy bundle itself, rather than having the separate bundle.

[2.5.0-next.2] ContextReplacementPlugin apparently ignored

Hello!

I'm trying to use the plugin on our app, and everything seems to be working, except for our exclusion of dynamic imported locale files.

For that, we were using ContextReplacementPlugin to only include specific local files from intl-related dependencies. But that plugin does not seem to be compatible with BabelMultiTargetPlugin, as all locale files are now outputted as chunks.

If that's not supposed to happen, do you have any insight at what I might be missing here?

new webpack.ContextReplacementPlugin(/intl\/locale-data\/jsonp$/, /(fr-FR|en-US).js$/),
await import(/* webpackChunkName: "intl/[request]" */ `intl/locale-data/jsonp/${intlLocale}.js`);

Using array entry breaks module execution order

When using an array entry point:

{
  entry: [
    './src/one.js',
    './src/two.js',
    './src/three.js',
    './src/four.js',
  ]
}

Modules are executed in alphabetical order instead of the order of inclusion.

Switch to core-js 3.x not mentioned in 2.3.1 release changelog

Hi,

The change to bump core-js 3.x in 2.3.1 is kind of a breaking change for me due to the fact that one has to exclude the core-js node module. see zloirock/core-js#514

In my case I have core-js pinned down to 2.x, which will cause the following error

ERROR in ./core/frontend/test.js?babel-target=legacy
Module not found: Error: Can't resolve 'core-js/modules/es.array.iterator?babel-target=legacy' 
 @ ./core/frontend/test.js?babel-target=legacy 5:0-44

maybe you should bump a major or mention the dependency to core-js 3.x

ignores externals

If you have any modules defined in webpack.config as externals, for some reason the inclusion of this plugin is causing webpack to try bundle them. I tried adding the same references in my externals to both the exclude array and also the doNotTarget array to see if they would be ignored, but webpack is dying on trying to import these even though they are defined as externals.

If i remove this plugin, webpack behave as expected so i feel the issue exists internal here.

[Discussion] Any thoughts about generates the true es6 ( aka not flattened ) modules?

Great project! But I'm thinking further: What about generates unflattened modules?

  1. Parse the whole repo recursively, to build an import-map. Or transform import Xxx from 'some_module' into import Xxx from '/node_modules/some_module/index.js'.
  2. All files imported are still transformed using loaders specified by webpack configuration rules, with optional .js extension suffix. For example a.scss can be compiled into file a.scss.js, with its content
    document.head.insertAdjacentHTML("beforeEnd", `<style>Generated CSS text</style>`);
  3. Commonjs require is compiled into synced XHR
// Pseudo code
function require(id) {
  const moduleId = getAbsolutePathSomehow(id);

  if (installedModules[moduleId]) {
    return resolvedModules[moduleId].exports;
  }

  const req = new XMLHttpRequest();
  req.open("GET", moduleId, false);
  req.send();
  const module = require.installedModules[moduleId] = {
    id: moduleId,
    loaded: false,
    exports: {},
  };
  new Function('module', 'exports', 'use strict;\n' + req.responseText)
    .call(module.exports, module, module.exports);
  module.loaded = true;
  return module.exports;
}
require.installedModules = {};

globalThis.require = require;

Another option is compiling commonjs module into ES6 default-export module.

Related issue: webpack/webpack#8010 (comment)

Error normalizing CSS files

When running a build I get the following error:

75% basic chunk optimization BabelMultiTargetPlugin(node:11621) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'reasons' of undefined
  at chunk.modulesIterable.forEach.module (/home/ranger/some-project/node_modules/webpack-babel-multi-target-plugin/src/normalize.css.chunks.plugin.js:46:49)
  at Set.forEach (<anonymous>:null:null)
  at chunks.forEach.chunk (/home/ranger/some-project/node_modules/webpack-babel-multi-target-plugin/src/normalize.css.chunks.plugin.js:41:35)
  at Array.forEach (<anonymous>:null:null)
  at NormalizeCssChunksPlugin.extractCssChunks (/home/ranger/some-project/node_modules/webpack-babel-multi-target-plugin/src/normalize.css.chunks.plugin.js:26:16)
  at SyncBailHook.eval (<anonymous>:12:16)
  at SyncBailHook.lazyCompileHook [as _call] (/home/ranger/some-project/node_modules/tapable/lib/Hook.js:35:21)
  at Compilation.seal (/home/ranger/some-project/node_modules/webpack/lib/Compilation.js:1205:35)
  at hooks.make.callAsync.err (/home/ranger/some-project/node_modules/webpack/lib/Compiler.js:547:17)
  at _done (<anonymous>:9:1)
  at _fn4.then._result4 (<anonymous>:64:22)
  at <anonymous>:null:null
  at process._tickDomainCallback (internal/process/next_tick.js:229:7)

(node:11621) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 3)
(node:11621) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Cannot find module 'webpack-dev-server/client/index'

Doesnt seem to work if you dont have webpack-dev-server installed. Dies on Cannot find module 'webpack-dev-server/client/index'

Also docs/example feel incomplete. Example code in README shows pluginFactory but that is never referenced. Would it be possible to update this with a full working example config?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.