Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: integrate vite runtime #155

Merged
merged 69 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
696621a
fix(vite-node-miniflare): use `ssr.optimizeDeps` for vite-node
hi-ogawa Jan 9, 2024
39cddc4
Merge branch 'main' into fix-vite-node-ssr-optimizeDeps
hi-ogawa Jan 9, 2024
f916f85
fix: optimizeDeps.exclude for node deps
hi-ogawa Jan 9, 2024
21b2889
fix: resolve.conditions browser for react-dom/server
hi-ogawa Jan 9, 2024
4129b00
chore: comment
hi-ogawa Jan 9, 2024
2a5b25a
chore: direct import "react-dom/server.browser"
hi-ogawa Jan 9, 2024
8b74dd4
fix: fix pre-bundle externalization + fix "require" esbuild banner
hi-ogawa Jan 9, 2024
46bfac3
chore: remove optimizeDeps.force for debuggin
hi-ogawa Jan 9, 2024
8bd55cd
chore: comment
hi-ogawa Jan 9, 2024
9b70f49
chore: comment
hi-ogawa Jan 9, 2024
8f56565
chore: comment
hi-ogawa Jan 9, 2024
fc2d72b
chore: "serve" only
hi-ogawa Jan 10, 2024
190305e
chore: comment
hi-ogawa Jan 10, 2024
6128685
chore: remove redundant noDiscovery
hi-ogawa Jan 10, 2024
6e7cdeb
Merge branch 'main' into fix-vite-node-ssr-optimizeDeps
hi-ogawa Jan 12, 2024
c415228
chore(deps): vite 5.1.0-beta.0
hi-ogawa Jan 16, 2024
c337335
Merge remote-tracking branch 'origin/fix-vite-node-ssr-optimizeDeps' …
hi-ogawa Jan 16, 2024
d79805e
Merge branch 'main' into fix-vite-node-ssr-optimizeDeps
hi-ogawa Jan 16, 2024
4781972
feat: move config to plugin
hi-ogawa Jan 16, 2024
b4477d3
chore: more examples
hi-ogawa Jan 16, 2024
808ee5c
chore: more
hi-ogawa Jan 16, 2024
0fe2858
chore: remove unused
hi-ogawa Jan 16, 2024
5484643
chore: cleanup react-dom import
hi-ogawa Jan 16, 2024
599be33
chore: use sapphi-red's prebuilt vite
hi-ogawa Jan 21, 2024
385dc74
wip: simple integration without hmr/invalidation
hi-ogawa Jan 21, 2024
6337df8
fix: disable installSourcemapsSupport
hi-ogawa Jan 21, 2024
bf9efdf
fix: tweak optimizeDeps for remix dep cjs
hi-ogawa Jan 21, 2024
62a70c4
chore: lint
hi-ogawa Jan 21, 2024
8b45d0f
chore: comment
hi-ogawa Jan 21, 2024
cc0aee8
chore: manual invalidation
hi-ogawa Jan 21, 2024
9fd7a29
test: xfail sourcemap
hi-ogawa Jan 21, 2024
2dd0ecf
feat: handle HMRPayload on client
hi-ogawa Jan 21, 2024
e5eb876
chore: todo
hi-ogawa Jan 21, 2024
da12968
wip: fix stack trace
hi-ogawa Jan 21, 2024
a6d4c76
wip: try newAsyncFunction
hi-ogawa Jan 21, 2024
e5a2219
chore: comment
hi-ogawa Jan 21, 2024
2dcd233
chore: comment
hi-ogawa Jan 21, 2024
dfe98ac
chore: update build
hi-ogawa Jan 24, 2024
930f20f
wip: prepareStackTrace
hi-ogawa Jan 24, 2024
83a36ce
wip: struggle
hi-ogawa Jan 24, 2024
fa739bc
fix: fix source map by two empty lines
hi-ogawa Jan 24, 2024
2f6337f
chore: remove local build
hi-ogawa Jan 24, 2024
ace8ef1
chore: lint
hi-ogawa Jan 24, 2024
b4938df
chore: remove unused
hi-ogawa Jan 24, 2024
9bf1c7d
chore: comment
hi-ogawa Jan 24, 2024
596b232
chore: prebuilt vite
hi-ogawa Jan 26, 2024
45c5f4c
chore: use custom fetchModule to avoid two empty lines injection
hi-ogawa Jan 26, 2024
748f523
Merge branch 'main' into fix-vite-node-ssr-optimizeDeps
hi-ogawa Jan 29, 2024
b4a8f10
Merge branch 'fix-vite-node-ssr-optimizeDeps' into refactor-vite-runtime
hi-ogawa Jan 29, 2024
9702304
chore: update vite
hi-ogawa Jan 30, 2024
24f47ac
chore: update vite
hi-ogawa Jan 30, 2024
38ee21b
chore: remove unused
hi-ogawa Jan 30, 2024
9b40a28
fix: back to `moduleCache.invalidateDepTree` without SSR HMR
hi-ogawa Jan 31, 2024
95d6705
feat: optional ssr hmr (disabled by default)
hi-ogawa Jan 31, 2024
c97a158
chore: log
hi-ogawa Jan 31, 2024
8b32d19
chore: comment
hi-ogawa Jan 31, 2024
322c69e
refactor: obtain HMRHandler from onUpdate callback
hi-ogawa Feb 1, 2024
fda6e52
chore: typing
hi-ogawa Feb 1, 2024
ec4a93f
chore: vite beta
hi-ogawa Feb 2, 2024
37e8fbc
Merge branch 'main' into fix-vite-node-ssr-optimizeDeps
hi-ogawa Feb 9, 2024
c5c5f62
chore: remove unused
hi-ogawa Feb 9, 2024
eee7c16
chore: remove deprecated `optimizeDeps.disabled`
hi-ogawa Feb 9, 2024
e4dc5a2
Merge branch 'main' into fix-vite-node-ssr-optimizeDeps
hi-ogawa Feb 9, 2024
4ffec98
Merge branch 'fix-vite-node-ssr-optimizeDeps' into refactor-vite-runtime
hi-ogawa Feb 9, 2024
8889857
chore: comment
hi-ogawa Feb 9, 2024
e165313
chore: comment
hi-ogawa Feb 9, 2024
2ad4f3c
chore: type
hi-ogawa Feb 9, 2024
64b6ba9
chore: comment
hi-ogawa Feb 9, 2024
68412a2
Merge branch 'main' into refactor-vite-runtime
hi-ogawa Feb 9, 2024
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
1 change: 1 addition & 0 deletions packages/vite-node-miniflare/examples/remix/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default defineConfig({
"react/jsx-dev-runtime",
"react-dom",
"react-dom/server.browser",
"@remix-run/server-runtime",
Copy link
Owner Author

@hi-ogawa hi-ogawa Feb 9, 2024

Choose a reason for hiding this comment

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

Previously vite-node was providing exports etc... in global, so it was magically working.

https://github.com/vitest-dev/vitest/blob/5ed537f03ea33df3516e530b55a5c1f0fc219e24/packages/vite-node/src/client.ts#L392-L395

Now, vite-runtime doesn't provide these out-of-the-box, so we get this error unless cookie etc... are pre-bundled.

ReferenceError: exports is not defined
    at /home/hiroshi/code/personal/vite-plugins/node_modules/.pnpm/cookie@0.6.0/node_modules/cookie/index.js:15:1
    at Object.runViteModule (__vite_node_miniflare_entry.js:2704:15)
    at ViteRuntime.directRequest (__vite_node_miniflare_entry.js:2609:23)
    at ViteRuntime.cachedRequest (__vite_node_miniflare_entry.js:2489:28)
    at request (__vite_node_miniflare_entry.js:2539:19)
    at /@fs/home/hiroshi/code/personal/vite-plugins/node_modules/.pnpm/@remix-run+server-runtime@2.5.1_typescript@5.1.6/node_modules/@remix-run/server-runtime/dist/esm/cookies.js?v=83897ad1:1:110
    at Object.runViteModule (__vite_node_miniflare_entry.js:2704:9)
    at ViteRuntime.directRequest (__vite_node_miniflare_entry.js:2609:5)
    at ViteRuntime.cachedRequest (__vite_node_miniflare_entry.js:2492:33)
    at /@fs/home/hiroshi/code/personal/vite-plugins/node_modules/.pnpm/@remix-run+server-runtime@2.5.1_typescript@5.1.6/node_modules/@remix-run/server-runtime/dist/esm/index.js?v=83897ad1:1:110

],
},
},
Expand Down
56 changes: 54 additions & 2 deletions packages/vite-node-miniflare/src/client/vite-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@ import {
proxyTinyRpc,
} from "@hiogawa/tiny-rpc";
import { tinyassert } from "@hiogawa/utils";
import type { HMRPayload } from "vite";
import type { ViteNodeRunnerOptions } from "vite-node";
import { ViteNodeRunner } from "vite-node/client";
import { installSourcemapsSupport } from "vite-node/source-map";
import { ViteRuntime } from "vite/runtime";
import type { ViteNodeRpc } from "..";
import { __setDebug } from "./polyfills/debug";
import { __setUnsafeEval } from "./polyfills/node-vm";

export interface ViteNodeMiniflareClient {
rpc: TinyRpcProxy<ViteNodeRpc>;
runner: ViteNodeRunner;
runtime: ViteRuntime;
runtimeHMRHandler: (payload: HMRPayload) => void;
}

export function createViteNodeClient(options: {
Expand All @@ -29,6 +33,53 @@ export function createViteNodeClient(options: {
adapter: httpClientAdapter({ url: options.serverRpcUrl }),
});

let runtimeHMRHandler!: (payload: HMRPayload) => void;

const runtime = new ViteRuntime(
{
root: options.runnerOptions.root,
fetchModule(id, importer) {
return rpc.ssrFetchModule(id, importer);
},
sourcemapInterceptor: "prepareStackTrace",
hmr: {
connection: {
isReady() {
return true;
},
// TODO: only for custom event to server?
send(messages) {
console.log("[runtime.hmr.connection.send]", messages);
},
// TODO: for now, we fetch HMRPayload via separate rpc, so we just grab the callback and use it later.
onUpdate(callback) {
// this is called during ViteRuntime constructor
runtimeHMRHandler = callback;
},
},
logger: console,
},
},
{
async runViteModule(context, transformed, id) {
// do same as vite-node/client
// https://github.com/vitest-dev/vitest/blob/c6e04125fb4a0af2db8bd58ea193b965d50d415f/packages/vite-node/src/client.ts#L415
const codeDefinition = `'use strict';async (${Object.keys(context).join(
","
)})=>{{`;
const code = `${codeDefinition}${transformed}\n}}`;
const fn = options.unsafeEval.eval(code, id);
await fn(...Object.values(context));
Object.freeze(context.__vite_ssr_exports__);
},

runExternalModule(filepath) {
console.error("[runExternalModule]", filepath);
throw new Error(`[runExternalModule] ${filepath}`);
},
}
);

const runner = new ViteNodeRunner({
...options.runnerOptions,
fetchModule(id) {
Expand All @@ -42,7 +93,8 @@ export function createViteNodeClient(options: {
// 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({
// prettier-ignore
0 && installSourcemapsSupport({
getSourceMap: (source) => {
const teardown = setupBufferPolyfill();
try {
Expand All @@ -53,7 +105,7 @@ export function createViteNodeClient(options: {
},
});

return { rpc, runner };
return { rpc, runner, runtime, runtimeHMRHandler };
}

function setupBufferPolyfill() {
Expand Down
37 changes: 37 additions & 0 deletions packages/vite-node-miniflare/src/client/worker-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface Env {
__VITE_NODE_SERVER_RPC_URL: string;
__VITE_NODE_RUNNER_OPTIONS: any;
__VITE_NODE_DEBUG: boolean;
__VITE_RUNTIME_HMR: boolean;
__WORKER_ENTRY: string;
}

Expand All @@ -24,6 +25,42 @@ export default {
debug: env.__VITE_NODE_DEBUG,
});

if (1) {
// fetch HMRPayload before execution
// TODO: listen HMRPayload event (birpc? websocket? SSE?)
const payloads = await client.rpc.getHMRPayloads();
for (const payload of payloads) {
if (env.__VITE_NODE_DEBUG) {
console.log("[HMRPayload]", payload);
}
// simple module tree invalidation when ssr hmr is disabled
if (!env.__VITE_RUNTIME_HMR && payload.type === "update") {
for (const update of payload.updates) {
// TODO: unwrapId?
const invalidated = client.runtime.moduleCache.invalidateDepTree([
update.path,
]);
if (env.__VITE_NODE_DEBUG) {
console.log("[vite-node-miniflare] invalidateDepTree:", [
...invalidated,
]);
}
}
continue;
}
await (client.runtimeHMRHandler(payload) as any as Promise<void>);
}

const workerEntry = await client.runtime.executeEntrypoint(
env.__WORKER_ENTRY
);
const workerEnv = {
...env,
__VITE_NODE_MINIFLARE_CLIENT: client,
};
return await workerEntry.default.fetch(request, workerEnv, ctx);
}

// invalidate modules similar to nuxt
// https://github.com/nuxt/nuxt/blob/1de44a5a5ca5757d53a8b52c9809cbc027d2d246/packages/vite/src/runtime/vite-node.mjs#L21-L23
const invalidatedModules = await client.rpc.getInvalidatedModules();
Expand Down
3 changes: 3 additions & 0 deletions packages/vite-node-miniflare/src/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { setupViteNodeServerRpc } from "./vite-node";
export function vitePluginViteNodeMiniflare(pluginOptions: {
entry: string;
debug?: boolean;
// for now disable ssr hmr by default for react plugin
hmr?: boolean;
// hooks to customize options
miniflareOptions?: (options: MiniflareOptions) => void;
viteNodeServerOptions?: (options: ViteNodeServerOptions) => void;
Expand Down Expand Up @@ -82,6 +84,7 @@ export function vitePluginViteNodeMiniflare(pluginOptions: {
entry: pluginOptions.entry,
rpcOrigin: ctx.url.origin,
debug: pluginOptions.debug,
hmr: pluginOptions.hmr,
viteNodeRunnerOptions,
});
pluginOptions.miniflareOptions?.(miniflareOptions);
Expand Down
32 changes: 30 additions & 2 deletions packages/vite-node-miniflare/src/server/vite-node.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { exposeTinyRpc, httpServerAdapter } from "@hiogawa/tiny-rpc";
import type { MiniflareOptions } from "miniflare";
import { type ViteDevServer, normalizePath } from "vite";
import {
type HMRPayload,
ServerHMRConnector,
type ViteDevServer,
fetchModule,
normalizePath,
} from "vite";
import type { ViteNodeRunnerOptions } from "vite-node";
import type { ViteNodeServer } from "vite-node/server";
import { WORKER_ENTRY_SCRIPT } from "../client/worker-entry-script";
Expand All @@ -10,9 +16,10 @@ import { WORKER_ENTRY_SCRIPT } from "../client/worker-entry-script";
// prettier-ignore
export type ViteNodeRpc =
Pick<ViteNodeServer, "fetchModule" | "resolveId"> &
Pick<ViteDevServer, "transformIndexHtml"> &
Pick<ViteDevServer, "transformIndexHtml" | "ssrFetchModule"> &
{
getInvalidatedModules: () => string[];
getHMRPayloads: () => HMRPayload[];
};

export function setupViteNodeServerRpc(
Expand All @@ -25,16 +32,35 @@ export function setupViteNodeServerRpc(
// https://github.com/nuxt/nuxt/blob/1de44a5a5ca5757d53a8b52c9809cbc027d2d246/packages/vite/src/vite-node.ts#L62
const invalidatedModules = new Set<string>();

// for starter, collect HMRPayload with builtin ServerHMRConnector
// and let worker entry fetch them via rpc before rendering
const connector = new ServerHMRConnector(viteNodeServer.server);
let hmrPayloads: HMRPayload[] = [];
connector.onUpdate((payload) => {
hmrPayloads.push(payload);
});

const rpcRoutes: ViteNodeRpc = {
fetchModule: viteNodeServer.fetchModule.bind(viteNodeServer),
resolveId: viteNodeServer.resolveId.bind(viteNodeServer),
transformIndexHtml: viteNodeServer.server.transformIndexHtml,
ssrFetchModule: (id, importer) => {
// not using default `viteDevServer.ssrFetchModule` since its source map expects mysterious two empty lines,
// which doesn't exist in workerd's unsafe eval
// https://github.com/vitejs/vite/pull/12165#issuecomment-1910686678
return fetchModule(viteDevServer, id, importer);
},
getInvalidatedModules: () => {
// there must be at most one client to make use of this RPC
const result = [...invalidatedModules];
invalidatedModules.clear();
return result;
},
getHMRPayloads: () => {
const result = hmrPayloads;
hmrPayloads = [];
return result;
},
// framework can utilize custom RPC to implement some features on main Vite process and expose them to Workerd
// (e.g. Remix's DevServerHooks)
...options.customRpc,
Expand Down Expand Up @@ -64,6 +90,7 @@ export function setupViteNodeServerRpc(
entry: string;
rpcOrigin: string;
debug?: boolean;
hmr?: boolean;
viteNodeRunnerOptions: Partial<ViteNodeRunnerOptions>;
}) {
return {
Expand All @@ -85,6 +112,7 @@ export function setupViteNodeServerRpc(
__VITE_NODE_SERVER_RPC_URL: options.rpcOrigin + rpcBase,
__VITE_NODE_RUNNER_OPTIONS: options.viteNodeRunnerOptions as any,
__VITE_NODE_DEBUG: options.debug ?? false,
__VITE_RUNTIME_HMR: options.hmr ?? false,
},
} satisfies MiniflareOptions;
}
Expand Down
Binary file added vite-5.1.0-beta.5.tgz
Binary file not shown.
Loading