Giter Site home page Giter Site logo

esbuild-plugin-commonjs's Introduction

@hyrious/esbuild-plugin-commonjs

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.

Install

npm add -D @hyrious/esbuild-plugin-commonjs

Usage

const { commonjs } = require("@hyrious/esbuild-plugin-commonjs");

require("esbuild").build({
  entryPoints: ["lib.js"],
  bundle: true,
  format: "esm",
  external: ["react"],
  outfile: "out.js",
  plugins: [commonjs()],
}).catch(() => process.exit(1));

Options

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.

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'

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)

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 to true, 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.

Changelog

0.2.0

Add experimental option transform and transformConfig.

0.2.3

Add options requireReturnsDefault and ignore.

License

MIT @ hyrious

esbuild-plugin-commonjs's People

Contributors

hyrious avatar richard-better 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

Watchers

 avatar  avatar

Forkers

stackla

esbuild-plugin-commonjs's Issues

feat: transform commonjs to esm

I came up with a super easy way of doing this instead of going "syntheticNamedExports". To be clear I just want to replicate the node17 behavior: use cjs-module-lexer to support partial of the cjs modules. The transformed module will be like:

var exports = {}, module = {exports}
// have to use { block } to prevent names conflict
{
  // original code
}
export default module.exports
// analyzed possible named exports
export var useState = module.exports.useState
...

The downside is obvious: some modules will cause error at runtime when it doesn't follow the pattern. But the advantage is we can reduce some verbosing code about __commonJS. I can make this transformation optional.

Those requires still need the trick in this repo. But I want to add additional info here:

try { require("x") } catch {}

Code like this won't be transformed to import statement because esbuild allowed this pattern and it won't warn about it. Frankly speaking, even "require is undefined" won't cause error at runtime when it was caught like this.

Error: No matching export in "xxx" for import "default"

Using the plugin results in the following error when trying to build.
A similar error happens when I try to use typeorm in my project. The error that occurs if I use typeorm is with "uuid" not having a default import.)

✘ [ERROR] No matching export in "node_modules/acorn-loose/dist/acorn-loose.mjs" for import "default"

    node_modules/@mikro-orm/core/utils/Utils.js:1:251:
      1 │ ...ort __import_os from "os";import __import_url from "url";import __import_fsExtra from "fs-extra";import __import_crypto from "crypto";import __import_acornLoose from "acorn-loose";import __import_acornWalk from "acorn-walk";import __import___clone from "./clone";import __import____enums from "../enu...

Nodejs version

I can't install the package with a newer version of NodeJS.

"engines": {
    "node": "14"
}

Couldn't you edit the value to ">=14"?

feat: simply replace the `__require()` call

Since tree-shaking is not applied to re-exports (it always has a top level variable holding the whole cjs module), maybe there's a simple case to just handle the require()s:

// input.js
import React from "react"

// module: react
module.exports = () => require("./lib.js") // not a typical re-export

// module: react/lib.js
module.exports = 42

↓ ↓ ↓

// input.js
import React from "react"

// module: react
import lib from "./lib.js"
var modules = { "./lib.js": lib };
var require = /* @__PURE__ */ (name) => {
  return modules[name];
};
var module={exports:{}};
module.exports = () => require("./lib.js")
export default module.exports

// module: react/lib.js: not found `require()`, so not changed

Looks like some sort of things webpack does.

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.