Socket
Socket
Sign inDemoInstall

rollup-plugin-cjs-es

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rollup-plugin-cjs-es

Convert CommonJS module into ES module


Version published
Weekly downloads
755
decreased by-33.89%
Maintainers
1
Weekly downloads
 
Created
Source

rollup-plugin-cjs-es

Convert CommonJS module into ES module. Powered by cjs-es.

Installation

npm install -D rollup-plugin-cjs-es

Usage

import cjsEs from "rollup-plugin-cjs-es"

export default {
  input: ["entry.js"],
  output: {
    dir: "dist",
    format: "cjs"
  },
  plugins: [
    cjsEs({
      include: ["*.js"],
      exclude: [],
      importStyle: "default",
      exportStyle: "default",
      sourceMap: true,
      splitCode: true,
      hoist: true,
      dynamicImport: true
    })
  ]
};

Compatibility

cjs-es can transform top-level require, exports, and module.exports statements. For those non-top-level statements, the transformer hoist them to top-level (optional, off by default):

const baz = require("foo").bar.baz;
if (baz) {
  const bak = require("bar");
}

After hoisting:

const _require_foo_ = require("foo");
const baz = _require_foo_.bar.baz;
const _require_bar_ = require("bar");
if (baz) {
  const bak = _require_bar_;
}

However, if require, exports, or module are dynamically assigned, the transformer can't find them e.g.

(function(r) {
  r("foo");
})(require);
const r = require;
r("foo");

These patterns are common in module loaders like UMD. I suggest using other plugin to unwrap the module back to normal CJS pattern.

Lazy load and code splitting

To lazy load an ES module, we can use import() function:

foo.js

export const foo = () => {
  return import("./bar");
};

bar.js

export const bar = "bar";

After rolluped into CommonJS format, the dist folder would contain two entries:

foo.js

'use strict';

const foo = () => {
  return Promise.resolve(require("./bar.js"));
};

exports.foo = foo;

bar.js

'use strict';

const bar = "bar";

exports.bar = bar;

So that bar.js is not loaded until require("foo").foo() is called.

With this plugin, you can use the same feature in CommonJS syntax, by writing the require statement inside a promise i.e. Promise.resolve(require("...")) (must set options.dynamicImport to true):

module.exports = {
  foo: () => {
    return Promise.resolve(require("./bar.js"));
  }
};

Or, by adding a special comment // split if you can't use async function (must set options.splitCode to true):

module.exports = {
  foo: () => {
    return require("./bar"); // split
  }
};

Note that in the later form, the result is a sync require function call, which means the output format must be cjs.

Named import/export v.s. default import/export

Cannot call a namespace

In the following example, you would get an error:

entry.js

const foo = require("./foo");
foo();

foo.js

module.exports = function() {
  console.log("foo");
};
entry.js → dist...
[!] Error: Cannot call a namespace ('foo')
entry.js (2:0)
1: const foo = require("./foo");
2: foo();
   ^

To fix it, mark the require as // default or use the importStyle option.

However, adding comments manually could be a problem if they are mixed everywhere. In this case, you may want to set both importStyle and exportStyle to "default" so the plugin uses default import/export everywhere.

Dynamic import() problem with default export

In the following example, you would get an error under ES enviroment:

entry.js

Promise.resolve(require("./foo"))
  .then(foo => foo());

foo.js

module.exports = function() {
  console.log("foo");
};

After rolluped into ES format and renamed them into .mjs:

entry.mjs

import("./foo.mjs")
  .then(foo => foo());

foo.mjs

function foo() {
  console.log("foo");
}

export default foo;
(node:9996) ExperimentalWarning: The ESM module loader is experimental.
(node:9996) UnhandledPromiseRejectionWarning: TypeError: foo is not a function
    at then.foo (file:///D:/Dev/node-test/dist/entry.mjs:2:16)
    at <anonymous>

To correctly call the default member, entry.js has to be modified:

Promise.resolve(require("./foo"))
  .then(foo => foo.default());

However, this would break other enviroments like CommonJS:

(node:9432) UnhandledPromiseRejectionWarning: TypeError: foo.default is not a fu
nction
    at Promise.resolve.then.foo (D:\Dev\node-test\dist\entry.js:4:27)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:118:7)
    at Function.Module.runMain (module.js:705:11)
    at startup (bootstrap_node.js:193:16)
    at bootstrap_node.js:660:3

Avoid default export if you want to use dynamic import() + CommonJS in the same time.

rollup-plugin-commonjs

The commonjs plugin uses a smart method to determine whether to use named or default import. It creates a proxy loader when a module is imported:

source

const foo = require("foo");
foo.bar();

transformed

import "foo";
import foo from "commonjs-proxy:foo";
foo.bar();

With this method, it can first look into the "foo" module to check its export type, then generate the proxy module which maps named exports into a default export. However, if the required module "foo" uses named exports, it has to be converted into a single object:

commonjs-proxy:foo

import * as _ from "foo";
export default _;

As a result, all named exports are included in the bundle even that only bar is used.

The same problem applies to cjs-es as well if you force a module to use default export:

entry.js

const foo = require("./foo");
foo.foo();

foo.js

module.exports = {
  foo: () => console.log("foo"),
  bar: () => console.log("bar")
};

bundled

const _export_foo_ = () => console.log("foo");

_export_foo_();

bundled with importStyle: "default" and exportStyle: "default"

var foo = {
  foo: () => console.log("foo"),
  bar: () => console.log("bar")
};

foo.foo();

API reference

This module exports a single function.

cjsEsFactory(options?: object): RollupPlugin object

options may have following optional properties:

  • include: Array<string>. A list of minimatch pattern. Only matched files would be transformed. Match all files by default.

  • exclude: Array<string>. A list of minimatch pattern. Override options.include. Default: [].

  • sourceMap: boolean. If true then generate the source map. Default: true.

  • splitCode: boolean|function. If true then enable code-splitting for require statements which are marked as // split. See Lazy load and code splitting for details.

    If splitCode is a function, it would receives 2 arguments:

    • importer: string. The module ID which is being transformed. It is usually an absolute path.
    • importee: string. The id inside require() function.

    The return value should be a boolean.

    Default: false

  • hoist: boolean. If true then enable hoist transformer. Default: false.

  • dynamicImport: boolean. If true then enable dynamic import transformer. Default: false.

  • importStyle: string|object|function. Change the importStyle option for cjs-es.

    If importStyle is a function, it receives 2 arguments:

    • importer: string. The module ID which is being transformed.
    • importee: string. The id inside require() function.

    The return value should be "named" or "default".

    If importStyle is an object, it is a 2 depth map. For example:

    importStyle: {
      "path/to/moduleA": "default", // set importStyle to "default" for moduleA
      "path/to/moduleB": {
        "./foo": "default" // set importStyle to "default" for moduleB and 
      }                    // only when requiring `./foo` module.
    }
    

    Default: "named".

  • exportStyle: string|object|function. Change the exportStyle option for cjs-es.

    If exportStyle is a function, it receives 1 argument:

    • exporter: string. The module ID which is being transformed.

    If exportStyle is an object, it is a "path/to/module": "value" map.

    Default: "named".

Changelog

  • 0.1.0 (Apr 27, 2018)

    • Initial releast.

Keywords

FAQs

Package last updated on 27 Apr 2018

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc