Skip to content

Instantly share code, notes, and snippets.

@developit
Last active January 5, 2021 18:40
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save developit/80a6926679ac33570f66ca8184a249d2 to your computer and use it in GitHub Desktop.
Save developit/80a6926679ac33570f66ca8184a249d2 to your computer and use it in GitHub Desktop.

webpack-plugin-modern-npm

Automatically transpile modern packages in node_modules.

Available in 3 fun flavours: plugin, loader and loader factory.

Plugin

Add the plugin to your webpack config, and it should handle everything for you.

You can customize the syntax target in your Babel configuration.

const ModernNpmPlugin = require('webpack-plugin-modern-npm');

module.exports = {
  plugins: [
    new ModernNpmPlugin()
  ]
};

Loader

The loader works just like the plugin and accepts the same options, it's just defined inline.

You can customize the syntax target in your Babel configuration.

const modernNpmLoader = require('webpack-plugin-modern-npm/loader');

module.exports = {
  module: {
    rules: [
      // auto-transpile modern stuff found in node_modules:
      'webpack-plugin-modern-npm/loader',
      
      // keep your current babel-loader for your own code:
      {
        loader: 'babel-loader',
        exclude: /node_modules/,
        test: /\.m?jsx?$/i
      }
    ]
  }
};

Loader Factory (custom)

Note: In general, direct usage of this factory should be unnecessary and is not recommended.

See anyway
const autoBabelLoader = require('webpack-plugin-modern-npm/auto-babel-loader');

module.exports = {
  module: {
    rules: [
      // auto-transpile modern stuff found in node_modules:
      autoBabelLoader({
        env: 'nodemodules' // if you want to custimize
      }),
      
      // keep your current babel-loader for your own code:
      {
        loader: 'babel-loader',
        exclude: /node_modules/,
        test: /\.m?jsx?$/i
      }
    ]
  }
};

Customizing the Syntax Target

The syntax target (which version of JavaScript, or which browsers to support) for @babel/preset-env within node_modules can be customized using "env" overrides in your existing Babel configuration file (.babelrc or babel.config.js):

{
  "env": {
    "npm": {
      // these are the defaults:
      "presets": [
        ["@babel/preset-env", {
          "loose": true,
          "bugfixes": true,
          "useBuiltIns": false
        }]
      ]
    }
  }
}

The "env" name defaults to "npm" and can be changed by passing the {env:"foo"} option to either the plugin or loader.

const path = require('path');
const MODULE_DIR = /(.*([\/\\]node_modules|\.\.)[\/\\](@[^\/\\]+[\/\\])?[^\/\\]+)([\/\\].*)?$/g;
function filter() {
return function include(filepath) {
if (filepath.split(/[/\\]/).indexOf('node_modules')===-1) return true;
let pkg, manifest = path.resolve(filepath.replace(MODULE_DIR, '$1'), 'package.json');
try { pkg = require(manifest); } catch (e) {}
return !!(pkg.exports || pkg.modern);
};
}
/**
* @param {object} [options]
* @param {string|true} [options.env = 'npm'] Babel configuration envName - see https://babeljs.io/docs/en/options#envname
*/
module.exports = function autoBabelLoader(options) {
options = options || {};
const customEnv = options.env === true ? 'npm' : options.env;
return {
loader: 'babel-loader',
test: /\.[mc]?js$/i,
include: filter(),
options: {
cacheDirectory: true,
cacheCompression: false,
//configFile: false,
//babelrc: false,
generatorOpts: {
compact: true
},
envName: customEnv || 'npm',
caller: {
autobabel: true
},
presets: [[
'@babel/preset-env',
{
targets: { esmodules: true },
loose: true,
bugfixes: true,
useBuiltIns: false
}
]]
}
};
};
module.exports._filter = filter;
const autoBabelLoader = require('./auto-babel-loader');
const NAME = 'webpack-plugin-modern-npm';
module.exports = class ModernNpmPlugin {
constructor(options) {
this.options = options || {};
this.loader = autoBabelLoader(this.options);
}
apply(compiler) {
compiler.hooks.normalModuleFactory.tap(NAME, factory => {
factory.hooks.afterResolve.tapAsync(NAME, (data, callback) => {
if (this.loader.include(data.resourcePath)) {
data.loaders.unshift(this.loader);
}
callback(null, data);
});
});
}
};
const autoBabelLoader = require('./auto-babel-loader');
exports.pitch = function(remainingRequest, precedingRequest, data) {
if (autoBabelLoader._filter(this.resourcePath)) {
let opts;
if (this.getOptions) opts = this.getOptions();
else if (this.query && typeof this.query === 'object') opts = this.query;
else {
try { opts = require('loader-utils').getOptions(this); }
catch (e) { console.error('webpack-plugin-modern-npm/loader: failed to parse loader options.\n', e); }
}
this.loaders.unshift(autoBabelLoader(opts));
}
};
{
"name": "webpack-plugin-modern-npm",
"main": "index.js",
"version": "0.1.0",
"license": "Apache-2.0",
"homepage": "https://gist.github.com/developit/80a6926679ac33570f66ca8184a249d2",
"repository": "gist:80a6926679a",
"scripts": {
"prepack": "mv '*webpack-plugin-modern-npm.md' README.md",
"postpack": "mv README.md '*webpack-plugin-modern-npm.md'"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment