@jsenv/plugin-commonjs 2.5.1 → 2.6.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/plugin-commonjs",
3
- "version": "2.5.1",
3
+ "version": "2.6.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -28,16 +28,15 @@
28
28
  "@jsenv/log": "3.4.0",
29
29
  "@jsenv/url-meta": "8.1.0",
30
30
  "@jsenv/urls": "2.2.1",
31
- "@rollup/plugin-commonjs": "25.0.3",
31
+ "@rollup/plugin-commonjs": "25.0.4",
32
32
  "@rollup/plugin-json": "6.0.0",
33
- "@rollup/plugin-node-resolve": "15.1.0",
33
+ "@rollup/plugin-node-resolve": "15.2.1",
34
34
  "@rollup/plugin-replace": "5.0.2",
35
35
  "cjs-module-lexer": "1.2.3",
36
36
  "is-valid-identifier": "2.0.2",
37
- "resolve": "1.22.2",
38
- "rollup": "3.27.0",
37
+ "resolve": "1.22.4",
38
+ "rollup": "3.29.1",
39
39
  "rollup-plugin-node-globals": "1.4.0",
40
- "rollup-plugin-polyfill-node": "0.12.0",
41
- "vm2": "3.9.19"
40
+ "rollup-plugin-polyfill-node": "0.12.0"
42
41
  }
43
42
  }
@@ -3,7 +3,7 @@
3
3
  import { readFileSync } from "node:fs";
4
4
  import { fileURLToPath, pathToFileURL } from "node:url";
5
5
  import path from "node:path";
6
- import { VM as VM2 } from "vm2";
6
+ import { Worker } from "node:worker_threads";
7
7
  import resolve from "resolve";
8
8
  import isValidIdentifier from "is-valid-identifier";
9
9
  import { init, parse } from "cjs-module-lexer";
@@ -27,23 +27,29 @@ export const rollupPluginCommonJsNamedExports = ({ logger }) => {
27
27
  name: "scan_cjs_named_exports",
28
28
  async buildStart({ input }) {
29
29
  await init();
30
- Object.keys(input).forEach((key) => {
31
- const inputFilePath = input[key];
32
- const namedCjs =
33
- detectStaticExports({ logger, filePath: inputFilePath }) ||
34
- detectExportsUsingSandboxedRuntime({
30
+ await Promise.all(
31
+ Object.keys(input).map(async (key) => {
32
+ const inputFilePath = input[key];
33
+ let namedCjs = detectStaticExports({
35
34
  logger,
36
35
  filePath: inputFilePath,
37
36
  });
38
- scanResults[inputFilePath] = {
39
- all: true,
40
- default: true,
41
- namespace: true,
42
- named: [],
43
- namedCjs,
44
- };
45
- input[key] = importWrapper.wrap(inputFilePath);
46
- });
37
+ if (!namedCjs) {
38
+ namedCjs = await detectExportsUsingSandboxedRuntime({
39
+ logger,
40
+ filePath: inputFilePath,
41
+ });
42
+ }
43
+ scanResults[inputFilePath] = {
44
+ all: true,
45
+ default: true,
46
+ namespace: true,
47
+ named: [],
48
+ namedCjs,
49
+ };
50
+ input[key] = importWrapper.wrap(inputFilePath);
51
+ }),
52
+ );
47
53
  },
48
54
  resolveId(specifier) {
49
55
  if (importWrapper.isWrapped(specifier)) {
@@ -126,20 +132,36 @@ const detectStaticExports = ({ logger, filePath, visited = new Set() }) => {
126
132
  /*
127
133
  * Attempt #2b - Sandboxed runtime analysis: More powerful, but slower.
128
134
  * This will only work on UMD and very simple CJS files (require not supported).
129
- * Uses VM2 to run safely sandbox untrusted code (no access no Node.js primitives, just JS).
135
+ * run safely sandbox untrusted code (no access no Node.js primitives, just JS).
130
136
  * If nothing was detected, return undefined.
131
137
  */
132
- const detectExportsUsingSandboxedRuntime = ({ logger, filePath }) => {
138
+ export const detectExportsUsingSandboxedRuntime = async ({
139
+ logger,
140
+ filePath,
141
+ }) => {
133
142
  try {
134
- const fileContents = readFileSync(filePath, "utf8");
135
- const vm = new VM2({ wasm: false, fixAsync: false });
136
- const codeToRun = wrapCodeToRunInVm(fileContents);
137
- const vmResult = vm.run(codeToRun);
138
- const exportsResult = Object.keys(vmResult);
143
+ const workerThread = new Worker(
144
+ new URL("./worker_runtime.mjs", import.meta.url),
145
+ );
146
+ workerThread.postMessage({
147
+ filePath,
148
+ });
149
+ const promise = new Promise((resolve, reject) => {
150
+ workerThread.on("error", reject);
151
+ workerThread.on("message", (message) => {
152
+ resolve(message);
153
+ workerThread.terminate();
154
+ });
155
+ });
156
+ const result = await promise;
157
+ if (result.errorMessage) {
158
+ throw new Error(result.errorMessage);
159
+ }
160
+ const exportNames = result.exportNames;
139
161
  logger.debug(
140
- `detectExportsUsingSandboxedRuntime success ${filePath}: ${exportsResult}`,
162
+ `detectExportsUsingSandboxedRuntime success ${filePath}: ${exportNames}`,
141
163
  );
142
- return exportsResult.filter((identifier) => isValidIdentifier(identifier));
164
+ return exportNames.filter((identifier) => isValidIdentifier(identifier));
143
165
  } catch (err) {
144
166
  logger.debug(
145
167
  `detectExportsUsingSandboxedRuntime error ${filePath}: ${err.message}`,
@@ -151,13 +173,6 @@ const detectExportsUsingSandboxedRuntime = ({ logger, filePath }) => {
151
173
  const isValidNamedExport = (name) =>
152
174
  name !== "default" && name !== "__esModule" && isValidIdentifier(name);
153
175
 
154
- const wrapCodeToRunInVm = (code) => {
155
- return `const exports = {};
156
- const module = { exports };
157
- ${code};;
158
- module.exports;`;
159
- };
160
-
161
176
  const generateCodeForExports = ({
162
177
  uniqueNamedExports,
163
178
  scanResult,
@@ -0,0 +1,34 @@
1
+ import { parentPort } from "node:worker_threads";
2
+ import { readFileSync } from "node:fs";
3
+ import vm from "node:vm";
4
+
5
+ parentPort.once("message", ({ filePath }) => {
6
+ const fileContents = readFileSync(filePath, "utf8");
7
+ const codeToRun = wrapCodeToRunInVm(fileContents);
8
+ const script = new vm.Script(codeToRun);
9
+ try {
10
+ const returnValue = script.runInNewContext(
11
+ {},
12
+ {
13
+ contextCodeGeneration: {
14
+ wasm: false,
15
+ },
16
+ },
17
+ );
18
+ const exportNames = Object.keys(returnValue);
19
+ parentPort.postMessage({
20
+ exportNames,
21
+ });
22
+ } catch (e) {
23
+ parentPort.postMessage({
24
+ errorMessage: e.message,
25
+ });
26
+ }
27
+ });
28
+
29
+ const wrapCodeToRunInVm = (code) => {
30
+ return `const exports = {};
31
+ const module = { exports };
32
+ ${code};;
33
+ module.exports;`;
34
+ };