Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/build/plugins/sourcemap-min.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function sourcemapMinify() {
// Remove x_google_ignoreList
delete sourcemap.x_google_ignoreList;

if ((sourcemap.sources || []).some((s) => s.includes("node_modules"))) {
if ((sourcemap.sources || []).every((s) => s.includes("node_modules"))) {
sourcemap.mappings = ""; // required key
}

Expand Down
112 changes: 112 additions & 0 deletions test/unit/sourcemap-min.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { describe, expect, it } from "vitest";
import { sourcemapMinify } from "../../src/build/plugins/sourcemap-min.ts";

type BundleAsset = { type: "asset"; source: string };

function createSourcemapAsset(sourcemap: {
sources?: string[];
sourcesContent?: string[];
mappings?: string;
x_google_ignoreList?: number[];
}): BundleAsset {
return {
type: "asset",
source: JSON.stringify({
version: 3,
sources: [],
mappings: "AAAA,CAAC",
...sourcemap,
}),
};
}

function runPlugin(bundle: Record<string, BundleAsset>) {
const plugin = sourcemapMinify();
(plugin.generateBundle as Function).call(null, {}, bundle);
const results: Record<string, ReturnType<typeof JSON.parse>> = {};
for (const [key, asset] of Object.entries(bundle)) {
if (key.endsWith(".map")) {
results[key] = JSON.parse(asset.source);
}
}
return results;
}

describe("sourcemapMinify", () => {
it("removes sourcesContent from all sourcemaps", () => {
const bundle = {
"index.mjs.map": createSourcemapAsset({
sources: ["src/index.ts"],
sourcesContent: ["export default 42;"],
}),
};
const results = runPlugin(bundle);
expect(results["index.mjs.map"].sourcesContent).toBeUndefined();
});

it("removes x_google_ignoreList from all sourcemaps", () => {
const bundle = {
"index.mjs.map": createSourcemapAsset({
sources: ["src/index.ts"],
x_google_ignoreList: [0],
}),
};
const results = runPlugin(bundle);
expect(results["index.mjs.map"].x_google_ignoreList).toBeUndefined();
});

it("wipes mappings for pure library chunks", () => {
const bundle = {
"_libs/express.mjs.map": createSourcemapAsset({
sources: [
"../../node_modules/express/index.js",
"../../node_modules/express/lib/router.js",
],
mappings: "AAAA,CAAC",
}),
};
const results = runPlugin(bundle);
expect(results["_libs/express.mjs.map"].mappings).toBe("");
});

it("preserves mappings for pure user code chunks", () => {
const bundle = {
"_routes/api/hello.mjs.map": createSourcemapAsset({
sources: ["src/routes/api/hello.ts"],
mappings: "AAAA,CAAC",
}),
};
const results = runPlugin(bundle);
expect(results["_routes/api/hello.mjs.map"].mappings).toBe("AAAA,CAAC");
});

it("preserves mappings when library is hoisted into user chunk", () => {
const bundle = {
"_routes/api/hello.mjs.map": createSourcemapAsset({
sources: ["src/routes/api/hello.ts", "../../node_modules/some-lib/index.js"],
mappings: "AAAA,CAAC",
}),
};
const results = runPlugin(bundle);
expect(results["_routes/api/hello.mjs.map"].mappings).toBe("AAAA,CAAC");
});

it("skips non-sourcemap files", () => {
const bundle = {
"index.mjs": { type: "asset" as const, source: "console.log(42)" },
};
runPlugin(bundle as any);
expect(bundle["index.mjs"].source).toBe("console.log(42)");
});

it("handles empty sources array", () => {
const bundle = {
"chunk.mjs.map": createSourcemapAsset({
sources: [],
mappings: "AAAA,CAAC",
}),
};
const results = runPlugin(bundle);
expect(results["chunk.mjs.map"].mappings).toBe("");
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "handles empty sources array" assertion expects mappings to be wiped when sources: []. With the current implementation this happens because .every(...) is true for an empty array, but an empty sources list doesn't indicate a "pure library chunk" and can lead to dropping mappings for sourcemaps where sources is empty/missing. Consider changing this test to expect mappings to be preserved for empty/undefined sources, and keep the wiping behavior covered by the "pure library chunks" test (where all sources are under node_modules).

Suggested change
expect(results["chunk.mjs.map"].mappings).toBe("");
expect(results["chunk.mjs.map"].mappings).toBe("AAAA,CAAC");

Copilot uses AI. Check for mistakes.
});
});
Loading