@modern-js/server 1.4.4 → 1.4.7-canary.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.
@@ -0,0 +1 @@
1
+ export default null;
@@ -0,0 +1,3 @@
1
+ export default {
2
+ 'GET /api/getInfo': 'foo',
3
+ };
@@ -0,0 +1,11 @@
1
+ export default {
2
+ 'GET /api/getInfo': { data: [1, 2, 3, 4] },
3
+
4
+ '/api/getExample': { id: 1 },
5
+
6
+ 'GET /api/addInfo': (req: any, res: any) => {
7
+ setTimeout(() => {
8
+ res.end('delay 2000ms');
9
+ }, 2000);
10
+ },
11
+ };
@@ -0,0 +1,3 @@
1
+ export const noop = () => {
2
+ // empty
3
+ };
@@ -0,0 +1,17 @@
1
+ import { genHttpsOptions } from '../src/dev-tools/https';
2
+
3
+ describe('test dev tools', () => {
4
+ test('should get http cert correctly', async () => {
5
+ // Todo Interaction in CI
6
+ // const options = await genHttpsOptions(true);
7
+ // expect(options.key).toBeDefined();
8
+ // expect(options.cert).toBeDefined();
9
+
10
+ const useOpt = await genHttpsOptions({
11
+ key: 'foo',
12
+ cert: 'baz',
13
+ });
14
+ expect(useOpt.key).toBe('foo');
15
+ expect(useOpt.cert).toBe('baz');
16
+ });
17
+ });
@@ -0,0 +1,58 @@
1
+ import { LAUNCH_EDITOR_ENDPOINT } from '@modern-js/utils';
2
+ import { createLaunchEditorHandler } from '../src/dev-tools/launch-editor';
3
+ import { noop } from './helper';
4
+
5
+ describe('should createLaunchEditorHandler work correctly', () => {
6
+ const middleware = createLaunchEditorHandler();
7
+
8
+ it('should return 200 if filename exist', () => {
9
+ let response: any;
10
+ const context: any = {
11
+ url: LAUNCH_EDITOR_ENDPOINT,
12
+ query: {
13
+ filename: 'test.ts',
14
+ },
15
+ res: {
16
+ end: (data: any) => {
17
+ response = data;
18
+ },
19
+ },
20
+ };
21
+ middleware(context, noop);
22
+ expect(context.status).toBe(200);
23
+ expect(response).toBeUndefined();
24
+ });
25
+
26
+ it('should return 500 if filename not exist', () => {
27
+ let response: any;
28
+ const context: any = {
29
+ url: LAUNCH_EDITOR_ENDPOINT,
30
+ query: {
31
+ filename: '',
32
+ },
33
+ res: {
34
+ end: (data: any) => {
35
+ response = data;
36
+ },
37
+ },
38
+ };
39
+ middleware(context, noop);
40
+ expect(context.status).toBe(500);
41
+ expect(typeof response === 'string').toBeTruthy();
42
+ });
43
+
44
+ it('should invoke next if not launch editor url', () => {
45
+ let response: any;
46
+ const context: any = {
47
+ url: '',
48
+ res: {
49
+ end: (data: any) => {
50
+ response = data;
51
+ },
52
+ },
53
+ };
54
+ middleware(context, noop);
55
+ expect(context.status).toBeUndefined();
56
+ expect(response).toBeUndefined();
57
+ });
58
+ });
@@ -0,0 +1,150 @@
1
+ import path from 'path';
2
+ import { createMockHandler } from '../src/dev-tools/mock';
3
+ import getMockData, { getMatched } from '../src/dev-tools/mock/getMockData';
4
+ import { noop } from './helper';
5
+
6
+ describe('test dev tools', () => {
7
+ describe('should mock middleware work correctly', () => {
8
+ const pwd = path.join(__dirname, './fixtures/mock');
9
+
10
+ it('should return null if no config mock dir', () => {
11
+ expect(createMockHandler({ pwd: path.join(pwd, 'empty') })).toBeNull();
12
+ });
13
+
14
+ it('should return null if no api dir', () => {
15
+ expect(createMockHandler({ pwd: path.join(pwd, 'zero') })).toBeNull();
16
+ });
17
+
18
+ it('should return middleware if mock api exist', async () => {
19
+ const middleware = createMockHandler({ pwd: path.join(pwd, 'exist') });
20
+
21
+ expect(middleware).not.toBeNull();
22
+
23
+ let response: any;
24
+ const context: any = {
25
+ path: '/api/getInfo',
26
+ method: 'get',
27
+ res: {
28
+ setHeader: noop,
29
+ end: (data: any) => {
30
+ response = JSON.parse(data);
31
+ },
32
+ },
33
+ };
34
+ await middleware?.(context, noop);
35
+ expect(response).toEqual({
36
+ data: [1, 2, 3, 4],
37
+ });
38
+ });
39
+
40
+ it('should not return middleware if mock api exist', async () => {
41
+ const middleware = createMockHandler({ pwd: path.join(pwd, 'exist') });
42
+
43
+ expect(middleware).not.toBeNull();
44
+
45
+ let response: any;
46
+ const context: any = {
47
+ path: '/api/getInfp',
48
+ method: 'get',
49
+ res: {
50
+ setHeader: noop,
51
+ end: (data: any) => {
52
+ response = JSON.parse(data);
53
+ },
54
+ },
55
+ };
56
+ await middleware?.(context, noop);
57
+ expect(response).toBeUndefined();
58
+ });
59
+
60
+ it('should get api list correctly', resolve => {
61
+ const apiList = getMockData(path.join(pwd, 'exist/config/mock/index.ts'));
62
+ expect(apiList.length).toBe(3);
63
+
64
+ const pathList = apiList.map(api => api.path);
65
+ expect(pathList).toEqual([
66
+ '/api/getInfo',
67
+ '/api/getExample',
68
+ '/api/addInfo',
69
+ ]);
70
+
71
+ let response: any;
72
+ const context: any = {
73
+ res: {
74
+ setHeader: noop,
75
+ end: (data: any) => {
76
+ try {
77
+ response = JSON.parse(data);
78
+ } catch (e) {
79
+ response = data;
80
+ }
81
+ },
82
+ },
83
+ };
84
+
85
+ apiList[0].handler(context, noop as any);
86
+ expect(response).toEqual({
87
+ data: [1, 2, 3, 4],
88
+ });
89
+ apiList[1].handler(context, noop as any);
90
+ expect(response).toEqual({ id: 1 });
91
+
92
+ apiList[2].handler(context, noop as any);
93
+ setTimeout(() => {
94
+ expect(response).toBe('delay 2000ms');
95
+ resolve();
96
+ }, 3000);
97
+ });
98
+
99
+ it('should match api correctly', () => {
100
+ const apiList = [
101
+ {
102
+ method: 'get',
103
+ path: '/api/getInfo',
104
+ handler: noop,
105
+ },
106
+ {
107
+ method: 'get',
108
+ path: '/api/getExample',
109
+ handler: noop,
110
+ },
111
+ {
112
+ method: 'get',
113
+ path: '/api/addInfo',
114
+ handler: noop,
115
+ },
116
+ ];
117
+ const matched = getMatched(
118
+ { path: '/api/getInfo', method: 'get' } as any,
119
+ apiList,
120
+ );
121
+ expect(matched).toBe(apiList[0]);
122
+
123
+ const missMethod = getMatched(
124
+ { path: '/api/getModern', method: 'post' } as any,
125
+ apiList,
126
+ );
127
+ expect(missMethod).toBeUndefined();
128
+ });
129
+
130
+ it('should throw error if get mock file fail', resolve => {
131
+ try {
132
+ createMockHandler({ pwd: path.join(pwd, 'module-error') });
133
+ } catch (e: any) {
134
+ expect(e.message).toMatch('parsed failed!');
135
+ resolve();
136
+ }
137
+ });
138
+
139
+ it('should throw error if get mock api has wrong type', resolve => {
140
+ try {
141
+ createMockHandler({ pwd: path.join(pwd, 'type-error') });
142
+ } catch (e: any) {
143
+ expect(e.message).toMatch(
144
+ 'should be object or function, but got string',
145
+ );
146
+ resolve();
147
+ }
148
+ });
149
+ });
150
+ });
@@ -1,10 +1,16 @@
1
1
  import path from 'path';
2
2
  import { defaultsConfig, NormalizedConfig } from '@modern-js/core';
3
3
  import { ModernServerContext, NextFunction } from '@modern-js/types';
4
+ import { webpack } from 'webpack';
5
+ import { AGGRED_DIR, RUN_MODE } from '@modern-js/prod-server';
4
6
  import createServer, { Server } from '../src';
5
7
  import Watcher from '../src/dev-tools/watcher';
8
+ import { ModernDevServer } from '../src/server/dev-server';
6
9
 
7
- describe('test server', () => {
10
+ jest.useFakeTimers();
11
+ (global as any).setImmediate = () => false;
12
+ const appDirectory = path.join(__dirname, './fixtures/pure');
13
+ describe('test dev server', () => {
8
14
  test('should throw error when ', resolve => {
9
15
  try {
10
16
  createServer(null as any);
@@ -18,17 +24,24 @@ describe('test server', () => {
18
24
 
19
25
  test('shoule get modern server instance', async () => {
20
26
  const server = await createServer({
21
- config: defaultsConfig as NormalizedConfig,
22
- pwd: path.join(__dirname, './fixtures/pure'),
27
+ config: {
28
+ ...defaultsConfig,
29
+ tools: {
30
+ devServer: {
31
+ proxy: {
32
+ '/simple': `http://localhost:8080`,
33
+ },
34
+ },
35
+ },
36
+ } as any,
37
+ pwd: appDirectory,
23
38
  dev: true,
24
39
  });
25
40
  expect(server instanceof Server).toBe(true);
26
41
  await server.close();
27
42
  });
28
43
 
29
- describe('shoule get production modern server instance', () => {
30
- const appDirectory = path.join(__dirname, './fixtures/pure');
31
-
44
+ describe('shoule get dev modern server instance', () => {
32
45
  test('should init server correctly', async () => {
33
46
  const server = await createServer({
34
47
  config: defaultsConfig as NormalizedConfig,
@@ -105,23 +118,121 @@ describe('test server', () => {
105
118
  expect(typeof handler === 'function').toBeTruthy();
106
119
  await server.close();
107
120
  });
108
- });
109
- });
110
121
 
111
- describe('dev server', () => {
112
- const pwd = path.join(__dirname, './fixtures/pure');
113
- let devServer: Server;
122
+ test('should get request handler correctly', async () => {
123
+ const server = await createServer({
124
+ config: defaultsConfig as NormalizedConfig,
125
+ pwd: appDirectory,
126
+ dev: true,
127
+ });
114
128
 
115
- test('watch', async () => {
116
- devServer = await createServer({
117
- config: defaultsConfig as NormalizedConfig,
118
- pwd,
119
- dev: true,
129
+ const modernServer: any = (server as any).server;
130
+ const handler = modernServer.getRequestHandler();
131
+ expect(typeof handler === 'function').toBeTruthy();
132
+ await server.close();
133
+ });
134
+
135
+ test('should invoke onrepack correctly', async () => {
136
+ const server = await createServer({
137
+ config: defaultsConfig as NormalizedConfig,
138
+ pwd: appDirectory,
139
+ dev: true,
140
+ });
141
+
142
+ const modernServer: ModernDevServer = (server as any).server;
143
+ modernServer.onRepack({ routes: [] });
144
+
145
+ expect((modernServer as any).router.matchers.length).toBe(0);
146
+ server.close();
147
+ });
148
+
149
+ test('should invoke onserver change correctly', async () => {
150
+ const server = await createServer({
151
+ config: defaultsConfig as NormalizedConfig,
152
+ pwd: appDirectory,
153
+ dev: true,
154
+ });
155
+
156
+ const modernServer = (server as any).server;
157
+ modernServer.onServerChange({
158
+ filepath: path.join(appDirectory, AGGRED_DIR.mock),
159
+ });
160
+ expect(modernServer.mockHandler).not.toBeNull();
161
+
162
+ modernServer.onServerChange({
163
+ filepath: path.join(appDirectory, 'index.js'),
164
+ });
165
+ expect(modernServer.mockHandler).not.toBeNull();
166
+ server.close();
167
+ });
168
+
169
+ test('should compiler work correctly', async () => {
170
+ const compiler = webpack({});
171
+ const server = await createServer({
172
+ config: defaultsConfig as NormalizedConfig,
173
+ pwd: appDirectory,
174
+ dev: true,
175
+ compiler,
176
+ });
177
+
178
+ expect(server).toBeDefined();
179
+ server.close();
180
+ });
181
+
182
+ test('should multi compiler work correctly', async () => {
183
+ const compiler = webpack([{}, { name: 'client' }]);
184
+ const server = await createServer({
185
+ config: defaultsConfig as NormalizedConfig,
186
+ pwd: appDirectory,
187
+ dev: true,
188
+ compiler,
189
+ });
190
+
191
+ expect(server).toBeDefined();
192
+ server.close();
193
+ });
194
+
195
+ test('should watcher work well', async () => {
196
+ const devServer = await createServer({
197
+ config: defaultsConfig as NormalizedConfig,
198
+ pwd: appDirectory,
199
+ dev: true,
200
+ });
201
+
202
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
203
+ // @ts-expect-error
204
+ expect(devServer.server.watcher).toBeInstanceOf(Watcher);
205
+ await devServer.close();
120
206
  });
207
+ });
121
208
 
122
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
123
- // @ts-expect-error
124
- expect(devServer.server.watcher).toBeInstanceOf(Watcher);
125
- await devServer.close();
209
+ describe('should split server work correctly', () => {
210
+ test('should init api server correctly', async () => {
211
+ const server = await createServer({
212
+ config: defaultsConfig as NormalizedConfig,
213
+ pwd: appDirectory,
214
+ dev: true,
215
+ apiOnly: true,
216
+ runMode: RUN_MODE.FULL,
217
+ });
218
+ const modernServer = (server as any).server;
219
+ modernServer.emitRouteHook('reset', {});
220
+ expect(modernServer.prepareWebHandler()).toBe(null);
221
+ await server.close();
222
+ });
223
+
224
+ test('should init ssr server correctly', async () => {
225
+ const server = await createServer({
226
+ config: defaultsConfig as NormalizedConfig,
227
+ pwd: appDirectory,
228
+ dev: true,
229
+ ssrOnly: true,
230
+ runMode: RUN_MODE.FULL,
231
+ });
232
+ const modernServer = (server as any).server;
233
+ modernServer.emitRouteHook('reset', {});
234
+ expect(modernServer.prepareAPIHandler()).toBe(null);
235
+ await server.close();
236
+ });
126
237
  });
127
238
  });
@@ -1,40 +1,70 @@
1
- import * as path from 'path';
1
+ import path from 'path';
2
2
  import { fs } from '@modern-js/utils';
3
- import Watcher from '../src/dev-tools/watcher';
3
+ import Watcher, { getWatchedFiles } from '../src/dev-tools/watcher';
4
+ import { StatsCache } from '../src/dev-tools/watcher/stats-cache';
4
5
 
5
6
  jest.useRealTimers();
6
7
 
7
8
  describe('watcher', () => {
8
- let watcher: Watcher;
9
9
  jest.setTimeout(25000);
10
+ const pwd = path.join(__dirname, './fixtures/watch');
11
+ const serverDir = path.normalize(path.join(pwd, './tmp-server'));
12
+
10
13
  beforeAll(() => {
11
- watcher = new Watcher();
14
+ if (fs.existsSync(serverDir)) {
15
+ fs.removeSync(serverDir);
16
+ }
17
+ fs.mkdirSync(serverDir);
12
18
  });
13
19
 
14
- test('should emit change', done => {
15
- const pwd = path.join(__dirname, './fixtures/pure');
16
- const serverDir = path.normalize(path.join(pwd, './tmp-server'));
20
+ afterAll(() => {
21
+ fs.removeSync(serverDir);
22
+ });
17
23
 
24
+ const writeFiles = (content: string, filepath: string) => {
25
+ fs.writeFileSync(path.normalize(filepath), content, 'utf8');
26
+ };
27
+
28
+ test('should emit add', done => {
29
+ const watcher = new Watcher();
18
30
  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
31
 
30
- const clear = () => {
31
- fs.removeSync(serverDir);
32
- };
32
+ const watchDir = path.join(serverDir, 'add');
33
+ fs.mkdirSync(watchDir);
33
34
 
34
- fs.mkdirSync(serverDir);
35
+ watcher.listen(
36
+ [`${watchDir}/**/*`],
37
+ {
38
+ ignoreInitial: true,
39
+ ignored: /api\/typings\/.*/,
40
+ },
41
+ async () => {
42
+ try {
43
+ callback();
44
+ expect(callback).toHaveBeenCalledTimes(1);
45
+ await watcher.close();
46
+ } catch (e) {
47
+ console.error(e);
48
+ }
49
+ done();
50
+ },
51
+ );
52
+
53
+ setTimeout(() => writeFiles('test', path.join(watchDir, 'index.js')), 100);
54
+ });
55
+
56
+ test('should emit unlink', done => {
57
+ const watcher = new Watcher();
58
+
59
+ const callback = jest.fn();
60
+ const watchDir = path.join(serverDir, 'unlink');
61
+ fs.mkdirSync(watchDir);
62
+
63
+ const filepath = path.join(watchDir, 'index.js');
64
+ writeFiles('unlink', filepath);
35
65
 
36
66
  watcher.listen(
37
- [`${serverDir}/**/*`],
67
+ [`${watchDir}/**/*`],
38
68
  {
39
69
  ignoreInitial: true,
40
70
  ignored: /api\/typings\/.*/,
@@ -43,16 +73,44 @@ describe('watcher', () => {
43
73
  callback();
44
74
  expect(callback).toHaveBeenCalledTimes(1);
45
75
  await watcher.close();
46
- clear();
47
76
  done();
48
77
  },
49
78
  );
50
79
 
51
- setTimeout(writeFiles, 100);
80
+ setTimeout(() => {
81
+ fs.removeSync(filepath);
82
+ }, 100);
83
+ });
84
+
85
+ test('should emit change', done => {
86
+ const watcher = new Watcher();
87
+
88
+ const callback = jest.fn();
89
+ const watchDir = path.join(serverDir, 'change');
90
+ fs.mkdirSync(watchDir);
91
+
92
+ const filepath = path.join(watchDir, 'index.js');
93
+ writeFiles('start', filepath);
94
+
95
+ watcher.listen(
96
+ [`${watchDir}/**/*`],
97
+ {
98
+ ignoreInitial: true,
99
+ ignored: /api\/typings\/.*/,
100
+ },
101
+ async () => {
102
+ callback();
103
+ expect(callback).toHaveBeenCalledTimes(1);
104
+ await watcher.close();
105
+ done();
106
+ },
107
+ );
108
+
109
+ setTimeout(() => writeFiles('end', filepath), 100);
52
110
  });
53
111
 
54
112
  test('should not emit change when typings file changed', done => {
55
- const pwd = path.join(__dirname, './fixtures/pure');
113
+ const watcher = new Watcher();
56
114
  const apiDir = path.normalize(path.join(pwd, './api'));
57
115
 
58
116
  const callback = jest.fn();
@@ -61,14 +119,6 @@ describe('watcher', () => {
61
119
  fs.removeSync(apiDir);
62
120
  }
63
121
 
64
- const writeFiles = () => {
65
- fs.writeFileSync(
66
- path.normalize(path.join(apiDir, 'typings/index.js')),
67
- 'test',
68
- 'utf8',
69
- );
70
- };
71
-
72
122
  const clear = () => {
73
123
  fs.removeSync(apiDir);
74
124
  };
@@ -92,7 +142,87 @@ describe('watcher', () => {
92
142
  clear();
93
143
  done();
94
144
  }, 1000);
145
+ });
146
+ });
147
+
148
+ describe('test watcher', () => {
149
+ let watcher: any;
150
+ const baseDir = path.join(__dirname, 'fixtures');
151
+ const watchDir = path.join(baseDir, 'watch/**');
152
+ const filepath = path.join(baseDir, 'watch', 'index.ts');
153
+ const filepatha = path.join(baseDir, 'watch', 'a.ts');
154
+ const txt = path.join(baseDir, 'watch', 'stats.txt');
155
+
156
+ afterEach(() => {
157
+ if (watcher) {
158
+ watcher.close();
159
+ }
160
+ fs.writeFileSync(txt, '1');
161
+ });
162
+
163
+ it('should create watcher instance correctly', resolve => {
164
+ watcher = new Watcher();
165
+ expect(watcher.dependencyTree).toBeNull();
166
+ watcher.createDepTree();
167
+ expect(watcher.dependencyTree).not.toBeNull();
168
+
169
+ expect(watcher.watcher).toBeUndefined();
170
+ watcher.listen([watchDir], {}, () => {
171
+ // empty
172
+ });
173
+
174
+ expect(watcher.watcher).toBeDefined();
175
+ require(filepath);
176
+ expect(watcher.dependencyTree.getNode(filepath)).toBeUndefined();
177
+ watcher.updateDepTree();
178
+ expect(watcher.dependencyTree.getNode(filepath)).toBeDefined();
179
+ watcher.cleanDepCache(filepath);
180
+ expect(watcher.dependencyTree.getNode(filepath)).toBeDefined();
181
+
182
+ jest.resetModules();
183
+ watcher.updateDepTree();
184
+ expect(watcher.dependencyTree.getNode(filepath)).toBeUndefined();
185
+
186
+ setTimeout(() => {
187
+ const fl = getWatchedFiles(watcher.watcher);
188
+ expect(fl.includes(filepatha)).toBeTruthy();
189
+ expect(fl.includes(filepath)).toBeTruthy();
190
+ expect(fl.includes(txt)).toBeTruthy();
191
+ resolve();
192
+ }, 1000);
193
+ });
194
+
195
+ it('should stats cache instance work correctly', () => {
196
+ const statsCache = new StatsCache();
197
+
198
+ // should not exist false before add
199
+ expect(statsCache.has(txt)).toBeFalsy();
200
+
201
+ // should exist true after add
202
+ statsCache.add([txt]);
203
+ expect(statsCache.has(txt)).toBeTruthy();
204
+
205
+ // should diff correctly
206
+ fs.writeFileSync(txt, 'foo');
207
+ expect(statsCache.isDiff(txt)).toBeTruthy();
208
+
209
+ // should not diff if not refresh
210
+ fs.writeFileSync(txt, '1');
211
+ expect(statsCache.isDiff(txt)).toBeFalsy();
212
+
213
+ // should diff after refresh
214
+ fs.writeFileSync(txt, 'foo');
215
+ statsCache.refresh(txt);
216
+ fs.writeFileSync(txt, '1');
217
+ expect(statsCache.isDiff(txt)).toBeTruthy();
218
+
219
+ // should diff when content change
220
+ statsCache.refresh(txt);
221
+ fs.writeFileSync(txt, '2');
222
+ expect(statsCache.isDiff(txt)).toBeTruthy();
95
223
 
96
- setTimeout(writeFiles);
224
+ // should not exist after del
225
+ statsCache.del(txt);
226
+ expect(statsCache.has(txt)).toBeFalsy();
97
227
  });
98
228
  });