Spaces:
Running
Running
| ; | |
| Object.defineProperty(exports, "__esModule", { | |
| value: true | |
| }); | |
| Object.defineProperty(exports, "FlightClientEntryPlugin", { | |
| enumerable: true, | |
| get: function() { | |
| return FlightClientEntryPlugin; | |
| } | |
| }); | |
| const _webpack = require("next/dist/compiled/webpack/webpack"); | |
| const _querystring = require("querystring"); | |
| const _path = /*#__PURE__*/ _interop_require_default(require("path")); | |
| const _ondemandentryhandler = require("../../../server/dev/on-demand-entry-handler"); | |
| const _constants = require("../../../lib/constants"); | |
| const _constants1 = require("../../../shared/lib/constants"); | |
| const _entryconstants = require("../../../shared/lib/entry-constants"); | |
| const _utils = require("../loaders/utils"); | |
| const _utils1 = require("../utils"); | |
| const _normalizepathsep = require("../../../shared/lib/page-path/normalize-path-sep"); | |
| const _buildcontext = require("../../build-context"); | |
| const _pagetypes = require("../../../lib/page-types"); | |
| const _getmodulebuildinfo = require("../loaders/get-module-build-info"); | |
| const _nextflightloader = require("../loaders/next-flight-loader"); | |
| const _isapprouteroute = require("../../../lib/is-app-route-route"); | |
| const _ismetadataroute = require("../../../lib/metadata/is-metadata-route"); | |
| const _getwebpackbundler = /*#__PURE__*/ _interop_require_default(require("../../../shared/lib/get-webpack-bundler")); | |
| const _utils2 = require("../../utils"); | |
| function _interop_require_default(obj) { | |
| return obj && obj.__esModule ? obj : { | |
| default: obj | |
| }; | |
| } | |
| const PLUGIN_NAME = 'FlightClientEntryPlugin'; | |
| const pluginState = (0, _buildcontext.getProxiedPluginState)({ | |
| // A map to track "action" -> "list of bundles". | |
| serverActions: {}, | |
| edgeServerActions: {}, | |
| serverActionModules: {}, | |
| edgeServerActionModules: {}, | |
| ssrModules: {}, | |
| edgeSsrModules: {}, | |
| rscModules: {}, | |
| edgeRscModules: {}, | |
| injectedClientEntries: {} | |
| }); | |
| const POSSIBLE_SHARED_CONVENTIONS = [ | |
| 'template', | |
| 'layout' | |
| ]; | |
| const STANDALONE_BUNDLE_CONVENTION = 'global-not-found'; | |
| function deduplicateCSSImportsForEntry(mergedCSSimports) { | |
| // If multiple entry module connections are having the same CSS import, | |
| // we only need to have one module to keep track of that CSS import. | |
| // It is based on the fact that if a page or a layout is rendered in the | |
| // given entry, all its parent layouts are always rendered too. | |
| // This can avoid duplicate CSS imports in the generated CSS manifest, | |
| // for example, if a page and its parent layout are both using the same | |
| // CSS import, we only need to have the layout to keep track of that CSS | |
| // import. | |
| // To achieve this, we need to first collect all the CSS imports from | |
| // every connection, and deduplicate them in the order of layers from | |
| // top to bottom. The implementation can be generally described as: | |
| // - Sort by number of `/` in the request path (the more `/`, the deeper) | |
| // - When in the same depth, sort by the filename (template < layout < page and others) | |
| // Sort the connections as described above. | |
| const sortedCSSImports = Object.entries(mergedCSSimports).sort((a, b)=>{ | |
| const [aPath] = a; | |
| const [bPath] = b; | |
| const aDepth = aPath.split('/').length; | |
| const bDepth = bPath.split('/').length; | |
| if (aDepth !== bDepth) { | |
| return aDepth - bDepth; | |
| } | |
| const aName = _path.default.parse(aPath).name; | |
| const bName = _path.default.parse(bPath).name; | |
| const indexA = POSSIBLE_SHARED_CONVENTIONS.indexOf(aName); | |
| const indexB = POSSIBLE_SHARED_CONVENTIONS.indexOf(bName); | |
| if (indexA === -1) return 1; | |
| if (indexB === -1) return -1; | |
| return indexA - indexB; | |
| }); | |
| const dedupedCSSImports = {}; | |
| const trackedCSSImports = new Set(); | |
| for (const [entryFilePath, cssImports] of sortedCSSImports){ | |
| const entryConventionName = _path.default.parse(entryFilePath).name; | |
| for (const cssImport of cssImports){ | |
| // If the CSS import is already tracked, we can skip it. | |
| // Or if it's any standalone entry such as `global-not-found`, it won't share any resources with other entry, skip it. | |
| if (trackedCSSImports.has(cssImport) && STANDALONE_BUNDLE_CONVENTION !== entryConventionName) { | |
| continue; | |
| } | |
| // Only track CSS imports that are in files that can inherit CSS. | |
| if (POSSIBLE_SHARED_CONVENTIONS.includes(entryConventionName)) { | |
| trackedCSSImports.add(cssImport); | |
| } | |
| if (!dedupedCSSImports[entryFilePath]) { | |
| dedupedCSSImports[entryFilePath] = []; | |
| } | |
| dedupedCSSImports[entryFilePath].push(cssImport); | |
| } | |
| } | |
| return dedupedCSSImports; | |
| } | |
| class FlightClientEntryPlugin { | |
| constructor(options){ | |
| this.dev = options.dev; | |
| this.appDir = options.appDir; | |
| this.projectDir = _path.default.join(options.appDir, '..'); | |
| this.isEdgeServer = options.isEdgeServer; | |
| this.assetPrefix = !this.dev && !this.isEdgeServer ? '../' : ''; | |
| this.encryptionKey = options.encryptionKey; | |
| this.webpackRuntime = this.isEdgeServer ? _constants1.EDGE_RUNTIME_WEBPACK : _constants1.DEFAULT_RUNTIME_WEBPACK; | |
| } | |
| apply(compiler) { | |
| compiler.hooks.finishMake.tapPromise(PLUGIN_NAME, (compilation)=>this.createClientEntries(compiler, compilation)); | |
| compiler.hooks.afterCompile.tap(PLUGIN_NAME, (compilation)=>{ | |
| const recordModule = (modId, mod)=>{ | |
| var _mod_resourceResolveData, _mod_resourceResolveData1; | |
| // Match Resource is undefined unless an import is using the inline match resource syntax | |
| // https://webpack.js.org/api/loaders/#inline-matchresource | |
| const modPath = mod.matchResource || ((_mod_resourceResolveData = mod.resourceResolveData) == null ? void 0 : _mod_resourceResolveData.path); | |
| const modQuery = ((_mod_resourceResolveData1 = mod.resourceResolveData) == null ? void 0 : _mod_resourceResolveData1.query) || ''; | |
| // query is already part of mod.resource | |
| // so it's only necessary to add it for matchResource or mod.resourceResolveData | |
| const modResource = modPath ? modPath.startsWith(_constants1.BARREL_OPTIMIZATION_PREFIX) ? (0, _utils1.formatBarrelOptimizedResource)(mod.resource, modPath) : modPath + modQuery : mod.resource; | |
| if (typeof modId !== 'undefined' && modResource) { | |
| if (mod.layer === _constants.WEBPACK_LAYERS.reactServerComponents) { | |
| const key = _path.default.relative(compiler.context, modResource).replace(/\/next\/dist\/esm\//, '/next/dist/'); | |
| const moduleInfo = { | |
| moduleId: modId, | |
| async: compilation.moduleGraph.isAsync(mod) | |
| }; | |
| if (this.isEdgeServer) { | |
| pluginState.edgeRscModules[key] = moduleInfo; | |
| } else { | |
| pluginState.rscModules[key] = moduleInfo; | |
| } | |
| } | |
| } | |
| if (mod.layer !== _constants.WEBPACK_LAYERS.serverSideRendering) { | |
| return; | |
| } | |
| // Check mod resource to exclude the empty resource module like virtual module created by next-flight-client-entry-loader | |
| if (typeof modId !== 'undefined' && modResource) { | |
| // Note that this isn't that reliable as webpack is still possible to assign | |
| // additional queries to make sure there's no conflict even using the `named` | |
| // module ID strategy. | |
| let ssrNamedModuleId = _path.default.relative(compiler.context, modResource); | |
| if (!ssrNamedModuleId.startsWith('.')) { | |
| // TODO use getModuleId instead | |
| ssrNamedModuleId = `./${(0, _normalizepathsep.normalizePathSep)(ssrNamedModuleId)}`; | |
| } | |
| const moduleInfo = { | |
| moduleId: modId, | |
| async: compilation.moduleGraph.isAsync(mod) | |
| }; | |
| if (this.isEdgeServer) { | |
| pluginState.edgeSsrModules[ssrNamedModuleId.replace(/\/next\/dist\/esm\//, '/next/dist/')] = moduleInfo; | |
| } else { | |
| pluginState.ssrModules[ssrNamedModuleId] = moduleInfo; | |
| } | |
| } | |
| }; | |
| (0, _utils1.traverseModules)(compilation, (mod, _chunk, _chunkGroup, modId)=>{ | |
| if (modId) recordModule(modId, mod); | |
| }); | |
| }); | |
| compiler.hooks.make.tap(PLUGIN_NAME, (compilation)=>{ | |
| compilation.hooks.processAssets.tapPromise({ | |
| name: PLUGIN_NAME, | |
| stage: _webpack.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_HASH | |
| }, ()=>this.createActionAssets(compilation)); | |
| }); | |
| } | |
| async createClientEntries(compiler, compilation) { | |
| const addClientEntryAndSSRModulesList = []; | |
| const createdSSRDependenciesForEntry = {}; | |
| const addActionEntryList = []; | |
| const actionMapsPerEntry = {}; | |
| const createdActionIds = new Set(); | |
| // For each SC server compilation entry, we need to create its corresponding | |
| // client component entry. | |
| (0, _utils1.forEachEntryModule)(compilation, ({ name, entryModule })=>{ | |
| const internalClientComponentEntryImports = {}; | |
| const actionEntryImports = new Map(); | |
| const clientEntriesToInject = []; | |
| const mergedCSSimports = {}; | |
| const moduleReferences = (0, _utils1.getModuleReferencesInOrder)(entryModule, compilation.moduleGraph); | |
| for (const connection of moduleReferences){ | |
| // Entry can be any user defined entry files such as layout, page, error, loading, etc. | |
| let entryRequest = connection.dependency.request; | |
| if (entryRequest.endsWith(_constants.WEBPACK_RESOURCE_QUERIES.metadataRoute)) { | |
| const { filePath, isDynamicRouteExtension } = getMetadataRouteResource(entryRequest); | |
| if (isDynamicRouteExtension === '1') { | |
| entryRequest = filePath; | |
| } | |
| } | |
| const { clientComponentImports, actionImports, cssImports } = this.collectComponentInfoFromServerEntryDependency({ | |
| entryRequest, | |
| compilation, | |
| resolvedModule: connection.resolvedModule | |
| }); | |
| actionImports.forEach(([dep, actions])=>actionEntryImports.set(dep, actions)); | |
| const isAbsoluteRequest = _path.default.isAbsolute(entryRequest); | |
| const isAppRouterBuiltinPage = (0, _utils2.isAppBuiltinPage)(entryRequest); | |
| // Next.js internals are put into a separate entry. | |
| if (!isAbsoluteRequest) { | |
| Object.keys(clientComponentImports).forEach((value)=>internalClientComponentEntryImports[value] = new Set()); | |
| if (!isAppRouterBuiltinPage) { | |
| continue; | |
| } | |
| } | |
| // TODO-APP: Enable these lines. This ensures no entrypoint is created for layout/page when there are no client components. | |
| // Currently disabled because it causes test failures in CI. | |
| // if (clientImports.length === 0 && actionImports.length === 0) { | |
| // continue | |
| // } | |
| const relativeRequest = isAbsoluteRequest && !isAppRouterBuiltinPage ? _path.default.relative(compilation.options.context, entryRequest) : entryRequest; | |
| // Replace file suffix as `.js` will be added. | |
| // bundlePath will have app/ prefix but not src/. | |
| // e.g. src/app/foo/page.js -> app/foo/page | |
| let bundlePath = (0, _normalizepathsep.normalizePathSep)(relativeRequest.replace(/\.[^.\\/]+$/, '').replace(/^src[\\/]/, '')); | |
| // For metadata routes, the entry name can be used as the bundle path, | |
| // as it has been normalized already. | |
| // e.g. | |
| // When `relativeRequest` is 'src/app/sitemap.js', | |
| // `appDirRelativeRequest` will be '/sitemap.js' | |
| // then `isMetadataEntryFile` will be `true` | |
| const appDirRelativeRequest = relativeRequest.replace(/^src[\\/]/, '').replace(/^app[\\/]/, '/'); | |
| const isMetadataEntryFile = (0, _ismetadataroute.isMetadataRouteFile)(appDirRelativeRequest, _ismetadataroute.DEFAULT_METADATA_ROUTE_EXTENSIONS, true); | |
| if (isMetadataEntryFile) { | |
| bundlePath = name; | |
| } | |
| Object.assign(mergedCSSimports, cssImports); | |
| clientEntriesToInject.push({ | |
| compiler, | |
| compilation, | |
| entryName: name, | |
| clientComponentImports, | |
| bundlePath, | |
| absolutePagePath: entryRequest | |
| }); | |
| // The webpack implementation of writing the client reference manifest relies on all entrypoints writing a page.js even when there is no client components in the page. | |
| // It needs the file in order to write the reference manifest for the path in the `.next/server` folder. | |
| // TODO-APP: This could be better handled, however Turbopack does not have the same problem as we resolve client components in a single graph. | |
| if (name === `app${_entryconstants.UNDERSCORE_NOT_FOUND_ROUTE_ENTRY}` && bundlePath === 'app/not-found') { | |
| clientEntriesToInject.push({ | |
| compiler, | |
| compilation, | |
| entryName: name, | |
| clientComponentImports: {}, | |
| bundlePath: `app${_entryconstants.UNDERSCORE_NOT_FOUND_ROUTE_ENTRY}`, | |
| absolutePagePath: entryRequest | |
| }); | |
| } | |
| if (name === `app${_entryconstants.UNDERSCORE_NOT_FOUND_ROUTE_ENTRY}` && bundlePath === 'app/global-not-found') { | |
| clientEntriesToInject.push({ | |
| compiler, | |
| compilation, | |
| entryName: name, | |
| clientComponentImports, | |
| bundlePath: `app${_entryconstants.UNDERSCORE_NOT_FOUND_ROUTE_ENTRY}`, | |
| absolutePagePath: entryRequest | |
| }); | |
| } | |
| if (name === `app${_entryconstants.UNDERSCORE_GLOBAL_ERROR_ROUTE_ENTRY}`) { | |
| clientEntriesToInject.push({ | |
| compiler, | |
| compilation, | |
| entryName: name, | |
| clientComponentImports, | |
| bundlePath: `app${_entryconstants.UNDERSCORE_GLOBAL_ERROR_ROUTE_ENTRY}`, | |
| absolutePagePath: entryRequest | |
| }); | |
| } | |
| } | |
| // Make sure CSS imports are deduplicated before injecting the client entry | |
| // and SSR modules. | |
| const dedupedCSSImports = deduplicateCSSImportsForEntry(mergedCSSimports); | |
| for (const clientEntryToInject of clientEntriesToInject){ | |
| const injected = this.injectClientEntryAndSSRModules({ | |
| ...clientEntryToInject, | |
| clientImports: { | |
| ...clientEntryToInject.clientComponentImports, | |
| ...(dedupedCSSImports[clientEntryToInject.absolutePagePath] || []).reduce((res, curr)=>{ | |
| res[curr] = new Set(); | |
| return res; | |
| }, {}) | |
| } | |
| }); | |
| // Track all created SSR dependencies for each entry from the server layer. | |
| if (!createdSSRDependenciesForEntry[clientEntryToInject.entryName]) { | |
| createdSSRDependenciesForEntry[clientEntryToInject.entryName] = []; | |
| } | |
| createdSSRDependenciesForEntry[clientEntryToInject.entryName].push(injected[3]); | |
| addClientEntryAndSSRModulesList.push(injected); | |
| } | |
| if (!(0, _isapprouteroute.isAppRouteRoute)(name)) { | |
| // Create internal app | |
| addClientEntryAndSSRModulesList.push(this.injectClientEntryAndSSRModules({ | |
| compiler, | |
| compilation, | |
| entryName: name, | |
| clientImports: { | |
| ...internalClientComponentEntryImports | |
| }, | |
| bundlePath: _constants1.APP_CLIENT_INTERNALS | |
| })); | |
| } | |
| if (actionEntryImports.size > 0) { | |
| if (!actionMapsPerEntry[name]) { | |
| actionMapsPerEntry[name] = new Map(); | |
| } | |
| actionMapsPerEntry[name] = new Map([ | |
| ...actionMapsPerEntry[name], | |
| ...actionEntryImports | |
| ]); | |
| } | |
| }); | |
| for (const [name, actionEntryImports] of Object.entries(actionMapsPerEntry)){ | |
| addActionEntryList.push(this.injectActionEntry({ | |
| compiler, | |
| compilation, | |
| actions: actionEntryImports, | |
| entryName: name, | |
| bundlePath: name, | |
| createdActionIds | |
| })); | |
| } | |
| // Invalidate in development to trigger recompilation | |
| const invalidator = (0, _ondemandentryhandler.getInvalidator)(compiler.outputPath); | |
| // Check if any of the entry injections need an invalidation | |
| if (invalidator && addClientEntryAndSSRModulesList.some(([shouldInvalidate])=>shouldInvalidate === true)) { | |
| invalidator.invalidate([ | |
| _constants1.COMPILER_NAMES.client | |
| ]); | |
| } | |
| // Client compiler is invalidated before awaiting the compilation of the SSR | |
| // and RSC client component entries so that the client compiler is running | |
| // in parallel to the server compiler. | |
| await Promise.all(addClientEntryAndSSRModulesList.flatMap((addClientEntryAndSSRModules)=>[ | |
| addClientEntryAndSSRModules[1], | |
| addClientEntryAndSSRModules[2] | |
| ])); | |
| // Wait for action entries to be added. | |
| await Promise.all(addActionEntryList); | |
| const addedClientActionEntryList = []; | |
| const actionMapsPerClientEntry = {}; | |
| // We need to create extra action entries that are created from the | |
| // client layer. | |
| // Start from each entry's created SSR dependency from our previous step. | |
| for (const [name, ssrEntryDependencies] of Object.entries(createdSSRDependenciesForEntry)){ | |
| // Collect from all entries, e.g. layout.js, page.js, loading.js, ... | |
| // add aggregate them. | |
| const actionEntryImports = this.collectClientActionsFromDependencies({ | |
| compilation, | |
| dependencies: ssrEntryDependencies | |
| }); | |
| if (actionEntryImports.size > 0) { | |
| if (!actionMapsPerClientEntry[name]) { | |
| actionMapsPerClientEntry[name] = new Map(); | |
| } | |
| actionMapsPerClientEntry[name] = new Map([ | |
| ...actionMapsPerClientEntry[name], | |
| ...actionEntryImports | |
| ]); | |
| } | |
| } | |
| for (const [entryName, actionEntryImports] of Object.entries(actionMapsPerClientEntry)){ | |
| // If an action method is already created in the server layer, we don't | |
| // need to create it again in the action layer. | |
| // This is to avoid duplicate action instances and make sure the module | |
| // state is shared. | |
| let remainingClientImportedActions = false; | |
| const remainingActionEntryImports = new Map(); | |
| for (const [dep, actions] of actionEntryImports){ | |
| const remainingActionNames = []; | |
| for (const action of actions){ | |
| if (!createdActionIds.has(entryName + '@' + action.id)) { | |
| remainingActionNames.push(action); | |
| } | |
| } | |
| if (remainingActionNames.length > 0) { | |
| remainingActionEntryImports.set(dep, remainingActionNames); | |
| remainingClientImportedActions = true; | |
| } | |
| } | |
| if (remainingClientImportedActions) { | |
| addedClientActionEntryList.push(this.injectActionEntry({ | |
| compiler, | |
| compilation, | |
| actions: remainingActionEntryImports, | |
| entryName, | |
| bundlePath: entryName, | |
| fromClient: true, | |
| createdActionIds | |
| })); | |
| } | |
| } | |
| await Promise.all(addedClientActionEntryList); | |
| } | |
| collectClientActionsFromDependencies({ compilation, dependencies }) { | |
| // action file path -> action names | |
| const collectedActions = new Map(); | |
| // Keep track of checked modules to avoid infinite loops with recursive imports. | |
| const visitedModule = new Set(); | |
| const visitedEntry = new Set(); | |
| const collectActions = ({ entryRequest, resolvedModule })=>{ | |
| const collectActionsInDep = (mod)=>{ | |
| var _getModuleBuildInfo_rsc; | |
| if (!mod) return; | |
| const modResource = getModuleResource(mod); | |
| if (!modResource) return; | |
| if (visitedModule.has(modResource)) return; | |
| visitedModule.add(modResource); | |
| const actionIds = (_getModuleBuildInfo_rsc = (0, _getmodulebuildinfo.getModuleBuildInfo)(mod).rsc) == null ? void 0 : _getModuleBuildInfo_rsc.actionIds; | |
| if (actionIds) { | |
| collectedActions.set(modResource, Object.entries(actionIds).map(([id, exportedName])=>({ | |
| id, | |
| exportedName, | |
| filename: _path.default.posix.relative(this.projectDir, modResource) | |
| }))); | |
| } | |
| // Collect used exported actions transversely. | |
| (0, _utils1.getModuleReferencesInOrder)(mod, compilation.moduleGraph).forEach((connection)=>{ | |
| collectActionsInDep(connection.resolvedModule); | |
| }); | |
| }; | |
| // Don't traverse the module graph anymore once hitting the action layer. | |
| if (entryRequest && !entryRequest.includes('next-flight-action-entry-loader')) { | |
| // Traverse the module graph to find all client components. | |
| collectActionsInDep(resolvedModule); | |
| } | |
| }; | |
| for (const entryDependency of dependencies){ | |
| const ssrEntryModule = compilation.moduleGraph.getResolvedModule(entryDependency); | |
| for (const connection of (0, _utils1.getModuleReferencesInOrder)(ssrEntryModule, compilation.moduleGraph)){ | |
| const depModule = connection.dependency; | |
| const request = depModule.request; | |
| // It is possible that the same entry is added multiple times in the | |
| // connection graph. We can just skip these to speed up the process. | |
| if (visitedEntry.has(request)) continue; | |
| visitedEntry.add(request); | |
| collectActions({ | |
| entryRequest: request, | |
| resolvedModule: connection.resolvedModule | |
| }); | |
| } | |
| } | |
| return collectedActions; | |
| } | |
| collectComponentInfoFromServerEntryDependency({ entryRequest, compilation, resolvedModule }) { | |
| // Keep track of checked modules to avoid infinite loops with recursive imports. | |
| const visitedOfClientComponentsTraverse = new Set(); | |
| // Info to collect. | |
| const clientComponentImports = {}; | |
| const actionImports = []; | |
| const CSSImports = new Set(); | |
| const filterClientComponents = (mod, importedIdentifiers)=>{ | |
| var _getModuleBuildInfo_rsc; | |
| if (!mod) return; | |
| const modResource = getModuleResource(mod); | |
| if (!modResource) return; | |
| if (visitedOfClientComponentsTraverse.has(modResource)) { | |
| if (clientComponentImports[modResource]) { | |
| addClientImport(mod, modResource, clientComponentImports, importedIdentifiers, false); | |
| } | |
| return; | |
| } | |
| visitedOfClientComponentsTraverse.add(modResource); | |
| const actionIds = (_getModuleBuildInfo_rsc = (0, _getmodulebuildinfo.getModuleBuildInfo)(mod).rsc) == null ? void 0 : _getModuleBuildInfo_rsc.actionIds; | |
| if (actionIds) { | |
| actionImports.push([ | |
| modResource, | |
| Object.entries(actionIds).map(([id, exportedName])=>({ | |
| id, | |
| exportedName, | |
| filename: _path.default.posix.relative(this.projectDir, modResource) | |
| })) | |
| ]); | |
| } | |
| if ((0, _utils.isCSSMod)(mod)) { | |
| const sideEffectFree = mod.factoryMeta && mod.factoryMeta.sideEffectFree; | |
| if (sideEffectFree) { | |
| const unused = !compilation.moduleGraph.getExportsInfo(mod).isModuleUsed(this.webpackRuntime); | |
| if (unused) return; | |
| } | |
| CSSImports.add(modResource); | |
| } else if ((0, _utils.isClientComponentEntryModule)(mod)) { | |
| if (!clientComponentImports[modResource]) { | |
| clientComponentImports[modResource] = new Set(); | |
| } | |
| addClientImport(mod, modResource, clientComponentImports, importedIdentifiers, true); | |
| return; | |
| } | |
| (0, _utils1.getModuleReferencesInOrder)(mod, compilation.moduleGraph).forEach((connection)=>{ | |
| var _connection_dependency; | |
| let dependencyIds = []; | |
| // `ids` are the identifiers that are imported from the dependency, | |
| // if it's present, it's an array of strings. | |
| if ((_connection_dependency = connection.dependency) == null ? void 0 : _connection_dependency.ids) { | |
| dependencyIds.push(...connection.dependency.ids); | |
| } else { | |
| dependencyIds = [ | |
| '*' | |
| ]; | |
| } | |
| filterClientComponents(connection.resolvedModule, dependencyIds); | |
| }); | |
| }; | |
| // Traverse the module graph to find all client components. | |
| filterClientComponents(resolvedModule, []); | |
| return { | |
| clientComponentImports, | |
| cssImports: CSSImports.size ? { | |
| [entryRequest]: Array.from(CSSImports) | |
| } : {}, | |
| actionImports | |
| }; | |
| } | |
| injectClientEntryAndSSRModules({ compiler, compilation, entryName, clientImports, bundlePath, absolutePagePath }) { | |
| const bundler = (0, _getwebpackbundler.default)(); | |
| let shouldInvalidate = false; | |
| const modules = Object.keys(clientImports).sort((a, b)=>_utils.regexCSS.test(b) ? 1 : a.localeCompare(b)).map((clientImportPath)=>({ | |
| request: clientImportPath, | |
| ids: [ | |
| ...clientImports[clientImportPath] | |
| ] | |
| })); | |
| // For the client entry, we always use the CJS build of Next.js. If the | |
| // server is using the ESM build (when using the Edge runtime), we need to | |
| // replace them. | |
| const clientBrowserLoader = `next-flight-client-entry-loader?${(0, _querystring.stringify)({ | |
| modules: (this.isEdgeServer ? modules.map(({ request, ids })=>({ | |
| request: request.replace(/[\\/]next[\\/]dist[\\/]esm[\\/]/, '/next/dist/'.replace(/\//g, _path.default.sep)), | |
| ids | |
| })) : modules).map((x)=>JSON.stringify(x)), | |
| server: false | |
| })}!`; | |
| const clientServerLoader = `next-flight-client-entry-loader?${(0, _querystring.stringify)({ | |
| modules: modules.map((x)=>JSON.stringify(x)), | |
| server: true | |
| })}!`; | |
| // Add for the client compilation | |
| // Inject the entry to the client compiler. | |
| if (this.dev) { | |
| const entries = (0, _ondemandentryhandler.getEntries)(compiler.outputPath); | |
| const pageKey = (0, _ondemandentryhandler.getEntryKey)(_constants1.COMPILER_NAMES.client, _pagetypes.PAGE_TYPES.APP, bundlePath); | |
| if (!entries[pageKey]) { | |
| entries[pageKey] = { | |
| type: _ondemandentryhandler.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 === _ondemandentryhandler.EntryTypes.CHILD_ENTRY) { | |
| entryData.parentEntries.add(entryName); | |
| } | |
| entryData.dispose = false; | |
| entryData.lastActiveTime = Date.now(); | |
| } | |
| } else { | |
| pluginState.injectedClientEntries[bundlePath] = clientBrowserLoader; | |
| } | |
| const clientComponentSSREntryDep = bundler.EntryPlugin.createDependency(clientServerLoader, { | |
| name: bundlePath | |
| }); | |
| const clientComponentRSCEntryDep = bundler.EntryPlugin.createDependency(clientServerLoader, { | |
| name: bundlePath | |
| }); | |
| return [ | |
| shouldInvalidate, | |
| // Add the entries to the server compiler for the SSR and RSC layers. The | |
| // promises are awaited later using `Promise.all` in order to parallelize | |
| // adding the entries. | |
| this.addEntry(compilation, compiler.context, clientComponentSSREntryDep, { | |
| name: entryName, | |
| layer: _constants.WEBPACK_LAYERS.serverSideRendering | |
| }), | |
| this.addEntry(compilation, compiler.context, clientComponentRSCEntryDep, { | |
| name: entryName, | |
| layer: _constants.WEBPACK_LAYERS.reactServerComponents | |
| }), | |
| clientComponentSSREntryDep | |
| ]; | |
| } | |
| injectActionEntry({ compiler, compilation, actions, entryName, bundlePath, fromClient, createdActionIds }) { | |
| const bundler = (0, _getwebpackbundler.default)(); | |
| const actionsArray = Array.from(actions.entries()); | |
| for (const [, actionsFromModule] of actions){ | |
| for (const { id } of actionsFromModule){ | |
| createdActionIds.add(entryName + '@' + id); | |
| } | |
| } | |
| if (actionsArray.length === 0) { | |
| return Promise.resolve(); | |
| } | |
| const actionLoader = `next-flight-action-entry-loader?${(0, _querystring.stringify)({ | |
| actions: JSON.stringify(actionsArray), | |
| __client_imported__: fromClient | |
| })}!`; | |
| const currentCompilerServerActions = this.isEdgeServer ? pluginState.edgeServerActions : pluginState.serverActions; | |
| for (const [, actionsFromModule] of actionsArray){ | |
| for (const { id, exportedName, filename } of actionsFromModule){ | |
| if (typeof currentCompilerServerActions[id] === 'undefined') { | |
| currentCompilerServerActions[id] = { | |
| workers: {}, | |
| layer: {}, | |
| filename, | |
| exportedName | |
| }; | |
| } | |
| currentCompilerServerActions[id].workers[bundlePath] = { | |
| moduleId: '', | |
| async: false | |
| }; | |
| currentCompilerServerActions[id].layer[bundlePath] = fromClient ? _constants.WEBPACK_LAYERS.actionBrowser : _constants.WEBPACK_LAYERS.reactServerComponents; | |
| } | |
| } | |
| // Inject the entry to the server compiler | |
| const actionEntryDep = bundler.EntryPlugin.createDependency(actionLoader, { | |
| name: bundlePath | |
| }); | |
| return this.addEntry(compilation, // Reuse compilation context. | |
| compiler.context, actionEntryDep, { | |
| name: entryName, | |
| layer: fromClient ? _constants.WEBPACK_LAYERS.actionBrowser : _constants.WEBPACK_LAYERS.reactServerComponents | |
| }); | |
| } | |
| addEntry(compilation, context, dependency, options) /* Promise<module> */ { | |
| return new Promise((resolve, reject)=>{ | |
| if ('rspack' in compilation.compiler) { | |
| compilation.addInclude(context, dependency, options, (err, module)=>{ | |
| if (err) { | |
| return reject(err); | |
| } | |
| compilation.moduleGraph.getExportsInfo(module).setUsedInUnknownWay(this.isEdgeServer ? _constants1.EDGE_RUNTIME_WEBPACK : _constants1.DEFAULT_RUNTIME_WEBPACK); | |
| return resolve(module); | |
| }); | |
| } else { | |
| 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, module)=>{ | |
| 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 ? _constants1.EDGE_RUNTIME_WEBPACK : _constants1.DEFAULT_RUNTIME_WEBPACK); | |
| return resolve(module); | |
| }); | |
| } | |
| }); | |
| } | |
| async createActionAssets(compilation) { | |
| const serverActions = {}; | |
| const edgeServerActions = {}; | |
| (0, _utils1.traverseModules)(compilation, (mod, _chunk, chunkGroup, modId)=>{ | |
| // Go through all action entries and record the module ID for each entry. | |
| if (chunkGroup.name && mod.request && modId && /next-flight-action-entry-loader/.test(mod.request)) { | |
| const fromClient = /&__client_imported__=true/.test(mod.request); | |
| const mapping = this.isEdgeServer ? pluginState.edgeServerActionModules : pluginState.serverActionModules; | |
| if (!mapping[chunkGroup.name]) { | |
| mapping[chunkGroup.name] = {}; | |
| } | |
| mapping[chunkGroup.name][fromClient ? 'client' : 'server'] = { | |
| moduleId: modId, | |
| async: compilation.moduleGraph.isAsync(mod) | |
| }; | |
| } | |
| }); | |
| for(let id in pluginState.serverActions){ | |
| const action = pluginState.serverActions[id]; | |
| for(let name in action.workers){ | |
| const modId = pluginState.serverActionModules[name][action.layer[name] === _constants.WEBPACK_LAYERS.actionBrowser ? 'client' : 'server']; | |
| action.workers[name] = modId; | |
| } | |
| serverActions[id] = action; | |
| } | |
| for(let id in pluginState.edgeServerActions){ | |
| const action = pluginState.edgeServerActions[id]; | |
| for(let name in action.workers){ | |
| const modId = pluginState.edgeServerActionModules[name][action.layer[name] === _constants.WEBPACK_LAYERS.actionBrowser ? 'client' : 'server']; | |
| action.workers[name] = modId; | |
| } | |
| edgeServerActions[id] = action; | |
| } | |
| const serverManifest = { | |
| node: serverActions, | |
| edge: edgeServerActions, | |
| encryptionKey: this.encryptionKey | |
| }; | |
| const edgeServerManifest = { | |
| ...serverManifest, | |
| encryptionKey: 'process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY' | |
| }; | |
| const json = JSON.stringify(serverManifest, null, this.dev ? 2 : undefined); | |
| const edgeJson = JSON.stringify(edgeServerManifest, null, this.dev ? 2 : undefined); | |
| compilation.emitAsset(`${this.assetPrefix}${_constants1.SERVER_REFERENCE_MANIFEST}.js`, new _webpack.sources.RawSource(`self.__RSC_SERVER_MANIFEST=${JSON.stringify(edgeJson)}`)); | |
| compilation.emitAsset(`${this.assetPrefix}${_constants1.SERVER_REFERENCE_MANIFEST}.json`, new _webpack.sources.RawSource(json)); | |
| } | |
| } | |
| function addClientImport(mod, modRequest, clientComponentImports, importedIdentifiers, isFirstVisitModule) { | |
| var _getModuleBuildInfo_rsc; | |
| const clientEntryType = (_getModuleBuildInfo_rsc = (0, _getmodulebuildinfo.getModuleBuildInfo)(mod).rsc) == null ? void 0 : _getModuleBuildInfo_rsc.clientEntryType; | |
| const isCjsModule = clientEntryType === 'cjs'; | |
| const assumedSourceType = (0, _nextflightloader.getAssumedSourceType)(mod, isCjsModule ? 'commonjs' : 'auto'); | |
| const clientImportsSet = clientComponentImports[modRequest]; | |
| if (importedIdentifiers[0] === '*') { | |
| // If there's collected import path with named import identifiers, | |
| // or there's nothing in collected imports are empty. | |
| // we should include the whole module. | |
| if (!isFirstVisitModule && [ | |
| ...clientImportsSet | |
| ][0] !== '*') { | |
| clientComponentImports[modRequest] = new Set([ | |
| '*' | |
| ]); | |
| } | |
| } else { | |
| const isAutoModuleSourceType = assumedSourceType === 'auto'; | |
| if (isAutoModuleSourceType) { | |
| clientComponentImports[modRequest] = new Set([ | |
| '*' | |
| ]); | |
| } else { | |
| // If it's not analyzed as named ESM exports, e.g. if it's mixing `export *` with named exports, | |
| // We'll include all modules since it's not able to do tree-shaking. | |
| for (const name of importedIdentifiers){ | |
| // For cjs module default import, we include the whole module since | |
| const isCjsDefaultImport = isCjsModule && name === 'default'; | |
| // Always include __esModule along with cjs module default export, | |
| // to make sure it work with client module proxy from React. | |
| if (isCjsDefaultImport) { | |
| clientComponentImports[modRequest].add('__esModule'); | |
| } | |
| clientComponentImports[modRequest].add(name); | |
| } | |
| } | |
| } | |
| } | |
| function getModuleResource(mod) { | |
| var _mod_resourceResolveData, _mod_resourceResolveData1, _mod_matchResource; | |
| const modPath = ((_mod_resourceResolveData = mod.resourceResolveData) == null ? void 0 : _mod_resourceResolveData.path) || ''; | |
| const modQuery = ((_mod_resourceResolveData1 = mod.resourceResolveData) == null ? void 0 : _mod_resourceResolveData1.query) || ''; | |
| // We have to always use the resolved request here to make sure the | |
| // server and client are using the same module path (required by RSC), as | |
| // the server compiler and client compiler have different resolve configs. | |
| let modResource = modPath + modQuery; | |
| // Context modules don't have a resource path, we use the identifier instead. | |
| if (mod.constructor.name === 'ContextModule') { | |
| modResource = mod.identifier(); | |
| } | |
| // For the barrel optimization, we need to use the match resource instead | |
| // because there will be 2 modules for the same file (same resource path) | |
| // but they're different modules and can't be deduped via `visitedModule`. | |
| // The first module is a virtual re-export module created by the loader. | |
| if ((_mod_matchResource = mod.matchResource) == null ? void 0 : _mod_matchResource.startsWith(_constants1.BARREL_OPTIMIZATION_PREFIX)) { | |
| modResource = mod.matchResource + ':' + modResource; | |
| } | |
| if (mod.resource === `?${_constants.WEBPACK_RESOURCE_QUERIES.metadataRoute}`) { | |
| return getMetadataRouteResource(mod.rawRequest).filePath; | |
| } | |
| return modResource; | |
| } | |
| function getMetadataRouteResource(request) { | |
| // e.g. next-metadata-route-loader?filePath=<some-url-encoded-path>&isDynamicRouteExtension=1!?__next_metadata_route__ | |
| const query = request.split('!')[0].split('next-metadata-route-loader?')[1]; | |
| return (0, _querystring.parse)(query); | |
| } | |
| //# sourceMappingURL=flight-client-entry-plugin.js.map |