mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
fix(javascript): bind factory export roles to parameter values
This commit is contained in:
@@ -313,7 +313,10 @@ Selected bounded text is then parsed as inert data to recover:
|
||||
- local HTML script entrypoints;
|
||||
- Webpack and Rspack chunk registrations and module factories represented as
|
||||
AST literals, plus esbuild `__commonJS` module tables; recovered esbuild
|
||||
CommonJS exports use its factory's `exports`-then-`module` parameter order;
|
||||
CommonJS exports use its factory's `exports`-then-`module` parameter order.
|
||||
Export receivers and loader helpers retain those calling-convention roles only
|
||||
while the binding at the read still holds its original parameter value;
|
||||
replacing a parameter does not give the replacement object that role;
|
||||
- static imports, dynamic imports, CommonJS `require` calls, workers, and
|
||||
service workers (only `new Worker(new URL(path, import.meta.url))` resolves
|
||||
relative to the calling module; string worker and service-worker paths use
|
||||
|
||||
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
||||
|
||||
import * as t from "@babel/types";
|
||||
|
||||
import { semanticBindingAtRead } from "./javascriptSemanticMutationInitializers.js";
|
||||
import { isUnmodifiedGlobalReference } from "./javascriptSemanticAmbientWrites.js";
|
||||
|
||||
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
|
||||
@@ -60,7 +61,25 @@ export const collectJavaScriptExports = (
|
||||
return (reference: t.Node | null): boolean => {
|
||||
if (!t.isIdentifier(reference)) return false;
|
||||
if (roles === undefined) return reference.name === fallback;
|
||||
return id !== undefined && bindingId(reference) === id;
|
||||
if (id === undefined || bindings === undefined) return false;
|
||||
const binding = resolveSemanticBindingState(
|
||||
bindings,
|
||||
reference,
|
||||
reference.name,
|
||||
);
|
||||
if (binding?.bindingId !== id) return false;
|
||||
// The calling convention owns the parameter's entry value, not every
|
||||
// later value assigned to the same lexical binding.
|
||||
const read = semanticBindingAtRead(
|
||||
binding,
|
||||
reference,
|
||||
bindings.parentsByNode,
|
||||
);
|
||||
return (
|
||||
read.status === "ordered" &&
|
||||
read.binding.initializers.length === 0 &&
|
||||
read.binding.referenceInitializers.length === 0
|
||||
);
|
||||
};
|
||||
};
|
||||
const isExportsAlias = identity("exports", "exports");
|
||||
|
||||
@@ -3,14 +3,13 @@ import { join } from "node:path";
|
||||
|
||||
import { expect } from "vitest";
|
||||
|
||||
import { parseEvidence } from "../../../src/domain/evidence.js";
|
||||
import { javascriptApplicationAnalysisResultSchema } from "../../../src/domain/javascript/javascriptApplicationAnalysis.js";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
import { cliTest } from "../../support/cli/cliFixture.js";
|
||||
import { analyzeJavaScriptDirectory } from "../../support/cli/javascriptApplication.js";
|
||||
|
||||
cliTest(
|
||||
"reports Webpack exports attached to renamed factory parameters",
|
||||
async ({ cli }) => {
|
||||
cliTest.for(["CLI", "stdio MCP"] as const)(
|
||||
"reports Webpack exports attached to original renamed factory parameter values through %s",
|
||||
async (surface, { cli }) => {
|
||||
const root = await createTestTempDirectory("rea-webpack-renamed-exports-");
|
||||
await mkdir(join(root, "src"));
|
||||
await writeFile(
|
||||
@@ -21,20 +20,24 @@ cliTest(
|
||||
Object.defineProperty(__webpack_exports__, "delta", { enumerable: true, get() { return 3; } });
|
||||
__webpack_require__.d(__webpack_exports__.nested, { nested_target: () => 0 });
|
||||
__webpack_exports__ = { rebound: () => 0 };
|
||||
__webpack_exports__.ghost = 4;
|
||||
Object.defineProperty(__webpack_exports__, "definedGhost", {value: 5});
|
||||
__webpack_require__.d(__webpack_exports__, { declaredGhost: () => 6 });
|
||||
module.exports.moduleBefore = 7;
|
||||
module = {exports: {}};
|
||||
module.exports.moduleGhost = 8;
|
||||
__webpack_require__.d({}, { unrelated: () => 0 });
|
||||
{ const exports = {}; __webpack_require__.d(exports, { shadowed_exports: () => 5 }); }
|
||||
},
|
||||
23(m, e, r) { r.d(e, { alpha: () => 1 }); }
|
||||
23(m, e, r) {
|
||||
r.d(e, { alpha: () => 1 });
|
||||
r = {d() {}};
|
||||
r.d(e, { loaderGhost: () => 2 });
|
||||
e.afterLoaderReplacement = 3;
|
||||
}
|
||||
}]);`,
|
||||
);
|
||||
const response = await cli.run({
|
||||
arguments: ["analyze-javascript-application", root, "--format", "json"],
|
||||
environment: { REA_LOG_LEVEL: "silent" },
|
||||
});
|
||||
expect(response.exitCode).toBe(0);
|
||||
const result = javascriptApplicationAnalysisResultSchema.parse(
|
||||
parseEvidence(response.json).normalized_result,
|
||||
);
|
||||
const result = await analyzeJavaScriptDirectory(surface, cli, root);
|
||||
const modules = result.graph.nodes
|
||||
.filter(({ kind }) => kind === "javascript-module")
|
||||
.map(({ observations }) => observations[0]?.properties);
|
||||
@@ -42,9 +45,12 @@ cliTest(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
module_key: "17",
|
||||
exports: ["alpha", "beta.gamma", "delta"],
|
||||
exports: ["alpha", "beta.gamma", "delta", "moduleBefore"],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
module_key: "23",
|
||||
exports: ["afterLoaderReplacement", "alpha"],
|
||||
}),
|
||||
expect.objectContaining({ module_key: "23", exports: ["alpha"] }),
|
||||
]),
|
||||
);
|
||||
expect(result.graph.coverage.status).toBe("complete");
|
||||
|
||||
Reference in New Issue
Block a user