Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
wbinnssmith committed Feb 13, 2025
1 parent 00a6042 commit 243d2cb
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 31 deletions.
8 changes: 7 additions & 1 deletion packages/next/src/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ import { WellKnownErrorsPlugin } from './webpack/plugins/wellknown-errors-plugin
import { regexLikeCss } from './webpack/config/blocks/css'
import { CopyFilePlugin } from './webpack/plugins/copy-file-plugin'
import { ClientReferenceManifestPlugin } from './webpack/plugins/flight-manifest-plugin'
import { FlightClientEntryPlugin } from './webpack/plugins/flight-client-entry-plugin'
import { FlightClientEntryPlugin as NextFlightClientEntryPlugin } from './webpack/plugins/flight-client-entry-plugin'
import { RspackFlightClientEntryPlugin } from './webpack/plugins/rspack-flight-client-entry-plugin'
import { NextTypesPlugin } from './webpack/plugins/next-types-plugin'
import type {
Feature,
Expand Down Expand Up @@ -342,6 +343,11 @@ export default async function getBaseWebpackConfig(

const isRspack = Boolean(process.env.NEXT_RSPACK)

const FlightClientEntryPlugin =
isRspack && process.env.BUILTIN_FLIGHT_CLIENT_ENTRY_PLUGIN
? RspackFlightClientEntryPlugin
: NextFlightClientEntryPlugin

// If the current compilation is aimed at server-side code instead of client-side code.
const isNodeOrEdgeCompilation = isNodeServer || isEdgeServer

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ export default function transformSource(
buildInfo.rsc = {
type: RSC_MODULE_TYPES.client,
}
if (process.env.BUILTIN_FLIGHT_CLIENT_ENTRY_PLUGIN) {
const rscModuleInformationJson = JSON.stringify(buildInfo.rsc)
return (
`/* __rspack_internal_rsc_module_information_do_not_use__ ${rscModuleInformationJson} */\n` +
code
)
}

return code
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ const flightClientModuleLoader: webpack.LoaderDefinitionFunction =
// Assign the RSC meta information to buildInfo.
const buildInfo = getModuleBuildInfo(this._module)
buildInfo.rsc = getRSCModuleInformation(source, false)
let prefix = ''
if (process.env.BUILTIN_FLIGHT_CLIENT_ENTRY_PLUGIN) {
const rscModuleInformationJson = JSON.stringify(buildInfo.rsc)
prefix = `/* __rspack_internal_rsc_module_information_do_not_use__ ${rscModuleInformationJson} */\n`
source = prefix + source
}

// This is a server action entry module in the client layer. We need to
// create re-exports of "virtual modules" to expose the reference IDs to the
Expand All @@ -23,11 +29,14 @@ const flightClientModuleLoader: webpack.LoaderDefinitionFunction =
// production mode. In development mode, we want to preserve the original
// modules (as transformed by SWC) to ensure that source mapping works.
if (buildInfo.rsc.actionIds && process.env.NODE_ENV === 'production') {
return Object.entries(buildInfo.rsc.actionIds)
.map(([id, name]) => {
return `export { ${name} } from 'next-flight-server-reference-proxy-loader?id=${id}&name=${name}!'`
})
.join('\n')
return (
prefix +
Object.entries(buildInfo.rsc.actionIds)
.map(([id, name]) => {
return `export { ${name} } from 'next-flight-server-reference-proxy-loader?id=${id}&name=${name}!'`
})
.join('\n')
)
}

return this.callback(null, source, sourceMap)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,18 @@ export default function transformSource(
if (typeof source !== 'string') {
throw new Error('Expected source to have been transformed to a string.')
}

const module = this._module!

// Assign the RSC meta information to buildInfo.
// Exclude next internal files which are not marked as client files
const buildInfo = getModuleBuildInfo(module)
buildInfo.rsc = getRSCModuleInformation(source, true)
let prefix = ''
if (process.env.BUILTIN_FLIGHT_CLIENT_ENTRY_PLUGIN) {
const rscModuleInformationJson = JSON.stringify(buildInfo.rsc)
prefix = `/* __rspack_internal_rsc_module_information_do_not_use__ ${rscModuleInformationJson} */\n`
source = prefix + source
}

// Resource key is the unique identifier for the resource. When RSC renders
// a client module, that key is used to identify that module across all compiler
Expand Down Expand Up @@ -112,7 +117,9 @@ export default function transformSource(
return
}

let esmSource = `\
let esmSource =
prefix +
`\
import { registerClientReference } from "react-server-dom-webpack/server.edge";
`
for (const ref of clientRefs) {
Expand All @@ -139,12 +146,13 @@ ${JSON.stringify(ref)},

return this.callback(null, esmSource, sourceMap)
} else if (assumedSourceType === 'commonjs') {
let cjsSource = `\
let cjsSource =
prefix +
`\
const { createProxy } = require("${MODULE_PROXY_PATH}")
module.exports = createProxy(${stringifiedResourceKey})
`

return this.callback(null, cjsSource, sourceMap)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -941,38 +941,55 @@ export class FlightClientEntryPlugin {
}

addEntry(
compilation: any,
compilation: webpack.Compilation,
context: string,
dependency: webpack.Dependency,
options: webpack.EntryOptions
): Promise<any> /* Promise<module> */ {
return new Promise((resolve, reject) => {
const entry = compilation.entries.get(options.name)
entry.includeDependencies.push(dependency)
compilation.hooks.addEntry.call(entry, options)
compilation.addModuleTree(
{
context,
dependency,
contextInfo: { issuerLayer: options.layer },
},
(err: Error | undefined, module: any) => {
if ('rspack' in compilation.compiler) {
compilation.addInclude(context, dependency, options, (err, module) => {
if (err) {
compilation.hooks.failedEntry.call(dependency, options, err)
return reject(err)
}

compilation.hooks.succeedEntry.call(dependency, options, module)

compilation.moduleGraph
.getExportsInfo(module)
.getExportsInfo(module!)
.setUsedInUnknownWay(
this.isEdgeServer ? EDGE_RUNTIME_WEBPACK : DEFAULT_RUNTIME_WEBPACK
)

return resolve(module)
}
)
})
} else {
const entry = compilation.entries.get(options.name!)!
entry.includeDependencies.push(dependency)
compilation.hooks.addEntry.call(entry as any, options)
compilation.addModuleTree(
{
context,
dependency,
contextInfo: { issuerLayer: options.layer },
},
(err: any, module: any) => {
if (err) {
compilation.hooks.failedEntry.call(dependency, options, err)
return reject(err)
}

compilation.hooks.succeedEntry.call(dependency, options, module)

compilation.moduleGraph
.getExportsInfo(module)
.setUsedInUnknownWay(
this.isEdgeServer
? EDGE_RUNTIME_WEBPACK
: DEFAULT_RUNTIME_WEBPACK
)

return resolve(module)
}
)
}
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,7 @@ export class ClientReferenceManifestPlugin {
const recordModule = (modId: ModuleId, mod: webpack.NormalModule) => {
let resource =
mod.type === 'css/mini-extract'
? // @ts-expect-error TODO: use `identifier()` instead.
mod._identifier.slice(mod._identifier.lastIndexOf('!') + 1)
? mod.identifier().slice(mod.identifier().lastIndexOf('!') + 1)
: mod.resource

if (!resource) {
Expand Down Expand Up @@ -525,7 +524,11 @@ export class ClientReferenceManifestPlugin {
} else {
// If this is a concatenation, register each child to the parent ID.
if (
connection.module?.constructor.name === 'ConcatenatedModule'
connection.module?.constructor.name ===
'ConcatenatedModule' ||
(Boolean(process.env.NEXT_RSPACK) &&
(connection.module as any)?.constructorName ===
'ConcatenatedModule')
) {
const concatenatedMod = connection.module
const concatenatedModId =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import type { Compiler } from '@rspack/core'
import {
getInvalidator,
getEntries,
EntryTypes,
getEntryKey,
} from '../../../server/dev/on-demand-entry-handler'
import { COMPILER_NAMES } from '../../../shared/lib/constants'

import { getProxiedPluginState } from '../../build-context'
import { PAGE_TYPES } from '../../../lib/page-types'

// @ts-ignore
import { FlightClientEntryPlugin } from '@rspack/core'

type Actions = {
[actionId: string]: {
workers: {
[name: string]: { moduleId: string | number; async: boolean }
}
// Record which layer the action is in (rsc or sc_action), in the specific entry.
layer: {
[name: string]: string
}
}
}

export type ActionManifest = {
// Assign a unique encryption key during production build.
encryptionKey: string
node: Actions
edge: Actions
}

export interface ModuleInfo {
moduleId: string | number
async: boolean
}

const pluginState = getProxiedPluginState({
// A map to track "action" -> "list of bundles".
serverActions: {} as ActionManifest['node'],
edgeServerActions: {} as ActionManifest['edge'],

serverActionModules: {} as {
[workerName: string]: { server?: ModuleInfo; client?: ModuleInfo }
},

edgeServerActionModules: {} as {
[workerName: string]: { server?: ModuleInfo; client?: ModuleInfo }
},

ssrModules: {} as { [ssrModuleId: string]: ModuleInfo },
edgeSsrModules: {} as { [ssrModuleId: string]: ModuleInfo },

rscModules: {} as { [rscModuleId: string]: ModuleInfo },
edgeRscModules: {} as { [rscModuleId: string]: ModuleInfo },

injectedClientEntries: {} as Record<string, string>,
})

interface Options {
dev: boolean
appDir: string
isEdgeServer: boolean
encryptionKey: string
}

export class RspackFlightClientEntryPlugin {
plugin: any
compiler?: Compiler

constructor(options: Options) {
this.plugin = new FlightClientEntryPlugin({
...options,
builtinAppLoader: !!process.env.BUILTIN_SWC_LOADER,
shouldInvalidateCb: ({
bundlePath,
entryName,
absolutePagePath,
clientBrowserLoader,
}: any) => {
console.log(
'shouldInvalidateCb',
bundlePath,
entryName,
absolutePagePath,
clientBrowserLoader
)
let shouldInvalidate = false
const compiler = this.compiler!

const entries = getEntries(compiler.outputPath)
const pageKey = getEntryKey(
COMPILER_NAMES.client,
PAGE_TYPES.APP,
bundlePath
)

if (!entries[pageKey]) {
entries[pageKey] = {
type: EntryTypes.CHILD_ENTRY,
parentEntries: new Set([entryName]),
absoluteEntryFilePath: absolutePagePath,
bundlePath,
request: clientBrowserLoader,
dispose: false,
lastActiveTime: Date.now(),
}
shouldInvalidate = true
} else {
const entryData = entries[pageKey]
// New version of the client loader
if (entryData.request !== clientBrowserLoader) {
entryData.request = clientBrowserLoader
shouldInvalidate = true
}
if (entryData.type === EntryTypes.CHILD_ENTRY) {
entryData.parentEntries.add(entryName)
}
entryData.dispose = false
entryData.lastActiveTime = Date.now()
}

return shouldInvalidate
},
invalidateCb: () => {
const compiler = this.compiler!

// Invalidate in development to trigger recompilation
const invalidator = getInvalidator(compiler.outputPath)
// Check if any of the entry injections need an invalidation
if (invalidator) {
invalidator.invalidate([COMPILER_NAMES.client])
}
},
stateCb: (state: any) => {
Object.assign(pluginState.serverActions, state.serverActions)
Object.assign(pluginState.edgeServerActions, state.edgeServerActions)
Object.assign(
pluginState.serverActionModules,
state.serverActionModules
)
Object.assign(
pluginState.edgeServerActionModules,
state.edgeServerActionModules
)
Object.assign(pluginState.ssrModules, state.ssrModules)
Object.assign(pluginState.edgeSsrModules, state.edgeSsrModules)
Object.assign(pluginState.rscModules, state.rscModules)
Object.assign(pluginState.edgeRscModules, state.edgeRscModules)
Object.assign(
pluginState.injectedClientEntries,
state.injectedClientEntries
)
},
})
}

apply(compiler: Compiler) {
this.compiler = compiler
this.plugin.apply(compiler)
}
}

0 comments on commit 243d2cb

Please # to comment.