@jsenv/core 27.3.4 → 27.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -5928,19 +5928,6 @@ const generateInlineContentUrl = ({
5928
5928
  return inlineContentUrl;
5929
5929
  };
5930
5930
 
5931
- const urlToFileSystemPath = url => {
5932
- let urlString = String(url);
5933
-
5934
- if (urlString[urlString.length - 1] === "/") {
5935
- // remove trailing / so that nodejs path becomes predictable otherwise it logs
5936
- // the trailing slash on linux but does not on windows
5937
- urlString = urlString.slice(0, -1);
5938
- }
5939
-
5940
- const fileSystemPath = fileURLToPath(urlString);
5941
- return fileSystemPath;
5942
- };
5943
-
5944
5931
  // consider switching to https://babeljs.io/docs/en/babel-code-frame
5945
5932
  const stringifyUrlSite = ({
5946
5933
  url,
@@ -5953,7 +5940,7 @@ const stringifyUrlSite = ({
5953
5940
  lineMaxLength,
5954
5941
  color
5955
5942
  } = {}) => {
5956
- let string = `${humanizeUrl(url)}`;
5943
+ let string = url;
5957
5944
 
5958
5945
  if (typeof line === "number") {
5959
5946
  string += `:${line}`;
@@ -5978,15 +5965,6 @@ const stringifyUrlSite = ({
5978
5965
  return `${string}
5979
5966
  ${sourceLoc}`;
5980
5967
  };
5981
- const humanizeUrl = url => {
5982
- if (url.startsWith("file://")) {
5983
- // we prefer file system path because vscode reliably make them clickable
5984
- // and sometimes it won't for file:// urls
5985
- return urlToFileSystemPath(url);
5986
- }
5987
-
5988
- return url;
5989
- };
5990
5968
  const showSourceLocation = ({
5991
5969
  content,
5992
5970
  line,
@@ -6489,6 +6467,19 @@ const urlToBasename = url => {
6489
6467
  return basename;
6490
6468
  };
6491
6469
 
6470
+ const urlToFileSystemPath = url => {
6471
+ let urlString = String(url);
6472
+
6473
+ if (urlString[urlString.length - 1] === "/") {
6474
+ // remove trailing / so that nodejs path becomes predictable otherwise it logs
6475
+ // the trailing slash on linux but does not on windows
6476
+ urlString = urlString.slice(0, -1);
6477
+ }
6478
+
6479
+ const fileSystemPath = fileURLToPath(urlString);
6480
+ return fileSystemPath;
6481
+ };
6482
+
6492
6483
  const assertAndNormalizeDirectoryUrl = value => {
6493
6484
  let urlString;
6494
6485
 
@@ -8622,7 +8613,9 @@ const jsenvPluginUrlAnalysis = ({
8622
8613
  type: "filesystem",
8623
8614
  subtype: "directory_entry",
8624
8615
  specifier: directoryEntryName,
8625
- trace: `"${directoryRelativeUrl}${directoryEntryName}" entry in directory referenced by ${originalDirectoryReference.trace}`
8616
+ trace: {
8617
+ message: `"${directoryRelativeUrl}${directoryEntryName}" entry in directory referenced by ${originalDirectoryReference.trace.message}`
8618
+ }
8626
8619
  });
8627
8620
  });
8628
8621
  }
@@ -12037,6 +12030,8 @@ const jsenvPluginInlineUrls = () => {
12037
12030
  };
12038
12031
  };
12039
12032
 
12033
+ const requireFromJsenv = createRequire(import.meta.url);
12034
+
12040
12035
  /*
12041
12036
  * Things happening here
12042
12037
  * - html supervisor module injection
@@ -12054,13 +12049,32 @@ const jsenvPluginHtmlSupervisor = ({
12054
12049
  dev: true,
12055
12050
  test: true
12056
12051
  },
12052
+ serve: request => {
12053
+ if (!request.ressource.startsWith("/__open_in_editor__/")) {
12054
+ return null;
12055
+ }
12056
+
12057
+ const file = request.ressource.slice("/__open_in_editor__/".length);
12058
+
12059
+ if (!file) {
12060
+ return {
12061
+ status: 400,
12062
+ body: 'Missing "file" in url search params'
12063
+ };
12064
+ }
12065
+
12066
+ const launch = requireFromJsenv("launch-editor");
12067
+ launch(fileURLToPath(file), () => {// ignore error for now
12068
+ });
12069
+ return {
12070
+ status: 200
12071
+ };
12072
+ },
12057
12073
  transformUrlContent: {
12058
12074
  html: ({
12059
12075
  url,
12060
12076
  content
12061
- }, {
12062
- referenceUtils
12063
- }) => {
12077
+ }, context) => {
12064
12078
  const htmlAst = parseHtmlString(content);
12065
12079
  const scriptsToSupervise = [];
12066
12080
 
@@ -12083,7 +12097,7 @@ const jsenvPluginHtmlSupervisor = ({
12083
12097
  lineEnd,
12084
12098
  columnEnd
12085
12099
  });
12086
- const [inlineScriptReference] = referenceUtils.foundInline({
12100
+ const [inlineScriptReference] = context.referenceUtils.foundInline({
12087
12101
  type: "script_src",
12088
12102
  expectedType: {
12089
12103
  classic: "js_classic",
@@ -12160,7 +12174,7 @@ const jsenvPluginHtmlSupervisor = ({
12160
12174
  }
12161
12175
  }
12162
12176
  });
12163
- const [htmlSupervisorInstallerFileReference] = referenceUtils.inject({
12177
+ const [htmlSupervisorInstallerFileReference] = context.referenceUtils.inject({
12164
12178
  type: "js_import_export",
12165
12179
  expectedType: "js_module",
12166
12180
  specifier: htmlSupervisorInstallerFileUrl
@@ -12172,11 +12186,12 @@ const jsenvPluginHtmlSupervisor = ({
12172
12186
  import { installHtmlSupervisor } from ${htmlSupervisorInstallerFileReference.generatedSpecifier}
12173
12187
  installHtmlSupervisor(${JSON.stringify({
12174
12188
  logs,
12175
- measurePerf
12189
+ measurePerf,
12190
+ rootDirectoryUrl: context.rootDirectoryUrl
12176
12191
  }, null, " ")})`,
12177
12192
  "injected-by": "jsenv:html_supervisor"
12178
12193
  }));
12179
- const [htmlSupervisorSetupFileReference] = referenceUtils.inject({
12194
+ const [htmlSupervisorSetupFileReference] = context.referenceUtils.inject({
12180
12195
  type: "script_src",
12181
12196
  expectedType: "js_classic",
12182
12197
  specifier: htmlSupervisorSetupFileUrl
@@ -12763,8 +12778,6 @@ export default inlineContent.text`,
12763
12778
  return [asJsonModule, asCssModule, asTextModule];
12764
12779
  };
12765
12780
 
12766
- const requireFromJsenv = createRequire(import.meta.url);
12767
-
12768
12781
  const babelPluginPackagePath = requireFromJsenv.resolve("@jsenv/babel-plugins");
12769
12782
  const babelPluginPackageUrl = pathToFileURL(babelPluginPackagePath);
12770
12783
  const requireBabelPlugin = createRequire(babelPluginPackageUrl);
@@ -20226,267 +20239,216 @@ const jsenvPluginDevSSEClient = () => {
20226
20239
  };
20227
20240
  };
20228
20241
 
20229
- const createSSEService = ({
20230
- serverEventCallbackList
20231
- }) => {
20232
- const destroyCallbackList = createCallbackListNotifiedOnce();
20233
- const cache = [];
20234
- const sseRoomLimit = 100;
20235
-
20236
- const getOrCreateSSERoom = request => {
20237
- const htmlFileRelativeUrl = request.ressource.slice(1);
20238
- const cacheEntry = cache.find(cacheEntryCandidate => cacheEntryCandidate.htmlFileRelativeUrl === htmlFileRelativeUrl);
20239
-
20240
- if (cacheEntry) {
20241
- return cacheEntry.sseRoom;
20242
- }
20243
-
20244
- const sseRoom = createSSERoom({
20245
- retryDuration: 2000,
20246
- historyLength: 100,
20247
- welcomeEventEnabled: true,
20248
- effect: () => {
20249
- return serverEventCallbackList.add(event => {
20250
- sseRoom.sendEvent(event);
20251
- });
20252
- }
20253
- });
20254
- const removeSSECleanupCallback = destroyCallbackList.add(() => {
20255
- removeSSECleanupCallback();
20256
- sseRoom.close();
20257
- });
20258
- cache.push({
20259
- htmlFileRelativeUrl,
20260
- sseRoom,
20261
- cleanup: () => {
20262
- removeSSECleanupCallback();
20263
- sseRoom.close();
20264
- }
20265
- });
20266
-
20267
- if (cache.length >= sseRoomLimit) {
20268
- const firstCacheEntry = cache.shift();
20269
- firstCacheEntry.cleanup();
20270
- }
20271
-
20272
- return sseRoom;
20273
- };
20274
-
20275
- return {
20276
- getOrCreateSSERoom,
20277
- destroy: () => {
20278
- destroyCallbackList.notify();
20279
- }
20280
- };
20281
- };
20282
-
20283
20242
  const jsenvPluginDevSSEServer = ({
20284
- rootDirectoryUrl,
20285
- urlGraph,
20286
20243
  clientFileChangeCallbackList,
20287
20244
  clientFilesPruneCallbackList
20288
20245
  }) => {
20289
- const serverEventCallbackList = createCallbackList();
20290
- const sseService = createSSEService({
20291
- serverEventCallbackList
20292
- });
20293
-
20294
- const notifyDeclined = ({
20295
- cause,
20296
- reason,
20297
- declinedBy
20298
- }) => {
20299
- serverEventCallbackList.notify({
20300
- type: "reload",
20301
- data: JSON.stringify({
20246
+ return {
20247
+ name: "jsenv:sse_server",
20248
+ appliesDuring: {
20249
+ dev: true
20250
+ },
20251
+ registerServerEvents: ({
20252
+ sendServerEvent
20253
+ }, {
20254
+ rootDirectoryUrl,
20255
+ urlGraph
20256
+ }) => {
20257
+ const notifyDeclined = ({
20302
20258
  cause,
20303
- type: "full",
20304
- typeReason: reason,
20259
+ reason,
20305
20260
  declinedBy
20306
- })
20307
- });
20308
- };
20261
+ }) => {
20262
+ sendServerEvent({
20263
+ type: "reload",
20264
+ data: {
20265
+ cause,
20266
+ type: "full",
20267
+ typeReason: reason,
20268
+ declinedBy
20269
+ }
20270
+ });
20271
+ };
20309
20272
 
20310
- const notifyAccepted = ({
20311
- cause,
20312
- reason,
20313
- instructions
20314
- }) => {
20315
- serverEventCallbackList.notify({
20316
- type: "reload",
20317
- data: JSON.stringify({
20273
+ const notifyAccepted = ({
20318
20274
  cause,
20319
- type: "hot",
20320
- typeReason: reason,
20321
- hotInstructions: instructions
20322
- })
20323
- });
20324
- };
20275
+ reason,
20276
+ instructions
20277
+ }) => {
20278
+ sendServerEvent({
20279
+ type: "reload",
20280
+ data: {
20281
+ cause,
20282
+ type: "hot",
20283
+ typeReason: reason,
20284
+ hotInstructions: instructions
20285
+ }
20286
+ });
20287
+ };
20325
20288
 
20326
- const propagateUpdate = firstUrlInfo => {
20327
- const iterate = (urlInfo, trace) => {
20328
- if (urlInfo.data.hotAcceptSelf) {
20329
- return {
20330
- accepted: true,
20331
- reason: urlInfo === firstUrlInfo ? `file accepts hot reload` : `a dependent file accepts hot reload`,
20332
- instructions: [{
20333
- type: urlInfo.type,
20334
- boundary: urlToRelativeUrl(urlInfo.url, rootDirectoryUrl),
20335
- acceptedBy: urlToRelativeUrl(urlInfo.url, rootDirectoryUrl)
20336
- }]
20337
- };
20338
- }
20289
+ const propagateUpdate = firstUrlInfo => {
20290
+ const iterate = (urlInfo, seen) => {
20291
+ if (urlInfo.data.hotAcceptSelf) {
20292
+ return {
20293
+ accepted: true,
20294
+ reason: urlInfo === firstUrlInfo ? `file accepts hot reload` : `a dependent file accepts hot reload`,
20295
+ instructions: [{
20296
+ type: urlInfo.type,
20297
+ boundary: urlToRelativeUrl(urlInfo.url, rootDirectoryUrl),
20298
+ acceptedBy: urlToRelativeUrl(urlInfo.url, rootDirectoryUrl)
20299
+ }]
20300
+ };
20301
+ }
20339
20302
 
20340
- const {
20341
- dependents
20342
- } = urlInfo;
20343
- const instructions = [];
20303
+ const {
20304
+ dependents
20305
+ } = urlInfo;
20306
+ const instructions = [];
20344
20307
 
20345
- for (const dependentUrl of dependents) {
20346
- const dependentUrlInfo = urlGraph.getUrlInfo(dependentUrl);
20308
+ for (const dependentUrl of dependents) {
20309
+ const dependentUrlInfo = urlGraph.getUrlInfo(dependentUrl);
20347
20310
 
20348
- if (dependentUrlInfo.data.hotDecline) {
20349
- return {
20350
- declined: true,
20351
- reason: `a dependent file declines hot reload`,
20352
- declinedBy: dependentUrl
20353
- };
20354
- }
20311
+ if (dependentUrlInfo.data.hotDecline) {
20312
+ return {
20313
+ declined: true,
20314
+ reason: `a dependent file declines hot reload`,
20315
+ declinedBy: dependentUrl
20316
+ };
20317
+ }
20355
20318
 
20356
- const {
20357
- hotAcceptDependencies = []
20358
- } = dependentUrlInfo.data;
20319
+ const {
20320
+ hotAcceptDependencies = []
20321
+ } = dependentUrlInfo.data;
20322
+
20323
+ if (hotAcceptDependencies.includes(urlInfo.url)) {
20324
+ instructions.push({
20325
+ type: dependentUrlInfo.type,
20326
+ boundary: urlToRelativeUrl(dependentUrl, rootDirectoryUrl),
20327
+ acceptedBy: urlToRelativeUrl(urlInfo.url, rootDirectoryUrl)
20328
+ });
20329
+ continue;
20330
+ }
20359
20331
 
20360
- if (hotAcceptDependencies.includes(urlInfo.url)) {
20361
- instructions.push({
20362
- type: dependentUrlInfo.type,
20363
- boundary: urlToRelativeUrl(dependentUrl, rootDirectoryUrl),
20364
- acceptedBy: urlToRelativeUrl(urlInfo.url, rootDirectoryUrl)
20365
- });
20366
- continue;
20367
- }
20332
+ if (seen.includes(dependentUrl)) {
20333
+ return {
20334
+ declined: true,
20335
+ reason: "circular dependency",
20336
+ declinedBy: urlToRelativeUrl(dependentUrl, rootDirectoryUrl)
20337
+ };
20338
+ }
20368
20339
 
20369
- if (trace.includes(dependentUrl)) {
20370
- return {
20371
- declined: true,
20372
- reason: "circular dependency",
20373
- declinedBy: urlToRelativeUrl(dependentUrl, rootDirectoryUrl)
20374
- };
20375
- }
20340
+ const dependentPropagationResult = iterate(dependentUrlInfo, [...seen, dependentUrl]);
20376
20341
 
20377
- const dependentPropagationResult = iterate(dependentUrlInfo, [...trace, dependentUrl]);
20342
+ if (dependentPropagationResult.accepted) {
20343
+ instructions.push(...dependentPropagationResult.instructions);
20344
+ continue;
20345
+ }
20378
20346
 
20379
- if (dependentPropagationResult.accepted) {
20380
- instructions.push(...dependentPropagationResult.instructions);
20381
- continue;
20382
- }
20347
+ if ( // declined explicitely by an other file, it must decline the whole update
20348
+ dependentPropagationResult.declinedBy) {
20349
+ return dependentPropagationResult;
20350
+ } // declined by absence of boundary, we can keep searching
20383
20351
 
20384
- if ( // declined explicitely by an other file, it must decline the whole update
20385
- dependentPropagationResult.declinedBy) {
20386
- return dependentPropagationResult;
20387
- } // declined by absence of boundary, we can keep searching
20388
20352
 
20353
+ continue;
20354
+ }
20389
20355
 
20390
- continue;
20391
- }
20356
+ if (instructions.length === 0) {
20357
+ return {
20358
+ declined: true,
20359
+ reason: `there is no file accepting hot reload while propagating update`
20360
+ };
20361
+ }
20392
20362
 
20393
- if (instructions.length === 0) {
20394
- return {
20395
- declined: true,
20396
- reason: `there is no file accepting hot reload while propagating update`
20363
+ return {
20364
+ accepted: true,
20365
+ reason: `${instructions.length} dependent file(s) accepts hot reload`,
20366
+ instructions
20367
+ };
20397
20368
  };
20398
- }
20399
20369
 
20400
- return {
20401
- accepted: true,
20402
- reason: `${instructions.length} dependent file(s) accepts hot reload`,
20403
- instructions
20370
+ const seen = [];
20371
+ return iterate(firstUrlInfo, seen);
20404
20372
  };
20405
- };
20406
20373
 
20407
- const trace = [];
20408
- return iterate(firstUrlInfo, trace);
20409
- };
20410
-
20411
- clientFileChangeCallbackList.push(({
20412
- url,
20413
- event
20414
- }) => {
20415
- const urlInfo = urlGraph.getUrlInfo(url); // file not part of dependency graph
20374
+ clientFileChangeCallbackList.push(({
20375
+ url,
20376
+ event
20377
+ }) => {
20378
+ const urlInfo = urlGraph.getUrlInfo(url); // file not part of dependency graph
20416
20379
 
20417
- if (!urlInfo) {
20418
- return;
20419
- }
20380
+ if (!urlInfo) {
20381
+ return;
20382
+ }
20420
20383
 
20421
- const relativeUrl = urlToRelativeUrl(url, rootDirectoryUrl);
20422
- const hotUpdate = propagateUpdate(urlInfo);
20384
+ const relativeUrl = urlToRelativeUrl(url, rootDirectoryUrl);
20385
+ const hotUpdate = propagateUpdate(urlInfo);
20423
20386
 
20424
- if (hotUpdate.declined) {
20425
- notifyDeclined({
20426
- cause: `${relativeUrl} ${event}`,
20427
- reason: hotUpdate.reason,
20428
- declinedBy: hotUpdate.declinedBy
20429
- });
20430
- } else {
20431
- notifyAccepted({
20432
- cause: `${relativeUrl} ${event}`,
20433
- reason: hotUpdate.reason,
20434
- instructions: hotUpdate.instructions
20387
+ if (hotUpdate.declined) {
20388
+ notifyDeclined({
20389
+ cause: `${relativeUrl} ${event}`,
20390
+ reason: hotUpdate.reason,
20391
+ declinedBy: hotUpdate.declinedBy
20392
+ });
20393
+ } else {
20394
+ notifyAccepted({
20395
+ cause: `${relativeUrl} ${event}`,
20396
+ reason: hotUpdate.reason,
20397
+ instructions: hotUpdate.instructions
20398
+ });
20399
+ }
20435
20400
  });
20436
- }
20437
- });
20438
- clientFilesPruneCallbackList.push(({
20439
- prunedUrlInfos,
20440
- firstUrlInfo
20441
- }) => {
20442
- const mainHotUpdate = propagateUpdate(firstUrlInfo);
20443
- const cause = `following files are no longer referenced: ${prunedUrlInfos.map(prunedUrlInfo => urlToRelativeUrl(prunedUrlInfo.url, rootDirectoryUrl))}`; // now check if we can hot update the main ressource
20444
- // then if we can hot update all dependencies
20401
+ clientFilesPruneCallbackList.push(({
20402
+ prunedUrlInfos,
20403
+ firstUrlInfo
20404
+ }) => {
20405
+ const mainHotUpdate = propagateUpdate(firstUrlInfo);
20406
+ const cause = `following files are no longer referenced: ${prunedUrlInfos.map(prunedUrlInfo => urlToRelativeUrl(prunedUrlInfo.url, rootDirectoryUrl))}`; // now check if we can hot update the main ressource
20407
+ // then if we can hot update all dependencies
20408
+
20409
+ if (mainHotUpdate.declined) {
20410
+ notifyDeclined({
20411
+ cause,
20412
+ reason: mainHotUpdate.reason,
20413
+ declinedBy: mainHotUpdate.declinedBy
20414
+ });
20415
+ return;
20416
+ } // main can hot update
20445
20417
 
20446
- if (mainHotUpdate.declined) {
20447
- notifyDeclined({
20448
- cause,
20449
- reason: mainHotUpdate.reason,
20450
- declinedBy: mainHotUpdate.declinedBy
20451
- });
20452
- return;
20453
- } // main can hot update
20454
20418
 
20419
+ let i = 0;
20420
+ const instructions = [];
20455
20421
 
20456
- let i = 0;
20457
- const instructions = [];
20422
+ while (i < prunedUrlInfos.length) {
20423
+ const prunedUrlInfo = prunedUrlInfos[i++];
20458
20424
 
20459
- while (i < prunedUrlInfos.length) {
20460
- const prunedUrlInfo = prunedUrlInfos[i++];
20425
+ if (prunedUrlInfo.data.hotDecline) {
20426
+ notifyDeclined({
20427
+ cause,
20428
+ reason: `a pruned file declines hot reload`,
20429
+ declinedBy: urlToRelativeUrl(prunedUrlInfo.url, rootDirectoryUrl)
20430
+ });
20431
+ return;
20432
+ }
20433
+
20434
+ instructions.push({
20435
+ type: "prune",
20436
+ boundary: urlToRelativeUrl(prunedUrlInfo.url, rootDirectoryUrl),
20437
+ acceptedBy: urlToRelativeUrl(firstUrlInfo.url, rootDirectoryUrl)
20438
+ });
20439
+ }
20461
20440
 
20462
- if (prunedUrlInfo.data.hotDecline) {
20463
- notifyDeclined({
20441
+ notifyAccepted({
20464
20442
  cause,
20465
- reason: `a pruned file declines hot reload`,
20466
- declinedBy: urlToRelativeUrl(prunedUrlInfo.url, rootDirectoryUrl)
20443
+ reason: mainHotUpdate.reason,
20444
+ instructions
20467
20445
  });
20468
- return;
20469
- }
20470
-
20471
- instructions.push({
20472
- type: "prune",
20473
- boundary: urlToRelativeUrl(prunedUrlInfo.url, rootDirectoryUrl),
20474
- acceptedBy: urlToRelativeUrl(firstUrlInfo.url, rootDirectoryUrl)
20475
20446
  });
20476
- }
20477
-
20478
- notifyAccepted({
20479
- cause,
20480
- reason: mainHotUpdate.reason,
20481
- instructions
20482
- });
20483
- });
20484
- return {
20485
- name: "jsenv:sse_server",
20486
- appliesDuring: {
20487
- dev: true
20488
20447
  },
20489
- serve: request => {
20448
+ serve: (request, {
20449
+ rootDirectoryUrl,
20450
+ urlGraph
20451
+ }) => {
20490
20452
  if (request.ressource === "/__graph__") {
20491
20453
  const graphJson = JSON.stringify(urlGraph.toJSON(rootDirectoryUrl));
20492
20454
  return {
@@ -20499,26 +20461,12 @@ const jsenvPluginDevSSEServer = ({
20499
20461
  };
20500
20462
  }
20501
20463
 
20502
- const {
20503
- accept
20504
- } = request.headers;
20505
-
20506
- if (accept && accept.includes("text/event-stream")) {
20507
- const room = sseService.getOrCreateSSERoom(request);
20508
- return room.join(request);
20509
- }
20510
-
20511
20464
  return null;
20512
- },
20513
- destroy: () => {
20514
- sseService.destroy();
20515
20465
  }
20516
20466
  };
20517
20467
  };
20518
20468
 
20519
20469
  const jsenvPluginAutoreload = ({
20520
- rootDirectoryUrl,
20521
- urlGraph,
20522
20470
  scenario,
20523
20471
  clientFileChangeCallbackList,
20524
20472
  clientFilesPruneCallbackList
@@ -20528,8 +20476,6 @@ const jsenvPluginAutoreload = ({
20528
20476
  }
20529
20477
 
20530
20478
  return [jsenvPluginHmr(), jsenvPluginDevSSEClient(), jsenvPluginDevSSEServer({
20531
- rootDirectoryUrl,
20532
- urlGraph,
20533
20479
  clientFileChangeCallbackList,
20534
20480
  clientFilesPruneCallbackList
20535
20481
  })];
@@ -20618,8 +20564,6 @@ const getCorePlugins = ({
20618
20564
  }), jsenvPluginUrlResolution(), jsenvPluginUrlVersion(), jsenvPluginCommonJsGlobals(), jsenvPluginImportMetaScenarios(), jsenvPluginNodeRuntime({
20619
20565
  runtimeCompat
20620
20566
  }), jsenvPluginBundling(bundling), jsenvPluginMinification(minification), jsenvPluginImportMetaHot(), ...(clientAutoreload ? [jsenvPluginAutoreload({ ...clientAutoreload,
20621
- rootDirectoryUrl,
20622
- urlGraph,
20623
20567
  scenario,
20624
20568
  clientFileChangeCallbackList,
20625
20569
  clientFilesPruneCallbackList
@@ -21506,7 +21450,7 @@ const createResolveUrlError = ({
21506
21450
  reason,
21507
21451
  ...details,
21508
21452
  "specifier": `"${reference.specifier}"`,
21509
- "specifier trace": reference.trace,
21453
+ "specifier trace": reference.trace.message,
21510
21454
  ...detailsFromPluginController(pluginController)
21511
21455
  }));
21512
21456
  resolveError.name = "RESOLVE_URL_ERROR";
@@ -21537,17 +21481,21 @@ const createFetchUrlContentError = ({
21537
21481
  reason,
21538
21482
  ...details
21539
21483
  }) => {
21540
- const fetchContentError = new Error(createDetailedMessage$1(`Failed to fetch url content`, {
21484
+ const fetchError = new Error(createDetailedMessage$1(`Failed to fetch url content`, {
21541
21485
  reason,
21542
21486
  ...details,
21543
21487
  "url": urlInfo.url,
21544
- "url reference trace": reference.trace,
21488
+ "url reference trace": reference.trace.message,
21545
21489
  ...detailsFromPluginController(pluginController)
21546
21490
  }));
21547
- fetchContentError.name = "FETCH_URL_CONTENT_ERROR";
21548
- fetchContentError.code = code;
21549
- fetchContentError.reason = reason;
21550
- return fetchContentError;
21491
+ fetchError.name = "FETCH_URL_CONTENT_ERROR";
21492
+ fetchError.code = code;
21493
+ fetchError.reason = reason;
21494
+ fetchError.url = reference.trace.url;
21495
+ fetchError.line = reference.trace.line;
21496
+ fetchError.column = reference.trace.column;
21497
+ fetchError.contentFrame = reference.trace.message;
21498
+ return fetchError;
21551
21499
  };
21552
21500
 
21553
21501
  if (error.code === "EPERM") {
@@ -21591,12 +21539,42 @@ const createTransformUrlContentError = ({
21591
21539
  reason,
21592
21540
  ...details,
21593
21541
  "url": urlInfo.url,
21594
- "url reference trace": reference.trace,
21542
+ "url reference trace": reference.trace.message,
21595
21543
  ...detailsFromPluginController(pluginController)
21596
21544
  }));
21597
21545
  transformError.name = "TRANSFORM_URL_CONTENT_ERROR";
21598
21546
  transformError.code = code;
21599
21547
  transformError.reason = reason;
21548
+ transformError.url = reference.trace.url;
21549
+ transformError.line = reference.trace.line;
21550
+ transformError.column = reference.trace.column;
21551
+ transformError.stack = error.stack;
21552
+ transformError.contentFrame = reference.trace.message;
21553
+
21554
+ if (code === "PARSE_ERROR") {
21555
+ transformError.reason = error.message;
21556
+
21557
+ if (urlInfo.isInline) {
21558
+ transformError.line = reference.trace.line + error.line - 1;
21559
+ transformError.column = reference.trace.column + error.column;
21560
+ transformError.contentFrame = stringifyUrlSite({
21561
+ url: urlInfo.inlineUrlSite.url,
21562
+ line: transformError.line,
21563
+ column: transformError.column,
21564
+ content: urlInfo.inlineUrlSite.content
21565
+ });
21566
+ } else {
21567
+ transformError.line = error.line;
21568
+ transformError.column = error.column;
21569
+ transformError.contentFrame = stringifyUrlSite({
21570
+ url: urlInfo.url,
21571
+ line: transformError.line,
21572
+ column: transformError.column,
21573
+ content: urlInfo.content
21574
+ });
21575
+ }
21576
+ }
21577
+
21600
21578
  return transformError;
21601
21579
  };
21602
21580
 
@@ -21615,7 +21593,7 @@ const createFinalizeUrlContentError = ({
21615
21593
  "reason": `An error occured during "finalizeUrlContent"`,
21616
21594
  ...detailsFromValueThrown(error),
21617
21595
  "url": urlInfo.url,
21618
- "url reference trace": reference.trace,
21596
+ "url reference trace": reference.trace.message,
21619
21597
  ...detailsFromPluginController(pluginController)
21620
21598
  }));
21621
21599
  finalizeError.name = "FINALIZE_URL_CONTENT_ERROR";
@@ -22010,7 +21988,10 @@ const createKitchen = ({
22010
21988
  specifier
22011
21989
  }) => {
22012
21990
  const sourcemapReference = createReference({
22013
- trace: `sourcemap comment placeholder for ${urlInfo.url}`,
21991
+ trace: {
21992
+ message: `sourcemap comment placeholder`,
21993
+ url: urlInfo.url
21994
+ },
22014
21995
  type: "sourcemap_comment",
22015
21996
  subtype: urlInfo.contentType === "text/javascript" ? "js" : "css",
22016
21997
  parentUrl: urlInfo.url,
@@ -22027,13 +22008,14 @@ const createKitchen = ({
22027
22008
  specifierLine,
22028
22009
  specifierColumn
22029
22010
  }) => {
22011
+ const sourcemapUrlSite = adjustUrlSite(urlInfo, {
22012
+ urlGraph,
22013
+ url: urlInfo.url,
22014
+ line: specifierLine,
22015
+ column: specifierColumn
22016
+ });
22030
22017
  const sourcemapReference = createReference({
22031
- trace: stringifyUrlSite(adjustUrlSite(urlInfo, {
22032
- urlGraph,
22033
- url: urlInfo.url,
22034
- line: specifierLine,
22035
- column: specifierColumn
22036
- })),
22018
+ trace: traceFromUrlSite(sourcemapUrlSite),
22037
22019
  type,
22038
22020
  parentUrl: urlInfo.url,
22039
22021
  specifier,
@@ -22056,7 +22038,7 @@ const createKitchen = ({
22056
22038
  if (!fetchUrlContentReturnValue) {
22057
22039
  logger.warn(createDetailedMessage$1(`no plugin has handled url during "fetchUrlContent" hook -> url will be ignored`, {
22058
22040
  "url": urlInfo.url,
22059
- "url reference trace": reference.trace
22041
+ "url reference trace": reference.trace.message
22060
22042
  }));
22061
22043
  return;
22062
22044
  }
@@ -22213,7 +22195,7 @@ const createKitchen = ({
22213
22195
  ...rest
22214
22196
  }) => {
22215
22197
  if (trace === undefined) {
22216
- trace = stringifyUrlSite(adjustUrlSite(urlInfo, {
22198
+ trace = traceFromUrlSite(adjustUrlSite(urlInfo, {
22217
22199
  urlGraph,
22218
22200
  url: urlInfo.url,
22219
22201
  line: rest.specifierLine,
@@ -22229,22 +22211,22 @@ const createKitchen = ({
22229
22211
  },
22230
22212
  foundInline: ({
22231
22213
  isOriginalPosition,
22232
- line,
22233
- column,
22214
+ specifierLine,
22215
+ specifierColumn,
22234
22216
  ...rest
22235
22217
  }) => {
22236
22218
  const parentUrl = isOriginalPosition ? urlInfo.url : urlInfo.generatedUrl;
22237
22219
  const parentContent = isOriginalPosition ? urlInfo.originalContent : urlInfo.content;
22238
22220
  return addReference({
22239
- trace: stringifyUrlSite({
22221
+ trace: traceFromUrlSite({
22240
22222
  url: parentUrl,
22241
22223
  content: parentContent,
22242
- line,
22243
- column
22224
+ line: specifierLine,
22225
+ column: specifierColumn
22244
22226
  }),
22245
22227
  isOriginalPosition,
22246
- line,
22247
- column,
22228
+ specifierLine,
22229
+ specifierColumn,
22248
22230
  isInline: true,
22249
22231
  ...rest
22250
22232
  });
@@ -22282,7 +22264,7 @@ const createKitchen = ({
22282
22264
  const parentUrl = isOriginalPosition ? urlInfo.url : urlInfo.generatedUrl;
22283
22265
  const parentContent = isOriginalPosition ? urlInfo.originalContent : urlInfo.content;
22284
22266
  return referenceUtils.update(reference, {
22285
- trace: stringifyUrlSite({
22267
+ trace: traceFromUrlSite({
22286
22268
  url: parentUrl,
22287
22269
  content: parentContent,
22288
22270
  line: specifierLine,
@@ -22307,7 +22289,7 @@ const createKitchen = ({
22307
22289
  line,
22308
22290
  column
22309
22291
  } = getCallerPosition();
22310
- trace = stringifyUrlSite({
22292
+ trace = traceFromUrlSite({
22311
22293
  url,
22312
22294
  line,
22313
22295
  column
@@ -22516,6 +22498,15 @@ const memoizeCook = cook => {
22516
22498
  };
22517
22499
  };
22518
22500
 
22501
+ const traceFromUrlSite = urlSite => {
22502
+ return {
22503
+ message: stringifyUrlSite(urlSite),
22504
+ url: urlSite.url,
22505
+ line: urlSite.line,
22506
+ column: urlSite.column
22507
+ };
22508
+ };
22509
+
22519
22510
  const applyReferenceEffectsOnUrlInfo = (reference, urlInfo, context) => {
22520
22511
  if (reference.shouldHandle) {
22521
22512
  urlInfo.shouldHandle = true;
@@ -22744,6 +22735,60 @@ const determineFileUrlForOutDirectory = ({
22744
22735
  // }
22745
22736
  // }
22746
22737
 
22738
+ const createSSEService = ({
22739
+ serverEventCallbackList
22740
+ }) => {
22741
+ const destroyCallbackList = createCallbackListNotifiedOnce();
22742
+ const cache = [];
22743
+ const sseRoomLimit = 100;
22744
+
22745
+ const getOrCreateSSERoom = request => {
22746
+ const htmlFileRelativeUrl = request.ressource.slice(1);
22747
+ const cacheEntry = cache.find(cacheEntryCandidate => cacheEntryCandidate.htmlFileRelativeUrl === htmlFileRelativeUrl);
22748
+
22749
+ if (cacheEntry) {
22750
+ return cacheEntry.sseRoom;
22751
+ }
22752
+
22753
+ const sseRoom = createSSERoom({
22754
+ retryDuration: 2000,
22755
+ historyLength: 100,
22756
+ welcomeEventEnabled: true,
22757
+ effect: () => {
22758
+ return serverEventCallbackList.add(event => {
22759
+ sseRoom.sendEvent(event);
22760
+ });
22761
+ }
22762
+ });
22763
+ const removeSSECleanupCallback = destroyCallbackList.add(() => {
22764
+ removeSSECleanupCallback();
22765
+ sseRoom.close();
22766
+ });
22767
+ cache.push({
22768
+ htmlFileRelativeUrl,
22769
+ sseRoom,
22770
+ cleanup: () => {
22771
+ removeSSECleanupCallback();
22772
+ sseRoom.close();
22773
+ }
22774
+ });
22775
+
22776
+ if (cache.length >= sseRoomLimit) {
22777
+ const firstCacheEntry = cache.shift();
22778
+ firstCacheEntry.cleanup();
22779
+ }
22780
+
22781
+ return sseRoom;
22782
+ };
22783
+
22784
+ return {
22785
+ getOrCreateSSERoom,
22786
+ destroy: () => {
22787
+ destroyCallbackList.notify();
22788
+ }
22789
+ };
22790
+ };
22791
+
22747
22792
  const memoizeByFirstArgument = compute => {
22748
22793
  const urlCache = new Map();
22749
22794
 
@@ -22796,7 +22841,10 @@ const createFileService = ({
22796
22841
  rootDirectoryUrl,
22797
22842
  urlGraph,
22798
22843
  kitchen,
22799
- scenario
22844
+ scenario,
22845
+ onParseError,
22846
+ onFileNotFound,
22847
+ onUnexpectedError
22800
22848
  }) => {
22801
22849
  kitchen.pluginController.addHook("serve");
22802
22850
  kitchen.pluginController.addHook("augmentResponse");
@@ -22836,7 +22884,9 @@ const createFileService = ({
22836
22884
 
22837
22885
  if (!reference) {
22838
22886
  const entryPoint = kitchen.injectReference({
22839
- trace: parentUrl || rootDirectoryUrl,
22887
+ trace: {
22888
+ message: parentUrl || rootDirectoryUrl
22889
+ },
22840
22890
  parentUrl: parentUrl || rootDirectoryUrl,
22841
22891
  type: "http_request",
22842
22892
  specifier: request.ressource
@@ -22916,10 +22966,18 @@ const createFileService = ({
22916
22966
  const code = e.code;
22917
22967
 
22918
22968
  if (code === "PARSE_ERROR") {
22919
- // let the browser re-throw the syntax error
22969
+ onParseError({
22970
+ reason: e.reason,
22971
+ message: e.message,
22972
+ url: e.url,
22973
+ line: e.line,
22974
+ column: e.column,
22975
+ contentFrame: e.contentFrame
22976
+ });
22920
22977
  return {
22921
22978
  url: reference.url,
22922
22979
  status: 200,
22980
+ // let the browser re-throw the syntax error
22923
22981
  statusText: e.reason,
22924
22982
  statusMessage: e.message,
22925
22983
  headers: {
@@ -22950,6 +23008,14 @@ const createFileService = ({
22950
23008
  }
22951
23009
 
22952
23010
  if (code === "NOT_FOUND") {
23011
+ onFileNotFound({
23012
+ reason: e.reason,
23013
+ message: e.message,
23014
+ url: e.url,
23015
+ line: e.line,
23016
+ column: e.column,
23017
+ contentFrame: e.contentFrame
23018
+ });
22953
23019
  return {
22954
23020
  url: reference.url,
22955
23021
  status: 404,
@@ -22958,6 +23024,15 @@ const createFileService = ({
22958
23024
  };
22959
23025
  }
22960
23026
 
23027
+ onUnexpectedError({
23028
+ reason: e.reason,
23029
+ message: e.message,
23030
+ stack: e.stack,
23031
+ url: e.url,
23032
+ line: e.line,
23033
+ column: e.column,
23034
+ contentFrame: e.contentFrame
23035
+ });
22961
23036
  return {
22962
23037
  url: reference.url,
22963
23038
  status: 500,
@@ -23024,12 +23099,75 @@ const startOmegaServer = async ({
23024
23099
  kitchen
23025
23100
  }) => {
23026
23101
  const serverStopCallbackList = createCallbackListNotifiedOnce();
23102
+ const serverEventCallbackList = createCallbackList();
23103
+ const sseService = createSSEService({
23104
+ serverEventCallbackList
23105
+ });
23106
+
23107
+ const sendServerEvent = ({
23108
+ type,
23109
+ data
23110
+ }) => {
23111
+ serverEventCallbackList.notify({
23112
+ type,
23113
+ data: JSON.stringify(data)
23114
+ });
23115
+ };
23116
+
23117
+ kitchen.pluginController.addHook("registerServerEvents");
23118
+ kitchen.pluginController.callHooks("registerServerEvents", {
23119
+ sendServerEvent
23120
+ }, {
23121
+ rootDirectoryUrl,
23122
+ urlGraph,
23123
+ scenario
23124
+ }, () => {});
23125
+
23126
+ const sendServerErrorEvent = event => {
23127
+ // setTimeout display first the error
23128
+ // dispatched on window by browser
23129
+ // then display the jsenv error
23130
+ setTimeout(() => {
23131
+ sendServerEvent(event);
23132
+ }, 10);
23133
+ };
23134
+
23027
23135
  const coreServices = {
23136
+ "service:server_events": request => {
23137
+ const {
23138
+ accept
23139
+ } = request.headers;
23140
+
23141
+ if (accept && accept.includes("text/event-stream")) {
23142
+ const room = sseService.getOrCreateSSERoom(request);
23143
+ return room.join(request);
23144
+ }
23145
+
23146
+ return null;
23147
+ },
23028
23148
  "service:file": createFileService({
23029
23149
  rootDirectoryUrl,
23030
23150
  urlGraph,
23031
23151
  kitchen,
23032
- scenario
23152
+ scenario,
23153
+ onFileNotFound: data => {
23154
+ sendServerErrorEvent({
23155
+ type: "file_not_found",
23156
+ data
23157
+ });
23158
+ },
23159
+ onParseError: data => {
23160
+ sendServerErrorEvent({
23161
+ type: "parse_error",
23162
+ data
23163
+ });
23164
+ },
23165
+ onUnexpectedError: data => {
23166
+ sendServerErrorEvent({
23167
+ type: "unexpected_error",
23168
+ data
23169
+ });
23170
+ }
23033
23171
  })
23034
23172
  };
23035
23173
  const server = await startServer({
@@ -23110,6 +23248,7 @@ const startOmegaServer = async ({
23110
23248
  }),
23111
23249
  onStop: reason => {
23112
23250
  onStop();
23251
+ sseService.destroy();
23113
23252
  serverStopCallbackList.notify(reason);
23114
23253
  }
23115
23254
  });
@@ -24571,20 +24710,78 @@ const formatConsoleCalls = consoleCalls => {
24571
24710
  error: 0,
24572
24711
  log: 0
24573
24712
  };
24574
- let consoleOutput = ``;
24575
24713
  consoleCalls.forEach(consoleCall => {
24576
24714
  repartition[consoleCall.type]++;
24577
- const text = consoleCall.text;
24715
+ });
24716
+ const consoleOutput = formatConsoleOutput(consoleCalls);
24717
+ return `${ANSI.color(`-------- ${formatConsoleSummary(repartition)} --------`, ANSI.GREY)}
24718
+ ${consoleOutput}
24719
+ ${ANSI.color(`-------------------------`, ANSI.GREY)}`;
24720
+ };
24721
+
24722
+ const formatConsoleOutput = consoleCalls => {
24723
+ // inside Node.js you can do process.stdout.write()
24724
+ // and in that case the consoleCall is not suffixed with "\n"
24725
+ // we want to keep these calls together in the output
24726
+ const regroupedCalls = [];
24727
+ consoleCalls.forEach((consoleCall, index) => {
24728
+ if (index === 0) {
24729
+ regroupedCalls.push(consoleCall);
24730
+ return;
24731
+ }
24732
+
24733
+ const previousCall = consoleCalls[index - 1];
24734
+
24735
+ if (previousCall.type !== consoleCall.type) {
24736
+ regroupedCalls.push(consoleCall);
24737
+ return;
24738
+ }
24739
+
24740
+ if (previousCall.text.endsWith("\n")) {
24741
+ regroupedCalls.push(consoleCall);
24742
+ return;
24743
+ }
24744
+
24745
+ if (previousCall.text.endsWith("\r")) {
24746
+ regroupedCalls.push(consoleCall);
24747
+ return;
24748
+ }
24749
+
24750
+ const previousRegroupedCallIndex = regroupedCalls.length - 1;
24751
+ const previousRegroupedCall = regroupedCalls[previousRegroupedCallIndex];
24752
+ previousRegroupedCall.text = `${previousRegroupedCall.text}${consoleCall.text}`;
24753
+ });
24754
+ let consoleOutput = ``;
24755
+ regroupedCalls.forEach((regroupedCall, index) => {
24756
+ const text = regroupedCall.text;
24578
24757
  const textFormatted = prefixFirstAndIndentRemainingLines({
24579
- prefix: CONSOLE_ICONS[consoleCall.type],
24758
+ prefix: CONSOLE_ICONS[regroupedCall.type],
24580
24759
  text,
24581
- trimLastLine: consoleCall === consoleCalls[consoleCalls.length - 1]
24760
+ trimLastLine: index === regroupedCalls.length - 1
24582
24761
  });
24583
24762
  consoleOutput += textFormatted;
24584
24763
  });
24585
- return `${ANSI.color(`-------- ${formatConsoleSummary(repartition)} --------`, ANSI.GREY)}
24586
- ${consoleOutput}
24587
- ${ANSI.color(`-------------------------`, ANSI.GREY)}`;
24764
+ return consoleOutput;
24765
+ };
24766
+
24767
+ const prefixFirstAndIndentRemainingLines = ({
24768
+ prefix,
24769
+ text,
24770
+ trimLastLine
24771
+ }) => {
24772
+ const lines = text.split(/\r?\n/);
24773
+ const firstLine = lines.shift();
24774
+ let result = `${prefix} ${firstLine}`;
24775
+ let i = 0;
24776
+ const indentation = ` `;
24777
+
24778
+ while (i < lines.length) {
24779
+ const line = lines[i].trim();
24780
+ i++;
24781
+ result += line.length ? `\n${indentation}${line}` : trimLastLine && i === lines.length ? "" : `\n`;
24782
+ }
24783
+
24784
+ return result;
24588
24785
  };
24589
24786
 
24590
24787
  const CONSOLE_ICONS = {
@@ -24627,26 +24824,6 @@ const formatConsoleSummary = repartition => {
24627
24824
  return `console (${parts.join(" ")})`;
24628
24825
  };
24629
24826
 
24630
- const prefixFirstAndIndentRemainingLines = ({
24631
- prefix,
24632
- text,
24633
- trimLastLine
24634
- }) => {
24635
- const lines = text.split(/\r?\n/);
24636
- const firstLine = lines.shift();
24637
- let result = `${prefix} ${firstLine}`;
24638
- let i = 0;
24639
- const indentation = ` `;
24640
-
24641
- while (i < lines.length) {
24642
- const line = lines[i].trim();
24643
- i++;
24644
- result += line.length ? `\n${indentation}${line}` : trimLastLine && i === lines.length ? "" : `\n`;
24645
- }
24646
-
24647
- return result;
24648
- };
24649
-
24650
24827
  const formatExecution = ({
24651
24828
  label,
24652
24829
  details = {},
@@ -27980,7 +28157,9 @@ build ${entryPointKeys.length} entry points`);
27980
28157
  startLoading: cookEntryFile => {
27981
28158
  Object.keys(entryPoints).forEach(key => {
27982
28159
  const [, entryUrlInfo] = cookEntryFile({
27983
- trace: `"${key}" in entryPoints parameter`,
28160
+ trace: {
28161
+ message: `"${key}" in entryPoints parameter`
28162
+ },
27984
28163
  type: "entry_point",
27985
28164
  specifier: key
27986
28165
  });
@@ -28476,7 +28655,9 @@ build ${entryPointKeys.length} entry points`);
28476
28655
  startLoading: cookEntryFile => {
28477
28656
  entryUrls.forEach(entryUrl => {
28478
28657
  const [, postBuildEntryUrlInfo] = cookEntryFile({
28479
- trace: `entryPoint`,
28658
+ trace: {
28659
+ message: `entryPoint`
28660
+ },
28480
28661
  type: "entry_point",
28481
28662
  specifier: entryUrl
28482
28663
  });