@modern-js/prod-server 2.31.2 → 2.32.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/CHANGELOG.md +30 -0
- package/LICENSE +1 -1
- package/dist/cjs/constants.js +10 -0
- package/dist/cjs/libs/context/context.js +7 -2
- package/dist/cjs/libs/preload/flushServerHeader.js +67 -0
- package/dist/cjs/libs/preload/index.js +7 -0
- package/dist/cjs/libs/preload/parseLinks.js +100 -0
- package/dist/cjs/libs/preload/transformLinks2String.js +113 -0
- package/dist/cjs/libs/proxy.js +44 -41
- package/dist/cjs/libs/render/index.js +16 -2
- package/dist/cjs/libs/serverTiming.js +1 -1
- package/dist/cjs/server/modernServer.js +21 -16
- package/dist/esm/constants.js +7 -0
- package/dist/esm/libs/context/context.js +5 -2
- package/dist/esm/libs/preload/flushServerHeader.js +70 -0
- package/dist/esm/libs/preload/index.js +2 -0
- package/dist/esm/libs/preload/parseLinks.js +170 -0
- package/dist/esm/libs/preload/transformLinks2String.js +118 -0
- package/dist/esm/libs/proxy.js +88 -50
- package/dist/esm/libs/render/index.js +17 -3
- package/dist/esm/libs/serverTiming.js +1 -1
- package/dist/esm/server/modernServer.js +22 -17
- package/dist/esm-node/constants.js +7 -0
- package/dist/esm-node/libs/context/context.js +7 -2
- package/dist/esm-node/libs/preload/flushServerHeader.js +45 -0
- package/dist/esm-node/libs/preload/index.js +2 -0
- package/dist/esm-node/libs/preload/parseLinks.js +88 -0
- package/dist/esm-node/libs/preload/transformLinks2String.js +103 -0
- package/dist/esm-node/libs/proxy.js +44 -41
- package/dist/esm-node/libs/render/index.js +16 -2
- package/dist/esm-node/libs/serverTiming.js +1 -1
- package/dist/esm-node/server/modernServer.js +22 -17
- package/dist/types/constants.d.ts +7 -1
- package/dist/types/libs/context/context.d.ts +1 -1
- package/dist/types/libs/preload/flushServerHeader.d.ts +20 -0
- package/dist/types/libs/preload/index.d.ts +2 -0
- package/dist/types/libs/preload/parseLinks.d.ts +14 -0
- package/dist/types/libs/preload/transformLinks2String.d.ts +3 -0
- package/dist/types/libs/proxy.d.ts +3 -3
- package/dist/types/libs/render/index.d.ts +2 -0
- package/dist/types/libs/serverTiming.d.ts +1 -0
- package/dist/types/type.d.ts +3 -0
- package/dist/types/utils.d.ts +1 -1
- package/package.json +18 -17
|
@@ -16,7 +16,7 @@ import { createErrorDocument, createMiddlewareCollecter, getStaticReg, mergeExte
|
|
|
16
16
|
import * as reader from "../libs/render/reader";
|
|
17
17
|
import { createProxyHandler } from "../libs/proxy";
|
|
18
18
|
import { createContext } from "../libs/context";
|
|
19
|
-
import { AGGRED_DIR, ERROR_DIGEST, ERROR_PAGE_TEXT, RUN_MODE } from "../constants";
|
|
19
|
+
import { AGGRED_DIR, ERROR_DIGEST, ERROR_PAGE_TEXT, RUN_MODE, ServerReportTimings } from "../constants";
|
|
20
20
|
import { createAfterMatchContext, createAfterRenderContext, createMiddlewareContext } from "../libs/hook-api";
|
|
21
21
|
var SERVER_DIR = "./server";
|
|
22
22
|
export var ModernServer = /* @__PURE__ */ function() {
|
|
@@ -68,7 +68,7 @@ export var ModernServer = /* @__PURE__ */ function() {
|
|
|
68
68
|
function onInit(runner, app) {
|
|
69
69
|
var _this = this;
|
|
70
70
|
return _async_to_generator(function() {
|
|
71
|
-
var _conf_bff, _app,
|
|
71
|
+
var _conf_bff, _app, _this_conf_output, distDir, conf, _createProxyHandler, handlers, handleUpgrade, usageRoutes;
|
|
72
72
|
return _ts_generator(this, function(_state) {
|
|
73
73
|
switch (_state.label) {
|
|
74
74
|
case 0:
|
|
@@ -76,12 +76,14 @@ export var ModernServer = /* @__PURE__ */ function() {
|
|
|
76
76
|
distDir = _this.distDir, conf = _this.conf;
|
|
77
77
|
_this.initReader();
|
|
78
78
|
debug("final server conf", _this.conf);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
79
|
+
if ((_conf_bff = conf.bff) === null || _conf_bff === void 0 ? void 0 : _conf_bff.proxy) {
|
|
80
|
+
_createProxyHandler = createProxyHandler(conf.bff.proxy), handlers = _createProxyHandler.handlers, handleUpgrade = _createProxyHandler.handleUpgrade;
|
|
81
|
+
app && handleUpgrade(app);
|
|
82
|
+
handlers.forEach(function(handler) {
|
|
83
|
+
_this.addHandler(handler);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
(_app = app) === null || _app === void 0 ? void 0 : _app.on("close", function() {
|
|
85
87
|
_this.reader.close();
|
|
86
88
|
});
|
|
87
89
|
usageRoutes = _this.filterRoutes(_this.getRoutes());
|
|
@@ -130,6 +132,7 @@ export var ModernServer = /* @__PURE__ */ function() {
|
|
|
130
132
|
distDir: distDir,
|
|
131
133
|
staticGenerate: staticGenerate,
|
|
132
134
|
forceCSR: forceCSR,
|
|
135
|
+
conf: this.conf,
|
|
133
136
|
nonce: (_conf_security = conf.security) === null || _conf_security === void 0 ? void 0 : _conf_security.nonce,
|
|
134
137
|
metaName: metaName
|
|
135
138
|
});
|
|
@@ -613,7 +616,7 @@ export var ModernServer = /* @__PURE__ */ function() {
|
|
|
613
616
|
null
|
|
614
617
|
];
|
|
615
618
|
}
|
|
616
|
-
res.
|
|
619
|
+
res.set("content-type", renderResult.contentType);
|
|
617
620
|
return [
|
|
618
621
|
2,
|
|
619
622
|
renderResult
|
|
@@ -690,7 +693,7 @@ export var ModernServer = /* @__PURE__ */ function() {
|
|
|
690
693
|
end = time();
|
|
691
694
|
res.on("finish", function() {
|
|
692
695
|
var cost3 = end();
|
|
693
|
-
reporter.reportTiming(
|
|
696
|
+
reporter.reportTiming(ServerReportTimings.SERVER_HANDLE_REQUEST, cost3);
|
|
694
697
|
});
|
|
695
698
|
route = matched.generate(context.url);
|
|
696
699
|
if (!route.isApi)
|
|
@@ -729,7 +732,7 @@ export var ModernServer = /* @__PURE__ */ function() {
|
|
|
729
732
|
case 4:
|
|
730
733
|
_state.sent();
|
|
731
734
|
cost = end1();
|
|
732
|
-
reporter.reportTiming(
|
|
735
|
+
cost && reporter.reportTiming(ServerReportTimings.SERVER_HOOK_AFTER_MATCH, cost);
|
|
733
736
|
_state.label = 5;
|
|
734
737
|
case 5:
|
|
735
738
|
if (_this.isSend(res)) {
|
|
@@ -771,7 +774,7 @@ export var ModernServer = /* @__PURE__ */ function() {
|
|
|
771
774
|
case 7:
|
|
772
775
|
_state.sent();
|
|
773
776
|
cost1 = end2();
|
|
774
|
-
reporter.reportTiming(
|
|
777
|
+
cost1 && reporter.reportTiming(ServerReportTimings.SERVER_MIDDLEWARE, cost1);
|
|
775
778
|
res.locals = _object_spread({}, res.locals, middlewareContext.response.locals);
|
|
776
779
|
if (_this.isSend(res)) {
|
|
777
780
|
return [
|
|
@@ -820,7 +823,7 @@ export var ModernServer = /* @__PURE__ */ function() {
|
|
|
820
823
|
case 10:
|
|
821
824
|
_state.sent();
|
|
822
825
|
cost2 = end3();
|
|
823
|
-
reporter.reportTiming(
|
|
826
|
+
cost2 && reporter.reportTiming(ServerReportTimings.SERVER_HOOK_AFTER_RENDER, cost2);
|
|
824
827
|
_state.label = 11;
|
|
825
828
|
case 11:
|
|
826
829
|
if (_this.isSend(res)) {
|
|
@@ -844,7 +847,7 @@ export var ModernServer = /* @__PURE__ */ function() {
|
|
|
844
847
|
{
|
|
845
848
|
key: "isSend",
|
|
846
849
|
value: function isSend(res) {
|
|
847
|
-
if (res.
|
|
850
|
+
if (res.writableEnded) {
|
|
848
851
|
return true;
|
|
849
852
|
}
|
|
850
853
|
if (res.getHeader("Location") && isRedirect(res.statusCode)) {
|
|
@@ -923,7 +926,9 @@ export var ModernServer = /* @__PURE__ */ function() {
|
|
|
923
926
|
req.metrics = req.metrics || this.metrics;
|
|
924
927
|
var context;
|
|
925
928
|
try {
|
|
926
|
-
context = this.createContext(req, res
|
|
929
|
+
context = this.createContext(req, res, {
|
|
930
|
+
metaName: this.metaName
|
|
931
|
+
});
|
|
927
932
|
} catch (e) {
|
|
928
933
|
this.logger.error(e);
|
|
929
934
|
res.statusCode = 500;
|
|
@@ -941,7 +946,7 @@ export var ModernServer = /* @__PURE__ */ function() {
|
|
|
941
946
|
key: "redirect",
|
|
942
947
|
value: function redirect(res, url) {
|
|
943
948
|
var status = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : 302;
|
|
944
|
-
res.
|
|
949
|
+
res.set("Location", url);
|
|
945
950
|
res.statusCode = status;
|
|
946
951
|
res.end();
|
|
947
952
|
}
|
|
@@ -964,7 +969,7 @@ export var ModernServer = /* @__PURE__ */ function() {
|
|
|
964
969
|
case 0:
|
|
965
970
|
res = context.res;
|
|
966
971
|
context.status = status;
|
|
967
|
-
res.
|
|
972
|
+
res.set("content-type", mime.contentType("html"));
|
|
968
973
|
statusPage = "/".concat(status);
|
|
969
974
|
customErrorPage = "/_error";
|
|
970
975
|
matched = _this.router.match(statusPage) || _this.router.match(customErrorPage);
|
|
@@ -26,3 +26,10 @@ export const RUN_MODE = {
|
|
|
26
26
|
FULL: "full",
|
|
27
27
|
TYPE: "type"
|
|
28
28
|
};
|
|
29
|
+
export var ServerReportTimings;
|
|
30
|
+
(function(ServerReportTimings2) {
|
|
31
|
+
ServerReportTimings2["SERVER_HANDLE_REQUEST"] = "server-handle-request";
|
|
32
|
+
ServerReportTimings2["SERVER_MIDDLEWARE"] = "server-middleware";
|
|
33
|
+
ServerReportTimings2["SERVER_HOOK_AFTER_RENDER"] = "server-hook-after-render";
|
|
34
|
+
ServerReportTimings2["SERVER_HOOK_AFTER_MATCH"] = "server-hook-after-match";
|
|
35
|
+
})(ServerReportTimings || (ServerReportTimings = {}));
|
|
@@ -27,7 +27,12 @@ export class ModernServerContext {
|
|
|
27
27
|
bind() {
|
|
28
28
|
const { req, res } = this;
|
|
29
29
|
req.get = (key) => this.getReqHeader(key);
|
|
30
|
-
res.set = (key, value) =>
|
|
30
|
+
res.set = (key, value) => {
|
|
31
|
+
if (!res.headersSent) {
|
|
32
|
+
res.setHeader(key, value);
|
|
33
|
+
}
|
|
34
|
+
return res;
|
|
35
|
+
};
|
|
31
36
|
res.send = (body) => {
|
|
32
37
|
this.send(body);
|
|
33
38
|
};
|
|
@@ -167,7 +172,7 @@ export class ModernServerContext {
|
|
|
167
172
|
this.req = req;
|
|
168
173
|
this.res = res;
|
|
169
174
|
this.options = options || {};
|
|
170
|
-
this.serverTiming = new ServerTiming(res, cutNameByHyphen(((_options = options) === null || _options === void 0 ? void 0 : _options.metaName) || "modern-js"));
|
|
171
175
|
this.bind();
|
|
176
|
+
this.serverTiming = new ServerTiming(this.res, cutNameByHyphen(((_options = options) === null || _options === void 0 ? void 0 : _options.metaName) || "modern-js"));
|
|
172
177
|
}
|
|
173
178
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { parseLinks } from "./parseLinks";
|
|
2
|
+
import { transformLinks2String } from "./transformLinks2String";
|
|
3
|
+
export function transformToRegExp(input) {
|
|
4
|
+
if (typeof input === "string") {
|
|
5
|
+
return new RegExp(input);
|
|
6
|
+
}
|
|
7
|
+
return input;
|
|
8
|
+
}
|
|
9
|
+
export function shouldFlushServerHeader(serverConf, userAgent, disablePreload) {
|
|
10
|
+
const { ssr: ssrConf } = serverConf || {};
|
|
11
|
+
if (disablePreload) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
if (typeof ssrConf === "object" && ssrConf.preload) {
|
|
15
|
+
if (typeof ssrConf.preload === "object") {
|
|
16
|
+
const { userAgentFilter } = ssrConf.preload;
|
|
17
|
+
if (userAgentFilter && userAgent) {
|
|
18
|
+
return !transformToRegExp(userAgentFilter).test(userAgent);
|
|
19
|
+
}
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
export async function flushServerHeader({ serverConf, ctx, distDir, template, headers }) {
|
|
27
|
+
const { ssr: ssrConf } = serverConf || {};
|
|
28
|
+
if (typeof ssrConf !== "object") {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const { res } = ctx;
|
|
32
|
+
const links = await parseLinks({
|
|
33
|
+
template,
|
|
34
|
+
distDir,
|
|
35
|
+
pathname: ctx.path
|
|
36
|
+
});
|
|
37
|
+
const link = transformLinks2String(links, ssrConf.preload);
|
|
38
|
+
res.set("link", link);
|
|
39
|
+
for (const key in headers || {}) {
|
|
40
|
+
var _headers;
|
|
41
|
+
const value = (_headers = headers) === null || _headers === void 0 ? void 0 : _headers[key];
|
|
42
|
+
value && res.set(key, value);
|
|
43
|
+
}
|
|
44
|
+
res.flushHeaders();
|
|
45
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { NESTED_ROUTE_SPEC_FILE, ROUTE_MANIFEST_FILE, ROUTE_SPEC_FILE, fs } from "@modern-js/utils";
|
|
3
|
+
import { parse as htmlParse } from "node-html-parser";
|
|
4
|
+
import { matchRoutes } from "@modern-js/utils/runtime/remix-router";
|
|
5
|
+
import { matchEntry } from "@modern-js/utils/runtime-node";
|
|
6
|
+
export async function parseLinks({ pathname, distDir, template }) {
|
|
7
|
+
const links = await parseLinksFromRoutes(pathname, distDir);
|
|
8
|
+
return links.length > 0 ? links : parseLinksFromHtml(template);
|
|
9
|
+
}
|
|
10
|
+
function parseLinksFromHtml(html) {
|
|
11
|
+
const root = htmlParse(html);
|
|
12
|
+
const scripts = root.querySelectorAll("script").filter((elem) => Boolean(elem.getAttribute("src")));
|
|
13
|
+
const css = root.querySelectorAll("link").filter((elem) => {
|
|
14
|
+
const href = elem.getAttribute("href");
|
|
15
|
+
const rel = elem.getAttribute("rel");
|
|
16
|
+
return href && rel === "stylesheet";
|
|
17
|
+
});
|
|
18
|
+
const images = root.querySelectorAll("img").filter((elem) => Boolean(elem.getAttribute("src")));
|
|
19
|
+
return scripts.map((elem) => {
|
|
20
|
+
const src = elem.getAttribute("src");
|
|
21
|
+
return {
|
|
22
|
+
uri: src,
|
|
23
|
+
as: "script"
|
|
24
|
+
};
|
|
25
|
+
}).concat(css.map((elem) => {
|
|
26
|
+
const href = elem.getAttribute("href");
|
|
27
|
+
return {
|
|
28
|
+
uri: href,
|
|
29
|
+
as: "style"
|
|
30
|
+
};
|
|
31
|
+
})).concat(images.map((elem) => {
|
|
32
|
+
const src = elem.getAttribute("src");
|
|
33
|
+
return {
|
|
34
|
+
uri: src,
|
|
35
|
+
as: "image"
|
|
36
|
+
};
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
async function parseLinksFromRoutes(pathname, distDir) {
|
|
40
|
+
const noopLinks = [];
|
|
41
|
+
const nestedRoutesSpec = path.join(distDir, NESTED_ROUTE_SPEC_FILE);
|
|
42
|
+
const routesJsonPath = path.join(distDir, ROUTE_SPEC_FILE);
|
|
43
|
+
const routeManifestPath = path.join(distDir, ROUTE_MANIFEST_FILE);
|
|
44
|
+
if (!fs.existsSync(nestedRoutesSpec) || !fs.existsSync(routesJsonPath) || !fs.existsSync(routeManifestPath)) {
|
|
45
|
+
return noopLinks;
|
|
46
|
+
}
|
|
47
|
+
const routesJson = await import(routesJsonPath);
|
|
48
|
+
const serverRoutes = routesJson.routes;
|
|
49
|
+
const entry = matchEntry(pathname, serverRoutes);
|
|
50
|
+
if (entry) {
|
|
51
|
+
var _routeAssets_entryName, _matches, _assets_filter, _assets, _assets_filter1, _assets1;
|
|
52
|
+
const routes = await import(nestedRoutesSpec);
|
|
53
|
+
const { entryName } = entry;
|
|
54
|
+
if (!entryName) {
|
|
55
|
+
return noopLinks;
|
|
56
|
+
}
|
|
57
|
+
const entryRoutes = routes[entryName];
|
|
58
|
+
if (!entryRoutes) {
|
|
59
|
+
return noopLinks;
|
|
60
|
+
}
|
|
61
|
+
const routesManifest = await import(routeManifestPath);
|
|
62
|
+
const { routeAssets } = routesManifest;
|
|
63
|
+
const matches = matchRoutes(entryRoutes, pathname, entry.urlPath);
|
|
64
|
+
const entryAssets = (_routeAssets_entryName = routeAssets[entryName]) === null || _routeAssets_entryName === void 0 ? void 0 : _routeAssets_entryName.assets;
|
|
65
|
+
const assets = (_matches = matches) === null || _matches === void 0 ? void 0 : _matches.reduce((acc, match) => {
|
|
66
|
+
const routeId = match.route.id;
|
|
67
|
+
if (routeId) {
|
|
68
|
+
var _matchedManifest;
|
|
69
|
+
const matchedManifest = routeAssets[routeId];
|
|
70
|
+
const assets2 = (_matchedManifest = matchedManifest) === null || _matchedManifest === void 0 ? void 0 : _matchedManifest.assets;
|
|
71
|
+
if (Array.isArray(assets2)) {
|
|
72
|
+
acc.push(...assets2);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return acc;
|
|
76
|
+
}, []).concat(entryAssets || []);
|
|
77
|
+
const cssLinks = (_assets = assets) === null || _assets === void 0 ? void 0 : (_assets_filter = _assets.filter((asset) => asset.endsWith(".css"))) === null || _assets_filter === void 0 ? void 0 : _assets_filter.map((uri) => ({
|
|
78
|
+
uri,
|
|
79
|
+
as: "style"
|
|
80
|
+
}));
|
|
81
|
+
const scriptLinks = (_assets1 = assets) === null || _assets1 === void 0 ? void 0 : (_assets_filter1 = _assets1.filter((asset) => asset.endsWith(".js"))) === null || _assets_filter1 === void 0 ? void 0 : _assets_filter1.map((uri) => ({
|
|
82
|
+
uri,
|
|
83
|
+
as: "script"
|
|
84
|
+
}));
|
|
85
|
+
return (cssLinks || []).concat(scriptLinks || []);
|
|
86
|
+
}
|
|
87
|
+
return noopLinks;
|
|
88
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { transformToRegExp } from "./flushServerHeader";
|
|
2
|
+
export function transformLinks2String(links, preload) {
|
|
3
|
+
if (typeof preload === "boolean") {
|
|
4
|
+
return links.map(({ uri, as }) => as ? `<${uri}>; rel=preload; as=${as}` : `<${uri}>; rel=preload`).join(", ");
|
|
5
|
+
}
|
|
6
|
+
const { include, exclude, attributes } = preload;
|
|
7
|
+
const resolveLinks = addAttributes(removeExclude(addInclude(links, include), exclude), attributes);
|
|
8
|
+
return resolveLinks.join(", ");
|
|
9
|
+
function addInclude(links2, include2) {
|
|
10
|
+
var _include;
|
|
11
|
+
const images = [
|
|
12
|
+
"gif",
|
|
13
|
+
"jpg",
|
|
14
|
+
"jpeg",
|
|
15
|
+
"png",
|
|
16
|
+
"webp",
|
|
17
|
+
"bmp",
|
|
18
|
+
"tiff",
|
|
19
|
+
"anpg",
|
|
20
|
+
"ico"
|
|
21
|
+
];
|
|
22
|
+
const videos = [
|
|
23
|
+
"mp4",
|
|
24
|
+
"webm",
|
|
25
|
+
"ogm",
|
|
26
|
+
"ogv",
|
|
27
|
+
"ogg"
|
|
28
|
+
];
|
|
29
|
+
const fonts = [
|
|
30
|
+
"woff",
|
|
31
|
+
"woff2",
|
|
32
|
+
"eot",
|
|
33
|
+
"ttf",
|
|
34
|
+
"otf"
|
|
35
|
+
];
|
|
36
|
+
const includes = ((_include = include2) === null || _include === void 0 ? void 0 : _include.map((item) => {
|
|
37
|
+
if (typeof item === "string") {
|
|
38
|
+
const type = (() => {
|
|
39
|
+
if (item.endsWith(".js")) {
|
|
40
|
+
return "script";
|
|
41
|
+
}
|
|
42
|
+
if (item.endsWith(".css")) {
|
|
43
|
+
return "style";
|
|
44
|
+
}
|
|
45
|
+
if (images.some((image) => item.endsWith(`.${image}`))) {
|
|
46
|
+
return "image";
|
|
47
|
+
}
|
|
48
|
+
if (videos.some((video) => item.endsWith(`.${video}`))) {
|
|
49
|
+
return "video";
|
|
50
|
+
}
|
|
51
|
+
if (fonts.some((font) => item.endsWith(`.${font}`))) {
|
|
52
|
+
return "font";
|
|
53
|
+
}
|
|
54
|
+
})();
|
|
55
|
+
return {
|
|
56
|
+
uri: item,
|
|
57
|
+
as: type
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
uri: item.url,
|
|
62
|
+
as: item.type
|
|
63
|
+
};
|
|
64
|
+
})) || [];
|
|
65
|
+
return links2.concat(includes);
|
|
66
|
+
}
|
|
67
|
+
function removeExclude(links2, exclude2) {
|
|
68
|
+
return exclude2 ? links2.filter(({ uri }) => !transformToRegExp(exclude2).test(uri)) : links2;
|
|
69
|
+
}
|
|
70
|
+
function addAttributes(links2, attributes2) {
|
|
71
|
+
const parseAttributes = (_attributes) => {
|
|
72
|
+
return Object.entries(_attributes || {}).reduce((results, [key, value]) => {
|
|
73
|
+
if (typeof value === "boolean") {
|
|
74
|
+
value && results.push(`; ${key}`);
|
|
75
|
+
return results;
|
|
76
|
+
}
|
|
77
|
+
results.push(`; ${key}=${value}`);
|
|
78
|
+
return results;
|
|
79
|
+
}, []).join("");
|
|
80
|
+
};
|
|
81
|
+
return links2.map(({ uri, as }) => {
|
|
82
|
+
if (as) {
|
|
83
|
+
const attributesStr = (() => {
|
|
84
|
+
const { style, script, image, font } = attributes2 || {};
|
|
85
|
+
switch (as) {
|
|
86
|
+
case "script":
|
|
87
|
+
return parseAttributes(script);
|
|
88
|
+
case "style":
|
|
89
|
+
return parseAttributes(style);
|
|
90
|
+
case "image":
|
|
91
|
+
return parseAttributes(image);
|
|
92
|
+
case "font":
|
|
93
|
+
return parseAttributes(font);
|
|
94
|
+
default:
|
|
95
|
+
return "";
|
|
96
|
+
}
|
|
97
|
+
})();
|
|
98
|
+
return `<${uri}>; rel=preload; as=${as}${attributesStr}`;
|
|
99
|
+
}
|
|
100
|
+
return `<${uri}>; rel=preload`;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -1,52 +1,46 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HttpProxyMiddleware } from "http-proxy-middleware/dist/http-proxy-middleware";
|
|
2
2
|
import { debug } from "../utils";
|
|
3
3
|
export function formatProxyOptions(proxyOptions) {
|
|
4
|
-
const
|
|
5
|
-
if (
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Array.prototype.push.apply(formattedProxy, Object.keys(proxyOptions).reduce((total, source) => {
|
|
10
|
-
const option = proxyOptions[source];
|
|
11
|
-
total.push({
|
|
12
|
-
context: source,
|
|
13
|
-
changeOrigin: true,
|
|
14
|
-
logLevel: "warn",
|
|
15
|
-
...typeof option === "string" ? {
|
|
16
|
-
target: option
|
|
17
|
-
} : option
|
|
18
|
-
});
|
|
19
|
-
return total;
|
|
20
|
-
}, []));
|
|
21
|
-
}
|
|
4
|
+
const ret = [];
|
|
5
|
+
if (Array.isArray(proxyOptions)) {
|
|
6
|
+
ret.push(...proxyOptions);
|
|
7
|
+
} else if ("target" in proxyOptions) {
|
|
8
|
+
ret.push(proxyOptions);
|
|
22
9
|
} else {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (typeof middleware.upgrade === "function") {
|
|
34
|
-
middleware.upgrade(req, socket, head);
|
|
10
|
+
for (const [context, options] of Object.entries(proxyOptions)) {
|
|
11
|
+
const opts = {
|
|
12
|
+
context,
|
|
13
|
+
changeOrigin: true,
|
|
14
|
+
logLevel: "warn"
|
|
15
|
+
};
|
|
16
|
+
if (typeof options === "string") {
|
|
17
|
+
opts.target = options;
|
|
18
|
+
} else {
|
|
19
|
+
Object.assign(opts, options);
|
|
35
20
|
}
|
|
21
|
+
ret.push(opts);
|
|
36
22
|
}
|
|
23
|
+
}
|
|
24
|
+
const handleError = (err, _req, _res, _target) => {
|
|
25
|
+
console.error(err);
|
|
37
26
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
};
|
|
27
|
+
for (const opts of ret) {
|
|
28
|
+
var _opts;
|
|
29
|
+
var _onError;
|
|
30
|
+
(_onError = (_opts = opts).onError) !== null && _onError !== void 0 ? _onError : _opts.onError = handleError;
|
|
43
31
|
}
|
|
32
|
+
return ret;
|
|
33
|
+
}
|
|
34
|
+
export const createProxyHandler = (proxyOptions) => {
|
|
35
|
+
debug("createProxyHandler", proxyOptions);
|
|
44
36
|
const formattedOptionsList = formatProxyOptions(proxyOptions);
|
|
45
|
-
|
|
46
|
-
|
|
37
|
+
const proxies = [];
|
|
38
|
+
const handlers = [];
|
|
39
|
+
for (const opts of formattedOptionsList) {
|
|
40
|
+
const proxy = new HttpProxyMiddleware(opts.context, opts);
|
|
47
41
|
const handler = async (ctx, next) => {
|
|
48
42
|
const { req, res } = ctx;
|
|
49
|
-
const bypassUrl = typeof
|
|
43
|
+
const bypassUrl = typeof opts.bypass === "function" ? opts.bypass(req, res, opts) : null;
|
|
50
44
|
if (typeof bypassUrl === "boolean") {
|
|
51
45
|
ctx.status = 404;
|
|
52
46
|
next();
|
|
@@ -54,12 +48,21 @@ export const createProxyHandler = (proxyOptions) => {
|
|
|
54
48
|
ctx.url = bypassUrl;
|
|
55
49
|
next();
|
|
56
50
|
} else {
|
|
57
|
-
middleware(req, res, next);
|
|
51
|
+
proxy.middleware(req, res, next);
|
|
58
52
|
}
|
|
59
53
|
};
|
|
60
|
-
|
|
54
|
+
proxies.push(proxy);
|
|
61
55
|
handlers.push(handler);
|
|
62
56
|
}
|
|
57
|
+
const handleUpgrade = (server) => {
|
|
58
|
+
for (const proxy of proxies) {
|
|
59
|
+
const raw = proxy;
|
|
60
|
+
if (raw.proxyOptions.ws === true && !raw.wsInternalSubscribed) {
|
|
61
|
+
server.on("upgrade", raw.handleUpgrade);
|
|
62
|
+
raw.wsInternalSubscribed = true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
63
66
|
return {
|
|
64
67
|
handlers,
|
|
65
68
|
handleUpgrade
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import { cutNameByHyphen, mime } from "@modern-js/utils";
|
|
3
3
|
import { ERROR_DIGEST } from "../../constants";
|
|
4
|
+
import { flushServerHeader, shouldFlushServerHeader } from "../preload";
|
|
4
5
|
import { handleDirectory } from "./static";
|
|
5
6
|
import { readFile } from "./reader";
|
|
6
7
|
import * as ssr from "./ssr";
|
|
7
8
|
import { injectServerData } from "./utils";
|
|
8
|
-
export const createRenderHandler = ({ distDir, staticGenerate, forceCSR, nonce, ssrRender, metaName = "modern-js" }) => async function render({ ctx, route, runner }) {
|
|
9
|
+
export const createRenderHandler = ({ distDir, staticGenerate, conf, forceCSR, nonce, ssrRender, metaName = "modern-js" }) => async function render({ ctx, route, runner }) {
|
|
9
10
|
if (ctx.resHasHandled()) {
|
|
10
11
|
return null;
|
|
11
12
|
}
|
|
@@ -23,6 +24,19 @@ export const createRenderHandler = ({ distDir, staticGenerate, forceCSR, nonce,
|
|
|
23
24
|
const useCSR = forceCSR && (ctx.query.csr || ctx.headers[`x-${cutNameByHyphen(metaName)}-ssr-fallback`]);
|
|
24
25
|
if (route.isSSR && !useCSR) {
|
|
25
26
|
try {
|
|
27
|
+
const userAgent = ctx.getReqHeader("User-Agent");
|
|
28
|
+
const disablePreload = Boolean(ctx.headers[`x-${cutNameByHyphen(metaName)}-disable-preload`]);
|
|
29
|
+
if (shouldFlushServerHeader(conf.server, userAgent, disablePreload)) {
|
|
30
|
+
flushServerHeader({
|
|
31
|
+
serverConf: conf.server,
|
|
32
|
+
ctx,
|
|
33
|
+
distDir,
|
|
34
|
+
template: content.toString(),
|
|
35
|
+
headers: {
|
|
36
|
+
"Content-Type": mime.contentType(path.extname(templatePath))
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
26
40
|
const ssrRenderOptions = {
|
|
27
41
|
distDir,
|
|
28
42
|
entryName: route.entryName,
|
|
@@ -44,7 +58,7 @@ export const createRenderHandler = ({ distDir, staticGenerate, forceCSR, nonce,
|
|
|
44
58
|
return result;
|
|
45
59
|
} catch (err) {
|
|
46
60
|
ctx.error(ERROR_DIGEST.ERENDER, err.stack || err.message);
|
|
47
|
-
ctx.res.
|
|
61
|
+
ctx.res.set("x-modern-ssr-fallback", "1");
|
|
48
62
|
}
|
|
49
63
|
}
|
|
50
64
|
return {
|
|
@@ -5,7 +5,7 @@ export class ServerTiming {
|
|
|
5
5
|
const _name = `bd-${this.meta}-${name}`;
|
|
6
6
|
const serverTiming = this.res.getHeader(SERVER_TIMING) || this.res.getHeader(SERVER_TIMING.toLocaleLowerCase());
|
|
7
7
|
const value = `${_name};${desc ? `decs="${desc}";` : ""} dur=${dur}`;
|
|
8
|
-
this.res.
|
|
8
|
+
this.res.set(SERVER_TIMING, serverTiming ? `${serverTiming}, ${value}` : value);
|
|
9
9
|
return this;
|
|
10
10
|
}
|
|
11
11
|
constructor(res, meta) {
|