From 2eef220f595ed00fa227f29d8f2706687d2a3779 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Mon, 26 Sep 2022 22:38:22 +0200 Subject: [PATCH] refactor code --- .../core/src/react_server_components.rs | 56 +++++++++++------ .../plugins/flight-client-entry-plugin.ts | 33 +++------- .../webpack/plugins/flight-manifest-plugin.ts | 62 +++++++++++-------- test/e2e/app-dir/rsc-basic/app/page.js | 4 +- 4 files changed, 82 insertions(+), 73 deletions(-) diff --git a/packages/next-swc/crates/core/src/react_server_components.rs b/packages/next-swc/crates/core/src/react_server_components.rs index 4f7e4f3849e03..e4b89e29bd6c6 100644 --- a/packages/next-swc/crates/core/src/react_server_components.rs +++ b/packages/next-swc/crates/core/src/react_server_components.rs @@ -161,29 +161,47 @@ impl ReactServerComponents { prepend_stmts( &mut module.body, vec![ - ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl { + ModuleItem::Stmt(Stmt::Decl(Decl::Var(Box::new(VarDecl { span: DUMMY_SP, - specifiers: vec![ImportSpecifier::Named(ImportNamedSpecifier { + kind: VarDeclKind::Const, + decls: vec![VarDeclarator { span: DUMMY_SP, - local: proxy_ident.clone(), - imported: None, - is_type_only: false, - })], - src: Box::new(Str { - span: DUMMY_SP, - raw: None, - value: "private-next-rsc-mod-ref-proxy".into(), - }), - type_only: Default::default(), - asserts: Default::default(), - })), - ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(ExportDefaultExpr { + name: Pat::Object(ObjectPat { + span: DUMMY_SP, + props: vec![ObjectPatProp::Assign(AssignPatProp { + span: DUMMY_SP, + key: proxy_ident, + value: None, + })], + optional: false, + type_ann: None, + }), + init: Some(Box::new(Expr::Call(CallExpr { + span: DUMMY_SP, + callee: quote_ident!("require").as_callee(), + args: vec![quote_str!("private-next-rsc-mod-ref-proxy").as_arg()], + type_args: Default::default(), + }))), + definite: false, + }], + declare: false, + })))), + ModuleItem::Stmt(Stmt::Expr(ExprStmt { span: DUMMY_SP, - expr: Box::new(Expr::Call(CallExpr { + expr: Box::new(Expr::Assign(AssignExpr { span: DUMMY_SP, - callee: proxy_ident.clone().as_callee(), - args: vec![filepath.as_arg()], - type_args: None, + left: PatOrExpr::Expr(Box::new(Expr::Member(MemberExpr { + span: DUMMY_SP, + obj: Box::new(Expr::Ident(quote_ident!("module"))), + prop: MemberProp::Ident(quote_ident!("exports")), + }))), + op: op!("="), + right: Box::new(Expr::Call(CallExpr { + span: DUMMY_SP, + callee: quote_ident!("createProxy").as_callee(), + args: vec![filepath.as_arg()], + type_args: Default::default(), + })), })), })), ] diff --git a/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts b/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts index 1abbce194f410..d39dce912f92c 100644 --- a/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts @@ -16,7 +16,7 @@ import { COMPILER_NAMES, FLIGHT_SERVER_CSS_MANIFEST, } from '../../../shared/lib/constants' -import type { FlightCSSManifest } from './flight-manifest-plugin' +import { FlightCSSManifest, traverseModules } from './flight-manifest-plugin' import { ASYNC_CLIENT_MODULES } from './flight-manifest-plugin' import { isClientComponentModule } from '../loaders/utils' @@ -64,15 +64,15 @@ export class FlightClientEntryPlugin { }) compiler.hooks.afterCompile.tap(PLUGIN_NAME, (compilation) => { - ;(compilation.moduleGraph._moduleMap as any).forEach( - (_: any, mod: any) => { - if (mod.request && mod.resource && !mod.buildInfo.rsc) { - if (compilation.moduleGraph.isAsync(mod)) { - ASYNC_CLIENT_MODULES.add(mod.resource) - } + traverseModules(compilation, (mod) => { + // The module must has request, and resource so it's not a new entry created with loader. + // Using the client layer module, which doesn't have `rsc` tag in buildInfo. + if (mod.request && mod.resource && !mod.buildInfo.rsc) { + if (compilation.moduleGraph.isAsync(mod)) { + ASYNC_CLIENT_MODULES.add(mod.resource) } } - ) + }) }) } @@ -92,13 +92,6 @@ export class FlightClientEntryPlugin { const request = entryDependency.request - console.log('------', request) - // if (modRequest.includes('random')) { - // globalThis.__G = compilation.moduleGraph - // globalThis.__R = mod - // console.log(modRequest, '->', compilation.moduleGraph.isAsync(mod)) - // } - if ( !request.startsWith('next-edge-ssr-loader?') && !request.startsWith('next-app-loader?') @@ -393,16 +386,6 @@ export class FlightClientEntryPlugin { return reject(err) } - // console.log(entry); - - // [...module.dependencies].forEach(m => { - // if (m.request && m.request.includes('random')) { - // globalThis.__G = compilation.moduleGraph - // globalThis.__R = m - // console.log(m.request, '->', compilation.moduleGraph.isAsync(m), Object.keys(m)) - // } - // }) - compilation.hooks.succeedEntry.call(entry, options, module) return resolve(module) } diff --git a/packages/next/build/webpack/plugins/flight-manifest-plugin.ts b/packages/next/build/webpack/plugins/flight-manifest-plugin.ts index 3313fe5a1eed4..70e120975203c 100644 --- a/packages/next/build/webpack/plugins/flight-manifest-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-manifest-plugin.ts @@ -65,8 +65,37 @@ export type FlightCSSManifest = { const PLUGIN_NAME = 'FlightManifestPlugin' +// Collect modules from server/edge compiler in client layer, +// and detect if it's been used, and mark it as `async: true` for react. +// So that react could unwrap the async module from promise and render module itself. export const ASYNC_CLIENT_MODULES = new Set() +export function traverseModules( + compilation: webpack.Compilation, + callback: ( + mod: any, + chunk: webpack.Chunk, + chunkGroup: typeof compilation.chunkGroups[0] + ) => any +) { + compilation.chunkGroups.forEach((chunkGroup) => { + chunkGroup.chunks.forEach((chunk: webpack.Chunk) => { + const chunkModules = compilation.chunkGraph.getChunkModulesIterable( + chunk + // TODO: Update type so that it doesn't have to be cast. + ) as Iterable + for (const mod of chunkModules) { + callback(mod, chunk, chunkGroup) + const anyModule = mod as any + if (anyModule.modules) { + for (const subMod of anyModule.modules) + callback(subMod, chunk, chunkGroup) + } + } + }) + }) +} + export class FlightManifestPlugin { dev: Options['dev'] = false @@ -124,21 +153,7 @@ export class FlightManifestPlugin { } } - compilation.chunkGroups.forEach((chunkGroup) => { - chunkGroup.chunks.forEach((chunk: webpack.Chunk) => { - const chunkModules = compilation.chunkGraph.getChunkModulesIterable( - chunk - // TODO: Update type so that it doesn't have to be cast. - ) as Iterable - for (const mod of chunkModules) { - collectClientRequest(mod) - const anyModule = mod as any - if (anyModule.modules) { - for (const subMod of anyModule.modules) collectClientRequest(subMod) - } - } - }) - }) + traverseModules(compilation, (mod) => collectClientRequest(mod)) compilation.chunkGroups.forEach((chunkGroup) => { const cssResourcesInChunkGroup = new Set() @@ -215,16 +230,7 @@ export class FlightManifestPlugin { } const exportsInfo = compilation.moduleGraph.getExportsInfo(mod) - const isAsync = false // ASYNC_CLIENT_MODULES.has(mod.resource) // compilation.moduleGraph.isAsync(mod) - - if (isAsync) { - console.log('!!!!!!', mod.resource) - } - - // if (mod.resource.includes('random')) { - // console.log('!!!!!!!', mod.resource, isAsync, compilation.moduleGraph._getModuleGraphModule(mod).async, compilation.moduleGraph.isAsync) - // console.log('???????', mod.resource, ASYNC_CLIENT_MODULES.has(mod.resource)) - // } + const isAsyncModule = ASYNC_CLIENT_MODULES.has(mod.resource) const cjsExports = [ ...new Set([ @@ -278,8 +284,10 @@ export class FlightManifestPlugin { id, name, chunks: requiredChunks, - // Forcing the chunk to be `async` for esm compatible - async: isAsync, + // E.g. + // page (server) -> local module (client) -> package (esm) + // The esm package will bubble up to make the entire chain till the client entry as async module. + async: isAsyncModule, } } diff --git a/test/e2e/app-dir/rsc-basic/app/page.js b/test/e2e/app-dir/rsc-basic/app/page.js index 18625cb68eb0c..cde5a725d984a 100644 --- a/test/e2e/app-dir/rsc-basic/app/page.js +++ b/test/e2e/app-dir/rsc-basic/app/page.js @@ -1,6 +1,6 @@ import Nav from '../components/nav' import { headers } from 'next/dist/client/components/hooks-server' -// import { name } from 'random-module-instance' +import { name } from 'random-module-instance' const envVar = process.env.ENV_VAR_TEST const headerKey = 'x-next-test-client' @@ -14,7 +14,7 @@ export default function Index() {

{`component:index.server`}

{'env:' + envVar}
{'header:' + header}
- {/*

{name}

*/} +

{name}