diff --git a/packages/next-swc/crates/next-core/src/next_manifests/client_reference_manifest.rs b/packages/next-swc/crates/next-core/src/next_manifests/client_reference_manifest.rs index 4e7cbec12e..6b8e7fbbaf 100644 --- a/packages/next-swc/crates/next-core/src/next_manifests/client_reference_manifest.rs +++ b/packages/next-swc/crates/next-core/src/next_manifests/client_reference_manifest.rs @@ -74,6 +74,11 @@ impl ClientReferenceManifest { .entry(server_component_name.clone_value()) .or_default(); + let entry_js_files = entry_manifest + .entry_js_files + .entry(server_component_name.clone_value()) + .or_default(); + match app_client_reference_ty { ClientReferenceType::CssClientReference(_) => { entry_css_files.extend( @@ -88,9 +93,22 @@ impl ClientReferenceManifest { }) .map(ToString::to_string), ); + entry_js_files.extend( + client_chunks_paths + .iter() + .filter_map(|chunk_path| { + if chunk_path.extension_ref() != Some("css") { + client_relative_path.get_path_to(chunk_path) + } else { + None + } + }) + .map(ToString::to_string), + ); } ClientReferenceType::EcmascriptClientReference(_) => { + // TODO should this be removed? does it make sense? entry_css_files.extend( client_chunks_paths .iter() diff --git a/packages/next-swc/crates/next-core/src/next_manifests/mod.rs b/packages/next-swc/crates/next-core/src/next_manifests/mod.rs index b850c50634..9d1b98bb0b 100644 --- a/packages/next-swc/crates/next-core/src/next_manifests/mod.rs +++ b/packages/next-swc/crates/next-core/src/next_manifests/mod.rs @@ -206,6 +206,9 @@ pub struct ClientReferenceManifest { /// Mapping of server component path to required CSS client chunks. #[serde(rename = "entryCSSFiles")] pub entry_css_files: HashMap>, + /// Mapping of server component path to required JS client chunks. + #[serde(rename = "entryJSFiles")] + pub entry_js_files: HashMap>, } #[derive(Serialize, Default, Debug)] diff --git a/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts b/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts index 02af50abfb..aec39673ab 100644 --- a/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts +++ b/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts @@ -76,6 +76,9 @@ export type ClientReferenceManifest = { entryCSSFiles: { [entry: string]: string[] } + entryJSFiles?: { + [entry: string]: string[] + } } function getAppPathRequiredChunks( diff --git a/packages/next/src/client/components/error-boundary.tsx b/packages/next/src/client/components/error-boundary.tsx index 9cd2e6f9da..eb0f972d1f 100644 --- a/packages/next/src/client/components/error-boundary.tsx +++ b/packages/next/src/client/components/error-boundary.tsx @@ -32,6 +32,7 @@ export interface ErrorBoundaryProps { children?: React.ReactNode errorComponent: ErrorComponent errorStyles?: React.ReactNode | undefined + errorScripts?: React.ReactNode | undefined } interface ErrorBoundaryHandlerProps extends ErrorBoundaryProps { @@ -106,6 +107,7 @@ export class ErrorBoundaryHandler extends React.Component< <> {this.props.errorStyles} + {this.props.errorScripts} {children} diff --git a/packages/next/src/client/components/layout-router.tsx b/packages/next/src/client/components/layout-router.tsx index 2a2be86cb0..39a8bc794f 100644 --- a/packages/next/src/client/components/layout-router.tsx +++ b/packages/next/src/client/components/layout-router.tsx @@ -466,11 +466,13 @@ function LoadingBoundary({ children, loading, loadingStyles, + loadingScripts, hasLoading, }: { children: React.ReactNode loading?: React.ReactNode loadingStyles?: React.ReactNode + loadingScripts?: React.ReactNode hasLoading: boolean }): JSX.Element { if (hasLoading) { @@ -479,6 +481,7 @@ function LoadingBoundary({ fallback={ <> {loadingStyles} + {loadingScripts} {loading} } @@ -501,9 +504,12 @@ export default function OuterLayoutRouter({ childProp, error, errorStyles, + errorScripts, templateStyles, + templateScripts, loading, loadingStyles, + loadingScripts, hasLoading, template, notFound, @@ -515,10 +521,13 @@ export default function OuterLayoutRouter({ childProp: ChildProp error: ErrorComponent errorStyles: React.ReactNode | undefined + errorScripts: React.ReactNode | undefined templateStyles: React.ReactNode | undefined + templateScripts: React.ReactNode | undefined template: React.ReactNode loading: React.ReactNode | undefined loadingStyles: React.ReactNode | undefined + loadingScripts: React.ReactNode | undefined hasLoading: boolean notFound: React.ReactNode | undefined notFoundStyles: React.ReactNode | undefined @@ -580,11 +589,16 @@ export default function OuterLayoutRouter({ key={createRouterCacheKey(preservedSegment, true)} value={ - + {templateStyles} + {templateScripts} {template} ) diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index 44153006aa..46128261be 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -273,6 +273,7 @@ async function generateFlight( ), injectedCSS: new Set(), + injectedJS: new Set(), injectedFontPreloadTags: new Set(), rootLayoutIncluded: false, asNotFound: ctx.isNotFoundPath || options?.asNotFound, @@ -318,6 +319,7 @@ function createServerComponentsRenderer( preinitScripts() // Create full component tree from root to leaf. const injectedCSS = new Set() + const injectedJS = new Set() const injectedFontPreloadTags = new Set() const { getDynamicParamFromSegment, @@ -349,6 +351,7 @@ function createServerComponentsRenderer( parentParams: {}, firstItem: true, injectedCSS, + injectedJS, injectedFontPreloadTags, rootLayoutIncluded: false, asNotFound: props.asNotFound, diff --git a/packages/next/src/server/app-render/create-component-and-styles.tsx b/packages/next/src/server/app-render/create-component-styles-and-scripts.tsx similarity index 78% rename from packages/next/src/server/app-render/create-component-and-styles.tsx rename to packages/next/src/server/app-render/create-component-styles-and-scripts.tsx index 698716f983..de69f08662 100644 --- a/packages/next/src/server/app-render/create-component-and-styles.tsx +++ b/packages/next/src/server/app-render/create-component-styles-and-scripts.tsx @@ -1,24 +1,27 @@ import React from 'react' import { interopDefault } from './interop-default' -import { getCssInlinedLinkTags } from './get-css-inlined-link-tags' +import { getLinkAndScriptTags } from './get-css-inlined-link-tags' import type { AppRenderContext } from './app-render' import { getAssetQueryString } from './get-asset-query-string' -export async function createComponentAndStyles({ +export async function createComponentStylesAndScripts({ filePath, getComponent, injectedCSS, + injectedJS, ctx, }: { filePath: string getComponent: () => any injectedCSS: Set + injectedJS: Set ctx: AppRenderContext -}): Promise { - const cssHrefs = getCssInlinedLinkTags( +}): Promise<[any, React.ReactNode, React.ReactNode]> { + const { styles: cssHrefs, scripts: jsHrefs } = getLinkAndScriptTags( ctx.clientReferenceManifest, filePath, - injectedCSS + injectedCSS, + injectedJS ) const styles = cssHrefs @@ -56,7 +59,13 @@ export async function createComponentAndStyles({ }) : null + const scripts = jsHrefs + ? jsHrefs.map((href) => ( +