@tanstack/start-plugin-core 1.167.35 → 1.169.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/import-protection/adapterUtils.d.ts +27 -0
- package/dist/esm/import-protection/adapterUtils.js +31 -0
- package/dist/esm/import-protection/adapterUtils.js.map +1 -0
- package/dist/esm/import-protection/analysis.d.ts +36 -0
- package/dist/esm/import-protection/analysis.js +407 -0
- package/dist/esm/import-protection/analysis.js.map +1 -0
- package/dist/esm/{import-protection-plugin → import-protection}/ast.js +1 -1
- package/dist/esm/import-protection/ast.js.map +1 -0
- package/dist/esm/import-protection/constants.d.ts +11 -0
- package/dist/esm/{import-protection-plugin → import-protection}/constants.js +7 -2
- package/dist/esm/import-protection/constants.js.map +1 -0
- package/dist/esm/{import-protection-plugin → import-protection}/defaults.js +1 -1
- package/dist/esm/import-protection/defaults.js.map +1 -0
- package/dist/esm/{import-protection-plugin → import-protection}/extensionlessAbsoluteIdResolver.js +2 -2
- package/dist/esm/import-protection/extensionlessAbsoluteIdResolver.js.map +1 -0
- package/dist/esm/{import-protection-plugin → import-protection}/matchers.js +1 -1
- package/dist/esm/import-protection/matchers.js.map +1 -0
- package/dist/esm/{import-protection-plugin/rewriteDeniedImports.d.ts → import-protection/rewrite.d.ts} +0 -4
- package/dist/esm/import-protection/rewrite.js +121 -0
- package/dist/esm/import-protection/rewrite.js.map +1 -0
- package/dist/esm/{import-protection-plugin → import-protection}/sourceLocation.d.ts +32 -3
- package/dist/esm/{import-protection-plugin → import-protection}/sourceLocation.js +65 -10
- package/dist/esm/import-protection/sourceLocation.js.map +1 -0
- package/dist/esm/{import-protection-plugin → import-protection}/trace.d.ts +0 -1
- package/dist/esm/{import-protection-plugin → import-protection}/trace.js +1 -1
- package/dist/esm/import-protection/trace.js.map +1 -0
- package/dist/esm/{import-protection-plugin → import-protection}/utils.d.ts +18 -1
- package/dist/esm/{import-protection-plugin → import-protection}/utils.js +13 -20
- package/dist/esm/import-protection/utils.js.map +1 -0
- package/dist/esm/import-protection/virtualModules.d.ts +25 -0
- package/dist/esm/{import-protection-plugin → import-protection}/virtualModules.js +5 -117
- package/dist/esm/import-protection/virtualModules.js.map +1 -0
- package/dist/esm/index.d.ts +1 -5
- package/dist/esm/index.js +2 -4
- package/dist/esm/post-build.d.ts +9 -0
- package/dist/esm/post-build.js +37 -0
- package/dist/esm/post-build.js.map +1 -0
- package/dist/esm/prerender.d.ts +11 -0
- package/dist/esm/prerender.js +159 -0
- package/dist/esm/prerender.js.map +1 -0
- package/dist/esm/rsbuild/dev-server.d.ts +21 -0
- package/dist/esm/rsbuild/dev-server.js +76 -0
- package/dist/esm/rsbuild/dev-server.js.map +1 -0
- package/dist/esm/rsbuild/import-protection.d.ts +10 -0
- package/dist/esm/rsbuild/import-protection.js +775 -0
- package/dist/esm/rsbuild/import-protection.js.map +1 -0
- package/dist/esm/rsbuild/index.d.ts +4 -0
- package/dist/esm/rsbuild/index.js +3 -0
- package/dist/esm/rsbuild/normalized-client-build.d.ts +18 -0
- package/dist/esm/rsbuild/normalized-client-build.js +207 -0
- package/dist/esm/rsbuild/normalized-client-build.js.map +1 -0
- package/dist/esm/rsbuild/planning.d.ts +52 -0
- package/dist/esm/rsbuild/planning.js +108 -0
- package/dist/esm/rsbuild/planning.js.map +1 -0
- package/dist/esm/rsbuild/plugin.d.ts +4 -0
- package/dist/esm/rsbuild/plugin.js +344 -0
- package/dist/esm/rsbuild/plugin.js.map +1 -0
- package/dist/esm/rsbuild/post-build.d.ts +6 -0
- package/dist/esm/rsbuild/post-build.js +57 -0
- package/dist/esm/rsbuild/post-build.js.map +1 -0
- package/dist/esm/rsbuild/schema.d.ts +3372 -0
- package/dist/esm/rsbuild/schema.js +12 -0
- package/dist/esm/rsbuild/schema.js.map +1 -0
- package/dist/esm/rsbuild/start-compiler-host.d.ts +20 -0
- package/dist/esm/rsbuild/start-compiler-host.js +150 -0
- package/dist/esm/rsbuild/start-compiler-host.js.map +1 -0
- package/dist/esm/rsbuild/start-router-plugin.d.ts +18 -0
- package/dist/esm/rsbuild/start-router-plugin.js +63 -0
- package/dist/esm/rsbuild/start-router-plugin.js.map +1 -0
- package/dist/esm/rsbuild/swc-rsc.d.ts +14 -0
- package/dist/esm/rsbuild/swc-rsc.js +93 -0
- package/dist/esm/rsbuild/swc-rsc.js.map +1 -0
- package/dist/esm/rsbuild/types.d.ts +17 -0
- package/dist/esm/rsbuild/types.js +0 -0
- package/dist/esm/rsbuild/virtual-modules.d.ts +53 -0
- package/dist/esm/rsbuild/virtual-modules.js +287 -0
- package/dist/esm/rsbuild/virtual-modules.js.map +1 -0
- package/dist/esm/schema.d.ts +43 -43
- package/dist/esm/start-compiler/compiler.d.ts +1 -1
- package/dist/esm/start-compiler/compiler.js +80 -9
- package/dist/esm/start-compiler/compiler.js.map +1 -1
- package/dist/esm/start-compiler/handleCreateServerFn.js +9 -0
- package/dist/esm/start-compiler/handleCreateServerFn.js.map +1 -1
- package/dist/esm/start-compiler/host.js +5 -1
- package/dist/esm/start-compiler/host.js.map +1 -1
- package/dist/esm/start-compiler/types.d.ts +1 -0
- package/dist/esm/utils.d.ts +1 -0
- package/dist/esm/utils.js +10 -1
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/{import-protection-plugin → vite/import-protection-plugin}/plugin.js +41 -92
- package/dist/esm/vite/import-protection-plugin/plugin.js.map +1 -0
- package/dist/esm/{import-protection-plugin → vite/import-protection-plugin}/types.d.ts +5 -5
- package/dist/esm/vite/import-protection-plugin/virtualModules.d.ts +8 -0
- package/dist/esm/vite/import-protection-plugin/virtualModules.js +49 -0
- package/dist/esm/vite/import-protection-plugin/virtualModules.js.map +1 -0
- package/dist/esm/vite/index.d.ts +5 -0
- package/dist/esm/vite/index.js +4 -0
- package/dist/esm/vite/plugin.js +1 -1
- package/dist/esm/vite/plugin.js.map +1 -1
- package/dist/esm/vite/post-server-build.js +14 -32
- package/dist/esm/vite/post-server-build.js.map +1 -1
- package/dist/esm/vite/prerender.d.ts +2 -2
- package/dist/esm/vite/prerender.js +17 -147
- package/dist/esm/vite/prerender.js.map +1 -1
- package/dist/esm/vite/schema.d.ts +23 -23
- package/dist/esm/vite/start-compiler-plugin/hot-update.d.ts +2 -0
- package/dist/esm/vite/start-compiler-plugin/hot-update.js +16 -0
- package/dist/esm/vite/start-compiler-plugin/hot-update.js.map +1 -0
- package/dist/esm/vite/start-compiler-plugin/module-specifier.js +9 -4
- package/dist/esm/vite/start-compiler-plugin/module-specifier.js.map +1 -1
- package/dist/esm/vite/start-compiler-plugin/plugin.js +86 -13
- package/dist/esm/vite/start-compiler-plugin/plugin.js.map +1 -1
- package/package.json +32 -4
- package/src/import-protection/INTERNALS.md +266 -0
- package/src/import-protection/adapterUtils.ts +94 -0
- package/src/import-protection/analysis.ts +853 -0
- package/src/{import-protection-plugin → import-protection}/constants.ts +7 -0
- package/src/import-protection/rewrite.ts +229 -0
- package/src/{import-protection-plugin → import-protection}/sourceLocation.ts +125 -9
- package/src/{import-protection-plugin → import-protection}/trace.ts +0 -1
- package/src/{import-protection-plugin → import-protection}/utils.ts +36 -21
- package/src/{import-protection-plugin → import-protection}/virtualModules.ts +30 -177
- package/src/index.ts +1 -8
- package/src/post-build.ts +64 -0
- package/src/prerender.ts +292 -0
- package/src/rsbuild/INTERNALS-import-protection.md +169 -0
- package/src/rsbuild/dev-server.ts +129 -0
- package/src/rsbuild/import-protection.ts +1599 -0
- package/src/rsbuild/index.ts +4 -0
- package/src/rsbuild/normalized-client-build.ts +346 -0
- package/src/rsbuild/planning.ts +234 -0
- package/src/rsbuild/plugin.ts +754 -0
- package/src/rsbuild/post-build.ts +96 -0
- package/src/rsbuild/schema.ts +31 -0
- package/src/rsbuild/start-compiler-host.ts +250 -0
- package/src/rsbuild/start-router-plugin.ts +86 -0
- package/src/rsbuild/swc-rsc.ts +166 -0
- package/src/rsbuild/types.ts +20 -0
- package/src/rsbuild/virtual-modules.ts +565 -0
- package/src/start-compiler/compiler.ts +153 -19
- package/src/start-compiler/handleCreateServerFn.ts +18 -0
- package/src/start-compiler/types.ts +1 -0
- package/src/utils.ts +14 -0
- package/src/vite/import-protection-plugin/INTERNALS.md +187 -0
- package/src/{import-protection-plugin → vite/import-protection-plugin}/plugin.ts +73 -158
- package/src/{import-protection-plugin → vite/import-protection-plugin}/types.ts +5 -5
- package/src/vite/import-protection-plugin/virtualModules.ts +122 -0
- package/src/vite/index.ts +8 -0
- package/src/vite/plugin.ts +1 -1
- package/src/vite/post-server-build.ts +14 -57
- package/src/vite/prerender.ts +19 -260
- package/src/vite/start-compiler-plugin/hot-update.ts +24 -0
- package/src/vite/start-compiler-plugin/module-specifier.ts +15 -5
- package/src/vite/start-compiler-plugin/plugin.ts +193 -18
- package/dist/esm/import-protection-plugin/ast.js.map +0 -1
- package/dist/esm/import-protection-plugin/constants.d.ts +0 -6
- package/dist/esm/import-protection-plugin/constants.js.map +0 -1
- package/dist/esm/import-protection-plugin/defaults.js.map +0 -1
- package/dist/esm/import-protection-plugin/extensionlessAbsoluteIdResolver.js.map +0 -1
- package/dist/esm/import-protection-plugin/matchers.js.map +0 -1
- package/dist/esm/import-protection-plugin/plugin.js.map +0 -1
- package/dist/esm/import-protection-plugin/postCompileUsage.d.ts +0 -13
- package/dist/esm/import-protection-plugin/postCompileUsage.js +0 -63
- package/dist/esm/import-protection-plugin/postCompileUsage.js.map +0 -1
- package/dist/esm/import-protection-plugin/rewriteDeniedImports.js +0 -205
- package/dist/esm/import-protection-plugin/rewriteDeniedImports.js.map +0 -1
- package/dist/esm/import-protection-plugin/sourceLocation.js.map +0 -1
- package/dist/esm/import-protection-plugin/trace.js.map +0 -1
- package/dist/esm/import-protection-plugin/utils.js.map +0 -1
- package/dist/esm/import-protection-plugin/virtualModules.d.ts +0 -78
- package/dist/esm/import-protection-plugin/virtualModules.js.map +0 -1
- package/dist/esm/start-compiler/load-module.d.ts +0 -14
- package/dist/esm/start-compiler/load-module.js +0 -18
- package/dist/esm/start-compiler/load-module.js.map +0 -1
- package/src/import-protection-plugin/INTERNALS.md +0 -700
- package/src/import-protection-plugin/postCompileUsage.ts +0 -100
- package/src/import-protection-plugin/rewriteDeniedImports.ts +0 -379
- package/src/start-compiler/load-module.ts +0 -31
- /package/dist/esm/{import-protection-plugin → import-protection}/ast.d.ts +0 -0
- /package/dist/esm/{import-protection-plugin → import-protection}/defaults.d.ts +0 -0
- /package/dist/esm/{import-protection-plugin → import-protection}/extensionlessAbsoluteIdResolver.d.ts +0 -0
- /package/dist/esm/{import-protection-plugin → import-protection}/matchers.d.ts +0 -0
- /package/dist/esm/{import-protection-plugin → vite/import-protection-plugin}/plugin.d.ts +0 -0
- /package/src/{import-protection-plugin → import-protection}/ast.ts +0 -0
- /package/src/{import-protection-plugin → import-protection}/defaults.ts +0 -0
- /package/src/{import-protection-plugin → import-protection}/extensionlessAbsoluteIdResolver.ts +0 -0
- /package/src/{import-protection-plugin → import-protection}/matchers.ts +0 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { tsrSplit } from '@tanstack/router-plugin'
|
|
2
|
+
import { RSBUILD_ENVIRONMENT_NAMES } from './planning'
|
|
3
|
+
import type { RsbuildPluginAPI, Rspack } from '@rsbuild/core'
|
|
4
|
+
import type { NormalizedClientBuild, NormalizedClientChunk } from '../types'
|
|
5
|
+
|
|
6
|
+
type ProcessAssetsContext = Parameters<
|
|
7
|
+
Parameters<RsbuildPluginAPI['processAssets']>[1]
|
|
8
|
+
>[0]
|
|
9
|
+
type RspackCompilation = Rspack.Compilation
|
|
10
|
+
type RspackCompilationChunk = Rspack.Chunk
|
|
11
|
+
type RspackModule = Rspack.Module
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Extract route file paths from rspack module identifiers.
|
|
15
|
+
*
|
|
16
|
+
* In rspack, module identifiers contain query params similar to Vite's moduleIds.
|
|
17
|
+
* We look for the `tsr-split` query to identify route-split chunks.
|
|
18
|
+
*/
|
|
19
|
+
function getRouteFilePathsFromModules(
|
|
20
|
+
modules: Array<RspackModule>,
|
|
21
|
+
): Array<string> {
|
|
22
|
+
let routeFilePaths: Array<string> | undefined
|
|
23
|
+
let seen: Set<string> | undefined
|
|
24
|
+
|
|
25
|
+
for (const mod of modules) {
|
|
26
|
+
const identifier = mod.identifier()
|
|
27
|
+
|
|
28
|
+
// rspack module identifiers include loader prefixes separated by '!'.
|
|
29
|
+
// The actual file path (with query string) is after the last '!'.
|
|
30
|
+
// Example: "builtin:swc-loader??ruleSet[...]!.../transform.js??...!.../rsc-basic.tsx?tsr-split=component"
|
|
31
|
+
const lastBangIndex = identifier.lastIndexOf('!')
|
|
32
|
+
const resourcePart =
|
|
33
|
+
lastBangIndex >= 0 ? identifier.slice(lastBangIndex + 1) : identifier
|
|
34
|
+
|
|
35
|
+
const queryIndex = resourcePart.indexOf('?')
|
|
36
|
+
if (queryIndex < 0) continue
|
|
37
|
+
|
|
38
|
+
const query = resourcePart.slice(queryIndex + 1)
|
|
39
|
+
if (!query.includes(tsrSplit)) continue
|
|
40
|
+
if (!new URLSearchParams(query).has(tsrSplit)) continue
|
|
41
|
+
|
|
42
|
+
const nameForCondition = mod.nameForCondition()
|
|
43
|
+
const routeFilePath = nameForCondition ?? resourcePart.slice(0, queryIndex)
|
|
44
|
+
|
|
45
|
+
if (seen?.has(routeFilePath)) continue
|
|
46
|
+
|
|
47
|
+
if (!routeFilePaths || !seen) {
|
|
48
|
+
routeFilePaths = []
|
|
49
|
+
seen = new Set()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
routeFilePaths.push(routeFilePath)
|
|
53
|
+
seen.add(routeFilePath)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return routeFilePaths ?? []
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Returns true for Rspack/webpack HMR runtime chunks that should never be
|
|
61
|
+
* surfaced to the Start manifest. These files are emitted on every rebuild
|
|
62
|
+
* (e.g. `index.<hash>.hot-update.mjs`) and must not be treated as the entry
|
|
63
|
+
* chunk, route preloads, or sibling imports.
|
|
64
|
+
*/
|
|
65
|
+
function isHotUpdateAsset(file: string): boolean {
|
|
66
|
+
return file.includes('.hot-update.')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* True for any JS/MJS asset that should be included in the manifest.
|
|
71
|
+
* Excludes HMR runtime patches.
|
|
72
|
+
*/
|
|
73
|
+
function isManifestJsAsset(file: string): boolean {
|
|
74
|
+
if (!file.endsWith('.js') && !file.endsWith('.mjs')) return false
|
|
75
|
+
return !isHotUpdateAsset(file)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get all JS file names from a chunk.
|
|
80
|
+
*/
|
|
81
|
+
function getChunkJsFiles(chunk: RspackCompilationChunk): Array<string> {
|
|
82
|
+
const jsFiles: Array<string> = []
|
|
83
|
+
for (const file of chunk.files) {
|
|
84
|
+
if (isManifestJsAsset(file)) {
|
|
85
|
+
jsFiles.push(file)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return jsFiles
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Compute dynamicImports for a chunk by traversing its chunk groups'
|
|
93
|
+
* childrenIterable (async/dynamic import edges).
|
|
94
|
+
*
|
|
95
|
+
* In rspack, a chunk belongs to one or more ChunkGroups. Each ChunkGroup
|
|
96
|
+
* has childrenIterable — child ChunkGroups representing dynamic import()
|
|
97
|
+
* points. The JS files from those child groups' chunks are the
|
|
98
|
+
* dynamicImports (analogous to Rollup's OutputChunk.dynamicImports).
|
|
99
|
+
*/
|
|
100
|
+
function computeDynamicImports(chunk: RspackCompilationChunk): Array<string> {
|
|
101
|
+
const dynamicImportFiles: Array<string> = []
|
|
102
|
+
const seen = new Set<string>()
|
|
103
|
+
|
|
104
|
+
for (const group of chunk.groupsIterable) {
|
|
105
|
+
for (const childGroup of group.childrenIterable) {
|
|
106
|
+
for (const childChunk of childGroup.chunks) {
|
|
107
|
+
for (const file of childChunk.files) {
|
|
108
|
+
if (isManifestJsAsset(file) && !seen.has(file)) {
|
|
109
|
+
seen.add(file)
|
|
110
|
+
dynamicImportFiles.push(file)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return dynamicImportFiles
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Compute static imports (sibling chunks) for an async chunk.
|
|
122
|
+
*
|
|
123
|
+
* In rspack/webpack, an async chunk's ChunkGroup contains ALL chunks needed to
|
|
124
|
+
* satisfy that dynamic import — the async chunk itself plus any shared/vendor
|
|
125
|
+
* chunks it statically imports. This is analogous to Rollup's
|
|
126
|
+
* `OutputChunk.imports` for async chunks.
|
|
127
|
+
*
|
|
128
|
+
* We collect JS files from all sibling chunks in the group (excluding the
|
|
129
|
+
* current chunk's own file) to populate the `imports` field.
|
|
130
|
+
*/
|
|
131
|
+
function computeAsyncChunkImports(
|
|
132
|
+
chunk: RspackCompilationChunk,
|
|
133
|
+
currentFile: string,
|
|
134
|
+
): Array<string> {
|
|
135
|
+
const imports: Array<string> = []
|
|
136
|
+
const seen = new Set<string>()
|
|
137
|
+
seen.add(currentFile)
|
|
138
|
+
|
|
139
|
+
for (const group of chunk.groupsIterable) {
|
|
140
|
+
for (const siblingChunk of group.chunks) {
|
|
141
|
+
for (const file of siblingChunk.files) {
|
|
142
|
+
if (isManifestJsAsset(file) && !seen.has(file)) {
|
|
143
|
+
seen.add(file)
|
|
144
|
+
imports.push(file)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return imports
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Normalize an rspack compilation into a NormalizedClientBuild.
|
|
155
|
+
*
|
|
156
|
+
* Iterates ALL chunks in the compilation (initial + async), not just
|
|
157
|
+
* entrypoint chunks, to ensure route-split async chunks are included.
|
|
158
|
+
*/
|
|
159
|
+
export function normalizeRspackClientBuild(
|
|
160
|
+
compilation: RspackCompilation,
|
|
161
|
+
): NormalizedClientBuild {
|
|
162
|
+
const chunksByFileName = new Map<string, NormalizedClientChunk>()
|
|
163
|
+
const chunkFileNamesByRouteFilePath = new Map<string, Array<string>>()
|
|
164
|
+
const cssFilesBySourcePath = new Map<string, Array<string>>()
|
|
165
|
+
let entryChunkFileName: string | undefined
|
|
166
|
+
|
|
167
|
+
// Collect all initial JS file names from the main entry for computing
|
|
168
|
+
// the entry chunk's `imports` (vendor/shared sibling chunks).
|
|
169
|
+
const entrypoint = compilation.entrypoints.get('index')
|
|
170
|
+
const initialJsFileNames: Array<string> = []
|
|
171
|
+
const entryChunkSet = new Set<RspackCompilationChunk>()
|
|
172
|
+
if (entrypoint) {
|
|
173
|
+
for (const chunk of entrypoint.chunks) {
|
|
174
|
+
entryChunkSet.add(chunk)
|
|
175
|
+
for (const file of chunk.files) {
|
|
176
|
+
if (isManifestJsAsset(file)) {
|
|
177
|
+
initialJsFileNames.push(file)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Iterate ALL chunks (initial + async) to capture route-split chunks
|
|
184
|
+
for (const chunk of compilation.chunks) {
|
|
185
|
+
const modules = compilation.chunkGraph.getChunkModules(chunk)
|
|
186
|
+
const routeFilePaths = getRouteFilePathsFromModules(modules)
|
|
187
|
+
const cssFiles: Array<string> = []
|
|
188
|
+
const seenCssFiles = new Set<string>()
|
|
189
|
+
|
|
190
|
+
for (const auxFile of chunk.auxiliaryFiles) {
|
|
191
|
+
if (auxFile.endsWith('.css') && !seenCssFiles.has(auxFile)) {
|
|
192
|
+
seenCssFiles.add(auxFile)
|
|
193
|
+
cssFiles.push(auxFile)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
for (const mainFile of chunk.files) {
|
|
198
|
+
if (mainFile.endsWith('.css') && !seenCssFiles.has(mainFile)) {
|
|
199
|
+
seenCssFiles.add(mainFile)
|
|
200
|
+
cssFiles.push(mainFile)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (cssFiles.length > 0) {
|
|
205
|
+
for (const mod of modules) {
|
|
206
|
+
const sourcePath = mod.nameForCondition()
|
|
207
|
+
if (!sourcePath) continue
|
|
208
|
+
|
|
209
|
+
const existing = cssFilesBySourcePath.get(sourcePath)
|
|
210
|
+
cssFilesBySourcePath.set(
|
|
211
|
+
sourcePath,
|
|
212
|
+
existing ? appendUniqueStrings(existing, cssFiles) : cssFiles.slice(),
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// The entry chunk is the one named 'index' in the 'index' entrypoint
|
|
218
|
+
const isEntryChunk = chunk.name === 'index' && entryChunkSet.has(chunk)
|
|
219
|
+
|
|
220
|
+
const jsFiles = getChunkJsFiles(chunk)
|
|
221
|
+
if (jsFiles.length === 0) continue
|
|
222
|
+
|
|
223
|
+
// Compute dynamicImports from chunk group children
|
|
224
|
+
const dynamicImports = computeDynamicImports(chunk)
|
|
225
|
+
|
|
226
|
+
for (const file of jsFiles) {
|
|
227
|
+
// For the entry chunk, `imports` contains all sibling initial chunks
|
|
228
|
+
// (vendor/shared). For async chunks, `imports` contains all sibling
|
|
229
|
+
// chunks from the ChunkGroup (shared dependencies the browser must
|
|
230
|
+
// load alongside this chunk). This mirrors Rollup's
|
|
231
|
+
// OutputChunk.imports which lists statically imported chunks.
|
|
232
|
+
const imports = isEntryChunk
|
|
233
|
+
? initialJsFileNames.filter((f) => f !== file)
|
|
234
|
+
: computeAsyncChunkImports(chunk, file)
|
|
235
|
+
|
|
236
|
+
const normalizedChunk: NormalizedClientChunk = {
|
|
237
|
+
fileName: file,
|
|
238
|
+
isEntry: isEntryChunk,
|
|
239
|
+
imports,
|
|
240
|
+
dynamicImports,
|
|
241
|
+
css: [],
|
|
242
|
+
routeFilePaths,
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
chunksByFileName.set(file, normalizedChunk)
|
|
246
|
+
|
|
247
|
+
if (isEntryChunk && !entryChunkFileName) {
|
|
248
|
+
entryChunkFileName = file
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
for (const routeFilePath of routeFilePaths) {
|
|
252
|
+
let chunkFileNames = chunkFileNamesByRouteFilePath.get(routeFilePath)
|
|
253
|
+
if (!chunkFileNames) {
|
|
254
|
+
chunkFileNames = []
|
|
255
|
+
chunkFileNamesByRouteFilePath.set(routeFilePath, chunkFileNames)
|
|
256
|
+
}
|
|
257
|
+
chunkFileNames.push(file)
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
for (const cssFile of cssFiles) {
|
|
262
|
+
for (const file of jsFiles) {
|
|
263
|
+
const existing = chunksByFileName.get(file)
|
|
264
|
+
if (existing && !existing.css.includes(cssFile)) {
|
|
265
|
+
existing.css.push(cssFile)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (!entryChunkFileName) {
|
|
272
|
+
throw new Error('No entry file found in rspack client build')
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// In RSC mode, CSS from server components is associated with the 'rsc'
|
|
276
|
+
// client entry chunk (not the main 'index' entry). The manifest builder
|
|
277
|
+
// merges the entry chunk's CSS into __root__, so by appending RSC CSS
|
|
278
|
+
// to the entry chunk, those stylesheets get loaded on all pages.
|
|
279
|
+
// CSS may appear in either `files` or `auxiliaryFiles` depending on
|
|
280
|
+
// rspack's CSS extraction strategy.
|
|
281
|
+
const rscEntrypoint = compilation.entrypoints.get('rsc')
|
|
282
|
+
|
|
283
|
+
if (rscEntrypoint && entryChunkFileName) {
|
|
284
|
+
const mainEntryChunk = chunksByFileName.get(entryChunkFileName)
|
|
285
|
+
if (mainEntryChunk) {
|
|
286
|
+
for (const rscChunk of rscEntrypoint.chunks) {
|
|
287
|
+
const allFiles = [...rscChunk.files, ...rscChunk.auxiliaryFiles]
|
|
288
|
+
for (const file of allFiles) {
|
|
289
|
+
if (file.endsWith('.css') && !mainEntryChunk.css.includes(file)) {
|
|
290
|
+
mainEntryChunk.css.push(file)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
entryChunkFileName,
|
|
299
|
+
chunksByFileName,
|
|
300
|
+
chunkFileNamesByRouteFilePath,
|
|
301
|
+
cssFilesBySourcePath,
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function appendUniqueStrings(
|
|
306
|
+
target: Array<string>,
|
|
307
|
+
source: Array<string>,
|
|
308
|
+
): Array<string> {
|
|
309
|
+
const seen = new Set(target)
|
|
310
|
+
let result: Array<string> | undefined
|
|
311
|
+
|
|
312
|
+
for (const value of source) {
|
|
313
|
+
if (seen.has(value)) continue
|
|
314
|
+
seen.add(value)
|
|
315
|
+
if (!result) {
|
|
316
|
+
result = target.slice()
|
|
317
|
+
}
|
|
318
|
+
result.push(value)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return result ?? target
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Registers a processAssets hook to capture the client build stats
|
|
326
|
+
* after compilation. Returns a getter for the captured build.
|
|
327
|
+
*/
|
|
328
|
+
export function registerClientBuildCapture(api: RsbuildPluginAPI): {
|
|
329
|
+
getClientBuild: () => NormalizedClientBuild | undefined
|
|
330
|
+
} {
|
|
331
|
+
let clientBuild: NormalizedClientBuild | undefined
|
|
332
|
+
|
|
333
|
+
api.processAssets(
|
|
334
|
+
{
|
|
335
|
+
stage: 'report',
|
|
336
|
+
environments: [RSBUILD_ENVIRONMENT_NAMES.client],
|
|
337
|
+
},
|
|
338
|
+
(context: ProcessAssetsContext) => {
|
|
339
|
+
clientBuild = normalizeRspackClientBuild(context.compilation)
|
|
340
|
+
},
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
return {
|
|
344
|
+
getClientBuild: () => clientBuild,
|
|
345
|
+
}
|
|
346
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
import { join } from 'pathe'
|
|
3
|
+
import { mergeRsbuildConfig } from '@rsbuild/core'
|
|
4
|
+
import { ENTRY_POINTS } from '../constants'
|
|
5
|
+
import type { EnvironmentConfig } from '@rsbuild/core'
|
|
6
|
+
import type { ResolvedStartEntryPlan } from '../planning'
|
|
7
|
+
import type { RsbuildEnvironmentOverrides } from './types'
|
|
8
|
+
|
|
9
|
+
const require = createRequire(import.meta.url)
|
|
10
|
+
|
|
11
|
+
export const RSBUILD_ENVIRONMENT_NAMES = {
|
|
12
|
+
client: 'client',
|
|
13
|
+
server: 'ssr',
|
|
14
|
+
} as const
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Rspack layer names for the rsbuild RSC layered model.
|
|
18
|
+
* These match the canonical names from `rspack.experiments.rsc.Layers`.
|
|
19
|
+
*/
|
|
20
|
+
export const RSBUILD_RSC_LAYERS = {
|
|
21
|
+
/** React Server Components layer — uses `react-server` resolve condition */
|
|
22
|
+
rsc: 'react-server-components',
|
|
23
|
+
/** Server-Side Rendering layer — standard Node resolve */
|
|
24
|
+
ssr: 'server-side-rendering',
|
|
25
|
+
} as const
|
|
26
|
+
|
|
27
|
+
export type RsbuildEnvironmentName =
|
|
28
|
+
(typeof RSBUILD_ENVIRONMENT_NAMES)[keyof typeof RSBUILD_ENVIRONMENT_NAMES]
|
|
29
|
+
|
|
30
|
+
type RsbuildDistPath = NonNullable<EnvironmentConfig['output']>['distPath']
|
|
31
|
+
|
|
32
|
+
export interface RsbuildResolvedEntryAliases {
|
|
33
|
+
client: string
|
|
34
|
+
server: string
|
|
35
|
+
start: string
|
|
36
|
+
router: string
|
|
37
|
+
alias: Record<(typeof ENTRY_POINTS)[keyof typeof ENTRY_POINTS], string>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function createRsbuildResolvedEntryAliases(opts: {
|
|
41
|
+
entryPaths: ResolvedStartEntryPlan['entryPaths']
|
|
42
|
+
}): RsbuildResolvedEntryAliases {
|
|
43
|
+
const client = normalizeEntryPath(opts.entryPaths.client)
|
|
44
|
+
const server = normalizeEntryPath(opts.entryPaths.server)
|
|
45
|
+
const start = normalizeEntryPath(opts.entryPaths.start)
|
|
46
|
+
const router = normalizeEntryPath(opts.entryPaths.router)
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
client,
|
|
50
|
+
server,
|
|
51
|
+
start,
|
|
52
|
+
router,
|
|
53
|
+
alias: {
|
|
54
|
+
[ENTRY_POINTS.client]: client,
|
|
55
|
+
[ENTRY_POINTS.server]: server,
|
|
56
|
+
[ENTRY_POINTS.start]: start,
|
|
57
|
+
[ENTRY_POINTS.router]: router,
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface RsbuildEnvironmentPlanResult {
|
|
63
|
+
environments: Record<string, EnvironmentConfig>
|
|
64
|
+
alias: Record<string, string>
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function createRsbuildEnvironmentPlan(opts: {
|
|
68
|
+
root: string
|
|
69
|
+
entryAliases: RsbuildResolvedEntryAliases
|
|
70
|
+
clientOutputDirectory: string
|
|
71
|
+
serverOutputDirectory: string
|
|
72
|
+
publicBase: string
|
|
73
|
+
serverFnProviderEnv: string
|
|
74
|
+
environmentOverrides?: RsbuildEnvironmentOverrides
|
|
75
|
+
rsc?: boolean | undefined
|
|
76
|
+
dev?: boolean | undefined
|
|
77
|
+
}): RsbuildEnvironmentPlanResult {
|
|
78
|
+
const alias = {
|
|
79
|
+
...opts.entryAliases.alias,
|
|
80
|
+
...(opts.rsc
|
|
81
|
+
? {
|
|
82
|
+
'react-server-dom-rspack/server$': resolveFromRoot(
|
|
83
|
+
'react-server-dom-rspack/server.node',
|
|
84
|
+
opts.root,
|
|
85
|
+
),
|
|
86
|
+
}
|
|
87
|
+
: {}),
|
|
88
|
+
}
|
|
89
|
+
const environmentOverrides = opts.environmentOverrides ?? {}
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
environments: {
|
|
93
|
+
[RSBUILD_ENVIRONMENT_NAMES.client]: mergeRsbuildConfig(
|
|
94
|
+
{
|
|
95
|
+
source: {
|
|
96
|
+
entry: {
|
|
97
|
+
index: {
|
|
98
|
+
import: opts.entryAliases.client,
|
|
99
|
+
html: false,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
output: {
|
|
104
|
+
target: 'web',
|
|
105
|
+
module: true,
|
|
106
|
+
distPath: {
|
|
107
|
+
root: opts.clientOutputDirectory,
|
|
108
|
+
},
|
|
109
|
+
assetPrefix: opts.publicBase,
|
|
110
|
+
},
|
|
111
|
+
resolve: {
|
|
112
|
+
alias,
|
|
113
|
+
},
|
|
114
|
+
// Only split async chunks (route code-splitting). Keep all initial
|
|
115
|
+
// vendor/shared code inlined in the entry chunk so the SSR HTML only
|
|
116
|
+
// needs the single client entry bootstrap.
|
|
117
|
+
performance: {
|
|
118
|
+
chunkSplit: {
|
|
119
|
+
strategy: 'custom',
|
|
120
|
+
override: {
|
|
121
|
+
chunks: 'async',
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
environmentOverrides.all,
|
|
127
|
+
environmentOverrides.client,
|
|
128
|
+
),
|
|
129
|
+
[RSBUILD_ENVIRONMENT_NAMES.server]: mergeRsbuildConfig(
|
|
130
|
+
{
|
|
131
|
+
source: {
|
|
132
|
+
entry: {
|
|
133
|
+
index: {
|
|
134
|
+
import: opts.entryAliases.server,
|
|
135
|
+
html: false,
|
|
136
|
+
...(opts.rsc ? { layer: RSBUILD_RSC_LAYERS.ssr } : {}),
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
output: {
|
|
141
|
+
target: 'node',
|
|
142
|
+
// Rsbuild's dev `loadBundle()` path evaluates ESM via vm.SourceTextModule,
|
|
143
|
+
// which requires `--experimental-vm-modules`. Emit CJS for the dev
|
|
144
|
+
// server bundle so SSR works without extra Node flags.
|
|
145
|
+
...(opts.dev ? { module: false } : {}),
|
|
146
|
+
distPath: {
|
|
147
|
+
root: opts.serverOutputDirectory,
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
resolve: {
|
|
151
|
+
alias,
|
|
152
|
+
},
|
|
153
|
+
...(opts.rsc
|
|
154
|
+
? {
|
|
155
|
+
splitChunks: {
|
|
156
|
+
preset: 'single-vendor',
|
|
157
|
+
},
|
|
158
|
+
}
|
|
159
|
+
: {}),
|
|
160
|
+
},
|
|
161
|
+
environmentOverrides.all,
|
|
162
|
+
environmentOverrides.server,
|
|
163
|
+
),
|
|
164
|
+
// When provider is a separate environment (not layered RSC),
|
|
165
|
+
// create a third environment. With the layered RSC setup this branch
|
|
166
|
+
// is not taken because provider maps to the same `ssr` environment.
|
|
167
|
+
...(opts.serverFnProviderEnv !== RSBUILD_ENVIRONMENT_NAMES.server &&
|
|
168
|
+
!opts.rsc
|
|
169
|
+
? {
|
|
170
|
+
[opts.serverFnProviderEnv]: mergeRsbuildConfig(
|
|
171
|
+
{
|
|
172
|
+
source: {
|
|
173
|
+
entry: {
|
|
174
|
+
index: {
|
|
175
|
+
import: opts.entryAliases.server,
|
|
176
|
+
html: false,
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
output: {
|
|
181
|
+
target: 'node',
|
|
182
|
+
...(opts.dev ? { module: false } : {}),
|
|
183
|
+
distPath: {
|
|
184
|
+
root: `${opts.serverOutputDirectory}/${opts.serverFnProviderEnv}`,
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
resolve: {
|
|
188
|
+
alias,
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
environmentOverrides.all,
|
|
192
|
+
environmentOverrides.provider,
|
|
193
|
+
),
|
|
194
|
+
}
|
|
195
|
+
: {}),
|
|
196
|
+
},
|
|
197
|
+
alias,
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function resolveRsbuildOutputDirectory(opts: {
|
|
202
|
+
distPath: RsbuildDistPath | undefined
|
|
203
|
+
rootDistPath: RsbuildDistPath | undefined
|
|
204
|
+
fallback: string
|
|
205
|
+
subdirectory: string
|
|
206
|
+
}): string {
|
|
207
|
+
if (typeof opts.distPath === 'string') {
|
|
208
|
+
return opts.distPath
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (typeof opts.distPath?.root === 'string') {
|
|
212
|
+
return opts.distPath.root
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (typeof opts.rootDistPath === 'string') {
|
|
216
|
+
return join(opts.rootDistPath, opts.subdirectory)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (typeof opts.rootDistPath?.root === 'string') {
|
|
220
|
+
return join(opts.rootDistPath.root, opts.subdirectory)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return opts.fallback
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function normalizeEntryPath(path: string) {
|
|
227
|
+
return path.replaceAll('\\', '/')
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function resolveFromRoot(specifier: string, root: string): string {
|
|
231
|
+
return require.resolve(specifier, {
|
|
232
|
+
paths: [root],
|
|
233
|
+
})
|
|
234
|
+
}
|