Skip to content

Commit 19048b8

Browse files
authored
Add metadata to server reference manifest (#82695)
This exposes additional metadata in the server reference manifest, it is not directly used at this time though.
1 parent 2ad342a commit 19048b8

File tree

6 files changed

+95
-7
lines changed

6 files changed

+95
-7
lines changed

crates/next-api/src/server_actions.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,16 +174,33 @@ async fn build_manifest(
174174
NextRuntime::NodeJs => &mut manifest.node,
175175
};
176176

177-
for (hash_id, (layer, _name, _module)) in actions_value {
177+
// Collect all the action metadata including filenames
178+
let mut action_metadata: Vec<(String, (ActionLayer, String, String))> = Vec::new();
179+
for (hash_id, (layer, name, module)) in actions_value.iter() {
180+
// Get the module path and use the full path
181+
let module_path = module.ident().path().await?;
182+
let full_path = module_path.to_string();
183+
184+
action_metadata.push((hash_id.clone(), (*layer, name.clone(), full_path)));
185+
}
186+
187+
// Now create the manifest entries
188+
for (hash_id, (layer, name, filename)) in &action_metadata {
178189
let entry = mapping.entry(hash_id.as_str()).or_default();
179190
entry.workers.insert(
180191
&key,
181192
ActionManifestWorkerEntry {
182193
module_id: ActionManifestModuleId::String(loader_id.as_str()),
183194
is_async: *async_module_info.is_async(chunk_item.module()).await?,
195+
exported_name: name.as_str(),
196+
filename: filename.as_str(),
184197
},
185198
);
186199
entry.layer.insert(&key, *layer);
200+
201+
// Hoist the filename and exported_name to the entry level
202+
entry.exported_name = name.as_str();
203+
entry.filename = filename.as_str();
187204
}
188205

189206
Ok(ResolvedVc::upcast(

crates/next-core/src/next_manifests/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,11 @@ pub struct ActionManifestEntry<'a> {
311311
pub workers: FxIndexMap<&'a str, ActionManifestWorkerEntry<'a>>,
312312

313313
pub layer: FxIndexMap<&'a str, ActionLayer>,
314+
315+
#[serde(rename = "exportedName")]
316+
pub exported_name: &'a str,
317+
318+
pub filename: &'a str,
314319
}
315320

316321
#[derive(Serialize, Debug)]
@@ -319,6 +324,9 @@ pub struct ActionManifestWorkerEntry<'a> {
319324
pub module_id: ActionManifestModuleId<'a>,
320325
#[serde(rename = "async")]
321326
pub is_async: bool,
327+
#[serde(rename = "exportedName")]
328+
pub exported_name: &'a str,
329+
pub filename: &'a str,
322330
}
323331

324332
#[derive(Serialize, Debug)]

packages/next/src/build/webpack/loaders/next-flight-action-entry-loader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export type NextFlightActionEntryLoaderOptions = {
66

77
export type FlightActionEntryLoaderActions = [
88
path: string,
9-
actions: { id: string; exportedName: string }[],
9+
actions: { id: string; exportedName?: string; filename?: string }[],
1010
][]
1111

1212
function nextFlightActionEntryLoader(

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

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,13 @@ const PLUGIN_NAME = 'FlightClientEntryPlugin'
6262

6363
type Actions = {
6464
[actionId: string]: {
65+
exportedName?: string
66+
filename?: string
6567
workers: {
66-
[name: string]: { moduleId: string | number; async: boolean }
68+
[name: string]: {
69+
moduleId: string | number
70+
async: boolean
71+
}
6772
}
6873
// Record which layer the action is in (rsc or sc_action), in the specific entry.
6974
layer: {
@@ -72,7 +77,7 @@ type Actions = {
7277
}
7378
}
7479

75-
type ActionIdNamePair = { id: string; exportedName: string }
80+
type ActionIdNamePair = { id: string; exportedName?: string; filename?: string }
7681

7782
export type ActionManifest = {
7883
// Assign a unique encryption key during production build.
@@ -92,11 +97,17 @@ const pluginState = getProxiedPluginState({
9297
edgeServerActions: {} as ActionManifest['edge'],
9398

9499
serverActionModules: {} as {
95-
[workerName: string]: { server?: ModuleInfo; client?: ModuleInfo }
100+
[workerName: string]: {
101+
server?: ModuleInfo
102+
client?: ModuleInfo
103+
}
96104
},
97105

98106
edgeServerActionModules: {} as {
99-
[workerName: string]: { server?: ModuleInfo; client?: ModuleInfo }
107+
[workerName: string]: {
108+
server?: ModuleInfo
109+
client?: ModuleInfo
110+
}
100111
},
101112

102113
ssrModules: {} as { [ssrModuleId: string]: ModuleInfo },
@@ -183,6 +194,7 @@ function deduplicateCSSImportsForEntry(mergedCSSimports: CssImports) {
183194
export class FlightClientEntryPlugin {
184195
dev: boolean
185196
appDir: string
197+
projectDir: string
186198
encryptionKey: string
187199
isEdgeServer: boolean
188200
assetPrefix: string
@@ -191,6 +203,7 @@ export class FlightClientEntryPlugin {
191203
constructor(options: Options) {
192204
this.dev = options.dev
193205
this.appDir = options.appDir
206+
this.projectDir = path.join(options.appDir, '..')
194207
this.isEdgeServer = options.isEdgeServer
195208
this.assetPrefix = !this.dev && !this.isEdgeServer ? '../' : ''
196209
this.encryptionKey = options.encryptionKey
@@ -628,6 +641,7 @@ export class FlightClientEntryPlugin {
628641
Object.entries(actionIds).map(([id, exportedName]) => ({
629642
id,
630643
exportedName,
644+
filename: path.posix.relative(this.projectDir, modResource),
631645
}))
632646
)
633647
}
@@ -728,6 +742,7 @@ export class FlightClientEntryPlugin {
728742
Object.entries(actionIds).map(([id, exportedName]) => ({
729743
id,
730744
exportedName,
745+
filename: path.posix.relative(this.projectDir, modResource),
731746
})),
732747
])
733748
}
@@ -949,11 +964,13 @@ export class FlightClientEntryPlugin {
949964
: pluginState.serverActions
950965

951966
for (const [, actionsFromModule] of actionsArray) {
952-
for (const { id } of actionsFromModule) {
967+
for (const { id, exportedName, filename } of actionsFromModule) {
953968
if (typeof currentCompilerServerActions[id] === 'undefined') {
954969
currentCompilerServerActions[id] = {
955970
workers: {},
956971
layer: {},
972+
filename,
973+
exportedName,
957974
}
958975
}
959976
currentCompilerServerActions[id].workers[bundlePath] = {
@@ -1060,6 +1077,7 @@ export class FlightClientEntryPlugin {
10601077
if (!mapping[chunkGroup.name]) {
10611078
mapping[chunkGroup.name] = {}
10621079
}
1080+
10631081
mapping[chunkGroup.name][fromClient ? 'client' : 'server'] = {
10641082
moduleId: modId,
10651083
async: compilation.moduleGraph.isAsync(mod),

packages/next/src/shared/lib/turbopack/manifest-loader.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ export class TurbopackManifestLoader {
203203
workers: {},
204204
layer: {},
205205
})
206+
action.filename = other[key].filename
207+
action.exportedName = other[key].exportedName
206208
Object.assign(action.workers, other[key].workers)
207209
Object.assign(action.layer, other[key].layer)
208210
}

test/e2e/app-dir/actions/app-action.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,49 @@ describe('app-dir action handling', () => {
2929
},
3030
})
3131

32+
if (isNextStart) {
33+
it('should output exportName and filename info in manifest', async () => {
34+
const referenceManifest = await next.readJSON(
35+
'.next/server/server-reference-manifest.json'
36+
)
37+
let foundExportNames = []
38+
39+
for (const item in referenceManifest.node) {
40+
try {
41+
const itemInfo = referenceManifest.node[item]
42+
43+
foundExportNames.push(itemInfo.exportedName)
44+
45+
expect(itemInfo.filename).toBeString()
46+
// can be outside app dir but this test suite has them all in app
47+
expect(itemInfo.filename.startsWith('app/')).toBe(true)
48+
expect(itemInfo.exportedName).toBeString()
49+
} catch (err) {
50+
require('console').error(`Invalid action entry ${item}`, err)
51+
throw err
52+
}
53+
}
54+
for (const item in referenceManifest.edge) {
55+
try {
56+
const itemInfo = referenceManifest.edge[item]
57+
58+
foundExportNames.push(itemInfo.exportedName)
59+
60+
expect(itemInfo.filename).toBeString()
61+
expect(itemInfo.exportedName).toBeString()
62+
} catch (err) {
63+
require('console').error(`Invalid action entry ${item}`, err)
64+
throw err
65+
}
66+
}
67+
68+
expect(foundExportNames).toContain('setCookie')
69+
expect(foundExportNames).toContain('getCookie')
70+
expect(foundExportNames).toContain('getHeader')
71+
expect(foundExportNames).toContain('setCookieWithMaxAge')
72+
})
73+
}
74+
3275
it('should handle action correctly with middleware rewrite', async () => {
3376
const browser = await next.browser('/rewrite-to-static-first')
3477
let actionRequestStatus: number | undefined

0 commit comments

Comments
 (0)