@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.
Files changed (44) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/LICENSE +1 -1
  3. package/dist/cjs/constants.js +10 -0
  4. package/dist/cjs/libs/context/context.js +7 -2
  5. package/dist/cjs/libs/preload/flushServerHeader.js +67 -0
  6. package/dist/cjs/libs/preload/index.js +7 -0
  7. package/dist/cjs/libs/preload/parseLinks.js +100 -0
  8. package/dist/cjs/libs/preload/transformLinks2String.js +113 -0
  9. package/dist/cjs/libs/proxy.js +44 -41
  10. package/dist/cjs/libs/render/index.js +16 -2
  11. package/dist/cjs/libs/serverTiming.js +1 -1
  12. package/dist/cjs/server/modernServer.js +21 -16
  13. package/dist/esm/constants.js +7 -0
  14. package/dist/esm/libs/context/context.js +5 -2
  15. package/dist/esm/libs/preload/flushServerHeader.js +70 -0
  16. package/dist/esm/libs/preload/index.js +2 -0
  17. package/dist/esm/libs/preload/parseLinks.js +170 -0
  18. package/dist/esm/libs/preload/transformLinks2String.js +118 -0
  19. package/dist/esm/libs/proxy.js +88 -50
  20. package/dist/esm/libs/render/index.js +17 -3
  21. package/dist/esm/libs/serverTiming.js +1 -1
  22. package/dist/esm/server/modernServer.js +22 -17
  23. package/dist/esm-node/constants.js +7 -0
  24. package/dist/esm-node/libs/context/context.js +7 -2
  25. package/dist/esm-node/libs/preload/flushServerHeader.js +45 -0
  26. package/dist/esm-node/libs/preload/index.js +2 -0
  27. package/dist/esm-node/libs/preload/parseLinks.js +88 -0
  28. package/dist/esm-node/libs/preload/transformLinks2String.js +103 -0
  29. package/dist/esm-node/libs/proxy.js +44 -41
  30. package/dist/esm-node/libs/render/index.js +16 -2
  31. package/dist/esm-node/libs/serverTiming.js +1 -1
  32. package/dist/esm-node/server/modernServer.js +22 -17
  33. package/dist/types/constants.d.ts +7 -1
  34. package/dist/types/libs/context/context.d.ts +1 -1
  35. package/dist/types/libs/preload/flushServerHeader.d.ts +20 -0
  36. package/dist/types/libs/preload/index.d.ts +2 -0
  37. package/dist/types/libs/preload/parseLinks.d.ts +14 -0
  38. package/dist/types/libs/preload/transformLinks2String.d.ts +3 -0
  39. package/dist/types/libs/proxy.d.ts +3 -3
  40. package/dist/types/libs/render/index.d.ts +2 -0
  41. package/dist/types/libs/serverTiming.d.ts +1 -0
  42. package/dist/types/type.d.ts +3 -0
  43. package/dist/types/utils.d.ts +1 -1
  44. 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, _app1, _this_conf_output, distDir, conf, proxyHandlers, usageRoutes;
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
- proxyHandlers = createProxyHandler((_conf_bff = conf.bff) === null || _conf_bff === void 0 ? void 0 : _conf_bff.proxy);
80
- (_app = app) === null || _app === void 0 ? void 0 : _app.on("upgrade", proxyHandlers.handleUpgrade);
81
- proxyHandlers.handlers.forEach(function(handler) {
82
- _this.addHandler(handler);
83
- });
84
- (_app1 = app) === null || _app1 === void 0 ? void 0 : _app1.on("close", function() {
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.setHeader("content-type", renderResult.contentType);
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("server_handle_request", cost3);
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("server_hook_after_render", cost);
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("server_middleware", cost1);
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("server_hook_after_render", cost2);
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.headersSent) {
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.setHeader("Location", url);
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.setHeader("content-type", mime.contentType("html"));
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) => this.res.setHeader(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,2 @@
1
+ export * from "./parseLinks";
2
+ export * from "./flushServerHeader";
@@ -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 { createProxyMiddleware } from "http-proxy-middleware";
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 formattedProxy = [];
5
- if (!Array.isArray(proxyOptions)) {
6
- if ("target" in proxyOptions) {
7
- formattedProxy.push(proxyOptions);
8
- } else {
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
- formattedProxy.push(...proxyOptions);
24
- }
25
- return formattedProxy;
26
- }
27
- export const createProxyHandler = (proxyOptions) => {
28
- debug("createProxyHandler", proxyOptions);
29
- const middlewares = [];
30
- const handlers = [];
31
- const handleUpgrade = (req, socket, head) => {
32
- for (const middleware of middlewares) {
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
- if (!proxyOptions) {
39
- return {
40
- handlers,
41
- handleUpgrade
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
- for (const options of formattedOptionsList) {
46
- const middleware = createProxyMiddleware(options.context, options);
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 options.bypass === "function" ? options.bypass(req, res, options) : null;
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
- middlewares.push(middleware);
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.setHeader("x-modern-ssr-fallback", "1");
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.setHeader(SERVER_TIMING, serverTiming ? `${serverTiming}, ${value}` : value);
8
+ this.res.set(SERVER_TIMING, serverTiming ? `${serverTiming}, ${value}` : value);
9
9
  return this;
10
10
  }
11
11
  constructor(res, meta) {