rolldown-plugin-require-cjs 0.1.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -0
- package/dist/index.d.ts +13 -5
- package/dist/index.js +49 -27
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -10,14 +10,58 @@ Transform ESM imports to CJS requires when the imported module is pure CJS.
|
|
|
10
10
|
|
|
11
11
|
Some packages only provide CJS builds (e.g., [`typescript`](https://npmjs.com/package/typescript), [`@babel/parser`](https://npmjs.com/package/@babel/parser)), and importing them using ESM syntax increases Node's `cjs-module-lexer` overhead. This plugin converts ESM imports to CJS requires for such pure CJS packages, allowing Node to skip the cjs-module-lexer step and improve performance.
|
|
12
12
|
|
|
13
|
+
If performance is insignificant for your project, please do not use this plugin, as it introduces additional complexity and maintenance overhead.
|
|
14
|
+
|
|
13
15
|
See more: https://x.com/sanxiaozhizi/status/1968580207322808812
|
|
14
16
|
|
|
17
|
+
## Appeal
|
|
18
|
+
|
|
19
|
+
We encourage the JavaScript ecosystem to continue its transition toward ESM. If you maintain a package that is still CJS-only, please consider offering an ESM build or migrating fully to ESM. Doing so will reduce reliance on plugins like this one and enhance the overall performance of the ecosystem.
|
|
20
|
+
|
|
15
21
|
## Install
|
|
16
22
|
|
|
17
23
|
```bash
|
|
18
24
|
npm i rolldown-plugin-require-cjs
|
|
19
25
|
```
|
|
20
26
|
|
|
27
|
+
## Options
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
export interface Options {
|
|
31
|
+
include?: Array<string | RegExp> | string | RegExp
|
|
32
|
+
exclude?: Array<string | RegExp> | string | RegExp
|
|
33
|
+
order?: 'pre' | 'post' | undefined
|
|
34
|
+
/**
|
|
35
|
+
* A function to determine whether a module should be transformed.
|
|
36
|
+
* Return `true` to force transformation, `false` to skip transformation,
|
|
37
|
+
* or `undefined` to let the plugin decide automatically.
|
|
38
|
+
*/
|
|
39
|
+
shouldTransform?: string[] | TransformFn
|
|
40
|
+
/**
|
|
41
|
+
* Whether to transform Node.js built-in modules (e.g., `fs`, `path`)
|
|
42
|
+
* to `process.getBuiltinModule()` calls, which has the best performance.
|
|
43
|
+
*
|
|
44
|
+
* Note: `process.getBuiltinModule` is available since Node.js 20.16.0 and 22.3.0.
|
|
45
|
+
*/
|
|
46
|
+
builtinNodeModules?: boolean
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @returns A boolean or a promise that resolves to a boolean,
|
|
51
|
+
* or `undefined` to let the plugin decide automatically.
|
|
52
|
+
*/
|
|
53
|
+
export type TransformFn = (
|
|
54
|
+
/**
|
|
55
|
+
* The module ID (path) being imported.
|
|
56
|
+
*/
|
|
57
|
+
id: string,
|
|
58
|
+
/**
|
|
59
|
+
* The module ID (path) of the importer.
|
|
60
|
+
*/
|
|
61
|
+
importer: string,
|
|
62
|
+
) => Awaitable<boolean | undefined | void>
|
|
63
|
+
```
|
|
64
|
+
|
|
21
65
|
## Example
|
|
22
66
|
|
|
23
67
|
```ts
|
package/dist/index.d.ts
CHANGED
|
@@ -1,23 +1,31 @@
|
|
|
1
|
+
import { FilterPattern } from "unplugin-utils";
|
|
1
2
|
import { Plugin } from "rolldown";
|
|
2
3
|
|
|
3
4
|
//#region src/options.d.ts
|
|
4
|
-
type FilterPattern = Array<string | RegExp> | string | RegExp;
|
|
5
5
|
type Awaitable<T> = T | Promise<T>;
|
|
6
|
+
/**
|
|
7
|
+
* @returns A boolean or a promise that resolves to a boolean,
|
|
8
|
+
* or `undefined` to let the plugin decide automatically.
|
|
9
|
+
*/
|
|
6
10
|
type TransformFn = (id: string, importer: string) => Awaitable<boolean | undefined | void>;
|
|
7
11
|
interface Options {
|
|
8
12
|
include?: FilterPattern;
|
|
9
13
|
exclude?: FilterPattern;
|
|
10
14
|
order?: "pre" | "post" | undefined;
|
|
15
|
+
cwd?: string;
|
|
11
16
|
/**
|
|
12
17
|
* A function to determine whether a module should be transformed.
|
|
13
18
|
* Return `true` to force transformation, `false` to skip transformation,
|
|
14
19
|
* or `undefined` to let the plugin decide automatically.
|
|
15
|
-
*
|
|
16
|
-
* @param id The module ID (path) being imported.
|
|
17
|
-
* @param importer The module ID (path) of the importer.
|
|
18
|
-
* @returns A boolean or a promise that resolves to a boolean, or `undefined`.
|
|
19
20
|
*/
|
|
20
21
|
shouldTransform?: string[] | TransformFn;
|
|
22
|
+
/**
|
|
23
|
+
* Whether to transform Node.js built-in modules (e.g., `fs`, `path`)
|
|
24
|
+
* to `process.getBuiltinModule()` calls, which has the best performance.
|
|
25
|
+
*
|
|
26
|
+
* Note: `process.getBuiltinModule` is available since Node.js 20.16.0 and 22.3.0.
|
|
27
|
+
*/
|
|
28
|
+
builtinNodeModules?: boolean;
|
|
21
29
|
}
|
|
22
30
|
type Overwrite<T, U> = Pick<T, Exclude<keyof T, keyof U>> & U;
|
|
23
31
|
type OptionsResolved = Overwrite<Required<Options>, Pick<Options, "order"> & {
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
1
|
+
import { builtinModules, createRequire } from "node:module";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { init, parse } from "cjs-module-lexer";
|
|
@@ -6,6 +6,8 @@ import { up } from "empathic/package";
|
|
|
6
6
|
import { MagicStringAST, generateTransform } from "magic-string-ast";
|
|
7
7
|
import { resolvePathSync } from "mlly";
|
|
8
8
|
import { parseAst } from "rolldown/parseAst";
|
|
9
|
+
import { createFilter } from "unplugin-utils";
|
|
10
|
+
import process from "node:process";
|
|
9
11
|
|
|
10
12
|
//#region rolldown:runtime
|
|
11
13
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
@@ -21,20 +23,26 @@ function resolveOptions(options) {
|
|
|
21
23
|
include: options.include || [/\.[cm]?[jt]sx?$/],
|
|
22
24
|
exclude: options.exclude || [/node_modules/, /\.d\.[cm]?ts$/],
|
|
23
25
|
order: "order" in options ? options.order : "pre",
|
|
24
|
-
|
|
26
|
+
cwd: options.cwd || process.cwd(),
|
|
27
|
+
shouldTransform: options.shouldTransform,
|
|
28
|
+
builtinNodeModules: !!options.builtinNodeModules
|
|
25
29
|
};
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
//#endregion
|
|
29
33
|
//#region src/index.ts
|
|
30
34
|
let initted = false;
|
|
35
|
+
const REQUIRE = `__cjs_require`;
|
|
31
36
|
function RequireCJS(userOptions = {}) {
|
|
32
|
-
const { include, exclude, order, shouldTransform } = resolveOptions(userOptions);
|
|
37
|
+
const { include, exclude, order, cwd, shouldTransform, builtinNodeModules } = resolveOptions(userOptions);
|
|
38
|
+
const filter = createFilter(include, exclude);
|
|
33
39
|
return {
|
|
34
40
|
name: "rolldown-plugin-require-cjs",
|
|
35
41
|
async buildStart() {
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
if (!initted) {
|
|
43
|
+
await init();
|
|
44
|
+
initted = true;
|
|
45
|
+
}
|
|
38
46
|
},
|
|
39
47
|
options(options) {
|
|
40
48
|
if (options.platform !== "node") this.error("`rolldown-plugin-require-cjs` plugin is designed only for the Node.js environment. Please make sure to set `platform: \"node\"` in the options.");
|
|
@@ -46,42 +54,56 @@ function RequireCJS(userOptions = {}) {
|
|
|
46
54
|
"module"
|
|
47
55
|
].includes(options.format)) throw new Error("`rolldown-plugin-require-cjs` plugin is only necessary for ESM output");
|
|
48
56
|
},
|
|
49
|
-
|
|
50
|
-
filter: { id: {
|
|
51
|
-
include,
|
|
52
|
-
exclude
|
|
53
|
-
} },
|
|
57
|
+
renderChunk: {
|
|
54
58
|
order,
|
|
55
|
-
async handler(code,
|
|
56
|
-
|
|
59
|
+
async handler(code, { fileName }) {
|
|
60
|
+
if (!filter(fileName)) return;
|
|
61
|
+
const { body } = parseAst(code, { lang: void 0 }, fileName);
|
|
57
62
|
const s = new MagicStringAST(code);
|
|
63
|
+
let usingRequire = false;
|
|
58
64
|
for (const stmt of body) if (stmt.type === "ImportDeclaration") {
|
|
59
65
|
if (stmt.importKind === "type") continue;
|
|
60
66
|
const source = stmt.source.value;
|
|
61
|
-
const
|
|
62
|
-
if (
|
|
63
|
-
if (!(await shouldTransform?.(source, id) ?? await isPureCJS(source, id))) continue;
|
|
67
|
+
const isBuiltinModule = builtinNodeModules && (builtinModules.includes(source) || source.startsWith("node:"));
|
|
68
|
+
if (!(isBuiltinModule || (await shouldTransform?.(source, cwd) ?? await isPureCJS(source, cwd)))) continue;
|
|
64
69
|
if (stmt.specifiers.length === 0) {
|
|
65
|
-
|
|
70
|
+
if (isBuiltinModule) s.removeNode(stmt);
|
|
71
|
+
else {
|
|
72
|
+
s.overwriteNode(stmt, `${REQUIRE}(${JSON.stringify(source)});`);
|
|
73
|
+
usingRequire = true;
|
|
74
|
+
}
|
|
66
75
|
continue;
|
|
67
76
|
}
|
|
68
|
-
const mapping =
|
|
77
|
+
const mapping = [];
|
|
69
78
|
let namespaceId;
|
|
70
79
|
let defaultId;
|
|
71
|
-
for (const specifier of stmt.specifiers) if (specifier.type === "ImportNamespaceSpecifier") namespaceId =
|
|
80
|
+
for (const specifier of stmt.specifiers) if (specifier.type === "ImportNamespaceSpecifier") namespaceId = specifier.local.name;
|
|
72
81
|
else if (specifier.type === "ImportSpecifier") {
|
|
73
82
|
if (specifier.importKind === "type") continue;
|
|
74
|
-
mapping[s.sliceNode(specifier.imported)
|
|
75
|
-
} else defaultId =
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
mapping.push([s.sliceNode(specifier.imported), specifier.local.name]);
|
|
84
|
+
} else defaultId = specifier.local.name;
|
|
85
|
+
let requireCode;
|
|
86
|
+
if (isBuiltinModule) requireCode = source === "process" || source === "node:process" ? "globalThis.process" : `globalThis.process.getBuiltinModule(${JSON.stringify(source)})`;
|
|
87
|
+
else {
|
|
88
|
+
requireCode = `__cjs_require(${JSON.stringify(source)})`;
|
|
89
|
+
usingRequire = true;
|
|
90
|
+
}
|
|
91
|
+
const codes = [];
|
|
78
92
|
if (namespaceId) defaultId ||= `_cjs_${namespaceId}_default`;
|
|
79
|
-
if (defaultId)
|
|
80
|
-
if (namespaceId)
|
|
81
|
-
if (
|
|
82
|
-
s.overwriteNode(stmt,
|
|
93
|
+
if (defaultId) codes.push(`const ${defaultId} = ${requireCode};`);
|
|
94
|
+
if (namespaceId) codes.push(`const ${namespaceId} = { ...${defaultId}, default: ${defaultId} };`);
|
|
95
|
+
if (mapping.length > 0) codes.push(`const {\n${mapping.map(([k, v]) => ` ${k === v ? v : `${k}: ${v}`}`).join(",\n")}\n} = ${defaultId || requireCode};`);
|
|
96
|
+
s.overwriteNode(stmt, codes.join("\n"));
|
|
97
|
+
}
|
|
98
|
+
if (usingRequire) {
|
|
99
|
+
const preamble = builtinNodeModules ? `const ${REQUIRE} = globalThis.process.getBuiltinModule("module").createRequire(import.meta.url);\n` : `import { createRequire as __cjs_createRequire } from "node:module";
|
|
100
|
+
const ${REQUIRE} = __cjs_createRequire(import.meta.url);\n`;
|
|
101
|
+
if (code[0] === "#") {
|
|
102
|
+
const firstNewLineIndex = code.indexOf("\n") + 1;
|
|
103
|
+
s.appendLeft(firstNewLineIndex, preamble);
|
|
104
|
+
} else s.prepend(preamble);
|
|
83
105
|
}
|
|
84
|
-
return generateTransform(s,
|
|
106
|
+
return generateTransform(s, fileName);
|
|
85
107
|
}
|
|
86
108
|
}
|
|
87
109
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rolldown-plugin-require-cjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Transform ESM imports to CJS requires when the imported module is pure CJS.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,20 +34,21 @@
|
|
|
34
34
|
"cjs-module-lexer": "^2.1.0",
|
|
35
35
|
"empathic": "^2.0.0",
|
|
36
36
|
"magic-string-ast": "^1.0.2",
|
|
37
|
-
"mlly": "^1.8.0"
|
|
37
|
+
"mlly": "^1.8.0",
|
|
38
|
+
"unplugin-utils": "^0.3.0"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
41
|
"@babel/parser": "^7.28.4",
|
|
41
|
-
"@sxzz/eslint-config": "^7.
|
|
42
|
+
"@sxzz/eslint-config": "^7.2.5",
|
|
42
43
|
"@sxzz/prettier-config": "^2.2.4",
|
|
43
44
|
"@sxzz/test-utils": "^0.5.11",
|
|
44
|
-
"@types/node": "^24.
|
|
45
|
+
"@types/node": "^24.5.2",
|
|
45
46
|
"bumpp": "^10.2.3",
|
|
46
|
-
"eslint": "^9.
|
|
47
|
+
"eslint": "^9.36.0",
|
|
47
48
|
"eslint-plugin-vue": "^10.5.0",
|
|
48
49
|
"prettier": "^3.6.2",
|
|
49
|
-
"rolldown": "1.0.0-beta.
|
|
50
|
-
"tsdown": "^0.15.
|
|
50
|
+
"rolldown": "1.0.0-beta.40",
|
|
51
|
+
"tsdown": "^0.15.4",
|
|
51
52
|
"typescript": "^5.9.2",
|
|
52
53
|
"vitest": "^3.2.4"
|
|
53
54
|
},
|