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!: migrate to Vite 6 environment API #634

Merged
merged 66 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
5265ff9
chore: squashed all
hi-ogawa Aug 29, 2024
f8f5657
chore(deps): vite 6.0.0-beta.1
hi-ogawa Sep 22, 2024
eca9ebc
chore: tweak
hi-ogawa Dec 8, 2024
06eaecf
feat!: migrate to Vite 6 environment API (wip)
hi-ogawa Aug 29, 2024
28412e6
wip: switch plugin by `applyToEnvironment`
hi-ogawa Aug 29, 2024
6f25783
wip
hi-ogawa Aug 29, 2024
5944315
wip: move plugins
hi-ogawa Aug 30, 2024
ade37e9
wip: createBuilder and serverDepsConfigPlugin
hi-ogawa Aug 30, 2024
acc1f38
wip: nextOgPlugin resolve.alias
hi-ogawa Sep 22, 2024
6230004
chore: typo
hi-ogawa Aug 30, 2024
31c24ba
feat: export wrapServerPlugin
hi-ogawa Aug 30, 2024
ccdc5ed
wip: vitePluginServerAssets
hi-ogawa Aug 30, 2024
c27dc81
chore: cleanup
hi-ogawa Aug 30, 2024
5497ed7
fix: wrapClientPlugin
hi-ogawa Aug 30, 2024
d037eff
fix: check error in routeManifestPluginServer buildEnd
hi-ogawa Aug 30, 2024
9d57a80
chore: fix react-swc mdx warning
hi-ogawa Aug 30, 2024
c2bfb6b
chore: wrapClientPlugin for unocss
hi-ogawa Aug 30, 2024
33278fa
wip: hotUpdate
hi-ogawa Sep 22, 2024
2047aac
fix: vitePluginServerAssets
hi-ogawa Aug 30, 2024
0495303
chore: cleanup
hi-ogawa Aug 30, 2024
835641b
fix: sort `resolve.noExternal` for stable getConfigHash
hi-ogawa Aug 30, 2024
723d078
fix: fix rsc hmr with postcss
hi-ogawa Aug 30, 2024
6c77fd8
chore(deps): vite 6.0.0-alpha.23
hi-ogawa Aug 30, 2024
3b8b8f1
Revert "chore(deps): vite 6.0.0-alpha.23"
hi-ogawa Aug 30, 2024
f8d2171
fix: fix client hmr
hi-ogawa Aug 30, 2024
24f6d80
chore: fix version
hi-ogawa Aug 31, 2024
99b13f2
fix: fix postcss tailwind hmr
hi-ogawa Sep 22, 2024
2b98c3e
chore: cleanup
hi-ogawa Aug 31, 2024
a6ffe91
refactor: minor
hi-ogawa Sep 22, 2024
afbe8de
fix: change reloadEnvironmentModule
hi-ogawa Sep 22, 2024
179522f
chore: cleanup
hi-ogawa Sep 22, 2024
8cad6c4
chore(deps): vite 6.0.0-beta.1
hi-ogawa Sep 22, 2024
0b3429e
chore: disable hmr for rsc runner
hi-ogawa Sep 24, 2024
48e60d9
chore: just comment
hi-ogawa Sep 24, 2024
acce4fc
chore: bump
hi-ogawa Dec 8, 2024
2763d04
fix: fix vite-node-miniflare
hi-ogawa Dec 8, 2024
8b5160f
fix: fix `emitFile({source})` type
hi-ogawa Dec 8, 2024
4d7f9f5
fix: dedupe esbuild
hi-ogawa Dec 8, 2024
a4b4cdd
fix: externalize vite/module-runner
hi-ogawa Dec 8, 2024
9b392ba
chore: dedupe @vitejs/plugin-react
hi-ogawa Dec 8, 2024
1dca6bb
chore: lint
hi-ogawa Dec 8, 2024
fb83aaf
chore: fix transforms
hi-ogawa Dec 8, 2024
45a0e07
chore: squashed all
hi-ogawa Aug 29, 2024
01aef73
chore(deps): vite 6.0.0-beta.1
hi-ogawa Sep 22, 2024
7f94158
chore: tweak
hi-ogawa Dec 8, 2024
b8277c0
fix: fix vite-node-miniflare
hi-ogawa Dec 8, 2024
bc6c294
fix: fix `emitFile({source})` type
hi-ogawa Dec 8, 2024
506d66d
fix: dedupe esbuild
hi-ogawa Dec 8, 2024
e42cc39
fix: externalize vite/module-runner
hi-ogawa Dec 8, 2024
615ee94
chore: dedupe @vitejs/plugin-react
hi-ogawa Dec 8, 2024
74112b2
chore: lint
hi-ogawa Dec 8, 2024
1aeaf2a
chore: fix transforms
hi-ogawa Dec 8, 2024
0389aff
chore: update vite, vitest
hi-ogawa Jan 23, 2025
8dee099
Merge remote-tracking branch 'origin/chore-vite-6' into chore-vite-6
hi-ogawa Jan 23, 2025
67b87a3
fix: fix esbuildOptions.platform and keepProcessEnv
hi-ogawa Jan 23, 2025
e3ca3cc
test: update
hi-ogawa Jan 23, 2025
785f422
fix: patch @remix-run/dev for vite 6
hi-ogawa Jan 23, 2025
7bf12ae
Merge branch 'chore-vite-6' into feat-rsc-vite-6
hi-ogawa Jan 23, 2025
bd5df98
Merge branch 'main' into feat-rsc-vite-6
hi-ogawa Jan 23, 2025
19f7596
fix: `dev.optimizeDeps -> optimizeDeps`
hi-ogawa Jan 23, 2025
15af521
fix: rename environment `react-server` to `rsc`
hi-ogawa Jan 23, 2025
51d21eb
test: wip skip
hi-ogawa Jan 23, 2025
3cade9f
fix: fix loadEnv reload
hi-ogawa Jan 23, 2025
0688121
Merge branch 'main' into feat-rsc-vite-6
hi-ogawa Jan 23, 2025
7751349
Merge branch 'main' into feat-rsc-vite-6
hi-ogawa Jan 23, 2025
e7f36f7
chore: comment
hi-ogawa Jan 24, 2025
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
14 changes: 8 additions & 6 deletions packages/react-server-next/src/vite/adapters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ export type AdapterType = "node" | "vercel" | "vercel-edge" | "cloudflare";
export function adapterPlugin(options: {
adapter: AdapterType;
outDir: string;
}): { server?: Plugin[]; client?: Plugin[] } {
}): Plugin[] {
const adapter = options.adapter ?? autoSelectAdapter();
if (adapter === "node") {
return {};
return [];
}

const buildPlugin: Plugin = {
Expand Down Expand Up @@ -102,10 +102,12 @@ export function adapterPlugin(options: {
},
};

return {
server: [aliasPlatformPlugin],
client: [registerHooksPlugin, buildPlugin, devPlatformPlugin],
};
return [
registerHooksPlugin,
buildPlugin,
devPlatformPlugin,
aliasPlatformPlugin,
];
}

// cf. https://github.com/sveltejs/kit/blob/52e5461b055a104694f276859a7104f58452fab0/packages/adapter-auto/adapters.js
Expand Down
78 changes: 36 additions & 42 deletions packages/react-server-next/src/vite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import path from "node:path";
import {
type ReactServerPluginOptions,
vitePluginReactServer,
wrapServerPlugin,
} from "@hiogawa/react-server/plugin";
import {
vitePluginFetchUrlImportMetaUrl,
Expand Down Expand Up @@ -32,10 +33,6 @@ export default function vitePluginReactServerNext(
): PluginOption {
const outDir = options?.outDir ?? "dist";
const adapter = options?.adapter ?? autoSelectAdapter();
const adapterPlugins = adapterPlugin({
adapter,
outDir,
});

return [
react(),
Expand All @@ -45,34 +42,33 @@ export default function vitePluginReactServerNext(
vitePluginReactServer({
...options,
routeDir: options?.routeDir ?? "app",
plugins: [
nextJsxPlugin(),
tsconfigPaths(),
nextOgPlugin(),
vitePluginWasmModule({
buildMode:
adapter === "cloudflare" || adapter === "vercel-edge"
? "import"
: "fs",
}),
vitePluginFetchUrlImportMetaUrl({
buildMode:
adapter === "cloudflare"
? "import"
: adapter === "vercel-edge"
? "inline"
: "fs",
}),
adapterPlugins.server,
options?.plugins,
],
}),
nextOgPlugin(),
wrapServerPlugin([
vitePluginWasmModule({
buildMode:
adapter === "cloudflare" || adapter === "vercel-edge"
? "import"
: "fs",
}),
vitePluginFetchUrlImportMetaUrl({
buildMode:
adapter === "cloudflare"
? "import"
: adapter === "vercel-edge"
? "inline"
: "fs",
}),
]),
vitePluginLogger(),
vitePluginSsrMiddleware({
entry: "next/vite/entry-ssr",
preview: path.resolve(outDir, "server", "index.js"),
}),
adapterPlugins.client,
adapterPlugin({
adapter,
outDir,
}),
appFaviconPlugin(),
{
name: "next-exclude-optimize",
Expand Down Expand Up @@ -115,10 +111,19 @@ function nextOgPlugin(): Plugin[] {
];
}

// workaround https://github.com/vitejs/vite/issues/17689
(globalThis as any).__next_vite_last_env__ ??= [];
declare let __next_vite_last_env__: string[];

function nextConfigPlugin(): Plugin {
return {
name: nextConfigPlugin.name,
config() {
// remove last loaded env so that Vite reloads a new value
for (const key of __next_vite_last_env__) {
delete process.env[key];
}

// TODO
// this is only for import.meta.env.NEXT_PUBLIC_xxx replacement.
// we might want to define process.env.NEXT_PUBLIC_xxx for better compatibility.
Expand All @@ -128,26 +133,15 @@ function nextConfigPlugin(): Plugin {
};
},
configResolved(config) {
updateEnv(() => loadEnv(config.mode, config.envDir, ""));
const loadedEnv = loadEnv(config.mode, config.envDir, "");
__next_vite_last_env__ = Object.keys(loadedEnv).filter(
(key) => !(key in process.env),
);
Object.assign(process.env, loadedEnv);
},
};
}

// workaround https://github.com/vitejs/vite/issues/17689
(globalThis as any).__next_vite_last_env__ ??= [];
declare let __next_vite_last_env__: string[];

function updateEnv(loadEnv: () => Record<string, string>) {
for (const key of __next_vite_last_env__) {
delete process.env[key];
}
const loadedEnv = loadEnv();
__next_vite_last_env__ = Object.keys(loadedEnv).filter(
(key) => !(key in process.env),
);
Object.assign(process.env, loadedEnv);
}

function nextJsxPlugin(): Plugin {
return {
name: nextJsxPlugin.name,
Expand Down
1 change: 1 addition & 0 deletions packages/react-server/examples/basic/e2e/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export async function inspectDevModules<T extends string>(
}

export const testNoJs = test.extend({
// @ts-ignore
javaScriptEnabled: ({}, use) => use(false),
});

Expand Down
44 changes: 19 additions & 25 deletions packages/react-server/examples/basic/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import path from "node:path";
import { vitePluginReactServer } from "@hiogawa/react-server/plugin";
import {
vitePluginReactServer,
wrapClientPlugin,
wrapServerPlugin,
} from "@hiogawa/react-server/plugin";
import { vitePluginErrorOverlay } from "@hiogawa/vite-plugin-error-overlay";
import { vitePluginWasmModule } from "@hiogawa/vite-plugin-server-asset";
import {
Expand All @@ -14,40 +18,21 @@ import { type Plugin, defineConfig } from "vite";
export default defineConfig({
clearScreen: false,
plugins: [
// TODO: for now mdx is server only.
// see https://mdxjs.com/docs/getting-started/#vite for how to setup client hmr.
mdx(),
process.env["USE_SWC"]
? (await import("@vitejs/plugin-react-swc".slice())).default()
: react(),
unocss(),
// TODO: remove from ssr build
wrapClientPlugin(unocss()),
!process.env["E2E"] &&
vitePluginErrorOverlay({
patchConsoleError: true,
}),
vitePluginReactServer({
entryBrowser: "/src/entry-browser",
entryServer: "/src/entry-server",
plugins: [
// TODO: for now mdx is server only.
// see https://mdxjs.com/docs/getting-started/#vite for how to setup client hmr.
mdx(),
testVitePluginVirtual(),
vitePluginWasmModule({
buildMode:
process.env.VERCEL || process.env.CF_PAGES ? "import" : "fs",
}),
{
name: "cusotm-react-server-config",
config() {
return {
build: {
assetsInlineLimit(filePath) {
// test non-inlined server asset
return !filePath.includes("/test/assets/");
},
},
};
},
},
],
}),
vitePluginLogger(),
vitePluginSsrMiddleware({
Expand All @@ -66,9 +51,18 @@ export default defineConfig({
},
},
testVitePluginVirtual(),
wrapServerPlugin([
vitePluginWasmModule({
buildMode: process.env.VERCEL || process.env.CF_PAGES ? "import" : "fs",
}),
]),
],
build: {
ssrEmitAssets: true,
assetsInlineLimit(filePath) {
// test non-inlined server asset
return !filePath.includes("/test/assets/");
},
},
ssr: {
noExternal: [
Expand Down
27 changes: 15 additions & 12 deletions packages/react-server/src/entry/ssr.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createDebug, tinyassert } from "@hiogawa/utils";
import { createMemoryHistory } from "@tanstack/history";
import ReactDOMServer from "react-dom/server.edge";
import type { ModuleNode, ViteDevServer } from "vite";
import type { DevEnvironment, EnvironmentModuleNode } from "vite";
import type { SsrAssetsType } from "../features/assets/plugin";
import { DEV_SSR_CSS, SERVER_CSS_PROXY } from "../features/assets/shared";
import {
Expand Down Expand Up @@ -79,7 +79,7 @@ export async function prerender(request: Request) {

export async function importReactServer(): Promise<typeof import("./server")> {
if (import.meta.env.DEV) {
return $__global.dev.reactServer.ssrLoadModule(ENTRY_SERVER_WRAPPER) as any;
return $__global.dev.reactServerRunner.import(ENTRY_SERVER_WRAPPER);
} else {
return import("virtual:react-server-build" as string);
}
Expand Down Expand Up @@ -264,21 +264,24 @@ async function devInspectHandler(request: Request) {
tinyassert(request.method === "POST");
const data = await request.json();
if (data.type === "module") {
let mod: ModuleNode | undefined;
let mod: EnvironmentModuleNode | undefined;
if (data.environment === "ssr") {
mod = await getModuleNode($__global.dev.server, data.url, true);
mod = await getModuleNode(
$__global.dev.server.environments.ssr,
data.url,
);
}
if (data.environment === "react-server") {
mod = await getModuleNode($__global.dev.reactServer, data.url, true);
if (data.environment === "rsc") {
mod = await getModuleNode(
$__global.dev.server.environments["rsc"]!,
data.url,
);
}
const result = mod && {
id: mod.id,
lastInvalidationTimestamp: mod.lastInvalidationTimestamp,
importers: [...(mod.importers ?? [])].map((m) => m.id),
ssrImportedModules: [...(mod.ssrImportedModules ?? [])].map((m) => m.id),
clientImportedModules: [...(mod.clientImportedModules ?? [])].map(
(m) => m.id,
),
importedModules: [...(mod.importedModules ?? [])].map((m) => m.id),
};
return new Response(JSON.stringify(result || false, null, 2), {
headers: { "content-type": "application/json" },
Expand All @@ -287,8 +290,8 @@ async function devInspectHandler(request: Request) {
tinyassert(false);
}

async function getModuleNode(server: ViteDevServer, url: string, ssr: boolean) {
const resolved = await server.moduleGraph.resolveUrl(url, ssr);
async function getModuleNode(server: DevEnvironment, url: string) {
const resolved = await server.moduleGraph.resolveUrl(url);
return server.moduleGraph.getModuleById(resolved[1]);
}

Expand Down
34 changes: 15 additions & 19 deletions packages/react-server/src/features/assets/css.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import type { ViteDevServer } from "vite";
import {
type DevEnvironment,
type EnvironmentModuleNode,
isCSSRequest,
} from "vite";

// cf
// https://github.com/hi-ogawa/vite-plugins/blob/3c496fa1bb5ac66d2880986877a37ed262f1d2a6/packages/vite-glob-routes/examples/demo/vite-plugin-ssr-css.ts
// https://github.com/remix-run/remix/blob/dev/packages/remix-dev/vite/styles.ts

export async function collectStyle(
server: ViteDevServer,
options: { entries: string[]; ssr: boolean },
export async function transformStyleUrls(
server: DevEnvironment,
urls: string[],
) {
const urls = await collectStyleUrls(server, options);
const styles = await Promise.all(
urls.map(async (url) => {
const res = await server.transformRequest(url + "?direct");
Expand All @@ -19,35 +22,28 @@ export async function collectStyle(
}

export async function collectStyleUrls(
server: ViteDevServer,
{ entries, ssr }: { entries: string[]; ssr: boolean },
server: DevEnvironment,
{ entries }: { entries: string[] },
) {
const visited = new Set<string>();
const visited = new Set<EnvironmentModuleNode>();

async function traverse(url: string) {
const [, id] = await server.moduleGraph.resolveUrl(url);
if (visited.has(id)) {
return;
}
visited.add(id);
const mod = server.moduleGraph.getModuleById(id);
if (!mod) {
if (!mod || visited.has(mod)) {
return;
}
visited.add(mod);
await Promise.all(
[...mod.importedModules].map((childMod) => traverse(childMod.url)),
);
}

// ensure import analysis is ready for top entries
await Promise.all(entries.map((e) => server.transformRequest(e, { ssr })));
await Promise.all(entries.map((e) => server.transformRequest(e)));

// traverse
await Promise.all(entries.map((url) => traverse(url)));

return [...visited].filter((url) => url.match(CSS_LANGS_RE));
return [...visited].map((mod) => mod.url).filter((url) => isCSSRequest(url));
}

// cf. https://github.com/vitejs/vite/blob/d6bde8b03d433778aaed62afc2be0630c8131908/packages/vite/src/node/constants.ts#L49C23-L50
export const CSS_LANGS_RE =
/\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/;
Loading
Loading