mirror of
https://github.com/morluto/rea
synced 2026-10-27 06:33:32 -04:00
fix(web): retain source-map declaration provenance
This commit is contained in:
@@ -121,6 +121,11 @@ preceding browser mappings when a nested section starts with a gap or has no
|
||||
child leaves. Raw map text, original source identities and the dedicated decoder
|
||||
profile are preserved.
|
||||
|
||||
Browser `original_module_edges` carry a `declaration_index` joining each edge to
|
||||
its `original_sources` declaration, including repeated source URLs across indexed
|
||||
sections. Imports are deduplicated within each declaration. Older observations
|
||||
may omit the index; their edge declaration identity remains unknown.
|
||||
|
||||
Browser collection accepts up to 64 MiB of map responses per inspection and
|
||||
preflights indexed sections, sources, names, generated rows and mapping segments
|
||||
against a 262144-record decode budget before upstream flattening. Indexed maps
|
||||
|
||||
@@ -868,7 +868,6 @@ const originalModuleEdges = (
|
||||
) => void,
|
||||
): OriginalModuleEdges => {
|
||||
const edges: ParsedSourceMapItem["original_module_edges"] = [];
|
||||
const seen = new Set<string>();
|
||||
const incomplete: string[] = [];
|
||||
for (const source of sources) {
|
||||
check();
|
||||
@@ -892,6 +891,7 @@ const originalModuleEdges = (
|
||||
// A recovered program still yields the imports it did parse, but nodes
|
||||
// after an unrecoverable point are missing, so the edges are a subset.
|
||||
if (parsed.errors.length > 0) incomplete.push(sourceName);
|
||||
const seen = new Set<string>();
|
||||
let unboundRequires: ReadonlySet<string> | undefined;
|
||||
traverseJavaScriptAst(parsed, {
|
||||
enter: (node) => {
|
||||
@@ -920,10 +920,11 @@ const originalModuleEdges = (
|
||||
)
|
||||
return;
|
||||
}
|
||||
const key = `${sourceName}\0${kind}\0${specifier}`;
|
||||
const key = `${kind}\0${specifier}`;
|
||||
if (seen.has(key)) return;
|
||||
seen.add(key);
|
||||
const edge = {
|
||||
declaration_index: source.declaration_index,
|
||||
from_source: sourceName,
|
||||
kind,
|
||||
specifier,
|
||||
|
||||
@@ -254,6 +254,8 @@ const originalSourceSchema = z.object({
|
||||
artifact: webTextArtifactSchema.nullable(),
|
||||
});
|
||||
const originalModuleEdgeSchema = z.object({
|
||||
// Historical observations omit this link to original_sources declarations.
|
||||
declaration_index: z.int().min(0).optional(),
|
||||
from_source: z.string(),
|
||||
kind: z.enum(["static_import", "dynamic_import", "require"]),
|
||||
specifier: z.string(),
|
||||
|
||||
@@ -31,6 +31,120 @@ const expectInvalidIncludedSourceMaps = (result: WebSourceMaps): void => {
|
||||
expectInvalidSourceMaps({ ...result, status: "unavailable" });
|
||||
};
|
||||
|
||||
it.each(["regular", "indexed"])(
|
||||
"retains declaration identity for duplicate source URLs in HTTP %s maps",
|
||||
async (format) => {
|
||||
const contents = [
|
||||
null,
|
||||
'import "./shared.js"; import "./shared.js"; import "./first.js";',
|
||||
'import "./shared.js"; import "./second.js";',
|
||||
];
|
||||
const leaf = (sourcesContent: (string | null)[]) => ({
|
||||
version: 3,
|
||||
sources: sourcesContent.map(() => "same.ts"),
|
||||
sourcesContent,
|
||||
names: [],
|
||||
mappings: "",
|
||||
});
|
||||
const map =
|
||||
format === "regular"
|
||||
? leaf(contents)
|
||||
: {
|
||||
version: 3,
|
||||
sections: contents.map((content, line) => ({
|
||||
offset: { line, column: 0 },
|
||||
map: leaf([content]),
|
||||
})),
|
||||
};
|
||||
const server = createServer((_incoming, response) => {
|
||||
response.setHeader("content-type", "application/source-map+json");
|
||||
response.end(JSON.stringify(map));
|
||||
});
|
||||
try {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
server.once("error", reject);
|
||||
server.listen(0, "127.0.0.1", resolve);
|
||||
});
|
||||
const address = server.address();
|
||||
if (address === null || typeof address === "string")
|
||||
throw new TypeError("Expected a TCP listener address");
|
||||
const localOrigin = `http://127.0.0.1:${String(address.port)}`;
|
||||
const url = `${localOrigin}/app.js.map`;
|
||||
const result = await fetchWebSourceMapsOk(
|
||||
[{ ...request, fetchUrl: url, declaredUrl: url }],
|
||||
input({ allowed_origins: [localOrigin] }),
|
||||
);
|
||||
const item = result.items[0];
|
||||
if (item?.status !== "included") throw new Error("map not included");
|
||||
expect(item.limitation).toBeNull();
|
||||
expect(
|
||||
item.original_sources.map((source) => ({
|
||||
declaration_index: source.declaration_index,
|
||||
source_index: source.source_index,
|
||||
source: source.source,
|
||||
text: source.artifact?.text ?? null,
|
||||
})),
|
||||
).toEqual(
|
||||
contents.map((text, declaration_index) => ({
|
||||
declaration_index,
|
||||
source_index: format === "regular" ? declaration_index : 0,
|
||||
source: `${localOrigin}/same.ts`,
|
||||
text,
|
||||
})),
|
||||
);
|
||||
expect(item.original_module_edges).toEqual(
|
||||
[
|
||||
[1, "./shared.js"],
|
||||
[1, "./first.js"],
|
||||
[2, "./shared.js"],
|
||||
[2, "./second.js"],
|
||||
].map(([declaration_index, specifier]) => ({
|
||||
declaration_index,
|
||||
from_source: `${localOrigin}/same.ts`,
|
||||
kind: "static_import",
|
||||
specifier,
|
||||
resolved_source: new URL(String(specifier), url).href,
|
||||
})),
|
||||
);
|
||||
expect(webSourceMapsSchema.parse(result)).toEqual(result);
|
||||
const historical = {
|
||||
...result,
|
||||
items: [
|
||||
{
|
||||
...item,
|
||||
original_module_edges: item.original_module_edges.map(
|
||||
({ declaration_index: _index, ...edge }) => edge,
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(webSourceMapsSchema.parse(historical)).toEqual(historical);
|
||||
expectInvalidSourceMaps({
|
||||
...result,
|
||||
items: [
|
||||
{
|
||||
...item,
|
||||
original_module_edges: [
|
||||
{
|
||||
...item.original_module_edges[0],
|
||||
declaration_index: -1,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
} finally {
|
||||
server.closeAllConnections();
|
||||
if (server.listening)
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
server.close((error) =>
|
||||
error === undefined ? resolve() : reject(error),
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["regular", "indexed"])(
|
||||
"collects an HTTP %s source map with omitted names without accepting invalid names",
|
||||
async (format) => {
|
||||
|
||||
Reference in New Issue
Block a user