Skip to content

Commit

Permalink
feat(vite-node-miniflare): support error stack sourcemap (#144)
Browse files Browse the repository at this point in the history
  • Loading branch information
hi-ogawa authored Dec 19, 2023
1 parent b6140c3 commit 6d79a7f
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 22 deletions.
5 changes: 5 additions & 0 deletions .changeset/nasty-llamas-glow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hiogawa/vite-node-miniflare": patch
---

feat: improve error stack with sourcemap
22 changes: 15 additions & 7 deletions packages/vite-node-miniflare/examples/basic/e2e/basic.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import process from "node:process";
import { expect, test } from "@playwright/test";

test("basic", async ({ page }) => {
Expand All @@ -13,11 +14,18 @@ test("basic", async ({ page }) => {
expect(pageErrors).toEqual([]);
});

test("server error", async ({ page }) => {
await page.goto("/crash-ssr");
await page
.getByText(
"[vite-node-miniflare error] Error: crash ssr at App (eval:11:11) at eval:674:45 "
)
.click();
test("server error", async ({ request }) => {
const res = await request.get("/crash-ssr");
expect(res.status()).toBe(500);

let text = await res.text();
text = text.replaceAll(/[/].*node_modules/gm, "__NODE_MODULES__");
text = text.replaceAll(process.cwd(), "__CWD__");
expect(text).toMatch(`\
[vite-node-miniflare error]
Error: crash ssr
at Module.crash (__CWD__/src/crash-dep.ts:3:9)
at CrashSsr (__CWD__/src/crash.tsx:5:5)
at __NODE_MODULES__/@hiogawa/tiny-react/dist/index.js:674:45
`);
});
6 changes: 2 additions & 4 deletions packages/vite-node-miniflare/examples/basic/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { useState } from "@hiogawa/tiny-react";
import { TestComponent } from "./component";
import { CrashSsr } from "./crash";

export function App(props: { url: string }) {
const [input, setInput] = useState("");
const [counter, setCounter] = useState(0);

if (import.meta.env.SSR && props.url.includes("crash-ssr")) {
throw new Error("crash ssr");
}

return (
<div style="display: flex; flex-direction: column; gap: 0.5rem; max-width: 300px">
<h4>Vite Node Miniflare Demo</h4>
Expand All @@ -27,6 +24,7 @@ export function App(props: { url: string }) {
<button onclick={() => setCounter(counter + 1)}>+1</button>
</div>
<TestComponent />
<CrashSsr url={props.url} />
</div>
);
}
4 changes: 4 additions & 0 deletions packages/vite-node-miniflare/examples/basic/src/crash-dep.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// test stack trace follows multiple files correctly
export function crash(message: string): never {
throw new Error(message);
}
8 changes: 8 additions & 0 deletions packages/vite-node-miniflare/examples/basic/src/crash.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { crash } from "./crash-dep";

export function CrashSsr(props: { url: string }) {
if (import.meta.env.SSR && props.url.includes("crash-ssr")) {
crash("crash ssr");
}
return <div>Hello</div>;
}
2 changes: 1 addition & 1 deletion packages/vite-node-miniflare/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hiogawa/vite-node-miniflare",
"version": "0.0.0-pre.11",
"version": "0.0.0-pre.12",
"homepage": "https://github.com/hi-ogawa/vite-plugins/tree/main/packages/vite-node-miniflare",
"repository": {
"type": "git",
Expand Down
11 changes: 5 additions & 6 deletions packages/vite-node-miniflare/src/client/polyfills/node-path.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { createUsageChecker } from "./usage-checker";
import * as pathe from "pathe";

// TODO don't have to polyfill?
// https://developers.cloudflare.com/workers/runtime-apis/nodejs/path/

export { dirname } from "pathe";
export default createUsageChecker("node:path");
// used for source map path manipulation?
// https://github.com/vitest-dev/vitest/blob/8dabef860a3f51f5a4c4debc10faa1837fdcdd71/packages/vite-node/src/source-map-handler.ts#L81
export const dirname = pathe.dirname;
export default pathe;
9 changes: 7 additions & 2 deletions packages/vite-node-miniflare/src/client/polyfills/node-vm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ export function __setUnsafeEval(v: any) {
__unsafeEval = v;
}

const runInThisContext: typeof vm.runInThisContext = (code) => {
return __unsafeEval.eval(code);
// Workerd's unsafe eval supports 2nd argument for stacktrace filename
// https://github.com/cloudflare/workerd/blob/5e2544fd2948b53e68831a9b219dc1e9970cf96f/src/workerd/api/unsafe.c%2B%2B#L18-L23
// https://github.com/cloudflare/workerd/pull/1338
// https://github.com/vitest-dev/vitest/blob/8dabef860a3f51f5a4c4debc10faa1837fdcdd71/packages/vite-node/src/client.ts#L414
// https://nodejs.org/docs/latest/api/vm.html#vmrunincontextcode-contextifiedobject-options
const runInThisContext: typeof vm.runInThisContext = (code, options) => {
return __unsafeEval.eval(code, (options as any).filename);
};

export default {
Expand Down
35 changes: 33 additions & 2 deletions packages/vite-node-miniflare/src/client/vite-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
httpClientAdapter,
proxyTinyRpc,
} from "@hiogawa/tiny-rpc";
import { tinyassert } from "@hiogawa/utils";
import type { ViteNodeRunnerOptions } from "vite-node";
import { ViteNodeRunner } from "vite-node/client";
import { installSourcemapsSupport } from "vite-node/source-map";
Expand Down Expand Up @@ -38,10 +39,40 @@ export function createViteNodeClient(options: {
},
});

// TODO: probably this is not enough. (cf. packages/vite-node-miniflare/src/client/polyfills/node-vm.ts)
// Since Vitest's getSourceMap/extractSourceMap relies on `Buffer.from(mapString, 'base64').toString('utf-8')`,
// we inject minimal Buffer polyfill temporary during this function.
// https://github.com/vitest-dev/vitest/blob/8dabef860a3f51f5a4c4debc10faa1837fdcdd71/packages/vite-node/src/source-map.ts#L57-L62
installSourcemapsSupport({
getSourceMap: (source) => runner.moduleCache.getSourceMap(source),
getSourceMap: (source) => {
const teardown = setupBufferPolyfill();
try {
return runner.moduleCache.getSourceMap(source);
} finally {
teardown();
}
},
});

return { rpc, runner };
}

function setupBufferPolyfill() {
const prev = globalThis.Buffer;
globalThis.Buffer = BufferPolyfill as any;
return () => {
globalThis.Buffer = prev;
};
}

const BufferPolyfill = {
from: (s: unknown, encoding: unknown) => {
tinyassert(typeof s === "string");
tinyassert(encoding === "base64");
return {
toString: (encoding: unknown) => {
tinyassert(encoding === "utf-8");
return atob(s);
},
};
},
};

0 comments on commit 6d79a7f

Please sign in to comment.