mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 01:41:21 +00:00
* fix(javascript): require exact loader suffix case * fix(javascript): share exact loader mode across import resolution * test(process): align symlink fixture with filesystem checkpoints * test(javascript): normalize the module resolution oracle path separators * test(frida): assert the CLI spawn Evidence contract * test(mcp): use an unsupported analysis-view parent * test(browser): separate source-location line bounds from parity * fix(javascript): preserve uncertain loader provenance * test(javascript): cover uncertain require resolution * fix(javascript): retain conditional Electron evidence * fix(javascript): preserve timed semantic provenance * fix(javascript): retain conditional builtin effects * test(javascript): distinguish bundled host authority * fix(javascript): preserve nested loader invocation identity * test(javascript): qualify Electron fixture authority * fix(javascript): preserve loader invocation authority * fix(javascript): respect source order for resolver escapes * fix(javascript): check nested loader callable mutations * fix(javascript): retain conditional constructor requests * fix(reference): retain constructor loader relationships * fix(reference): follow nested loader callable paths * refactor(javascript): share callable mutation interpretation * fix(javascript): require exact package loader suffixes * refactor(javascript): unify conditional relation evidence * refactor(javascript): unify conditional node evidence * fix(javascript): retain unknown authority for mixed case sources * fix(javascript): preserve loader uncertainty for dotfiles * fix(reference): classify javascript suffix dotfiles * test(reference): cover suffix dotfiles through cli * fix(javascript): retain equivalent conditional built-in origins * fix(javascript): preserve wrapped resolver operation * fix(javascript): retain nested conditional built-in receivers * test(javascript): declare CommonJS in native addon fixtures * fix(javascript): retain wrapped resolver candidates * style: format merged native command schema * fix(javascript): preserve uncertain loader authority * test(javascript): require candidate preload evidence * test(javascript): cover reference loader member writes --------- Co-authored-by: ShuaixinHuang <1836656514@qq.com>
593 lines
19 KiB
TypeScript
593 lines
19 KiB
TypeScript
import { Module, createRequire } from "node:module";
|
|
import * as path from "node:path";
|
|
import { runInNewContext } from "node:vm";
|
|
import { expect, it } from "vitest";
|
|
|
|
import {
|
|
analyzeJavaScriptStaticSource,
|
|
analyzeParsedJavaScriptStaticSource,
|
|
} from "./javascriptStaticAnalysis.js";
|
|
import { analyzeCommonJsStaticFixture } from "../../../tests/fixtures/javascriptCommonJsAnalysis.js";
|
|
import { parseJavaScriptSource } from "./javascriptSourceParser.js";
|
|
import { referenceSourceRequireCalls } from "../referenceSourceRequireBindings.js";
|
|
import { parseReferenceSourceImports } from "../referenceSourceImportParsing.js";
|
|
|
|
it.each([
|
|
'(require.resolve as any)("./dep.cjs");',
|
|
'(require as any).resolve("./dep.cjs");',
|
|
'(require as any)?.["resolve"]("./dep.cjs");',
|
|
])(
|
|
"retains an unknown-host resolver through TypeScript wrappers: %s",
|
|
(source) => {
|
|
const parsed = parseJavaScriptSource(source, "entry.TS");
|
|
if (parsed === null) throw new Error("Expected TypeScript fixture");
|
|
expect(
|
|
analyzeParsedJavaScriptStaticSource(source, parsed).references,
|
|
).toContainEqual(
|
|
expect.objectContaining({
|
|
kind: "require",
|
|
specifier: "./dep.cjs",
|
|
operation: "resolve",
|
|
loader_authority: "candidate",
|
|
}),
|
|
);
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
["require", "load"],
|
|
["require?.", "load"],
|
|
["require.resolve", "resolve"],
|
|
["require.resolve?.", "resolve"],
|
|
["require?.resolve", "resolve"],
|
|
["require?.resolve?.", "resolve"],
|
|
['require?.["resolve"]', "resolve"],
|
|
["(require?.resolve)", "resolve"],
|
|
["require.main", "not-callable"],
|
|
] as const)(
|
|
"distinguishes invoked loading from reference association for %s",
|
|
(callee, operation) => {
|
|
const source = `${callee}('node:path');`;
|
|
const execute = () =>
|
|
runInNewContext(source, { require: createRequire(import.meta.url) });
|
|
if (operation === "load") expect(execute().join).toBe(path.join);
|
|
else if (operation === "resolve") expect(execute()).toBe("node:path");
|
|
else expect(execute).toThrow();
|
|
const analysis = analyzeJavaScriptStaticSource(source);
|
|
expect(analysis.parse_error_count).toBe(0);
|
|
expect(analysis.references).toEqual(
|
|
operation === "load"
|
|
? [expect.objectContaining({ kind: "require", specifier: "node:path" })]
|
|
: operation === "resolve"
|
|
? [
|
|
expect.objectContaining({
|
|
kind: "require",
|
|
specifier: "node:path",
|
|
}),
|
|
]
|
|
: [],
|
|
);
|
|
const parsed = parseJavaScriptSource(source);
|
|
if (parsed === null) throw new Error("Expected loader association fixture");
|
|
expect(referenceSourceRequireCalls(parsed, "entry.cjs").size).toBe(
|
|
operation === "not-callable" ? 0 : 1,
|
|
);
|
|
const historical = parseReferenceSourceImports(
|
|
"entry.cjs",
|
|
new TextEncoder().encode(source),
|
|
"JavaScript",
|
|
);
|
|
expect(historical.parse_failures).toEqual([]);
|
|
expect(historical.relationships).toEqual(
|
|
operation === "not-callable"
|
|
? []
|
|
: [
|
|
{
|
|
from_path: "entry.cjs",
|
|
to: "node:path",
|
|
kind: "requires",
|
|
resolution: "external",
|
|
parse_state: "parsed",
|
|
resolutionStrategy: "runtime-require",
|
|
},
|
|
],
|
|
);
|
|
},
|
|
);
|
|
|
|
const imported = "import {createRequire as make} from 'node:module';";
|
|
it.each([
|
|
["call", 'null,"./valid.cjs"', 'null,"./invented.cjs"'],
|
|
["apply", 'null,["./valid.cjs"]', 'null,["./invented.cjs"]'],
|
|
])(
|
|
"distinguishes nested loader-%s replacement from a child property write",
|
|
(method, valid, invented) => {
|
|
const references = (source: string) =>
|
|
analyzeCommonJsStaticFixture(source).references.map(
|
|
({ operation, specifier }) => ({ operation, specifier }),
|
|
);
|
|
expect(
|
|
references(
|
|
`const box={load:require};box.load.${method}.extra=1;box.load.${method}(${valid});`,
|
|
),
|
|
).toContainEqual({ operation: "load", specifier: "./valid.cjs" });
|
|
expect(
|
|
references(
|
|
`const box={load:require};box.load.${method}=custom;box.load.${method}(${invented});`,
|
|
),
|
|
).not.toContainEqual({ operation: "load", specifier: "./invented.cjs" });
|
|
},
|
|
);
|
|
it("preserves a native resolver before a later loader receiver escape", () => {
|
|
const references = (source: string) =>
|
|
analyzeCommonJsStaticFixture(source).references.map(
|
|
({ operation, specifier }) => ({ operation, specifier }),
|
|
);
|
|
expect(
|
|
references(
|
|
'require.resolve.paths=custom;require.resolve("./first.cjs");require.call.extra=1;require.call(null,"./second.cjs");',
|
|
),
|
|
).toEqual([
|
|
{ operation: "resolve", specifier: "./first.cjs" },
|
|
{ operation: "load", specifier: "./second.cjs" },
|
|
]);
|
|
expect(
|
|
references('opaque(require);require.resolve("./unsafe.cjs");').at(-1),
|
|
).toEqual({
|
|
operation: "load",
|
|
specifier: null,
|
|
});
|
|
for (const source of [
|
|
'function run(){require.resolve("./unsafe.cjs");opaque(require);}run();run();',
|
|
'function mutate(){opaque(require)}mutate();require.resolve("./unsafe.cjs");',
|
|
]) {
|
|
expect(references(source)).not.toContainEqual({
|
|
operation: "resolve",
|
|
specifier: "./unsafe.cjs",
|
|
});
|
|
}
|
|
});
|
|
it("retains consecutive native resolver calls while keeping replaced or escaped loaders uncertain", () => {
|
|
const references = (source: string) =>
|
|
analyzeCommonJsStaticFixture(source).references.map(
|
|
({ operation, specifier }) => ({ operation, specifier }),
|
|
);
|
|
expect(
|
|
references('require.resolve("./first.js");require.resolve("./second.js");'),
|
|
).toEqual([
|
|
{ operation: "resolve", specifier: "./first.js" },
|
|
{ operation: "resolve", specifier: "./second.js" },
|
|
]);
|
|
for (const prefix of [
|
|
"require.resolve=custom;",
|
|
"opaque(require);",
|
|
"const box={r:require};box.r.resolve=custom;",
|
|
'Object.defineProperty(require,"resolve",{value:custom});',
|
|
])
|
|
expect(
|
|
references(`${prefix}require.resolve("./second.js");`).at(-1),
|
|
).toEqual({
|
|
operation: "load",
|
|
specifier: null,
|
|
});
|
|
});
|
|
it.each([
|
|
[
|
|
"proven factory",
|
|
`${imported}const require=make(import.meta.url);require('node:path');`,
|
|
1,
|
|
"node:path",
|
|
],
|
|
[
|
|
"renamed proven factory",
|
|
`${imported}const load=make(import.meta.url);load('node:path');`,
|
|
1,
|
|
"node:path",
|
|
],
|
|
[
|
|
"opaque replacement factory",
|
|
"import*as mod from'node:module';mod.default.createRequire=replacement;const require=mod.default.createRequire(import.meta.url);require('node:path');",
|
|
0,
|
|
null,
|
|
],
|
|
] as const)(
|
|
"uses invocation proof for external source association after a %s",
|
|
(_name, source, associated, specifier) => {
|
|
const parsed = parseJavaScriptSource(source);
|
|
if (parsed === null) throw new Error("Expected factory proof fixture");
|
|
expect(referenceSourceRequireCalls(parsed, "entry.mjs").size).toBe(
|
|
associated,
|
|
);
|
|
const analysis = analyzeJavaScriptStaticSource(source);
|
|
expect(analysis.parse_error_count).toBe(0);
|
|
expect(
|
|
analysis.references.filter(({ kind }) => kind === "require"),
|
|
).toEqual([expect.objectContaining({ kind: "require", specifier })]);
|
|
},
|
|
);
|
|
|
|
it("associates a renamed createRequire resolve method with search options", () => {
|
|
const parsed = parseJavaScriptSource(
|
|
`${imported}const load=make(import.meta.url);load.resolve('node:path',{paths:[]});`,
|
|
);
|
|
if (parsed === null) throw new Error("Expected factory proof fixture");
|
|
expect(referenceSourceRequireCalls(parsed, "entry.mjs").size).toBe(1);
|
|
});
|
|
|
|
it("keeps current-file and mixed createRequire bases distinct", () => {
|
|
const currentFile = parseJavaScriptSource(
|
|
`${imported}const load=make(import.meta.url);load('./dep.cjs');`,
|
|
);
|
|
if (currentFile === null)
|
|
throw new Error("Expected current-file base fixture");
|
|
const currentCall = [
|
|
...referenceSourceRequireCalls(currentFile, "sub/main.mjs").keys(),
|
|
][0];
|
|
expect(
|
|
referenceSourceRequireCalls(currentFile, "sub/main.mjs").get(currentCall!)
|
|
?.resolutionBase,
|
|
).toBe("source-file");
|
|
|
|
const mixed = parseJavaScriptSource(
|
|
`${imported}const load=make(flag?import.meta.url:new URL('./sub/entry.cjs',import.meta.url));load('./dep.cjs');`,
|
|
);
|
|
if (mixed === null) throw new Error("Expected mixed-base fixture");
|
|
const mixedBases = referenceSourceRequireCalls(mixed, "main.mjs");
|
|
expect(
|
|
[...mixedBases.values()].map(({ resolutionBase }) => resolutionBase),
|
|
).toEqual(["unknown"]);
|
|
});
|
|
|
|
it.each(["require.resolve", "require.resolve?.", "require?.resolve"])(
|
|
"keeps search-path uncertainty for historical %s calls",
|
|
(callee) => {
|
|
const source = [
|
|
'require.resolve("./dep.cjs");',
|
|
'require.resolve("./same-path.cjs", { paths: [__dirname] });',
|
|
'require.resolve("./dep.cjs", { paths: [__dirname + "/sub"] });',
|
|
'require.resolve("./dep.cjs", { paths: [] });',
|
|
'function shadow(__dirname) { require.resolve("./shadow.cjs", { paths: [__dirname] }); }',
|
|
]
|
|
.join("\n")
|
|
.replaceAll("require.resolve", callee);
|
|
const result = parseReferenceSourceImports(
|
|
"main.cjs",
|
|
new TextEncoder().encode(source),
|
|
"JavaScript",
|
|
);
|
|
expect(
|
|
result.relationships.map(({ to, resolution, parse_state }) => ({
|
|
to,
|
|
resolution,
|
|
parse_state,
|
|
})),
|
|
).toEqual([
|
|
{ to: "./dep.cjs", resolution: "internal", parse_state: "parsed" },
|
|
{ to: "./same-path.cjs", resolution: "internal", parse_state: "parsed" },
|
|
{ to: "./dep.cjs", resolution: "unknown", parse_state: "partial" },
|
|
{ to: "./dep.cjs", resolution: "unknown", parse_state: "partial" },
|
|
{ to: "./shadow.cjs", resolution: "unknown", parse_state: "partial" },
|
|
]);
|
|
},
|
|
);
|
|
|
|
it.each(["load.resolve", "load.resolve?.", "load?.resolve"])(
|
|
"retains a mutated %s request as an unknown fact",
|
|
(callee) => {
|
|
const source = `${imported}const load=make(import.meta.url);load.resolve=custom;${callee}("./dep.cjs");`;
|
|
const result = parseReferenceSourceImports(
|
|
"main.mjs",
|
|
new TextEncoder().encode(source),
|
|
"JavaScript",
|
|
);
|
|
expect(
|
|
result.relationships.filter(({ kind }) => kind === "requires"),
|
|
).toEqual([
|
|
expect.objectContaining({
|
|
from_path: "main.mjs",
|
|
to: "./dep.cjs",
|
|
kind: "requires",
|
|
resolution: "unknown",
|
|
parse_state: "partial",
|
|
}),
|
|
]);
|
|
expect(
|
|
analyzeJavaScriptStaticSource(source).references.filter(
|
|
({ kind }) => kind === "require",
|
|
),
|
|
).toEqual([
|
|
expect.objectContaining({
|
|
kind: "require",
|
|
operation: "load",
|
|
specifier: null,
|
|
}),
|
|
]);
|
|
},
|
|
);
|
|
|
|
it("does not treat bundler factory resolve members as Node resolvers", () => {
|
|
const analysis = analyzeJavaScriptStaticSource(`
|
|
globalThis.webpackChunkApp.push([[1], {
|
|
"factory": function (module, exports, loader) {
|
|
loader.resolve("./not-a-dependency.cjs");
|
|
}
|
|
}]);
|
|
`);
|
|
expect(analysis.references.filter(({ kind }) => kind === "require")).toEqual(
|
|
[],
|
|
);
|
|
});
|
|
|
|
it.each([
|
|
[
|
|
"captured wrapper",
|
|
"const load=require;load('node:path')",
|
|
"node:path",
|
|
true,
|
|
],
|
|
[
|
|
"wrapper member",
|
|
"const box={load:require};box.load('node:path')",
|
|
"node:path",
|
|
true,
|
|
],
|
|
[
|
|
"wrapper array",
|
|
"const box=[require];box[0]('node:path')",
|
|
"node:path",
|
|
true,
|
|
],
|
|
[
|
|
"projected wrapper",
|
|
"const {load}={load:require};load('node:path')",
|
|
"node:path",
|
|
true,
|
|
],
|
|
[
|
|
"captured before wrapper replacement",
|
|
"const load=require;require=()=>({});load('node:path')",
|
|
"node:path",
|
|
true,
|
|
],
|
|
[
|
|
"shadowed wrapper",
|
|
"const require=()=>({});const load=require;load('node:path')",
|
|
undefined,
|
|
false,
|
|
],
|
|
[
|
|
"delegate replaced after capture",
|
|
"const load=require;module.require=()=>({});load('node:path')",
|
|
null,
|
|
false,
|
|
],
|
|
] as const)(
|
|
"retains the selected native %s value",
|
|
(_name, source, specifier, host) => {
|
|
const module = new Module("wrapper-alias.cjs");
|
|
const result = runInNewContext(source, {
|
|
module,
|
|
require: (request: string) => module.require(request),
|
|
});
|
|
expect(result.join === path.join).toBe(host);
|
|
const analysis = analyzeCommonJsStaticFixture(source);
|
|
expect(analysis.parse_error_count).toBe(0);
|
|
expect(analysis.references).toEqual(
|
|
specifier === undefined
|
|
? []
|
|
: [expect.objectContaining({ kind: "require", specifier })],
|
|
);
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
[
|
|
"renamed loader",
|
|
"const load=make(import.meta.url);load('./shared.cjs')",
|
|
"./shared.cjs",
|
|
],
|
|
[
|
|
"captured renamed loader",
|
|
"const load=make(import.meta.url);const saved=load;saved('./shared.cjs')",
|
|
"./shared.cjs",
|
|
],
|
|
[
|
|
"object member loader",
|
|
"const box={load:make(import.meta.url)};box.load('./shared.cjs')",
|
|
"./shared.cjs",
|
|
],
|
|
[
|
|
"destructured loader",
|
|
"const {load}={load:make(import.meta.url)};load('./shared.cjs')",
|
|
"./shared.cjs",
|
|
],
|
|
[
|
|
"array member loader",
|
|
"const box=[make(import.meta.url)];box[0]('./shared.cjs')",
|
|
"./shared.cjs",
|
|
],
|
|
[
|
|
"generic loader invocation",
|
|
"const load=make(import.meta.url);load.call(null,'./shared.cjs')",
|
|
"./shared.cjs",
|
|
],
|
|
[
|
|
"reflected created loader",
|
|
"const load=make(import.meta.url);Reflect.apply(load,null,['./shared.cjs'])",
|
|
"./shared.cjs",
|
|
],
|
|
[
|
|
"unknown create base",
|
|
"const load=make('/elsewhere/entry.cjs');load('./shared.cjs')",
|
|
null,
|
|
],
|
|
[
|
|
"conditional create bases",
|
|
"const load=flag?make(import.meta.url):make('/elsewhere/entry.cjs');load('./shared.cjs')",
|
|
null,
|
|
],
|
|
[
|
|
"opaque loader replacement",
|
|
"let load=make(import.meta.url);load=replacement;load('./shared.cjs')",
|
|
null,
|
|
],
|
|
[
|
|
"copied child with a later leaf replacement",
|
|
"const box={child:{load:make(import.meta.url)}};const copy={...box};box.child.load=make('/elsewhere/entry.cjs');copy.child.load('./shared.cjs')",
|
|
null,
|
|
],
|
|
[
|
|
"copied child before a parent replacement",
|
|
"const box={child:{load:make(import.meta.url)}};const copy={...box};box.child={load:make('/elsewhere/entry.cjs')};copy.child.load('./shared.cjs')",
|
|
"./shared.cjs",
|
|
],
|
|
[
|
|
"spread excludes an inherited loader",
|
|
"const box={__proto__:{load:make(import.meta.url)}};const copy={...box};copy.load('./shared.cjs')",
|
|
undefined,
|
|
],
|
|
[
|
|
"local loader replacement",
|
|
"let load=make(import.meta.url);load=()=>({});load('./shared.cjs')",
|
|
undefined,
|
|
],
|
|
["local loader", "const load=()=>({});load('./shared.cjs')", undefined],
|
|
[
|
|
"shadowed create factory",
|
|
"function f(make){const load=make(import.meta.url);load('./shared.cjs')}",
|
|
undefined,
|
|
],
|
|
] as const)(
|
|
"classifies %s from the selected loader value",
|
|
(_name, body, specifier) => {
|
|
const result = analyzeJavaScriptStaticSource(`${imported}${body};`);
|
|
expect(result.parse_error_count).toBe(0);
|
|
const calls = result.references.filter(({ kind }) => kind === "require");
|
|
expect(calls).toEqual(
|
|
specifier === undefined ? [] : [expect.objectContaining({ specifier })],
|
|
);
|
|
},
|
|
);
|
|
|
|
it("preserves a renamed native createRequire callable", () => {
|
|
const load = createRequire(import.meta.url);
|
|
const saved = load;
|
|
expect(saved("node:path").join).toBe(path.join);
|
|
});
|
|
|
|
it.each(["leaf", "parent"])(
|
|
"keeps the native created loader identity after a copied child %s write",
|
|
(write) => {
|
|
const selected = runInNewContext(
|
|
`const original=make(base);const outside=make(outsideBase);
|
|
const box={child:{load:original}};const copy={...box};
|
|
${write === "leaf" ? "box.child.load=outside" : "box.child={load:outside}"};
|
|
({actual:copy.child.load,expected:${write === "leaf" ? "outside" : "original"}});`,
|
|
{
|
|
make: createRequire,
|
|
base: import.meta.url,
|
|
outsideBase: new URL("./elsewhere/entry.cjs", import.meta.url).href,
|
|
},
|
|
);
|
|
expect(selected.actual).toBe(selected.expected);
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
[
|
|
"direct Reflect.apply",
|
|
"Reflect.apply(module.require,module,['node:path'])",
|
|
true,
|
|
],
|
|
[
|
|
"captured Reflect.apply",
|
|
"const invoke=Reflect.apply;invoke(module.require,module,['node:path'])",
|
|
true,
|
|
],
|
|
[
|
|
"generic Reflect.apply",
|
|
"Reflect.apply.call(null,module.require,module,['node:path'])",
|
|
true,
|
|
],
|
|
[
|
|
"Reflect.apply captured before replacement",
|
|
"const invoke=Reflect.apply;Reflect.apply=()=>({join:false});invoke(module.require,module,['node:path'])",
|
|
true,
|
|
],
|
|
[
|
|
"local Reflect",
|
|
"const Reflect={apply(){return {join:false}}};Reflect.apply(module.require,module,['node:path'])",
|
|
false,
|
|
],
|
|
[
|
|
"local callable",
|
|
"Reflect.apply(()=>({join:false}),module,['node:path'])",
|
|
false,
|
|
],
|
|
[
|
|
"local module",
|
|
"const module={require(){return {join:false}}};Reflect.apply(module.require,module,['node:path'])",
|
|
false,
|
|
],
|
|
] as const)(
|
|
"preserves module delegate provenance through %s",
|
|
(_name, source, host) => {
|
|
const result = runInNewContext(source, {
|
|
module: new Module("reflected-loader.cjs"),
|
|
});
|
|
expect(result.join === path.join).toBe(host);
|
|
const analysis = analyzeJavaScriptStaticSource(source);
|
|
expect(analysis.parse_error_count).toBe(0);
|
|
expect(analysis.references).toEqual(
|
|
host
|
|
? [expect.objectContaining({ kind: "require", specifier: null })]
|
|
: [],
|
|
);
|
|
},
|
|
);
|
|
|
|
it("retains unknown historical .js loader scope until a package manifest establishes it", () => {
|
|
const bytes = new TextEncoder().encode('require("./dep.cjs");');
|
|
const relationships = (
|
|
mode?: "module" | "commonjs" | "unspecified" | "unknown",
|
|
) =>
|
|
parseReferenceSourceImports("main.js", bytes, "JavaScript", mode)
|
|
.relationships;
|
|
expect(relationships()).toEqual([
|
|
expect.objectContaining({
|
|
to: "./dep.cjs",
|
|
resolution: "unknown",
|
|
parse_state: "partial",
|
|
}),
|
|
]);
|
|
expect(relationships("module")).toEqual([]);
|
|
for (const mode of ["commonjs", "unspecified"] as const)
|
|
expect(relationships(mode)).toEqual([
|
|
expect.objectContaining({
|
|
to: "./dep.cjs",
|
|
resolution: "internal",
|
|
parse_state: "parsed",
|
|
}),
|
|
]);
|
|
});
|
|
|
|
it.each(["main.cjs", "main.MJS"])(
|
|
"does not use an unproven invocation receiver as a dependency: %s",
|
|
(path) => {
|
|
for (const source of [
|
|
'require.call=custom;require.call("./receiver.cjs","./actual.cjs");',
|
|
'require.call=custom;require.call.call(require,"./receiver.cjs","./actual.cjs");',
|
|
'require.call=custom;require.call.apply(require,["./receiver.cjs","./actual.cjs"]);',
|
|
]) {
|
|
const result = parseReferenceSourceImports(
|
|
path,
|
|
new TextEncoder().encode(source),
|
|
"JavaScript",
|
|
);
|
|
expect(
|
|
result.relationships.filter(({ kind }) => kind === "requires"),
|
|
).toEqual([]);
|
|
}
|
|
},
|
|
);
|