@jsenv/core 41.0.2 → 41.0.4
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/dist/build/build.js +1 -43
- package/dist/start_build_server/jsenv_core_packages.js +1 -114
- package/dist/start_build_server/start_build_server.js +13 -59
- package/dist/start_dev_server/jsenv_core_packages.js +2213 -2213
- package/dist/start_dev_server/start_dev_server.js +9240 -9200
- package/package.json +2 -2
- package/src/build/start_build_server.js +12 -58
- package/src/{plugins/chrome_devtools_json/jsenv_plugin_chrome_devtools_json.js → dev/dev_server_plugins/dev_server_plugin_chrome_devtools_json.js} +4 -6
- package/src/dev/dev_server_plugins/dev_server_plugin_inject_server_response_header.js +27 -0
- package/src/dev/dev_server_plugins/dev_server_plugin_omega_error_handler.js +44 -0
- package/src/dev/dev_server_plugins/dev_server_plugin_serve_source_files.js +410 -0
- package/src/dev/start_dev_server.js +88 -521
- package/src/plugins/plugins.js +0 -2
- /package/src/dev/{user_agent.js → dev_server_plugins/user_agent.js} +0 -0
|
@@ -1,35 +1,27 @@
|
|
|
1
1
|
import {
|
|
2
2
|
assertAndNormalizeDirectoryUrl,
|
|
3
|
-
bufferToEtag,
|
|
4
3
|
lookupPackageDirectory,
|
|
5
4
|
} from "@jsenv/filesystem";
|
|
6
|
-
import { createLogger, createTaskLog
|
|
5
|
+
import { createLogger, createTaskLog } from "@jsenv/humanize";
|
|
7
6
|
import {
|
|
8
|
-
composeTwoResponses,
|
|
9
7
|
jsenvAccessControlAllowedHeaders,
|
|
10
|
-
serveDirectory,
|
|
11
8
|
serverPluginCORS,
|
|
12
|
-
serverPluginErrorHandler,
|
|
13
9
|
startServer,
|
|
14
10
|
} from "@jsenv/server";
|
|
15
|
-
import { convertFileSystemErrorToResponseProperties } from "@jsenv/server/src/plugins/filesystem/filesystem_error_to_response.js";
|
|
16
|
-
import { URL_META } from "@jsenv/url-meta";
|
|
17
11
|
import { urlIsOrIsInsideOf, urlToRelativeUrl } from "@jsenv/urls";
|
|
18
|
-
import { existsSync
|
|
12
|
+
import { existsSync } from "node:fs";
|
|
13
|
+
|
|
19
14
|
import { defaultRuntimeCompat } from "../build/build_params.js";
|
|
20
15
|
import { createEventEmitter } from "../helpers/event_emitter.js";
|
|
21
|
-
import { watchSourceFiles } from "../helpers/watch_source_files.js";
|
|
22
|
-
import { WEB_URL_CONVERTER } from "../helpers/web_url_converter.js";
|
|
23
16
|
import { jsenvCoreDirectoryUrl } from "../jsenv_core_directory_url.js";
|
|
24
|
-
import { createKitchen } from "../kitchen/kitchen.js";
|
|
25
17
|
import { createPackageDirectory } from "../kitchen/package_directory.js";
|
|
26
|
-
import {
|
|
27
|
-
createJsenvPluginsController,
|
|
28
|
-
createJsenvPluginStore,
|
|
29
|
-
} from "../plugins/jsenv_plugins_controller.js";
|
|
18
|
+
import { createJsenvPluginStore } from "../plugins/jsenv_plugins_controller.js";
|
|
30
19
|
import { getCorePlugins } from "../plugins/plugins.js";
|
|
31
20
|
import { jsenvPluginServerEvents } from "../plugins/server_events/jsenv_plugin_server_events.js";
|
|
32
|
-
import {
|
|
21
|
+
import { devServerPluginChromeDevToolsJson } from "./dev_server_plugins/dev_server_plugin_chrome_devtools_json.js";
|
|
22
|
+
import { devServerPluginInjectServerResponseHeader } from "./dev_server_plugins/dev_server_plugin_inject_server_response_header.js";
|
|
23
|
+
import { devServerPluginOmegaErrorHandler } from "./dev_server_plugins/dev_server_plugin_omega_error_handler.js";
|
|
24
|
+
import { devServerPluginServeSourceFiles } from "./dev_server_plugins/dev_server_plugin_serve_source_files.js";
|
|
33
25
|
|
|
34
26
|
const EXECUTED_BY_TEST_PLAN = process.argv.includes("--jsenv-test");
|
|
35
27
|
|
|
@@ -152,7 +144,6 @@ export const startDevServer = async ({
|
|
|
152
144
|
);
|
|
153
145
|
}
|
|
154
146
|
}
|
|
155
|
-
|
|
156
147
|
// params normalization
|
|
157
148
|
{
|
|
158
149
|
if (clientAutoreload === true) {
|
|
@@ -174,511 +165,94 @@ export const startDevServer = async ({
|
|
|
174
165
|
serverStopAbortController.abort();
|
|
175
166
|
});
|
|
176
167
|
const serverStopAbortSignal = serverStopAbortController.signal;
|
|
177
|
-
const kitchenCache = new Map();
|
|
178
168
|
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
fetch: () =>
|
|
191
|
-
Response.json({
|
|
192
|
-
server: "jsenv_dev_server/1",
|
|
193
|
-
sourceDirectoryUrl,
|
|
194
|
-
}),
|
|
195
|
-
},
|
|
196
|
-
],
|
|
197
|
-
injectResponseProperties: () => {
|
|
198
|
-
return {
|
|
199
|
-
headers: {
|
|
200
|
-
server: "jsenv_dev_server/1",
|
|
201
|
-
},
|
|
202
|
-
};
|
|
203
|
-
},
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
// cors service
|
|
207
|
-
{
|
|
208
|
-
finalServerPlugins.push(
|
|
209
|
-
serverPluginCORS({
|
|
210
|
-
accessControlAllowRequestOrigin: true,
|
|
211
|
-
accessControlAllowRequestMethod: true,
|
|
212
|
-
accessControlAllowRequestHeaders: true,
|
|
213
|
-
accessControlAllowedRequestHeaders: [
|
|
214
|
-
...jsenvAccessControlAllowedHeaders,
|
|
215
|
-
"x-jsenv-execution-id",
|
|
216
|
-
],
|
|
217
|
-
accessControlAllowCredentials: true,
|
|
218
|
-
timingAllowOrigin: true,
|
|
219
|
-
}),
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
// custom server plugins
|
|
223
|
-
{
|
|
224
|
-
finalServerPlugins.push(...serverPlugins);
|
|
225
|
-
}
|
|
226
|
-
// file_service
|
|
227
|
-
{
|
|
228
|
-
const clientFileChangeEventEmitter = createEventEmitter();
|
|
229
|
-
const clientFileDereferencedEventEmitter = createEventEmitter();
|
|
230
|
-
clientAutoreload = {
|
|
231
|
-
enabled: true,
|
|
232
|
-
clientServerEventsConfig: {},
|
|
233
|
-
clientFileChangeEventEmitter,
|
|
234
|
-
clientFileDereferencedEventEmitter,
|
|
235
|
-
...clientAutoreload,
|
|
236
|
-
};
|
|
237
|
-
const stopWatchingSourceFiles = watchSourceFiles(
|
|
238
|
-
sourceDirectoryUrl,
|
|
239
|
-
(fileInfo) => {
|
|
240
|
-
clientFileChangeEventEmitter.emit(fileInfo);
|
|
241
|
-
},
|
|
242
|
-
{
|
|
243
|
-
sourceFilesConfig,
|
|
244
|
-
keepProcessAlive: false,
|
|
245
|
-
cooldownBetweenFileEvents: clientAutoreload.cooldownBetweenFileEvents,
|
|
246
|
-
},
|
|
247
|
-
);
|
|
248
|
-
serverStopCallbackSet.add(stopWatchingSourceFiles);
|
|
169
|
+
const kitchenCache = new Map();
|
|
170
|
+
const packageDirectory = createPackageDirectory({ sourceDirectoryUrl });
|
|
171
|
+
const clientFileChangeEventEmitter = createEventEmitter();
|
|
172
|
+
const clientFileDereferencedEventEmitter = createEventEmitter();
|
|
173
|
+
clientAutoreload = {
|
|
174
|
+
enabled: true,
|
|
175
|
+
clientServerEventsConfig: {},
|
|
176
|
+
clientFileChangeEventEmitter,
|
|
177
|
+
clientFileDereferencedEventEmitter,
|
|
178
|
+
...clientAutoreload,
|
|
179
|
+
};
|
|
249
180
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
181
|
+
const devServerJsenvPluginStore = await createJsenvPluginStore([
|
|
182
|
+
jsenvPluginServerEvents({ clientAutoreload }),
|
|
183
|
+
...plugins,
|
|
184
|
+
...getCorePlugins({
|
|
185
|
+
packageDirectory,
|
|
186
|
+
rootDirectoryUrl: sourceDirectoryUrl,
|
|
187
|
+
mainFilePath: sourceMainFilePath,
|
|
188
|
+
runtimeCompat,
|
|
189
|
+
sourceFilesConfig,
|
|
253
190
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
191
|
+
referenceAnalysis,
|
|
192
|
+
nodeEsmResolution,
|
|
193
|
+
packageConditions,
|
|
194
|
+
packageConditionsConfig,
|
|
195
|
+
magicExtensions,
|
|
196
|
+
magicDirectoryIndex,
|
|
197
|
+
directoryListing,
|
|
198
|
+
supervisor,
|
|
199
|
+
injections,
|
|
200
|
+
transpilation,
|
|
201
|
+
spa,
|
|
202
|
+
packageBundle,
|
|
263
203
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
supervisor,
|
|
272
|
-
injections,
|
|
273
|
-
transpilation,
|
|
274
|
-
spa,
|
|
275
|
-
packageBundle,
|
|
204
|
+
clientAutoreload,
|
|
205
|
+
clientAutoreloadOnServerRestart,
|
|
206
|
+
cacheControl,
|
|
207
|
+
ribbon,
|
|
208
|
+
dropToOpen,
|
|
209
|
+
}),
|
|
210
|
+
]);
|
|
276
211
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
} else {
|
|
304
|
-
urlInfo.onModified();
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
});
|
|
308
|
-
const clientRuntimeCompat = { [runtimeName]: runtimeVersion };
|
|
309
|
-
|
|
310
|
-
kitchen = createKitchen({
|
|
311
|
-
name: runtimeId,
|
|
312
|
-
signal: serverStopAbortSignal,
|
|
313
|
-
logLevel,
|
|
314
|
-
rootDirectoryUrl: sourceDirectoryUrl,
|
|
315
|
-
mainFilePath: sourceMainFilePath,
|
|
316
|
-
ignore,
|
|
317
|
-
dev: true,
|
|
318
|
-
runtimeCompat,
|
|
319
|
-
clientRuntimeCompat,
|
|
320
|
-
supervisor,
|
|
321
|
-
sourcemaps,
|
|
322
|
-
sourcemapsSourcesContent,
|
|
323
|
-
outDirectoryUrl: outDirectoryUrl
|
|
324
|
-
? new URL(`${runtimeName}@${runtimeVersion}/`, outDirectoryUrl)
|
|
325
|
-
: undefined,
|
|
326
|
-
packageDirectory,
|
|
327
|
-
});
|
|
328
|
-
kitchen.graph.urlInfoCreatedEventEmitter.on((urlInfoCreated) => {
|
|
329
|
-
const { watch } = URL_META.applyAssociations({
|
|
330
|
-
url: urlInfoCreated.url,
|
|
331
|
-
associations: watchAssociations,
|
|
332
|
-
});
|
|
333
|
-
urlInfoCreated.isWatched = watch;
|
|
334
|
-
// when an url depends on many others, we check all these (like package.json)
|
|
335
|
-
urlInfoCreated.isValid = () => {
|
|
336
|
-
const seenSet = new Set();
|
|
337
|
-
const checkValidity = (urlInfo) => {
|
|
338
|
-
if (seenSet.has(urlInfo)) {
|
|
339
|
-
return true;
|
|
340
|
-
}
|
|
341
|
-
seenSet.add(urlInfo);
|
|
342
|
-
if (!urlInfo.url.startsWith("file:")) {
|
|
343
|
-
return false;
|
|
344
|
-
}
|
|
345
|
-
if (urlInfo.content === undefined) {
|
|
346
|
-
// urlInfo content is undefined when:
|
|
347
|
-
// - url info content never fetched
|
|
348
|
-
// - it is considered as modified because undelying file is watched and got saved
|
|
349
|
-
// - it is considered as modified because underlying file content
|
|
350
|
-
// was compared using etag and it has changed
|
|
351
|
-
return false;
|
|
352
|
-
}
|
|
353
|
-
if (!urlInfo.isWatched) {
|
|
354
|
-
// file is not watched, check the filesystem
|
|
355
|
-
let fileContentAsBuffer;
|
|
356
|
-
try {
|
|
357
|
-
fileContentAsBuffer = readFileSync(new URL(urlInfo.url));
|
|
358
|
-
} catch (e) {
|
|
359
|
-
if (e.code === "ENOENT") {
|
|
360
|
-
urlInfo.onModified();
|
|
361
|
-
return false;
|
|
362
|
-
}
|
|
363
|
-
return false;
|
|
364
|
-
}
|
|
365
|
-
const fileContentEtag = bufferToEtag(fileContentAsBuffer);
|
|
366
|
-
if (fileContentEtag !== urlInfo.originalContentEtag) {
|
|
367
|
-
urlInfo.onModified();
|
|
368
|
-
// restore content to be able to compare it again later
|
|
369
|
-
urlInfo.kitchen.urlInfoTransformer.setContent(
|
|
370
|
-
urlInfo,
|
|
371
|
-
String(fileContentAsBuffer),
|
|
372
|
-
{
|
|
373
|
-
contentEtag: fileContentEtag,
|
|
374
|
-
},
|
|
375
|
-
);
|
|
376
|
-
return false;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
for (const implicitUrl of urlInfo.implicitUrlSet) {
|
|
380
|
-
const implicitUrlInfo = urlInfo.graph.getUrlInfo(implicitUrl);
|
|
381
|
-
if (!implicitUrlInfo) {
|
|
382
|
-
continue;
|
|
383
|
-
}
|
|
384
|
-
if (implicitUrlInfo.content === undefined) {
|
|
385
|
-
// happens when we explicitely load an url with a search param
|
|
386
|
-
// - it creates an implicit url info to the url without params
|
|
387
|
-
// - we never explicitely request the url without search param so it has no content
|
|
388
|
-
// in that case the underlying urlInfo cannot be invalidate by the implicit
|
|
389
|
-
// we use modifiedTimestamp to detect if the url was loaded once
|
|
390
|
-
// or is just here to be used later
|
|
391
|
-
if (implicitUrlInfo.modifiedTimestamp) {
|
|
392
|
-
return false;
|
|
393
|
-
}
|
|
394
|
-
continue;
|
|
395
|
-
}
|
|
396
|
-
if (!checkValidity(implicitUrlInfo)) {
|
|
397
|
-
return false;
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
return true;
|
|
401
|
-
};
|
|
402
|
-
const valid = checkValidity(urlInfoCreated);
|
|
403
|
-
return valid;
|
|
404
|
-
};
|
|
405
|
-
});
|
|
406
|
-
kitchen.graph.urlInfoDereferencedEventEmitter.on(
|
|
407
|
-
(urlInfoDereferenced, lastReferenceFromOther) => {
|
|
408
|
-
clientFileDereferencedEventEmitter.emit(
|
|
409
|
-
urlInfoDereferenced,
|
|
410
|
-
lastReferenceFromOther,
|
|
411
|
-
);
|
|
412
|
-
},
|
|
413
|
-
);
|
|
414
|
-
const devServerJsenvPluginController = await createJsenvPluginsController(
|
|
415
|
-
devServerJsenvPluginStore,
|
|
416
|
-
kitchen,
|
|
417
|
-
);
|
|
418
|
-
kitchen.setJsenvPluginsController(devServerJsenvPluginController);
|
|
212
|
+
const finalServerPlugins = [];
|
|
213
|
+
finalServerPlugins.push(
|
|
214
|
+
// "header" service
|
|
215
|
+
devServerPluginInjectServerResponseHeader({ sourceDirectoryUrl }),
|
|
216
|
+
// cors service
|
|
217
|
+
serverPluginCORS({
|
|
218
|
+
accessControlAllowRequestOrigin: true,
|
|
219
|
+
accessControlAllowRequestMethod: true,
|
|
220
|
+
accessControlAllowRequestHeaders: true,
|
|
221
|
+
accessControlAllowedRequestHeaders: [
|
|
222
|
+
...jsenvAccessControlAllowedHeaders,
|
|
223
|
+
"x-jsenv-execution-id",
|
|
224
|
+
],
|
|
225
|
+
accessControlAllowCredentials: true,
|
|
226
|
+
timingAllowOrigin: true,
|
|
227
|
+
}),
|
|
228
|
+
// chrome devtools
|
|
229
|
+
devServerPluginChromeDevToolsJson({ sourceDirectoryUrl }),
|
|
230
|
+
...serverPlugins,
|
|
231
|
+
devServerPluginServeSourceFiles({
|
|
232
|
+
packageDirectory,
|
|
233
|
+
sourceDirectoryUrl,
|
|
234
|
+
sourceMainFilePath,
|
|
235
|
+
ignore,
|
|
236
|
+
sourceFilesConfig,
|
|
237
|
+
clientAutoreload,
|
|
419
238
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
kitchenCache.set(runtimeId, kitchen);
|
|
424
|
-
onKitchenCreated(kitchen);
|
|
425
|
-
return kitchen;
|
|
426
|
-
};
|
|
239
|
+
logLevel,
|
|
240
|
+
runtimeCompat,
|
|
241
|
+
onKitchenCreated,
|
|
427
242
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
return { kitchen };
|
|
433
|
-
},
|
|
434
|
-
routes: [
|
|
435
|
-
...devServerJsenvPluginStore.allServerRoutes,
|
|
436
|
-
{
|
|
437
|
-
endpoint: "GET *",
|
|
438
|
-
description: "Serve project files.",
|
|
439
|
-
declarationSource: import.meta.url,
|
|
440
|
-
fetch: async (request, { kitchen }) => {
|
|
441
|
-
const { rootDirectoryUrl, mainFilePath } = kitchen.context;
|
|
442
|
-
let requestResource = request.resource;
|
|
443
|
-
let requestedUrl;
|
|
444
|
-
if (requestResource.startsWith("/@fs/")) {
|
|
445
|
-
const fsRootRelativeUrl = requestResource.slice("/@fs/".length);
|
|
446
|
-
requestedUrl = `file:///${fsRootRelativeUrl}`;
|
|
447
|
-
} else {
|
|
448
|
-
const requestedUrlObject = new URL(
|
|
449
|
-
requestResource === "/"
|
|
450
|
-
? mainFilePath
|
|
451
|
-
: requestResource.slice(1),
|
|
452
|
-
rootDirectoryUrl,
|
|
453
|
-
);
|
|
454
|
-
requestedUrlObject.searchParams.delete("hot");
|
|
455
|
-
requestedUrl = requestedUrlObject.href;
|
|
456
|
-
}
|
|
457
|
-
const { referer } = request.headers;
|
|
458
|
-
const parentUrl = referer
|
|
459
|
-
? WEB_URL_CONVERTER.asFileUrl(referer, {
|
|
460
|
-
origin: request.origin,
|
|
461
|
-
rootDirectoryUrl: sourceDirectoryUrl,
|
|
462
|
-
})
|
|
463
|
-
: sourceDirectoryUrl;
|
|
464
|
-
let reference = kitchen.graph.inferReference(
|
|
465
|
-
request.resource,
|
|
466
|
-
parentUrl,
|
|
467
|
-
);
|
|
468
|
-
if (reference) {
|
|
469
|
-
reference.urlInfo.context.request = request;
|
|
470
|
-
reference.urlInfo.context.requestedUrl = requestedUrl;
|
|
471
|
-
} else {
|
|
472
|
-
const rootUrlInfo = kitchen.graph.rootUrlInfo;
|
|
473
|
-
rootUrlInfo.context.request = request;
|
|
474
|
-
rootUrlInfo.context.requestedUrl = requestedUrl;
|
|
475
|
-
reference = rootUrlInfo.dependencies.createResolveAndFinalize({
|
|
476
|
-
trace: { message: parentUrl },
|
|
477
|
-
type: "http_request",
|
|
478
|
-
specifier: request.resource,
|
|
479
|
-
});
|
|
480
|
-
reference.urlInfo.context.requestedUrl = requestedUrl;
|
|
481
|
-
rootUrlInfo.context.request = null;
|
|
482
|
-
rootUrlInfo.context.requestedUrl = null;
|
|
483
|
-
}
|
|
484
|
-
const urlInfo = reference.urlInfo;
|
|
485
|
-
const ifNoneMatch = request.headers["if-none-match"];
|
|
486
|
-
const urlInfoTargetedByCache =
|
|
487
|
-
urlInfo.findParentIfInline() || urlInfo;
|
|
243
|
+
supervisor,
|
|
244
|
+
sourcemaps,
|
|
245
|
+
sourcemapsSourcesContent,
|
|
246
|
+
outDirectoryUrl,
|
|
488
247
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
urlInfoTargetedByCache.isValid()
|
|
498
|
-
) {
|
|
499
|
-
const headers = {
|
|
500
|
-
"cache-control": `private,max-age=0,must-revalidate`,
|
|
501
|
-
};
|
|
502
|
-
Object.keys(urlInfo.headers).forEach((key) => {
|
|
503
|
-
if (key !== "content-length") {
|
|
504
|
-
headers[key] = urlInfo.headers[key];
|
|
505
|
-
}
|
|
506
|
-
});
|
|
507
|
-
return {
|
|
508
|
-
status: 304,
|
|
509
|
-
headers,
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
await urlInfo.cook({ request, reference });
|
|
514
|
-
let { response } = urlInfo;
|
|
515
|
-
if (response) {
|
|
516
|
-
return response;
|
|
517
|
-
}
|
|
518
|
-
response = {
|
|
519
|
-
url: reference.url,
|
|
520
|
-
status: 200,
|
|
521
|
-
headers: {
|
|
522
|
-
// when we send eTag to the client the next request to the server
|
|
523
|
-
// will send etag in request headers.
|
|
524
|
-
// If they match jsenv bypass cooking and returns 304
|
|
525
|
-
// This must not happen when a plugin uses "no-store" or "no-cache" as it means
|
|
526
|
-
// plugin logic wants to happens for every request to this url
|
|
527
|
-
...(cacheIsDisabledInResponseHeader(urlInfoTargetedByCache)
|
|
528
|
-
? {
|
|
529
|
-
"cache-control": "no-store", // for inline file we force no-store when parent is no-store
|
|
530
|
-
}
|
|
531
|
-
: {
|
|
532
|
-
"cache-control": `private,max-age=0,must-revalidate`,
|
|
533
|
-
// it's safe to use "_" separator because etag is encoded with base64 (see https://stackoverflow.com/a/13195197)
|
|
534
|
-
"eTag": `${urlInfoTargetedByCache.originalContentEtag}_${urlInfoTargetedByCache.contentEtag}`,
|
|
535
|
-
}),
|
|
536
|
-
...urlInfo.headers,
|
|
537
|
-
"content-type": urlInfo.contentType,
|
|
538
|
-
"content-length": urlInfo.contentLength,
|
|
539
|
-
},
|
|
540
|
-
body: urlInfo.content,
|
|
541
|
-
timing: urlInfo.timing, // TODO: use something else
|
|
542
|
-
};
|
|
543
|
-
const augmentResponseInfo = {
|
|
544
|
-
...kitchen.context,
|
|
545
|
-
reference,
|
|
546
|
-
urlInfo,
|
|
547
|
-
};
|
|
548
|
-
kitchen.jsenvPluginsController.callHooks(
|
|
549
|
-
"augmentResponse",
|
|
550
|
-
augmentResponseInfo,
|
|
551
|
-
(returnValue) => {
|
|
552
|
-
response = composeTwoResponses(response, returnValue);
|
|
553
|
-
},
|
|
554
|
-
);
|
|
555
|
-
return response;
|
|
556
|
-
} catch (error) {
|
|
557
|
-
const originalError = error ? error.cause || error : error;
|
|
558
|
-
if (originalError.asResponse) {
|
|
559
|
-
return originalError.asResponse();
|
|
560
|
-
}
|
|
561
|
-
const code = originalError.code;
|
|
562
|
-
if (code === "PARSE_ERROR") {
|
|
563
|
-
// when possible let browser re-throw the syntax error
|
|
564
|
-
// it's not possible to do that when url info content is not available
|
|
565
|
-
// (happens for js_module_fallback for instance)
|
|
566
|
-
if (urlInfo.content !== undefined) {
|
|
567
|
-
kitchen.context.logger
|
|
568
|
-
.error(`Error while handling ${request.url}:
|
|
569
|
-
${originalError.reasonCode || originalError.code}
|
|
570
|
-
${error.trace?.message}`);
|
|
571
|
-
return {
|
|
572
|
-
url: reference.url,
|
|
573
|
-
status: 200,
|
|
574
|
-
// reason becomes the http response statusText, it must not contain invalid chars
|
|
575
|
-
// https://github.com/nodejs/node/blob/0c27ca4bc9782d658afeaebcec85ec7b28f1cc35/lib/_http_common.js#L221
|
|
576
|
-
statusText: error.reason,
|
|
577
|
-
statusMessage: originalError.message,
|
|
578
|
-
headers: {
|
|
579
|
-
"content-type": urlInfo.contentType,
|
|
580
|
-
"content-length": urlInfo.contentLength,
|
|
581
|
-
"cache-control": "no-store",
|
|
582
|
-
},
|
|
583
|
-
body: urlInfo.content,
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
return {
|
|
587
|
-
url: reference.url,
|
|
588
|
-
status: 500,
|
|
589
|
-
statusText: error.reason,
|
|
590
|
-
statusMessage: originalError.message,
|
|
591
|
-
headers: {
|
|
592
|
-
"cache-control": "no-store",
|
|
593
|
-
},
|
|
594
|
-
body: urlInfo.content,
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
if (code === "DIRECTORY_REFERENCE_NOT_ALLOWED") {
|
|
598
|
-
return serveDirectory(reference.url, {
|
|
599
|
-
headers: {
|
|
600
|
-
accept: "text/html",
|
|
601
|
-
},
|
|
602
|
-
canReadDirectory: true,
|
|
603
|
-
rootDirectoryUrl: sourceDirectoryUrl,
|
|
604
|
-
});
|
|
605
|
-
}
|
|
606
|
-
if (code === "NOT_ALLOWED") {
|
|
607
|
-
return {
|
|
608
|
-
url: reference.url,
|
|
609
|
-
status: 403,
|
|
610
|
-
statusText: originalError.reason,
|
|
611
|
-
};
|
|
612
|
-
}
|
|
613
|
-
if (code === "NOT_FOUND") {
|
|
614
|
-
return {
|
|
615
|
-
url: reference.url,
|
|
616
|
-
status: 404,
|
|
617
|
-
statusText: originalError.reason,
|
|
618
|
-
statusMessage: originalError.message,
|
|
619
|
-
};
|
|
620
|
-
}
|
|
621
|
-
return {
|
|
622
|
-
url: reference.url,
|
|
623
|
-
status: 500,
|
|
624
|
-
statusText: error.reason,
|
|
625
|
-
statusMessage: formatError(error),
|
|
626
|
-
headers: {
|
|
627
|
-
"cache-control": "no-store",
|
|
628
|
-
},
|
|
629
|
-
};
|
|
630
|
-
}
|
|
631
|
-
},
|
|
632
|
-
},
|
|
633
|
-
],
|
|
634
|
-
});
|
|
635
|
-
finalServerPlugins.push(...devServerJsenvPluginStore.allServerPlugins);
|
|
636
|
-
}
|
|
637
|
-
// jsenv error handler service
|
|
638
|
-
{
|
|
639
|
-
finalServerPlugins.push({
|
|
640
|
-
name: "jsenv:omega_error_handler",
|
|
641
|
-
handleError: (error) => {
|
|
642
|
-
const getResponseForError = () => {
|
|
643
|
-
if (error && error.asResponse) {
|
|
644
|
-
return error.asResponse();
|
|
645
|
-
}
|
|
646
|
-
if (error && error.statusText === "Unexpected directory operation") {
|
|
647
|
-
return {
|
|
648
|
-
status: 403,
|
|
649
|
-
};
|
|
650
|
-
}
|
|
651
|
-
return convertFileSystemErrorToResponseProperties(error);
|
|
652
|
-
};
|
|
653
|
-
const response = getResponseForError();
|
|
654
|
-
if (!response) {
|
|
655
|
-
return null;
|
|
656
|
-
}
|
|
657
|
-
const body = JSON.stringify({
|
|
658
|
-
status: response.status,
|
|
659
|
-
statusText: response.statusText,
|
|
660
|
-
headers: response.headers,
|
|
661
|
-
body: response.body,
|
|
662
|
-
});
|
|
663
|
-
return {
|
|
664
|
-
status: response.status,
|
|
665
|
-
headers: {
|
|
666
|
-
"content-type": "application/json",
|
|
667
|
-
"content-length": Buffer.byteLength(body),
|
|
668
|
-
},
|
|
669
|
-
body,
|
|
670
|
-
};
|
|
671
|
-
},
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
// default error handler
|
|
675
|
-
{
|
|
676
|
-
finalServerPlugins.push(
|
|
677
|
-
serverPluginErrorHandler({
|
|
678
|
-
sendErrorDetails: true,
|
|
679
|
-
}),
|
|
680
|
-
);
|
|
681
|
-
}
|
|
248
|
+
serverStopAbortSignal,
|
|
249
|
+
serverStopCallbackSet,
|
|
250
|
+
devServerJsenvPluginStore,
|
|
251
|
+
kitchenCache,
|
|
252
|
+
}),
|
|
253
|
+
// jsenv error handler service
|
|
254
|
+
devServerPluginOmegaErrorHandler(),
|
|
255
|
+
);
|
|
682
256
|
|
|
683
257
|
const server = await startServer({
|
|
684
258
|
signal,
|
|
@@ -724,10 +298,3 @@ export const startDevServer = async ({
|
|
|
724
298
|
kitchenCache,
|
|
725
299
|
};
|
|
726
300
|
};
|
|
727
|
-
|
|
728
|
-
const cacheIsDisabledInResponseHeader = (urlInfo) => {
|
|
729
|
-
return (
|
|
730
|
-
urlInfo.headers["cache-control"] === "no-store" ||
|
|
731
|
-
urlInfo.headers["cache-control"] === "no-cache"
|
|
732
|
-
);
|
|
733
|
-
};
|
package/src/plugins/plugins.js
CHANGED
|
@@ -25,7 +25,6 @@ import { jsenvPluginCacheControl } from "./cache_control/jsenv_plugin_cache_cont
|
|
|
25
25
|
import { jsenvPluginRibbon } from "./ribbon/jsenv_plugin_ribbon.js";
|
|
26
26
|
import { jsenvPluginDropToOpen } from "./drop_to_open/jsenv_plugin_drop_to_open.js";
|
|
27
27
|
import { jsenvPluginCleanHTML } from "./clean_html/jsenv_plugin_clean_html.js";
|
|
28
|
-
import { jsenvPluginChromeDevtoolsJson } from "./chrome_devtools_json/jsenv_plugin_chrome_devtools_json.js";
|
|
29
28
|
import { jsenvPluginAutoreloadOnServerRestart } from "./autoreload_on_server_restart/jsenv_plugin_autoreload_on_server_restart.js";
|
|
30
29
|
import { jsenvPluginPackageSideEffects } from "./package_side_effects/jsenv_plugin_package_side_effects.js";
|
|
31
30
|
import { jsenvPluginWorkspaceBundle } from "./workspace_bundle/jsenv_plugin_workspace_bundle.js";
|
|
@@ -156,7 +155,6 @@ export const getCorePlugins = ({
|
|
|
156
155
|
...(ribbon ? [jsenvPluginRibbon({ rootDirectoryUrl, ...ribbon })] : []),
|
|
157
156
|
...(dropToOpen ? [jsenvPluginDropToOpen()] : []),
|
|
158
157
|
jsenvPluginCleanHTML(),
|
|
159
|
-
jsenvPluginChromeDevtoolsJson(),
|
|
160
158
|
...(packageSideEffects
|
|
161
159
|
? [jsenvPluginPackageSideEffects({ packageDirectory })]
|
|
162
160
|
: []),
|
|
File without changes
|