fix(javascript): share source-map resolution and preserve path uncert… · codehornets/rea@3ae826c · GitHub
Skip to content

Commit 3ae826c

Browse files
committed
fix(javascript): share source-map resolution and preserve path uncertainty
1 parent 73dd8d6 commit 3ae826c

16 files changed

Lines changed: 551 additions & 67 deletions

‎docs/javascript-application-workflows.md‎

Lines changed: 6 additions & 3 deletions
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { expect, it } from "vitest";
2+
import { TraceMap } from "@jridgewell/trace-mapping";
3+
4+
import { analyzeJavaScriptArtifactFiles } from "./JavaScriptArtifactAnalysis.js";
5+
6+
const HASH = "a".repeat(64);
7+
8+
it("retains batched TraceMap-resolved paths and null source positions", () => {
9+
const sourceMapText = JSON.stringify({
10+
version: 3,
11+
file: "main.js",
12+
sourceRoot: "../src/",
13+
sources: ["cafe\u0301.ts", null, "other.ts"],
14+
sourcesContent: ["export const value = 1;", null, "export {};"],
15+
names: [],
16+
mappings: "",
17+
});
18+
const analysis = analyzeJavaScriptArtifactFiles({
19+
files: [
20+
{
21+
path: "dist/main.js.map",
22+
container_sha256: HASH,
23+
sha256: HASH,
24+
bytes: Buffer.byteLength(sourceMapText),
25+
inventory_artifact_id: "artifact_test",
26+
kind: "source-map",
27+
unpacked: false,
28+
text: { included: true, value: sourceMapText },
29+
},
30+
],
31+
containers: [],
32+
text_bytes_read: Buffer.byteLength(sourceMapText),
33+
invalid_utf8_files: 0,
34+
});
35+
const originals = analysis.source_maps[0]?.sources;
36+
const original = originals?.[0];
37+
const producerPath = new TraceMap(
38+
{
39+
version: 3,
40+
file: "main.js",
41+
sourceRoot: "../src/",
42+
sources: ["cafe\u0301.ts", "", "other.ts"],
43+
names: [],
44+
mappings: "",
45+
},
46+
"file:///artifact/dist/main.js.map",
47+
).resolvedSources[0];
48+
49+
if (producerPath === undefined)
50+
throw new Error("TraceMap should resolve its single source declaration");
51+
expect(originals?.[1]?.reference).toBeNull();
52+
expect(originals?.[2]?.reference?.resolution).toEqual({
53+
kind: "artifact-relative",
54+
path: "src/other.ts",
55+
});
56+
expect(original?.reference).toMatchObject({
57+
source_name: "cafe\u0301.ts",
58+
source_root: "../src/",
59+
map_path: "dist/main.js.map",
60+
resolution: {
61+
kind: "artifact-relative",
62+
path: "src/cafe\u0301.ts",
63+
},
64+
});
65+
expect(original?.reference?.resolution).toEqual({
66+
kind: "artifact-relative",
67+
path: decodeURIComponent(
68+
new URL(producerPath).pathname.slice("/artifact/".length),
69+
),
70+
});
71+
});

‎src/application/javascript/JavaScriptArtifactAnalysis.ts‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { createHash } from "node:crypto";
33
import { moduleKindForJavaScriptReference } from "./JavaScriptArtifactPathResolution.js";
44
import { setImmediate } from "node:timers/promises";
55

6-
import { resolveJavaScriptSourceMapReference } from "../../domain/javascript/javascriptSourceMapPaths.js";
6+
import { resolveJavaScriptSourceMapReferences } from "../../domain/javascript/javascriptSourceMapPaths.js";
77
import { analyzeParsedJavaScriptStaticSourceSteps } from "../../domain/javascript/javascriptStaticAnalysis.js";
88
import {
99
analyzeParsedJavaScriptSemanticsSteps,
@@ -561,21 +561,27 @@ const collectSourceMapOriginals = (
561561
"Source map sourceRoot must be a string or null.",
562562
);
563563
const root = typeof map.sourceRoot === "string" ? map.sourceRoot : null;
564-
for (const [index, raw] of names.entries()) {
564+
const sourceNames: (string | null)[] = [];
565+
for (const raw of names) {
565566
if (raw !== null && typeof raw !== "string")
566567
return invalidSourceMap(
567568
file,
568569
"Source map contains a non-string source name.",
569570
);
571+
sourceNames.push(raw);
572+
}
573+
const references = resolveJavaScriptSourceMapReferences(
574+
sourceNames,
575+
root,
576+
file.path,
577+
);
578+
for (const [index, reference] of references.entries()) {
570579
const rawContent = Array.isArray(contents) ? contents[index] : undefined;
571580
const content = typeof rawContent === "string" ? rawContent : null;
572581
sources.push({
573582
source_index: index,
574583
declaration_index: declarationIndex++,
575-
reference:
576-
typeof raw === "string"
577-
? resolveJavaScriptSourceMapReference(raw, root, file.path)
578-
: null,
584+
reference,
579585
content,
580586
content_sha256: content === null ? null : sha256(content),
581587
});

‎src/browser/WebSourceMapFetcher.ts‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import * as t from "@babel/types";
22
import {
33
decodeValidatedSourceMapLeaves,
44
isBeforeSourceMapLeafStop,
5-
resolveSourceMapSource,
65
} from "../javascript/sourceMaps/DecodedSourceMap.js";
76

87
import { sanitizeBrowserUrl } from "../domain/browserObservation.js";
@@ -21,6 +20,7 @@ import { webSourceMapsSchema } from "../domain/webBundleAnalysis.js";
2120
import { createWebTextArtifact } from "../domain/webContentArtifact.js";
2221
import { safeParseJson } from "../domain/safeJson.js";
2322
import { sourceMapJsonText } from "../domain/sourceMapEnvelope.js";
23+
import { resolveJavaScriptSourceMapUrl } from "../domain/javascript/javascriptSourceMapPaths.js";
2424
import { jsonParts } from "../domain/jsonSerialization.js";
2525
import {
2626
inspectSourceMapValue,
@@ -867,7 +867,11 @@ const preflightResolvedSourceBytes = (
867867
const sourceKey = source;
868868
let resolved = sourcesForRoot.get(sourceKey);
869869
if (resolved === undefined)
870-
resolved = resolveSourceMapSource(source, map.sourceRoot, mapUrl);
870+
resolved = resolveJavaScriptSourceMapUrl(
871+
source,
872+
map.sourceRoot,
873+
mapUrl,
874+
);
871875
const bytes = Buffer.byteLength(resolved, "utf8");
872876
if (bytes > SOURCE_MAP_RESPONSE_BYTES - resolvedBytes)
873877
throw resolvedSourceLimitFailure();

‎src/domain/analysisView/analysisView.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { expect, it } from "vitest";
22

3-
import { resolveJavaScriptSourceMapReference } from "../javascript/javascriptSourceMapPaths.js";
43
import { functionDossierSchema } from "../hopperValues.js";
54
import { ghidraFunctionDossier } from "../ghidraValues.fixture.js";
65
import { JAVASCRIPT_APPLICATION_EVIDENCE_EXAMPLE } from "../../contracts/javascript/javascriptRuntimeReconciliationExample.js";
@@ -24,13 +23,14 @@ import {
2423
completeWithinViewCoverage,
2524
pageViewCoverage,
2625
} from "./analysisViewCoverage.js";
26+
import { sourceMapReference } from "../javascript/javascriptApplicationGraph.fixture.js";
2727
import { javascriptModulePath } from "./javascriptApplicationView.js";
28-
2928
import {
3029
inspectAnalysisViewInputSchema,
3130
projectAnalysisView,
3231
type AnalysisViewParent,
3332
} from "./analysisView.js";
33+
3434
const layoutParent = (): AnalysisViewParent => {
3535
const evidence = analysisViewLayoutEvidence();
3636
return {
@@ -532,7 +532,7 @@ it("pages actual modules, including unknown and empty paths, without unrelated r
532532
observations: original.observations.map((observation) => ({
533533
...(kind === "source-module"
534534
? {
535-
source_map_reference: resolveJavaScriptSourceMapReference(
535+
source_map_reference: sourceMapReference(
536536
"",
537537
null,
538538
"renderer.js",

‎src/domain/javascript/javascriptApplicationGraph.fixture.ts‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
} from "./javascriptApplicationGraph.js";
77
import type { ApplicationNode } from "./javascriptApplicationGraphSchemas.js";
88
import type { ApplicationGraphEvidence } from "./javascriptApplicationEvidenceSchemas.js";
9+
import { resolveJavaScriptSourceMapReferences } from "./javascriptSourceMapPaths.js";
910

1011
export const APPLICATION_GRAPH_DIGESTS = {
1112
package: "1".repeat(64),
@@ -22,6 +23,20 @@ export const completeCoverage = {
2223
limits: [],
2324
} satisfies ApplicationGraphEvidence["coverage"];
2425

26+
export const sourceMapReference = (
27+
sourceName: string,
28+
sourceRoot: string | null,
29+
mapPath: string,
30+
) => {
31+
const reference = resolveJavaScriptSourceMapReferences(
32+
[sourceName],
33+
sourceRoot,
34+
mapPath,
35+
)[0];
36+
if (reference == null) throw new TypeError("Expected a source-map reference");
37+
return reference;
38+
};
39+
2540
const extractor = (operation: string) => ({
2641
name: "rea-test-fixture",
2742
version: "1.0.0",

‎src/domain/javascript/javascriptApplicationGraph.test.ts‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,9 @@ import {
2020
inferredArtifactEvidence,
2121
nodeByLabel,
2222
runtimeEvidence,
23+
sourceMapReference,
2324
unknownEvidence,
2425
} from "./javascriptApplicationGraph.fixture.js";
25-
26-
import { resolveJavaScriptSourceMapReference } from "./javascriptSourceMapPaths.js";
2726
const createLargeNativeExportGraph = (): JavaScriptApplicationGraph => {
2827
const properties = Object.fromEntries(
2928
Array.from({ length: 1_001 }, (_, index) => [
@@ -438,7 +437,7 @@ describe("JavaScript Application Graph: source maps and observations", () => {
438437
observations: [
439438
{
440439
label: "src/editor.ts",
441-
source_map_reference: resolveJavaScriptSourceMapReference(
440+
source_map_reference: sourceMapReference(
442441
"webpack:///src/editor.ts",
443442
null,
444443
"dist/app.js.map",
@@ -535,7 +534,7 @@ describe("JavaScript Application Graph: source-map digest consistency", () => {
535534
observations: [
536535
{
537536
label: null,
538-
source_map_reference: resolveJavaScriptSourceMapReference(
537+
source_map_reference: sourceMapReference(
539538
"src/editor.ts",
540539
null,
541540
"dist/app.js.map",

‎src/domain/javascript/javascriptSourceMapPaths.ts‎

Lines changed: 55 additions & 12 deletions

0 commit comments

Comments
 (0)