From 5b2624cddb0b4d723e54ba9b910b7e1219c3f1f0 Mon Sep 17 00:00:00 2001 From: fibibot Date: Thu, 14 May 2026 00:10:26 +0000 Subject: [PATCH 1/5] fix(vite): strip `deno::` prefix from server-side stack traces (#3464) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stack traces from server-side errors in Fresh 2 Vite apps leaked the internal `\0deno::{type}::{specifier}` virtual module IDs used by the deno plugin, displayed as `deno::N::…` in frames like `at async eval (deno::0::https:/jsr.io/@fresh/core/2.1.0/src/render.ts:9:332)`. The plugin's `load` hook returned the loader's code with its inline `//# sourceMappingURL=` data URL untouched. That inline source map embeds `sources` as a *relative* path (e.g. `packages/plugin-vite/demo/routes/tests/throw.tsx`). When V8 cannot apply the source map, it falls back to the module ID — the `\0deno::…` virtual ID — leaking it into stack frames. When V8 *does* apply the relative source path, it can be resolved against the module URL's directory, producing doubled paths like `packages/fresh/src/packages/fresh/src/segments.ts`. This patch: - Rewrites the inline source map in the deno-specifier branch of the `load` hook so `sources` is the absolute specifier with an empty `sourceRoot`, while keeping the inline `//# sourceMappingURL=` comment in place for V8 to pick up natively. - Returns the rewritten map as the explicit `map` field too, so Rollup composes consistent `sources` during production builds. - For the Babel-transformed JSX branch, rewrites both the inline and the explicit map's `sources` to the specifier with `sourceRoot: ""`. - Skips the rewrite for non-JS media (JSON, CSS, …) since appending a `//# sourceMappingURL=` comment would corrupt those payloads. - Adds unit tests for the two new helpers, plus dev-server and prod-build integration tests that fetch `/tests/throw` and assert the response contains neither `deno::N::` nor doubled cwd paths. Closes bartlomieju/orchid-inbox#56 --- packages/plugin-vite/src/plugins/deno.ts | 114 +++++++++++++++++- packages/plugin-vite/src/plugins/deno_test.ts | 95 +++++++++++++++ packages/plugin-vite/tests/build_test.ts | 19 +++ packages/plugin-vite/tests/dev_server_test.ts | 18 +++ 4 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 packages/plugin-vite/src/plugins/deno_test.ts diff --git a/packages/plugin-vite/src/plugins/deno.ts b/packages/plugin-vite/src/plugins/deno.ts index 6cefed7e463..269772d76ab 100644 --- a/packages/plugin-vite/src/plugins/deno.ts +++ b/packages/plugin-vite/src/plugins/deno.ts @@ -190,12 +190,32 @@ export function deno(): Plugin { isDev, }); if (maybeJsx !== null) { + if (maybeJsx.map) { + // Babel reads the loader's inline source map but inherits its + // `sources` (relative path). Rewrite to the absolute specifier + // with an empty `sourceRoot` so stack frames show the real URL + // instead of the `\0deno::…` virtual ID and don't double the cwd. + maybeJsx.map.sources = [specifier]; + maybeJsx.map.sourceRoot = ""; + } + // Babel emits its own inline `//# sourceMappingURL=` comment via + // `sourceMaps: "both"`. Rewrite that one too so V8 stack traces + // (which read the inline map natively) point at the specifier. + maybeJsx.code = rewriteInlineSourceMapSources( + maybeJsx.code, + specifier, + ); return maybeJsx; } - return { - code, - }; + // For non-JS media (JSON, CSS, …) the loaded code is not JavaScript, + // so appending a `//# sourceMappingURL=` comment would corrupt it. + // Those modules don't show up in JS stack traces, so leave them alone. + if (!isJsMediaType(result.mediaType)) { + return { code }; + } + + return rewriteLoadedSourceMap(code, specifier); } if (id.startsWith("\0")) { @@ -368,6 +388,94 @@ function getDenoType(id: string, type: string): RequestedModuleType { } } +// Builds a 1:1 (line-by-line, column 0) source map so that Vite/V8 can +// rewrite stack frames from `\0deno::{type}::{specifier}` virtual IDs back +// to the original specifier. Uses an absolute URL/path in `sources` combined +// with an empty `sourceRoot` to avoid Vite resolving sources relative to the +// cwd, which would produce doubled paths like +// `packages/fresh/src/packages/fresh/src/segments.ts`. +export function identitySourceMap(source: string, code: string) { + const lineCount = code.split("\n").length; + // VLQ "AAAA" → [genCol=0, srcIdx=0, srcLine=0, srcCol=0] + // ";AACA" → newline, then deltas [0, 0, +1, 0] + let mappings = "AAAA"; + for (let i = 1; i < lineCount; i++) { + mappings += ";AACA"; + } + return { + version: 3, + sources: [source], + sourcesContent: [code], + names: [] as string[], + mappings, + sourceRoot: "", + }; +} + +const INLINE_SOURCE_MAP_RE = + /\n?\/\/# sourceMappingURL=data:application\/json(?:;charset=[^;]+)?;base64,([A-Za-z0-9+/=]+)\s*$/; + +// `ssrLoader.load()` returns code with an inline `//# sourceMappingURL=` data +// URL whose `sources` array contains a path relative to the cwd. Without +// fixing this up, stack traces either leak the `\0deno::…` virtual module ID +// (when V8 falls back to the module ID) or display doubled cwd paths like +// `packages/fresh/src/packages/fresh/src/segments.ts` (the caveat described +// in denoland/fresh#3464). +// +// Rewrites the inline source map (if any) so `sources` is the absolute +// specifier with an empty `sourceRoot`. The inline comment itself is kept in +// place so that V8 picks it up natively for stack-trace translation. When the +// loader did not emit a source map, an identity map is appended so the +// virtual ID is still replaced in stack traces. The same map is also +// returned alongside the code so Rollup (production builds) sees consistent +// `sources` during source-map chaining. +export function rewriteLoadedSourceMap(code: string, source: string) { + const match = code.match(INLINE_SOURCE_MAP_RE); + if (match !== null) { + try { + const parsed = JSON.parse(atob(match[1])); + parsed.sources = [source]; + parsed.sourceRoot = ""; + const start = match.index!; + const reencoded = btoa(JSON.stringify(parsed)); + const newCode = code.slice(0, start) + + `\n//# sourceMappingURL=data:application/json;base64,${reencoded}`; + return { code: newCode, map: parsed }; + } catch { + // fall through to identity map + } + } + const map = identitySourceMap(source, code); + const encoded = btoa(JSON.stringify(map)); + return { + code: + `${code}\n//# sourceMappingURL=data:application/json;base64,${encoded}`, + map, + }; +} + +// Rewrites just the `sources` of an existing inline `//# sourceMappingURL=` +// comment in JS code, without touching mappings or appending a new comment +// when none exists. Used after Babel has already produced its own source +// map and we only need to fix the specifier. +export function rewriteInlineSourceMapSources( + code: string, + source: string, +): string { + const match = code.match(INLINE_SOURCE_MAP_RE); + if (match === null) return code; + try { + const parsed = JSON.parse(atob(match[1])); + parsed.sources = [source]; + parsed.sourceRoot = ""; + const reencoded = btoa(JSON.stringify(parsed)); + return code.slice(0, match.index!) + + `\n//# sourceMappingURL=data:application/json;base64,${reencoded}`; + } catch { + return code; + } +} + function babelTransform( options: { media: MediaType; diff --git a/packages/plugin-vite/src/plugins/deno_test.ts b/packages/plugin-vite/src/plugins/deno_test.ts new file mode 100644 index 00000000000..c4e757a57de --- /dev/null +++ b/packages/plugin-vite/src/plugins/deno_test.ts @@ -0,0 +1,95 @@ +import { expect } from "@std/expect/expect"; +import { identitySourceMap, rewriteLoadedSourceMap } from "./deno.ts"; + +Deno.test("identitySourceMap - single line", () => { + const map = identitySourceMap("jsr:@fresh/core/src/render.ts", "foo();"); + expect(map.version).toBe(3); + expect(map.sources).toEqual(["jsr:@fresh/core/src/render.ts"]); + expect(map.sourcesContent).toEqual(["foo();"]); + expect(map.sourceRoot).toBe(""); + // Single line => single segment, no `;` separators. + expect(map.mappings).toBe("AAAA"); +}); + +Deno.test("identitySourceMap - multi line", () => { + const code = "a;\nb;\nc;"; + const map = identitySourceMap("https://example.com/mod.js", code); + // Three lines => `AAAA` + `;AACA` per additional line. + expect(map.mappings).toBe("AAAA;AACA;AACA"); + expect(map.sources).toEqual(["https://example.com/mod.js"]); + expect(map.sourceRoot).toBe(""); +}); + +Deno.test("identitySourceMap - keeps full URL in sources to avoid doubled cwd paths", () => { + // Regression test for the caveat from denoland/fresh#3464: Vite resolves + // source-map `sources` entries relative to the (virtual) module location + // and falls back to concatenating the cwd. Using absolute URLs/paths in + // `sources` together with `sourceRoot: ""` prevents the doubling. + const map = identitySourceMap( + "file:///abs/path/to/segments.ts", + "export const x = 1;", + ); + expect(map.sources[0]).toBe("file:///abs/path/to/segments.ts"); + expect(map.sourceRoot).toBe(""); +}); + +Deno.test("rewriteLoadedSourceMap - rewrites inline map sources to absolute", () => { + const inputMap = { + version: 3, + sources: ["packages/foo/src/bar.ts"], + sourcesContent: ["original;"], + names: [], + mappings: "AAAA", + }; + const inlined = btoa(JSON.stringify(inputMap)); + const body = "transformed;"; + const code = + `${body}\n//# sourceMappingURL=data:application/json;base64,${inlined}`; + + const result = rewriteLoadedSourceMap( + code, + "https://jsr.io/@fresh/core/2.1.0/src/render.ts", + ); + + // The returned map (also embedded inline) has `sources` rewritten. + expect(result.map.sources).toEqual([ + "https://jsr.io/@fresh/core/2.1.0/src/render.ts", + ]); + expect(result.map.sourceRoot).toBe(""); + expect(result.map.mappings).toBe("AAAA"); + + // The inline `//# sourceMappingURL=` comment is preserved (rewritten) so + // V8 can apply it natively for stack-trace translation. + const m = result.code.match( + /\/\/# sourceMappingURL=data:application\/json;base64,([A-Za-z0-9+/=]+)/, + ); + expect(m).not.toBeNull(); + const reparsed = JSON.parse(atob(m![1])); + expect(reparsed.sources).toEqual([ + "https://jsr.io/@fresh/core/2.1.0/src/render.ts", + ]); + expect(reparsed.sourceRoot).toBe(""); + // The original transformed code is preserved before the comment. + expect(result.code.startsWith(body)).toBe(true); +}); + +Deno.test("rewriteLoadedSourceMap - appends identity map when no inline map", () => { + const result = rewriteLoadedSourceMap( + "foo();\nbar();", + "jsr:@fresh/core/src/render.ts", + ); + expect(result.map.sources).toEqual(["jsr:@fresh/core/src/render.ts"]); + expect(result.map.sourceRoot).toBe(""); + expect(result.map.mappings).toBe("AAAA;AACA"); + // The original code is preserved and an inline source map is appended. + expect(result.code.startsWith("foo();\nbar();")).toBe(true); + expect(result.code).toContain( + "//# sourceMappingURL=data:application/json;base64,", + ); +}); + +Deno.test("rewriteLoadedSourceMap - falls back to identity on malformed inline map", () => { + const code = "foo();\n//# sourceMappingURL=data:application/json;base64,!!!"; + const result = rewriteLoadedSourceMap(code, "file:///abs.ts"); + expect(result.map.sources).toEqual(["file:///abs.ts"]); +}); diff --git a/packages/plugin-vite/tests/build_test.ts b/packages/plugin-vite/tests/build_test.ts index 8edafe63d2f..4c73eb1b8c5 100644 --- a/packages/plugin-vite/tests/build_test.ts +++ b/packages/plugin-vite/tests/build_test.ts @@ -128,6 +128,25 @@ integrationTest("vite build - without routes/ dir", async () => { ); }); +integrationTest( + "vite build - strips deno:: from server stack traces", + async () => { + // Regression test for denoland/fresh#3464. Without identity source + // maps in the deno plugin, server-side stack traces contained the + // virtual module ID `\0deno::{type}::{specifier}` (rendered as + // `deno::N::…`) instead of the original specifier. + await launchProd( + { cwd: viteResult.tmp }, + async (address) => { + const res = await fetch(`${address}/tests/throw`); + const text = await res.text(); + expect(text).toContain("FAIL"); + expect(text).not.toMatch(/deno::\d+::/); + }, + ); + }, +); + integrationTest("vite build - load json inside npm package", async () => { await launchProd( { cwd: viteResult.tmp }, diff --git a/packages/plugin-vite/tests/dev_server_test.ts b/packages/plugin-vite/tests/dev_server_test.ts index ffc30f92ee2..be0b1de0790 100644 --- a/packages/plugin-vite/tests/dev_server_test.ts +++ b/packages/plugin-vite/tests/dev_server_test.ts @@ -471,6 +471,24 @@ integrationTest("vite dev - source mapped stack traces", async () => { expect(text).toContain("throw.tsx:5:11"); }); +integrationTest( + "vite dev - strips deno:: prefix from stack traces", + async () => { + // Regression test for denoland/fresh#3464: the deno plugin's virtual + // module IDs (`\0deno::{type}::{specifier}`) used to leak into stack + // traces. The plugin now rewrites the loader's source maps so frames + // reference the original `jsr:` / `https:` / `file://` specifier. + const res = await fetch(`${demoServer.address()}/tests/throw`); + const text = await res.text(); + expect(text).toContain("FAIL"); + expect(text).not.toMatch(/deno::\d+::/); + // And no doubled cwd paths from a missing `sourceRoot` (the caveat + // described in the issue). + const cwd = Deno.cwd().replaceAll("\\", "/"); + expect(text).not.toContain(`${cwd}${cwd}`); + }, +); + integrationTest("vite dev - client side ", async () => { await withBrowser(async (page) => { await page.goto(`${demoServer.address()}/tests/head_counter`, { From 37a39867e379d4fe27e429b827e11bc1f080e50c Mon Sep 17 00:00:00 2001 From: fibibot Date: Fri, 15 May 2026 13:35:12 +0000 Subject: [PATCH 2/5] ci: rerun pr checks From 007df57656e0006cb0da695dcf6068fca7785295 Mon Sep 17 00:00:00 2001 From: fibibot Date: Fri, 15 May 2026 13:37:45 +0000 Subject: [PATCH 3/5] fix: type hmr reconnect timer portably --- packages/fresh/src/runtime/client/dev_hmr.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fresh/src/runtime/client/dev_hmr.ts b/packages/fresh/src/runtime/client/dev_hmr.ts index b86c98e0358..106b42d055d 100644 --- a/packages/fresh/src/runtime/client/dev_hmr.ts +++ b/packages/fresh/src/runtime/client/dev_hmr.ts @@ -3,7 +3,7 @@ import { IS_BROWSER } from "../shared.ts"; let ws: WebSocket; let revision = 0; -let reconnectTimer: number; +let reconnectTimer: ReturnType; const backoff = [ // Wait 100ms initially, because we could also be // disconnected because of a form submit. From 0a47c4d27e2eba4945caa05d701e960b58c17ca5 Mon Sep 17 00:00:00 2001 From: fibibot Date: Fri, 15 May 2026 13:58:50 +0000 Subject: [PATCH 4/5] fix(vite): lazy load deno loader --- packages/plugin-vite/src/plugins/deno.ts | 35 ++++++++++++++++-------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/packages/plugin-vite/src/plugins/deno.ts b/packages/plugin-vite/src/plugins/deno.ts index 269772d76ab..f5ce96d7853 100644 --- a/packages/plugin-vite/src/plugins/deno.ts +++ b/packages/plugin-vite/src/plugins/deno.ts @@ -1,10 +1,8 @@ import type { Plugin } from "vite"; -import { - type Loader, - MediaType, - RequestedModuleType, - ResolutionMode, - Workspace, +import type { + Loader, + MediaType as DenoMediaType, + RequestedModuleType as DenoRequestedModuleType, } from "@deno/loader"; import * as path from "@std/path"; import * as babel from "@babel/core"; @@ -17,8 +15,15 @@ const { default: babelReact } = await import("@babel/preset-react"); const BUILTINS = new Set(builtinModules); +type LoaderModule = typeof import("@deno/loader"); + +let MediaType: LoaderModule["MediaType"]; +let RequestedModuleType: LoaderModule["RequestedModuleType"]; +let ResolutionMode: LoaderModule["ResolutionMode"]; +let Workspace: LoaderModule["Workspace"]; + interface DenoState { - type: RequestedModuleType; + type: DenoRequestedModuleType; } export function deno(): Plugin { @@ -38,6 +43,12 @@ export function deno(): Plugin { isDev = env.command === "serve"; }, async configResolved() { + const loaderModule = await import("@deno/loader"); + MediaType = loaderModule.MediaType; + RequestedModuleType = loaderModule.RequestedModuleType; + ResolutionMode = loaderModule.ResolutionMode; + Workspace = loaderModule.Workspace; + // TODO: Pass conditions ssrLoader = await new Workspace({ platform: "node", @@ -309,7 +320,7 @@ export function deno(): Plugin { }; } -function isJsMediaType(media: MediaType): boolean { +function isJsMediaType(media: DenoMediaType): boolean { switch (media) { case MediaType.JavaScript: case MediaType.Jsx: @@ -342,13 +353,13 @@ function isDenoSpecifier(str: unknown): str is DenoSpecifier { return typeof str === "string" && str.startsWith("\0deno::"); } -function toDenoSpecifier(spec: string, type: RequestedModuleType) { +function toDenoSpecifier(spec: string, type: DenoRequestedModuleType) { return `\0deno::${type}::${spec}`; } function parseDenoSpecifier( spec: DenoSpecifier, -): { type: RequestedModuleType; specifier: string } { +): { type: DenoRequestedModuleType; specifier: string } { const match = spec.match(/^\0deno::([^:]+)::(.*)$/)!; let specifier = match[2]; @@ -372,7 +383,7 @@ function parseDenoSpecifier( return { type: +match[1], specifier }; } -function getDenoType(id: string, type: string): RequestedModuleType { +function getDenoType(id: string, type: string): DenoRequestedModuleType { switch (type) { case "json": return RequestedModuleType.Json; @@ -478,7 +489,7 @@ export function rewriteInlineSourceMapSources( function babelTransform( options: { - media: MediaType; + media: DenoMediaType; ssr: boolean; code: string; id: string; From c45de7bbd5299e5c79467e04a6a59fde12b7c369 Mon Sep 17 00:00:00 2001 From: fibibot Date: Fri, 15 May 2026 14:22:05 +0000 Subject: [PATCH 5/5] fix(vite): handle server builtins in deno --- deno.lock | 8 ++---- packages/plugin-vite/deno.json | 2 +- packages/plugin-vite/src/mod.ts | 3 +++ packages/plugin-vite/src/plugins/deno.ts | 34 +++++++++++++++++++++--- 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/deno.lock b/deno.lock index 3d5970098a8..2f9f32c9da7 100644 --- a/deno.lock +++ b/deno.lock @@ -9,7 +9,6 @@ "jsr:@deno/esbuild-plugin@^1.2.0": "1.2.1", "jsr:@deno/graph@0.86": "0.86.9", "jsr:@deno/graph@~0.82.3": "0.82.3", - "jsr:@deno/loader@0.4": "0.4.0", "jsr:@deno/loader@~0.3.10": "0.3.14", "jsr:@marvinh-test/fresh-island@^0.0.3": "0.0.3", "jsr:@marvinh-test/import-json@^0.0.1": "0.0.1", @@ -177,7 +176,7 @@ "@deno/esbuild-plugin@1.2.1": { "integrity": "df629467913adc1f960149fdfa3a3430ba8c20381c310fba096db244e6c3c9f6", "dependencies": [ - "jsr:@deno/loader@~0.3.10", + "jsr:@deno/loader", "jsr:@std/path@^1.1.1", "npm:esbuild@~0.25.5" ] @@ -191,9 +190,6 @@ "@deno/loader@0.3.14": { "integrity": "97bc63a6cc2d27a60bcdc953f588c5213331d866d44212eebb24cebfb9b011ca" }, - "@deno/loader@0.4.0": { - "integrity": "6c1b18cfa18592740613ce79e15625c24268d60dbfc54bebfb5153bf512c536b" - }, "@marvinh-test/fresh-island@0.0.3": { "integrity": "6d06b6009b7dfba9bba28e941e03e6ff652c4ef4f2fbfdf4b78741abd6c6c1c6", "dependencies": [ @@ -6078,7 +6074,7 @@ }, "packages/plugin-vite": { "dependencies": [ - "jsr:@deno/loader@0.4", + "jsr:@deno/loader@~0.3.10", "jsr:@fresh/core@2", "jsr:@marvinh-test/import-json@^0.0.1", "npm:@babel/core@^7.28.0", diff --git a/packages/plugin-vite/deno.json b/packages/plugin-vite/deno.json index 57af17393e6..f12166aebaf 100644 --- a/packages/plugin-vite/deno.json +++ b/packages/plugin-vite/deno.json @@ -23,7 +23,7 @@ "imports": { "@babel/core": "npm:@babel/core@^7.28.0", "@babel/preset-react": "npm:@babel/preset-react@^7.27.1", - "@deno/loader": "jsr:@deno/loader@^0.4.0", + "@deno/loader": "jsr:@deno/loader@~0.3.10", "@marvinh-test/import-json": "jsr:@marvinh-test/import-json@^0.0.1", "@remix-run/node-fetch-server": "npm:@remix-run/node-fetch-server@^0.12.0", "@prefresh/vite": "npm:@prefresh/vite@^2.4.8", diff --git a/packages/plugin-vite/src/mod.ts b/packages/plugin-vite/src/mod.ts index c49c244526c..43cb2c7406a 100644 --- a/packages/plugin-vite/src/mod.ts +++ b/packages/plugin-vite/src/mod.ts @@ -167,6 +167,9 @@ export function fresh(config?: FreshViteConfig): Plugin[] { }, }, ssr: { + resolve: { + builtins: [], + }, build: { manifest: true, emitAssets: true, diff --git a/packages/plugin-vite/src/plugins/deno.ts b/packages/plugin-vite/src/plugins/deno.ts index f5ce96d7853..5ddc26dbafe 100644 --- a/packages/plugin-vite/src/plugins/deno.ts +++ b/packages/plugin-vite/src/plugins/deno.ts @@ -14,6 +14,7 @@ import { builtinModules } from "node:module"; const { default: babelReact } = await import("@babel/preset-react"); const BUILTINS = new Set(builtinModules); +const NODE_BUILTIN_PREFIX = "\0fresh-node-builtin::"; type LoaderModule = typeof import("@deno/loader"); @@ -65,11 +66,13 @@ export function deno(): Plugin { return true; }, async resolveId(id, importer, options) { - if (BUILTINS.has(id)) { - // `node:` prefix is not included in builtins list. - if (!id.startsWith("node:")) { - id = `node:${id}`; + const builtin = id.startsWith("node:") ? id.slice("node:".length) : id; + if (BUILTINS.has(builtin)) { + id = id.startsWith("node:") ? id : `node:${id}`; + if (this.environment.config.consumer === "server") { + return NODE_BUILTIN_PREFIX + id; } + // `node:` prefix is not included in builtins list. return { id, external: true, @@ -179,6 +182,10 @@ export function deno(): Plugin { } }, async load(id) { + if (id.startsWith(NODE_BUILTIN_PREFIX)) { + return nodeBuiltinModule(id.slice(NODE_BUILTIN_PREFIX.length)); + } + const loader = this.environment.config.consumer === "server" ? ssrLoader : browserLoader; @@ -399,6 +406,25 @@ function getDenoType(id: string, type: string): DenoRequestedModuleType { } } +async function nodeBuiltinModule(id: string) { + const names = Object.keys(await import(id)); + return [ + `const mod = await Function("id", "return import(id)")(${ + JSON.stringify(id) + });`, + "const requireValue = mod.default ?? mod;", + "export { requireValue as __require };", + "export default requireValue;", + ...names + .filter((name) => + /^[$_\p{ID_Start}][$_\u200c\u200d\p{ID_Continue}]*$/u + .test(name) + ) + .filter((name) => name !== "default") + .map((name) => `export const ${name} = mod[${JSON.stringify(name)}];`), + ].join("\n"); +} + // Builds a 1:1 (line-by-line, column 0) source map so that Vite/V8 can // rewrite stack frames from `\0deno::{type}::{specifier}` virtual IDs back // to the original specifier. Uses an absolute URL/path in `sources` combined