@jsenv/core 27.1.0 → 27.2.2

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 (36) hide show
  1. package/dist/controllable_child_process.mjs +139 -0
  2. package/dist/controllable_worker_thread.mjs +103 -0
  3. package/dist/js/execute_using_dynamic_import.js +169 -0
  4. package/dist/js/v8_coverage.js +539 -0
  5. package/dist/main.js +580 -806
  6. package/package.json +10 -9
  7. package/src/build/build.js +9 -12
  8. package/src/build/build_urls_generator.js +1 -1
  9. package/src/build/inject_global_version_mappings.js +3 -2
  10. package/src/build/inject_service_worker_urls.js +1 -2
  11. package/src/execute/run.js +50 -68
  12. package/src/execute/runtimes/browsers/chromium.js +1 -1
  13. package/src/execute/runtimes/browsers/firefox.js +1 -1
  14. package/src/execute/runtimes/browsers/from_playwright.js +13 -8
  15. package/src/execute/runtimes/browsers/webkit.js +1 -1
  16. package/src/execute/runtimes/node/{controllable_file.mjs → controllable_child_process.mjs} +18 -50
  17. package/src/execute/runtimes/node/controllable_worker_thread.mjs +103 -0
  18. package/src/execute/runtimes/node/execute_using_dynamic_import.js +49 -0
  19. package/src/execute/runtimes/node/exit_codes.js +9 -0
  20. package/src/execute/runtimes/node/{node_process.js → node_child_process.js} +56 -50
  21. package/src/execute/runtimes/node/node_worker_thread.js +268 -25
  22. package/src/execute/runtimes/node/profiler_v8_coverage.js +56 -0
  23. package/src/main.js +3 -1
  24. package/src/omega/kitchen.js +19 -6
  25. package/src/omega/server/file_service.js +2 -2
  26. package/src/omega/url_graph/url_graph_load.js +0 -1
  27. package/src/omega/url_graph.js +1 -0
  28. package/src/plugins/bundling/js_module/bundle_js_module.js +2 -5
  29. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +18 -15
  30. package/src/plugins/url_resolution/jsenv_plugin_url_resolution.js +2 -1
  31. package/src/test/coverage/report_to_coverage.js +16 -19
  32. package/src/test/coverage/v8_coverage.js +26 -0
  33. package/src/test/coverage/{v8_coverage_from_directory.js → v8_coverage_node_directory.js} +22 -26
  34. package/src/test/execute_plan.js +92 -91
  35. package/src/test/execute_test_plan.js +15 -13
  36. package/dist/js/controllable_file.mjs +0 -227
package/dist/main.js CHANGED
@@ -13,17 +13,17 @@ import { performance } from "node:perf_hooks";
13
13
  import { extname, dirname, basename } from "node:path";
14
14
  import { pathToFileURL, fileURLToPath } from "node:url";
15
15
  import crypto, { createHash } from "node:crypto";
16
+ import { U as URL_META, f as filterV8Coverage } from "./js/v8_coverage.js";
16
17
  import { parseHtmlString, stringifyHtmlAst, visitHtmlNodes, getHtmlNodeAttribute, setHtmlNodeAttributes, parseSrcSet, getHtmlNodePosition, getHtmlNodeAttributePosition, applyPostCss, postCssPluginUrlVisitor, parseJsUrls, findHtmlNode, getHtmlNodeText, removeHtmlNode, setHtmlNodeText, analyzeScriptNode, applyBabelPlugins, injectScriptNodeAsEarlyAsPossible, createHtmlNode, removeHtmlNodeText, transpileWithParcel, injectJsImport, minifyWithParcel, analyzeLinkNode } from "@jsenv/ast";
17
18
  import { createMagicSource, composeTwoSourcemaps, sourcemapConverter, SOURCEMAP, generateSourcemapFileUrl, generateSourcemapDataUrl } from "@jsenv/sourcemap";
18
19
  import { createRequire } from "node:module";
19
20
  import babelParser from "@babel/parser";
21
+ import v8, { takeCoverage } from "node:v8";
20
22
  import wrapAnsi from "wrap-ansi";
21
23
  import stripAnsi from "strip-ansi";
22
24
  import cuid from "cuid";
23
- import v8 from "node:v8";
24
25
  import { runInNewContext, Script } from "node:vm";
25
26
  import { fork } from "node:child_process";
26
- import { u as uneval } from "./js/uneval.js";
27
27
 
28
28
  const LOG_LEVEL_OFF = "off";
29
29
  const LOG_LEVEL_DEBUG = "debug";
@@ -5742,515 +5742,6 @@ const createEventHistory = limit => {
5742
5742
  };
5743
5743
  };
5744
5744
 
5745
- const assertUrlLike = (value, name = "url") => {
5746
- if (typeof value !== "string") {
5747
- throw new TypeError(`${name} must be a url string, got ${value}`);
5748
- }
5749
-
5750
- if (isWindowsPathnameSpecifier(value)) {
5751
- throw new TypeError(`${name} must be a url but looks like a windows pathname, got ${value}`);
5752
- }
5753
-
5754
- if (!hasScheme$1(value)) {
5755
- throw new TypeError(`${name} must be a url and no scheme found, got ${value}`);
5756
- }
5757
- };
5758
- const isPlainObject = value => {
5759
- if (value === null) {
5760
- return false;
5761
- }
5762
-
5763
- if (typeof value === "object") {
5764
- if (Array.isArray(value)) {
5765
- return false;
5766
- }
5767
-
5768
- return true;
5769
- }
5770
-
5771
- return false;
5772
- };
5773
-
5774
- const isWindowsPathnameSpecifier = specifier => {
5775
- const firstChar = specifier[0];
5776
- if (!/[a-zA-Z]/.test(firstChar)) return false;
5777
- const secondChar = specifier[1];
5778
- if (secondChar !== ":") return false;
5779
- const thirdChar = specifier[2];
5780
- return thirdChar === "/" || thirdChar === "\\";
5781
- };
5782
-
5783
- const hasScheme$1 = specifier => /^[a-zA-Z]+:/.test(specifier);
5784
-
5785
- const resolveAssociations = (associations, baseUrl) => {
5786
- assertUrlLike(baseUrl, "baseUrl");
5787
- const associationsResolved = {};
5788
- Object.keys(associations).forEach(key => {
5789
- const valueMap = associations[key];
5790
- const valueMapResolved = {};
5791
- Object.keys(valueMap).forEach(pattern => {
5792
- const value = valueMap[pattern];
5793
- const patternResolved = normalizeUrlPattern(pattern, baseUrl);
5794
- valueMapResolved[patternResolved] = value;
5795
- });
5796
- associationsResolved[key] = valueMapResolved;
5797
- });
5798
- return associationsResolved;
5799
- };
5800
-
5801
- const normalizeUrlPattern = (urlPattern, baseUrl) => {
5802
- // starts with a scheme
5803
- if (/^[a-zA-Z]{2,}:/.test(urlPattern)) {
5804
- return urlPattern;
5805
- }
5806
-
5807
- return String(new URL(urlPattern, baseUrl));
5808
- };
5809
-
5810
- const asFlatAssociations = associations => {
5811
- if (!isPlainObject(associations)) {
5812
- throw new TypeError(`associations must be a plain object, got ${associations}`);
5813
- }
5814
-
5815
- const flatAssociations = {};
5816
- Object.keys(associations).forEach(key => {
5817
- const valueMap = associations[key];
5818
-
5819
- if (!isPlainObject(valueMap)) {
5820
- throw new TypeError(`all associations value must be objects, found "${key}": ${valueMap}`);
5821
- }
5822
-
5823
- Object.keys(valueMap).forEach(pattern => {
5824
- const value = valueMap[pattern];
5825
- const previousValue = flatAssociations[pattern];
5826
- flatAssociations[pattern] = previousValue ? { ...previousValue,
5827
- [key]: value
5828
- } : {
5829
- [key]: value
5830
- };
5831
- });
5832
- });
5833
- return flatAssociations;
5834
- };
5835
-
5836
- /*
5837
- * Link to things doing pattern matching:
5838
- * https://git-scm.com/docs/gitignore
5839
- * https://github.com/kaelzhang/node-ignore
5840
- */
5841
- /** @module jsenv_url_meta **/
5842
-
5843
- /**
5844
- * An object representing the result of applying a pattern to an url
5845
- * @typedef {Object} MatchResult
5846
- * @property {boolean} matched Indicates if url matched pattern
5847
- * @property {number} patternIndex Index where pattern stopped matching url, otherwise pattern.length
5848
- * @property {number} urlIndex Index where url stopped matching pattern, otherwise url.length
5849
- * @property {Array} matchGroups Array of strings captured during pattern matching
5850
- */
5851
-
5852
- /**
5853
- * Apply a pattern to an url
5854
- * @param {Object} applyPatternMatchingParams
5855
- * @param {string} applyPatternMatchingParams.pattern "*", "**" and trailing slash have special meaning
5856
- * @param {string} applyPatternMatchingParams.url a string representing an url
5857
- * @return {MatchResult}
5858
- */
5859
-
5860
- const applyPatternMatching = ({
5861
- url,
5862
- pattern
5863
- }) => {
5864
- assertUrlLike(pattern, "pattern");
5865
- assertUrlLike(url, "url");
5866
- const {
5867
- matched,
5868
- patternIndex,
5869
- index,
5870
- groups
5871
- } = applyMatching(pattern, url);
5872
- const matchGroups = [];
5873
- let groupIndex = 0;
5874
- groups.forEach(group => {
5875
- if (group.name) {
5876
- matchGroups[group.name] = group.string;
5877
- } else {
5878
- matchGroups[groupIndex] = group.string;
5879
- groupIndex++;
5880
- }
5881
- });
5882
- return {
5883
- matched,
5884
- patternIndex,
5885
- urlIndex: index,
5886
- matchGroups
5887
- };
5888
- };
5889
-
5890
- const applyMatching = (pattern, string) => {
5891
- const groups = [];
5892
- let patternIndex = 0;
5893
- let index = 0;
5894
- let remainingPattern = pattern;
5895
- let remainingString = string;
5896
- let restoreIndexes = true;
5897
-
5898
- const consumePattern = count => {
5899
- const subpattern = remainingPattern.slice(0, count);
5900
- remainingPattern = remainingPattern.slice(count);
5901
- patternIndex += count;
5902
- return subpattern;
5903
- };
5904
-
5905
- const consumeString = count => {
5906
- const substring = remainingString.slice(0, count);
5907
- remainingString = remainingString.slice(count);
5908
- index += count;
5909
- return substring;
5910
- };
5911
-
5912
- const consumeRemainingString = () => {
5913
- return consumeString(remainingString.length);
5914
- };
5915
-
5916
- let matched;
5917
-
5918
- const iterate = () => {
5919
- const patternIndexBefore = patternIndex;
5920
- const indexBefore = index;
5921
- matched = matchOne();
5922
-
5923
- if (matched === undefined) {
5924
- consumePattern(1);
5925
- consumeString(1);
5926
- iterate();
5927
- return;
5928
- }
5929
-
5930
- if (matched === false && restoreIndexes) {
5931
- patternIndex = patternIndexBefore;
5932
- index = indexBefore;
5933
- }
5934
- };
5935
-
5936
- const matchOne = () => {
5937
- // pattern consumed and string consumed
5938
- if (remainingPattern === "" && remainingString === "") {
5939
- return true; // string fully matched pattern
5940
- } // pattern consumed, string not consumed
5941
-
5942
-
5943
- if (remainingPattern === "" && remainingString !== "") {
5944
- return false; // fails because string longer than expected
5945
- } // -- from this point pattern is not consumed --
5946
- // string consumed, pattern not consumed
5947
-
5948
-
5949
- if (remainingString === "") {
5950
- if (remainingPattern === "**") {
5951
- // trailing "**" is optional
5952
- consumePattern(2);
5953
- return true;
5954
- }
5955
-
5956
- if (remainingPattern === "*") {
5957
- groups.push({
5958
- string: ""
5959
- });
5960
- }
5961
-
5962
- return false; // fail because string shorter than expected
5963
- } // -- from this point pattern and string are not consumed --
5964
- // fast path trailing slash
5965
-
5966
-
5967
- if (remainingPattern === "/") {
5968
- if (remainingString[0] === "/") {
5969
- // trailing slash match remaining
5970
- consumePattern(1);
5971
- groups.push({
5972
- string: consumeRemainingString()
5973
- });
5974
- return true;
5975
- }
5976
-
5977
- return false;
5978
- } // fast path trailing '**'
5979
-
5980
-
5981
- if (remainingPattern === "**") {
5982
- consumePattern(2);
5983
- consumeRemainingString();
5984
- return true;
5985
- } // pattern leading **
5986
-
5987
-
5988
- if (remainingPattern.slice(0, 2) === "**") {
5989
- consumePattern(2); // consumes "**"
5990
-
5991
- if (remainingPattern[0] === "/") {
5992
- consumePattern(1); // consumes "/"
5993
- } // pattern ending with ** always match remaining string
5994
-
5995
-
5996
- if (remainingPattern === "") {
5997
- consumeRemainingString();
5998
- return true;
5999
- }
6000
-
6001
- const skipResult = skipUntilMatch({
6002
- pattern: remainingPattern,
6003
- string: remainingString,
6004
- canSkipSlash: true
6005
- });
6006
- groups.push(...skipResult.groups);
6007
- consumePattern(skipResult.patternIndex);
6008
- consumeRemainingString();
6009
- restoreIndexes = false;
6010
- return skipResult.matched;
6011
- }
6012
-
6013
- if (remainingPattern[0] === "*") {
6014
- consumePattern(1); // consumes "*"
6015
-
6016
- if (remainingPattern === "") {
6017
- // matches everything except '/'
6018
- const slashIndex = remainingString.indexOf("/");
6019
-
6020
- if (slashIndex === -1) {
6021
- groups.push({
6022
- string: consumeRemainingString()
6023
- });
6024
- return true;
6025
- }
6026
-
6027
- groups.push({
6028
- string: consumeString(slashIndex)
6029
- });
6030
- return false;
6031
- } // the next char must not the one expected by remainingPattern[0]
6032
- // because * is greedy and expect to skip at least one char
6033
-
6034
-
6035
- if (remainingPattern[0] === remainingString[0]) {
6036
- groups.push({
6037
- string: ""
6038
- });
6039
- patternIndex = patternIndex - 1;
6040
- return false;
6041
- }
6042
-
6043
- const skipResult = skipUntilMatch({
6044
- pattern: remainingPattern,
6045
- string: remainingString,
6046
- canSkipSlash: false
6047
- });
6048
- groups.push(skipResult.group, ...skipResult.groups);
6049
- consumePattern(skipResult.patternIndex);
6050
- consumeString(skipResult.index);
6051
- restoreIndexes = false;
6052
- return skipResult.matched;
6053
- }
6054
-
6055
- if (remainingPattern[0] !== remainingString[0]) {
6056
- return false;
6057
- }
6058
-
6059
- return undefined;
6060
- };
6061
-
6062
- iterate();
6063
- return {
6064
- matched,
6065
- patternIndex,
6066
- index,
6067
- groups
6068
- };
6069
- };
6070
-
6071
- const skipUntilMatch = ({
6072
- pattern,
6073
- string,
6074
- canSkipSlash
6075
- }) => {
6076
- let index = 0;
6077
- let remainingString = string;
6078
- let longestMatchRange = null;
6079
-
6080
- const tryToMatch = () => {
6081
- const matchAttempt = applyMatching(pattern, remainingString);
6082
-
6083
- if (matchAttempt.matched) {
6084
- return {
6085
- matched: true,
6086
- patternIndex: matchAttempt.patternIndex,
6087
- index: index + matchAttempt.index,
6088
- groups: matchAttempt.groups,
6089
- group: {
6090
- string: remainingString === "" ? string : string.slice(0, -remainingString.length)
6091
- }
6092
- };
6093
- }
6094
-
6095
- const matchAttemptIndex = matchAttempt.index;
6096
- const matchRange = {
6097
- patternIndex: matchAttempt.patternIndex,
6098
- index,
6099
- length: matchAttemptIndex,
6100
- groups: matchAttempt.groups
6101
- };
6102
-
6103
- if (!longestMatchRange || longestMatchRange.length < matchRange.length) {
6104
- longestMatchRange = matchRange;
6105
- }
6106
-
6107
- const nextIndex = matchAttemptIndex + 1;
6108
- const canSkip = nextIndex < remainingString.length && (canSkipSlash || remainingString[0] !== "/");
6109
-
6110
- if (canSkip) {
6111
- // search against the next unattempted string
6112
- index += nextIndex;
6113
- remainingString = remainingString.slice(nextIndex);
6114
- return tryToMatch();
6115
- }
6116
-
6117
- return {
6118
- matched: false,
6119
- patternIndex: longestMatchRange.patternIndex,
6120
- index: longestMatchRange.index + longestMatchRange.length,
6121
- groups: longestMatchRange.groups,
6122
- group: {
6123
- string: string.slice(0, longestMatchRange.index)
6124
- }
6125
- };
6126
- };
6127
-
6128
- return tryToMatch();
6129
- };
6130
-
6131
- const applyAssociations = ({
6132
- url,
6133
- associations
6134
- }) => {
6135
- assertUrlLike(url);
6136
- const flatAssociations = asFlatAssociations(associations);
6137
- return Object.keys(flatAssociations).reduce((previousValue, pattern) => {
6138
- const {
6139
- matched
6140
- } = applyPatternMatching({
6141
- pattern,
6142
- url
6143
- });
6144
-
6145
- if (matched) {
6146
- const value = flatAssociations[pattern];
6147
-
6148
- if (isPlainObject(previousValue) && isPlainObject(value)) {
6149
- return { ...previousValue,
6150
- ...value
6151
- };
6152
- }
6153
-
6154
- return value;
6155
- }
6156
-
6157
- return previousValue;
6158
- }, {});
6159
- };
6160
-
6161
- const applyAliases = ({
6162
- url,
6163
- aliases
6164
- }) => {
6165
- let aliasFullMatchResult;
6166
- const aliasMatchingKey = Object.keys(aliases).find(key => {
6167
- const aliasMatchResult = applyPatternMatching({
6168
- pattern: key,
6169
- url
6170
- });
6171
-
6172
- if (aliasMatchResult.matched) {
6173
- aliasFullMatchResult = aliasMatchResult;
6174
- return true;
6175
- }
6176
-
6177
- return false;
6178
- });
6179
-
6180
- if (!aliasMatchingKey) {
6181
- return url;
6182
- }
6183
-
6184
- const {
6185
- matchGroups
6186
- } = aliasFullMatchResult;
6187
- const alias = aliases[aliasMatchingKey];
6188
- const parts = alias.split("*");
6189
- const newUrl = parts.reduce((previous, value, index) => {
6190
- return `${previous}${value}${index === parts.length - 1 ? "" : matchGroups[index]}`;
6191
- }, "");
6192
- return newUrl;
6193
- };
6194
-
6195
- const urlChildMayMatch = ({
6196
- url,
6197
- associations,
6198
- predicate
6199
- }) => {
6200
- assertUrlLike(url, "url"); // the function was meants to be used on url ending with '/'
6201
-
6202
- if (!url.endsWith("/")) {
6203
- throw new Error(`url should end with /, got ${url}`);
6204
- }
6205
-
6206
- if (typeof predicate !== "function") {
6207
- throw new TypeError(`predicate must be a function, got ${predicate}`);
6208
- }
6209
-
6210
- const flatAssociations = asFlatAssociations(associations); // for full match we must create an object to allow pattern to override previous ones
6211
-
6212
- let fullMatchMeta = {};
6213
- let someFullMatch = false; // for partial match, any meta satisfying predicate will be valid because
6214
- // we don't know for sure if pattern will still match for a file inside pathname
6215
-
6216
- const partialMatchMetaArray = [];
6217
- Object.keys(flatAssociations).forEach(pattern => {
6218
- const value = flatAssociations[pattern];
6219
- const matchResult = applyPatternMatching({
6220
- pattern,
6221
- url
6222
- });
6223
-
6224
- if (matchResult.matched) {
6225
- someFullMatch = true;
6226
-
6227
- if (isPlainObject(fullMatchMeta) && isPlainObject(value)) {
6228
- fullMatchMeta = { ...fullMatchMeta,
6229
- ...value
6230
- };
6231
- } else {
6232
- fullMatchMeta = value;
6233
- }
6234
- } else if (someFullMatch === false && matchResult.urlIndex >= url.length) {
6235
- partialMatchMetaArray.push(value);
6236
- }
6237
- });
6238
-
6239
- if (someFullMatch) {
6240
- return Boolean(predicate(fullMatchMeta));
6241
- }
6242
-
6243
- return partialMatchMetaArray.some(partialMatchMeta => predicate(partialMatchMeta));
6244
- };
6245
-
6246
- const URL_META = {
6247
- resolveAssociations,
6248
- applyAssociations,
6249
- urlChildMayMatch,
6250
- applyPatternMatching,
6251
- applyAliases
6252
- };
6253
-
6254
5745
  const pluginRequestWaitingCheck = ({
6255
5746
  requestWaitingMs = 20000,
6256
5747
  requestWaitingCallback = ({
@@ -10071,7 +9562,10 @@ const jsenvPluginUrlResolution = () => {
10071
9562
  name: "jsenv:url_resolution",
10072
9563
  appliesDuring: "*",
10073
9564
  resolveUrl: {
9565
+ "http_request": urlResolver,
9566
+ // during dev
10074
9567
  "entry_point": urlResolver,
9568
+ // during build
10075
9569
  "link_href": urlResolver,
10076
9570
  "script_src": urlResolver,
10077
9571
  "a_href": urlResolver,
@@ -18026,16 +17520,16 @@ const jsenvPluginAsJsClassicConversion = ({
18026
17520
  return null;
18027
17521
  }
18028
17522
 
18029
- const isJsEntryPoint = // in general html files are entry points
18030
- // but during build js can be sepcified as an entry point
18031
- // (meaning there is no html file where we can inject systemjs)
18032
- // in that case we need to inject systemjs in the js file
18033
- originalUrlInfo.data.isEntryPoint || // In thoose case we need to inject systemjs the worker js file
18034
- originalUrlInfo.data.isWebWorkerEntryPoint; // if it's an entry point without dependency (it does not use import)
17523
+ const jsClassicFormat = // in general html file are entry points, but js can be entry point when:
17524
+ // - passed in entryPoints to build
17525
+ // - is used by web worker
17526
+ // - the reference contains ?entry_point
17527
+ // When js is entry point there can be no HTML to inject systemjs
17528
+ // and systemjs must be injected into the js file
17529
+ originalUrlInfo.isEntryPoint && // if it's an entry point without dependency (it does not use import)
18035
17530
  // then we can use UMD, otherwise we have to use systemjs
18036
17531
  // because it is imported by systemjs
18037
-
18038
- const jsClassicFormat = isJsEntryPoint && !originalUrlInfo.data.usesImport ? "umd" : "system";
17532
+ !originalUrlInfo.data.usesImport ? "umd" : "system";
18039
17533
  const {
18040
17534
  content,
18041
17535
  sourcemap
@@ -18043,7 +17537,6 @@ const jsenvPluginAsJsClassicConversion = ({
18043
17537
  systemJsInjection,
18044
17538
  systemJsClientFileUrl,
18045
17539
  urlInfo: originalUrlInfo,
18046
- isJsEntryPoint,
18047
17540
  jsClassicFormat
18048
17541
  });
18049
17542
  urlInfo.data.jsClassicFormat = jsClassicFormat;
@@ -18087,7 +17580,6 @@ const convertJsModuleToJsClassic = async ({
18087
17580
  systemJsInjection,
18088
17581
  systemJsClientFileUrl,
18089
17582
  urlInfo,
18090
- isJsEntryPoint,
18091
17583
  jsClassicFormat
18092
17584
  }) => {
18093
17585
  const {
@@ -18106,7 +17598,7 @@ const convertJsModuleToJsClassic = async ({
18106
17598
  let sourcemap = urlInfo.sourcemap;
18107
17599
  sourcemap = await composeTwoSourcemaps(sourcemap, map);
18108
17600
 
18109
- if (systemJsInjection && jsClassicFormat === "system" && isJsEntryPoint) {
17601
+ if (systemJsInjection && jsClassicFormat === "system" && urlInfo.isEntryPoint) {
18110
17602
  const magicSource = createMagicSource(code);
18111
17603
  const systemjsCode = readFileSync(systemJsClientFileUrl, {
18112
17604
  as: "string"
@@ -19800,7 +19292,7 @@ const rollupPluginJsenv = ({
19800
19292
  jsModuleUrlInfos.forEach(jsModuleUrlInfo => {
19801
19293
  const id = jsModuleUrlInfo.url;
19802
19294
 
19803
- if (jsModuleUrlInfo.data.isEntryPoint) {
19295
+ if (jsModuleUrlInfo.isEntryPoint) {
19804
19296
  emitChunk({
19805
19297
  id
19806
19298
  });
@@ -20083,7 +19575,7 @@ const willBeInsideJsDirectory = ({
20083
19575
  return true;
20084
19576
  }
20085
19577
 
20086
- if (!jsModuleUrlInfo.data.isEntryPoint && !jsModuleUrlInfo.data.isWebWorkerEntryPoint) {
19578
+ if (!jsModuleUrlInfo.isEntryPoint) {
20087
19579
  // not an entry point, jsenv will put it inside js/ directory
20088
19580
  return true;
20089
19581
  }
@@ -21443,6 +20935,7 @@ const createUrlInfo = url => {
21443
20935
  originalUrl: undefined,
21444
20936
  generatedUrl: null,
21445
20937
  filename: "",
20938
+ isEntryPoint: false,
21446
20939
  isInline: false,
21447
20940
  inlineUrlSite: null,
21448
20941
  shouldHandle: undefined,
@@ -22374,6 +21867,7 @@ const createKitchen = ({
22374
21867
  baseUrl,
22375
21868
  isOriginalPosition,
22376
21869
  shouldHandle,
21870
+ isEntryPoint = false,
22377
21871
  isInline = false,
22378
21872
  injected = false,
22379
21873
  isRessourceHint = false,
@@ -22411,6 +21905,7 @@ const createKitchen = ({
22411
21905
  baseUrl,
22412
21906
  isOriginalPosition,
22413
21907
  shouldHandle,
21908
+ isEntryPoint,
22414
21909
  isInline,
22415
21910
  injected,
22416
21911
  isRessourceHint,
@@ -22427,7 +21922,7 @@ const createKitchen = ({
22427
21922
  const mutateReference = (reference, newReference) => {
22428
21923
  reference.next = newReference;
22429
21924
  newReference.prev = reference;
22430
- newReference.original = reference.original || reference;
21925
+ newReference.original = reference.original || reference; // newReference.isEntryPoint = reference.isEntryPoint
22431
21926
  };
22432
21927
 
22433
21928
  const resolveReference = reference => {
@@ -22452,11 +21947,16 @@ const createKitchen = ({
22452
21947
  reference.url = normalizedReturnValue;
22453
21948
  mutateReference(previousReference, reference);
22454
21949
  });
22455
- const urlInfo = urlGraph.reuseOrCreateUrlInfo(reference.url);
22456
- applyReferenceEffectsOnUrlInfo(reference, urlInfo, kitchenContext);
22457
21950
  const referenceUrlObject = new URL(reference.url);
22458
21951
  reference.searchParams = referenceUrlObject.searchParams;
22459
- reference.generatedUrl = reference.url; // This hook must touch reference.generatedUrl, NOT reference.url
21952
+ reference.generatedUrl = reference.url;
21953
+
21954
+ if (reference.searchParams.has("entry_point")) {
21955
+ reference.isEntryPoint = true;
21956
+ }
21957
+
21958
+ const urlInfo = urlGraph.reuseOrCreateUrlInfo(reference.url);
21959
+ applyReferenceEffectsOnUrlInfo(reference, urlInfo, kitchenContext); // This hook must touch reference.generatedUrl, NOT reference.url
22460
21960
  // And this is because this hook inject query params used to:
22461
21961
  // - bypass browser cache (?v)
22462
21962
  // - convey information (?hmr)
@@ -22557,7 +22057,8 @@ const createKitchen = ({
22557
22057
  filename,
22558
22058
  status = 200,
22559
22059
  headers = {},
22560
- body
22060
+ body,
22061
+ isEntryPoint
22561
22062
  } = fetchUrlContentReturnValue;
22562
22063
 
22563
22064
  if (status !== 200) {
@@ -22593,6 +22094,10 @@ const createKitchen = ({
22593
22094
  Object.assign(urlInfo.data, data);
22594
22095
  }
22595
22096
 
22097
+ if (typeof isEntryPoint === "boolean") {
22098
+ urlInfo.isEntryPoint = isEntryPoint;
22099
+ }
22100
+
22596
22101
  if (filename) {
22597
22102
  urlInfo.filename = filename;
22598
22103
  }
@@ -22895,6 +22400,7 @@ const createKitchen = ({
22895
22400
 
22896
22401
  const prepareEntryPoint = params => {
22897
22402
  const entryReference = createReference(params);
22403
+ entryReference.isEntryPoint = true;
22898
22404
  const entryUrlInfo = resolveReference(entryReference);
22899
22405
  return [entryReference, entryUrlInfo];
22900
22406
  };
@@ -23002,6 +22508,11 @@ const applyReferenceEffectsOnUrlInfo = (reference, urlInfo, context) => {
23002
22508
  }
23003
22509
 
23004
22510
  urlInfo.originalUrl = urlInfo.originalUrl || reference.url;
22511
+
22512
+ if (reference.isEntryPoint || isWebWorkerEntryPointReference(reference)) {
22513
+ urlInfo.isEntryPoint = true;
22514
+ }
22515
+
23005
22516
  Object.assign(urlInfo.data, reference.data);
23006
22517
  Object.assign(urlInfo.timing, reference.timing);
23007
22518
 
@@ -23026,10 +22537,6 @@ const applyReferenceEffectsOnUrlInfo = (reference, urlInfo, context) => {
23026
22537
  urlInfo.originalContent = context === "build" ? urlInfo.originalContent === undefined ? reference.content : urlInfo.originalContent : reference.content;
23027
22538
  urlInfo.content = reference.content;
23028
22539
  }
23029
-
23030
- if (isWebWorkerEntryPointReference(reference)) {
23031
- urlInfo.data.isWebWorkerEntryPoint = true;
23032
- }
23033
22540
  };
23034
22541
 
23035
22542
  const adjustUrlSite = (urlInfo, {
@@ -23312,10 +22819,10 @@ const createFileService = ({
23312
22819
  }
23313
22820
 
23314
22821
  if (!reference) {
23315
- const entryPoint = kitchen.prepareEntryPoint({
22822
+ const entryPoint = kitchen.injectReference({
23316
22823
  trace: parentUrl || rootDirectoryUrl,
23317
22824
  parentUrl: parentUrl || rootDirectoryUrl,
23318
- type: "entry_point",
22825
+ type: "http_request",
23319
22826
  specifier: request.ressource
23320
22827
  });
23321
22828
  reference = entryPoint[0];
@@ -24068,23 +23575,31 @@ const babelPluginInstrument = (api, {
24068
23575
  };
24069
23576
  };
24070
23577
 
24071
- const visitNodeV8Directory = async ({
23578
+ const readNodeV8CoverageDirectory = async ({
24072
23579
  logger,
24073
23580
  signal,
24074
- NODE_V8_COVERAGE,
24075
23581
  onV8Coverage,
24076
23582
  maxMsWaitingForNodeToWriteCoverageFile = 2000
24077
23583
  }) => {
23584
+ const NODE_V8_COVERAGE = process.env.NODE_V8_COVERAGE;
24078
23585
  const operation = Abort.startOperation();
24079
23586
  operation.addAbortSignal(signal);
23587
+ let timeSpentTrying = 0;
24080
23588
 
24081
23589
  const tryReadDirectory = async () => {
24082
- const dirContent = await readDirectory(NODE_V8_COVERAGE);
23590
+ const dirContent = readdirSync(NODE_V8_COVERAGE);
24083
23591
 
24084
23592
  if (dirContent.length > 0) {
24085
23593
  return dirContent;
24086
23594
  }
24087
23595
 
23596
+ if (timeSpentTrying < maxMsWaitingForNodeToWriteCoverageFile) {
23597
+ await new Promise(resolve => setTimeout(resolve, 200));
23598
+ timeSpentTrying += 200;
23599
+ logger.debug("retry to read coverage directory");
23600
+ return tryReadDirectory();
23601
+ }
23602
+
24088
23603
  logger.warn(`v8 coverage directory is empty at ${NODE_V8_COVERAGE}`);
24089
23604
  return dirContent;
24090
23605
  };
@@ -24096,17 +23611,16 @@ const visitNodeV8Directory = async ({
24096
23611
  await dirContent.reduce(async (previous, dirEntry) => {
24097
23612
  operation.throwIfAborted();
24098
23613
  await previous;
24099
- const dirEntryUrl = resolveUrl$1(dirEntry, coverageDirectoryUrl);
23614
+ const dirEntryUrl = new URL(dirEntry, coverageDirectoryUrl);
24100
23615
 
24101
- const tryReadJsonFile = async (timeSpentTrying = 0) => {
24102
- const fileContent = await readFile(dirEntryUrl, {
24103
- as: "string"
24104
- });
23616
+ const tryReadJsonFile = async () => {
23617
+ const fileContent = String(readFileSync$1(dirEntryUrl));
24105
23618
 
24106
23619
  if (fileContent === "") {
24107
- if (timeSpentTrying < 400) {
23620
+ if (timeSpentTrying < maxMsWaitingForNodeToWriteCoverageFile) {
24108
23621
  await new Promise(resolve => setTimeout(resolve, 200));
24109
- return tryReadJsonFile(timeSpentTrying + 200);
23622
+ timeSpentTrying += 200;
23623
+ return tryReadJsonFile();
24110
23624
  }
24111
23625
 
24112
23626
  console.warn(`Coverage JSON file is empty at ${dirEntryUrl}`);
@@ -24119,7 +23633,8 @@ const visitNodeV8Directory = async ({
24119
23633
  } catch (e) {
24120
23634
  if (timeSpentTrying < maxMsWaitingForNodeToWriteCoverageFile) {
24121
23635
  await new Promise(resolve => setTimeout(resolve, 200));
24122
- return tryReadJsonFile(timeSpentTrying + 200);
23636
+ timeSpentTrying += 200;
23637
+ return tryReadJsonFile();
24123
23638
  }
24124
23639
 
24125
23640
  console.warn(createDetailedMessage$1(`Error while reading coverage file`, {
@@ -24133,21 +23648,13 @@ const visitNodeV8Directory = async ({
24133
23648
  const fileContent = await tryReadJsonFile();
24134
23649
 
24135
23650
  if (fileContent) {
24136
- onV8Coverage(fileContent);
23651
+ await onV8Coverage(fileContent);
24137
23652
  }
24138
23653
  }, Promise.resolve());
24139
23654
  } finally {
24140
23655
  await operation.end();
24141
23656
  }
24142
23657
  };
24143
- const filterV8Coverage = (v8Coverage, {
24144
- urlShouldBeCovered
24145
- }) => {
24146
- const v8CoverageFiltered = { ...v8Coverage,
24147
- result: v8Coverage.result.filter(fileReport => urlShouldBeCovered(fileReport.url))
24148
- };
24149
- return v8CoverageFiltered;
24150
- };
24151
23658
 
24152
23659
  const composeTwoV8Coverages = (firstV8Coverage, secondV8Coverage) => {
24153
23660
  if (secondV8Coverage.result.length === 0) {
@@ -24448,8 +23955,7 @@ const reportToCoverage = async (report, {
24448
23955
  rootDirectoryUrl,
24449
23956
  coverageConfig,
24450
23957
  coverageIncludeMissing,
24451
- urlShouldBeCovered,
24452
- coverageForceIstanbul,
23958
+ coverageMethodForNodeJs,
24453
23959
  coverageV8ConflictWarning
24454
23960
  }) => {
24455
23961
  // collect v8 and istanbul coverage from executions
@@ -24478,20 +23984,20 @@ const reportToCoverage = async (report, {
24478
23984
  // in any scenario we are fine because
24479
23985
  // coverDescription will generate empty coverage for files
24480
23986
  // that were suppose to be coverage but were not.
24481
- if (executionResult.status === "completed" && executionResult.runtimeName !== "node" && !process.env.NODE_V8_COVERAGE) {
24482
- logger.warn(`No execution.coverageFileUrl from execution named "${executionName}" of ${file}`);
23987
+ if (executionResult.status === "completed" && executionResult.type === "node" && coverageMethodForNodeJs !== "NODE_V8_COVERAGE") {
23988
+ logger.warn(`No "coverageFileUrl" from execution named "${executionName}" of ${file}`);
24483
23989
  }
24484
23990
  }
24485
23991
  });
24486
23992
 
24487
- if (!coverageForceIstanbul && process.env.NODE_V8_COVERAGE) {
24488
- await visitNodeV8Directory({
23993
+ if (coverageMethodForNodeJs === "NODE_V8_COVERAGE") {
23994
+ await readNodeV8CoverageDirectory({
24489
23995
  logger,
24490
23996
  signal,
24491
- NODE_V8_COVERAGE: process.env.NODE_V8_COVERAGE,
24492
- onV8Coverage: nodeV8Coverage => {
24493
- const nodeV8CoverageLight = filterV8Coverage(nodeV8Coverage, {
24494
- urlShouldBeCovered
23997
+ onV8Coverage: async nodeV8Coverage => {
23998
+ const nodeV8CoverageLight = await filterV8Coverage(nodeV8Coverage, {
23999
+ rootDirectoryUrl,
24000
+ coverageConfig
24495
24001
  });
24496
24002
  v8Coverage = v8Coverage ? composeTwoV8Coverages(v8Coverage, nodeV8CoverageLight) : nodeV8CoverageLight;
24497
24003
  }
@@ -24570,9 +24076,7 @@ const getCoverageFromReport = async ({
24570
24076
  return;
24571
24077
  }
24572
24078
 
24573
- const executionCoverage = await readFile(coverageFileUrl, {
24574
- as: "json"
24575
- });
24079
+ const executionCoverage = JSON.parse(String(readFileSync$1(new URL(coverageFileUrl))));
24576
24080
 
24577
24081
  if (isV8Coverage(executionCoverage)) {
24578
24082
  v8Coverage = v8Coverage ? composeTwoV8Coverages(v8Coverage, executionCoverage) : executionCoverage;
@@ -24599,21 +24103,20 @@ const run = async ({
24599
24103
  keepRunning = false,
24600
24104
  mirrorConsole = false,
24601
24105
  collectConsole = false,
24602
- collectCoverage = false,
24106
+ coverageEnabled = false,
24603
24107
  coverageTempDirectoryUrl,
24604
24108
  collectPerformance = false,
24605
24109
  runtime,
24606
24110
  runtimeParams
24607
24111
  }) => {
24112
+ let result = {};
24113
+ const callbacks = [];
24608
24114
  const onConsoleRef = {
24609
24115
  current: () => {}
24610
24116
  };
24611
24117
  const stopSignal = {
24612
24118
  notify: () => {}
24613
24119
  };
24614
-
24615
- let resultTransformer = result => result;
24616
-
24617
24120
  const runtimeLabel = `${runtime.name}/${runtime.version}`;
24618
24121
  const runOperation = Abort.startOperation();
24619
24122
  runOperation.addAbortSignal(signal);
@@ -24622,21 +24125,21 @@ const run = async ({
24622
24125
  // when keepRunning is true
24623
24126
  !keepRunning && typeof allocatedMs === "number" && allocatedMs !== Infinity) {
24624
24127
  const timeoutAbortSource = runOperation.timeout(allocatedMs);
24625
- resultTransformer = composeTransformer$1(resultTransformer, result => {
24128
+ callbacks.push(() => {
24626
24129
  if (result.status === "errored" && Abort.isAbortError(result.error) && timeoutAbortSource.signal.aborted) {
24627
- return createTimedoutResult();
24130
+ result = {
24131
+ status: "timedout"
24132
+ };
24628
24133
  }
24629
-
24630
- return result;
24631
24134
  });
24632
24135
  }
24633
24136
 
24634
- resultTransformer = composeTransformer$1(resultTransformer, result => {
24137
+ callbacks.push(() => {
24635
24138
  if (result.status === "errored" && Abort.isAbortError(result.error)) {
24636
- return createAbortedResult();
24139
+ result = {
24140
+ status: "aborted"
24141
+ };
24637
24142
  }
24638
-
24639
- return result;
24640
24143
  });
24641
24144
  const consoleCalls = [];
24642
24145
 
@@ -24661,44 +24164,14 @@ const run = async ({
24661
24164
  };
24662
24165
 
24663
24166
  if (collectConsole) {
24664
- resultTransformer = composeTransformer$1(resultTransformer, result => {
24167
+ callbacks.push(() => {
24665
24168
  result.consoleCalls = consoleCalls;
24666
- return result;
24667
- });
24668
- }
24669
-
24670
- if (collectCoverage) {
24671
- resultTransformer = composeTransformer$1(resultTransformer, async result => {
24672
- // we do not keep coverage in memory, it can grow very big
24673
- // instead we store it on the filesystem
24674
- // and they can be read later at "coverageFileUrl"
24675
- const {
24676
- coverage
24677
- } = result;
24678
-
24679
- if (coverage) {
24680
- const coverageFileUrl = resolveUrl$1(`./${runtime.name}/${cuid()}`, coverageTempDirectoryUrl);
24681
- await writeFile(coverageFileUrl, JSON.stringify(coverage, null, " "));
24682
- result.coverageFileUrl = coverageFileUrl;
24683
- delete result.coverage;
24684
- }
24685
-
24686
- return result;
24687
- });
24688
- } else {
24689
- resultTransformer = composeTransformer$1(resultTransformer, result => {
24690
- // as collectCoverage is disabled
24691
- // executionResult.coverage is undefined or {}
24692
- // we delete it just to have a cleaner object
24693
- delete result.coverage;
24694
- return result;
24695
24169
  });
24696
24170
  }
24697
24171
 
24698
24172
  const startMs = Date.now();
24699
- resultTransformer = composeTransformer$1(resultTransformer, result => {
24173
+ callbacks.push(() => {
24700
24174
  result.duration = Date.now() - startMs;
24701
- return result;
24702
24175
  });
24703
24176
 
24704
24177
  try {
@@ -24714,7 +24187,7 @@ const run = async ({
24714
24187
  },
24715
24188
  runned: async cb => {
24716
24189
  try {
24717
- const result = await runtime.run({
24190
+ const runResult = await runtime.run({
24718
24191
  signal: runOperation.signal,
24719
24192
  logger,
24720
24193
  ...runtimeParams,
@@ -24723,7 +24196,7 @@ const run = async ({
24723
24196
  stopSignal,
24724
24197
  onConsole: log => onConsoleRef.current(log)
24725
24198
  });
24726
- cb(result);
24199
+ cb(runResult);
24727
24200
  } catch (e) {
24728
24201
  cb({
24729
24202
  status: "errored",
@@ -24739,40 +24212,55 @@ const run = async ({
24739
24212
  runOperation.throwIfAborted();
24740
24213
  }
24741
24214
 
24742
- let result = winner.data;
24743
- result = await resultTransformer(result);
24215
+ const {
24216
+ status,
24217
+ namespace,
24218
+ error,
24219
+ performance,
24220
+ coverage
24221
+ } = winner.data;
24222
+ result.status = status;
24223
+
24224
+ if (status === "errored") {
24225
+ result.error = error;
24226
+ } else {
24227
+ result.namespace = namespace;
24228
+ }
24229
+
24230
+ if (collectPerformance) {
24231
+ result.performance = performance;
24232
+ }
24233
+
24234
+ if (coverageEnabled) {
24235
+ if (coverage) {
24236
+ // we do not keep coverage in memory, it can grow very big
24237
+ // instead we store it on the filesystem
24238
+ // and they can be read later at "coverageFileUrl"
24239
+ const coverageFileUrl = new URL(`./${runtime.name}/${cuid()}.json`, coverageTempDirectoryUrl);
24240
+ writeFileSync(coverageFileUrl, JSON.stringify(coverage, null, " "));
24241
+ result.coverageFileUrl = coverageFileUrl.href;
24242
+ } else {// will eventually log a warning in report_to_coverage.js
24243
+ }
24244
+ }
24245
+
24246
+ callbacks.forEach(callback => {
24247
+ callback();
24248
+ });
24744
24249
  return result;
24745
24250
  } catch (e) {
24746
- let result = {
24251
+ result = {
24747
24252
  status: "errored",
24748
24253
  error: e
24749
24254
  };
24750
- result = await resultTransformer(result);
24255
+ callbacks.forEach(callback => {
24256
+ callback();
24257
+ });
24751
24258
  return result;
24752
24259
  } finally {
24753
24260
  await runOperation.end();
24754
24261
  }
24755
24262
  };
24756
24263
 
24757
- const composeTransformer$1 = (previousTransformer, transformer) => {
24758
- return async value => {
24759
- const transformedValue = await previousTransformer(value);
24760
- return transformer(transformedValue);
24761
- };
24762
- };
24763
-
24764
- const createAbortedResult = () => {
24765
- return {
24766
- status: "aborted"
24767
- };
24768
- };
24769
-
24770
- const createTimedoutResult = () => {
24771
- return {
24772
- status: "timedout"
24773
- };
24774
- };
24775
-
24776
24264
  const ensureGlobalGc = () => {
24777
24265
  if (!global.gc) {
24778
24266
  v8.setFlagsFromString("--expose_gc");
@@ -25176,10 +24664,11 @@ const executePlan = async (plan, {
25176
24664
  failFast,
25177
24665
  gcBetweenExecutions,
25178
24666
  cooldownBetweenExecutions,
25179
- coverage,
24667
+ coverageEnabled,
25180
24668
  coverageConfig,
25181
24669
  coverageIncludeMissing,
25182
- coverageForceIstanbul,
24670
+ coverageMethodForBrowsers,
24671
+ coverageMethodForNodeJs,
25183
24672
  coverageV8ConflictWarning,
25184
24673
  coverageTempDirectoryRelativeUrl,
25185
24674
  scenario,
@@ -25197,10 +24686,14 @@ const executePlan = async (plan, {
25197
24686
  beforeExecutionCallback = () => {},
25198
24687
  afterExecutionCallback = () => {}
25199
24688
  } = {}) => {
24689
+ const executePlanReturnValue = {};
24690
+ const report = {};
24691
+ const callbacks = [];
25200
24692
  const stopAfterAllSignal = {
25201
24693
  notify: () => {}
25202
24694
  };
25203
24695
  let someNeedsServer = false;
24696
+ let someNodeRuntime = false;
25204
24697
  const runtimes = {};
25205
24698
  Object.keys(plan).forEach(filePattern => {
25206
24699
  const filePlan = plan[filePattern];
@@ -25216,6 +24709,10 @@ const executePlan = async (plan, {
25216
24709
  if (runtime.needsServer) {
25217
24710
  someNeedsServer = true;
25218
24711
  }
24712
+
24713
+ if (runtime.type === "node") {
24714
+ someNodeRuntime = true;
24715
+ }
25219
24716
  }
25220
24717
  });
25221
24718
  });
@@ -25243,10 +24740,67 @@ const executePlan = async (plan, {
25243
24740
  }
25244
24741
 
25245
24742
  try {
24743
+ const coverageTempDirectoryUrl = new URL(coverageTempDirectoryRelativeUrl, rootDirectoryUrl).href;
24744
+
24745
+ if (someNodeRuntime && coverageEnabled && coverageMethodForNodeJs === "NODE_V8_COVERAGE") {
24746
+ if (process.env.NODE_V8_COVERAGE) {
24747
+ // when runned multiple times, we don't want to keep previous files in this directory
24748
+ await ensureEmptyDirectory(process.env.NODE_V8_COVERAGE);
24749
+ } else {
24750
+ coverageMethodForNodeJs = "Profiler";
24751
+ logger.warn(createDetailedMessage$1(`process.env.NODE_V8_COVERAGE is required to generate coverage for Node.js subprocesses`, {
24752
+ "suggestion": `Preprend NODE_V8_COVERAGE=.coverage/node to the command executing this process`,
24753
+ "suggestion 2": `use coverageMethodForNodeJs: "Profiler". But it means coverage for child_process and worker_thread cannot be collected`
24754
+ }));
24755
+ }
24756
+ }
24757
+
24758
+ if (gcBetweenExecutions) {
24759
+ ensureGlobalGc();
24760
+ }
24761
+
24762
+ if (coverageEnabled) {
24763
+ // when runned multiple times, we don't want to keep previous files in this directory
24764
+ await ensureEmptyDirectory(coverageTempDirectoryUrl);
24765
+ callbacks.push(async () => {
24766
+ if (multipleExecutionsOperation.signal.aborted) {
24767
+ // don't try to do the coverage stuff
24768
+ return;
24769
+ }
24770
+
24771
+ try {
24772
+ if (coverageMethodForNodeJs === "NODE_V8_COVERAGE") {
24773
+ takeCoverage(); // conceptually we don't need coverage anymore so it would be
24774
+ // good to call v8.stopCoverage()
24775
+ // but it logs a strange message about "result is not an object"
24776
+ }
24777
+
24778
+ const planCoverage = await reportToCoverage(report, {
24779
+ signal: multipleExecutionsOperation.signal,
24780
+ logger,
24781
+ rootDirectoryUrl,
24782
+ coverageConfig,
24783
+ coverageIncludeMissing,
24784
+ coverageMethodForBrowsers,
24785
+ coverageV8ConflictWarning
24786
+ });
24787
+ executePlanReturnValue.planCoverage = planCoverage;
24788
+ } catch (e) {
24789
+ if (Abort.isAbortError(e)) {
24790
+ return;
24791
+ }
24792
+
24793
+ throw e;
24794
+ }
24795
+ });
24796
+ }
24797
+
25246
24798
  let runtimeParams = {
25247
24799
  rootDirectoryUrl,
25248
- collectCoverage: coverage,
25249
- coverageForceIstanbul,
24800
+ coverageEnabled,
24801
+ coverageConfig,
24802
+ coverageMethodForBrowsers,
24803
+ coverageMethodForNodeJs,
25250
24804
  stopAfterAllSignal
25251
24805
  };
25252
24806
 
@@ -25273,7 +24827,7 @@ const executePlan = async (plan, {
25273
24827
  getCustomBabelPlugins: ({
25274
24828
  clientRuntimeCompat
25275
24829
  }) => {
25276
- if (coverage && Object.keys(clientRuntimeCompat)[0] !== "chrome") {
24830
+ if (coverageEnabled && Object.keys(clientRuntimeCompat)[0] !== "chrome") {
25277
24831
  return {
25278
24832
  "transform-instrument": [babelPluginInstrument, {
25279
24833
  rootDirectoryUrl,
@@ -25329,79 +24883,7 @@ const executePlan = async (plan, {
25329
24883
  // so we enable spinner only when !process.exitCode (no error so far)
25330
24884
  process.exitCode !== 1;
25331
24885
  const startMs = Date.now();
25332
- const report = {};
25333
24886
  let rawOutput = "";
25334
-
25335
- let transformReturnValue = value => value;
25336
-
25337
- if (gcBetweenExecutions) {
25338
- ensureGlobalGc();
25339
- }
25340
-
25341
- const coverageTempDirectoryUrl = new URL(coverageTempDirectoryRelativeUrl, rootDirectoryUrl).href;
25342
-
25343
- if (coverage) {
25344
- const associations = URL_META.resolveAssociations({
25345
- cover: coverageConfig
25346
- }, rootDirectoryUrl);
25347
-
25348
- const urlShouldBeCovered = url => {
25349
- const {
25350
- cover
25351
- } = URL_META.applyAssociations({
25352
- url: new URL(url, rootDirectoryUrl).href,
25353
- associations
25354
- });
25355
- return cover;
25356
- };
25357
-
25358
- runtimeParams.urlShouldBeCovered = urlShouldBeCovered; // in case runned multiple times, we don't want to keep writing lot of files in this directory
25359
-
25360
- if (!process.env.NODE_V8_COVERAGE) {
25361
- await ensureEmptyDirectory(coverageTempDirectoryUrl);
25362
- }
25363
-
25364
- if (runtimes.node) {
25365
- // v8 coverage is written in a directoy and auto propagate to subprocesses
25366
- // through process.env.NODE_V8_COVERAGE.
25367
- if (!coverageForceIstanbul && !process.env.NODE_V8_COVERAGE) {
25368
- const v8CoverageDirectory = new URL(`./node_v8/${cuid()}`, coverageTempDirectoryUrl).href;
25369
- await writeDirectory(v8CoverageDirectory, {
25370
- allowUseless: true
25371
- });
25372
- process.env.NODE_V8_COVERAGE = urlToFileSystemPath(v8CoverageDirectory);
25373
- }
25374
- }
25375
-
25376
- transformReturnValue = async value => {
25377
- if (multipleExecutionsOperation.signal.aborted) {
25378
- // don't try to do the coverage stuff
25379
- return value;
25380
- }
25381
-
25382
- try {
25383
- value.coverage = await reportToCoverage(value.report, {
25384
- signal: multipleExecutionsOperation.signal,
25385
- logger,
25386
- rootDirectoryUrl,
25387
- coverageConfig,
25388
- coverageIncludeMissing,
25389
- coverageForceIstanbul,
25390
- urlShouldBeCovered,
25391
- coverageV8ConflictWarning
25392
- });
25393
- } catch (e) {
25394
- if (Abort.isAbortError(e)) {
25395
- return value;
25396
- }
25397
-
25398
- throw e;
25399
- }
25400
-
25401
- return value;
25402
- };
25403
- }
25404
-
25405
24887
  logger.info("");
25406
24888
  let executionLog = createLog({
25407
24889
  newLine: ""
@@ -25426,6 +24908,7 @@ const executePlan = async (plan, {
25426
24908
  fileRelativeUrl,
25427
24909
  runtime
25428
24910
  } = paramsFromStep;
24911
+ const runtimeType = runtime.type;
25429
24912
  const runtimeName = runtime.name;
25430
24913
  const runtimeVersion = runtime.version;
25431
24914
  const executionParams = {
@@ -25441,6 +24924,7 @@ const executePlan = async (plan, {
25441
24924
  };
25442
24925
  const beforeExecutionInfo = {
25443
24926
  fileRelativeUrl,
24927
+ runtimeType,
25444
24928
  runtimeName,
25445
24929
  runtimeVersion,
25446
24930
  executionIndex,
@@ -25484,7 +24968,7 @@ const executePlan = async (plan, {
25484
24968
  mirrorConsole: false,
25485
24969
  // file are executed in parallel, log would be a mess to read
25486
24970
  collectConsole: executionParams.collectConsole,
25487
- collectCoverage: coverage,
24971
+ coverageEnabled,
25488
24972
  coverageTempDirectoryUrl,
25489
24973
  runtime: executionParams.runtime,
25490
24974
  runtimeParams: { ...runtimeParams,
@@ -25602,16 +25086,14 @@ const executePlan = async (plan, {
25602
25086
  logger.info(`-> ${urlToFileSystemPath(logFileUrl)}`);
25603
25087
  }
25604
25088
 
25605
- const result = await transformReturnValue({
25606
- summary,
25607
- report
25608
- });
25609
- return {
25610
- aborted: multipleExecutionsOperation.signal.aborted,
25611
- planSummary: result.summary,
25612
- planReport: result.report,
25613
- planCoverage: result.coverage
25614
- };
25089
+ executePlanReturnValue.aborted = multipleExecutionsOperation.signal.aborted;
25090
+ executePlanReturnValue.planSummary = summary;
25091
+ executePlanReturnValue.planReport = report;
25092
+ await callbacks.reduce(async (previous, callback) => {
25093
+ await previous;
25094
+ await callback();
25095
+ }, Promise.resolve());
25096
+ return executePlanReturnValue;
25615
25097
  } finally {
25616
25098
  await multipleExecutionsOperation.end();
25617
25099
  }
@@ -25732,7 +25214,7 @@ const executeInParallel = async ({
25732
25214
  * @param {boolean} [testPlanParameters.failFast=false] Fails immediatly when a test execution fails
25733
25215
  * @param {number} [testPlanParameters.cooldownBetweenExecutions=0] Millisecond to wait between each execution
25734
25216
  * @param {boolean} [testPlanParameters.logMemoryHeapUsage=false] Add memory heap usage during logs
25735
- * @param {boolean} [testPlanParameters.coverage=false] Controls if coverage is collected during files executions
25217
+ * @param {boolean} [testPlanParameters.coverageEnabled=false] Controls if coverage is collected during files executions
25736
25218
  * @param {boolean} [testPlanParameters.coverageV8ConflictWarning=true] Warn when coverage from 2 executions cannot be merged
25737
25219
  * @return {Object} An object containing the result of all file executions
25738
25220
  */
@@ -25763,22 +25245,25 @@ const executeTestPlan = async ({
25763
25245
  keepRunning = false,
25764
25246
  cooldownBetweenExecutions = 0,
25765
25247
  gcBetweenExecutions = logMemoryHeapUsage,
25766
- coverage = process.argv.includes("--cover") || process.argv.includes("--coverage"),
25767
- coverageTempDirectoryRelativeUrl = "./.coverage/tmp/",
25248
+ coverageEnabled = process.argv.includes("--cover") || process.argv.includes("--coverage"),
25768
25249
  coverageConfig = {
25769
25250
  "./src/": true
25770
25251
  },
25771
25252
  coverageIncludeMissing = true,
25772
25253
  coverageAndExecutionAllowed = false,
25773
- coverageForceIstanbul = false,
25254
+ coverageMethodForNodeJs = "NODE_V8_COVERAGE",
25255
+ // "Profiler" also accepted
25256
+ coverageMethodForBrowsers = "playwright_api",
25257
+ // "istanbul" also accepted
25774
25258
  coverageV8ConflictWarning = true,
25775
- coverageReportTextLog = true,
25776
- coverageReportJsonFile = process.env.CI ? null : "./.coverage/coverage.json",
25777
- coverageReportHtmlDirectory = process.env.CI ? "./.coverage/" : null,
25259
+ coverageTempDirectoryRelativeUrl = "./.coverage/tmp/",
25778
25260
  // skip empty means empty files won't appear in the coverage reports (json and html)
25779
25261
  coverageReportSkipEmpty = false,
25780
25262
  // skip full means file with 100% coverage won't appear in coverage reports (json and html)
25781
25263
  coverageReportSkipFull = false,
25264
+ coverageReportTextLog = true,
25265
+ coverageReportJsonFile = process.env.CI ? null : "./.coverage/coverage.json",
25266
+ coverageReportHtmlDirectory = process.env.CI ? "./.coverage/" : null,
25782
25267
  sourcemaps = "inline",
25783
25268
  plugins = [],
25784
25269
  nodeEsmResolution,
@@ -25799,7 +25284,7 @@ const executeTestPlan = async ({
25799
25284
  throw new Error(`testPlan must be an object, got ${testPlan}`);
25800
25285
  }
25801
25286
 
25802
- if (coverage) {
25287
+ if (coverageEnabled) {
25803
25288
  if (typeof coverageConfig !== "object") {
25804
25289
  throw new TypeError(`coverageConfig must be an object, got ${coverageConfig}`);
25805
25290
  }
@@ -25854,10 +25339,11 @@ const executeTestPlan = async ({
25854
25339
  keepRunning,
25855
25340
  cooldownBetweenExecutions,
25856
25341
  gcBetweenExecutions,
25857
- coverage,
25342
+ coverageEnabled,
25858
25343
  coverageConfig,
25859
25344
  coverageIncludeMissing,
25860
- coverageForceIstanbul,
25345
+ coverageMethodForBrowsers,
25346
+ coverageMethodForNodeJs,
25861
25347
  coverageV8ConflictWarning,
25862
25348
  coverageTempDirectoryRelativeUrl,
25863
25349
  scenario: "test",
@@ -25884,7 +25370,7 @@ const executeTestPlan = async ({
25884
25370
  // and in case coverage json file gets written in the same directory
25885
25371
  // it must be done before
25886
25372
 
25887
- if (coverage && coverageReportHtmlDirectory) {
25373
+ if (coverageEnabled && coverageReportHtmlDirectory) {
25888
25374
  const coverageHtmlDirectoryUrl = resolveDirectoryUrl(coverageReportHtmlDirectory, rootDirectoryUrl);
25889
25375
 
25890
25376
  if (!urlIsInsideOf(coverageHtmlDirectoryUrl, rootDirectoryUrl)) {
@@ -25902,7 +25388,7 @@ const executeTestPlan = async ({
25902
25388
  }));
25903
25389
  }
25904
25390
 
25905
- if (coverage && coverageReportJsonFile) {
25391
+ if (coverageEnabled && coverageReportJsonFile) {
25906
25392
  const coverageJsonFileUrl = new URL(coverageReportJsonFile, rootDirectoryUrl).href;
25907
25393
  promises.push(generateCoverageJsonFile({
25908
25394
  coverage: result.planCoverage,
@@ -25911,7 +25397,7 @@ const executeTestPlan = async ({
25911
25397
  }));
25912
25398
  }
25913
25399
 
25914
- if (coverage && coverageReportTextLog) {
25400
+ if (coverageEnabled && coverageReportTextLog) {
25915
25401
  promises.push(generateCoverageTextLog(result.planCoverage, {
25916
25402
  coverageReportSkipEmpty,
25917
25403
  coverageReportSkipFull
@@ -25986,6 +25472,7 @@ const createRuntimeFromPlaywright = ({
25986
25472
  isolatedTab = false
25987
25473
  }) => {
25988
25474
  const runtime = {
25475
+ type: "browser",
25989
25476
  name: browserName,
25990
25477
  version: browserVersion,
25991
25478
  needsServer: true
@@ -26000,9 +25487,9 @@ const createRuntimeFromPlaywright = ({
26000
25487
  server,
26001
25488
  // measurePerformance,
26002
25489
  collectPerformance,
26003
- collectCoverage = false,
26004
- coverageForceIstanbul,
26005
- urlShouldBeCovered,
25490
+ coverageEnabled = false,
25491
+ coverageConfig,
25492
+ coverageMethodForBrowsers,
26006
25493
  stopAfterAllSignal,
26007
25494
  stopSignal,
26008
25495
  keepRunning,
@@ -26087,8 +25574,8 @@ const createRuntimeFromPlaywright = ({
26087
25574
 
26088
25575
  let resultTransformer = result => result;
26089
25576
 
26090
- if (collectCoverage) {
26091
- if (coveragePlaywrightAPIAvailable && !coverageForceIstanbul) {
25577
+ if (coverageEnabled) {
25578
+ if (coveragePlaywrightAPIAvailable && coverageMethodForBrowsers === "playwright_api") {
26092
25579
  await page.coverage.startJSCoverage({// reportAnonymousScripts: true,
26093
25580
  });
26094
25581
  resultTransformer = composeTransformer(resultTransformer, async result => {
@@ -26106,10 +25593,11 @@ const createRuntimeFromPlaywright = ({
26106
25593
  url: fsUrl
26107
25594
  };
26108
25595
  });
26109
- const coverage = filterV8Coverage({
25596
+ const coverage = await filterV8Coverage({
26110
25597
  result: v8CoveragesWithFsUrls
26111
25598
  }, {
26112
- urlShouldBeCovered
25599
+ rootDirectoryUrl,
25600
+ coverageConfig
26113
25601
  });
26114
25602
  return { ...result,
26115
25603
  coverage
@@ -26534,20 +26022,23 @@ const evalException = (exceptionSource, {
26534
26022
 
26535
26023
  const chromium = createRuntimeFromPlaywright({
26536
26024
  browserName: "chromium",
26537
- browserVersion: "97.0.4666.0",
26025
+ browserVersion: "104.0.5112.20",
26026
+ // to update, check https://github.com/microsoft/playwright/releases
26538
26027
  coveragePlaywrightAPIAvailable: true
26539
26028
  });
26540
26029
  const chromiumIsolatedTab = chromium.isolatedTab;
26541
26030
 
26542
26031
  const firefox = createRuntimeFromPlaywright({
26543
26032
  browserName: "firefox",
26544
- browserVersion: "93.0"
26033
+ browserVersion: "100.0.2" // to update, check https://github.com/microsoft/playwright/releases
26034
+
26545
26035
  });
26546
26036
  const firefoxIsolatedTab = firefox.isolatedTab;
26547
26037
 
26548
26038
  const webkit = createRuntimeFromPlaywright({
26549
26039
  browserName: "webkit",
26550
26040
  browserVersion: "15.4",
26041
+ // to update, check https://github.com/microsoft/playwright/releases
26551
26042
  ignoreErrorHook: error => {
26552
26043
  // we catch error during execution but safari throw unhandled rejection
26553
26044
  // in a non-deterministic way.
@@ -26846,13 +26337,24 @@ const killProcessTree = async (processId, {
26846
26337
  await check();
26847
26338
  };
26848
26339
 
26849
- const NODE_CONTROLLABLE_FILE_URL = new URL("./js/controllable_file.mjs", import.meta.url).href;
26850
- const nodeProcess = {
26851
- name: "node",
26340
+ // https://nodejs.org/api/process.html#process_signal_events
26341
+ const SIGINT_SIGNAL_NUMBER = 2;
26342
+ const SIGABORT_SIGNAL_NUMBER = 6;
26343
+ const SIGTERM_SIGNAL_NUMBER = 15;
26344
+ const EXIT_CODES = {
26345
+ SIGINT: 128 + SIGINT_SIGNAL_NUMBER,
26346
+ SIGABORT: 128 + SIGABORT_SIGNAL_NUMBER,
26347
+ SIGTERM: 128 + SIGTERM_SIGNAL_NUMBER
26348
+ };
26349
+
26350
+ const CONTROLLABLE_CHILD_PROCESS_URL = new URL("./controllable_child_process.mjs", import.meta.url).href;
26351
+ const nodeChildProcess = {
26352
+ type: "node",
26353
+ name: "node_child_process",
26852
26354
  version: process.version.slice(1)
26853
26355
  };
26854
26356
 
26855
- nodeProcess.run = async ({
26357
+ nodeChildProcess.run = async ({
26856
26358
  signal = new AbortController().signal,
26857
26359
  logger,
26858
26360
  logProcessCommand = false,
@@ -26862,13 +26364,14 @@ nodeProcess.run = async ({
26862
26364
  gracefulStopAllocatedMs = 4000,
26863
26365
  stopSignal,
26864
26366
  onConsole,
26865
- collectCoverage = false,
26866
- coverageForceIstanbul,
26367
+ coverageEnabled = false,
26368
+ coverageConfig,
26369
+ coverageMethodForNodeJs,
26867
26370
  collectPerformance,
26371
+ env,
26868
26372
  debugPort,
26869
26373
  debugMode,
26870
26374
  debugModeInheritBreak,
26871
- env,
26872
26375
  inheritProcessEnv = true,
26873
26376
  commandLineOptions = [],
26874
26377
  stdin = "pipe",
@@ -26880,13 +26383,10 @@ nodeProcess.run = async ({
26880
26383
  }
26881
26384
 
26882
26385
  env = { ...env,
26883
- COVERAGE_ENABLED: collectCoverage,
26884
26386
  JSENV: true
26885
26387
  };
26886
26388
 
26887
- if (coverageForceIstanbul) {
26888
- // if we want to force istanbul, we will set process.env.NODE_V8_COVERAGE = ''
26889
- // into the child_process
26389
+ if (coverageMethodForNodeJs !== "NODE_V8_COVERAGE") {
26890
26390
  env.NODE_V8_COVERAGE = "";
26891
26391
  }
26892
26392
 
@@ -26911,8 +26411,8 @@ nodeProcess.run = async ({
26911
26411
  const envForChildProcess = { ...(inheritProcessEnv ? process.env : {}),
26912
26412
  ...env
26913
26413
  };
26914
- logger[logProcessCommand ? "info" : "debug"](`${process.argv[0]} ${execArgv.join(" ")} ${urlToFileSystemPath(NODE_CONTROLLABLE_FILE_URL)}`);
26915
- const childProcess = fork(urlToFileSystemPath(NODE_CONTROLLABLE_FILE_URL), {
26414
+ logger[logProcessCommand ? "info" : "debug"](`${process.argv[0]} ${execArgv.join(" ")} ${fileURLToPath(CONTROLLABLE_CHILD_PROCESS_URL)}`);
26415
+ const childProcess = fork(fileURLToPath(CONTROLLABLE_CHILD_PROCESS_URL), {
26916
26416
  execArgv,
26917
26417
  // silent: true
26918
26418
  stdio: ["pipe", "pipe", "pipe", "ipc"],
@@ -26936,9 +26436,9 @@ nodeProcess.run = async ({
26936
26436
  }
26937
26437
 
26938
26438
  const childProcessReadyPromise = new Promise(resolve => {
26939
- onceProcessMessage(childProcess, "ready", resolve);
26439
+ onceChildProcessMessage(childProcess, "ready", resolve);
26940
26440
  });
26941
- const removeOutputListener = installProcessOutputListener(childProcess, ({
26441
+ const removeOutputListener = installChildProcessOutputListener(childProcess, ({
26942
26442
  type,
26943
26443
  text
26944
26444
  }) => {
@@ -26999,10 +26499,10 @@ nodeProcess.run = async ({
26999
26499
  // },
27000
26500
  // https://nodejs.org/api/child_process.html#child_process_event_error
27001
26501
  error: cb => {
27002
- return onceProcessEvent(childProcess, "error", cb);
26502
+ return onceChildProcessEvent(childProcess, "error", cb);
27003
26503
  },
27004
26504
  exit: cb => {
27005
- return onceProcessEvent(childProcess, "exit", (code, signal) => {
26505
+ return onceChildProcessEvent(childProcess, "exit", (code, signal) => {
27006
26506
  cb({
27007
26507
  code,
27008
26508
  signal
@@ -27010,7 +26510,7 @@ nodeProcess.run = async ({
27010
26510
  });
27011
26511
  },
27012
26512
  response: cb => {
27013
- onceProcessMessage(childProcess, "action-result", cb);
26513
+ return onceChildProcessMessage(childProcess, "action-result", cb);
27014
26514
  }
27015
26515
  }, resolve);
27016
26516
  });
@@ -27019,11 +26519,19 @@ nodeProcess.run = async ({
27019
26519
  actionOperation.throwIfAborted();
27020
26520
  await childProcessReadyPromise;
27021
26521
  actionOperation.throwIfAborted();
27022
- await sendToProcess(childProcess, "action", {
27023
- actionType: "execute-using-dynamic-import",
27024
- actionParams: {
27025
- fileUrl: new URL(fileRelativeUrl, rootDirectoryUrl).href,
27026
- collectPerformance
26522
+ await sendToChildProcess(childProcess, {
26523
+ type: "action",
26524
+ data: {
26525
+ actionType: "execute-using-dynamic-import",
26526
+ actionParams: {
26527
+ rootDirectoryUrl,
26528
+ fileUrl: new URL(fileRelativeUrl, rootDirectoryUrl).href,
26529
+ collectPerformance,
26530
+ coverageEnabled,
26531
+ coverageConfig,
26532
+ coverageMethodForNodeJs,
26533
+ exitAfterAction: true
26534
+ }
27027
26535
  }
27028
26536
  });
27029
26537
  const winner = await winnerPromise;
@@ -27056,7 +26564,7 @@ nodeProcess.run = async ({
27056
26564
  };
27057
26565
  }
27058
26566
 
27059
- if (code === null || code === 0 || code === SIGINT_EXIT_CODE || code === SIGTERM_EXIT_CODE || code === SIGABORT_EXIT_CODE) {
26567
+ if (code === null || code === 0 || code === EXIT_CODES.SIGINT || code === EXIT_CODES.SIGTERM || code === EXIT_CODES.SIGABORT) {
27060
26568
  return {
27061
26569
  status: "errored",
27062
26570
  error: new Error(`node process exited during execution`)
@@ -27110,31 +26618,25 @@ nodeProcess.run = async ({
27110
26618
 
27111
26619
  await actionOperation.end();
27112
26620
  return result;
27113
- }; // https://nodejs.org/api/process.html#process_signal_events
27114
-
27115
-
27116
- const SIGINT_SIGNAL_NUMBER = 2;
27117
- const SIGABORT_SIGNAL_NUMBER = 6;
27118
- const SIGTERM_SIGNAL_NUMBER = 15;
27119
- const SIGINT_EXIT_CODE = 128 + SIGINT_SIGNAL_NUMBER;
27120
- const SIGABORT_EXIT_CODE = 128 + SIGABORT_SIGNAL_NUMBER;
27121
- const SIGTERM_EXIT_CODE = 128 + SIGTERM_SIGNAL_NUMBER; // http://man7.org/linux/man-pages/man7/signal.7.html
26621
+ }; // http://man7.org/linux/man-pages/man7/signal.7.html
27122
26622
  // https:// github.com/nodejs/node/blob/1d9511127c419ec116b3ddf5fc7a59e8f0f1c1e4/lib/internal/child_process.js#L472
27123
26623
 
26624
+
27124
26625
  const GRACEFUL_STOP_SIGNAL = "SIGTERM";
27125
26626
  const STOP_SIGNAL = "SIGKILL"; // it would be more correct if GRACEFUL_STOP_FAILED_SIGNAL was SIGHUP instead of SIGKILL.
27126
26627
  // but I'm not sure and it changes nothing so just use SIGKILL
27127
26628
 
27128
26629
  const GRACEFUL_STOP_FAILED_SIGNAL = "SIGKILL";
27129
26630
 
27130
- const sendToProcess = async (childProcess, type, data) => {
27131
- const source = uneval(data, {
27132
- functionAllowed: true
27133
- });
26631
+ const sendToChildProcess = async (childProcess, {
26632
+ type,
26633
+ data
26634
+ }) => {
27134
26635
  return new Promise((resolve, reject) => {
27135
26636
  childProcess.send({
26637
+ jsenv: true,
27136
26638
  type,
27137
- data: source
26639
+ data
27138
26640
  }, error => {
27139
26641
  if (error) {
27140
26642
  reject(error);
@@ -27145,7 +26647,7 @@ const sendToProcess = async (childProcess, type, data) => {
27145
26647
  });
27146
26648
  };
27147
26649
 
27148
- const installProcessOutputListener = (childProcess, callback) => {
26650
+ const installChildProcessOutputListener = (childProcess, callback) => {
27149
26651
  // beware that we may receive ansi output here, should not be a problem but keep that in mind
27150
26652
  const stdoutDataCallback = chunk => {
27151
26653
  callback({
@@ -27170,9 +26672,9 @@ const installProcessOutputListener = (childProcess, callback) => {
27170
26672
  };
27171
26673
  };
27172
26674
 
27173
- const onceProcessMessage = (childProcess, type, callback) => {
26675
+ const onceChildProcessMessage = (childProcess, type, callback) => {
27174
26676
  const onmessage = message => {
27175
- if (message.type === type) {
26677
+ if (message && message.jsenv && message.type === type) {
27176
26678
  childProcess.removeListener("message", onmessage); // eslint-disable-next-line no-eval
27177
26679
 
27178
26680
  callback(message.data ? eval(`(${message.data})`) : "");
@@ -27185,13 +26687,290 @@ const onceProcessMessage = (childProcess, type, callback) => {
27185
26687
  };
27186
26688
  };
27187
26689
 
27188
- const onceProcessEvent = (childProcess, type, callback) => {
26690
+ const onceChildProcessEvent = (childProcess, type, callback) => {
27189
26691
  childProcess.once(type, callback);
27190
26692
  return () => {
27191
26693
  childProcess.removeListener(type, callback);
27192
26694
  };
27193
26695
  };
27194
26696
 
26697
+ // https://github.com/avajs/ava/blob/576f534b345259055c95fa0c2b33bef10847a2af/lib/fork.js#L23
26698
+ const CONTROLLABLE_WORKER_THREAD_URL = new URL("./controllable_worker_thread.mjs", import.meta.url).href;
26699
+ const nodeWorkerThread = {
26700
+ type: "node",
26701
+ name: "node_worker_thread",
26702
+ version: process.version.slice(1)
26703
+ };
26704
+
26705
+ nodeWorkerThread.run = async ({
26706
+ signal = new AbortController().signal,
26707
+ // logger,
26708
+ rootDirectoryUrl,
26709
+ fileRelativeUrl,
26710
+ keepRunning,
26711
+ stopSignal,
26712
+ onConsole,
26713
+ coverageConfig,
26714
+ coverageMethodForNodeJs,
26715
+ coverageEnabled = false,
26716
+ collectPerformance,
26717
+ env,
26718
+ debugPort,
26719
+ debugMode,
26720
+ debugModeInheritBreak,
26721
+ inheritProcessEnv = true,
26722
+ commandLineOptions = []
26723
+ }) => {
26724
+ if (env !== undefined && typeof env !== "object") {
26725
+ throw new TypeError(`env must be an object, got ${env}`);
26726
+ }
26727
+
26728
+ env = { ...env,
26729
+ JSENV: true
26730
+ };
26731
+
26732
+ if (coverageMethodForNodeJs !== "NODE_V8_COVERAGE") {
26733
+ env.NODE_V8_COVERAGE = "";
26734
+ }
26735
+
26736
+ const workerThreadExecOptions = await createChildExecOptions({
26737
+ signal,
26738
+ debugPort,
26739
+ debugMode,
26740
+ debugModeInheritBreak
26741
+ });
26742
+ const execArgvForWorkerThread = ExecOptions.toExecArgv({ ...workerThreadExecOptions,
26743
+ ...ExecOptions.fromExecArgv(commandLineOptions)
26744
+ });
26745
+ const envForWorkerThread = { ...(inheritProcessEnv ? process.env : {}),
26746
+ ...env
26747
+ };
26748
+ const cleanupCallbackList = createCallbackListNotifiedOnce();
26749
+
26750
+ const cleanup = async reason => {
26751
+ await cleanupCallbackList.notify({
26752
+ reason
26753
+ });
26754
+ };
26755
+
26756
+ const actionOperation = Abort.startOperation();
26757
+ actionOperation.addAbortSignal(signal); // https://nodejs.org/api/worker_threads.html#new-workerfilename-options
26758
+
26759
+ const workerThread = new Worker(fileURLToPath(CONTROLLABLE_WORKER_THREAD_URL), {
26760
+ env: envForWorkerThread,
26761
+ execArgv: execArgvForWorkerThread,
26762
+ // workerData: { options },
26763
+ // trackUnmanagedFds: true,
26764
+ stdin: true,
26765
+ stdout: true,
26766
+ stderr: true
26767
+ });
26768
+ const removeOutputListener = installWorkerThreadOutputListener(workerThread, ({
26769
+ type,
26770
+ text
26771
+ }) => {
26772
+ onConsole({
26773
+ type,
26774
+ text
26775
+ });
26776
+ });
26777
+ const workerThreadReadyPromise = new Promise(resolve => {
26778
+ onceWorkerThreadMessage(workerThread, "ready", resolve);
26779
+ });
26780
+ const stop = memoize(async () => {
26781
+ await workerThreadReadyPromise;
26782
+ await workerThread.terminate();
26783
+ });
26784
+ const winnerPromise = new Promise(resolve => {
26785
+ raceCallbacks({
26786
+ aborted: cb => {
26787
+ return actionOperation.addAbortCallback(cb);
26788
+ },
26789
+ error: cb => {
26790
+ return onceWorkerThreadEvent(workerThread, "error", cb);
26791
+ },
26792
+ exit: cb => {
26793
+ return onceWorkerThreadEvent(workerThread, "exit", (code, signal) => {
26794
+ cb({
26795
+ code,
26796
+ signal
26797
+ });
26798
+ });
26799
+ },
26800
+ response: cb => {
26801
+ return onceWorkerThreadMessage(workerThread, "action-result", cb);
26802
+ }
26803
+ }, resolve);
26804
+ });
26805
+
26806
+ const getResult = async () => {
26807
+ actionOperation.throwIfAborted();
26808
+ await workerThreadReadyPromise;
26809
+ actionOperation.throwIfAborted();
26810
+ await sendToWorkerThread(workerThread, {
26811
+ type: "action",
26812
+ data: {
26813
+ actionType: "execute-using-dynamic-import",
26814
+ actionParams: {
26815
+ rootDirectoryUrl,
26816
+ fileUrl: new URL(fileRelativeUrl, rootDirectoryUrl).href,
26817
+ collectPerformance,
26818
+ coverageEnabled,
26819
+ coverageConfig,
26820
+ coverageMethodForNodeJs,
26821
+ exitAfterAction: true
26822
+ }
26823
+ }
26824
+ });
26825
+ const winner = await winnerPromise;
26826
+
26827
+ if (winner.name === "aborted") {
26828
+ return {
26829
+ status: "aborted"
26830
+ };
26831
+ }
26832
+
26833
+ if (winner.name === "error") {
26834
+ const error = winner.data;
26835
+ removeOutputListener();
26836
+ return {
26837
+ status: "errored",
26838
+ error
26839
+ };
26840
+ }
26841
+
26842
+ if (winner.name === "exit") {
26843
+ const {
26844
+ code
26845
+ } = winner.data;
26846
+ await cleanup("process exit");
26847
+
26848
+ if (code === 12) {
26849
+ return {
26850
+ status: "errored",
26851
+ error: new Error(`node process exited with 12 (the forked child process wanted to use a non-available port for debug)`)
26852
+ };
26853
+ }
26854
+
26855
+ if (code === null || code === 0 || code === EXIT_CODES.SIGINT || code === EXIT_CODES.SIGTERM || code === EXIT_CODES.SIGABORT) {
26856
+ return {
26857
+ status: "errored",
26858
+ error: new Error(`node worker thread exited during execution`)
26859
+ };
26860
+ } // process.exit(1) in child process or process.exitCode = 1 + process.exit()
26861
+ // means there was an error even if we don't know exactly what.
26862
+
26863
+
26864
+ return {
26865
+ status: "errored",
26866
+ error: new Error(`node worker thread exited with code ${code} during execution`)
26867
+ };
26868
+ }
26869
+
26870
+ const {
26871
+ status,
26872
+ value
26873
+ } = winner.data;
26874
+
26875
+ if (status === "action-failed") {
26876
+ return {
26877
+ status: "errored",
26878
+ error: value
26879
+ };
26880
+ }
26881
+
26882
+ const {
26883
+ namespace,
26884
+ performance,
26885
+ coverage
26886
+ } = value;
26887
+ return {
26888
+ status: "completed",
26889
+ namespace,
26890
+ performance,
26891
+ coverage
26892
+ };
26893
+ };
26894
+
26895
+ let result;
26896
+
26897
+ try {
26898
+ result = await getResult();
26899
+ } catch (e) {
26900
+ result = {
26901
+ status: "errored",
26902
+ error: e
26903
+ };
26904
+ }
26905
+
26906
+ if (keepRunning) {
26907
+ stopSignal.notify = stop;
26908
+ } else {
26909
+ await stop();
26910
+ }
26911
+
26912
+ await actionOperation.end();
26913
+ return result;
26914
+ };
26915
+
26916
+ const installWorkerThreadOutputListener = (workerThread, callback) => {
26917
+ // beware that we may receive ansi output here, should not be a problem but keep that in mind
26918
+ const stdoutDataCallback = chunk => {
26919
+ callback({
26920
+ type: "log",
26921
+ text: String(chunk)
26922
+ });
26923
+ };
26924
+
26925
+ workerThread.stdout.on("data", stdoutDataCallback);
26926
+
26927
+ const stdErrorDataCallback = chunk => {
26928
+ callback({
26929
+ type: "error",
26930
+ text: String(chunk)
26931
+ });
26932
+ };
26933
+
26934
+ workerThread.stderr.on("data", stdErrorDataCallback);
26935
+ return () => {
26936
+ workerThread.stdout.removeListener("data", stdoutDataCallback);
26937
+ workerThread.stderr.removeListener("data", stdoutDataCallback);
26938
+ };
26939
+ };
26940
+
26941
+ const sendToWorkerThread = (worker, {
26942
+ type,
26943
+ data
26944
+ }) => {
26945
+ worker.postMessage({
26946
+ jsenv: true,
26947
+ type,
26948
+ data
26949
+ });
26950
+ };
26951
+
26952
+ const onceWorkerThreadMessage = (workerThread, type, callback) => {
26953
+ const onmessage = message => {
26954
+ if (message && message.jsenv && message.type === type) {
26955
+ workerThread.removeListener("message", onmessage); // eslint-disable-next-line no-eval
26956
+
26957
+ callback(message.data ? eval(`(${message.data})`) : undefined);
26958
+ }
26959
+ };
26960
+
26961
+ workerThread.on("message", onmessage);
26962
+ return () => {
26963
+ workerThread.removeListener("message", onmessage);
26964
+ };
26965
+ };
26966
+
26967
+ const onceWorkerThreadEvent = (worker, type, callback) => {
26968
+ worker.once(type, callback);
26969
+ return () => {
26970
+ worker.removeListener(type, callback);
26971
+ };
26972
+ };
26973
+
27195
26974
  const loadUrlGraph = async ({
27196
26975
  operation,
27197
26976
  urlGraph,
@@ -27261,7 +27040,6 @@ const loadUrlGraph = async ({
27261
27040
  type,
27262
27041
  specifier
27263
27042
  });
27264
- entryUrlInfo.data.isEntryPoint = true;
27265
27043
  cook(entryUrlInfo, {
27266
27044
  reference: entryReference
27267
27045
  });
@@ -27635,7 +27413,7 @@ const determineDirectoryPath = ({
27635
27413
  return parentDirectoryPath;
27636
27414
  }
27637
27415
 
27638
- if (urlInfo.data.isEntryPoint || urlInfo.data.isWebWorkerEntryPoint) {
27416
+ if (urlInfo.isEntryPoint) {
27639
27417
  return "";
27640
27418
  }
27641
27419
 
@@ -27669,7 +27447,7 @@ const injectGlobalVersionMapping = async ({
27669
27447
  versionMappings
27670
27448
  }) => {
27671
27449
  await Promise.all(GRAPH.map(finalGraph, async urlInfo => {
27672
- if (urlInfo.data.isEntryPoint || urlInfo.data.isWebWorkerEntryPoint) {
27450
+ if (urlInfo.isEntryPoint) {
27673
27451
  await injectVersionMappings({
27674
27452
  urlInfo,
27675
27453
  kitchen: finalGraphKitchen,
@@ -27705,7 +27483,7 @@ const jsInjector = (urlInfo, {
27705
27483
  }) => {
27706
27484
  const magicSource = createMagicSource(urlInfo.content);
27707
27485
  magicSource.prepend(generateClientCodeForVersionMappings(versionMappings, {
27708
- globalName: urlInfo.data.isWebWorkerEntryPoint ? "self" : "window"
27486
+ globalName: isWebWorkerUrlInfo(urlInfo) ? "self" : "window"
27709
27487
  }));
27710
27488
  return magicSource.toContentAndSourcemap();
27711
27489
  };
@@ -27811,7 +27589,7 @@ const injectServiceWorkerUrls = async ({
27811
27589
  lineBreakNormalization
27812
27590
  }) => {
27813
27591
  const serviceWorkerEntryUrlInfos = GRAPH.filter(finalGraph, finalUrlInfo => {
27814
- return finalUrlInfo.subtype === "service_worker" && finalUrlInfo.data.isWebWorkerEntryPoint;
27592
+ return finalUrlInfo.subtype === "service_worker" && finalUrlInfo.isEntryPoint;
27815
27593
  });
27816
27594
 
27817
27595
  if (serviceWorkerEntryUrlInfos.length === 0) {
@@ -28227,7 +28005,7 @@ build ${entryPointKeys.length} entry points`);
28227
28005
  };
28228
28006
 
28229
28007
  GRAPH.forEach(rawGraph, rawUrlInfo => {
28230
- if (rawUrlInfo.data.isEntryPoint) {
28008
+ if (rawUrlInfo.isEntryPoint) {
28231
28009
  addToBundlerIfAny(rawUrlInfo);
28232
28010
 
28233
28011
  if (rawUrlInfo.type === "html") {
@@ -28305,6 +28083,7 @@ build ${entryPointKeys.length} entry points`);
28305
28083
  const bundleUrlInfo = {
28306
28084
  type,
28307
28085
  subtype: rawUrlInfo ? rawUrlInfo.subtype : undefined,
28086
+ isEntryPoint: rawUrlInfo ? rawUrlInfo.isEntryPoint : undefined,
28308
28087
  filename: rawUrlInfo ? rawUrlInfo.filename : undefined,
28309
28088
  originalUrl: rawUrlInfo ? rawUrlInfo.originalUrl : undefined,
28310
28089
  originalContent: rawUrlInfo ? rawUrlInfo.originalContent : undefined,
@@ -28442,9 +28221,8 @@ build ${entryPointKeys.length} entry points`);
28442
28221
 
28443
28222
  const buildUrl = buildUrlsGenerator.generate(rawUrl, {
28444
28223
  urlInfo: {
28445
- data: { ...reference.data,
28446
- isWebWorkerEntryPoint: isWebWorkerEntryPointReference(reference)
28447
- },
28224
+ data: reference.data,
28225
+ isEntryPoint: reference.isEntryPoint || isWebWorkerEntryPointReference(reference),
28448
28226
  type: reference.expectedType,
28449
28227
  subtype: reference.expectedSubtype,
28450
28228
  filename: reference.filename
@@ -28736,7 +28514,7 @@ ${Object.keys(finalGraph.urlInfos).join("\n")}`);
28736
28514
  // nothing uses this url anymore
28737
28515
  // - versioning update inline content
28738
28516
  // - file converted for import assertion of js_classic conversion
28739
- if (!urlInfo.data.isEntryPoint && urlInfo.type !== "sourcemap" && urlInfo.dependents.size === 0) {
28517
+ if (!urlInfo.isEntryPoint && urlInfo.type !== "sourcemap" && urlInfo.dependents.size === 0) {
28740
28518
  cleanupActions.push(() => {
28741
28519
  finalGraph.deleteUrlInfo(urlInfo.url);
28742
28520
  });
@@ -28958,7 +28736,7 @@ const applyUrlVersioning = async ({
28958
28736
  return;
28959
28737
  }
28960
28738
 
28961
- if (!urlInfo.data.isEntryPoint && urlInfo.dependents.size === 0) {
28739
+ if (!urlInfo.isEntryPoint && urlInfo.dependents.size === 0) {
28962
28740
  return;
28963
28741
  }
28964
28742
 
@@ -29204,7 +28982,7 @@ const assertEntryPoints = ({
29204
28982
  };
29205
28983
 
29206
28984
  const canUseVersionedUrl = urlInfo => {
29207
- if (urlInfo.data.isEntryPoint) {
28985
+ if (urlInfo.isEntryPoint) {
29208
28986
  return false;
29209
28987
  }
29210
28988
 
@@ -29212,10 +28990,6 @@ const canUseVersionedUrl = urlInfo => {
29212
28990
  return false;
29213
28991
  }
29214
28992
 
29215
- if (urlInfo.subtype === "service_worker") {
29216
- return !urlInfo.data.isWebWorkerEntryPoint;
29217
- }
29218
-
29219
28993
  return true;
29220
28994
  };
29221
28995
 
@@ -29677,4 +29451,4 @@ const jsenvPluginInjectGlobals = urlAssociations => {
29677
29451
  };
29678
29452
  };
29679
29453
 
29680
- export { build, chromium, chromiumIsolatedTab, execute, executeTestPlan, firefox, firefoxIsolatedTab, jsenvPluginInjectGlobals, nodeProcess, startBuildServer, startDevServer, webkit, webkitIsolatedTab };
29454
+ export { build, chromium, chromiumIsolatedTab, execute, executeTestPlan, firefox, firefoxIsolatedTab, jsenvPluginInjectGlobals, nodeChildProcess, nodeWorkerThread, startBuildServer, startDevServer, webkit, webkitIsolatedTab };