@jsenv/core 27.0.0-alpha.81 → 27.0.0-alpha.84

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 (81) hide show
  1. package/dist/babel_helpers/applyDecs/applyDecs.js +756 -0
  2. package/dist/babel_helpers/construct/construct.js +1 -1
  3. package/dist/babel_helpers/extends/extends.js +1 -1
  4. package/dist/babel_helpers/get/get.js +1 -1
  5. package/dist/babel_helpers/getPrototypeOf/getPrototypeOf.js +1 -1
  6. package/dist/babel_helpers/identity/identity.js +3 -0
  7. package/dist/babel_helpers/setPrototypeOf/setPrototypeOf.js +2 -2
  8. package/dist/js/event_source_client.js +206 -2
  9. package/dist/main.js +934 -90
  10. package/dist/s.js.map +2 -1
  11. package/package.json +9 -9
  12. package/src/build/build.js +5 -5
  13. package/src/build/build_urls_generator.js +1 -2
  14. package/src/build/inject_global_version_mappings.js +2 -2
  15. package/src/build/inject_service_worker_urls.js +2 -2
  16. package/src/build/resync_ressource_hints.js +1 -1
  17. package/src/build/start_build_server.js +33 -26
  18. package/src/dev/plugins/explorer/jsenv_plugin_explorer.js +1 -2
  19. package/src/dev/plugins/toolbar/client/util/fetching.js +1 -1
  20. package/src/dev/plugins/toolbar/jsenv_plugin_toolbar.js +1 -1
  21. package/src/dev/start_dev_server.js +38 -30
  22. package/src/execute/runtimes/browsers/from_playwright.js +5 -4
  23. package/src/execute/runtimes/node/node_process.js +2 -2
  24. package/src/helpers/command/command.js +73 -0
  25. package/src/helpers/event_source/event_source.js +197 -0
  26. package/src/helpers/event_source/sse_service.js +53 -0
  27. package/src/helpers/worker_reload.js +57 -0
  28. package/src/omega/compat/runtime_compat.js +2 -1
  29. package/src/omega/kitchen.js +2 -1
  30. package/src/omega/server/user_agent.js +2 -1
  31. package/src/omega/url_graph/sort_by_dependencies.js +27 -0
  32. package/src/omega/url_graph/url_info_transformations.js +24 -14
  33. package/src/plugins/autoreload/dev_sse/client/event_source_client.js +1 -1
  34. package/src/plugins/autoreload/dev_sse/client/reload.js +1 -1
  35. package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_client.js +1 -1
  36. package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_server.js +1 -1
  37. package/src/plugins/bundling/css/bundle_css.js +5 -4
  38. package/src/plugins/bundling/js_module/bundle_js_module.js +2 -2
  39. package/src/plugins/commonjs_globals/jsenv_plugin_commonjs_globals.js +2 -2
  40. package/src/plugins/file_urls/jsenv_plugin_file_urls.js +1 -2
  41. package/src/plugins/html_supervisor/jsenv_plugin_html_supervisor.js +1 -1
  42. package/src/plugins/import_meta_hot/html_hot_dependencies.js +2 -2
  43. package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +4 -3
  44. package/src/plugins/import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js +2 -2
  45. package/src/plugins/importmap/jsenv_plugin_importmap.js +2 -3
  46. package/src/plugins/inject_globals/inject_globals.js +2 -2
  47. package/src/plugins/inline/jsenv_plugin_data_urls.js +1 -1
  48. package/src/plugins/inline/jsenv_plugin_html_inline_content.js +3 -3
  49. package/src/plugins/inline/jsenv_plugin_js_inline_content.js +4 -4
  50. package/src/plugins/minification/css/minify_css.js +1 -1
  51. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +2 -4
  52. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_html.js +5 -5
  53. package/src/plugins/transpilation/babel/global_this/babel_plugin_global_this_as_jsenv_import.js +1 -1
  54. package/src/plugins/transpilation/babel/helpers/babel_plugin_babel_helpers_as_jsenv_imports.js +2 -3
  55. package/src/plugins/transpilation/babel/jsenv_plugin_babel.js +1 -1
  56. package/src/plugins/transpilation/babel/new_stylesheet/babel_plugin_new_stylesheet_as_jsenv_import.js +1 -2
  57. package/src/plugins/transpilation/babel/regenerator_runtime/babel_plugin_regenerator_runtime_as_jsenv_import.js +1 -2
  58. package/src/plugins/transpilation/css_parcel/jsenv_plugin_css_parcel.js +1 -1
  59. package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +1 -1
  60. package/src/plugins/transpilation/jsenv_plugin_top_level_await.js +2 -1
  61. package/src/plugins/url_analysis/css/css_urls.js +3 -3
  62. package/src/plugins/url_analysis/html/html_urls.js +2 -2
  63. package/src/plugins/url_analysis/js/js_urls.js +3 -2
  64. package/src/test/coverage/babel_plugin_instrument.js +82 -0
  65. package/src/test/coverage/coverage_reporter_html_directory.js +36 -0
  66. package/src/test/coverage/coverage_reporter_json_file.js +22 -0
  67. package/src/test/coverage/coverage_reporter_text_log.js +19 -0
  68. package/src/test/coverage/empty_coverage_factory.js +52 -0
  69. package/src/test/coverage/file_by_file_coverage.js +25 -0
  70. package/src/test/coverage/istanbul_coverage_composition.js +28 -0
  71. package/src/test/coverage/istanbul_coverage_map_from_coverage.js +16 -0
  72. package/src/test/coverage/list_files_not_covered.js +15 -0
  73. package/src/test/coverage/missing_coverage.js +41 -0
  74. package/src/test/coverage/report_to_coverage.js +196 -0
  75. package/src/test/coverage/v8_and_istanbul.js +37 -0
  76. package/src/test/coverage/v8_coverage_composition.js +24 -0
  77. package/src/test/coverage/v8_coverage_from_directory.js +87 -0
  78. package/src/test/coverage/v8_coverage_to_istanbul.js +99 -0
  79. package/src/test/execute_plan.js +2 -2
  80. package/src/test/execute_test_plan.js +3 -3
  81. package/dist/babel_helpers/readme.md +0 -8
package/dist/main.js CHANGED
@@ -1,56 +1,97 @@
1
- import { parentPort } from "node:worker_threads";
2
- import { registerFileLifecycle, readFileSync as readFileSync$1, bufferToEtag, writeFileSync, ensureWindowsDriveLetter, collectFiles, assertAndNormalizeDirectoryUrl, registerDirectoryLifecycle, writeFile, ensureEmptyDirectory, writeDirectory } from "@jsenv/filesystem";
3
- import { createDetailedMessage, createLogger, createTaskLog, loggerToLevels, ANSI, msAsDuration, msAsEllapsedTime, byteAsMemoryUsage, UNICODE, createLog, startSpinner, distributePercentages, byteAsFileSize } from "@jsenv/log";
1
+ import { createSSERoom, timeStart, fetchFileSystem, composeTwoResponses, serveDirectory, startServer, pluginCORS, jsenvAccessControlAllowedHeaders, pluginServerTiming, pluginRequestWaitingCheck, composeServices, findFreePort } from "@jsenv/server";
2
+ import { registerFileLifecycle, readFileSync as readFileSync$1, bufferToEtag, writeFileSync, ensureWindowsDriveLetter, collectFiles, assertAndNormalizeDirectoryUrl, registerDirectoryLifecycle, writeFile, readFile, readDirectory, ensureEmptyDirectory, writeDirectory } from "@jsenv/filesystem";
3
+ import { createCallbackListNotifiedOnce, createCallbackList, Abort, raceProcessTeardownEvents, raceCallbacks } from "@jsenv/abort";
4
+ import { createDetailedMessage, createLogger, createTaskLog, loggerToLevels, byteAsFileSize, ANSI, msAsDuration, msAsEllapsedTime, byteAsMemoryUsage, UNICODE, createLog, startSpinner, distributePercentages } from "@jsenv/log";
4
5
  import { urlToRelativeUrl, generateInlineContentUrl, ensurePathnameTrailingSlash, urlIsInsideOf, urlToFilename, DATA_URL, injectQueryParams, injectQueryParamsIntoSpecifier, fileSystemPathToUrl, urlToFileSystemPath, isFileSystemPath, normalizeUrl, stringifyUrlSite, setUrlFilename, moveUrl, getCallerPosition, resolveUrl, resolveDirectoryUrl, asUrlWithoutSearch, asUrlUntilPathname, urlToBasename, urlToExtension } from "@jsenv/urls";
5
- import { initReloadableProcess } from "@jsenv/utils/process_reload/process_reload.js";
6
+ import { fileURLToPath, pathToFileURL } from "node:url";
7
+ import { workerData, Worker } from "node:worker_threads";
6
8
  import { URL_META } from "@jsenv/url-meta";
7
- import { parseHtmlString, stringifyHtmlAst, visitHtmlAst, getHtmlNodeAttributeByName, htmlNodePosition, findNode, getHtmlNodeTextNode, removeHtmlNode, setHtmlNodeGeneratedText, removeHtmlNodeAttributeByName, parseScriptNode, injectScriptAsEarlyAsPossible, createHtmlNode, removeHtmlNodeText, assignHtmlNodeAttributes, parseLinkNode } from "@jsenv/utils/html_ast/html_ast.js";
8
- import { htmlAttributeSrcSet } from "@jsenv/utils/html_ast/html_attribute_src_set.js";
9
- import { createMagicSource } from "@jsenv/utils/sourcemap/magic_source.js";
10
- import { applyPostCss } from "@jsenv/utils/css_ast/apply_post_css.js";
11
- import { postCssPluginUrlVisitor } from "@jsenv/utils/css_ast/postcss_plugin_url_visitor.js";
12
- import { parseJsUrls } from "@jsenv/utils/js_ast/parse_js_urls.js";
9
+ import { parseHtmlString, stringifyHtmlAst, visitHtmlAst, getHtmlNodeAttributeByName, htmlNodePosition, findNode, getHtmlNodeTextNode, removeHtmlNode, setHtmlNodeGeneratedText, removeHtmlNodeAttributeByName, parseScriptNode, injectScriptAsEarlyAsPossible, createHtmlNode, removeHtmlNodeText, assignHtmlNodeAttributes, parseLinkNode } from "@jsenv/utils/src/html_ast/html_ast.js";
10
+ import { htmlAttributeSrcSet } from "@jsenv/utils/src/html_ast/html_attribute_src_set.js";
11
+ import { createMagicSource, composeTwoSourcemaps, sourcemapConverter, SOURCEMAP, generateSourcemapFileUrl, generateSourcemapDataUrl } from "@jsenv/sourcemap";
12
+ import { applyPostCss } from "@jsenv/utils/src/css_ast/apply_post_css.js";
13
+ import { postCssPluginUrlVisitor } from "@jsenv/utils/src/css_ast/postcss_plugin_url_visitor.js";
14
+ import { parseJsUrls } from "@jsenv/utils/src/js_ast/parse_js_urls.js";
13
15
  import { resolveImport, normalizeImportMap, composeTwoImportMaps } from "@jsenv/importmap";
14
16
  import { applyNodeEsmResolution, defaultLookupPackageScope, defaultReadPackageJson, readCustomConditionsFromProcessArgs, applyFileSystemMagicResolution, getExtensionsToTry } from "@jsenv/node-esm-resolution";
15
17
  import { statSync, realpathSync, readdirSync, readFileSync, existsSync } from "node:fs";
16
- import { pathToFileURL } from "node:url";
17
- import { CONTENT_TYPE } from "@jsenv/utils/content_type/content_type.js";
18
- import { JS_QUOTES } from "@jsenv/utils/string/js_quotes.js";
19
- import { applyBabelPlugins } from "@jsenv/utils/js_ast/apply_babel_plugins.js";
20
- import { transpileWithParcel, minifyWithParcel } from "@jsenv/utils/css_ast/parcel_css.js";
18
+ import { CONTENT_TYPE } from "@jsenv/utils/src/content_type/content_type.js";
19
+ import { JS_QUOTES } from "@jsenv/utils/src/string/js_quotes.js";
20
+ import { applyBabelPlugins } from "@jsenv/utils/src/js_ast/apply_babel_plugins.js";
21
+ import { transpileWithParcel, minifyWithParcel } from "@jsenv/utils/src/css_ast/parcel_css.js";
21
22
  import { createRequire } from "node:module";
22
- import { composeTwoSourcemaps } from "@jsenv/utils/sourcemap/sourcemap_composition_v3.js";
23
23
  import babelParser from "@babel/parser";
24
- import { findHighestVersion } from "@jsenv/utils/semantic_versioning/highest_version.js";
25
- import { injectImport } from "@jsenv/utils/js_ast/babel_utils.js";
26
- import { sortByDependencies } from "@jsenv/utils/graph/sort_by_dependencies.js";
27
- import { applyRollupPlugins } from "@jsenv/utils/js_ast/apply_rollup_plugins.js";
28
- import { sourcemapConverter } from "@jsenv/utils/sourcemap/sourcemap_converter.js";
29
- import { createCallbackList, createCallbackListNotifiedOnce, Abort, raceCallbacks, raceProcessTeardownEvents } from "@jsenv/abort";
30
- import { createSSEService } from "@jsenv/utils/event_source/sse_service.js";
31
- import { timeStart, fetchFileSystem, composeTwoResponses, serveDirectory, startServer, pluginCORS, jsenvAccessControlAllowedHeaders, pluginServerTiming, pluginRequestWaitingCheck, composeServices, findFreePort } from "@jsenv/server";
32
- import { SOURCEMAP, generateSourcemapUrl, sourcemapToBase64Url } from "@jsenv/utils/sourcemap/sourcemap_utils.js";
24
+ import { findHighestVersion } from "@jsenv/utils/src/semantic_versioning/highest_version.js";
25
+ import { injectImport } from "@jsenv/utils/src/js_ast/babel_utils.js";
26
+ import { applyRollupPlugins } from "@jsenv/utils/src/js_ast/apply_rollup_plugins.js";
33
27
  import { validateResponseIntegrity } from "@jsenv/integrity";
34
28
  import { convertFileSystemErrorToResponseProperties } from "@jsenv/server/src/internal/convertFileSystemErrorToResponseProperties.js";
35
- import { memoizeByFirstArgument } from "@jsenv/utils/memoize/memoize_by_first_argument.js";
36
- import { generateCoverageJsonFile } from "@jsenv/utils/coverage/coverage_reporter_json_file.js";
37
- import { generateCoverageHtmlDirectory } from "@jsenv/utils/coverage/coverage_reporter_html_directory.js";
38
- import { generateCoverageTextLog } from "@jsenv/utils/coverage/coverage_reporter_text_log.js";
29
+ import { memoizeByFirstArgument } from "@jsenv/utils/src/memoize/memoize_by_first_argument.js";
39
30
  import { memoryUsage } from "node:process";
40
31
  import wrapAnsi from "wrap-ansi";
41
32
  import stripAnsi from "strip-ansi";
42
33
  import cuid from "cuid";
43
- import { babelPluginInstrument } from "@jsenv/utils/coverage/babel_plugin_instrument.js";
44
- import { reportToCoverage } from "@jsenv/utils/coverage/report_to_coverage.js";
45
34
  import v8 from "node:v8";
46
35
  import { runInNewContext, Script } from "node:vm";
47
- import { memoize } from "@jsenv/utils/memoize/memoize.js";
48
- import { filterV8Coverage } from "@jsenv/utils/coverage/v8_coverage_from_directory.js";
49
- import { composeTwoFileByFileIstanbulCoverages } from "@jsenv/utils/coverage/istanbul_coverage_composition.js";
50
- import { escapeRegexpSpecialChars } from "@jsenv/utils/string/escape_regexp_special_chars.js";
36
+ import { memoize } from "@jsenv/utils/src/memoize/memoize.js";
37
+ import { escapeRegexpSpecialChars } from "@jsenv/utils/src/string/escape_regexp_special_chars.js";
51
38
  import { fork } from "node:child_process";
52
39
  import { uneval } from "@jsenv/uneval";
53
- import { createVersionGenerator } from "@jsenv/utils/versioning/version_generator.js";
40
+ import { createVersionGenerator } from "@jsenv/utils/src/versioning/version_generator.js";
41
+
42
+ const createReloadableWorker = (workerFileUrl, options = {}) => {
43
+ const workerFilePath = fileURLToPath(workerFileUrl);
44
+ const isPrimary = !workerData || workerData.workerFilePath !== workerFilePath;
45
+ let worker;
46
+
47
+ const terminate = async () => {
48
+ if (worker) {
49
+ let _worker = worker;
50
+ worker = null;
51
+ const exitPromise = new Promise(resolve => {
52
+ _worker.once("exit", resolve);
53
+ });
54
+
55
+ _worker.terminate();
56
+
57
+ await exitPromise;
58
+ }
59
+ };
60
+
61
+ const load = async () => {
62
+ if (!isPrimary) {
63
+ throw new Error(`worker can be loaded from primary file only`);
64
+ }
65
+
66
+ worker = new Worker(workerFilePath, { ...options,
67
+ workerData: { ...options.workerData,
68
+ workerFilePath
69
+ }
70
+ });
71
+ worker.once("error", error => {
72
+ console.error(error);
73
+ });
74
+ await new Promise(resolve => {
75
+ worker.once("online", resolve);
76
+ });
77
+ worker.once("exit", () => {
78
+ worker = null;
79
+ });
80
+ return worker;
81
+ };
82
+
83
+ const reload = async () => {
84
+ await terminate();
85
+ await load();
86
+ };
87
+
88
+ return {
89
+ isPrimary,
90
+ load,
91
+ reload,
92
+ terminate
93
+ };
94
+ };
54
95
 
55
96
  const parseAndTransformHtmlUrls = async (urlInfo, context) => {
56
97
  const url = urlInfo.originalUrl;
@@ -3386,9 +3427,13 @@ const getFeatureCompat = feature => {
3386
3427
  return feature;
3387
3428
  };
3388
3429
 
3389
- // https://github.com/babel/babel/blob/99f4f6c3b03c7f3f67cf1b9f1a21b80cfd5b0224/packages/babel-core/src/tools/build-external-helpers.js
3390
- // the list of possible helpers:
3391
- // https://github.com/babel/babel/blob/99f4f6c3b03c7f3f67cf1b9f1a21b80cfd5b0224/packages/babel-helpers/src/helpers.js#L13
3430
+ /*
3431
+ * Generated helpers
3432
+ * - https://github.com/babel/babel/commits/main/packages/babel-helpers/src/helpers.ts
3433
+ * File helpers
3434
+ * - https://github.com/babel/babel/tree/main/packages/babel-helpers/src/helpers
3435
+ *
3436
+ */
3392
3437
  const babelHelperClientDirectoryUrl = new URL("./babel_helpers/", import.meta.url).href; // we cannot use "@jsenv/core/src/*" because babel helper might be injected
3393
3438
  // into node_modules not depending on "@jsenv/core"
3394
3439
 
@@ -4455,6 +4500,39 @@ const jsenvPluginNodeRuntime = ({
4455
4500
  };
4456
4501
  };
4457
4502
 
4503
+ const sortByDependencies = nodes => {
4504
+ const visited = [];
4505
+ const sorted = [];
4506
+ const circular = [];
4507
+
4508
+ const visit = url => {
4509
+ const isSorted = sorted.includes(url);
4510
+
4511
+ if (isSorted) {
4512
+ return;
4513
+ }
4514
+
4515
+ const isVisited = visited.includes(url);
4516
+
4517
+ if (isVisited) {
4518
+ circular.push(url);
4519
+ sorted.push(url);
4520
+ } else {
4521
+ visited.push(url);
4522
+ nodes[url].dependencies.forEach(dependencyUrl => {
4523
+ visit(dependencyUrl);
4524
+ });
4525
+ sorted.push(url);
4526
+ }
4527
+ };
4528
+
4529
+ Object.keys(nodes).forEach(url => {
4530
+ visit(url);
4531
+ });
4532
+ sorted.circular = circular;
4533
+ return sorted;
4534
+ };
4535
+
4458
4536
  /*
4459
4537
  * Each @import found in css is replaced by the file content
4460
4538
  * - There is no need to worry about urls (such as background-image: url())
@@ -5633,6 +5711,60 @@ const jsenvPluginDevSSEClient = () => {
5633
5711
  };
5634
5712
  };
5635
5713
 
5714
+ const createSSEService = ({
5715
+ serverEventCallbackList
5716
+ }) => {
5717
+ const destroyCallbackList = createCallbackListNotifiedOnce();
5718
+ const cache = [];
5719
+ const sseRoomLimit = 100;
5720
+
5721
+ const getOrCreateSSERoom = request => {
5722
+ const htmlFileRelativeUrl = request.ressource.slice(1);
5723
+ const cacheEntry = cache.find(cacheEntryCandidate => cacheEntryCandidate.htmlFileRelativeUrl === htmlFileRelativeUrl);
5724
+
5725
+ if (cacheEntry) {
5726
+ return cacheEntry.sseRoom;
5727
+ }
5728
+
5729
+ const sseRoom = createSSERoom({
5730
+ retryDuration: 2000,
5731
+ historyLength: 100,
5732
+ welcomeEventEnabled: true,
5733
+ effect: () => {
5734
+ return serverEventCallbackList.add(event => {
5735
+ sseRoom.sendEvent(event);
5736
+ });
5737
+ }
5738
+ });
5739
+ const removeSSECleanupCallback = destroyCallbackList.add(() => {
5740
+ removeSSECleanupCallback();
5741
+ sseRoom.close();
5742
+ });
5743
+ cache.push({
5744
+ htmlFileRelativeUrl,
5745
+ sseRoom,
5746
+ cleanup: () => {
5747
+ removeSSECleanupCallback();
5748
+ sseRoom.close();
5749
+ }
5750
+ });
5751
+
5752
+ if (cache.length >= sseRoomLimit) {
5753
+ const firstCacheEntry = cache.shift();
5754
+ firstCacheEntry.cleanup();
5755
+ }
5756
+
5757
+ return sseRoom;
5758
+ };
5759
+
5760
+ return {
5761
+ getOrCreateSSERoom,
5762
+ destroy: () => {
5763
+ destroyCallbackList.notify();
5764
+ }
5765
+ };
5766
+ };
5767
+
5636
5768
  const jsenvPluginDevSSEServer = ({
5637
5769
  rootDirectoryUrl,
5638
5770
  urlGraph,
@@ -6604,13 +6736,27 @@ const createUrlInfoTransformer = ({
6604
6736
  const sourcemapsEnabled = sourcemaps === "inline" || sourcemaps === "file" || sourcemaps === "programmatic";
6605
6737
 
6606
6738
  const normalizeSourcemap = (urlInfo, sourcemap) => {
6739
+ let {
6740
+ sources
6741
+ } = sourcemap;
6742
+
6743
+ if (sources) {
6744
+ sources = sources.map(source => {
6745
+ if (source && isFileSystemPath(source)) {
6746
+ return String(pathToFileURL(source));
6747
+ }
6748
+
6749
+ return source;
6750
+ });
6751
+ }
6752
+
6607
6753
  const wantSourcesContent = // for inline content (<script> insdide html)
6608
6754
  // chrome won't be able to fetch the file as it does not exists
6609
6755
  // so sourcemap must contain sources
6610
- sourcemapsSourcesContent || urlInfo.isInline || sourcemap.sources && sourcemap.sources.some(source => !source || !source.startsWith("file:"));
6756
+ sourcemapsSourcesContent || urlInfo.isInline || sources && sources.some(source => !source || !source.startsWith("file:"));
6611
6757
 
6612
- if (sourcemap.sources && sourcemap.sources.length > 1) {
6613
- sourcemap.sources = sourcemap.sources.map(source => new URL(source, urlInfo.originalUrl).href);
6758
+ if (sources && sources.length > 1) {
6759
+ sourcemap.sources = sources.map(source => new URL(source, urlInfo.originalUrl).href);
6614
6760
 
6615
6761
  if (!wantSourcesContent) {
6616
6762
  sourcemap.sourcesContent = undefined;
@@ -6646,7 +6792,7 @@ const createUrlInfoTransformer = ({
6646
6792
  // but otherwise it's generatedUrl to be inside .jsenv/ directory
6647
6793
 
6648
6794
 
6649
- urlInfo.sourcemapGeneratedUrl = generateSourcemapUrl(urlInfo.generatedUrl);
6795
+ urlInfo.sourcemapGeneratedUrl = generateSourcemapFileUrl(urlInfo.generatedUrl);
6650
6796
  const [sourcemapReference, sourcemapUrlInfo] = injectSourcemapPlaceholder({
6651
6797
  urlInfo,
6652
6798
  specifier: urlInfo.sourcemapGeneratedUrl
@@ -6752,7 +6898,7 @@ const createUrlInfoTransformer = ({
6752
6898
  sourcemapUrlInfo.content = JSON.stringify(sourcemap, null, " ");
6753
6899
 
6754
6900
  if (sourcemaps === "inline") {
6755
- sourcemapReference.generatedSpecifier = sourcemapToBase64Url(sourcemap);
6901
+ sourcemapReference.generatedSpecifier = generateSourcemapDataUrl(sourcemap);
6756
6902
  }
6757
6903
 
6758
6904
  if (sourcemaps === "file" || sourcemaps === "inline") {
@@ -8291,7 +8437,7 @@ const jsenvPluginExplorer = ({
8291
8437
 
8292
8438
  const startDevServer = async ({
8293
8439
  signal = new AbortController().signal,
8294
- handleSIGINT,
8440
+ handleSIGINT = true,
8295
8441
  logLevel = "info",
8296
8442
  omegaServerLogLevel = "warn",
8297
8443
  port = 3456,
@@ -8311,9 +8457,9 @@ const startDevServer = async ({
8311
8457
  devServerMainFile = getCallerPosition().url,
8312
8458
  // force disable server autoreload when this code is executed:
8313
8459
  // - inside a forked child process
8314
- // - inside a worker thread
8315
- // (because node cluster won't work)
8316
- devServerAutoreload = typeof process.send !== "function" && !parentPort && !process.env.VSCODE_INSPECTOR_OPTIONS,
8460
+ // - debugged by vscode
8461
+ // otherwise we get net:ERR_CONNECTION_REFUSED
8462
+ devServerAutoreload = typeof process.send !== "function" && !process.env.VSCODE_INSPECTOR_OPTIONS,
8317
8463
  clientFiles = {
8318
8464
  "./src/": true,
8319
8465
  "./test/": true
@@ -8353,32 +8499,36 @@ const startDevServer = async ({
8353
8499
  logLevel
8354
8500
  });
8355
8501
  rootDirectoryUrl = assertAndNormalizeDirectoryUrl(rootDirectoryUrl);
8356
- const reloadableProcess = await initReloadableProcess({
8357
- signal,
8358
- handleSIGINT,
8359
- ...(devServerAutoreload ? {
8360
- enabled: true,
8361
- logLevel: "warn",
8362
- fileToRestart: devServerMainFile
8363
- } : {
8364
- enabled: false
8365
- })
8366
- });
8502
+ const operation = Abort.startOperation();
8503
+ operation.addAbortSignal(signal);
8367
8504
 
8368
- if (reloadableProcess.isPrimary) {
8505
+ if (handleSIGINT) {
8506
+ operation.addAbortSource(abort => {
8507
+ return raceProcessTeardownEvents({
8508
+ SIGINT: true
8509
+ }, abort);
8510
+ });
8511
+ }
8512
+
8513
+ if (port === 0) {
8514
+ port = await findFreePort(port, {
8515
+ signal: operation.signal
8516
+ });
8517
+ }
8518
+
8519
+ const reloadableWorker = createReloadableWorker(devServerMainFile);
8520
+
8521
+ if (devServerAutoreload && reloadableWorker.isPrimary) {
8369
8522
  const devServerFileChangeCallback = ({
8370
8523
  relativeUrl,
8371
8524
  event
8372
8525
  }) => {
8373
8526
  const url = new URL(relativeUrl, rootDirectoryUrl).href;
8374
-
8375
- if (devServerAutoreload) {
8376
- logger.info(`file ${event} ${url} -> restarting server...`);
8377
- reloadableProcess.reload();
8378
- }
8527
+ logger.info(`file ${event} ${url} -> restarting server...`);
8528
+ reloadableWorker.reload();
8379
8529
  };
8380
8530
 
8381
- const unregisterDevServerFilesWatcher = registerDirectoryLifecycle(rootDirectoryUrl, {
8531
+ const stopWatchingDevServerFiles = registerDirectoryLifecycle(rootDirectoryUrl, {
8382
8532
  watchPatterns: {
8383
8533
  [devServerMainFile]: true,
8384
8534
  ...devServerFiles
@@ -8411,14 +8561,21 @@ const startDevServer = async ({
8411
8561
  });
8412
8562
  }
8413
8563
  });
8414
- signal.addEventListener("abort", () => {
8415
- unregisterDevServerFilesWatcher();
8564
+ operation.addAbortCallback(() => {
8565
+ stopWatchingDevServerFiles();
8566
+ reloadableWorker.terminate();
8416
8567
  });
8568
+ const worker = await reloadableWorker.load();
8569
+
8570
+ if (!keepProcessAlive) {
8571
+ worker.unref();
8572
+ }
8573
+
8417
8574
  return {
8418
8575
  origin: `${protocol}://127.0.0.1:${port}`,
8419
8576
  stop: () => {
8420
- unregisterDevServerFilesWatcher();
8421
- reloadableProcess.stop();
8577
+ stopWatchingDevServerFiles();
8578
+ reloadableWorker.terminate();
8422
8579
  }
8423
8580
  };
8424
8581
  }
@@ -8537,6 +8694,679 @@ const startDevServer = async ({
8537
8694
  };
8538
8695
  };
8539
8696
 
8697
+ const generateCoverageJsonFile = async ({
8698
+ coverage,
8699
+ coverageJsonFileUrl,
8700
+ coverageJsonFileLog,
8701
+ logger
8702
+ }) => {
8703
+ const coverageAsText = JSON.stringify(coverage, null, " ");
8704
+
8705
+ if (coverageJsonFileLog) {
8706
+ logger.info(`-> ${urlToFileSystemPath(coverageJsonFileUrl)} (${byteAsFileSize(Buffer.byteLength(coverageAsText))})`);
8707
+ }
8708
+
8709
+ await writeFile(coverageJsonFileUrl, coverageAsText);
8710
+ };
8711
+
8712
+ const istanbulCoverageMapFromCoverage = coverage => {
8713
+ const {
8714
+ createCoverageMap
8715
+ } = requireFromJsenv("istanbul-lib-coverage");
8716
+ const coverageAdjusted = {};
8717
+ Object.keys(coverage).forEach(key => {
8718
+ coverageAdjusted[key.slice(2)] = { ...coverage[key],
8719
+ path: key.slice(2)
8720
+ };
8721
+ });
8722
+ const coverageMap = createCoverageMap(coverageAdjusted);
8723
+ return coverageMap;
8724
+ };
8725
+
8726
+ const generateCoverageHtmlDirectory = async (coverage, {
8727
+ rootDirectoryUrl,
8728
+ coverageHtmlDirectoryRelativeUrl,
8729
+ coverageSkipEmpty,
8730
+ coverageSkipFull
8731
+ }) => {
8732
+ const libReport = requireFromJsenv("istanbul-lib-report");
8733
+ const reports = requireFromJsenv("istanbul-reports");
8734
+ const context = libReport.createContext({
8735
+ dir: urlToFileSystemPath(rootDirectoryUrl),
8736
+ coverageMap: istanbulCoverageMapFromCoverage(coverage),
8737
+ sourceFinder: path => {
8738
+ return readFileSync(urlToFileSystemPath(resolveUrl(path, rootDirectoryUrl)), "utf8");
8739
+ }
8740
+ });
8741
+ const report = reports.create("html", {
8742
+ skipEmpty: coverageSkipEmpty,
8743
+ skipFull: coverageSkipFull,
8744
+ subdir: coverageHtmlDirectoryRelativeUrl
8745
+ });
8746
+ report.execute(context);
8747
+ };
8748
+
8749
+ const generateCoverageTextLog = (coverage, {
8750
+ coverageSkipEmpty,
8751
+ coverageSkipFull
8752
+ }) => {
8753
+ const libReport = requireFromJsenv("istanbul-lib-report");
8754
+ const reports = requireFromJsenv("istanbul-reports");
8755
+ const context = libReport.createContext({
8756
+ coverageMap: istanbulCoverageMapFromCoverage(coverage)
8757
+ });
8758
+ const report = reports.create("text", {
8759
+ skipEmpty: coverageSkipEmpty,
8760
+ skipFull: coverageSkipFull
8761
+ });
8762
+ report.execute(context);
8763
+ };
8764
+
8765
+ const babelPluginInstrument = (api, {
8766
+ rootDirectoryUrl,
8767
+ useInlineSourceMaps = false,
8768
+ coverageConfig = {
8769
+ "./**/*": true
8770
+ }
8771
+ }) => {
8772
+ const {
8773
+ programVisitor
8774
+ } = requireFromJsenv("istanbul-lib-instrument");
8775
+ const {
8776
+ types
8777
+ } = api;
8778
+ const associations = URL_META.resolveAssociations({
8779
+ cover: coverageConfig
8780
+ }, rootDirectoryUrl);
8781
+
8782
+ const shouldInstrument = url => {
8783
+ return URL_META.applyAssociations({
8784
+ url,
8785
+ associations
8786
+ }).cover;
8787
+ };
8788
+
8789
+ return {
8790
+ name: "transform-instrument",
8791
+ visitor: {
8792
+ Program: {
8793
+ enter(path) {
8794
+ const {
8795
+ file
8796
+ } = this;
8797
+ const {
8798
+ opts
8799
+ } = file;
8800
+
8801
+ if (!opts.sourceFileName) {
8802
+ console.warn(`cannot instrument file when "sourceFileName" option is not set`);
8803
+ return;
8804
+ }
8805
+
8806
+ const fileUrl = fileSystemPathToUrl(opts.sourceFileName);
8807
+
8808
+ if (!shouldInstrument(fileUrl)) {
8809
+ return;
8810
+ }
8811
+
8812
+ this.__dv__ = null;
8813
+ let inputSourceMap;
8814
+
8815
+ if (useInlineSourceMaps) {
8816
+ // https://github.com/istanbuljs/babel-plugin-istanbul/commit/a9e15643d249a2985e4387e4308022053b2cd0ad#diff-1fdf421c05c1140f6d71444ea2b27638R65
8817
+ inputSourceMap = opts.inputSourceMap || file.inputMap ? file.inputMap.sourcemap : null;
8818
+ } else {
8819
+ inputSourceMap = opts.inputSourceMap;
8820
+ }
8821
+
8822
+ this.__dv__ = programVisitor(types, opts.filenameRelative || opts.filename, {
8823
+ coverageVariable: "__coverage__",
8824
+ inputSourceMap
8825
+ });
8826
+
8827
+ this.__dv__.enter(path);
8828
+ },
8829
+
8830
+ exit(path) {
8831
+ if (!this.__dv__) {
8832
+ return;
8833
+ }
8834
+
8835
+ const object = this.__dv__.exit(path); // object got two properties: fileCoverage and sourceMappingURL
8836
+
8837
+
8838
+ this.file.metadata.coverage = object.fileCoverage;
8839
+ }
8840
+
8841
+ }
8842
+ }
8843
+ };
8844
+ };
8845
+
8846
+ const visitNodeV8Directory = async ({
8847
+ logger,
8848
+ signal,
8849
+ NODE_V8_COVERAGE,
8850
+ onV8Coverage,
8851
+ maxMsWaitingForNodeToWriteCoverageFile = 2000
8852
+ }) => {
8853
+ const operation = Abort.startOperation();
8854
+ operation.addAbortSignal(signal);
8855
+
8856
+ const tryReadDirectory = async () => {
8857
+ const dirContent = await readDirectory(NODE_V8_COVERAGE);
8858
+
8859
+ if (dirContent.length > 0) {
8860
+ return dirContent;
8861
+ }
8862
+
8863
+ logger.warn(`v8 coverage directory is empty at ${NODE_V8_COVERAGE}`);
8864
+ return dirContent;
8865
+ };
8866
+
8867
+ try {
8868
+ operation.throwIfAborted();
8869
+ const dirContent = await tryReadDirectory();
8870
+ const coverageDirectoryUrl = assertAndNormalizeDirectoryUrl(NODE_V8_COVERAGE);
8871
+ await dirContent.reduce(async (previous, dirEntry) => {
8872
+ operation.throwIfAborted();
8873
+ await previous;
8874
+ const dirEntryUrl = resolveUrl(dirEntry, coverageDirectoryUrl);
8875
+
8876
+ const tryReadJsonFile = async (timeSpentTrying = 0) => {
8877
+ const fileContent = await readFile(dirEntryUrl, {
8878
+ as: "string"
8879
+ });
8880
+
8881
+ if (fileContent === "") {
8882
+ if (timeSpentTrying < 400) {
8883
+ await new Promise(resolve => setTimeout(resolve, 200));
8884
+ return tryReadJsonFile(timeSpentTrying + 200);
8885
+ }
8886
+
8887
+ console.warn(`Coverage JSON file is empty at ${dirEntryUrl}`);
8888
+ return null;
8889
+ }
8890
+
8891
+ try {
8892
+ const fileAsJson = JSON.parse(fileContent);
8893
+ return fileAsJson;
8894
+ } catch (e) {
8895
+ if (timeSpentTrying < maxMsWaitingForNodeToWriteCoverageFile) {
8896
+ await new Promise(resolve => setTimeout(resolve, 200));
8897
+ return tryReadJsonFile(timeSpentTrying + 200);
8898
+ }
8899
+
8900
+ console.warn(createDetailedMessage(`Error while reading coverage file`, {
8901
+ "error stack": e.stack,
8902
+ "file": dirEntryUrl
8903
+ }));
8904
+ return null;
8905
+ }
8906
+ };
8907
+
8908
+ const fileContent = await tryReadJsonFile();
8909
+
8910
+ if (fileContent) {
8911
+ onV8Coverage(fileContent);
8912
+ }
8913
+ }, Promise.resolve());
8914
+ } finally {
8915
+ await operation.end();
8916
+ }
8917
+ };
8918
+ const filterV8Coverage = (v8Coverage, {
8919
+ urlShouldBeCovered
8920
+ }) => {
8921
+ const v8CoverageFiltered = { ...v8Coverage,
8922
+ result: v8Coverage.result.filter(fileReport => urlShouldBeCovered(fileReport.url))
8923
+ };
8924
+ return v8CoverageFiltered;
8925
+ };
8926
+
8927
+ const composeTwoV8Coverages = (firstV8Coverage, secondV8Coverage) => {
8928
+ if (secondV8Coverage.result.length === 0) {
8929
+ return firstV8Coverage;
8930
+ } // eslint-disable-next-line import/no-unresolved
8931
+
8932
+
8933
+ const {
8934
+ mergeProcessCovs
8935
+ } = requireFromJsenv("@c88/v8-coverage"); // "mergeProcessCovs" do not preserves source-map-cache during the merge
8936
+ // so we store sourcemap cache now
8937
+
8938
+ const sourceMapCache = {};
8939
+
8940
+ const visit = coverageReport => {
8941
+ if (coverageReport["source-map-cache"]) {
8942
+ Object.assign(sourceMapCache, coverageReport["source-map-cache"]);
8943
+ }
8944
+ };
8945
+
8946
+ visit(firstV8Coverage);
8947
+ visit(secondV8Coverage);
8948
+ const v8Coverage = mergeProcessCovs([firstV8Coverage, secondV8Coverage]);
8949
+ v8Coverage["source-map-cache"] = sourceMapCache;
8950
+ return v8Coverage;
8951
+ };
8952
+
8953
+ const composeTwoFileByFileIstanbulCoverages = (firstFileByFileIstanbulCoverage, secondFileByFileIstanbulCoverage) => {
8954
+ const fileByFileIstanbulCoverage = {};
8955
+ Object.keys(firstFileByFileIstanbulCoverage).forEach(key => {
8956
+ fileByFileIstanbulCoverage[key] = firstFileByFileIstanbulCoverage[key];
8957
+ });
8958
+ Object.keys(secondFileByFileIstanbulCoverage).forEach(key => {
8959
+ const firstCoverage = firstFileByFileIstanbulCoverage[key];
8960
+ const secondCoverage = secondFileByFileIstanbulCoverage[key];
8961
+ fileByFileIstanbulCoverage[key] = firstCoverage ? merge(firstCoverage, secondCoverage) : secondCoverage;
8962
+ });
8963
+ return fileByFileIstanbulCoverage;
8964
+ };
8965
+
8966
+ const merge = (firstIstanbulCoverage, secondIstanbulCoverage) => {
8967
+ const {
8968
+ createFileCoverage
8969
+ } = requireFromJsenv("istanbul-lib-coverage");
8970
+ const istanbulFileCoverageObject = createFileCoverage(firstIstanbulCoverage);
8971
+ istanbulFileCoverageObject.merge(secondIstanbulCoverage);
8972
+ const istanbulCoverage = istanbulFileCoverageObject.toJSON();
8973
+ return istanbulCoverage;
8974
+ };
8975
+
8976
+ const v8CoverageToIstanbul = async (v8Coverage, {
8977
+ signal
8978
+ }) => {
8979
+ const operation = Abort.startOperation();
8980
+ operation.addAbortSignal(signal);
8981
+
8982
+ try {
8983
+ const v8ToIstanbul = requireFromJsenv("v8-to-istanbul");
8984
+ const sourcemapCache = v8Coverage["source-map-cache"];
8985
+ let istanbulCoverageComposed = null;
8986
+ await v8Coverage.result.reduce(async (previous, fileV8Coverage) => {
8987
+ operation.throwIfAborted();
8988
+ await previous;
8989
+ const {
8990
+ source
8991
+ } = fileV8Coverage;
8992
+ let sources; // when v8 coverage comes from playwright (chromium) v8Coverage.source is set
8993
+
8994
+ if (typeof source === "string") {
8995
+ sources = {
8996
+ source
8997
+ };
8998
+ } // when v8 coverage comes from Node.js, the source can be read from sourcemapCache
8999
+ else if (sourcemapCache) {
9000
+ sources = sourcesFromSourceMapCache(fileV8Coverage.url, sourcemapCache);
9001
+ }
9002
+
9003
+ const path = urlToFileSystemPath(fileV8Coverage.url);
9004
+ const converter = v8ToIstanbul(path, // wrapperLength is undefined we don't need it
9005
+ // https://github.com/istanbuljs/v8-to-istanbul/blob/2b54bc97c5edf8a37b39a171ec29134ba9bfd532/lib/v8-to-istanbul.js#L27
9006
+ undefined, sources);
9007
+ await converter.load();
9008
+ converter.applyCoverage(fileV8Coverage.functions);
9009
+ const istanbulCoverage = converter.toIstanbul();
9010
+ istanbulCoverageComposed = istanbulCoverageComposed ? composeTwoFileByFileIstanbulCoverages(istanbulCoverageComposed, istanbulCoverage) : istanbulCoverage;
9011
+ }, Promise.resolve());
9012
+
9013
+ if (!istanbulCoverageComposed) {
9014
+ return {};
9015
+ }
9016
+
9017
+ istanbulCoverageComposed = markAsConvertedFromV8(istanbulCoverageComposed);
9018
+ return istanbulCoverageComposed;
9019
+ } finally {
9020
+ await operation.end();
9021
+ }
9022
+ };
9023
+
9024
+ const markAsConvertedFromV8 = fileByFileCoverage => {
9025
+ const fileByFileMarked = {};
9026
+ Object.keys(fileByFileCoverage).forEach(key => {
9027
+ const fileCoverage = fileByFileCoverage[key];
9028
+ fileByFileMarked[key] = { ...fileCoverage,
9029
+ fromV8: true
9030
+ };
9031
+ });
9032
+ return fileByFileMarked;
9033
+ };
9034
+
9035
+ const sourcesFromSourceMapCache = (url, sourceMapCache) => {
9036
+ const sourceMapAndLineLengths = sourceMapCache[url];
9037
+
9038
+ if (!sourceMapAndLineLengths) {
9039
+ return {};
9040
+ }
9041
+
9042
+ const {
9043
+ data,
9044
+ lineLengths
9045
+ } = sourceMapAndLineLengths; // See: https://github.com/nodejs/node/pull/34305
9046
+
9047
+ if (!data) {
9048
+ return undefined;
9049
+ }
9050
+
9051
+ const sources = {
9052
+ sourcemap: data,
9053
+ ...(lineLengths ? {
9054
+ source: sourcesFromLineLengths(lineLengths)
9055
+ } : {})
9056
+ };
9057
+ return sources;
9058
+ };
9059
+
9060
+ const sourcesFromLineLengths = lineLengths => {
9061
+ let source = "";
9062
+ lineLengths.forEach(length => {
9063
+ source += `${"".padEnd(length, ".")}\n`;
9064
+ });
9065
+ return source;
9066
+ };
9067
+
9068
+ const composeV8AndIstanbul = (v8FileByFileCoverage, istanbulFileByFileCoverage, {
9069
+ coverageV8ConflictWarning
9070
+ }) => {
9071
+ const fileByFileCoverage = {};
9072
+ const v8Files = Object.keys(v8FileByFileCoverage);
9073
+ const istanbulFiles = Object.keys(istanbulFileByFileCoverage);
9074
+ v8Files.forEach(key => {
9075
+ fileByFileCoverage[key] = v8FileByFileCoverage[key];
9076
+ });
9077
+ istanbulFiles.forEach(key => {
9078
+ const v8Coverage = v8FileByFileCoverage[key];
9079
+
9080
+ if (v8Coverage) {
9081
+ if (coverageV8ConflictWarning) {
9082
+ console.warn(createDetailedMessage(`Coverage conflict on "${key}", found two coverage that cannot be merged together: v8 and istanbul. The istanbul coverage will be ignored.`, {
9083
+ details: `This happens when a file is executed on a runtime using v8 coverage (node or chromium) and on runtime using istanbul coverage (firefox or webkit)`,
9084
+ suggestion: "You can disable this warning with coverageV8ConflictWarning: false"
9085
+ }));
9086
+ }
9087
+
9088
+ fileByFileCoverage[key] = v8Coverage;
9089
+ } else {
9090
+ fileByFileCoverage[key] = istanbulFileByFileCoverage[key];
9091
+ }
9092
+ });
9093
+ return fileByFileCoverage;
9094
+ };
9095
+
9096
+ const normalizeFileByFileCoveragePaths = (fileByFileCoverage, rootDirectoryUrl) => {
9097
+ const fileByFileNormalized = {};
9098
+ Object.keys(fileByFileCoverage).forEach(key => {
9099
+ const fileCoverage = fileByFileCoverage[key];
9100
+ const {
9101
+ path
9102
+ } = fileCoverage;
9103
+ const url = isFileSystemPath(path) ? fileSystemPathToUrl(path) : new URL(path, rootDirectoryUrl).href;
9104
+ const relativeUrl = urlToRelativeUrl(url, rootDirectoryUrl);
9105
+ fileByFileNormalized[`./${relativeUrl}`] = { ...fileCoverage,
9106
+ path: `./${relativeUrl}`
9107
+ };
9108
+ });
9109
+ return fileByFileNormalized;
9110
+ };
9111
+
9112
+ const listRelativeFileUrlToCover = async ({
9113
+ signal,
9114
+ rootDirectoryUrl,
9115
+ coverageConfig
9116
+ }) => {
9117
+ const matchingFileResultArray = await collectFiles({
9118
+ signal,
9119
+ directoryUrl: rootDirectoryUrl,
9120
+ associations: {
9121
+ cover: coverageConfig
9122
+ },
9123
+ predicate: ({
9124
+ cover
9125
+ }) => cover
9126
+ });
9127
+ return matchingFileResultArray.map(({
9128
+ relativeUrl
9129
+ }) => relativeUrl);
9130
+ };
9131
+
9132
+ const relativeUrlToEmptyCoverage = async (relativeUrl, {
9133
+ signal,
9134
+ rootDirectoryUrl
9135
+ }) => {
9136
+ const operation = Abort.startOperation();
9137
+ operation.addAbortSignal(signal);
9138
+
9139
+ try {
9140
+ const fileUrl = resolveUrl(relativeUrl, rootDirectoryUrl);
9141
+ const content = await readFile(fileUrl, {
9142
+ as: "string"
9143
+ });
9144
+ operation.throwIfAborted();
9145
+ const {
9146
+ metadata
9147
+ } = await applyBabelPlugins({
9148
+ babelPlugins: [[babelPluginInstrument, {
9149
+ rootDirectoryUrl
9150
+ }]],
9151
+ urlInfo: {
9152
+ originalUrl: fileUrl,
9153
+ content
9154
+ }
9155
+ });
9156
+ const {
9157
+ coverage
9158
+ } = metadata;
9159
+
9160
+ if (!coverage) {
9161
+ throw new Error(`missing coverage for file`);
9162
+ } // https://github.com/gotwarlost/istanbul/blob/bc84c315271a5dd4d39bcefc5925cfb61a3d174a/lib/command/common/run-with-cover.js#L229
9163
+
9164
+
9165
+ Object.keys(coverage.s).forEach(function (key) {
9166
+ coverage.s[key] = 0;
9167
+ });
9168
+ return coverage;
9169
+ } catch (e) {
9170
+ if (e && e.code === "PARSE_ERROR") {
9171
+ // return an empty coverage for that file when
9172
+ // it contains a syntax error
9173
+ return createEmptyCoverage(relativeUrl);
9174
+ }
9175
+
9176
+ throw e;
9177
+ } finally {
9178
+ await operation.end();
9179
+ }
9180
+ };
9181
+
9182
+ const createEmptyCoverage = relativeUrl => {
9183
+ const {
9184
+ createFileCoverage
9185
+ } = requireFromJsenv("istanbul-lib-coverage");
9186
+ return createFileCoverage(relativeUrl).toJSON();
9187
+ };
9188
+
9189
+ const getMissingFileByFileCoverage = async ({
9190
+ signal,
9191
+ rootDirectoryUrl,
9192
+ coverageConfig,
9193
+ fileByFileCoverage
9194
+ }) => {
9195
+ const relativeUrlsToCover = await listRelativeFileUrlToCover({
9196
+ signal,
9197
+ rootDirectoryUrl,
9198
+ coverageConfig
9199
+ });
9200
+ const relativeUrlsMissing = relativeUrlsToCover.filter(relativeUrlToCover => Object.keys(fileByFileCoverage).every(key => {
9201
+ return key !== `./${relativeUrlToCover}`;
9202
+ }));
9203
+ const operation = Abort.startOperation();
9204
+ operation.addAbortSignal(signal);
9205
+ const missingFileByFileCoverage = {};
9206
+ await relativeUrlsMissing.reduce(async (previous, relativeUrlMissing) => {
9207
+ operation.throwIfAborted();
9208
+ await previous;
9209
+ await operation.withSignal(async signal => {
9210
+ const emptyCoverage = await relativeUrlToEmptyCoverage(relativeUrlMissing, {
9211
+ signal,
9212
+ rootDirectoryUrl
9213
+ });
9214
+ missingFileByFileCoverage[`./${relativeUrlMissing}`] = emptyCoverage;
9215
+ });
9216
+ }, Promise.resolve());
9217
+ return missingFileByFileCoverage;
9218
+ };
9219
+
9220
+ const reportToCoverage = async (report, {
9221
+ signal,
9222
+ logger,
9223
+ rootDirectoryUrl,
9224
+ coverageConfig,
9225
+ coverageIncludeMissing,
9226
+ urlShouldBeCovered,
9227
+ coverageForceIstanbul,
9228
+ coverageV8ConflictWarning
9229
+ }) => {
9230
+ // collect v8 and istanbul coverage from executions
9231
+ let {
9232
+ v8Coverage,
9233
+ fileByFileIstanbulCoverage
9234
+ } = await getCoverageFromReport({
9235
+ signal,
9236
+ report,
9237
+ onMissing: ({
9238
+ file,
9239
+ executionResult,
9240
+ executionName
9241
+ }) => {
9242
+ // several reasons not to have coverage here:
9243
+ // 1. the file we executed did not import an instrumented file.
9244
+ // - a test file without import
9245
+ // - a test file importing only file excluded from coverage
9246
+ // - a coverDescription badly configured so that we don't realize
9247
+ // a file should be covered
9248
+ // 2. the file we wanted to executed timedout
9249
+ // - infinite loop
9250
+ // - too extensive operation
9251
+ // - a badly configured or too low allocatedMs for that execution.
9252
+ // 3. the file we wanted to execute contains syntax-error
9253
+ // in any scenario we are fine because
9254
+ // coverDescription will generate empty coverage for files
9255
+ // that were suppose to be coverage but were not.
9256
+ if (executionResult.status === "completed" && executionResult.runtimeName !== "node" && !process.env.NODE_V8_COVERAGE) {
9257
+ logger.warn(`No execution.coverageFileUrl from execution named "${executionName}" of ${file}`);
9258
+ }
9259
+ }
9260
+ });
9261
+
9262
+ if (!coverageForceIstanbul && process.env.NODE_V8_COVERAGE) {
9263
+ await visitNodeV8Directory({
9264
+ logger,
9265
+ signal,
9266
+ NODE_V8_COVERAGE: process.env.NODE_V8_COVERAGE,
9267
+ onV8Coverage: nodeV8Coverage => {
9268
+ const nodeV8CoverageLight = filterV8Coverage(nodeV8Coverage, {
9269
+ urlShouldBeCovered
9270
+ });
9271
+ v8Coverage = v8Coverage ? composeTwoV8Coverages(v8Coverage, nodeV8CoverageLight) : nodeV8CoverageLight;
9272
+ }
9273
+ });
9274
+ } // try to merge v8 with istanbul, if any
9275
+
9276
+
9277
+ let fileByFileCoverage;
9278
+
9279
+ if (v8Coverage) {
9280
+ let v8FileByFileCoverage = await v8CoverageToIstanbul(v8Coverage, {
9281
+ signal
9282
+ });
9283
+ v8FileByFileCoverage = normalizeFileByFileCoveragePaths(v8FileByFileCoverage, rootDirectoryUrl);
9284
+
9285
+ if (fileByFileIstanbulCoverage) {
9286
+ fileByFileIstanbulCoverage = normalizeFileByFileCoveragePaths(fileByFileIstanbulCoverage, rootDirectoryUrl);
9287
+ fileByFileCoverage = composeV8AndIstanbul(v8FileByFileCoverage, fileByFileIstanbulCoverage, {
9288
+ coverageV8ConflictWarning
9289
+ });
9290
+ } else {
9291
+ fileByFileCoverage = v8FileByFileCoverage;
9292
+ }
9293
+ } // get istanbul only
9294
+ else if (fileByFileIstanbulCoverage) {
9295
+ fileByFileCoverage = normalizeFileByFileCoveragePaths(fileByFileIstanbulCoverage, rootDirectoryUrl);
9296
+ } // no coverage found in execution (or zero file where executed)
9297
+ else {
9298
+ fileByFileCoverage = {};
9299
+ } // now add coverage for file not covered
9300
+
9301
+
9302
+ if (coverageIncludeMissing) {
9303
+ const missingFileByFileCoverage = await getMissingFileByFileCoverage({
9304
+ signal,
9305
+ rootDirectoryUrl,
9306
+ coverageConfig,
9307
+ fileByFileCoverage
9308
+ });
9309
+ Object.assign(fileByFileCoverage, normalizeFileByFileCoveragePaths(missingFileByFileCoverage, rootDirectoryUrl));
9310
+ }
9311
+
9312
+ return fileByFileCoverage;
9313
+ };
9314
+
9315
+ const getCoverageFromReport = async ({
9316
+ signal,
9317
+ report,
9318
+ onMissing
9319
+ }) => {
9320
+ const operation = Abort.startOperation();
9321
+ operation.addAbortSignal(signal);
9322
+
9323
+ try {
9324
+ let v8Coverage;
9325
+ let fileByFileIstanbulCoverage; // collect v8 and istanbul coverage from executions
9326
+
9327
+ await Object.keys(report).reduce(async (previous, file) => {
9328
+ operation.throwIfAborted();
9329
+ await previous;
9330
+ const executionResultForFile = report[file];
9331
+ await Object.keys(executionResultForFile).reduce(async (previous, executionName) => {
9332
+ operation.throwIfAborted();
9333
+ await previous;
9334
+ const executionResultForFileOnRuntime = executionResultForFile[executionName];
9335
+ const {
9336
+ coverageFileUrl
9337
+ } = executionResultForFileOnRuntime;
9338
+
9339
+ if (!coverageFileUrl) {
9340
+ onMissing({
9341
+ executionName,
9342
+ file,
9343
+ executionResult: executionResultForFileOnRuntime
9344
+ });
9345
+ return;
9346
+ }
9347
+
9348
+ const executionCoverage = await readFile(coverageFileUrl, {
9349
+ as: "json"
9350
+ });
9351
+
9352
+ if (isV8Coverage(executionCoverage)) {
9353
+ v8Coverage = v8Coverage ? composeTwoV8Coverages(v8Coverage, executionCoverage) : executionCoverage;
9354
+ } else {
9355
+ fileByFileIstanbulCoverage = fileByFileIstanbulCoverage ? composeTwoFileByFileIstanbulCoverages(fileByFileIstanbulCoverage, executionCoverage) : executionCoverage;
9356
+ }
9357
+ }, Promise.resolve());
9358
+ }, Promise.resolve());
9359
+ return {
9360
+ v8Coverage,
9361
+ fileByFileIstanbulCoverage
9362
+ };
9363
+ } finally {
9364
+ await operation.end();
9365
+ }
9366
+ };
9367
+
9368
+ const isV8Coverage = coverage => Boolean(coverage.result);
9369
+
8540
9370
  const run = async ({
8541
9371
  signal = new AbortController().signal,
8542
9372
  logger,
@@ -12296,7 +13126,7 @@ build ${entryPointKeys.length} entry points`);
12296
13126
 
12297
13127
  if (reference.type === "sourcemap_comment") {
12298
13128
  // inherit parent build url
12299
- return generateSourcemapUrl(reference.parentUrl);
13129
+ return generateSourcemapFileUrl(reference.parentUrl);
12300
13130
  } // files generated during the final graph:
12301
13131
  // - sourcemaps
12302
13132
  // const finalUrlInfo = finalGraph.getUrlInfo(url)
@@ -13022,6 +13852,7 @@ const startBuildServer = async ({
13022
13852
  ip,
13023
13853
  port = 9779,
13024
13854
  services = {},
13855
+ keepProcessAlive = true,
13025
13856
  rootDirectoryUrl,
13026
13857
  buildDirectoryUrl,
13027
13858
  mainBuildFileUrl = "/index.html",
@@ -13032,9 +13863,9 @@ const startBuildServer = async ({
13032
13863
  buildServerMainFile = getCallerPosition().url,
13033
13864
  // force disable server autoreload when this code is executed:
13034
13865
  // - inside a forked child process
13035
- // - inside a worker thread
13036
- // (because node cluster won't work)
13037
- buildServerAutoreload = typeof process.send !== "function" && !parentPort && !process.env.VSCODE_INSPECTOR_OPTIONS,
13866
+ // - debugged by vscode
13867
+ // otherwise we get net:ERR_CONNECTION_REFUSED
13868
+ buildServerAutoreload = typeof process.send !== "function" && !process.env.VSCODE_INSPECTOR_OPTIONS,
13038
13869
  cooldownBetweenFileEvents
13039
13870
  }) => {
13040
13871
  const logger = createLogger({
@@ -13042,19 +13873,26 @@ const startBuildServer = async ({
13042
13873
  });
13043
13874
  rootDirectoryUrl = assertAndNormalizeDirectoryUrl(rootDirectoryUrl);
13044
13875
  buildDirectoryUrl = assertAndNormalizeDirectoryUrl(buildDirectoryUrl);
13045
- const reloadableProcess = await initReloadableProcess({
13046
- signal,
13047
- handleSIGINT,
13048
- ...(buildServerAutoreload ? {
13049
- enabled: true,
13050
- logLevel: "info",
13051
- fileToRestart: buildServerMainFile
13052
- } : {
13053
- enabled: false
13054
- })
13055
- });
13876
+ const operation = Abort.startOperation();
13877
+ operation.addAbortSignal(signal);
13056
13878
 
13057
- if (reloadableProcess.isPrimary) {
13879
+ if (handleSIGINT) {
13880
+ operation.addAbortSource(abort => {
13881
+ return raceProcessTeardownEvents({
13882
+ SIGINT: true
13883
+ }, abort);
13884
+ });
13885
+ }
13886
+
13887
+ if (port === 0) {
13888
+ port = await findFreePort(port, {
13889
+ signal: operation.signal
13890
+ });
13891
+ }
13892
+
13893
+ const reloadableWorker = createReloadableWorker(buildServerMainFile);
13894
+
13895
+ if (buildServerAutoreload && reloadableWorker.isPrimary) {
13058
13896
  const buildServerFileChangeCallback = ({
13059
13897
  relativeUrl,
13060
13898
  event
@@ -13063,7 +13901,7 @@ const startBuildServer = async ({
13063
13901
 
13064
13902
  if (buildServerAutoreload) {
13065
13903
  logger.info(`file ${event} ${url} -> restarting server...`);
13066
- reloadableProcess.reload();
13904
+ reloadableWorker.reload();
13067
13905
  }
13068
13906
  };
13069
13907
 
@@ -13100,19 +13938,25 @@ const startBuildServer = async ({
13100
13938
  });
13101
13939
  }
13102
13940
  });
13103
- signal.addEventListener("abort", () => {
13941
+ operation.addAbortCallback(() => {
13104
13942
  stopWatchingBuildServerFiles();
13943
+ reloadableWorker.terminate();
13105
13944
  });
13945
+ const worker = await reloadableWorker.load();
13946
+
13947
+ if (!keepProcessAlive) {
13948
+ worker.unref();
13949
+ }
13950
+
13106
13951
  return {
13107
13952
  origin: `${protocol}://127.0.0.1:${port}`,
13108
13953
  stop: () => {
13109
13954
  stopWatchingBuildServerFiles();
13110
- reloadableProcess.stop();
13955
+ reloadableWorker.terminate();
13111
13956
  }
13112
13957
  };
13113
13958
  }
13114
13959
 
13115
- signal = reloadableProcess.signal;
13116
13960
  const startBuildServerTask = createTaskLog("start build server", {
13117
13961
  disabled: !loggerToLevels(logger).info
13118
13962
  });