@modern-js/app-tools 3.2.2 → 3.4.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.
@@ -36,18 +36,27 @@ const utils_namespaceObject = require("@modern-js/utils");
36
36
  const core_namespaceObject = require("@rsbuild/core");
37
37
  const utils_js_namespaceObject = require("../../../plugins/analyze/utils.js");
38
38
  const index_js_namespaceObject = require("../bundlerPlugins/index.js");
39
+ const external_lazyCompilation_js_namespaceObject = require("../lazyCompilation.js");
39
40
  const builderPluginAdapterSSR = (options)=>({
40
41
  name: 'builder-plugin-adapter-modern-ssr',
41
42
  setup (api) {
42
- const { normalizedConfig } = options;
43
- api.modifyRsbuildConfig((config)=>(0, core_namespaceObject.mergeRsbuildConfig)(config, {
43
+ const { normalizedConfig, appContext, eagerRouteComponentFilesByEntry } = options;
44
+ api.modifyRsbuildConfig((config)=>{
45
+ const merged = (0, core_namespaceObject.mergeRsbuildConfig)(config, {
44
46
  html: {
45
47
  inject: isStreamingSSR(normalizedConfig) ? 'head' : void 0
46
48
  },
47
49
  server: {
48
50
  compress: isStreamingSSR(normalizedConfig) || (0, utils_namespaceObject.isUseRsc)(normalizedConfig) ? false : void 0
49
51
  }
50
- }));
52
+ });
53
+ const lazyCompilation = getSSRLazyCompilation(merged.dev?.lazyCompilation, normalizedConfig, appContext, eagerRouteComponentFilesByEntry);
54
+ if (void 0 !== lazyCompilation) merged.dev = {
55
+ ...merged.dev,
56
+ lazyCompilation
57
+ };
58
+ return merged;
59
+ });
51
60
  api.modifyBundlerChain(async (chain, { target, isProd, HtmlPlugin: HtmlBundlerPlugin, isServer, environment })=>{
52
61
  const builderConfig = environment.config;
53
62
  const { normalizedConfig } = options;
@@ -83,6 +92,22 @@ const isStreamingSSR = (userConfig)=>{
83
92
  }
84
93
  return false;
85
94
  };
95
+ function getSSRLazyCompilation(current, normalizedConfig, appContext, eagerRouteComponentFilesByEntry) {
96
+ if (!current || (0, utils_namespaceObject.isUseRsc)(normalizedConfig) || !isStreamingSSR(normalizedConfig)) return;
97
+ const plan = (0, external_lazyCompilation_js_namespaceObject.planSSRLazyCompilation)(current, (0, external_lazyCompilation_js_namespaceObject.aggregateEagerRouteComponentFiles)(eagerRouteComponentFilesByEntry));
98
+ if (!plan.apply) {
99
+ if (plan.unresolvedByEntry) warnUnresolvedRouteComponents(appContext.appDirectory, plan.unresolvedByEntry);
100
+ return;
101
+ }
102
+ return plan.lazyCompilation;
103
+ }
104
+ const warnedLazyApps = new Set();
105
+ function warnUnresolvedRouteComponents(appDirectory, unresolvedByEntry) {
106
+ if (warnedLazyApps.has(appDirectory)) return;
107
+ warnedLazyApps.add(appDirectory);
108
+ const detail = Array.from(unresolvedByEntry).map(([entry, comps])=>`${entry}: ${comps.join(', ')}`).join('; ');
109
+ utils_namespaceObject.logger.warn(`[lazyCompilation] Skipped stream SSR route-eager optimization because some route components could not be resolved to a file (${detail}). Lazy compilation may break first-screen CSS/JS for these routes.`);
110
+ }
86
111
  function applyAsyncChunkHtmlPlugin({ chain, modernConfig, HtmlBundlerPlugin }) {
87
112
  if (isStreamingSSR(modernConfig) || (0, utils_namespaceObject.isUseRsc)(modernConfig)) chain.plugin('html-async-chunk').use(index_js_namespaceObject.HtmlAsyncChunkPlugin, [
88
113
  HtmlBundlerPlugin
@@ -102,7 +127,8 @@ function applyRouterPlugin(chain, pluginName, options, HtmlBundlerPlugin) {
102
127
  staticJsDir: normalizedConfig.output?.distPath?.js,
103
128
  disableFilenameHash: normalizedConfig.output?.filenameHash === false,
104
129
  scriptLoading: normalizedConfig.html?.scriptLoading,
105
- nonce: normalizedConfig.security?.nonce
130
+ nonce: normalizedConfig.security?.nonce,
131
+ useRsc: (0, utils_namespaceObject.isUseRsc)(normalizedConfig)
106
132
  }
107
133
  ]);
108
134
  }
@@ -96,6 +96,25 @@ class RouterPlugin {
96
96
  routeAssets: {}
97
97
  });
98
98
  const prevManifest = JSON.parse(prevManifestStr);
99
+ const namedChunkGroupInstances = new Map();
100
+ for (const cg of compilation.chunkGroups || [])if (cg.name) namedChunkGroupInstances.set(cg.name, cg);
101
+ const collectDescendantCssAssets = (name)=>{
102
+ const root = namedChunkGroupInstances.get(name);
103
+ if (!root) return [];
104
+ const cssFiles = new Set();
105
+ const visited = new Set();
106
+ const stack = [
107
+ ...root.childrenIterable
108
+ ];
109
+ while(stack.length){
110
+ const child = stack.pop();
111
+ if (visited.has(child)) continue;
112
+ visited.add(child);
113
+ for (const chunk of child.chunks)for (const file of chunk.files)if (/\.css$/.test(file)) cssFiles.add(publicPath ? normalizePath(publicPath) + file : file);
114
+ for (const c of child.childrenIterable)stack.push(c);
115
+ }
116
+ return Array.from(cssFiles);
117
+ };
99
118
  const asyncEntryNames = [];
100
119
  for (const [name, chunkGroup] of Object.entries(namedChunkGroups)){
101
120
  if (name.startsWith('async-')) asyncEntryNames.push(name);
@@ -103,7 +122,12 @@ class RouterPlugin {
103
122
  const filename = asset.name;
104
123
  return publicPath ? normalizePath(publicPath) + filename : filename;
105
124
  });
106
- const referenceCssAssets = assets.filter((asset)=>/\.css$/.test(asset));
125
+ const directCssAssets = assets.filter((asset)=>/\.css$/.test(asset));
126
+ const descendantCssAssets = collectDescendantCssAssets(name).filter((asset)=>!directCssAssets.includes(asset));
127
+ const referenceCssAssets = [
128
+ ...directCssAssets,
129
+ ...descendantCssAssets
130
+ ];
107
131
  routeAssets[name] = {
108
132
  chunkIds: chunkGroup.chunks,
109
133
  assets,
@@ -145,10 +169,14 @@ class RouterPlugin {
145
169
  const manifest = {
146
170
  routeAssets: relatedAssets
147
171
  };
172
+ const { useRsc } = this;
148
173
  const injectedContent = `
149
174
  ;(function(){
150
175
  window.${constants_namespaceObject.ROUTE_MANIFEST} = ${JSON.stringify(manifest, (k, v)=>{
151
- if (('assets' === k || 'referenceCssAssets' === k) && Array.isArray(v)) return v.map((item)=>item.replace(publicPath, ''));
176
+ if (('assets' === k || 'referenceCssAssets' === k) && Array.isArray(v)) {
177
+ if (!useRsc) return;
178
+ return v.map((item)=>item.replace(publicPath, ''));
179
+ }
152
180
  return v;
153
181
  })};
154
182
  })();
@@ -176,7 +204,7 @@ class RouterPlugin {
176
204
  });
177
205
  });
178
206
  }
179
- constructor({ staticJsDir = 'static/js', HtmlBundlerPlugin, enableInlineRouteManifests, disableFilenameHash = false, scriptLoading = 'defer', nonce }){
207
+ constructor({ staticJsDir = 'static/js', HtmlBundlerPlugin, enableInlineRouteManifests, disableFilenameHash = false, scriptLoading = 'defer', nonce, useRsc = false }){
180
208
  this.name = 'RouterPlugin';
181
209
  this.HtmlBundlerPlugin = HtmlBundlerPlugin;
182
210
  this.enableInlineRouteManifests = enableInlineRouteManifests;
@@ -184,6 +212,7 @@ class RouterPlugin {
184
212
  this.disableFilenameHash = disableFilenameHash;
185
213
  this.scriptLoading = scriptLoading;
186
214
  this.nonce = nonce;
215
+ this.useRsc = useRsc;
187
216
  }
188
217
  }
189
218
  exports.RouterPlugin = __webpack_exports__.RouterPlugin;
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, getters, values)=>{
5
+ var define = (defs, kind)=>{
6
+ for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
7
+ enumerable: true,
8
+ [kind]: defs[key]
9
+ });
10
+ };
11
+ define(getters, "get");
12
+ define(values, "value");
13
+ };
14
+ })();
15
+ (()=>{
16
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
17
+ })();
18
+ (()=>{
19
+ __webpack_require__.r = (exports1)=>{
20
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
21
+ value: 'Module'
22
+ });
23
+ Object.defineProperty(exports1, '__esModule', {
24
+ value: true
25
+ });
26
+ };
27
+ })();
28
+ var __webpack_exports__ = {};
29
+ __webpack_require__.r(__webpack_exports__);
30
+ __webpack_require__.d(__webpack_exports__, {
31
+ aggregateEagerRouteComponentFiles: ()=>aggregateEagerRouteComponentFiles,
32
+ buildSSRLazyCompilationTest: ()=>buildSSRLazyCompilationTest,
33
+ collectRouteComponentFiles: ()=>utils_namespaceObject.collectRouteComponentFiles,
34
+ normalizeModulePath: ()=>utils_namespaceObject.normalizeModulePath,
35
+ planSSRLazyCompilation: ()=>planSSRLazyCompilation
36
+ });
37
+ const utils_namespaceObject = require("@modern-js/utils");
38
+ function aggregateEagerRouteComponentFiles(byEntry) {
39
+ const files = new Set();
40
+ const unresolvedByEntry = new Map();
41
+ if (byEntry) for (const [entryName, collection] of byEntry){
42
+ for (const file of collection.resolvedFiles)files.add(file);
43
+ if (collection.unresolvedSpecifiers.length > 0) unresolvedByEntry.set(entryName, collection.unresolvedSpecifiers);
44
+ }
45
+ return {
46
+ files,
47
+ unresolvedByEntry
48
+ };
49
+ }
50
+ function buildSSRLazyCompilationTest(eagerRouteFiles, userTest) {
51
+ const userTestFn = 'function' == typeof userTest ? userTest : userTest instanceof RegExp ? (m)=>userTest.test(m.resource || '') : ()=>true;
52
+ return (m)=>{
53
+ const resource = m.resource;
54
+ if (!resource) return userTestFn(m);
55
+ if (eagerRouteFiles.has((0, utils_namespaceObject.normalizeModulePath)(resource))) return false;
56
+ return userTestFn(m);
57
+ };
58
+ }
59
+ function planSSRLazyCompilation(current, info) {
60
+ if (!current) return {
61
+ apply: false
62
+ };
63
+ if (info.unresolvedByEntry.size > 0) return {
64
+ apply: false,
65
+ unresolvedByEntry: info.unresolvedByEntry
66
+ };
67
+ if (0 === info.files.size) return {
68
+ apply: false
69
+ };
70
+ const base = 'object' == typeof current ? current : {};
71
+ const userTest = current.test;
72
+ return {
73
+ apply: true,
74
+ lazyCompilation: {
75
+ ...base,
76
+ test: buildSSRLazyCompilationTest(info.files, userTest)
77
+ }
78
+ };
79
+ }
80
+ exports.aggregateEagerRouteComponentFiles = __webpack_exports__.aggregateEagerRouteComponentFiles;
81
+ exports.buildSSRLazyCompilationTest = __webpack_exports__.buildSSRLazyCompilationTest;
82
+ exports.collectRouteComponentFiles = __webpack_exports__.collectRouteComponentFiles;
83
+ exports.normalizeModulePath = __webpack_exports__.normalizeModulePath;
84
+ exports.planSSRLazyCompilation = __webpack_exports__.planSSRLazyCompilation;
85
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
86
+ "aggregateEagerRouteComponentFiles",
87
+ "buildSSRLazyCompilationTest",
88
+ "collectRouteComponentFiles",
89
+ "normalizeModulePath",
90
+ "planSSRLazyCompilation"
91
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
92
+ Object.defineProperty(exports, '__esModule', {
93
+ value: true
94
+ });
@@ -28,7 +28,8 @@ var __webpack_require__ = {};
28
28
  var __webpack_exports__ = {};
29
29
  __webpack_require__.r(__webpack_exports__);
30
30
  __webpack_require__.d(__webpack_exports__, {
31
- createDefaultConfig: ()=>createDefaultConfig
31
+ createDefaultConfig: ()=>createDefaultConfig,
32
+ isLazyCompilationSafeByDefault: ()=>isLazyCompilationSafeByDefault
32
33
  });
33
34
  const utils_namespaceObject = require("@modern-js/utils");
34
35
  const env_js_namespaceObject = require("../utils/env.js");
@@ -120,9 +121,24 @@ function createDefaultConfig(appContext) {
120
121
  builderPlugins: []
121
122
  };
122
123
  }
124
+ const isStreamSSRConfig = (ssr)=>{
125
+ if (!ssr) return false;
126
+ if ('boolean' == typeof ssr) return ssr;
127
+ return 'string' !== ssr.mode;
128
+ };
129
+ function isLazyCompilationSafeByDefault(userConfig) {
130
+ const { server, output } = userConfig;
131
+ if (output?.ssg || output?.ssgByEntries && Object.keys(output.ssgByEntries).length > 0) return false;
132
+ if (server?.rsc) return false;
133
+ if (server?.ssr && !isStreamSSRConfig(server.ssr)) return false;
134
+ if (server?.ssrByEntries && 'object' == typeof server.ssrByEntries && Object.values(server.ssrByEntries).some((ssr)=>Boolean(ssr) && !isStreamSSRConfig(ssr))) return false;
135
+ return true;
136
+ }
123
137
  exports.createDefaultConfig = __webpack_exports__.createDefaultConfig;
138
+ exports.isLazyCompilationSafeByDefault = __webpack_exports__.isLazyCompilationSafeByDefault;
124
139
  for(var __rspack_i in __webpack_exports__)if (-1 === [
125
- "createDefaultConfig"
140
+ "createDefaultConfig",
141
+ "isLazyCompilationSafeByDefault"
126
142
  ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
127
143
  Object.defineProperty(exports, '__esModule', {
128
144
  value: true
@@ -154,10 +154,12 @@ const analyze = ()=>({
154
154
  entrypoints
155
155
  });
156
156
  const normalizedConfig = api.getNormalizedConfig();
157
+ const { eagerRouteComponentFilesByEntry } = api.getAppContext();
157
158
  const createBuilderForModern = await (0, index_js_namespaceObject.createBuilderGenerator)();
158
159
  const builder = await createBuilderForModern({
159
160
  normalizedConfig: normalizedConfig,
160
- appContext: appContext
161
+ appContext: appContext,
162
+ eagerRouteComponentFilesByEntry
161
163
  });
162
164
  builder.onBeforeBuild(async ({ bundlerConfigs, isFirstCompile, environments, isWatch })=>{
163
165
  if (!isFirstCompile) return;
@@ -43,7 +43,16 @@ const initialize = ()=>({
43
43
  setup (api) {
44
44
  api.config(()=>{
45
45
  const appContext = api.getAppContext();
46
- return (0, index_js_namespaceObject.createDefaultConfig)(appContext);
46
+ const userConfig = api.getConfig();
47
+ const defaultConfig = (0, index_js_namespaceObject.createDefaultConfig)(appContext);
48
+ if (userConfig.dev?.lazyCompilation === void 0 && (0, index_js_namespaceObject.isLazyCompilationSafeByDefault)(userConfig)) defaultConfig.dev = {
49
+ ...defaultConfig.dev,
50
+ lazyCompilation: {
51
+ imports: true,
52
+ entries: false
53
+ }
54
+ };
55
+ return defaultConfig;
47
56
  });
48
57
  api.modifyResolvedConfig(async (resolved)=>{
49
58
  let appContext = api.getAppContext();
@@ -1,21 +1,30 @@
1
1
  import { SERVICE_WORKER_ENVIRONMENT_NAME, isHtmlDisabled } from "@modern-js/builder";
2
- import { fs, isUseRsc, isUseSSRBundle } from "@modern-js/utils";
2
+ import { fs, isUseRsc, isUseSSRBundle, logger } from "@modern-js/utils";
3
3
  import { mergeRsbuildConfig } from "@rsbuild/core";
4
4
  import { getServerCombinedModuleFile } from "../../../plugins/analyze/utils.mjs";
5
5
  import { HtmlAsyncChunkPlugin, RouterPlugin } from "../bundlerPlugins/index.mjs";
6
+ import { aggregateEagerRouteComponentFiles, planSSRLazyCompilation } from "../lazyCompilation.mjs";
6
7
  import * as __rspack_external_path from "path";
7
8
  const builderPluginAdapterSSR = (options)=>({
8
9
  name: 'builder-plugin-adapter-modern-ssr',
9
10
  setup (api) {
10
- const { normalizedConfig } = options;
11
- api.modifyRsbuildConfig((config)=>mergeRsbuildConfig(config, {
11
+ const { normalizedConfig, appContext, eagerRouteComponentFilesByEntry } = options;
12
+ api.modifyRsbuildConfig((config)=>{
13
+ const merged = mergeRsbuildConfig(config, {
12
14
  html: {
13
15
  inject: isStreamingSSR(normalizedConfig) ? 'head' : void 0
14
16
  },
15
17
  server: {
16
18
  compress: isStreamingSSR(normalizedConfig) || isUseRsc(normalizedConfig) ? false : void 0
17
19
  }
18
- }));
20
+ });
21
+ const lazyCompilation = getSSRLazyCompilation(merged.dev?.lazyCompilation, normalizedConfig, appContext, eagerRouteComponentFilesByEntry);
22
+ if (void 0 !== lazyCompilation) merged.dev = {
23
+ ...merged.dev,
24
+ lazyCompilation
25
+ };
26
+ return merged;
27
+ });
19
28
  api.modifyBundlerChain(async (chain, { target, isProd, HtmlPlugin: HtmlBundlerPlugin, isServer, environment })=>{
20
29
  const builderConfig = environment.config;
21
30
  const { normalizedConfig } = options;
@@ -51,6 +60,22 @@ const isStreamingSSR = (userConfig)=>{
51
60
  }
52
61
  return false;
53
62
  };
63
+ function getSSRLazyCompilation(current, normalizedConfig, appContext, eagerRouteComponentFilesByEntry) {
64
+ if (!current || isUseRsc(normalizedConfig) || !isStreamingSSR(normalizedConfig)) return;
65
+ const plan = planSSRLazyCompilation(current, aggregateEagerRouteComponentFiles(eagerRouteComponentFilesByEntry));
66
+ if (!plan.apply) {
67
+ if (plan.unresolvedByEntry) warnUnresolvedRouteComponents(appContext.appDirectory, plan.unresolvedByEntry);
68
+ return;
69
+ }
70
+ return plan.lazyCompilation;
71
+ }
72
+ const warnedLazyApps = new Set();
73
+ function warnUnresolvedRouteComponents(appDirectory, unresolvedByEntry) {
74
+ if (warnedLazyApps.has(appDirectory)) return;
75
+ warnedLazyApps.add(appDirectory);
76
+ const detail = Array.from(unresolvedByEntry).map(([entry, comps])=>`${entry}: ${comps.join(', ')}`).join('; ');
77
+ logger.warn(`[lazyCompilation] Skipped stream SSR route-eager optimization because some route components could not be resolved to a file (${detail}). Lazy compilation may break first-screen CSS/JS for these routes.`);
78
+ }
54
79
  function applyAsyncChunkHtmlPlugin({ chain, modernConfig, HtmlBundlerPlugin }) {
55
80
  if (isStreamingSSR(modernConfig) || isUseRsc(modernConfig)) chain.plugin('html-async-chunk').use(HtmlAsyncChunkPlugin, [
56
81
  HtmlBundlerPlugin
@@ -70,7 +95,8 @@ function applyRouterPlugin(chain, pluginName, options, HtmlBundlerPlugin) {
70
95
  staticJsDir: normalizedConfig.output?.distPath?.js,
71
96
  disableFilenameHash: normalizedConfig.output?.filenameHash === false,
72
97
  scriptLoading: normalizedConfig.html?.scriptLoading,
73
- nonce: normalizedConfig.security?.nonce
98
+ nonce: normalizedConfig.security?.nonce,
99
+ useRsc: isUseRsc(normalizedConfig)
74
100
  }
75
101
  ]);
76
102
  }
@@ -64,6 +64,25 @@ class RouterPlugin {
64
64
  routeAssets: {}
65
65
  });
66
66
  const prevManifest = JSON.parse(prevManifestStr);
67
+ const namedChunkGroupInstances = new Map();
68
+ for (const cg of compilation.chunkGroups || [])if (cg.name) namedChunkGroupInstances.set(cg.name, cg);
69
+ const collectDescendantCssAssets = (name)=>{
70
+ const root = namedChunkGroupInstances.get(name);
71
+ if (!root) return [];
72
+ const cssFiles = new Set();
73
+ const visited = new Set();
74
+ const stack = [
75
+ ...root.childrenIterable
76
+ ];
77
+ while(stack.length){
78
+ const child = stack.pop();
79
+ if (visited.has(child)) continue;
80
+ visited.add(child);
81
+ for (const chunk of child.chunks)for (const file of chunk.files)if (/\.css$/.test(file)) cssFiles.add(publicPath ? normalizePath(publicPath) + file : file);
82
+ for (const c of child.childrenIterable)stack.push(c);
83
+ }
84
+ return Array.from(cssFiles);
85
+ };
67
86
  const asyncEntryNames = [];
68
87
  for (const [name, chunkGroup] of Object.entries(namedChunkGroups)){
69
88
  if (name.startsWith('async-')) asyncEntryNames.push(name);
@@ -71,7 +90,12 @@ class RouterPlugin {
71
90
  const filename = asset.name;
72
91
  return publicPath ? normalizePath(publicPath) + filename : filename;
73
92
  });
74
- const referenceCssAssets = assets.filter((asset)=>/\.css$/.test(asset));
93
+ const directCssAssets = assets.filter((asset)=>/\.css$/.test(asset));
94
+ const descendantCssAssets = collectDescendantCssAssets(name).filter((asset)=>!directCssAssets.includes(asset));
95
+ const referenceCssAssets = [
96
+ ...directCssAssets,
97
+ ...descendantCssAssets
98
+ ];
75
99
  routeAssets[name] = {
76
100
  chunkIds: chunkGroup.chunks,
77
101
  assets,
@@ -113,10 +137,14 @@ class RouterPlugin {
113
137
  const manifest = {
114
138
  routeAssets: relatedAssets
115
139
  };
140
+ const { useRsc } = this;
116
141
  const injectedContent = `
117
142
  ;(function(){
118
143
  window.${ROUTE_MANIFEST} = ${JSON.stringify(manifest, (k, v)=>{
119
- if (('assets' === k || 'referenceCssAssets' === k) && Array.isArray(v)) return v.map((item)=>item.replace(publicPath, ''));
144
+ if (('assets' === k || 'referenceCssAssets' === k) && Array.isArray(v)) {
145
+ if (!useRsc) return;
146
+ return v.map((item)=>item.replace(publicPath, ''));
147
+ }
120
148
  return v;
121
149
  })};
122
150
  })();
@@ -144,7 +172,7 @@ class RouterPlugin {
144
172
  });
145
173
  });
146
174
  }
147
- constructor({ staticJsDir = 'static/js', HtmlBundlerPlugin, enableInlineRouteManifests, disableFilenameHash = false, scriptLoading = 'defer', nonce }){
175
+ constructor({ staticJsDir = 'static/js', HtmlBundlerPlugin, enableInlineRouteManifests, disableFilenameHash = false, scriptLoading = 'defer', nonce, useRsc = false }){
148
176
  this.name = 'RouterPlugin';
149
177
  this.HtmlBundlerPlugin = HtmlBundlerPlugin;
150
178
  this.enableInlineRouteManifests = enableInlineRouteManifests;
@@ -152,6 +180,7 @@ class RouterPlugin {
152
180
  this.disableFilenameHash = disableFilenameHash;
153
181
  this.scriptLoading = scriptLoading;
154
182
  this.nonce = nonce;
183
+ this.useRsc = useRsc;
155
184
  }
156
185
  }
157
186
  export { RouterPlugin };
@@ -0,0 +1,44 @@
1
+ import { collectRouteComponentFiles, normalizeModulePath } from "@modern-js/utils";
2
+ function aggregateEagerRouteComponentFiles(byEntry) {
3
+ const files = new Set();
4
+ const unresolvedByEntry = new Map();
5
+ if (byEntry) for (const [entryName, collection] of byEntry){
6
+ for (const file of collection.resolvedFiles)files.add(file);
7
+ if (collection.unresolvedSpecifiers.length > 0) unresolvedByEntry.set(entryName, collection.unresolvedSpecifiers);
8
+ }
9
+ return {
10
+ files,
11
+ unresolvedByEntry
12
+ };
13
+ }
14
+ function buildSSRLazyCompilationTest(eagerRouteFiles, userTest) {
15
+ const userTestFn = 'function' == typeof userTest ? userTest : userTest instanceof RegExp ? (m)=>userTest.test(m.resource || '') : ()=>true;
16
+ return (m)=>{
17
+ const resource = m.resource;
18
+ if (!resource) return userTestFn(m);
19
+ if (eagerRouteFiles.has(normalizeModulePath(resource))) return false;
20
+ return userTestFn(m);
21
+ };
22
+ }
23
+ function planSSRLazyCompilation(current, info) {
24
+ if (!current) return {
25
+ apply: false
26
+ };
27
+ if (info.unresolvedByEntry.size > 0) return {
28
+ apply: false,
29
+ unresolvedByEntry: info.unresolvedByEntry
30
+ };
31
+ if (0 === info.files.size) return {
32
+ apply: false
33
+ };
34
+ const base = 'object' == typeof current ? current : {};
35
+ const userTest = current.test;
36
+ return {
37
+ apply: true,
38
+ lazyCompilation: {
39
+ ...base,
40
+ test: buildSSRLazyCompilationTest(info.files, userTest)
41
+ }
42
+ };
43
+ }
44
+ export { aggregateEagerRouteComponentFiles, buildSSRLazyCompilationTest, collectRouteComponentFiles, normalizeModulePath, planSSRLazyCompilation };
@@ -88,4 +88,17 @@ function createDefaultConfig(appContext) {
88
88
  builderPlugins: []
89
89
  };
90
90
  }
91
- export { createDefaultConfig };
91
+ const isStreamSSRConfig = (ssr)=>{
92
+ if (!ssr) return false;
93
+ if ('boolean' == typeof ssr) return ssr;
94
+ return 'string' !== ssr.mode;
95
+ };
96
+ function isLazyCompilationSafeByDefault(userConfig) {
97
+ const { server, output } = userConfig;
98
+ if (output?.ssg || output?.ssgByEntries && Object.keys(output.ssgByEntries).length > 0) return false;
99
+ if (server?.rsc) return false;
100
+ if (server?.ssr && !isStreamSSRConfig(server.ssr)) return false;
101
+ if (server?.ssrByEntries && 'object' == typeof server.ssrByEntries && Object.values(server.ssrByEntries).some((ssr)=>Boolean(ssr) && !isStreamSSRConfig(ssr))) return false;
102
+ return true;
103
+ }
104
+ export { createDefaultConfig, isLazyCompilationSafeByDefault };
@@ -122,10 +122,12 @@ const analyze = ()=>({
122
122
  entrypoints
123
123
  });
124
124
  const normalizedConfig = api.getNormalizedConfig();
125
+ const { eagerRouteComponentFilesByEntry } = api.getAppContext();
125
126
  const createBuilderForModern = await createBuilderGenerator();
126
127
  const builder = await createBuilderForModern({
127
128
  normalizedConfig: normalizedConfig,
128
- appContext: appContext
129
+ appContext: appContext,
130
+ eagerRouteComponentFilesByEntry
129
131
  });
130
132
  builder.onBeforeBuild(async ({ bundlerConfigs, isFirstCompile, environments, isWatch })=>{
131
133
  if (!isFirstCompile) return;
@@ -1,5 +1,5 @@
1
1
  import { ensureAbsolutePath, getPort, isDev, isDevCommand } from "@modern-js/utils";
2
- import { createDefaultConfig } from "../../config/index.mjs";
2
+ import { createDefaultConfig, isLazyCompilationSafeByDefault } from "../../config/index.mjs";
3
3
  const initialize = ()=>({
4
4
  name: '@modern-js/plugin-initialize',
5
5
  post: [
@@ -11,7 +11,16 @@ const initialize = ()=>({
11
11
  setup (api) {
12
12
  api.config(()=>{
13
13
  const appContext = api.getAppContext();
14
- return createDefaultConfig(appContext);
14
+ const userConfig = api.getConfig();
15
+ const defaultConfig = createDefaultConfig(appContext);
16
+ if (userConfig.dev?.lazyCompilation === void 0 && isLazyCompilationSafeByDefault(userConfig)) defaultConfig.dev = {
17
+ ...defaultConfig.dev,
18
+ lazyCompilation: {
19
+ imports: true,
20
+ entries: false
21
+ }
22
+ };
23
+ return defaultConfig;
15
24
  });
16
25
  api.modifyResolvedConfig(async (resolved)=>{
17
26
  let appContext = api.getAppContext();
@@ -1,23 +1,32 @@
1
1
  import __rslib_shim_module__ from "node:module";
2
2
  const require = /*#__PURE__*/ __rslib_shim_module__.createRequire(/*#__PURE__*/ (()=>import.meta.url)());
3
3
  import { SERVICE_WORKER_ENVIRONMENT_NAME, isHtmlDisabled } from "@modern-js/builder";
4
- import { fs, isUseRsc, isUseSSRBundle } from "@modern-js/utils";
4
+ import { fs, isUseRsc, isUseSSRBundle, logger } from "@modern-js/utils";
5
5
  import { mergeRsbuildConfig } from "@rsbuild/core";
6
6
  import { getServerCombinedModuleFile } from "../../../plugins/analyze/utils.mjs";
7
7
  import { HtmlAsyncChunkPlugin, RouterPlugin } from "../bundlerPlugins/index.mjs";
8
+ import { aggregateEagerRouteComponentFiles, planSSRLazyCompilation } from "../lazyCompilation.mjs";
8
9
  import * as __rspack_external_path from "path";
9
10
  const builderPluginAdapterSSR = (options)=>({
10
11
  name: 'builder-plugin-adapter-modern-ssr',
11
12
  setup (api) {
12
- const { normalizedConfig } = options;
13
- api.modifyRsbuildConfig((config)=>mergeRsbuildConfig(config, {
13
+ const { normalizedConfig, appContext, eagerRouteComponentFilesByEntry } = options;
14
+ api.modifyRsbuildConfig((config)=>{
15
+ const merged = mergeRsbuildConfig(config, {
14
16
  html: {
15
17
  inject: isStreamingSSR(normalizedConfig) ? 'head' : void 0
16
18
  },
17
19
  server: {
18
20
  compress: isStreamingSSR(normalizedConfig) || isUseRsc(normalizedConfig) ? false : void 0
19
21
  }
20
- }));
22
+ });
23
+ const lazyCompilation = getSSRLazyCompilation(merged.dev?.lazyCompilation, normalizedConfig, appContext, eagerRouteComponentFilesByEntry);
24
+ if (void 0 !== lazyCompilation) merged.dev = {
25
+ ...merged.dev,
26
+ lazyCompilation
27
+ };
28
+ return merged;
29
+ });
21
30
  api.modifyBundlerChain(async (chain, { target, isProd, HtmlPlugin: HtmlBundlerPlugin, isServer, environment })=>{
22
31
  const builderConfig = environment.config;
23
32
  const { normalizedConfig } = options;
@@ -53,6 +62,22 @@ const isStreamingSSR = (userConfig)=>{
53
62
  }
54
63
  return false;
55
64
  };
65
+ function getSSRLazyCompilation(current, normalizedConfig, appContext, eagerRouteComponentFilesByEntry) {
66
+ if (!current || isUseRsc(normalizedConfig) || !isStreamingSSR(normalizedConfig)) return;
67
+ const plan = planSSRLazyCompilation(current, aggregateEagerRouteComponentFiles(eagerRouteComponentFilesByEntry));
68
+ if (!plan.apply) {
69
+ if (plan.unresolvedByEntry) warnUnresolvedRouteComponents(appContext.appDirectory, plan.unresolvedByEntry);
70
+ return;
71
+ }
72
+ return plan.lazyCompilation;
73
+ }
74
+ const warnedLazyApps = new Set();
75
+ function warnUnresolvedRouteComponents(appDirectory, unresolvedByEntry) {
76
+ if (warnedLazyApps.has(appDirectory)) return;
77
+ warnedLazyApps.add(appDirectory);
78
+ const detail = Array.from(unresolvedByEntry).map(([entry, comps])=>`${entry}: ${comps.join(', ')}`).join('; ');
79
+ logger.warn(`[lazyCompilation] Skipped stream SSR route-eager optimization because some route components could not be resolved to a file (${detail}). Lazy compilation may break first-screen CSS/JS for these routes.`);
80
+ }
56
81
  function applyAsyncChunkHtmlPlugin({ chain, modernConfig, HtmlBundlerPlugin }) {
57
82
  if (isStreamingSSR(modernConfig) || isUseRsc(modernConfig)) chain.plugin('html-async-chunk').use(HtmlAsyncChunkPlugin, [
58
83
  HtmlBundlerPlugin
@@ -72,7 +97,8 @@ function applyRouterPlugin(chain, pluginName, options, HtmlBundlerPlugin) {
72
97
  staticJsDir: normalizedConfig.output?.distPath?.js,
73
98
  disableFilenameHash: normalizedConfig.output?.filenameHash === false,
74
99
  scriptLoading: normalizedConfig.html?.scriptLoading,
75
- nonce: normalizedConfig.security?.nonce
100
+ nonce: normalizedConfig.security?.nonce,
101
+ useRsc: isUseRsc(normalizedConfig)
76
102
  }
77
103
  ]);
78
104
  }
@@ -65,6 +65,25 @@ class RouterPlugin {
65
65
  routeAssets: {}
66
66
  });
67
67
  const prevManifest = JSON.parse(prevManifestStr);
68
+ const namedChunkGroupInstances = new Map();
69
+ for (const cg of compilation.chunkGroups || [])if (cg.name) namedChunkGroupInstances.set(cg.name, cg);
70
+ const collectDescendantCssAssets = (name)=>{
71
+ const root = namedChunkGroupInstances.get(name);
72
+ if (!root) return [];
73
+ const cssFiles = new Set();
74
+ const visited = new Set();
75
+ const stack = [
76
+ ...root.childrenIterable
77
+ ];
78
+ while(stack.length){
79
+ const child = stack.pop();
80
+ if (visited.has(child)) continue;
81
+ visited.add(child);
82
+ for (const chunk of child.chunks)for (const file of chunk.files)if (/\.css$/.test(file)) cssFiles.add(publicPath ? normalizePath(publicPath) + file : file);
83
+ for (const c of child.childrenIterable)stack.push(c);
84
+ }
85
+ return Array.from(cssFiles);
86
+ };
68
87
  const asyncEntryNames = [];
69
88
  for (const [name, chunkGroup] of Object.entries(namedChunkGroups)){
70
89
  if (name.startsWith('async-')) asyncEntryNames.push(name);
@@ -72,7 +91,12 @@ class RouterPlugin {
72
91
  const filename = asset.name;
73
92
  return publicPath ? normalizePath(publicPath) + filename : filename;
74
93
  });
75
- const referenceCssAssets = assets.filter((asset)=>/\.css$/.test(asset));
94
+ const directCssAssets = assets.filter((asset)=>/\.css$/.test(asset));
95
+ const descendantCssAssets = collectDescendantCssAssets(name).filter((asset)=>!directCssAssets.includes(asset));
96
+ const referenceCssAssets = [
97
+ ...directCssAssets,
98
+ ...descendantCssAssets
99
+ ];
76
100
  routeAssets[name] = {
77
101
  chunkIds: chunkGroup.chunks,
78
102
  assets,
@@ -114,10 +138,14 @@ class RouterPlugin {
114
138
  const manifest = {
115
139
  routeAssets: relatedAssets
116
140
  };
141
+ const { useRsc } = this;
117
142
  const injectedContent = `
118
143
  ;(function(){
119
144
  window.${ROUTE_MANIFEST} = ${JSON.stringify(manifest, (k, v)=>{
120
- if (('assets' === k || 'referenceCssAssets' === k) && Array.isArray(v)) return v.map((item)=>item.replace(publicPath, ''));
145
+ if (('assets' === k || 'referenceCssAssets' === k) && Array.isArray(v)) {
146
+ if (!useRsc) return;
147
+ return v.map((item)=>item.replace(publicPath, ''));
148
+ }
121
149
  return v;
122
150
  })};
123
151
  })();
@@ -145,7 +173,7 @@ class RouterPlugin {
145
173
  });
146
174
  });
147
175
  }
148
- constructor({ staticJsDir = 'static/js', HtmlBundlerPlugin, enableInlineRouteManifests, disableFilenameHash = false, scriptLoading = 'defer', nonce }){
176
+ constructor({ staticJsDir = 'static/js', HtmlBundlerPlugin, enableInlineRouteManifests, disableFilenameHash = false, scriptLoading = 'defer', nonce, useRsc = false }){
149
177
  this.name = 'RouterPlugin';
150
178
  this.HtmlBundlerPlugin = HtmlBundlerPlugin;
151
179
  this.enableInlineRouteManifests = enableInlineRouteManifests;
@@ -153,6 +181,7 @@ class RouterPlugin {
153
181
  this.disableFilenameHash = disableFilenameHash;
154
182
  this.scriptLoading = scriptLoading;
155
183
  this.nonce = nonce;
184
+ this.useRsc = useRsc;
156
185
  }
157
186
  }
158
187
  export { RouterPlugin };
@@ -0,0 +1,45 @@
1
+ import "node:module";
2
+ import { collectRouteComponentFiles, normalizeModulePath } from "@modern-js/utils";
3
+ function aggregateEagerRouteComponentFiles(byEntry) {
4
+ const files = new Set();
5
+ const unresolvedByEntry = new Map();
6
+ if (byEntry) for (const [entryName, collection] of byEntry){
7
+ for (const file of collection.resolvedFiles)files.add(file);
8
+ if (collection.unresolvedSpecifiers.length > 0) unresolvedByEntry.set(entryName, collection.unresolvedSpecifiers);
9
+ }
10
+ return {
11
+ files,
12
+ unresolvedByEntry
13
+ };
14
+ }
15
+ function buildSSRLazyCompilationTest(eagerRouteFiles, userTest) {
16
+ const userTestFn = 'function' == typeof userTest ? userTest : userTest instanceof RegExp ? (m)=>userTest.test(m.resource || '') : ()=>true;
17
+ return (m)=>{
18
+ const resource = m.resource;
19
+ if (!resource) return userTestFn(m);
20
+ if (eagerRouteFiles.has(normalizeModulePath(resource))) return false;
21
+ return userTestFn(m);
22
+ };
23
+ }
24
+ function planSSRLazyCompilation(current, info) {
25
+ if (!current) return {
26
+ apply: false
27
+ };
28
+ if (info.unresolvedByEntry.size > 0) return {
29
+ apply: false,
30
+ unresolvedByEntry: info.unresolvedByEntry
31
+ };
32
+ if (0 === info.files.size) return {
33
+ apply: false
34
+ };
35
+ const base = 'object' == typeof current ? current : {};
36
+ const userTest = current.test;
37
+ return {
38
+ apply: true,
39
+ lazyCompilation: {
40
+ ...base,
41
+ test: buildSSRLazyCompilationTest(info.files, userTest)
42
+ }
43
+ };
44
+ }
45
+ export { aggregateEagerRouteComponentFiles, buildSSRLazyCompilationTest, collectRouteComponentFiles, normalizeModulePath, planSSRLazyCompilation };
@@ -89,4 +89,17 @@ function createDefaultConfig(appContext) {
89
89
  builderPlugins: []
90
90
  };
91
91
  }
92
- export { createDefaultConfig };
92
+ const isStreamSSRConfig = (ssr)=>{
93
+ if (!ssr) return false;
94
+ if ('boolean' == typeof ssr) return ssr;
95
+ return 'string' !== ssr.mode;
96
+ };
97
+ function isLazyCompilationSafeByDefault(userConfig) {
98
+ const { server, output } = userConfig;
99
+ if (output?.ssg || output?.ssgByEntries && Object.keys(output.ssgByEntries).length > 0) return false;
100
+ if (server?.rsc) return false;
101
+ if (server?.ssr && !isStreamSSRConfig(server.ssr)) return false;
102
+ if (server?.ssrByEntries && 'object' == typeof server.ssrByEntries && Object.values(server.ssrByEntries).some((ssr)=>Boolean(ssr) && !isStreamSSRConfig(ssr))) return false;
103
+ return true;
104
+ }
105
+ export { createDefaultConfig, isLazyCompilationSafeByDefault };
@@ -123,10 +123,12 @@ const analyze = ()=>({
123
123
  entrypoints
124
124
  });
125
125
  const normalizedConfig = api.getNormalizedConfig();
126
+ const { eagerRouteComponentFilesByEntry } = api.getAppContext();
126
127
  const createBuilderForModern = await createBuilderGenerator();
127
128
  const builder = await createBuilderForModern({
128
129
  normalizedConfig: normalizedConfig,
129
- appContext: appContext
130
+ appContext: appContext,
131
+ eagerRouteComponentFilesByEntry
130
132
  });
131
133
  builder.onBeforeBuild(async ({ bundlerConfigs, isFirstCompile, environments, isWatch })=>{
132
134
  if (!isFirstCompile) return;
@@ -1,6 +1,6 @@
1
1
  import "node:module";
2
2
  import { ensureAbsolutePath, getPort, isDev, isDevCommand } from "@modern-js/utils";
3
- import { createDefaultConfig } from "../../config/index.mjs";
3
+ import { createDefaultConfig, isLazyCompilationSafeByDefault } from "../../config/index.mjs";
4
4
  const initialize = ()=>({
5
5
  name: '@modern-js/plugin-initialize',
6
6
  post: [
@@ -12,7 +12,16 @@ const initialize = ()=>({
12
12
  setup (api) {
13
13
  api.config(()=>{
14
14
  const appContext = api.getAppContext();
15
- return createDefaultConfig(appContext);
15
+ const userConfig = api.getConfig();
16
+ const defaultConfig = createDefaultConfig(appContext);
17
+ if (userConfig.dev?.lazyCompilation === void 0 && isLazyCompilationSafeByDefault(userConfig)) defaultConfig.dev = {
18
+ ...defaultConfig.dev,
19
+ lazyCompilation: {
20
+ imports: true,
21
+ entries: false
22
+ }
23
+ };
24
+ return defaultConfig;
16
25
  });
17
26
  api.modifyResolvedConfig(async (resolved)=>{
18
27
  let appContext = api.getAppContext();
@@ -14,6 +14,7 @@ type Options = {
14
14
  disableFilenameHash?: boolean;
15
15
  scriptLoading?: ScriptLoading;
16
16
  nonce?: string;
17
+ useRsc?: boolean;
17
18
  };
18
19
  export declare class RouterPlugin {
19
20
  readonly name: string;
@@ -23,7 +24,8 @@ export declare class RouterPlugin {
23
24
  private disableFilenameHash?;
24
25
  private scriptLoading?;
25
26
  private nonce?;
26
- constructor({ staticJsDir, HtmlBundlerPlugin, enableInlineRouteManifests, disableFilenameHash, scriptLoading, nonce, }: Options);
27
+ private useRsc;
28
+ constructor({ staticJsDir, HtmlBundlerPlugin, enableInlineRouteManifests, disableFilenameHash, scriptLoading, nonce, useRsc, }: Options);
27
29
  private isTargetNodeOrWebWorker;
28
30
  private getEntryChunks;
29
31
  private getEntryChunkFiles;
@@ -0,0 +1,43 @@
1
+ import { type EagerRouteComponentFilesByEntry } from '@modern-js/utils';
2
+ export { type EagerRouteComponentFilesByEntry, type RouteComponentFileCollection, collectRouteComponentFiles, normalizeModulePath, } from '@modern-js/utils';
3
+ type ModuleLike = {
4
+ resource?: string;
5
+ };
6
+ type LazyCompilationTestFn = (m: ModuleLike) => boolean;
7
+ /** Matches Rspack's `LazyCompilationOptions['test']`. */
8
+ type LazyCompilationTest = RegExp | LazyCompilationTestFn | undefined;
9
+ export type EagerRouteComponentInfo = {
10
+ files: Set<string>;
11
+ /** Specifiers that could not be resolved, keyed by entry name. */
12
+ unresolvedByEntry: Map<string, string[]>;
13
+ };
14
+ /**
15
+ * Aggregate the per-entry route component data (collected by the router plugin
16
+ * during route generation and threaded in as
17
+ * `BuilderOptions.eagerRouteComponentFilesByEntry`) into the flat shape
18
+ * {@link planSSRLazyCompilation} expects: one Set of all route files plus the
19
+ * unresolved specifiers keyed by entry.
20
+ */
21
+ export declare function aggregateEagerRouteComponentFiles(byEntry: EagerRouteComponentFilesByEntry | undefined): EagerRouteComponentInfo;
22
+ /**
23
+ * Build a `lazyCompilation.test` that forces route component modules to compile
24
+ * eagerly (so SSR first-screen chunk/CSS injection has the assets it needs at
25
+ * render time), while delegating all other modules to the user's `test`
26
+ * (defaulting to lazy when the user did not provide one).
27
+ */
28
+ export declare function buildSSRLazyCompilationTest(eagerRouteFiles: Set<string>, userTest?: LazyCompilationTest): LazyCompilationTestFn;
29
+ export type SSRLazyPlan = {
30
+ apply: false;
31
+ unresolvedByEntry?: Map<string, string[]>;
32
+ } | {
33
+ apply: true;
34
+ lazyCompilation: Record<string, unknown>;
35
+ };
36
+ /**
37
+ * Decide whether to apply the route-eager lazy compilation for an SSR project.
38
+ * Checks unresolved route components FIRST: if any exist we cannot guarantee
39
+ * they are eager, so we skip the optimization (and surface them so the caller
40
+ * can warn) rather than silently leaving a route lazy. `current` is the
41
+ * existing `dev.lazyCompilation` value (lazy must be enabled for this to apply).
42
+ */
43
+ export declare function planSSRLazyCompilation(current: unknown, info: EagerRouteComponentInfo): SSRLazyPlan;
@@ -1,6 +1,16 @@
1
+ import type { EagerRouteComponentFilesByEntry } from '@modern-js/utils';
1
2
  import type { AppNormalizedConfig } from '../../types';
2
3
  import type { AppToolsContext } from '../../types/plugin';
3
4
  export type BuilderOptions = {
4
5
  normalizedConfig: AppNormalizedConfig;
5
6
  appContext: AppToolsContext;
7
+ /**
8
+ * Route component files collected from the FINAL file-system routes (after
9
+ * all `modifyFileSystemRoutes` consumers ran), keyed by entry name. Populated
10
+ * by the router plugin during route generation and threaded in here (read
11
+ * FRESH from the app context AFTER `generateEntryCode` runs) so the SSR
12
+ * builder plugin can force route component chunks eager under lazy
13
+ * compilation. Explicit param instead of a direct `_internalContext` read.
14
+ */
15
+ eagerRouteComponentFilesByEntry?: EagerRouteComponentFilesByEntry;
6
16
  };
@@ -1,3 +1,9 @@
1
1
  import type { AppUserConfig } from '../types';
2
2
  import type { AppToolsContext } from '../types/plugin';
3
3
  export declare function createDefaultConfig(appContext: AppToolsContext): AppUserConfig;
4
+ /**
5
+ * Default-enable lazy compilation for pure CSR and stream SSR. Stream SSR keeps
6
+ * first-screen route assets correct via the route-eager lazyCompilation.test
7
+ * injected by the SSR builder plugin. String SSR, RSC and SSG stay disabled.
8
+ */
9
+ export declare function isLazyCompilationSafeByDefault(userConfig: Pick<AppUserConfig, 'server' | 'output'>): boolean;
@@ -1,6 +1,7 @@
1
1
  import type { AppContext, AsyncHook, InternalContext, PluginHook, PluginHookTap, TransformFunction } from '@modern-js/plugin';
2
2
  import type { Hooks } from '@modern-js/plugin/cli';
3
3
  import type { Entrypoint, HtmlPartials, HtmlTemplates, NestedRouteForCli, PageRoute, RouteLegacy, ServerPlugin, ServerRoute } from '@modern-js/types';
4
+ import type { EagerRouteComponentFilesByEntry } from '@modern-js/utils';
4
5
  import type { AppTools } from '.';
5
6
  import type { getHookRunners } from '../compat/hooks';
6
7
  import type { AppToolsNormalizedConfig, AppToolsUserConfig } from './config';
@@ -109,6 +110,19 @@ export interface AppToolsExtendContext {
109
110
  * @private
110
111
  */
111
112
  bffRuntimeFramework?: string;
113
+ /**
114
+ * Route component files collected from the FINAL file-system routes (after all
115
+ * `modifyFileSystemRoutes` consumers ran), keyed by entry name. Populated by
116
+ * the router plugin during route generation and consumed (currently by stream
117
+ * SSR lazy compilation) to force route component chunks eager.
118
+ *
119
+ * Published via the app context (`api.updateAppContext`) by the router plugin,
120
+ * then read fresh when assembling the builder options and threaded into
121
+ * `BuilderOptions.eagerRouteComponentFilesByEntry`; the SSR builder plugin
122
+ * reads it from those options (not from the context directly).
123
+ * @private
124
+ */
125
+ eagerRouteComponentFilesByEntry?: EagerRouteComponentFilesByEntry;
112
126
  }
113
127
  export type AppToolsContext = AppContext<AppTools> & AppToolsExtendContext;
114
128
  export type AppToolsHooks = Hooks<AppToolsUserConfig, AppToolsNormalizedConfig, {}, {}> & AppToolsExtendHooks;
package/package.json CHANGED
@@ -15,7 +15,7 @@
15
15
  "modern",
16
16
  "modern.js"
17
17
  ],
18
- "version": "3.2.2",
18
+ "version": "3.4.0",
19
19
  "types": "./dist/types/index.d.ts",
20
20
  "main": "./dist/cjs/index.js",
21
21
  "exports": {
@@ -84,7 +84,7 @@
84
84
  "@babel/traverse": "^7.29.7",
85
85
  "@babel/types": "^7.29.7",
86
86
  "@rsbuild/core": "2.0.10",
87
- "@swc/core": "1.15.33",
87
+ "@swc/core": "1.15.41",
88
88
  "@swc/helpers": "^0.5.17",
89
89
  "es-module-lexer": "^1.7.0",
90
90
  "flatted": "^3.4.2",
@@ -93,19 +93,19 @@
93
93
  "ndepe": "^0.1.13",
94
94
  "pkg-types": "^1.3.1",
95
95
  "std-env": "^3.10.0",
96
- "@modern-js/builder": "3.2.2",
97
- "@modern-js/i18n-utils": "3.2.2",
98
- "@modern-js/plugin-data-loader": "3.2.2",
99
- "@modern-js/prod-server": "3.2.2",
100
- "@modern-js/server": "3.2.2",
101
- "@modern-js/plugin": "3.2.2",
102
- "@modern-js/server-core": "3.2.2",
103
- "@modern-js/types": "3.2.2",
104
- "@modern-js/server-utils": "3.2.2",
105
- "@modern-js/utils": "3.2.2"
96
+ "@modern-js/builder": "3.4.0",
97
+ "@modern-js/plugin-data-loader": "3.4.0",
98
+ "@modern-js/plugin": "3.4.0",
99
+ "@modern-js/i18n-utils": "3.4.0",
100
+ "@modern-js/prod-server": "3.4.0",
101
+ "@modern-js/server-core": "3.4.0",
102
+ "@modern-js/server": "3.4.0",
103
+ "@modern-js/server-utils": "3.4.0",
104
+ "@modern-js/types": "3.4.0",
105
+ "@modern-js/utils": "3.4.0"
106
106
  },
107
107
  "devDependencies": {
108
- "@rslib/core": "0.21.5",
108
+ "@rslib/core": "0.22.1",
109
109
  "@types/babel__traverse": "7.28.0",
110
110
  "@types/node": "^20",
111
111
  "ts-node": "^10.9.2",