An esbuild plugin to help you bundle commonjs external modules.
This plugin is used to address evanw/esbuild#1467, where you want to
bundle some commonjs external modules in es modules context. But accidentally
you see a __require
in your code prints error at runtime and forbids
other bundlers from analyzing the dependencies. For example:
// some commonjs library, like react-dom
var React = require('react')
// your esm code
export { render } from 'react-dom'
// after esbuild --bundle
var React = __require('react') // <- you dislike this
// ...
export { render }
// with this plugin
import __import_react from 'react' // <- you want this
var React = __import_react
// ...
export { render }
This plugin was inspired by a comment under esbuild#1921 and the prototype was done after a day.
npm add -D @hyrious/esbuild-plugin-commonjs
const { commonjs } = require("@hyrious/esbuild-plugin-commonjs");
require("esbuild").build({
entryPoints: ["lib.js"],
bundle: true,
format: "esm",
external: ["react"],
outfile: "out.js",
plugins: [commonjs({ only: 'external' })],
}).catch(() => process.exit(1));
commonjs({ filter: /\.c?js$/, transform: false })
-
filter (default:
/\.c?js$/
)A RegExp passed to
onLoad()
to match commonjs modules, it is recommended to set a custom filter to skip files for better performance. -
include
Determine whether a file should be transformed after
filter
has take effect.Example:
include: path => path.includes('node_modules/react')
-
requireReturnsDefault (default:
true
)requireReturnsDefault: boolean | ((path: string) => boolean)
Controls which style of import statement to use replacing require calls in commonjs modules.
// input const foo = require('foo') // output if requireReturnsDefault is true (default behavior) import foo from 'foo' // output if requireReturnsDefault is false import * as foo from 'foo'
-
only (default:
false
)Only convert require calls to these modules. You can also use wildcards here. If
ignore
is also set, the final modules list would beonly - ignore
.only: false | 'external' | string[] | ((path: string) => boolean)
If
'external'
, it will use theexternal
option from esbuild, which is likely the most common case. -
ignore
Do not convert require calls to these modules. Note that this will cause esbuild to generate
__require()
wrappers and throw errors at runtime.ignore: string[] | ((path: string) => boolean)
Experimental options
-
transform (default:
false
)Try to transform commonjs to es modules. This trick is done with
cjs-module-lexer
to match the native (node) behavior as much as possible. Because this transformation may cause many bugs around the interop between cjs and esm, it can also accept a function to filter in the "safe to convert" modules by yourself.transform: boolean | ((path: string) => { behavior?: "node" | "babel", exports?: string[], sideEffects?: boolean } | null | void)
By default, if you toggle
transform
totrue
, it will convert this code:exports.__esModule = true exports.default = {} exports.foo = 42
To this:
var exports = {}, module = { exports }; { exports.__esModule = true; exports.default = {}; exports.foo = 42; } export default exports; var { foo } = exports; export { foo };
This is not equal to @rollup/plugin-commonjs.
This plugin does not convert your commonjs file into es modules, it just
replace those require("x")
expressions with import statements. It turns out
that esbuild can handle this kind of mixed module (having import statement and
module.exports
at the same time) correctly.
The one acting the same exists in the branch rollup
, but is not a good
solution. It depends on a feature syntheticNamedExports
and evanw
(the author of esbuild) doesn't want to implement something out of spec.
Without which you have to tell the plugin every single commonjs file's named
exports, which sucks obviously.
Add options include
and only
.
Fix: skip shebangs when injecting import statements. (#6)
Add options requireReturnsDefault
and ignore
.
Add experimental option transform
and transformConfig
.
MIT @ hyrious