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

Open
wants to merge 33 commits into
base: chore-vite-6
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
297f7ba
feat!: migrate to Vite 6 environment API (wip)
hi-ogawa Aug 29, 2024
416294c
wip: switch plugin by `applyToEnvironment`
hi-ogawa Aug 29, 2024
7a2118d
wip
hi-ogawa Aug 29, 2024
a2dd1c0
wip: move plugins
hi-ogawa Aug 30, 2024
8d2c376
wip: createBuilder and serverDepsConfigPlugin
hi-ogawa Aug 30, 2024
880570c
wip: nextOgPlugin resolve.alias
hi-ogawa Sep 22, 2024
5116cb5
chore: typo
hi-ogawa Aug 30, 2024
2f169e3
feat: export wrapServerPlugin
hi-ogawa Aug 30, 2024
38399e2
wip: vitePluginServerAssets
hi-ogawa Aug 30, 2024
ba11eb0
chore: cleanup
hi-ogawa Aug 30, 2024
9dfb234
fix: wrapClientPlugin
hi-ogawa Aug 30, 2024
0aa5f49
fix: check error in routeManifestPluginServer buildEnd
hi-ogawa Aug 30, 2024
407a57d
chore: fix react-swc mdx warning
hi-ogawa Aug 30, 2024
e5aaee7
chore: update vite in example
hi-ogawa Aug 30, 2024
d1e4626
chore: wrapClientPlugin for unocss
hi-ogawa Aug 30, 2024
011f000
wip: hotUpdate
hi-ogawa Sep 22, 2024
cbe479e
fix: vitePluginServerAssets
hi-ogawa Aug 30, 2024
9189c29
chore: cleanup
hi-ogawa Aug 30, 2024
f56e08b
fix: sort `resolve.noExternal` for stable getConfigHash
hi-ogawa Aug 30, 2024
f570665
chore: vite alpha in examples/basic
hi-ogawa Aug 30, 2024
dce137d
fix: fix rsc hmr with postcss
hi-ogawa Aug 30, 2024
88a9cbc
chore(deps): vite 6.0.0-alpha.23
hi-ogawa Aug 30, 2024
06c7728
Revert "chore(deps): vite 6.0.0-alpha.23"
hi-ogawa Aug 30, 2024
a0ee14b
fix: fix client hmr
hi-ogawa Aug 30, 2024
5c610bb
chore: fix version
hi-ogawa Aug 31, 2024
dafb75a
fix: fix postcss tailwind hmr
hi-ogawa Sep 22, 2024
a9af3ef
chore: cleanup
hi-ogawa Aug 31, 2024
37f9acb
refactor: minor
hi-ogawa Sep 22, 2024
c3dbb40
fix: change reloadEnvironmentModule
hi-ogawa Sep 22, 2024
2fe323c
chore: cleanup
hi-ogawa Sep 22, 2024
8f17204
chore(deps): vite 6.0.0-beta.1
hi-ogawa Sep 22, 2024
b971ba1
chore: disable hmr for rsc runner
hi-ogawa Sep 24, 2024
9f3d40b
chore: just comment
hi-ogawa Sep 24, 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
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
48 changes: 22 additions & 26 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
43 changes: 18 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,20 @@ 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(),
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 +50,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
25 changes: 14 additions & 11 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 @@ -265,21 +265,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);
mod = await getModuleNode(
$__global.dev.server.environments["react-server"]!,
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 @@ -288,8 +291,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)(?:$|\?)/;
46 changes: 30 additions & 16 deletions packages/react-server/src/features/assets/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { Manifest, Plugin, ViteDevServer } from "vite";
import { $__global } from "../../global";
import type { PluginStateManager } from "../../plugin";
import { ENTRY_BROWSER_WRAPPER, createVirtualPlugin } from "../../plugin/utils";
import { collectStyle, collectStyleUrls } from "./css";
import { collectStyleUrls, transformStyleUrls } from "./css";
import { DEV_SSR_CSS, SERVER_CSS_PROXY } from "./shared";

export interface SsrAssetsType {
Expand Down Expand Up @@ -103,22 +103,34 @@ export function vitePluginServerAssets({
return file.startsWith(root) ? file.slice(root.length) : file;
},
);
const styles = await Promise.all([
`/******* react-server ********/`,
collectStyle($__global.dev.reactServer, {
const serverStyleUrls = await collectStyleUrls(
$__global.dev.server.environments["react-server"]!,
{
entries: [entryServer, "virtual:server-routes"],
ssr: true,
}),
`/******* client **************/`,
collectStyle($__global.dev.server, {
},
);
const clientStyleUrls = await collectStyleUrls(
$__global.dev.server.environments.client,
{
entries: [
entryBrowser,
"virtual:client-routes",
// TODO: dev should also use RouteManifest to manage client css
...clientReferences,
],
ssr: false,
}),
},
);
const styles = await Promise.all([
`/******* react-server ********/`,
await transformStyleUrls(
$__global.dev.server.environments.client,
serverStyleUrls,
),
`/******* client **************/`,
await transformStyleUrls(
$__global.dev.server.environments.client,
clientStyleUrls,
),
]);
return styles.join("\n\n");
}),
Expand All @@ -127,10 +139,12 @@ export function vitePluginServerAssets({
// virtual module to proxy css imports from react server to client
// TODO: invalidate + full reload when add/remove css file?
if (!manager.buildType) {
const urls = await collectStyleUrls($__global.dev.reactServer, {
entries: [entryServer, "virtual:server-routes"],
ssr: true,
});
const urls = await collectStyleUrls(
$__global.dev.server.environments["react-server"]!,
{
entries: [entryServer, "virtual:server-routes"],
},
);
const code = urls.map((url) => `import "${url}";\n`).join("");
// ensure hmr boundary since css module doesn't have `import.meta.hot.accept`
return code + `if (import.meta.hot) { import.meta.hot.accept() }`;
Expand All @@ -157,7 +171,7 @@ export function vitePluginServerAssets({
];
}

export function serverAssertsPluginServer({
export function serverAssetsPluginServer({
manager,
}: { manager: PluginStateManager }): Plugin[] {
// 0. track server assets during server build (this plugin)
Expand All @@ -172,7 +186,7 @@ export function serverAssertsPluginServer({

return [
{
name: serverAssertsPluginServer.name + ":build",
name: serverAssetsPluginServer.name + ":build",
apply: "build",
generateBundle(_options, bundle) {
if (manager.buildType !== "server") {
Expand Down
Loading