@rindo/core 3.0.0 → 3.1.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/cli/config-flags.d.ts +122 -122
- package/cli/index.cjs +2301 -2432
- package/cli/index.d.ts +19 -19
- package/cli/index.js +2302 -2433
- package/cli/package.json +1 -1
- package/compiler/package.json +1 -1
- package/compiler/rindo.d.ts +73 -73
- package/compiler/rindo.js +66137 -62812
- package/compiler/rindo.min.js +2 -2
- package/compiler/sys/in-memory-fs.d.ts +218 -218
- package/compiler/transpile.d.ts +32 -32
- package/dev-server/client/app-error.d.ts +18 -18
- package/dev-server/client/events.d.ts +6 -6
- package/dev-server/client/hmr-components.d.ts +1 -1
- package/dev-server/client/hmr-external-styles.d.ts +1 -1
- package/dev-server/client/hmr-images.d.ts +1 -1
- package/dev-server/client/hmr-inline-styles.d.ts +1 -1
- package/dev-server/client/hmr-util.d.ts +9 -9
- package/dev-server/client/hmr-window.d.ts +10 -10
- package/dev-server/client/index.d.ts +6 -6
- package/dev-server/client/index.js +781 -781
- package/dev-server/client/logger.d.ts +5 -5
- package/dev-server/client/package.json +1 -1
- package/dev-server/client/progress.d.ts +3 -3
- package/dev-server/client/status.d.ts +4 -4
- package/dev-server/connector.html +2 -2
- package/dev-server/index.d.ts +3 -3
- package/dev-server/index.js +228 -228
- package/dev-server/open-in-editor-api.js +1 -1
- package/dev-server/package.json +1 -1
- package/dev-server/server-process.js +1301 -1279
- package/dev-server/ws.js +1 -1
- package/dev-server/xdg-open +0 -0
- package/internal/app-data/index.cjs +88 -88
- package/internal/app-data/index.d.ts +4 -4
- package/internal/app-data/index.js +88 -88
- package/internal/app-data/package.json +1 -1
- package/internal/client/css-shim.js +1 -1
- package/internal/client/dom.js +1 -1
- package/internal/client/index.js +3380 -3373
- package/internal/client/package.json +1 -1
- package/internal/client/patch-browser.js +155 -155
- package/internal/client/patch-esm.js +25 -25
- package/internal/client/shadow-css.js +382 -382
- package/internal/hydrate/package.json +1 -1
- package/internal/index.d.ts +2 -2
- package/internal/index.js +1 -1
- package/internal/package.json +1 -1
- package/internal/rindo-private.d.ts +2272 -2268
- package/internal/rindo-public-compiler.d.ts +2377 -2353
- package/internal/rindo-public-docs.d.ts +139 -139
- package/internal/rindo-public-runtime.d.ts +1636 -1636
- package/internal/testing/package.json +1 -1
- package/mock-doc/index.cjs +4766 -4766
- package/mock-doc/index.d.ts +1006 -1006
- package/mock-doc/index.js +4766 -4766
- package/mock-doc/package.json +1 -1
- package/package.json +10 -10
- package/screenshot/connector-base.d.ts +42 -42
- package/screenshot/connector-local.d.ts +7 -7
- package/screenshot/index.d.ts +3 -3
- package/screenshot/index.js +615 -615
- package/screenshot/package.json +1 -1
- package/screenshot/pixel-match.d.ts +1 -1
- package/screenshot/pixel-match.js +14 -14
- package/screenshot/screenshot-compare.d.ts +3 -3
- package/screenshot/screenshot-fs.d.ts +15 -15
- package/sys/node/autoprefixer.js +2 -2
- package/sys/node/glob.js +1 -1
- package/sys/node/graceful-fs.js +1 -1
- package/sys/node/index.d.ts +22 -22
- package/sys/node/index.js +6 -3
- package/sys/node/node-fetch.js +1 -1
- package/sys/node/package.json +1 -1
- package/sys/node/prompts.js +1 -1
- package/sys/node/worker.js +1 -1
- package/testing/index.d.ts +12 -12
- package/testing/index.js +7 -3
- package/testing/jest/jest-config.d.ts +16 -16
- package/testing/jest/jest-environment.d.ts +15 -15
- package/testing/jest/jest-preprocessor.d.ts +59 -59
- package/testing/jest/jest-runner.d.ts +10 -10
- package/testing/jest/jest-screenshot.d.ts +2 -2
- package/testing/jest/jest-serializer.d.ts +4 -4
- package/testing/jest/jest-setup-test-framework.d.ts +1 -1
- package/testing/matchers/attributes.d.ts +14 -14
- package/testing/matchers/class-list.d.ts +12 -12
- package/testing/matchers/events.d.ts +21 -21
- package/testing/matchers/html.d.ts +12 -12
- package/testing/matchers/index.d.ts +23 -23
- package/testing/matchers/screenshot.d.ts +5 -5
- package/testing/matchers/text.d.ts +4 -4
- package/testing/mock-fetch.d.ts +11 -11
- package/testing/mocks.d.ts +56 -56
- package/testing/package.json +1 -1
- package/testing/puppeteer/index.d.ts +2 -2
- package/testing/puppeteer/puppeteer-browser.d.ts +6 -6
- package/testing/puppeteer/puppeteer-declarations.d.ts +403 -403
- package/testing/puppeteer/puppeteer-element.d.ts +67 -67
- package/testing/puppeteer/puppeteer-emulate.d.ts +2 -2
- package/testing/puppeteer/puppeteer-events.d.ts +21 -21
- package/testing/puppeteer/puppeteer-page.d.ts +2 -2
- package/testing/puppeteer/puppeteer-screenshot.d.ts +4 -4
- package/testing/reset-build-conditionals.d.ts +2 -2
- package/testing/spec-page.d.ts +2 -2
- package/testing/test-transpile.d.ts +2 -2
- package/testing/testing-logger.d.ts +25 -25
- package/testing/testing-sys.d.ts +6 -6
- package/testing/testing-utils.d.ts +79 -79
- package/testing/testing.d.ts +2 -2
- package/dependencies.json +0 -120
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
Rindo Dev Server Process v3.
|
|
2
|
+
Rindo Dev Server Process v3.1.0 | MIT Licensed | https://rindojs.web.app
|
|
3
3
|
*/
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
@@ -53,321 +53,324 @@ const openInEditorApi__default = /*#__PURE__*/_interopDefaultLegacy(openInEditor
|
|
|
53
53
|
const zlib__namespace = /*#__PURE__*/_interopNamespace(zlib);
|
|
54
54
|
const ws__namespace = /*#__PURE__*/_interopNamespace(ws);
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
56
|
+
/**
|
|
57
|
+
* This is just a no-op, don't expect it to do anything.
|
|
58
|
+
*/
|
|
59
|
+
const noop = () => {
|
|
60
|
+
/* noop*/
|
|
61
|
+
};
|
|
62
|
+
const isFunction = (v) => typeof v === 'function';
|
|
60
63
|
const isString = (v) => typeof v === 'string';
|
|
61
64
|
|
|
62
|
-
/**
|
|
63
|
-
* Builds a diagnostic from an `Error`, appends it to the `diagnostics` parameter, and returns the created diagnostic
|
|
64
|
-
* @param diagnostics the series of diagnostics the newly created diagnostics should be added to
|
|
65
|
-
* @param err the error to derive information from in generating the diagnostic
|
|
66
|
-
* @param msg an optional message to use in place of `err` to generate the diagnostic
|
|
67
|
-
* @returns the generated diagnostic
|
|
68
|
-
*/
|
|
69
|
-
const catchError = (diagnostics, err, msg) => {
|
|
70
|
-
const diagnostic = {
|
|
71
|
-
level: 'error',
|
|
72
|
-
type: 'build',
|
|
73
|
-
header: 'Build Error',
|
|
74
|
-
messageText: 'build error',
|
|
75
|
-
relFilePath: null,
|
|
76
|
-
absFilePath: null,
|
|
77
|
-
lines: [],
|
|
78
|
-
};
|
|
79
|
-
if (isString(msg)) {
|
|
80
|
-
diagnostic.messageText = msg.length ? msg : 'UNKNOWN ERROR';
|
|
81
|
-
}
|
|
82
|
-
else if (err != null) {
|
|
83
|
-
if (err.stack != null) {
|
|
84
|
-
diagnostic.messageText = err.stack.toString();
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
if (err.message != null) {
|
|
88
|
-
diagnostic.messageText = err.message.length ? err.message : 'UNKNOWN ERROR';
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
diagnostic.messageText = err.toString();
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
if (diagnostics != null && !shouldIgnoreError(diagnostic.messageText)) {
|
|
96
|
-
diagnostics.push(diagnostic);
|
|
97
|
-
}
|
|
98
|
-
return diagnostic;
|
|
99
|
-
};
|
|
100
|
-
const shouldIgnoreError = (msg) => {
|
|
101
|
-
return msg === TASK_CANCELED_MSG;
|
|
102
|
-
};
|
|
65
|
+
/**
|
|
66
|
+
* Builds a diagnostic from an `Error`, appends it to the `diagnostics` parameter, and returns the created diagnostic
|
|
67
|
+
* @param diagnostics the series of diagnostics the newly created diagnostics should be added to
|
|
68
|
+
* @param err the error to derive information from in generating the diagnostic
|
|
69
|
+
* @param msg an optional message to use in place of `err` to generate the diagnostic
|
|
70
|
+
* @returns the generated diagnostic
|
|
71
|
+
*/
|
|
72
|
+
const catchError = (diagnostics, err, msg) => {
|
|
73
|
+
const diagnostic = {
|
|
74
|
+
level: 'error',
|
|
75
|
+
type: 'build',
|
|
76
|
+
header: 'Build Error',
|
|
77
|
+
messageText: 'build error',
|
|
78
|
+
relFilePath: null,
|
|
79
|
+
absFilePath: null,
|
|
80
|
+
lines: [],
|
|
81
|
+
};
|
|
82
|
+
if (isString(msg)) {
|
|
83
|
+
diagnostic.messageText = msg.length ? msg : 'UNKNOWN ERROR';
|
|
84
|
+
}
|
|
85
|
+
else if (err != null) {
|
|
86
|
+
if (err.stack != null) {
|
|
87
|
+
diagnostic.messageText = err.stack.toString();
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
if (err.message != null) {
|
|
91
|
+
diagnostic.messageText = err.message.length ? err.message : 'UNKNOWN ERROR';
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
diagnostic.messageText = err.toString();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (diagnostics != null && !shouldIgnoreError(diagnostic.messageText)) {
|
|
99
|
+
diagnostics.push(diagnostic);
|
|
100
|
+
}
|
|
101
|
+
return diagnostic;
|
|
102
|
+
};
|
|
103
|
+
const shouldIgnoreError = (msg) => {
|
|
104
|
+
return msg === TASK_CANCELED_MSG;
|
|
105
|
+
};
|
|
103
106
|
const TASK_CANCELED_MSG = `task canceled`;
|
|
104
107
|
|
|
105
|
-
/**
|
|
106
|
-
* Convert Windows backslash paths to slash paths: foo\\bar ➔ foo/bar
|
|
107
|
-
* Forward-slash paths can be used in Windows as long as they're not
|
|
108
|
-
* extended-length paths and don't contain any non-ascii characters.
|
|
109
|
-
* This was created since the path methods in Node.js outputs \\ paths on Windows.
|
|
110
|
-
* @param path the Windows-based path to convert
|
|
111
|
-
* @returns the converted path
|
|
112
|
-
*/
|
|
113
|
-
const normalizePath = (path) => {
|
|
114
|
-
if (typeof path !== 'string') {
|
|
115
|
-
throw new Error(`invalid path to normalize`);
|
|
116
|
-
}
|
|
117
|
-
path = normalizeSlashes(path.trim());
|
|
118
|
-
const components = pathComponents(path, getRootLength(path));
|
|
119
|
-
const reducedComponents = reducePathComponents(components);
|
|
120
|
-
const rootPart = reducedComponents[0];
|
|
121
|
-
const secondPart = reducedComponents[1];
|
|
122
|
-
const normalized = rootPart + reducedComponents.slice(1).join('/');
|
|
123
|
-
if (normalized === '') {
|
|
124
|
-
return '.';
|
|
125
|
-
}
|
|
126
|
-
if (rootPart === '' &&
|
|
127
|
-
secondPart &&
|
|
128
|
-
path.includes('/') &&
|
|
129
|
-
!secondPart.startsWith('.') &&
|
|
130
|
-
!secondPart.startsWith('@')) {
|
|
131
|
-
return './' + normalized;
|
|
132
|
-
}
|
|
133
|
-
return normalized;
|
|
134
|
-
};
|
|
135
|
-
const normalizeSlashes = (path) => path.replace(backslashRegExp, '/');
|
|
136
|
-
const altDirectorySeparator = '\\';
|
|
137
|
-
const urlSchemeSeparator = '://';
|
|
138
|
-
const backslashRegExp = /\\/g;
|
|
139
|
-
const reducePathComponents = (components) => {
|
|
140
|
-
if (!Array.isArray(components) || components.length === 0) {
|
|
141
|
-
return [];
|
|
142
|
-
}
|
|
143
|
-
const reduced = [components[0]];
|
|
144
|
-
for (let i = 1; i < components.length; i++) {
|
|
145
|
-
const component = components[i];
|
|
146
|
-
if (!component)
|
|
147
|
-
continue;
|
|
148
|
-
if (component === '.')
|
|
149
|
-
continue;
|
|
150
|
-
if (component === '..') {
|
|
151
|
-
if (reduced.length > 1) {
|
|
152
|
-
if (reduced[reduced.length - 1] !== '..') {
|
|
153
|
-
reduced.pop();
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
else if (reduced[0])
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
reduced.push(component);
|
|
161
|
-
}
|
|
162
|
-
return reduced;
|
|
163
|
-
};
|
|
164
|
-
const getRootLength = (path) => {
|
|
165
|
-
const rootLength = getEncodedRootLength(path);
|
|
166
|
-
return rootLength < 0 ? ~rootLength : rootLength;
|
|
167
|
-
};
|
|
168
|
-
const getEncodedRootLength = (path) => {
|
|
169
|
-
if (!path)
|
|
170
|
-
return 0;
|
|
171
|
-
const ch0 = path.charCodeAt(0);
|
|
172
|
-
// POSIX or UNC
|
|
173
|
-
if (ch0 === 47 /* CharacterCodes.slash */ || ch0 === 92 /* CharacterCodes.backslash */) {
|
|
174
|
-
if (path.charCodeAt(1) !== ch0)
|
|
175
|
-
return 1; // POSIX: "/" (or non-normalized "\")
|
|
176
|
-
const p1 = path.indexOf(ch0 === 47 /* CharacterCodes.slash */ ? '/' : altDirectorySeparator, 2);
|
|
177
|
-
if (p1 < 0)
|
|
178
|
-
return path.length; // UNC: "//server" or "\\server"
|
|
179
|
-
return p1 + 1; // UNC: "//server/" or "\\server\"
|
|
180
|
-
}
|
|
181
|
-
// DOS
|
|
182
|
-
if (isVolumeCharacter(ch0) && path.charCodeAt(1) === 58 /* CharacterCodes.colon */) {
|
|
183
|
-
const ch2 = path.charCodeAt(2);
|
|
184
|
-
if (ch2 === 47 /* CharacterCodes.slash */ || ch2 === 92 /* CharacterCodes.backslash */)
|
|
185
|
-
return 3; // DOS: "c:/" or "c:\"
|
|
186
|
-
if (path.length === 2)
|
|
187
|
-
return 2; // DOS: "c:" (but not "c:d")
|
|
188
|
-
}
|
|
189
|
-
// URL
|
|
190
|
-
const schemeEnd = path.indexOf(urlSchemeSeparator);
|
|
191
|
-
if (schemeEnd !== -1) {
|
|
192
|
-
const authorityStart = schemeEnd + urlSchemeSeparator.length;
|
|
193
|
-
const authorityEnd = path.indexOf('/', authorityStart);
|
|
194
|
-
if (authorityEnd !== -1) {
|
|
195
|
-
// URL: "file:///", "file://server/", "file://server/path"
|
|
196
|
-
// For local "file" URLs, include the leading DOS volume (if present).
|
|
197
|
-
// Per https://www.ietf.org/rfc/rfc1738.txt, a host of "" or "localhost" is a
|
|
198
|
-
// special case interpreted as "the machine from which the URL is being interpreted".
|
|
199
|
-
const scheme = path.slice(0, schemeEnd);
|
|
200
|
-
const authority = path.slice(authorityStart, authorityEnd);
|
|
201
|
-
if (scheme === 'file' &&
|
|
202
|
-
(authority === '' || authority === 'localhost') &&
|
|
203
|
-
isVolumeCharacter(path.charCodeAt(authorityEnd + 1))) {
|
|
204
|
-
const volumeSeparatorEnd = getFileUrlVolumeSeparatorEnd(path, authorityEnd + 2);
|
|
205
|
-
if (volumeSeparatorEnd !== -1) {
|
|
206
|
-
if (path.charCodeAt(volumeSeparatorEnd) === 47 /* CharacterCodes.slash */) {
|
|
207
|
-
// URL: "file:///c:/", "file://localhost/c:/", "file:///c%3a/", "file://localhost/c%3a/"
|
|
208
|
-
return ~(volumeSeparatorEnd + 1);
|
|
209
|
-
}
|
|
210
|
-
if (volumeSeparatorEnd === path.length) {
|
|
211
|
-
// URL: "file:///c:", "file://localhost/c:", "file:///c$3a", "file://localhost/c%3a"
|
|
212
|
-
// but not "file:///c:d" or "file:///c%3ad"
|
|
213
|
-
return ~volumeSeparatorEnd;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
return ~(authorityEnd + 1); // URL: "file://server/", "http://server/"
|
|
218
|
-
}
|
|
219
|
-
return ~path.length; // URL: "file://server", "http://server"
|
|
220
|
-
}
|
|
221
|
-
// relative
|
|
222
|
-
return 0;
|
|
223
|
-
};
|
|
224
|
-
const isVolumeCharacter = (charCode) => (charCode >= 97 /* CharacterCodes.a */ && charCode <= 122 /* CharacterCodes.z */) ||
|
|
225
|
-
(charCode >= 65 /* CharacterCodes.A */ && charCode <= 90 /* CharacterCodes.Z */);
|
|
226
|
-
const getFileUrlVolumeSeparatorEnd = (url, start) => {
|
|
227
|
-
const ch0 = url.charCodeAt(start);
|
|
228
|
-
if (ch0 === 58 /* CharacterCodes.colon */)
|
|
229
|
-
return start + 1;
|
|
230
|
-
if (ch0 === 37 /* CharacterCodes.percent */ && url.charCodeAt(start + 1) === 51 /* CharacterCodes._3 */) {
|
|
231
|
-
const ch2 = url.charCodeAt(start + 2);
|
|
232
|
-
if (ch2 === 97 /* CharacterCodes.a */ || ch2 === 65 /* CharacterCodes.A */)
|
|
233
|
-
return start + 3;
|
|
234
|
-
}
|
|
235
|
-
return -1;
|
|
236
|
-
};
|
|
237
|
-
const pathComponents = (path, rootLength) => {
|
|
238
|
-
const root = path.substring(0, rootLength);
|
|
239
|
-
const rest = path.substring(rootLength).split('/');
|
|
240
|
-
const restLen = rest.length;
|
|
241
|
-
if (restLen > 0 && !rest[restLen - 1]) {
|
|
242
|
-
rest.pop();
|
|
243
|
-
}
|
|
244
|
-
return [root, ...rest];
|
|
108
|
+
/**
|
|
109
|
+
* Convert Windows backslash paths to slash paths: foo\\bar ➔ foo/bar
|
|
110
|
+
* Forward-slash paths can be used in Windows as long as they're not
|
|
111
|
+
* extended-length paths and don't contain any non-ascii characters.
|
|
112
|
+
* This was created since the path methods in Node.js outputs \\ paths on Windows.
|
|
113
|
+
* @param path the Windows-based path to convert
|
|
114
|
+
* @returns the converted path
|
|
115
|
+
*/
|
|
116
|
+
const normalizePath = (path) => {
|
|
117
|
+
if (typeof path !== 'string') {
|
|
118
|
+
throw new Error(`invalid path to normalize`);
|
|
119
|
+
}
|
|
120
|
+
path = normalizeSlashes(path.trim());
|
|
121
|
+
const components = pathComponents(path, getRootLength(path));
|
|
122
|
+
const reducedComponents = reducePathComponents(components);
|
|
123
|
+
const rootPart = reducedComponents[0];
|
|
124
|
+
const secondPart = reducedComponents[1];
|
|
125
|
+
const normalized = rootPart + reducedComponents.slice(1).join('/');
|
|
126
|
+
if (normalized === '') {
|
|
127
|
+
return '.';
|
|
128
|
+
}
|
|
129
|
+
if (rootPart === '' &&
|
|
130
|
+
secondPart &&
|
|
131
|
+
path.includes('/') &&
|
|
132
|
+
!secondPart.startsWith('.') &&
|
|
133
|
+
!secondPart.startsWith('@')) {
|
|
134
|
+
return './' + normalized;
|
|
135
|
+
}
|
|
136
|
+
return normalized;
|
|
137
|
+
};
|
|
138
|
+
const normalizeSlashes = (path) => path.replace(backslashRegExp, '/');
|
|
139
|
+
const altDirectorySeparator = '\\';
|
|
140
|
+
const urlSchemeSeparator = '://';
|
|
141
|
+
const backslashRegExp = /\\/g;
|
|
142
|
+
const reducePathComponents = (components) => {
|
|
143
|
+
if (!Array.isArray(components) || components.length === 0) {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
const reduced = [components[0]];
|
|
147
|
+
for (let i = 1; i < components.length; i++) {
|
|
148
|
+
const component = components[i];
|
|
149
|
+
if (!component)
|
|
150
|
+
continue;
|
|
151
|
+
if (component === '.')
|
|
152
|
+
continue;
|
|
153
|
+
if (component === '..') {
|
|
154
|
+
if (reduced.length > 1) {
|
|
155
|
+
if (reduced[reduced.length - 1] !== '..') {
|
|
156
|
+
reduced.pop();
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else if (reduced[0])
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
reduced.push(component);
|
|
164
|
+
}
|
|
165
|
+
return reduced;
|
|
166
|
+
};
|
|
167
|
+
const getRootLength = (path) => {
|
|
168
|
+
const rootLength = getEncodedRootLength(path);
|
|
169
|
+
return rootLength < 0 ? ~rootLength : rootLength;
|
|
170
|
+
};
|
|
171
|
+
const getEncodedRootLength = (path) => {
|
|
172
|
+
if (!path)
|
|
173
|
+
return 0;
|
|
174
|
+
const ch0 = path.charCodeAt(0);
|
|
175
|
+
// POSIX or UNC
|
|
176
|
+
if (ch0 === 47 /* CharacterCodes.slash */ || ch0 === 92 /* CharacterCodes.backslash */) {
|
|
177
|
+
if (path.charCodeAt(1) !== ch0)
|
|
178
|
+
return 1; // POSIX: "/" (or non-normalized "\")
|
|
179
|
+
const p1 = path.indexOf(ch0 === 47 /* CharacterCodes.slash */ ? '/' : altDirectorySeparator, 2);
|
|
180
|
+
if (p1 < 0)
|
|
181
|
+
return path.length; // UNC: "//server" or "\\server"
|
|
182
|
+
return p1 + 1; // UNC: "//server/" or "\\server\"
|
|
183
|
+
}
|
|
184
|
+
// DOS
|
|
185
|
+
if (isVolumeCharacter(ch0) && path.charCodeAt(1) === 58 /* CharacterCodes.colon */) {
|
|
186
|
+
const ch2 = path.charCodeAt(2);
|
|
187
|
+
if (ch2 === 47 /* CharacterCodes.slash */ || ch2 === 92 /* CharacterCodes.backslash */)
|
|
188
|
+
return 3; // DOS: "c:/" or "c:\"
|
|
189
|
+
if (path.length === 2)
|
|
190
|
+
return 2; // DOS: "c:" (but not "c:d")
|
|
191
|
+
}
|
|
192
|
+
// URL
|
|
193
|
+
const schemeEnd = path.indexOf(urlSchemeSeparator);
|
|
194
|
+
if (schemeEnd !== -1) {
|
|
195
|
+
const authorityStart = schemeEnd + urlSchemeSeparator.length;
|
|
196
|
+
const authorityEnd = path.indexOf('/', authorityStart);
|
|
197
|
+
if (authorityEnd !== -1) {
|
|
198
|
+
// URL: "file:///", "file://server/", "file://server/path"
|
|
199
|
+
// For local "file" URLs, include the leading DOS volume (if present).
|
|
200
|
+
// Per https://www.ietf.org/rfc/rfc1738.txt, a host of "" or "localhost" is a
|
|
201
|
+
// special case interpreted as "the machine from which the URL is being interpreted".
|
|
202
|
+
const scheme = path.slice(0, schemeEnd);
|
|
203
|
+
const authority = path.slice(authorityStart, authorityEnd);
|
|
204
|
+
if (scheme === 'file' &&
|
|
205
|
+
(authority === '' || authority === 'localhost') &&
|
|
206
|
+
isVolumeCharacter(path.charCodeAt(authorityEnd + 1))) {
|
|
207
|
+
const volumeSeparatorEnd = getFileUrlVolumeSeparatorEnd(path, authorityEnd + 2);
|
|
208
|
+
if (volumeSeparatorEnd !== -1) {
|
|
209
|
+
if (path.charCodeAt(volumeSeparatorEnd) === 47 /* CharacterCodes.slash */) {
|
|
210
|
+
// URL: "file:///c:/", "file://localhost/c:/", "file:///c%3a/", "file://localhost/c%3a/"
|
|
211
|
+
return ~(volumeSeparatorEnd + 1);
|
|
212
|
+
}
|
|
213
|
+
if (volumeSeparatorEnd === path.length) {
|
|
214
|
+
// URL: "file:///c:", "file://localhost/c:", "file:///c$3a", "file://localhost/c%3a"
|
|
215
|
+
// but not "file:///c:d" or "file:///c%3ad"
|
|
216
|
+
return ~volumeSeparatorEnd;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return ~(authorityEnd + 1); // URL: "file://server/", "http://server/"
|
|
221
|
+
}
|
|
222
|
+
return ~path.length; // URL: "file://server", "http://server"
|
|
223
|
+
}
|
|
224
|
+
// relative
|
|
225
|
+
return 0;
|
|
226
|
+
};
|
|
227
|
+
const isVolumeCharacter = (charCode) => (charCode >= 97 /* CharacterCodes.a */ && charCode <= 122 /* CharacterCodes.z */) ||
|
|
228
|
+
(charCode >= 65 /* CharacterCodes.A */ && charCode <= 90 /* CharacterCodes.Z */);
|
|
229
|
+
const getFileUrlVolumeSeparatorEnd = (url, start) => {
|
|
230
|
+
const ch0 = url.charCodeAt(start);
|
|
231
|
+
if (ch0 === 58 /* CharacterCodes.colon */)
|
|
232
|
+
return start + 1;
|
|
233
|
+
if (ch0 === 37 /* CharacterCodes.percent */ && url.charCodeAt(start + 1) === 51 /* CharacterCodes._3 */) {
|
|
234
|
+
const ch2 = url.charCodeAt(start + 2);
|
|
235
|
+
if (ch2 === 97 /* CharacterCodes.a */ || ch2 === 65 /* CharacterCodes.A */)
|
|
236
|
+
return start + 3;
|
|
237
|
+
}
|
|
238
|
+
return -1;
|
|
239
|
+
};
|
|
240
|
+
const pathComponents = (path, rootLength) => {
|
|
241
|
+
const root = path.substring(0, rootLength);
|
|
242
|
+
const rest = path.substring(rootLength).split('/');
|
|
243
|
+
const restLen = rest.length;
|
|
244
|
+
if (restLen > 0 && !rest[restLen - 1]) {
|
|
245
|
+
rest.pop();
|
|
246
|
+
}
|
|
247
|
+
return [root, ...rest];
|
|
245
248
|
};
|
|
246
249
|
|
|
247
|
-
const DEV_SERVER_URL = '/~dev-server';
|
|
248
|
-
const DEV_MODULE_URL = '/~dev-module';
|
|
249
|
-
const DEV_SERVER_INIT_URL = `${DEV_SERVER_URL}-init`;
|
|
250
|
+
const DEV_SERVER_URL = '/~dev-server';
|
|
251
|
+
const DEV_MODULE_URL = '/~dev-module';
|
|
252
|
+
const DEV_SERVER_INIT_URL = `${DEV_SERVER_URL}-init`;
|
|
250
253
|
const OPEN_IN_EDITOR_URL = `${DEV_SERVER_URL}-open-in-editor`;
|
|
251
254
|
|
|
252
|
-
const version = '3.
|
|
255
|
+
const version = '3.1.0';
|
|
253
256
|
|
|
254
257
|
const contentTypes = {"123":"application/vnd.lotus-1-2-3","1km":"application/vnd.1000minds.decision-model+xml","3dml":"text/vnd.in3d.3dml","3ds":"image/x-3ds","3g2":"video/3gpp2","3gp":"video/3gpp","3gpp":"video/3gpp","3mf":"model/3mf","7z":"application/x-7z-compressed","aab":"application/x-authorware-bin","aac":"audio/x-aac","aam":"application/x-authorware-map","aas":"application/x-authorware-seg","abw":"application/x-abiword","ac":"application/vnd.nokia.n-gage.ac+xml","acc":"application/vnd.americandynamics.acc","ace":"application/x-ace-compressed","acu":"application/vnd.acucobol","acutc":"application/vnd.acucorp","adp":"audio/adpcm","aep":"application/vnd.audiograph","afm":"application/x-font-type1","afp":"application/vnd.ibm.modcap","age":"application/vnd.age","ahead":"application/vnd.ahead.space","ai":"application/postscript","aif":"audio/x-aiff","aifc":"audio/x-aiff","aiff":"audio/x-aiff","air":"application/vnd.adobe.air-application-installer-package+zip","ait":"application/vnd.dvb.ait","ami":"application/vnd.amiga.ami","amr":"audio/amr","apk":"application/vnd.android.package-archive","apng":"image/apng","appcache":"text/cache-manifest","application":"application/x-ms-application","apr":"application/vnd.lotus-approach","arc":"application/x-freearc","arj":"application/x-arj","asc":"application/pgp-signature","asf":"video/x-ms-asf","asm":"text/x-asm","aso":"application/vnd.accpac.simply.aso","asx":"video/x-ms-asf","atc":"application/vnd.acucorp","atom":"application/atom+xml","atomcat":"application/atomcat+xml","atomdeleted":"application/atomdeleted+xml","atomsvc":"application/atomsvc+xml","atx":"application/vnd.antix.game-component","au":"audio/basic","avci":"image/avci","avcs":"image/avcs","avi":"video/x-msvideo","avif":"image/avif","aw":"application/applixware","azf":"application/vnd.airzip.filesecure.azf","azs":"application/vnd.airzip.filesecure.azs","azv":"image/vnd.airzip.accelerator.azv","azw":"application/vnd.amazon.ebook","b16":"image/vnd.pco.b16","bat":"application/x-msdownload","bcpio":"application/x-bcpio","bdf":"application/x-font-bdf","bdm":"application/vnd.syncml.dm+wbxml","bdoc":"application/x-bdoc","bed":"application/vnd.realvnc.bed","bh2":"application/vnd.fujitsu.oasysprs","bin":"application/octet-stream","blb":"application/x-blorb","blorb":"application/x-blorb","bmi":"application/vnd.bmi","bmml":"application/vnd.balsamiq.bmml+xml","bmp":"image/x-ms-bmp","book":"application/vnd.framemaker","box":"application/vnd.previewsystems.box","boz":"application/x-bzip2","bpk":"application/octet-stream","bsp":"model/vnd.valve.source.compiled-map","btif":"image/prs.btif","buffer":"application/octet-stream","bz":"application/x-bzip","bz2":"application/x-bzip2","c":"text/x-c","c11amc":"application/vnd.cluetrust.cartomobile-config","c11amz":"application/vnd.cluetrust.cartomobile-config-pkg","c4d":"application/vnd.clonk.c4group","c4f":"application/vnd.clonk.c4group","c4g":"application/vnd.clonk.c4group","c4p":"application/vnd.clonk.c4group","c4u":"application/vnd.clonk.c4group","cab":"application/vnd.ms-cab-compressed","caf":"audio/x-caf","cap":"application/vnd.tcpdump.pcap","car":"application/vnd.curl.car","cat":"application/vnd.ms-pki.seccat","cb7":"application/x-cbr","cba":"application/x-cbr","cbr":"application/x-cbr","cbt":"application/x-cbr","cbz":"application/x-cbr","cc":"text/x-c","cco":"application/x-cocoa","cct":"application/x-director","ccxml":"application/ccxml+xml","cdbcmsg":"application/vnd.contact.cmsg","cdf":"application/x-netcdf","cdfx":"application/cdfx+xml","cdkey":"application/vnd.mediastation.cdkey","cdmia":"application/cdmi-capability","cdmic":"application/cdmi-container","cdmid":"application/cdmi-domain","cdmio":"application/cdmi-object","cdmiq":"application/cdmi-queue","cdx":"chemical/x-cdx","cdxml":"application/vnd.chemdraw+xml","cdy":"application/vnd.cinderella","cer":"application/pkix-cert","cfs":"application/x-cfs-compressed","cgm":"image/cgm","chat":"application/x-chat","chm":"application/vnd.ms-htmlhelp","chrt":"application/vnd.kde.kchart","cif":"chemical/x-cif","cii":"application/vnd.anser-web-certificate-issue-initiation","cil":"application/vnd.ms-artgalry","cjs":"application/node","cla":"application/vnd.claymore","class":"application/java-vm","clkk":"application/vnd.crick.clicker.keyboard","clkp":"application/vnd.crick.clicker.palette","clkt":"application/vnd.crick.clicker.template","clkw":"application/vnd.crick.clicker.wordbank","clkx":"application/vnd.crick.clicker","clp":"application/x-msclip","cmc":"application/vnd.cosmocaller","cmdf":"chemical/x-cmdf","cml":"chemical/x-cml","cmp":"application/vnd.yellowriver-custom-menu","cmx":"image/x-cmx","cod":"application/vnd.rim.cod","coffee":"text/coffeescript","com":"application/x-msdownload","conf":"text/plain","cpio":"application/x-cpio","cpl":"application/cpl+xml","cpp":"text/x-c","cpt":"application/mac-compactpro","crd":"application/x-mscardfile","crl":"application/pkix-crl","crt":"application/x-x509-ca-cert","crx":"application/x-chrome-extension","cryptonote":"application/vnd.rig.cryptonote","csh":"application/x-csh","csl":"application/vnd.citationstyles.style+xml","csml":"chemical/x-csml","csp":"application/vnd.commonspace","css":"text/css","cst":"application/x-director","csv":"text/csv","cu":"application/cu-seeme","curl":"text/vnd.curl","cww":"application/prs.cww","cxt":"application/x-director","cxx":"text/x-c","dae":"model/vnd.collada+xml","daf":"application/vnd.mobius.daf","dart":"application/vnd.dart","dataless":"application/vnd.fdsn.seed","davmount":"application/davmount+xml","dbf":"application/vnd.dbf","dbk":"application/docbook+xml","dcr":"application/x-director","dcurl":"text/vnd.curl.dcurl","dd2":"application/vnd.oma.dd2+xml","ddd":"application/vnd.fujixerox.ddd","ddf":"application/vnd.syncml.dmddf+xml","dds":"image/vnd.ms-dds","deb":"application/x-debian-package","def":"text/plain","deploy":"application/octet-stream","der":"application/x-x509-ca-cert","dfac":"application/vnd.dreamfactory","dgc":"application/x-dgc-compressed","dic":"text/x-c","dir":"application/x-director","dis":"application/vnd.mobius.dis","disposition-notification":"message/disposition-notification","dist":"application/octet-stream","distz":"application/octet-stream","djv":"image/vnd.djvu","djvu":"image/vnd.djvu","dll":"application/x-msdownload","dmg":"application/x-apple-diskimage","dmp":"application/vnd.tcpdump.pcap","dms":"application/octet-stream","dna":"application/vnd.dna","doc":"application/msword","docm":"application/vnd.ms-word.document.macroenabled.12","docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document","dot":"application/msword","dotm":"application/vnd.ms-word.template.macroenabled.12","dotx":"application/vnd.openxmlformats-officedocument.wordprocessingml.template","dp":"application/vnd.osgi.dp","dpg":"application/vnd.dpgraph","dra":"audio/vnd.dra","drle":"image/dicom-rle","dsc":"text/prs.lines.tag","dssc":"application/dssc+der","dtb":"application/x-dtbook+xml","dtd":"application/xml-dtd","dts":"audio/vnd.dts","dtshd":"audio/vnd.dts.hd","dump":"application/octet-stream","dvb":"video/vnd.dvb.file","dvi":"application/x-dvi","dwd":"application/atsc-dwd+xml","dwf":"model/vnd.dwf","dwg":"image/vnd.dwg","dxf":"image/vnd.dxf","dxp":"application/vnd.spotfire.dxp","dxr":"application/x-director","ear":"application/java-archive","ecelp4800":"audio/vnd.nuera.ecelp4800","ecelp7470":"audio/vnd.nuera.ecelp7470","ecelp9600":"audio/vnd.nuera.ecelp9600","ecma":"application/ecmascript","edm":"application/vnd.novadigm.edm","edx":"application/vnd.novadigm.edx","efif":"application/vnd.picsel","ei6":"application/vnd.pg.osasli","elc":"application/octet-stream","emf":"image/emf","eml":"message/rfc822","emma":"application/emma+xml","emotionml":"application/emotionml+xml","emz":"application/x-msmetafile","eol":"audio/vnd.digital-winds","eot":"application/vnd.ms-fontobject","eps":"application/postscript","epub":"application/epub+zip","es":"application/ecmascript","es3":"application/vnd.eszigno3+xml","esa":"application/vnd.osgi.subsystem","esf":"application/vnd.epson.esf","et3":"application/vnd.eszigno3+xml","etx":"text/x-setext","eva":"application/x-eva","evy":"application/x-envoy","exe":"application/x-msdownload","exi":"application/exi","exp":"application/express","exr":"image/aces","ext":"application/vnd.novadigm.ext","ez":"application/andrew-inset","ez2":"application/vnd.ezpix-album","ez3":"application/vnd.ezpix-package","f":"text/x-fortran","f4v":"video/x-f4v","f77":"text/x-fortran","f90":"text/x-fortran","fbs":"image/vnd.fastbidsheet","fcdt":"application/vnd.adobe.formscentral.fcdt","fcs":"application/vnd.isac.fcs","fdf":"application/vnd.fdf","fdt":"application/fdt+xml","fe_launch":"application/vnd.denovo.fcselayout-link","fg5":"application/vnd.fujitsu.oasysgp","fgd":"application/x-director","fh":"image/x-freehand","fh4":"image/x-freehand","fh5":"image/x-freehand","fh7":"image/x-freehand","fhc":"image/x-freehand","fig":"application/x-xfig","fits":"image/fits","flac":"audio/x-flac","fli":"video/x-fli","flo":"application/vnd.micrografx.flo","flv":"video/x-flv","flw":"application/vnd.kde.kivio","flx":"text/vnd.fmi.flexstor","fly":"text/vnd.fly","fm":"application/vnd.framemaker","fnc":"application/vnd.frogans.fnc","fo":"application/vnd.software602.filler.form+xml","for":"text/x-fortran","fpx":"image/vnd.fpx","frame":"application/vnd.framemaker","fsc":"application/vnd.fsc.weblaunch","fst":"image/vnd.fst","ftc":"application/vnd.fluxtime.clip","fti":"application/vnd.anser-web-funds-transfer-initiation","fvt":"video/vnd.fvt","fxp":"application/vnd.adobe.fxp","fxpl":"application/vnd.adobe.fxp","fzs":"application/vnd.fuzzysheet","g2w":"application/vnd.geoplan","g3":"image/g3fax","g3w":"application/vnd.geospace","gac":"application/vnd.groove-account","gam":"application/x-tads","gbr":"application/rpki-ghostbusters","gca":"application/x-gca-compressed","gdl":"model/vnd.gdl","gdoc":"application/vnd.google-apps.document","ged":"text/vnd.familysearch.gedcom","geo":"application/vnd.dynageo","geojson":"application/geo+json","gex":"application/vnd.geometry-explorer","ggb":"application/vnd.geogebra.file","ggt":"application/vnd.geogebra.tool","ghf":"application/vnd.groove-help","gif":"image/gif","gim":"application/vnd.groove-identity-message","glb":"model/gltf-binary","gltf":"model/gltf+json","gml":"application/gml+xml","gmx":"application/vnd.gmx","gnumeric":"application/x-gnumeric","gph":"application/vnd.flographit","gpx":"application/gpx+xml","gqf":"application/vnd.grafeq","gqs":"application/vnd.grafeq","gram":"application/srgs","gramps":"application/x-gramps-xml","gre":"application/vnd.geometry-explorer","grv":"application/vnd.groove-injector","grxml":"application/srgs+xml","gsf":"application/x-font-ghostscript","gsheet":"application/vnd.google-apps.spreadsheet","gslides":"application/vnd.google-apps.presentation","gtar":"application/x-gtar","gtm":"application/vnd.groove-tool-message","gtw":"model/vnd.gtw","gv":"text/vnd.graphviz","gxf":"application/gxf","gxt":"application/vnd.geonext","gz":"application/gzip","h":"text/x-c","h261":"video/h261","h263":"video/h263","h264":"video/h264","hal":"application/vnd.hal+xml","hbci":"application/vnd.hbci","hbs":"text/x-handlebars-template","hdd":"application/x-virtualbox-hdd","hdf":"application/x-hdf","heic":"image/heic","heics":"image/heic-sequence","heif":"image/heif","heifs":"image/heif-sequence","hej2":"image/hej2k","held":"application/atsc-held+xml","hh":"text/x-c","hjson":"application/hjson","hlp":"application/winhlp","hpgl":"application/vnd.hp-hpgl","hpid":"application/vnd.hp-hpid","hps":"application/vnd.hp-hps","hqx":"application/mac-binhex40","hsj2":"image/hsj2","htc":"text/x-component","htke":"application/vnd.kenameaapp","htm":"text/html","html":"text/html","hvd":"application/vnd.yamaha.hv-dic","hvp":"application/vnd.yamaha.hv-voice","hvs":"application/vnd.yamaha.hv-script","i2g":"application/vnd.intergeo","icc":"application/vnd.iccprofile","ice":"x-conference/x-cooltalk","icm":"application/vnd.iccprofile","ico":"image/x-icon","ics":"text/calendar","ief":"image/ief","ifb":"text/calendar","ifm":"application/vnd.shana.informed.formdata","iges":"model/iges","igl":"application/vnd.igloader","igm":"application/vnd.insors.igm","igs":"model/iges","igx":"application/vnd.micrografx.igx","iif":"application/vnd.shana.informed.interchange","img":"application/octet-stream","imp":"application/vnd.accpac.simply.imp","ims":"application/vnd.ms-ims","in":"text/plain","ini":"text/plain","ink":"application/inkml+xml","inkml":"application/inkml+xml","install":"application/x-install-instructions","iota":"application/vnd.astraea-software.iota","ipfix":"application/ipfix","ipk":"application/vnd.shana.informed.package","irm":"application/vnd.ibm.rights-management","irp":"application/vnd.irepository.package+xml","iso":"application/x-iso9660-image","itp":"application/vnd.shana.informed.formtemplate","its":"application/its+xml","ivp":"application/vnd.immervision-ivp","ivu":"application/vnd.immervision-ivu","jad":"text/vnd.sun.j2me.app-descriptor","jade":"text/jade","jam":"application/vnd.jam","jar":"application/java-archive","jardiff":"application/x-java-archive-diff","java":"text/x-java-source","jhc":"image/jphc","jisp":"application/vnd.jisp","jls":"image/jls","jlt":"application/vnd.hp-jlyt","jng":"image/x-jng","jnlp":"application/x-java-jnlp-file","joda":"application/vnd.joost.joda-archive","jp2":"image/jp2","jpe":"image/jpeg","jpeg":"image/jpeg","jpf":"image/jpx","jpg":"image/jpeg","jpg2":"image/jp2","jpgm":"video/jpm","jpgv":"video/jpeg","jph":"image/jph","jpm":"video/jpm","jpx":"image/jpx","js":"application/javascript","json":"application/json","json5":"application/json5","jsonld":"application/ld+json","jsonml":"application/jsonml+json","jsx":"text/jsx","jxr":"image/jxr","jxra":"image/jxra","jxrs":"image/jxrs","jxs":"image/jxs","jxsc":"image/jxsc","jxsi":"image/jxsi","jxss":"image/jxss","kar":"audio/midi","karbon":"application/vnd.kde.karbon","kdbx":"application/x-keepass2","key":"application/x-iwork-keynote-sffkey","kfo":"application/vnd.kde.kformula","kia":"application/vnd.kidspiration","kml":"application/vnd.google-earth.kml+xml","kmz":"application/vnd.google-earth.kmz","kne":"application/vnd.kinar","knp":"application/vnd.kinar","kon":"application/vnd.kde.kontour","kpr":"application/vnd.kde.kpresenter","kpt":"application/vnd.kde.kpresenter","kpxx":"application/vnd.ds-keypoint","ksp":"application/vnd.kde.kspread","ktr":"application/vnd.kahootz","ktx":"image/ktx","ktx2":"image/ktx2","ktz":"application/vnd.kahootz","kwd":"application/vnd.kde.kword","kwt":"application/vnd.kde.kword","lasxml":"application/vnd.las.las+xml","latex":"application/x-latex","lbd":"application/vnd.llamagraphics.life-balance.desktop","lbe":"application/vnd.llamagraphics.life-balance.exchange+xml","les":"application/vnd.hhe.lesson-player","less":"text/less","lgr":"application/lgr+xml","lha":"application/x-lzh-compressed","link66":"application/vnd.route66.link66+xml","list":"text/plain","list3820":"application/vnd.ibm.modcap","listafp":"application/vnd.ibm.modcap","litcoffee":"text/coffeescript","lnk":"application/x-ms-shortcut","log":"text/plain","lostxml":"application/lost+xml","lrf":"application/octet-stream","lrm":"application/vnd.ms-lrm","ltf":"application/vnd.frogans.ltf","lua":"text/x-lua","luac":"application/x-lua-bytecode","lvp":"audio/vnd.lucent.voice","lwp":"application/vnd.lotus-wordpro","lzh":"application/x-lzh-compressed","m13":"application/x-msmediaview","m14":"application/x-msmediaview","m1v":"video/mpeg","m21":"application/mp21","m2a":"audio/mpeg","m2v":"video/mpeg","m3a":"audio/mpeg","m3u":"audio/x-mpegurl","m3u8":"application/vnd.apple.mpegurl","m4a":"audio/x-m4a","m4p":"application/mp4","m4s":"video/iso.segment","m4u":"video/vnd.mpegurl","m4v":"video/x-m4v","ma":"application/mathematica","mads":"application/mads+xml","maei":"application/mmt-aei+xml","mag":"application/vnd.ecowin.chart","maker":"application/vnd.framemaker","man":"text/troff","manifest":"text/cache-manifest","map":"application/json","mar":"application/octet-stream","markdown":"text/markdown","mathml":"application/mathml+xml","mb":"application/mathematica","mbk":"application/vnd.mobius.mbk","mbox":"application/mbox","mc1":"application/vnd.medcalcdata","mcd":"application/vnd.mcd","mcurl":"text/vnd.curl.mcurl","md":"text/markdown","mdb":"application/x-msaccess","mdi":"image/vnd.ms-modi","mdx":"text/mdx","me":"text/troff","mesh":"model/mesh","meta4":"application/metalink4+xml","metalink":"application/metalink+xml","mets":"application/mets+xml","mfm":"application/vnd.mfmp","mft":"application/rpki-manifest","mgp":"application/vnd.osgeo.mapguide.package","mgz":"application/vnd.proteus.magazine","mid":"audio/midi","midi":"audio/midi","mie":"application/x-mie","mif":"application/vnd.mif","mime":"message/rfc822","mj2":"video/mj2","mjp2":"video/mj2","mjs":"application/javascript","mk3d":"video/x-matroska","mka":"audio/x-matroska","mkd":"text/x-markdown","mks":"video/x-matroska","mkv":"video/x-matroska","mlp":"application/vnd.dolby.mlp","mmd":"application/vnd.chipnuts.karaoke-mmd","mmf":"application/vnd.smaf","mml":"text/mathml","mmr":"image/vnd.fujixerox.edmics-mmr","mng":"video/x-mng","mny":"application/x-msmoney","mobi":"application/x-mobipocket-ebook","mods":"application/mods+xml","mov":"video/quicktime","movie":"video/x-sgi-movie","mp2":"audio/mpeg","mp21":"application/mp21","mp2a":"audio/mpeg","mp3":"audio/mpeg","mp4":"video/mp4","mp4a":"audio/mp4","mp4s":"application/mp4","mp4v":"video/mp4","mpc":"application/vnd.mophun.certificate","mpd":"application/dash+xml","mpe":"video/mpeg","mpeg":"video/mpeg","mpf":"application/media-policy-dataset+xml","mpg":"video/mpeg","mpg4":"video/mp4","mpga":"audio/mpeg","mpkg":"application/vnd.apple.installer+xml","mpm":"application/vnd.blueice.multipass","mpn":"application/vnd.mophun.application","mpp":"application/vnd.ms-project","mpt":"application/vnd.ms-project","mpy":"application/vnd.ibm.minipay","mqy":"application/vnd.mobius.mqy","mrc":"application/marc","mrcx":"application/marcxml+xml","ms":"text/troff","mscml":"application/mediaservercontrol+xml","mseed":"application/vnd.fdsn.mseed","mseq":"application/vnd.mseq","msf":"application/vnd.epson.msf","msg":"application/vnd.ms-outlook","msh":"model/mesh","msi":"application/x-msdownload","msl":"application/vnd.mobius.msl","msm":"application/octet-stream","msp":"application/octet-stream","msty":"application/vnd.muvee.style","mtl":"model/mtl","mts":"model/vnd.mts","mus":"application/vnd.musician","musd":"application/mmt-usd+xml","musicxml":"application/vnd.recordare.musicxml+xml","mvb":"application/x-msmediaview","mvt":"application/vnd.mapbox-vector-tile","mwf":"application/vnd.mfer","mxf":"application/mxf","mxl":"application/vnd.recordare.musicxml","mxmf":"audio/mobile-xmf","mxml":"application/xv+xml","mxs":"application/vnd.triscape.mxs","mxu":"video/vnd.mpegurl","n-gage":"application/vnd.nokia.n-gage.symbian.install","n3":"text/n3","nb":"application/mathematica","nbp":"application/vnd.wolfram.player","nc":"application/x-netcdf","ncx":"application/x-dtbncx+xml","nfo":"text/x-nfo","ngdat":"application/vnd.nokia.n-gage.data","nitf":"application/vnd.nitf","nlu":"application/vnd.neurolanguage.nlu","nml":"application/vnd.enliven","nnd":"application/vnd.noblenet-directory","nns":"application/vnd.noblenet-sealer","nnw":"application/vnd.noblenet-web","npx":"image/vnd.net-fpx","nq":"application/n-quads","nsc":"application/x-conference","nsf":"application/vnd.lotus-notes","nt":"application/n-triples","ntf":"application/vnd.nitf","numbers":"application/x-iwork-numbers-sffnumbers","nzb":"application/x-nzb","oa2":"application/vnd.fujitsu.oasys2","oa3":"application/vnd.fujitsu.oasys3","oas":"application/vnd.fujitsu.oasys","obd":"application/x-msbinder","obgx":"application/vnd.openblox.game+xml","obj":"model/obj","oda":"application/oda","odb":"application/vnd.oasis.opendocument.database","odc":"application/vnd.oasis.opendocument.chart","odf":"application/vnd.oasis.opendocument.formula","odft":"application/vnd.oasis.opendocument.formula-template","odg":"application/vnd.oasis.opendocument.graphics","odi":"application/vnd.oasis.opendocument.image","odm":"application/vnd.oasis.opendocument.text-master","odp":"application/vnd.oasis.opendocument.presentation","ods":"application/vnd.oasis.opendocument.spreadsheet","odt":"application/vnd.oasis.opendocument.text","oga":"audio/ogg","ogex":"model/vnd.opengex","ogg":"audio/ogg","ogv":"video/ogg","ogx":"application/ogg","omdoc":"application/omdoc+xml","onepkg":"application/onenote","onetmp":"application/onenote","onetoc":"application/onenote","onetoc2":"application/onenote","opf":"application/oebps-package+xml","opml":"text/x-opml","oprc":"application/vnd.palm","opus":"audio/ogg","org":"text/x-org","osf":"application/vnd.yamaha.openscoreformat","osfpvg":"application/vnd.yamaha.openscoreformat.osfpvg+xml","osm":"application/vnd.openstreetmap.data+xml","otc":"application/vnd.oasis.opendocument.chart-template","otf":"font/otf","otg":"application/vnd.oasis.opendocument.graphics-template","oth":"application/vnd.oasis.opendocument.text-web","oti":"application/vnd.oasis.opendocument.image-template","otp":"application/vnd.oasis.opendocument.presentation-template","ots":"application/vnd.oasis.opendocument.spreadsheet-template","ott":"application/vnd.oasis.opendocument.text-template","ova":"application/x-virtualbox-ova","ovf":"application/x-virtualbox-ovf","owl":"application/rdf+xml","oxps":"application/oxps","oxt":"application/vnd.openofficeorg.extension","p":"text/x-pascal","p10":"application/pkcs10","p12":"application/x-pkcs12","p7b":"application/x-pkcs7-certificates","p7c":"application/pkcs7-mime","p7m":"application/pkcs7-mime","p7r":"application/x-pkcs7-certreqresp","p7s":"application/pkcs7-signature","p8":"application/pkcs8","pac":"application/x-ns-proxy-autoconfig","pages":"application/x-iwork-pages-sffpages","pas":"text/x-pascal","paw":"application/vnd.pawaafile","pbd":"application/vnd.powerbuilder6","pbm":"image/x-portable-bitmap","pcap":"application/vnd.tcpdump.pcap","pcf":"application/x-font-pcf","pcl":"application/vnd.hp-pcl","pclxl":"application/vnd.hp-pclxl","pct":"image/x-pict","pcurl":"application/vnd.curl.pcurl","pcx":"image/x-pcx","pdb":"application/x-pilot","pde":"text/x-processing","pdf":"application/pdf","pem":"application/x-x509-ca-cert","pfa":"application/x-font-type1","pfb":"application/x-font-type1","pfm":"application/x-font-type1","pfr":"application/font-tdpfr","pfx":"application/x-pkcs12","pgm":"image/x-portable-graymap","pgn":"application/x-chess-pgn","pgp":"application/pgp-encrypted","php":"application/x-httpd-php","pic":"image/x-pict","pkg":"application/octet-stream","pki":"application/pkixcmp","pkipath":"application/pkix-pkipath","pkpass":"application/vnd.apple.pkpass","pl":"application/x-perl","plb":"application/vnd.3gpp.pic-bw-large","plc":"application/vnd.mobius.plc","plf":"application/vnd.pocketlearn","pls":"application/pls+xml","pm":"application/x-perl","pml":"application/vnd.ctc-posml","png":"image/png","pnm":"image/x-portable-anymap","portpkg":"application/vnd.macports.portpkg","pot":"application/vnd.ms-powerpoint","potm":"application/vnd.ms-powerpoint.template.macroenabled.12","potx":"application/vnd.openxmlformats-officedocument.presentationml.template","ppam":"application/vnd.ms-powerpoint.addin.macroenabled.12","ppd":"application/vnd.cups-ppd","ppm":"image/x-portable-pixmap","pps":"application/vnd.ms-powerpoint","ppsm":"application/vnd.ms-powerpoint.slideshow.macroenabled.12","ppsx":"application/vnd.openxmlformats-officedocument.presentationml.slideshow","ppt":"application/vnd.ms-powerpoint","pptm":"application/vnd.ms-powerpoint.presentation.macroenabled.12","pptx":"application/vnd.openxmlformats-officedocument.presentationml.presentation","pqa":"application/vnd.palm","prc":"application/x-pilot","pre":"application/vnd.lotus-freelance","prf":"application/pics-rules","provx":"application/provenance+xml","ps":"application/postscript","psb":"application/vnd.3gpp.pic-bw-small","psd":"image/vnd.adobe.photoshop","psf":"application/x-font-linux-psf","pskcxml":"application/pskc+xml","pti":"image/prs.pti","ptid":"application/vnd.pvi.ptid1","pub":"application/x-mspublisher","pvb":"application/vnd.3gpp.pic-bw-var","pwn":"application/vnd.3m.post-it-notes","pya":"audio/vnd.ms-playready.media.pya","pyv":"video/vnd.ms-playready.media.pyv","qam":"application/vnd.epson.quickanime","qbo":"application/vnd.intu.qbo","qfx":"application/vnd.intu.qfx","qps":"application/vnd.publishare-delta-tree","qt":"video/quicktime","qwd":"application/vnd.quark.quarkxpress","qwt":"application/vnd.quark.quarkxpress","qxb":"application/vnd.quark.quarkxpress","qxd":"application/vnd.quark.quarkxpress","qxl":"application/vnd.quark.quarkxpress","qxt":"application/vnd.quark.quarkxpress","ra":"audio/x-realaudio","ram":"audio/x-pn-realaudio","raml":"application/raml+yaml","rapd":"application/route-apd+xml","rar":"application/x-rar-compressed","ras":"image/x-cmu-raster","rcprofile":"application/vnd.ipunplugged.rcprofile","rdf":"application/rdf+xml","rdz":"application/vnd.data-vision.rdz","relo":"application/p2p-overlay+xml","rep":"application/vnd.businessobjects","res":"application/x-dtbresource+xml","rgb":"image/x-rgb","rif":"application/reginfo+xml","rip":"audio/vnd.rip","ris":"application/x-research-info-systems","rl":"application/resource-lists+xml","rlc":"image/vnd.fujixerox.edmics-rlc","rld":"application/resource-lists-diff+xml","rm":"application/vnd.rn-realmedia","rmi":"audio/midi","rmp":"audio/x-pn-realaudio-plugin","rms":"application/vnd.jcp.javame.midlet-rms","rmvb":"application/vnd.rn-realmedia-vbr","rnc":"application/relax-ng-compact-syntax","rng":"application/xml","roa":"application/rpki-roa","roff":"text/troff","rp9":"application/vnd.cloanto.rp9","rpm":"application/x-redhat-package-manager","rpss":"application/vnd.nokia.radio-presets","rpst":"application/vnd.nokia.radio-preset","rq":"application/sparql-query","rs":"application/rls-services+xml","rsat":"application/atsc-rsat+xml","rsd":"application/rsd+xml","rsheet":"application/urc-ressheet+xml","rss":"application/rss+xml","rtf":"text/rtf","rtx":"text/richtext","run":"application/x-makeself","rusd":"application/route-usd+xml","s":"text/x-asm","s3m":"audio/s3m","saf":"application/vnd.yamaha.smaf-audio","sass":"text/x-sass","sbml":"application/sbml+xml","sc":"application/vnd.ibm.secure-container","scd":"application/x-msschedule","scm":"application/vnd.lotus-screencam","scq":"application/scvp-cv-request","scs":"application/scvp-cv-response","scss":"text/x-scss","scurl":"text/vnd.curl.scurl","sda":"application/vnd.stardivision.draw","sdc":"application/vnd.stardivision.calc","sdd":"application/vnd.stardivision.impress","sdkd":"application/vnd.solent.sdkm+xml","sdkm":"application/vnd.solent.sdkm+xml","sdp":"application/sdp","sdw":"application/vnd.stardivision.writer","sea":"application/x-sea","see":"application/vnd.seemail","seed":"application/vnd.fdsn.seed","sema":"application/vnd.sema","semd":"application/vnd.semd","semf":"application/vnd.semf","senmlx":"application/senml+xml","sensmlx":"application/sensml+xml","ser":"application/java-serialized-object","setpay":"application/set-payment-initiation","setreg":"application/set-registration-initiation","sfd-hdstx":"application/vnd.hydrostatix.sof-data","sfs":"application/vnd.spotfire.sfs","sfv":"text/x-sfv","sgi":"image/sgi","sgl":"application/vnd.stardivision.writer-global","sgm":"text/sgml","sgml":"text/sgml","sh":"application/x-sh","shar":"application/x-shar","shex":"text/shex","shf":"application/shf+xml","shtml":"text/html","sid":"image/x-mrsid-image","sieve":"application/sieve","sig":"application/pgp-signature","sil":"audio/silk","silo":"model/mesh","sis":"application/vnd.symbian.install","sisx":"application/vnd.symbian.install","sit":"application/x-stuffit","sitx":"application/x-stuffitx","siv":"application/sieve","skd":"application/vnd.koan","skm":"application/vnd.koan","skp":"application/vnd.koan","skt":"application/vnd.koan","sldm":"application/vnd.ms-powerpoint.slide.macroenabled.12","sldx":"application/vnd.openxmlformats-officedocument.presentationml.slide","slim":"text/slim","slm":"text/slim","sls":"application/route-s-tsid+xml","slt":"application/vnd.epson.salt","sm":"application/vnd.stepmania.stepchart","smf":"application/vnd.stardivision.math","smi":"application/smil+xml","smil":"application/smil+xml","smv":"video/x-smv","smzip":"application/vnd.stepmania.package","snd":"audio/basic","snf":"application/x-font-snf","so":"application/octet-stream","spc":"application/x-pkcs7-certificates","spdx":"text/spdx","spf":"application/vnd.yamaha.smaf-phrase","spl":"application/x-futuresplash","spot":"text/vnd.in3d.spot","spp":"application/scvp-vp-response","spq":"application/scvp-vp-request","spx":"audio/ogg","sql":"application/x-sql","src":"application/x-wais-source","srt":"application/x-subrip","sru":"application/sru+xml","srx":"application/sparql-results+xml","ssdl":"application/ssdl+xml","sse":"application/vnd.kodak-descriptor","ssf":"application/vnd.epson.ssf","ssml":"application/ssml+xml","st":"application/vnd.sailingtracker.track","stc":"application/vnd.sun.xml.calc.template","std":"application/vnd.sun.xml.draw.template","stf":"application/vnd.wt.stf","sti":"application/vnd.sun.xml.impress.template","stk":"application/hyperstudio","stl":"model/stl","stpx":"model/step+xml","stpxz":"model/step-xml+zip","stpz":"model/step+zip","str":"application/vnd.pg.format","stw":"application/vnd.sun.xml.writer.template","styl":"text/stylus","stylus":"text/stylus","sub":"text/vnd.dvb.subtitle","sus":"application/vnd.sus-calendar","susp":"application/vnd.sus-calendar","sv4cpio":"application/x-sv4cpio","sv4crc":"application/x-sv4crc","svc":"application/vnd.dvb.service","svd":"application/vnd.svd","svg":"image/svg+xml","svgz":"image/svg+xml","swa":"application/x-director","swf":"application/x-shockwave-flash","swi":"application/vnd.aristanetworks.swi","swidtag":"application/swid+xml","sxc":"application/vnd.sun.xml.calc","sxd":"application/vnd.sun.xml.draw","sxg":"application/vnd.sun.xml.writer.global","sxi":"application/vnd.sun.xml.impress","sxm":"application/vnd.sun.xml.math","sxw":"application/vnd.sun.xml.writer","t":"text/troff","t3":"application/x-t3vm-image","t38":"image/t38","taglet":"application/vnd.mynfc","tao":"application/vnd.tao.intent-module-archive","tap":"image/vnd.tencent.tap","tar":"application/x-tar","tcap":"application/vnd.3gpp2.tcap","tcl":"application/x-tcl","td":"application/urc-targetdesc+xml","teacher":"application/vnd.smart.teacher","tei":"application/tei+xml","teicorpus":"application/tei+xml","tex":"application/x-tex","texi":"application/x-texinfo","texinfo":"application/x-texinfo","text":"text/plain","tfi":"application/thraud+xml","tfm":"application/x-tex-tfm","tfx":"image/tiff-fx","tga":"image/x-tga","thmx":"application/vnd.ms-officetheme","tif":"image/tiff","tiff":"image/tiff","tk":"application/x-tcl","tmo":"application/vnd.tmobile-livetv","toml":"application/toml","torrent":"application/x-bittorrent","tpl":"application/vnd.groove-tool-template","tpt":"application/vnd.trid.tpt","tr":"text/troff","tra":"application/vnd.trueapp","trig":"application/trig","trm":"application/x-msterminal","ts":"video/mp2t","tsd":"application/timestamped-data","tsv":"text/tab-separated-values","ttc":"font/collection","ttf":"font/ttf","ttl":"text/turtle","ttml":"application/ttml+xml","twd":"application/vnd.simtech-mindmapper","twds":"application/vnd.simtech-mindmapper","txd":"application/vnd.genomatix.tuxedo","txf":"application/vnd.mobius.txf","txt":"text/plain","u32":"application/x-authorware-bin","u8dsn":"message/global-delivery-status","u8hdr":"message/global-headers","u8mdn":"message/global-disposition-notification","u8msg":"message/global","ubj":"application/ubjson","udeb":"application/x-debian-package","ufd":"application/vnd.ufdl","ufdl":"application/vnd.ufdl","ulx":"application/x-glulx","umj":"application/vnd.umajin","unityweb":"application/vnd.unity","uoml":"application/vnd.uoml+xml","uri":"text/uri-list","uris":"text/uri-list","urls":"text/uri-list","usdz":"model/vnd.usdz+zip","ustar":"application/x-ustar","utz":"application/vnd.uiq.theme","uu":"text/x-uuencode","uva":"audio/vnd.dece.audio","uvd":"application/vnd.dece.data","uvf":"application/vnd.dece.data","uvg":"image/vnd.dece.graphic","uvh":"video/vnd.dece.hd","uvi":"image/vnd.dece.graphic","uvm":"video/vnd.dece.mobile","uvp":"video/vnd.dece.pd","uvs":"video/vnd.dece.sd","uvt":"application/vnd.dece.ttml+xml","uvu":"video/vnd.uvvu.mp4","uvv":"video/vnd.dece.video","uvva":"audio/vnd.dece.audio","uvvd":"application/vnd.dece.data","uvvf":"application/vnd.dece.data","uvvg":"image/vnd.dece.graphic","uvvh":"video/vnd.dece.hd","uvvi":"image/vnd.dece.graphic","uvvm":"video/vnd.dece.mobile","uvvp":"video/vnd.dece.pd","uvvs":"video/vnd.dece.sd","uvvt":"application/vnd.dece.ttml+xml","uvvu":"video/vnd.uvvu.mp4","uvvv":"video/vnd.dece.video","uvvx":"application/vnd.dece.unspecified","uvvz":"application/vnd.dece.zip","uvx":"application/vnd.dece.unspecified","uvz":"application/vnd.dece.zip","vbox":"application/x-virtualbox-vbox","vbox-extpack":"application/x-virtualbox-vbox-extpack","vcard":"text/vcard","vcd":"application/x-cdlink","vcf":"text/x-vcard","vcg":"application/vnd.groove-vcard","vcs":"text/x-vcalendar","vcx":"application/vnd.vcx","vdi":"application/x-virtualbox-vdi","vds":"model/vnd.sap.vds","vhd":"application/x-virtualbox-vhd","vis":"application/vnd.visionary","viv":"video/vnd.vivo","vmdk":"application/x-virtualbox-vmdk","vob":"video/x-ms-vob","vor":"application/vnd.stardivision.writer","vox":"application/x-authorware-bin","vrml":"model/vrml","vsd":"application/vnd.visio","vsf":"application/vnd.vsf","vss":"application/vnd.visio","vst":"application/vnd.visio","vsw":"application/vnd.visio","vtf":"image/vnd.valve.source.texture","vtt":"text/vtt","vtu":"model/vnd.vtu","vxml":"application/voicexml+xml","w3d":"application/x-director","wad":"application/x-doom","wadl":"application/vnd.sun.wadl+xml","war":"application/java-archive","wasm":"application/wasm","wav":"audio/x-wav","wax":"audio/x-ms-wax","wbmp":"image/vnd.wap.wbmp","wbs":"application/vnd.criticaltools.wbs+xml","wbxml":"application/vnd.wap.wbxml","wcm":"application/vnd.ms-works","wdb":"application/vnd.ms-works","wdp":"image/vnd.ms-photo","weba":"audio/webm","webapp":"application/x-web-app-manifest+json","webm":"video/webm","webmanifest":"application/manifest+json","webp":"image/webp","wg":"application/vnd.pmi.widget","wgt":"application/widget","wif":"application/watcherinfo+xml","wks":"application/vnd.ms-works","wm":"video/x-ms-wm","wma":"audio/x-ms-wma","wmd":"application/x-ms-wmd","wmf":"image/wmf","wml":"text/vnd.wap.wml","wmlc":"application/vnd.wap.wmlc","wmls":"text/vnd.wap.wmlscript","wmlsc":"application/vnd.wap.wmlscriptc","wmv":"video/x-ms-wmv","wmx":"video/x-ms-wmx","wmz":"application/x-msmetafile","woff":"font/woff","woff2":"font/woff2","wpd":"application/vnd.wordperfect","wpl":"application/vnd.ms-wpl","wps":"application/vnd.ms-works","wqd":"application/vnd.wqd","wri":"application/x-mswrite","wrl":"model/vrml","wsc":"message/vnd.wfa.wsc","wsdl":"application/wsdl+xml","wspolicy":"application/wspolicy+xml","wtb":"application/vnd.webturbo","wvx":"video/x-ms-wvx","x32":"application/x-authorware-bin","x3d":"model/x3d+xml","x3db":"model/x3d+fastinfoset","x3dbz":"model/x3d+binary","x3dv":"model/x3d-vrml","x3dvz":"model/x3d+vrml","x3dz":"model/x3d+xml","x_b":"model/vnd.parasolid.transmit.binary","x_t":"model/vnd.parasolid.transmit.text","xaml":"application/xaml+xml","xap":"application/x-silverlight-app","xar":"application/vnd.xara","xav":"application/xcap-att+xml","xbap":"application/x-ms-xbap","xbd":"application/vnd.fujixerox.docuworks.binder","xbm":"image/x-xbitmap","xca":"application/xcap-caps+xml","xcs":"application/calendar+xml","xdf":"application/xcap-diff+xml","xdm":"application/vnd.syncml.dm+xml","xdp":"application/vnd.adobe.xdp+xml","xdssc":"application/dssc+xml","xdw":"application/vnd.fujixerox.docuworks","xel":"application/xcap-el+xml","xenc":"application/xenc+xml","xer":"application/patch-ops-error+xml","xfdf":"application/vnd.adobe.xfdf","xfdl":"application/vnd.xfdl","xht":"application/xhtml+xml","xhtml":"application/xhtml+xml","xhvml":"application/xv+xml","xif":"image/vnd.xiff","xla":"application/vnd.ms-excel","xlam":"application/vnd.ms-excel.addin.macroenabled.12","xlc":"application/vnd.ms-excel","xlf":"application/xliff+xml","xlm":"application/vnd.ms-excel","xls":"application/vnd.ms-excel","xlsb":"application/vnd.ms-excel.sheet.binary.macroenabled.12","xlsm":"application/vnd.ms-excel.sheet.macroenabled.12","xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","xlt":"application/vnd.ms-excel","xltm":"application/vnd.ms-excel.template.macroenabled.12","xltx":"application/vnd.openxmlformats-officedocument.spreadsheetml.template","xlw":"application/vnd.ms-excel","xm":"audio/xm","xml":"text/xml","xns":"application/xcap-ns+xml","xo":"application/vnd.olpc-sugar","xop":"application/xop+xml","xpi":"application/x-xpinstall","xpl":"application/xproc+xml","xpm":"image/x-xpixmap","xpr":"application/vnd.is-xpr","xps":"application/vnd.ms-xpsdocument","xpw":"application/vnd.intercon.formnet","xpx":"application/vnd.intercon.formnet","xsd":"application/xml","xsl":"application/xslt+xml","xslt":"application/xslt+xml","xsm":"application/vnd.syncml+xml","xspf":"application/xspf+xml","xul":"application/vnd.mozilla.xul+xml","xvm":"application/xv+xml","xvml":"application/xv+xml","xwd":"image/x-xwindowdump","xyz":"chemical/x-xyz","xz":"application/x-xz","yaml":"text/yaml","yang":"application/yang","yin":"application/yin+xml","yml":"text/yaml","ymp":"text/x-suse-ymp","z1":"application/x-zmachine","z2":"application/x-zmachine","z3":"application/x-zmachine","z4":"application/x-zmachine","z5":"application/x-zmachine","z6":"application/x-zmachine","z7":"application/x-zmachine","z8":"application/x-zmachine","zaz":"application/vnd.zzazz.deck+xml","zip":"application/zip","zir":"application/vnd.zul","zirz":"application/vnd.zul","zmm":"application/vnd.handheld-entertainment+xml"};
|
|
255
258
|
|
|
256
|
-
function responseHeaders(headers, httpCache = false) {
|
|
257
|
-
headers = { ...DEFAULT_HEADERS, ...headers };
|
|
258
|
-
if (httpCache) {
|
|
259
|
-
headers['cache-control'] = 'max-age=3600';
|
|
260
|
-
delete headers['date'];
|
|
261
|
-
delete headers['expires'];
|
|
262
|
-
}
|
|
263
|
-
return headers;
|
|
264
|
-
}
|
|
265
|
-
const DEFAULT_HEADERS = {
|
|
266
|
-
'cache-control': 'no-cache, no-store, must-revalidate, max-age=0',
|
|
267
|
-
expires: '0',
|
|
268
|
-
date: 'Wed, 1 Jan 2000 00:00:00 GMT',
|
|
269
|
-
server: 'Rindo Dev Server ' + version,
|
|
270
|
-
'access-control-allow-origin': '*',
|
|
271
|
-
'access-control-expose-headers': '*',
|
|
272
|
-
};
|
|
273
|
-
function getBrowserUrl(protocol, address, port, basePath, pathname) {
|
|
274
|
-
address = address === `0.0.0.0` ? `localhost` : address;
|
|
275
|
-
const portSuffix = !port || port === 80 || port === 443 ? '' : ':' + port;
|
|
276
|
-
let path = basePath;
|
|
277
|
-
if (pathname.startsWith('/')) {
|
|
278
|
-
pathname = pathname.substring(1);
|
|
279
|
-
}
|
|
280
|
-
path += pathname;
|
|
281
|
-
protocol = protocol.replace(/\:/g, '');
|
|
282
|
-
return `${protocol}://${address}${portSuffix}${path}`;
|
|
283
|
-
}
|
|
284
|
-
function getDevServerClientUrl(devServerConfig, host, protocol) {
|
|
285
|
-
let address = devServerConfig.address;
|
|
286
|
-
let port = devServerConfig.port;
|
|
287
|
-
if (host) {
|
|
288
|
-
address = host;
|
|
289
|
-
port = null;
|
|
290
|
-
}
|
|
291
|
-
return getBrowserUrl(protocol !== null && protocol !== void 0 ? protocol : devServerConfig.protocol, address, port, devServerConfig.basePath, DEV_SERVER_URL);
|
|
292
|
-
}
|
|
293
|
-
function getContentType(filePath) {
|
|
294
|
-
const last = filePath.replace(/^.*[/\\]/, '').toLowerCase();
|
|
295
|
-
const ext = last.replace(/^.*\./, '').toLowerCase();
|
|
296
|
-
const hasPath = last.length < filePath.length;
|
|
297
|
-
const hasDot = ext.length < last.length - 1;
|
|
298
|
-
return ((hasDot || !hasPath) && contentTypes[ext]) || 'application/octet-stream';
|
|
299
|
-
}
|
|
300
|
-
function isHtmlFile(filePath) {
|
|
301
|
-
filePath = filePath.toLowerCase().trim();
|
|
302
|
-
return filePath.endsWith('.html') || filePath.endsWith('.htm');
|
|
303
|
-
}
|
|
304
|
-
function isCssFile(filePath) {
|
|
305
|
-
filePath = filePath.toLowerCase().trim();
|
|
306
|
-
return filePath.endsWith('.css');
|
|
307
|
-
}
|
|
308
|
-
const TXT_EXT = ['css', 'html', 'htm', 'js', 'json', 'svg', 'xml'];
|
|
309
|
-
function isSimpleText(filePath) {
|
|
310
|
-
const ext = filePath.toLowerCase().trim().split('.').pop();
|
|
311
|
-
return TXT_EXT.includes(ext);
|
|
312
|
-
}
|
|
313
|
-
function isExtensionLessPath(pathname) {
|
|
314
|
-
const parts = pathname.split('/');
|
|
315
|
-
const lastPart = parts[parts.length - 1];
|
|
316
|
-
return !lastPart.includes('.');
|
|
317
|
-
}
|
|
318
|
-
function isSsrStaticDataPath(pathname) {
|
|
319
|
-
const parts = pathname.split('/');
|
|
320
|
-
const fileName = parts[parts.length - 1].split('?')[0];
|
|
321
|
-
return fileName === 'page.state.json';
|
|
322
|
-
}
|
|
323
|
-
function getSsrStaticDataPath(req) {
|
|
324
|
-
const parts = req.url.href.split('/');
|
|
325
|
-
const fileName = parts[parts.length - 1];
|
|
326
|
-
const fileNameParts = fileName.split('?');
|
|
327
|
-
parts.pop();
|
|
328
|
-
let ssrPath = new URL(parts.join('/')).href;
|
|
329
|
-
if (!ssrPath.endsWith('/') && req.headers) {
|
|
330
|
-
const h = new Headers(req.headers);
|
|
331
|
-
if (h.get('referer').endsWith('/')) {
|
|
332
|
-
ssrPath += '/';
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
return {
|
|
336
|
-
ssrPath,
|
|
337
|
-
fileName: fileNameParts[0],
|
|
338
|
-
hasQueryString: typeof fileNameParts[1] === 'string' && fileNameParts[1].length > 0,
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
function isDevClient(pathname) {
|
|
342
|
-
return pathname.startsWith(DEV_SERVER_URL);
|
|
343
|
-
}
|
|
344
|
-
function isDevModule(pathname) {
|
|
345
|
-
return pathname.includes(DEV_MODULE_URL);
|
|
346
|
-
}
|
|
347
|
-
function isOpenInEditor(pathname) {
|
|
348
|
-
return pathname === OPEN_IN_EDITOR_URL;
|
|
349
|
-
}
|
|
350
|
-
function isInitialDevServerLoad(pathname) {
|
|
351
|
-
return pathname === DEV_SERVER_INIT_URL;
|
|
352
|
-
}
|
|
353
|
-
function isDevServerClient(pathname) {
|
|
354
|
-
return pathname === DEV_SERVER_URL;
|
|
355
|
-
}
|
|
356
|
-
function shouldCompress(devServerConfig, req) {
|
|
357
|
-
if (!devServerConfig.gzip) {
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
if (req.method !== 'GET') {
|
|
361
|
-
return false;
|
|
362
|
-
}
|
|
363
|
-
const acceptEncoding = req.headers && req.headers['accept-encoding'];
|
|
364
|
-
if (typeof acceptEncoding !== 'string') {
|
|
365
|
-
return false;
|
|
366
|
-
}
|
|
367
|
-
if (!acceptEncoding.includes('gzip')) {
|
|
368
|
-
return false;
|
|
369
|
-
}
|
|
370
|
-
return true;
|
|
259
|
+
function responseHeaders(headers, httpCache = false) {
|
|
260
|
+
headers = { ...DEFAULT_HEADERS, ...headers };
|
|
261
|
+
if (httpCache) {
|
|
262
|
+
headers['cache-control'] = 'max-age=3600';
|
|
263
|
+
delete headers['date'];
|
|
264
|
+
delete headers['expires'];
|
|
265
|
+
}
|
|
266
|
+
return headers;
|
|
267
|
+
}
|
|
268
|
+
const DEFAULT_HEADERS = {
|
|
269
|
+
'cache-control': 'no-cache, no-store, must-revalidate, max-age=0',
|
|
270
|
+
expires: '0',
|
|
271
|
+
date: 'Wed, 1 Jan 2000 00:00:00 GMT',
|
|
272
|
+
server: 'Rindo Dev Server ' + version,
|
|
273
|
+
'access-control-allow-origin': '*',
|
|
274
|
+
'access-control-expose-headers': '*',
|
|
275
|
+
};
|
|
276
|
+
function getBrowserUrl(protocol, address, port, basePath, pathname) {
|
|
277
|
+
address = address === `0.0.0.0` ? `localhost` : address;
|
|
278
|
+
const portSuffix = !port || port === 80 || port === 443 ? '' : ':' + port;
|
|
279
|
+
let path = basePath;
|
|
280
|
+
if (pathname.startsWith('/')) {
|
|
281
|
+
pathname = pathname.substring(1);
|
|
282
|
+
}
|
|
283
|
+
path += pathname;
|
|
284
|
+
protocol = protocol.replace(/\:/g, '');
|
|
285
|
+
return `${protocol}://${address}${portSuffix}${path}`;
|
|
286
|
+
}
|
|
287
|
+
function getDevServerClientUrl(devServerConfig, host, protocol) {
|
|
288
|
+
let address = devServerConfig.address;
|
|
289
|
+
let port = devServerConfig.port;
|
|
290
|
+
if (host) {
|
|
291
|
+
address = host;
|
|
292
|
+
port = null;
|
|
293
|
+
}
|
|
294
|
+
return getBrowserUrl(protocol !== null && protocol !== void 0 ? protocol : devServerConfig.protocol, address, port, devServerConfig.basePath, DEV_SERVER_URL);
|
|
295
|
+
}
|
|
296
|
+
function getContentType(filePath) {
|
|
297
|
+
const last = filePath.replace(/^.*[/\\]/, '').toLowerCase();
|
|
298
|
+
const ext = last.replace(/^.*\./, '').toLowerCase();
|
|
299
|
+
const hasPath = last.length < filePath.length;
|
|
300
|
+
const hasDot = ext.length < last.length - 1;
|
|
301
|
+
return ((hasDot || !hasPath) && contentTypes[ext]) || 'application/octet-stream';
|
|
302
|
+
}
|
|
303
|
+
function isHtmlFile(filePath) {
|
|
304
|
+
filePath = filePath.toLowerCase().trim();
|
|
305
|
+
return filePath.endsWith('.html') || filePath.endsWith('.htm');
|
|
306
|
+
}
|
|
307
|
+
function isCssFile(filePath) {
|
|
308
|
+
filePath = filePath.toLowerCase().trim();
|
|
309
|
+
return filePath.endsWith('.css');
|
|
310
|
+
}
|
|
311
|
+
const TXT_EXT = ['css', 'html', 'htm', 'js', 'json', 'svg', 'xml'];
|
|
312
|
+
function isSimpleText(filePath) {
|
|
313
|
+
const ext = filePath.toLowerCase().trim().split('.').pop();
|
|
314
|
+
return TXT_EXT.includes(ext);
|
|
315
|
+
}
|
|
316
|
+
function isExtensionLessPath(pathname) {
|
|
317
|
+
const parts = pathname.split('/');
|
|
318
|
+
const lastPart = parts[parts.length - 1];
|
|
319
|
+
return !lastPart.includes('.');
|
|
320
|
+
}
|
|
321
|
+
function isSsrStaticDataPath(pathname) {
|
|
322
|
+
const parts = pathname.split('/');
|
|
323
|
+
const fileName = parts[parts.length - 1].split('?')[0];
|
|
324
|
+
return fileName === 'page.state.json';
|
|
325
|
+
}
|
|
326
|
+
function getSsrStaticDataPath(req) {
|
|
327
|
+
const parts = req.url.href.split('/');
|
|
328
|
+
const fileName = parts[parts.length - 1];
|
|
329
|
+
const fileNameParts = fileName.split('?');
|
|
330
|
+
parts.pop();
|
|
331
|
+
let ssrPath = new URL(parts.join('/')).href;
|
|
332
|
+
if (!ssrPath.endsWith('/') && req.headers) {
|
|
333
|
+
const h = new Headers(req.headers);
|
|
334
|
+
if (h.get('referer').endsWith('/')) {
|
|
335
|
+
ssrPath += '/';
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
ssrPath,
|
|
340
|
+
fileName: fileNameParts[0],
|
|
341
|
+
hasQueryString: typeof fileNameParts[1] === 'string' && fileNameParts[1].length > 0,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
function isDevClient(pathname) {
|
|
345
|
+
return pathname.startsWith(DEV_SERVER_URL);
|
|
346
|
+
}
|
|
347
|
+
function isDevModule(pathname) {
|
|
348
|
+
return pathname.includes(DEV_MODULE_URL);
|
|
349
|
+
}
|
|
350
|
+
function isOpenInEditor(pathname) {
|
|
351
|
+
return pathname === OPEN_IN_EDITOR_URL;
|
|
352
|
+
}
|
|
353
|
+
function isInitialDevServerLoad(pathname) {
|
|
354
|
+
return pathname === DEV_SERVER_INIT_URL;
|
|
355
|
+
}
|
|
356
|
+
function isDevServerClient(pathname) {
|
|
357
|
+
return pathname === DEV_SERVER_URL;
|
|
358
|
+
}
|
|
359
|
+
function shouldCompress(devServerConfig, req) {
|
|
360
|
+
if (!devServerConfig.gzip) {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
if (req.method !== 'GET') {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
const acceptEncoding = req.headers && req.headers['accept-encoding'];
|
|
367
|
+
if (typeof acceptEncoding !== 'string') {
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
if (!acceptEncoding.includes('gzip')) {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
return true;
|
|
371
374
|
}
|
|
372
375
|
|
|
373
376
|
function createCommonjsModule(fn, basedir, module) {
|
|
@@ -473,6 +476,25 @@ const localXdgOpenPath = path__default['default'].join(__dirname, 'xdg-open');
|
|
|
473
476
|
|
|
474
477
|
const {platform, arch} = process;
|
|
475
478
|
|
|
479
|
+
// Podman detection
|
|
480
|
+
const hasContainerEnv = () => {
|
|
481
|
+
try {
|
|
482
|
+
fs.statSync('/run/.containerenv');
|
|
483
|
+
return true;
|
|
484
|
+
} catch {
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
let cachedResult;
|
|
490
|
+
function isInsideContainer() {
|
|
491
|
+
if (cachedResult === undefined) {
|
|
492
|
+
cachedResult = hasContainerEnv() || isDocker_1();
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return cachedResult;
|
|
496
|
+
}
|
|
497
|
+
|
|
476
498
|
/**
|
|
477
499
|
Get the mount point for fixed drives in WSL.
|
|
478
500
|
|
|
@@ -583,7 +605,7 @@ const baseOpen = async options => {
|
|
|
583
605
|
if (app) {
|
|
584
606
|
cliArguments.push('-a', app);
|
|
585
607
|
}
|
|
586
|
-
} else if (platform === 'win32' || (isWsl_1 && !
|
|
608
|
+
} else if (platform === 'win32' || (isWsl_1 && !isInsideContainer() && !app)) {
|
|
587
609
|
const mountPoint = await getWslDrivesMountPoint();
|
|
588
610
|
|
|
589
611
|
command = isWsl_1 ?
|
|
@@ -672,7 +694,7 @@ const baseOpen = async options => {
|
|
|
672
694
|
subprocess.once('error', reject);
|
|
673
695
|
|
|
674
696
|
subprocess.once('close', exitCode => {
|
|
675
|
-
if (options.allowNonzeroExitCode && exitCode > 0) {
|
|
697
|
+
if (!options.allowNonzeroExitCode && exitCode > 0) {
|
|
676
698
|
reject(new Error(`Exited with code ${exitCode}`));
|
|
677
699
|
return;
|
|
678
700
|
}
|
|
@@ -777,682 +799,682 @@ open.openApp = openApp;
|
|
|
777
799
|
|
|
778
800
|
var open_1 = open;
|
|
779
801
|
|
|
780
|
-
async function openInBrowser(opts) {
|
|
781
|
-
// await open(opts.url, { app: ['google chrome', '--auto-open-devtools-for-tabs'] });
|
|
782
|
-
await open_1(opts.url);
|
|
802
|
+
async function openInBrowser(opts) {
|
|
803
|
+
// await open(opts.url, { app: ['google chrome', '--auto-open-devtools-for-tabs'] });
|
|
804
|
+
await open_1(opts.url);
|
|
783
805
|
}
|
|
784
806
|
|
|
785
|
-
function createServerContext(sys, sendMsg, devServerConfig, buildResultsResolves, compilerRequestResolves) {
|
|
786
|
-
const logRequest = (req, status) => {
|
|
787
|
-
if (devServerConfig) {
|
|
788
|
-
sendMsg({
|
|
789
|
-
requestLog: {
|
|
790
|
-
method: req.method || '?',
|
|
791
|
-
url: req.pathname || '?',
|
|
792
|
-
status,
|
|
793
|
-
},
|
|
794
|
-
});
|
|
795
|
-
}
|
|
796
|
-
};
|
|
797
|
-
const serve500 = (req, res, error, xSource) => {
|
|
798
|
-
try {
|
|
799
|
-
res.writeHead(500, responseHeaders({
|
|
800
|
-
'content-type': 'text/plain; charset=utf-8',
|
|
801
|
-
'x-source': xSource,
|
|
802
|
-
}));
|
|
803
|
-
res.write(util__default['default'].inspect(error));
|
|
804
|
-
res.end();
|
|
805
|
-
logRequest(req, 500);
|
|
806
|
-
}
|
|
807
|
-
catch (e) {
|
|
808
|
-
sendMsg({ error: { message: 'serve500: ' + e } });
|
|
809
|
-
}
|
|
810
|
-
};
|
|
811
|
-
const serve404 = (req, res, xSource, content = null) => {
|
|
812
|
-
try {
|
|
813
|
-
if (req.pathname === '/favicon.ico') {
|
|
814
|
-
const defaultFavicon = path__default['default'].join(devServerConfig.devServerDir, 'static', 'favicon.ico');
|
|
815
|
-
res.writeHead(200, responseHeaders({
|
|
816
|
-
'content-type': 'image/x-icon',
|
|
817
|
-
'x-source': `favicon: ${xSource}`,
|
|
818
|
-
}));
|
|
819
|
-
const rs = fs__default$1['default'].createReadStream(defaultFavicon);
|
|
820
|
-
rs.on('error', (err) => {
|
|
821
|
-
res.writeHead(404, responseHeaders({
|
|
822
|
-
'content-type': 'text/plain; charset=utf-8',
|
|
823
|
-
'x-source': `createReadStream error: ${err}, ${xSource}`,
|
|
824
|
-
}));
|
|
825
|
-
res.write(util__default['default'].inspect(err));
|
|
826
|
-
res.end();
|
|
827
|
-
});
|
|
828
|
-
rs.pipe(res);
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
if (content == null) {
|
|
832
|
-
content = ['404 File Not Found', 'Url: ' + req.pathname, 'File: ' + req.filePath].join('\n');
|
|
833
|
-
}
|
|
834
|
-
res.writeHead(404, responseHeaders({
|
|
835
|
-
'content-type': 'text/plain; charset=utf-8',
|
|
836
|
-
'x-source': xSource,
|
|
837
|
-
}));
|
|
838
|
-
res.write(content);
|
|
839
|
-
res.end();
|
|
840
|
-
logRequest(req, 400);
|
|
841
|
-
}
|
|
842
|
-
catch (e) {
|
|
843
|
-
serve500(req, res, e, xSource);
|
|
844
|
-
}
|
|
845
|
-
};
|
|
846
|
-
const serve302 = (req, res, pathname = null) => {
|
|
847
|
-
logRequest(req, 302);
|
|
848
|
-
res.writeHead(302, { location: pathname || devServerConfig.basePath || '/' });
|
|
849
|
-
res.end();
|
|
850
|
-
};
|
|
851
|
-
const getBuildResults = () => new Promise((resolve, reject) => {
|
|
852
|
-
if (serverCtx.isServerListening) {
|
|
853
|
-
buildResultsResolves.push({ resolve, reject });
|
|
854
|
-
sendMsg({ requestBuildResults: true });
|
|
855
|
-
}
|
|
856
|
-
else {
|
|
857
|
-
reject('dev server closed');
|
|
858
|
-
}
|
|
859
|
-
});
|
|
860
|
-
const getCompilerRequest = (compilerRequestPath) => new Promise((resolve, reject) => {
|
|
861
|
-
if (serverCtx.isServerListening) {
|
|
862
|
-
compilerRequestResolves.push({
|
|
863
|
-
path: compilerRequestPath,
|
|
864
|
-
resolve,
|
|
865
|
-
reject,
|
|
866
|
-
});
|
|
867
|
-
sendMsg({ compilerRequestPath });
|
|
868
|
-
}
|
|
869
|
-
else {
|
|
870
|
-
reject('dev server closed');
|
|
871
|
-
}
|
|
872
|
-
});
|
|
873
|
-
const serverCtx = {
|
|
874
|
-
connectorHtml: null,
|
|
875
|
-
dirTemplate: null,
|
|
876
|
-
getBuildResults,
|
|
877
|
-
getCompilerRequest,
|
|
878
|
-
isServerListening: false,
|
|
879
|
-
logRequest,
|
|
880
|
-
prerenderConfig: null,
|
|
881
|
-
serve302,
|
|
882
|
-
serve404,
|
|
883
|
-
serve500,
|
|
884
|
-
sys,
|
|
885
|
-
};
|
|
886
|
-
return serverCtx;
|
|
807
|
+
function createServerContext(sys, sendMsg, devServerConfig, buildResultsResolves, compilerRequestResolves) {
|
|
808
|
+
const logRequest = (req, status) => {
|
|
809
|
+
if (devServerConfig) {
|
|
810
|
+
sendMsg({
|
|
811
|
+
requestLog: {
|
|
812
|
+
method: req.method || '?',
|
|
813
|
+
url: req.pathname || '?',
|
|
814
|
+
status,
|
|
815
|
+
},
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
const serve500 = (req, res, error, xSource) => {
|
|
820
|
+
try {
|
|
821
|
+
res.writeHead(500, responseHeaders({
|
|
822
|
+
'content-type': 'text/plain; charset=utf-8',
|
|
823
|
+
'x-source': xSource,
|
|
824
|
+
}));
|
|
825
|
+
res.write(util__default['default'].inspect(error));
|
|
826
|
+
res.end();
|
|
827
|
+
logRequest(req, 500);
|
|
828
|
+
}
|
|
829
|
+
catch (e) {
|
|
830
|
+
sendMsg({ error: { message: 'serve500: ' + e } });
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
const serve404 = (req, res, xSource, content = null) => {
|
|
834
|
+
try {
|
|
835
|
+
if (req.pathname === '/favicon.ico') {
|
|
836
|
+
const defaultFavicon = path__default['default'].join(devServerConfig.devServerDir, 'static', 'favicon.ico');
|
|
837
|
+
res.writeHead(200, responseHeaders({
|
|
838
|
+
'content-type': 'image/x-icon',
|
|
839
|
+
'x-source': `favicon: ${xSource}`,
|
|
840
|
+
}));
|
|
841
|
+
const rs = fs__default$1['default'].createReadStream(defaultFavicon);
|
|
842
|
+
rs.on('error', (err) => {
|
|
843
|
+
res.writeHead(404, responseHeaders({
|
|
844
|
+
'content-type': 'text/plain; charset=utf-8',
|
|
845
|
+
'x-source': `createReadStream error: ${err}, ${xSource}`,
|
|
846
|
+
}));
|
|
847
|
+
res.write(util__default['default'].inspect(err));
|
|
848
|
+
res.end();
|
|
849
|
+
});
|
|
850
|
+
rs.pipe(res);
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
if (content == null) {
|
|
854
|
+
content = ['404 File Not Found', 'Url: ' + req.pathname, 'File: ' + req.filePath].join('\n');
|
|
855
|
+
}
|
|
856
|
+
res.writeHead(404, responseHeaders({
|
|
857
|
+
'content-type': 'text/plain; charset=utf-8',
|
|
858
|
+
'x-source': xSource,
|
|
859
|
+
}));
|
|
860
|
+
res.write(content);
|
|
861
|
+
res.end();
|
|
862
|
+
logRequest(req, 400);
|
|
863
|
+
}
|
|
864
|
+
catch (e) {
|
|
865
|
+
serve500(req, res, e, xSource);
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
const serve302 = (req, res, pathname = null) => {
|
|
869
|
+
logRequest(req, 302);
|
|
870
|
+
res.writeHead(302, { location: pathname || devServerConfig.basePath || '/' });
|
|
871
|
+
res.end();
|
|
872
|
+
};
|
|
873
|
+
const getBuildResults = () => new Promise((resolve, reject) => {
|
|
874
|
+
if (serverCtx.isServerListening) {
|
|
875
|
+
buildResultsResolves.push({ resolve, reject });
|
|
876
|
+
sendMsg({ requestBuildResults: true });
|
|
877
|
+
}
|
|
878
|
+
else {
|
|
879
|
+
reject('dev server closed');
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
const getCompilerRequest = (compilerRequestPath) => new Promise((resolve, reject) => {
|
|
883
|
+
if (serverCtx.isServerListening) {
|
|
884
|
+
compilerRequestResolves.push({
|
|
885
|
+
path: compilerRequestPath,
|
|
886
|
+
resolve,
|
|
887
|
+
reject,
|
|
888
|
+
});
|
|
889
|
+
sendMsg({ compilerRequestPath });
|
|
890
|
+
}
|
|
891
|
+
else {
|
|
892
|
+
reject('dev server closed');
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
const serverCtx = {
|
|
896
|
+
connectorHtml: null,
|
|
897
|
+
dirTemplate: null,
|
|
898
|
+
getBuildResults,
|
|
899
|
+
getCompilerRequest,
|
|
900
|
+
isServerListening: false,
|
|
901
|
+
logRequest,
|
|
902
|
+
prerenderConfig: null,
|
|
903
|
+
serve302,
|
|
904
|
+
serve404,
|
|
905
|
+
serve500,
|
|
906
|
+
sys,
|
|
907
|
+
};
|
|
908
|
+
return serverCtx;
|
|
887
909
|
}
|
|
888
910
|
|
|
889
|
-
async function serveOpenInEditor(serverCtx, req, res) {
|
|
890
|
-
let status = 200;
|
|
891
|
-
const data = {};
|
|
892
|
-
try {
|
|
893
|
-
const editors = await getEditors();
|
|
894
|
-
if (editors.length > 0) {
|
|
895
|
-
await parseData(editors, serverCtx.sys, req, data);
|
|
896
|
-
await openDataInEditor(data);
|
|
897
|
-
}
|
|
898
|
-
else {
|
|
899
|
-
data.error = `no editors available`;
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
catch (e) {
|
|
903
|
-
data.error = e + '';
|
|
904
|
-
status = 500;
|
|
905
|
-
}
|
|
906
|
-
serverCtx.logRequest(req, status);
|
|
907
|
-
res.writeHead(status, responseHeaders({
|
|
908
|
-
'content-type': 'application/json; charset=utf-8',
|
|
909
|
-
}));
|
|
910
|
-
res.write(JSON.stringify(data, null, 2));
|
|
911
|
-
res.end();
|
|
912
|
-
}
|
|
913
|
-
async function parseData(editors, sys, req, data) {
|
|
914
|
-
const qs = req.searchParams;
|
|
915
|
-
if (!qs.has('file')) {
|
|
916
|
-
data.error = `missing file`;
|
|
917
|
-
return;
|
|
918
|
-
}
|
|
919
|
-
data.file = qs.get('file');
|
|
920
|
-
if (qs.has('line') && !isNaN(qs.get('line'))) {
|
|
921
|
-
data.line = parseInt(qs.get('line'), 10);
|
|
922
|
-
}
|
|
923
|
-
if (typeof data.line !== 'number' || data.line < 1) {
|
|
924
|
-
data.line = 1;
|
|
925
|
-
}
|
|
926
|
-
if (qs.has('column') && !isNaN(qs.get('column'))) {
|
|
927
|
-
data.column = parseInt(qs.get('column'), 10);
|
|
928
|
-
}
|
|
929
|
-
if (typeof data.column !== 'number' || data.column < 1) {
|
|
930
|
-
data.column = 1;
|
|
931
|
-
}
|
|
932
|
-
let editor = qs.get('editor');
|
|
933
|
-
if (typeof editor === 'string') {
|
|
934
|
-
editor = editor.trim().toLowerCase();
|
|
935
|
-
if (editors.some((e) => e.id === editor)) {
|
|
936
|
-
data.editor = editor;
|
|
937
|
-
}
|
|
938
|
-
else {
|
|
939
|
-
data.error = `invalid editor: ${editor}`;
|
|
940
|
-
return;
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
else {
|
|
944
|
-
data.editor = editors[0].id;
|
|
945
|
-
}
|
|
946
|
-
const stat = await sys.stat(data.file);
|
|
947
|
-
data.exists = stat.isFile;
|
|
948
|
-
}
|
|
949
|
-
async function openDataInEditor(data) {
|
|
950
|
-
if (!data.exists || data.error) {
|
|
951
|
-
return;
|
|
952
|
-
}
|
|
953
|
-
try {
|
|
954
|
-
const opts = {
|
|
955
|
-
editor: data.editor,
|
|
956
|
-
};
|
|
957
|
-
const editor = openInEditorApi__default['default'].configure(opts, (err) => (data.error = err + ''));
|
|
958
|
-
if (data.error) {
|
|
959
|
-
return;
|
|
960
|
-
}
|
|
961
|
-
data.open = `${data.file}:${data.line}:${data.column}`;
|
|
962
|
-
await editor.open(data.open);
|
|
963
|
-
}
|
|
964
|
-
catch (e) {
|
|
965
|
-
data.error = e + '';
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
let editors = null;
|
|
969
|
-
function getEditors() {
|
|
970
|
-
if (!editors) {
|
|
971
|
-
editors = new Promise(async (resolve) => {
|
|
972
|
-
const editors = [];
|
|
973
|
-
try {
|
|
974
|
-
await Promise.all(Object.keys(openInEditorApi__default['default'].editors).map(async (editorId) => {
|
|
975
|
-
const isSupported = await isEditorSupported(editorId);
|
|
976
|
-
editors.push({
|
|
977
|
-
id: editorId,
|
|
978
|
-
priority: EDITOR_PRIORITY[editorId],
|
|
979
|
-
supported: isSupported,
|
|
980
|
-
});
|
|
981
|
-
}));
|
|
982
|
-
}
|
|
983
|
-
catch (e) { }
|
|
984
|
-
resolve(editors
|
|
985
|
-
.filter((e) => e.supported)
|
|
986
|
-
.sort((a, b) => {
|
|
987
|
-
if (a.priority < b.priority)
|
|
988
|
-
return -1;
|
|
989
|
-
if (a.priority > b.priority)
|
|
990
|
-
return 1;
|
|
991
|
-
return 0;
|
|
992
|
-
})
|
|
993
|
-
.map((e) => {
|
|
994
|
-
return {
|
|
995
|
-
id: e.id,
|
|
996
|
-
name: EDITORS[e.id],
|
|
997
|
-
};
|
|
998
|
-
}));
|
|
999
|
-
});
|
|
1000
|
-
}
|
|
1001
|
-
return editors;
|
|
1002
|
-
}
|
|
1003
|
-
async function isEditorSupported(editorId) {
|
|
1004
|
-
let isSupported = false;
|
|
1005
|
-
try {
|
|
1006
|
-
await openInEditorApi__default['default'].editors[editorId].detect();
|
|
1007
|
-
isSupported = true;
|
|
1008
|
-
}
|
|
1009
|
-
catch (e) { }
|
|
1010
|
-
return isSupported;
|
|
1011
|
-
}
|
|
1012
|
-
const EDITORS = {
|
|
1013
|
-
atom: 'Atom',
|
|
1014
|
-
code: 'Code',
|
|
1015
|
-
emacs: 'Emacs',
|
|
1016
|
-
idea14ce: 'IDEA 14 Community Edition',
|
|
1017
|
-
phpstorm: 'PhpStorm',
|
|
1018
|
-
sublime: 'Sublime',
|
|
1019
|
-
webstorm: 'WebStorm',
|
|
1020
|
-
vim: 'Vim',
|
|
1021
|
-
visualstudio: 'Visual Studio',
|
|
1022
|
-
};
|
|
1023
|
-
const EDITOR_PRIORITY = {
|
|
1024
|
-
code: 1,
|
|
1025
|
-
atom: 2,
|
|
1026
|
-
sublime: 3,
|
|
1027
|
-
visualstudio: 4,
|
|
1028
|
-
idea14ce: 5,
|
|
1029
|
-
webstorm: 6,
|
|
1030
|
-
phpstorm: 7,
|
|
1031
|
-
vim: 8,
|
|
1032
|
-
emacs: 9,
|
|
911
|
+
async function serveOpenInEditor(serverCtx, req, res) {
|
|
912
|
+
let status = 200;
|
|
913
|
+
const data = {};
|
|
914
|
+
try {
|
|
915
|
+
const editors = await getEditors();
|
|
916
|
+
if (editors.length > 0) {
|
|
917
|
+
await parseData(editors, serverCtx.sys, req, data);
|
|
918
|
+
await openDataInEditor(data);
|
|
919
|
+
}
|
|
920
|
+
else {
|
|
921
|
+
data.error = `no editors available`;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
catch (e) {
|
|
925
|
+
data.error = e + '';
|
|
926
|
+
status = 500;
|
|
927
|
+
}
|
|
928
|
+
serverCtx.logRequest(req, status);
|
|
929
|
+
res.writeHead(status, responseHeaders({
|
|
930
|
+
'content-type': 'application/json; charset=utf-8',
|
|
931
|
+
}));
|
|
932
|
+
res.write(JSON.stringify(data, null, 2));
|
|
933
|
+
res.end();
|
|
934
|
+
}
|
|
935
|
+
async function parseData(editors, sys, req, data) {
|
|
936
|
+
const qs = req.searchParams;
|
|
937
|
+
if (!qs.has('file')) {
|
|
938
|
+
data.error = `missing file`;
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
data.file = qs.get('file');
|
|
942
|
+
if (qs.has('line') && !isNaN(qs.get('line'))) {
|
|
943
|
+
data.line = parseInt(qs.get('line'), 10);
|
|
944
|
+
}
|
|
945
|
+
if (typeof data.line !== 'number' || data.line < 1) {
|
|
946
|
+
data.line = 1;
|
|
947
|
+
}
|
|
948
|
+
if (qs.has('column') && !isNaN(qs.get('column'))) {
|
|
949
|
+
data.column = parseInt(qs.get('column'), 10);
|
|
950
|
+
}
|
|
951
|
+
if (typeof data.column !== 'number' || data.column < 1) {
|
|
952
|
+
data.column = 1;
|
|
953
|
+
}
|
|
954
|
+
let editor = qs.get('editor');
|
|
955
|
+
if (typeof editor === 'string') {
|
|
956
|
+
editor = editor.trim().toLowerCase();
|
|
957
|
+
if (editors.some((e) => e.id === editor)) {
|
|
958
|
+
data.editor = editor;
|
|
959
|
+
}
|
|
960
|
+
else {
|
|
961
|
+
data.error = `invalid editor: ${editor}`;
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
else {
|
|
966
|
+
data.editor = editors[0].id;
|
|
967
|
+
}
|
|
968
|
+
const stat = await sys.stat(data.file);
|
|
969
|
+
data.exists = stat.isFile;
|
|
970
|
+
}
|
|
971
|
+
async function openDataInEditor(data) {
|
|
972
|
+
if (!data.exists || data.error) {
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
try {
|
|
976
|
+
const opts = {
|
|
977
|
+
editor: data.editor,
|
|
978
|
+
};
|
|
979
|
+
const editor = openInEditorApi__default['default'].configure(opts, (err) => (data.error = err + ''));
|
|
980
|
+
if (data.error) {
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
data.open = `${data.file}:${data.line}:${data.column}`;
|
|
984
|
+
await editor.open(data.open);
|
|
985
|
+
}
|
|
986
|
+
catch (e) {
|
|
987
|
+
data.error = e + '';
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
let editors = null;
|
|
991
|
+
function getEditors() {
|
|
992
|
+
if (!editors) {
|
|
993
|
+
editors = new Promise(async (resolve) => {
|
|
994
|
+
const editors = [];
|
|
995
|
+
try {
|
|
996
|
+
await Promise.all(Object.keys(openInEditorApi__default['default'].editors).map(async (editorId) => {
|
|
997
|
+
const isSupported = await isEditorSupported(editorId);
|
|
998
|
+
editors.push({
|
|
999
|
+
id: editorId,
|
|
1000
|
+
priority: EDITOR_PRIORITY[editorId],
|
|
1001
|
+
supported: isSupported,
|
|
1002
|
+
});
|
|
1003
|
+
}));
|
|
1004
|
+
}
|
|
1005
|
+
catch (e) { }
|
|
1006
|
+
resolve(editors
|
|
1007
|
+
.filter((e) => e.supported)
|
|
1008
|
+
.sort((a, b) => {
|
|
1009
|
+
if (a.priority < b.priority)
|
|
1010
|
+
return -1;
|
|
1011
|
+
if (a.priority > b.priority)
|
|
1012
|
+
return 1;
|
|
1013
|
+
return 0;
|
|
1014
|
+
})
|
|
1015
|
+
.map((e) => {
|
|
1016
|
+
return {
|
|
1017
|
+
id: e.id,
|
|
1018
|
+
name: EDITORS[e.id],
|
|
1019
|
+
};
|
|
1020
|
+
}));
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
return editors;
|
|
1024
|
+
}
|
|
1025
|
+
async function isEditorSupported(editorId) {
|
|
1026
|
+
let isSupported = false;
|
|
1027
|
+
try {
|
|
1028
|
+
await openInEditorApi__default['default'].editors[editorId].detect();
|
|
1029
|
+
isSupported = true;
|
|
1030
|
+
}
|
|
1031
|
+
catch (e) { }
|
|
1032
|
+
return isSupported;
|
|
1033
|
+
}
|
|
1034
|
+
const EDITORS = {
|
|
1035
|
+
atom: 'Atom',
|
|
1036
|
+
code: 'Code',
|
|
1037
|
+
emacs: 'Emacs',
|
|
1038
|
+
idea14ce: 'IDEA 14 Community Edition',
|
|
1039
|
+
phpstorm: 'PhpStorm',
|
|
1040
|
+
sublime: 'Sublime',
|
|
1041
|
+
webstorm: 'WebStorm',
|
|
1042
|
+
vim: 'Vim',
|
|
1043
|
+
visualstudio: 'Visual Studio',
|
|
1044
|
+
};
|
|
1045
|
+
const EDITOR_PRIORITY = {
|
|
1046
|
+
code: 1,
|
|
1047
|
+
atom: 2,
|
|
1048
|
+
sublime: 3,
|
|
1049
|
+
visualstudio: 4,
|
|
1050
|
+
idea14ce: 5,
|
|
1051
|
+
webstorm: 6,
|
|
1052
|
+
phpstorm: 7,
|
|
1053
|
+
vim: 8,
|
|
1054
|
+
emacs: 9,
|
|
1033
1055
|
};
|
|
1034
1056
|
|
|
1035
|
-
async function serveFile(devServerConfig, serverCtx, req, res) {
|
|
1036
|
-
try {
|
|
1037
|
-
if (isSimpleText(req.filePath)) {
|
|
1038
|
-
// easy text file, use the internal cache
|
|
1039
|
-
let content = await serverCtx.sys.readFile(req.filePath, 'utf8');
|
|
1040
|
-
if (devServerConfig.websocket && isHtmlFile(req.filePath) && !isDevServerClient(req.pathname)) {
|
|
1041
|
-
// auto inject our dev server script
|
|
1042
|
-
content = appendDevServerClientScript(devServerConfig, req, content);
|
|
1043
|
-
}
|
|
1044
|
-
else if (isCssFile(req.filePath)) {
|
|
1045
|
-
content = updateStyleUrls(req.url, content);
|
|
1046
|
-
}
|
|
1047
|
-
if (shouldCompress(devServerConfig, req)) {
|
|
1048
|
-
// let's gzip this well known web dev text file
|
|
1049
|
-
res.writeHead(200, responseHeaders({
|
|
1050
|
-
'content-type': getContentType(req.filePath) + '; charset=utf-8',
|
|
1051
|
-
'content-encoding': 'gzip',
|
|
1052
|
-
vary: 'Accept-Encoding',
|
|
1053
|
-
}));
|
|
1054
|
-
zlib__namespace.gzip(content, { level: 9 }, (_, data) => {
|
|
1055
|
-
res.end(data);
|
|
1056
|
-
});
|
|
1057
|
-
}
|
|
1058
|
-
else {
|
|
1059
|
-
// let's not gzip this file
|
|
1060
|
-
res.writeHead(200, responseHeaders({
|
|
1061
|
-
'content-type': getContentType(req.filePath) + '; charset=utf-8',
|
|
1062
|
-
'content-length': buffer.Buffer.byteLength(content, 'utf8'),
|
|
1063
|
-
}));
|
|
1064
|
-
res.write(content);
|
|
1065
|
-
res.end();
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
else {
|
|
1069
|
-
// non-well-known text file or other file, probably best we use a stream
|
|
1070
|
-
// but don't bother trying to gzip this file for the dev server
|
|
1071
|
-
res.writeHead(200, responseHeaders({
|
|
1072
|
-
'content-type': getContentType(req.filePath),
|
|
1073
|
-
'content-length': req.stats.size,
|
|
1074
|
-
}));
|
|
1075
|
-
fs__default$1['default'].createReadStream(req.filePath).pipe(res);
|
|
1076
|
-
}
|
|
1077
|
-
serverCtx.logRequest(req, 200);
|
|
1078
|
-
}
|
|
1079
|
-
catch (e) {
|
|
1080
|
-
serverCtx.serve500(req, res, e, 'serveFile');
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
function updateStyleUrls(url, oldCss) {
|
|
1084
|
-
const versionId = url.searchParams.get('s-hmr');
|
|
1085
|
-
const hmrUrls = url.searchParams.get('s-hmr-urls');
|
|
1086
|
-
if (versionId && hmrUrls) {
|
|
1087
|
-
hmrUrls.split(',').forEach((hmrUrl) => {
|
|
1088
|
-
urlVersionIds.set(hmrUrl, versionId);
|
|
1089
|
-
});
|
|
1090
|
-
}
|
|
1091
|
-
const reg = /url\((['"]?)(.*)\1\)/gi;
|
|
1092
|
-
let result;
|
|
1093
|
-
let newCss = oldCss;
|
|
1094
|
-
while ((result = reg.exec(oldCss)) !== null) {
|
|
1095
|
-
const oldUrl = result[2];
|
|
1096
|
-
const parsedUrl = new URL(oldUrl, url);
|
|
1097
|
-
const fileName = path__default['default'].basename(parsedUrl.pathname);
|
|
1098
|
-
const versionId = urlVersionIds.get(fileName);
|
|
1099
|
-
if (!versionId) {
|
|
1100
|
-
continue;
|
|
1101
|
-
}
|
|
1102
|
-
parsedUrl.searchParams.set('s-hmr', versionId);
|
|
1103
|
-
newCss = newCss.replace(oldUrl, parsedUrl.pathname);
|
|
1104
|
-
}
|
|
1105
|
-
return newCss;
|
|
1106
|
-
}
|
|
1107
|
-
const urlVersionIds = new Map();
|
|
1108
|
-
function appendDevServerClientScript(devServerConfig, req, content) {
|
|
1109
|
-
var _a, _b, _c;
|
|
1110
|
-
const devServerClientUrl = getDevServerClientUrl(devServerConfig, (_b = (_a = req.headers) === null || _a === void 0 ? void 0 : _a['x-forwarded-host']) !== null && _b !== void 0 ? _b : req.host, (_c = req.headers) === null || _c === void 0 ? void 0 : _c['x-forwarded-proto']);
|
|
1111
|
-
const iframe = `<iframe title="Rindo Dev Server Connector ${version} ⚡" src="${devServerClientUrl}" style="display:block;width:0;height:0;border:0;visibility:hidden" aria-hidden="true"></iframe>`;
|
|
1112
|
-
return appendDevServerClientIframe(content, iframe);
|
|
1113
|
-
}
|
|
1114
|
-
function appendDevServerClientIframe(content, iframe) {
|
|
1115
|
-
if (content.includes('</body>')) {
|
|
1116
|
-
return content.replace('</body>', `${iframe}</body>`);
|
|
1117
|
-
}
|
|
1118
|
-
if (content.includes('</html>')) {
|
|
1119
|
-
return content.replace('</html>', `${iframe}</html>`);
|
|
1120
|
-
}
|
|
1121
|
-
return `${content}${iframe}`;
|
|
1057
|
+
async function serveFile(devServerConfig, serverCtx, req, res) {
|
|
1058
|
+
try {
|
|
1059
|
+
if (isSimpleText(req.filePath)) {
|
|
1060
|
+
// easy text file, use the internal cache
|
|
1061
|
+
let content = await serverCtx.sys.readFile(req.filePath, 'utf8');
|
|
1062
|
+
if (devServerConfig.websocket && isHtmlFile(req.filePath) && !isDevServerClient(req.pathname)) {
|
|
1063
|
+
// auto inject our dev server script
|
|
1064
|
+
content = appendDevServerClientScript(devServerConfig, req, content);
|
|
1065
|
+
}
|
|
1066
|
+
else if (isCssFile(req.filePath)) {
|
|
1067
|
+
content = updateStyleUrls(req.url, content);
|
|
1068
|
+
}
|
|
1069
|
+
if (shouldCompress(devServerConfig, req)) {
|
|
1070
|
+
// let's gzip this well known web dev text file
|
|
1071
|
+
res.writeHead(200, responseHeaders({
|
|
1072
|
+
'content-type': getContentType(req.filePath) + '; charset=utf-8',
|
|
1073
|
+
'content-encoding': 'gzip',
|
|
1074
|
+
vary: 'Accept-Encoding',
|
|
1075
|
+
}));
|
|
1076
|
+
zlib__namespace.gzip(content, { level: 9 }, (_, data) => {
|
|
1077
|
+
res.end(data);
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
else {
|
|
1081
|
+
// let's not gzip this file
|
|
1082
|
+
res.writeHead(200, responseHeaders({
|
|
1083
|
+
'content-type': getContentType(req.filePath) + '; charset=utf-8',
|
|
1084
|
+
'content-length': buffer.Buffer.byteLength(content, 'utf8'),
|
|
1085
|
+
}));
|
|
1086
|
+
res.write(content);
|
|
1087
|
+
res.end();
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
else {
|
|
1091
|
+
// non-well-known text file or other file, probably best we use a stream
|
|
1092
|
+
// but don't bother trying to gzip this file for the dev server
|
|
1093
|
+
res.writeHead(200, responseHeaders({
|
|
1094
|
+
'content-type': getContentType(req.filePath),
|
|
1095
|
+
'content-length': req.stats.size,
|
|
1096
|
+
}));
|
|
1097
|
+
fs__default$1['default'].createReadStream(req.filePath).pipe(res);
|
|
1098
|
+
}
|
|
1099
|
+
serverCtx.logRequest(req, 200);
|
|
1100
|
+
}
|
|
1101
|
+
catch (e) {
|
|
1102
|
+
serverCtx.serve500(req, res, e, 'serveFile');
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
function updateStyleUrls(url, oldCss) {
|
|
1106
|
+
const versionId = url.searchParams.get('s-hmr');
|
|
1107
|
+
const hmrUrls = url.searchParams.get('s-hmr-urls');
|
|
1108
|
+
if (versionId && hmrUrls) {
|
|
1109
|
+
hmrUrls.split(',').forEach((hmrUrl) => {
|
|
1110
|
+
urlVersionIds.set(hmrUrl, versionId);
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
const reg = /url\((['"]?)(.*)\1\)/gi;
|
|
1114
|
+
let result;
|
|
1115
|
+
let newCss = oldCss;
|
|
1116
|
+
while ((result = reg.exec(oldCss)) !== null) {
|
|
1117
|
+
const oldUrl = result[2];
|
|
1118
|
+
const parsedUrl = new URL(oldUrl, url);
|
|
1119
|
+
const fileName = path__default['default'].basename(parsedUrl.pathname);
|
|
1120
|
+
const versionId = urlVersionIds.get(fileName);
|
|
1121
|
+
if (!versionId) {
|
|
1122
|
+
continue;
|
|
1123
|
+
}
|
|
1124
|
+
parsedUrl.searchParams.set('s-hmr', versionId);
|
|
1125
|
+
newCss = newCss.replace(oldUrl, parsedUrl.pathname);
|
|
1126
|
+
}
|
|
1127
|
+
return newCss;
|
|
1128
|
+
}
|
|
1129
|
+
const urlVersionIds = new Map();
|
|
1130
|
+
function appendDevServerClientScript(devServerConfig, req, content) {
|
|
1131
|
+
var _a, _b, _c;
|
|
1132
|
+
const devServerClientUrl = getDevServerClientUrl(devServerConfig, (_b = (_a = req.headers) === null || _a === void 0 ? void 0 : _a['x-forwarded-host']) !== null && _b !== void 0 ? _b : req.host, (_c = req.headers) === null || _c === void 0 ? void 0 : _c['x-forwarded-proto']);
|
|
1133
|
+
const iframe = `<iframe title="Rindo Dev Server Connector ${version} ⚡" src="${devServerClientUrl}" style="display:block;width:0;height:0;border:0;visibility:hidden" aria-hidden="true"></iframe>`;
|
|
1134
|
+
return appendDevServerClientIframe(content, iframe);
|
|
1135
|
+
}
|
|
1136
|
+
function appendDevServerClientIframe(content, iframe) {
|
|
1137
|
+
if (content.includes('</body>')) {
|
|
1138
|
+
return content.replace('</body>', `${iframe}</body>`);
|
|
1139
|
+
}
|
|
1140
|
+
if (content.includes('</html>')) {
|
|
1141
|
+
return content.replace('</html>', `${iframe}</html>`);
|
|
1142
|
+
}
|
|
1143
|
+
return `${content}${iframe}`;
|
|
1122
1144
|
}
|
|
1123
1145
|
|
|
1124
|
-
async function serveDevClient(devServerConfig, serverCtx, req, res) {
|
|
1125
|
-
try {
|
|
1126
|
-
if (isOpenInEditor(req.pathname)) {
|
|
1127
|
-
return serveOpenInEditor(serverCtx, req, res);
|
|
1128
|
-
}
|
|
1129
|
-
if (isDevServerClient(req.pathname)) {
|
|
1130
|
-
return serveDevClientScript(devServerConfig, serverCtx, req, res);
|
|
1131
|
-
}
|
|
1132
|
-
if (isInitialDevServerLoad(req.pathname)) {
|
|
1133
|
-
req.filePath = path__default['default'].join(devServerConfig.devServerDir, 'templates', 'initial-load.html');
|
|
1134
|
-
}
|
|
1135
|
-
else {
|
|
1136
|
-
const staticFile = req.pathname.replace(DEV_SERVER_URL + '/', '');
|
|
1137
|
-
req.filePath = path__default['default'].join(devServerConfig.devServerDir, 'static', staticFile);
|
|
1138
|
-
}
|
|
1139
|
-
try {
|
|
1140
|
-
req.stats = await serverCtx.sys.stat(req.filePath);
|
|
1141
|
-
if (req.stats.isFile) {
|
|
1142
|
-
return serveFile(devServerConfig, serverCtx, req, res);
|
|
1143
|
-
}
|
|
1144
|
-
return serverCtx.serve404(req, res, 'serveDevClient not file');
|
|
1145
|
-
}
|
|
1146
|
-
catch (e) {
|
|
1147
|
-
return serverCtx.serve404(req, res, `serveDevClient stats error ${e}`);
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
catch (e) {
|
|
1151
|
-
return serverCtx.serve500(req, res, e, 'serveDevClient');
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
async function serveDevClientScript(devServerConfig, serverCtx, req, res) {
|
|
1155
|
-
try {
|
|
1156
|
-
if (serverCtx.connectorHtml == null) {
|
|
1157
|
-
const filePath = path__default['default'].join(devServerConfig.devServerDir, 'connector.html');
|
|
1158
|
-
serverCtx.connectorHtml = serverCtx.sys.readFileSync(filePath, 'utf8');
|
|
1159
|
-
if (typeof serverCtx.connectorHtml !== 'string') {
|
|
1160
|
-
return serverCtx.serve404(req, res, `serveDevClientScript`);
|
|
1161
|
-
}
|
|
1162
|
-
const devClientConfig = {
|
|
1163
|
-
basePath: devServerConfig.basePath,
|
|
1164
|
-
editors: await getEditors(),
|
|
1165
|
-
reloadStrategy: devServerConfig.reloadStrategy,
|
|
1166
|
-
};
|
|
1167
|
-
serverCtx.connectorHtml = serverCtx.connectorHtml.replace('window.__DEV_CLIENT_CONFIG__', JSON.stringify(devClientConfig));
|
|
1168
|
-
}
|
|
1169
|
-
res.writeHead(200, responseHeaders({
|
|
1170
|
-
'content-type': 'text/html; charset=utf-8',
|
|
1171
|
-
}));
|
|
1172
|
-
res.write(serverCtx.connectorHtml);
|
|
1173
|
-
res.end();
|
|
1174
|
-
}
|
|
1175
|
-
catch (e) {
|
|
1176
|
-
return serverCtx.serve500(req, res, e, `serveDevClientScript`);
|
|
1177
|
-
}
|
|
1146
|
+
async function serveDevClient(devServerConfig, serverCtx, req, res) {
|
|
1147
|
+
try {
|
|
1148
|
+
if (isOpenInEditor(req.pathname)) {
|
|
1149
|
+
return serveOpenInEditor(serverCtx, req, res);
|
|
1150
|
+
}
|
|
1151
|
+
if (isDevServerClient(req.pathname)) {
|
|
1152
|
+
return serveDevClientScript(devServerConfig, serverCtx, req, res);
|
|
1153
|
+
}
|
|
1154
|
+
if (isInitialDevServerLoad(req.pathname)) {
|
|
1155
|
+
req.filePath = path__default['default'].join(devServerConfig.devServerDir, 'templates', 'initial-load.html');
|
|
1156
|
+
}
|
|
1157
|
+
else {
|
|
1158
|
+
const staticFile = req.pathname.replace(DEV_SERVER_URL + '/', '');
|
|
1159
|
+
req.filePath = path__default['default'].join(devServerConfig.devServerDir, 'static', staticFile);
|
|
1160
|
+
}
|
|
1161
|
+
try {
|
|
1162
|
+
req.stats = await serverCtx.sys.stat(req.filePath);
|
|
1163
|
+
if (req.stats.isFile) {
|
|
1164
|
+
return serveFile(devServerConfig, serverCtx, req, res);
|
|
1165
|
+
}
|
|
1166
|
+
return serverCtx.serve404(req, res, 'serveDevClient not file');
|
|
1167
|
+
}
|
|
1168
|
+
catch (e) {
|
|
1169
|
+
return serverCtx.serve404(req, res, `serveDevClient stats error ${e}`);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
catch (e) {
|
|
1173
|
+
return serverCtx.serve500(req, res, e, 'serveDevClient');
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
async function serveDevClientScript(devServerConfig, serverCtx, req, res) {
|
|
1177
|
+
try {
|
|
1178
|
+
if (serverCtx.connectorHtml == null) {
|
|
1179
|
+
const filePath = path__default['default'].join(devServerConfig.devServerDir, 'connector.html');
|
|
1180
|
+
serverCtx.connectorHtml = serverCtx.sys.readFileSync(filePath, 'utf8');
|
|
1181
|
+
if (typeof serverCtx.connectorHtml !== 'string') {
|
|
1182
|
+
return serverCtx.serve404(req, res, `serveDevClientScript`);
|
|
1183
|
+
}
|
|
1184
|
+
const devClientConfig = {
|
|
1185
|
+
basePath: devServerConfig.basePath,
|
|
1186
|
+
editors: await getEditors(),
|
|
1187
|
+
reloadStrategy: devServerConfig.reloadStrategy,
|
|
1188
|
+
};
|
|
1189
|
+
serverCtx.connectorHtml = serverCtx.connectorHtml.replace('window.__DEV_CLIENT_CONFIG__', JSON.stringify(devClientConfig));
|
|
1190
|
+
}
|
|
1191
|
+
res.writeHead(200, responseHeaders({
|
|
1192
|
+
'content-type': 'text/html; charset=utf-8',
|
|
1193
|
+
}));
|
|
1194
|
+
res.write(serverCtx.connectorHtml);
|
|
1195
|
+
res.end();
|
|
1196
|
+
}
|
|
1197
|
+
catch (e) {
|
|
1198
|
+
return serverCtx.serve500(req, res, e, `serveDevClientScript`);
|
|
1199
|
+
}
|
|
1178
1200
|
}
|
|
1179
1201
|
|
|
1180
|
-
async function serveDevNodeModule(serverCtx, req, res) {
|
|
1181
|
-
try {
|
|
1182
|
-
const results = await serverCtx.getCompilerRequest(req.pathname);
|
|
1183
|
-
const headers = {
|
|
1184
|
-
'content-type': 'application/javascript; charset=utf-8',
|
|
1185
|
-
'content-length': Buffer.byteLength(results.content, 'utf8'),
|
|
1186
|
-
'x-dev-node-module-id': results.nodeModuleId,
|
|
1187
|
-
'x-dev-node-module-version': results.nodeModuleVersion,
|
|
1188
|
-
'x-dev-node-module-resolved-path': results.nodeResolvedPath,
|
|
1189
|
-
'x-dev-node-module-cache-path': results.cachePath,
|
|
1190
|
-
'x-dev-node-module-cache-hit': results.cacheHit,
|
|
1191
|
-
};
|
|
1192
|
-
res.writeHead(results.status, responseHeaders(headers));
|
|
1193
|
-
res.write(results.content);
|
|
1194
|
-
res.end();
|
|
1195
|
-
}
|
|
1196
|
-
catch (e) {
|
|
1197
|
-
serverCtx.serve500(req, res, e, `serveDevNodeModule`);
|
|
1198
|
-
}
|
|
1202
|
+
async function serveDevNodeModule(serverCtx, req, res) {
|
|
1203
|
+
try {
|
|
1204
|
+
const results = await serverCtx.getCompilerRequest(req.pathname);
|
|
1205
|
+
const headers = {
|
|
1206
|
+
'content-type': 'application/javascript; charset=utf-8',
|
|
1207
|
+
'content-length': Buffer.byteLength(results.content, 'utf8'),
|
|
1208
|
+
'x-dev-node-module-id': results.nodeModuleId,
|
|
1209
|
+
'x-dev-node-module-version': results.nodeModuleVersion,
|
|
1210
|
+
'x-dev-node-module-resolved-path': results.nodeResolvedPath,
|
|
1211
|
+
'x-dev-node-module-cache-path': results.cachePath,
|
|
1212
|
+
'x-dev-node-module-cache-hit': results.cacheHit,
|
|
1213
|
+
};
|
|
1214
|
+
res.writeHead(results.status, responseHeaders(headers));
|
|
1215
|
+
res.write(results.content);
|
|
1216
|
+
res.end();
|
|
1217
|
+
}
|
|
1218
|
+
catch (e) {
|
|
1219
|
+
serverCtx.serve500(req, res, e, `serveDevNodeModule`);
|
|
1220
|
+
}
|
|
1199
1221
|
}
|
|
1200
1222
|
|
|
1201
|
-
async function serveDirectoryIndex(devServerConfig, serverCtx, req, res) {
|
|
1202
|
-
const indexFilePath = path__default['default'].join(req.filePath, 'index.html');
|
|
1203
|
-
req.stats = await serverCtx.sys.stat(indexFilePath);
|
|
1204
|
-
if (req.stats.isFile) {
|
|
1205
|
-
req.filePath = indexFilePath;
|
|
1206
|
-
return serveFile(devServerConfig, serverCtx, req, res);
|
|
1207
|
-
}
|
|
1208
|
-
if (!req.pathname.endsWith('/')) {
|
|
1209
|
-
return serverCtx.serve302(req, res, req.pathname + '/');
|
|
1210
|
-
}
|
|
1211
|
-
try {
|
|
1212
|
-
const dirFilePaths = await serverCtx.sys.readDir(req.filePath);
|
|
1213
|
-
try {
|
|
1214
|
-
if (serverCtx.dirTemplate == null) {
|
|
1215
|
-
const dirTemplatePath = path__default['default'].join(devServerConfig.devServerDir, 'templates', 'directory-index.html');
|
|
1216
|
-
serverCtx.dirTemplate = serverCtx.sys.readFileSync(dirTemplatePath);
|
|
1217
|
-
}
|
|
1218
|
-
const files = await getFiles(serverCtx.sys, req.url, dirFilePaths);
|
|
1219
|
-
const templateHtml = serverCtx.dirTemplate
|
|
1220
|
-
.replace('{{title}}', getTitle(req.pathname))
|
|
1221
|
-
.replace('{{nav}}', getName(req.pathname))
|
|
1222
|
-
.replace('{{files}}', files);
|
|
1223
|
-
serverCtx.logRequest(req, 200);
|
|
1224
|
-
res.writeHead(200, responseHeaders({
|
|
1225
|
-
'content-type': 'text/html; charset=utf-8',
|
|
1226
|
-
'x-directory-index': req.pathname,
|
|
1227
|
-
}));
|
|
1228
|
-
res.write(templateHtml);
|
|
1229
|
-
res.end();
|
|
1230
|
-
}
|
|
1231
|
-
catch (e) {
|
|
1232
|
-
return serverCtx.serve500(req, res, e, 'serveDirectoryIndex');
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
catch (e) {
|
|
1236
|
-
return serverCtx.serve404(req, res, 'serveDirectoryIndex');
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
async function getFiles(sys, baseUrl, dirItemNames) {
|
|
1240
|
-
const items = await getDirectoryItems(sys, baseUrl, dirItemNames);
|
|
1241
|
-
if (baseUrl.pathname !== '/') {
|
|
1242
|
-
items.unshift({
|
|
1243
|
-
isDirectory: true,
|
|
1244
|
-
pathname: '../',
|
|
1245
|
-
name: '..',
|
|
1246
|
-
});
|
|
1247
|
-
}
|
|
1248
|
-
return items
|
|
1249
|
-
.map((item) => {
|
|
1223
|
+
async function serveDirectoryIndex(devServerConfig, serverCtx, req, res) {
|
|
1224
|
+
const indexFilePath = path__default['default'].join(req.filePath, 'index.html');
|
|
1225
|
+
req.stats = await serverCtx.sys.stat(indexFilePath);
|
|
1226
|
+
if (req.stats.isFile) {
|
|
1227
|
+
req.filePath = indexFilePath;
|
|
1228
|
+
return serveFile(devServerConfig, serverCtx, req, res);
|
|
1229
|
+
}
|
|
1230
|
+
if (!req.pathname.endsWith('/')) {
|
|
1231
|
+
return serverCtx.serve302(req, res, req.pathname + '/');
|
|
1232
|
+
}
|
|
1233
|
+
try {
|
|
1234
|
+
const dirFilePaths = await serverCtx.sys.readDir(req.filePath);
|
|
1235
|
+
try {
|
|
1236
|
+
if (serverCtx.dirTemplate == null) {
|
|
1237
|
+
const dirTemplatePath = path__default['default'].join(devServerConfig.devServerDir, 'templates', 'directory-index.html');
|
|
1238
|
+
serverCtx.dirTemplate = serverCtx.sys.readFileSync(dirTemplatePath);
|
|
1239
|
+
}
|
|
1240
|
+
const files = await getFiles(serverCtx.sys, req.url, dirFilePaths);
|
|
1241
|
+
const templateHtml = serverCtx.dirTemplate
|
|
1242
|
+
.replace('{{title}}', getTitle(req.pathname))
|
|
1243
|
+
.replace('{{nav}}', getName(req.pathname))
|
|
1244
|
+
.replace('{{files}}', files);
|
|
1245
|
+
serverCtx.logRequest(req, 200);
|
|
1246
|
+
res.writeHead(200, responseHeaders({
|
|
1247
|
+
'content-type': 'text/html; charset=utf-8',
|
|
1248
|
+
'x-directory-index': req.pathname,
|
|
1249
|
+
}));
|
|
1250
|
+
res.write(templateHtml);
|
|
1251
|
+
res.end();
|
|
1252
|
+
}
|
|
1253
|
+
catch (e) {
|
|
1254
|
+
return serverCtx.serve500(req, res, e, 'serveDirectoryIndex');
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
catch (e) {
|
|
1258
|
+
return serverCtx.serve404(req, res, 'serveDirectoryIndex');
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
async function getFiles(sys, baseUrl, dirItemNames) {
|
|
1262
|
+
const items = await getDirectoryItems(sys, baseUrl, dirItemNames);
|
|
1263
|
+
if (baseUrl.pathname !== '/') {
|
|
1264
|
+
items.unshift({
|
|
1265
|
+
isDirectory: true,
|
|
1266
|
+
pathname: '../',
|
|
1267
|
+
name: '..',
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
return items
|
|
1271
|
+
.map((item) => {
|
|
1250
1272
|
return `
|
|
1251
1273
|
<li class="${item.isDirectory ? 'directory' : 'file'}">
|
|
1252
1274
|
<a href="${item.pathname}">
|
|
1253
1275
|
<span class="icon"></span>
|
|
1254
1276
|
<span>${item.name}</span>
|
|
1255
1277
|
</a>
|
|
1256
|
-
</li>`;
|
|
1257
|
-
})
|
|
1258
|
-
.join('');
|
|
1259
|
-
}
|
|
1260
|
-
async function getDirectoryItems(sys, baseUrl, dirFilePaths) {
|
|
1261
|
-
const items = await Promise.all(dirFilePaths.map(async (dirFilePath) => {
|
|
1262
|
-
const fileName = path__default['default'].basename(dirFilePath);
|
|
1263
|
-
const url = new URL(fileName, baseUrl);
|
|
1264
|
-
const stats = await sys.stat(dirFilePath);
|
|
1265
|
-
const item = {
|
|
1266
|
-
name: fileName,
|
|
1267
|
-
pathname: url.pathname,
|
|
1268
|
-
isDirectory: stats.isDirectory,
|
|
1269
|
-
};
|
|
1270
|
-
return item;
|
|
1271
|
-
}));
|
|
1272
|
-
return items;
|
|
1273
|
-
}
|
|
1274
|
-
function getTitle(pathName) {
|
|
1275
|
-
return pathName;
|
|
1276
|
-
}
|
|
1277
|
-
function getName(pathName) {
|
|
1278
|
-
const dirs = pathName.split('/');
|
|
1279
|
-
dirs.pop();
|
|
1280
|
-
let url = '';
|
|
1281
|
-
return (dirs
|
|
1282
|
-
.map((dir, index) => {
|
|
1283
|
-
url += dir + '/';
|
|
1284
|
-
const text = index === 0 ? `~` : dir;
|
|
1285
|
-
return `<a href="${url}">${text}</a>`;
|
|
1286
|
-
})
|
|
1287
|
-
.join('<span>/</span>') + '<span>/</span>');
|
|
1278
|
+
</li>`;
|
|
1279
|
+
})
|
|
1280
|
+
.join('');
|
|
1281
|
+
}
|
|
1282
|
+
async function getDirectoryItems(sys, baseUrl, dirFilePaths) {
|
|
1283
|
+
const items = await Promise.all(dirFilePaths.map(async (dirFilePath) => {
|
|
1284
|
+
const fileName = path__default['default'].basename(dirFilePath);
|
|
1285
|
+
const url = new URL(fileName, baseUrl);
|
|
1286
|
+
const stats = await sys.stat(dirFilePath);
|
|
1287
|
+
const item = {
|
|
1288
|
+
name: fileName,
|
|
1289
|
+
pathname: url.pathname,
|
|
1290
|
+
isDirectory: stats.isDirectory,
|
|
1291
|
+
};
|
|
1292
|
+
return item;
|
|
1293
|
+
}));
|
|
1294
|
+
return items;
|
|
1295
|
+
}
|
|
1296
|
+
function getTitle(pathName) {
|
|
1297
|
+
return pathName;
|
|
1298
|
+
}
|
|
1299
|
+
function getName(pathName) {
|
|
1300
|
+
const dirs = pathName.split('/');
|
|
1301
|
+
dirs.pop();
|
|
1302
|
+
let url = '';
|
|
1303
|
+
return (dirs
|
|
1304
|
+
.map((dir, index) => {
|
|
1305
|
+
url += dir + '/';
|
|
1306
|
+
const text = index === 0 ? `~` : dir;
|
|
1307
|
+
return `<a href="${url}">${text}</a>`;
|
|
1308
|
+
})
|
|
1309
|
+
.join('<span>/</span>') + '<span>/</span>');
|
|
1288
1310
|
}
|
|
1289
1311
|
|
|
1290
|
-
async function ssrPageRequest(devServerConfig, serverCtx, req, res) {
|
|
1291
|
-
try {
|
|
1292
|
-
let status = 500;
|
|
1293
|
-
let content = '';
|
|
1294
|
-
const { hydrateApp, srcIndexHtml, diagnostics } = await setupHydrateApp(devServerConfig, serverCtx);
|
|
1295
|
-
if (!diagnostics.some((diagnostic) => diagnostic.level === 'error')) {
|
|
1296
|
-
try {
|
|
1297
|
-
const opts = getSsrHydrateOptions(devServerConfig, serverCtx, req.url);
|
|
1298
|
-
const ssrResults = await hydrateApp.renderToString(srcIndexHtml, opts);
|
|
1299
|
-
diagnostics.push(...ssrResults.diagnostics);
|
|
1300
|
-
status = ssrResults.httpStatus;
|
|
1301
|
-
content = ssrResults.html;
|
|
1302
|
-
}
|
|
1303
|
-
catch (e) {
|
|
1304
|
-
catchError(diagnostics, e);
|
|
1305
|
-
}
|
|
1306
|
-
}
|
|
1307
|
-
if (diagnostics.some((diagnostic) => diagnostic.level === 'error')) {
|
|
1308
|
-
content = getSsrErrorContent(diagnostics);
|
|
1309
|
-
status = 500;
|
|
1310
|
-
}
|
|
1311
|
-
if (devServerConfig.websocket) {
|
|
1312
|
-
content = appendDevServerClientScript(devServerConfig, req, content);
|
|
1313
|
-
}
|
|
1314
|
-
serverCtx.logRequest(req, status);
|
|
1315
|
-
res.writeHead(status, responseHeaders({
|
|
1316
|
-
'content-type': 'text/html; charset=utf-8',
|
|
1317
|
-
'content-length': Buffer.byteLength(content, 'utf8'),
|
|
1318
|
-
}));
|
|
1319
|
-
res.write(content);
|
|
1320
|
-
res.end();
|
|
1321
|
-
}
|
|
1322
|
-
catch (e) {
|
|
1323
|
-
serverCtx.serve500(req, res, e, `ssrPageRequest`);
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
async function ssrStaticDataRequest(devServerConfig, serverCtx, req, res) {
|
|
1327
|
-
try {
|
|
1328
|
-
const data = {};
|
|
1329
|
-
let httpCache = false;
|
|
1330
|
-
const { hydrateApp, srcIndexHtml, diagnostics } = await setupHydrateApp(devServerConfig, serverCtx);
|
|
1331
|
-
if (!diagnostics.some((diagnostic) => diagnostic.level === 'error')) {
|
|
1332
|
-
try {
|
|
1333
|
-
const { ssrPath, hasQueryString } = getSsrStaticDataPath(req);
|
|
1334
|
-
const url = new URL(ssrPath, req.url);
|
|
1335
|
-
const opts = getSsrHydrateOptions(devServerConfig, serverCtx, url);
|
|
1336
|
-
const ssrResults = await hydrateApp.renderToString(srcIndexHtml, opts);
|
|
1337
|
-
diagnostics.push(...ssrResults.diagnostics);
|
|
1338
|
-
ssrResults.staticData.forEach((s) => {
|
|
1339
|
-
if (s.type === 'application/json') {
|
|
1340
|
-
data[s.id] = JSON.parse(s.content);
|
|
1341
|
-
}
|
|
1342
|
-
else {
|
|
1343
|
-
data[s.id] = s.content;
|
|
1344
|
-
}
|
|
1345
|
-
});
|
|
1346
|
-
data.components = ssrResults.components.map((c) => c.tag).sort();
|
|
1347
|
-
httpCache = hasQueryString;
|
|
1348
|
-
}
|
|
1349
|
-
catch (e) {
|
|
1350
|
-
catchError(diagnostics, e);
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
if (diagnostics.length > 0) {
|
|
1354
|
-
data.diagnostics = diagnostics;
|
|
1355
|
-
}
|
|
1356
|
-
const status = diagnostics.some((diagnostic) => diagnostic.level === 'error') ? 500 : 200;
|
|
1357
|
-
const content = JSON.stringify(data);
|
|
1358
|
-
serverCtx.logRequest(req, status);
|
|
1359
|
-
res.writeHead(status, responseHeaders({
|
|
1360
|
-
'content-type': 'application/json; charset=utf-8',
|
|
1361
|
-
'content-length': Buffer.byteLength(content, 'utf8'),
|
|
1362
|
-
}, httpCache && status === 200));
|
|
1363
|
-
res.write(content);
|
|
1364
|
-
res.end();
|
|
1365
|
-
}
|
|
1366
|
-
catch (e) {
|
|
1367
|
-
serverCtx.serve500(req, res, e, `ssrStaticDataRequest`);
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1370
|
-
async function setupHydrateApp(devServerConfig, serverCtx) {
|
|
1371
|
-
let srcIndexHtml = null;
|
|
1372
|
-
let hydrateApp = null;
|
|
1373
|
-
const buildResults = await serverCtx.getBuildResults();
|
|
1374
|
-
const diagnostics = [];
|
|
1375
|
-
if (serverCtx.prerenderConfig == null && isString(devServerConfig.prerenderConfig)) {
|
|
1376
|
-
const compilerPath = path__default['default'].join(devServerConfig.devServerDir, '..', 'compiler', 'rindo.js');
|
|
1377
|
-
const compiler = require(compilerPath);
|
|
1378
|
-
const prerenderConfigResults = compiler.nodeRequire(devServerConfig.prerenderConfig);
|
|
1379
|
-
diagnostics.push(...prerenderConfigResults.diagnostics);
|
|
1380
|
-
if (prerenderConfigResults.module && prerenderConfigResults.module.config) {
|
|
1381
|
-
serverCtx.prerenderConfig = prerenderConfigResults.module.config;
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
if (!isString(buildResults.hydrateAppFilePath)) {
|
|
1385
|
-
diagnostics.push({ messageText: `Missing hydrateAppFilePath`, level: `error`, type: `ssr`, lines: [] });
|
|
1386
|
-
}
|
|
1387
|
-
else if (!isString(devServerConfig.srcIndexHtml)) {
|
|
1388
|
-
diagnostics.push({ messageText: `Missing srcIndexHtml`, level: `error`, type: `ssr`, lines: [] });
|
|
1389
|
-
}
|
|
1390
|
-
else {
|
|
1391
|
-
srcIndexHtml = await serverCtx.sys.readFile(devServerConfig.srcIndexHtml);
|
|
1392
|
-
if (!isString(srcIndexHtml)) {
|
|
1393
|
-
diagnostics.push({
|
|
1394
|
-
level: `error`,
|
|
1395
|
-
lines: [],
|
|
1396
|
-
messageText: `Unable to load src index html: ${devServerConfig.srcIndexHtml}`,
|
|
1397
|
-
type: `ssr`,
|
|
1398
|
-
});
|
|
1399
|
-
}
|
|
1400
|
-
else {
|
|
1401
|
-
// ensure we cleared out node's internal require() cache for this file
|
|
1402
|
-
const hydrateAppFilePath = path__default['default'].resolve(buildResults.hydrateAppFilePath);
|
|
1403
|
-
// brute force way of clearning node's module cache
|
|
1404
|
-
// not using `delete require.cache[id]` since it'll cause memory leaks
|
|
1405
|
-
require.cache = {};
|
|
1406
|
-
const Module = require('module');
|
|
1407
|
-
Module._cache[hydrateAppFilePath] = undefined;
|
|
1408
|
-
hydrateApp = require(hydrateAppFilePath);
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
return {
|
|
1412
|
-
hydrateApp,
|
|
1413
|
-
srcIndexHtml,
|
|
1414
|
-
diagnostics,
|
|
1415
|
-
};
|
|
1416
|
-
}
|
|
1417
|
-
function getSsrHydrateOptions(devServerConfig, serverCtx, url) {
|
|
1418
|
-
const opts = {
|
|
1419
|
-
url: url.href,
|
|
1420
|
-
addModulePreloads: false,
|
|
1421
|
-
approximateLineWidth: 120,
|
|
1422
|
-
inlineExternalStyleSheets: false,
|
|
1423
|
-
minifyScriptElements: false,
|
|
1424
|
-
minifyStyleElements: false,
|
|
1425
|
-
removeAttributeQuotes: false,
|
|
1426
|
-
removeBooleanAttributeQuotes: false,
|
|
1427
|
-
removeEmptyAttributes: false,
|
|
1428
|
-
removeHtmlComments: false,
|
|
1429
|
-
prettyHtml: true,
|
|
1430
|
-
};
|
|
1431
|
-
const prerenderConfig = serverCtx === null || serverCtx === void 0 ? void 0 : serverCtx.prerenderConfig;
|
|
1432
|
-
if (isFunction(prerenderConfig === null || prerenderConfig === void 0 ? void 0 : prerenderConfig.hydrateOptions)) {
|
|
1433
|
-
const userOpts = prerenderConfig.hydrateOptions(url);
|
|
1434
|
-
if (userOpts) {
|
|
1435
|
-
Object.assign(opts, userOpts);
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
if (isFunction(serverCtx.sys.applyPrerenderGlobalPatch)) {
|
|
1439
|
-
const orgBeforeHydrate = opts.beforeHydrate;
|
|
1440
|
-
opts.beforeHydrate = (document) => {
|
|
1441
|
-
// patch this new window with the fetch global from node-fetch
|
|
1442
|
-
const devServerBaseUrl = new URL(devServerConfig.browserUrl);
|
|
1443
|
-
const devServerHostUrl = devServerBaseUrl.origin;
|
|
1444
|
-
serverCtx.sys.applyPrerenderGlobalPatch({
|
|
1445
|
-
devServerHostUrl: devServerHostUrl,
|
|
1446
|
-
window: document.defaultView,
|
|
1447
|
-
});
|
|
1448
|
-
if (typeof orgBeforeHydrate === 'function') {
|
|
1449
|
-
return orgBeforeHydrate(document);
|
|
1450
|
-
}
|
|
1451
|
-
};
|
|
1452
|
-
}
|
|
1453
|
-
return opts;
|
|
1454
|
-
}
|
|
1455
|
-
function getSsrErrorContent(diagnostics) {
|
|
1312
|
+
async function ssrPageRequest(devServerConfig, serverCtx, req, res) {
|
|
1313
|
+
try {
|
|
1314
|
+
let status = 500;
|
|
1315
|
+
let content = '';
|
|
1316
|
+
const { hydrateApp, srcIndexHtml, diagnostics } = await setupHydrateApp(devServerConfig, serverCtx);
|
|
1317
|
+
if (!diagnostics.some((diagnostic) => diagnostic.level === 'error')) {
|
|
1318
|
+
try {
|
|
1319
|
+
const opts = getSsrHydrateOptions(devServerConfig, serverCtx, req.url);
|
|
1320
|
+
const ssrResults = await hydrateApp.renderToString(srcIndexHtml, opts);
|
|
1321
|
+
diagnostics.push(...ssrResults.diagnostics);
|
|
1322
|
+
status = ssrResults.httpStatus;
|
|
1323
|
+
content = ssrResults.html;
|
|
1324
|
+
}
|
|
1325
|
+
catch (e) {
|
|
1326
|
+
catchError(diagnostics, e);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
if (diagnostics.some((diagnostic) => diagnostic.level === 'error')) {
|
|
1330
|
+
content = getSsrErrorContent(diagnostics);
|
|
1331
|
+
status = 500;
|
|
1332
|
+
}
|
|
1333
|
+
if (devServerConfig.websocket) {
|
|
1334
|
+
content = appendDevServerClientScript(devServerConfig, req, content);
|
|
1335
|
+
}
|
|
1336
|
+
serverCtx.logRequest(req, status);
|
|
1337
|
+
res.writeHead(status, responseHeaders({
|
|
1338
|
+
'content-type': 'text/html; charset=utf-8',
|
|
1339
|
+
'content-length': Buffer.byteLength(content, 'utf8'),
|
|
1340
|
+
}));
|
|
1341
|
+
res.write(content);
|
|
1342
|
+
res.end();
|
|
1343
|
+
}
|
|
1344
|
+
catch (e) {
|
|
1345
|
+
serverCtx.serve500(req, res, e, `ssrPageRequest`);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
async function ssrStaticDataRequest(devServerConfig, serverCtx, req, res) {
|
|
1349
|
+
try {
|
|
1350
|
+
const data = {};
|
|
1351
|
+
let httpCache = false;
|
|
1352
|
+
const { hydrateApp, srcIndexHtml, diagnostics } = await setupHydrateApp(devServerConfig, serverCtx);
|
|
1353
|
+
if (!diagnostics.some((diagnostic) => diagnostic.level === 'error')) {
|
|
1354
|
+
try {
|
|
1355
|
+
const { ssrPath, hasQueryString } = getSsrStaticDataPath(req);
|
|
1356
|
+
const url = new URL(ssrPath, req.url);
|
|
1357
|
+
const opts = getSsrHydrateOptions(devServerConfig, serverCtx, url);
|
|
1358
|
+
const ssrResults = await hydrateApp.renderToString(srcIndexHtml, opts);
|
|
1359
|
+
diagnostics.push(...ssrResults.diagnostics);
|
|
1360
|
+
ssrResults.staticData.forEach((s) => {
|
|
1361
|
+
if (s.type === 'application/json') {
|
|
1362
|
+
data[s.id] = JSON.parse(s.content);
|
|
1363
|
+
}
|
|
1364
|
+
else {
|
|
1365
|
+
data[s.id] = s.content;
|
|
1366
|
+
}
|
|
1367
|
+
});
|
|
1368
|
+
data.components = ssrResults.components.map((c) => c.tag).sort();
|
|
1369
|
+
httpCache = hasQueryString;
|
|
1370
|
+
}
|
|
1371
|
+
catch (e) {
|
|
1372
|
+
catchError(diagnostics, e);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
if (diagnostics.length > 0) {
|
|
1376
|
+
data.diagnostics = diagnostics;
|
|
1377
|
+
}
|
|
1378
|
+
const status = diagnostics.some((diagnostic) => diagnostic.level === 'error') ? 500 : 200;
|
|
1379
|
+
const content = JSON.stringify(data);
|
|
1380
|
+
serverCtx.logRequest(req, status);
|
|
1381
|
+
res.writeHead(status, responseHeaders({
|
|
1382
|
+
'content-type': 'application/json; charset=utf-8',
|
|
1383
|
+
'content-length': Buffer.byteLength(content, 'utf8'),
|
|
1384
|
+
}, httpCache && status === 200));
|
|
1385
|
+
res.write(content);
|
|
1386
|
+
res.end();
|
|
1387
|
+
}
|
|
1388
|
+
catch (e) {
|
|
1389
|
+
serverCtx.serve500(req, res, e, `ssrStaticDataRequest`);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
async function setupHydrateApp(devServerConfig, serverCtx) {
|
|
1393
|
+
let srcIndexHtml = null;
|
|
1394
|
+
let hydrateApp = null;
|
|
1395
|
+
const buildResults = await serverCtx.getBuildResults();
|
|
1396
|
+
const diagnostics = [];
|
|
1397
|
+
if (serverCtx.prerenderConfig == null && isString(devServerConfig.prerenderConfig)) {
|
|
1398
|
+
const compilerPath = path__default['default'].join(devServerConfig.devServerDir, '..', 'compiler', 'rindo.js');
|
|
1399
|
+
const compiler = require(compilerPath);
|
|
1400
|
+
const prerenderConfigResults = compiler.nodeRequire(devServerConfig.prerenderConfig);
|
|
1401
|
+
diagnostics.push(...prerenderConfigResults.diagnostics);
|
|
1402
|
+
if (prerenderConfigResults.module && prerenderConfigResults.module.config) {
|
|
1403
|
+
serverCtx.prerenderConfig = prerenderConfigResults.module.config;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
if (!isString(buildResults.hydrateAppFilePath)) {
|
|
1407
|
+
diagnostics.push({ messageText: `Missing hydrateAppFilePath`, level: `error`, type: `ssr`, lines: [] });
|
|
1408
|
+
}
|
|
1409
|
+
else if (!isString(devServerConfig.srcIndexHtml)) {
|
|
1410
|
+
diagnostics.push({ messageText: `Missing srcIndexHtml`, level: `error`, type: `ssr`, lines: [] });
|
|
1411
|
+
}
|
|
1412
|
+
else {
|
|
1413
|
+
srcIndexHtml = await serverCtx.sys.readFile(devServerConfig.srcIndexHtml);
|
|
1414
|
+
if (!isString(srcIndexHtml)) {
|
|
1415
|
+
diagnostics.push({
|
|
1416
|
+
level: `error`,
|
|
1417
|
+
lines: [],
|
|
1418
|
+
messageText: `Unable to load src index html: ${devServerConfig.srcIndexHtml}`,
|
|
1419
|
+
type: `ssr`,
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
else {
|
|
1423
|
+
// ensure we cleared out node's internal require() cache for this file
|
|
1424
|
+
const hydrateAppFilePath = path__default['default'].resolve(buildResults.hydrateAppFilePath);
|
|
1425
|
+
// brute force way of clearning node's module cache
|
|
1426
|
+
// not using `delete require.cache[id]` since it'll cause memory leaks
|
|
1427
|
+
require.cache = {};
|
|
1428
|
+
const Module = require('module');
|
|
1429
|
+
Module._cache[hydrateAppFilePath] = undefined;
|
|
1430
|
+
hydrateApp = require(hydrateAppFilePath);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
return {
|
|
1434
|
+
hydrateApp,
|
|
1435
|
+
srcIndexHtml,
|
|
1436
|
+
diagnostics,
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
function getSsrHydrateOptions(devServerConfig, serverCtx, url) {
|
|
1440
|
+
const opts = {
|
|
1441
|
+
url: url.href,
|
|
1442
|
+
addModulePreloads: false,
|
|
1443
|
+
approximateLineWidth: 120,
|
|
1444
|
+
inlineExternalStyleSheets: false,
|
|
1445
|
+
minifyScriptElements: false,
|
|
1446
|
+
minifyStyleElements: false,
|
|
1447
|
+
removeAttributeQuotes: false,
|
|
1448
|
+
removeBooleanAttributeQuotes: false,
|
|
1449
|
+
removeEmptyAttributes: false,
|
|
1450
|
+
removeHtmlComments: false,
|
|
1451
|
+
prettyHtml: true,
|
|
1452
|
+
};
|
|
1453
|
+
const prerenderConfig = serverCtx === null || serverCtx === void 0 ? void 0 : serverCtx.prerenderConfig;
|
|
1454
|
+
if (isFunction(prerenderConfig === null || prerenderConfig === void 0 ? void 0 : prerenderConfig.hydrateOptions)) {
|
|
1455
|
+
const userOpts = prerenderConfig.hydrateOptions(url);
|
|
1456
|
+
if (userOpts) {
|
|
1457
|
+
Object.assign(opts, userOpts);
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
if (isFunction(serverCtx.sys.applyPrerenderGlobalPatch)) {
|
|
1461
|
+
const orgBeforeHydrate = opts.beforeHydrate;
|
|
1462
|
+
opts.beforeHydrate = (document) => {
|
|
1463
|
+
// patch this new window with the fetch global from node-fetch
|
|
1464
|
+
const devServerBaseUrl = new URL(devServerConfig.browserUrl);
|
|
1465
|
+
const devServerHostUrl = devServerBaseUrl.origin;
|
|
1466
|
+
serverCtx.sys.applyPrerenderGlobalPatch({
|
|
1467
|
+
devServerHostUrl: devServerHostUrl,
|
|
1468
|
+
window: document.defaultView,
|
|
1469
|
+
});
|
|
1470
|
+
if (typeof orgBeforeHydrate === 'function') {
|
|
1471
|
+
return orgBeforeHydrate(document);
|
|
1472
|
+
}
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
return opts;
|
|
1476
|
+
}
|
|
1477
|
+
function getSsrErrorContent(diagnostics) {
|
|
1456
1478
|
return `<!doctype html>
|
|
1457
1479
|
<html>
|
|
1458
1480
|
<head>
|
|
@@ -1471,330 +1493,330 @@ function getSsrErrorContent(diagnostics) {
|
|
|
1471
1493
|
</p>
|
|
1472
1494
|
`)}
|
|
1473
1495
|
</body>
|
|
1474
|
-
</html>`;
|
|
1496
|
+
</html>`;
|
|
1475
1497
|
}
|
|
1476
1498
|
|
|
1477
|
-
function createRequestHandler(devServerConfig, serverCtx) {
|
|
1478
|
-
let userRequestHandler = null;
|
|
1479
|
-
if (typeof devServerConfig.requestListenerPath === 'string') {
|
|
1480
|
-
userRequestHandler = require(devServerConfig.requestListenerPath);
|
|
1481
|
-
}
|
|
1482
|
-
return async function (incomingReq, res) {
|
|
1483
|
-
async function defaultHandler() {
|
|
1484
|
-
try {
|
|
1485
|
-
const req = normalizeHttpRequest(devServerConfig, incomingReq);
|
|
1486
|
-
if (!req.url) {
|
|
1487
|
-
return serverCtx.serve302(req, res);
|
|
1488
|
-
}
|
|
1489
|
-
if (isDevClient(req.pathname) && devServerConfig.websocket) {
|
|
1490
|
-
return serveDevClient(devServerConfig, serverCtx, req, res);
|
|
1491
|
-
}
|
|
1492
|
-
if (isDevModule(req.pathname)) {
|
|
1493
|
-
return serveDevNodeModule(serverCtx, req, res);
|
|
1494
|
-
}
|
|
1495
|
-
if (!isValidUrlBasePath(devServerConfig.basePath, req.url)) {
|
|
1496
|
-
return serverCtx.serve404(req, res, `invalid basePath`, `404 File Not Found, base path: ${devServerConfig.basePath}`);
|
|
1497
|
-
}
|
|
1498
|
-
if (devServerConfig.ssr) {
|
|
1499
|
-
if (isExtensionLessPath(req.url.pathname)) {
|
|
1500
|
-
return ssrPageRequest(devServerConfig, serverCtx, req, res);
|
|
1501
|
-
}
|
|
1502
|
-
if (isSsrStaticDataPath(req.url.pathname)) {
|
|
1503
|
-
return ssrStaticDataRequest(devServerConfig, serverCtx, req, res);
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
req.stats = await serverCtx.sys.stat(req.filePath);
|
|
1507
|
-
if (req.stats.isFile) {
|
|
1508
|
-
return serveFile(devServerConfig, serverCtx, req, res);
|
|
1509
|
-
}
|
|
1510
|
-
if (req.stats.isDirectory) {
|
|
1511
|
-
return serveDirectoryIndex(devServerConfig, serverCtx, req, res);
|
|
1512
|
-
}
|
|
1513
|
-
const xSource = ['notfound'];
|
|
1514
|
-
const validHistoryApi = isValidHistoryApi(devServerConfig, req);
|
|
1515
|
-
xSource.push(`validHistoryApi: ${validHistoryApi}`);
|
|
1516
|
-
if (validHistoryApi) {
|
|
1517
|
-
try {
|
|
1518
|
-
const indexFilePath = path__default['default'].join(devServerConfig.root, devServerConfig.historyApiFallback.index);
|
|
1519
|
-
xSource.push(`indexFilePath: ${indexFilePath}`);
|
|
1520
|
-
req.stats = await serverCtx.sys.stat(indexFilePath);
|
|
1521
|
-
if (req.stats.isFile) {
|
|
1522
|
-
req.filePath = indexFilePath;
|
|
1523
|
-
return serveFile(devServerConfig, serverCtx, req, res);
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
catch (e) {
|
|
1527
|
-
xSource.push(`notfound error: ${e}`);
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
return serverCtx.serve404(req, res, xSource.join(', '));
|
|
1531
|
-
}
|
|
1532
|
-
catch (e) {
|
|
1533
|
-
return serverCtx.serve500(incomingReq, res, e, `not found error`);
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
|
-
if (typeof userRequestHandler === 'function') {
|
|
1537
|
-
await userRequestHandler(incomingReq, res, defaultHandler);
|
|
1538
|
-
}
|
|
1539
|
-
else {
|
|
1540
|
-
await defaultHandler();
|
|
1541
|
-
}
|
|
1542
|
-
};
|
|
1543
|
-
}
|
|
1544
|
-
function isValidUrlBasePath(basePath, url) {
|
|
1545
|
-
// normalize the paths to always end with a slash for the check
|
|
1546
|
-
let pathname = url.pathname;
|
|
1547
|
-
if (!pathname.endsWith('/')) {
|
|
1548
|
-
pathname += '/';
|
|
1549
|
-
}
|
|
1550
|
-
if (!basePath.endsWith('/')) {
|
|
1551
|
-
basePath += '/';
|
|
1552
|
-
}
|
|
1553
|
-
return pathname.startsWith(basePath);
|
|
1554
|
-
}
|
|
1555
|
-
function normalizeHttpRequest(devServerConfig, incomingReq) {
|
|
1556
|
-
const req = {
|
|
1557
|
-
method: (incomingReq.method || 'GET').toUpperCase(),
|
|
1558
|
-
headers: incomingReq.headers,
|
|
1559
|
-
acceptHeader: (incomingReq.headers && typeof incomingReq.headers.accept === 'string' && incomingReq.headers.accept) || '',
|
|
1560
|
-
host: (incomingReq.headers && typeof incomingReq.headers.host === 'string' && incomingReq.headers.host) || null,
|
|
1561
|
-
url: null,
|
|
1562
|
-
searchParams: null,
|
|
1563
|
-
};
|
|
1564
|
-
const incomingUrl = (incomingReq.url || '').trim() || null;
|
|
1565
|
-
if (incomingUrl) {
|
|
1566
|
-
if (req.host) {
|
|
1567
|
-
req.url = new URL(incomingReq.url, `http://${req.host}`);
|
|
1568
|
-
}
|
|
1569
|
-
else {
|
|
1570
|
-
req.url = new URL(incomingReq.url, `http://rindojs-dev.web.app`);
|
|
1571
|
-
}
|
|
1572
|
-
req.searchParams = req.url.searchParams;
|
|
1573
|
-
}
|
|
1574
|
-
if (req.url) {
|
|
1575
|
-
const parts = req.url.pathname.replace(/\\/g, '/').split('/');
|
|
1576
|
-
req.pathname = parts.map((part) => decodeURIComponent(part)).join('/');
|
|
1577
|
-
if (req.pathname.length > 0 && !isDevClient(req.pathname)) {
|
|
1578
|
-
req.pathname = '/' + req.pathname.substring(devServerConfig.basePath.length);
|
|
1579
|
-
}
|
|
1580
|
-
req.filePath = normalizePath(path__default['default'].normalize(path__default['default'].join(devServerConfig.root, path__default['default'].relative('/', req.pathname))));
|
|
1581
|
-
}
|
|
1582
|
-
return req;
|
|
1583
|
-
}
|
|
1584
|
-
function isValidHistoryApi(devServerConfig, req) {
|
|
1585
|
-
if (!devServerConfig.historyApiFallback) {
|
|
1586
|
-
return false;
|
|
1587
|
-
}
|
|
1588
|
-
if (req.method !== 'GET') {
|
|
1589
|
-
return false;
|
|
1590
|
-
}
|
|
1591
|
-
if (!req.acceptHeader.includes('text/html')) {
|
|
1592
|
-
return false;
|
|
1593
|
-
}
|
|
1594
|
-
if (!devServerConfig.historyApiFallback.disableDotRule && req.pathname.includes('.')) {
|
|
1595
|
-
return false;
|
|
1596
|
-
}
|
|
1597
|
-
return true;
|
|
1499
|
+
function createRequestHandler(devServerConfig, serverCtx) {
|
|
1500
|
+
let userRequestHandler = null;
|
|
1501
|
+
if (typeof devServerConfig.requestListenerPath === 'string') {
|
|
1502
|
+
userRequestHandler = require(devServerConfig.requestListenerPath);
|
|
1503
|
+
}
|
|
1504
|
+
return async function (incomingReq, res) {
|
|
1505
|
+
async function defaultHandler() {
|
|
1506
|
+
try {
|
|
1507
|
+
const req = normalizeHttpRequest(devServerConfig, incomingReq);
|
|
1508
|
+
if (!req.url) {
|
|
1509
|
+
return serverCtx.serve302(req, res);
|
|
1510
|
+
}
|
|
1511
|
+
if (isDevClient(req.pathname) && devServerConfig.websocket) {
|
|
1512
|
+
return serveDevClient(devServerConfig, serverCtx, req, res);
|
|
1513
|
+
}
|
|
1514
|
+
if (isDevModule(req.pathname)) {
|
|
1515
|
+
return serveDevNodeModule(serverCtx, req, res);
|
|
1516
|
+
}
|
|
1517
|
+
if (!isValidUrlBasePath(devServerConfig.basePath, req.url)) {
|
|
1518
|
+
return serverCtx.serve404(req, res, `invalid basePath`, `404 File Not Found, base path: ${devServerConfig.basePath}`);
|
|
1519
|
+
}
|
|
1520
|
+
if (devServerConfig.ssr) {
|
|
1521
|
+
if (isExtensionLessPath(req.url.pathname)) {
|
|
1522
|
+
return ssrPageRequest(devServerConfig, serverCtx, req, res);
|
|
1523
|
+
}
|
|
1524
|
+
if (isSsrStaticDataPath(req.url.pathname)) {
|
|
1525
|
+
return ssrStaticDataRequest(devServerConfig, serverCtx, req, res);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
req.stats = await serverCtx.sys.stat(req.filePath);
|
|
1529
|
+
if (req.stats.isFile) {
|
|
1530
|
+
return serveFile(devServerConfig, serverCtx, req, res);
|
|
1531
|
+
}
|
|
1532
|
+
if (req.stats.isDirectory) {
|
|
1533
|
+
return serveDirectoryIndex(devServerConfig, serverCtx, req, res);
|
|
1534
|
+
}
|
|
1535
|
+
const xSource = ['notfound'];
|
|
1536
|
+
const validHistoryApi = isValidHistoryApi(devServerConfig, req);
|
|
1537
|
+
xSource.push(`validHistoryApi: ${validHistoryApi}`);
|
|
1538
|
+
if (validHistoryApi) {
|
|
1539
|
+
try {
|
|
1540
|
+
const indexFilePath = path__default['default'].join(devServerConfig.root, devServerConfig.historyApiFallback.index);
|
|
1541
|
+
xSource.push(`indexFilePath: ${indexFilePath}`);
|
|
1542
|
+
req.stats = await serverCtx.sys.stat(indexFilePath);
|
|
1543
|
+
if (req.stats.isFile) {
|
|
1544
|
+
req.filePath = indexFilePath;
|
|
1545
|
+
return serveFile(devServerConfig, serverCtx, req, res);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
catch (e) {
|
|
1549
|
+
xSource.push(`notfound error: ${e}`);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
return serverCtx.serve404(req, res, xSource.join(', '));
|
|
1553
|
+
}
|
|
1554
|
+
catch (e) {
|
|
1555
|
+
return serverCtx.serve500(incomingReq, res, e, `not found error`);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
if (typeof userRequestHandler === 'function') {
|
|
1559
|
+
await userRequestHandler(incomingReq, res, defaultHandler);
|
|
1560
|
+
}
|
|
1561
|
+
else {
|
|
1562
|
+
await defaultHandler();
|
|
1563
|
+
}
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
function isValidUrlBasePath(basePath, url) {
|
|
1567
|
+
// normalize the paths to always end with a slash for the check
|
|
1568
|
+
let pathname = url.pathname;
|
|
1569
|
+
if (!pathname.endsWith('/')) {
|
|
1570
|
+
pathname += '/';
|
|
1571
|
+
}
|
|
1572
|
+
if (!basePath.endsWith('/')) {
|
|
1573
|
+
basePath += '/';
|
|
1574
|
+
}
|
|
1575
|
+
return pathname.startsWith(basePath);
|
|
1576
|
+
}
|
|
1577
|
+
function normalizeHttpRequest(devServerConfig, incomingReq) {
|
|
1578
|
+
const req = {
|
|
1579
|
+
method: (incomingReq.method || 'GET').toUpperCase(),
|
|
1580
|
+
headers: incomingReq.headers,
|
|
1581
|
+
acceptHeader: (incomingReq.headers && typeof incomingReq.headers.accept === 'string' && incomingReq.headers.accept) || '',
|
|
1582
|
+
host: (incomingReq.headers && typeof incomingReq.headers.host === 'string' && incomingReq.headers.host) || null,
|
|
1583
|
+
url: null,
|
|
1584
|
+
searchParams: null,
|
|
1585
|
+
};
|
|
1586
|
+
const incomingUrl = (incomingReq.url || '').trim() || null;
|
|
1587
|
+
if (incomingUrl) {
|
|
1588
|
+
if (req.host) {
|
|
1589
|
+
req.url = new URL(incomingReq.url, `http://${req.host}`);
|
|
1590
|
+
}
|
|
1591
|
+
else {
|
|
1592
|
+
req.url = new URL(incomingReq.url, `http://rindojs-dev.web.app`);
|
|
1593
|
+
}
|
|
1594
|
+
req.searchParams = req.url.searchParams;
|
|
1595
|
+
}
|
|
1596
|
+
if (req.url) {
|
|
1597
|
+
const parts = req.url.pathname.replace(/\\/g, '/').split('/');
|
|
1598
|
+
req.pathname = parts.map((part) => decodeURIComponent(part)).join('/');
|
|
1599
|
+
if (req.pathname.length > 0 && !isDevClient(req.pathname)) {
|
|
1600
|
+
req.pathname = '/' + req.pathname.substring(devServerConfig.basePath.length);
|
|
1601
|
+
}
|
|
1602
|
+
req.filePath = normalizePath(path__default['default'].normalize(path__default['default'].join(devServerConfig.root, path__default['default'].relative('/', req.pathname))));
|
|
1603
|
+
}
|
|
1604
|
+
return req;
|
|
1605
|
+
}
|
|
1606
|
+
function isValidHistoryApi(devServerConfig, req) {
|
|
1607
|
+
if (!devServerConfig.historyApiFallback) {
|
|
1608
|
+
return false;
|
|
1609
|
+
}
|
|
1610
|
+
if (req.method !== 'GET') {
|
|
1611
|
+
return false;
|
|
1612
|
+
}
|
|
1613
|
+
if (!req.acceptHeader.includes('text/html')) {
|
|
1614
|
+
return false;
|
|
1615
|
+
}
|
|
1616
|
+
if (!devServerConfig.historyApiFallback.disableDotRule && req.pathname.includes('.')) {
|
|
1617
|
+
return false;
|
|
1618
|
+
}
|
|
1619
|
+
return true;
|
|
1598
1620
|
}
|
|
1599
1621
|
|
|
1600
|
-
function createHttpServer(devServerConfig, serverCtx) {
|
|
1601
|
-
// create our request handler
|
|
1602
|
-
const reqHandler = createRequestHandler(devServerConfig, serverCtx);
|
|
1603
|
-
const credentials = devServerConfig.https;
|
|
1604
|
-
return credentials ? https__namespace.createServer(credentials, reqHandler) : http__namespace.createServer(reqHandler);
|
|
1605
|
-
}
|
|
1606
|
-
async function findClosestOpenPort(host, port) {
|
|
1607
|
-
async function t(portToCheck) {
|
|
1608
|
-
const isTaken = await isPortTaken(host, portToCheck);
|
|
1609
|
-
if (!isTaken) {
|
|
1610
|
-
return portToCheck;
|
|
1611
|
-
}
|
|
1612
|
-
return t(portToCheck + 1);
|
|
1613
|
-
}
|
|
1614
|
-
return t(port);
|
|
1615
|
-
}
|
|
1616
|
-
function isPortTaken(host, port) {
|
|
1617
|
-
return new Promise((resolve, reject) => {
|
|
1618
|
-
const tester = net__namespace
|
|
1619
|
-
.createServer()
|
|
1620
|
-
.once('error', () => {
|
|
1621
|
-
resolve(true);
|
|
1622
|
-
})
|
|
1623
|
-
.once('listening', () => {
|
|
1624
|
-
tester
|
|
1625
|
-
.once('close', () => {
|
|
1626
|
-
resolve(false);
|
|
1627
|
-
})
|
|
1628
|
-
.close();
|
|
1629
|
-
})
|
|
1630
|
-
.on('error', (err) => {
|
|
1631
|
-
reject(err);
|
|
1632
|
-
})
|
|
1633
|
-
.listen(port, host);
|
|
1634
|
-
});
|
|
1622
|
+
function createHttpServer(devServerConfig, serverCtx) {
|
|
1623
|
+
// create our request handler
|
|
1624
|
+
const reqHandler = createRequestHandler(devServerConfig, serverCtx);
|
|
1625
|
+
const credentials = devServerConfig.https;
|
|
1626
|
+
return credentials ? https__namespace.createServer(credentials, reqHandler) : http__namespace.createServer(reqHandler);
|
|
1627
|
+
}
|
|
1628
|
+
async function findClosestOpenPort(host, port) {
|
|
1629
|
+
async function t(portToCheck) {
|
|
1630
|
+
const isTaken = await isPortTaken(host, portToCheck);
|
|
1631
|
+
if (!isTaken) {
|
|
1632
|
+
return portToCheck;
|
|
1633
|
+
}
|
|
1634
|
+
return t(portToCheck + 1);
|
|
1635
|
+
}
|
|
1636
|
+
return t(port);
|
|
1637
|
+
}
|
|
1638
|
+
function isPortTaken(host, port) {
|
|
1639
|
+
return new Promise((resolve, reject) => {
|
|
1640
|
+
const tester = net__namespace
|
|
1641
|
+
.createServer()
|
|
1642
|
+
.once('error', () => {
|
|
1643
|
+
resolve(true);
|
|
1644
|
+
})
|
|
1645
|
+
.once('listening', () => {
|
|
1646
|
+
tester
|
|
1647
|
+
.once('close', () => {
|
|
1648
|
+
resolve(false);
|
|
1649
|
+
})
|
|
1650
|
+
.close();
|
|
1651
|
+
})
|
|
1652
|
+
.on('error', (err) => {
|
|
1653
|
+
reject(err);
|
|
1654
|
+
})
|
|
1655
|
+
.listen(port, host);
|
|
1656
|
+
});
|
|
1635
1657
|
}
|
|
1636
1658
|
|
|
1637
|
-
function createWebSocket(httpServer, onMessageFromClient) {
|
|
1638
|
-
const wsConfig = {
|
|
1639
|
-
server: httpServer,
|
|
1640
|
-
};
|
|
1641
|
-
const wsServer = new ws__namespace.Server(wsConfig);
|
|
1642
|
-
function heartbeat() {
|
|
1643
|
-
// we need to coerce the `ws` type to our custom `DevWS` type here, since
|
|
1644
|
-
// this function is going to be passed in to `ws.on('pong'` which expects
|
|
1645
|
-
// to be passed a functon where `this` is bound to `ws`.
|
|
1646
|
-
this.isAlive = true;
|
|
1647
|
-
}
|
|
1648
|
-
wsServer.on('connection', (ws) => {
|
|
1649
|
-
ws.on('message', (data) => {
|
|
1650
|
-
// the server process has received a message from the browser
|
|
1651
|
-
// pass the message received from the browser to the main cli process
|
|
1652
|
-
try {
|
|
1653
|
-
onMessageFromClient(JSON.parse(data.toString()));
|
|
1654
|
-
}
|
|
1655
|
-
catch (e) {
|
|
1656
|
-
console.error(e);
|
|
1657
|
-
}
|
|
1658
|
-
});
|
|
1659
|
-
ws.isAlive = true;
|
|
1660
|
-
ws.on('pong', heartbeat);
|
|
1661
|
-
// ignore invalid close frames sent by Safari 15
|
|
1662
|
-
ws.on('error', console.error);
|
|
1663
|
-
});
|
|
1664
|
-
const pingInternval = setInterval(() => {
|
|
1665
|
-
wsServer.clients.forEach((ws) => {
|
|
1666
|
-
if (!ws.isAlive) {
|
|
1667
|
-
return ws.close(1000);
|
|
1668
|
-
}
|
|
1669
|
-
ws.isAlive = false;
|
|
1670
|
-
ws.ping(noop);
|
|
1671
|
-
});
|
|
1672
|
-
}, 10000);
|
|
1673
|
-
return {
|
|
1674
|
-
sendToBrowser: (msg) => {
|
|
1675
|
-
if (msg && wsServer && wsServer.clients) {
|
|
1676
|
-
const data = JSON.stringify(msg);
|
|
1677
|
-
wsServer.clients.forEach((ws) => {
|
|
1678
|
-
if (ws.readyState === ws.OPEN) {
|
|
1679
|
-
ws.send(data);
|
|
1680
|
-
}
|
|
1681
|
-
});
|
|
1682
|
-
}
|
|
1683
|
-
},
|
|
1684
|
-
close: () => {
|
|
1685
|
-
return new Promise((resolve, reject) => {
|
|
1686
|
-
clearInterval(pingInternval);
|
|
1687
|
-
wsServer.clients.forEach((ws) => {
|
|
1688
|
-
ws.close(1000);
|
|
1689
|
-
});
|
|
1690
|
-
wsServer.close((err) => {
|
|
1691
|
-
if (err) {
|
|
1692
|
-
reject(err);
|
|
1693
|
-
}
|
|
1694
|
-
else {
|
|
1695
|
-
resolve();
|
|
1696
|
-
}
|
|
1697
|
-
});
|
|
1698
|
-
});
|
|
1699
|
-
},
|
|
1700
|
-
};
|
|
1659
|
+
function createWebSocket(httpServer, onMessageFromClient) {
|
|
1660
|
+
const wsConfig = {
|
|
1661
|
+
server: httpServer,
|
|
1662
|
+
};
|
|
1663
|
+
const wsServer = new ws__namespace.Server(wsConfig);
|
|
1664
|
+
function heartbeat() {
|
|
1665
|
+
// we need to coerce the `ws` type to our custom `DevWS` type here, since
|
|
1666
|
+
// this function is going to be passed in to `ws.on('pong'` which expects
|
|
1667
|
+
// to be passed a functon where `this` is bound to `ws`.
|
|
1668
|
+
this.isAlive = true;
|
|
1669
|
+
}
|
|
1670
|
+
wsServer.on('connection', (ws) => {
|
|
1671
|
+
ws.on('message', (data) => {
|
|
1672
|
+
// the server process has received a message from the browser
|
|
1673
|
+
// pass the message received from the browser to the main cli process
|
|
1674
|
+
try {
|
|
1675
|
+
onMessageFromClient(JSON.parse(data.toString()));
|
|
1676
|
+
}
|
|
1677
|
+
catch (e) {
|
|
1678
|
+
console.error(e);
|
|
1679
|
+
}
|
|
1680
|
+
});
|
|
1681
|
+
ws.isAlive = true;
|
|
1682
|
+
ws.on('pong', heartbeat);
|
|
1683
|
+
// ignore invalid close frames sent by Safari 15
|
|
1684
|
+
ws.on('error', console.error);
|
|
1685
|
+
});
|
|
1686
|
+
const pingInternval = setInterval(() => {
|
|
1687
|
+
wsServer.clients.forEach((ws) => {
|
|
1688
|
+
if (!ws.isAlive) {
|
|
1689
|
+
return ws.close(1000);
|
|
1690
|
+
}
|
|
1691
|
+
ws.isAlive = false;
|
|
1692
|
+
ws.ping(noop);
|
|
1693
|
+
});
|
|
1694
|
+
}, 10000);
|
|
1695
|
+
return {
|
|
1696
|
+
sendToBrowser: (msg) => {
|
|
1697
|
+
if (msg && wsServer && wsServer.clients) {
|
|
1698
|
+
const data = JSON.stringify(msg);
|
|
1699
|
+
wsServer.clients.forEach((ws) => {
|
|
1700
|
+
if (ws.readyState === ws.OPEN) {
|
|
1701
|
+
ws.send(data);
|
|
1702
|
+
}
|
|
1703
|
+
});
|
|
1704
|
+
}
|
|
1705
|
+
},
|
|
1706
|
+
close: () => {
|
|
1707
|
+
return new Promise((resolve, reject) => {
|
|
1708
|
+
clearInterval(pingInternval);
|
|
1709
|
+
wsServer.clients.forEach((ws) => {
|
|
1710
|
+
ws.close(1000);
|
|
1711
|
+
});
|
|
1712
|
+
wsServer.close((err) => {
|
|
1713
|
+
if (err) {
|
|
1714
|
+
reject(err);
|
|
1715
|
+
}
|
|
1716
|
+
else {
|
|
1717
|
+
resolve();
|
|
1718
|
+
}
|
|
1719
|
+
});
|
|
1720
|
+
});
|
|
1721
|
+
},
|
|
1722
|
+
};
|
|
1701
1723
|
}
|
|
1702
1724
|
|
|
1703
|
-
function initServerProcess(sendMsg) {
|
|
1704
|
-
let server = null;
|
|
1705
|
-
let webSocket = null;
|
|
1706
|
-
let serverCtx = null;
|
|
1707
|
-
const buildResultsResolves = [];
|
|
1708
|
-
const compilerRequestResolves = [];
|
|
1709
|
-
const startServer = async (msg) => {
|
|
1710
|
-
const devServerConfig = msg.startServer;
|
|
1711
|
-
devServerConfig.port = await findClosestOpenPort(devServerConfig.address, devServerConfig.port);
|
|
1712
|
-
devServerConfig.browserUrl = getBrowserUrl(devServerConfig.protocol, devServerConfig.address, devServerConfig.port, devServerConfig.basePath, '/');
|
|
1713
|
-
devServerConfig.root = normalizePath(devServerConfig.root);
|
|
1714
|
-
const sys = index_js.createNodeSys({ process });
|
|
1715
|
-
serverCtx = createServerContext(sys, sendMsg, devServerConfig, buildResultsResolves, compilerRequestResolves);
|
|
1716
|
-
server = createHttpServer(devServerConfig, serverCtx);
|
|
1717
|
-
webSocket = devServerConfig.websocket ? createWebSocket(server, sendMsg) : null;
|
|
1718
|
-
server.listen(devServerConfig.port, devServerConfig.address);
|
|
1719
|
-
serverCtx.isServerListening = true;
|
|
1720
|
-
if (devServerConfig.openBrowser) {
|
|
1721
|
-
const initialLoadUrl = getBrowserUrl(devServerConfig.protocol, devServerConfig.address, devServerConfig.port, devServerConfig.basePath, devServerConfig.initialLoadUrl || DEV_SERVER_INIT_URL);
|
|
1722
|
-
openInBrowser({ url: initialLoadUrl });
|
|
1723
|
-
}
|
|
1724
|
-
sendMsg({ serverStarted: devServerConfig });
|
|
1725
|
-
};
|
|
1726
|
-
const closeServer = () => {
|
|
1727
|
-
const promises = [];
|
|
1728
|
-
buildResultsResolves.forEach((r) => r.reject('dev server closed'));
|
|
1729
|
-
buildResultsResolves.length = 0;
|
|
1730
|
-
compilerRequestResolves.forEach((r) => r.reject('dev server closed'));
|
|
1731
|
-
compilerRequestResolves.length = 0;
|
|
1732
|
-
if (serverCtx) {
|
|
1733
|
-
if (serverCtx.sys) {
|
|
1734
|
-
promises.push(serverCtx.sys.destroy());
|
|
1735
|
-
}
|
|
1736
|
-
}
|
|
1737
|
-
if (webSocket) {
|
|
1738
|
-
promises.push(webSocket.close());
|
|
1739
|
-
webSocket = null;
|
|
1740
|
-
}
|
|
1741
|
-
if (server) {
|
|
1742
|
-
promises.push(new Promise((resolve) => {
|
|
1743
|
-
server.close((err) => {
|
|
1744
|
-
if (err) {
|
|
1745
|
-
console.error(`close error: ${err}`);
|
|
1746
|
-
}
|
|
1747
|
-
resolve();
|
|
1748
|
-
});
|
|
1749
|
-
}));
|
|
1750
|
-
}
|
|
1751
|
-
Promise.all(promises).finally(() => {
|
|
1752
|
-
sendMsg({
|
|
1753
|
-
serverClosed: true,
|
|
1754
|
-
});
|
|
1755
|
-
});
|
|
1756
|
-
};
|
|
1757
|
-
const receiveMessageFromMain = (msg) => {
|
|
1758
|
-
// the server process received a message from main thread
|
|
1759
|
-
try {
|
|
1760
|
-
if (msg) {
|
|
1761
|
-
if (msg.startServer) {
|
|
1762
|
-
startServer(msg);
|
|
1763
|
-
}
|
|
1764
|
-
else if (msg.closeServer) {
|
|
1765
|
-
closeServer();
|
|
1766
|
-
}
|
|
1767
|
-
else if (msg.compilerRequestResults) {
|
|
1768
|
-
for (let i = compilerRequestResolves.length - 1; i >= 0; i--) {
|
|
1769
|
-
const r = compilerRequestResolves[i];
|
|
1770
|
-
if (r.path === msg.compilerRequestResults.path) {
|
|
1771
|
-
r.resolve(msg.compilerRequestResults);
|
|
1772
|
-
compilerRequestResolves.splice(i, 1);
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
else if (serverCtx) {
|
|
1777
|
-
if (msg.buildResults && !msg.isActivelyBuilding) {
|
|
1778
|
-
buildResultsResolves.forEach((r) => r.resolve(msg.buildResults));
|
|
1779
|
-
buildResultsResolves.length = 0;
|
|
1780
|
-
}
|
|
1781
|
-
if (webSocket) {
|
|
1782
|
-
webSocket.sendToBrowser(msg);
|
|
1783
|
-
}
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
catch (e) {
|
|
1788
|
-
let stack = null;
|
|
1789
|
-
if (e instanceof Error) {
|
|
1790
|
-
stack = e.stack;
|
|
1791
|
-
}
|
|
1792
|
-
sendMsg({
|
|
1793
|
-
error: { message: e + '', stack },
|
|
1794
|
-
});
|
|
1795
|
-
}
|
|
1796
|
-
};
|
|
1797
|
-
return receiveMessageFromMain;
|
|
1725
|
+
function initServerProcess(sendMsg) {
|
|
1726
|
+
let server = null;
|
|
1727
|
+
let webSocket = null;
|
|
1728
|
+
let serverCtx = null;
|
|
1729
|
+
const buildResultsResolves = [];
|
|
1730
|
+
const compilerRequestResolves = [];
|
|
1731
|
+
const startServer = async (msg) => {
|
|
1732
|
+
const devServerConfig = msg.startServer;
|
|
1733
|
+
devServerConfig.port = await findClosestOpenPort(devServerConfig.address, devServerConfig.port);
|
|
1734
|
+
devServerConfig.browserUrl = getBrowserUrl(devServerConfig.protocol, devServerConfig.address, devServerConfig.port, devServerConfig.basePath, '/');
|
|
1735
|
+
devServerConfig.root = normalizePath(devServerConfig.root);
|
|
1736
|
+
const sys = index_js.createNodeSys({ process });
|
|
1737
|
+
serverCtx = createServerContext(sys, sendMsg, devServerConfig, buildResultsResolves, compilerRequestResolves);
|
|
1738
|
+
server = createHttpServer(devServerConfig, serverCtx);
|
|
1739
|
+
webSocket = devServerConfig.websocket ? createWebSocket(server, sendMsg) : null;
|
|
1740
|
+
server.listen(devServerConfig.port, devServerConfig.address);
|
|
1741
|
+
serverCtx.isServerListening = true;
|
|
1742
|
+
if (devServerConfig.openBrowser) {
|
|
1743
|
+
const initialLoadUrl = getBrowserUrl(devServerConfig.protocol, devServerConfig.address, devServerConfig.port, devServerConfig.basePath, devServerConfig.initialLoadUrl || DEV_SERVER_INIT_URL);
|
|
1744
|
+
openInBrowser({ url: initialLoadUrl });
|
|
1745
|
+
}
|
|
1746
|
+
sendMsg({ serverStarted: devServerConfig });
|
|
1747
|
+
};
|
|
1748
|
+
const closeServer = () => {
|
|
1749
|
+
const promises = [];
|
|
1750
|
+
buildResultsResolves.forEach((r) => r.reject('dev server closed'));
|
|
1751
|
+
buildResultsResolves.length = 0;
|
|
1752
|
+
compilerRequestResolves.forEach((r) => r.reject('dev server closed'));
|
|
1753
|
+
compilerRequestResolves.length = 0;
|
|
1754
|
+
if (serverCtx) {
|
|
1755
|
+
if (serverCtx.sys) {
|
|
1756
|
+
promises.push(serverCtx.sys.destroy());
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
if (webSocket) {
|
|
1760
|
+
promises.push(webSocket.close());
|
|
1761
|
+
webSocket = null;
|
|
1762
|
+
}
|
|
1763
|
+
if (server) {
|
|
1764
|
+
promises.push(new Promise((resolve) => {
|
|
1765
|
+
server.close((err) => {
|
|
1766
|
+
if (err) {
|
|
1767
|
+
console.error(`close error: ${err}`);
|
|
1768
|
+
}
|
|
1769
|
+
resolve();
|
|
1770
|
+
});
|
|
1771
|
+
}));
|
|
1772
|
+
}
|
|
1773
|
+
Promise.all(promises).finally(() => {
|
|
1774
|
+
sendMsg({
|
|
1775
|
+
serverClosed: true,
|
|
1776
|
+
});
|
|
1777
|
+
});
|
|
1778
|
+
};
|
|
1779
|
+
const receiveMessageFromMain = (msg) => {
|
|
1780
|
+
// the server process received a message from main thread
|
|
1781
|
+
try {
|
|
1782
|
+
if (msg) {
|
|
1783
|
+
if (msg.startServer) {
|
|
1784
|
+
startServer(msg);
|
|
1785
|
+
}
|
|
1786
|
+
else if (msg.closeServer) {
|
|
1787
|
+
closeServer();
|
|
1788
|
+
}
|
|
1789
|
+
else if (msg.compilerRequestResults) {
|
|
1790
|
+
for (let i = compilerRequestResolves.length - 1; i >= 0; i--) {
|
|
1791
|
+
const r = compilerRequestResolves[i];
|
|
1792
|
+
if (r.path === msg.compilerRequestResults.path) {
|
|
1793
|
+
r.resolve(msg.compilerRequestResults);
|
|
1794
|
+
compilerRequestResolves.splice(i, 1);
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
else if (serverCtx) {
|
|
1799
|
+
if (msg.buildResults && !msg.isActivelyBuilding) {
|
|
1800
|
+
buildResultsResolves.forEach((r) => r.resolve(msg.buildResults));
|
|
1801
|
+
buildResultsResolves.length = 0;
|
|
1802
|
+
}
|
|
1803
|
+
if (webSocket) {
|
|
1804
|
+
webSocket.sendToBrowser(msg);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
catch (e) {
|
|
1810
|
+
let stack = null;
|
|
1811
|
+
if (e instanceof Error) {
|
|
1812
|
+
stack = e.stack;
|
|
1813
|
+
}
|
|
1814
|
+
sendMsg({
|
|
1815
|
+
error: { message: e + '', stack },
|
|
1816
|
+
});
|
|
1817
|
+
}
|
|
1818
|
+
};
|
|
1819
|
+
return receiveMessageFromMain;
|
|
1798
1820
|
}
|
|
1799
1821
|
|
|
1800
1822
|
exports.initServerProcess = initServerProcess;
|