@jsenv/core 38.3.10 → 38.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.
Files changed (118) hide show
  1. package/README.md +17 -18
  2. package/dist/babel_helpers/AsyncGenerator/AsyncGenerator.js +3 -3
  3. package/dist/babel_helpers/AwaitValue/AwaitValue.js +2 -2
  4. package/dist/babel_helpers/{overloadYield/overloadYield.js → OverloadYield/OverloadYield.js} +1 -1
  5. package/dist/babel_helpers/applyDecoratedDescriptor/applyDecoratedDescriptor.js +23 -14
  6. package/dist/babel_helpers/applyDecs/applyDecs.js +32 -21
  7. package/dist/babel_helpers/applyDecs2203/applyDecs2203.js +549 -549
  8. package/dist/babel_helpers/applyDecs2203R/applyDecs2203R.js +27 -15
  9. package/dist/babel_helpers/applyDecs2301/applyDecs2301.js +29 -18
  10. package/dist/babel_helpers/applyDecs2305/applyDecs2305.js +362 -541
  11. package/dist/babel_helpers/arrayLikeToArray/arrayLikeToArray.js +4 -4
  12. package/dist/babel_helpers/arrayWithHoles/arrayWithHoles.js +2 -2
  13. package/dist/babel_helpers/arrayWithoutHoles/arrayWithoutHoles.js +3 -3
  14. package/dist/babel_helpers/assertThisInitialized/assertThisInitialized.js +5 -3
  15. package/dist/babel_helpers/asyncGeneratorDelegate/asyncGeneratorDelegate.js +2 -2
  16. package/dist/babel_helpers/asyncIterator/asyncIterator.js +1 -1
  17. package/dist/babel_helpers/asyncToGenerator/asyncToGenerator.js +17 -17
  18. package/dist/babel_helpers/awaitAsyncGenerator/awaitAsyncGenerator.js +2 -2
  19. package/dist/babel_helpers/callSuper/callSuper.js +25 -0
  20. package/dist/babel_helpers/checkInRHS/checkInRHS.js +10 -8
  21. package/dist/babel_helpers/classApplyDescriptorDestructureSet/classApplyDescriptorDestructureSet.js +9 -6
  22. package/dist/babel_helpers/classApplyDescriptorGet/classApplyDescriptorGet.js +2 -2
  23. package/dist/babel_helpers/classApplyDescriptorSet/classApplyDescriptorSet.js +3 -3
  24. package/dist/babel_helpers/classCallCheck/classCallCheck.js +1 -1
  25. package/dist/babel_helpers/classCheckPrivateStaticAccess/classCheckPrivateStaticAccess.js +5 -2
  26. package/dist/babel_helpers/classCheckPrivateStaticFieldDescriptor/classCheckPrivateStaticFieldDescriptor.js +7 -2
  27. package/dist/babel_helpers/classExtractFieldDescriptor/classExtractFieldDescriptor.js +9 -3
  28. package/dist/babel_helpers/classNameTDZError/classNameTDZError.js +3 -1
  29. package/dist/babel_helpers/classPrivateFieldDestructureSet/classPrivateFieldDestructureSet.js +4 -4
  30. package/dist/babel_helpers/classPrivateFieldGet/classPrivateFieldGet.js +4 -4
  31. package/dist/babel_helpers/classPrivateFieldLooseBase/classPrivateFieldLooseBase.js +2 -2
  32. package/dist/babel_helpers/classPrivateFieldLooseKey/classPrivateFieldLooseKey.js +2 -2
  33. package/dist/babel_helpers/classPrivateFieldSet/classPrivateFieldSet.js +5 -5
  34. package/dist/babel_helpers/classPrivateMethodGet/classPrivateMethodGet.js +2 -2
  35. package/dist/babel_helpers/classPrivateMethodSet/classPrivateMethodSet.js +1 -1
  36. package/dist/babel_helpers/classStaticPrivateFieldSpecGet/classStaticPrivateFieldSpecGet.js +11 -7
  37. package/dist/babel_helpers/classStaticPrivateFieldSpecSet/classStaticPrivateFieldSpecSet.js +7 -7
  38. package/dist/babel_helpers/classStaticPrivateMethodGet/classStaticPrivateMethodGet.js +8 -4
  39. package/dist/babel_helpers/classStaticPrivateMethodSet/classStaticPrivateMethodSet.js +1 -1
  40. package/dist/babel_helpers/construct/construct.js +18 -14
  41. package/dist/babel_helpers/createClass/createClass.js +1 -1
  42. package/dist/babel_helpers/createForOfIteratorHelper/createForOfIteratorHelper.js +21 -20
  43. package/dist/babel_helpers/createForOfIteratorHelperLoose/createForOfIteratorHelperLoose.js +10 -9
  44. package/dist/babel_helpers/createRawReactElement/createRawReactElement.js +11 -11
  45. package/dist/babel_helpers/createSuper/createSuper.js +11 -11
  46. package/dist/babel_helpers/decorate/decorate.js +255 -170
  47. package/dist/babel_helpers/defaults/defaults.js +5 -5
  48. package/dist/babel_helpers/defineAccessor/defineAccessor.js +1 -1
  49. package/dist/babel_helpers/defineEnumerableProperties/defineEnumerableProperties.js +11 -11
  50. package/dist/babel_helpers/defineProperty/defineProperty.js +4 -4
  51. package/dist/babel_helpers/dispose/dispose.js +39 -39
  52. package/dist/babel_helpers/extends/extends.js +4 -4
  53. package/dist/babel_helpers/get/get.js +2 -2
  54. package/dist/babel_helpers/getPrototypeOf/getPrototypeOf.js +1 -1
  55. package/dist/babel_helpers/identity/identity.js +1 -1
  56. package/dist/babel_helpers/importDeferProxy/importDeferProxy.js +27 -27
  57. package/dist/babel_helpers/inherits/inherits.js +4 -4
  58. package/dist/babel_helpers/inheritsLoose/inheritsLoose.js +4 -4
  59. package/dist/babel_helpers/initializerDefineProperty/initializerDefineProperty.js +5 -3
  60. package/dist/babel_helpers/initializerWarningHelper/initializerWarningHelper.js +1 -1
  61. package/dist/babel_helpers/instanceof/instanceof.js +7 -3
  62. package/dist/babel_helpers/interopRequireDefault/interopRequireDefault.js +1 -1
  63. package/dist/babel_helpers/interopRequireWildcard/interopRequireWildcard.js +3 -1
  64. package/dist/babel_helpers/isNativeFunction/isNativeFunction.js +1 -1
  65. package/dist/babel_helpers/isNativeReflectConstruct/isNativeReflectConstruct.js +12 -14
  66. package/dist/babel_helpers/iterableToArray/iterableToArray.js +1 -1
  67. package/dist/babel_helpers/iterableToArrayLimit/iterableToArrayLimit.js +1 -1
  68. package/dist/babel_helpers/iterableToArrayLimitLoose/iterableToArrayLimitLoose.js +1 -1
  69. package/dist/babel_helpers/jsx/jsx.js +1 -1
  70. package/dist/babel_helpers/maybeArrayLike/maybeArrayLike.js +4 -4
  71. package/dist/babel_helpers/newArrowCheck/newArrowCheck.js +1 -1
  72. package/dist/babel_helpers/nonIterableRest/nonIterableRest.js +2 -2
  73. package/dist/babel_helpers/nonIterableSpread/nonIterableSpread.js +2 -2
  74. package/dist/babel_helpers/nullishReceiverError/nullishReceiverError.js +1 -1
  75. package/dist/babel_helpers/objectDestructuringEmpty/objectDestructuringEmpty.js +1 -1
  76. package/dist/babel_helpers/objectSpread/objectSpread.js +8 -8
  77. package/dist/babel_helpers/objectSpread2/objectSpread2.js +5 -3
  78. package/dist/babel_helpers/objectWithoutProperties/objectWithoutProperties.js +12 -12
  79. package/dist/babel_helpers/objectWithoutPropertiesLoose/objectWithoutPropertiesLoose.js +10 -10
  80. package/dist/babel_helpers/possibleConstructorReturn/possibleConstructorReturn.js +7 -5
  81. package/dist/babel_helpers/readOnlyError/readOnlyError.js +1 -1
  82. package/dist/babel_helpers/regeneratorRuntime/regeneratorRuntime.js +1 -1
  83. package/dist/babel_helpers/set/set.js +22 -19
  84. package/dist/babel_helpers/setFunctionName/setFunctionName.js +18 -0
  85. package/dist/babel_helpers/setPrototypeOf/setPrototypeOf.js +7 -6
  86. package/dist/babel_helpers/skipFirstGeneratorNext/skipFirstGeneratorNext.js +4 -4
  87. package/dist/babel_helpers/slicedToArray/slicedToArray.js +5 -5
  88. package/dist/babel_helpers/slicedToArrayLoose/slicedToArrayLoose.js +5 -5
  89. package/dist/babel_helpers/superPropBase/superPropBase.js +4 -4
  90. package/dist/babel_helpers/taggedTemplateLiteral/taggedTemplateLiteral.js +2 -2
  91. package/dist/babel_helpers/taggedTemplateLiteralLoose/taggedTemplateLiteralLoose.js +3 -3
  92. package/dist/babel_helpers/tdz/tdz.js +1 -1
  93. package/dist/babel_helpers/temporalRef/temporalRef.js +3 -3
  94. package/dist/babel_helpers/toArray/toArray.js +5 -5
  95. package/dist/babel_helpers/toConsumableArray/toConsumableArray.js +5 -5
  96. package/dist/babel_helpers/toPrimitive/toPrimitive.js +11 -7
  97. package/dist/babel_helpers/toPropertyKey/toPropertyKey.js +8 -4
  98. package/dist/babel_helpers/typeof/typeof.js +6 -5
  99. package/dist/babel_helpers/unsupportedIterableToArray/unsupportedIterableToArray.js +7 -7
  100. package/dist/babel_helpers/using/using.js +20 -20
  101. package/dist/babel_helpers/wrapAsyncGenerator/wrapAsyncGenerator.js +3 -3
  102. package/dist/babel_helpers/wrapNativeSuper/wrapNativeSuper.js +12 -12
  103. package/dist/babel_helpers/wrapRegExp/wrapRegExp.js +5 -3
  104. package/dist/babel_helpers/writeOnlyError/writeOnlyError.js +1 -1
  105. package/dist/js/ribbon.js +1 -1
  106. package/dist/js/ws.js +234 -146
  107. package/dist/jsenv_core.js +1365 -1211
  108. package/package.json +26 -22
  109. package/src/build/build.js +22 -14
  110. package/src/dev/start_dev_server.js +8 -21
  111. package/src/kitchen/errors.js +29 -24
  112. package/src/kitchen/kitchen.js +1 -1
  113. package/src/kitchen/url_graph/references.js +8 -0
  114. package/src/kitchen/url_graph/url_graph_report.js +5 -3
  115. package/src/plugins/plugins.js +2 -2
  116. package/src/plugins/protocol_file/jsenv_plugin_protocol_file.js +17 -7
  117. package/src/plugins/reference_analysis/js/jsenv_plugin_js_reference_analysis.js +10 -12
  118. package/src/plugins/ribbon/client/ribbon.js +1 -1
@@ -13,119 +13,19 @@ import http from "node:http";
13
13
  import { Readable, Stream, Writable } from "node:stream";
14
14
  import { Http2ServerResponse } from "node:http2";
15
15
  import { lookup } from "node:dns";
16
- import { injectJsImport, visitJsAstUntil, applyBabelPlugins, parseHtml, visitHtmlNodes, getHtmlNodeAttribute, analyzeScriptNode, getHtmlNodeText, stringifyHtmlAst, setHtmlNodeAttributes, injectHtmlNodeAsEarlyAsPossible, createHtmlNode, generateUrlForInlineContent, parseJsWithAcorn, getHtmlNodePosition, getUrlForContentInsideHtml, getHtmlNodeAttributePosition, parseSrcSet, removeHtmlNodeText, setHtmlNodeText, removeHtmlNode, parseCssUrls, parseJsUrls, getUrlForContentInsideJs, analyzeLinkNode, findHtmlNode, insertHtmlNodeAfter } from "@jsenv/ast";
16
+ import { injectJsImport, visitJsAstUntil, applyBabelPlugins, parseHtml, visitHtmlNodes, getHtmlNodeAttribute, analyzeScriptNode, getHtmlNodeText, stringifyHtmlAst, setHtmlNodeAttributes, parseJsUrls, injectHtmlNodeAsEarlyAsPossible, createHtmlNode, generateUrlForInlineContent, parseJsWithAcorn, getHtmlNodePosition, getUrlForContentInsideHtml, getHtmlNodeAttributePosition, parseSrcSet, removeHtmlNodeText, setHtmlNodeText, removeHtmlNode, parseCssUrls, getUrlForContentInsideJs, analyzeLinkNode, findHtmlNode, insertHtmlNodeAfter } from "@jsenv/ast";
17
17
  import { sourcemapConverter, createMagicSource, composeTwoSourcemaps, SOURCEMAP, generateSourcemapFileUrl, generateSourcemapDataUrl } from "@jsenv/sourcemap";
18
18
  import { createRequire } from "node:module";
19
19
  import { systemJsClientFileUrlDefault, convertJsModuleToJsClassic } from "@jsenv/js-module-fallback";
20
20
  import { RUNTIME_COMPAT } from "@jsenv/runtime-compat";
21
21
  import { jsenvPluginSupervisor } from "@jsenv/plugin-supervisor";
22
22
 
23
- const assertUrlLike = (value, name = "url") => {
24
- if (typeof value !== "string") {
25
- throw new TypeError(`${name} must be a url string, got ${value}`);
26
- }
27
- if (isWindowsPathnameSpecifier(value)) {
28
- throw new TypeError(
29
- `${name} must be a url but looks like a windows pathname, got ${value}`,
30
- );
31
- }
32
- if (!hasScheme$1(value)) {
33
- throw new TypeError(
34
- `${name} must be a url and no scheme found, got ${value}`,
35
- );
36
- }
37
- };
38
-
39
- const isPlainObject = (value) => {
40
- if (value === null) {
41
- return false;
42
- }
43
- if (typeof value === "object") {
44
- if (Array.isArray(value)) {
45
- return false;
46
- }
47
- return true;
48
- }
49
- return false;
50
- };
51
-
52
- const isWindowsPathnameSpecifier = (specifier) => {
53
- const firstChar = specifier[0];
54
- if (!/[a-zA-Z]/.test(firstChar)) return false;
55
- const secondChar = specifier[1];
56
- if (secondChar !== ":") return false;
57
- const thirdChar = specifier[2];
58
- return thirdChar === "/" || thirdChar === "\\";
59
- };
60
-
61
- const hasScheme$1 = (specifier) => /^[a-zA-Z]+:/.test(specifier);
62
-
63
- const resolveAssociations = (associations, baseUrl) => {
64
- if (baseUrl && typeof baseUrl.href === "string") baseUrl = baseUrl.href;
65
- assertUrlLike(baseUrl, "baseUrl");
66
- const associationsResolved = {};
67
- Object.keys(associations).forEach((key) => {
68
- const value = associations[key];
69
- if (typeof value === "object" && value !== null) {
70
- const valueMapResolved = {};
71
- Object.keys(value).forEach((pattern) => {
72
- const valueAssociated = value[pattern];
73
- const patternResolved = normalizeUrlPattern(pattern, baseUrl);
74
- valueMapResolved[patternResolved] = valueAssociated;
75
- });
76
- associationsResolved[key] = valueMapResolved;
77
- } else {
78
- associationsResolved[key] = value;
79
- }
80
- });
81
- return associationsResolved;
82
- };
83
-
84
- const normalizeUrlPattern = (urlPattern, baseUrl) => {
85
- try {
86
- return String(new URL(urlPattern, baseUrl));
87
- } catch (e) {
88
- // it's not really an url, no need to perform url resolution nor encoding
89
- return urlPattern;
90
- }
91
- };
92
-
93
- const asFlatAssociations = (associations) => {
94
- if (!isPlainObject(associations)) {
95
- throw new TypeError(
96
- `associations must be a plain object, got ${associations}`,
97
- );
98
- }
99
- const flatAssociations = {};
100
- Object.keys(associations).forEach((associationName) => {
101
- const associationValue = associations[associationName];
102
- if (isPlainObject(associationValue)) {
103
- Object.keys(associationValue).forEach((pattern) => {
104
- const patternValue = associationValue[pattern];
105
- const previousValue = flatAssociations[pattern];
106
- if (isPlainObject(previousValue)) {
107
- flatAssociations[pattern] = {
108
- ...previousValue,
109
- [associationName]: patternValue,
110
- };
111
- } else {
112
- flatAssociations[pattern] = {
113
- [associationName]: patternValue,
114
- };
115
- }
116
- });
117
- }
118
- });
119
- return flatAssociations;
120
- };
121
-
122
23
  /*
123
24
  * Link to things doing pattern matching:
124
25
  * https://git-scm.com/docs/gitignore
125
26
  * https://github.com/kaelzhang/node-ignore
126
27
  */
127
28
 
128
-
129
29
  /** @module jsenv_url_meta **/
130
30
  /**
131
31
  * An object representing the result of applying a pattern to an url
@@ -143,21 +43,18 @@ const asFlatAssociations = (associations) => {
143
43
  * @param {string} applyPatternMatchingParams.url a string representing an url
144
44
  * @return {MatchResult}
145
45
  */
146
- const applyPatternMatching = ({ url, pattern }) => {
147
- assertUrlLike(pattern, "pattern");
148
- if (url && typeof url.href === "string") url = url.href;
149
- assertUrlLike(url, "url");
46
+ const applyPattern = ({ url, pattern }) => {
150
47
  const { matched, patternIndex, index, groups } = applyMatching(pattern, url);
151
48
  const matchGroups = [];
152
49
  let groupIndex = 0;
153
- groups.forEach((group) => {
50
+ for (const group of groups) {
154
51
  if (group.name) {
155
52
  matchGroups[group.name] = group.string;
156
53
  } else {
157
54
  matchGroups[groupIndex] = group.string;
158
55
  groupIndex++;
159
56
  }
160
- });
57
+ }
161
58
  return {
162
59
  matched,
163
60
  patternIndex,
@@ -392,54 +289,100 @@ const skipUntilMatch = ({ pattern, string, canSkipSlash }) => {
392
289
  return tryToMatch();
393
290
  };
394
291
 
292
+ const applyPatternMatching = ({ url, pattern }) => {
293
+ assertUrlLike(pattern, "pattern");
294
+ if (url && typeof url.href === "string") url = url.href;
295
+ assertUrlLike(url, "url");
296
+ return applyPattern({ url, pattern });
297
+ };
298
+
299
+ const resolveAssociations = (associations, baseUrl) => {
300
+ if (baseUrl && typeof baseUrl.href === "string") baseUrl = baseUrl.href;
301
+ assertUrlLike(baseUrl, "baseUrl");
302
+
303
+ const associationsResolved = {};
304
+ for (const key of Object.keys(associations)) {
305
+ const value = associations[key];
306
+ if (typeof value === "object" && value !== null) {
307
+ const valueMapResolved = {};
308
+ for (const pattern of Object.keys(value)) {
309
+ const valueAssociated = value[pattern];
310
+ let patternResolved;
311
+ try {
312
+ patternResolved = String(new URL(pattern, baseUrl));
313
+ } catch (e) {
314
+ // it's not really an url, no need to perform url resolution nor encoding
315
+ patternResolved = pattern;
316
+ }
317
+
318
+ valueMapResolved[patternResolved] = valueAssociated;
319
+ }
320
+ associationsResolved[key] = valueMapResolved;
321
+ } else {
322
+ associationsResolved[key] = value;
323
+ }
324
+ }
325
+ return associationsResolved;
326
+ };
327
+
328
+ const asFlatAssociations = (associations) => {
329
+ if (!isPlainObject(associations)) {
330
+ throw new TypeError(
331
+ `associations must be a plain object, got ${associations}`,
332
+ );
333
+ }
334
+ const flatAssociations = {};
335
+ for (const associationName of Object.keys(associations)) {
336
+ const associationValue = associations[associationName];
337
+ if (!isPlainObject(associationValue)) {
338
+ continue;
339
+ }
340
+ for (const pattern of Object.keys(associationValue)) {
341
+ const patternValue = associationValue[pattern];
342
+ const previousValue = flatAssociations[pattern];
343
+ if (isPlainObject(previousValue)) {
344
+ flatAssociations[pattern] = {
345
+ ...previousValue,
346
+ [associationName]: patternValue,
347
+ };
348
+ } else {
349
+ flatAssociations[pattern] = {
350
+ [associationName]: patternValue,
351
+ };
352
+ }
353
+ }
354
+ }
355
+ return flatAssociations;
356
+ };
357
+
395
358
  const applyAssociations = ({ url, associations }) => {
396
359
  if (url && typeof url.href === "string") url = url.href;
397
360
  assertUrlLike(url);
398
361
  const flatAssociations = asFlatAssociations(associations);
399
- return Object.keys(flatAssociations).reduce((previousValue, pattern) => {
362
+ let associatedValue = {};
363
+ for (const pattern of Object.keys(flatAssociations)) {
400
364
  const { matched } = applyPatternMatching({
401
365
  pattern,
402
366
  url,
403
367
  });
404
368
  if (matched) {
405
369
  const value = flatAssociations[pattern];
406
- if (isPlainObject(previousValue) && isPlainObject(value)) {
407
- return {
408
- ...previousValue,
409
- ...value,
410
- };
411
- }
412
- return value;
370
+ associatedValue = deepAssign(associatedValue, value);
413
371
  }
414
- return previousValue;
415
- }, {});
372
+ }
373
+ return associatedValue;
416
374
  };
417
375
 
418
- const applyAliases = ({ url, aliases }) => {
419
- let aliasFullMatchResult;
420
- const aliasMatchingKey = Object.keys(aliases).find((key) => {
421
- const aliasMatchResult = applyPatternMatching({
422
- pattern: key,
423
- url,
424
- });
425
- if (aliasMatchResult.matched) {
426
- aliasFullMatchResult = aliasMatchResult;
427
- return true;
428
- }
429
- return false;
430
- });
431
- if (!aliasMatchingKey) {
432
- return url;
376
+ const deepAssign = (firstValue, secondValue) => {
377
+ if (!isPlainObject(firstValue) || !isPlainObject(secondValue)) {
378
+ return secondValue;
433
379
  }
434
- const { matchGroups } = aliasFullMatchResult;
435
- const alias = aliases[aliasMatchingKey];
436
- const parts = alias.split("*");
437
- const newUrl = parts.reduce((previous, value, index) => {
438
- return `${previous}${value}${
439
- index === parts.length - 1 ? "" : matchGroups[index]
440
- }`;
441
- }, "");
442
- return newUrl;
380
+ for (const key of Object.keys(secondValue)) {
381
+ const leftValue = firstValue[key];
382
+ const rightValue = secondValue[key];
383
+ firstValue[key] = deepAssign(leftValue, rightValue);
384
+ }
385
+ return firstValue;
443
386
  };
444
387
 
445
388
  const urlChildMayMatch = ({ url, associations, predicate }) => {
@@ -459,7 +402,7 @@ const urlChildMayMatch = ({ url, associations, predicate }) => {
459
402
  // for partial match, any meta satisfying predicate will be valid because
460
403
  // we don't know for sure if pattern will still match for a file inside pathname
461
404
  const partialMatchMetaArray = [];
462
- Object.keys(flatAssociations).forEach((pattern) => {
405
+ for (const pattern of Object.keys(flatAssociations)) {
463
406
  const value = flatAssociations[pattern];
464
407
  const matchResult = applyPatternMatching({
465
408
  pattern,
@@ -478,7 +421,7 @@ const urlChildMayMatch = ({ url, associations, predicate }) => {
478
421
  } else if (someFullMatch === false && matchResult.urlIndex >= url.length) {
479
422
  partialMatchMetaArray.push(value);
480
423
  }
481
- });
424
+ }
482
425
  if (someFullMatch) {
483
426
  return Boolean(predicate(fullMatchMeta));
484
427
  }
@@ -487,12 +430,111 @@ const urlChildMayMatch = ({ url, associations, predicate }) => {
487
430
  );
488
431
  };
489
432
 
433
+ const applyAliases = ({ url, aliases }) => {
434
+ let aliasFullMatchResult;
435
+ const aliasMatchingKey = Object.keys(aliases).find((key) => {
436
+ const aliasMatchResult = applyPatternMatching({
437
+ pattern: key,
438
+ url,
439
+ });
440
+ if (aliasMatchResult.matched) {
441
+ aliasFullMatchResult = aliasMatchResult;
442
+ return true;
443
+ }
444
+ return false;
445
+ });
446
+ if (!aliasMatchingKey) {
447
+ return url;
448
+ }
449
+ const { matchGroups } = aliasFullMatchResult;
450
+ const alias = aliases[aliasMatchingKey];
451
+ const parts = alias.split("*");
452
+ let newUrl = "";
453
+ let index = 0;
454
+ for (const part of parts) {
455
+ newUrl += `${part}`;
456
+ if (index < parts.length - 1) {
457
+ newUrl += matchGroups[index];
458
+ }
459
+ index++;
460
+ }
461
+ return newUrl;
462
+ };
463
+
464
+ const matches = (url, patterns) => {
465
+ return Boolean(
466
+ applyAssociations({
467
+ url,
468
+ associations: {
469
+ yes: patterns,
470
+ },
471
+ }).yes,
472
+ );
473
+ };
474
+
475
+ // const assertSpecifierMetaMap = (value, checkComposition = true) => {
476
+ // if (!isPlainObject(value)) {
477
+ // throw new TypeError(
478
+ // `specifierMetaMap must be a plain object, got ${value}`,
479
+ // );
480
+ // }
481
+ // if (checkComposition) {
482
+ // const plainObject = value;
483
+ // Object.keys(plainObject).forEach((key) => {
484
+ // assertUrlLike(key, "specifierMetaMap key");
485
+ // const value = plainObject[key];
486
+ // if (value !== null && !isPlainObject(value)) {
487
+ // throw new TypeError(
488
+ // `specifierMetaMap value must be a plain object or null, got ${value} under key ${key}`,
489
+ // );
490
+ // }
491
+ // });
492
+ // }
493
+ // };
494
+ const assertUrlLike = (value, name = "url") => {
495
+ if (typeof value !== "string") {
496
+ throw new TypeError(`${name} must be a url string, got ${value}`);
497
+ }
498
+ if (isWindowsPathnameSpecifier(value)) {
499
+ throw new TypeError(
500
+ `${name} must be a url but looks like a windows pathname, got ${value}`,
501
+ );
502
+ }
503
+ if (!hasScheme$1(value)) {
504
+ throw new TypeError(
505
+ `${name} must be a url and no scheme found, got ${value}`,
506
+ );
507
+ }
508
+ };
509
+ const isPlainObject = (value) => {
510
+ if (value === null) {
511
+ return false;
512
+ }
513
+ if (typeof value === "object") {
514
+ if (Array.isArray(value)) {
515
+ return false;
516
+ }
517
+ return true;
518
+ }
519
+ return false;
520
+ };
521
+ const isWindowsPathnameSpecifier = (specifier) => {
522
+ const firstChar = specifier[0];
523
+ if (!/[a-zA-Z]/.test(firstChar)) return false;
524
+ const secondChar = specifier[1];
525
+ if (secondChar !== ":") return false;
526
+ const thirdChar = specifier[2];
527
+ return thirdChar === "/" || thirdChar === "\\";
528
+ };
529
+ const hasScheme$1 = (specifier) => /^[a-zA-Z]+:/.test(specifier);
530
+
490
531
  const URL_META = {
491
532
  resolveAssociations,
492
533
  applyAssociations,
493
- urlChildMayMatch,
494
- applyPatternMatching,
495
534
  applyAliases,
535
+ applyPatternMatching,
536
+ urlChildMayMatch,
537
+ matches,
496
538
  };
497
539
 
498
540
  /*
@@ -546,132 +588,111 @@ const DATA_URL = {
546
588
  },
547
589
  };
548
590
 
549
- // consider switching to https://babeljs.io/docs/en/babel-code-frame
550
- // https://github.com/postcss/postcss/blob/fd30d3df5abc0954a0ec642a3cdc644ab2aacf9c/lib/css-syntax-error.js#L43
551
- // https://github.com/postcss/postcss/blob/fd30d3df5abc0954a0ec642a3cdc644ab2aacf9c/lib/terminal-highlight.js#L50
552
- // https://github.com/babel/babel/blob/eea156b2cb8deecfcf82d52aa1b71ba4995c7d68/packages/babel-code-frame/src/index.js#L1
553
-
591
+ const formatDefault = (v) => v;
554
592
 
555
- const stringifyUrlSite = (
556
- { url, line, column, content },
557
- {
558
- showCodeFrame = true,
559
- numberOfSurroundingLinesToShow,
560
- lineMaxLength,
561
- color,
562
- } = {},
563
- ) => {
564
- let string = url;
593
+ const inspectFileContent = ({
594
+ content,
595
+ line,
596
+ column,
565
597
 
566
- if (typeof line === "number") {
567
- string += `:${line}`;
568
- if (typeof column === "number") {
569
- string += `:${column}`;
570
- }
571
- }
572
-
573
- if (!showCodeFrame || typeof line !== "number" || !content) {
574
- return string;
575
- }
576
-
577
- const sourceLoc = showSourceLocation({
578
- content,
579
- line,
580
- column,
581
- numberOfSurroundingLinesToShow,
582
- lineMaxLength,
583
- color,
584
- });
585
-
586
- return `${string}
587
- ${sourceLoc}`;
588
- };
589
-
590
- const showSourceLocation = ({
591
- content,
592
- line,
593
- column,
594
- numberOfSurroundingLinesToShow = 1,
595
- lineMaxLength = 120,
598
+ linesAbove = 3,
599
+ linesBelow = 0,
600
+ lineMaxWidth = 120,
601
+ lineNumbersOnTheLeft = true,
602
+ lineMarker = true,
603
+ columnMarker = true,
604
+ format = formatDefault,
596
605
  } = {}) => {
597
- let mark = (string) => string;
598
- let aside = (string) => string;
599
- // if (color) {
600
- // mark = (string) => ANSI.color(string, ANSI.RED)
601
- // aside = (string) => ANSI.color(string, ANSI.GREY)
602
- // }
603
-
604
- const lines = content.split(/\r?\n/);
606
+ const lineStrings = content.split(/\r?\n/);
605
607
  if (line === 0) line = 1;
606
- let lineRange = {
607
- start: line - 1,
608
- end: line,
609
- };
610
- lineRange = moveLineRangeUp(lineRange, numberOfSurroundingLinesToShow);
611
- lineRange = moveLineRangeDown(lineRange, numberOfSurroundingLinesToShow);
612
- lineRange = lineRangeWithinLines(lineRange, lines);
613
- const linesToShow = lines.slice(lineRange.start, lineRange.end);
614
- const endLineNumber = lineRange.end;
615
- const lineNumberMaxWidth = String(endLineNumber).length;
616
-
608
+ if (column === undefined) {
609
+ columnMarker = false;
610
+ column = 1;
611
+ }
617
612
  if (column === 0) column = 1;
618
613
 
619
- const columnRange = {};
620
- if (column === undefined) {
621
- columnRange.start = 0;
622
- columnRange.end = lineMaxLength;
623
- } else if (column > lineMaxLength) {
624
- columnRange.start = column - Math.floor(lineMaxLength / 2);
625
- columnRange.end = column + Math.ceil(lineMaxLength / 2);
614
+ let lineStartIndex = line - 1 - linesAbove;
615
+ if (lineStartIndex < 0) {
616
+ lineStartIndex = 0;
617
+ }
618
+ let lineEndIndex = line - 1 + linesBelow;
619
+ if (lineEndIndex > lineStrings.length - 1) {
620
+ lineEndIndex = lineStrings.length - 1;
621
+ }
622
+ if (columnMarker) {
623
+ // human reader deduce the line when there is a column marker
624
+ lineMarker = false;
625
+ }
626
+ if (line - 1 === lineEndIndex) {
627
+ lineMarker = false; // useless because last line
628
+ }
629
+ let lineIndex = lineStartIndex;
630
+
631
+ let columnsBefore;
632
+ let columnsAfter;
633
+ if (column > lineMaxWidth) {
634
+ columnsBefore = column - Math.ceil(lineMaxWidth / 2);
635
+ columnsAfter = column + Math.floor(lineMaxWidth / 2);
626
636
  } else {
627
- columnRange.start = 0;
628
- columnRange.end = lineMaxLength;
637
+ columnsBefore = 0;
638
+ columnsAfter = lineMaxWidth;
629
639
  }
640
+ let columnMarkerIndex = column - 1 - columnsBefore;
630
641
 
631
- return linesToShow.map((lineSource, index) => {
632
- const lineNumber = lineRange.start + index + 1;
642
+ let source = "";
643
+ while (lineIndex <= lineEndIndex) {
644
+ const lineString = lineStrings[lineIndex];
645
+ const lineNumber = lineIndex + 1;
646
+ const isLastLine = lineIndex === lineEndIndex;
633
647
  const isMainLine = lineNumber === line;
634
- const lineSourceTruncated = applyColumnRange(columnRange, lineSource);
635
- const lineNumberWidth = String(lineNumber).length;
636
- // ensure if line moves from 7,8,9 to 10 the display is still great
637
- const lineNumberRightSpacing = " ".repeat(
638
- lineNumberMaxWidth - lineNumberWidth,
639
- );
640
- const asideSource = `${lineNumber}${lineNumberRightSpacing} |`;
641
- const lineFormatted = `${aside(asideSource)} ${lineSourceTruncated}`;
642
- if (isMainLine) {
643
- if (column === undefined) {
644
- return `${mark(">")} ${lineFormatted}`;
645
- }
646
- const spacing = stringToSpaces(
647
- `${asideSource} ${lineSourceTruncated.slice(
648
- 0,
649
- column - columnRange.start - 1,
650
- )}`,
651
- );
652
- return `${mark(">")} ${lineFormatted}
653
- ${spacing}${mark("^")}`;
654
- }
655
- return ` ${lineFormatted}`;
656
- }).join(`
657
- `);
658
- };
648
+ lineIndex++;
659
649
 
660
- const applyColumnRange = ({ start, end }, line) => {
661
- if (typeof start !== "number") {
662
- throw new TypeError(`start must be a number, received ${start}`);
663
- }
664
- if (typeof end !== "number") {
665
- throw new TypeError(`end must be a number, received ${end}`);
666
- }
667
- if (end < start) {
668
- throw new Error(
669
- `end must be greater than start, but ${end} is smaller than ${start}`,
670
- );
650
+ {
651
+ if (lineMarker) {
652
+ if (isMainLine) {
653
+ source += `${format(">", "marker_line")} `;
654
+ } else {
655
+ source += " ";
656
+ }
657
+ }
658
+ if (lineNumbersOnTheLeft) {
659
+ // fill with spaces to ensure if line moves from 7,8,9 to 10 the display is still great
660
+ const asideSource = `${fillLeft(lineNumber, lineEndIndex + 1)} |`;
661
+ source += `${format(asideSource, "line_number_aside")} `;
662
+ }
663
+ }
664
+ {
665
+ source += truncateLine(lineString, {
666
+ start: columnsBefore,
667
+ end: columnsAfter,
668
+ prefix: "…",
669
+ suffix: "…",
670
+ format,
671
+ });
672
+ }
673
+ {
674
+ if (columnMarker && isMainLine) {
675
+ source += `\n`;
676
+ if (lineMarker) {
677
+ source += " ";
678
+ }
679
+ if (lineNumbersOnTheLeft) {
680
+ const asideSpaces = `${fillLeft(lineNumber, lineEndIndex + 1)} | `
681
+ .length;
682
+ source += " ".repeat(asideSpaces);
683
+ }
684
+ source += " ".repeat(columnMarkerIndex);
685
+ source += format("^", "marker_column");
686
+ }
687
+ }
688
+ if (!isLastLine) {
689
+ source += "\n";
690
+ }
671
691
  }
692
+ return source;
693
+ };
672
694
 
673
- const prefix = "…";
674
- const suffix = "…";
695
+ const truncateLine = (line, { start, end, prefix, suffix, format }) => {
675
696
  const lastIndex = line.length;
676
697
 
677
698
  if (line.length === 0) {
@@ -690,53 +711,360 @@ const applyColumnRange = ({ start, end }, line) => {
690
711
  if (start >= lastIndex || from === to) {
691
712
  return "";
692
713
  }
693
-
694
714
  let result = "";
695
715
  while (from < to) {
696
- result += line[from];
716
+ result += format(line[from], "char");
697
717
  from++;
698
718
  }
699
-
700
719
  if (result.length === 0) {
701
720
  return "";
702
721
  }
703
722
  if (startTruncated && endTruncated) {
704
- return `${prefix}${result}${suffix}`;
723
+ return `${format(prefix, "marker_overflow_left")}${result}${format(
724
+ suffix,
725
+ "marker_overflow_right",
726
+ )}`;
705
727
  }
706
728
  if (startTruncated) {
707
- return `${prefix}${result}`;
729
+ return `${format(prefix, "marker_overflow_left")}${result}`;
708
730
  }
709
731
  if (endTruncated) {
710
- return `${result}${suffix}`;
732
+ return `${result}${format(suffix, "marker_overflow_right")}`;
711
733
  }
712
734
  return result;
713
735
  };
714
736
 
715
- const stringToSpaces = (string) => string.replace(/[^\t]/g, " ");
737
+ const fillLeft = (value, biggestValue, char = " ") => {
738
+ const width = String(value).length;
739
+ const biggestWidth = String(biggestValue).length;
740
+ let missingWidth = biggestWidth - width;
741
+ let padded = "";
742
+ while (missingWidth--) {
743
+ padded += char;
744
+ }
745
+ padded += value;
746
+ return padded;
747
+ };
716
748
 
717
- // const getLineRangeLength = ({ start, end }) => end - start
749
+ const getPrecision = (number) => {
750
+ if (Math.floor(number) === number) return 0;
751
+ const [, decimals] = number.toString().split(".");
752
+ return decimals.length || 0;
753
+ };
718
754
 
719
- const moveLineRangeUp = ({ start, end }, number) => {
720
- return {
721
- start: start - number,
722
- end,
723
- };
755
+ const setRoundedPrecision = (
756
+ number,
757
+ { decimals = 1, decimalsWhenSmall = decimals } = {},
758
+ ) => {
759
+ return setDecimalsPrecision(number, {
760
+ decimals,
761
+ decimalsWhenSmall,
762
+ transform: Math.round,
763
+ });
724
764
  };
725
765
 
726
- const moveLineRangeDown = ({ start, end }, number) => {
727
- return {
728
- start,
729
- end: end + number,
730
- };
766
+ const setPrecision = (
767
+ number,
768
+ { decimals = 1, decimalsWhenSmall = decimals } = {},
769
+ ) => {
770
+ return setDecimalsPrecision(number, {
771
+ decimals,
772
+ decimalsWhenSmall,
773
+ transform: parseInt,
774
+ });
731
775
  };
732
776
 
733
- const lineRangeWithinLines = ({ start, end }, lines) => {
777
+ const setDecimalsPrecision = (
778
+ number,
779
+ {
780
+ transform,
781
+ decimals, // max decimals for number in [-Infinity, -1[]1, Infinity]
782
+ decimalsWhenSmall, // max decimals for number in [-1,1]
783
+ } = {},
784
+ ) => {
785
+ if (number === 0) {
786
+ return 0;
787
+ }
788
+ let numberCandidate = Math.abs(number);
789
+ if (numberCandidate < 1) {
790
+ const integerGoal = Math.pow(10, decimalsWhenSmall - 1);
791
+ let i = 1;
792
+ while (numberCandidate < integerGoal) {
793
+ numberCandidate *= 10;
794
+ i *= 10;
795
+ }
796
+ const asInteger = transform(numberCandidate);
797
+ const asFloat = asInteger / i;
798
+ return number < 0 ? -asFloat : asFloat;
799
+ }
800
+ const coef = Math.pow(10, decimals);
801
+ const numberMultiplied = (number + Number.EPSILON) * coef;
802
+ const asInteger = transform(numberMultiplied);
803
+ const asFloat = asInteger / coef;
804
+ return number < 0 ? -asFloat : asFloat;
805
+ };
806
+
807
+ // https://www.codingem.com/javascript-how-to-limit-decimal-places/
808
+ // export const roundNumber = (number, maxDecimals) => {
809
+ // const decimalsExp = Math.pow(10, maxDecimals)
810
+ // const numberRoundInt = Math.round(decimalsExp * (number + Number.EPSILON))
811
+ // const numberRoundFloat = numberRoundInt / decimalsExp
812
+ // return numberRoundFloat
813
+ // }
814
+
815
+ // export const setPrecision = (number, precision) => {
816
+ // if (Math.floor(number) === number) return number
817
+ // const [int, decimals] = number.toString().split(".")
818
+ // if (precision <= 0) return int
819
+ // const numberTruncated = `${int}.${decimals.slice(0, precision)}`
820
+ // return numberTruncated
821
+ // }
822
+
823
+ const unitShort = {
824
+ year: "y",
825
+ month: "m",
826
+ week: "w",
827
+ day: "d",
828
+ hour: "h",
829
+ minute: "m",
830
+ second: "s",
831
+ };
832
+
833
+ const inspectDuration = (
834
+ ms,
835
+ { short, rounded = true, decimals } = {},
836
+ ) => {
837
+ // ignore ms below meaningfulMs so that:
838
+ // inspectDuration(0.5) -> "0 second"
839
+ // inspectDuration(1.1) -> "0.001 second" (and not "0.0011 second")
840
+ // This tool is meant to be read by humans and it would be barely readable to see
841
+ // "0.0001 second" (stands for 0.1 millisecond)
842
+ // yes we could return "0.1 millisecond" but we choosed consistency over precision
843
+ // so that the prefered unit is "second" (and does not become millisecond when ms is super small)
844
+ if (ms < 1) {
845
+ return short ? "0s" : "0 second";
846
+ }
847
+ const { primary, remaining } = parseMs(ms);
848
+ if (!remaining) {
849
+ return inspectDurationUnit(primary, {
850
+ decimals:
851
+ decimals === undefined ? (primary.name === "second" ? 1 : 0) : decimals,
852
+ short,
853
+ rounded,
854
+ });
855
+ }
856
+ return `${inspectDurationUnit(primary, {
857
+ decimals: decimals === undefined ? 0 : decimals,
858
+ short,
859
+ rounded,
860
+ })} and ${inspectDurationUnit(remaining, {
861
+ decimals: decimals === undefined ? 0 : decimals,
862
+ short,
863
+ rounded,
864
+ })}`;
865
+ };
866
+ const inspectDurationUnit = (unit, { decimals, short, rounded }) => {
867
+ const count = rounded
868
+ ? setRoundedPrecision(unit.count, { decimals })
869
+ : setPrecision(unit.count, { decimals });
870
+ let name = unit.name;
871
+ if (short) {
872
+ name = unitShort[name];
873
+ return `${count}${name}`;
874
+ }
875
+ if (count <= 1) {
876
+ return `${count} ${name}`;
877
+ }
878
+ return `${count} ${name}s`;
879
+ };
880
+ const MS_PER_UNITS = {
881
+ year: 31_557_600_000,
882
+ month: 2_629_000_000,
883
+ week: 604_800_000,
884
+ day: 86_400_000,
885
+ hour: 3_600_000,
886
+ minute: 60_000,
887
+ second: 1000,
888
+ };
889
+
890
+ const parseMs = (ms) => {
891
+ const unitNames = Object.keys(MS_PER_UNITS);
892
+ const smallestUnitName = unitNames[unitNames.length - 1];
893
+ let firstUnitName = smallestUnitName;
894
+ let firstUnitCount = ms / MS_PER_UNITS[smallestUnitName];
895
+ const firstUnitIndex = unitNames.findIndex((unitName) => {
896
+ if (unitName === smallestUnitName) {
897
+ return false;
898
+ }
899
+ const msPerUnit = MS_PER_UNITS[unitName];
900
+ const unitCount = Math.floor(ms / msPerUnit);
901
+ if (unitCount) {
902
+ firstUnitName = unitName;
903
+ firstUnitCount = unitCount;
904
+ return true;
905
+ }
906
+ return false;
907
+ });
908
+ if (firstUnitName === smallestUnitName) {
909
+ return {
910
+ primary: {
911
+ name: firstUnitName,
912
+ count: firstUnitCount,
913
+ },
914
+ };
915
+ }
916
+ const remainingMs = ms - firstUnitCount * MS_PER_UNITS[firstUnitName];
917
+ const remainingUnitName = unitNames[firstUnitIndex + 1];
918
+ const remainingUnitCount = remainingMs / MS_PER_UNITS[remainingUnitName];
919
+ // - 1 year and 1 second is too much information
920
+ // so we don't check the remaining units
921
+ // - 1 year and 0.0001 week is awful
922
+ // hence the if below
923
+ if (Math.round(remainingUnitCount) < 1) {
924
+ return {
925
+ primary: {
926
+ name: firstUnitName,
927
+ count: firstUnitCount,
928
+ },
929
+ };
930
+ }
931
+ // - 1 year and 1 month is great
734
932
  return {
735
- start: start < 0 ? 0 : start,
736
- end: end > lines.length ? lines.length : end,
933
+ primary: {
934
+ name: firstUnitName,
935
+ count: firstUnitCount,
936
+ },
937
+ remaining: {
938
+ name: remainingUnitName,
939
+ count: remainingUnitCount,
940
+ },
737
941
  };
738
942
  };
739
943
 
944
+ const inspectFileSize = (numberOfBytes, { decimals, short } = {}) => {
945
+ return inspectBytes(numberOfBytes, { decimals, short });
946
+ };
947
+
948
+ const inspectBytes = (
949
+ number,
950
+ { fixedDecimals = false, decimals, short } = {},
951
+ ) => {
952
+ if (number === 0) {
953
+ return `0 B`;
954
+ }
955
+ const exponent = Math.min(
956
+ Math.floor(Math.log10(number) / 3),
957
+ BYTE_UNITS.length - 1,
958
+ );
959
+ const unitNumber = number / Math.pow(1000, exponent);
960
+ const unitName = BYTE_UNITS[exponent];
961
+ if (decimals === undefined) {
962
+ if (unitNumber < 100) {
963
+ decimals = 1;
964
+ } else {
965
+ decimals = 0;
966
+ }
967
+ }
968
+ const unitNumberRounded = setRoundedPrecision(unitNumber, {
969
+ decimals,
970
+ decimalsWhenSmall: 1,
971
+ });
972
+ const value = fixedDecimals
973
+ ? unitNumberRounded.toFixed(decimals)
974
+ : unitNumberRounded;
975
+ if (short) {
976
+ return `${value}${unitName}`;
977
+ }
978
+ return `${value} ${unitName}`;
979
+ };
980
+
981
+ const BYTE_UNITS = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
982
+
983
+ const distributePercentages = (
984
+ namedNumbers,
985
+ { maxPrecisionHint = 2 } = {},
986
+ ) => {
987
+ const numberNames = Object.keys(namedNumbers);
988
+ if (numberNames.length === 0) {
989
+ return {};
990
+ }
991
+ if (numberNames.length === 1) {
992
+ const firstNumberName = numberNames[0];
993
+ return { [firstNumberName]: "100 %" };
994
+ }
995
+ const numbers = numberNames.map((name) => namedNumbers[name]);
996
+ const total = numbers.reduce((sum, value) => sum + value, 0);
997
+ const ratios = numbers.map((number) => number / total);
998
+ const percentages = {};
999
+ ratios.pop();
1000
+ ratios.forEach((ratio, index) => {
1001
+ const percentage = ratio * 100;
1002
+ percentages[numberNames[index]] = percentage;
1003
+ });
1004
+ const lowestPercentage = (1 / Math.pow(10, maxPrecisionHint)) * 100;
1005
+ let precision = 0;
1006
+ Object.keys(percentages).forEach((name) => {
1007
+ const percentage = percentages[name];
1008
+ if (percentage < lowestPercentage) {
1009
+ // check the amout of meaningful decimals
1010
+ // and that what we will use
1011
+ const percentageRounded = setRoundedPrecision(percentage);
1012
+ const percentagePrecision = getPrecision(percentageRounded);
1013
+ if (percentagePrecision > precision) {
1014
+ precision = percentagePrecision;
1015
+ }
1016
+ }
1017
+ });
1018
+ let remainingPercentage = 100;
1019
+
1020
+ Object.keys(percentages).forEach((name) => {
1021
+ const percentage = percentages[name];
1022
+ const percentageAllocated = setRoundedPrecision(percentage, {
1023
+ decimals: precision,
1024
+ });
1025
+ remainingPercentage -= percentageAllocated;
1026
+ percentages[name] = percentageAllocated;
1027
+ });
1028
+ const lastName = numberNames[numberNames.length - 1];
1029
+ percentages[lastName] = setRoundedPrecision(remainingPercentage, {
1030
+ decimals: precision,
1031
+ });
1032
+ return percentages;
1033
+ };
1034
+
1035
+ // consider switching to https://babeljs.io/docs/en/babel-code-frame
1036
+ // https://github.com/postcss/postcss/blob/fd30d3df5abc0954a0ec642a3cdc644ab2aacf9c/lib/css-syntax-error.js#L43
1037
+ // https://github.com/postcss/postcss/blob/fd30d3df5abc0954a0ec642a3cdc644ab2aacf9c/lib/terminal-highlight.js#L50
1038
+ // https://github.com/babel/babel/blob/eea156b2cb8deecfcf82d52aa1b71ba4995c7d68/packages/babel-code-frame/src/index.js#L1
1039
+
1040
+
1041
+ const stringifyUrlSite = (
1042
+ { url, line, column, content },
1043
+ { showCodeFrame = true, ...params } = {},
1044
+ ) => {
1045
+ let string = url;
1046
+
1047
+ if (typeof line === "number") {
1048
+ string += `:${line}`;
1049
+ if (typeof column === "number") {
1050
+ string += `:${column}`;
1051
+ }
1052
+ }
1053
+
1054
+ if (!showCodeFrame || typeof line !== "number" || !content) {
1055
+ return string;
1056
+ }
1057
+
1058
+ const sourceLoc = inspectFileContent({
1059
+ content,
1060
+ line,
1061
+ column,
1062
+ params,
1063
+ });
1064
+ return `${string}
1065
+ ${sourceLoc}`;
1066
+ };
1067
+
740
1068
  const urlToScheme$1 = (url) => {
741
1069
  const urlString = String(url);
742
1070
  const colonIndex = urlString.indexOf(":");
@@ -1761,6 +2089,18 @@ const createOperation = () => {
1761
2089
  });
1762
2090
  };
1763
2091
 
2092
+ const wait = (ms) => {
2093
+ return new Promise((resolve) => {
2094
+ const timeoutId = setTimeout(() => {
2095
+ removeAbortCallback();
2096
+ resolve();
2097
+ }, ms);
2098
+ const removeAbortCallback = addAbortCallback(() => {
2099
+ clearTimeout(timeoutId);
2100
+ });
2101
+ });
2102
+ };
2103
+
1764
2104
  const withSignal = async (asyncCallback) => {
1765
2105
  const abortController = new AbortController();
1766
2106
  const signal = abortController.signal;
@@ -1797,6 +2137,12 @@ const createOperation = () => {
1797
2137
  }
1798
2138
  };
1799
2139
 
2140
+ const fork = () => {
2141
+ const forkedOperation = createOperation();
2142
+ forkedOperation.addAbortSignal(operationSignal);
2143
+ return forkedOperation;
2144
+ };
2145
+
1800
2146
  return {
1801
2147
  // We could almost hide the operationSignal
1802
2148
  // But it can be handy for 2 things:
@@ -1808,7 +2154,9 @@ const createOperation = () => {
1808
2154
  addAbortCallback,
1809
2155
  addAbortSignal,
1810
2156
  addAbortSource,
2157
+ fork,
1811
2158
  timeout,
2159
+ wait,
1812
2160
  withSignal,
1813
2161
  withSignalSync,
1814
2162
  addEndCallback,
@@ -2389,65 +2737,258 @@ const removeDirectory = async (
2389
2737
  );
2390
2738
  };
2391
2739
 
2392
- const visitFile = async (fileUrl) => {
2393
- await removeNonDirectory(fileUrl, { maxRetries, retryDelay });
2394
- };
2740
+ const visitFile = async (fileUrl) => {
2741
+ await removeNonDirectory(fileUrl, { maxRetries, retryDelay });
2742
+ };
2743
+
2744
+ const visitSymbolicLink = async (symbolicLinkUrl) => {
2745
+ await removeNonDirectory(symbolicLinkUrl, { maxRetries, retryDelay });
2746
+ };
2747
+
2748
+ try {
2749
+ if (onlyContent) {
2750
+ await removeDirectoryContent(rootDirectoryUrl);
2751
+ } else {
2752
+ await visitDirectory(rootDirectoryUrl);
2753
+ }
2754
+ } finally {
2755
+ await removeDirectoryOperation.end();
2756
+ }
2757
+ };
2758
+
2759
+ const removeDirectoryNaive = (
2760
+ directoryPath,
2761
+ { handleNotEmptyError = null, handlePermissionError = null } = {},
2762
+ ) => {
2763
+ return new Promise((resolve, reject) => {
2764
+ rmdir(directoryPath, (error, lstatObject) => {
2765
+ if (error) {
2766
+ if (handlePermissionError && error.code === "EPERM") {
2767
+ resolve(handlePermissionError(error));
2768
+ } else if (error.code === "ENOENT") {
2769
+ resolve();
2770
+ } else if (
2771
+ handleNotEmptyError &&
2772
+ // linux os
2773
+ (error.code === "ENOTEMPTY" ||
2774
+ // SunOS
2775
+ error.code === "EEXIST")
2776
+ ) {
2777
+ resolve(handleNotEmptyError(error));
2778
+ } else {
2779
+ reject(error);
2780
+ }
2781
+ } else {
2782
+ resolve(lstatObject);
2783
+ }
2784
+ });
2785
+ });
2786
+ };
2787
+
2788
+ process.platform === "win32";
2789
+
2790
+ /*
2791
+ * - stats object documentation on Node.js
2792
+ * https://nodejs.org/docs/latest-v13.x/api/fs.html#fs_class_fs_stats
2793
+ */
2794
+
2795
+
2796
+ process.platform === "win32";
2797
+
2798
+ process.platform === "win32";
2799
+
2800
+ const mediaTypeInfos = {
2801
+ "application/json": {
2802
+ extensions: ["json", "map"],
2803
+ isTextual: true,
2804
+ },
2805
+ "application/importmap+json": {
2806
+ extensions: ["importmap"],
2807
+ isTextual: true,
2808
+ },
2809
+ "application/manifest+json": {
2810
+ extensions: ["webmanifest"],
2811
+ isTextual: true,
2812
+ },
2813
+ "application/octet-stream": {},
2814
+ "application/pdf": {
2815
+ extensions: ["pdf"],
2816
+ },
2817
+ "application/xml": {
2818
+ extensions: ["xml"],
2819
+ isTextual: true,
2820
+ },
2821
+ "application/x-gzip": {
2822
+ extensions: ["gz"],
2823
+ },
2824
+ "application/wasm": {
2825
+ extensions: ["wasm"],
2826
+ },
2827
+ "application/zip": {
2828
+ extensions: ["zip"],
2829
+ },
2830
+ "audio/basic": {
2831
+ extensions: ["au", "snd"],
2832
+ },
2833
+ "audio/mpeg": {
2834
+ extensions: ["mpga", "mp2", "mp2a", "mp3", "m2a", "m3a"],
2835
+ },
2836
+ "audio/midi": {
2837
+ extensions: ["midi", "mid", "kar", "rmi"],
2838
+ },
2839
+ "audio/mp4": {
2840
+ extensions: ["m4a", "mp4a"],
2841
+ },
2842
+ "audio/ogg": {
2843
+ extensions: ["oga", "ogg", "spx"],
2844
+ },
2845
+ "audio/webm": {
2846
+ extensions: ["weba"],
2847
+ },
2848
+ "audio/x-wav": {
2849
+ extensions: ["wav"],
2850
+ },
2851
+ "font/ttf": {
2852
+ extensions: ["ttf"],
2853
+ },
2854
+ "font/woff": {
2855
+ extensions: ["woff"],
2856
+ },
2857
+ "font/woff2": {
2858
+ extensions: ["woff2"],
2859
+ },
2860
+ "image/png": {
2861
+ extensions: ["png"],
2862
+ },
2863
+ "image/gif": {
2864
+ extensions: ["gif"],
2865
+ },
2866
+ "image/jpeg": {
2867
+ extensions: ["jpg"],
2868
+ },
2869
+ "image/svg+xml": {
2870
+ extensions: ["svg", "svgz"],
2871
+ isTextual: true,
2872
+ },
2873
+ "text/plain": {
2874
+ extensions: ["txt"],
2875
+ isTextual: true,
2876
+ },
2877
+ "text/html": {
2878
+ extensions: ["html"],
2879
+ isTextual: true,
2880
+ },
2881
+ "text/css": {
2882
+ extensions: ["css"],
2883
+ isTextual: true,
2884
+ },
2885
+ "text/javascript": {
2886
+ extensions: ["js", "cjs", "mjs", "ts", "jsx", "tsx"],
2887
+ isTextual: true,
2888
+ },
2889
+ "text/x-sass": {
2890
+ extensions: ["sass"],
2891
+ isTextual: true,
2892
+ },
2893
+ "text/x-scss": {
2894
+ extensions: ["scss"],
2895
+ isTextual: true,
2896
+ },
2897
+ "text/cache-manifest": {
2898
+ extensions: ["appcache"],
2899
+ },
2900
+ "video/mp4": {
2901
+ extensions: ["mp4", "mp4v", "mpg4"],
2902
+ },
2903
+ "video/mpeg": {
2904
+ extensions: ["mpeg", "mpg", "mpe", "m1v", "m2v"],
2905
+ },
2906
+ "video/ogg": {
2907
+ extensions: ["ogv"],
2908
+ },
2909
+ "video/webm": {
2910
+ extensions: ["webm"],
2911
+ },
2912
+ };
2913
+
2914
+ const CONTENT_TYPE = {
2915
+ parse: (string) => {
2916
+ const [mediaType, charset] = string.split(";");
2917
+ return { mediaType: normalizeMediaType(mediaType), charset };
2918
+ },
2395
2919
 
2396
- const visitSymbolicLink = async (symbolicLinkUrl) => {
2397
- await removeNonDirectory(symbolicLinkUrl, { maxRetries, retryDelay });
2398
- };
2920
+ stringify: ({ mediaType, charset }) => {
2921
+ if (charset) {
2922
+ return `${mediaType};${charset}`;
2923
+ }
2924
+ return mediaType;
2925
+ },
2399
2926
 
2400
- try {
2401
- if (onlyContent) {
2402
- await removeDirectoryContent(rootDirectoryUrl);
2403
- } else {
2404
- await visitDirectory(rootDirectoryUrl);
2927
+ asMediaType: (value) => {
2928
+ if (typeof value === "string") {
2929
+ return CONTENT_TYPE.parse(value).mediaType;
2405
2930
  }
2406
- } finally {
2407
- await removeDirectoryOperation.end();
2408
- }
2409
- };
2931
+ if (typeof value === "object") {
2932
+ return value.mediaType;
2933
+ }
2934
+ return null;
2935
+ },
2410
2936
 
2411
- const removeDirectoryNaive = (
2412
- directoryPath,
2413
- { handleNotEmptyError = null, handlePermissionError = null } = {},
2414
- ) => {
2415
- return new Promise((resolve, reject) => {
2416
- rmdir(directoryPath, (error, lstatObject) => {
2417
- if (error) {
2418
- if (handlePermissionError && error.code === "EPERM") {
2419
- resolve(handlePermissionError(error));
2420
- } else if (error.code === "ENOENT") {
2421
- resolve();
2422
- } else if (
2423
- handleNotEmptyError &&
2424
- // linux os
2425
- (error.code === "ENOTEMPTY" ||
2426
- // SunOS
2427
- error.code === "EEXIST")
2428
- ) {
2429
- resolve(handleNotEmptyError(error));
2430
- } else {
2431
- reject(error);
2432
- }
2433
- } else {
2434
- resolve(lstatObject);
2435
- }
2436
- });
2437
- });
2438
- };
2937
+ isJson: (value) => {
2938
+ const mediaType = CONTENT_TYPE.asMediaType(value);
2939
+ return (
2940
+ mediaType === "application/json" ||
2941
+ /^application\/\w+\+json$/.test(mediaType)
2942
+ );
2943
+ },
2439
2944
 
2440
- process.platform === "win32";
2945
+ isTextual: (value) => {
2946
+ const mediaType = CONTENT_TYPE.asMediaType(value);
2947
+ if (mediaType.startsWith("text/")) {
2948
+ return true;
2949
+ }
2950
+ const mediaTypeInfo = mediaTypeInfos[mediaType];
2951
+ if (mediaTypeInfo && mediaTypeInfo.isTextual) {
2952
+ return true;
2953
+ }
2954
+ // catch things like application/manifest+json, application/importmap+json
2955
+ if (/^application\/\w+\+json$/.test(mediaType)) {
2956
+ return true;
2957
+ }
2958
+ return false;
2959
+ },
2441
2960
 
2442
- /*
2443
- * - stats object documentation on Node.js
2444
- * https://nodejs.org/docs/latest-v13.x/api/fs.html#fs_class_fs_stats
2445
- */
2961
+ isBinary: (value) => !CONTENT_TYPE.isTextual(value),
2446
2962
 
2963
+ asFileExtension: (value) => {
2964
+ const mediaType = CONTENT_TYPE.asMediaType(value);
2965
+ const mediaTypeInfo = mediaTypeInfos[mediaType];
2966
+ return mediaTypeInfo ? `.${mediaTypeInfo.extensions[0]}` : "";
2967
+ },
2447
2968
 
2448
- process.platform === "win32";
2969
+ fromUrlExtension: (url) => {
2970
+ const { pathname } = new URL(url);
2971
+ const extensionWithDot = extname(pathname);
2972
+ if (!extensionWithDot || extensionWithDot === ".") {
2973
+ return "application/octet-stream";
2974
+ }
2975
+ const extension = extensionWithDot.slice(1);
2976
+ const mediaTypeFound = Object.keys(mediaTypeInfos).find((mediaType) => {
2977
+ const mediaTypeInfo = mediaTypeInfos[mediaType];
2978
+ return (
2979
+ mediaTypeInfo.extensions && mediaTypeInfo.extensions.includes(extension)
2980
+ );
2981
+ });
2982
+ return mediaTypeFound || "application/octet-stream";
2983
+ },
2984
+ };
2449
2985
 
2450
- process.platform === "win32";
2986
+ const normalizeMediaType = (value) => {
2987
+ if (value === "application/javascript") {
2988
+ return "text/javascript";
2989
+ }
2990
+ return value;
2991
+ };
2451
2992
 
2452
2993
  const ensureEmptyDirectory = async (source) => {
2453
2994
  const stats = await readEntryStat(source, {
@@ -3216,355 +3757,122 @@ function createSupportsColor(stream, options = {}) {
3216
3757
  });
3217
3758
 
3218
3759
  const processSupportsBasicColor = createSupportsColor(process.stdout).hasBasic;
3219
- let canUseColors = processSupportsBasicColor;
3220
-
3221
- // GitHub workflow does support ANSI but "supports-color" returns false
3222
- // because stream.isTTY returns false, see https://github.com/actions/runner/issues/241
3223
- if (process.env.GITHUB_WORKFLOW) {
3224
- // Check on FORCE_COLOR is to ensure it is prio over GitHub workflow check
3225
- if (process.env.FORCE_COLOR !== "false") {
3226
- // in unit test we use process.env.FORCE_COLOR = 'false' to fake
3227
- // that colors are not supported. Let it have priority
3228
- canUseColors = true;
3229
- }
3230
- }
3231
-
3232
3760
  // https://github.com/Marak/colors.js/blob/master/lib/styles.js
3233
- const RED = "\x1b[31m";
3234
- const GREEN = "\x1b[32m";
3235
- const YELLOW = "\x1b[33m";
3236
- const BLUE = "\x1b[34m";
3237
- const MAGENTA = "\x1b[35m";
3238
- const GREY = "\x1b[90m";
3761
+ // https://stackoverflow.com/a/75985833/2634179
3239
3762
  const RESET = "\x1b[0m";
3240
3763
 
3241
- const setANSIColor = canUseColors
3242
- ? (text, ANSI_COLOR) => `${ANSI_COLOR}${text}${RESET}`
3243
- : (text) => text;
3244
-
3245
3764
  const ANSI = {
3246
- supported: canUseColors,
3247
-
3248
- RED,
3249
- GREEN,
3250
- YELLOW,
3251
- BLUE,
3252
- MAGENTA,
3253
- GREY,
3254
- RESET,
3255
-
3256
- color: setANSIColor,
3257
- };
3258
-
3259
- function isUnicodeSupported() {
3260
- if (process$1.platform !== 'win32') {
3261
- return process$1.env.TERM !== 'linux'; // Linux console (kernel)
3262
- }
3263
-
3264
- return Boolean(process$1.env.WT_SESSION) // Windows Terminal
3265
- || Boolean(process$1.env.TERMINUS_SUBLIME) // Terminus (<0.2.27)
3266
- || process$1.env.ConEmuTask === '{cmd::Cmder}' // ConEmu and cmder
3267
- || process$1.env.TERM_PROGRAM === 'Terminus-Sublime'
3268
- || process$1.env.TERM_PROGRAM === 'vscode'
3269
- || process$1.env.TERM === 'xterm-256color'
3270
- || process$1.env.TERM === 'alacritty'
3271
- || process$1.env.TERMINAL_EMULATOR === 'JetBrains-JediTerm';
3272
- }
3273
-
3274
- // see also https://github.com/sindresorhus/figures
3275
-
3276
-
3277
- const canUseUnicode = isUnicodeSupported();
3278
-
3279
- const COMMAND_RAW = canUseUnicode ? `❯` : `>`;
3280
- const OK_RAW = canUseUnicode ? `✔` : `√`;
3281
- const FAILURE_RAW = canUseUnicode ? `✖` : `×`;
3282
- const DEBUG_RAW = canUseUnicode ? `◆` : `♦`;
3283
- const INFO_RAW = canUseUnicode ? `ℹ` : `i`;
3284
- const WARNING_RAW = canUseUnicode ? `⚠` : `‼`;
3285
- const CIRCLE_CROSS_RAW = canUseUnicode ? `ⓧ` : `(×)`;
3286
-
3287
- const COMMAND = ANSI.color(COMMAND_RAW, ANSI.GREY); // ANSI_MAGENTA)
3288
- const OK = ANSI.color(OK_RAW, ANSI.GREEN);
3289
- const FAILURE = ANSI.color(FAILURE_RAW, ANSI.RED);
3290
- const DEBUG = ANSI.color(DEBUG_RAW, ANSI.GREY);
3291
- const INFO = ANSI.color(INFO_RAW, ANSI.BLUE);
3292
- const WARNING = ANSI.color(WARNING_RAW, ANSI.YELLOW);
3293
- const CIRCLE_CROSS = ANSI.color(CIRCLE_CROSS_RAW, ANSI.RED);
3294
-
3295
- const UNICODE = {
3296
- COMMAND,
3297
- OK,
3298
- FAILURE,
3299
- DEBUG,
3300
- INFO,
3301
- WARNING,
3302
- CIRCLE_CROSS,
3303
-
3304
- COMMAND_RAW,
3305
- OK_RAW,
3306
- FAILURE_RAW,
3307
- DEBUG_RAW,
3308
- INFO_RAW,
3309
- WARNING_RAW,
3310
- CIRCLE_CROSS_RAW,
3311
-
3312
- supported: canUseUnicode,
3313
- };
3314
-
3315
- const createDetailedMessage$1 = (message, details = {}) => {
3316
- let string = `${message}`;
3317
-
3318
- Object.keys(details).forEach((key) => {
3319
- const value = details[key];
3320
- string += `
3321
- --- ${key} ---
3322
- ${
3323
- Array.isArray(value)
3324
- ? value.join(`
3325
- `)
3326
- : value
3327
- }`;
3328
- });
3329
-
3330
- return string;
3331
- };
3332
-
3333
- const getPrecision = (number) => {
3334
- if (Math.floor(number) === number) return 0;
3335
- const [, decimals] = number.toString().split(".");
3336
- return decimals.length || 0;
3337
- };
3338
-
3339
- const setRoundedPrecision = (
3340
- number,
3341
- { decimals = 1, decimalsWhenSmall = decimals } = {},
3342
- ) => {
3343
- return setDecimalsPrecision(number, {
3344
- decimals,
3345
- decimalsWhenSmall,
3346
- transform: Math.round,
3347
- });
3348
- };
3765
+ supported: processSupportsBasicColor,
3766
+
3767
+ RED: "\x1b[31m",
3768
+ GREEN: "\x1b[32m",
3769
+ YELLOW: "\x1b[33m",
3770
+ BLUE: "\x1b[34m",
3771
+ MAGENTA: "\x1b[35m",
3772
+ GREY: "\x1b[90m",
3773
+ color: (text, ANSI_COLOR) => {
3774
+ return ANSI.supported ? `${ANSI_COLOR}${text}${RESET}` : text;
3775
+ },
3349
3776
 
3350
- const setDecimalsPrecision = (
3351
- number,
3352
- {
3353
- transform,
3354
- decimals, // max decimals for number in [-Infinity, -1[]1, Infinity]
3355
- decimalsWhenSmall, // max decimals for number in [-1,1]
3356
- } = {},
3357
- ) => {
3358
- if (number === 0) {
3359
- return 0;
3360
- }
3361
- let numberCandidate = Math.abs(number);
3362
- if (numberCandidate < 1) {
3363
- const integerGoal = Math.pow(10, decimalsWhenSmall - 1);
3364
- let i = 1;
3365
- while (numberCandidate < integerGoal) {
3366
- numberCandidate *= 10;
3367
- i *= 10;
3368
- }
3369
- const asInteger = transform(numberCandidate);
3370
- const asFloat = asInteger / i;
3371
- return number < 0 ? -asFloat : asFloat;
3372
- }
3373
- const coef = Math.pow(10, decimals);
3374
- const numberMultiplied = (number + Number.EPSILON) * coef;
3375
- const asInteger = transform(numberMultiplied);
3376
- const asFloat = asInteger / coef;
3377
- return number < 0 ? -asFloat : asFloat;
3777
+ BOLD: "\x1b[1m",
3778
+ effect: (text, ANSI_EFFECT) => {
3779
+ return ANSI.supported ? `${ANSI_EFFECT}${text}${RESET}` : text;
3780
+ },
3378
3781
  };
3379
3782
 
3380
- // https://www.codingem.com/javascript-how-to-limit-decimal-places/
3381
- // export const roundNumber = (number, maxDecimals) => {
3382
- // const decimalsExp = Math.pow(10, maxDecimals)
3383
- // const numberRoundInt = Math.round(decimalsExp * (number + Number.EPSILON))
3384
- // const numberRoundFloat = numberRoundInt / decimalsExp
3385
- // return numberRoundFloat
3386
- // }
3387
-
3388
- // export const setPrecision = (number, precision) => {
3389
- // if (Math.floor(number) === number) return number
3390
- // const [int, decimals] = number.toString().split(".")
3391
- // if (precision <= 0) return int
3392
- // const numberTruncated = `${int}.${decimals.slice(0, precision)}`
3393
- // return numberTruncated
3394
- // }
3395
-
3396
- const msAsDuration = (ms) => {
3397
- // ignore ms below meaningfulMs so that:
3398
- // msAsDuration(0.5) -> "0 second"
3399
- // msAsDuration(1.1) -> "0.001 second" (and not "0.0011 second")
3400
- // This tool is meant to be read by humans and it would be barely readable to see
3401
- // "0.0001 second" (stands for 0.1 millisecond)
3402
- // yes we could return "0.1 millisecond" but we choosed consistency over precision
3403
- // so that the prefered unit is "second" (and does not become millisecond when ms is super small)
3404
- if (ms < 1) {
3405
- return "0 second";
3406
- }
3407
- const { primary, remaining } = parseMs(ms);
3408
- if (!remaining) {
3409
- return formatDurationUnit(primary, primary.name === "second" ? 1 : 0);
3410
- }
3411
- return `${formatDurationUnit(primary, 0)} and ${formatDurationUnit(
3412
- remaining,
3413
- 0,
3414
- )}`;
3415
- };
3783
+ // GitHub workflow does support ANSI but "supports-color" returns false
3784
+ // because stream.isTTY returns false, see https://github.com/actions/runner/issues/241
3785
+ if (
3786
+ process.env.GITHUB_WORKFLOW &&
3787
+ // Check on FORCE_COLOR is to ensure it is prio over GitHub workflow check
3788
+ // in unit test we use process.env.FORCE_COLOR = 'false' to fake
3789
+ // that colors are not supported. Let it have priority
3790
+ process.env.FORCE_COLOR !== "false"
3791
+ ) {
3792
+ ANSI.supported = true;
3793
+ }
3416
3794
 
3417
- const formatDurationUnit = (unit, decimals) => {
3418
- const count = setRoundedPrecision(unit.count, {
3419
- decimals,
3420
- });
3421
- if (count <= 1) {
3422
- return `${count} ${unit.name}`;
3423
- }
3424
- return `${count} ${unit.name}s`;
3425
- };
3795
+ function isUnicodeSupported() {
3796
+ if (process$1.platform !== 'win32') {
3797
+ return process$1.env.TERM !== 'linux'; // Linux console (kernel)
3798
+ }
3426
3799
 
3427
- const MS_PER_UNITS = {
3428
- year: 31_557_600_000,
3429
- month: 2_629_000_000,
3430
- week: 604_800_000,
3431
- day: 86_400_000,
3432
- hour: 3_600_000,
3433
- minute: 60_000,
3434
- second: 1000,
3435
- };
3800
+ return Boolean(process$1.env.WT_SESSION) // Windows Terminal
3801
+ || Boolean(process$1.env.TERMINUS_SUBLIME) // Terminus (<0.2.27)
3802
+ || process$1.env.ConEmuTask === '{cmd::Cmder}' // ConEmu and cmder
3803
+ || process$1.env.TERM_PROGRAM === 'Terminus-Sublime'
3804
+ || process$1.env.TERM_PROGRAM === 'vscode'
3805
+ || process$1.env.TERM === 'xterm-256color'
3806
+ || process$1.env.TERM === 'alacritty'
3807
+ || process$1.env.TERMINAL_EMULATOR === 'JetBrains-JediTerm';
3808
+ }
3436
3809
 
3437
- const parseMs = (ms) => {
3438
- const unitNames = Object.keys(MS_PER_UNITS);
3439
- const smallestUnitName = unitNames[unitNames.length - 1];
3440
- let firstUnitName = smallestUnitName;
3441
- let firstUnitCount = ms / MS_PER_UNITS[smallestUnitName];
3442
- const firstUnitIndex = unitNames.findIndex((unitName) => {
3443
- if (unitName === smallestUnitName) {
3444
- return false;
3445
- }
3446
- const msPerUnit = MS_PER_UNITS[unitName];
3447
- const unitCount = Math.floor(ms / msPerUnit);
3448
- if (unitCount) {
3449
- firstUnitName = unitName;
3450
- firstUnitCount = unitCount;
3451
- return true;
3452
- }
3453
- return false;
3454
- });
3455
- if (firstUnitName === smallestUnitName) {
3456
- return {
3457
- primary: {
3458
- name: firstUnitName,
3459
- count: firstUnitCount,
3460
- },
3461
- };
3462
- }
3463
- const remainingMs = ms - firstUnitCount * MS_PER_UNITS[firstUnitName];
3464
- const remainingUnitName = unitNames[firstUnitIndex + 1];
3465
- const remainingUnitCount = remainingMs / MS_PER_UNITS[remainingUnitName];
3466
- // - 1 year and 1 second is too much information
3467
- // so we don't check the remaining units
3468
- // - 1 year and 0.0001 week is awful
3469
- // hence the if below
3470
- if (Math.round(remainingUnitCount) < 1) {
3471
- return {
3472
- primary: {
3473
- name: firstUnitName,
3474
- count: firstUnitCount,
3475
- },
3476
- };
3477
- }
3478
- // - 1 year and 1 month is great
3479
- return {
3480
- primary: {
3481
- name: firstUnitName,
3482
- count: firstUnitCount,
3483
- },
3484
- remaining: {
3485
- name: remainingUnitName,
3486
- count: remainingUnitCount,
3487
- },
3488
- };
3489
- };
3810
+ // see also https://github.com/sindresorhus/figures
3490
3811
 
3491
- const byteAsFileSize = (numberOfBytes) => {
3492
- return formatBytes(numberOfBytes);
3493
- };
3494
3812
 
3495
- const formatBytes = (number, { fixedDecimals = false } = {}) => {
3496
- if (number === 0) {
3497
- return `0 B`;
3498
- }
3499
- const exponent = Math.min(
3500
- Math.floor(Math.log10(number) / 3),
3501
- BYTE_UNITS.length - 1,
3502
- );
3503
- const unitNumber = number / Math.pow(1000, exponent);
3504
- const unitName = BYTE_UNITS[exponent];
3505
- const maxDecimals = unitNumber < 100 ? 1 : 0;
3506
- const unitNumberRounded = setRoundedPrecision(unitNumber, {
3507
- decimals: maxDecimals,
3508
- decimalsWhenSmall: 1,
3509
- });
3510
- if (fixedDecimals) {
3511
- return `${unitNumberRounded.toFixed(maxDecimals)} ${unitName}`;
3512
- }
3513
- return `${unitNumberRounded} ${unitName}`;
3813
+ const UNICODE = {
3814
+ supported: isUnicodeSupported(),
3815
+
3816
+ get COMMAND_RAW() {
3817
+ return UNICODE.supported ? `❯` : `>`;
3818
+ },
3819
+ get OK_RAW() {
3820
+ return UNICODE.supported ? `✔` : `√`;
3821
+ },
3822
+ get FAILURE_RAW() {
3823
+ return UNICODE.supported ? `✖` : `×`;
3824
+ },
3825
+ get DEBUG_RAW() {
3826
+ return UNICODE.supported ? `◆` : `♦`;
3827
+ },
3828
+ get INFO_RAW() {
3829
+ return UNICODE.supported ? `ℹ` : `i`;
3830
+ },
3831
+ get WARNING_RAW() {
3832
+ return UNICODE.supported ? `⚠` : `‼`;
3833
+ },
3834
+ get CIRCLE_CROSS_RAW() {
3835
+ return UNICODE.supported ? `ⓧ` : `(×)`;
3836
+ },
3837
+ get COMMAND() {
3838
+ return ANSI.color(UNICODE.COMMAND_RAW, ANSI.GREY); // ANSI_MAGENTA)
3839
+ },
3840
+ get OK() {
3841
+ return ANSI.color(UNICODE.OK_RAW, ANSI.GREEN);
3842
+ },
3843
+ get FAILURE() {
3844
+ return ANSI.color(UNICODE.FAILURE_RAW, ANSI.RED);
3845
+ },
3846
+ get DEBUG() {
3847
+ return ANSI.color(UNICODE.DEBUG_RAW, ANSI.GREY);
3848
+ },
3849
+ get INFO() {
3850
+ return ANSI.color(UNICODE.INFO_RAW, ANSI.BLUE);
3851
+ },
3852
+ get WARNING() {
3853
+ return ANSI.color(UNICODE.WARNING_RAW, ANSI.YELLOW);
3854
+ },
3855
+ get CIRCLE_CROSS() {
3856
+ return ANSI.color(UNICODE.CIRCLE_CROSS_RAW, ANSI.RED);
3857
+ },
3514
3858
  };
3515
3859
 
3516
- const BYTE_UNITS = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
3860
+ const createDetailedMessage$1 = (message, details = {}) => {
3861
+ let string = `${message}`;
3517
3862
 
3518
- const distributePercentages = (
3519
- namedNumbers,
3520
- { maxPrecisionHint = 2 } = {},
3521
- ) => {
3522
- const numberNames = Object.keys(namedNumbers);
3523
- if (numberNames.length === 0) {
3524
- return {};
3525
- }
3526
- if (numberNames.length === 1) {
3527
- const firstNumberName = numberNames[0];
3528
- return { [firstNumberName]: "100 %" };
3529
- }
3530
- const numbers = numberNames.map((name) => namedNumbers[name]);
3531
- const total = numbers.reduce((sum, value) => sum + value, 0);
3532
- const ratios = numbers.map((number) => number / total);
3533
- const percentages = {};
3534
- ratios.pop();
3535
- ratios.forEach((ratio, index) => {
3536
- const percentage = ratio * 100;
3537
- percentages[numberNames[index]] = percentage;
3538
- });
3539
- const lowestPercentage = (1 / Math.pow(10, maxPrecisionHint)) * 100;
3540
- let precision = 0;
3541
- Object.keys(percentages).forEach((name) => {
3542
- const percentage = percentages[name];
3543
- if (percentage < lowestPercentage) {
3544
- // check the amout of meaningful decimals
3545
- // and that what we will use
3546
- const percentageRounded = setRoundedPrecision(percentage);
3547
- const percentagePrecision = getPrecision(percentageRounded);
3548
- if (percentagePrecision > precision) {
3549
- precision = percentagePrecision;
3550
- }
3551
- }
3863
+ Object.keys(details).forEach((key) => {
3864
+ const value = details[key];
3865
+ string += `
3866
+ --- ${key} ---
3867
+ ${
3868
+ Array.isArray(value)
3869
+ ? value.join(`
3870
+ `)
3871
+ : value
3872
+ }`;
3552
3873
  });
3553
- let remainingPercentage = 100;
3554
3874
 
3555
- Object.keys(percentages).forEach((name) => {
3556
- const percentage = percentages[name];
3557
- const percentageAllocated = setRoundedPrecision(percentage, {
3558
- decimals: precision,
3559
- });
3560
- remainingPercentage -= percentageAllocated;
3561
- percentages[name] = percentageAllocated;
3562
- });
3563
- const lastName = numberNames[numberNames.length - 1];
3564
- percentages[lastName] = setRoundedPrecision(remainingPercentage, {
3565
- decimals: precision,
3566
- });
3567
- return percentages;
3875
+ return string;
3568
3876
  };
3569
3877
 
3570
3878
  const ESC = '\u001B[';
@@ -3732,58 +4040,28 @@ ansiEscapes.iTerm = {
3732
4040
  },
3733
4041
  };
3734
4042
 
3735
- /*
3736
- *
3737
- */
3738
-
3739
- // maybe https://github.com/gajus/output-interceptor/tree/v3.0.0 ?
3740
- // the problem with listening data on stdout
3741
- // is that node.js will later throw error if stream gets closed
3742
- // while something listening data on it
3743
- const spyStreamOutput = (stream) => {
3744
- const originalWrite = stream.write;
3745
-
3746
- let output = "";
3747
- let installed = true;
3748
-
3749
- stream.write = function (...args /* chunk, encoding, callback */) {
3750
- output += args;
3751
- return originalWrite.call(stream, ...args);
3752
- };
3753
-
3754
- const uninstall = () => {
3755
- if (!installed) {
3756
- return;
3757
- }
3758
- stream.write = originalWrite;
3759
- installed = false;
3760
- };
3761
-
3762
- return () => {
3763
- uninstall();
3764
- return output;
3765
- };
3766
- };
3767
-
3768
4043
  /*
3769
4044
  * see also https://github.com/vadimdemedes/ink
3770
4045
  */
3771
4046
 
3772
4047
 
3773
- const createLog = ({
4048
+ const createDynamicLog = ({
3774
4049
  stream = process.stdout,
3775
- newLine = "after",
4050
+ clearTerminalAllowed,
4051
+ onVerticalOverflow = () => {},
4052
+ onWriteFromOutside = () => {},
3776
4053
  } = {}) => {
3777
4054
  const { columns = 80, rows = 24 } = stream;
3778
-
3779
- const log = {
4055
+ const dynamicLog = {
3780
4056
  destroyed: false,
3781
- onVerticalOverflow: () => {},
4057
+ onVerticalOverflow,
4058
+ onWriteFromOutside,
3782
4059
  };
3783
4060
 
3784
4061
  let lastOutput = "";
4062
+ let lastOutputFromOutside = "";
3785
4063
  let clearAttemptResult;
3786
- let streamOutputSpy = noopStreamSpy;
4064
+ let writing = false;
3787
4065
 
3788
4066
  const getErasePreviousOutput = () => {
3789
4067
  // nothing to clear
@@ -3796,19 +4074,27 @@ const createLog = ({
3796
4074
 
3797
4075
  const logLines = lastOutput.split(/\r\n|\r|\n/);
3798
4076
  let visualLineCount = 0;
3799
- logLines.forEach((logLine) => {
4077
+ for (const logLine of logLines) {
3800
4078
  const width = stringWidth(logLine);
3801
- visualLineCount += width === 0 ? 1 : Math.ceil(width / columns);
3802
- });
4079
+ if (width === 0) {
4080
+ visualLineCount++;
4081
+ } else {
4082
+ visualLineCount += Math.ceil(width / columns);
4083
+ }
4084
+ }
3803
4085
 
3804
4086
  if (visualLineCount > rows) {
4087
+ if (clearTerminalAllowed) {
4088
+ clearAttemptResult = true;
4089
+ return ansiEscapes.clearTerminal;
4090
+ }
3805
4091
  // the whole log cannot be cleared because it's vertically to long
3806
4092
  // (longer than terminal height)
3807
4093
  // readline.moveCursor cannot move cursor higher than screen height
3808
4094
  // it means we would only clear the visible part of the log
3809
4095
  // better keep the log untouched
3810
4096
  clearAttemptResult = false;
3811
- log.onVerticalOverflow();
4097
+ dynamicLog.onVerticalOverflow();
3812
4098
  return "";
3813
4099
  }
3814
4100
 
@@ -3816,95 +4102,129 @@ const createLog = ({
3816
4102
  return ansiEscapes.eraseLines(visualLineCount);
3817
4103
  };
3818
4104
 
3819
- const spyStream = () => {
3820
- if (stream === process.stdout) {
3821
- const stdoutSpy = spyStreamOutput(process.stdout);
3822
- const stderrSpy = spyStreamOutput(process.stderr);
3823
- return () => {
3824
- return stdoutSpy() + stderrSpy();
3825
- };
4105
+ const update = (string) => {
4106
+ if (dynamicLog.destroyed) {
4107
+ throw new Error("Cannot write log after destroy");
3826
4108
  }
3827
- return spyStreamOutput(stream);
3828
- };
3829
-
3830
- const doWrite = (string) => {
3831
- string = addNewLines(string, newLine);
3832
- stream.write(string);
4109
+ let stringToWrite = string;
4110
+ if (lastOutput) {
4111
+ if (lastOutputFromOutside) {
4112
+ // We don't want to clear logs written by other code,
4113
+ // it makes output unreadable and might erase precious information
4114
+ // To detect this we put a spy on the stream.
4115
+ // The spy is required only if we actually wrote something in the stream
4116
+ // something else than this code has written in the stream
4117
+ // so we just write without clearing (append instead of replacing)
4118
+ lastOutputFromOutside = "";
4119
+ } else {
4120
+ stringToWrite = `${getErasePreviousOutput()}${string}`;
4121
+ }
4122
+ }
4123
+ writing = true;
4124
+ stream.write(stringToWrite);
3833
4125
  lastOutput = string;
4126
+ writing = false;
3834
4127
  clearAttemptResult = undefined;
4128
+ };
4129
+
4130
+ const clearDuringFunctionCall = (callback) => {
4131
+ // 1. Erase the current log
4132
+ // 2. Call callback (expected to write something on stdout)
4133
+ // 3. Restore the current log
4134
+ // During step 2. we expect a "write from outside" so we uninstall
4135
+ // the stream spy during function call
4136
+ const currentOutput = lastOutput;
4137
+ update("");
4138
+
4139
+ writing = true;
4140
+ callback();
4141
+ writing = false;
3835
4142
 
3836
- // We don't want to clear logs written by other code,
3837
- // it makes output unreadable and might erase precious information
3838
- // To detect this we put a spy on the stream.
3839
- // The spy is required only if we actually wrote something in the stream
3840
- // otherwise tryToClear() won't do a thing so spy is useless
3841
- streamOutputSpy = string ? spyStream() : noopStreamSpy;
4143
+ update(currentOutput);
3842
4144
  };
3843
4145
 
3844
- const write = (string, outputFromOutside = streamOutputSpy()) => {
3845
- if (log.destroyed) {
3846
- throw new Error("Cannot write log after destroy");
3847
- }
4146
+ const writeFromOutsideEffect = (value) => {
3848
4147
  if (!lastOutput) {
3849
- doWrite(string);
4148
+ // we don't care if the log never wrote anything
4149
+ // or if last update() wrote an empty string
3850
4150
  return;
3851
4151
  }
3852
- if (outputFromOutside) {
3853
- // something else than this code has written in the stream
3854
- // so we just write without clearing (append instead of replacing)
3855
- doWrite(string);
3856
- } else {
3857
- doWrite(`${getErasePreviousOutput()}${string}`);
4152
+ if (writing) {
4153
+ return;
3858
4154
  }
4155
+ lastOutputFromOutside = value;
4156
+ dynamicLog.onWriteFromOutside(value);
3859
4157
  };
3860
4158
 
3861
- const dynamicWrite = (callback) => {
3862
- const outputFromOutside = streamOutputSpy();
3863
- const string = callback({ outputFromOutside });
3864
- return write(string, outputFromOutside);
3865
- };
4159
+ let removeStreamSpy;
4160
+ if (stream === process.stdout) {
4161
+ const removeStdoutSpy = spyStreamOutput(
4162
+ process.stdout,
4163
+ writeFromOutsideEffect,
4164
+ );
4165
+ const removeStderrSpy = spyStreamOutput(
4166
+ process.stderr,
4167
+ writeFromOutsideEffect,
4168
+ );
4169
+ removeStreamSpy = () => {
4170
+ removeStdoutSpy();
4171
+ removeStderrSpy();
4172
+ };
4173
+ } else {
4174
+ removeStreamSpy = spyStreamOutput(stream, writeFromOutsideEffect);
4175
+ }
3866
4176
 
3867
4177
  const destroy = () => {
3868
- log.destroyed = true;
3869
- if (streamOutputSpy) {
3870
- streamOutputSpy(); // this uninstalls the spy
3871
- streamOutputSpy = null;
4178
+ dynamicLog.destroyed = true;
4179
+ if (removeStreamSpy) {
4180
+ removeStreamSpy();
4181
+ removeStreamSpy = null;
3872
4182
  lastOutput = "";
4183
+ lastOutputFromOutside = "";
3873
4184
  }
3874
4185
  };
3875
4186
 
3876
- Object.assign(log, {
3877
- write,
3878
- dynamicWrite,
4187
+ Object.assign(dynamicLog, {
4188
+ update,
3879
4189
  destroy,
3880
4190
  stream,
4191
+ clearDuringFunctionCall,
3881
4192
  });
3882
- return log;
4193
+ return dynamicLog;
3883
4194
  };
3884
4195
 
3885
- const noopStreamSpy = () => "";
4196
+ // maybe https://github.com/gajus/output-interceptor/tree/v3.0.0 ?
4197
+ // the problem with listening data on stdout
4198
+ // is that node.js will later throw error if stream gets closed
4199
+ // while something listening data on it
4200
+ const spyStreamOutput = (stream, callback) => {
4201
+ const originalWrite = stream.write;
4202
+
4203
+ let output = "";
4204
+ let installed = true;
4205
+
4206
+ stream.write = function (...args /* chunk, encoding, callback */) {
4207
+ output += args;
4208
+ callback(output);
4209
+ return originalWrite.call(stream, ...args);
4210
+ };
4211
+
4212
+ const uninstall = () => {
4213
+ if (!installed) {
4214
+ return;
4215
+ }
4216
+ stream.write = originalWrite;
4217
+ installed = false;
4218
+ };
3886
4219
 
3887
- // could be inlined but vscode do not correctly
3888
- // expand/collapse template strings, so I put it at the bottom
3889
- const addNewLines = (string, newLine) => {
3890
- if (newLine === "before") {
3891
- return `
3892
- ${string}`;
3893
- }
3894
- if (newLine === "after") {
3895
- return `${string}
3896
- `;
3897
- }
3898
- if (newLine === "around") {
3899
- return `
3900
- ${string}
3901
- `;
3902
- }
3903
- return string;
4220
+ return () => {
4221
+ uninstall();
4222
+ return output;
4223
+ };
3904
4224
  };
3905
4225
 
3906
4226
  const startSpinner = ({
3907
- log,
4227
+ dynamicLog,
3908
4228
  frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
3909
4229
  fps = 20,
3910
4230
  keepProcessAlive = false,
@@ -3912,7 +4232,7 @@ const startSpinner = ({
3912
4232
  stopOnVerticalOverflow = true,
3913
4233
  render = () => "",
3914
4234
  effect = () => {},
3915
- animated = log.stream.isTTY,
4235
+ animated = dynamicLog.stream.isTTY,
3916
4236
  }) => {
3917
4237
  let frameIndex = 0;
3918
4238
  let interval;
@@ -3923,7 +4243,9 @@ const startSpinner = ({
3923
4243
  };
3924
4244
 
3925
4245
  const update = (message) => {
3926
- spinner.message = running ? `${frames[frameIndex]} ${message}` : message;
4246
+ spinner.message = running
4247
+ ? `${frames[frameIndex]} ${message}\n`
4248
+ : `${message}\n`;
3927
4249
  return spinner.message;
3928
4250
  };
3929
4251
  spinner.update = update;
@@ -3932,23 +4254,17 @@ const startSpinner = ({
3932
4254
  if (animated && ANSI.supported) {
3933
4255
  running = true;
3934
4256
  cleanup = effect();
3935
- log.write(update(render()));
4257
+ dynamicLog.update(update(render()));
3936
4258
 
3937
4259
  interval = setInterval(() => {
3938
4260
  frameIndex = frameIndex === frames.length - 1 ? 0 : frameIndex + 1;
3939
- log.dynamicWrite(({ outputFromOutside }) => {
3940
- if (outputFromOutside && stopOnWriteFromOutside) {
3941
- stop();
3942
- return "";
3943
- }
3944
- return update(render());
3945
- });
4261
+ dynamicLog.update(update(render()));
3946
4262
  }, 1000 / fps);
3947
4263
  if (!keepProcessAlive) {
3948
4264
  interval.unref();
3949
4265
  }
3950
4266
  } else {
3951
- log.write(update(render()));
4267
+ dynamicLog.update(update(render()));
3952
4268
  }
3953
4269
 
3954
4270
  const stop = (message) => {
@@ -3961,15 +4277,18 @@ const startSpinner = ({
3961
4277
  cleanup();
3962
4278
  cleanup = null;
3963
4279
  }
3964
- if (log && message) {
3965
- log.write(update(message));
3966
- log = null;
4280
+ if (dynamicLog && message) {
4281
+ dynamicLog.update(update(message));
4282
+ dynamicLog = null;
3967
4283
  }
3968
4284
  };
3969
4285
  spinner.stop = stop;
3970
4286
 
3971
4287
  if (stopOnVerticalOverflow) {
3972
- log.onVerticalOverflow = stop;
4288
+ dynamicLog.onVerticalOverflow = stop;
4289
+ }
4290
+ if (stopOnWriteFromOutside) {
4291
+ dynamicLog.onWriteFromOutside = stop;
3973
4292
  }
3974
4293
 
3975
4294
  return spinner;
@@ -3988,10 +4307,10 @@ const createTaskLog = (
3988
4307
  };
3989
4308
  }
3990
4309
  const startMs = Date.now();
3991
- const log = createLog();
4310
+ const dynamicLog = createDynamicLog();
3992
4311
  let message = label;
3993
4312
  const taskSpinner = startSpinner({
3994
- log,
4313
+ dynamicLog,
3995
4314
  render: () => message,
3996
4315
  stopOnWriteFromOutside,
3997
4316
  animated,
@@ -4003,7 +4322,7 @@ const createTaskLog = (
4003
4322
  done: () => {
4004
4323
  const msEllapsed = Date.now() - startMs;
4005
4324
  taskSpinner.stop(
4006
- `${UNICODE.OK} ${label} (done in ${msAsDuration(msEllapsed)})`,
4325
+ `${UNICODE.OK} ${label} (done in ${inspectDuration(msEllapsed)})`,
4007
4326
  );
4008
4327
  },
4009
4328
  happen: (message) => {
@@ -6004,7 +6323,7 @@ const startServer = async ({
6004
6323
  let status = "starting";
6005
6324
  let nodeServer;
6006
6325
  const startServerOperation = Abort.startOperation();
6007
- const stopCallbackList = createCallbackListNotifiedOnce();
6326
+ const stopCallbackSet = new Set();
6008
6327
  const serverOrigins = {
6009
6328
  local: "", // favors hostname when possible
6010
6329
  };
@@ -6128,10 +6447,10 @@ const startServer = async ({
6128
6447
  // (otherwise an AbortError is thrown to the code calling "startServer")
6129
6448
  // we can proceed to create a stop function to stop it gacefully
6130
6449
  // and add a request handler
6131
- stopCallbackList.add(({ reason }) => {
6450
+ stopCallbackSet.add(({ reason }) => {
6132
6451
  logger.info(`${serverName} stopping server (reason: ${reason})`);
6133
6452
  });
6134
- stopCallbackList.add(async () => {
6453
+ stopCallbackSet.add(async () => {
6135
6454
  await stopListening(nodeServer);
6136
6455
  });
6137
6456
  let stoppedResolve;
@@ -6140,7 +6459,12 @@ const startServer = async ({
6140
6459
  });
6141
6460
  const stop = memoize(async (reason = STOP_REASON_NOT_SPECIFIED) => {
6142
6461
  status = "stopping";
6143
- await Promise.all(stopCallbackList.notify({ reason }));
6462
+ const promises = [];
6463
+ for (const stopCallback of stopCallbackSet) {
6464
+ promises.push(stopCallback({ reason }));
6465
+ }
6466
+ stopCallbackSet.clear();
6467
+ await Promise.all(promises);
6144
6468
  serviceController.callHooks("serverStopped", { reason });
6145
6469
  status = "stopped";
6146
6470
  stoppedResolve(reason);
@@ -6152,7 +6476,7 @@ const startServer = async ({
6152
6476
  stop(PROCESS_TEARDOWN_EVENTS_MAP[winner.name]);
6153
6477
  },
6154
6478
  );
6155
- stopCallbackList.add(cancelProcessTeardownRace);
6479
+ stopCallbackSet.add(cancelProcessTeardownRace);
6156
6480
 
6157
6481
  const onError = (error) => {
6158
6482
  if (status === "stopping" && error.code === "ECONNRESET") {
@@ -6167,19 +6491,19 @@ const startServer = async ({
6167
6491
  nodeServer,
6168
6492
  onError,
6169
6493
  );
6170
- stopCallbackList.add(removeConnectionErrorListener);
6494
+ stopCallbackSet.add(removeConnectionErrorListener);
6171
6495
 
6172
6496
  const connectionsTracker = trackServerPendingConnections(nodeServer, {
6173
6497
  http2,
6174
6498
  });
6175
6499
  // opened connection must be shutdown before the close event is emitted
6176
- stopCallbackList.add(connectionsTracker.stop);
6500
+ stopCallbackSet.add(connectionsTracker.stop);
6177
6501
 
6178
6502
  const pendingRequestsTracker = trackServerPendingRequests(nodeServer, {
6179
6503
  http2,
6180
6504
  });
6181
6505
  // ensure pending requests got a response from the server
6182
- stopCallbackList.add((reason) => {
6506
+ stopCallbackSet.add((reason) => {
6183
6507
  pendingRequestsTracker.stop({
6184
6508
  status: reason === STOP_REASON_INTERNAL_ERROR ? 500 : 503,
6185
6509
  reason,
@@ -6219,13 +6543,13 @@ const startServer = async ({
6219
6543
  };
6220
6544
  });
6221
6545
  receiveRequestOperation.addAbortSource((abort) => {
6222
- return stopCallbackList.add(abort);
6546
+ return stopCallbackSet.add(abort);
6223
6547
  });
6224
6548
 
6225
6549
  const sendResponseOperation = Abort.startOperation();
6226
6550
  sendResponseOperation.addAbortSignal(receiveRequestOperation.signal);
6227
6551
  sendResponseOperation.addAbortSource((abort) => {
6228
- return stopCallbackList.add(abort);
6552
+ return stopCallbackSet.add(abort);
6229
6553
  });
6230
6554
 
6231
6555
  const request = fromNodeRequest(nodeRequest, {
@@ -6778,7 +7102,7 @@ const startServer = async ({
6778
7102
  };
6779
7103
  const removeRequestListener = listenRequest(nodeServer, requestCallback);
6780
7104
  // ensure we don't try to handle new requests while server is stopping
6781
- stopCallbackList.add(removeRequestListener);
7105
+ stopCallbackSet.add(removeRequestListener);
6782
7106
  }
6783
7107
 
6784
7108
  {
@@ -6795,325 +7119,139 @@ const startServer = async ({
6795
7119
  const { WebSocketServer } = await import("./js/ws.js");
6796
7120
  let websocketServer = new WebSocketServer({ noServer: true });
6797
7121
  const websocketOrigin = https
6798
- ? `wss://${hostname}:${port}`
6799
- : `ws://${hostname}:${port}`;
6800
- server.websocketOrigin = websocketOrigin;
6801
- const upgradeCallback = (nodeRequest, socket, head) => {
6802
- websocketServer.handleUpgrade(
6803
- nodeRequest,
6804
- socket,
6805
- head,
6806
- async (websocket) => {
6807
- websocketClients.add(websocket);
6808
- websocket.once("close", () => {
6809
- websocketClients.delete(websocket);
6810
- });
6811
- const request = fromNodeRequest(nodeRequest, {
6812
- serverOrigin: websocketOrigin,
6813
- signal: new AbortController().signal,
6814
- requestBodyLifetime,
6815
- });
6816
- serviceController.callAsyncHooksUntil(
6817
- "handleWebsocket",
6818
- websocket,
6819
- {
6820
- request,
6821
- },
6822
- );
6823
- },
6824
- );
6825
- };
6826
-
6827
- // see server-polyglot.js, upgrade must be listened on https server when used
6828
- const facadeServer = nodeServer._tlsServer || nodeServer;
6829
- const removeUpgradeCallback = listenEvent(
6830
- facadeServer,
6831
- "upgrade",
6832
- upgradeCallback,
6833
- );
6834
- stopCallbackList.add(removeUpgradeCallback);
6835
- stopCallbackList.add(() => {
6836
- websocketClients.forEach((websocketClient) => {
6837
- websocketClient.close();
6838
- });
6839
- websocketClients.clear();
6840
- websocketServer.close();
6841
- websocketServer = null;
6842
- });
6843
- }
6844
- }
6845
-
6846
- if (startLog) {
6847
- if (serverOrigins.network) {
6848
- logger.info(
6849
- `${serverName} started at ${serverOrigins.local} (${serverOrigins.network})`,
6850
- );
6851
- } else {
6852
- logger.info(`${serverName} started at ${serverOrigins.local}`);
6853
- }
6854
- }
6855
-
6856
- Object.assign(server, {
6857
- getStatus: () => status,
6858
- port,
6859
- hostname,
6860
- origin: serverOrigin,
6861
- origins: serverOrigins,
6862
- nodeServer,
6863
- stop,
6864
- stoppedPromise,
6865
- addEffect: (callback) => {
6866
- const cleanup = callback();
6867
- if (typeof cleanup === "function") {
6868
- stopCallbackList.add(cleanup);
6869
- }
6870
- },
6871
- });
6872
- return server;
6873
- };
6874
-
6875
- const createNodeServer = async ({
6876
- https,
6877
- redirectHttpToHttps,
6878
- allowHttpRequestOnHttps,
6879
- http2,
6880
- http1Allowed,
6881
- }) => {
6882
- if (https) {
6883
- const { certificate, privateKey } = https;
6884
- if (redirectHttpToHttps || allowHttpRequestOnHttps) {
6885
- return createPolyglotServer({
6886
- certificate,
6887
- privateKey,
6888
- http2,
6889
- http1Allowed,
6890
- });
6891
- }
6892
- const { createServer } = await import("node:https");
6893
- return createServer({
6894
- cert: certificate,
6895
- key: privateKey,
6896
- });
6897
- }
6898
- const { createServer } = await import("node:http");
6899
- return createServer();
6900
- };
6901
-
6902
- const testCanPushStream = (http2Stream) => {
6903
- if (!http2Stream.pushAllowed) {
6904
- return {
6905
- can: false,
6906
- reason: `stream.pushAllowed is false`,
6907
- };
6908
- }
6909
-
6910
- // See https://nodejs.org/dist/latest-v16.x/docs/api/http2.html#http2sessionstate
6911
- // And https://github.com/google/node-h2-auto-push/blob/67a36c04cbbd6da7b066a4e8d361c593d38853a4/src/index.ts#L100-L106
6912
- const { remoteWindowSize } = http2Stream.session.state;
6913
- if (remoteWindowSize === 0) {
6914
- return {
6915
- can: false,
6916
- reason: `no more remoteWindowSize`,
6917
- };
6918
- }
6919
-
6920
- return {
6921
- can: true,
6922
- };
6923
- };
6924
-
6925
- const PROCESS_TEARDOWN_EVENTS_MAP = {
6926
- SIGHUP: STOP_REASON_PROCESS_SIGHUP,
6927
- SIGTERM: STOP_REASON_PROCESS_SIGTERM,
6928
- SIGINT: STOP_REASON_PROCESS_SIGINT,
6929
- beforeExit: STOP_REASON_PROCESS_BEFORE_EXIT,
6930
- exit: STOP_REASON_PROCESS_EXIT,
6931
- };
6932
-
6933
- const mediaTypeInfos = {
6934
- "application/json": {
6935
- extensions: ["json", "map"],
6936
- isTextual: true,
6937
- },
6938
- "application/importmap+json": {
6939
- extensions: ["importmap"],
6940
- isTextual: true,
6941
- },
6942
- "application/manifest+json": {
6943
- extensions: ["webmanifest"],
6944
- isTextual: true,
6945
- },
6946
- "application/octet-stream": {},
6947
- "application/pdf": {
6948
- extensions: ["pdf"],
6949
- },
6950
- "application/xml": {
6951
- extensions: ["xml"],
6952
- },
6953
- "application/x-gzip": {
6954
- extensions: ["gz"],
6955
- },
6956
- "application/wasm": {
6957
- extensions: ["wasm"],
6958
- },
6959
- "application/zip": {
6960
- extensions: ["zip"],
6961
- },
6962
- "audio/basic": {
6963
- extensions: ["au", "snd"],
6964
- },
6965
- "audio/mpeg": {
6966
- extensions: ["mpga", "mp2", "mp2a", "mp3", "m2a", "m3a"],
6967
- },
6968
- "audio/midi": {
6969
- extensions: ["midi", "mid", "kar", "rmi"],
6970
- },
6971
- "audio/mp4": {
6972
- extensions: ["m4a", "mp4a"],
6973
- },
6974
- "audio/ogg": {
6975
- extensions: ["oga", "ogg", "spx"],
6976
- },
6977
- "audio/webm": {
6978
- extensions: ["weba"],
6979
- },
6980
- "audio/x-wav": {
6981
- extensions: ["wav"],
6982
- },
6983
- "font/ttf": {
6984
- extensions: ["ttf"],
6985
- },
6986
- "font/woff": {
6987
- extensions: ["woff"],
6988
- },
6989
- "font/woff2": {
6990
- extensions: ["woff2"],
6991
- },
6992
- "image/png": {
6993
- extensions: ["png"],
6994
- },
6995
- "image/gif": {
6996
- extensions: ["gif"],
6997
- },
6998
- "image/jpeg": {
6999
- extensions: ["jpg"],
7000
- },
7001
- "image/svg+xml": {
7002
- extensions: ["svg", "svgz"],
7003
- isTextual: true,
7004
- },
7005
- "text/plain": {
7006
- extensions: ["txt"],
7007
- },
7008
- "text/html": {
7009
- extensions: ["html"],
7010
- },
7011
- "text/css": {
7012
- extensions: ["css"],
7013
- },
7014
- "text/javascript": {
7015
- extensions: ["js", "cjs", "mjs", "ts", "jsx", "tsx"],
7016
- },
7017
- "text/x-sass": {
7018
- extensions: ["sass"],
7019
- },
7020
- "text/x-scss": {
7021
- extensions: ["scss"],
7022
- },
7023
- "text/cache-manifest": {
7024
- extensions: ["appcache"],
7025
- },
7026
- "video/mp4": {
7027
- extensions: ["mp4", "mp4v", "mpg4"],
7028
- },
7029
- "video/mpeg": {
7030
- extensions: ["mpeg", "mpg", "mpe", "m1v", "m2v"],
7031
- },
7032
- "video/ogg": {
7033
- extensions: ["ogv"],
7034
- },
7035
- "video/webm": {
7036
- extensions: ["webm"],
7037
- },
7038
- };
7039
-
7040
- const CONTENT_TYPE = {
7041
- parse: (string) => {
7042
- const [mediaType, charset] = string.split(";");
7043
- return { mediaType: normalizeMediaType(mediaType), charset };
7044
- },
7122
+ ? `wss://${hostname}:${port}`
7123
+ : `ws://${hostname}:${port}`;
7124
+ server.websocketOrigin = websocketOrigin;
7125
+ const upgradeCallback = (nodeRequest, socket, head) => {
7126
+ websocketServer.handleUpgrade(
7127
+ nodeRequest,
7128
+ socket,
7129
+ head,
7130
+ async (websocket) => {
7131
+ websocketClients.add(websocket);
7132
+ websocket.once("close", () => {
7133
+ websocketClients.delete(websocket);
7134
+ });
7135
+ const request = fromNodeRequest(nodeRequest, {
7136
+ serverOrigin: websocketOrigin,
7137
+ signal: new AbortController().signal,
7138
+ requestBodyLifetime,
7139
+ });
7140
+ serviceController.callAsyncHooksUntil(
7141
+ "handleWebsocket",
7142
+ websocket,
7143
+ {
7144
+ request,
7145
+ },
7146
+ );
7147
+ },
7148
+ );
7149
+ };
7045
7150
 
7046
- stringify: ({ mediaType, charset }) => {
7047
- if (charset) {
7048
- return `${mediaType};${charset}`;
7151
+ // see server-polyglot.js, upgrade must be listened on https server when used
7152
+ const facadeServer = nodeServer._tlsServer || nodeServer;
7153
+ const removeUpgradeCallback = listenEvent(
7154
+ facadeServer,
7155
+ "upgrade",
7156
+ upgradeCallback,
7157
+ );
7158
+ stopCallbackSet.add(removeUpgradeCallback);
7159
+ stopCallbackSet.add(() => {
7160
+ websocketClients.forEach((websocketClient) => {
7161
+ websocketClient.close();
7162
+ });
7163
+ websocketClients.clear();
7164
+ websocketServer.close();
7165
+ websocketServer = null;
7166
+ });
7049
7167
  }
7050
- return mediaType;
7051
- },
7168
+ }
7052
7169
 
7053
- asMediaType: (value) => {
7054
- if (typeof value === "string") {
7055
- return CONTENT_TYPE.parse(value).mediaType;
7056
- }
7057
- if (typeof value === "object") {
7058
- return value.mediaType;
7170
+ if (startLog) {
7171
+ if (serverOrigins.network) {
7172
+ logger.info(
7173
+ `${serverName} started at ${serverOrigins.local} (${serverOrigins.network})`,
7174
+ );
7175
+ } else {
7176
+ logger.info(`${serverName} started at ${serverOrigins.local}`);
7059
7177
  }
7060
- return null;
7061
- },
7178
+ }
7062
7179
 
7063
- isJson: (value) => {
7064
- const mediaType = CONTENT_TYPE.asMediaType(value);
7065
- return (
7066
- mediaType === "application/json" ||
7067
- /^application\/\w+\+json$/.test(mediaType)
7068
- );
7069
- },
7180
+ Object.assign(server, {
7181
+ getStatus: () => status,
7182
+ port,
7183
+ hostname,
7184
+ origin: serverOrigin,
7185
+ origins: serverOrigins,
7186
+ nodeServer,
7187
+ stop,
7188
+ stoppedPromise,
7189
+ addEffect: (callback) => {
7190
+ const cleanup = callback();
7191
+ if (typeof cleanup === "function") {
7192
+ stopCallbackSet.add(cleanup);
7193
+ }
7194
+ },
7195
+ });
7196
+ return server;
7197
+ };
7070
7198
 
7071
- isTextual: (value) => {
7072
- const mediaType = CONTENT_TYPE.asMediaType(value);
7073
- if (mediaType.startsWith("text/")) {
7074
- return true;
7075
- }
7076
- const mediaTypeInfo = mediaTypeInfos[mediaType];
7077
- if (mediaTypeInfo && mediaTypeInfo.isTextual) {
7078
- return true;
7079
- }
7080
- // catch things like application/manifest+json, application/importmap+json
7081
- if (/^application\/\w+\+json$/.test(mediaType)) {
7082
- return true;
7199
+ const createNodeServer = async ({
7200
+ https,
7201
+ redirectHttpToHttps,
7202
+ allowHttpRequestOnHttps,
7203
+ http2,
7204
+ http1Allowed,
7205
+ }) => {
7206
+ if (https) {
7207
+ const { certificate, privateKey } = https;
7208
+ if (redirectHttpToHttps || allowHttpRequestOnHttps) {
7209
+ return createPolyglotServer({
7210
+ certificate,
7211
+ privateKey,
7212
+ http2,
7213
+ http1Allowed,
7214
+ });
7083
7215
  }
7084
- return false;
7085
- },
7216
+ const { createServer } = await import("node:https");
7217
+ return createServer({
7218
+ cert: certificate,
7219
+ key: privateKey,
7220
+ });
7221
+ }
7222
+ const { createServer } = await import("node:http");
7223
+ return createServer();
7224
+ };
7086
7225
 
7087
- isBinary: (value) => !CONTENT_TYPE.isTextual(value),
7226
+ const testCanPushStream = (http2Stream) => {
7227
+ if (!http2Stream.pushAllowed) {
7228
+ return {
7229
+ can: false,
7230
+ reason: `stream.pushAllowed is false`,
7231
+ };
7232
+ }
7088
7233
 
7089
- asFileExtension: (value) => {
7090
- const mediaType = CONTENT_TYPE.asMediaType(value);
7091
- const mediaTypeInfo = mediaTypeInfos[mediaType];
7092
- return mediaTypeInfo ? `.${mediaTypeInfo.extensions[0]}` : "";
7093
- },
7234
+ // See https://nodejs.org/dist/latest-v16.x/docs/api/http2.html#http2sessionstate
7235
+ // And https://github.com/google/node-h2-auto-push/blob/67a36c04cbbd6da7b066a4e8d361c593d38853a4/src/index.ts#L100-L106
7236
+ const { remoteWindowSize } = http2Stream.session.state;
7237
+ if (remoteWindowSize === 0) {
7238
+ return {
7239
+ can: false,
7240
+ reason: `no more remoteWindowSize`,
7241
+ };
7242
+ }
7094
7243
 
7095
- fromUrlExtension: (url) => {
7096
- const { pathname } = new URL(url);
7097
- const extensionWithDot = extname(pathname);
7098
- if (!extensionWithDot || extensionWithDot === ".") {
7099
- return "application/octet-stream";
7100
- }
7101
- const extension = extensionWithDot.slice(1);
7102
- const mediaTypeFound = Object.keys(mediaTypeInfos).find((mediaType) => {
7103
- const mediaTypeInfo = mediaTypeInfos[mediaType];
7104
- return (
7105
- mediaTypeInfo.extensions && mediaTypeInfo.extensions.includes(extension)
7106
- );
7107
- });
7108
- return mediaTypeFound || "application/octet-stream";
7109
- },
7244
+ return {
7245
+ can: true,
7246
+ };
7110
7247
  };
7111
7248
 
7112
- const normalizeMediaType = (value) => {
7113
- if (value === "application/javascript") {
7114
- return "text/javascript";
7115
- }
7116
- return value;
7249
+ const PROCESS_TEARDOWN_EVENTS_MAP = {
7250
+ SIGHUP: STOP_REASON_PROCESS_SIGHUP,
7251
+ SIGTERM: STOP_REASON_PROCESS_SIGTERM,
7252
+ SIGINT: STOP_REASON_PROCESS_SIGINT,
7253
+ beforeExit: STOP_REASON_PROCESS_BEFORE_EXIT,
7254
+ exit: STOP_REASON_PROCESS_EXIT,
7117
7255
  };
7118
7256
 
7119
7257
  const isFileSystemPath = (value) => {
@@ -8701,7 +8839,10 @@ const rollupPluginJsenv = ({
8701
8839
  // rollup generate specifiers only for static and dynamic imports
8702
8840
  // other references (like new URL()) are ignored
8703
8841
  // there is no need to remap them back
8704
- if (reference.type === "js_import") {
8842
+ if (
8843
+ reference.type === "js_import" &&
8844
+ reference.subtype !== "import_meta_resolve"
8845
+ ) {
8705
8846
  return specifierToUrlMap.get(reference.specifier);
8706
8847
  }
8707
8848
  return reference.specifier;
@@ -10768,34 +10909,32 @@ const jsenvPluginImportMetaResolve = ({ needJsModuleFallback }) => {
10768
10909
  },
10769
10910
  transformUrlContent: {
10770
10911
  js_module: async (urlInfo) => {
10912
+ const jsUrls = parseJsUrls({
10913
+ js: urlInfo.content,
10914
+ url: urlInfo.url,
10915
+ isJsModule: true,
10916
+ });
10771
10917
  const magicSource = createMagicSource(urlInfo.content);
10772
- for (const referenceToOther of urlInfo.referenceToOthersSet) {
10773
- if (referenceToOther.subtype === "import_meta_resolve") {
10774
- const originalSpecifierLength = Buffer.byteLength(
10775
- referenceToOther.specifier,
10776
- );
10777
- const specifierLength = Buffer.byteLength(
10778
- referenceToOther.generatedSpecifier.slice(1, -1), // remove `"` around
10779
- );
10780
- const specifierLengthDiff =
10781
- specifierLength - originalSpecifierLength;
10782
- const { node } = referenceToOther.astInfo;
10783
- const end = node.end + specifierLengthDiff;
10784
- magicSource.replace({
10785
- start: node.start,
10786
- end,
10787
- replacement: `new URL(${referenceToOther.generatedSpecifier}, import.meta.url).href`,
10788
- });
10789
- const currentLengthBeforeSpecifier = "import.meta.resolve(".length;
10790
- const newLengthBeforeSpecifier = "new URL(".length;
10791
- const lengthDiff =
10792
- currentLengthBeforeSpecifier - newLengthBeforeSpecifier;
10793
- referenceToOther.specifierColumn -= lengthDiff;
10794
- referenceToOther.specifierStart -= lengthDiff;
10795
- referenceToOther.specifierEnd =
10796
- referenceToOther.specifierStart +
10797
- Buffer.byteLength(referenceToOther.generatedSpecifier);
10918
+ for (const jsUrl of jsUrls) {
10919
+ if (jsUrl.subtype !== "import_meta_resolve") {
10920
+ continue;
10798
10921
  }
10922
+ const { node } = jsUrl.astInfo;
10923
+ let reference;
10924
+ for (const referenceToOther of urlInfo.referenceToOthersSet) {
10925
+ if (
10926
+ referenceToOther.generatedSpecifier.slice(1, -1) ===
10927
+ jsUrl.specifier
10928
+ ) {
10929
+ reference = referenceToOther;
10930
+ break;
10931
+ }
10932
+ }
10933
+ magicSource.replace({
10934
+ start: node.start,
10935
+ end: node.end,
10936
+ replacement: `new URL(${reference.generatedSpecifier}, import.meta.url).href`,
10937
+ });
10799
10938
  }
10800
10939
  return magicSource.toContentAndSourcemap();
10801
10940
  },
@@ -12096,6 +12235,13 @@ const applyDependencyRemovalEffects = (reference) => {
12096
12235
 
12097
12236
  const traceFromUrlSite = (urlSite) => {
12098
12237
  return {
12238
+ codeFrame: urlSite.content
12239
+ ? inspectFileContent({
12240
+ content: urlSite.content,
12241
+ line: urlSite.line,
12242
+ column: urlSite.column,
12243
+ })
12244
+ : "",
12099
12245
  message: stringifyUrlSite(urlSite),
12100
12246
  url: urlSite.url,
12101
12247
  line: urlSite.line,
@@ -13680,15 +13826,9 @@ const createFetchUrlContentError = ({
13680
13826
  fetchError.reason = reason;
13681
13827
  fetchError.url = urlInfo.url;
13682
13828
  if (code === "PARSE_ERROR") {
13683
- fetchError.traceUrl = error.traceUrl;
13684
- fetchError.traceLine = error.traceLine;
13685
- fetchError.traceColumn = error.traceColumn;
13686
- fetchError.traceMessage = error.traceMessage;
13829
+ fetchError.trace = error.trace;
13687
13830
  } else {
13688
- fetchError.traceUrl = urlInfo.firstReference.trace.url;
13689
- fetchError.traceLine = urlInfo.firstReference.trace.line;
13690
- fetchError.traceColumn = urlInfo.firstReference.trace.column;
13691
- fetchError.traceMessage = urlInfo.firstReference.trace.message;
13831
+ fetchError.trace = urlInfo.firstReference.trace;
13692
13832
  }
13693
13833
  fetchError.asResponse = error.asResponse;
13694
13834
  return fetchError;
@@ -13723,7 +13863,7 @@ const createFetchUrlContentError = ({
13723
13863
  "code": "PARSE_ERROR",
13724
13864
  "reason": error.reasonCode,
13725
13865
  ...(error.cause ? { "parse error message": error.cause.message } : {}),
13726
- "parse error trace": error.traceMessage,
13866
+ "parse error trace": error.trace?.message,
13727
13867
  });
13728
13868
  }
13729
13869
  return createFailedToFetchUrlContentError({
@@ -13762,33 +13902,43 @@ const createTransformUrlContentError = ({
13762
13902
  transformError.reason = reason;
13763
13903
  transformError.stack = error.stack;
13764
13904
  transformError.url = urlInfo.url;
13765
- transformError.traceUrl = urlInfo.firstReference.trace.url;
13766
- transformError.traceLine = urlInfo.firstReference.trace.line;
13767
- transformError.traceColumn = urlInfo.firstReference.trace.column;
13768
- transformError.traceMessage = urlInfo.firstReference.trace.message;
13905
+ transformError.trace = urlInfo.firstReference.trace;
13769
13906
  if (code === "PARSE_ERROR") {
13770
13907
  transformError.reason = `parse error on ${urlInfo.type}`;
13771
13908
  transformError.cause = error;
13772
13909
  if (urlInfo.isInline) {
13773
- transformError.traceLine =
13910
+ transformError.trace.line =
13774
13911
  urlInfo.firstReference.trace.line + error.line - 1;
13775
- transformError.traceColumn =
13912
+ transformError.trace.column =
13776
13913
  urlInfo.firstReference.trace.column + error.column;
13777
- transformError.traceMessage = stringifyUrlSite({
13914
+ transformError.trace.codeFrame = inspectFileContent({
13915
+ line: transformError.trace.line,
13916
+ column: transformError.trace.column,
13917
+ content: urlInfo.inlineUrlSite.content,
13918
+ });
13919
+ transformError.trace.message = stringifyUrlSite({
13778
13920
  url: urlInfo.inlineUrlSite.url,
13779
- line: transformError.traceLine,
13780
- column: transformError.traceColumn,
13921
+ line: transformError.trace.line,
13922
+ column: transformError.trace.column,
13781
13923
  content: urlInfo.inlineUrlSite.content,
13782
13924
  });
13783
13925
  } else {
13784
- transformError.traceLine = error.line;
13785
- transformError.traceColumn = error.column;
13786
- transformError.traceMessage = stringifyUrlSite({
13926
+ transformError.trace = {
13787
13927
  url: urlInfo.url,
13788
- line: error.line - 1,
13928
+ line: error.line,
13789
13929
  column: error.column,
13790
- content: urlInfo.content,
13791
- });
13930
+ codeFrame: inspectFileContent({
13931
+ line: error.line - 1,
13932
+ column: error.column,
13933
+ content: urlInfo.content,
13934
+ }),
13935
+ message: stringifyUrlSite({
13936
+ url: urlInfo.url,
13937
+ line: error.line - 1,
13938
+ column: error.column,
13939
+ content: urlInfo.content,
13940
+ }),
13941
+ };
13792
13942
  }
13793
13943
  }
13794
13944
  transformError.asResponse = error.asResponse;
@@ -14453,7 +14603,7 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
14453
14603
  ) {
14454
14604
  const errorInfo =
14455
14605
  e.code === "PARSE_ERROR" && e.cause
14456
- ? `${e.cause.reasonCode}\n${e.traceMessage}`
14606
+ ? `${e.cause.reasonCode}\n${e.trace?.message}`
14457
14607
  : e.stack;
14458
14608
  // When something like <style> or <script> contains syntax error
14459
14609
  // the HTML in itself it still valid
@@ -14859,7 +15009,7 @@ const determineCategory = (urlInfo) => {
14859
15009
  const createRepartitionMessage = ({ html, css, js, json, other, total }) => {
14860
15010
  const addPart = (name, { count, size, percentage }) => {
14861
15011
  parts.push(
14862
- `${ANSI.color(`${name}:`, ANSI.GREY)} ${count} (${byteAsFileSize(
15012
+ `${ANSI.color(`${name}:`, ANSI.GREY)} ${count} (${inspectFileSize(
14863
15013
  size,
14864
15014
  )} / ${percentage} %)`,
14865
15015
  );
@@ -14870,7 +15020,7 @@ const createRepartitionMessage = ({ html, css, js, json, other, total }) => {
14870
15020
  // parts.push(
14871
15021
  // `${ANSI.color(`sourcemaps:`, ANSI.GREY)} ${
14872
15022
  // sourcemaps.count
14873
- // } (${byteAsFileSize(sourcemaps.size)})`,
15023
+ // } (${inspectFileSize(sourcemaps.size)})`,
14874
15024
  // )
14875
15025
  // }
14876
15026
  if (html.count) {
@@ -16462,18 +16612,20 @@ const jsenvPluginJsReferenceAnalysis = ({ inlineContent }) => {
16462
16612
  name: "jsenv:js_reference_analysis",
16463
16613
  appliesDuring: "*",
16464
16614
  transformUrlContent: {
16465
- js_classic: (urlInfo) =>
16466
- parseAndTransformJsReferences(urlInfo, {
16615
+ js_classic: (urlInfo) => {
16616
+ return parseAndTransformJsReferences(urlInfo, {
16467
16617
  inlineContent,
16468
16618
  canUseTemplateLiterals:
16469
16619
  urlInfo.context.isSupportedOnCurrentClients("template_literals"),
16470
- }),
16471
- js_module: (urlInfo) =>
16472
- parseAndTransformJsReferences(urlInfo, {
16620
+ });
16621
+ },
16622
+ js_module: (urlInfo) => {
16623
+ return parseAndTransformJsReferences(urlInfo, {
16473
16624
  inlineContent,
16474
16625
  canUseTemplateLiterals:
16475
16626
  urlInfo.context.isSupportedOnCurrentClients("template_literals"),
16476
- }),
16627
+ });
16628
+ },
16477
16629
  },
16478
16630
  },
16479
16631
  ];
@@ -16602,13 +16754,9 @@ const parseAndTransformJsReferences = async (
16602
16754
  if (parallelActions.length > 0) {
16603
16755
  await Promise.all(parallelActions.map((action) => action()));
16604
16756
  }
16605
- if (sequentialActions.length > 0) {
16606
- await sequentialActions.reduce(async (previous, action) => {
16607
- await previous;
16608
- await action();
16609
- }, Promise.resolve());
16757
+ for (const sequentialAction of sequentialActions) {
16758
+ await sequentialAction();
16610
16759
  }
16611
-
16612
16760
  const { content, sourcemap } = magicSource.toContentAndSourcemap();
16613
16761
  return { content, sourcemap };
16614
16762
  };
@@ -18051,7 +18199,7 @@ const jsenvPluginProtocolFile = ({
18051
18199
  magicExtensions = ["inherit", ".js"],
18052
18200
  magicDirectoryIndex = true,
18053
18201
  preserveSymlinks = false,
18054
- directoryReferenceAllowed = false,
18202
+ directoryReferenceEffect = "error",
18055
18203
  }) => {
18056
18204
  return [
18057
18205
  {
@@ -18132,17 +18280,27 @@ const jsenvPluginProtocolFile = ({
18132
18280
  }
18133
18281
  reference.leadsToADirectory = stat && stat.isDirectory();
18134
18282
  if (reference.leadsToADirectory) {
18135
- const directoryAllowed =
18283
+ let actionForDirectory;
18284
+ if (
18136
18285
  reference.type === "http_request" ||
18137
- reference.type === "filesystem" ||
18138
- (typeof directoryReferenceAllowed === "function" &&
18139
- directoryReferenceAllowed(reference)) ||
18140
- directoryReferenceAllowed;
18141
- if (!directoryAllowed) {
18286
+ reference.type === "filesystem"
18287
+ ) {
18288
+ actionForDirectory = "copy";
18289
+ } else if (typeof directoryReferenceEffect === "string") {
18290
+ actionForDirectory = directoryReferenceEffect;
18291
+ } else if (typeof directoryReferenceEffect === "function") {
18292
+ actionForDirectory = directoryReferenceEffect(reference);
18293
+ } else {
18294
+ actionForDirectory = "error";
18295
+ }
18296
+ if (actionForDirectory === "error") {
18142
18297
  const error = new Error("Reference leads to a directory");
18143
18298
  error.code = "DIRECTORY_REFERENCE_NOT_ALLOWED";
18144
18299
  throw error;
18145
18300
  }
18301
+ if (actionForDirectory === "preserve") {
18302
+ return `ignore:${url}${search}${hash}`;
18303
+ }
18146
18304
  }
18147
18305
  const urlRaw = preserveSymlinks ? url : resolveSymlink(url);
18148
18306
  const resolvedUrl = `${urlRaw}${search}${hash}`;
@@ -19898,7 +20056,7 @@ const getCorePlugins = ({
19898
20056
  nodeEsmResolution = {},
19899
20057
  magicExtensions,
19900
20058
  magicDirectoryIndex,
19901
- directoryReferenceAllowed,
20059
+ directoryReferenceEffect,
19902
20060
  supervisor,
19903
20061
  injections,
19904
20062
  transpilation = true,
@@ -19933,7 +20091,7 @@ const getCorePlugins = ({
19933
20091
  - All the rest uses web standard url resolution
19934
20092
  */
19935
20093
  jsenvPluginProtocolFile({
19936
- directoryReferenceAllowed,
20094
+ directoryReferenceEffect,
19937
20095
  magicExtensions,
19938
20096
  magicDirectoryIndex,
19939
20097
  }),
@@ -21533,7 +21691,7 @@ const build = async ({
21533
21691
  nodeEsmResolution,
21534
21692
  magicExtensions,
21535
21693
  magicDirectoryIndex,
21536
- directoryReferenceAllowed,
21694
+ directoryReferenceEffect,
21537
21695
  scenarioPlaceholders,
21538
21696
  injections,
21539
21697
  transpilation = {},
@@ -21623,6 +21781,17 @@ const build = async ({
21623
21781
  }
21624
21782
  }
21625
21783
 
21784
+ if (assetsDirectory && assetsDirectory[assetsDirectory.length - 1] !== "/") {
21785
+ assetsDirectory = `${assetsDirectory}/`;
21786
+ }
21787
+ if (directoryToClean === undefined) {
21788
+ if (assetsDirectory === undefined) {
21789
+ directoryToClean = buildDirectoryUrl;
21790
+ } else {
21791
+ directoryToClean = new URL(assetsDirectory, buildDirectoryUrl).href;
21792
+ }
21793
+ }
21794
+
21626
21795
  const operation = Abort.startOperation();
21627
21796
  operation.addAbortSignal(signal);
21628
21797
  if (handleSIGINT) {
@@ -21636,17 +21805,6 @@ const build = async ({
21636
21805
  });
21637
21806
  }
21638
21807
 
21639
- if (assetsDirectory && assetsDirectory[assetsDirectory.length - 1] !== "/") {
21640
- assetsDirectory = `${assetsDirectory}/`;
21641
- }
21642
- if (directoryToClean === undefined) {
21643
- if (assetsDirectory === undefined) {
21644
- directoryToClean = buildDirectoryUrl;
21645
- } else {
21646
- directoryToClean = new URL(assetsDirectory, buildDirectoryUrl).href;
21647
- }
21648
- }
21649
-
21650
21808
  const runBuild = async ({ signal, logLevel }) => {
21651
21809
  const logger = createLogger({ logLevel });
21652
21810
  const createBuildTask = (label) => {
@@ -21719,7 +21877,7 @@ build ${entryPointKeys.length} entry points`);
21719
21877
  nodeEsmResolution,
21720
21878
  magicExtensions,
21721
21879
  magicDirectoryIndex,
21722
- directoryReferenceAllowed,
21880
+ directoryReferenceEffect,
21723
21881
  injections,
21724
21882
  transpilation: {
21725
21883
  babelHelpersAsImport: !explicitJsModuleConversion,
@@ -22087,7 +22245,15 @@ build ${entryPointKeys.length} entry points`);
22087
22245
  };
22088
22246
 
22089
22247
  if (!watch) {
22090
- return runBuild({ signal: operation.signal, logLevel });
22248
+ try {
22249
+ const result = await runBuild({
22250
+ signal: operation.signal,
22251
+ logLevel,
22252
+ });
22253
+ return result;
22254
+ } finally {
22255
+ await operation.end();
22256
+ }
22091
22257
  }
22092
22258
 
22093
22259
  let resolveFirstBuild;
@@ -22326,25 +22492,13 @@ const startDevServer = async ({
22326
22492
  }
22327
22493
 
22328
22494
  const logger = createLogger({ logLevel });
22329
- const operation = Abort.startOperation();
22330
- operation.addAbortSignal(signal);
22331
- if (handleSIGINT) {
22332
- operation.addAbortSource((abort) => {
22333
- return raceProcessTeardownEvents(
22334
- {
22335
- SIGINT: true,
22336
- },
22337
- abort,
22338
- );
22339
- });
22340
- }
22341
22495
  const startDevServerTask = createTaskLog("start dev server", {
22342
22496
  disabled: !logger.levels.info,
22343
22497
  });
22344
22498
 
22345
- const serverStopCallbacks = [];
22499
+ const serverStopCallbackSet = new Set();
22346
22500
  const serverEventsDispatcher = createServerEventsDispatcher();
22347
- serverStopCallbacks.push(() => {
22501
+ serverStopCallbackSet.add(() => {
22348
22502
  serverEventsDispatcher.destroy();
22349
22503
  });
22350
22504
  const kitchenCache = new Map();
@@ -22419,7 +22573,7 @@ const startDevServer = async ({
22419
22573
  cooldownBetweenFileEvents: clientAutoreload.cooldownBetweenFileEvents,
22420
22574
  },
22421
22575
  );
22422
- serverStopCallbacks.push(stopWatchingSourceFiles);
22576
+ serverStopCallbackSet.add(stopWatchingSourceFiles);
22423
22577
 
22424
22578
  const getOrCreateKitchen = (request) => {
22425
22579
  const { runtimeName, runtimeVersion } = parseUserAgentHeader(
@@ -22544,7 +22698,7 @@ const startDevServer = async ({
22544
22698
  },
22545
22699
  );
22546
22700
 
22547
- serverStopCallbacks.push(() => {
22701
+ serverStopCallbackSet.add(() => {
22548
22702
  kitchen.pluginController.callHooks("destroy", kitchen.context);
22549
22703
  });
22550
22704
  {
@@ -22708,7 +22862,7 @@ const startDevServer = async ({
22708
22862
  if (urlInfo.content !== undefined) {
22709
22863
  kitchen.context.logger.error(`Error while handling ${request.url}:
22710
22864
  ${originalError.reasonCode || originalError.code}
22711
- ${e.traceMessage}`);
22865
+ ${e.trace?.message}`);
22712
22866
  return {
22713
22867
  url: reference.url,
22714
22868
  status: 200,
@@ -22841,10 +22995,10 @@ ${e.traceMessage}`);
22841
22995
  });
22842
22996
  server.stoppedPromise.then((reason) => {
22843
22997
  onStop();
22844
- serverStopCallbacks.forEach((serverStopCallback) => {
22998
+ for (const serverStopCallback of serverStopCallbackSet) {
22845
22999
  serverStopCallback(reason);
22846
- });
22847
- serverStopCallbacks.length = 0;
23000
+ }
23001
+ serverStopCallbackSet.clear();
22848
23002
  });
22849
23003
  startDevServerTask.done();
22850
23004
  if (hostname) {