Skip to content

Commit b8a6abc

Browse files
Fix name minifying (#10252)
1 parent bcbf374 commit b8a6abc

3 files changed

Lines changed: 59 additions & 4 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
3+
changeKind: fix
4+
packages:
5+
- "@typespec/bundler"
6+
---
7+
8+
Fix name minifying

packages/bundler/src/bundler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ async function createEsBuildContext(
239239
format: "esm",
240240
target: "es2024",
241241
minify,
242+
keepNames: minify,
242243
plugins: [virtualPlugin, nodeModulesPolyfillPlugin({}), ...plugins],
243244
});
244245
}

packages/bundler/test/test.test.ts

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,54 @@
1-
import { ok } from "assert";
2-
import { describe, it } from "vitest";
1+
import { mkdtemp, rm, writeFile } from "fs/promises";
2+
import { tmpdir } from "os";
3+
import { join } from "path";
4+
import { describe, expect, it } from "vitest";
5+
import { createTypeSpecBundle } from "../src/bundler.js";
36

47
describe("bundler", () => {
5-
it("works", () => {
6-
ok(true);
8+
/**
9+
* Regression test: TypeSpec decorator functions are identified by their `.name` property
10+
* at runtime (e.g., `d.decorator.name === "$armResourceOperations"`).
11+
* When esbuild minifies library bundles, it can rename functions, changing their `.name`.
12+
* The bundler must use `keepNames: true` to preserve function names in minified output.
13+
*/
14+
it("preserves function names when minifying", async () => {
15+
const tmpDir = await mkdtemp(join(tmpdir(), "typespec-bundler-test-"));
16+
try {
17+
// Create a minimal TypeSpec library with named decorator function exports
18+
await writeFile(
19+
join(tmpDir, "package.json"),
20+
JSON.stringify({
21+
name: "test-lib",
22+
version: "1.0.0",
23+
main: "index.js",
24+
tspMain: "main.tsp",
25+
peerDependencies: {},
26+
}),
27+
);
28+
await writeFile(
29+
join(tmpDir, "main.tsp"),
30+
['import "./index.js";', "namespace TestLib;"].join("\n"),
31+
);
32+
await writeFile(
33+
join(tmpDir, "index.js"),
34+
[
35+
"export function $testDecorator(context, target) { }",
36+
"export function $anotherDecorator(context, target) { }",
37+
].join("\n"),
38+
);
39+
40+
const bundle = await createTypeSpecBundle(tmpDir, { minify: true });
41+
const indexFile = bundle.files.find((f) => f.filename === "index.js");
42+
expect(indexFile, "index.js should be in bundle output").toBeDefined();
43+
44+
// The bundle's jsSourceFiles should contain modules where the function
45+
// .name property is preserved. With keepNames, esbuild emits a helper
46+
// that restores the original name via Object.defineProperty.
47+
// We verify the original function names appear as string literals in the output.
48+
expect(indexFile!.content).toContain('"$testDecorator"');
49+
expect(indexFile!.content).toContain('"$anotherDecorator"');
50+
} finally {
51+
await rm(tmpDir, { recursive: true });
52+
}
753
});
854
});

0 commit comments

Comments
 (0)