Files
reverse-engineering-anything/src/domain/javascript/javascriptStaticAnalysisMainModule.test.ts
T

104 lines
3.2 KiB
TypeScript

import { Module } from "node:module";
import { runInNewContext } from "node:vm";
import { expect, it } from "vitest";
import { analyzeJavaScriptStaticSource } from "../../../tests/fixtures/javascriptStaticAnalysis.js";
const cases = [
["native main", "require.main.require('node:path');", true, true],
[
"captured main",
"const main=require.main;main.require('node:path');",
true,
true,
],
[
"captured delegate",
"const load=require.main.require;load.call(require.main,'node:path');",
true,
true,
],
["wrapper var", "var require;require.main.require('node:path');", true, true],
[
"local require",
"const require={main:{require:()=>({kind:'local'})}};require.main.require('node:path');",
false,
false,
],
[
"local alias",
"const load={main:{require:()=>({kind:'local'})}};load.main.require('node:path');",
false,
false,
],
[
"wrapper main replacement stays conservative",
"var require;require.main={require:()=>({kind:'local'})};require.main.require('node:path');",
false,
true,
],
[
"conditional main replacement",
"var require;if(flag)require.main={require:()=>({kind:'local'})};require.main.require('node:path');",
false,
true,
],
[
"main captured before replacement",
"const main=require.main;require.main={require:()=>({kind:'local'})};main.require('node:path');",
true,
true,
],
[
"ambient main replacement stays conservative",
"require.main={require:()=>({kind:'local'})};require.main.require('node:path');",
false,
true,
],
[
"shared deep child local write",
"const old={child:{inner:{make:()=>module}}};const box={...old};old.child={inner:box.child.inner};old.child.inner.make=()=>({require(){}});box.child.inner.make().require('node:path');",
false,
false,
],
[
"shared deep child host write",
"const old={child:{inner:{make:()=>({})}}};const box={...old};old.child={inner:box.child.inner};old.child.inner.make=()=>module;box.child.inner.make().require('node:path');",
true,
true,
],
] as const;
it.each(cases)(
"retains main-module source for %s",
(_name, source, host, unknown) => {
const module = new Module("main-module-source.cjs");
const require = Object.assign(
(request: string) => module.require(request),
{ main: module },
);
const observed = runInNewContext(source, { module, require, flag: true });
expect(typeof observed?.join === "function").toBe(host);
const analysis = analyzeJavaScriptStaticSource(source);
expect(analysis.parse_error_count).toBe(0);
expect(analysis.references).toEqual(
unknown
? [expect.objectContaining({ kind: "require", specifier: null })]
: [],
);
},
);
it("keeps unknown wrapper eligibility separate from local replacement", () => {
const local = analyzeJavaScriptStaticSource(
'var require; require={main:{require:()=>0}}; require.main.require("node:path");',
);
expect(local.references).toEqual([]);
const conditional = analyzeJavaScriptStaticSource(
'var require; if(flag) require={main:{require:()=>0}}; require.main.require("node:path");',
);
expect(conditional.references).toMatchObject([
{ kind: "require", specifier: null },
]);
});