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

Colocate styles with special entries #42506

Merged
merged 10 commits into from Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/next/build/utils.ts
Expand Up @@ -1095,7 +1095,9 @@ export const collectGenerateParams = async (
): Promise<GenerateParams> => {
if (!Array.isArray(segment)) return generateParams
const isLayout = !!segment[2]?.layout
const mod = await (isLayout ? segment[2]?.layout?.() : segment[2]?.page?.())
const mod = await (isLayout
? segment[2]?.layout?.[0]?.()
: segment[2]?.page?.[0]?.())
const config = collectAppConfig(mod)

const result = {
Expand Down
21 changes: 10 additions & 11 deletions packages/next/build/webpack/loaders/next-app-loader.ts
Expand Up @@ -21,11 +21,11 @@ const PAGE_SEGMENT = 'page$'

// TODO-APP: check if this can be narrowed.
type ComponentModule = () => any
type ModuleReference = [componentModule: ComponentModule, filePath: string]
export type ComponentsType = {
readonly [componentKey in ValueOf<typeof FILE_TYPES>]?: ComponentModule
readonly [componentKey in ValueOf<typeof FILE_TYPES>]?: ModuleReference
} & {
readonly layoutOrPagePath?: string
readonly page?: ComponentModule
readonly page?: ModuleReference
}

async function createTreeCodeFromPath({
Expand Down Expand Up @@ -67,9 +67,10 @@ async function createTreeCodeFromPath({
if (resolvedPagePath) pages.push(resolvedPagePath)

// Use '' for segment as it's the page. There can't be a segment called '' so this is the safest way to add it.
props[parallelKey] = `['', {}, {layoutOrPagePath: ${JSON.stringify(
resolvedPagePath
)}, page: () => require(${JSON.stringify(resolvedPagePath)})}]`
props[parallelKey] = `['', {}, {
page: [() => require(${JSON.stringify(
resolvedPagePath
)}), ${JSON.stringify(resolvedPagePath)}]}]`
continue
}

Expand Down Expand Up @@ -105,11 +106,9 @@ async function createTreeCodeFromPath({
if (filePath === undefined) {
return ''
}
return `${
file === FILE_TYPES.layout
? `layoutOrPagePath: ${JSON.stringify(filePath)},`
: ''
}'${file}': () => require(${JSON.stringify(filePath)}),`
return `'${file}': [() => require(${JSON.stringify(
filePath
)}), ${JSON.stringify(filePath)}],`
})
.join('\n')}
}
Expand Down
40 changes: 20 additions & 20 deletions packages/next/build/webpack/plugins/flight-client-entry-plugin.ts
Expand Up @@ -179,17 +179,18 @@ export class FlightClientEntryPlugin {
for (const connection of compilation.moduleGraph.getOutgoingConnections(
entryModule
)) {
const layoutOrPageDependency = connection.dependency
const layoutOrPageRequest = connection.dependency.request
// Entry can be any user defined entry files such as layout, page, error, loading, etc.
const entryDependency = connection.dependency
const entryRequest = connection.dependency.request
shuding marked this conversation as resolved.
Show resolved Hide resolved

const [clientComponentImports] =
this.collectClientComponentsAndCSSForDependency({
layoutOrPageRequest,
entryRequest,
compilation,
dependency: layoutOrPageDependency,
dependency: entryDependency,
})

const isAbsoluteRequest = path.isAbsolute(layoutOrPageRequest)
const isAbsoluteRequest = path.isAbsolute(entryRequest)

// Next.js internals are put into a separate entry.
if (!isAbsoluteRequest) {
Expand All @@ -200,8 +201,8 @@ export class FlightClientEntryPlugin {
}

const relativeRequest = isAbsoluteRequest
? path.relative(compilation.options.context, layoutOrPageRequest)
: layoutOrPageRequest
? path.relative(compilation.options.context, entryRequest)
: entryRequest

// Replace file suffix as `.js` will be added.
const bundlePath = relativeRequest.replace(/\.(js|ts)x?$/, '')
Expand Down Expand Up @@ -308,14 +309,14 @@ export class FlightClientEntryPlugin {
for (const connection of compilation.moduleGraph.getOutgoingConnections(
entryModule
)) {
const layoutOrPageDependency = connection.dependency
const layoutOrPageRequest = connection.dependency.request
const entryDependency = connection.dependency
const entryRequest = connection.dependency.request

const [, cssImports] =
this.collectClientComponentsAndCSSForDependency({
layoutOrPageRequest,
entryRequest,
compilation,
dependency: layoutOrPageDependency,
dependency: entryDependency,
clientEntryDependencyMap,
})

Expand Down Expand Up @@ -353,12 +354,12 @@ export class FlightClientEntryPlugin {
}

collectClientComponentsAndCSSForDependency({
layoutOrPageRequest,
entryRequest,
compilation,
dependency,
clientEntryDependencyMap,
}: {
layoutOrPageRequest: string
entryRequest: string
compilation: any
dependency: any /* Dependency */
clientEntryDependencyMap?: Record<string, any>
Expand Down Expand Up @@ -397,15 +398,15 @@ export class FlightClientEntryPlugin {
: mod.resourceResolveData?.path + mod.resourceResolveData?.query

// Ensure module is not walked again if it's already been visited
if (!visitedBySegment[layoutOrPageRequest]) {
visitedBySegment[layoutOrPageRequest] = new Set()
if (!visitedBySegment[entryRequest]) {
visitedBySegment[entryRequest] = new Set()
}
const storeKey =
(inClientComponentBoundary ? '0' : '1') + ':' + modRequest
if (!modRequest || visitedBySegment[layoutOrPageRequest].has(storeKey)) {
if (!modRequest || visitedBySegment[entryRequest].has(storeKey)) {
return
}
visitedBySegment[layoutOrPageRequest].add(storeKey)
visitedBySegment[entryRequest].add(storeKey)

const isClientComponent = isClientComponentModule(mod)

Expand All @@ -425,9 +426,8 @@ export class FlightClientEntryPlugin {
}
}

serverCSSImports[layoutOrPageRequest] =
serverCSSImports[layoutOrPageRequest] || []
serverCSSImports[layoutOrPageRequest].push(modRequest)
serverCSSImports[entryRequest] = serverCSSImports[entryRequest] || []
serverCSSImports[entryRequest].push(modRequest)
}

// Check if request is for css file.
Expand Down
18 changes: 13 additions & 5 deletions packages/next/client/components/error-boundary.tsx
Expand Up @@ -6,6 +6,7 @@ export type ErrorComponent = React.ComponentType<{
}>
interface ErrorBoundaryProps {
errorComponent: ErrorComponent
errorStyles?: React.ReactNode | undefined
}

/**
Expand All @@ -32,10 +33,13 @@ class ErrorBoundaryHandler extends React.Component<
render() {
if (this.state.error) {
return (
<this.props.errorComponent
error={this.state.error}
reset={this.reset}
/>
<>
{this.props.errorStyles}
<this.props.errorComponent
error={this.state.error}
reset={this.reset}
/>
</>
)
}

Expand All @@ -49,11 +53,15 @@ class ErrorBoundaryHandler extends React.Component<
*/
export function ErrorBoundary({
errorComponent,
errorStyles,
children,
}: ErrorBoundaryProps & { children: React.ReactNode }): JSX.Element {
if (errorComponent) {
return (
<ErrorBoundaryHandler errorComponent={errorComponent}>
<ErrorBoundaryHandler
errorComponent={errorComponent}
errorStyles={errorStyles}
>
{children}
</ErrorBoundaryHandler>
)
Expand Down
52 changes: 44 additions & 8 deletions packages/next/client/components/layout-router.tsx
Expand Up @@ -257,15 +257,27 @@ export function InnerLayoutRouter({
function LoadingBoundary({
children,
loading,
loadingStyles,
hasLoading,
}: {
children: React.ReactNode
loading?: React.ReactNode
loadingStyles?: React.ReactNode
hasLoading: boolean
}): JSX.Element {
if (hasLoading) {
// @ts-expect-error TODO-APP: React.Suspense fallback type is wrong
return <React.Suspense fallback={loading}>{children}</React.Suspense>
return (
<React.Suspense
fallback={
<>
{loadingStyles}
{loading}
</>
}
>
{children}
</React.Suspense>
)
}

return <>{children}</>
Expand Down Expand Up @@ -322,6 +334,7 @@ function RedirectBoundary({ children }: { children: React.ReactNode }) {

interface NotFoundBoundaryProps {
notFound?: React.ReactNode
notFoundStyles?: React.ReactNode
children: React.ReactNode
}

Expand All @@ -347,6 +360,7 @@ class NotFoundErrorBoundary extends React.Component<
return (
<>
<meta name="robots" content="noindex" />
{this.props.notFoundStyles}
{this.props.notFound}
</>
)
Expand All @@ -356,9 +370,13 @@ class NotFoundErrorBoundary extends React.Component<
}
}

function NotFoundBoundary({ notFound, children }: NotFoundBoundaryProps) {
function NotFoundBoundary({
notFound,
notFoundStyles,
children,
}: NotFoundBoundaryProps) {
return notFound ? (
<NotFoundErrorBoundary notFound={notFound}>
<NotFoundErrorBoundary notFound={notFound} notFoundStyles={notFoundStyles}>
{children}
</NotFoundErrorBoundary>
) : (
Expand All @@ -375,20 +393,28 @@ export default function OuterLayoutRouter({
segmentPath,
childProp,
error,
errorStyles,
templateStyles,
loading,
loadingStyles,
hasLoading,
template,
notFound,
notFoundStyles,
rootLayoutIncluded,
}: {
parallelRouterKey: string
segmentPath: FlightSegmentPath
childProp: ChildProp
error: ErrorComponent
errorStyles: React.ReactNode | undefined
templateStyles: React.ReactNode | undefined
template: React.ReactNode
loading: React.ReactNode | undefined
loadingStyles: React.ReactNode | undefined
hasLoading: boolean
notFound: React.ReactNode | undefined
notFoundStyles: React.ReactNode | undefined
rootLayoutIncluded: boolean
}) {
const context = useContext(LayoutRouterContext)
Expand Down Expand Up @@ -442,9 +468,16 @@ export default function OuterLayoutRouter({
<TemplateContext.Provider
key={preservedSegment}
value={
<ErrorBoundary errorComponent={error}>
<LoadingBoundary hasLoading={hasLoading} loading={loading}>
<NotFoundBoundary notFound={notFound}>
<ErrorBoundary errorComponent={error} errorStyles={errorStyles}>
<LoadingBoundary
hasLoading={hasLoading}
loading={loading}
loadingStyles={loadingStyles}
>
<NotFoundBoundary
notFound={notFound}
notFoundStyles={notFoundStyles}
>
<RedirectBoundary>
<InnerLayoutRouter
parallelRouterKey={parallelRouterKey}
Expand All @@ -467,7 +500,10 @@ export default function OuterLayoutRouter({
</ErrorBoundary>
}
>
{template}
<>
{templateStyles}
{template}
</>
</TemplateContext.Provider>
)
})}
Expand Down