@modern-js/server 1.3.0 → 1.4.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 (121) hide show
  1. package/CHANGELOG.md +55 -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 +6 -9
  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/ssr.js +7 -2
  13. package/dist/js/modern/libs/route/index.js +0 -1
  14. package/dist/js/modern/libs/route/matcher.js +15 -3
  15. package/dist/js/modern/libs/route/route.js +1 -0
  16. package/dist/js/modern/server/dev-server/dev-server.js +18 -5
  17. package/dist/js/modern/server/index.js +5 -4
  18. package/dist/js/modern/server/modern-server-split.js +1 -1
  19. package/dist/js/modern/server/modern-server.js +9 -5
  20. package/dist/js/modern/utils.js +7 -0
  21. package/dist/js/node/dev-tools/babel/register.js +1 -0
  22. package/dist/js/node/dev-tools/dev-server-plugin.js +1 -2
  23. package/dist/js/node/dev-tools/mock/getMockData.js +29 -2
  24. package/dist/js/node/dev-tools/mock/index.js +5 -26
  25. package/dist/js/node/dev-tools/socket-server.js +4 -2
  26. package/dist/js/node/dev-tools/watcher/index.js +9 -7
  27. package/dist/js/node/dev-tools/watcher/stats-cache.js +33 -20
  28. package/dist/js/node/libs/context/context.js +6 -0
  29. package/dist/js/node/libs/hook-api/route.js +6 -4
  30. package/dist/js/node/libs/render/index.js +1 -0
  31. package/dist/js/node/libs/render/ssr.js +8 -2
  32. package/dist/js/node/libs/route/index.js +0 -1
  33. package/dist/js/node/libs/route/matcher.js +16 -3
  34. package/dist/js/node/libs/route/route.js +1 -0
  35. package/dist/js/node/server/dev-server/dev-server.js +20 -5
  36. package/dist/js/node/server/index.js +9 -6
  37. package/dist/js/node/server/modern-server-split.js +1 -1
  38. package/dist/js/node/server/modern-server.js +9 -5
  39. package/dist/js/node/utils.js +13 -2
  40. package/dist/types/dev-tools/mock/getMockData.d.ts +2 -1
  41. package/dist/types/dev-tools/socket-server.d.ts +1 -2
  42. package/dist/types/dev-tools/watcher/index.d.ts +3 -1
  43. package/dist/types/dev-tools/watcher/stats-cache.d.ts +3 -2
  44. package/dist/types/libs/context/context.d.ts +2 -0
  45. package/dist/types/libs/hook-api/route.d.ts +3 -2
  46. package/dist/types/libs/render/ssr.d.ts +1 -0
  47. package/dist/types/libs/route/matcher.d.ts +1 -1
  48. package/dist/types/libs/route/route.d.ts +1 -0
  49. package/dist/types/server/dev-server/dev-server-split.d.ts +3 -3
  50. package/dist/types/server/modern-server-split.d.ts +3 -3
  51. package/dist/types/server/modern-server.d.ts +1 -1
  52. package/dist/types/type.d.ts +6 -4
  53. package/dist/types/utils.d.ts +2 -1
  54. package/jest.config.js +1 -0
  55. package/package.json +12 -10
  56. package/tests/context.test.ts +12 -1
  57. package/tests/dev.test.ts +306 -7
  58. package/tests/fixtures/mock/exist/config/mock/index.ts +11 -0
  59. package/tests/fixtures/mock/zero/config/mock/index.ts +1 -0
  60. package/tests/fixtures/pure/tsconfig.json +0 -1
  61. package/tests/fixtures/route-spec/dynamic.json +13 -0
  62. package/tests/fixtures/ssr/bundle.js +5 -0
  63. package/tests/fixtures/watch/a.ts +3 -0
  64. package/tests/fixtures/watch/index.ts +5 -0
  65. package/tests/fixtures/watch/stats.txt +1 -0
  66. package/tests/hook.test.ts +1 -1
  67. package/tests/route.test.ts +26 -3
  68. package/tests/server.test.ts +19 -0
  69. package/tests/ssr.test.ts +34 -0
  70. package/tests/utils.test.ts +6 -0
  71. package/tests/watcher.test.ts +98 -0
  72. package/src/constants.ts +0 -26
  73. package/src/dev-tools/babel/register.ts +0 -37
  74. package/src/dev-tools/dev-server-plugin.ts +0 -48
  75. package/src/dev-tools/https/global.d.ts +0 -3
  76. package/src/dev-tools/https/index.ts +0 -12
  77. package/src/dev-tools/launch-editor/index.ts +0 -29
  78. package/src/dev-tools/mock/getMockData.ts +0 -109
  79. package/src/dev-tools/mock/index.ts +0 -63
  80. package/src/dev-tools/socket-server.ts +0 -192
  81. package/src/dev-tools/watcher/dependency-tree.ts +0 -94
  82. package/src/dev-tools/watcher/index.ts +0 -77
  83. package/src/dev-tools/watcher/stats-cache.ts +0 -53
  84. package/src/index.ts +0 -16
  85. package/src/libs/context/context.ts +0 -176
  86. package/src/libs/context/index.ts +0 -7
  87. package/src/libs/hook-api/route.ts +0 -38
  88. package/src/libs/hook-api/template.ts +0 -53
  89. package/src/libs/metrics.ts +0 -15
  90. package/src/libs/proxy.ts +0 -85
  91. package/src/libs/render/cache/__tests__/cache.fun.test.ts +0 -94
  92. package/src/libs/render/cache/__tests__/cache.test.ts +0 -240
  93. package/src/libs/render/cache/__tests__/cacheable.ts +0 -44
  94. package/src/libs/render/cache/__tests__/error-configuration.ts +0 -34
  95. package/src/libs/render/cache/__tests__/matched-cache.ts +0 -88
  96. package/src/libs/render/cache/index.ts +0 -75
  97. package/src/libs/render/cache/page-caches/index.ts +0 -11
  98. package/src/libs/render/cache/page-caches/lru.ts +0 -38
  99. package/src/libs/render/cache/spr.ts +0 -301
  100. package/src/libs/render/cache/type.ts +0 -59
  101. package/src/libs/render/cache/util.ts +0 -97
  102. package/src/libs/render/index.ts +0 -78
  103. package/src/libs/render/modern/browser-list.ts +0 -7
  104. package/src/libs/render/modern/index.ts +0 -41
  105. package/src/libs/render/modern/module.d.ts +0 -4
  106. package/src/libs/render/reader.ts +0 -119
  107. package/src/libs/render/ssr.ts +0 -62
  108. package/src/libs/render/static.ts +0 -52
  109. package/src/libs/render/type.ts +0 -38
  110. package/src/libs/route/index.ts +0 -77
  111. package/src/libs/route/matcher.ts +0 -93
  112. package/src/libs/route/route.ts +0 -32
  113. package/src/libs/serve-file.ts +0 -34
  114. package/src/server/dev-server/dev-server-split.ts +0 -41
  115. package/src/server/dev-server/dev-server.ts +0 -284
  116. package/src/server/dev-server/index.ts +0 -2
  117. package/src/server/index.ts +0 -163
  118. package/src/server/modern-server-split.ts +0 -97
  119. package/src/server/modern-server.ts +0 -636
  120. package/src/type.ts +0 -88
  121. package/src/utils.ts +0 -79
@@ -0,0 +1,34 @@
1
+ import path from 'path';
2
+ import { render } from '../src/libs/render/ssr';
3
+
4
+ describe('test ssr render function', () => {
5
+ test('should return content correctly ', async () => {
6
+ const renderResult = await render(
7
+ {
8
+ params: {},
9
+ pathname: '/foo',
10
+ host: 'localhost:8080',
11
+ query: {},
12
+ url: 'localhost:8080/foo',
13
+ cookieMap: {},
14
+ headers: {},
15
+ } as any,
16
+ {
17
+ urlPath: '/foo',
18
+ bundle: 'bundle.js',
19
+ distDir: path.join(__dirname, 'fixtures', 'ssr'),
20
+ template: 'tpl.html',
21
+ entryName: 'foo',
22
+ staticGenerate: false,
23
+ } as any,
24
+ {
25
+ extendSSRContext: () => {
26
+ // empty
27
+ },
28
+ } as any,
29
+ );
30
+
31
+ expect(renderResult.content).toMatch('Modern.js');
32
+ expect(renderResult.contentType).toMatch('text/html; charset=utf-8');
33
+ });
34
+ });
@@ -1,3 +1,4 @@
1
+ import { compile } from 'path-to-regexp';
1
2
  import {
2
3
  noop,
3
4
  mergeExtension,
@@ -68,4 +69,9 @@ describe('test server utils', () => {
68
69
  expect(after.api).toEqual([middleware]);
69
70
  });
70
71
  });
72
+
73
+ test('should return full path', () => {
74
+ const fn = compile('/home/:id', { encode: encodeURIComponent });
75
+ expect(fn({ id: 2 })).toBe('/home/2');
76
+ });
71
77
  });
@@ -0,0 +1,98 @@
1
+ import * as path from 'path';
2
+ import { fs } from '@modern-js/utils';
3
+ import Watcher from '../src/dev-tools/watcher';
4
+
5
+ jest.useRealTimers();
6
+
7
+ describe('watcher', () => {
8
+ let watcher: Watcher;
9
+ jest.setTimeout(25000);
10
+ beforeAll(() => {
11
+ watcher = new Watcher();
12
+ });
13
+
14
+ test('should emit change', done => {
15
+ const pwd = path.join(__dirname, './fixtures/pure');
16
+ const serverDir = path.normalize(path.join(pwd, './tmp-server'));
17
+
18
+ const callback = jest.fn();
19
+ if (fs.pathExistsSync(serverDir)) {
20
+ fs.removeSync(serverDir);
21
+ }
22
+ const writeFiles = () => {
23
+ fs.writeFileSync(
24
+ path.normalize(path.join(serverDir, 'index.js')),
25
+ 'test',
26
+ 'utf8',
27
+ );
28
+ };
29
+
30
+ const clear = () => {
31
+ fs.removeSync(serverDir);
32
+ };
33
+
34
+ fs.mkdirSync(serverDir);
35
+
36
+ watcher.listen(
37
+ [`${serverDir}/**/*`],
38
+ {
39
+ ignoreInitial: true,
40
+ ignored: /api\/typings\/.*/,
41
+ },
42
+ async () => {
43
+ callback();
44
+ expect(callback).toHaveBeenCalledTimes(1);
45
+ await watcher.close();
46
+ clear();
47
+ done();
48
+ },
49
+ );
50
+
51
+ setTimeout(writeFiles, 100);
52
+ });
53
+
54
+ test('should not emit change when typings file changed', done => {
55
+ const pwd = path.join(__dirname, './fixtures/pure');
56
+ const apiDir = path.normalize(path.join(pwd, './api'));
57
+
58
+ const callback = jest.fn();
59
+
60
+ if (fs.pathExistsSync(apiDir)) {
61
+ fs.removeSync(apiDir);
62
+ }
63
+
64
+ const writeFiles = () => {
65
+ fs.writeFileSync(
66
+ path.normalize(path.join(apiDir, 'typings/index.js')),
67
+ 'test',
68
+ 'utf8',
69
+ );
70
+ };
71
+
72
+ const clear = () => {
73
+ fs.removeSync(apiDir);
74
+ };
75
+
76
+ fs.mkdirSync(path.normalize(path.join(apiDir, 'typings')), {
77
+ recursive: true,
78
+ });
79
+
80
+ watcher.listen(
81
+ [`${apiDir}/**/*`],
82
+ {
83
+ ignoreInitial: true,
84
+ ignored: /api\/typings\/.*/,
85
+ },
86
+ callback,
87
+ );
88
+
89
+ setTimeout(async () => {
90
+ expect(callback).toHaveBeenCalledTimes(0);
91
+ await watcher.close();
92
+ clear();
93
+ done();
94
+ }, 1000);
95
+
96
+ setTimeout(writeFiles);
97
+ });
98
+ });
package/src/constants.ts DELETED
@@ -1,26 +0,0 @@
1
- export const AGGRED_DIR = {
2
- mock: 'config/mock',
3
- server: 'server',
4
- api: 'api',
5
- shared: 'shared',
6
- lambda: 'lambda',
7
- };
8
-
9
- export enum ApiServerMode {
10
- func = 'function',
11
- frame = 'framework',
12
- }
13
-
14
- export const ERROR_DIGEST = {
15
- INIT: 'Server init error',
16
- ENOTF: 'Page could not be found',
17
- WARMUP: 'SSR warmup failed',
18
- EINTER: 'Internal server error',
19
- ERENDER: 'SSR render failed',
20
- EMICROINJ: 'Get micro-frontend info failed',
21
- };
22
-
23
- export const ERROR_PAGE_TEXT: Record<number, string> = {
24
- 404: 'This page could not be found.',
25
- 500: 'Internal Server Error.',
26
- };
@@ -1,37 +0,0 @@
1
- import path from 'path';
2
- import { resolveBabelConfig } from '@modern-js/server-utils';
3
- import { ModernServerOptions } from '../../type';
4
-
5
- const registerDirs = ['./api', './server', './config/mock', './shared'];
6
-
7
- export const enableRegister = (
8
- projectRoot: string,
9
- config: ModernServerOptions['config'],
10
- ) => {
11
- const TS_CONFIG_FILENAME = `tsconfig.json`;
12
- const tsconfigPath = path.resolve(projectRoot, TS_CONFIG_FILENAME);
13
-
14
- const babelConfig = resolveBabelConfig(projectRoot, config, {
15
- tsconfigPath,
16
- syntax: 'es6+',
17
- type: 'commonjs',
18
- });
19
-
20
- return require('@babel/register')({
21
- ...babelConfig,
22
- only: [
23
- function (filePath: string) {
24
- // TODO: wait params
25
- if (filePath.includes(`node_modules${path.sep}.modern-js`)) {
26
- return true;
27
- }
28
- return registerDirs.some(registerDir =>
29
- filePath.startsWith(path.join(projectRoot, registerDir)),
30
- );
31
- },
32
- ],
33
- extensions: ['.js', '.ts'],
34
- babelrc: false,
35
- root: projectRoot,
36
- });
37
- };
@@ -1,48 +0,0 @@
1
- import Webpack from 'webpack';
2
- import { DevServerOptions } from '../type';
3
-
4
- const { EntryPlugin } = Webpack;
5
- export default class DevServerPlugin {
6
- private readonly options: DevServerOptions;
7
-
8
- constructor(options: DevServerOptions) {
9
- this.options = options;
10
- }
11
-
12
- apply(compiler: Webpack.Compiler) {
13
- const { options } = this;
14
-
15
- const host = `&host=${options.client.host || 'localhost'}`;
16
- const path = `&path=${options.client.path}`;
17
- const port = `&port=${options.client.port}`;
18
-
19
- // Todo @songzhenwei
20
- const clientEntry = `${require.resolve(
21
- '@modern-js/hmr-client',
22
- )}?${host}${path}${port}`;
23
- const hotEntry = require.resolve('webpack/hot/dev-server');
24
- const additionalEntries = [clientEntry, hotEntry];
25
-
26
- // use a hook to add entries if available
27
- for (const additionalEntry of additionalEntries) {
28
- new EntryPlugin(compiler.context, additionalEntry, {
29
- name: undefined,
30
- }).apply(compiler);
31
- }
32
-
33
- // Todo remove, client must inject.
34
- const compilerOptions = compiler.options;
35
- compilerOptions.plugins = compilerOptions.plugins || [];
36
- if (
37
- hotEntry &&
38
- !compilerOptions.plugins.find(
39
- p => p.constructor === Webpack.HotModuleReplacementPlugin,
40
- )
41
- ) {
42
- // apply the HMR plugin, if it didn't exist before.
43
- const plugin = new Webpack.HotModuleReplacementPlugin();
44
-
45
- plugin.apply(compiler);
46
- }
47
- }
48
- }
@@ -1,3 +0,0 @@
1
- declare module 'selfsigned' {
2
- function generate(attributes: any, opt: any): { private: any; cert: any };
3
- }
@@ -1,12 +0,0 @@
1
- import * as devcert from 'devcert';
2
-
3
- export const genHttpsOptions = async (userOptions: any) => {
4
- const httpsOptions = userOptions === true ? {} : userOptions;
5
-
6
- if (!httpsOptions.key || !httpsOptions.cert) {
7
- const selfsign = await devcert.certificateFor(['localhost']);
8
- return selfsign;
9
- }
10
-
11
- return httpsOptions;
12
- };
@@ -1,29 +0,0 @@
1
- import { LAUNCH_EDITOR_ENDPOINT, logger } from '@modern-js/utils';
2
- import { ModernServerContext } from '../../libs/context';
3
- import { NextFunction } from '../../type';
4
-
5
- export const createLaunchEditorHandler =
6
- // eslint-disable-next-line consistent-return
7
- () => (ctx: ModernServerContext, next: NextFunction) => {
8
- if (ctx.url.startsWith(LAUNCH_EDITOR_ENDPOINT)) {
9
- const { filename, line = 1, column = 1 } = ctx.query;
10
- if (!filename) {
11
- ctx.status = 500;
12
- ctx.res.end(
13
- `launch-editor-middleware: required query param "filename" is missing.`,
14
- );
15
- } else {
16
- require('launch-editor')(
17
- `${filename as string}:${line as string}:${column as string}`,
18
- 'code',
19
- (file: string, errorMessage: string) => {
20
- logger.error(`Launch ${file} in editor failed.\n${errorMessage}`);
21
- },
22
- );
23
- ctx.status = 200;
24
- ctx.res.end();
25
- }
26
- } else {
27
- return next();
28
- }
29
- };
@@ -1,109 +0,0 @@
1
- import { IncomingMessage, ServerResponse } from 'http';
2
- import { compatRequire } from '@modern-js/utils';
3
- import { NextFunction } from '../../type';
4
- import { ModernServerContext } from '../../libs/context';
5
-
6
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
7
- const VALID_METHODS = ['get', 'post', 'put', 'delete', 'patch'];
8
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
9
- const BODY_PARSED_METHODS = ['post', 'put', 'delete', 'patch'];
10
-
11
- export type MockConfig = Record<
12
- string,
13
- | {
14
- data: any;
15
- }
16
- | ((
17
- req: IncomingMessage,
18
- res: ServerResponse,
19
- next: NextFunction,
20
- ) => Promise<void>)
21
- >;
22
-
23
- export type MockApi = {
24
- method: string;
25
- path: string;
26
- handler: ReturnType<
27
- typeof createFunctionDataHandler | typeof createStaticDataHandler
28
- >;
29
- };
30
-
31
- const createFunctionDataHandler =
32
- (
33
- method: string,
34
- handler: (
35
- req: IncomingMessage,
36
- res: ServerResponse,
37
- next: NextFunction,
38
- ) => void,
39
- ) =>
40
- async (context: ModernServerContext, next: NextFunction) => {
41
- const { req, res } = context;
42
- return handler(req, res, next);
43
- };
44
-
45
- const createStaticDataHandler =
46
- (method: string, handler: Record<string, any>) =>
47
- (context: ModernServerContext) => {
48
- const { res } = context;
49
- res.setHeader('Content-Type', 'application/json');
50
- res.end(JSON.stringify(handler));
51
- };
52
-
53
- const allowTypes = ['object', 'function'];
54
- const normalizeConfig = (mockConfig: MockConfig) =>
55
- Object.keys(mockConfig).reduce((acc, key) => {
56
- const handler = mockConfig[key];
57
- const type = typeof handler;
58
-
59
- if (!allowTypes.includes(type)) {
60
- throw new Error(
61
- `mock value of ${key} should be object or function, but got ${type}`,
62
- );
63
- }
64
-
65
- const meta = parseKey(key);
66
- if (type === 'object') {
67
- acc.push({
68
- ...meta,
69
- handler: createStaticDataHandler(meta.method, handler),
70
- });
71
- } else {
72
- acc.push({
73
- ...meta,
74
- handler: createFunctionDataHandler(meta.method, handler as any),
75
- });
76
- }
77
-
78
- return acc;
79
- }, [] as MockApi[]);
80
-
81
- const _blank = ' ';
82
- const parseKey = (key: string) => {
83
- // 'Method /pathname' | '/pathname'
84
- const splited = key.split(_blank).filter(Boolean);
85
- if (splited.length > 1) {
86
- const [method, pathname] = splited;
87
- return {
88
- method: method.toLowerCase(),
89
- path: pathname,
90
- };
91
- }
92
-
93
- // default return get method
94
- return {
95
- method: 'get',
96
- path: key,
97
- };
98
- };
99
-
100
- export default (filepath: string) => {
101
- const mockModule = compatRequire(filepath);
102
-
103
- if (!mockModule) {
104
- throw new Error(`Mock file ${filepath} parsed failed!`);
105
- }
106
-
107
- const data = normalizeConfig(mockModule);
108
- return data;
109
- };
@@ -1,63 +0,0 @@
1
- import path from 'path';
2
- import { fs } from '@modern-js/utils';
3
- import { match } from 'path-to-regexp';
4
- import { NextFunction } from '../../type';
5
- import { ModernServerContext } from '../../libs/context';
6
- import { AGGRED_DIR } from '../../constants';
7
- import getMockData, { MockApi } from './getMockData';
8
-
9
- const getMatched = (context: ModernServerContext, mockApiList: MockApi[]) => {
10
- const { path: targetPathname, method: targetMethod } = context;
11
-
12
- const matched = mockApiList.find(mockApi => {
13
- const { method, path: pathname } = mockApi;
14
- if (method.toLowerCase() === targetMethod.toLowerCase()) {
15
- return match(pathname, {
16
- encode: encodeURI,
17
- decode: decodeURIComponent,
18
- })(targetPathname);
19
- }
20
-
21
- return false;
22
- });
23
-
24
- return matched;
25
- };
26
-
27
- export const createMockHandler = ({ pwd }: { pwd: string }) => {
28
- const exts = ['.ts', '.js'];
29
- let filepath = '';
30
-
31
- for (const ext of exts) {
32
- const maybeMatch = path.join(pwd, `${AGGRED_DIR.mock}/index${ext}`);
33
- if (fs.existsSync(maybeMatch)) {
34
- filepath = maybeMatch;
35
- break;
36
- }
37
- }
38
-
39
- if (!filepath) {
40
- return null;
41
- }
42
-
43
- const apiList = getMockData(filepath);
44
- if (!apiList || apiList.length === 0) {
45
- return null;
46
- }
47
-
48
- return async (context: ModernServerContext, next: NextFunction) => {
49
- const { res } = context;
50
- const matched = getMatched(context, apiList);
51
-
52
- if (!matched) {
53
- return next();
54
- }
55
-
56
- if (matched) {
57
- res.setHeader('Access-Control-Allow-Origin', '*');
58
- return matched.handler(context, next);
59
- } else {
60
- return next();
61
- }
62
- };
63
- };
@@ -1,192 +0,0 @@
1
- import { Server } from 'http';
2
- import { Socket } from 'net';
3
- import ws from 'ws';
4
- import type { Stats } from 'webpack';
5
- import { logger } from '@modern-js/utils';
6
- import { DevServerOptions } from '../type';
7
- import { noop } from '../utils';
8
-
9
- interface ExtWebSocket extends ws {
10
- isAlive: boolean;
11
- }
12
-
13
- export default class SocketServer {
14
- private wsServer!: ws.Server;
15
-
16
- private readonly sockets: ws[] = [];
17
-
18
- private readonly options: DevServerOptions;
19
-
20
- private app?: Server;
21
-
22
- private stats?: Stats;
23
-
24
- constructor(options: DevServerOptions) {
25
- this.options = options;
26
- }
27
-
28
- // create socket, install socket handler, bind socket event
29
- public prepare(app: Server) {
30
- this.app = app;
31
-
32
- this.wsServer = new ws.Server({
33
- noServer: true,
34
- path: this.options.client.path,
35
- });
36
-
37
- // listen upgrade event to handle socket
38
- this.app.on('upgrade', (req, sock, head) => {
39
- if (!this.wsServer.shouldHandle(req)) {
40
- return;
41
- }
42
-
43
- this.wsServer.handleUpgrade(req, sock as Socket, head, connection => {
44
- this.wsServer.emit('connection', connection, req);
45
- });
46
- });
47
-
48
- this.wsServer.on('error', (err: Error) => {
49
- // only dev server, use default logger
50
- logger.error(err);
51
- });
52
-
53
- setInterval(() => {
54
- this.wsServer.clients.forEach(socket => {
55
- const extWs = socket as ExtWebSocket;
56
- if (!extWs.isAlive) {
57
- extWs.terminate();
58
- } else {
59
- extWs.isAlive = false;
60
- extWs.ping(noop);
61
- }
62
- });
63
- }, 30000);
64
-
65
- this.wsServer.on('connection', socket => {
66
- const connection = socket as ExtWebSocket;
67
-
68
- connection.isAlive = true;
69
- connection.on('pong', () => {
70
- connection.isAlive = true;
71
- });
72
-
73
- if (!connection) {
74
- return;
75
- }
76
-
77
- this.sockets.push(connection);
78
-
79
- connection.on('close', () => {
80
- const idx = this.sockets.indexOf(connection);
81
-
82
- if (idx >= 0) {
83
- this.sockets.splice(idx, 1);
84
- }
85
- });
86
-
87
- if (this.options.client.logging) {
88
- this.sockWrite('logging', this.options.client.logging);
89
- }
90
-
91
- if (this.options.hot || this.options.hot === 'only') {
92
- this.sockWrite('hot');
93
- }
94
-
95
- if (this.options.liveReload) {
96
- this.sockWrite('liveReload');
97
- }
98
-
99
- if (this.options.client.progress) {
100
- this.sockWrite('progress', this.options.client.progress);
101
- }
102
-
103
- if (this.options.client.overlay) {
104
- this.sockWrite('overlay', this.options.client.overlay);
105
- }
106
-
107
- // send first stats to active client sock if stats exist
108
- if (this.stats) {
109
- this.sendStats(true);
110
- }
111
- });
112
- }
113
-
114
- public updateStats(stats: Stats) {
115
- this.stats = stats;
116
- this.sendStats();
117
- }
118
-
119
- // write message to each socket
120
- public sockWrite(
121
- type: string,
122
- data?: Record<string, any> | string | boolean,
123
- ) {
124
- this.sockets.forEach(socket => {
125
- this.send(socket, JSON.stringify({ type, data }));
126
- });
127
- }
128
-
129
- public close(connection: ws) {
130
- connection.close();
131
- }
132
-
133
- // get standard stats
134
- private getStats() {
135
- const curStats = this.stats;
136
-
137
- if (!curStats) {
138
- return null;
139
- }
140
-
141
- const defaultStats: Record<string, boolean> = {
142
- all: false,
143
- hash: true,
144
- assets: true,
145
- warnings: true,
146
- errors: true,
147
- errorDetails: false,
148
- };
149
-
150
- return curStats.toJson(defaultStats);
151
- }
152
-
153
- // determine what message should send by stats
154
- private sendStats(force = false) {
155
- const stats = this.getStats();
156
-
157
- // this should never happend
158
- if (!stats) {
159
- return null;
160
- }
161
-
162
- const shouldEmit =
163
- !force &&
164
- stats &&
165
- (!stats.errors || stats.errors.length === 0) &&
166
- stats.assets &&
167
- stats.assets.every((asset: any) => !asset.emitted);
168
-
169
- if (shouldEmit) {
170
- return this.sockWrite('still-ok');
171
- }
172
-
173
- this.sockWrite('hash', stats.hash);
174
-
175
- if (stats.errors && stats.errors.length > 0) {
176
- return this.sockWrite('errors', stats.errors);
177
- } else if (stats.warnings && stats.warnings.length > 0) {
178
- return this.sockWrite('warnings', stats.warnings);
179
- } else {
180
- return this.sockWrite('ok');
181
- }
182
- }
183
-
184
- // send message to connecting socket
185
- private send(connection: ws, message: string) {
186
- if (connection.readyState !== 1) {
187
- return;
188
- }
189
-
190
- connection.send(message);
191
- }
192
- }