diff --git a/src/runtime/bake/bake.private.d.ts b/src/runtime/bake/bake.private.d.ts index 1c8649f07bbb..ae3d2e1676bf 100644 --- a/src/runtime/bake/bake.private.d.ts +++ b/src/runtime/bake/bake.private.d.ts @@ -144,14 +144,3 @@ declare module "react-dom/server.node" { options: RenderToPipeableStreamOptions, ): PipeableStream; } - -declare module "bun:wrap" { - export const __name: unique symbol; - export const __legacyDecorateClassTS: unique symbol; - export const __legacyDecorateParamTS: unique symbol; - export const __legacyMetadataTS: unique symbol; - export const __using: unique symbol; - export const __callDispose: unique symbol; - export const __MEMO_CACHE_SENTINEL: unique symbol; - export const __EARLY_RETURN_SENTINEL: unique symbol; -} diff --git a/src/runtime/bake/hmr-module.ts b/src/runtime/bake/hmr-module.ts index b61c2c12380e..4395bb1059b4 100644 --- a/src/runtime/bake/hmr-module.ts +++ b/src/runtime/bake/hmr-module.ts @@ -6,16 +6,7 @@ // Some build failures from the bundler surface as runtime errors here, such as // `require` on a module with transitive top-level await, or a missing export. // This was done to make incremental updates as isolated as possible. -import { - __callDispose, - __EARLY_RETURN_SENTINEL, - __legacyDecorateClassTS, - __legacyDecorateParamTS, - __legacyMetadataTS, - __MEMO_CACHE_SENTINEL, - __name, - __using, -} from "../../runtime.bun"; +import * as runtimeHelpers from "../../runtime.bun"; // This import is different based on client vs server side. // On the server, remapping is done automatically. import { type SourceMapURL, derefMapping } from "#stack-trace"; @@ -946,16 +937,7 @@ declare global { // bun:bake/server, bun:bake/client, and bun:wrap are // provided by this file instead of the bundler -registerSynthetic("bun:wrap", { - __name, - __legacyDecorateClassTS, - __legacyDecorateParamTS, - __legacyMetadataTS, - __using, - __callDispose, - __MEMO_CACHE_SENTINEL, - __EARLY_RETURN_SENTINEL, -}); +registerSynthetic("bun:wrap", runtimeHelpers); if (side === "server") { registerSynthetic("bun:bake/server", { diff --git a/test/bake/dev-and-prod.test.ts b/test/bake/dev-and-prod.test.ts index 26c769669420..162fdf53dc45 100644 --- a/test/bake/dev-and-prod.test.ts +++ b/test/bake/dev-and-prod.test.ts @@ -1,6 +1,6 @@ // Tests which apply to both dev and prod. They are run twice. import { writeFileSync } from "node:fs"; -import { devAndProductionTest, devTest, emptyHtmlFile, WAIT_MULTIPLIER } from "./bake-harness"; +import { devAndProductionTest, devTest, emptyHtmlFile, minimalFramework, WAIT_MULTIPLIER } from "./bake-harness"; const hmrSelfAcceptingModule = (label: string) => ` console.log(${JSON.stringify(label)}); @@ -202,6 +202,71 @@ devTest("using runtime import", { ); }, }); +// Standard (TC39) decorators, `accessor` fields, and the `#private` members of a +// decorated class are lowered into calls to helpers imported from "bun:wrap" +// (__decoratorStart, __decorateElement, __runInitializers, __decoratorMetadata, +// __privateAdd, __privateGet, __privateSet, __privateIn, __privateMethod). The +// dev server does not bundle runtime.js; its HMR runtime provides "bun:wrap" +// itself, so it has to export everything the production runtime does. This +// class imports all nine helpers. +const standardDecoratorsSource = ` + const applied: string[] = []; + function dec(_value: unknown, ctx: DecoratorContext) { + applied.push(ctx.kind + ":" + String(ctx.name)); + } + @dec + class Foo { + @dec static s = 1; + @dec #p = 2; + @dec accessor x = 3; + @dec m() {} + @dec get g() { return this.#p; } + @dec #pm() { return 5; } + accessor y = 4; + has(o: object) { return #p in o; } + pm() { return this.#pm(); } + } + const foo = new Foo(); + foo.x += 10; + foo.y += 10; + export const decorated = applied.sort().join(","); + export const values = [Foo.s, foo.x, foo.y, foo.g, foo.pm(), foo.has(foo), foo.has({})].join(","); +`; +const expectedDecorated = "accessor:x,class:Foo,field:#p,field:s,getter:g,method:#pm,method:m"; +const expectedValues = "1,13,14,2,5,true,false"; +devAndProductionTest("standard decorators runtime import", { + files: { + "index.html": emptyHtmlFile({ + styles: [], + scripts: ["index.ts"], + }), + "index.ts": ` + import { decorated, values } from "./decorated"; + console.log(decorated); + console.log(values); + `, + "decorated.ts": standardDecoratorsSource, + }, + async test(dev) { + await using c = await dev.client("/"); + await c.expectMessage(expectedDecorated, expectedValues); + }, +}); +devTest("standard decorators runtime import on the server", { + framework: minimalFramework, + files: { + "decorated.ts": standardDecoratorsSource, + "routes/index.ts": ` + import { decorated, values } from "../decorated"; + export default function (req, meta) { + return new Response(decorated + "\\n" + values); + } + `, + }, + async test(dev) { + await dev.fetch("/").equals(expectedDecorated + "\n" + expectedValues); + }, +}); devTest("hmr handles rapid consecutive edits", { files: { "index.html": emptyHtmlFile({