@modern-js/runtime 3.0.3 → 3.0.5
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 +1 -1
- package/dist/cjs/cli/ssr/index.js +27 -28
- package/dist/cjs/document/cli/index.js +4 -0
- package/dist/cjs/router/cli/code/index.js +6 -2
- package/dist/cjs/router/cli/code/templates.js +37 -9
- package/dist/cjs/router/runtime/rsc-router.js +119 -55
- package/dist/cjs/router/runtime/utils.js +5 -0
- package/dist/esm/cli/index.mjs +1 -1
- package/dist/esm/cli/ssr/index.mjs +27 -28
- package/dist/esm/document/cli/index.mjs +4 -0
- package/dist/esm/router/cli/code/index.mjs +6 -2
- package/dist/esm/router/cli/code/templates.mjs +38 -10
- package/dist/esm/router/runtime/rsc-router.mjs +119 -55
- package/dist/esm/router/runtime/utils.mjs +5 -0
- package/dist/esm-node/cli/index.mjs +1 -1
- package/dist/esm-node/cli/ssr/index.mjs +27 -28
- package/dist/esm-node/document/cli/index.mjs +4 -0
- package/dist/esm-node/router/cli/code/index.mjs +6 -2
- package/dist/esm-node/router/cli/code/templates.mjs +38 -10
- package/dist/esm-node/router/runtime/rsc-router.mjs +119 -55
- package/dist/esm-node/router/runtime/utils.mjs +5 -0
- package/dist/types/core/context/serverPayload/index.server.d.ts +5 -5
- package/dist/types/router/cli/code/templates.d.ts +4 -2
- package/dist/types/router/runtime/rsc-router.d.ts +8 -2
- package/dist/types/router/runtime/types.d.ts +17 -0
- package/package.json +8 -8
package/dist/cjs/cli/index.js
CHANGED
|
@@ -105,7 +105,7 @@ const runtimePlugin = (params)=>({
|
|
|
105
105
|
'process.env.IS_REACT18': process.env.IS_REACT18
|
|
106
106
|
},
|
|
107
107
|
include: [
|
|
108
|
-
new RegExp(`[\\\\/]node_modules[\\\\/]@${metaName}[\\\\/]runtime[\\\\/].*[\\\\/]head
|
|
108
|
+
new RegExp(`[\\\\/]node_modules[\\\\/]@${metaName}[\\\\/]runtime[\\\\/].*[\\\\/]head\\.`)
|
|
109
109
|
]
|
|
110
110
|
},
|
|
111
111
|
tools: {
|
|
@@ -57,7 +57,7 @@ const checkUseStringSSR = (config, appDirectory, entrypoints)=>{
|
|
|
57
57
|
}
|
|
58
58
|
return true;
|
|
59
59
|
};
|
|
60
|
-
const ssrBuilderPlugin = (modernAPI, outputModule)=>({
|
|
60
|
+
const ssrBuilderPlugin = (modernAPI, outputModule, exportLoadablePath)=>({
|
|
61
61
|
name: '@modern-js/builder-plugin-ssr',
|
|
62
62
|
setup (api) {
|
|
63
63
|
api.modifyEnvironmentConfig((config, { name, mergeEnvironmentConfig })=>{
|
|
@@ -67,6 +67,7 @@ const ssrBuilderPlugin = (modernAPI, outputModule)=>({
|
|
|
67
67
|
const appContext = modernAPI.getAppContext();
|
|
68
68
|
const { appDirectory, entrypoints } = appContext;
|
|
69
69
|
const useLoadablePlugin = (0, utils_namespaceObject.isUseSSRBundle)(userConfig) && !isServerEnvironment && checkUseStringSSR(userConfig, appDirectory, entrypoints);
|
|
70
|
+
const useLoadableComponents = (0, utils_namespaceObject.isUseSSRBundle)(userConfig) && checkUseStringSSR(userConfig, appDirectory, entrypoints);
|
|
70
71
|
return mergeEnvironmentConfig(config, {
|
|
71
72
|
source: {
|
|
72
73
|
define: {
|
|
@@ -84,33 +85,8 @@ const ssrBuilderPlugin = (modernAPI, outputModule)=>({
|
|
|
84
85
|
filename: utils_namespaceObject.LOADABLE_STATS_FILE
|
|
85
86
|
}
|
|
86
87
|
]);
|
|
87
|
-
} : void 0
|
|
88
|
-
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
const ssrPlugin = ()=>({
|
|
94
|
-
name: '@modern-js/plugin-ssr',
|
|
95
|
-
required: [
|
|
96
|
-
'@modern-js/runtime'
|
|
97
|
-
],
|
|
98
|
-
setup: (api)=>{
|
|
99
|
-
const appContext = api.getAppContext();
|
|
100
|
-
const exportLoadablePath = `@${appContext.metaName}/runtime/loadable`;
|
|
101
|
-
const runtimeUtilsPath = require.resolve('@modern-js/runtime-utils/node');
|
|
102
|
-
const aliasPath = runtimeUtilsPath.replace(`${external_path_default().sep}cjs${external_path_default().sep}`, `${external_path_default().sep}esm${external_path_default().sep}`).replace(/\.js$/, '.mjs');
|
|
103
|
-
api.config(()=>({
|
|
104
|
-
builderPlugins: [
|
|
105
|
-
ssrBuilderPlugin(api, 'module' === appContext.moduleType)
|
|
106
|
-
],
|
|
107
|
-
resolve: {
|
|
108
|
-
alias: {
|
|
109
|
-
'@modern-js/runtime-utils/node$': aliasPath
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
tools: {
|
|
113
|
-
swc: {
|
|
88
|
+
} : void 0,
|
|
89
|
+
swc: useLoadableComponents ? {
|
|
114
90
|
jsc: {
|
|
115
91
|
experimental: {
|
|
116
92
|
plugins: [
|
|
@@ -140,6 +116,29 @@ const ssrPlugin = ()=>({
|
|
|
140
116
|
]
|
|
141
117
|
}
|
|
142
118
|
}
|
|
119
|
+
} : void 0
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
const ssrPlugin = ()=>({
|
|
126
|
+
name: '@modern-js/plugin-ssr',
|
|
127
|
+
required: [
|
|
128
|
+
'@modern-js/runtime'
|
|
129
|
+
],
|
|
130
|
+
setup: (api)=>{
|
|
131
|
+
const appContext = api.getAppContext();
|
|
132
|
+
const exportLoadablePath = `@${appContext.metaName}/runtime/loadable`;
|
|
133
|
+
const runtimeUtilsPath = require.resolve('@modern-js/runtime-utils/node');
|
|
134
|
+
const aliasPath = runtimeUtilsPath.replace(`${external_path_default().sep}cjs${external_path_default().sep}`, `${external_path_default().sep}esm${external_path_default().sep}`).replace(/\.js$/, '.mjs');
|
|
135
|
+
api.config(()=>({
|
|
136
|
+
builderPlugins: [
|
|
137
|
+
ssrBuilderPlugin(api, 'module' === appContext.moduleType, exportLoadablePath)
|
|
138
|
+
],
|
|
139
|
+
resolve: {
|
|
140
|
+
alias: {
|
|
141
|
+
'@modern-js/runtime-utils/node$': aliasPath
|
|
143
142
|
}
|
|
144
143
|
}
|
|
145
144
|
}));
|
|
@@ -129,6 +129,10 @@ const configureChildCompiler = (child, compiler, appDirectory)=>{
|
|
|
129
129
|
node: true
|
|
130
130
|
};
|
|
131
131
|
child.options.devtool = false;
|
|
132
|
+
if (child.options.optimization && 'object' == typeof child.options.optimization) child.options.optimization.minimize = false;
|
|
133
|
+
else child.options.optimization = {
|
|
134
|
+
minimize: false
|
|
135
|
+
};
|
|
132
136
|
};
|
|
133
137
|
const applyExternalsPlugin = (child, compiler)=>{
|
|
134
138
|
const ExternalsPlugin = compiler.rspack?.ExternalsPlugin;
|
|
@@ -130,7 +130,9 @@ const generateCode = async (appContext, config, entrypoints, api)=>{
|
|
|
130
130
|
entryName: entrypoint.entryName,
|
|
131
131
|
internalDirectory,
|
|
132
132
|
splitRouteChunks: config?.output?.splitRouteChunks,
|
|
133
|
-
|
|
133
|
+
isRscClientBundle: (0, utils_namespaceObject.isUseRsc)(config),
|
|
134
|
+
srcDirectory,
|
|
135
|
+
internalSrcAlias: appContext.internalSrcAlias
|
|
134
136
|
})
|
|
135
137
|
});
|
|
136
138
|
if (entrypoint.nestedRoutesEntry && ((0, utils_namespaceObject.isUseSSRBundle)(config) || (0, utils_namespaceObject.isUseRsc)(config))) {
|
|
@@ -150,7 +152,9 @@ const generateCode = async (appContext, config, entrypoints, api)=>{
|
|
|
150
152
|
entryName: entrypoint.entryName,
|
|
151
153
|
internalDirectory,
|
|
152
154
|
splitRouteChunks: config?.output?.splitRouteChunks,
|
|
153
|
-
|
|
155
|
+
isRscClientBundle: false,
|
|
156
|
+
srcDirectory,
|
|
157
|
+
internalSrcAlias: appContext.internalSrcAlias
|
|
154
158
|
});
|
|
155
159
|
await utils_namespaceObject.fs.outputFile(external_path_default().resolve(internalDirectory, `./${entryName}/routes.server.js`), serverRoutesCode, 'utf8');
|
|
156
160
|
}
|
|
@@ -38,6 +38,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
38
38
|
routesForServer: ()=>routesForServer,
|
|
39
39
|
fileSystemRoutes: ()=>fileSystemRoutes
|
|
40
40
|
});
|
|
41
|
+
const promises_namespaceObject = require("node:fs/promises");
|
|
41
42
|
const external_path_namespaceObject = require("path");
|
|
42
43
|
var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
|
|
43
44
|
const utils_namespaceObject = require("@modern-js/utils");
|
|
@@ -111,7 +112,25 @@ const routesForServer = ({ routesForServerLoaderMatches })=>{
|
|
|
111
112
|
`;
|
|
112
113
|
};
|
|
113
114
|
const createMatchReg = (keyword)=>new RegExp(`("${keyword}":\\s)"([^\n]+)"`, 'g');
|
|
114
|
-
|
|
115
|
+
async function hasUseClientDirective(componentPath, srcDirectory, internalSrcAlias) {
|
|
116
|
+
let realPath = componentPath;
|
|
117
|
+
if (internalSrcAlias && srcDirectory && realPath.startsWith(internalSrcAlias)) realPath = external_path_default().join(srcDirectory, realPath.slice(internalSrcAlias.length));
|
|
118
|
+
const filePath = (0, utils_namespaceObject.findExists)(utils_namespaceObject.JS_EXTENSIONS.map((ext)=>`${realPath}${ext}`));
|
|
119
|
+
if (!filePath) return false;
|
|
120
|
+
let fh;
|
|
121
|
+
try {
|
|
122
|
+
fh = await (0, promises_namespaceObject.open)(filePath, 'r');
|
|
123
|
+
const buf = Buffer.alloc(64);
|
|
124
|
+
const { bytesRead } = await fh.read(buf, 0, 64, 0);
|
|
125
|
+
const content = buf.toString('utf-8', 0, bytesRead).trimStart();
|
|
126
|
+
return content.startsWith("'use client'") || content.startsWith('"use client"');
|
|
127
|
+
} catch {
|
|
128
|
+
return false;
|
|
129
|
+
} finally{
|
|
130
|
+
await fh?.close();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry, entryName, internalDirectory, splitRouteChunks = true, isRscClientBundle = false, srcDirectory, internalSrcAlias })=>{
|
|
115
134
|
const components = [];
|
|
116
135
|
const loadings = [];
|
|
117
136
|
const errors = [];
|
|
@@ -136,9 +155,9 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
136
155
|
const importOptions = webpackChunkName ? `/* webpackChunkName: "${routeId}" */ ` : eager ? '/* webpackMode: "eager" */ ' : '';
|
|
137
156
|
return `() => import(${importOptions}'${componentPath}').then(routeModule => handleRouteModule(routeModule, "${routeId}")).catch(handleRouteModuleError)`;
|
|
138
157
|
};
|
|
139
|
-
const traverseRouteTree = (route,
|
|
158
|
+
const traverseRouteTree = async (route, isRscClientBundle)=>{
|
|
140
159
|
let children;
|
|
141
|
-
if ('children' in route && route.children) children = route
|
|
160
|
+
if ('children' in route && route.children) children = await Promise.all(route.children.map((child)=>traverseRouteTree(child, isRscClientBundle)));
|
|
142
161
|
let loading;
|
|
143
162
|
let error;
|
|
144
163
|
let loader;
|
|
@@ -210,6 +229,8 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
210
229
|
components.push(route._component);
|
|
211
230
|
component = `component_${components.length - 1}`;
|
|
212
231
|
}
|
|
232
|
+
const isClientComponent = 'nested' === route.type && Boolean(route._component) && Boolean(srcDirectory) && Boolean(internalSrcAlias) && await hasUseClientDirective(route._component, srcDirectory, internalSrcAlias);
|
|
233
|
+
const shouldIncludeClientBundle = !isRscClientBundle || isClientComponent;
|
|
213
234
|
const finalRoute = {
|
|
214
235
|
...route,
|
|
215
236
|
loading,
|
|
@@ -217,10 +238,17 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
217
238
|
action,
|
|
218
239
|
config,
|
|
219
240
|
error,
|
|
220
|
-
children
|
|
241
|
+
children,
|
|
242
|
+
...isClientComponent && {
|
|
243
|
+
isClientComponent: true
|
|
244
|
+
},
|
|
245
|
+
...shouldIncludeClientBundle && {
|
|
246
|
+
lazyImport
|
|
247
|
+
},
|
|
248
|
+
...shouldIncludeClientBundle && route._component && {
|
|
249
|
+
component
|
|
250
|
+
}
|
|
221
251
|
};
|
|
222
|
-
if (!isRscClient) finalRoute.lazyImport = lazyImport;
|
|
223
|
-
if (route._component && !isRscClient) finalRoute.component = component;
|
|
224
252
|
if ('nested' === route.type && route._component && (route.loader || route.data)) finalRoute.shouldRevalidate = `createShouldRevalidate("${route.id}")`;
|
|
225
253
|
return finalRoute;
|
|
226
254
|
};
|
|
@@ -228,7 +256,7 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
228
256
|
export const routes = [
|
|
229
257
|
`;
|
|
230
258
|
for (const route of routes)if ('type' in route) {
|
|
231
|
-
const newRoute = traverseRouteTree(route,
|
|
259
|
+
const newRoute = await traverseRouteTree(route, isRscClientBundle);
|
|
232
260
|
const routeStr = JSON.stringify(newRoute, null, 2);
|
|
233
261
|
const keywords = [
|
|
234
262
|
'component',
|
|
@@ -303,9 +331,9 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
|
|
|
303
331
|
`;
|
|
304
332
|
return `
|
|
305
333
|
${importLazyCode}
|
|
306
|
-
${!
|
|
334
|
+
${!isRscClientBundle ? importComponentsCode : ''}
|
|
307
335
|
${importRuntimeRouterCode}
|
|
308
|
-
${!
|
|
336
|
+
${!isRscClientBundle ? rootLayoutCode : ''}
|
|
309
337
|
${importLoadingCode}
|
|
310
338
|
${importErrorComponentsCode}
|
|
311
339
|
${importLoadersCode}
|
|
@@ -45,8 +45,9 @@ const router_namespaceObject = require("@modern-js/runtime-utils/router");
|
|
|
45
45
|
const external_react_namespaceObject = require("react");
|
|
46
46
|
var external_react_default = /*#__PURE__*/ __webpack_require__.n(external_react_namespaceObject);
|
|
47
47
|
const external_CSSLinks_js_namespaceObject = require("./CSSLinks.js");
|
|
48
|
-
const safeUse = (
|
|
49
|
-
|
|
48
|
+
const safeUse = (value)=>{
|
|
49
|
+
const reactUse = external_react_default().use;
|
|
50
|
+
if ('function' == typeof reactUse) return reactUse(value);
|
|
50
51
|
return null;
|
|
51
52
|
};
|
|
52
53
|
function collectCssFilesFromRoutes(matches, routes) {
|
|
@@ -64,6 +65,17 @@ function collectCssFilesFromRoutes(matches, routes) {
|
|
|
64
65
|
}
|
|
65
66
|
const createServerPayload = (routerContext, routes)=>{
|
|
66
67
|
const cssFiles = collectCssFilesFromRoutes(routerContext.matches, routes);
|
|
68
|
+
let cssInjectionIndex = -1;
|
|
69
|
+
if (cssFiles.length > 0) {
|
|
70
|
+
for(let i = routerContext.matches.length - 1; i >= 0; i--){
|
|
71
|
+
const matchRoute = findRouteInTree(routes, routerContext.matches[i].route.id);
|
|
72
|
+
if (matchRoute && !matchRoute.isClientComponent) {
|
|
73
|
+
cssInjectionIndex = i;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (-1 === cssInjectionIndex) cssInjectionIndex = routerContext.matches.length - 1;
|
|
78
|
+
}
|
|
67
79
|
return {
|
|
68
80
|
type: 'render',
|
|
69
81
|
actionData: routerContext.actionData,
|
|
@@ -71,45 +83,42 @@ const createServerPayload = (routerContext, routes)=>{
|
|
|
71
83
|
loaderData: routerContext.loaderData,
|
|
72
84
|
location: routerContext.location,
|
|
73
85
|
routes: routerContext.matches.map((match, index, matches)=>{
|
|
74
|
-
const
|
|
86
|
+
const route = match.route;
|
|
87
|
+
const element = route.element;
|
|
75
88
|
const parentMatch = index > 0 ? matches[index - 1] : void 0;
|
|
76
89
|
let processedElement;
|
|
77
90
|
if (element) {
|
|
78
91
|
const ElementComponent = element.type;
|
|
79
92
|
const elementProps = {
|
|
80
|
-
loaderData: routerContext?.loaderData?.[
|
|
81
|
-
actionData: routerContext?.actionData?.[
|
|
93
|
+
loaderData: routerContext?.loaderData?.[route.id],
|
|
94
|
+
actionData: routerContext?.actionData?.[route.id],
|
|
82
95
|
params: match.params,
|
|
83
|
-
matches: routerContext.matches.map((m)=>{
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
handle: route.handle
|
|
91
|
-
};
|
|
92
|
-
})
|
|
96
|
+
matches: routerContext.matches.map((m)=>({
|
|
97
|
+
id: m.route.id,
|
|
98
|
+
pathname: m.pathname,
|
|
99
|
+
params: m.params,
|
|
100
|
+
data: routerContext?.loaderData?.[m.route.id],
|
|
101
|
+
handle: m.route.handle
|
|
102
|
+
}))
|
|
93
103
|
};
|
|
94
104
|
const routeElement = /*#__PURE__*/ external_react_default().createElement(ElementComponent, elementProps);
|
|
95
|
-
|
|
96
|
-
processedElement = isLeafRoute && cssFiles.length > 0 ? /*#__PURE__*/ external_react_default().createElement(external_react_default().Fragment, null, /*#__PURE__*/ external_react_default().createElement(external_CSSLinks_js_namespaceObject.CSSLinks, {
|
|
105
|
+
processedElement = index === cssInjectionIndex ? /*#__PURE__*/ external_react_default().createElement(external_react_default().Fragment, null, /*#__PURE__*/ external_react_default().createElement(external_CSSLinks_js_namespaceObject.CSSLinks, {
|
|
97
106
|
cssFiles
|
|
98
107
|
}), routeElement) : routeElement;
|
|
99
108
|
}
|
|
100
109
|
return {
|
|
101
110
|
element: processedElement,
|
|
102
|
-
errorElement:
|
|
103
|
-
handle:
|
|
104
|
-
hasAction: !!
|
|
105
|
-
hasErrorBoundary: !!
|
|
106
|
-
hasLoader: !!
|
|
107
|
-
hasClientLoader: !!
|
|
108
|
-
id:
|
|
109
|
-
index:
|
|
111
|
+
errorElement: route.errorElement,
|
|
112
|
+
handle: route.handle,
|
|
113
|
+
hasAction: !!route.action,
|
|
114
|
+
hasErrorBoundary: !!route.hasErrorBoundary,
|
|
115
|
+
hasLoader: !!route.loader,
|
|
116
|
+
hasClientLoader: !!route.hasClientLoader,
|
|
117
|
+
id: route.id,
|
|
118
|
+
index: route.index,
|
|
110
119
|
params: match.params,
|
|
111
|
-
parentId: parentMatch?.route.id ||
|
|
112
|
-
path:
|
|
120
|
+
parentId: parentMatch?.route.id || route.parentId,
|
|
121
|
+
path: route.path,
|
|
113
122
|
pathname: match.pathname,
|
|
114
123
|
pathnameBase: match.pathnameBase
|
|
115
124
|
};
|
|
@@ -129,10 +138,11 @@ const handleRSCRedirect = (headers, basename, status)=>{
|
|
|
129
138
|
});
|
|
130
139
|
};
|
|
131
140
|
const prepareRSCRoutes = async (routes)=>{
|
|
132
|
-
const isLazyComponent = (component)=>component && 'object' == typeof component &&
|
|
141
|
+
const isLazyComponent = (component)=>null != component && 'object' == typeof component && '_init' in component && '_payload' in component;
|
|
133
142
|
const processRoutes = async (routesList)=>{
|
|
134
143
|
await Promise.all(routesList.map(async (route)=>{
|
|
135
|
-
|
|
144
|
+
const modernRoute = route;
|
|
145
|
+
if ('lazyImport' in modernRoute && isLazyComponent(modernRoute.component)) modernRoute.component = (await modernRoute.lazyImport()).default;
|
|
136
146
|
if (route.children && Array.isArray(route.children)) await processRoutes(route.children);
|
|
137
147
|
}));
|
|
138
148
|
};
|
|
@@ -149,12 +159,14 @@ const mergeRoutes = (routes, originalRoutes)=>{
|
|
|
149
159
|
};
|
|
150
160
|
buildRoutesMap(routes);
|
|
151
161
|
const mergeRoutesRecursive = (origRoutes)=>origRoutes.map((origRoute)=>{
|
|
152
|
-
|
|
153
|
-
|
|
162
|
+
const modernOrig = origRoute;
|
|
163
|
+
if (modernOrig.id && routesMap.has(modernOrig.id)) {
|
|
164
|
+
const matchedRoute = routesMap.get(modernOrig.id);
|
|
154
165
|
const result = {
|
|
155
|
-
loader:
|
|
166
|
+
loader: modernOrig.hasClientLoader ? modernOrig.loader : void 0,
|
|
156
167
|
...matchedRoute
|
|
157
168
|
};
|
|
169
|
+
if (modernOrig.isClientComponent) result.isClientComponent = true;
|
|
158
170
|
if (origRoute.children && Array.isArray(origRoute.children)) result.children = mergeRoutesRecursive(origRoute.children);
|
|
159
171
|
return result;
|
|
160
172
|
}
|
|
@@ -172,6 +184,68 @@ const findRouteInTree = (routes, routeId)=>{
|
|
|
172
184
|
}
|
|
173
185
|
return null;
|
|
174
186
|
};
|
|
187
|
+
function getChangedMatches(matches, currentMatches) {
|
|
188
|
+
const currentById = new Map();
|
|
189
|
+
for (const m of currentMatches)if (m.route?.id) currentById.set(m.route.id, m);
|
|
190
|
+
return matches.filter((match)=>{
|
|
191
|
+
const current = currentById.get(match.route?.id);
|
|
192
|
+
return !current || JSON.stringify(current.params) !== JSON.stringify(match.params);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
function canSkipRscFetch(matches, routerState) {
|
|
196
|
+
const changedMatches = getChangedMatches(matches, routerState?.matches || []);
|
|
197
|
+
return changedMatches.length > 0 && changedMatches.every((m)=>{
|
|
198
|
+
const route = m.route;
|
|
199
|
+
return route.isClientComponent && !(route.hasLoader && !route.hasClientLoader);
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
function injectRouteCss(routeId) {
|
|
203
|
+
if ("u" < typeof window) return;
|
|
204
|
+
const cssAssets = window._MODERNJS_ROUTE_MANIFEST?.routeAssets?.[routeId]?.referenceCssAssets;
|
|
205
|
+
if (!cssAssets) return;
|
|
206
|
+
const publicPath = window.__webpack_public_path__ || '/';
|
|
207
|
+
for (const css of cssAssets){
|
|
208
|
+
const href = css.startsWith('http') || css.startsWith('/') ? css : publicPath + css;
|
|
209
|
+
if (!document.querySelector(`link[href="${CSS.escape(href)}"]`)) {
|
|
210
|
+
const link = document.createElement('link');
|
|
211
|
+
link.rel = 'stylesheet';
|
|
212
|
+
link.href = href;
|
|
213
|
+
document.head.appendChild(link);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function ensureClientComponent(route, originalRoutes) {
|
|
218
|
+
if (route.isClientComponent && !route.Component) {
|
|
219
|
+
const origRoute = findRouteInTree(originalRoutes, route.id);
|
|
220
|
+
if (origRoute?.Component) {
|
|
221
|
+
route.Component = origRoute.Component;
|
|
222
|
+
delete route.element;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function resolveClientLoaders(matches, originalRoutes) {
|
|
227
|
+
const clientMatches = matches.filter((m)=>m.route.hasClientLoader);
|
|
228
|
+
if (0 === clientMatches.length) return Promise.resolve([]);
|
|
229
|
+
return Promise.all(clientMatches.map(async (clientMatch)=>{
|
|
230
|
+
const origRoute = findRouteInTree(originalRoutes, clientMatch.route.id);
|
|
231
|
+
clientMatch.route.loader = origRoute?.loader;
|
|
232
|
+
const result = await clientMatch.resolve();
|
|
233
|
+
return {
|
|
234
|
+
routeId: clientMatch.route.id,
|
|
235
|
+
result
|
|
236
|
+
};
|
|
237
|
+
}));
|
|
238
|
+
}
|
|
239
|
+
function applyLoaderResults(results, loaderResults) {
|
|
240
|
+
for (const { routeId, result } of loaderResults)results[routeId] = result;
|
|
241
|
+
}
|
|
242
|
+
async function resolveClientOnlyNavigation(matches, results, originalRoutes, routerState) {
|
|
243
|
+
const changedMatches = getChangedMatches(matches, routerState?.matches || []);
|
|
244
|
+
for (const match of changedMatches)ensureClientComponent(match.route, originalRoutes);
|
|
245
|
+
applyLoaderResults(results, await resolveClientLoaders(changedMatches, originalRoutes));
|
|
246
|
+
for (const match of changedMatches)if (match.route.id) injectRouteCss(match.route.id);
|
|
247
|
+
return results;
|
|
248
|
+
}
|
|
175
249
|
const createClientRouterFromPayload = (payload, originalRoutes, basename = '')=>{
|
|
176
250
|
const processedRoutes = payload.routes.reduceRight((previous, route)=>{
|
|
177
251
|
if (previous.length > 0) return [
|
|
@@ -188,42 +262,28 @@ const createClientRouterFromPayload = (payload, originalRoutes, basename = '')=>
|
|
|
188
262
|
const router = (0, router_namespaceObject.createBrowserRouter)(mergedRoutes, {
|
|
189
263
|
hydrationData: payload,
|
|
190
264
|
basename: basename,
|
|
191
|
-
dataStrategy: async (
|
|
192
|
-
const { request, matches } = context;
|
|
265
|
+
dataStrategy: async ({ request, matches })=>{
|
|
193
266
|
const results = {};
|
|
194
|
-
|
|
267
|
+
if (canSkipRscFetch(matches, router.state)) return resolveClientOnlyNavigation(matches, results, originalRoutes, router.state);
|
|
195
268
|
const fetchPromise = fetch(request.url, {
|
|
196
269
|
headers: {
|
|
197
270
|
'x-rsc-tree': 'true'
|
|
198
271
|
}
|
|
199
272
|
});
|
|
200
|
-
const clientLoadersPromise =
|
|
201
|
-
const foundRoute = findRouteInTree(originalRoutes, clientMatch.route.id);
|
|
202
|
-
clientMatch.route.loader = foundRoute?.loader;
|
|
203
|
-
const res = await clientMatch.resolve();
|
|
204
|
-
return {
|
|
205
|
-
routeId: clientMatch.route.id,
|
|
206
|
-
result: res
|
|
207
|
-
};
|
|
208
|
-
})) : Promise.resolve([]);
|
|
273
|
+
const clientLoadersPromise = resolveClientLoaders(matches, originalRoutes);
|
|
209
274
|
const res = await fetchPromise;
|
|
210
275
|
const redirectLocation = res.headers.get('X-Modernjs-Redirect');
|
|
211
276
|
if (redirectLocation) {
|
|
212
277
|
matches.forEach((match)=>{
|
|
213
278
|
const routeId = match.route.id;
|
|
214
279
|
if (routeId) results[routeId] = {
|
|
215
|
-
type: '
|
|
280
|
+
type: 'data',
|
|
216
281
|
result: (0, router_namespaceObject.redirect)(redirectLocation)
|
|
217
282
|
};
|
|
218
283
|
});
|
|
219
284
|
return results;
|
|
220
285
|
}
|
|
221
|
-
|
|
222
|
-
clientLoadersPromise
|
|
223
|
-
]);
|
|
224
|
-
clientLoaderResults.forEach(({ routeId, result })=>{
|
|
225
|
-
results[routeId] = result;
|
|
226
|
-
});
|
|
286
|
+
applyLoaderResults(results, await clientLoadersPromise);
|
|
227
287
|
if (!res.body) throw new Error('Response body is null');
|
|
228
288
|
const payload = await (0, client_namespaceObject.createFromReadableStream)(res.body);
|
|
229
289
|
if ('object' != typeof payload || null === payload || void 0 === payload.type || 'render' !== payload.type) throw new Error('Unexpected payload type');
|
|
@@ -231,10 +291,14 @@ const createClientRouterFromPayload = (payload, originalRoutes, basename = '')=>
|
|
|
231
291
|
matches.forEach((match)=>{
|
|
232
292
|
const routeId = match.route.id;
|
|
233
293
|
const matchedRoute = serverPayload.routes.find((route)=>route.id === routeId);
|
|
234
|
-
if (matchedRoute)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
294
|
+
if (matchedRoute) {
|
|
295
|
+
const modernMatch = match.route;
|
|
296
|
+
router.patchRoutes(matchedRoute.parentId ?? null, [
|
|
297
|
+
matchedRoute
|
|
298
|
+
], true);
|
|
299
|
+
if (modernMatch.isClientComponent && modernMatch.Component) delete modernMatch.Component;
|
|
300
|
+
}
|
|
301
|
+
if (serverPayload.loaderData && routeId in serverPayload.loaderData) results[routeId] = {
|
|
238
302
|
type: 'data',
|
|
239
303
|
result: serverPayload.loaderData[routeId]
|
|
240
304
|
};
|
|
@@ -114,7 +114,12 @@ function getRouteObjects(routes, { globalApp, ssrMode, props }) {
|
|
|
114
114
|
...'object' == typeof route.config ? route.config?.handle : {}
|
|
115
115
|
},
|
|
116
116
|
index: route.index,
|
|
117
|
+
hasLoader: !!route.loader,
|
|
117
118
|
hasClientLoader: !!route.clientData,
|
|
119
|
+
hasAction: !!route.action,
|
|
120
|
+
...route.isClientComponent ? {
|
|
121
|
+
isClientComponent: true
|
|
122
|
+
} : {},
|
|
118
123
|
Component: route.component ? route.component : void 0,
|
|
119
124
|
errorElement: route.error ? /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(route.error, {}) : void 0,
|
|
120
125
|
children: route.children ? route.children.map((child)=>getRouteObjects([
|
package/dist/esm/cli/index.mjs
CHANGED
|
@@ -62,7 +62,7 @@ const runtimePlugin = (params)=>({
|
|
|
62
62
|
'process.env.IS_REACT18': process.env.IS_REACT18
|
|
63
63
|
},
|
|
64
64
|
include: [
|
|
65
|
-
new RegExp(`[\\\\/]node_modules[\\\\/]@${metaName}[\\\\/]runtime[\\\\/].*[\\\\/]head
|
|
65
|
+
new RegExp(`[\\\\/]node_modules[\\\\/]@${metaName}[\\\\/]runtime[\\\\/].*[\\\\/]head\\.`)
|
|
66
66
|
]
|
|
67
67
|
},
|
|
68
68
|
tools: {
|
|
@@ -17,7 +17,7 @@ const checkUseStringSSR = (config, appDirectory, entrypoints)=>{
|
|
|
17
17
|
}
|
|
18
18
|
return true;
|
|
19
19
|
};
|
|
20
|
-
const ssrBuilderPlugin = (modernAPI, outputModule)=>({
|
|
20
|
+
const ssrBuilderPlugin = (modernAPI, outputModule, exportLoadablePath)=>({
|
|
21
21
|
name: '@modern-js/builder-plugin-ssr',
|
|
22
22
|
setup (api) {
|
|
23
23
|
api.modifyEnvironmentConfig((config, { name, mergeEnvironmentConfig })=>{
|
|
@@ -27,6 +27,7 @@ const ssrBuilderPlugin = (modernAPI, outputModule)=>({
|
|
|
27
27
|
const appContext = modernAPI.getAppContext();
|
|
28
28
|
const { appDirectory, entrypoints } = appContext;
|
|
29
29
|
const useLoadablePlugin = isUseSSRBundle(userConfig) && !isServerEnvironment && checkUseStringSSR(userConfig, appDirectory, entrypoints);
|
|
30
|
+
const useLoadableComponents = isUseSSRBundle(userConfig) && checkUseStringSSR(userConfig, appDirectory, entrypoints);
|
|
30
31
|
return mergeEnvironmentConfig(config, {
|
|
31
32
|
source: {
|
|
32
33
|
define: {
|
|
@@ -44,33 +45,8 @@ const ssrBuilderPlugin = (modernAPI, outputModule)=>({
|
|
|
44
45
|
filename: LOADABLE_STATS_FILE
|
|
45
46
|
}
|
|
46
47
|
]);
|
|
47
|
-
} : void 0
|
|
48
|
-
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
const ssrPlugin = ()=>({
|
|
54
|
-
name: '@modern-js/plugin-ssr',
|
|
55
|
-
required: [
|
|
56
|
-
'@modern-js/runtime'
|
|
57
|
-
],
|
|
58
|
-
setup: (api)=>{
|
|
59
|
-
const appContext = api.getAppContext();
|
|
60
|
-
const exportLoadablePath = `@${appContext.metaName}/runtime/loadable`;
|
|
61
|
-
const runtimeUtilsPath = require.resolve('@modern-js/runtime-utils/node');
|
|
62
|
-
const aliasPath = runtimeUtilsPath.replace(`${path.sep}cjs${path.sep}`, `${path.sep}esm${path.sep}`).replace(/\.js$/, '.mjs');
|
|
63
|
-
api.config(()=>({
|
|
64
|
-
builderPlugins: [
|
|
65
|
-
ssrBuilderPlugin(api, 'module' === appContext.moduleType)
|
|
66
|
-
],
|
|
67
|
-
resolve: {
|
|
68
|
-
alias: {
|
|
69
|
-
'@modern-js/runtime-utils/node$': aliasPath
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
tools: {
|
|
73
|
-
swc: {
|
|
48
|
+
} : void 0,
|
|
49
|
+
swc: useLoadableComponents ? {
|
|
74
50
|
jsc: {
|
|
75
51
|
experimental: {
|
|
76
52
|
plugins: [
|
|
@@ -100,6 +76,29 @@ const ssrPlugin = ()=>({
|
|
|
100
76
|
]
|
|
101
77
|
}
|
|
102
78
|
}
|
|
79
|
+
} : void 0
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
const ssrPlugin = ()=>({
|
|
86
|
+
name: '@modern-js/plugin-ssr',
|
|
87
|
+
required: [
|
|
88
|
+
'@modern-js/runtime'
|
|
89
|
+
],
|
|
90
|
+
setup: (api)=>{
|
|
91
|
+
const appContext = api.getAppContext();
|
|
92
|
+
const exportLoadablePath = `@${appContext.metaName}/runtime/loadable`;
|
|
93
|
+
const runtimeUtilsPath = require.resolve('@modern-js/runtime-utils/node');
|
|
94
|
+
const aliasPath = runtimeUtilsPath.replace(`${path.sep}cjs${path.sep}`, `${path.sep}esm${path.sep}`).replace(/\.js$/, '.mjs');
|
|
95
|
+
api.config(()=>({
|
|
96
|
+
builderPlugins: [
|
|
97
|
+
ssrBuilderPlugin(api, 'module' === appContext.moduleType, exportLoadablePath)
|
|
98
|
+
],
|
|
99
|
+
resolve: {
|
|
100
|
+
alias: {
|
|
101
|
+
'@modern-js/runtime-utils/node$': aliasPath
|
|
103
102
|
}
|
|
104
103
|
}
|
|
105
104
|
}));
|
|
@@ -88,6 +88,10 @@ const configureChildCompiler = (child, compiler, appDirectory)=>{
|
|
|
88
88
|
node: true
|
|
89
89
|
};
|
|
90
90
|
child.options.devtool = false;
|
|
91
|
+
if (child.options.optimization && 'object' == typeof child.options.optimization) child.options.optimization.minimize = false;
|
|
92
|
+
else child.options.optimization = {
|
|
93
|
+
minimize: false
|
|
94
|
+
};
|
|
91
95
|
};
|
|
92
96
|
const applyExternalsPlugin = (child, compiler)=>{
|
|
93
97
|
const ExternalsPlugin = compiler.rspack?.ExternalsPlugin;
|
|
@@ -89,7 +89,9 @@ const generateCode = async (appContext, config, entrypoints, api)=>{
|
|
|
89
89
|
entryName: entrypoint.entryName,
|
|
90
90
|
internalDirectory,
|
|
91
91
|
splitRouteChunks: config?.output?.splitRouteChunks,
|
|
92
|
-
|
|
92
|
+
isRscClientBundle: isUseRsc(config),
|
|
93
|
+
srcDirectory,
|
|
94
|
+
internalSrcAlias: appContext.internalSrcAlias
|
|
93
95
|
})
|
|
94
96
|
});
|
|
95
97
|
if (entrypoint.nestedRoutesEntry && (isUseSSRBundle(config) || isUseRsc(config))) {
|
|
@@ -109,7 +111,9 @@ const generateCode = async (appContext, config, entrypoints, api)=>{
|
|
|
109
111
|
entryName: entrypoint.entryName,
|
|
110
112
|
internalDirectory,
|
|
111
113
|
splitRouteChunks: config?.output?.splitRouteChunks,
|
|
112
|
-
|
|
114
|
+
isRscClientBundle: false,
|
|
115
|
+
srcDirectory,
|
|
116
|
+
internalSrcAlias: appContext.internalSrcAlias
|
|
113
117
|
});
|
|
114
118
|
await fs.outputFile(path.resolve(internalDirectory, `./${entryName}/routes.server.js`), serverRoutesCode, 'utf8');
|
|
115
119
|
}
|