@jsenv/core 27.7.0 → 28.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/js/server_events_client.js +1 -1
  2. package/dist/main.js +504 -457
  3. package/package.json +2 -2
  4. package/src/build/build.js +4 -5
  5. package/src/build/start_build_server.js +2 -2
  6. package/src/dev/start_dev_server.js +3 -3
  7. package/src/execute/execute.js +14 -52
  8. package/src/execute/runtimes/browsers/from_playwright.js +19 -8
  9. package/src/main.js +3 -0
  10. package/src/omega/kitchen.js +9 -14
  11. package/src/omega/omega_server.js +4 -4
  12. package/src/omega/server/file_service.js +11 -11
  13. package/src/ping_server.js +30 -0
  14. package/src/plugins/autoreload/jsenv_plugin_autoreload.js +0 -4
  15. package/src/plugins/autoreload/jsenv_plugin_autoreload_client.js +1 -1
  16. package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +1 -1
  17. package/src/plugins/autoreload/jsenv_plugin_hmr.js +1 -1
  18. package/src/plugins/bundling/jsenv_plugin_bundling.js +1 -3
  19. package/src/plugins/cache_control/jsenv_plugin_cache_control.js +2 -5
  20. package/src/plugins/commonjs_globals/jsenv_plugin_commonjs_globals.js +2 -2
  21. package/src/plugins/file_urls/jsenv_plugin_file_urls.js +4 -8
  22. package/src/plugins/html_supervisor/jsenv_plugin_html_supervisor.js +1 -4
  23. package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +2 -2
  24. package/src/plugins/import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js +12 -26
  25. package/src/plugins/importmap/jsenv_plugin_importmap.js +1 -1
  26. package/src/plugins/minification/jsenv_plugin_minification.js +1 -3
  27. package/src/plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +9 -7
  28. package/src/plugins/plugin_controller.js +17 -6
  29. package/src/plugins/plugins.js +0 -2
  30. package/src/plugins/server_events/client/server_events_client.js +1 -1
  31. package/src/plugins/toolbar/jsenv_plugin_toolbar.js +1 -3
  32. package/src/plugins/transpilation/babel/jsenv_plugin_babel.js +13 -0
  33. package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +1 -1
  34. package/src/plugins/url_analysis/html/html_urls.js +2 -2
  35. package/src/test/execute_plan.js +15 -68
  36. package/src/test/execute_test_plan.js +4 -26
package/dist/main.js CHANGED
@@ -12,10 +12,10 @@ import { createMagicSource, composeTwoSourcemaps, getOriginalPosition, sourcemap
12
12
  import { parseHtmlString, stringifyHtmlAst, visitHtmlNodes, getHtmlNodeAttribute, analyzeScriptNode, setHtmlNodeAttributes, parseSrcSet, getHtmlNodePosition, getHtmlNodeAttributePosition, applyPostCss, postCssPluginUrlVisitor, parseJsUrls, getHtmlNodeText, setHtmlNodeText, applyBabelPlugins, injectScriptNodeAsEarlyAsPossible, createHtmlNode, findHtmlNode, removeHtmlNode, removeHtmlNodeText, transpileWithParcel, injectJsImport, minifyWithParcel, analyzeLinkNode } from "@jsenv/ast";
13
13
  import { createRequire } from "node:module";
14
14
  import babelParser from "@babel/parser";
15
+ import net, { createServer, isIP } from "node:net";
15
16
  import http from "node:http";
16
17
  import cluster from "node:cluster";
17
18
  import { performance as performance$1 } from "node:perf_hooks";
18
- import net, { createServer } from "node:net";
19
19
  import { Readable, Stream, Writable } from "node:stream";
20
20
  import { Http2ServerResponse } from "node:http2";
21
21
  import { lookup } from "node:dns";
@@ -4217,11 +4217,11 @@ const parseAndTransformHtmlUrls = async (urlInfo, context) => {
4217
4217
  const url = urlInfo.originalUrl;
4218
4218
  const content = urlInfo.content;
4219
4219
  const {
4220
- scenario,
4220
+ scenarios,
4221
4221
  referenceUtils
4222
4222
  } = context;
4223
4223
  const htmlAst = parseHtmlString(content, {
4224
- storeOriginalPositions: scenario !== "build"
4224
+ storeOriginalPositions: scenarios.dev
4225
4225
  });
4226
4226
  const actions = [];
4227
4227
  visitHtmlUrls({
@@ -11464,7 +11464,7 @@ const jsenvPluginImportmap = () => {
11464
11464
  // Duing build we get rid of it
11465
11465
 
11466
11466
 
11467
- if (context.scenario === "build") {
11467
+ if (context.scenarios.build) {
11468
11468
  removeHtmlNode(importmap);
11469
11469
  }
11470
11470
 
@@ -12801,13 +12801,8 @@ const jsenvPluginNodeEsmResolution = ({
12801
12801
  return {
12802
12802
  name: "jsenv:node_esm_resolution",
12803
12803
  appliesDuring: "*",
12804
- init: ({
12805
- rootDirectoryUrl,
12806
- scenario,
12807
- runtimeCompat,
12808
- urlGraph
12809
- }) => {
12810
- const nodeRuntimeEnabled = Object.keys(runtimeCompat).includes("node"); // https://nodejs.org/api/esm.html#resolver-algorithm-specification
12804
+ init: context => {
12805
+ const nodeRuntimeEnabled = Object.keys(context.runtimeCompat).includes("node"); // https://nodejs.org/api/esm.html#resolver-algorithm-specification
12811
12806
 
12812
12807
  packageConditions = packageConditions || [...readCustomConditionsFromProcessArgs(), nodeRuntimeEnabled ? "node" : "browser", "import"];
12813
12808
  const packageScopesCache = new Map();
@@ -12838,19 +12833,19 @@ const jsenvPluginNodeEsmResolution = ({
12838
12833
  return packageJson;
12839
12834
  };
12840
12835
 
12841
- if (scenario === "dev") {
12836
+ if (context.scenarios.dev) {
12842
12837
  const onFileChange = () => {
12843
12838
  packageScopesCache.clear();
12844
12839
  packageJsonsCache.clear();
12845
- urlGraph.urlInfoMap.forEach(urlInfo => {
12840
+ context.urlGraph.urlInfoMap.forEach(urlInfo => {
12846
12841
  if (urlInfo.dependsOnPackageJson) {
12847
- urlGraph.considerModified(urlInfo);
12842
+ context.urlGraph.considerModified(urlInfo);
12848
12843
  }
12849
12844
  });
12850
12845
  };
12851
12846
 
12852
12847
  filesInvalidatingCache.forEach(file => {
12853
- const unregister = registerFileLifecycle(new URL(file, rootDirectoryUrl), {
12848
+ const unregister = registerFileLifecycle(new URL(file, context.rootDirectoryUrl), {
12854
12849
  added: () => {
12855
12850
  onFileChange();
12856
12851
  },
@@ -12912,7 +12907,7 @@ const jsenvPluginNodeEsmResolution = ({
12912
12907
  return null;
12913
12908
  },
12914
12909
  transformUrlSearchParams: (reference, context) => {
12915
- if (context.scenario === "build") {
12910
+ if (context.scenarios.build) {
12916
12911
  return null;
12917
12912
  }
12918
12913
 
@@ -13091,14 +13086,10 @@ const jsenvPluginFileUrls = ({
13091
13086
  }
13092
13087
  }, {
13093
13088
  name: "jsenv:@fs_resolution",
13094
- appliesDuring: {
13095
- // during dev and test it's a browser running the code
13096
- // so absolute file urls needs to be relativized
13097
- dev: true,
13098
- test: true,
13099
- // during build it's fine to use file:// urls
13100
- build: false
13101
- },
13089
+ // during dev and test it's a browser running the code
13090
+ // so absolute file urls needs to be relativized
13091
+ // during build it's fine to use file:// urls
13092
+ appliesDuring: "dev",
13102
13093
  resolveUrl: reference => {
13103
13094
  if (reference.specifier.startsWith("/@fs/")) {
13104
13095
  const fsRootRelativeUrl = reference.specifier.slice("/@fs/".length);
@@ -13197,10 +13188,7 @@ const jsenvPluginHtmlSupervisor = ({
13197
13188
  const htmlSupervisorInstallerFileUrl = new URL("./js/html_supervisor_installer.js", import.meta.url).href;
13198
13189
  return {
13199
13190
  name: "jsenv:html_supervisor",
13200
- appliesDuring: {
13201
- dev: true,
13202
- test: true
13203
- },
13191
+ appliesDuring: "dev",
13204
13192
  serve: async (request, context) => {
13205
13193
  if (request.ressource.startsWith("/__get_code_frame__/")) {
13206
13194
  const {
@@ -13569,16 +13557,14 @@ const generateCodeToSuperviseScript = ({
13569
13557
  * - global
13570
13558
  */
13571
13559
  const jsenvPluginCommonJsGlobals = () => {
13572
- const transformCommonJsGlobals = async (urlInfo, {
13573
- scenario
13574
- }) => {
13560
+ const transformCommonJsGlobals = async (urlInfo, context) => {
13575
13561
  if (!urlInfo.content.includes("process.env.NODE_ENV") && !urlInfo.content.includes("__filename") && !urlInfo.content.includes("__dirname")) {
13576
13562
  return null;
13577
13563
  }
13578
13564
 
13579
13565
  const isJsModule = urlInfo.type === "js_module";
13580
13566
  const replaceMap = {
13581
- "process.env.NODE_ENV": `("${scenario === "dev" || scenario === "test" ? "development" : "production"}")`,
13567
+ "process.env.NODE_ENV": `("${context.scenarios.dev ? "development" : "production"}")`,
13582
13568
  "global": "globalThis",
13583
13569
  "__filename": isJsModule ? `import.meta.url.slice('file:///'.length)` : `document.currentScript.src`,
13584
13570
  "__dirname": isJsModule ? `import.meta.url.slice('file:///'.length).replace(/[\\\/\\\\][^\\\/\\\\]*$/, '')` : `new URL('./', document.currentScript.src).href`
@@ -13732,11 +13718,10 @@ const babelPluginMetadataExpressionPaths = (babel, {
13732
13718
  /*
13733
13719
  * Source code can contain the following
13734
13720
  * - import.meta.dev
13735
- * - import.meta.test
13736
13721
  * - import.meta.build
13737
13722
  * They are either:
13738
13723
  * - replaced by true: When scenario matches (import.meta.dev and it's the dev server)
13739
- * - left as is to be evaluated to undefined (import.meta.test but it's the dev server)
13724
+ * - left as is to be evaluated to undefined (import.meta.build but it's the dev server)
13740
13725
  * - replaced by undefined (import.meta.dev but it's build; the goal is to ensure it's tree-shaked)
13741
13726
  */
13742
13727
  const jsenvPluginImportMetaScenarios = () => {
@@ -13744,9 +13729,7 @@ const jsenvPluginImportMetaScenarios = () => {
13744
13729
  name: "jsenv:import_meta_scenario",
13745
13730
  appliesDuring: "*",
13746
13731
  transformUrlContent: {
13747
- js_module: async (urlInfo, {
13748
- scenario
13749
- }) => {
13732
+ js_module: async (urlInfo, context) => {
13750
13733
  if (!urlInfo.content.includes("import.meta.dev") && !urlInfo.content.includes("import.meta.test") && !urlInfo.content.includes("import.meta.build")) {
13751
13734
  return null;
13752
13735
  }
@@ -13759,7 +13742,6 @@ const jsenvPluginImportMetaScenarios = () => {
13759
13742
  });
13760
13743
  const {
13761
13744
  dev = [],
13762
- test = [],
13763
13745
  build = []
13764
13746
  } = metadata.importMetaScenarios;
13765
13747
  const replacements = [];
@@ -13771,32 +13753,20 @@ const jsenvPluginImportMetaScenarios = () => {
13771
13753
  });
13772
13754
  };
13773
13755
 
13774
- if (scenario === "dev") {
13756
+ if (context.scenarios.build) {
13757
+ // during build ensure replacement for tree-shaking
13775
13758
  dev.forEach(path => {
13776
- replace(path, "true");
13777
- });
13778
- } else if (scenario === "test") {
13779
- // test is also considered a dev environment
13780
- // just like the dev server can be used to debug test files
13781
- // without this people would have to write
13782
- // if (import.meta.dev || import.meta.test) or if (!import.meta.build)
13783
- dev.forEach(path => {
13784
- replace(path, "true");
13759
+ replace(path, "undefined");
13785
13760
  });
13786
- test.forEach(path => {
13761
+ build.forEach(path => {
13787
13762
  replace(path, "true");
13788
13763
  });
13789
- } else if (scenario === "build") {
13790
- // replacing by undefined might not be required
13791
- // as I suppose rollup would consider them as undefined
13792
- // but let's make it explicit to ensure code is properly tree-shaked
13764
+ } else {
13765
+ // during dev we can let "import.meta.build" untouched
13766
+ // it will be evaluated to undefined.
13767
+ // Moreover it can be surprising to see some "undefined"
13768
+ // when source file contains "import.meta.build"
13793
13769
  dev.forEach(path => {
13794
- replace(path, "undefined");
13795
- });
13796
- test.forEach(path => {
13797
- replace(path, "undefined");
13798
- });
13799
- build.forEach(path => {
13800
13770
  replace(path, "true");
13801
13771
  });
13802
13772
  }
@@ -13863,7 +13833,6 @@ const babelPluginMetadataImportMetaScenarios = () => {
13863
13833
  });
13864
13834
  state.file.metadata.importMetaScenarios = {
13865
13835
  dev: importMetas.dev,
13866
- test: importMetas.test,
13867
13836
  build: importMetas.build
13868
13837
  };
13869
13838
  }
@@ -13933,7 +13902,7 @@ const jsenvPluginImportAssertions = ({
13933
13902
  // We would have to tell rollup to ignore import with assertion
13934
13903
  // - means rollup can bundle more js file together
13935
13904
  // - means url versioning can work for css inlined in js
13936
- if (context.scenario === "build") {
13905
+ if (context.scenarios.build) {
13937
13906
  json = true;
13938
13907
  css = true;
13939
13908
  text = true;
@@ -14083,6 +14052,87 @@ export default inlineContent.text`,
14083
14052
  return [asJsonModule, asCssModule, asTextModule];
14084
14053
  };
14085
14054
 
14055
+ const babelPluginInstrument = (api, {
14056
+ rootDirectoryUrl,
14057
+ useInlineSourceMaps = false,
14058
+ coverageConfig = {
14059
+ "./**/*": true
14060
+ }
14061
+ }) => {
14062
+ const {
14063
+ programVisitor
14064
+ } = requireFromJsenv("istanbul-lib-instrument");
14065
+ const {
14066
+ types
14067
+ } = api;
14068
+ const associations = URL_META.resolveAssociations({
14069
+ cover: coverageConfig
14070
+ }, rootDirectoryUrl);
14071
+
14072
+ const shouldInstrument = url => {
14073
+ return URL_META.applyAssociations({
14074
+ url,
14075
+ associations
14076
+ }).cover;
14077
+ };
14078
+
14079
+ return {
14080
+ name: "transform-instrument",
14081
+ visitor: {
14082
+ Program: {
14083
+ enter(path) {
14084
+ const {
14085
+ file
14086
+ } = this;
14087
+ const {
14088
+ opts
14089
+ } = file;
14090
+
14091
+ if (!opts.sourceFileName) {
14092
+ console.warn(`cannot instrument file when "sourceFileName" option is not set`);
14093
+ return;
14094
+ }
14095
+
14096
+ const fileUrl = fileSystemPathToUrl$1(opts.sourceFileName);
14097
+
14098
+ if (!shouldInstrument(fileUrl)) {
14099
+ return;
14100
+ }
14101
+
14102
+ this.__dv__ = null;
14103
+ let inputSourceMap;
14104
+
14105
+ if (useInlineSourceMaps) {
14106
+ // https://github.com/istanbuljs/babel-plugin-istanbul/commit/a9e15643d249a2985e4387e4308022053b2cd0ad#diff-1fdf421c05c1140f6d71444ea2b27638R65
14107
+ inputSourceMap = opts.inputSourceMap || file.inputMap ? file.inputMap.sourcemap : null;
14108
+ } else {
14109
+ inputSourceMap = opts.inputSourceMap;
14110
+ }
14111
+
14112
+ this.__dv__ = programVisitor(types, opts.filenameRelative || opts.filename, {
14113
+ coverageVariable: "__coverage__",
14114
+ inputSourceMap
14115
+ });
14116
+
14117
+ this.__dv__.enter(path);
14118
+ },
14119
+
14120
+ exit(path) {
14121
+ if (!this.__dv__) {
14122
+ return;
14123
+ }
14124
+
14125
+ const object = this.__dv__.exit(path); // object got two properties: fileCoverage and sourceMappingURL
14126
+
14127
+
14128
+ this.file.metadata.coverage = object.fileCoverage;
14129
+ }
14130
+
14131
+ }
14132
+ }
14133
+ };
14134
+ };
14135
+
14086
14136
  const versionFromValue = value => {
14087
14137
  if (typeof value === "number") {
14088
14138
  return numberToVersion(value);
@@ -15264,6 +15314,17 @@ const jsenvPluginBabel = ({
15264
15314
  getImportSpecifier
15265
15315
  });
15266
15316
 
15317
+ if (context.scenarios.dev) {
15318
+ const requestHeaders = context.request.headers;
15319
+
15320
+ if (requestHeaders["x-coverage-instanbul"]) {
15321
+ babelPluginStructure["transform-instrument"] = [babelPluginInstrument, {
15322
+ rootDirectoryUrl: context.rootDirectoryUrl,
15323
+ coverageConfig: JSON.parse(requestHeaders["x-coverage-instanbul"])
15324
+ }];
15325
+ }
15326
+ }
15327
+
15267
15328
  if (getCustomBabelPlugins) {
15268
15329
  Object.assign(babelPluginStructure, getCustomBabelPlugins(context));
15269
15330
  }
@@ -16034,9 +16095,7 @@ const jsenvPluginBundling = bundling => {
16034
16095
  });
16035
16096
  return {
16036
16097
  name: "jsenv:bundling",
16037
- appliesDuring: {
16038
- build: true
16039
- },
16098
+ appliesDuring: "build",
16040
16099
  bundle: {
16041
16100
  css: bundling.css ? (cssUrlInfos, context) => {
16042
16101
  return bundleCss({
@@ -16172,9 +16231,7 @@ const jsenvPluginMinification = minification => {
16172
16231
  }) : null;
16173
16232
  return {
16174
16233
  name: "jsenv:minification",
16175
- appliesDuring: {
16176
- build: true
16177
- },
16234
+ appliesDuring: "build",
16178
16235
  optimizeUrlContent: {
16179
16236
  html: htmlOptimizer,
16180
16237
  svg: htmlOptimizer,
@@ -16494,7 +16551,7 @@ const jsenvPluginImportMetaHot = () => {
16494
16551
  transformUrlContent: {
16495
16552
  html: (htmlUrlInfo, context) => {
16496
16553
  // during build we don't really care to parse html hot dependencies
16497
- if (context.scenario === "build") {
16554
+ if (context.scenarios.build) {
16498
16555
  return;
16499
16556
  }
16500
16557
 
@@ -16543,7 +16600,7 @@ const jsenvPluginImportMetaHot = () => {
16543
16600
  return null;
16544
16601
  }
16545
16602
 
16546
- if (context.scenario === "build") {
16603
+ if (context.scenarios.build) {
16547
16604
  return removeImportMetaHots(urlInfo, importMetaHotPaths);
16548
16605
  }
16549
16606
 
@@ -16587,7 +16644,8 @@ const jsenvPluginHmr = () => {
16587
16644
  return {
16588
16645
  name: "jsenv:hmr",
16589
16646
  appliesDuring: {
16590
- dev: true
16647
+ dev: true,
16648
+ test: false
16591
16649
  },
16592
16650
  redirectUrl: reference => {
16593
16651
  const urlObject = new URL(reference.url);
@@ -16632,7 +16690,8 @@ const jsenvPluginAutoreloadClient = () => {
16632
16690
  return {
16633
16691
  name: "jsenv:autoreload_client",
16634
16692
  appliesDuring: {
16635
- dev: true
16693
+ dev: true,
16694
+ test: false
16636
16695
  },
16637
16696
  transformUrlContent: {
16638
16697
  html: (htmlUrlInfo, context) => {
@@ -16664,7 +16723,8 @@ const jsenvPluginAutoreloadServer = ({
16664
16723
  return {
16665
16724
  name: "jsenv:autoreload_server",
16666
16725
  appliesDuring: {
16667
- dev: true
16726
+ dev: true,
16727
+ test: false
16668
16728
  },
16669
16729
  serverEvents: {
16670
16730
  reload: ({
@@ -16892,14 +16952,9 @@ const jsenvPluginAutoreloadServer = ({
16892
16952
  };
16893
16953
 
16894
16954
  const jsenvPluginAutoreload = ({
16895
- scenario,
16896
16955
  clientFileChangeCallbackList,
16897
16956
  clientFilesPruneCallbackList
16898
16957
  }) => {
16899
- if (scenario === "build") {
16900
- return [];
16901
- }
16902
-
16903
16958
  return [jsenvPluginHmr(), jsenvPluginAutoreloadClient(), jsenvPluginAutoreloadServer({
16904
16959
  clientFileChangeCallbackList,
16905
16960
  clientFilesPruneCallbackList
@@ -16909,14 +16964,11 @@ const jsenvPluginAutoreload = ({
16909
16964
  const jsenvPluginCacheControl = () => {
16910
16965
  return {
16911
16966
  name: "jsenv:cache_control",
16912
- appliesDuring: {
16913
- dev: true,
16914
- test: true
16915
- },
16967
+ appliesDuring: "dev",
16916
16968
  augmentResponse: ({
16917
16969
  reference
16918
16970
  }, context) => {
16919
- if (context.scenario === "test") {
16971
+ if (context.scenarios.test) {
16920
16972
  // During dev, all files are put into browser cache for 1 hour because:
16921
16973
  // 1: Browser cache is a temporary directory created by playwright
16922
16974
  // 2: We assume source files won't be modified while tests are running
@@ -17003,7 +17055,6 @@ const jsenvPluginExplorer = ({
17003
17055
 
17004
17056
  const getCorePlugins = ({
17005
17057
  rootDirectoryUrl,
17006
- scenario,
17007
17058
  runtimeCompat,
17008
17059
  urlAnalysis = {},
17009
17060
  htmlSupervisor,
@@ -17048,7 +17099,6 @@ const getCorePlugins = ({
17048
17099
  jsenvPluginNodeEsmResolution(nodeEsmResolution), jsenvPluginUrlResolution(), jsenvPluginUrlVersion(), jsenvPluginCommonJsGlobals(), jsenvPluginImportMetaScenarios(), jsenvPluginNodeRuntime({
17049
17100
  runtimeCompat
17050
17101
  }), jsenvPluginBundling(bundling), jsenvPluginMinification(minification), jsenvPluginImportMetaHot(), ...(clientAutoreload ? [jsenvPluginAutoreload({ ...clientAutoreload,
17051
- scenario,
17052
17102
  clientFileChangeCallbackList,
17053
17103
  clientFilesPruneCallbackList
17054
17104
  })] : []), jsenvPluginCacheControl(), ...(explorer ? [jsenvPluginExplorer(explorer)] : [])];
@@ -18399,101 +18449,12 @@ const statusIsClientError = status => status >= 400 && status < 500;
18399
18449
 
18400
18450
  const statusIsServerError = status => status >= 500 && status < 600;
18401
18451
 
18402
- const applyDnsResolution = async (hostname, {
18403
- verbatim = false
18404
- } = {}) => {
18405
- const dnsResolution = await new Promise((resolve, reject) => {
18406
- lookup(hostname, {
18407
- verbatim
18408
- }, (error, address, family) => {
18409
- if (error) {
18410
- reject(error);
18411
- } else {
18412
- resolve({
18413
- address,
18414
- family
18415
- });
18416
- }
18417
- });
18418
- });
18419
- return dnsResolution;
18420
- };
18421
-
18422
- const getServerOrigins = async ({
18423
- protocol,
18424
- host,
18425
- port
18426
- }) => {
18427
- const isLocal = LOOPBACK_HOSTNAMES.includes(host);
18428
- const localhostDnsResolution = await applyDnsResolution("localhost");
18429
- const localOrigin = createServerOrigin({
18430
- protocol,
18431
- hostname: localhostDnsResolution.address === "127.0.0.1" ? "localhost" : "127.0.0.1",
18432
- port
18433
- });
18434
-
18435
- if (isLocal) {
18436
- return {
18437
- local: localOrigin
18438
- };
18439
- }
18440
-
18441
- const isAnyIp = WILDCARD_HOSTNAMES.includes(host);
18442
- const networkOrigin = createServerOrigin({
18443
- protocol,
18444
- hostname: isAnyIp ? getExternalIp() : host,
18445
- port
18446
- });
18447
- return {
18448
- local: localOrigin,
18449
- network: networkOrigin
18450
- };
18451
- };
18452
- const LOOPBACK_HOSTNAMES = ["localhost", "127.0.0.1", "::1", "0000:0000:0000:0000:0000:0000:0000:0001"];
18453
- const WILDCARD_HOSTNAMES = [undefined, "0.0.0.0", "::", "0000:0000:0000:0000:0000:0000:0000:0000"];
18454
-
18455
- const createServerOrigin = ({
18456
- protocol,
18457
- hostname,
18458
- port
18459
- }) => {
18460
- const url = new URL("https://127.0.0.1:80");
18461
- url.protocol = protocol;
18462
- url.hostname = hostname;
18463
- url.port = port;
18464
- return url.origin;
18465
- };
18466
-
18467
- const getExternalIp = () => {
18468
- const networkInterfaceMap = networkInterfaces();
18469
- let internalIPV4NetworkAddress;
18470
- Object.keys(networkInterfaceMap).find(key => {
18471
- const networkAddressArray = networkInterfaceMap[key];
18472
- return networkAddressArray.find(networkAddress => {
18473
- if (networkAddress.internal) return false;
18474
- if (!isIpV4(networkAddress)) return false;
18475
- internalIPV4NetworkAddress = networkAddress;
18476
- return true;
18477
- });
18478
- });
18479
- return internalIPV4NetworkAddress ? internalIPV4NetworkAddress.address : null;
18480
- };
18481
-
18482
- const isIpV4 = networkAddress => {
18483
- // node 18+
18484
- if (typeof networkAddress.family === "number") {
18485
- return networkAddress.family === 4;
18486
- }
18487
-
18488
- return networkAddress.family === "IPv4";
18489
- };
18490
-
18491
18452
  const listen = async ({
18492
18453
  signal = new AbortController().signal,
18493
18454
  server,
18494
18455
  port,
18495
18456
  portHint,
18496
- host
18457
+ hostname
18497
18458
  }) => {
18498
18459
  const listeningOperation = Abort.startOperation();
18499
18460
 
@@ -18504,7 +18465,7 @@ const listen = async ({
18504
18465
  listeningOperation.throwIfAborted();
18505
18466
  port = await findFreePort(portHint, {
18506
18467
  signal: listeningOperation.signal,
18507
- host
18468
+ hostname
18508
18469
  });
18509
18470
  }
18510
18471
 
@@ -18512,7 +18473,7 @@ const listen = async ({
18512
18473
  port = await startListening({
18513
18474
  server,
18514
18475
  port,
18515
- host
18476
+ hostname
18516
18477
  });
18517
18478
  listeningOperation.addAbortCallback(() => stopListening(server));
18518
18479
  listeningOperation.throwIfAborted();
@@ -18524,7 +18485,7 @@ const listen = async ({
18524
18485
 
18525
18486
  const findFreePort = async (initialPort = 1, {
18526
18487
  signal = new AbortController().signal,
18527
- host = "127.0.0.1",
18488
+ hostname = "127.0.0.1",
18528
18489
  min = 1,
18529
18490
  max = 65534,
18530
18491
  next = port => port + 1
@@ -18546,27 +18507,27 @@ const findFreePort = async (initialPort = 1, {
18546
18507
  const nextPort = next(port);
18547
18508
 
18548
18509
  if (nextPort > max) {
18549
- throw new Error(`${host} has no available port between ${min} and ${max}`);
18510
+ throw new Error(`${hostname} has no available port between ${min} and ${max}`);
18550
18511
  }
18551
18512
 
18552
- return testUntil(nextPort, host);
18513
+ return testUntil(nextPort, hostname);
18553
18514
  };
18554
18515
 
18555
- const freePort = await testUntil(initialPort, host);
18516
+ const freePort = await testUntil(initialPort, hostname);
18556
18517
  return freePort;
18557
18518
  } finally {
18558
18519
  await findFreePortOperation.end();
18559
18520
  }
18560
18521
  };
18561
18522
 
18562
- const portIsFree = async (port, host) => {
18523
+ const portIsFree = async (port, hostname) => {
18563
18524
  const server = createServer();
18564
18525
 
18565
18526
  try {
18566
18527
  await startListening({
18567
18528
  server,
18568
18529
  port,
18569
- host
18530
+ hostname
18570
18531
  });
18571
18532
  } catch (error) {
18572
18533
  if (error && error.code === "EADDRINUSE") {
@@ -18587,7 +18548,7 @@ const portIsFree = async (port, host) => {
18587
18548
  const startListening = ({
18588
18549
  server,
18589
18550
  port,
18590
- host
18551
+ hostname
18591
18552
  }) => {
18592
18553
  return new Promise((resolve, reject) => {
18593
18554
  server.on("error", reject);
@@ -18596,7 +18557,7 @@ const startListening = ({
18596
18557
  // https://nodejs.org/api/net.html#net_server_listen_port_host_backlog_callback
18597
18558
  resolve(server.address().port);
18598
18559
  });
18599
- server.listen(port, host);
18560
+ server.listen(port, hostname);
18600
18561
  });
18601
18562
  };
18602
18563
 
@@ -18835,6 +18796,134 @@ const STOP_REASON_PROCESS_BEFORE_EXIT = createReason("process before exit");
18835
18796
  const STOP_REASON_PROCESS_EXIT = createReason("process exit");
18836
18797
  const STOP_REASON_NOT_SPECIFIED = createReason("not specified");
18837
18798
 
18799
+ const createIpGetters = () => {
18800
+ const networkAddresses = [];
18801
+ const networkInterfaceMap = networkInterfaces();
18802
+
18803
+ for (const key of Object.keys(networkInterfaceMap)) {
18804
+ for (const networkAddress of networkInterfaceMap[key]) {
18805
+ networkAddresses.push(networkAddress);
18806
+ }
18807
+ }
18808
+
18809
+ return {
18810
+ getFirstInternalIp: ({
18811
+ preferIpv6
18812
+ }) => {
18813
+ const isPref = preferIpv6 ? isIpV6 : isIpV4;
18814
+ let firstInternalIp;
18815
+
18816
+ for (const networkAddress of networkAddresses) {
18817
+ if (networkAddress.internal) {
18818
+ firstInternalIp = networkAddress.address;
18819
+
18820
+ if (isPref(networkAddress)) {
18821
+ break;
18822
+ }
18823
+ }
18824
+ }
18825
+
18826
+ return firstInternalIp;
18827
+ },
18828
+ getFirstExternalIp: ({
18829
+ preferIpv6
18830
+ }) => {
18831
+ const isPref = preferIpv6 ? isIpV6 : isIpV4;
18832
+ let firstExternalIp;
18833
+
18834
+ for (const networkAddress of networkAddresses) {
18835
+ if (!networkAddress.internal) {
18836
+ firstExternalIp = networkAddress.address;
18837
+
18838
+ if (isPref(networkAddress)) {
18839
+ break;
18840
+ }
18841
+ }
18842
+ }
18843
+
18844
+ return firstExternalIp;
18845
+ }
18846
+ };
18847
+ };
18848
+
18849
+ const isIpV4 = networkAddress => {
18850
+ // node 18.5
18851
+ if (typeof networkAddress.family === "number") {
18852
+ return networkAddress.family === 4;
18853
+ }
18854
+
18855
+ return networkAddress.family === "IPv4";
18856
+ };
18857
+
18858
+ const isIpV6 = networkAddress => !isIpV4(networkAddress);
18859
+
18860
+ const parseHostname = hostname => {
18861
+ if (hostname === "0.0.0.0") {
18862
+ return {
18863
+ type: "ip",
18864
+ label: "unspecified",
18865
+ version: 4
18866
+ };
18867
+ }
18868
+
18869
+ if (hostname === "::" || hostname === "0000:0000:0000:0000:0000:0000:0000:0000") {
18870
+ return {
18871
+ type: "ip",
18872
+ label: "unspecified",
18873
+ version: 6
18874
+ };
18875
+ }
18876
+
18877
+ if (hostname === "127.0.0.1") {
18878
+ return {
18879
+ type: "ip",
18880
+ label: "loopback",
18881
+ version: 4
18882
+ };
18883
+ }
18884
+
18885
+ if (hostname === "::1" || hostname === "0000:0000:0000:0000:0000:0000:0000:0001") {
18886
+ return {
18887
+ type: "ip",
18888
+ label: "loopback",
18889
+ version: 6
18890
+ };
18891
+ }
18892
+
18893
+ const ipVersion = isIP(hostname);
18894
+
18895
+ if (ipVersion === 0) {
18896
+ return {
18897
+ type: "hostname"
18898
+ };
18899
+ }
18900
+
18901
+ return {
18902
+ type: "ip",
18903
+ version: ipVersion
18904
+ };
18905
+ };
18906
+
18907
+ const applyDnsResolution = async (hostname, {
18908
+ verbatim = false
18909
+ } = {}) => {
18910
+ const dnsResolution = await new Promise((resolve, reject) => {
18911
+ lookup(hostname, {
18912
+ verbatim
18913
+ }, (error, address, family) => {
18914
+ if (error) {
18915
+ reject(error);
18916
+ } else {
18917
+ resolve({
18918
+ address,
18919
+ family
18920
+ });
18921
+ }
18922
+ });
18923
+ });
18924
+ return dnsResolution;
18925
+ };
18926
+
18838
18927
  const startServer = async ({
18839
18928
  signal = new AbortController().signal,
18840
18929
  logLevel,
@@ -18846,7 +18935,8 @@ const startServer = async ({
18846
18935
  redirectHttpToHttps,
18847
18936
  allowHttpRequestOnHttps = false,
18848
18937
  acceptAnyIp = false,
18849
- host = acceptAnyIp ? undefined : "localhost",
18938
+ preferIpv6,
18939
+ hostname = "localhost",
18850
18940
  port = 0,
18851
18941
  // assign a random available port
18852
18942
  portHint,
@@ -18876,6 +18966,10 @@ const startServer = async ({
18876
18966
  }));
18877
18967
  }
18878
18968
  } = {}) => {
18969
+ const logger = createLogger({
18970
+ logLevel
18971
+ });
18972
+
18879
18973
  if (protocol !== "http" && protocol !== "https") {
18880
18974
  throw new Error(`protocol must be http or https, got ${protocol}`);
18881
18975
  }
@@ -18894,10 +18988,6 @@ const startServer = async ({
18894
18988
  throw new Error(`http2 needs "https" but protocol is "${protocol}"`);
18895
18989
  }
18896
18990
 
18897
- const logger = createLogger({
18898
- logLevel
18899
- });
18900
-
18901
18991
  if (redirectHttpToHttps === undefined && protocol === "https" && !allowHttpRequestOnHttps) {
18902
18992
  redirectHttpToHttps = true;
18903
18993
  }
@@ -18930,6 +19020,10 @@ const startServer = async ({
18930
19020
  let nodeServer;
18931
19021
  const startServerOperation = Abort.startOperation();
18932
19022
  const stopCallbackList = createCallbackListNotifiedOnce();
19023
+ const serverOrigins = {
19024
+ local: "" // favors hostname when possible
19025
+
19026
+ };
18933
19027
 
18934
19028
  try {
18935
19029
  startServerOperation.addAbortSignal(signal);
@@ -18957,12 +19051,85 @@ const startServer = async ({
18957
19051
  nodeServer.unref();
18958
19052
  }
18959
19053
 
19054
+ const createOrigin = hostname => {
19055
+ if (isIP(hostname) === 6) {
19056
+ return `${protocol}://[${hostname}]`;
19057
+ }
19058
+
19059
+ return `${protocol}://${hostname}`;
19060
+ };
19061
+
19062
+ const ipGetters = createIpGetters();
19063
+ let hostnameToListen;
19064
+
19065
+ if (acceptAnyIp) {
19066
+ const firstInternalIp = ipGetters.getFirstInternalIp({
19067
+ preferIpv6
19068
+ });
19069
+ serverOrigins.local = createOrigin(firstInternalIp);
19070
+ serverOrigins.localip = createOrigin(firstInternalIp);
19071
+ const firstExternalIp = ipGetters.getFirstExternalIp({
19072
+ preferIpv6
19073
+ });
19074
+ serverOrigins.externalip = createOrigin(firstExternalIp);
19075
+ hostnameToListen = preferIpv6 ? "::" : "0.0.0.0";
19076
+ } else {
19077
+ hostnameToListen = hostname;
19078
+ }
19079
+
19080
+ const hostnameInfo = parseHostname(hostname);
19081
+
19082
+ if (hostnameInfo.type === "ip") {
19083
+ if (acceptAnyIp) {
19084
+ throw new Error(`hostname cannot be an ip when acceptAnyIp is enabled, got ${hostname}`);
19085
+ }
19086
+
19087
+ preferIpv6 = hostnameInfo.version === 6;
19088
+ const firstInternalIp = ipGetters.getFirstInternalIp({
19089
+ preferIpv6
19090
+ });
19091
+ serverOrigins.local = createOrigin(firstInternalIp);
19092
+ serverOrigins.localip = createOrigin(firstInternalIp);
19093
+
19094
+ if (hostnameInfo.label === "unspecified") {
19095
+ const firstExternalIp = ipGetters.getFirstExternalIp({
19096
+ preferIpv6
19097
+ });
19098
+ serverOrigins.externalip = createOrigin(firstExternalIp);
19099
+ } else if (hostnameInfo.label === "loopback") {} else {
19100
+ serverOrigins.local = createOrigin(hostname);
19101
+ }
19102
+ } else {
19103
+ const hostnameDnsResolution = await applyDnsResolution(hostname, {
19104
+ verbatim: true
19105
+ });
19106
+
19107
+ if (hostnameDnsResolution) {
19108
+ const hostnameIp = hostnameDnsResolution.address;
19109
+ serverOrigins.localip = createOrigin(hostnameIp);
19110
+ serverOrigins.local = createOrigin(hostname);
19111
+ } else {
19112
+ const firstInternalIp = ipGetters.getFirstInternalIp({
19113
+ preferIpv6
19114
+ }); // fallback to internal ip because there is no ip
19115
+ // associated to this hostname on operating system (in hosts file)
19116
+
19117
+ hostname = firstInternalIp;
19118
+ hostnameToListen = firstInternalIp;
19119
+ serverOrigins.local = createOrigin(firstInternalIp);
19120
+ }
19121
+ }
19122
+
18960
19123
  port = await listen({
18961
19124
  signal: startServerOperation.signal,
18962
19125
  server: nodeServer,
18963
19126
  port,
18964
19127
  portHint,
18965
- host
19128
+ hostname: hostnameToListen
19129
+ }); // normalize origins (remove :80 when port is 80 for instance)
19130
+
19131
+ Object.keys(serverOrigins).forEach(key => {
19132
+ serverOrigins[key] = new URL(`${serverOrigins[key]}:${port}`).origin;
18966
19133
  });
18967
19134
  serviceController.callHooks("serverListening", {
18968
19135
  port
@@ -18973,12 +19140,23 @@ const startServer = async ({
18973
19140
  startServerOperation.throwIfAborted();
18974
19141
  } finally {
18975
19142
  await startServerOperation.end();
18976
- } // now the server is started (listening) it cannot be aborted anymore
19143
+ } // the main server origin
19144
+ // - when protocol is http
19145
+ // node-fetch do not apply local dns resolution to map localhost back to 127.0.0.1
19146
+ // despites localhost being mapped so we prefer to use the internal ip
19147
+ // (127.0.0.1)
19148
+ // - when protocol is https
19149
+ // using the hostname becomes important because the certificate is generated
19150
+ // for hostnames, not for ips
19151
+ // so we prefer https://locahost or https://local_hostname
19152
+ // over the ip
19153
+
19154
+
19155
+ const serverOrigin = serverOrigins.local; // now the server is started (listening) it cannot be aborted anymore
18977
19156
  // (otherwise an AbortError is thrown to the code calling "startServer")
18978
19157
  // we can proceed to create a stop function to stop it gacefully
18979
19158
  // and add a request handler
18980
19159
 
18981
-
18982
19160
  stopCallbackList.add(({
18983
19161
  reason
18984
19162
  }) => {
@@ -19016,12 +19194,6 @@ const startServer = async ({
19016
19194
  };
19017
19195
 
19018
19196
  status = "opened";
19019
- const serverOrigins = await getServerOrigins({
19020
- protocol,
19021
- host,
19022
- port
19023
- });
19024
- const serverOrigin = serverOrigins.local;
19025
19197
  const removeConnectionErrorListener = listenServerConnectionError(nodeServer, onError);
19026
19198
  stopCallbackList.add(removeConnectionErrorListener);
19027
19199
  const connectionsTracker = trackServerPendingConnections(nodeServer, {
@@ -19641,7 +19813,7 @@ const startServer = async ({
19641
19813
  let websocketServer = new WebSocketServer({
19642
19814
  noServer: true
19643
19815
  });
19644
- const websocketOrigin = protocol === "https" ? `wss://${host}:${port}` : `ws://${host}:${port}`;
19816
+ const websocketOrigin = protocol === "https" ? `wss://${hostname}:${port}` : `ws://${hostname}:${port}`;
19645
19817
  server.websocketOrigin = websocketOrigin;
19646
19818
 
19647
19819
  const upgradeCallback = (nodeRequest, socket, head) => {
@@ -19682,6 +19854,7 @@ const startServer = async ({
19682
19854
  Object.assign(server, {
19683
19855
  getStatus: () => status,
19684
19856
  port,
19857
+ hostname,
19685
19858
  origin: serverOrigin,
19686
19859
  origins: serverOrigins,
19687
19860
  nodeServer,
@@ -20743,10 +20916,10 @@ const HOOK_NAMES = ["init", "serve", // is called only during dev/tests
20743
20916
  "destroy"];
20744
20917
  const createPluginController = ({
20745
20918
  plugins,
20746
- scenario
20919
+ scenarios
20747
20920
  }) => {
20748
20921
  const flatPlugins = flattenAndFilterPlugins(plugins, {
20749
- scenario
20922
+ scenarios
20750
20923
  }); // precompute a list of hooks per hookName for one major reason:
20751
20924
  // - When debugging, there is less iteration
20752
20925
  // also it should increase perf as there is less work to do
@@ -20953,7 +21126,7 @@ const createPluginController = ({
20953
21126
  };
20954
21127
 
20955
21128
  const flattenAndFilterPlugins = (plugins, {
20956
- scenario
21129
+ scenarios
20957
21130
  }) => {
20958
21131
  const flatPlugins = [];
20959
21132
 
@@ -20983,12 +21156,13 @@ const flattenAndFilterPlugins = (plugins, {
20983
21156
  }
20984
21157
 
20985
21158
  if (typeof appliesDuring === "string") {
20986
- if (!["dev", "build", "test"].includes(appliesDuring)) {
21159
+ if (!["dev", "test", "build"].includes(appliesDuring)) {
20987
21160
  throw new Error(`"appliesDuring" must be "dev", "test" or "build", got ${appliesDuring}`);
20988
21161
  }
20989
21162
 
20990
- if (appliesDuring === scenario) {
21163
+ if (scenarios[appliesDuring]) {
20991
21164
  flatPlugins.push(pluginEntry);
21165
+ return;
20992
21166
  }
20993
21167
 
20994
21168
  return;
@@ -20998,7 +21172,20 @@ const flattenAndFilterPlugins = (plugins, {
20998
21172
  throw new Error(`"appliesDuring" must be an object or a string, got ${appliesDuring}`);
20999
21173
  }
21000
21174
 
21001
- if (appliesDuring[scenario]) {
21175
+ let applies;
21176
+
21177
+ for (const key of Object.keys(appliesDuring)) {
21178
+ if (!appliesDuring[key] && scenarios[key]) {
21179
+ applies = false;
21180
+ break;
21181
+ }
21182
+
21183
+ if (appliesDuring[key] && scenarios[key]) {
21184
+ applies = true;
21185
+ }
21186
+ }
21187
+
21188
+ if (applies) {
21002
21189
  flatPlugins.push(pluginEntry);
21003
21190
  return;
21004
21191
  }
@@ -21710,19 +21897,15 @@ const createKitchen = ({
21710
21897
  signal,
21711
21898
  logLevel,
21712
21899
  rootDirectoryUrl,
21713
- scenario,
21900
+ scenarios,
21714
21901
  runtimeCompat,
21715
21902
  // during dev/test clientRuntimeCompat is a single runtime
21716
21903
  // during build clientRuntimeCompat is runtimeCompat
21717
21904
  clientRuntimeCompat = runtimeCompat,
21718
21905
  urlGraph,
21719
21906
  plugins,
21720
- sourcemaps = {
21721
- dev: "inline",
21722
- // "programmatic" and "file" also allowed
21723
- test: "inline",
21724
- build: "none"
21725
- }[scenario],
21907
+ sourcemaps = scenarios.dev ? "inline" : "none",
21908
+ // "programmatic" and "file" also allowed
21726
21909
  sourcemapsSourcesProtocol,
21727
21910
  sourcemapsSourcesContent,
21728
21911
  sourcemapsRelativeSources,
@@ -21733,7 +21916,7 @@ const createKitchen = ({
21733
21916
  });
21734
21917
  const pluginController = createPluginController({
21735
21918
  plugins,
21736
- scenario
21919
+ scenarios
21737
21920
  });
21738
21921
  const jsenvDirectoryUrl = new URL(".jsenv/", rootDirectoryUrl).href;
21739
21922
  const kitchenContext = {
@@ -21741,7 +21924,7 @@ const createKitchen = ({
21741
21924
  logger,
21742
21925
  rootDirectoryUrl,
21743
21926
  urlGraph,
21744
- scenario,
21927
+ scenarios,
21745
21928
  runtimeCompat,
21746
21929
  clientRuntimeCompat,
21747
21930
  isSupportedOnCurrentClients: feature => {
@@ -22446,7 +22629,7 @@ const applyReferenceEffectsOnUrlInfo = (reference, urlInfo, context) => {
22446
22629
  column: reference.specifierColumn
22447
22630
  };
22448
22631
  urlInfo.contentType = reference.contentType;
22449
- urlInfo.originalContent = context.scenario === "build" ? urlInfo.originalContent === undefined ? reference.content : urlInfo.originalContent : reference.content;
22632
+ urlInfo.originalContent = context.scenarios.build ? urlInfo.originalContent === undefined ? reference.content : urlInfo.originalContent : reference.content;
22450
22633
  urlInfo.content = reference.content;
22451
22634
  }
22452
22635
  };
@@ -23581,7 +23764,9 @@ build ${entryPointKeys.length} entry points`);
23581
23764
  logger,
23582
23765
  rootDirectoryUrl,
23583
23766
  urlGraph: rawGraph,
23584
- scenario: "build",
23767
+ scenarios: {
23768
+ build: true
23769
+ },
23585
23770
  sourcemaps,
23586
23771
  sourcemapsSourcesContent,
23587
23772
  runtimeCompat,
@@ -23603,7 +23788,6 @@ build ${entryPointKeys.length} entry points`);
23603
23788
  }, ...getCorePlugins({
23604
23789
  rootDirectoryUrl,
23605
23790
  urlGraph: rawGraph,
23606
- scenario: "build",
23607
23791
  runtimeCompat,
23608
23792
  urlAnalysis,
23609
23793
  nodeEsmResolution,
@@ -23821,7 +24005,9 @@ build ${entryPointKeys.length} entry points`);
23821
24005
  logger,
23822
24006
  rootDirectoryUrl,
23823
24007
  urlGraph: finalGraph,
23824
- scenario: "build",
24008
+ scenarios: {
24009
+ build: true
24010
+ },
23825
24011
  sourcemaps,
23826
24012
  sourcemapsSourcesContent,
23827
24013
  sourcemapsRelativeSources: !versioning,
@@ -24483,7 +24669,9 @@ const applyUrlVersioning = async ({
24483
24669
  logger,
24484
24670
  rootDirectoryUrl: buildDirectoryUrl,
24485
24671
  urlGraph: finalGraph,
24486
- scenario: "build",
24672
+ scenarios: {
24673
+ build: true
24674
+ },
24487
24675
  sourcemaps,
24488
24676
  sourcemapsSourcesContent,
24489
24677
  sourcemapsRelativeSources: true,
@@ -24496,9 +24684,7 @@ const applyUrlVersioning = async ({
24496
24684
  allowEscapeForVersioning: true
24497
24685
  }), {
24498
24686
  name: "jsenv:versioning",
24499
- appliesDuring: {
24500
- build: true
24501
- },
24687
+ appliesDuring: "build",
24502
24688
  resolveUrl: reference => {
24503
24689
  const buildUrl = buildUrls[reference.specifier];
24504
24690
 
@@ -24893,7 +25079,7 @@ const createFileService = ({
24893
25079
  serverStopCallbacks,
24894
25080
  serverEventsDispatcher,
24895
25081
  rootDirectoryUrl,
24896
- scenario,
25082
+ scenarios,
24897
25083
  runtimeCompat,
24898
25084
  plugins,
24899
25085
  urlAnalysis,
@@ -24931,7 +25117,7 @@ const createFileService = ({
24931
25117
  ".jsenv/": false
24932
25118
  };
24933
25119
 
24934
- if (scenario === "dev") {
25120
+ if (scenarios.dev) {
24935
25121
  const stopWatchingClientFiles = registerDirectoryLifecycle(rootDirectoryUrl, {
24936
25122
  watchPatterns: clientFilePatterns,
24937
25123
  cooldownBetweenFileEvents,
@@ -24995,13 +25181,13 @@ const createFileService = ({
24995
25181
 
24996
25182
  urlInfo.isValid = () => watch;
24997
25183
  },
24998
- includeOriginalUrls: scenario === "dev"
25184
+ includeOriginalUrls: scenarios.dev
24999
25185
  });
25000
25186
  const kitchen = createKitchen({
25001
25187
  signal,
25002
25188
  logLevel,
25003
25189
  rootDirectoryUrl,
25004
- scenario,
25190
+ scenarios,
25005
25191
  runtimeCompat,
25006
25192
  clientRuntimeCompat: {
25007
25193
  [runtimeName]: runtimeVersion
@@ -25009,7 +25195,6 @@ const createFileService = ({
25009
25195
  urlGraph,
25010
25196
  plugins: [...plugins, ...getCorePlugins({
25011
25197
  rootDirectoryUrl,
25012
- scenario,
25013
25198
  runtimeCompat,
25014
25199
  urlAnalysis,
25015
25200
  htmlSupervisor,
@@ -25052,7 +25237,7 @@ const createFileService = ({
25052
25237
  allServerEvents[serverEventName]({
25053
25238
  rootDirectoryUrl,
25054
25239
  urlGraph,
25055
- scenario,
25240
+ scenarios,
25056
25241
  sendServerEvent: data => {
25057
25242
  serverEventsDispatcher.dispatch({
25058
25243
  type: serverEventName,
@@ -25068,7 +25253,7 @@ const createFileService = ({
25068
25253
 
25069
25254
  const context = {
25070
25255
  rootDirectoryUrl,
25071
- scenario,
25256
+ scenarios,
25072
25257
  runtimeName,
25073
25258
  runtimeVersion,
25074
25259
  urlGraph,
@@ -25155,7 +25340,7 @@ const createFileService = ({
25155
25340
  await kitchen.cook(urlInfo, {
25156
25341
  request,
25157
25342
  reference,
25158
- outDirectoryUrl: scenario === "dev" ? `${rootDirectoryUrl}.jsenv/${runtimeName}@${runtimeVersion}/` : `${rootDirectoryUrl}.jsenv/${scenario}/${runtimeName}@${runtimeVersion}/`
25343
+ outDirectoryUrl: scenarios.dev ? `${rootDirectoryUrl}.jsenv/${runtimeName}@${runtimeVersion}/` : `${rootDirectoryUrl}.jsenv/${scenarios.test ? "test" : "build"}/${runtimeName}@${runtimeVersion}/`
25159
25344
  });
25160
25345
  let {
25161
25346
  response
@@ -25281,13 +25466,13 @@ const startOmegaServer = async ({
25281
25466
  privateKey,
25282
25467
  certificate,
25283
25468
  acceptAnyIp,
25284
- host,
25469
+ hostname,
25285
25470
  port = 0,
25286
25471
  keepProcessAlive = false,
25287
25472
  onStop = () => {},
25288
25473
  services = [],
25289
25474
  rootDirectoryUrl,
25290
- scenario,
25475
+ scenarios,
25291
25476
  runtimeCompat,
25292
25477
  plugins,
25293
25478
  urlAnalysis,
@@ -25322,7 +25507,7 @@ const startOmegaServer = async ({
25322
25507
  certificate,
25323
25508
  privateKey,
25324
25509
  acceptAnyIp,
25325
- host,
25510
+ hostname,
25326
25511
  port,
25327
25512
  requestWaitingMs: 60_1000,
25328
25513
  services: [jsenvServiceCORS({
@@ -25340,7 +25525,7 @@ const startOmegaServer = async ({
25340
25525
  serverStopCallbacks,
25341
25526
  serverEventsDispatcher,
25342
25527
  rootDirectoryUrl,
25343
- scenario,
25528
+ scenarios,
25344
25529
  runtimeCompat,
25345
25530
  plugins,
25346
25531
  urlAnalysis,
@@ -25428,7 +25613,7 @@ const startDevServer = async ({
25428
25613
  http2 = false,
25429
25614
  certificate,
25430
25615
  privateKey,
25431
- host,
25616
+ hostname,
25432
25617
  port = 3456,
25433
25618
  acceptAnyIp,
25434
25619
  keepProcessAlive = true,
@@ -25568,11 +25753,13 @@ const startDevServer = async ({
25568
25753
  http2,
25569
25754
  certificate,
25570
25755
  privateKey,
25571
- host,
25756
+ hostname,
25572
25757
  port,
25573
25758
  services,
25574
25759
  rootDirectoryUrl,
25575
- scenario: "dev",
25760
+ scenarios: {
25761
+ dev: true
25762
+ },
25576
25763
  runtimeCompat,
25577
25764
  plugins,
25578
25765
  urlAnalysis,
@@ -25669,87 +25856,6 @@ const generateCoverageTextLog = (coverage, {
25669
25856
  report.execute(context);
25670
25857
  };
25671
25858
 
25672
- const babelPluginInstrument = (api, {
25673
- rootDirectoryUrl,
25674
- useInlineSourceMaps = false,
25675
- coverageConfig = {
25676
- "./**/*": true
25677
- }
25678
- }) => {
25679
- const {
25680
- programVisitor
25681
- } = requireFromJsenv("istanbul-lib-instrument");
25682
- const {
25683
- types
25684
- } = api;
25685
- const associations = URL_META.resolveAssociations({
25686
- cover: coverageConfig
25687
- }, rootDirectoryUrl);
25688
-
25689
- const shouldInstrument = url => {
25690
- return URL_META.applyAssociations({
25691
- url,
25692
- associations
25693
- }).cover;
25694
- };
25695
-
25696
- return {
25697
- name: "transform-instrument",
25698
- visitor: {
25699
- Program: {
25700
- enter(path) {
25701
- const {
25702
- file
25703
- } = this;
25704
- const {
25705
- opts
25706
- } = file;
25707
-
25708
- if (!opts.sourceFileName) {
25709
- console.warn(`cannot instrument file when "sourceFileName" option is not set`);
25710
- return;
25711
- }
25712
-
25713
- const fileUrl = fileSystemPathToUrl$1(opts.sourceFileName);
25714
-
25715
- if (!shouldInstrument(fileUrl)) {
25716
- return;
25717
- }
25718
-
25719
- this.__dv__ = null;
25720
- let inputSourceMap;
25721
-
25722
- if (useInlineSourceMaps) {
25723
- // https://github.com/istanbuljs/babel-plugin-istanbul/commit/a9e15643d249a2985e4387e4308022053b2cd0ad#diff-1fdf421c05c1140f6d71444ea2b27638R65
25724
- inputSourceMap = opts.inputSourceMap || file.inputMap ? file.inputMap.sourcemap : null;
25725
- } else {
25726
- inputSourceMap = opts.inputSourceMap;
25727
- }
25728
-
25729
- this.__dv__ = programVisitor(types, opts.filenameRelative || opts.filename, {
25730
- coverageVariable: "__coverage__",
25731
- inputSourceMap
25732
- });
25733
-
25734
- this.__dv__.enter(path);
25735
- },
25736
-
25737
- exit(path) {
25738
- if (!this.__dv__) {
25739
- return;
25740
- }
25741
-
25742
- const object = this.__dv__.exit(path); // object got two properties: fileCoverage and sourceMappingURL
25743
-
25744
-
25745
- this.file.metadata.coverage = object.fileCoverage;
25746
- }
25747
-
25748
- }
25749
- }
25750
- };
25751
- };
25752
-
25753
25859
  const readNodeV8CoverageDirectory = async ({
25754
25860
  logger,
25755
25861
  signal,
@@ -26442,6 +26548,41 @@ const run = async ({
26442
26548
  }
26443
26549
  };
26444
26550
 
26551
+ const pingServer = async url => {
26552
+ const server = createServer();
26553
+ const {
26554
+ hostname,
26555
+ port
26556
+ } = new URL(url);
26557
+
26558
+ try {
26559
+ await new Promise((resolve, reject) => {
26560
+ server.on("error", reject);
26561
+ server.on("listening", () => {
26562
+ resolve();
26563
+ });
26564
+ server.listen(port, hostname);
26565
+ });
26566
+ } catch (error) {
26567
+ if (error && error.code === "EADDRINUSE") {
26568
+ return true;
26569
+ }
26570
+
26571
+ if (error && error.code === "EACCES") {
26572
+ return true;
26573
+ }
26574
+
26575
+ throw error;
26576
+ }
26577
+
26578
+ await new Promise((resolve, reject) => {
26579
+ server.on("error", reject);
26580
+ server.on("close", resolve);
26581
+ server.close();
26582
+ });
26583
+ return false;
26584
+ };
26585
+
26445
26586
  const ensureGlobalGc = () => {
26446
26587
  if (!global.gc) {
26447
26588
  v8.setFlagsFromString("--expose_gc");
@@ -26877,8 +27018,8 @@ const executePlan = async (plan, {
26877
27018
  completedExecutionLogMerging,
26878
27019
  completedExecutionLogAbbreviation,
26879
27020
  rootDirectoryUrl,
27021
+ devServerOrigin,
26880
27022
  keepRunning,
26881
- services,
26882
27023
  defaultMsAllocatedPerExecution,
26883
27024
  maxExecutionsInParallel,
26884
27025
  failFast,
@@ -26891,18 +27032,6 @@ const executePlan = async (plan, {
26891
27032
  coverageMethodForNodeJs,
26892
27033
  coverageV8ConflictWarning,
26893
27034
  coverageTempDirectoryRelativeUrl,
26894
- scenario,
26895
- sourcemaps,
26896
- plugins,
26897
- nodeEsmResolution,
26898
- fileSystemMagicResolution,
26899
- transpilation,
26900
- writeGeneratedFiles,
26901
- protocol,
26902
- privateKey,
26903
- certificate,
26904
- host,
26905
- port,
26906
27035
  beforeExecutionCallback = () => {},
26907
27036
  afterExecutionCallback = () => {}
26908
27037
  } = {}) => {
@@ -26926,7 +27055,7 @@ const executePlan = async (plan, {
26926
27055
  if (runtime) {
26927
27056
  runtimes[runtime.name] = runtime.version;
26928
27057
 
26929
- if (runtime.needsServer) {
27058
+ if (runtime.type === "browser") {
26930
27059
  someNeedsServer = true;
26931
27060
  }
26932
27061
 
@@ -27017,6 +27146,7 @@ const executePlan = async (plan, {
27017
27146
 
27018
27147
  let runtimeParams = {
27019
27148
  rootDirectoryUrl,
27149
+ devServerOrigin,
27020
27150
  coverageEnabled,
27021
27151
  coverageConfig,
27022
27152
  coverageMethodForBrowsers,
@@ -27025,48 +27155,15 @@ const executePlan = async (plan, {
27025
27155
  };
27026
27156
 
27027
27157
  if (someNeedsServer) {
27028
- const server = await startOmegaServer({
27029
- signal: multipleExecutionsOperation.signal,
27030
- logLevel: "warn",
27031
- keepProcessAlive: false,
27032
- port,
27033
- host,
27034
- protocol,
27035
- certificate,
27036
- privateKey,
27037
- services,
27038
- rootDirectoryUrl,
27039
- scenario,
27040
- runtimeCompat: runtimes,
27041
- plugins,
27042
- htmlSupervisor: true,
27043
- nodeEsmResolution,
27044
- fileSystemMagicResolution,
27045
- transpilation: { ...transpilation,
27046
- getCustomBabelPlugins: ({
27047
- clientRuntimeCompat
27048
- }) => {
27049
- if (coverageEnabled && (coverageMethodForBrowsers !== "playwright_api" || Object.keys(clientRuntimeCompat)[0] !== "chrome")) {
27050
- return {
27051
- "transform-instrument": [babelPluginInstrument, {
27052
- rootDirectoryUrl,
27053
- coverageConfig
27054
- }]
27055
- };
27056
- }
27158
+ if (!devServerOrigin) {
27159
+ throw new TypeError(`devServerOrigin is required when running tests on browser(s)`);
27160
+ }
27057
27161
 
27058
- return {};
27059
- }
27060
- },
27061
- sourcemaps,
27062
- writeGeneratedFiles
27063
- });
27064
- multipleExecutionsOperation.addEndCallback(async () => {
27065
- await server.stop();
27066
- });
27067
- runtimeParams = { ...runtimeParams,
27068
- server
27069
- };
27162
+ const devServerStarted = await pingServer(devServerOrigin);
27163
+
27164
+ if (!devServerStarted) {
27165
+ throw new Error(`dev server not started at ${devServerOrigin}. It is required to run tests`);
27166
+ }
27070
27167
  }
27071
27168
 
27072
27169
  logger.debug(`Generate executions`);
@@ -27409,9 +27506,10 @@ const executeInParallel = async ({
27409
27506
  };
27410
27507
 
27411
27508
  /**
27412
- * Execute a list of files and log how it goes
27509
+ * Execute a list of files and log how it goes.
27413
27510
  * @param {Object} testPlanParameters
27414
27511
  * @param {string|url} testPlanParameters.rootDirectoryUrl Root directory of the project
27512
+ * @param {string|url} [testPlanParameters.serverOrigin=undefined] Jsenv dev server origin; required when executing test on browsers
27415
27513
  * @param {Object} testPlanParameters.testPlan Object associating patterns leading to files to runtimes where they should be executed
27416
27514
  * @param {boolean} [testPlanParameters.completedExecutionLogAbbreviation=false] Abbreviate completed execution information to shorten terminal output
27417
27515
  * @param {boolean} [testPlanParameters.completedExecutionLogMerging=false] Merge completed execution logs to shorten terminal output
@@ -27438,6 +27536,7 @@ const executeTestPlan = async ({
27438
27536
  completedExecutionLogAbbreviation = false,
27439
27537
  completedExecutionLogMerging = false,
27440
27538
  rootDirectoryUrl,
27539
+ devServerOrigin,
27441
27540
  testPlan,
27442
27541
  updateProcessExitCode = true,
27443
27542
  maxExecutionsInParallel = 1,
@@ -27468,17 +27567,7 @@ const executeTestPlan = async ({
27468
27567
  coverageReportSkipFull = false,
27469
27568
  coverageReportTextLog = true,
27470
27569
  coverageReportJsonFile = process.env.CI ? null : "./.coverage/coverage.json",
27471
- coverageReportHtmlDirectory = process.env.CI ? "./.coverage/" : null,
27472
- sourcemaps = "inline",
27473
- plugins = [],
27474
- nodeEsmResolution,
27475
- fileSystemMagicResolution,
27476
- writeGeneratedFiles = false,
27477
- protocol,
27478
- privateKey,
27479
- certificate,
27480
- host,
27481
- port
27570
+ coverageReportHtmlDirectory = process.env.CI ? "./.coverage/" : null
27482
27571
  }) => {
27483
27572
  const logger = createLogger({
27484
27573
  logLevel
@@ -27538,6 +27627,7 @@ const executeTestPlan = async ({
27538
27627
  completedExecutionLogMerging,
27539
27628
  completedExecutionLogAbbreviation,
27540
27629
  rootDirectoryUrl,
27630
+ devServerOrigin,
27541
27631
  maxExecutionsInParallel,
27542
27632
  defaultMsAllocatedPerExecution,
27543
27633
  failFast,
@@ -27550,18 +27640,7 @@ const executeTestPlan = async ({
27550
27640
  coverageMethodForBrowsers,
27551
27641
  coverageMethodForNodeJs,
27552
27642
  coverageV8ConflictWarning,
27553
- coverageTempDirectoryRelativeUrl,
27554
- scenario: "test",
27555
- sourcemaps,
27556
- plugins,
27557
- nodeEsmResolution,
27558
- fileSystemMagicResolution,
27559
- writeGeneratedFiles,
27560
- protocol,
27561
- privateKey,
27562
- certificate,
27563
- host,
27564
- port
27643
+ coverageTempDirectoryRelativeUrl
27565
27644
  });
27566
27645
 
27567
27646
  if (updateProcessExitCode && result.planSummary.counters.total !== result.planSummary.counters.completed) {
@@ -27679,8 +27758,7 @@ const createRuntimeFromPlaywright = ({
27679
27758
  const runtime = {
27680
27759
  type: "browser",
27681
27760
  name: browserName,
27682
- version: browserVersion,
27683
- needsServer: true
27761
+ version: browserVersion
27684
27762
  };
27685
27763
  let browserAndContextPromise;
27686
27764
 
@@ -27689,7 +27767,7 @@ const createRuntimeFromPlaywright = ({
27689
27767
  logger,
27690
27768
  rootDirectoryUrl,
27691
27769
  fileRelativeUrl,
27692
- server,
27770
+ devServerOrigin,
27693
27771
  // measurePerformance,
27694
27772
  collectPerformance,
27695
27773
  coverageEnabled = false,
@@ -27764,7 +27842,13 @@ const createRuntimeFromPlaywright = ({
27764
27842
  await disconnected;
27765
27843
  };
27766
27844
 
27767
- const page = await browserContext.newPage();
27845
+ const coverageInHeaders = coverageEnabled && (!coveragePlaywrightAPIAvailable || coverageMethodForBrowsers !== "playwright_api");
27846
+ const page = await browserContext.newPage({
27847
+ extraHTTPHeaders: { ...(coverageInHeaders ? {
27848
+ "x-coverage-istanbul": JSON.stringify(coverageConfig)
27849
+ } : {})
27850
+ }
27851
+ });
27768
27852
 
27769
27853
  const closePage = async () => {
27770
27854
  try {
@@ -27792,7 +27876,7 @@ const createRuntimeFromPlaywright = ({
27792
27876
  const v8CoveragesWithFsUrls = v8CoveragesWithWebUrls.map(v8CoveragesWithWebUrl => {
27793
27877
  const fsUrl = moveUrl({
27794
27878
  url: v8CoveragesWithWebUrl.url,
27795
- from: `${server.origin}/`,
27879
+ from: `${devServerOrigin}/`,
27796
27880
  to: rootDirectoryUrl,
27797
27881
  preferAbsolute: true
27798
27882
  });
@@ -27863,7 +27947,7 @@ const createRuntimeFromPlaywright = ({
27863
27947
  });
27864
27948
  }
27865
27949
 
27866
- const fileClientUrl = new URL(fileRelativeUrl, `${server.origin}/`).href; // https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-console
27950
+ const fileClientUrl = new URL(fileRelativeUrl, `${devServerOrigin}/`).href; // https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-console
27867
27951
 
27868
27952
  const removeConsoleListener = registerEvent({
27869
27953
  object: page,
@@ -27977,7 +28061,7 @@ const createRuntimeFromPlaywright = ({
27977
28061
  } = returnValue;
27978
28062
  const error = evalException(exceptionSource, {
27979
28063
  rootDirectoryUrl,
27980
- server,
28064
+ devServerOrigin,
27981
28065
  transformErrorHook
27982
28066
  });
27983
28067
  cb({
@@ -28213,7 +28297,7 @@ const registerEvent = ({
28213
28297
 
28214
28298
  const evalException = (exceptionSource, {
28215
28299
  rootDirectoryUrl,
28216
- server,
28300
+ devServerOrigin,
28217
28301
  transformErrorHook
28218
28302
  }) => {
28219
28303
  const script = new Script(exceptionSource, {
@@ -28222,7 +28306,7 @@ const evalException = (exceptionSource, {
28222
28306
  const error = script.runInThisContext();
28223
28307
 
28224
28308
  if (error && error instanceof Error) {
28225
- const remoteRootRegexp = new RegExp(escapeRegexpSpecialChars(`${server.origin}/`), "g");
28309
+ const remoteRootRegexp = new RegExp(escapeRegexpSpecialChars(`${devServerOrigin}/`), "g");
28226
28310
  error.stack = error.stack.replace(remoteRootRegexp, rootDirectoryUrl);
28227
28311
  error.message = error.message.replace(remoteRootRegexp, rootDirectoryUrl);
28228
28312
  }
@@ -29220,7 +29304,7 @@ const startBuildServer = async ({
29220
29304
  certificate,
29221
29305
  privateKey,
29222
29306
  acceptAnyIp,
29223
- host,
29307
+ hostname,
29224
29308
  port = 9779,
29225
29309
  services = [],
29226
29310
  keepProcessAlive = true,
@@ -29357,7 +29441,7 @@ const startBuildServer = async ({
29357
29441
  certificate,
29358
29442
  privateKey,
29359
29443
  acceptAnyIp,
29360
- host,
29444
+ hostname,
29361
29445
  port,
29362
29446
  serverTiming: true,
29363
29447
  requestWaitingMs: 60_000,
@@ -29428,30 +29512,17 @@ const execute = async ({
29428
29512
  handleSIGINT = true,
29429
29513
  logLevel,
29430
29514
  rootDirectoryUrl,
29515
+ devServerOrigin,
29431
29516
  fileRelativeUrl,
29432
29517
  allocatedMs,
29433
29518
  mirrorConsole = true,
29434
29519
  keepRunning = false,
29435
- services,
29436
29520
  collectConsole,
29437
29521
  collectCoverage,
29438
29522
  coverageTempDirectoryUrl,
29439
29523
  collectPerformance = false,
29440
29524
  runtime,
29441
29525
  runtimeParams,
29442
- scenario = "dev",
29443
- plugins = [],
29444
- nodeEsmResolution,
29445
- fileSystemMagicResolution,
29446
- transpilation,
29447
- htmlSupervisor = true,
29448
- sourcemaps = "inline",
29449
- writeGeneratedFiles = false,
29450
- port,
29451
- protocol,
29452
- http2,
29453
- certificate,
29454
- privateKey,
29455
29526
  ignoreError = false
29456
29527
  }) => {
29457
29528
  const logger = createLogger({
@@ -29473,45 +29544,21 @@ const execute = async ({
29473
29544
 
29474
29545
  runtimeParams = {
29475
29546
  rootDirectoryUrl,
29547
+ devServerOrigin,
29476
29548
  fileRelativeUrl,
29477
29549
  ...runtimeParams
29478
29550
  };
29479
29551
 
29480
- if (runtime.needsServer) {
29481
- const server = await startOmegaServer({
29482
- signal: executeOperation.signal,
29483
- logLevel: "warn",
29484
- keepProcessAlive: false,
29485
- services,
29486
- port,
29487
- protocol,
29488
- http2,
29489
- certificate,
29490
- privateKey,
29491
- rootDirectoryUrl,
29492
- scenario,
29493
- runtimeCompat: {
29494
- [runtime.name]: runtime.version
29495
- },
29496
- plugins,
29497
- htmlSupervisor,
29498
- nodeEsmResolution,
29499
- fileSystemMagicResolution,
29500
- transpilation,
29501
- sourcemaps,
29502
- writeGeneratedFiles
29503
- });
29504
- executeOperation.addEndCallback(async () => {
29505
- await server.stop("execution done");
29506
- });
29507
- runtimeParams = { ...runtimeParams,
29508
- server
29509
- };
29552
+ if (runtime.type === "browser") {
29553
+ if (!devServerOrigin) {
29554
+ throw new TypeError(`devServerOrigin is required when running tests on browser(s)`);
29555
+ }
29510
29556
 
29511
- resultTransformer = result => {
29512
- result.server = server;
29513
- return result;
29514
- };
29557
+ const devServerStarted = await pingServer(devServerOrigin);
29558
+
29559
+ if (!devServerStarted) {
29560
+ throw new Error(`dev server not started at ${devServerOrigin}. It is required to run tests`);
29561
+ }
29515
29562
  }
29516
29563
 
29517
29564
  let result = await run({
@@ -29633,4 +29680,4 @@ const jsenvPluginInjectGlobals = urlAssociations => {
29633
29680
  };
29634
29681
  };
29635
29682
 
29636
- export { build, chromium, chromiumIsolatedTab, execute, executeTestPlan, firefox, firefoxIsolatedTab, jsenvPluginInjectGlobals, nodeChildProcess, nodeWorkerThread, startBuildServer, startDevServer, webkit, webkitIsolatedTab };
29683
+ export { build, chromium, chromiumIsolatedTab, execute, executeTestPlan, firefox, firefoxIsolatedTab, jsenvPluginInjectGlobals, nodeChildProcess, nodeWorkerThread, pingServer, startBuildServer, startDevServer, webkit, webkitIsolatedTab };