@umijs/server 4.0.0-rc.7 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/server.d.ts +3 -1
- package/dist/server.js +83 -82
- package/dist/ssr.d.ts +18 -0
- package/dist/ssr.js +89 -0
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -14,4 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.createServerRoutes = void 0;
|
|
18
|
+
var routes_1 = require("./routes");
|
|
19
|
+
Object.defineProperty(exports, "createServerRoutes", { enumerable: true, get: function () { return routes_1.createServerRoutes; } });
|
|
17
20
|
__exportStar(require("./server"), exports);
|
package/dist/server.d.ts
CHANGED
|
@@ -10,7 +10,8 @@ export interface IOpts {
|
|
|
10
10
|
links?: Record<string, string>[];
|
|
11
11
|
metas?: Record<string, string>[];
|
|
12
12
|
styles?: (Record<string, string> | string)[];
|
|
13
|
-
|
|
13
|
+
favicons?: string[];
|
|
14
|
+
title?: string;
|
|
14
15
|
headScripts?: (Record<string, string> | string)[];
|
|
15
16
|
scripts?: (Record<string, string> | string)[];
|
|
16
17
|
mountElementId?: string;
|
|
@@ -18,6 +19,7 @@ export interface IOpts {
|
|
|
18
19
|
modifyHTML?: (html: string, args: {
|
|
19
20
|
path?: string;
|
|
20
21
|
}) => Promise<string>;
|
|
22
|
+
historyType?: 'hash' | 'browser';
|
|
21
23
|
}
|
|
22
24
|
export declare function getMarkup(opts: Omit<IOpts, 'routes'> & {
|
|
23
25
|
path?: string;
|
package/dist/server.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
4
|
};
|
|
@@ -15,54 +6,57 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
6
|
exports.createRequestHandler = exports.getMarkup = void 0;
|
|
16
7
|
const react_1 = __importDefault(require("react"));
|
|
17
8
|
const server_1 = __importDefault(require("react-dom/server"));
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
// import { matchRoutes } from 'react-router-dom';
|
|
10
|
+
// import { createServerRoutes } from './routes';
|
|
20
11
|
const scripts_1 = require("./scripts");
|
|
21
12
|
const styles_1 = require("./styles");
|
|
22
|
-
function getMarkup(opts) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
13
|
+
async function getMarkup(opts) {
|
|
14
|
+
// TODO: use real component
|
|
15
|
+
let markup = server_1.default.renderToString(react_1.default.createElement('div', { id: opts.mountElementId || 'root' }));
|
|
16
|
+
function propsToString(opts) {
|
|
17
|
+
return Object.keys(opts.props)
|
|
18
|
+
.filter((key) => !(opts.filters || []).includes(key))
|
|
19
|
+
.map((key) => `${key}=${JSON.stringify(opts.props[key])}`)
|
|
20
|
+
.join(' ');
|
|
21
|
+
}
|
|
22
|
+
function getScriptContent(script) {
|
|
23
|
+
const attrs = propsToString({
|
|
24
|
+
props: script,
|
|
25
|
+
filters: ['src', 'content'],
|
|
26
|
+
});
|
|
27
|
+
return script.src
|
|
28
|
+
? `<script${opts.esmScript ? ' type="module"' : ''} ${attrs} src="${script.src}"></script>`
|
|
29
|
+
: `<script${opts.esmScript ? ' type="module"' : ''} ${attrs}>${script.content}</script>`;
|
|
30
|
+
}
|
|
31
|
+
function getStyleContent(style) {
|
|
32
|
+
const attrs = propsToString({
|
|
33
|
+
props: style,
|
|
34
|
+
filters: ['src', 'content'],
|
|
35
|
+
});
|
|
36
|
+
return style.src
|
|
37
|
+
? `<link rel="stylesheet" ${attrs} href="${style.src}" />`
|
|
38
|
+
: `<style ${attrs}>${style.content}</style>`;
|
|
39
|
+
}
|
|
40
|
+
function getTagContent(opts) {
|
|
41
|
+
const attrs = propsToString({
|
|
42
|
+
props: opts.attrs,
|
|
43
|
+
});
|
|
44
|
+
return `<${opts.tagName} ${attrs} />`;
|
|
45
|
+
}
|
|
46
|
+
const favicons = [];
|
|
47
|
+
if (Array.isArray(opts.favicons)) {
|
|
48
|
+
opts.favicons.forEach((e) => {
|
|
49
|
+
favicons.push(`<link rel="shortcut icon" href="${e}">`);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const title = opts.title ? `<title>${opts.title}</title>` : '';
|
|
53
|
+
const metas = (opts.metas || []).map((meta) => getTagContent({ attrs: meta, tagName: 'meta' }));
|
|
54
|
+
const links = (opts.links || []).map((link) => getTagContent({ attrs: link, tagName: 'link' }));
|
|
55
|
+
const styles = (0, styles_1.normalizeStyles)(opts.styles || []).map(getStyleContent);
|
|
56
|
+
const headScripts = (0, scripts_1.normalizeScripts)(opts.headScripts || []).map(getScriptContent);
|
|
57
|
+
const scripts = (0, scripts_1.normalizeScripts)(opts.scripts || []).map(getScriptContent);
|
|
58
|
+
markup = [
|
|
59
|
+
`<!DOCTYPE html>
|
|
66
60
|
<html>
|
|
67
61
|
<head>
|
|
68
62
|
<meta charset="UTF-8" />
|
|
@@ -71,43 +65,50 @@ function getMarkup(opts) {
|
|
|
71
65
|
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
|
|
72
66
|
/>
|
|
73
67
|
<meta http-equiv="X-UA-Compatible" content="ie=edge" />`,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
68
|
+
metas.join('\n'),
|
|
69
|
+
favicons.join('\n'),
|
|
70
|
+
title,
|
|
71
|
+
links.join('\n'),
|
|
72
|
+
styles.join('\n'),
|
|
73
|
+
headScripts.join('\n'),
|
|
74
|
+
`</head>
|
|
80
75
|
<body>`,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
76
|
+
markup,
|
|
77
|
+
scripts.join('\n'),
|
|
78
|
+
`</body>
|
|
84
79
|
</html>`,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
});
|
|
80
|
+
]
|
|
81
|
+
.filter(Boolean)
|
|
82
|
+
.join('\n');
|
|
83
|
+
if (opts.modifyHTML) {
|
|
84
|
+
markup = await opts.modifyHTML(markup, { path: opts.path });
|
|
85
|
+
}
|
|
86
|
+
return markup;
|
|
93
87
|
}
|
|
94
88
|
exports.getMarkup = getMarkup;
|
|
95
89
|
function createRequestHandler(opts) {
|
|
96
|
-
return (req, res, next) =>
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
90
|
+
return async (req, res, next) => {
|
|
91
|
+
var _a;
|
|
92
|
+
if (opts.historyType === 'browser' &&
|
|
93
|
+
opts.base !== '/' &&
|
|
94
|
+
req.path === '/') {
|
|
95
|
+
// 如果是 browser,并且配置了非 / base,访问 / 时 redirect 到 base 路径
|
|
96
|
+
res.redirect(opts.base);
|
|
97
|
+
}
|
|
98
|
+
else if ((_a = req.headers.accept) === null || _a === void 0 ? void 0 : _a.includes('text/html')) {
|
|
99
|
+
// 匹配路由,不匹配走 next()
|
|
100
|
+
// const routes = createServerRoutes({
|
|
101
|
+
// routesById: opts.routes,
|
|
102
|
+
// });
|
|
103
|
+
// const matches = matchRoutes(routes, req.path, opts.base);
|
|
104
|
+
// 其他接受 HTML 的请求都兜底返回 HTML
|
|
104
105
|
res.set('Content-Type', 'text/html');
|
|
105
|
-
const markup =
|
|
106
|
+
const markup = await getMarkup({ ...opts, path: req.path });
|
|
106
107
|
res.end(markup);
|
|
107
108
|
}
|
|
108
109
|
else {
|
|
109
110
|
next();
|
|
110
111
|
}
|
|
111
|
-
}
|
|
112
|
+
};
|
|
112
113
|
}
|
|
113
114
|
exports.createRequestHandler = createRequestHandler;
|
package/dist/ssr.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface RouteLoaders {
|
|
2
|
+
[key: string]: () => Promise<any>;
|
|
3
|
+
}
|
|
4
|
+
interface CreateRequestHandlerOptions {
|
|
5
|
+
routesWithServerLoader: RouteLoaders;
|
|
6
|
+
PluginManager: any;
|
|
7
|
+
manifest: (() => {
|
|
8
|
+
assets: Record<string, string>;
|
|
9
|
+
}) | {
|
|
10
|
+
assets: Record<string, string>;
|
|
11
|
+
};
|
|
12
|
+
getPlugins: () => any;
|
|
13
|
+
getValidKeys: () => any;
|
|
14
|
+
getRoutes: (PluginManager: any) => any;
|
|
15
|
+
getClientRootComponent: (PluginManager: any) => any;
|
|
16
|
+
}
|
|
17
|
+
export default function createRequestHandler(opts: CreateRequestHandlerOptions): (req: any, res: any, next: any) => Promise<any>;
|
|
18
|
+
export {};
|
package/dist/ssr.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const server_1 = require("react-dom/server");
|
|
4
|
+
const react_router_dom_1 = require("react-router-dom");
|
|
5
|
+
function createRequestHandler(opts) {
|
|
6
|
+
return async function (req, res, next) {
|
|
7
|
+
const { routesWithServerLoader, PluginManager, getPlugins, getValidKeys, getRoutes, } = opts;
|
|
8
|
+
// 切换路由场景下,会通过此 API 执行 server loader
|
|
9
|
+
if (req.url.startsWith('/__serverLoader') && req.query.route) {
|
|
10
|
+
const data = await executeLoader(req.query.route, routesWithServerLoader);
|
|
11
|
+
res.status(200).json(data);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const pluginManager = PluginManager.create({
|
|
15
|
+
plugins: getPlugins(),
|
|
16
|
+
validKeys: getValidKeys(),
|
|
17
|
+
});
|
|
18
|
+
const { routes, routeComponents } = await getRoutes(pluginManager);
|
|
19
|
+
const matches = matchRoutesForSSR(req.url, routes);
|
|
20
|
+
if (matches.length === 0) {
|
|
21
|
+
return next();
|
|
22
|
+
}
|
|
23
|
+
const loaderData = {};
|
|
24
|
+
await Promise.all(matches
|
|
25
|
+
.filter((id) => routes[id].hasServerLoader)
|
|
26
|
+
.map((id) => new Promise(async (resolve) => {
|
|
27
|
+
loaderData[id] = await executeLoader(id, routesWithServerLoader);
|
|
28
|
+
resolve();
|
|
29
|
+
})));
|
|
30
|
+
const manifest = typeof opts.manifest === 'function' ? opts.manifest() : opts.manifest;
|
|
31
|
+
const context = {
|
|
32
|
+
routes,
|
|
33
|
+
routeComponents,
|
|
34
|
+
pluginManager,
|
|
35
|
+
location: req.url,
|
|
36
|
+
manifest,
|
|
37
|
+
loaderData,
|
|
38
|
+
};
|
|
39
|
+
const jsx = await opts.getClientRootComponent(context);
|
|
40
|
+
const stream = (0, server_1.renderToPipeableStream)(jsx, {
|
|
41
|
+
bootstrapScripts: [manifest.assets['umi.js'] || '/umi.js'],
|
|
42
|
+
onShellReady() {
|
|
43
|
+
res.setHeader('Content-type', 'text/html');
|
|
44
|
+
stream.pipe(res);
|
|
45
|
+
},
|
|
46
|
+
onError(x) {
|
|
47
|
+
console.error(x);
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
exports.default = createRequestHandler;
|
|
53
|
+
function matchRoutesForSSR(reqUrl, routesById) {
|
|
54
|
+
var _a;
|
|
55
|
+
return (((_a = (0, react_router_dom_1.matchRoutes)(createClientRoutes({ routesById }), reqUrl)) === null || _a === void 0 ? void 0 : _a.map((route) => route.route.id)) || []);
|
|
56
|
+
}
|
|
57
|
+
function createClientRoutes(opts) {
|
|
58
|
+
const { routesById, parentId } = opts;
|
|
59
|
+
return Object.keys(routesById)
|
|
60
|
+
.filter((id) => routesById[id].parentId === parentId)
|
|
61
|
+
.map((id) => {
|
|
62
|
+
const route = createClientRoute(routesById[id]);
|
|
63
|
+
const children = createClientRoutes({
|
|
64
|
+
routesById,
|
|
65
|
+
parentId: route.id,
|
|
66
|
+
});
|
|
67
|
+
if (children.length > 0) {
|
|
68
|
+
// @ts-ignore
|
|
69
|
+
route.children = children;
|
|
70
|
+
}
|
|
71
|
+
return route;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function createClientRoute(route) {
|
|
75
|
+
const { id, path, index } = route;
|
|
76
|
+
return {
|
|
77
|
+
id,
|
|
78
|
+
path,
|
|
79
|
+
index,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
async function executeLoader(routeKey, routesWithServerLoader) {
|
|
83
|
+
const mod = await routesWithServerLoader[routeKey]();
|
|
84
|
+
if (!mod.serverLoader || typeof mod.serverLoader !== 'function') {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// TODO: 处理错误场景
|
|
88
|
+
return await mod.serverLoader();
|
|
89
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umijs/server",
|
|
3
|
-
"version": "4.0.0
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "@umijs/server",
|
|
5
5
|
"homepage": "https://github.com/umijs/umi-next/tree/master/packages/server#readme",
|
|
6
6
|
"bugs": "https://github.com/umijs/umi-next/issues",
|
|
@@ -16,15 +16,15 @@
|
|
|
16
16
|
],
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "pnpm tsc",
|
|
19
|
-
"build:deps": "
|
|
19
|
+
"build:deps": "umi-scripts bundleDeps",
|
|
20
20
|
"dev": "pnpm build -- --watch"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@umijs/bundler-utils": "4.0.0
|
|
23
|
+
"@umijs/bundler-utils": "4.0.0",
|
|
24
24
|
"history": "5.3.0",
|
|
25
|
-
"react": "
|
|
26
|
-
"react-dom": "
|
|
27
|
-
"react-router-dom": "6.
|
|
25
|
+
"react": "18.1.0",
|
|
26
|
+
"react-dom": "18.1.0",
|
|
27
|
+
"react-router-dom": "6.3.0"
|
|
28
28
|
},
|
|
29
29
|
"publishConfig": {
|
|
30
30
|
"access": "public"
|