@modern-js/prod-server 2.21.0 → 2.22.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 (78) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/cjs/constants.js +15 -5
  3. package/dist/cjs/index.js +12 -4
  4. package/dist/cjs/libs/context/context.js +4 -2
  5. package/dist/cjs/libs/context/index.js +6 -2
  6. package/dist/cjs/libs/hook-api/base.js +88 -0
  7. package/dist/cjs/libs/hook-api/index.js +17 -79
  8. package/dist/cjs/libs/hook-api/index.worker.js +109 -0
  9. package/dist/cjs/libs/hook-api/route.js +3 -1
  10. package/dist/cjs/libs/hook-api/template.js +6 -26
  11. package/dist/cjs/libs/hook-api/templateForStream.js +35 -0
  12. package/dist/cjs/libs/loadConfig.js +9 -3
  13. package/dist/cjs/libs/logger.js +6 -2
  14. package/dist/cjs/libs/metrics.js +3 -1
  15. package/dist/cjs/libs/proxy.js +6 -2
  16. package/dist/cjs/libs/render/cache/__tests__/cacheable.js +3 -1
  17. package/dist/cjs/libs/render/cache/__tests__/error-configuration.js +3 -1
  18. package/dist/cjs/libs/render/cache/__tests__/matched-cache.js +3 -1
  19. package/dist/cjs/libs/render/cache/index.js +3 -1
  20. package/dist/cjs/libs/render/cache/page-caches/index.js +3 -1
  21. package/dist/cjs/libs/render/cache/page-caches/lru.js +3 -1
  22. package/dist/cjs/libs/render/cache/spr.js +6 -2
  23. package/dist/cjs/libs/render/cache/util.js +24 -8
  24. package/dist/cjs/libs/render/index.js +3 -4
  25. package/dist/cjs/libs/render/measure.js +6 -2
  26. package/dist/cjs/libs/render/reader.js +32 -19
  27. package/dist/cjs/libs/render/ssr.js +3 -1
  28. package/dist/cjs/libs/render/static.js +3 -1
  29. package/dist/cjs/libs/render/type.js +3 -1
  30. package/dist/cjs/libs/render/utils.js +8 -3
  31. package/dist/cjs/libs/route/index.js +6 -2
  32. package/dist/cjs/libs/route/matcher.js +3 -1
  33. package/dist/cjs/libs/route/route.js +3 -1
  34. package/dist/cjs/libs/serveFile.js +6 -2
  35. package/dist/cjs/renderHtml.js +75 -0
  36. package/dist/cjs/server/index.js +9 -3
  37. package/dist/cjs/server/modernServer.js +21 -16
  38. package/dist/cjs/server/modernServerSplit.js +3 -1
  39. package/dist/cjs/utils.js +27 -9
  40. package/dist/cjs/workerServer.js +95 -11
  41. package/dist/esm/libs/context/context.js +1 -1
  42. package/dist/esm/libs/hook-api/base.js +104 -0
  43. package/dist/esm/libs/hook-api/index.js +5 -109
  44. package/dist/esm/libs/hook-api/index.worker.js +105 -0
  45. package/dist/esm/libs/hook-api/template.js +1 -28
  46. package/dist/esm/libs/hook-api/templateForStream.js +28 -0
  47. package/dist/esm/libs/render/index.js +1 -4
  48. package/dist/esm/libs/render/reader.js +26 -26
  49. package/dist/esm/libs/render/utils.js +2 -1
  50. package/dist/esm/renderHtml.js +141 -0
  51. package/dist/esm/server/index.js +11 -1
  52. package/dist/esm/server/modernServer.js +45 -37
  53. package/dist/esm/workerServer.js +103 -10
  54. package/dist/esm-node/libs/context/context.js +1 -1
  55. package/dist/esm-node/libs/hook-api/base.js +68 -0
  56. package/dist/esm-node/libs/hook-api/index.js +5 -74
  57. package/dist/esm-node/libs/hook-api/index.worker.js +84 -0
  58. package/dist/esm-node/libs/hook-api/template.js +1 -25
  59. package/dist/esm-node/libs/hook-api/templateForStream.js +25 -0
  60. package/dist/esm-node/libs/render/index.js +1 -4
  61. package/dist/esm-node/libs/render/reader.js +14 -11
  62. package/dist/esm-node/libs/render/utils.js +2 -1
  63. package/dist/esm-node/renderHtml.js +71 -0
  64. package/dist/esm-node/server/index.js +6 -2
  65. package/dist/esm-node/server/modernServer.js +18 -15
  66. package/dist/esm-node/workerServer.js +89 -9
  67. package/dist/types/libs/context/context.d.ts +1 -1
  68. package/dist/types/libs/hook-api/base.d.ts +53 -0
  69. package/dist/types/libs/hook-api/index.worker.d.ts +18 -0
  70. package/dist/types/libs/hook-api/template.d.ts +11 -14
  71. package/dist/types/libs/hook-api/templateForStream.d.ts +13 -0
  72. package/dist/types/libs/render/reader.d.ts +4 -2
  73. package/dist/types/renderHtml.d.ts +21 -0
  74. package/dist/types/server/index.d.ts +5 -1
  75. package/dist/types/server/modernServer.d.ts +4 -3
  76. package/dist/types/utils.d.ts +1 -1
  77. package/dist/types/workerServer.d.ts +19 -8
  78. package/package.json +21 -7
@@ -1,78 +1,11 @@
1
- import { _ as _define_property } from "@swc/helpers/_/_define_property";
2
- import cookie from "cookie";
3
1
  import { RouteAPI } from "./route";
4
2
  import { TemplateAPI } from "./template";
5
- class Response {
6
- get(key) {
7
- return this.res.getHeader(key);
8
- }
9
- set(key, value) {
10
- return this.res.setHeader(key, value);
11
- }
12
- status(code) {
13
- this.res.statusCode = code;
14
- }
15
- setCookie(key, value, options) {
16
- const cookieValue = this.res.getHeader("set-cookie");
17
- const fmt = Array.isArray(cookieValue) ? cookieValue : [
18
- cookieValue
19
- ].filter(Boolean);
20
- fmt.push(cookie.serialize(key, value, options));
21
- this.res.setHeader("set-cookie", fmt.length === 1 ? fmt[0] : fmt);
22
- }
23
- clearCookie() {
24
- this.res.removeHeader("set-cookie");
25
- }
26
- raw(body, options) {
27
- const { status, headers = {} } = options || {};
28
- Object.entries(headers).forEach(([key, value]) => {
29
- this.res.setHeader(key, value);
30
- });
31
- if (status) {
32
- this.res.statusCode = status;
33
- }
34
- this.res.end(body);
35
- }
36
- constructor(res) {
37
- _define_property(this, "cookies", void 0);
38
- _define_property(this, "res", void 0);
39
- this.res = res;
40
- this.cookies = {
41
- set: this.setCookie.bind(this),
42
- clear: this.clearCookie.bind(this)
43
- };
44
- }
45
- }
46
- class Request {
47
- getCookie(key) {
48
- return this._cookie[key];
49
- }
50
- constructor(ctx) {
51
- _define_property(this, "url", void 0);
52
- _define_property(this, "host", void 0);
53
- _define_property(this, "pathname", void 0);
54
- _define_property(this, "query", void 0);
55
- _define_property(this, "headers", void 0);
56
- _define_property(this, "cookie", void 0);
57
- _define_property(this, "cookies", void 0);
58
- _define_property(this, "_cookie", void 0);
59
- this.url = ctx.url;
60
- this.host = ctx.host;
61
- this.pathname = ctx.path;
62
- this.query = ctx.query;
63
- this.headers = ctx.headers;
64
- this.cookie = ctx.headers.cookie || "";
65
- this._cookie = cookie.parse(this.cookie);
66
- this.cookies = {
67
- get: this.getCookie.bind(this)
68
- };
69
- }
70
- }
3
+ import { BaseRequest, BaseResponse } from "./base";
71
4
  export const base = (context) => {
72
5
  const { res } = context;
73
6
  return {
74
- response: new Response(res),
75
- request: new Request(context),
7
+ response: new BaseResponse(res),
8
+ request: new BaseRequest(context),
76
9
  logger: context.logger,
77
10
  metrics: context.metrics
78
11
  };
@@ -93,12 +26,10 @@ export const createAfterRenderContext = (context, content) => {
93
26
  };
94
27
  export const createMiddlewareContext = (context) => {
95
28
  const baseContext = base(context);
29
+ baseContext.response.locals = context.res.locals || {};
96
30
  return {
97
31
  ...baseContext,
98
- response: {
99
- ...baseContext.response,
100
- locals: context.res.locals || {}
101
- },
32
+ response: baseContext.response,
102
33
  source: {
103
34
  req: context.req,
104
35
  res: context.res
@@ -0,0 +1,84 @@
1
+ import { _ as _define_property } from "@swc/helpers/_/_define_property";
2
+ import { BaseRequest, BaseResponse } from "./base";
3
+ import { RouteAPI } from "./route";
4
+ import { TemplateAPI } from "./template";
5
+ class ServerResponse {
6
+ set statusCode(code) {
7
+ this.res.status = code;
8
+ }
9
+ get statusCode() {
10
+ return this.res.status;
11
+ }
12
+ getHeader(key) {
13
+ var _this_res_headers_get;
14
+ return (_this_res_headers_get = this.res.headers.get(key)) !== null && _this_res_headers_get !== void 0 ? _this_res_headers_get : void 0;
15
+ }
16
+ setHeader(key, value) {
17
+ this.res.headers.set(key, value);
18
+ }
19
+ removeHeader(key) {
20
+ this.res.headers.delete(key);
21
+ }
22
+ end(body) {
23
+ this.res.body = body;
24
+ this.res.isSent = true;
25
+ }
26
+ constructor(res) {
27
+ _define_property(this, "locals", void 0);
28
+ _define_property(this, "res", void 0);
29
+ this.res = res;
30
+ this.locals = res.locals;
31
+ }
32
+ }
33
+ export const base = (context) => {
34
+ const { req, res, logger, metrics } = context;
35
+ const serverResponse = new ServerResponse(res);
36
+ const { host, pathname, searchParams } = new URL(req.url);
37
+ const headers = {};
38
+ req.headers.forEach((value, key) => {
39
+ headers[key] = value;
40
+ });
41
+ return {
42
+ response: new BaseResponse(serverResponse),
43
+ request: new BaseRequest({
44
+ url: req.url,
45
+ host,
46
+ path: pathname,
47
+ headers,
48
+ // eslint-disable-next-line node/no-unsupported-features/es-builtins
49
+ query: Object.fromEntries(searchParams)
50
+ }),
51
+ logger,
52
+ metrics
53
+ };
54
+ };
55
+ export const createAfterMatchContext = (context, entryName) => {
56
+ const baseContext = base(context);
57
+ return {
58
+ ...baseContext,
59
+ router: new RouteAPI(entryName)
60
+ };
61
+ };
62
+ export const createAfterRenderContext = (context, content) => {
63
+ const baseContext = base(context);
64
+ return {
65
+ ...baseContext,
66
+ template: new TemplateAPI(content)
67
+ };
68
+ };
69
+ export const createMiddlewareContext = (context) => {
70
+ const baseContext = base(context);
71
+ baseContext.response.locals = context.res.locals;
72
+ const { url, headers } = context.req;
73
+ const rawRequest = new Request(url, {
74
+ headers
75
+ });
76
+ return {
77
+ ...baseContext,
78
+ response: baseContext.response,
79
+ source: {
80
+ req: rawRequest,
81
+ res: baseContext.response
82
+ }
83
+ };
84
+ };
@@ -1,6 +1,5 @@
1
1
  import { _ as _define_property } from "@swc/helpers/_/_define_property";
2
- import { Transform } from "stream";
3
- const RegList = {
2
+ export const RegList = {
4
3
  before: {
5
4
  head: "<head>",
6
5
  body: "<body>"
@@ -41,26 +40,3 @@ export class TemplateAPI {
41
40
  this.content = content;
42
41
  }
43
42
  }
44
- export const templateInjectableStream = ({ prependHead, appendHead, prependBody, appendBody }) => new Transform({
45
- write(chunk, _, callback) {
46
- let chunk_str = chunk.toString();
47
- if (prependHead) {
48
- const { head } = RegList.before;
49
- chunk_str = chunk_str.replace(head, `${head}${prependHead}`);
50
- }
51
- if (appendHead) {
52
- const { head } = RegList.after;
53
- chunk_str = chunk_str.replace(head, `${appendHead}${head}`);
54
- }
55
- if (prependBody) {
56
- const { body } = RegList.before;
57
- chunk_str = chunk_str.replace(body, `${body}${prependBody}`);
58
- }
59
- if (appendBody) {
60
- const { body } = RegList.after;
61
- chunk_str = chunk_str.replace(body, `${appendBody}${body}`);
62
- }
63
- this.push(chunk_str);
64
- callback();
65
- }
66
- });
@@ -0,0 +1,25 @@
1
+ import { Transform } from "stream";
2
+ import { RegList } from "./template";
3
+ export const templateInjectableStream = ({ prependHead, appendHead, prependBody, appendBody }) => new Transform({
4
+ write(chunk, _, callback) {
5
+ let chunk_str = chunk.toString();
6
+ if (prependHead) {
7
+ const { head } = RegList.before;
8
+ chunk_str = chunk_str.replace(head, `${head}${prependHead}`);
9
+ }
10
+ if (appendHead) {
11
+ const { head } = RegList.after;
12
+ chunk_str = chunk_str.replace(head, `${appendHead}${head}`);
13
+ }
14
+ if (prependBody) {
15
+ const { body } = RegList.before;
16
+ chunk_str = chunk_str.replace(body, `${body}${prependBody}`);
17
+ }
18
+ if (appendBody) {
19
+ const { body } = RegList.after;
20
+ chunk_str = chunk_str.replace(body, `${appendBody}${body}`);
21
+ }
22
+ this.push(chunk_str);
23
+ callback();
24
+ }
25
+ });
@@ -1,5 +1,5 @@
1
1
  import path from "path";
2
- import { fs, mime } from "@modern-js/utils";
2
+ import { mime } from "@modern-js/utils";
3
3
  import { ERROR_DIGEST } from "../../constants";
4
4
  import { handleDirectory } from "./static";
5
5
  import { readFile } from "./reader";
@@ -16,9 +16,6 @@ export const createRenderHandler = ({ distDir, staticGenerate, forceCSR, nonce }
16
16
  return result;
17
17
  }
18
18
  const templatePath = entry;
19
- if (!fs.existsSync(templatePath)) {
20
- throw new Error(`Could not find template file: ${templatePath}`);
21
- }
22
19
  const content = await readFile(templatePath);
23
20
  if (!content) {
24
21
  return null;
@@ -1,27 +1,28 @@
1
1
  import { _ as _define_property } from "@swc/helpers/_/_define_property";
2
- import { fs } from "@modern-js/utils";
2
+ import { fs as extraFS } from "@modern-js/utils";
3
3
  import LRU from "lru-cache";
4
4
  const Byte = 1;
5
5
  const KB = 1024 * Byte;
6
6
  const MB = 1024 * KB;
7
7
  const getContentLength = (cache) => cache.content.length;
8
- const createCacheItem = async (filepath, mtime) => {
9
- const content = await fs.readFile(filepath);
8
+ const createCacheItem = async (content, mtime) => {
10
9
  return {
11
10
  content,
12
11
  mtime
13
12
  };
14
13
  };
15
14
  export class LruReader {
16
- init() {
15
+ init(fs) {
16
+ this.fs = fs || extraFS;
17
17
  }
18
18
  close() {
19
19
  }
20
20
  async read(filepath) {
21
+ const { fs } = this;
21
22
  if (this.cache.has(filepath)) {
22
- const { content } = this.cache.get(filepath);
23
+ const { content: content2 } = this.cache.get(filepath);
23
24
  return {
24
- content
25
+ content: content2
25
26
  };
26
27
  }
27
28
  if (!fs.existsSync(filepath)) {
@@ -34,12 +35,13 @@ export class LruReader {
34
35
  if (stat.size > 20 * MB) {
35
36
  return null;
36
37
  }
37
- const item = await createCacheItem(filepath, stat.mtime);
38
+ const content = await fs.promises.readFile(filepath);
39
+ const item = await createCacheItem(content, stat.mtime);
38
40
  this.cache.set(filepath, item);
39
41
  return item;
40
42
  }
41
43
  update() {
42
- const { cache } = this;
44
+ const { cache, fs } = this;
43
45
  const files = cache.keys();
44
46
  for (const filepath of files) {
45
47
  if (!fs.existsSync(filepath)) {
@@ -57,14 +59,15 @@ export class LruReader {
57
59
  }
58
60
  }
59
61
  }
60
- // private timer?: NodeJS.Timeout;
61
62
  constructor() {
62
63
  _define_property(this, "cache", void 0);
64
+ _define_property(this, "fs", void 0);
63
65
  this.cache = new LRU({
64
66
  max: 256 * MB,
65
67
  length: getContentLength,
66
68
  maxAge: 5 * 60 * 5e3
67
69
  });
70
+ this.fs = extraFS;
68
71
  }
69
72
  }
70
73
  const reader = new LruReader();
@@ -75,8 +78,8 @@ export const readFile = async (filepath) => {
75
78
  export const updateFile = () => {
76
79
  reader.update();
77
80
  };
78
- export const init = () => {
79
- reader.init();
81
+ export const init = (fs) => {
82
+ reader.init(fs);
80
83
  };
81
84
  export const close = () => {
82
85
  reader.close();
@@ -1,4 +1,5 @@
1
- import { TemplateAPI, templateInjectableStream } from "../hook-api/template";
1
+ import { TemplateAPI } from "../hook-api/template";
2
+ import { templateInjectableStream } from "../hook-api/templateForStream";
2
3
  export const injectServerData = (content, context) => {
3
4
  const template = new TemplateAPI(content);
4
5
  template.prependHead(`<script type="application/json" id="__MODERN_SERVER_DATA__">${JSON.stringify(context.serverData)}</script>`);
@@ -0,0 +1,71 @@
1
+ import { _ as _define_property } from "@swc/helpers/_/_define_property";
2
+ import { OutgoingMessage } from "http";
3
+ import { Readable } from "stream";
4
+ import { Socket } from "net";
5
+ import { Server } from "./server";
6
+ import { isRedirect } from "./utils";
7
+ class IncomingMessageLike extends Readable {
8
+ constructor({ method, url, headers }) {
9
+ super();
10
+ _define_property(this, "headers", void 0);
11
+ _define_property(this, "method", void 0);
12
+ _define_property(this, "url", void 0);
13
+ _define_property(this, "socket", void 0);
14
+ this.socket = new Socket();
15
+ this.headers = headers || {};
16
+ this.headers.host = "localhost:8080";
17
+ this.method = method || "get";
18
+ this.url = url;
19
+ }
20
+ }
21
+ class ServerResponseLike extends OutgoingMessage {
22
+ end(chunk, _encoding, cb) {
23
+ this.data.push(chunk.toString());
24
+ cb && cb();
25
+ this.emit("finish");
26
+ return this;
27
+ }
28
+ constructor() {
29
+ super();
30
+ _define_property(this, "statusCode", void 0);
31
+ _define_property(this, "data", void 0);
32
+ this.statusCode = 200;
33
+ this.data = [];
34
+ }
35
+ }
36
+ class CustomServer extends Server {
37
+ async render(req, res, _url) {
38
+ const handler = this.getRequestHandler();
39
+ handler(req, res);
40
+ return null;
41
+ }
42
+ }
43
+ async function renderHtml({ url, method, headers, body, serverOptions }) {
44
+ const req = new IncomingMessageLike({
45
+ method,
46
+ url,
47
+ headers
48
+ });
49
+ if (body) {
50
+ req.push(body);
51
+ req.push(null);
52
+ }
53
+ const res = new ServerResponseLike();
54
+ const customServer = new CustomServer(serverOptions);
55
+ await customServer.init({
56
+ disableHttpServer: true
57
+ });
58
+ customServer.render(req, res);
59
+ return new Promise((resolve) => {
60
+ res.addListener("finish", () => {
61
+ resolve({
62
+ headers: res.getHeaders(),
63
+ redirected: isRedirect(res.statusCode),
64
+ status: res.statusCode,
65
+ url,
66
+ body: res.data.join()
67
+ });
68
+ });
69
+ });
70
+ }
71
+ module.exports = renderHtml;
@@ -21,7 +21,9 @@ export class Server {
21
21
  * - 执行 prepare hook
22
22
  * - 执行 server init
23
23
  */
24
- async init() {
24
+ async init({ disableHttpServer = false } = {
25
+ disableHttpServer: false
26
+ }) {
25
27
  const { options } = this;
26
28
  await this.loadServerEnv(options);
27
29
  this.initServerConfig(options);
@@ -31,7 +33,9 @@ export class Server {
31
33
  await this.injectContext(this.runner, options);
32
34
  this.server = this.serverImpl(options);
33
35
  await this.runPrepareHook(this.runner);
34
- this.app = await this.server.createHTTPServer(this.getRequestHandler());
36
+ if (!disableHttpServer) {
37
+ this.app = await this.server.createHTTPServer(this.getRequestHandler());
38
+ }
35
39
  await this.server.onInit(this.runner, this.app);
36
40
  return this;
37
41
  }
@@ -18,6 +18,7 @@ export class ModernServer {
18
18
  var _conf_bff, _this_conf_server, _conf_security, _this_conf_output;
19
19
  this.runner = runner;
20
20
  const { distDir, staticGenerate, conf } = this;
21
+ this.initReader();
21
22
  debug("final server conf", this.conf);
22
23
  this.proxyHandler = createProxyHandler((_conf_bff = conf.bff) === null || _conf_bff === void 0 ? void 0 : _conf_bff.proxy);
23
24
  if (this.proxyHandler) {
@@ -25,8 +26,7 @@ export class ModernServer {
25
26
  this.addHandler(handler);
26
27
  });
27
28
  }
28
- this.reader.init();
29
- app.on("close", () => {
29
+ app === null || app === void 0 ? void 0 : app.on("close", () => {
30
30
  this.reader.close();
31
31
  });
32
32
  const usageRoutes = this.filterRoutes(this.getRoutes());
@@ -51,18 +51,6 @@ export class ModernServer {
51
51
  // server ready
52
52
  onRepack(_) {
53
53
  }
54
- async onServerChange({ filepath }) {
55
- const { pwd } = this;
56
- const { api, server } = AGGRED_DIR;
57
- const apiPath = path.normalize(path.join(pwd, api));
58
- const serverPath = path.normalize(path.join(pwd, server));
59
- const onlyApi = filepath.startsWith(apiPath);
60
- const onlyWeb = filepath.startsWith(serverPath);
61
- await this.prepareFrameHandler({
62
- onlyWeb,
63
- onlyApi
64
- });
65
- }
66
54
  // exposed requestHandler
67
55
  getRequestHandler() {
68
56
  return this.requestHandler.bind(this);
@@ -89,6 +77,21 @@ export class ModernServer {
89
77
  return createServer(handler);
90
78
  }
91
79
  /* —————————————————————— function will be overwrite —————————————————————— */
80
+ initReader() {
81
+ this.reader.init();
82
+ }
83
+ async onServerChange({ filepath }) {
84
+ const { pwd } = this;
85
+ const { api, server } = AGGRED_DIR;
86
+ const apiPath = path.normalize(path.join(pwd, api));
87
+ const serverPath = path.normalize(path.join(pwd, server));
88
+ const onlyApi = filepath.startsWith(apiPath);
89
+ const onlyWeb = filepath.startsWith(serverPath);
90
+ await this.prepareFrameHandler({
91
+ onlyWeb,
92
+ onlyApi
93
+ });
94
+ }
92
95
  // get routes info
93
96
  getRoutes() {
94
97
  if (this.presetRoutes) {
@@ -452,7 +455,7 @@ export class ModernServer {
452
455
  _define_property(this, "staticGenerate", void 0);
453
456
  require("ignore-styles");
454
457
  this.pwd = pwd;
455
- this.distDir = path.join(pwd, config.output.path || "dist");
458
+ this.distDir = path.resolve(pwd, config.output.path || "dist");
456
459
  this.workDir = this.distDir;
457
460
  this.conf = config;
458
461
  debug("server conf", this.conf);
@@ -1,4 +1,6 @@
1
1
  import { _ as _define_property } from "@swc/helpers/_/_define_property";
2
+ import { createAsyncPipeline } from "@modern-js/plugin";
3
+ import { createAfterMatchContext, createAfterRenderContext, createMiddlewareContext } from "./libs/hook-api/index.worker";
2
4
  import { Logger } from "./libs/logger";
3
5
  import { RouteMatchManager } from "./libs/route";
4
6
  import { metrics as defaultMetrics } from "./libs/metrics";
@@ -44,20 +46,56 @@ export class ReturnResponse {
44
46
  }
45
47
  }
46
48
  const RESPONSE_NOTFOUND = new ReturnResponse("404: Page not found", 404);
49
+ const isRedirect = (code) => {
50
+ return [
51
+ 301,
52
+ 302,
53
+ 307,
54
+ 308
55
+ ].includes(code);
56
+ };
57
+ const checkIsSent = (context) => {
58
+ if (context.res.isSent) {
59
+ return true;
60
+ }
61
+ if (context.res.headers.get("Location") && isRedirect(context.res.status)) {
62
+ return true;
63
+ }
64
+ return false;
65
+ };
66
+ const middlewarePipeline = createAsyncPipeline();
47
67
  export const createHandler = (manifest) => {
48
68
  const routeMgr = new RouteMatchManager();
49
69
  const { pages, routes } = manifest;
50
70
  routeMgr.reset(routes);
51
71
  return async (options) => {
72
+ var _page_serverHooks, _page_serverHooks_afterMatch;
52
73
  const { request, loadableStats, routeManifest } = options;
53
74
  const url = new URL(request.url);
54
75
  const pageMatch = routeMgr.match(url.pathname);
55
76
  if (!pageMatch) {
56
77
  return RESPONSE_NOTFOUND;
57
78
  }
58
- const page = pages[pageMatch.spec.urlPath];
79
+ const entryName = pageMatch.spec.urlPath;
80
+ const page = pages[entryName];
81
+ const logger = new Logger({
82
+ level: "warn"
83
+ });
84
+ const metrics = defaultMetrics;
85
+ const hookContext = createWorkerHookContext(request.url, logger, metrics);
86
+ const afterMatchHookContext = createAfterMatchContext(hookContext, entryName);
87
+ page === null || page === void 0 ? void 0 : (_page_serverHooks = page.serverHooks) === null || _page_serverHooks === void 0 ? void 0 : (_page_serverHooks_afterMatch = _page_serverHooks.afterMatch) === null || _page_serverHooks_afterMatch === void 0 ? void 0 : _page_serverHooks_afterMatch.call(_page_serverHooks, afterMatchHookContext, () => void 0);
88
+ if (checkIsSent(hookContext)) {
89
+ return new ReturnResponse(hookContext.res.body || "Unkown body", hookContext.res.status, hookContext.res.headers);
90
+ }
59
91
  if (page.serverRender) {
60
92
  try {
93
+ var _page_serverHooks1, _page_serverHooks2, _page_serverHooks_afterRender;
94
+ const middlewarsHookContext = createMiddlewareContext(hookContext);
95
+ applyMiddlewares(middlewarsHookContext, (_page_serverHooks1 = page.serverHooks) === null || _page_serverHooks1 === void 0 ? void 0 : _page_serverHooks1.middleware);
96
+ if (checkIsSent(hookContext)) {
97
+ return new ReturnResponse(hookContext.res.body || "Unkown body", hookContext.res.status, hookContext.res.headers);
98
+ }
61
99
  const responseLike = {
62
100
  headers: {},
63
101
  statusCode: 200,
@@ -70,33 +108,37 @@ export const createHandler = (manifest) => {
70
108
  }
71
109
  };
72
110
  const params = pageMatch.parseURLParams(url.pathname) || {};
111
+ const { urlPath: baseUrl } = pageMatch;
73
112
  const serverRenderContext = {
74
- request: createServerRequest(url, request, params),
113
+ request: createServerRequest(url, baseUrl, request, params),
75
114
  response: responseLike,
76
115
  loadableStats,
77
116
  routeManifest,
78
117
  redirection: {},
79
118
  template: page.template,
80
119
  entryName: page.entryName,
81
- logger: new Logger({
82
- level: "warn"
83
- }),
84
- metrics: defaultMetrics,
120
+ logger,
121
+ metrics,
85
122
  // FIXME: pass correctly req & res
86
123
  req: request,
87
124
  res: responseLike
88
125
  };
89
126
  const body = await page.serverRender(serverRenderContext);
90
- return new ReturnResponse(body, responseLike.statusCode, responseLike.headers);
127
+ const afterRenderHookContext = createAfterRenderContext(hookContext, body);
128
+ (_page_serverHooks2 = page.serverHooks) === null || _page_serverHooks2 === void 0 ? void 0 : (_page_serverHooks_afterRender = _page_serverHooks2.afterRender) === null || _page_serverHooks_afterRender === void 0 ? void 0 : _page_serverHooks_afterRender.call(_page_serverHooks2, afterRenderHookContext, () => void 0);
129
+ if (checkIsSent(hookContext)) {
130
+ return new ReturnResponse(hookContext.res.body || "Unkown body", hookContext.res.status, hookContext.res.headers);
131
+ }
132
+ return new ReturnResponse(afterRenderHookContext.template.get(), responseLike.statusCode, responseLike.headers);
91
133
  } catch (e) {
92
134
  console.warn(`page(${pageMatch.spec.urlPath}) serverRender occur error: `);
93
135
  console.warn(e);
94
136
  return createResponse(page.template);
95
137
  }
96
138
  }
97
- console.warn(`Can't not page(${pageMatch.spec.urlPath}) serverRender`);
139
+ console.warn(`Can't not page(${entryName}) serverRender`);
98
140
  return createResponse(page.template);
99
- function createServerRequest(url2, request2, params) {
141
+ function createServerRequest(url2, baseUrl, request2, params) {
100
142
  const { pathname, host, searchParams } = url2;
101
143
  const { headers: rawHeaders } = request2;
102
144
  const headers = {};
@@ -105,6 +147,8 @@ export const createHandler = (manifest) => {
105
147
  });
106
148
  const query = Object.fromEntries(searchParams);
107
149
  return {
150
+ url: url2.href,
151
+ baseUrl,
108
152
  pathname,
109
153
  host,
110
154
  headers,
@@ -121,3 +165,39 @@ function createResponse(template) {
121
165
  return RESPONSE_NOTFOUND;
122
166
  }
123
167
  }
168
+ function createWorkerHookContext(url, logger, metrics) {
169
+ const [res, req] = [
170
+ {
171
+ headers: new Headers(),
172
+ body: "",
173
+ status: 200,
174
+ isSent: false
175
+ },
176
+ new Request(url)
177
+ ];
178
+ return {
179
+ res,
180
+ req,
181
+ logger,
182
+ metrics
183
+ };
184
+ }
185
+ function applyMiddlewares(ctx, middleware) {
186
+ if (middleware) {
187
+ const middlewares = (() => {
188
+ if (Array.isArray(middleware)) {
189
+ return middleware;
190
+ } else {
191
+ return [
192
+ middleware
193
+ ];
194
+ }
195
+ })();
196
+ middlewares.forEach((middleware2) => {
197
+ middlewarePipeline.use(middleware2);
198
+ });
199
+ middlewarePipeline.run(ctx, {
200
+ onLast: () => void 0
201
+ });
202
+ }
203
+ }
@@ -2,7 +2,7 @@
2
2
  /// <reference types="node" />
3
3
  /// <reference types="node" />
4
4
  /// <reference types="node/http" />
5
- /// <reference types=".dts-temp/dQl0PkddbjYymDTKBgvwm/src/type" />
5
+ /// <reference types=".dts-temp/t0a-Q86lFJHlugCUZWCMB/src/type" />
6
6
  import { IncomingMessage, ServerResponse } from 'http';
7
7
  import qs from 'querystring';
8
8
  import type { ModernServerContext as ModernServerContextInterface } from '@modern-js/types';