Skip to content

Commit

Permalink
Move FlightManifestPlugin to server compilers (vercel#36810)
Browse files Browse the repository at this point in the history
* move FlightManifestPlugin to server compilers

* revert loader condition

* fix module id

* fix test and refactor

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
shuding and kodiakhq[bot] authored May 10, 2022
1 parent 22c0b65 commit fdc071c
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 51 deletions.
15 changes: 9 additions & 6 deletions packages/next/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -909,9 +909,8 @@ export default async function getBaseWebpackConfig(
},
}

const rscCodeCondition = {
const serverComponentCodeCondition = {
test: serverComponentsRegex,
// only apply to the pages as the begin process of rsc loaders
include: [dir, /next[\\/]dist[\\/]pages/],
}

Expand Down Expand Up @@ -1210,7 +1209,7 @@ export default async function getBaseWebpackConfig(
? [
// RSC server compilation loaders
{
...rscCodeCondition,
...serverComponentCodeCondition,
use: {
loader: 'next-flight-server-loader',
},
Expand All @@ -1219,7 +1218,7 @@ export default async function getBaseWebpackConfig(
: [
// RSC client compilation loaders
{
...rscCodeCondition,
...serverComponentCodeCondition,
use: {
loader: 'next-flight-server-loader',
options: {
Expand Down Expand Up @@ -1589,8 +1588,12 @@ export default async function getBaseWebpackConfig(
},
}),
hasServerComponents &&
isClient &&
new FlightManifestPlugin({ dev, pageExtensions: rawPageExtensions }),
!isClient &&
new FlightManifestPlugin({
dev,
pageExtensions: rawPageExtensions,
isEdgeServer,
}),
!dev &&
isClient &&
new TelemetryPlugin(
Expand Down
22 changes: 7 additions & 15 deletions packages/next/build/webpack/loaders/next-flight-server-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { builtinModules } from 'module'
import { parse } from '../../swc'
import {
buildExports,
createClientComponentFilter,
createServerComponentFilter,
clientComponentRegex,
serverComponentRegex,
isNextBuiltinClientComponent,
} from './utils'

Expand All @@ -25,15 +25,11 @@ async function parseModuleInfo({
resourcePath,
source,
isClientCompilation,
isServerComponent,
isClientComponent,
resolver,
}: {
resourcePath: string
source: string
isClientCompilation: boolean
isServerComponent: (name: string) => boolean
isClientComponent: (name: string) => boolean
resolver: (req: string) => Promise<string>
}): Promise<{
source: string
Expand Down Expand Up @@ -71,10 +67,10 @@ async function parseModuleInfo({
}

function addClientImport(path: string) {
if (isServerComponent(path) || hasFlightLoader(path, 'server')) {
if (serverComponentRegex.test(path) || hasFlightLoader(path, 'server')) {
// If it's a server component, we recursively import its dependencies.
imports.push(path)
} else if (isClientComponent(path)) {
} else if (clientComponentRegex.test(path)) {
// Client component.
imports.push(path)
} else {
Expand Down Expand Up @@ -102,7 +98,7 @@ async function parseModuleInfo({

if (!isClientCompilation) {
// Server compilation for .server.js.
if (isServerComponent(importSource)) {
if (serverComponentRegex.test(importSource)) {
continue
}

Expand All @@ -111,7 +107,7 @@ async function parseModuleInfo({
node.source.span.start - beginPos
)

if (isClientComponent(importSource)) {
if (clientComponentRegex.test(importSource)) {
transformedSource += importDeclarations
transformedSource += JSON.stringify(
`next-flight-client-loader!${importSource}`
Expand Down Expand Up @@ -210,12 +206,10 @@ export default async function transformSource(
throw new Error('Expected source to have been transformed to a string.')
}

const isServerComponent = createServerComponentFilter()
const isClientComponent = createClientComponentFilter()
const hasAppliedFlightServerLoader = this.loaders.some((loader: any) => {
return hasFlightLoader(loader.path, 'server')
})
const isServerExt = isServerComponent(resourcePath)
const isServerExt = serverComponentRegex.test(resourcePath)

if (!isClientCompilation) {
// We only apply the loader to server components, or shared components that
Expand All @@ -235,8 +229,6 @@ export default async function transformSource(
resourcePath,
source,
isClientCompilation,
isServerComponent,
isClientComponent,
resolver,
})

Expand Down
35 changes: 14 additions & 21 deletions packages/next/build/webpack/loaders/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,18 @@ export function buildExports(moduleExports: any, isESM: boolean) {
return ret
}

export const createClientComponentFilter = () => {
// Special cases for Next.js APIs that are considered as client components:
// - .client.[ext]
// - next built-in client components
// - .[imageExt]
const regex = new RegExp(
'(' +
`\\.client(\\.(${defaultJsFileExtensions.join('|')}))?|` +
`next/(${nextClientComponents.join('|')})(\\.js)?|` +
`\\.(${imageExtensions.join('|')})` +
')$'
)

return (importSource: string) => regex.test(importSource)
}
// Special cases for Next.js APIs that are considered as client components:
// - .client.[ext]
// - next built-in client components
// - .[imageExt]
export const clientComponentRegex = new RegExp(
'(' +
`\\.client(\\.(${defaultJsFileExtensions.join('|')}))?|` +
`next/(${nextClientComponents.join('|')})(\\.js)?|` +
`\\.(${imageExtensions.join('|')})` +
')$'
)

export const createServerComponentFilter = () => {
const regex = new RegExp(
`\\.server(\\.(${defaultJsFileExtensions.join('|')}))?$`
)
return (importSource: string) => regex.test(importSource)
}
export const serverComponentRegex = new RegExp(
`\\.server(\\.(${defaultJsFileExtensions.join('|')}))?$`
)
42 changes: 33 additions & 9 deletions packages/next/build/webpack/plugins/flight-manifest-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { webpack, sources } from 'next/dist/compiled/webpack/webpack'
import { MIDDLEWARE_FLIGHT_MANIFEST } from '../../../shared/lib/constants'
import { createClientComponentFilter } from '../loaders/utils'
import { clientComponentRegex } from '../loaders/utils'

// This is the module that will be used to anchor all client references to.
// I.e. it will have all the client files as async deps from this point on.
Expand All @@ -19,20 +19,25 @@ import { createClientComponentFilter } from '../loaders/utils'
type Options = {
dev: boolean
pageExtensions: string[]
isEdgeServer: boolean
}

const PLUGIN_NAME = 'FlightManifestPlugin'

const isClientComponent = createClientComponentFilter()
let edgeFlightManifest = {}
let nodeFlightManifest = {}

export class FlightManifestPlugin {
dev: boolean = false
pageExtensions: string[]
isEdgeServer: boolean

constructor(options: Options) {
if (typeof options.dev === 'boolean') {
this.dev = options.dev
}
this.pageExtensions = options.pageExtensions
this.isEdgeServer = options.isEdgeServer
}

apply(compiler: any) {
Expand Down Expand Up @@ -72,9 +77,14 @@ export class FlightManifestPlugin {
// TODO: Hook into deps instead of the target module.
// That way we know by the type of dep whether to include.
// It also resolves conflicts when the same module is in multiple chunks.
if (!resource || !isClientComponent(resource)) {
if (
!resource ||
!clientComponentRegex.test(resource) ||
!clientComponentRegex.test(id)
) {
return
}

const moduleExports: any = manifest[resource] || {}

const exportsInfo = compilation.moduleGraph.getExportsInfo(mod)
Expand Down Expand Up @@ -107,10 +117,13 @@ export class FlightManifestPlugin {
for (const mod of chunkModules) {
let modId = compilation.chunkGraph.getModuleId(mod)

// remove resource query on production
if (typeof modId === 'string') {
modId = modId.split('?')[0]
}
if (typeof modId !== 'string') continue

// Remove resource queries.
modId = modId.split('?')[0]
// Remove the loader prefix.
modId = modId.split('next-flight-client-loader.js!')[1] || modId

recordModule(modId, chunk, mod)
// If this is a concatenation, register each child to the parent ID.
if (mod.modules) {
Expand All @@ -124,8 +137,19 @@ export class FlightManifestPlugin {

// With switchable runtime, we need to emit the manifest files for both
// runtimes.
const file = `server/${MIDDLEWARE_FLIGHT_MANIFEST}`
const json = JSON.stringify(manifest)
if (this.isEdgeServer) {
edgeFlightManifest = manifest
} else {
nodeFlightManifest = manifest
}
const mergedManifest = {
...nodeFlightManifest,
...edgeFlightManifest,
}
const file =
(!this.dev && !this.isEdgeServer ? '../' : '') +
MIDDLEWARE_FLIGHT_MANIFEST
const json = JSON.stringify(mergedManifest)

assets[file + '.js'] = new sources.RawSource('self.__RSC_MANIFEST=' + json)
assets[file + '.json'] = new sources.RawSource(json)
Expand Down

0 comments on commit fdc071c

Please sign in to comment.