Skip to content

Commit

Permalink
polish
Browse files Browse the repository at this point in the history
  • Loading branch information
gaojude committed Nov 19, 2024
1 parent 1479146 commit 0c21888
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 112 deletions.
1 change: 1 addition & 0 deletions packages/next/src/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1906,6 +1906,7 @@ export default async function getBaseWebpackConfig(
? new ClientReferenceManifestPlugin({
dev,
appDir,
experimentalInlineCss: !!config.experimental.inlineCss,
})
: new FlightClientEntryPlugin({
appDir,
Expand Down
25 changes: 21 additions & 4 deletions packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import type { ModuleInfo } from './flight-client-entry-plugin'
interface Options {
dev: boolean
appDir: string
experimentalInlineCss: boolean
}

/**
Expand Down Expand Up @@ -83,11 +84,19 @@ export interface ClientReferenceManifestForRsc {
}
}

export interface EntryCssFile {
export type CssResource = InlinedCssFile | UninlinedCssFile

interface InlinedCssFile {
path: string
inlined: true
content: string
}

interface UninlinedCssFile {
path: string
inlined: false
}

export interface ClientReferenceManifest extends ClientReferenceManifestForRsc {
readonly moduleLoading: {
prefix: string
Expand All @@ -100,7 +109,7 @@ export interface ClientReferenceManifest extends ClientReferenceManifestForRsc {
[moduleId: string]: ManifestNode
}
entryCSSFiles: {
[entry: string]: EntryCssFile[]
[entry: string]: CssResource[]
}
entryJSFiles?: {
[entry: string]: string[]
Expand Down Expand Up @@ -205,11 +214,13 @@ export class ClientReferenceManifestPlugin {
dev: Options['dev'] = false
appDir: Options['appDir']
appDirBase: string
experimentalInlineCss: Options['experimentalInlineCss']

constructor(options: Options) {
this.dev = options.dev
this.appDir = options.appDir
this.appDirBase = path.dirname(this.appDir) + path.sep
this.experimentalInlineCss = options.experimentalInlineCss
}

apply(compiler: webpack.Compiler) {
Expand Down Expand Up @@ -307,10 +318,16 @@ export class ClientReferenceManifestPlugin {
.filter((f) => !f.startsWith('static/css/pages/') && f.endsWith('.css'))
.map((file) => {
const source = compilation.assets[file].source()
// TODO: only do this when inlineCss is enabled
if (this.experimentalInlineCss) {
return {
inlined: true,
path: file,
content: typeof source === 'string' ? source : source.toString(),
}
}
return {
inlined: false,
path: file,
content: typeof source === 'string' ? source : source.toString(),
}
})

Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/server/app-render/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import type { LoaderTree } from '../lib/app-dir-module'
import type { AppPageModule } from '../route-modules/app-page/module'
import type {
ClientReferenceManifest,
EntryCssFile,
CssResource,
ManifestNode,
} from '../../build/webpack/plugins/flight-manifest-plugin'
import type { DeepReadonly } from '../../shared/lib/deep-readonly'
Expand Down Expand Up @@ -691,7 +691,7 @@ async function getRSCPayload(
ctx: AppRenderContext,
is404: boolean
): Promise<InitialRSCPayload & { P: React.ReactNode }> {
const injectedCSS = new Set<EntryCssFile>()
const injectedCSS = new Set<CssResource>()
const injectedJS = new Set<string>()
const injectedFontPreloadTags = new Set<string>()
let missingSlots: Set<string> | undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { getLinkAndScriptTags } from './get-css-inlined-link-tags'
import type { AppRenderContext } from './app-render'
import { getAssetQueryString } from './get-asset-query-string'
import { encodeURIPath } from '../../shared/lib/encode-uri-path'
import type { EntryCssFile } from '../../build/webpack/plugins/flight-manifest-plugin'
import type { CssResource } from '../../build/webpack/plugins/flight-manifest-plugin'
import { renderCssResource } from './render-css-resource'

// [STEP 1] TODO: consolidate this with get-layer-assets.tsx
export async function createComponentStylesAndScripts({
filePath,
getComponent,
Expand All @@ -16,46 +16,18 @@ export async function createComponentStylesAndScripts({
}: {
filePath: string
getComponent: () => any
injectedCSS: Set<EntryCssFile>
injectedCSS: Set<CssResource>
injectedJS: Set<string>
ctx: AppRenderContext
}): Promise<[React.ComponentType<any>, React.ReactNode, React.ReactNode]> {
const { styles: cssHrefs, scripts: jsHrefs } = getLinkAndScriptTags(
const { styles: entryCssFiles, scripts: jsHrefs } = getLinkAndScriptTags(
ctx.clientReferenceManifest,
filePath,
injectedCSS,
injectedJS
)

const styles = cssHrefs
? cssHrefs.map((entryCssFile, index) => {
const fullHref = `${ctx.assetPrefix}/_next/${encodeURIPath(
entryCssFile.path
)}${getAssetQueryString(ctx, true)}`

// `Precedence` is an opt-in signal for React to handle resource
// loading and deduplication, etc. It's also used as the key to sort
// resources so they will be injected in the correct order.
// During HMR, it's critical to use different `precedence` values
// for different stylesheets, so their order will be kept.
// https://github.com/facebook/react/pull/25060
const precedence =
process.env.NODE_ENV === 'development'
? 'next_' + entryCssFile.path
: 'next'

return (
<link
rel="stylesheet"
href={fullHref}
// @ts-ignore
precedence={precedence}
crossOrigin={ctx.renderOpts.crossOrigin}
key={`style-${index}`}
/>
)
})
: null
const styles = renderCssResource(entryCssFiles, ctx)

const scripts = jsHrefs
? jsHrefs.map((href, index) => (
Expand Down
6 changes: 3 additions & 3 deletions packages/next/src/server/app-render/create-component-tree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import type { LoadingModuleData } from '../../shared/lib/app-router-context.shar
import type { Params } from '../request/params'
import { workUnitAsyncStorage } from './work-unit-async-storage.external'
import { OUTLET_BOUNDARY_NAME } from '../../lib/metadata/metadata-constants'
import type { EntryCssFile } from '../../build/webpack/plugins/flight-manifest-plugin'
import type { CssResource } from '../../build/webpack/plugins/flight-manifest-plugin'

/**
* Use the provided loader tree to create the React Component tree.
Expand All @@ -33,7 +33,7 @@ export function createComponentTree(props: {
parentParams: Params
rootLayoutIncluded: boolean
firstItem?: boolean
injectedCSS: Set<EntryCssFile>
injectedCSS: Set<CssResource>
injectedJS: Set<string>
injectedFontPreloadTags: Set<string>
getMetadataReady: () => Promise<void>
Expand Down Expand Up @@ -81,7 +81,7 @@ async function createComponentTreeInternal({
parentParams: Params
rootLayoutIncluded: boolean
firstItem?: boolean
injectedCSS: Set<EntryCssFile>
injectedCSS: Set<CssResource>
injectedJS: Set<string>
injectedFontPreloadTags: Set<string>
getMetadataReady: () => Promise<void>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {
ClientReferenceManifest,
EntryCssFile,
CssResource,
} from '../../build/webpack/plugins/flight-manifest-plugin'
import type { DeepReadonly } from '../../shared/lib/deep-readonly'

Expand All @@ -10,12 +10,12 @@ import type { DeepReadonly } from '../../shared/lib/deep-readonly'
export function getLinkAndScriptTags(
clientReferenceManifest: DeepReadonly<ClientReferenceManifest>,
filePath: string,
injectedCSS: Set<EntryCssFile>,
injectedCSS: Set<CssResource>,
injectedScripts: Set<string>,
collectNewImports?: boolean
): { styles: EntryCssFile[]; scripts: string[] } {
): { styles: CssResource[]; scripts: string[] } {
const filePathWithoutExt = filePath.replace(/\.[^.]+$/, '')
const cssChunks = new Set<EntryCssFile>()
const cssChunks = new Set<CssResource>()
const jsChunks = new Set<string>()

const entryCSSFiles =
Expand Down
68 changes: 4 additions & 64 deletions packages/next/src/server/app-render/get-layer-assets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import type { AppRenderContext } from './app-render'
import { getAssetQueryString } from './get-asset-query-string'
import { encodeURIPath } from '../../shared/lib/encode-uri-path'
import type { PreloadCallbacks } from './types'
import type { EntryCssFile } from '../../build/webpack/plugins/flight-manifest-plugin'
import type { CssResource } from '../../build/webpack/plugins/flight-manifest-plugin'
import { renderCssResource } from './render-css-resource'

export function getLayerAssets({
ctx,
Expand All @@ -16,7 +17,7 @@ export function getLayerAssets({
preloadCallbacks,
}: {
layoutOrPagePath: string | undefined
injectedCSS: Set<EntryCssFile>
injectedCSS: Set<CssResource>
injectedJS: Set<string>
injectedFontPreloadTags: Set<string>
ctx: AppRenderContext
Expand Down Expand Up @@ -73,68 +74,7 @@ export function getLayerAssets({
}
}

const styles = styleTags
? styleTags.map((entryCssFile, index) => {
// `Precedence` is an opt-in signal for React to handle resource
// loading and deduplication, etc. It's also used as the key to sort
// resources so they will be injected in the correct order.
// During HMR, it's critical to use different `precedence` values
// for different stylesheets, so their order will be kept.
// https://github.com/facebook/react/pull/25060
const precedence =
process.env.NODE_ENV === 'development'
? 'next_' + entryCssFile.path
: 'next'

// In dev, Safari and Firefox will cache the resource during HMR:
// - https://github.com/vercel/next.js/issues/5860
// - https://bugs.webkit.org/show_bug.cgi?id=187726
// Because of this, we add a `?v=` query to bypass the cache during
// development. We need to also make sure that the number is always
// increasing.
const fullHref = `${ctx.assetPrefix}/_next/${encodeURIPath(
entryCssFile.path
)}${getAssetQueryString(ctx, true)}`

if (
process.env.NEXT_RUNTIME !== 'edge' &&
ctx.renderOpts.experimental.inlineCss
) {
return (
<style
key={index}
dangerouslySetInnerHTML={{
__html: entryCssFile.content,
}}
nonce={ctx.nonce}
// @ts-ignore
precedence={precedence}
href={fullHref}
/>
)
}

preloadCallbacks.push(() => {
ctx.componentMod.preloadStyle(
fullHref,
ctx.renderOpts.crossOrigin,
ctx.nonce
)
})

return (
<link
rel="stylesheet"
href={fullHref}
// @ts-ignore
precedence={precedence}
crossOrigin={ctx.renderOpts.crossOrigin}
key={index}
nonce={ctx.nonce}
/>
)
})
: []
const styles = renderCssResource(styleTags, ctx, preloadCallbacks)

const scripts = scriptTags
? scriptTags.map((href, index) => {
Expand Down
74 changes: 74 additions & 0 deletions packages/next/src/server/app-render/render-css-resource.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import type { CssResource } from '../../build/webpack/plugins/flight-manifest-plugin'
import { encodeURIPath } from '../../shared/lib/encode-uri-path'
import type { AppRenderContext } from './app-render'
import { getAssetQueryString } from './get-asset-query-string'
import type { PreloadCallbacks } from './types'

/**
* Abstracts the rendering of CSS files based on whether they are inlined or not.
* For inlined CSS, renders a <style> tag with the CSS content directly embedded.
* For external CSS files, renders a <link> tag pointing to the CSS file.
*/
export function renderCssResource(
entryCssFiles: CssResource[],
ctx: AppRenderContext,
preloadCallbacks?: PreloadCallbacks
) {
return entryCssFiles.map((entryCssFile, index) => {
// `Precedence` is an opt-in signal for React to handle resource
// loading and deduplication, etc. It's also used as the key to sort
// resources so they will be injected in the correct order.
// During HMR, it's critical to use different `precedence` values
// for different stylesheets, so their order will be kept.
// https://github.com/facebook/react/pull/25060
const precedence =
process.env.NODE_ENV === 'development'
? 'next_' + entryCssFile.path
: 'next'

// In dev, Safari and Firefox will cache the resource during HMR:
// - https://github.com/vercel/next.js/issues/5860
// - https://bugs.webkit.org/show_bug.cgi?id=187726
// Because of this, we add a `?v=` query to bypass the cache during
// development. We need to also make sure that the number is always
// increasing.
const fullHref = `${ctx.assetPrefix}/_next/${encodeURIPath(
entryCssFile.path
)}${getAssetQueryString(ctx, true)}`

if (entryCssFile.inlined) {
return (
<style
key={index}
dangerouslySetInnerHTML={{
__html: entryCssFile.content,
}}
nonce={ctx.nonce}
// @ts-ignore
precedence={precedence}
href={fullHref}
/>
)
}

preloadCallbacks?.push(() => {
ctx.componentMod.preloadStyle(
fullHref,
ctx.renderOpts.crossOrigin,
ctx.nonce
)
})

return (
<link
key={index}
rel="stylesheet"
href={fullHref}
// @ts-ignore
precedence={precedence}
crossOrigin={ctx.renderOpts.crossOrigin}
nonce={ctx.nonce}
/>
)
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
addSearchParamsIfPageSegment,
} from '../../shared/lib/segment'
import { createComponentTree } from './create-component-tree'
import type { EntryCssFile } from '../../build/webpack/plugins/flight-manifest-plugin'
import type { CssResource } from '../../build/webpack/plugins/flight-manifest-plugin'

/**
* Use router state to decide at what common layout to render the page.
Expand Down Expand Up @@ -50,7 +50,7 @@ export async function walkTreeWithFlightRouterState({
flightRouterState?: FlightRouterState
parentRendered?: boolean
rscPayloadHead: React.ReactNode
injectedCSS: Set<EntryCssFile>
injectedCSS: Set<CssResource>
injectedJS: Set<string>
injectedFontPreloadTags: Set<string>
rootLayoutIncluded: boolean
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/server/config-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,7 @@ export const defaultConfig: NextConfig = {
staticGenerationMaxConcurrency: 8,
staticGenerationMinPagesPerWorker: 25,
dynamicIO: false,
inlineCss: false,
},
bundlePagesRouterDependencies: false,
}
Expand Down

0 comments on commit 0c21888

Please sign in to comment.