@bleedingdev/modern-js-runtime 3.4.0-ultramodern.2 → 3.4.0-ultramodern.3
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/cjs/cli/index.js +5 -2
- package/dist/cjs/cli/template.server.js +12 -1
- package/dist/cjs/core/server/stream/beforeTemplate.js +2 -2
- package/dist/cjs/core/server/stream/beforeTemplate.worker.js +2 -2
- package/dist/cjs/core/server/string/loadable.js +2 -6
- package/dist/cjs/router/cli/code/templates.js +8 -8
- package/dist/cjs/router/cli/index.js +7 -7
- package/dist/cjs/router/cli/nestedRoutesSpec.js +114 -0
- package/dist/cjs/router/runtime/routerHelper.js +48 -6
- package/dist/cjs/router/runtime/rsc-router.js +4 -3
- package/dist/esm/cli/index.mjs +2 -2
- package/dist/esm/cli/template.server.mjs +12 -1
- package/dist/esm/core/server/stream/beforeTemplate.mjs +2 -2
- package/dist/esm/core/server/stream/beforeTemplate.worker.mjs +2 -2
- package/dist/esm/core/server/string/loadable.mjs +2 -6
- package/dist/esm/router/cli/code/templates.mjs +9 -9
- package/dist/esm/router/cli/index.mjs +4 -7
- package/dist/esm/router/cli/nestedRoutesSpec.mjs +66 -0
- package/dist/esm/router/runtime/routerHelper.mjs +44 -5
- package/dist/esm/router/runtime/rsc-router.mjs +4 -3
- package/dist/esm-node/cli/index.mjs +2 -2
- package/dist/esm-node/cli/template.server.mjs +12 -1
- package/dist/esm-node/core/server/stream/beforeTemplate.mjs +2 -2
- package/dist/esm-node/core/server/stream/beforeTemplate.worker.mjs +2 -2
- package/dist/esm-node/core/server/string/loadable.mjs +2 -6
- package/dist/esm-node/router/cli/code/templates.mjs +9 -9
- package/dist/esm-node/router/cli/index.mjs +4 -7
- package/dist/esm-node/router/cli/nestedRoutesSpec.mjs +67 -0
- package/dist/esm-node/router/runtime/routerHelper.mjs +44 -5
- package/dist/esm-node/router/runtime/rsc-router.mjs +4 -3
- package/dist/types/cli/index.d.ts +1 -1
- package/dist/types/core/server/string/loadable.d.ts +0 -1
- package/dist/types/router/cli/index.d.ts +1 -0
- package/dist/types/router/cli/nestedRoutesSpec.d.ts +1 -0
- package/dist/types/router/runtime/routerHelper.d.ts +3 -2
- package/package.json +8 -8
package/dist/cjs/cli/index.js
CHANGED
|
@@ -50,7 +50,8 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
50
50
|
makeLegalIdentifier: ()=>makeLegalIdentifier_js_namespaceObject.makeLegalIdentifier,
|
|
51
51
|
routerPlugin: ()=>external_router_cli_index_js_namespaceObject.routerPlugin,
|
|
52
52
|
runtimePlugin: ()=>runtimePlugin,
|
|
53
|
-
ssrPlugin: ()=>external_ssr_index_js_namespaceObject.ssrPlugin
|
|
53
|
+
ssrPlugin: ()=>external_ssr_index_js_namespaceObject.ssrPlugin,
|
|
54
|
+
updateNestedRoutesSpec: ()=>external_router_cli_index_js_namespaceObject.updateNestedRoutesSpec
|
|
54
55
|
});
|
|
55
56
|
const utils_namespaceObject = require("@modern-js/utils");
|
|
56
57
|
const external_path_namespaceObject = require("path");
|
|
@@ -160,6 +161,7 @@ exports.makeLegalIdentifier = __webpack_exports__.makeLegalIdentifier;
|
|
|
160
161
|
exports.routerPlugin = __webpack_exports__.routerPlugin;
|
|
161
162
|
exports.runtimePlugin = __webpack_exports__.runtimePlugin;
|
|
162
163
|
exports.ssrPlugin = __webpack_exports__.ssrPlugin;
|
|
164
|
+
exports.updateNestedRoutesSpec = __webpack_exports__.updateNestedRoutesSpec;
|
|
163
165
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
164
166
|
"default",
|
|
165
167
|
"documentPlugin",
|
|
@@ -174,7 +176,8 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
|
174
176
|
"makeLegalIdentifier",
|
|
175
177
|
"routerPlugin",
|
|
176
178
|
"runtimePlugin",
|
|
177
|
-
"ssrPlugin"
|
|
179
|
+
"ssrPlugin",
|
|
180
|
+
"updateNestedRoutesSpec"
|
|
178
181
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
179
182
|
Object.defineProperty(exports, '__esModule', {
|
|
180
183
|
value: true
|
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
createRequestHandler,
|
|
58
58
|
} from '@#metaName/runtime/ssr/server';
|
|
59
59
|
import { RSCServerSlot } from '@#metaName/runtime/rsc/client';
|
|
60
|
-
import { renderRsc } from '@#metaName/runtime/rsc/server';
|
|
60
|
+
import { renderCSRWithRSC, renderRsc } from '@#metaName/runtime/rsc/server';
|
|
61
61
|
export { handleAction } from '@#metaName/runtime/rsc/server';
|
|
62
62
|
|
|
63
63
|
const handleRequest = async (request, ServerRoot, options) => {
|
|
@@ -84,6 +84,17 @@ export const requestHandler = createRequestHandler(handleRequest, {
|
|
|
84
84
|
enableRsc: true
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
+
const handleCSRRender = async (request, ServerRoot, options) => {
|
|
88
|
+
return renderCSRWithRSC({
|
|
89
|
+
html: options.html,
|
|
90
|
+
rscRoot: options.rscRoot,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const renderRscStreamHandler = createRequestHandler(handleCSRRender, {
|
|
95
|
+
enableRsc: true
|
|
96
|
+
});
|
|
97
|
+
|
|
87
98
|
const handleRSCRequest = async (request, ServerRoot, options) => {
|
|
88
99
|
const { serverPayload } = options;
|
|
89
100
|
const stream = renderRsc({
|
|
@@ -66,8 +66,8 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
|
66
66
|
return (0, external_utils_js_namespaceObject.safeReplace)(template, external_constants_js_namespaceObject.CHUNK_CSS_PLACEHOLDER, css);
|
|
67
67
|
async function getCssChunks() {
|
|
68
68
|
const { routeManifest, routerContext, routes } = runtimeContext;
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
const routeAssets = routeManifest?.routeAssets;
|
|
70
|
+
if (!routeAssets) return '';
|
|
71
71
|
let matchedRouteManifests = [];
|
|
72
72
|
const matchedRouteIds = (0, lifecycle_js_namespaceObject.getRouterMatchedRouteIds)(runtimeContext);
|
|
73
73
|
if (matchedRouteIds?.length) matchedRouteManifests = matchedRouteIds.map((routeId)=>routeAssets[routeId]).filter(Boolean);
|
|
@@ -60,8 +60,8 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
|
60
60
|
return (0, external_utils_js_namespaceObject.safeReplace)(template, external_constants_js_namespaceObject.CHUNK_CSS_PLACEHOLDER, css);
|
|
61
61
|
async function getCssChunks() {
|
|
62
62
|
const { routeManifest, routerContext, routes } = runtimeContext;
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
const routeAssets = routeManifest?.routeAssets;
|
|
64
|
+
if (!routeAssets) return '';
|
|
65
65
|
let matchedRouteManifests = [];
|
|
66
66
|
const matchedRouteIds = (0, lifecycle_js_namespaceObject.getRouterMatchedRouteIds)(runtimeContext);
|
|
67
67
|
if (matchedRouteIds?.length) matchedRouteManifests = matchedRouteIds.map((routeId)=>routeAssets[routeId]).filter(Boolean);
|
|
@@ -61,10 +61,6 @@ const readAsset = async (chunk)=>{
|
|
|
61
61
|
return fs.readFile(filepath, 'utf-8');
|
|
62
62
|
};
|
|
63
63
|
class LoadableCollector {
|
|
64
|
-
get existsAssets() {
|
|
65
|
-
const { routeManifest, entryName } = this.options;
|
|
66
|
-
return routeManifest?.routeAssets?.[entryName]?.assets;
|
|
67
|
-
}
|
|
68
64
|
getMatchedRouteChunks() {
|
|
69
65
|
const { routeManifest, runtimeContext } = this.options;
|
|
70
66
|
if (!routeManifest) return [];
|
|
@@ -131,7 +127,7 @@ class LoadableCollector {
|
|
|
131
127
|
const matchs = template.matchAll(jsScriptRegExp);
|
|
132
128
|
const existedScript = [];
|
|
133
129
|
for (const match of matchs)existedScript.push(match[1]);
|
|
134
|
-
const scripts = await Promise.all(chunks.filter((chunk)=>!existedScript.includes(chunk.url)
|
|
130
|
+
const scripts = await Promise.all(chunks.filter((chunk)=>!existedScript.includes(chunk.url)).map(async (chunk)=>{
|
|
135
131
|
const script = `<script${attributes} src="${chunk.url}"></script>`;
|
|
136
132
|
if ((0, external_utils_js_namespaceObject.checkIsNode)() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<script>${content}</script>`).catch((_)=>script);
|
|
137
133
|
return script;
|
|
@@ -142,7 +138,7 @@ class LoadableCollector {
|
|
|
142
138
|
const { template, chunkSet, config, moduleFederationCssAssets } = this.options;
|
|
143
139
|
const { inlineStyles } = config;
|
|
144
140
|
const atrributes = (0, external_utils_js_namespaceObject.attributesToString)(this.generateAttributes());
|
|
145
|
-
const emittedChunks = chunks.filter((chunk)=>!(0, external_utils_js_namespaceObject.hasStylesheetLink)(template, chunk.url)
|
|
141
|
+
const emittedChunks = chunks.filter((chunk)=>!(0, external_utils_js_namespaceObject.hasStylesheetLink)(template, chunk.url));
|
|
146
142
|
const css = await Promise.all(emittedChunks.map(async (chunk)=>{
|
|
147
143
|
const link = `<link${atrributes} href="${chunk.url}" rel="stylesheet" />`;
|
|
148
144
|
if ((0, external_utils_js_namespaceObject.checkIsNode)() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<style>${content}</style>`).catch((_)=>link);
|
|
@@ -214,14 +214,14 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
214
214
|
routeId: route.id,
|
|
215
215
|
webpackChunkName: true
|
|
216
216
|
});
|
|
217
|
-
component = 'string' === ssrMode ? `loadable(${lazyImport})` : `lazy(${lazyImport})`;
|
|
217
|
+
component = 'string' === ssrMode ? `loadable(${lazyImport}, { resolveComponent: resolveRouteComponent })` : `lazy(${lazyImport})`;
|
|
218
218
|
} else {
|
|
219
219
|
components.push(route._component);
|
|
220
220
|
component = `component_${components.length - 1}`;
|
|
221
221
|
}
|
|
222
222
|
} else if (route._component) if (splitRouteChunks) {
|
|
223
223
|
lazyImport = `() => import('${route._component}')`;
|
|
224
|
-
component = `loadable(${lazyImport})`;
|
|
224
|
+
component = `loadable(${lazyImport}, { resolveComponent: resolveRouteComponent })`;
|
|
225
225
|
} else {
|
|
226
226
|
components.push(route._component);
|
|
227
227
|
component = `component_${components.length - 1}`;
|
|
@@ -269,7 +269,7 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
269
269
|
const newRouteStr = regs.reduce((acc, reg)=>acc.replace(reg, '$1$2'), routeStr).replace(/"(RootLayout)"/g, '$1').replace(/\\"/g, '"');
|
|
270
270
|
routeComponentsCode += `${newRouteStr},`;
|
|
271
271
|
} else {
|
|
272
|
-
const component = `loadable(() => import('${route._component}'))`;
|
|
272
|
+
const component = `loadable(() => import('${route._component}'), { resolveComponent: resolveRouteComponent })`;
|
|
273
273
|
const finalRoute = {
|
|
274
274
|
...route,
|
|
275
275
|
component
|
|
@@ -319,7 +319,7 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
319
319
|
await utils_namespaceObject.fs.ensureFile(loadersMapFile);
|
|
320
320
|
await utils_namespaceObject.fs.writeJSON(loadersMapFile, loadersMap);
|
|
321
321
|
const importRuntimeRouterCode = `
|
|
322
|
-
import { createShouldRevalidate, handleRouteModule,
|
|
322
|
+
import { createShouldRevalidate, handleRouteModule, handleRouteModuleError, resolveRouteComponent } from '@${metaName}/runtime/routerHelper';
|
|
323
323
|
`;
|
|
324
324
|
const routeModulesCode = `
|
|
325
325
|
if(typeof document !== 'undefined'){
|
|
@@ -341,19 +341,19 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
341
341
|
};
|
|
342
342
|
function ssrLoaderCombinedModule(entrypoints, entrypoint, config, appContext) {
|
|
343
343
|
const { entryName, isMainEntry } = entrypoint;
|
|
344
|
-
const { packageName
|
|
344
|
+
const { packageName } = appContext;
|
|
345
345
|
const ssr = (0, utils_namespaceObject.getEntryOptions)(entryName, isMainEntry, config.server.ssr, config.server.ssrByEntries, packageName);
|
|
346
346
|
const ssg = (0, utils_namespaceObject.isSSGEntry)(config, entryName, entrypoints);
|
|
347
347
|
if (entrypoint.nestedRoutesEntry && (ssr || ssg)) {
|
|
348
348
|
const serverLoaderRuntime = require.resolve('@modern-js/plugin-data-loader/runtime');
|
|
349
|
-
const serverLoadersFile =
|
|
350
|
-
const combinedModule = `export * from "${(0, utils_namespaceObject.slash)(serverLoaderRuntime)}"; export * from "${
|
|
349
|
+
const serverLoadersFile = './route-server-loaders.js';
|
|
350
|
+
const combinedModule = `export * from "${(0, utils_namespaceObject.slash)(serverLoaderRuntime)}"; export * from "${serverLoadersFile}"`;
|
|
351
351
|
if (!config.source.enableAsyncEntry) return combinedModule;
|
|
352
352
|
return `
|
|
353
353
|
async function loadModules() {
|
|
354
354
|
const [moduleA, moduleB] = await Promise.all([
|
|
355
355
|
import("${(0, utils_namespaceObject.slash)(serverLoaderRuntime)}"),
|
|
356
|
-
import("${
|
|
356
|
+
import("${serverLoadersFile}")
|
|
357
357
|
]);
|
|
358
358
|
|
|
359
359
|
return {
|
|
@@ -45,7 +45,8 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
45
45
|
handleGeneratorEntryCode: ()=>external_handler_js_namespaceObject.handleGeneratorEntryCode,
|
|
46
46
|
handleModifyEntrypoints: ()=>external_handler_js_namespaceObject.handleModifyEntrypoints,
|
|
47
47
|
isRouteEntry: ()=>external_entry_js_namespaceObject.isRouteEntry,
|
|
48
|
-
routerPlugin: ()=>routerPlugin
|
|
48
|
+
routerPlugin: ()=>routerPlugin,
|
|
49
|
+
updateNestedRoutesSpec: ()=>external_nestedRoutesSpec_js_namespaceObject.updateNestedRoutesSpec
|
|
49
50
|
});
|
|
50
51
|
const external_node_path_namespaceObject = require("node:path");
|
|
51
52
|
var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
|
|
@@ -53,6 +54,7 @@ const utils_namespaceObject = require("@modern-js/utils");
|
|
|
53
54
|
const external_constants_js_namespaceObject = require("./constants.js");
|
|
54
55
|
const external_entry_js_namespaceObject = require("./entry.js");
|
|
55
56
|
const external_handler_js_namespaceObject = require("./handler.js");
|
|
57
|
+
const external_nestedRoutesSpec_js_namespaceObject = require("./nestedRoutesSpec.js");
|
|
56
58
|
function isBuiltInRouteEntrypoint(entrypoint) {
|
|
57
59
|
const entrypointRoutesOwner = (0, external_entry_js_namespaceObject.getEntrypointRoutesOwner)(entrypoint);
|
|
58
60
|
if (entrypointRoutesOwner) return entrypointRoutesOwner === external_entry_js_namespaceObject.BUILT_IN_ROUTES_OWNER;
|
|
@@ -137,11 +139,7 @@ const routerPlugin = ()=>({
|
|
|
137
139
|
if (isBuiltInRouteEntrypoint(entrypoint)) {
|
|
138
140
|
const { distDirectory } = api.getAppContext();
|
|
139
141
|
const nestedRoutesSpecPath = external_node_path_default().resolve(distDirectory, utils_namespaceObject.NESTED_ROUTE_SPEC_FILE);
|
|
140
|
-
|
|
141
|
-
await utils_namespaceObject.fs.outputJSON(nestedRoutesSpecPath, {
|
|
142
|
-
...existingNestedRoutes,
|
|
143
|
-
...nestedRoutesForServer
|
|
144
|
-
});
|
|
142
|
+
await (0, external_nestedRoutesSpec_js_namespaceObject.updateNestedRoutesSpec)(nestedRoutesSpecPath, nestedRoutesForServer);
|
|
145
143
|
}
|
|
146
144
|
return {
|
|
147
145
|
entrypoint,
|
|
@@ -160,6 +158,7 @@ exports.handleGeneratorEntryCode = __webpack_exports__.handleGeneratorEntryCode;
|
|
|
160
158
|
exports.handleModifyEntrypoints = __webpack_exports__.handleModifyEntrypoints;
|
|
161
159
|
exports.isRouteEntry = __webpack_exports__.isRouteEntry;
|
|
162
160
|
exports.routerPlugin = __webpack_exports__.routerPlugin;
|
|
161
|
+
exports.updateNestedRoutesSpec = __webpack_exports__.updateNestedRoutesSpec;
|
|
163
162
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
164
163
|
"BUILT_IN_ROUTES_OWNER",
|
|
165
164
|
"default",
|
|
@@ -169,7 +168,8 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
|
169
168
|
"handleGeneratorEntryCode",
|
|
170
169
|
"handleModifyEntrypoints",
|
|
171
170
|
"isRouteEntry",
|
|
172
|
-
"routerPlugin"
|
|
171
|
+
"routerPlugin",
|
|
172
|
+
"updateNestedRoutesSpec"
|
|
173
173
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
174
174
|
Object.defineProperty(exports, '__esModule', {
|
|
175
175
|
value: true
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.n = (module)=>{
|
|
5
|
+
var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
|
|
6
|
+
__webpack_require__.d(getter, {
|
|
7
|
+
a: getter
|
|
8
|
+
});
|
|
9
|
+
return getter;
|
|
10
|
+
};
|
|
11
|
+
})();
|
|
12
|
+
(()=>{
|
|
13
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
14
|
+
var define = (defs, kind)=>{
|
|
15
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
[kind]: defs[key]
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
define(getters, "get");
|
|
21
|
+
define(values, "value");
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
(()=>{
|
|
25
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
26
|
+
})();
|
|
27
|
+
(()=>{
|
|
28
|
+
__webpack_require__.r = (exports1)=>{
|
|
29
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
30
|
+
value: 'Module'
|
|
31
|
+
});
|
|
32
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
33
|
+
value: true
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
})();
|
|
37
|
+
var __webpack_exports__ = {};
|
|
38
|
+
__webpack_require__.r(__webpack_exports__);
|
|
39
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
40
|
+
updateNestedRoutesSpec: ()=>updateNestedRoutesSpec
|
|
41
|
+
});
|
|
42
|
+
const external_node_path_namespaceObject = require("node:path");
|
|
43
|
+
var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
|
|
44
|
+
const promises_namespaceObject = require("node:timers/promises");
|
|
45
|
+
const utils_namespaceObject = require("@modern-js/utils");
|
|
46
|
+
const lockPollIntervalMs = 25;
|
|
47
|
+
const staleLockAgeMs = 120000;
|
|
48
|
+
const pendingUpdates = new Map();
|
|
49
|
+
let tempFileCounter = 0;
|
|
50
|
+
async function acquireSpecLock(specPath) {
|
|
51
|
+
const lockDir = `${specPath}.lock`;
|
|
52
|
+
await utils_namespaceObject.fs.ensureDir(external_node_path_default().dirname(specPath));
|
|
53
|
+
while(true){
|
|
54
|
+
try {
|
|
55
|
+
await utils_namespaceObject.fs.mkdir(lockDir);
|
|
56
|
+
return async ()=>{
|
|
57
|
+
await utils_namespaceObject.fs.remove(lockDir);
|
|
58
|
+
};
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if ('EEXIST' !== error.code) throw error;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const stat = await utils_namespaceObject.fs.stat(lockDir);
|
|
64
|
+
if (performance.timeOrigin + performance.now() - stat.mtimeMs > staleLockAgeMs) {
|
|
65
|
+
await utils_namespaceObject.fs.remove(lockDir);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
} catch (error) {
|
|
69
|
+
if ('ENOENT' !== error.code) throw error;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
await (0, promises_namespaceObject.setTimeout)(lockPollIntervalMs);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function writeJSONAtomically(filePath, value) {
|
|
76
|
+
const directory = external_node_path_default().dirname(filePath);
|
|
77
|
+
const tempPath = external_node_path_default().join(directory, `.${external_node_path_default().basename(filePath)}.${process.pid}.${tempFileCounter += 1}.tmp`);
|
|
78
|
+
try {
|
|
79
|
+
await utils_namespaceObject.fs.writeFile(tempPath, `${JSON.stringify(value)}\n`);
|
|
80
|
+
await utils_namespaceObject.fs.rename(tempPath, filePath);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
await utils_namespaceObject.fs.remove(tempPath).catch(()=>{});
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async function updateNestedRoutesSpec(specPath, nextRoutes) {
|
|
87
|
+
const resolvedSpecPath = external_node_path_default().resolve(specPath);
|
|
88
|
+
const previousUpdate = pendingUpdates.get(resolvedSpecPath) ?? Promise.resolve();
|
|
89
|
+
const currentUpdate = previousUpdate.catch(()=>void 0).then(async ()=>{
|
|
90
|
+
const releaseLock = await acquireSpecLock(resolvedSpecPath);
|
|
91
|
+
try {
|
|
92
|
+
const existingRoutes = await utils_namespaceObject.fs.pathExists(resolvedSpecPath) ? await utils_namespaceObject.fs.readJSON(resolvedSpecPath) : {};
|
|
93
|
+
await writeJSONAtomically(resolvedSpecPath, {
|
|
94
|
+
...existingRoutes,
|
|
95
|
+
...nextRoutes
|
|
96
|
+
});
|
|
97
|
+
} finally{
|
|
98
|
+
await releaseLock();
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
pendingUpdates.set(resolvedSpecPath, currentUpdate);
|
|
102
|
+
try {
|
|
103
|
+
await currentUpdate;
|
|
104
|
+
} finally{
|
|
105
|
+
if (pendingUpdates.get(resolvedSpecPath) === currentUpdate) pendingUpdates.delete(resolvedSpecPath);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.updateNestedRoutesSpec = __webpack_exports__.updateNestedRoutesSpec;
|
|
109
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
110
|
+
"updateNestedRoutesSpec"
|
|
111
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
112
|
+
Object.defineProperty(exports, '__esModule', {
|
|
113
|
+
value: true
|
|
114
|
+
});
|
|
@@ -30,17 +30,57 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
30
30
|
__webpack_require__.d(__webpack_exports__, {
|
|
31
31
|
createShouldRevalidate: ()=>createShouldRevalidate,
|
|
32
32
|
handleRouteModule: ()=>handleRouteModule,
|
|
33
|
-
handleRouteModuleError: ()=>handleRouteModuleError
|
|
33
|
+
handleRouteModuleError: ()=>handleRouteModuleError,
|
|
34
|
+
resolveRouteComponent: ()=>resolveRouteComponent
|
|
34
35
|
});
|
|
35
36
|
const constants_namespaceObject = require("@modern-js/utils/universal/constants");
|
|
37
|
+
const isObjectLike = (value)=>'object' == typeof value && null !== value || 'function' == typeof value;
|
|
38
|
+
const isRouteComponent = (value)=>'function' == typeof value || isObjectLike(value) && '$$typeof' in value;
|
|
39
|
+
const getRouteModules = ()=>{
|
|
40
|
+
if ("u" < typeof window) return;
|
|
41
|
+
return window[constants_namespaceObject.ROUTE_MODULES];
|
|
42
|
+
};
|
|
43
|
+
const storeRouteModule = (routeModule, routeId)=>{
|
|
44
|
+
if ("u" < typeof document) return;
|
|
45
|
+
const routeModules = getRouteModules();
|
|
46
|
+
if (void 0 !== routeModules) routeModules[routeId] = routeModule;
|
|
47
|
+
};
|
|
48
|
+
const unwrapRspackAsyncModule = (routeModule)=>{
|
|
49
|
+
if (!isObjectLike(routeModule)) return routeModule;
|
|
50
|
+
const rspackExportsSymbol = Object.getOwnPropertySymbols(routeModule).find((symbol)=>'rspack exports' === symbol.description);
|
|
51
|
+
if (void 0 !== rspackExportsSymbol) return routeModule[rspackExportsSymbol];
|
|
52
|
+
if ('__webpack_exports__' in routeModule) return routeModule.__webpack_exports__;
|
|
53
|
+
return routeModule;
|
|
54
|
+
};
|
|
36
55
|
const createShouldRevalidate = (routeId)=>(arg)=>{
|
|
37
|
-
const routeModule =
|
|
38
|
-
if (routeModule
|
|
56
|
+
const routeModule = getRouteModules()?.[routeId];
|
|
57
|
+
if (isObjectLike(routeModule)) {
|
|
58
|
+
const shouldRevalidate = routeModule.shouldRevalidate;
|
|
59
|
+
if ('function' == typeof shouldRevalidate) return shouldRevalidate(arg);
|
|
60
|
+
}
|
|
39
61
|
return arg.defaultShouldRevalidate;
|
|
40
62
|
};
|
|
63
|
+
const pickRouteModuleComponent = (routeModule, seen = new Set())=>{
|
|
64
|
+
const unwrappedRouteModule = unwrapRspackAsyncModule(routeModule);
|
|
65
|
+
if (isRouteComponent(unwrappedRouteModule)) return unwrappedRouteModule;
|
|
66
|
+
if (!isObjectLike(unwrappedRouteModule) || seen.has(unwrappedRouteModule)) return;
|
|
67
|
+
seen.add(unwrappedRouteModule);
|
|
68
|
+
const componentModule = unwrappedRouteModule;
|
|
69
|
+
for (const candidate of [
|
|
70
|
+
componentModule.default,
|
|
71
|
+
componentModule.Component
|
|
72
|
+
]){
|
|
73
|
+
const component = pickRouteModuleComponent(candidate, seen);
|
|
74
|
+
if (void 0 !== component) return component;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const resolveRouteComponent = (routeModule)=>pickRouteModuleComponent(routeModule) ?? routeModule;
|
|
41
78
|
const handleRouteModule = (routeModule, routeId)=>{
|
|
42
|
-
|
|
43
|
-
|
|
79
|
+
storeRouteModule(routeModule, routeId);
|
|
80
|
+
const component = pickRouteModuleComponent(routeModule);
|
|
81
|
+
return void 0 !== component ? {
|
|
82
|
+
default: component
|
|
83
|
+
} : routeModule;
|
|
44
84
|
};
|
|
45
85
|
const handleRouteModuleError = (error)=>{
|
|
46
86
|
console.error(error);
|
|
@@ -49,10 +89,12 @@ const handleRouteModuleError = (error)=>{
|
|
|
49
89
|
exports.createShouldRevalidate = __webpack_exports__.createShouldRevalidate;
|
|
50
90
|
exports.handleRouteModule = __webpack_exports__.handleRouteModule;
|
|
51
91
|
exports.handleRouteModuleError = __webpack_exports__.handleRouteModuleError;
|
|
92
|
+
exports.resolveRouteComponent = __webpack_exports__.resolveRouteComponent;
|
|
52
93
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
53
94
|
"createShouldRevalidate",
|
|
54
95
|
"handleRouteModule",
|
|
55
|
-
"handleRouteModuleError"
|
|
96
|
+
"handleRouteModuleError",
|
|
97
|
+
"resolveRouteComponent"
|
|
56
98
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
57
99
|
Object.defineProperty(exports, '__esModule', {
|
|
58
100
|
value: true
|
|
@@ -90,10 +90,10 @@ const createServerPayload = (routerContext, routes)=>{
|
|
|
90
90
|
routes: routerContext.matches.map((match, index, matches)=>{
|
|
91
91
|
const route = match.route;
|
|
92
92
|
const element = route.element;
|
|
93
|
+
const Component = route.Component;
|
|
93
94
|
const parentMatch = index > 0 ? matches[index - 1] : void 0;
|
|
94
95
|
let processedElement;
|
|
95
|
-
if (element) {
|
|
96
|
-
const ElementComponent = element.type;
|
|
96
|
+
if (element || Component) {
|
|
97
97
|
const elementProps = {
|
|
98
98
|
loaderData: routerContext?.loaderData?.[route.id],
|
|
99
99
|
actionData: routerContext?.actionData?.[route.id],
|
|
@@ -106,7 +106,8 @@ const createServerPayload = (routerContext, routes)=>{
|
|
|
106
106
|
handle: m.route.handle
|
|
107
107
|
}))
|
|
108
108
|
};
|
|
109
|
-
const
|
|
109
|
+
const RouteComponent = Component;
|
|
110
|
+
const routeElement = element ? /*#__PURE__*/ external_react_default().cloneElement(element, elementProps) : /*#__PURE__*/ external_react_default().createElement(RouteComponent, elementProps);
|
|
110
111
|
processedElement = index === cssInjectionIndex ? /*#__PURE__*/ external_react_default().createElement(external_react_default().Fragment, null, /*#__PURE__*/ external_react_default().createElement(external_CSSLinks_js_namespaceObject.CSSLinks, {
|
|
111
112
|
cssFiles
|
|
112
113
|
}), routeElement) : routeElement;
|
package/dist/esm/cli/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cleanRequireCache, isReact18 as utils_isReact18 } from "@modern-js/utils";
|
|
2
2
|
import path_0 from "path";
|
|
3
3
|
import { documentPlugin } from "../document/cli/index.mjs";
|
|
4
|
-
import { getEntrypointRoutesDir, getEntrypointRoutesOwner, handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints, isRouteEntry, routerPlugin } from "../router/cli/index.mjs";
|
|
4
|
+
import { getEntrypointRoutesDir, getEntrypointRoutesOwner, handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints, isRouteEntry, routerPlugin, updateNestedRoutesSpec } from "../router/cli/index.mjs";
|
|
5
5
|
import { builderPluginAlias } from "./alias.mjs";
|
|
6
6
|
import { generateCode } from "./code.mjs";
|
|
7
7
|
import { ENTRY_BOOTSTRAP_FILE_NAME, ENTRY_POINT_FILE_NAME } from "./constants.mjs";
|
|
@@ -92,4 +92,4 @@ const cli = runtimePlugin;
|
|
|
92
92
|
export { makeLegalIdentifier } from "../router/cli/code/makeLegalIdentifier.mjs";
|
|
93
93
|
export { getPathWithoutExt } from "../router/cli/code/utils.mjs";
|
|
94
94
|
export default cli;
|
|
95
|
-
export { documentPlugin, getEntrypointRoutesDir, getEntrypointRoutesOwner, handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints, isRouteEntry, isRuntimeEntry, routerPlugin, runtimePlugin, ssrPlugin };
|
|
95
|
+
export { documentPlugin, getEntrypointRoutesDir, getEntrypointRoutesOwner, handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints, isRouteEntry, isRuntimeEntry, routerPlugin, runtimePlugin, ssrPlugin, updateNestedRoutesSpec };
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
createRequestHandler,
|
|
25
25
|
} from '@#metaName/runtime/ssr/server';
|
|
26
26
|
import { RSCServerSlot } from '@#metaName/runtime/rsc/client';
|
|
27
|
-
import { renderRsc } from '@#metaName/runtime/rsc/server';
|
|
27
|
+
import { renderCSRWithRSC, renderRsc } from '@#metaName/runtime/rsc/server';
|
|
28
28
|
export { handleAction } from '@#metaName/runtime/rsc/server';
|
|
29
29
|
|
|
30
30
|
const handleRequest = async (request, ServerRoot, options) => {
|
|
@@ -51,6 +51,17 @@ export const requestHandler = createRequestHandler(handleRequest, {
|
|
|
51
51
|
enableRsc: true
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
+
const handleCSRRender = async (request, ServerRoot, options) => {
|
|
55
|
+
return renderCSRWithRSC({
|
|
56
|
+
html: options.html,
|
|
57
|
+
rscRoot: options.rscRoot,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const renderRscStreamHandler = createRequestHandler(handleCSRRender, {
|
|
62
|
+
enableRsc: true
|
|
63
|
+
});
|
|
64
|
+
|
|
54
65
|
const handleRSCRequest = async (request, ServerRoot, options) => {
|
|
55
66
|
const { serverPayload } = options;
|
|
56
67
|
const stream = renderRsc({
|
|
@@ -34,8 +34,8 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
|
34
34
|
return safeReplace(template, CHUNK_CSS_PLACEHOLDER, css);
|
|
35
35
|
async function getCssChunks() {
|
|
36
36
|
const { routeManifest, routerContext, routes } = runtimeContext;
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
const routeAssets = routeManifest?.routeAssets;
|
|
38
|
+
if (!routeAssets) return '';
|
|
39
39
|
let matchedRouteManifests = [];
|
|
40
40
|
const matchedRouteIds = getRouterMatchedRouteIds(runtimeContext);
|
|
41
41
|
if (matchedRouteIds?.length) matchedRouteManifests = matchedRouteIds.map((routeId)=>routeAssets[routeId]).filter(Boolean);
|
|
@@ -28,8 +28,8 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
|
28
28
|
return safeReplace(template, CHUNK_CSS_PLACEHOLDER, css);
|
|
29
29
|
async function getCssChunks() {
|
|
30
30
|
const { routeManifest, routerContext, routes } = runtimeContext;
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
const routeAssets = routeManifest?.routeAssets;
|
|
32
|
+
if (!routeAssets) return '';
|
|
33
33
|
let matchedRouteManifests = [];
|
|
34
34
|
const matchedRouteIds = getRouterMatchedRouteIds(runtimeContext);
|
|
35
35
|
if (matchedRouteIds?.length) matchedRouteManifests = matchedRouteIds.map((routeId)=>routeAssets[routeId]).filter(Boolean);
|
|
@@ -28,10 +28,6 @@ const readAsset = async (chunk)=>{
|
|
|
28
28
|
return fs.readFile(filepath, 'utf-8');
|
|
29
29
|
};
|
|
30
30
|
class LoadableCollector {
|
|
31
|
-
get existsAssets() {
|
|
32
|
-
const { routeManifest, entryName } = this.options;
|
|
33
|
-
return routeManifest?.routeAssets?.[entryName]?.assets;
|
|
34
|
-
}
|
|
35
31
|
getMatchedRouteChunks() {
|
|
36
32
|
const { routeManifest, runtimeContext } = this.options;
|
|
37
33
|
if (!routeManifest) return [];
|
|
@@ -98,7 +94,7 @@ class LoadableCollector {
|
|
|
98
94
|
const matchs = template.matchAll(jsScriptRegExp);
|
|
99
95
|
const existedScript = [];
|
|
100
96
|
for (const match of matchs)existedScript.push(match[1]);
|
|
101
|
-
const scripts = await Promise.all(chunks.filter((chunk)=>!existedScript.includes(chunk.url)
|
|
97
|
+
const scripts = await Promise.all(chunks.filter((chunk)=>!existedScript.includes(chunk.url)).map(async (chunk)=>{
|
|
102
98
|
const script = `<script${attributes} src="${chunk.url}"></script>`;
|
|
103
99
|
if (checkIsNode() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<script>${content}</script>`).catch((_)=>script);
|
|
104
100
|
return script;
|
|
@@ -109,7 +105,7 @@ class LoadableCollector {
|
|
|
109
105
|
const { template, chunkSet, config, moduleFederationCssAssets } = this.options;
|
|
110
106
|
const { inlineStyles } = config;
|
|
111
107
|
const atrributes = attributesToString(this.generateAttributes());
|
|
112
|
-
const emittedChunks = chunks.filter((chunk)=>!hasStylesheetLink(template, chunk.url)
|
|
108
|
+
const emittedChunks = chunks.filter((chunk)=>!hasStylesheetLink(template, chunk.url));
|
|
113
109
|
const css = await Promise.all(emittedChunks.map(async (chunk)=>{
|
|
114
110
|
const link = `<link${atrributes} href="${chunk.url}" rel="stylesheet" />`;
|
|
115
111
|
if (checkIsNode() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<style>${content}</style>`).catch((_)=>link);
|
|
@@ -3,7 +3,7 @@ import { JS_EXTENSIONS, findExists, formatImportPath, fs, getEntryOptions, isSSG
|
|
|
3
3
|
import { ROUTE_MODULES } from "@modern-js/utils/universal/constants";
|
|
4
4
|
import path from "path";
|
|
5
5
|
import { APP_INIT_EXPORTED, TEMP_LOADERS_DIR } from "../constants.mjs";
|
|
6
|
-
import { getPathWithoutExt,
|
|
6
|
+
import { getPathWithoutExt, parseModule, replaceWithAlias } from "./utils.mjs";
|
|
7
7
|
const routesForServer = ({ routesForServerLoaderMatches })=>{
|
|
8
8
|
const loaders = [];
|
|
9
9
|
const actions = [];
|
|
@@ -169,14 +169,14 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
169
169
|
routeId: route.id,
|
|
170
170
|
webpackChunkName: true
|
|
171
171
|
});
|
|
172
|
-
component = 'string' === ssrMode ? `loadable(${lazyImport})` : `lazy(${lazyImport})`;
|
|
172
|
+
component = 'string' === ssrMode ? `loadable(${lazyImport}, { resolveComponent: resolveRouteComponent })` : `lazy(${lazyImport})`;
|
|
173
173
|
} else {
|
|
174
174
|
components.push(route._component);
|
|
175
175
|
component = `component_${components.length - 1}`;
|
|
176
176
|
}
|
|
177
177
|
} else if (route._component) if (splitRouteChunks) {
|
|
178
178
|
lazyImport = `() => import('${route._component}')`;
|
|
179
|
-
component = `loadable(${lazyImport})`;
|
|
179
|
+
component = `loadable(${lazyImport}, { resolveComponent: resolveRouteComponent })`;
|
|
180
180
|
} else {
|
|
181
181
|
components.push(route._component);
|
|
182
182
|
component = `component_${components.length - 1}`;
|
|
@@ -224,7 +224,7 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
224
224
|
const newRouteStr = regs.reduce((acc, reg)=>acc.replace(reg, '$1$2'), routeStr).replace(/"(RootLayout)"/g, '$1').replace(/\\"/g, '"');
|
|
225
225
|
routeComponentsCode += `${newRouteStr},`;
|
|
226
226
|
} else {
|
|
227
|
-
const component = `loadable(() => import('${route._component}'))`;
|
|
227
|
+
const component = `loadable(() => import('${route._component}'), { resolveComponent: resolveRouteComponent })`;
|
|
228
228
|
const finalRoute = {
|
|
229
229
|
...route,
|
|
230
230
|
component
|
|
@@ -274,7 +274,7 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
274
274
|
await fs.ensureFile(loadersMapFile);
|
|
275
275
|
await fs.writeJSON(loadersMapFile, loadersMap);
|
|
276
276
|
const importRuntimeRouterCode = `
|
|
277
|
-
import { createShouldRevalidate, handleRouteModule,
|
|
277
|
+
import { createShouldRevalidate, handleRouteModule, handleRouteModuleError, resolveRouteComponent } from '@${metaName}/runtime/routerHelper';
|
|
278
278
|
`;
|
|
279
279
|
const routeModulesCode = `
|
|
280
280
|
if(typeof document !== 'undefined'){
|
|
@@ -296,19 +296,19 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
296
296
|
};
|
|
297
297
|
function ssrLoaderCombinedModule(entrypoints, entrypoint, config, appContext) {
|
|
298
298
|
const { entryName, isMainEntry } = entrypoint;
|
|
299
|
-
const { packageName
|
|
299
|
+
const { packageName } = appContext;
|
|
300
300
|
const ssr = getEntryOptions(entryName, isMainEntry, config.server.ssr, config.server.ssrByEntries, packageName);
|
|
301
301
|
const ssg = isSSGEntry(config, entryName, entrypoints);
|
|
302
302
|
if (entrypoint.nestedRoutesEntry && (ssr || ssg)) {
|
|
303
303
|
const serverLoaderRuntime = require.resolve('@modern-js/plugin-data-loader/runtime');
|
|
304
|
-
const serverLoadersFile =
|
|
305
|
-
const combinedModule = `export * from "${slash(serverLoaderRuntime)}"; export * from "${
|
|
304
|
+
const serverLoadersFile = './route-server-loaders.js';
|
|
305
|
+
const combinedModule = `export * from "${slash(serverLoaderRuntime)}"; export * from "${serverLoadersFile}"`;
|
|
306
306
|
if (!config.source.enableAsyncEntry) return combinedModule;
|
|
307
307
|
return `
|
|
308
308
|
async function loadModules() {
|
|
309
309
|
const [moduleA, moduleB] = await Promise.all([
|
|
310
310
|
import("${slash(serverLoaderRuntime)}"),
|
|
311
|
-
import("${
|
|
311
|
+
import("${serverLoadersFile}")
|
|
312
312
|
]);
|
|
313
313
|
|
|
314
314
|
return {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import node_path from "node:path";
|
|
2
|
-
import { NESTED_ROUTE_SPEC_FILE, filterRoutesForServer
|
|
2
|
+
import { NESTED_ROUTE_SPEC_FILE, filterRoutesForServer } from "@modern-js/utils";
|
|
3
3
|
import { NESTED_ROUTES_DIR } from "./constants.mjs";
|
|
4
4
|
import { BUILT_IN_ROUTES_OWNER, getEntrypointRoutesDir, getEntrypointRoutesOwner, isRouteEntry } from "./entry.mjs";
|
|
5
5
|
import { handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints } from "./handler.mjs";
|
|
6
|
+
import { updateNestedRoutesSpec } from "./nestedRoutesSpec.mjs";
|
|
6
7
|
function isBuiltInRouteEntrypoint(entrypoint) {
|
|
7
8
|
const entrypointRoutesOwner = getEntrypointRoutesOwner(entrypoint);
|
|
8
9
|
if (entrypointRoutesOwner) return entrypointRoutesOwner === BUILT_IN_ROUTES_OWNER;
|
|
@@ -87,11 +88,7 @@ const routerPlugin = ()=>({
|
|
|
87
88
|
if (isBuiltInRouteEntrypoint(entrypoint)) {
|
|
88
89
|
const { distDirectory } = api.getAppContext();
|
|
89
90
|
const nestedRoutesSpecPath = node_path.resolve(distDirectory, NESTED_ROUTE_SPEC_FILE);
|
|
90
|
-
|
|
91
|
-
await fs.outputJSON(nestedRoutesSpecPath, {
|
|
92
|
-
...existingNestedRoutes,
|
|
93
|
-
...nestedRoutesForServer
|
|
94
|
-
});
|
|
91
|
+
await updateNestedRoutesSpec(nestedRoutesSpecPath, nestedRoutesForServer);
|
|
95
92
|
}
|
|
96
93
|
return {
|
|
97
94
|
entrypoint,
|
|
@@ -102,4 +99,4 @@ const routerPlugin = ()=>({
|
|
|
102
99
|
});
|
|
103
100
|
const cli = routerPlugin;
|
|
104
101
|
export default cli;
|
|
105
|
-
export { BUILT_IN_ROUTES_OWNER, getEntrypointRoutesDir, getEntrypointRoutesOwner, handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints, isRouteEntry, routerPlugin };
|
|
102
|
+
export { BUILT_IN_ROUTES_OWNER, getEntrypointRoutesDir, getEntrypointRoutesOwner, handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints, isRouteEntry, routerPlugin, updateNestedRoutesSpec };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import node_path from "node:path";
|
|
2
|
+
import { setTimeout as promises_setTimeout } from "node:timers/promises";
|
|
3
|
+
import { fs } from "@modern-js/utils";
|
|
4
|
+
const lockPollIntervalMs = 25;
|
|
5
|
+
const staleLockAgeMs = 120000;
|
|
6
|
+
const pendingUpdates = new Map();
|
|
7
|
+
let tempFileCounter = 0;
|
|
8
|
+
async function acquireSpecLock(specPath) {
|
|
9
|
+
const lockDir = `${specPath}.lock`;
|
|
10
|
+
await fs.ensureDir(node_path.dirname(specPath));
|
|
11
|
+
while(true){
|
|
12
|
+
try {
|
|
13
|
+
await fs.mkdir(lockDir);
|
|
14
|
+
return async ()=>{
|
|
15
|
+
await fs.remove(lockDir);
|
|
16
|
+
};
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if ('EEXIST' !== error.code) throw error;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const stat = await fs.stat(lockDir);
|
|
22
|
+
if (performance.timeOrigin + performance.now() - stat.mtimeMs > staleLockAgeMs) {
|
|
23
|
+
await fs.remove(lockDir);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
} catch (error) {
|
|
27
|
+
if ('ENOENT' !== error.code) throw error;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
await promises_setTimeout(lockPollIntervalMs);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function writeJSONAtomically(filePath, value) {
|
|
34
|
+
const directory = node_path.dirname(filePath);
|
|
35
|
+
const tempPath = node_path.join(directory, `.${node_path.basename(filePath)}.${process.pid}.${tempFileCounter += 1}.tmp`);
|
|
36
|
+
try {
|
|
37
|
+
await fs.writeFile(tempPath, `${JSON.stringify(value)}\n`);
|
|
38
|
+
await fs.rename(tempPath, filePath);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
await fs.remove(tempPath).catch(()=>{});
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function updateNestedRoutesSpec(specPath, nextRoutes) {
|
|
45
|
+
const resolvedSpecPath = node_path.resolve(specPath);
|
|
46
|
+
const previousUpdate = pendingUpdates.get(resolvedSpecPath) ?? Promise.resolve();
|
|
47
|
+
const currentUpdate = previousUpdate.catch(()=>void 0).then(async ()=>{
|
|
48
|
+
const releaseLock = await acquireSpecLock(resolvedSpecPath);
|
|
49
|
+
try {
|
|
50
|
+
const existingRoutes = await fs.pathExists(resolvedSpecPath) ? await fs.readJSON(resolvedSpecPath) : {};
|
|
51
|
+
await writeJSONAtomically(resolvedSpecPath, {
|
|
52
|
+
...existingRoutes,
|
|
53
|
+
...nextRoutes
|
|
54
|
+
});
|
|
55
|
+
} finally{
|
|
56
|
+
await releaseLock();
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
pendingUpdates.set(resolvedSpecPath, currentUpdate);
|
|
60
|
+
try {
|
|
61
|
+
await currentUpdate;
|
|
62
|
+
} finally{
|
|
63
|
+
if (pendingUpdates.get(resolvedSpecPath) === currentUpdate) pendingUpdates.delete(resolvedSpecPath);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export { updateNestedRoutesSpec };
|
|
@@ -1,15 +1,54 @@
|
|
|
1
1
|
import { ROUTE_MODULES } from "@modern-js/utils/universal/constants";
|
|
2
|
+
const isObjectLike = (value)=>'object' == typeof value && null !== value || 'function' == typeof value;
|
|
3
|
+
const isRouteComponent = (value)=>'function' == typeof value || isObjectLike(value) && '$$typeof' in value;
|
|
4
|
+
const getRouteModules = ()=>{
|
|
5
|
+
if ("u" < typeof window) return;
|
|
6
|
+
return window[ROUTE_MODULES];
|
|
7
|
+
};
|
|
8
|
+
const storeRouteModule = (routeModule, routeId)=>{
|
|
9
|
+
if ("u" < typeof document) return;
|
|
10
|
+
const routeModules = getRouteModules();
|
|
11
|
+
if (void 0 !== routeModules) routeModules[routeId] = routeModule;
|
|
12
|
+
};
|
|
13
|
+
const unwrapRspackAsyncModule = (routeModule)=>{
|
|
14
|
+
if (!isObjectLike(routeModule)) return routeModule;
|
|
15
|
+
const rspackExportsSymbol = Object.getOwnPropertySymbols(routeModule).find((symbol)=>'rspack exports' === symbol.description);
|
|
16
|
+
if (void 0 !== rspackExportsSymbol) return routeModule[rspackExportsSymbol];
|
|
17
|
+
if ('__webpack_exports__' in routeModule) return routeModule.__webpack_exports__;
|
|
18
|
+
return routeModule;
|
|
19
|
+
};
|
|
2
20
|
const createShouldRevalidate = (routeId)=>(arg)=>{
|
|
3
|
-
const routeModule =
|
|
4
|
-
if (routeModule
|
|
21
|
+
const routeModule = getRouteModules()?.[routeId];
|
|
22
|
+
if (isObjectLike(routeModule)) {
|
|
23
|
+
const shouldRevalidate = routeModule.shouldRevalidate;
|
|
24
|
+
if ('function' == typeof shouldRevalidate) return shouldRevalidate(arg);
|
|
25
|
+
}
|
|
5
26
|
return arg.defaultShouldRevalidate;
|
|
6
27
|
};
|
|
28
|
+
const pickRouteModuleComponent = (routeModule, seen = new Set())=>{
|
|
29
|
+
const unwrappedRouteModule = unwrapRspackAsyncModule(routeModule);
|
|
30
|
+
if (isRouteComponent(unwrappedRouteModule)) return unwrappedRouteModule;
|
|
31
|
+
if (!isObjectLike(unwrappedRouteModule) || seen.has(unwrappedRouteModule)) return;
|
|
32
|
+
seen.add(unwrappedRouteModule);
|
|
33
|
+
const componentModule = unwrappedRouteModule;
|
|
34
|
+
for (const candidate of [
|
|
35
|
+
componentModule.default,
|
|
36
|
+
componentModule.Component
|
|
37
|
+
]){
|
|
38
|
+
const component = pickRouteModuleComponent(candidate, seen);
|
|
39
|
+
if (void 0 !== component) return component;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const resolveRouteComponent = (routeModule)=>pickRouteModuleComponent(routeModule) ?? routeModule;
|
|
7
43
|
const handleRouteModule = (routeModule, routeId)=>{
|
|
8
|
-
|
|
9
|
-
|
|
44
|
+
storeRouteModule(routeModule, routeId);
|
|
45
|
+
const component = pickRouteModuleComponent(routeModule);
|
|
46
|
+
return void 0 !== component ? {
|
|
47
|
+
default: component
|
|
48
|
+
} : routeModule;
|
|
10
49
|
};
|
|
11
50
|
const handleRouteModuleError = (error)=>{
|
|
12
51
|
console.error(error);
|
|
13
52
|
return null;
|
|
14
53
|
};
|
|
15
|
-
export { createShouldRevalidate, handleRouteModule, handleRouteModuleError };
|
|
54
|
+
export { createShouldRevalidate, handleRouteModule, handleRouteModuleError, resolveRouteComponent };
|
|
@@ -44,10 +44,10 @@ const createServerPayload = (routerContext, routes)=>{
|
|
|
44
44
|
routes: routerContext.matches.map((match, index, matches)=>{
|
|
45
45
|
const route = match.route;
|
|
46
46
|
const element = route.element;
|
|
47
|
+
const Component = route.Component;
|
|
47
48
|
const parentMatch = index > 0 ? matches[index - 1] : void 0;
|
|
48
49
|
let processedElement;
|
|
49
|
-
if (element) {
|
|
50
|
-
const ElementComponent = element.type;
|
|
50
|
+
if (element || Component) {
|
|
51
51
|
const elementProps = {
|
|
52
52
|
loaderData: routerContext?.loaderData?.[route.id],
|
|
53
53
|
actionData: routerContext?.actionData?.[route.id],
|
|
@@ -60,7 +60,8 @@ const createServerPayload = (routerContext, routes)=>{
|
|
|
60
60
|
handle: m.route.handle
|
|
61
61
|
}))
|
|
62
62
|
};
|
|
63
|
-
const
|
|
63
|
+
const RouteComponent = Component;
|
|
64
|
+
const routeElement = element ? /*#__PURE__*/ react.cloneElement(element, elementProps) : /*#__PURE__*/ react.createElement(RouteComponent, elementProps);
|
|
64
65
|
processedElement = index === cssInjectionIndex ? /*#__PURE__*/ react.createElement(react.Fragment, null, /*#__PURE__*/ react.createElement(CSSLinks, {
|
|
65
66
|
cssFiles
|
|
66
67
|
}), routeElement) : routeElement;
|
|
@@ -3,7 +3,7 @@ const require = /*#__PURE__*/ __rslib_shim_module__.createRequire(/*#__PURE__*/
|
|
|
3
3
|
import { cleanRequireCache, isReact18 as utils_isReact18 } from "@modern-js/utils";
|
|
4
4
|
import path_0 from "path";
|
|
5
5
|
import { documentPlugin } from "../document/cli/index.mjs";
|
|
6
|
-
import { getEntrypointRoutesDir, getEntrypointRoutesOwner, handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints, isRouteEntry, routerPlugin } from "../router/cli/index.mjs";
|
|
6
|
+
import { getEntrypointRoutesDir, getEntrypointRoutesOwner, handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints, isRouteEntry, routerPlugin, updateNestedRoutesSpec } from "../router/cli/index.mjs";
|
|
7
7
|
import { builderPluginAlias } from "./alias.mjs";
|
|
8
8
|
import { generateCode } from "./code.mjs";
|
|
9
9
|
import { ENTRY_BOOTSTRAP_FILE_NAME, ENTRY_POINT_FILE_NAME } from "./constants.mjs";
|
|
@@ -94,4 +94,4 @@ const cli = runtimePlugin;
|
|
|
94
94
|
export { makeLegalIdentifier } from "../router/cli/code/makeLegalIdentifier.mjs";
|
|
95
95
|
export { getPathWithoutExt } from "../router/cli/code/utils.mjs";
|
|
96
96
|
export default cli;
|
|
97
|
-
export { documentPlugin, getEntrypointRoutesDir, getEntrypointRoutesOwner, handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints, isRouteEntry, isRuntimeEntry, routerPlugin, runtimePlugin, ssrPlugin };
|
|
97
|
+
export { documentPlugin, getEntrypointRoutesDir, getEntrypointRoutesOwner, handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints, isRouteEntry, isRuntimeEntry, routerPlugin, runtimePlugin, ssrPlugin, updateNestedRoutesSpec };
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
createRequestHandler,
|
|
26
26
|
} from '@#metaName/runtime/ssr/server';
|
|
27
27
|
import { RSCServerSlot } from '@#metaName/runtime/rsc/client';
|
|
28
|
-
import { renderRsc } from '@#metaName/runtime/rsc/server';
|
|
28
|
+
import { renderCSRWithRSC, renderRsc } from '@#metaName/runtime/rsc/server';
|
|
29
29
|
export { handleAction } from '@#metaName/runtime/rsc/server';
|
|
30
30
|
|
|
31
31
|
const handleRequest = async (request, ServerRoot, options) => {
|
|
@@ -52,6 +52,17 @@ export const requestHandler = createRequestHandler(handleRequest, {
|
|
|
52
52
|
enableRsc: true
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
+
const handleCSRRender = async (request, ServerRoot, options) => {
|
|
56
|
+
return renderCSRWithRSC({
|
|
57
|
+
html: options.html,
|
|
58
|
+
rscRoot: options.rscRoot,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const renderRscStreamHandler = createRequestHandler(handleCSRRender, {
|
|
63
|
+
enableRsc: true
|
|
64
|
+
});
|
|
65
|
+
|
|
55
66
|
const handleRSCRequest = async (request, ServerRoot, options) => {
|
|
56
67
|
const { serverPayload } = options;
|
|
57
68
|
const stream = renderRsc({
|
|
@@ -38,8 +38,8 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
|
38
38
|
return safeReplace(template, CHUNK_CSS_PLACEHOLDER, css);
|
|
39
39
|
async function getCssChunks() {
|
|
40
40
|
const { routeManifest, routerContext, routes } = runtimeContext;
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
const routeAssets = routeManifest?.routeAssets;
|
|
42
|
+
if (!routeAssets) return '';
|
|
43
43
|
let matchedRouteManifests = [];
|
|
44
44
|
const matchedRouteIds = getRouterMatchedRouteIds(runtimeContext);
|
|
45
45
|
if (matchedRouteIds?.length) matchedRouteManifests = matchedRouteIds.map((routeId)=>routeAssets[routeId]).filter(Boolean);
|
|
@@ -29,8 +29,8 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
|
29
29
|
return safeReplace(template, CHUNK_CSS_PLACEHOLDER, css);
|
|
30
30
|
async function getCssChunks() {
|
|
31
31
|
const { routeManifest, routerContext, routes } = runtimeContext;
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
const routeAssets = routeManifest?.routeAssets;
|
|
33
|
+
if (!routeAssets) return '';
|
|
34
34
|
let matchedRouteManifests = [];
|
|
35
35
|
const matchedRouteIds = getRouterMatchedRouteIds(runtimeContext);
|
|
36
36
|
if (matchedRouteIds?.length) matchedRouteManifests = matchedRouteIds.map((routeId)=>routeAssets[routeId]).filter(Boolean);
|
|
@@ -32,10 +32,6 @@ const readAsset = async (chunk)=>{
|
|
|
32
32
|
return fs.readFile(filepath, 'utf-8');
|
|
33
33
|
};
|
|
34
34
|
class LoadableCollector {
|
|
35
|
-
get existsAssets() {
|
|
36
|
-
const { routeManifest, entryName } = this.options;
|
|
37
|
-
return routeManifest?.routeAssets?.[entryName]?.assets;
|
|
38
|
-
}
|
|
39
35
|
getMatchedRouteChunks() {
|
|
40
36
|
const { routeManifest, runtimeContext } = this.options;
|
|
41
37
|
if (!routeManifest) return [];
|
|
@@ -102,7 +98,7 @@ class LoadableCollector {
|
|
|
102
98
|
const matchs = template.matchAll(jsScriptRegExp);
|
|
103
99
|
const existedScript = [];
|
|
104
100
|
for (const match of matchs)existedScript.push(match[1]);
|
|
105
|
-
const scripts = await Promise.all(chunks.filter((chunk)=>!existedScript.includes(chunk.url)
|
|
101
|
+
const scripts = await Promise.all(chunks.filter((chunk)=>!existedScript.includes(chunk.url)).map(async (chunk)=>{
|
|
106
102
|
const script = `<script${attributes} src="${chunk.url}"></script>`;
|
|
107
103
|
if (checkIsNode() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<script>${content}</script>`).catch((_)=>script);
|
|
108
104
|
return script;
|
|
@@ -113,7 +109,7 @@ class LoadableCollector {
|
|
|
113
109
|
const { template, chunkSet, config, moduleFederationCssAssets } = this.options;
|
|
114
110
|
const { inlineStyles } = config;
|
|
115
111
|
const atrributes = attributesToString(this.generateAttributes());
|
|
116
|
-
const emittedChunks = chunks.filter((chunk)=>!hasStylesheetLink(template, chunk.url)
|
|
112
|
+
const emittedChunks = chunks.filter((chunk)=>!hasStylesheetLink(template, chunk.url));
|
|
117
113
|
const css = await Promise.all(emittedChunks.map(async (chunk)=>{
|
|
118
114
|
const link = `<link${atrributes} href="${chunk.url}" rel="stylesheet" />`;
|
|
119
115
|
if (checkIsNode() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<style>${content}</style>`).catch((_)=>link);
|
|
@@ -5,7 +5,7 @@ import { JS_EXTENSIONS, findExists, formatImportPath, fs, getEntryOptions, isSSG
|
|
|
5
5
|
import { ROUTE_MODULES } from "@modern-js/utils/universal/constants";
|
|
6
6
|
import path from "path";
|
|
7
7
|
import { APP_INIT_EXPORTED, TEMP_LOADERS_DIR } from "../constants.mjs";
|
|
8
|
-
import { getPathWithoutExt,
|
|
8
|
+
import { getPathWithoutExt, parseModule, replaceWithAlias } from "./utils.mjs";
|
|
9
9
|
const routesForServer = ({ routesForServerLoaderMatches })=>{
|
|
10
10
|
const loaders = [];
|
|
11
11
|
const actions = [];
|
|
@@ -171,14 +171,14 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
171
171
|
routeId: route.id,
|
|
172
172
|
webpackChunkName: true
|
|
173
173
|
});
|
|
174
|
-
component = 'string' === ssrMode ? `loadable(${lazyImport})` : `lazy(${lazyImport})`;
|
|
174
|
+
component = 'string' === ssrMode ? `loadable(${lazyImport}, { resolveComponent: resolveRouteComponent })` : `lazy(${lazyImport})`;
|
|
175
175
|
} else {
|
|
176
176
|
components.push(route._component);
|
|
177
177
|
component = `component_${components.length - 1}`;
|
|
178
178
|
}
|
|
179
179
|
} else if (route._component) if (splitRouteChunks) {
|
|
180
180
|
lazyImport = `() => import('${route._component}')`;
|
|
181
|
-
component = `loadable(${lazyImport})`;
|
|
181
|
+
component = `loadable(${lazyImport}, { resolveComponent: resolveRouteComponent })`;
|
|
182
182
|
} else {
|
|
183
183
|
components.push(route._component);
|
|
184
184
|
component = `component_${components.length - 1}`;
|
|
@@ -226,7 +226,7 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
226
226
|
const newRouteStr = regs.reduce((acc, reg)=>acc.replace(reg, '$1$2'), routeStr).replace(/"(RootLayout)"/g, '$1').replace(/\\"/g, '"');
|
|
227
227
|
routeComponentsCode += `${newRouteStr},`;
|
|
228
228
|
} else {
|
|
229
|
-
const component = `loadable(() => import('${route._component}'))`;
|
|
229
|
+
const component = `loadable(() => import('${route._component}'), { resolveComponent: resolveRouteComponent })`;
|
|
230
230
|
const finalRoute = {
|
|
231
231
|
...route,
|
|
232
232
|
component
|
|
@@ -276,7 +276,7 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
276
276
|
await fs.ensureFile(loadersMapFile);
|
|
277
277
|
await fs.writeJSON(loadersMapFile, loadersMap);
|
|
278
278
|
const importRuntimeRouterCode = `
|
|
279
|
-
import { createShouldRevalidate, handleRouteModule,
|
|
279
|
+
import { createShouldRevalidate, handleRouteModule, handleRouteModuleError, resolveRouteComponent } from '@${metaName}/runtime/routerHelper';
|
|
280
280
|
`;
|
|
281
281
|
const routeModulesCode = `
|
|
282
282
|
if(typeof document !== 'undefined'){
|
|
@@ -298,19 +298,19 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
298
298
|
};
|
|
299
299
|
function ssrLoaderCombinedModule(entrypoints, entrypoint, config, appContext) {
|
|
300
300
|
const { entryName, isMainEntry } = entrypoint;
|
|
301
|
-
const { packageName
|
|
301
|
+
const { packageName } = appContext;
|
|
302
302
|
const ssr = getEntryOptions(entryName, isMainEntry, config.server.ssr, config.server.ssrByEntries, packageName);
|
|
303
303
|
const ssg = isSSGEntry(config, entryName, entrypoints);
|
|
304
304
|
if (entrypoint.nestedRoutesEntry && (ssr || ssg)) {
|
|
305
305
|
const serverLoaderRuntime = require.resolve('@modern-js/plugin-data-loader/runtime');
|
|
306
|
-
const serverLoadersFile =
|
|
307
|
-
const combinedModule = `export * from "${slash(serverLoaderRuntime)}"; export * from "${
|
|
306
|
+
const serverLoadersFile = './route-server-loaders.js';
|
|
307
|
+
const combinedModule = `export * from "${slash(serverLoaderRuntime)}"; export * from "${serverLoadersFile}"`;
|
|
308
308
|
if (!config.source.enableAsyncEntry) return combinedModule;
|
|
309
309
|
return `
|
|
310
310
|
async function loadModules() {
|
|
311
311
|
const [moduleA, moduleB] = await Promise.all([
|
|
312
312
|
import("${slash(serverLoaderRuntime)}"),
|
|
313
|
-
import("${
|
|
313
|
+
import("${serverLoadersFile}")
|
|
314
314
|
]);
|
|
315
315
|
|
|
316
316
|
return {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import node_path from "node:path";
|
|
3
|
-
import { NESTED_ROUTE_SPEC_FILE, filterRoutesForServer
|
|
3
|
+
import { NESTED_ROUTE_SPEC_FILE, filterRoutesForServer } from "@modern-js/utils";
|
|
4
4
|
import { NESTED_ROUTES_DIR } from "./constants.mjs";
|
|
5
5
|
import { BUILT_IN_ROUTES_OWNER, getEntrypointRoutesDir, getEntrypointRoutesOwner, isRouteEntry } from "./entry.mjs";
|
|
6
6
|
import { handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints } from "./handler.mjs";
|
|
7
|
+
import { updateNestedRoutesSpec } from "./nestedRoutesSpec.mjs";
|
|
7
8
|
import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
|
|
8
9
|
import { dirname as __rspack_dirname } from "node:path";
|
|
9
10
|
var cli_dirname = __rspack_dirname(__rspack_fileURLToPath(import.meta.url));
|
|
@@ -91,11 +92,7 @@ const routerPlugin = ()=>({
|
|
|
91
92
|
if (isBuiltInRouteEntrypoint(entrypoint)) {
|
|
92
93
|
const { distDirectory } = api.getAppContext();
|
|
93
94
|
const nestedRoutesSpecPath = node_path.resolve(distDirectory, NESTED_ROUTE_SPEC_FILE);
|
|
94
|
-
|
|
95
|
-
await fs.outputJSON(nestedRoutesSpecPath, {
|
|
96
|
-
...existingNestedRoutes,
|
|
97
|
-
...nestedRoutesForServer
|
|
98
|
-
});
|
|
95
|
+
await updateNestedRoutesSpec(nestedRoutesSpecPath, nestedRoutesForServer);
|
|
99
96
|
}
|
|
100
97
|
return {
|
|
101
98
|
entrypoint,
|
|
@@ -106,4 +103,4 @@ const routerPlugin = ()=>({
|
|
|
106
103
|
});
|
|
107
104
|
const cli = routerPlugin;
|
|
108
105
|
export default cli;
|
|
109
|
-
export { BUILT_IN_ROUTES_OWNER, getEntrypointRoutesDir, getEntrypointRoutesOwner, handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints, isRouteEntry, routerPlugin };
|
|
106
|
+
export { BUILT_IN_ROUTES_OWNER, getEntrypointRoutesDir, getEntrypointRoutesOwner, handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints, isRouteEntry, routerPlugin, updateNestedRoutesSpec };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import node_path from "node:path";
|
|
3
|
+
import { setTimeout as promises_setTimeout } from "node:timers/promises";
|
|
4
|
+
import { fs } from "@modern-js/utils";
|
|
5
|
+
const lockPollIntervalMs = 25;
|
|
6
|
+
const staleLockAgeMs = 120000;
|
|
7
|
+
const pendingUpdates = new Map();
|
|
8
|
+
let tempFileCounter = 0;
|
|
9
|
+
async function acquireSpecLock(specPath) {
|
|
10
|
+
const lockDir = `${specPath}.lock`;
|
|
11
|
+
await fs.ensureDir(node_path.dirname(specPath));
|
|
12
|
+
while(true){
|
|
13
|
+
try {
|
|
14
|
+
await fs.mkdir(lockDir);
|
|
15
|
+
return async ()=>{
|
|
16
|
+
await fs.remove(lockDir);
|
|
17
|
+
};
|
|
18
|
+
} catch (error) {
|
|
19
|
+
if ('EEXIST' !== error.code) throw error;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const stat = await fs.stat(lockDir);
|
|
23
|
+
if (performance.timeOrigin + performance.now() - stat.mtimeMs > staleLockAgeMs) {
|
|
24
|
+
await fs.remove(lockDir);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if ('ENOENT' !== error.code) throw error;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
await promises_setTimeout(lockPollIntervalMs);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function writeJSONAtomically(filePath, value) {
|
|
35
|
+
const directory = node_path.dirname(filePath);
|
|
36
|
+
const tempPath = node_path.join(directory, `.${node_path.basename(filePath)}.${process.pid}.${tempFileCounter += 1}.tmp`);
|
|
37
|
+
try {
|
|
38
|
+
await fs.writeFile(tempPath, `${JSON.stringify(value)}\n`);
|
|
39
|
+
await fs.rename(tempPath, filePath);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
await fs.remove(tempPath).catch(()=>{});
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function updateNestedRoutesSpec(specPath, nextRoutes) {
|
|
46
|
+
const resolvedSpecPath = node_path.resolve(specPath);
|
|
47
|
+
const previousUpdate = pendingUpdates.get(resolvedSpecPath) ?? Promise.resolve();
|
|
48
|
+
const currentUpdate = previousUpdate.catch(()=>void 0).then(async ()=>{
|
|
49
|
+
const releaseLock = await acquireSpecLock(resolvedSpecPath);
|
|
50
|
+
try {
|
|
51
|
+
const existingRoutes = await fs.pathExists(resolvedSpecPath) ? await fs.readJSON(resolvedSpecPath) : {};
|
|
52
|
+
await writeJSONAtomically(resolvedSpecPath, {
|
|
53
|
+
...existingRoutes,
|
|
54
|
+
...nextRoutes
|
|
55
|
+
});
|
|
56
|
+
} finally{
|
|
57
|
+
await releaseLock();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
pendingUpdates.set(resolvedSpecPath, currentUpdate);
|
|
61
|
+
try {
|
|
62
|
+
await currentUpdate;
|
|
63
|
+
} finally{
|
|
64
|
+
if (pendingUpdates.get(resolvedSpecPath) === currentUpdate) pendingUpdates.delete(resolvedSpecPath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export { updateNestedRoutesSpec };
|
|
@@ -1,16 +1,55 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { ROUTE_MODULES } from "@modern-js/utils/universal/constants";
|
|
3
|
+
const isObjectLike = (value)=>'object' == typeof value && null !== value || 'function' == typeof value;
|
|
4
|
+
const isRouteComponent = (value)=>'function' == typeof value || isObjectLike(value) && '$$typeof' in value;
|
|
5
|
+
const getRouteModules = ()=>{
|
|
6
|
+
if ("u" < typeof window) return;
|
|
7
|
+
return window[ROUTE_MODULES];
|
|
8
|
+
};
|
|
9
|
+
const storeRouteModule = (routeModule, routeId)=>{
|
|
10
|
+
if ("u" < typeof document) return;
|
|
11
|
+
const routeModules = getRouteModules();
|
|
12
|
+
if (void 0 !== routeModules) routeModules[routeId] = routeModule;
|
|
13
|
+
};
|
|
14
|
+
const unwrapRspackAsyncModule = (routeModule)=>{
|
|
15
|
+
if (!isObjectLike(routeModule)) return routeModule;
|
|
16
|
+
const rspackExportsSymbol = Object.getOwnPropertySymbols(routeModule).find((symbol)=>'rspack exports' === symbol.description);
|
|
17
|
+
if (void 0 !== rspackExportsSymbol) return routeModule[rspackExportsSymbol];
|
|
18
|
+
if ('__webpack_exports__' in routeModule) return routeModule.__webpack_exports__;
|
|
19
|
+
return routeModule;
|
|
20
|
+
};
|
|
3
21
|
const createShouldRevalidate = (routeId)=>(arg)=>{
|
|
4
|
-
const routeModule =
|
|
5
|
-
if (routeModule
|
|
22
|
+
const routeModule = getRouteModules()?.[routeId];
|
|
23
|
+
if (isObjectLike(routeModule)) {
|
|
24
|
+
const shouldRevalidate = routeModule.shouldRevalidate;
|
|
25
|
+
if ('function' == typeof shouldRevalidate) return shouldRevalidate(arg);
|
|
26
|
+
}
|
|
6
27
|
return arg.defaultShouldRevalidate;
|
|
7
28
|
};
|
|
29
|
+
const pickRouteModuleComponent = (routeModule, seen = new Set())=>{
|
|
30
|
+
const unwrappedRouteModule = unwrapRspackAsyncModule(routeModule);
|
|
31
|
+
if (isRouteComponent(unwrappedRouteModule)) return unwrappedRouteModule;
|
|
32
|
+
if (!isObjectLike(unwrappedRouteModule) || seen.has(unwrappedRouteModule)) return;
|
|
33
|
+
seen.add(unwrappedRouteModule);
|
|
34
|
+
const componentModule = unwrappedRouteModule;
|
|
35
|
+
for (const candidate of [
|
|
36
|
+
componentModule.default,
|
|
37
|
+
componentModule.Component
|
|
38
|
+
]){
|
|
39
|
+
const component = pickRouteModuleComponent(candidate, seen);
|
|
40
|
+
if (void 0 !== component) return component;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const resolveRouteComponent = (routeModule)=>pickRouteModuleComponent(routeModule) ?? routeModule;
|
|
8
44
|
const handleRouteModule = (routeModule, routeId)=>{
|
|
9
|
-
|
|
10
|
-
|
|
45
|
+
storeRouteModule(routeModule, routeId);
|
|
46
|
+
const component = pickRouteModuleComponent(routeModule);
|
|
47
|
+
return void 0 !== component ? {
|
|
48
|
+
default: component
|
|
49
|
+
} : routeModule;
|
|
11
50
|
};
|
|
12
51
|
const handleRouteModuleError = (error)=>{
|
|
13
52
|
console.error(error);
|
|
14
53
|
return null;
|
|
15
54
|
};
|
|
16
|
-
export { createShouldRevalidate, handleRouteModule, handleRouteModuleError };
|
|
55
|
+
export { createShouldRevalidate, handleRouteModule, handleRouteModuleError, resolveRouteComponent };
|
|
@@ -45,10 +45,10 @@ const createServerPayload = (routerContext, routes)=>{
|
|
|
45
45
|
routes: routerContext.matches.map((match, index, matches)=>{
|
|
46
46
|
const route = match.route;
|
|
47
47
|
const element = route.element;
|
|
48
|
+
const Component = route.Component;
|
|
48
49
|
const parentMatch = index > 0 ? matches[index - 1] : void 0;
|
|
49
50
|
let processedElement;
|
|
50
|
-
if (element) {
|
|
51
|
-
const ElementComponent = element.type;
|
|
51
|
+
if (element || Component) {
|
|
52
52
|
const elementProps = {
|
|
53
53
|
loaderData: routerContext?.loaderData?.[route.id],
|
|
54
54
|
actionData: routerContext?.actionData?.[route.id],
|
|
@@ -61,7 +61,8 @@ const createServerPayload = (routerContext, routes)=>{
|
|
|
61
61
|
handle: m.route.handle
|
|
62
62
|
}))
|
|
63
63
|
};
|
|
64
|
-
const
|
|
64
|
+
const RouteComponent = Component;
|
|
65
|
+
const routeElement = element ? /*#__PURE__*/ react.cloneElement(element, elementProps) : /*#__PURE__*/ react.createElement(RouteComponent, elementProps);
|
|
65
66
|
processedElement = index === cssInjectionIndex ? /*#__PURE__*/ react.createElement(react.Fragment, null, /*#__PURE__*/ react.createElement(CSSLinks, {
|
|
66
67
|
cssFiles
|
|
67
68
|
}), routeElement) : routeElement;
|
|
@@ -2,7 +2,7 @@ import type { AppTools, CliPlugin } from '@modern-js/app-tools';
|
|
|
2
2
|
import { documentPlugin } from '../document/cli';
|
|
3
3
|
import { routerPlugin } from '../router/cli';
|
|
4
4
|
import { ssrPlugin } from './ssr';
|
|
5
|
-
export { getEntrypointRoutesDir, getEntrypointRoutesOwner, handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints, isRouteEntry, } from '../router/cli';
|
|
5
|
+
export { getEntrypointRoutesDir, getEntrypointRoutesOwner, handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints, isRouteEntry, updateNestedRoutesSpec, } from '../router/cli';
|
|
6
6
|
export { makeLegalIdentifier } from '../router/cli/code/makeLegalIdentifier';
|
|
7
7
|
export { getPathWithoutExt } from '../router/cli/code/utils';
|
|
8
8
|
export { isRuntimeEntry } from './entry';
|
|
@@ -30,7 +30,6 @@ export declare class LoadableCollector implements Collector {
|
|
|
30
30
|
private options;
|
|
31
31
|
private extractor?;
|
|
32
32
|
constructor(options: LoadableCollectorOptions);
|
|
33
|
-
private get existsAssets();
|
|
34
33
|
private getMatchedRouteChunks;
|
|
35
34
|
collect(comopnent: ReactElement): ReactElement;
|
|
36
35
|
effect(): Promise<void>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AppTools, CliPlugin } from '@modern-js/app-tools';
|
|
2
2
|
export { BUILT_IN_ROUTES_OWNER, getEntrypointRoutesDir, getEntrypointRoutesOwner, isRouteEntry, } from './entry';
|
|
3
3
|
export { handleFileChange, handleGeneratorEntryCode, handleModifyEntrypoints, } from './handler';
|
|
4
|
+
export { updateNestedRoutesSpec } from './nestedRoutesSpec';
|
|
4
5
|
export declare const routerPlugin: () => CliPlugin<AppTools>;
|
|
5
6
|
export default routerPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function updateNestedRoutesSpec(specPath: string, nextRoutes: Record<string, unknown>): Promise<void>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ShouldRevalidateFunction } from '@modern-js/runtime-utils/router';
|
|
2
|
-
import type
|
|
2
|
+
import type { ElementType } from 'react';
|
|
3
3
|
export declare const createShouldRevalidate: (routeId: string) => ShouldRevalidateFunction;
|
|
4
|
-
export declare const
|
|
4
|
+
export declare const resolveRouteComponent: (routeModule: unknown) => ElementType<Record<string, unknown>>;
|
|
5
|
+
export declare const handleRouteModule: (routeModule: unknown, routeId: string) => unknown;
|
|
5
6
|
export declare const handleRouteModuleError: (error: Error) => null;
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"modern",
|
|
18
18
|
"modern.js"
|
|
19
19
|
],
|
|
20
|
-
"version": "3.4.0-ultramodern.
|
|
20
|
+
"version": "3.4.0-ultramodern.3",
|
|
21
21
|
"engines": {
|
|
22
22
|
"node": ">=20"
|
|
23
23
|
},
|
|
@@ -240,12 +240,12 @@
|
|
|
240
240
|
"react-helmet": "^6.1.0",
|
|
241
241
|
"react-helmet-async": "3.0.0",
|
|
242
242
|
"react-is": "^19.2.7",
|
|
243
|
-
"@modern-js/plugin
|
|
244
|
-
"@modern-js/
|
|
245
|
-
"@modern-js/render": "npm:@bleedingdev/modern-js-render@3.4.0-ultramodern.
|
|
246
|
-
"@modern-js/
|
|
247
|
-
"@modern-js/
|
|
248
|
-
"@modern-js/
|
|
243
|
+
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.4.0-ultramodern.3",
|
|
244
|
+
"@modern-js/plugin-data-loader": "npm:@bleedingdev/modern-js-plugin-data-loader@3.4.0-ultramodern.3",
|
|
245
|
+
"@modern-js/render": "npm:@bleedingdev/modern-js-render@3.4.0-ultramodern.3",
|
|
246
|
+
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.4.0-ultramodern.3",
|
|
247
|
+
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.4.0-ultramodern.3",
|
|
248
|
+
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.4.0-ultramodern.3"
|
|
249
249
|
},
|
|
250
250
|
"peerDependencies": {
|
|
251
251
|
"react": "^19.2.7",
|
|
@@ -265,7 +265,7 @@
|
|
|
265
265
|
"react-dom": "^19.2.7",
|
|
266
266
|
"ts-node": "^10.9.2",
|
|
267
267
|
"typescript": "^6.0.3",
|
|
268
|
-
"@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.4.0-ultramodern.
|
|
268
|
+
"@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.4.0-ultramodern.3",
|
|
269
269
|
"@scripts/rstest-config": "2.66.0"
|
|
270
270
|
},
|
|
271
271
|
"sideEffects": false,
|