@modern-js/server 1.3.1-beta.0 → 1.4.1

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 (126) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/dist/js/modern/dev-tools/babel/register.js +1 -0
  3. package/dist/js/modern/dev-tools/dev-server-plugin.js +1 -2
  4. package/dist/js/modern/dev-tools/mock/getMockData.js +24 -1
  5. package/dist/js/modern/dev-tools/mock/index.js +1 -26
  6. package/dist/js/modern/dev-tools/socket-server.js +4 -2
  7. package/dist/js/modern/dev-tools/watcher/index.js +4 -7
  8. package/dist/js/modern/dev-tools/watcher/stats-cache.js +32 -20
  9. package/dist/js/modern/libs/context/context.js +6 -0
  10. package/dist/js/modern/libs/hook-api/route.js +6 -4
  11. package/dist/js/modern/libs/render/index.js +1 -0
  12. package/dist/js/modern/libs/render/reader.js +1 -2
  13. package/dist/js/modern/libs/render/ssr.js +7 -2
  14. package/dist/js/modern/libs/route/index.js +0 -1
  15. package/dist/js/modern/libs/route/matcher.js +15 -3
  16. package/dist/js/modern/libs/route/route.js +1 -0
  17. package/dist/js/modern/server/dev-server/dev-server.js +3 -0
  18. package/dist/js/modern/server/index.js +5 -4
  19. package/dist/js/modern/server/modern-server-split.js +1 -1
  20. package/dist/js/modern/server/modern-server.js +13 -34
  21. package/dist/js/modern/utils.js +39 -0
  22. package/dist/js/node/dev-tools/babel/register.js +1 -0
  23. package/dist/js/node/dev-tools/dev-server-plugin.js +1 -2
  24. package/dist/js/node/dev-tools/mock/getMockData.js +29 -2
  25. package/dist/js/node/dev-tools/mock/index.js +5 -26
  26. package/dist/js/node/dev-tools/socket-server.js +4 -2
  27. package/dist/js/node/dev-tools/watcher/index.js +7 -5
  28. package/dist/js/node/dev-tools/watcher/stats-cache.js +33 -20
  29. package/dist/js/node/libs/context/context.js +6 -0
  30. package/dist/js/node/libs/hook-api/route.js +6 -4
  31. package/dist/js/node/libs/render/index.js +1 -0
  32. package/dist/js/node/libs/render/reader.js +2 -1
  33. package/dist/js/node/libs/render/ssr.js +8 -2
  34. package/dist/js/node/libs/route/index.js +0 -1
  35. package/dist/js/node/libs/route/matcher.js +16 -3
  36. package/dist/js/node/libs/route/route.js +1 -0
  37. package/dist/js/node/server/dev-server/dev-server.js +3 -0
  38. package/dist/js/node/server/index.js +9 -6
  39. package/dist/js/node/server/modern-server-split.js +1 -1
  40. package/dist/js/node/server/modern-server.js +12 -33
  41. package/dist/js/node/utils.js +51 -2
  42. package/dist/types/dev-tools/mock/getMockData.d.ts +2 -1
  43. package/dist/types/dev-tools/socket-server.d.ts +1 -2
  44. package/dist/types/dev-tools/watcher/index.d.ts +2 -1
  45. package/dist/types/dev-tools/watcher/stats-cache.d.ts +3 -2
  46. package/dist/types/libs/context/context.d.ts +2 -0
  47. package/dist/types/libs/hook-api/route.d.ts +3 -2
  48. package/dist/types/libs/render/reader.d.ts +13 -0
  49. package/dist/types/libs/render/ssr.d.ts +1 -0
  50. package/dist/types/libs/route/matcher.d.ts +1 -1
  51. package/dist/types/libs/route/route.d.ts +1 -0
  52. package/dist/types/server/dev-server/dev-server-split.d.ts +3 -3
  53. package/dist/types/server/modern-server-split.d.ts +3 -3
  54. package/dist/types/server/modern-server.d.ts +1 -2
  55. package/dist/types/type.d.ts +6 -4
  56. package/dist/types/utils.d.ts +5 -1
  57. package/package.json +13 -12
  58. package/tests/context.test.ts +12 -1
  59. package/tests/dev.test.ts +306 -7
  60. package/tests/fixtures/mock/exist/config/mock/index.ts +11 -0
  61. package/tests/fixtures/mock/zero/config/mock/index.ts +1 -0
  62. package/tests/fixtures/pure/tsconfig.json +0 -1
  63. package/tests/fixtures/reader/index.ts +3 -0
  64. package/tests/fixtures/route-spec/dynamic.json +13 -0
  65. package/tests/fixtures/ssr/bundle.js +5 -0
  66. package/tests/fixtures/static-dir/bar.html +11 -0
  67. package/tests/fixtures/static-dir/baz/index.html +11 -0
  68. package/tests/fixtures/static-dir/foo/index.html +11 -0
  69. package/tests/fixtures/watch/a.ts +3 -0
  70. package/tests/fixtures/watch/index.ts +5 -0
  71. package/tests/fixtures/watch/stats.txt +1 -0
  72. package/tests/hook.test.ts +1 -1
  73. package/tests/render.test.ts +102 -0
  74. package/tests/route.test.ts +26 -3
  75. package/tests/utils.test.ts +35 -0
  76. package/tests/watcher.test.ts +6 -4
  77. package/src/constants.ts +0 -26
  78. package/src/dev-tools/babel/register.ts +0 -37
  79. package/src/dev-tools/dev-server-plugin.ts +0 -48
  80. package/src/dev-tools/https/global.d.ts +0 -3
  81. package/src/dev-tools/https/index.ts +0 -12
  82. package/src/dev-tools/launch-editor/index.ts +0 -29
  83. package/src/dev-tools/mock/getMockData.ts +0 -109
  84. package/src/dev-tools/mock/index.ts +0 -63
  85. package/src/dev-tools/socket-server.ts +0 -192
  86. package/src/dev-tools/watcher/dependency-tree.ts +0 -94
  87. package/src/dev-tools/watcher/index.ts +0 -81
  88. package/src/dev-tools/watcher/stats-cache.ts +0 -53
  89. package/src/index.ts +0 -16
  90. package/src/libs/context/context.ts +0 -176
  91. package/src/libs/context/index.ts +0 -7
  92. package/src/libs/hook-api/route.ts +0 -38
  93. package/src/libs/hook-api/template.ts +0 -53
  94. package/src/libs/metrics.ts +0 -15
  95. package/src/libs/proxy.ts +0 -85
  96. package/src/libs/render/cache/__tests__/cache.fun.test.ts +0 -94
  97. package/src/libs/render/cache/__tests__/cache.test.ts +0 -240
  98. package/src/libs/render/cache/__tests__/cacheable.ts +0 -44
  99. package/src/libs/render/cache/__tests__/error-configuration.ts +0 -34
  100. package/src/libs/render/cache/__tests__/matched-cache.ts +0 -88
  101. package/src/libs/render/cache/index.ts +0 -75
  102. package/src/libs/render/cache/page-caches/index.ts +0 -11
  103. package/src/libs/render/cache/page-caches/lru.ts +0 -38
  104. package/src/libs/render/cache/spr.ts +0 -301
  105. package/src/libs/render/cache/type.ts +0 -59
  106. package/src/libs/render/cache/util.ts +0 -97
  107. package/src/libs/render/index.ts +0 -78
  108. package/src/libs/render/modern/browser-list.ts +0 -7
  109. package/src/libs/render/modern/index.ts +0 -41
  110. package/src/libs/render/modern/module.d.ts +0 -4
  111. package/src/libs/render/reader.ts +0 -119
  112. package/src/libs/render/ssr.ts +0 -62
  113. package/src/libs/render/static.ts +0 -52
  114. package/src/libs/render/type.ts +0 -38
  115. package/src/libs/route/index.ts +0 -77
  116. package/src/libs/route/matcher.ts +0 -93
  117. package/src/libs/route/route.ts +0 -32
  118. package/src/libs/serve-file.ts +0 -34
  119. package/src/server/dev-server/dev-server-split.ts +0 -41
  120. package/src/server/dev-server/dev-server.ts +0 -300
  121. package/src/server/dev-server/index.ts +0 -2
  122. package/src/server/index.ts +0 -163
  123. package/src/server/modern-server-split.ts +0 -97
  124. package/src/server/modern-server.ts +0 -636
  125. package/src/type.ts +0 -88
  126. package/src/utils.ts +0 -79
@@ -1,38 +0,0 @@
1
- import LRU from 'lru-cache';
2
- import { PageCache, PageCachesInterface } from '../type';
3
-
4
- export class LRUCaches implements PageCachesInterface {
5
- caches: LRU<string, PageCache>;
6
-
7
- private readonly max: number;
8
-
9
- constructor(options: { max: number }) {
10
- this.max = options.max;
11
- this.caches = new LRU(this.max);
12
- }
13
-
14
- init() {
15
- return Promise.resolve();
16
- }
17
-
18
- public keys(): string[] {
19
- return this.caches.keys();
20
- }
21
-
22
- public get(key: string) {
23
- return Promise.resolve(this.caches.get(key) || null);
24
- }
25
-
26
- public peek(key: string) {
27
- return this.caches.peek(key) || null;
28
- }
29
-
30
- public set(key: string, cache: PageCache) {
31
- this.caches.set(key, cache);
32
- return Promise.resolve();
33
- }
34
-
35
- public del(key: string) {
36
- this.caches.del(key);
37
- }
38
- }
@@ -1,301 +0,0 @@
1
- /* eslint-disable @typescript-eslint/member-ordering */
2
- /* eslint-disable no-lone-blocks */
3
- import crypto from 'crypto';
4
- import { IncomingHttpHeaders } from 'http';
5
- import url from 'url';
6
- import LRUCache from 'lru-cache';
7
- import mime from 'mime-types';
8
- import {
9
- cacheAddition,
10
- connectFactor,
11
- fname,
12
- maybeSync,
13
- namespaceHash,
14
- valueFactory,
15
- withCoalescedInvoke,
16
- } from './util';
17
- import { createPageCaches } from './page-caches';
18
- import {
19
- PageCache,
20
- PageCachesInterface,
21
- CacheContent,
22
- CacheManagerOptions,
23
- CacheResult,
24
- CacheContext,
25
- CacheConfig,
26
- } from './type';
27
-
28
- const MAX_CACHE_EACH_REQ = Number(process.env.ROUTE_CACHE_LIMIT) || 10;
29
- const MAX_SIZE_EACH_CLUSTER = Number(process.env.CLUSTER_CACHE_LIMIT) || 100;
30
- const BASE_LEVEL = 0;
31
- const QUERY_LEVEL = 1;
32
- const HEADER_LEVEL = 2;
33
- const QUERY_HEADER_LEVEL = 3;
34
-
35
- class CacheManager {
36
- cache: LRUCache<string, CacheContent>;
37
-
38
- cacheOptions: CacheManagerOptions;
39
-
40
- constructor(cacheOptions: CacheManagerOptions) {
41
- this.cacheOptions = cacheOptions;
42
-
43
- this.cache = new LRUCache({
44
- max: Math.min(MAX_SIZE_EACH_CLUSTER, 600) * 1024 * 1024, // 默认存 100M,最大 600M
45
- length(n: CacheContent) {
46
- const len = n.caches
47
- .keys()
48
- .reduce((total, cur) => total + (n.caches.peek(cur)?.size || 0), 1);
49
- return len;
50
- },
51
- });
52
- }
53
-
54
- private md5(content: string) {
55
- const md5 = crypto.createHash('md5');
56
- return md5.update(content).digest('hex');
57
- }
58
-
59
- private generateRequestKey(context: CacheContext) {
60
- const { pathname, entry } = context;
61
- return this.md5(`${pathname}_${entry}`);
62
- }
63
-
64
- private replaceValue(value: string, matcher: Record<string, string>) {
65
- let final = value;
66
- Object.keys(matcher).some(replacer => {
67
- const reg = new RegExp(matcher[replacer]);
68
- if (reg.test(value)) {
69
- final = replacer;
70
- return true;
71
- }
72
- return false;
73
- });
74
- return final;
75
- }
76
-
77
- private factor(
78
- keys: string[],
79
- obj: url.URLSearchParams | IncomingHttpHeaders,
80
- matches: Record<string, Record<string, string>> = {},
81
- ) {
82
- keys.sort();
83
- const getValue = valueFactory(obj);
84
-
85
- const factorAry = keys.reduce((ary: string[], key: string) => {
86
- let value: string = getValue(key) || '';
87
- const matcher = matches[key];
88
- if (matcher) {
89
- value = this.replaceValue(value, matcher);
90
- }
91
-
92
- return ary.concat([key, value]);
93
- }, []);
94
- return factorAry.join(',');
95
- }
96
-
97
- private queryFactor(context: CacheContext, data: CacheContent) {
98
- const queryKeys = data.includes?.query;
99
- const queryMatches = data.matches?.query;
100
- if (!queryKeys || queryKeys.length === 0) {
101
- return null;
102
- }
103
- const requestQuery: any = context.query;
104
- const queryFactor = this.factor(queryKeys, requestQuery, queryMatches);
105
- return queryFactor;
106
- }
107
-
108
- private headerFactor(context: CacheContext, data: CacheContent) {
109
- const headerKeys = data.includes?.header;
110
- const headerMatches = data.matches?.header;
111
- if (!headerKeys || headerKeys.length === 0) {
112
- return null;
113
- }
114
- const requestHeader: any = context.headers;
115
- const headerFactor = this.factor(headerKeys, requestHeader, headerMatches);
116
- return headerFactor;
117
- }
118
-
119
- private readonly find: any = (() => {
120
- {
121
- // eslint-disable-next-line consistent-this,@typescript-eslint/no-this-alias
122
- const _this = this;
123
- return {
124
- [fname(BASE_LEVEL)](
125
- context: CacheContext,
126
- cacheKey: string,
127
- // data: CacheContent,
128
- ): string | null {
129
- return _this.md5(cacheKey);
130
- },
131
- [fname(QUERY_LEVEL)](
132
- context: CacheContext,
133
- cacheKey: string,
134
- data: CacheContent,
135
- ): string | null {
136
- const queryFactor = _this.queryFactor(context, data);
137
- if (!queryFactor) {
138
- return null;
139
- }
140
- return _this.md5(connectFactor(cacheKey, queryFactor));
141
- },
142
- [fname(HEADER_LEVEL)](
143
- context: CacheContext,
144
- cacheKey: string,
145
- data: CacheContent,
146
- ): string | null {
147
- const headerFactor = _this.headerFactor(context, data);
148
- if (!headerFactor) {
149
- return null;
150
- }
151
- return _this.md5(connectFactor(cacheKey, headerFactor));
152
- },
153
- [fname(QUERY_HEADER_LEVEL)](
154
- context: CacheContext,
155
- cacheKey: string,
156
- data: CacheContent,
157
- ): string | null {
158
- const queryFactor = _this.queryFactor(context, data);
159
- const headerFactor = _this.headerFactor(context, data);
160
- if (!queryFactor || !headerFactor) {
161
- return null;
162
- }
163
- return _this.md5(connectFactor(cacheKey, headerFactor, queryFactor));
164
- },
165
- };
166
- }
167
- })();
168
-
169
- private async best(
170
- context: CacheContext,
171
- cacheKey: string,
172
- data: CacheContent,
173
- ): Promise<PageCache | null> {
174
- const { level } = data;
175
- const cacheHash = this.find[fname(level)](context, cacheKey, data);
176
- if (!cacheHash) {
177
- return null;
178
- }
179
- return data.caches.get(cacheHash);
180
- }
181
-
182
- private createCacheContent(
183
- config: CacheConfig,
184
- caches: PageCachesInterface,
185
- ): CacheContent {
186
- return {
187
- level: config.level,
188
- interval: config.interval,
189
- includes: config.includes || null,
190
- limit: config.staleLimit,
191
- matches: config.matches || null,
192
- caches,
193
- };
194
- }
195
-
196
- async get(context: CacheContext): Promise<CacheResult | null> {
197
- const cacheKey = this.generateRequestKey(context);
198
- const data = this.cache.get(cacheKey);
199
-
200
- // no cache key matched
201
- if (!data) {
202
- return null;
203
- }
204
-
205
- const dest = await this.best(context, cacheKey, data);
206
- // no cache for current page with current config
207
- if (!dest) {
208
- return null;
209
- }
210
-
211
- const { expireTime, limitTime, html, cacheHash } = dest;
212
- const isStale = Date.now() - expireTime > 0;
213
- const isGarbage = limitTime ? Date.now() - limitTime > 0 : false;
214
-
215
- return {
216
- content: html || '',
217
- contentType: mime.contentType('html') as string,
218
- isStale,
219
- isGarbage,
220
- hash: cacheHash,
221
- };
222
- }
223
-
224
- async set(
225
- context: CacheContext,
226
- html: string,
227
- cacheConfig: CacheConfig,
228
- sync = false,
229
- ) {
230
- if (!cacheConfig) {
231
- return false;
232
- }
233
-
234
- // each version with route is a separate cache
235
- const cacheKey = this.generateRequestKey(context);
236
- let data = this.cache.get(cacheKey);
237
- if (!data) {
238
- const caches = await createPageCaches(MAX_CACHE_EACH_REQ);
239
- // eslint-disable-next-line require-atomic-updates
240
- data = this.createCacheContent(cacheConfig, caches);
241
- }
242
-
243
- const cacheHash = this.find[fname(cacheConfig.level)](
244
- context,
245
- cacheKey,
246
- data,
247
- );
248
-
249
- // if cacheHash is null, maybe level not match meta key, do not cache
250
- if (!cacheHash) {
251
- return false;
252
- }
253
-
254
- const cacheSyncOrAsync = async () => {
255
- const next = data as CacheContent;
256
- const limit = cacheConfig.staleLimit;
257
-
258
- const storeHTML = cacheAddition(html, cacheHash);
259
- const size = storeHTML.length;
260
- await next.caches.set(cacheHash, {
261
- expireTime: Date.now() + cacheConfig.interval * 1000,
262
- limitTime: typeof limit === 'number' ? Date.now() + limit * 1000 : null,
263
- cacheHash,
264
- html: storeHTML,
265
- size,
266
- });
267
- this.cache.set(cacheKey, next);
268
- return true;
269
- };
270
-
271
- // cache set is async, each hash is cached only once at the same time
272
- const doCache = withCoalescedInvoke(cacheSyncOrAsync).bind(
273
- null,
274
- namespaceHash('stream', cacheHash),
275
- [],
276
- );
277
- return maybeSync(doCache)(sync);
278
- }
279
-
280
- async del(context: CacheContext, cacheHash: string) {
281
- const cacheKey = this.generateRequestKey(context);
282
- const data = this.cache.get(cacheKey);
283
- data?.caches.del(cacheHash);
284
- }
285
- }
286
-
287
- let manager: CacheManager;
288
- export function createCache() {
289
- if (manager) {
290
- return manager;
291
- }
292
-
293
- manager = new CacheManager({ max: 0 });
294
- return manager;
295
- }
296
-
297
- export function destroyCache() {
298
- manager = null!;
299
- }
300
- /* eslint-enable no-lone-blocks */
301
- /* eslint-enable @typescript-eslint/member-ordering */
@@ -1,59 +0,0 @@
1
- import { IncomingHttpHeaders } from 'http';
2
- import { RenderResult } from '../../../type';
3
-
4
- export type { CacheConfig } from '../type';
5
-
6
- export type CacheContent = {
7
- level: number;
8
- includes: {
9
- header?: string[];
10
- query?: string[];
11
- } | null;
12
- matches?: {
13
- header?: Record<string, Record<string, string>>;
14
- query?: Record<string, Record<string, string>>;
15
- } | null;
16
- interval: number;
17
- limit: number | boolean;
18
- caches: PageCachesInterface;
19
- };
20
- export interface PageCachesInterface {
21
- caches: any;
22
-
23
- init: () => void;
24
-
25
- get: (key: string) => Promise<PageCache | null>;
26
-
27
- peek: (key: string) => PageCache | null;
28
-
29
- set: (key: string, cache: PageCache) => Promise<void>;
30
-
31
- del: (key: string) => void;
32
-
33
- keys: () => string[];
34
- }
35
-
36
- export type PageCache = {
37
- html: string;
38
- expireTime: number;
39
- limitTime: number | null;
40
- cacheHash: string;
41
- size: number;
42
- };
43
-
44
- export type CacheManagerOptions = {
45
- max: number;
46
- };
47
-
48
- export interface CacheResult extends RenderResult {
49
- isStale: boolean;
50
- isGarbage: boolean;
51
- hash: string;
52
- }
53
-
54
- export type CacheContext = {
55
- entry: string;
56
- pathname: string;
57
- query: Record<string, string>;
58
- headers: IncomingHttpHeaders;
59
- };
@@ -1,97 +0,0 @@
1
- import { IncomingHttpHeaders } from 'http';
2
- import url from 'url';
3
-
4
- export function namespaceHash(namespace: string, hash: string) {
5
- return `${namespace}/${hash}`;
6
- }
7
-
8
- export function fname(lv: number) {
9
- return `f${lv}`;
10
- }
11
-
12
- export function connectFactor(...args: string[]) {
13
- return args.join('-');
14
- }
15
-
16
- export function valueFactory(obj: url.URLSearchParams | IncomingHttpHeaders) {
17
- if (obj instanceof url.URLSearchParams) {
18
- return function (key: string) {
19
- return obj.get(key);
20
- };
21
- } else {
22
- return function (key: string) {
23
- const value = obj[key];
24
- if (Array.isArray(value)) {
25
- return value.join(',');
26
- }
27
- return value;
28
- };
29
- }
30
- }
31
-
32
- export function getTime([s, ns]: [number, number]): number {
33
- return Math.floor(s * 1e3 + ns / 1e6);
34
- }
35
-
36
- const RE_START_IN_HEAD = /<head>/;
37
- export function cacheAddition(html: string, hash: string) {
38
- const additionHtml = html.replace(
39
- RE_START_IN_HEAD,
40
- `<head><meta name="x-moden-spr" content="${hash}">`,
41
- );
42
- return additionHtml;
43
- }
44
-
45
- type CoalescedInvoke<T> = {
46
- isOrigin: boolean;
47
- value: T;
48
- };
49
-
50
- type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
51
- const globalInvokeCache = new Map<string, Promise<CoalescedInvoke<unknown>>>();
52
- export function withCoalescedInvoke<F extends (...args: any[]) => Promise<any>>(
53
- func: F,
54
- ): (
55
- key: string,
56
- args: Parameters<F>,
57
- ) => Promise<CoalescedInvoke<UnwrapPromise<ReturnType<F>>>> {
58
- return async function (key: string, args: Parameters<F>) {
59
- const entry = globalInvokeCache.get(key);
60
- if (entry) {
61
- // eslint-disable-next-line promise/prefer-await-to-then
62
- return entry.then(res => ({
63
- isOrigin: false,
64
- value: res.value as UnwrapPromise<ReturnType<F>>,
65
- }));
66
- }
67
-
68
- function __wrapper() {
69
- return func(...args);
70
- }
71
-
72
- const future = __wrapper()
73
- // eslint-disable-next-line promise/prefer-await-to-then
74
- .then(res => {
75
- globalInvokeCache.delete(key);
76
- return { isOrigin: true, value: res as UnwrapPromise<ReturnType<F>> };
77
- })
78
- // eslint-disable-next-line promise/prefer-await-to-then
79
- .catch(err => {
80
- globalInvokeCache.delete(key);
81
- throw err;
82
- });
83
- globalInvokeCache.set(key, future);
84
- return future;
85
- };
86
- }
87
-
88
- export function maybeSync(fn: () => Promise<any>) {
89
- return (sync: boolean) => {
90
- if (sync) {
91
- return fn();
92
- } else {
93
- fn();
94
- return Promise.resolve();
95
- }
96
- };
97
- }
@@ -1,78 +0,0 @@
1
- import path from 'path';
2
- import { fs } from '@modern-js/utils';
3
- import mime from 'mime-types';
4
- import { RenderResult, ServerHookRunner } from '../../type';
5
- import { ModernRoute } from '../route';
6
- import { ModernServerContext } from '../context';
7
- import { ERROR_DIGEST } from '../../constants';
8
- import { handleDirectory } from './static';
9
- import { readFile } from './reader';
10
- import * as ssr from './ssr';
11
- import { supportModern, getModernEntry } from './modern';
12
-
13
- export const createRenderHandler = ({
14
- distDir,
15
- staticGenerate,
16
- }: {
17
- distDir: string;
18
- staticGenerate: boolean;
19
- }) =>
20
- async function render({
21
- ctx,
22
- route,
23
- runner,
24
- }: {
25
- ctx: ModernServerContext;
26
- route: ModernRoute;
27
- runner: ServerHookRunner;
28
- }): Promise<RenderResult | null> {
29
- if (ctx.resHasHandled()) {
30
- return null;
31
- }
32
-
33
- const { entryPath, urlPath } = route;
34
- const entry = path.join(distDir, entryPath);
35
-
36
- if (!route.isSPA) {
37
- const result = await handleDirectory(ctx, entry, urlPath);
38
- return result;
39
- }
40
-
41
- // only spa can use es6-html
42
- const modernEntry = getModernEntry(entry);
43
- const useModern =
44
- // route.enableModernMode &&
45
- supportModern(ctx) && fs.existsSync(modernEntry);
46
- const templateHTML = useModern ? modernEntry : entry;
47
-
48
- // handles ssr first
49
- if (route.isSSR) {
50
- try {
51
- const result = await ssr.render(
52
- ctx,
53
- {
54
- distDir,
55
- entryName: route.entryName,
56
- bundle: route.bundle,
57
- template: templateHTML,
58
- staticGenerate,
59
- },
60
- runner,
61
- );
62
- return result;
63
- } catch (err) {
64
- ctx.error(ERROR_DIGEST.ERENDER, (err as Error).stack);
65
- ctx.res.setHeader('x-modern-ssr-fallback', '1');
66
- }
67
- }
68
-
69
- const content = await readFile(templateHTML);
70
- if (!content) {
71
- return null;
72
- }
73
-
74
- return {
75
- content,
76
- contentType: mime.contentType(path.extname(templateHTML)) as string,
77
- };
78
- };
@@ -1,7 +0,0 @@
1
- export const NativeModuleNameMap: Record<string, string> = {
2
- Chrome: 'chrome',
3
- 'Mobile Safari': 'ios_saf',
4
- Safari: 'safari',
5
- Edge: 'edge',
6
- Firefox: 'firefox',
7
- };
@@ -1,41 +0,0 @@
1
- import Parser from 'ua-parser-js';
2
- import compareVersions from 'compare-versions';
3
- import { ModernServerContext } from '../../context';
4
- import { NativeModuleNameMap } from './browser-list';
5
-
6
- const nativeModules = require('@babel/compat-data/native-modules');
7
-
8
- export const supportModern = (context: ModernServerContext) => {
9
- if (context.query.modern_es6) {
10
- return true;
11
- }
12
-
13
- // no ua in request headers
14
- const userAgent = context.headers['user-agent'];
15
- if (!userAgent || typeof userAgent !== 'string') {
16
- return false;
17
- }
18
-
19
- const parsedUA = Parser(userAgent);
20
- const browserName = parsedUA.browser.name;
21
- const browserVersion = parsedUA.browser.version;
22
- if (!browserName || !browserVersion) {
23
- return false;
24
- }
25
-
26
- const nativeUAName = NativeModuleNameMap[browserName];
27
- if (!nativeUAName) {
28
- return false;
29
- }
30
-
31
- const version = nativeModules['es6.module'][nativeUAName];
32
- if (!version) {
33
- return false;
34
- }
35
-
36
- const result = compareVersions(browserVersion, version);
37
- return result >= 0;
38
- };
39
-
40
- export const getModernEntry = (filepath: string) =>
41
- filepath.replace(/\.html$/, '-es6.html');
@@ -1,4 +0,0 @@
1
- declare module '@babel/compat-data/native-modules' {
2
- const nativeModules: { 'es6.module': Record<string, string> };
3
- export default nativeModules;
4
- }