Skip to content

Commit 5b2102d

Browse files
authored
refactor: create shared utils for mod resource (vercel#69145)
1 parent afc73d5 commit 5b2102d

File tree

2 files changed

+65
-61
lines changed

2 files changed

+65
-61
lines changed

packages/next/src/build/webpack/loaders/utils.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,28 @@ import { RSC_MODULE_TYPES } from '../../../shared/lib/constants'
55
const imageExtensions = ['jpg', 'jpeg', 'png', 'webp', 'avif', 'ico', 'svg']
66
const imageRegex = new RegExp(`\\.(${imageExtensions.join('|')})$`)
77

8+
// Determine if the whole module is server action, 'use server' in the top level of module
9+
export function isActionServerLayerEntryModule(mod: {
10+
resource: string
11+
buildInfo?: any
12+
}) {
13+
const rscInfo = mod.buildInfo.rsc
14+
return !!(rscInfo?.actions && rscInfo?.type === RSC_MODULE_TYPES.server)
15+
}
16+
17+
// Determine if the whole module is client action, 'use server' in nested closure in the client module
18+
function isActionClientLayerModule(mod: { resource: string; buildInfo?: any }) {
19+
const rscInfo = mod.buildInfo.rsc
20+
return !!(rscInfo?.actions && rscInfo?.type === RSC_MODULE_TYPES.client)
21+
}
22+
823
export function isClientComponentEntryModule(mod: {
924
resource: string
1025
buildInfo?: any
1126
}) {
1227
const rscInfo = mod.buildInfo.rsc
1328
const hasClientDirective = rscInfo?.isClientRef
14-
const isActionLayerEntry =
15-
rscInfo?.actions && rscInfo?.type === RSC_MODULE_TYPES.client
29+
const isActionLayerEntry = isActionClientLayerModule(mod)
1630
return (
1731
hasClientDirective || isActionLayerEntry || imageRegex.test(mod.resource)
1832
)
@@ -39,7 +53,7 @@ export function isCSSMod(mod: {
3953
)
4054
}
4155

42-
export function getActions(mod: {
56+
export function getActionsFromBuildInfo(mod: {
4357
resource: string
4458
buildInfo?: any
4559
}): undefined | string[] {

packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts

Lines changed: 48 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
UNDERSCORE_NOT_FOUND_ROUTE_ENTRY,
2626
} from '../../../shared/lib/constants'
2727
import {
28-
getActions,
28+
getActionsFromBuildInfo,
2929
generateActionId,
3030
isClientComponentEntryModule,
3131
isCSSMod,
@@ -356,10 +356,10 @@ export class FlightClientEntryPlugin {
356356
...clientEntryToInject.clientComponentImports,
357357
...(
358358
dedupedCSSImports[clientEntryToInject.absolutePagePath] || []
359-
).reduce((res, curr) => {
359+
).reduce<ClientComponentImports>((res, curr) => {
360360
res[curr] = new Set()
361361
return res
362-
}, {} as ClientComponentImports),
362+
}, {}),
363363
},
364364
})
365365

@@ -529,27 +529,14 @@ export class FlightClientEntryPlugin {
529529
const collectActionsInDep = (mod: webpack.NormalModule): void => {
530530
if (!mod) return
531531

532-
const modPath: string = mod.resourceResolveData?.path || ''
533-
// We have to always use the resolved request here to make sure the
534-
// server and client are using the same module path (required by RSC), as
535-
// the server compiler and client compiler have different resolve configs.
536-
let modRequest: string =
537-
modPath + (mod.resourceResolveData?.query || '')
538-
539-
// For the barrel optimization, we need to use the match resource instead
540-
// because there will be 2 modules for the same file (same resource path)
541-
// but they're different modules and can't be deduped via `visitedModule`.
542-
// The first module is a virtual re-export module created by the loader.
543-
if (mod.matchResource?.startsWith(BARREL_OPTIMIZATION_PREFIX)) {
544-
modRequest = mod.matchResource + ':' + modRequest
545-
}
532+
const modResource = getModuleResource(mod)
546533

547-
if (!modRequest || visitedModule.has(modRequest)) return
548-
visitedModule.add(modRequest)
534+
if (!modResource || visitedModule.has(modResource)) return
535+
visitedModule.add(modResource)
549536

550-
const actions = getActions(mod)
537+
const actions = getActionsFromBuildInfo(mod)
551538
if (actions) {
552-
collectedActions.set(modRequest, actions)
539+
collectedActions.set(modResource, actions)
553540
}
554541

555542
getModuleReferencesInOrder(mod, compilation.moduleGraph).forEach(
@@ -578,8 +565,8 @@ export class FlightClientEntryPlugin {
578565
ssrEntryModule,
579566
compilation.moduleGraph
580567
)) {
581-
const dependency = connection.dependency!
582-
const request = (dependency as unknown as webpack.NormalModule).request
568+
const depModule = connection.dependency
569+
const request = (depModule as unknown as webpack.NormalModule).request
583570

584571
// It is possible that the same entry is added multiple times in the
585572
// connection graph. We can just skip these to speed up the process.
@@ -624,45 +611,26 @@ export class FlightClientEntryPlugin {
624611
if (!mod) return
625612

626613
const isCSS = isCSSMod(mod)
614+
const modResource = getModuleResource(mod)
627615

628-
const modPath: string = mod.resourceResolveData?.path || ''
629-
const modQuery = mod.resourceResolveData?.query || ''
630-
// We have to always use the resolved request here to make sure the
631-
// server and client are using the same module path (required by RSC), as
632-
// the server compiler and client compiler have different resolve configs.
633-
let modRequest: string = modPath + modQuery
634-
635-
// Context modules don't have a resource path, we use the identifier instead.
636-
if (mod.constructor.name === 'ContextModule') {
637-
modRequest = (mod as any)._identifier
638-
}
639-
640-
// For the barrel optimization, we need to use the match resource instead
641-
// because there will be 2 modules for the same file (same resource path)
642-
// but they're different modules and can't be deduped via `visitedModule`.
643-
// The first module is a virtual re-export module created by the loader.
644-
if (mod.matchResource?.startsWith(BARREL_OPTIMIZATION_PREFIX)) {
645-
modRequest = mod.matchResource + ':' + modRequest
646-
}
647-
648-
if (!modRequest) return
649-
if (visited.has(modRequest)) {
650-
if (clientComponentImports[modRequest]) {
616+
if (!modResource) return
617+
if (visited.has(modResource)) {
618+
if (clientComponentImports[modResource]) {
651619
addClientImport(
652620
mod,
653-
modRequest,
621+
modResource,
654622
clientComponentImports,
655623
importedIdentifiers,
656624
false
657625
)
658626
}
659627
return
660628
}
661-
visited.add(modRequest)
629+
visited.add(modResource)
662630

663-
const actions = getActions(mod)
631+
const actions = getActionsFromBuildInfo(mod)
664632
if (actions) {
665-
actionImports.push([modRequest, actions])
633+
actionImports.push([modResource, actions])
666634
}
667635

668636
const webpackRuntime = this.isEdgeServer
@@ -681,14 +649,14 @@ export class FlightClientEntryPlugin {
681649
if (unused) return
682650
}
683651

684-
CSSImports.add(modRequest)
652+
CSSImports.add(modResource)
685653
} else if (isClientComponentEntryModule(mod)) {
686-
if (!clientComponentImports[modRequest]) {
687-
clientComponentImports[modRequest] = new Set()
654+
if (!clientComponentImports[modResource]) {
655+
clientComponentImports[modResource] = new Set()
688656
}
689657
addClientImport(
690658
mod,
691-
modRequest,
659+
modResource,
692660
clientComponentImports,
693661
importedIdentifiers,
694662
true
@@ -700,7 +668,6 @@ export class FlightClientEntryPlugin {
700668
getModuleReferencesInOrder(mod, compilation.moduleGraph).forEach(
701669
(connection: any) => {
702670
let dependencyIds: string[] = []
703-
const depModule = connection.resolvedModule
704671

705672
// `ids` are the identifiers that are imported from the dependency,
706673
// if it's present, it's an array of strings.
@@ -710,7 +677,7 @@ export class FlightClientEntryPlugin {
710677
dependencyIds = ['*']
711678
}
712679

713-
filterClientComponents(depModule, dependencyIds)
680+
filterClientComponents(connection.resolvedModule, dependencyIds)
714681
}
715682
)
716683
}
@@ -1029,7 +996,7 @@ function addClientImport(
1029996
modRequest: string,
1030997
clientComponentImports: ClientComponentImports,
1031998
importedIdentifiers: string[],
1032-
isFirstImport: boolean
999+
isFirstVisitModule: boolean
10331000
) {
10341001
const clientEntryType = getModuleBuildInfo(mod).rsc?.clientEntryType
10351002
const isCjsModule = clientEntryType === 'cjs'
@@ -1044,7 +1011,7 @@ function addClientImport(
10441011
// If there's collected import path with named import identifiers,
10451012
// or there's nothing in collected imports are empty.
10461013
// we should include the whole module.
1047-
if (!isFirstImport && [...clientImportsSet][0] !== '*') {
1014+
if (!isFirstVisitModule && [...clientImportsSet][0] !== '*') {
10481015
clientComponentImports[modRequest] = new Set(['*'])
10491016
}
10501017
} else {
@@ -1069,3 +1036,26 @@ function addClientImport(
10691036
}
10701037
}
10711038
}
1039+
1040+
function getModuleResource(mod: webpack.NormalModule): string {
1041+
const modPath: string = mod.resourceResolveData?.path || ''
1042+
const modQuery = mod.resourceResolveData?.query || ''
1043+
// We have to always use the resolved request here to make sure the
1044+
// server and client are using the same module path (required by RSC), as
1045+
// the server compiler and client compiler have different resolve configs.
1046+
let modResource: string = modPath + modQuery
1047+
1048+
// Context modules don't have a resource path, we use the identifier instead.
1049+
if (mod.constructor.name === 'ContextModule') {
1050+
modResource = mod.identifier()
1051+
}
1052+
1053+
// For the barrel optimization, we need to use the match resource instead
1054+
// because there will be 2 modules for the same file (same resource path)
1055+
// but they're different modules and can't be deduped via `visitedModule`.
1056+
// The first module is a virtual re-export module created by the loader.
1057+
if (mod.matchResource?.startsWith(BARREL_OPTIMIZATION_PREFIX)) {
1058+
modResource = mod.matchResource + ':' + modResource
1059+
}
1060+
return modResource
1061+
}

0 commit comments

Comments
 (0)