@modern-js/server 1.2.0 → 1.2.1-rc.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.
- package/CHANGELOG.md +14 -0
- package/dist/js/modern/index.js +1 -7
- package/dist/js/modern/libs/context/context.js +3 -6
- package/dist/js/modern/libs/context/index.js +1 -7
- package/dist/js/modern/libs/hook-api/template.js +4 -4
- package/dist/js/modern/libs/proxy.js +1 -1
- package/dist/js/modern/libs/render/cache/__tests__/cache.test.js +1 -2
- package/dist/js/modern/libs/render/index.js +6 -2
- package/dist/js/modern/libs/render/reader.js +8 -11
- package/dist/js/modern/libs/render/ssr.js +2 -1
- package/dist/js/modern/libs/route/index.js +2 -1
- package/dist/js/modern/server/index.js +3 -1
- package/dist/js/modern/server/modern-server.js +21 -11
- package/dist/js/modern/utils.js +1 -9
- package/dist/js/node/index.js +1 -49
- package/dist/js/node/libs/context/context.js +3 -6
- package/dist/js/node/libs/context/index.js +1 -7
- package/dist/js/node/libs/hook-api/template.js +4 -4
- package/dist/js/node/libs/proxy.js +1 -1
- package/dist/js/node/libs/render/cache/__tests__/cache.test.js +1 -2
- package/dist/js/node/libs/render/index.js +6 -2
- package/dist/js/node/libs/render/reader.js +8 -11
- package/dist/js/node/libs/render/ssr.js +2 -1
- package/dist/js/node/libs/route/index.js +6 -0
- package/dist/js/node/server/index.js +3 -1
- package/dist/js/node/server/modern-server.js +21 -11
- package/dist/js/node/utils.js +2 -12
- package/dist/types/index.d.ts +1 -3
- package/dist/types/libs/context/context.d.ts +7 -7
- package/dist/types/libs/context/index.d.ts +1 -8
- package/dist/types/libs/hook-api/template.d.ts +4 -4
- package/dist/types/libs/render/index.d.ts +10 -1
- package/dist/types/libs/render/ssr.d.ts +2 -1
- package/dist/types/libs/render/type.d.ts +2 -21
- package/dist/types/libs/route/index.d.ts +2 -1
- package/dist/types/server/modern-server.d.ts +1 -1
- package/dist/types/type.d.ts +6 -0
- package/dist/types/utils.d.ts +1 -2
- package/package.json +14 -12
- package/src/index.ts +2 -8
- package/src/libs/context/context.ts +8 -7
- package/src/libs/context/index.ts +2 -6
- package/src/libs/hook-api/template.ts +4 -4
- package/src/libs/proxy.ts +1 -1
- package/src/libs/render/cache/__tests__/cache.test.ts +2 -2
- package/src/libs/render/index.ts +21 -11
- package/src/libs/render/reader.ts +8 -8
- package/src/libs/render/ssr.ts +4 -0
- package/src/libs/render/type.ts +2 -17
- package/src/libs/route/index.ts +2 -1
- package/src/server/index.ts +1 -1
- package/src/server/modern-server.ts +20 -12
- package/src/type.ts +7 -0
- package/src/utils.ts +0 -14
- package/tests/.eslintrc.js +6 -0
- package/tests/context.test.ts +41 -0
- package/tests/fixtures/hosting-files/static/index.js +1 -0
- package/tests/fixtures/pure/modern.config.js +5 -0
- package/tests/fixtures/pure/package.json +21 -0
- package/tests/fixtures/pure/src/App.css +119 -0
- package/tests/fixtures/pure/src/App.tsx +43 -0
- package/tests/fixtures/pure/tsconfig.json +13 -0
- package/tests/fixtures/route-spec/index.json +29 -0
- package/tests/helper.ts +8 -0
- package/tests/hook.test.ts +44 -0
- package/tests/middleware.test.ts +178 -0
- package/tests/route.test.ts +54 -0
- package/tests/server.test.ts +89 -0
- package/tests/tsconfig.json +14 -0
- package/tests/utils.test.ts +40 -0
package/src/server/index.ts
CHANGED
|
@@ -87,12 +87,12 @@ export class ModernServer {
|
|
|
87
87
|
|
|
88
88
|
private frameAPIHandler: Adapter | null = null;
|
|
89
89
|
|
|
90
|
+
private proxyHandler: ReturnType<typeof createProxyHandler> = null;
|
|
91
|
+
|
|
90
92
|
private _handler!: (context: ModernServerContext, next: NextFunction) => void;
|
|
91
93
|
|
|
92
94
|
private readonly staticGenerate: boolean = false;
|
|
93
95
|
|
|
94
|
-
private proxyHandler: ReturnType<typeof createProxyHandler> = null;
|
|
95
|
-
|
|
96
96
|
constructor({
|
|
97
97
|
pwd,
|
|
98
98
|
config,
|
|
@@ -107,7 +107,7 @@ export class ModernServer {
|
|
|
107
107
|
this.isDev = Boolean(dev);
|
|
108
108
|
|
|
109
109
|
this.pwd = pwd;
|
|
110
|
-
this.distDir = path.join(pwd, config.output
|
|
110
|
+
this.distDir = path.join(pwd, config.output?.path || 'dist');
|
|
111
111
|
this.workDir = this.isDev ? pwd : this.distDir;
|
|
112
112
|
this.conf = config;
|
|
113
113
|
this.logger = logger!;
|
|
@@ -162,7 +162,7 @@ export class ModernServer {
|
|
|
162
162
|
|
|
163
163
|
await this.prepareFrameHandler();
|
|
164
164
|
|
|
165
|
-
const { favicon, faviconByEntries } = this.conf.output;
|
|
165
|
+
const { favicon, faviconByEntries } = this.conf.output || {};
|
|
166
166
|
const favicons = this.prepareFavicons(favicon, faviconByEntries);
|
|
167
167
|
// Only work when without setting `assetPrefix`.
|
|
168
168
|
// Setting `assetPrefix` means these resources should be uploaded to CDN.
|
|
@@ -229,6 +229,7 @@ export class ModernServer {
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
// add promisify request handler to server
|
|
232
|
+
// handler should do not do more things after invoke next
|
|
232
233
|
protected addHandler(handler: ModernServerHandler) {
|
|
233
234
|
if ((handler as any)[Symbol.toStringTag] === 'AsyncFunction') {
|
|
234
235
|
this.handlers.push(handler as ModernServerAsyncHandler);
|
|
@@ -356,7 +357,11 @@ export class ModernServer {
|
|
|
356
357
|
}
|
|
357
358
|
|
|
358
359
|
protected async handleWeb(context: ModernServerContext, route: ModernRoute) {
|
|
359
|
-
return this.routeRenderHandler(
|
|
360
|
+
return this.routeRenderHandler({
|
|
361
|
+
ctx: context,
|
|
362
|
+
route,
|
|
363
|
+
runner: this.runner,
|
|
364
|
+
});
|
|
360
365
|
}
|
|
361
366
|
|
|
362
367
|
protected verifyMatch(_c: ModernServerContext, _m: RouteMatcher) {
|
|
@@ -372,7 +377,7 @@ export class ModernServer {
|
|
|
372
377
|
await this.emitRouteHook('beforeMatch', { context });
|
|
373
378
|
|
|
374
379
|
// match routes in the route spec
|
|
375
|
-
const matched = this.router.match(context.
|
|
380
|
+
const matched = this.router.match(context.path);
|
|
376
381
|
if (!matched) {
|
|
377
382
|
this.render404(context);
|
|
378
383
|
return;
|
|
@@ -498,7 +503,7 @@ export class ModernServer {
|
|
|
498
503
|
try {
|
|
499
504
|
// Todo Safety xss
|
|
500
505
|
const injection = JSON.stringify({ ...manifest, modules });
|
|
501
|
-
templateAPI.
|
|
506
|
+
templateAPI.appendHead(
|
|
502
507
|
`<script>window.modern_manifest=${injection}</script>`,
|
|
503
508
|
);
|
|
504
509
|
} catch (e) {
|
|
@@ -546,10 +551,9 @@ export class ModernServer {
|
|
|
546
551
|
},
|
|
547
552
|
) {
|
|
548
553
|
res.statusCode = 200;
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
});
|
|
554
|
+
req.logger = req.logger || this.logger;
|
|
555
|
+
req.metrics = req.metrics || this.metrics;
|
|
556
|
+
const context: ModernServerContext = createContext(req, res);
|
|
553
557
|
|
|
554
558
|
try {
|
|
555
559
|
this._handler(context, next);
|
|
@@ -580,7 +584,11 @@ export class ModernServer {
|
|
|
580
584
|
// check entryName, aviod matched '/' route
|
|
581
585
|
if (entryName === status.toString() || entryName === '_error') {
|
|
582
586
|
try {
|
|
583
|
-
const file = await this.routeRenderHandler(
|
|
587
|
+
const file = await this.routeRenderHandler({
|
|
588
|
+
route,
|
|
589
|
+
ctx: context,
|
|
590
|
+
runner: this.runner,
|
|
591
|
+
});
|
|
584
592
|
if (file) {
|
|
585
593
|
context.res.end(file.content);
|
|
586
594
|
return;
|
package/src/type.ts
CHANGED
|
@@ -5,6 +5,13 @@ import type { NormalizedConfig } from '@modern-js/core';
|
|
|
5
5
|
import type { Metrics, Logger, NextFunction } from '@modern-js/types/server';
|
|
6
6
|
import { ModernRouteInterface } from './libs/route';
|
|
7
7
|
|
|
8
|
+
declare module 'http' {
|
|
9
|
+
interface IncomingMessage {
|
|
10
|
+
logger: Logger;
|
|
11
|
+
metrics: Metrics;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
declare module '@modern-js/core' {
|
|
9
16
|
interface UserConfig {
|
|
10
17
|
bff: {
|
package/src/utils.ts
CHANGED
|
@@ -49,17 +49,3 @@ export const createErrorDocument = (status: number, text: string) => {
|
|
|
49
49
|
</html>
|
|
50
50
|
`;
|
|
51
51
|
};
|
|
52
|
-
|
|
53
|
-
// This can live anywhere in your codebase:
|
|
54
|
-
export function applyMixins(derivedCtor: any, constructors: any[]) {
|
|
55
|
-
constructors.forEach(baseCtor => {
|
|
56
|
-
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
|
|
57
|
-
Object.defineProperty(
|
|
58
|
-
derivedCtor.prototype,
|
|
59
|
-
name,
|
|
60
|
-
Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
|
|
61
|
-
Object.create(null),
|
|
62
|
-
);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import EventEmitter from 'events';
|
|
2
|
+
import { Readable } from 'stream';
|
|
3
|
+
import httpMocks from 'node-mocks-http';
|
|
4
|
+
import { createContext } from '../src/libs/context';
|
|
5
|
+
|
|
6
|
+
describe('test server context', () => {
|
|
7
|
+
test('should route api work correctly', () => {
|
|
8
|
+
const req = httpMocks.createRequest({
|
|
9
|
+
url: '/pathname?foo=baz',
|
|
10
|
+
headers: {
|
|
11
|
+
host: 'modernjs.com',
|
|
12
|
+
},
|
|
13
|
+
eventEmitter: Readable,
|
|
14
|
+
method: 'GET',
|
|
15
|
+
});
|
|
16
|
+
const res = httpMocks.createResponse({ eventEmitter: EventEmitter });
|
|
17
|
+
const {
|
|
18
|
+
method,
|
|
19
|
+
url,
|
|
20
|
+
origin,
|
|
21
|
+
host,
|
|
22
|
+
path,
|
|
23
|
+
href,
|
|
24
|
+
query,
|
|
25
|
+
querystring,
|
|
26
|
+
protocol,
|
|
27
|
+
params,
|
|
28
|
+
} = createContext(req, res);
|
|
29
|
+
|
|
30
|
+
expect(method).toBe('GET');
|
|
31
|
+
expect(url).toBe('/pathname?foo=baz');
|
|
32
|
+
expect(origin).toBe('http://modernjs.com');
|
|
33
|
+
expect(host).toBe('modernjs.com');
|
|
34
|
+
expect(path).toBe('/pathname');
|
|
35
|
+
expect(href).toBe('http://modernjs.com/pathname?foo=baz');
|
|
36
|
+
expect(query).toEqual({ foo: 'baz' });
|
|
37
|
+
expect(querystring).toBe('foo=baz');
|
|
38
|
+
expect(protocol).toBe('http');
|
|
39
|
+
expect(params).toEqual({});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
console.info('index.js');
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "deploy",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"reset": "del-cli node_modules",
|
|
6
|
+
"dev": "modern dev",
|
|
7
|
+
"build": "modern build",
|
|
8
|
+
"start": "modern start",
|
|
9
|
+
"new": "modern new",
|
|
10
|
+
"lint": "modern lint",
|
|
11
|
+
"deploy": "modern deploy"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {},
|
|
14
|
+
"devDependencies": {},
|
|
15
|
+
"modernConfig": {
|
|
16
|
+
"runtime": {
|
|
17
|
+
"router": true,
|
|
18
|
+
"state": true
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
html,
|
|
2
|
+
body {
|
|
3
|
+
padding: 0;
|
|
4
|
+
margin: 0;
|
|
5
|
+
font-family: nunito_for_arco, Helvetica Neue, Helvetica, PingFang SC,
|
|
6
|
+
Hiragino Sans GB, Microsoft YaHei, 微软雅黑, Arial, sans-serif;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
* {
|
|
10
|
+
-webkit-font-smoothing: antialiased;
|
|
11
|
+
-moz-osx-font-smoothing: grayscale;
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.container {
|
|
16
|
+
min-height: 100vh;
|
|
17
|
+
max-width: 100%;
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
align-items: center;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
main {
|
|
25
|
+
padding: 5rem 0;
|
|
26
|
+
flex: 1;
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-direction: column;
|
|
29
|
+
justify-content: center;
|
|
30
|
+
align-items: center;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.footer {
|
|
34
|
+
width: 100%;
|
|
35
|
+
height: 80px;
|
|
36
|
+
border-top: 1px solid #eaeaea;
|
|
37
|
+
display: flex;
|
|
38
|
+
justify-content: center;
|
|
39
|
+
align-items: center;
|
|
40
|
+
background-color: #470000;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.footer a {
|
|
44
|
+
display: flex;
|
|
45
|
+
justify-content: center;
|
|
46
|
+
align-items: center;
|
|
47
|
+
flex-grow: 1;
|
|
48
|
+
color: #f4f4f4;
|
|
49
|
+
text-decoration: none;
|
|
50
|
+
font-size: 1.1rem;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.logo {
|
|
54
|
+
margin-bottom: 2rem;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.logo svg {
|
|
58
|
+
width: 450px;
|
|
59
|
+
height: 132px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.description {
|
|
63
|
+
text-align: center;
|
|
64
|
+
line-height: 1.5;
|
|
65
|
+
font-size: 1.5rem;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.code {
|
|
69
|
+
background: #fafafa;
|
|
70
|
+
border-radius: 5px;
|
|
71
|
+
padding: 0.75rem;
|
|
72
|
+
font-size: 1.1rem;
|
|
73
|
+
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
|
|
74
|
+
Bitstream Vera Sans Mono, Courier New, monospace;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@media (max-width: 600px) {
|
|
78
|
+
.grid {
|
|
79
|
+
width: 100%;
|
|
80
|
+
flex-direction: column;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.grid {
|
|
85
|
+
display: flex;
|
|
86
|
+
align-items: center;
|
|
87
|
+
justify-content: center;
|
|
88
|
+
flex-wrap: wrap;
|
|
89
|
+
width: 800px;
|
|
90
|
+
margin-top: 3rem;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.card {
|
|
94
|
+
margin: 1rem;
|
|
95
|
+
padding: 1.5rem;
|
|
96
|
+
display: flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
justify-content: center;
|
|
99
|
+
height: 100px;
|
|
100
|
+
color: inherit;
|
|
101
|
+
text-decoration: none;
|
|
102
|
+
border: 1px solid #470000;
|
|
103
|
+
color: #470000;
|
|
104
|
+
transition: color 0.15s ease, border-color 0.15s ease;
|
|
105
|
+
width: 45%;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.card:hover,
|
|
109
|
+
.card:focus,
|
|
110
|
+
.card:active {
|
|
111
|
+
transform: scale(1.05);
|
|
112
|
+
transition: 0.1s ease-in-out;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.card h2 {
|
|
116
|
+
font-size: 1.5rem;
|
|
117
|
+
margin: 0;
|
|
118
|
+
padding: 0;
|
|
119
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import './App.css';
|
|
2
|
+
|
|
3
|
+
const App = () => (
|
|
4
|
+
<div className="container">
|
|
5
|
+
<main>
|
|
6
|
+
<div className="logo">
|
|
7
|
+
<img
|
|
8
|
+
src="https://lf3-static.bytednsdoc.com/obj/eden-cn/ylaelkeh7nuhfnuhf/modernjs-cover.png"
|
|
9
|
+
width="300"
|
|
10
|
+
alt="Modern.js Logo"
|
|
11
|
+
/>
|
|
12
|
+
</div>
|
|
13
|
+
<p className="description">
|
|
14
|
+
Get started by editing <code className="code">src/home/App.tsx</code>
|
|
15
|
+
</p>
|
|
16
|
+
<div className="grid">
|
|
17
|
+
<a href="https://modernjs.dev/docs/start" className="card">
|
|
18
|
+
<h2>Quick Start Tencent</h2>
|
|
19
|
+
</a>
|
|
20
|
+
<a href="https://modernjs.dev/docs/guides" className="card">
|
|
21
|
+
<h2>Handbook</h2>
|
|
22
|
+
</a>
|
|
23
|
+
<a href="https://modernjs.dev/docs/apis" className="card">
|
|
24
|
+
<h2>API Reference </h2>
|
|
25
|
+
</a>
|
|
26
|
+
<a
|
|
27
|
+
href="https://modernjs.dev/coming-soon"
|
|
28
|
+
target="_blank"
|
|
29
|
+
rel="noopener noreferrer"
|
|
30
|
+
className="card">
|
|
31
|
+
<h2>Community </h2>
|
|
32
|
+
</a>
|
|
33
|
+
</div>
|
|
34
|
+
</main>
|
|
35
|
+
<footer className="footer">
|
|
36
|
+
<a href="https://modernjs.dev" target="_blank" rel="noopener noreferrer">
|
|
37
|
+
Powered by Modern.js
|
|
38
|
+
</a>
|
|
39
|
+
</footer>
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
export default App;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"routes": [
|
|
3
|
+
{
|
|
4
|
+
"urlPath": "/entry",
|
|
5
|
+
"entryName": "entry",
|
|
6
|
+
"entryPath": "html/entry/index.html",
|
|
7
|
+
"isSPA": true,
|
|
8
|
+
"isSSR": true,
|
|
9
|
+
"enableModernMode": false,
|
|
10
|
+
"bundle": "bundles/entry.js"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"urlPath": "/home",
|
|
14
|
+
"entryName": "home",
|
|
15
|
+
"entryPath": "html/home/index.html",
|
|
16
|
+
"isSPA": true,
|
|
17
|
+
"isSSR": true,
|
|
18
|
+
"enableModernMode": false,
|
|
19
|
+
"bundle": "bundles/home.js"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"urlPath": "/api",
|
|
23
|
+
"isApi": true,
|
|
24
|
+
"entryPath": "",
|
|
25
|
+
"isSPA": false,
|
|
26
|
+
"isSSR": false
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
package/tests/helper.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createRouteAPI } from '../src/libs/hook-api/route';
|
|
2
|
+
import { createTemplateAPI } from '../src/libs/hook-api/template';
|
|
3
|
+
import { RouteMatchManager } from '../src/libs/route';
|
|
4
|
+
import { createDoc } from './helper';
|
|
5
|
+
import spec from './fixtures/route-spec/index.json';
|
|
6
|
+
|
|
7
|
+
describe('test hook api', () => {
|
|
8
|
+
test('should route api work correctly', () => {
|
|
9
|
+
const manager = new RouteMatchManager();
|
|
10
|
+
manager.reset(spec.routes);
|
|
11
|
+
const matcher = manager.match('/home');
|
|
12
|
+
|
|
13
|
+
const routeAPI = createRouteAPI(matcher!, manager);
|
|
14
|
+
expect(routeAPI.cur().entryName).toBe('home');
|
|
15
|
+
expect(routeAPI.get('entry')?.entryPath).toBe('html/entry/index.html');
|
|
16
|
+
|
|
17
|
+
expect(routeAPI.use('home')).toBeTruthy();
|
|
18
|
+
expect(routeAPI.cur().entryName).toBe('home');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('should template api work correctly', () => {
|
|
22
|
+
const content = createDoc();
|
|
23
|
+
const templateAPI = createTemplateAPI(content);
|
|
24
|
+
|
|
25
|
+
expect(templateAPI.get()).toMatch(content);
|
|
26
|
+
|
|
27
|
+
templateAPI.replace('mock', 'replace');
|
|
28
|
+
expect(templateAPI.get()).toMatch('replace');
|
|
29
|
+
|
|
30
|
+
templateAPI.appendBody('after body');
|
|
31
|
+
templateAPI.prependBody('before body');
|
|
32
|
+
templateAPI.appendHead('after head');
|
|
33
|
+
templateAPI.prependHead('before head');
|
|
34
|
+
|
|
35
|
+
const newContent = templateAPI.get();
|
|
36
|
+
expect(newContent).toMatch('<head>before head');
|
|
37
|
+
expect(newContent).toMatch('<body>before body');
|
|
38
|
+
expect(newContent).toMatch('after head</head>');
|
|
39
|
+
expect(newContent).toMatch('after body</body>');
|
|
40
|
+
|
|
41
|
+
templateAPI.set('<div>empty</div>');
|
|
42
|
+
expect(templateAPI.get()).toBe('<div>empty</div>');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/* eslint-disable max-nested-callbacks */
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import EventEmitter from 'events';
|
|
4
|
+
import { Readable } from 'stream';
|
|
5
|
+
import { createServer, Server } from 'http';
|
|
6
|
+
import httpMocks from 'node-mocks-http';
|
|
7
|
+
import portfinder from 'portfinder';
|
|
8
|
+
import axios from 'axios';
|
|
9
|
+
import { createContext } from '../src/libs/context';
|
|
10
|
+
import { createStaticFileHandler } from '../src/libs/serve-file';
|
|
11
|
+
import { createProxyHandler } from '../src/libs/proxy';
|
|
12
|
+
|
|
13
|
+
describe('test middleware create factory', () => {
|
|
14
|
+
describe('should create static-file handler correctly', () => {
|
|
15
|
+
const middleware = createStaticFileHandler([
|
|
16
|
+
{
|
|
17
|
+
path: /static\/|upload\//,
|
|
18
|
+
target: path.join(__dirname, './fixtures/hosting-files'),
|
|
19
|
+
},
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
test('should get static file correctly', resolve => {
|
|
23
|
+
const req = httpMocks.createRequest({
|
|
24
|
+
path: '/static/index.js',
|
|
25
|
+
eventEmitter: Readable,
|
|
26
|
+
});
|
|
27
|
+
const res = httpMocks.createResponse({ eventEmitter: EventEmitter });
|
|
28
|
+
const mockContext = createContext(req, res);
|
|
29
|
+
res.on('finish', () => {
|
|
30
|
+
expect(res._getBuffer().toString().trim()).toBe(
|
|
31
|
+
"console.info('index.js');",
|
|
32
|
+
);
|
|
33
|
+
resolve();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
middleware(mockContext, () => {
|
|
37
|
+
throw new Error('should not happened');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('should miss static file correctly', resolve => {
|
|
42
|
+
const req = httpMocks.createRequest({
|
|
43
|
+
path: '/static/index.css',
|
|
44
|
+
eventEmitter: Readable,
|
|
45
|
+
});
|
|
46
|
+
const res = httpMocks.createResponse({ eventEmitter: EventEmitter });
|
|
47
|
+
const mockContext = createContext(req, res);
|
|
48
|
+
res.on('finish', () => {
|
|
49
|
+
throw new Error('should not happened');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
middleware(mockContext, () => {
|
|
53
|
+
req.destroy();
|
|
54
|
+
expect(true).toBeTruthy();
|
|
55
|
+
resolve();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
jest.setTimeout(1000 * 10);
|
|
61
|
+
describe('should create proxy handler correctly', () => {
|
|
62
|
+
test('should return null if no options', () => {
|
|
63
|
+
expect(createProxyHandler(null as any)).toBeNull();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
let sourceServerPort = 8080;
|
|
67
|
+
let sourceServer: Server | null = null;
|
|
68
|
+
beforeAll(async () => {
|
|
69
|
+
sourceServerPort = await portfinder.getPortPromise();
|
|
70
|
+
sourceServer = createServer((req, res) => {
|
|
71
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
72
|
+
res.write(req.url?.slice(1));
|
|
73
|
+
res.end();
|
|
74
|
+
}).listen(sourceServerPort);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
afterAll(() => {
|
|
78
|
+
if (sourceServer) {
|
|
79
|
+
sourceServer.close();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('should proxy correctly use simply options', async () => {
|
|
84
|
+
const port = await portfinder.getPortPromise();
|
|
85
|
+
const middlewares = createProxyHandler({
|
|
86
|
+
'/simple': `http://127.0.0.1:${sourceServerPort}`,
|
|
87
|
+
});
|
|
88
|
+
const proxyHandler = middlewares![0];
|
|
89
|
+
|
|
90
|
+
const server = createServer((req, res) => {
|
|
91
|
+
const context = createContext(req, res);
|
|
92
|
+
proxyHandler(context, () => {
|
|
93
|
+
throw new Error('should not happened');
|
|
94
|
+
});
|
|
95
|
+
}).listen(port);
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const { data } = await axios.get(`http://127.0.0.1:${port}/simple`);
|
|
99
|
+
expect(data).toBe('simple');
|
|
100
|
+
} finally {
|
|
101
|
+
server.close();
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('should proxy correctly use simply obj options', async () => {
|
|
106
|
+
const port = await portfinder.getPortPromise();
|
|
107
|
+
const middlewares = createProxyHandler({
|
|
108
|
+
'/simple-obj': {
|
|
109
|
+
target: `http://127.0.0.1:${sourceServerPort}`,
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
const proxyHandler = middlewares![0];
|
|
113
|
+
|
|
114
|
+
const server = createServer((req, res) => {
|
|
115
|
+
const context = createContext(req, res);
|
|
116
|
+
proxyHandler(context, () => {
|
|
117
|
+
throw new Error('should not happened');
|
|
118
|
+
});
|
|
119
|
+
}).listen(port);
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const { data } = await axios.get(`http://127.0.0.1:${port}/simple-obj`);
|
|
123
|
+
expect(data).toBe('simple-obj');
|
|
124
|
+
} finally {
|
|
125
|
+
server.close();
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('should proxy correctly use context options', async () => {
|
|
130
|
+
const port = await portfinder.getPortPromise();
|
|
131
|
+
const middlewares = createProxyHandler({
|
|
132
|
+
context: '/context',
|
|
133
|
+
target: `http://127.0.0.1:${sourceServerPort}`,
|
|
134
|
+
});
|
|
135
|
+
const proxyHandler = middlewares![0];
|
|
136
|
+
|
|
137
|
+
const server = createServer((req, res) => {
|
|
138
|
+
const context = createContext(req, res);
|
|
139
|
+
proxyHandler(context, () => {
|
|
140
|
+
throw new Error('should not happened');
|
|
141
|
+
});
|
|
142
|
+
}).listen(port);
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const { data } = await axios.get(`http://127.0.0.1:${port}/context`);
|
|
146
|
+
expect(data).toBe('context');
|
|
147
|
+
} finally {
|
|
148
|
+
server.close();
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('should proxy correctly use array options', async () => {
|
|
153
|
+
const port = await portfinder.getPortPromise();
|
|
154
|
+
const middlewares = createProxyHandler([
|
|
155
|
+
{
|
|
156
|
+
context: '/array',
|
|
157
|
+
target: `http://127.0.0.1:${sourceServerPort}`,
|
|
158
|
+
},
|
|
159
|
+
]);
|
|
160
|
+
const proxyHandler = middlewares![0];
|
|
161
|
+
|
|
162
|
+
const server = createServer((req, res) => {
|
|
163
|
+
const context = createContext(req, res);
|
|
164
|
+
proxyHandler(context, () => {
|
|
165
|
+
throw new Error('should not happened');
|
|
166
|
+
});
|
|
167
|
+
}).listen(port);
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
const { data } = await axios.get(`http://127.0.0.1:${port}/array`);
|
|
171
|
+
expect(data).toBe('array');
|
|
172
|
+
} finally {
|
|
173
|
+
server.close();
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
/* eslint-enable max-nested-callbacks */
|