rsbuild-plugin-react-router 0.7.3 → 0.8.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/README.md +13 -1
- package/dist/468.js +2 -2
- package/dist/511.js +57 -38
- package/dist/constants.d.ts +1 -0
- package/dist/dev-hdr-channel.d.ts +9 -0
- package/dist/dev-hmr.d.ts +1 -22
- package/dist/dev-runtime-controller.d.ts +1 -6
- package/dist/dev-server.d.ts +3 -1
- package/dist/index.cjs +5515 -5303
- package/dist/index.js +392 -200
- package/dist/manifest-assets.d.ts +34 -0
- package/dist/manifest-snapshot.d.ts +17 -0
- package/dist/manifest-state.d.ts +12 -0
- package/dist/manifest.d.ts +2 -22
- package/dist/modify-browser-manifest.d.ts +2 -0
- package/dist/node-only-manifest.d.ts +8 -0
- package/dist/plugin-utils.d.ts +1 -1
- package/dist/types.d.ts +6 -0
- package/package.json +4 -4
- package/src/build-output-transforms.ts +5 -0
- package/src/classic-mode.ts +0 -1
- package/src/constants.ts +3 -0
- package/src/dev-hdr-channel.ts +38 -0
- package/src/dev-hmr.ts +57 -100
- package/src/dev-runtime-controller.ts +23 -18
- package/src/dev-server.ts +24 -4
- package/src/index.ts +206 -151
- package/src/lazy-compilation.ts +7 -2
- package/src/manifest-assets.ts +228 -0
- package/src/manifest-snapshot.ts +80 -0
- package/src/manifest-state.ts +71 -0
- package/src/manifest.ts +23 -161
- package/src/mode-plan.ts +12 -4
- package/src/modify-browser-manifest.ts +47 -18
- package/src/node-only-manifest.ts +52 -0
- package/src/plugin-utils.ts +6 -2
- package/src/route-chunks.ts +152 -63
- package/src/types.ts +7 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BROWSER_MANIFEST_ENTRY_NAME } from './constants.js';
|
|
2
|
+
import { getManifestAssetType, stripAssetQuery } from './manifest-assets.js';
|
|
1
3
|
import { createHash } from 'node:crypto';
|
|
2
4
|
import type { Route, PluginOptions } from './types.js';
|
|
3
5
|
import type { RsbuildPluginAPI, Rspack } from '@rsbuild/core';
|
|
@@ -30,6 +32,8 @@ type CompilationAssetWithIntegrity = {
|
|
|
30
32
|
source?: { source(): string | Buffer };
|
|
31
33
|
info?: {
|
|
32
34
|
integrity?: unknown;
|
|
35
|
+
assetType?: string;
|
|
36
|
+
javascriptModule?: boolean;
|
|
33
37
|
};
|
|
34
38
|
};
|
|
35
39
|
|
|
@@ -79,14 +83,10 @@ const toManifestAssetUrl = (assetPrefix: string, assetName: string) => {
|
|
|
79
83
|
const addIntegrity = (
|
|
80
84
|
sri: Record<string, string>,
|
|
81
85
|
assetPrefix: string,
|
|
82
|
-
assetName:
|
|
86
|
+
assetName: string,
|
|
83
87
|
integrity: unknown
|
|
84
88
|
) => {
|
|
85
|
-
if (
|
|
86
|
-
typeof assetName !== 'string' ||
|
|
87
|
-
!isManifestJsAsset(assetName) ||
|
|
88
|
-
typeof integrity !== 'string'
|
|
89
|
-
) {
|
|
89
|
+
if (typeof integrity !== 'string') {
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
92
|
sri[toManifestAssetUrl(assetPrefix, assetName)] = integrity;
|
|
@@ -111,6 +111,9 @@ export const collectSubresourceIntegrity = (
|
|
|
111
111
|
const sri: Record<string, string> = {};
|
|
112
112
|
|
|
113
113
|
for (const asset of stats?.assets ?? []) {
|
|
114
|
+
if (typeof asset.name !== 'string' || !isManifestJsAsset(asset.name)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
114
117
|
addIntegrity(sri, assetPrefix, asset.name, asset.integrity);
|
|
115
118
|
}
|
|
116
119
|
|
|
@@ -118,7 +121,17 @@ export const collectSubresourceIntegrity = (
|
|
|
118
121
|
const assets =
|
|
119
122
|
compilation.getAssets() as readonly CompilationAssetWithIntegrity[];
|
|
120
123
|
for (const asset of assets) {
|
|
121
|
-
|
|
124
|
+
const assetType =
|
|
125
|
+
asset.info?.assetType ??
|
|
126
|
+
(typeof asset.info?.javascriptModule === 'boolean'
|
|
127
|
+
? 'javascript'
|
|
128
|
+
: undefined);
|
|
129
|
+
if (
|
|
130
|
+
getManifestAssetType(
|
|
131
|
+
asset.name,
|
|
132
|
+
assetType === undefined ? undefined : { [asset.name]: assetType }
|
|
133
|
+
) !== 'javascript'
|
|
134
|
+
) {
|
|
122
135
|
continue;
|
|
123
136
|
}
|
|
124
137
|
addIntegrity(
|
|
@@ -144,13 +157,15 @@ export function registerModifyBrowserManifestAssets(
|
|
|
144
157
|
): void {
|
|
145
158
|
const getAssetPrefix =
|
|
146
159
|
typeof assetPrefix === 'function' ? assetPrefix : () => assetPrefix;
|
|
147
|
-
const manifestChunkNames =
|
|
160
|
+
const manifestChunkNames = new Set(
|
|
148
161
|
options?.manifestChunkNames ??
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
162
|
+
getReactRouterManifestChunkNames(
|
|
163
|
+
routes,
|
|
164
|
+
appDirectory,
|
|
165
|
+
routeChunkOptions?.splitRouteModules
|
|
166
|
+
)
|
|
167
|
+
);
|
|
168
|
+
manifestChunkNames.add(BROWSER_MANIFEST_ENTRY_NAME);
|
|
154
169
|
const isBuild = Boolean(routeChunkOptions?.isBuild);
|
|
155
170
|
const finalizeSri = Boolean(
|
|
156
171
|
isBuild &&
|
|
@@ -165,6 +180,7 @@ export function registerModifyBrowserManifestAssets(
|
|
|
165
180
|
{ assets, sources, compilation }: ManifestProcessAssetsContext,
|
|
166
181
|
{ withSri }: { withSri: boolean }
|
|
167
182
|
): Promise<void> => {
|
|
183
|
+
if (compilation.errors?.length) return;
|
|
168
184
|
const currentAssetPrefix = getAssetPrefix();
|
|
169
185
|
const stats = createReactRouterManifestStats(
|
|
170
186
|
compilation,
|
|
@@ -185,8 +201,17 @@ export function registerModifyBrowserManifestAssets(
|
|
|
185
201
|
const browserManifest = { ...manifest };
|
|
186
202
|
delete browserManifest.sri;
|
|
187
203
|
|
|
188
|
-
const
|
|
189
|
-
|
|
204
|
+
const browserManifestPaths = stats?.assetsByChunkName?.[
|
|
205
|
+
BROWSER_MANIFEST_ENTRY_NAME
|
|
206
|
+
]?.filter(
|
|
207
|
+
name =>
|
|
208
|
+
getManifestAssetType(name, stats.assetTypesByName) === 'javascript'
|
|
209
|
+
) ?? [BROWSER_MANIFEST_ASSET];
|
|
210
|
+
// Production consumes the separately emitted versioned manifest. Leave the
|
|
211
|
+
// placeholder chunk unchanged: its content hash has already been finalized.
|
|
212
|
+
for (const browserManifestPath of isBuild ? [] : browserManifestPaths) {
|
|
213
|
+
const browserManifestAsset = assets[browserManifestPath];
|
|
214
|
+
if (!browserManifestAsset) continue;
|
|
190
215
|
const originalSource = browserManifestAsset.source().toString();
|
|
191
216
|
const serializedManifest = jsesc(browserManifest, { es6: true });
|
|
192
217
|
const newSource = originalSource.replace(
|
|
@@ -194,18 +219,22 @@ export function registerModifyBrowserManifestAssets(
|
|
|
194
219
|
() => serializedManifest
|
|
195
220
|
);
|
|
196
221
|
compilation.updateAsset(
|
|
197
|
-
|
|
222
|
+
browserManifestPath,
|
|
198
223
|
new sources.RawSource(newSource)
|
|
199
224
|
);
|
|
200
225
|
}
|
|
201
226
|
|
|
202
227
|
if (isBuild) {
|
|
203
228
|
const entryAssets = stats?.assetsByChunkName?.['entry.client'];
|
|
204
|
-
const entryJsAssets =
|
|
229
|
+
const entryJsAssets =
|
|
230
|
+
entryAssets?.filter(
|
|
231
|
+
name =>
|
|
232
|
+
getManifestAssetType(name, stats?.assetTypesByName) === 'javascript'
|
|
233
|
+
) || [];
|
|
205
234
|
const manifestPath = getReactRouterManifestPath({
|
|
206
235
|
version: manifest.version,
|
|
207
236
|
isBuild: true,
|
|
208
|
-
entryModulePath: entryJsAssets[0],
|
|
237
|
+
entryModulePath: stripAssetQuery(entryJsAssets[0] ?? ''),
|
|
209
238
|
});
|
|
210
239
|
const manifestSource = `window.__reactRouterManifest=${jsesc(
|
|
211
240
|
browserManifest,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { rspack, type RsbuildPluginAPI, type Rspack } from '@rsbuild/core';
|
|
2
|
+
import { resolve } from 'pathe';
|
|
3
|
+
import type { ReactRouterManifestSnapshot } from './manifest-snapshot.js';
|
|
4
|
+
import type { Route } from './types.js';
|
|
5
|
+
|
|
6
|
+
// Inspect the compiled graph rather than parsing source: loaders, re-exports,
|
|
7
|
+
// and cached modules must all participate in the compatibility check.
|
|
8
|
+
export const registerNodeOnlyManifestValidation = ({
|
|
9
|
+
api,
|
|
10
|
+
routeByFilePath,
|
|
11
|
+
getSnapshot,
|
|
12
|
+
}: {
|
|
13
|
+
api: RsbuildPluginAPI;
|
|
14
|
+
routeByFilePath: ReadonlyMap<string, Route>;
|
|
15
|
+
getSnapshot: () => ReactRouterManifestSnapshot | null;
|
|
16
|
+
}): void => {
|
|
17
|
+
api.modifyRspackConfig((config, { environment }) => {
|
|
18
|
+
if (environment.name !== 'node') return;
|
|
19
|
+
config.plugins ??= [];
|
|
20
|
+
config.plugins.push({
|
|
21
|
+
apply(compiler: Rspack.Compiler) {
|
|
22
|
+
const name = 'ReactRouterNodeOnlyManifestValidation';
|
|
23
|
+
compiler.hooks.thisCompilation.tap(name, compilation => {
|
|
24
|
+
compilation.hooks.afterOptimizeModules.tap(name, modules => {
|
|
25
|
+
const snapshot = getSnapshot();
|
|
26
|
+
if (!snapshot) return;
|
|
27
|
+
for (const module of modules) {
|
|
28
|
+
if (!(module instanceof rspack.NormalModule)) continue;
|
|
29
|
+
const route = routeByFilePath.get(
|
|
30
|
+
resolve(module.resource.split('?')[0])
|
|
31
|
+
);
|
|
32
|
+
if (!route) continue;
|
|
33
|
+
const exports =
|
|
34
|
+
compilation.moduleGraph.getProvidedExports(module);
|
|
35
|
+
const saved = snapshot.browser.routes[route.id];
|
|
36
|
+
if (
|
|
37
|
+
!Array.isArray(exports) ||
|
|
38
|
+
!saved ||
|
|
39
|
+
exports.includes('loader') !== saved.hasLoader ||
|
|
40
|
+
exports.includes('action') !== saved.hasAction
|
|
41
|
+
) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Run a full build before building only the node environment: loader/action exports changed for route "${route.id}".`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
};
|
package/src/plugin-utils.ts
CHANGED
|
@@ -6,10 +6,14 @@ import { JS_EXTENSIONS } from './constants.js';
|
|
|
6
6
|
const requireFromApp = createRequire(resolve(process.cwd(), 'package.json'));
|
|
7
7
|
|
|
8
8
|
export const resolveAppPackagePath = (
|
|
9
|
-
specifier: string
|
|
9
|
+
specifier: string,
|
|
10
|
+
rootPath?: string
|
|
10
11
|
): string | undefined => {
|
|
11
12
|
try {
|
|
12
|
-
|
|
13
|
+
const require = rootPath
|
|
14
|
+
? createRequire(resolve(rootPath, 'package.json'))
|
|
15
|
+
: requireFromApp;
|
|
16
|
+
return require.resolve(specifier);
|
|
13
17
|
} catch {
|
|
14
18
|
return undefined;
|
|
15
19
|
}
|
package/src/route-chunks.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Analyzer,
|
|
3
|
+
SymbolFlags,
|
|
4
|
+
type Export as YukuExport,
|
|
3
5
|
type Module,
|
|
6
|
+
type Reference as YukuReference,
|
|
4
7
|
type Symbol as YukuSymbol,
|
|
5
8
|
} from 'yuku-analyzer';
|
|
6
9
|
import { print } from 'yuku-codegen';
|
|
7
|
-
import { walk } from 'yuku-parser';
|
|
10
|
+
import { walk, type Node } from 'yuku-parser';
|
|
8
11
|
import { dirname, normalize, relative, resolve } from 'pathe';
|
|
9
12
|
import { SERVER_ONLY_ROUTE_EXPORTS_SET } from './constants.js';
|
|
10
13
|
import { createRouteId } from './plugin-utils.js';
|
|
@@ -146,6 +149,7 @@ type ExportDependencies = {
|
|
|
146
149
|
importedIdentifierNames: Set<string>;
|
|
147
150
|
importSources: Set<string>;
|
|
148
151
|
exportedVariableDeclarators: Set<AnyNode>;
|
|
152
|
+
exportedLocalSymbols: Set<YukuSymbol>;
|
|
149
153
|
};
|
|
150
154
|
|
|
151
155
|
const getTopLevelStatementForNode = (
|
|
@@ -162,30 +166,30 @@ const getTopLevelStatementForNode = (
|
|
|
162
166
|
return current;
|
|
163
167
|
};
|
|
164
168
|
|
|
165
|
-
const
|
|
169
|
+
const getExportedVariableDeclaratorForNode = (
|
|
166
170
|
module: Module,
|
|
167
171
|
node: AnyNode
|
|
168
172
|
): AnyNode | null => {
|
|
169
|
-
let current
|
|
170
|
-
while (
|
|
171
|
-
|
|
172
|
-
|
|
173
|
+
let current = node as Node;
|
|
174
|
+
while (true) {
|
|
175
|
+
const parent = module.parentOf(current);
|
|
176
|
+
if (!parent || parent.type === 'Program') {
|
|
177
|
+
return null;
|
|
173
178
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
179
|
+
if (
|
|
180
|
+
current.type === 'VariableDeclarator' &&
|
|
181
|
+
parent.type === 'VariableDeclaration'
|
|
182
|
+
) {
|
|
183
|
+
const exported = module.parentOf(parent);
|
|
184
|
+
if (
|
|
185
|
+
exported?.type === 'ExportNamedDeclaration' &&
|
|
186
|
+
module.parentOf(exported)?.type === 'Program'
|
|
187
|
+
) {
|
|
188
|
+
return current;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
current = parent;
|
|
186
192
|
}
|
|
187
|
-
const statement = module.parentOf(declaration as never) as AnyNode | null;
|
|
188
|
-
return statement?.type === 'ExportNamedDeclaration';
|
|
189
193
|
};
|
|
190
194
|
|
|
191
195
|
const getExportedName = (exported: AnyNode): string => {
|
|
@@ -241,9 +245,29 @@ const getExportDependencies = (
|
|
|
241
245
|
code,
|
|
242
246
|
() => {
|
|
243
247
|
const { module } = analyzeCode(code, cache, cacheKey);
|
|
248
|
+
const namedExports = module.exports.filter(
|
|
249
|
+
(exp): exp is YukuExport & { name: string } =>
|
|
250
|
+
exp.name !== null &&
|
|
251
|
+
!exp.typeOnly &&
|
|
252
|
+
!exp.isStar &&
|
|
253
|
+
!exp.isExportEquals
|
|
254
|
+
);
|
|
255
|
+
// Removing type declarations can change legacy decorator metadata and
|
|
256
|
+
// name hygiene. Preserve the original dependency graph for decorated
|
|
257
|
+
// modules because the downstream compiler options are not known here.
|
|
258
|
+
let hasDecorators = false;
|
|
259
|
+
walk(module.ast, {
|
|
260
|
+
Decorator(_node, context) {
|
|
261
|
+
hasDecorators = true;
|
|
262
|
+
context.stop();
|
|
263
|
+
},
|
|
264
|
+
});
|
|
244
265
|
const exportDependencies = new Map<string, ExportDependencies>();
|
|
245
266
|
const topLevelStatementCache = new Map<AnyNode, AnyNode>();
|
|
246
|
-
const
|
|
267
|
+
const exportedVariableDeclaratorCache = new Map<
|
|
268
|
+
AnyNode,
|
|
269
|
+
AnyNode | null
|
|
270
|
+
>();
|
|
247
271
|
const getCachedTopLevelStatementForNode = (node: AnyNode): AnyNode => {
|
|
248
272
|
const cached = topLevelStatementCache.get(node);
|
|
249
273
|
if (cached) {
|
|
@@ -254,14 +278,67 @@ const getExportDependencies = (
|
|
|
254
278
|
return statement;
|
|
255
279
|
};
|
|
256
280
|
|
|
257
|
-
|
|
281
|
+
// Ordinary imports can be repeated in multiple chunks. Exported local
|
|
282
|
+
// bindings must keep a single owner, including functions and classes.
|
|
283
|
+
const nonShareableExportedSymbols = new Set<YukuSymbol>();
|
|
284
|
+
for (const { local } of namedExports) {
|
|
285
|
+
if (!local?.has(SymbolFlags.ValueSpace | SymbolFlags.ValueImport)) {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
const isImport = local.declarations.every(
|
|
289
|
+
declaration =>
|
|
290
|
+
getCachedTopLevelStatementForNode(declaration).type ===
|
|
291
|
+
'ImportDeclaration'
|
|
292
|
+
);
|
|
293
|
+
// Setup belongs to the imported value too. If another export consumes
|
|
294
|
+
// that value, moving its setup into a separate chunk changes behavior.
|
|
295
|
+
const hasSetup =
|
|
296
|
+
isImport &&
|
|
297
|
+
local.references.some(reference => {
|
|
298
|
+
const statement = getCachedTopLevelStatementForNode(reference.node);
|
|
299
|
+
return (
|
|
300
|
+
reference.kind === 'value' &&
|
|
301
|
+
statement.type !== 'ImportDeclaration' &&
|
|
302
|
+
!statement.type.startsWith('Export')
|
|
303
|
+
);
|
|
304
|
+
});
|
|
305
|
+
if (isImport && !hasSetup) continue;
|
|
306
|
+
nonShareableExportedSymbols.add(local);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const isValueImportEqualsReference = (
|
|
310
|
+
reference: YukuReference
|
|
311
|
+
): boolean => {
|
|
312
|
+
let node: Node = reference.node;
|
|
313
|
+
let parent = module.parentOf(node);
|
|
314
|
+
while (parent?.type === 'TSQualifiedName') {
|
|
315
|
+
node = parent;
|
|
316
|
+
parent = module.parentOf(node);
|
|
317
|
+
}
|
|
318
|
+
// Yuku also marks the runtime RHS of `import x = Namespace.value`
|
|
319
|
+
// as a type reference.
|
|
320
|
+
return (
|
|
321
|
+
parent?.type === 'TSImportEqualsDeclaration' &&
|
|
322
|
+
parent.moduleReference === node &&
|
|
323
|
+
parent.importKind !== 'type'
|
|
324
|
+
);
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const isRuntimeRelevantReference = (reference: YukuReference): boolean =>
|
|
328
|
+
hasDecorators ||
|
|
329
|
+
reference.kind === 'value' ||
|
|
330
|
+
isValueImportEqualsReference(reference);
|
|
331
|
+
|
|
332
|
+
const getCachedExportedVariableDeclaratorForNode = (
|
|
258
333
|
node: AnyNode
|
|
259
334
|
): AnyNode | null => {
|
|
260
|
-
if (
|
|
261
|
-
return
|
|
335
|
+
if (exportedVariableDeclaratorCache.has(node)) {
|
|
336
|
+
return exportedVariableDeclaratorCache.get(node) ?? null;
|
|
262
337
|
}
|
|
263
|
-
|
|
264
|
-
|
|
338
|
+
// Only direct exported declarators can be emitted independently.
|
|
339
|
+
// Every other top-level statement is moved as a whole.
|
|
340
|
+
const declarator = getExportedVariableDeclaratorForNode(module, node);
|
|
341
|
+
exportedVariableDeclaratorCache.set(node, declarator);
|
|
265
342
|
return declarator;
|
|
266
343
|
};
|
|
267
344
|
|
|
@@ -291,22 +368,41 @@ const getExportDependencies = (
|
|
|
291
368
|
importedIdentifierNames: new Set(),
|
|
292
369
|
importSources: new Set(),
|
|
293
370
|
exportedVariableDeclarators: new Set(),
|
|
371
|
+
exportedLocalSymbols: new Set(),
|
|
294
372
|
};
|
|
295
373
|
const visitedSymbols = new Set<YukuSymbol>();
|
|
296
374
|
const scannedNodes = new Set<AnyNode>();
|
|
297
375
|
|
|
376
|
+
const visitIdentifier = (node: YukuReference['node']) => {
|
|
377
|
+
const reference = module.referenceOf(node);
|
|
378
|
+
if (reference) {
|
|
379
|
+
if (reference.symbol && isRuntimeRelevantReference(reference)) {
|
|
380
|
+
visitSymbol(reference.symbol);
|
|
381
|
+
}
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const symbol = module.symbolOf(node);
|
|
385
|
+
if (
|
|
386
|
+
symbol?.scope === module.rootScope &&
|
|
387
|
+
symbol.has(SymbolFlags.ValueSpace | SymbolFlags.ValueImport) &&
|
|
388
|
+
dependencies.topLevelNonModuleStatements.has(
|
|
389
|
+
getCachedTopLevelStatementForNode(node)
|
|
390
|
+
)
|
|
391
|
+
) {
|
|
392
|
+
// Moving a statement also moves the bindings it declares. Follow
|
|
393
|
+
// their consumers so no references remain in another chunk.
|
|
394
|
+
visitSymbol(symbol);
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
|
|
298
398
|
const scanNode = (node: AnyNode) => {
|
|
299
399
|
if (scannedNodes.has(node)) {
|
|
300
400
|
return;
|
|
301
401
|
}
|
|
302
402
|
scannedNodes.add(node);
|
|
303
403
|
walk(node as any, {
|
|
304
|
-
Identifier
|
|
305
|
-
|
|
306
|
-
if (reference?.symbol) {
|
|
307
|
-
visitSymbol(reference.symbol);
|
|
308
|
-
}
|
|
309
|
-
},
|
|
404
|
+
Identifier: visitIdentifier,
|
|
405
|
+
JSXIdentifier: visitIdentifier,
|
|
310
406
|
});
|
|
311
407
|
};
|
|
312
408
|
|
|
@@ -318,6 +414,9 @@ const getExportDependencies = (
|
|
|
318
414
|
if (symbol.declarations.length === 0) {
|
|
319
415
|
return;
|
|
320
416
|
}
|
|
417
|
+
if (nonShareableExportedSymbols.has(symbol)) {
|
|
418
|
+
dependencies.exportedLocalSymbols.add(symbol);
|
|
419
|
+
}
|
|
321
420
|
|
|
322
421
|
for (const declaration of symbol.declarations as AnyNode[]) {
|
|
323
422
|
const statement = addCachedTopLevelStatement(
|
|
@@ -329,24 +428,27 @@ const getExportDependencies = (
|
|
|
329
428
|
if (typeof statement.source?.value === 'string') {
|
|
330
429
|
dependencies.importSources.add(statement.source.value);
|
|
331
430
|
}
|
|
332
|
-
|
|
431
|
+
// Ordinary imports are shareable; a directly exported import
|
|
432
|
+
// also owns setup statements such as `load.hydrate = true`.
|
|
433
|
+
if (symbol !== localSymbol) return;
|
|
333
434
|
}
|
|
334
|
-
const declarator =
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
isTopLevelExportedVariableDeclarator(module, declarator)
|
|
338
|
-
) {
|
|
435
|
+
const declarator =
|
|
436
|
+
getCachedExportedVariableDeclaratorForNode(declaration);
|
|
437
|
+
if (declarator) {
|
|
339
438
|
dependencies.exportedVariableDeclarators.add(declarator);
|
|
340
439
|
}
|
|
341
440
|
scanNode(declarator ?? statement);
|
|
342
441
|
}
|
|
343
442
|
|
|
344
|
-
for (const reference of symbol.references
|
|
443
|
+
for (const reference of symbol.references) {
|
|
444
|
+
if (!isRuntimeRelevantReference(reference)) {
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
345
447
|
const statement = addCachedTopLevelStatement(
|
|
346
448
|
dependencies,
|
|
347
449
|
reference.node
|
|
348
450
|
);
|
|
349
|
-
const declarator =
|
|
451
|
+
const declarator = getCachedExportedVariableDeclaratorForNode(
|
|
350
452
|
reference.node
|
|
351
453
|
);
|
|
352
454
|
scanNode(declarator ?? statement);
|
|
@@ -365,10 +467,7 @@ const getExportDependencies = (
|
|
|
365
467
|
exportDependencies.set(exportName, dependencies);
|
|
366
468
|
};
|
|
367
469
|
|
|
368
|
-
for (const exp of
|
|
369
|
-
if (exp.typeOnly || exp.isStar || exp.isExportEquals) {
|
|
370
|
-
continue;
|
|
371
|
-
}
|
|
470
|
+
for (const exp of namedExports) {
|
|
372
471
|
handleExport(exp.name, exp.node as AnyNode, exp.local ?? null);
|
|
373
472
|
}
|
|
374
473
|
|
|
@@ -383,7 +482,7 @@ const isExportChunkable = (
|
|
|
383
482
|
importer: string
|
|
384
483
|
) => {
|
|
385
484
|
const dependencies = exportDependencies.get(exportName);
|
|
386
|
-
if (!dependencies) {
|
|
485
|
+
if (!dependencies || dependencies.exportedVariableDeclarators.size > 1) {
|
|
387
486
|
return false;
|
|
388
487
|
}
|
|
389
488
|
if (exportName === 'clientLoader' && hasHydrateAssignment(dependencies)) {
|
|
@@ -403,29 +502,19 @@ const isExportChunkable = (
|
|
|
403
502
|
setsIntersect(
|
|
404
503
|
currentDependencies.topLevelNonModuleStatements,
|
|
405
504
|
dependencies.topLevelNonModuleStatements
|
|
505
|
+
) ||
|
|
506
|
+
setsIntersect(
|
|
507
|
+
currentDependencies.exportedVariableDeclarators,
|
|
508
|
+
dependencies.exportedVariableDeclarators
|
|
509
|
+
) ||
|
|
510
|
+
setsIntersect(
|
|
511
|
+
currentDependencies.exportedLocalSymbols,
|
|
512
|
+
dependencies.exportedLocalSymbols
|
|
406
513
|
)
|
|
407
514
|
) {
|
|
408
515
|
return false;
|
|
409
516
|
}
|
|
410
517
|
}
|
|
411
|
-
if (dependencies.exportedVariableDeclarators.size > 1) {
|
|
412
|
-
return false;
|
|
413
|
-
}
|
|
414
|
-
if (dependencies.exportedVariableDeclarators.size > 0) {
|
|
415
|
-
for (const [currentExportName, currentDependencies] of exportDependencies) {
|
|
416
|
-
if (currentExportName === exportName) {
|
|
417
|
-
continue;
|
|
418
|
-
}
|
|
419
|
-
if (
|
|
420
|
-
setsIntersect(
|
|
421
|
-
currentDependencies.exportedVariableDeclarators,
|
|
422
|
-
dependencies.exportedVariableDeclarators
|
|
423
|
-
)
|
|
424
|
-
) {
|
|
425
|
-
return false;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
518
|
return true;
|
|
430
519
|
};
|
|
431
520
|
|
package/src/types.ts
CHANGED
|
@@ -11,6 +11,13 @@ export type Route = {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
export type PluginOptions = {
|
|
14
|
+
/**
|
|
15
|
+
* Generate React Router route types during development and builds.
|
|
16
|
+
* Set to false when type generation is managed separately.
|
|
17
|
+
* @default true
|
|
18
|
+
*/
|
|
19
|
+
typegen?: boolean;
|
|
20
|
+
|
|
14
21
|
/**
|
|
15
22
|
* Whether to disable automatic middleware setup for custom server implementation.
|
|
16
23
|
* Use this when you want to handle server setup manually.
|