From 8fa162d5c567bbaba510f44dfd6c374a2d7243cf Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Sat, 22 Oct 2022 19:47:59 +0200 Subject: [PATCH] fix app layouts (vercel/turbo#190) Seems like app layout was left behind while shipping features to normal SSR rendering * fixes fallback error pages (update to error fallback changes) * fixes process communication (update to api route changes) * avoid busy looping * update to next.js updates * fix resolving cycle * fix HMR of client components * add some hard coded external packages --- .../next-swc/crates/next-core/js/package.json | 3 +- .../next-core/js/src/entry/app-renderer.tsx | 15 +++-- .../next-core/js/src/entry/app/hydrate.tsx | 8 ++- .../js/src/entry/app/layout-entry.tsx | 2 +- .../crates/next-core/js/types/modules.d.ts | 2 +- .../crates/next-core/src/app_source.rs | 10 ++-- .../crates/next-core/src/next_import_map.rs | 59 ++++++++----------- .../crates/next-core/src/nodejs/pool.rs | 15 ++++- .../next-dev/benches/bundlers/turbopack.rs | 4 +- 9 files changed, 65 insertions(+), 53 deletions(-) diff --git a/packages/next-swc/crates/next-core/js/package.json b/packages/next-swc/crates/next-core/js/package.json index 3662317db6..48e75ffb49 100644 --- a/packages/next-swc/crates/next-core/js/package.json +++ b/packages/next-swc/crates/next-core/js/package.json @@ -9,11 +9,10 @@ "build:compiled": "node build.mjs" }, "dependencies": { - "@next/react-refresh-utils": "^12.2.5", "@vercel/turbopack-runtime": "latest", "anser": "2.1.1", "css.escape": "1.5.1", - "next": "12.3.2-canary.32", + "next": "12.3.2-canary.33", "platform": "1.3.6", "react-dom": "^18.2.0", "react": "^18.2.0", diff --git a/packages/next-swc/crates/next-core/js/src/entry/app-renderer.tsx b/packages/next-swc/crates/next-core/js/src/entry/app-renderer.tsx index f30ce96496..30c940f375 100644 --- a/packages/next-swc/crates/next-core/js/src/entry/app-renderer.tsx +++ b/packages/next-swc/crates/next-core/js/src/entry/app-renderer.tsx @@ -6,8 +6,6 @@ declare global { const BOOTSTRAP: string[]; } -const END_OF_OPERATION = process.argv[2]; - import type { IncomingMessage, ServerResponse } from "node:http"; import type { FlightManifest } from "next/dist/build/webpack/plugins/flight-manifest-plugin"; import type { RenderData } from "types/turbopack"; @@ -27,9 +25,18 @@ globalThis.__next_chunk_load__ = () => Promise.resolve(); process.env.__NEXT_NEW_LINK_BEHAVIOR = "true"; -process.stdout.write("READY\n"); +const [MARKER, _OPERATION_STEP, OPERATION_SUCCESS, _OPERATION_ERROR] = + process.argv.slice(2, 6).map((arg) => Buffer.from(arg, "utf8")); const NEW_LINE = "\n".charCodeAt(0); +const OPERATION_SUCCESS_MARKER = Buffer.concat([ + OPERATION_SUCCESS, + Buffer.from(" ", "utf8"), + MARKER, +]); + +process.stdout.write("READY\n"); + const buffer: Buffer[] = []; process.stdin.on("data", async (data) => { let idx = data.indexOf(NEW_LINE); @@ -53,7 +60,7 @@ process.stdin.on("data", async (data) => { } catch (e: any) { console.log(`ERROR=${JSON.stringify(e.stack)}`); } - console.log(END_OF_OPERATION); + console.log(OPERATION_SUCCESS_MARKER.toString("utf8")); data = data.slice(idx + 1); idx = data.indexOf(NEW_LINE); } diff --git a/packages/next-swc/crates/next-core/js/src/entry/app/hydrate.tsx b/packages/next-swc/crates/next-core/js/src/entry/app/hydrate.tsx index 50059fb25b..93adeee007 100644 --- a/packages/next-swc/crates/next-core/js/src/entry/app/hydrate.tsx +++ b/packages/next-swc/crates/next-core/js/src/entry/app/hydrate.tsx @@ -1,10 +1,16 @@ import ReactDOMClient from "react-dom/client"; import React, { experimental_use as use } from "react"; import type { ReactElement } from "react"; -import { createFromReadableStream } from "next/dist/compiled/react-server-dom-webpack"; +import { createFromReadableStream } from "next/dist/compiled/react-server-dom-webpack/client"; import { HeadManagerContext } from "next/dist/shared/lib/head-manager-context"; +import { initializeHMR } from "@vercel/turbopack-next/dev/client"; + +initializeHMR({ + assetPrefix: "", +}); + globalThis.__next_require__ = (data) => { const [, client_id] = JSON.parse(data); return __turbopack_require__(client_id); diff --git a/packages/next-swc/crates/next-core/js/src/entry/app/layout-entry.tsx b/packages/next-swc/crates/next-core/js/src/entry/app/layout-entry.tsx index c5c5dd57d3..ad39d203f4 100644 --- a/packages/next-swc/crates/next-core/js/src/entry/app/layout-entry.tsx +++ b/packages/next-swc/crates/next-core/js/src/entry/app/layout-entry.tsx @@ -5,6 +5,6 @@ export { staticGenerationAsyncStorage } from "next/dist/esm/client/components/st export { requestAsyncStorage } from "next/dist/esm/client/components/request-async-storage.js"; import * as serverHooks from "next/dist/esm/client/components/hooks-server-context.js"; export { serverHooks }; -export { renderToReadableStream } from "next/dist/compiled/react-server-dom-webpack/writer.browser.server"; +export { renderToReadableStream } from "next/dist/compiled/react-server-dom-webpack/server.browser"; export { default } from "."; diff --git a/packages/next-swc/crates/next-core/js/types/modules.d.ts b/packages/next-swc/crates/next-core/js/types/modules.d.ts index 1996418b14..6400dd185c 100644 --- a/packages/next-swc/crates/next-core/js/types/modules.d.ts +++ b/packages/next-swc/crates/next-core/js/types/modules.d.ts @@ -1,4 +1,4 @@ declare module "next/dist/esm/client/components/static-generation-async-storage.js"; declare module "next/dist/esm/client/components/request-async-storage.js"; declare module "next/dist/esm/client/components/hooks-server-context.js"; -declare module "next/dist/compiled/react-server-dom-webpack/writer.browser.server"; +declare module "next/dist/compiled/react-server-dom-webpack/server.browser"; diff --git a/packages/next-swc/crates/next-core/src/app_source.rs b/packages/next-swc/crates/next-core/src/app_source.rs index 027ff9b77d..fb35ac2d7f 100644 --- a/packages/next-swc/crates/next-core/src/app_source.rs +++ b/packages/next-swc/crates/next-core/src/app_source.rs @@ -108,14 +108,14 @@ async fn next_client_transition( #[turbo_tasks::function] fn next_ssr_client_module_transition( - project_path: FileSystemPathVc, + project_root: FileSystemPathVc, app_dir: FileSystemPathVc, process_env: ProcessEnvVc, ) -> TransitionVc { let ty = Value::new(ServerContextType::AppSSR { app_dir }); NextSSRClientModuleTransition { ssr_module_options_context: get_server_module_options_context(ty), - ssr_resolve_options_context: get_server_resolve_options_context(project_path, ty), + ssr_resolve_options_context: get_server_resolve_options_context(project_root, ty), ssr_environment: get_server_environment(ty, process_env), } .cell() @@ -148,13 +148,13 @@ fn next_layout_entry_transition( /// Next.js app folder. #[turbo_tasks::function] pub async fn create_app_source( - project_path: FileSystemPathVc, + project_root: FileSystemPathVc, output_path: FileSystemPathVc, server_root: FileSystemPathVc, env: ProcessEnvVc, browserslist_query: &str, ) -> Result { - let project_root = wrap_with_next_js_fs(project_path); + let project_root = wrap_with_next_js_fs(project_root); let app = project_root.join("app"); let src_app = project_root.join("src/app"); @@ -202,7 +202,7 @@ pub async fn create_app_source( let server_runtime_entries = vec![ProcessEnvAssetVc::new(project_root, env).as_ecmascript_chunk_placeable()]; - let fallback_page = get_fallback_page(project_path, server_root, browserslist_query); + let fallback_page = get_fallback_page(project_root, server_root, browserslist_query); Ok(create_app_source_for_directory( context, diff --git a/packages/next-swc/crates/next-core/src/next_import_map.rs b/packages/next-swc/crates/next-core/src/next_import_map.rs index 69d899aad8..bf88c1ff16 100644 --- a/packages/next-swc/crates/next-core/src/next_import_map.rs +++ b/packages/next-swc/crates/next-core/src/next_import_map.rs @@ -3,7 +3,7 @@ use turbo_tasks_fs::FileSystemPathVc; use turbopack_core::resolve::options::{ImportMap, ImportMapVc, ImportMapping, ImportMappingVc}; use crate::{ - embed_js::{attached_next_js_package_path, next_js_fs, VIRTUAL_PACKAGE_NAME}, + embed_js::{attached_next_js_package_path, VIRTUAL_PACKAGE_NAME}, next_client::context::ContextType, next_server::ServerContextType, }; @@ -39,21 +39,14 @@ pub fn get_next_client_import_map( ); } ContextType::App { app_dir } => { - import_map.insert_exact_alias( - "react", - request_to_import_mapping(app_dir, "next/dist/compiled/react"), - ); - import_map.insert_wildcard_alias( - "react/", - request_to_import_mapping(app_dir, "next/dist/compiled/react/*"), - ); - import_map.insert_exact_alias( - "react-dom", - request_to_import_mapping(app_dir, "next/dist/compiled/react-dom"), - ); + import_map.insert_exact_alias("react", request_to_import_mapping(app_dir, "react")); + import_map + .insert_wildcard_alias("react/", request_to_import_mapping(app_dir, "react/*")); + import_map + .insert_exact_alias("react-dom", request_to_import_mapping(app_dir, "react-dom")); import_map.insert_wildcard_alias( "react-dom/", - request_to_import_mapping(app_dir, "next/dist/compiled/react-dom/*"), + request_to_import_mapping(app_dir, "react-dom/*"), ); } ContextType::Other => {} @@ -89,6 +82,10 @@ pub fn get_next_client_fallback_import_map(ty: Value) -> ImportMapV import_map.cell() } +// TODO this should be a user configurable option +/// Temporary hard coded externals list +static HARD_CODED_EXTERNALS: &[&str] = &["prisma", "@prisma/client", "@mux/blurhash"]; + /// Computes the Next-specific server-side import map. #[turbo_tasks::function] pub fn get_next_server_import_map( @@ -119,37 +116,33 @@ pub fn get_next_server_import_map( ], ); - import_map.insert_wildcard_alias( - format!("{VIRTUAL_PACKAGE_NAME}/"), - ImportMapping::PrimaryAlternative("./*".to_string(), Some(next_js_fs().root())) - .cell(), - ); - import_map.insert_exact_alias("next", ImportMapping::External(None).into()); import_map.insert_wildcard_alias("next/", ImportMapping::External(None).into()); import_map.insert_exact_alias("react", ImportMapping::External(None).into()); import_map.insert_wildcard_alias("react/", ImportMapping::External(None).into()); + import_map.insert_exact_alias("react-dom", ImportMapping::External(None).into()); + import_map.insert_wildcard_alias("react-dom/", ImportMapping::External(None).into()); } ServerContextType::AppSSR { app_dir } | ServerContextType::AppRSC { app_dir } => { - import_map.insert_exact_alias( - "react", - request_to_import_mapping(app_dir, "next/dist/compiled/react"), - ); - import_map.insert_wildcard_alias( - "react/", - request_to_import_mapping(app_dir, "next/dist/compiled/react/*"), - ); + import_map.insert_exact_alias("react", request_to_import_mapping(app_dir, "react")); + import_map + .insert_wildcard_alias("react/", request_to_import_mapping(app_dir, "react/*")); import_map.insert_exact_alias( "react-dom", - request_to_import_mapping( - app_dir, - "next/dist/compiled/react-dom/server-rendering-stub.js", - ), + request_to_import_mapping(app_dir, "react-dom/server-rendering-stub.js"), ); import_map.insert_wildcard_alias( "react-dom/", - request_to_import_mapping(app_dir, "next/dist/compiled/react-dom/*"), + request_to_import_mapping(app_dir, "react-dom/*"), ); + + for &external in HARD_CODED_EXTERNALS { + import_map.insert_exact_alias(external, ImportMapping::External(None).into()); + import_map.insert_wildcard_alias( + format!("{external}/"), + ImportMapping::External(None).into(), + ); + } } } diff --git a/packages/next-swc/crates/next-core/src/nodejs/pool.rs b/packages/next-swc/crates/next-core/src/nodejs/pool.rs index 1d281e556c..40f15cec6f 100644 --- a/packages/next-swc/crates/next-core/src/nodejs/pool.rs +++ b/packages/next-swc/crates/next-core/src/nodejs/pool.rs @@ -1,6 +1,6 @@ use std::{ collections::HashMap, - io::{BufRead, BufReader, Write}, + io::{BufRead, BufReader, ErrorKind, Write}, path::{Path, PathBuf}, process::{Child, ChildStdin, ChildStdout, Command, Stdio}, sync::{Arc, Mutex}, @@ -280,14 +280,23 @@ impl NodeJsOperation { let child = self.expect_child_mut(); let mut total_read = 0; loop { - total_read += child.read_until(b'\n', buf)?; + let read = child.read_until(b'\n', buf)?; + total_read += read; match child.marker.read_event(&buf) { Some((read, event)) => { buf.truncate(buf.len() - read); break Ok((total_read - read, event)); } - None => {} + None => { + if read == 0 { + // we need to stop reading in this case otherwise this loop infinitely + return Err(std::io::Error::new( + ErrorKind::UnexpectedEof, + "process closed unexpectedly while waiting for an operation result", + )); + } + } } } } diff --git a/packages/next-swc/crates/next-dev/benches/bundlers/turbopack.rs b/packages/next-swc/crates/next-dev/benches/bundlers/turbopack.rs index 6f60abea7c..6e5f2360f1 100644 --- a/packages/next-swc/crates/next-dev/benches/bundlers/turbopack.rs +++ b/packages/next-swc/crates/next-dev/benches/bundlers/turbopack.rs @@ -47,9 +47,7 @@ impl Bundler for Turbopack { npm::install( install_dir, &[ - NpmPackage::new("react-refresh", "^0.12.0"), - NpmPackage::new("next", "^12.3.1"), - NpmPackage::new("@next/react-refresh-utils", "^12.3.1"), + NpmPackage::new("next", "12.3.2-canary.33"), // Dependency on this is inserted by swc's preset_env NpmPackage::new("@swc/helpers", "^0.4.11"), ],