diff --git a/packages/next-swc/crates/next-core/js/src/dev/client.ts b/packages/next-swc/crates/next-core/js/src/dev/client.ts index b546f046f4..0130656896 100644 --- a/packages/next-swc/crates/next-core/js/src/dev/client.ts +++ b/packages/next-swc/crates/next-core/js/src/dev/client.ts @@ -2,7 +2,9 @@ import { connect } from "./hmr-client"; import { connectHMR } from "./websocket"; export function initializeHMR(options: { assetPrefix: string }) { - connect(); + connect({ + assetPrefix: options.assetPrefix, + }); connectHMR({ path: "/turbopack-hmr", assetPrefix: options.assetPrefix, diff --git a/packages/next-swc/crates/next-core/js/src/dev/hmr-client.ts b/packages/next-swc/crates/next-core/js/src/dev/hmr-client.ts index bfa070b412..60a60c109b 100644 --- a/packages/next-swc/crates/next-core/js/src/dev/hmr-client.ts +++ b/packages/next-swc/crates/next-core/js/src/dev/hmr-client.ts @@ -11,7 +11,11 @@ import { addEventListener, sendMessage } from "./websocket"; declare var globalThis: TurbopackGlobals; -export function connect() { +export type ClientOptions = { + assetPrefix: string; +}; + +export function connect({ assetPrefix }: ClientOptions) { addEventListener((event) => { switch (event.type) { case "connected": @@ -39,7 +43,7 @@ export function connect() { } } - subscribeToInitialCssChunksUpdates(); + subscribeToInitialCssChunksUpdates(assetPrefix); } const chunkUpdateCallbacks: Map = new Map(); @@ -102,15 +106,21 @@ function triggerChunkUpdate(update: ServerMessage) { // Unlike ES chunks, CSS chunks cannot contain the logic to accept updates. // They must be reloaded here instead. -function subscribeToInitialCssChunksUpdates() { +function subscribeToInitialCssChunksUpdates(assetPrefix: string) { const initialCssChunkLinks: NodeListOf = document.head.querySelectorAll("link"); + const cssChunkPrefix = `${assetPrefix}/`; initialCssChunkLinks.forEach((link) => { - if (!link.href) return; - const url = new URL(link.href); - if (url.origin !== location.origin) return; - const chunkPath = url.pathname.slice(1); + const href = link.href; + if (href == null) { + return; + } + const { pathname, origin } = new URL(href); + if (origin !== location.origin || !pathname.startsWith(cssChunkPrefix)) { + return; + } + const chunkPath = pathname.slice(cssChunkPrefix.length); onChunkUpdate(chunkPath, (update) => { switch (update.type) { case "restart": { diff --git a/packages/next-swc/crates/next-core/js/src/entry/next-hydrate.tsx b/packages/next-swc/crates/next-core/js/src/entry/next-hydrate.tsx index ffa1e6716b..2d030917b5 100644 --- a/packages/next-swc/crates/next-core/js/src/entry/next-hydrate.tsx +++ b/packages/next-swc/crates/next-core/js/src/entry/next-hydrate.tsx @@ -2,6 +2,7 @@ import "@vercel/turbopack-next/internal/shims"; import { initialize, hydrate } from "next/dist/client"; import { initializeHMR } from "@vercel/turbopack-next/dev/client"; +import { displayContent } from "next/dist/client/dev/fouc"; import * as _app from "@vercel/turbopack-next/pages/_app"; import * as page from "."; @@ -9,23 +10,23 @@ import * as page from "."; (async () => { console.debug("Initializing Next.js"); - initializeHMR({ - assetPrefix: "", - }); - - await initialize({ + const { assetPrefix } = await initialize({ webpackHMR: { // Expected when `process.env.NODE_ENV === 'development'` onUnrecoverableError() {}, }, }); + initializeHMR({ + assetPrefix, + }); + window.__NEXT_P.push(["/_app", () => _app]); window.__NEXT_P.push([window.__NEXT_DATA__.page, () => page]); console.debug("Hydrating the page"); - await hydrate(); + await hydrate({ beforeRender: displayContent }); console.debug("The page has been hydrated"); })().catch((err) => console.error(err)); diff --git a/packages/next-swc/crates/next-core/js/src/entry/server-renderer.tsx b/packages/next-swc/crates/next-core/js/src/entry/server-renderer.tsx index 1b7ad117b4..afd2f4cf94 100644 --- a/packages/next-swc/crates/next-core/js/src/entry/server-renderer.tsx +++ b/packages/next-swc/crates/next-core/js/src/entry/server-renderer.tsx @@ -10,6 +10,8 @@ import Document from "@vercel/turbopack-next/pages/_document"; import Component, * as otherExports from "."; ("TURBOPACK { transition: next-client }"); import chunkGroup from "."; +import { BuildManifest } from "next/dist/server/get-page-files"; +import { ChunkGroup } from "types/next"; const END_OF_OPERATION = process.argv[2]; const NEW_LINE = "\n".charCodeAt(0); @@ -168,14 +170,14 @@ async function operation(renderData: RenderData) { // TODO(alexkirsz) This is missing *a lot* of data, but it's enough to get a // basic render working. - /* BuildManifest */ - const buildManifest = { + const group = chunkGroup as ChunkGroup; + const buildManifest: BuildManifest = { pages: { // TODO(alexkirsz) We should separate _app and page chunks. Right now, we // computing the chunk items of `next-hydrate.js`, so they contain both // _app and page chunks. "/_app": [], - [renderData.path]: chunkGroup.map((c: { path: string }) => c.path), + [renderData.path]: group.map((chunk) => chunk.path), }, devFiles: [], @@ -202,6 +204,8 @@ async function operation(renderData: RenderData) { buildId: "", /* RenderOptsPartial */ + dev: true, + runtimeConfig: {}, assetPrefix: "", canonicalBase: "", previewProps: { diff --git a/packages/next-swc/crates/next-core/js/types/next.d.ts b/packages/next-swc/crates/next-core/js/types/next.d.ts new file mode 100644 index 0000000000..a80e0b151a --- /dev/null +++ b/packages/next-swc/crates/next-core/js/types/next.d.ts @@ -0,0 +1 @@ +export type ChunkGroup = Array<{ path: string; chunkId: string }>;