Skip to content

Commit 25df644

Browse files
committed
fix(cli): lexer-based comment handling and binding-verified createRequire detection
Scanning now runs on a comment-stripped, template-blanked copy of the source (string-aware, offsets preserved), so commented-out code never registers require names, closed inline comments don't hide real calls, and // inside a string is not mistaken for a comment. Calls are only recognized when the createRequire binding provably comes from the module builtin (named import, namespace member, or CJS destructure), typed require variables and two-level-nested createRequire arguments are matched, whitespace before require( is accepted, and the node_modules skip matches path segments instead of substrings.
1 parent 48e10d4 commit 25df644

2 files changed

Lines changed: 293 additions & 48 deletions

File tree

packages/cli-v3/src/build/createRequireWarnings.test.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,80 @@ const plugin = load("mssql");
165165
expect(scanSourceForCreateRequire(source)).toEqual([]);
166166
});
167167

168+
it("ignores a local createRequire function even when the module builtin is imported for something else", () => {
169+
const source = `import { builtinModules } from "node:module";
170+
function createRequire(config: string) {
171+
return (name: string) => registry.get(config, name);
172+
}
173+
const load = createRequire("defaults");
174+
const plugin = load("mssql");
175+
`;
176+
177+
expect(scanSourceForCreateRequire(source)).toEqual([]);
178+
});
179+
180+
it("does not register require names from commented-out assignments", () => {
181+
const source = `import { createRequire } from "node:module";
182+
// const req = createRequire(import.meta.url);
183+
declare function req(name: string): unknown;
184+
const y = req("mssql");
185+
`;
186+
187+
expect(scanSourceForCreateRequire(source)).toEqual([]);
188+
});
189+
190+
it("still finds calls after a closed inline block comment", () => {
191+
const source = `import { createRequire } from "node:module";
192+
const req = createRequire(import.meta.url);
193+
/* driver */ const mssql = req("mssql");
194+
`;
195+
196+
expect(scanSourceForCreateRequire(source).map((r) => r.specifier)).toEqual(["mssql"]);
197+
});
198+
199+
it("is not confused by // inside a string on the same line", () => {
200+
const source = `import { createRequire } from "node:module";
201+
const req = createRequire(import.meta.url);
202+
const api = "https://example.com"; const pg = req("pg");
203+
`;
204+
205+
expect(scanSourceForCreateRequire(source).map((r) => r.specifier)).toEqual(["pg"]);
206+
});
207+
208+
it("ignores code embedded in template literals", () => {
209+
const source =
210+
'import { createRequire } from "node:module";\nconst req = createRequire(import.meta.url);\nconst snippet = `const x = req("fake-pkg");`;\n';
211+
212+
expect(scanSourceForCreateRequire(source)).toEqual([]);
213+
});
214+
215+
it("supports whitespace before the require parenthesis in CJS bindings", () => {
216+
const source = `const { createRequire } = require ("module");
217+
const req = createRequire(__filename);
218+
const lib = req("canvas");
219+
`;
220+
221+
expect(scanSourceForCreateRequire(source).map((r) => r.specifier)).toEqual(["canvas"]);
222+
});
223+
224+
it("supports a type annotation on the assigned require variable", () => {
225+
const source = `import { createRequire } from "node:module";
226+
const req: NodeRequire = createRequire(import.meta.url);
227+
const mssql = req("mssql");
228+
`;
229+
230+
expect(scanSourceForCreateRequire(source).map((r) => r.specifier)).toEqual(["mssql"]);
231+
});
232+
233+
it("supports two levels of nesting in the createRequire argument", () => {
234+
const source = `import { createRequire } from "node:module";
235+
import { fileURLToPath } from "node:url";
236+
const mssql = createRequire(fileURLToPath(new URL(".", import.meta.url)))("mssql");
237+
`;
238+
239+
expect(scanSourceForCreateRequire(source).map((r) => r.specifier)).toEqual(["mssql"]);
240+
});
241+
168242
it("returns nothing when the source doesn't mention createRequire", () => {
169243
const source = `import mssql from "mssql";
170244
export const pool = mssql.connect();
@@ -219,6 +293,21 @@ export const mssql = createRequire(import.meta.url)("mssql");
219293
file: "entry.ts",
220294
line: 2,
221295
});
296+
297+
await build({
298+
entryPoints: [entryPoint],
299+
bundle: true,
300+
metafile: true,
301+
write: false,
302+
format: "esm",
303+
platform: "node",
304+
outdir: dir,
305+
absWorkingDir: dir,
306+
logLevel: "silent",
307+
plugins: [collector.plugin],
308+
});
309+
310+
expect(collector.usages).toHaveLength(1);
222311
} finally {
223312
await rm(dir, { recursive: true, force: true });
224313
}

0 commit comments

Comments
 (0)