@jsenv/core 33.0.2 → 34.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.
- package/dist/js/autoreload.js +1 -4
- package/dist/js/supervisor.js +498 -290
- package/dist/jsenv.js +870 -346
- package/package.json +2 -3
- package/src/basic_fetch.js +23 -13
- package/src/build/start_build_server.js +3 -2
- package/src/dev/file_service.js +1 -1
- package/src/dev/start_dev_server.js +9 -6
- package/src/execute/execute.js +7 -18
- package/src/execute/runtimes/browsers/from_playwright.js +168 -32
- package/src/execute/runtimes/browsers/webkit.js +1 -1
- package/src/execute/web_server_param.js +68 -0
- package/src/kitchen/compat/features_compatibility.js +3 -0
- package/src/plugins/autoreload/client/reload.js +1 -4
- package/src/plugins/inline/jsenv_plugin_html_inline_content.js +30 -18
- package/src/plugins/plugins.js +1 -1
- package/src/plugins/ribbon/jsenv_plugin_ribbon.js +3 -2
- package/src/plugins/supervisor/client/supervisor.js +467 -287
- package/src/plugins/supervisor/html_supervisor_injection.js +281 -0
- package/src/plugins/supervisor/js_supervisor_injection.js +281 -0
- package/src/plugins/supervisor/jsenv_plugin_supervisor.js +48 -233
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_html.js +1 -1
- package/src/plugins/transpilation/babel/jsenv_plugin_babel.js +5 -0
- package/src/plugins/transpilation/jsenv_plugin_top_level_await.js +1 -1
- package/src/test/execute_steps.js +10 -18
- package/src/test/execute_test_plan.js +12 -60
- package/src/test/logs_file_execution.js +74 -28
- package/dist/js/script_type_module_supervisor.js +0 -109
- package/src/plugins/supervisor/client/script_type_module_supervisor.js +0 -98
package/dist/jsenv.js
CHANGED
|
@@ -15,15 +15,15 @@ import { Readable, Stream, Writable } from "node:stream";
|
|
|
15
15
|
import { Http2ServerResponse } from "node:http2";
|
|
16
16
|
import { lookup } from "node:dns";
|
|
17
17
|
import { SOURCEMAP, generateSourcemapFileUrl, composeTwoSourcemaps, generateSourcemapDataUrl, createMagicSource, getOriginalPosition } from "@jsenv/sourcemap";
|
|
18
|
-
import { parseHtmlString, stringifyHtmlAst, getHtmlNodeAttribute, visitHtmlNodes, analyzeScriptNode, setHtmlNodeAttributes, parseSrcSet, getHtmlNodePosition, getHtmlNodeAttributePosition, parseCssUrls, parseJsUrls, getHtmlNodeText, setHtmlNodeText, applyBabelPlugins, injectScriptNodeAsEarlyAsPossible, createHtmlNode, findHtmlNode, removeHtmlNode,
|
|
18
|
+
import { parseHtmlString, stringifyHtmlAst, getHtmlNodeAttribute, visitHtmlNodes, analyzeScriptNode, setHtmlNodeAttributes, parseSrcSet, getHtmlNodePosition, getHtmlNodeAttributePosition, parseCssUrls, parseJsUrls, getHtmlNodeText, setHtmlNodeText, removeHtmlNodeText, applyBabelPlugins, injectScriptNodeAsEarlyAsPossible, createHtmlNode, findHtmlNode, removeHtmlNode, injectJsImport, analyzeLinkNode, injectHtmlNode, insertHtmlNodeAfter } from "@jsenv/ast";
|
|
19
19
|
import { createRequire } from "node:module";
|
|
20
20
|
import babelParser from "@babel/parser";
|
|
21
21
|
import { bundleJsModules } from "@jsenv/plugin-bundling";
|
|
22
22
|
import v8, { takeCoverage } from "node:v8";
|
|
23
|
-
import wrapAnsi from "wrap-ansi";
|
|
24
23
|
import stripAnsi from "strip-ansi";
|
|
25
24
|
import { createId } from "@paralleldrive/cuid2";
|
|
26
25
|
import { runInNewContext } from "node:vm";
|
|
26
|
+
import wrapAnsi from "wrap-ansi";
|
|
27
27
|
import { fork } from "node:child_process";
|
|
28
28
|
import { Worker } from "node:worker_threads";
|
|
29
29
|
|
|
@@ -8131,7 +8131,9 @@ const featuresCompatMap = {
|
|
|
8131
8131
|
samsung: "9.2"
|
|
8132
8132
|
},
|
|
8133
8133
|
import_meta_resolve: {
|
|
8134
|
-
chrome: "107"
|
|
8134
|
+
chrome: "107",
|
|
8135
|
+
edge: "105",
|
|
8136
|
+
firefox: "106"
|
|
8135
8137
|
},
|
|
8136
8138
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#browser_compatibility
|
|
8137
8139
|
import_dynamic: {
|
|
@@ -8161,7 +8163,8 @@ const featuresCompatMap = {
|
|
|
8161
8163
|
chrome: "89",
|
|
8162
8164
|
opera: "76",
|
|
8163
8165
|
samsung: "15",
|
|
8164
|
-
firefox: "108"
|
|
8166
|
+
firefox: "108",
|
|
8167
|
+
safari: "16.4"
|
|
8165
8168
|
},
|
|
8166
8169
|
import_type_json: {
|
|
8167
8170
|
chrome: "91",
|
|
@@ -10411,6 +10414,11 @@ const findOriginalDirectoryReference = (urlInfo, context) => {
|
|
|
10411
10414
|
return ref;
|
|
10412
10415
|
};
|
|
10413
10416
|
|
|
10417
|
+
/*
|
|
10418
|
+
* This plugin ensure content inlined inside HTML is cooked (inline <script> for instance)
|
|
10419
|
+
* For <script hot-accept> the script content will be moved to a virtual file
|
|
10420
|
+
* to enable hot reloading
|
|
10421
|
+
*/
|
|
10414
10422
|
const jsenvPluginHtmlInlineContent = ({
|
|
10415
10423
|
analyzeConvertedScripts
|
|
10416
10424
|
}) => {
|
|
@@ -10508,9 +10516,7 @@ ${e.traceMessage}`);
|
|
|
10508
10516
|
if (!analyzeConvertedScripts && getHtmlNodeAttribute(scriptNode, "jsenv-injected-by") === "jsenv:as_js_classic_html") {
|
|
10509
10517
|
return;
|
|
10510
10518
|
}
|
|
10511
|
-
|
|
10512
|
-
return;
|
|
10513
|
-
}
|
|
10519
|
+
const hotAccept = getHtmlNodeAttribute(scriptNode, "hot-accept") !== undefined;
|
|
10514
10520
|
const {
|
|
10515
10521
|
type,
|
|
10516
10522
|
contentType,
|
|
@@ -10555,14 +10561,26 @@ ${e.traceMessage}`);
|
|
|
10555
10561
|
inlineContentUrlInfo: inlineScriptUrlInfo,
|
|
10556
10562
|
inlineContentReference: inlineScriptReference
|
|
10557
10563
|
});
|
|
10558
|
-
|
|
10559
|
-
|
|
10560
|
-
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
10564
|
-
|
|
10565
|
-
|
|
10564
|
+
mutations.push(() => {
|
|
10565
|
+
const attributes = {
|
|
10566
|
+
"jsenv-cooked-by": "jsenv:html_inline_content",
|
|
10567
|
+
// 1. <script type="jsx"> becomes <script>
|
|
10568
|
+
// 2. <script type="module/jsx"> becomes <script type="module">
|
|
10569
|
+
...(extension ? {
|
|
10570
|
+
type: type === "js_module" ? "module" : undefined
|
|
10571
|
+
} : {})
|
|
10572
|
+
};
|
|
10573
|
+
if (hotAccept) {
|
|
10574
|
+
removeHtmlNodeText(scriptNode);
|
|
10575
|
+
setHtmlNodeAttributes(scriptNode, {
|
|
10576
|
+
...attributes
|
|
10577
|
+
});
|
|
10578
|
+
} else {
|
|
10579
|
+
setHtmlNodeText(scriptNode, inlineScriptUrlInfo.content);
|
|
10580
|
+
setHtmlNodeAttributes(scriptNode, {
|
|
10581
|
+
...attributes
|
|
10582
|
+
});
|
|
10583
|
+
}
|
|
10566
10584
|
});
|
|
10567
10585
|
});
|
|
10568
10586
|
}
|
|
@@ -15062,7 +15080,7 @@ const jsenvPluginAsJsClassicHtml = ({
|
|
|
15062
15080
|
break;
|
|
15063
15081
|
}
|
|
15064
15082
|
} catch (e) {
|
|
15065
|
-
if (context.dev) {
|
|
15083
|
+
if (context.dev && e.code !== "PARSE_ERROR") {
|
|
15066
15084
|
needsSystemJs = true;
|
|
15067
15085
|
// ignore cooking error, the browser will trigger it again on fetch
|
|
15068
15086
|
// + disable cache for this html file because when browser will reload
|
|
@@ -17484,49 +17502,501 @@ const jsenvPluginHttpUrls = () => {
|
|
|
17484
17502
|
};
|
|
17485
17503
|
|
|
17486
17504
|
/*
|
|
17487
|
-
*
|
|
17488
|
-
*
|
|
17505
|
+
* ```js
|
|
17506
|
+
* console.log(42)
|
|
17507
|
+
* ```
|
|
17508
|
+
* becomes
|
|
17509
|
+
* ```js
|
|
17510
|
+
* window.__supervisor__.jsClassicStart('main.html@L10-L13.js')
|
|
17511
|
+
* try {
|
|
17512
|
+
* console.log(42)
|
|
17513
|
+
* window.__supervisor__.jsClassicEnd('main.html@L10-L13.js')
|
|
17514
|
+
* } catch(e) {
|
|
17515
|
+
* window.__supervisor__.jsClassicError('main.html@L10-L13.js', e)
|
|
17516
|
+
* }
|
|
17517
|
+
* ```
|
|
17518
|
+
*
|
|
17519
|
+
* ```js
|
|
17520
|
+
* import value from "./file.js"
|
|
17521
|
+
* console.log(value)
|
|
17522
|
+
* ```
|
|
17523
|
+
* becomes
|
|
17524
|
+
* ```js
|
|
17525
|
+
* window.__supervisor__.jsModuleStart('main.html@L10-L13.js')
|
|
17526
|
+
* try {
|
|
17527
|
+
* const value = await import("./file.js")
|
|
17528
|
+
* console.log(value)
|
|
17529
|
+
* window.__supervisor__.jsModuleEnd('main.html@L10-L13.js')
|
|
17530
|
+
* } catch(e) {
|
|
17531
|
+
* window.__supervisor__.jsModuleError('main.html@L10-L13.js', e)
|
|
17532
|
+
* }
|
|
17533
|
+
* ```
|
|
17489
17534
|
*
|
|
17535
|
+
* -> TO KEEP IN MIND:
|
|
17536
|
+
* Static import can throw errors like
|
|
17537
|
+
* The requested module '/js_module_export_not_found/foo.js' does not provide an export named 'answerr'
|
|
17538
|
+
* While dynamic import will work just fine
|
|
17539
|
+
* and create a variable named "undefined"
|
|
17540
|
+
*/
|
|
17541
|
+
const injectSupervisorIntoJs = async ({
|
|
17542
|
+
content,
|
|
17543
|
+
url,
|
|
17544
|
+
type,
|
|
17545
|
+
inlineSrc
|
|
17546
|
+
}) => {
|
|
17547
|
+
const babelPluginJsSupervisor = type === "js_module" ? babelPluginJsModuleSupervisor : babelPluginJsClassicSupervisor;
|
|
17548
|
+
const result = await applyBabelPlugins({
|
|
17549
|
+
urlInfo: {
|
|
17550
|
+
content,
|
|
17551
|
+
originalUrl: url,
|
|
17552
|
+
type
|
|
17553
|
+
},
|
|
17554
|
+
babelPlugins: [[babelPluginJsSupervisor, {
|
|
17555
|
+
inlineSrc
|
|
17556
|
+
}]]
|
|
17557
|
+
});
|
|
17558
|
+
let code = result.code;
|
|
17559
|
+
let map = result.map;
|
|
17560
|
+
const sourcemapDataUrl = generateSourcemapDataUrl(map);
|
|
17561
|
+
code = SOURCEMAP.writeComment({
|
|
17562
|
+
contentType: "text/javascript",
|
|
17563
|
+
content: code,
|
|
17564
|
+
specifier: sourcemapDataUrl
|
|
17565
|
+
});
|
|
17566
|
+
code = `${code}
|
|
17567
|
+
//# sourceURL=${url}`;
|
|
17568
|
+
return code;
|
|
17569
|
+
};
|
|
17570
|
+
const babelPluginJsModuleSupervisor = babel => {
|
|
17571
|
+
const t = babel.types;
|
|
17572
|
+
return {
|
|
17573
|
+
name: "js-module-supervisor",
|
|
17574
|
+
visitor: {
|
|
17575
|
+
Program: (programPath, state) => {
|
|
17576
|
+
const {
|
|
17577
|
+
inlineSrc
|
|
17578
|
+
} = state.opts;
|
|
17579
|
+
if (state.file.metadata.jsExecutionInstrumented) return;
|
|
17580
|
+
state.file.metadata.jsExecutionInstrumented = true;
|
|
17581
|
+
const urlNode = t.stringLiteral(inlineSrc);
|
|
17582
|
+
const startCallNode = createSupervisionCall({
|
|
17583
|
+
t,
|
|
17584
|
+
urlNode,
|
|
17585
|
+
methodName: "jsModuleStart"
|
|
17586
|
+
});
|
|
17587
|
+
const endCallNode = createSupervisionCall({
|
|
17588
|
+
t,
|
|
17589
|
+
urlNode,
|
|
17590
|
+
methodName: "jsModuleEnd"
|
|
17591
|
+
});
|
|
17592
|
+
const errorCallNode = createSupervisionCall({
|
|
17593
|
+
t,
|
|
17594
|
+
urlNode,
|
|
17595
|
+
methodName: "jsModuleError",
|
|
17596
|
+
args: [t.identifier("e")]
|
|
17597
|
+
});
|
|
17598
|
+
const bodyPath = programPath.get("body");
|
|
17599
|
+
const importNodes = [];
|
|
17600
|
+
const topLevelNodes = [];
|
|
17601
|
+
for (const topLevelNodePath of bodyPath) {
|
|
17602
|
+
const topLevelNode = topLevelNodePath.node;
|
|
17603
|
+
if (t.isImportDeclaration(topLevelNode)) {
|
|
17604
|
+
importNodes.push(topLevelNode);
|
|
17605
|
+
} else {
|
|
17606
|
+
topLevelNodes.push(topLevelNode);
|
|
17607
|
+
}
|
|
17608
|
+
}
|
|
17609
|
+
|
|
17610
|
+
// replace all import nodes with dynamic imports
|
|
17611
|
+
const dynamicImports = [];
|
|
17612
|
+
importNodes.forEach(importNode => {
|
|
17613
|
+
const dynamicImportConversion = convertStaticImportIntoDynamicImport(importNode, t);
|
|
17614
|
+
if (Array.isArray(dynamicImportConversion)) {
|
|
17615
|
+
dynamicImports.push(...dynamicImportConversion);
|
|
17616
|
+
} else {
|
|
17617
|
+
dynamicImports.push(dynamicImportConversion);
|
|
17618
|
+
}
|
|
17619
|
+
});
|
|
17620
|
+
const tryCatchNode = t.tryStatement(t.blockStatement([...dynamicImports, ...topLevelNodes, endCallNode]), t.catchClause(t.identifier("e"), t.blockStatement([errorCallNode])));
|
|
17621
|
+
programPath.replaceWith(t.program([startCallNode, tryCatchNode]));
|
|
17622
|
+
}
|
|
17623
|
+
}
|
|
17624
|
+
};
|
|
17625
|
+
};
|
|
17626
|
+
const convertStaticImportIntoDynamicImport = (staticImportNode, t) => {
|
|
17627
|
+
const awaitExpression = t.awaitExpression(t.callExpression(t.import(), [t.stringLiteral(staticImportNode.source.value)]));
|
|
17628
|
+
|
|
17629
|
+
// import "./file.js" -> await import("./file.js")
|
|
17630
|
+
if (staticImportNode.specifiers.length === 0) {
|
|
17631
|
+
return t.expressionStatement(awaitExpression);
|
|
17632
|
+
}
|
|
17633
|
+
if (staticImportNode.specifiers.length === 1) {
|
|
17634
|
+
const [firstSpecifier] = staticImportNode.specifiers;
|
|
17635
|
+
if (firstSpecifier.type === "ImportNamespaceSpecifier") {
|
|
17636
|
+
return t.variableDeclaration("const", [t.variableDeclarator(t.identifier(firstSpecifier.local.name), awaitExpression)]);
|
|
17637
|
+
}
|
|
17638
|
+
}
|
|
17639
|
+
if (staticImportNode.specifiers.length === 2) {
|
|
17640
|
+
const [first, second] = staticImportNode.specifiers;
|
|
17641
|
+
if (first.type === "ImportDefaultSpecifier" && second.type === "ImportNamespaceSpecifier") {
|
|
17642
|
+
const namespaceDeclaration = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(second.local.name), awaitExpression)]);
|
|
17643
|
+
const defaultDeclaration = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(first.local.name), t.memberExpression(t.identifier(second.local.name), t.identifier("default")))]);
|
|
17644
|
+
return [namespaceDeclaration, defaultDeclaration];
|
|
17645
|
+
}
|
|
17646
|
+
}
|
|
17647
|
+
|
|
17648
|
+
// import { name } from "./file.js" -> const { name } = await import("./file.js")
|
|
17649
|
+
// import toto, { name } from "./file.js" -> const { name, default as toto } = await import("./file.js")
|
|
17650
|
+
const objectPattern = t.objectPattern(staticImportNode.specifiers.map(specifier => {
|
|
17651
|
+
if (specifier.type === "ImportDefaultSpecifier") {
|
|
17652
|
+
return t.objectProperty(t.identifier("default"), t.identifier(specifier.local.name), false,
|
|
17653
|
+
// computed
|
|
17654
|
+
false // shorthand
|
|
17655
|
+
);
|
|
17656
|
+
}
|
|
17657
|
+
// if (specifier.type === "ImportNamespaceSpecifier") {
|
|
17658
|
+
// return t.restElement(t.identifier(specifier.local.name))
|
|
17659
|
+
// }
|
|
17660
|
+
const isRenamed = specifier.imported.name !== specifier.local.name;
|
|
17661
|
+
if (isRenamed) {
|
|
17662
|
+
return t.objectProperty(t.identifier(specifier.imported.name), t.identifier(specifier.local.name), false,
|
|
17663
|
+
// computed
|
|
17664
|
+
false // shorthand
|
|
17665
|
+
);
|
|
17666
|
+
}
|
|
17667
|
+
// shorthand must be true
|
|
17668
|
+
return t.objectProperty(t.identifier(specifier.local.name), t.identifier(specifier.local.name), false,
|
|
17669
|
+
// computed
|
|
17670
|
+
true // shorthand
|
|
17671
|
+
);
|
|
17672
|
+
}));
|
|
17673
|
+
|
|
17674
|
+
const variableDeclarator = t.variableDeclarator(objectPattern, awaitExpression);
|
|
17675
|
+
const variableDeclaration = t.variableDeclaration("const", [variableDeclarator]);
|
|
17676
|
+
return variableDeclaration;
|
|
17677
|
+
};
|
|
17678
|
+
const babelPluginJsClassicSupervisor = babel => {
|
|
17679
|
+
const t = babel.types;
|
|
17680
|
+
return {
|
|
17681
|
+
name: "js-classic-supervisor",
|
|
17682
|
+
visitor: {
|
|
17683
|
+
Program: (programPath, state) => {
|
|
17684
|
+
const {
|
|
17685
|
+
inlineSrc
|
|
17686
|
+
} = state.opts;
|
|
17687
|
+
if (state.file.metadata.jsExecutionInstrumented) return;
|
|
17688
|
+
state.file.metadata.jsExecutionInstrumented = true;
|
|
17689
|
+
const urlNode = t.stringLiteral(inlineSrc);
|
|
17690
|
+
const startCallNode = createSupervisionCall({
|
|
17691
|
+
t,
|
|
17692
|
+
urlNode,
|
|
17693
|
+
methodName: "jsClassicStart"
|
|
17694
|
+
});
|
|
17695
|
+
const endCallNode = createSupervisionCall({
|
|
17696
|
+
t,
|
|
17697
|
+
urlNode,
|
|
17698
|
+
methodName: "jsClassicEnd"
|
|
17699
|
+
});
|
|
17700
|
+
const errorCallNode = createSupervisionCall({
|
|
17701
|
+
t,
|
|
17702
|
+
urlNode,
|
|
17703
|
+
methodName: "jsClassicError",
|
|
17704
|
+
args: [t.identifier("e")]
|
|
17705
|
+
});
|
|
17706
|
+
const topLevelNodes = programPath.node.body;
|
|
17707
|
+
const tryCatchNode = t.tryStatement(t.blockStatement([...topLevelNodes, endCallNode]), t.catchClause(t.identifier("e"), t.blockStatement([errorCallNode])));
|
|
17708
|
+
programPath.replaceWith(t.program([startCallNode, tryCatchNode]));
|
|
17709
|
+
}
|
|
17710
|
+
}
|
|
17711
|
+
};
|
|
17712
|
+
};
|
|
17713
|
+
const createSupervisionCall = ({
|
|
17714
|
+
t,
|
|
17715
|
+
methodName,
|
|
17716
|
+
urlNode,
|
|
17717
|
+
args = []
|
|
17718
|
+
}) => {
|
|
17719
|
+
return t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier("window"), t.identifier("__supervisor__")), t.identifier(methodName)), [urlNode, ...args]), [], null);
|
|
17720
|
+
};
|
|
17721
|
+
|
|
17722
|
+
/*
|
|
17723
|
+
* Jsenv needs to track js execution in order to:
|
|
17724
|
+
* 1. report errors
|
|
17725
|
+
* 2. wait for all js execution inside an HTML page before killing the browser
|
|
17726
|
+
*
|
|
17727
|
+
* A naive approach would rely on "load" events on window but:
|
|
17490
17728
|
* scenario | covered by window "load"
|
|
17491
17729
|
* ------------------------------------------- | -------------------------
|
|
17492
17730
|
* js referenced by <script src> | yes
|
|
17493
17731
|
* js inlined into <script> | yes
|
|
17494
17732
|
* js referenced by <script type="module" src> | partially (not for import and top level await)
|
|
17495
17733
|
* js inlined into <script type="module"> | not at all
|
|
17496
|
-
*
|
|
17497
|
-
* This plugin provides a way for jsenv to know when js execution is done
|
|
17498
|
-
* As a side effect this plugin enables ability to hot reload js inlined into <script hot-accept>
|
|
17734
|
+
* Same for "error" event on window who is not enough
|
|
17499
17735
|
*
|
|
17500
17736
|
* <script src="file.js">
|
|
17501
17737
|
* becomes
|
|
17502
17738
|
* <script>
|
|
17503
|
-
* window.__supervisor__.superviseScript(
|
|
17739
|
+
* window.__supervisor__.superviseScript('file.js')
|
|
17504
17740
|
* </script>
|
|
17505
17741
|
*
|
|
17506
17742
|
* <script>
|
|
17507
17743
|
* console.log(42)
|
|
17508
17744
|
* </script>
|
|
17509
17745
|
* becomes
|
|
17510
|
-
* <script>
|
|
17511
|
-
* window.
|
|
17746
|
+
* <script inlined-from-src="main.html@L10-C5.js">
|
|
17747
|
+
* window.__supervisor.__superviseScript("main.html@L10-C5.js")
|
|
17512
17748
|
* </script>
|
|
17513
17749
|
*
|
|
17514
17750
|
* <script type="module" src="module.js"></script>
|
|
17515
17751
|
* becomes
|
|
17516
17752
|
* <script type="module">
|
|
17517
|
-
*
|
|
17518
|
-
* superviseScriptTypeModule({ src: "module.js" })
|
|
17753
|
+
* window.__supervisor__.superviseScriptTypeModule('module.js')
|
|
17519
17754
|
* </script>
|
|
17520
17755
|
*
|
|
17521
17756
|
* <script type="module">
|
|
17522
17757
|
* console.log(42)
|
|
17523
17758
|
* </script>
|
|
17524
17759
|
* becomes
|
|
17525
|
-
* <script type="module">
|
|
17526
|
-
*
|
|
17527
|
-
* superviseScriptTypeModule({ src: 'main.html@L10-L13.js' })
|
|
17760
|
+
* <script type="module" inlined-from-src="main.html@L10-C5.js">
|
|
17761
|
+
* window.__supervisor__.superviseScriptTypeModule('main.html@L10-C5.js')
|
|
17528
17762
|
* </script>
|
|
17763
|
+
*
|
|
17764
|
+
* Why Inline scripts are converted to files dynamically?
|
|
17765
|
+
* -> No changes required on js source code, it's only the HTML that is modified
|
|
17766
|
+
* - Also allow to catch syntax errors and export missing
|
|
17767
|
+
*/
|
|
17768
|
+
const supervisorFileUrl$1 = new URL("./js/supervisor.js", import.meta.url).href;
|
|
17769
|
+
const injectSupervisorIntoHTML = async ({
|
|
17770
|
+
content,
|
|
17771
|
+
url
|
|
17772
|
+
}, {
|
|
17773
|
+
supervisorScriptSrc = supervisorFileUrl$1,
|
|
17774
|
+
supervisorOptions,
|
|
17775
|
+
webServer,
|
|
17776
|
+
onInlineScript = () => {},
|
|
17777
|
+
generateInlineScriptSrc = ({
|
|
17778
|
+
inlineScriptUrl
|
|
17779
|
+
}) => urlToRelativeUrl(inlineScriptUrl, webServer.rootDirectoryUrl),
|
|
17780
|
+
inlineAsRemote
|
|
17781
|
+
}) => {
|
|
17782
|
+
const htmlAst = parseHtmlString(content);
|
|
17783
|
+
const mutations = [];
|
|
17784
|
+
const actions = [];
|
|
17785
|
+
const scriptInfos = [];
|
|
17786
|
+
// 1. Find inline and remote scripts
|
|
17787
|
+
{
|
|
17788
|
+
const handleInlineScript = (scriptNode, {
|
|
17789
|
+
type,
|
|
17790
|
+
extension,
|
|
17791
|
+
textContent
|
|
17792
|
+
}) => {
|
|
17793
|
+
const {
|
|
17794
|
+
line,
|
|
17795
|
+
column,
|
|
17796
|
+
lineEnd,
|
|
17797
|
+
columnEnd,
|
|
17798
|
+
isOriginal
|
|
17799
|
+
} = getHtmlNodePosition(scriptNode, {
|
|
17800
|
+
preferOriginal: true
|
|
17801
|
+
});
|
|
17802
|
+
const inlineScriptUrl = generateInlineContentUrl({
|
|
17803
|
+
url,
|
|
17804
|
+
extension: extension || ".js",
|
|
17805
|
+
line,
|
|
17806
|
+
column,
|
|
17807
|
+
lineEnd,
|
|
17808
|
+
columnEnd
|
|
17809
|
+
});
|
|
17810
|
+
const inlineScriptSrc = generateInlineScriptSrc({
|
|
17811
|
+
type,
|
|
17812
|
+
textContent,
|
|
17813
|
+
inlineScriptUrl,
|
|
17814
|
+
isOriginal,
|
|
17815
|
+
line,
|
|
17816
|
+
column
|
|
17817
|
+
});
|
|
17818
|
+
onInlineScript({
|
|
17819
|
+
type,
|
|
17820
|
+
textContent,
|
|
17821
|
+
url: inlineScriptUrl,
|
|
17822
|
+
isOriginal,
|
|
17823
|
+
line,
|
|
17824
|
+
column,
|
|
17825
|
+
src: inlineScriptSrc
|
|
17826
|
+
});
|
|
17827
|
+
if (inlineAsRemote) {
|
|
17828
|
+
// prefere la version src
|
|
17829
|
+
scriptInfos.push({
|
|
17830
|
+
type,
|
|
17831
|
+
src: inlineScriptSrc
|
|
17832
|
+
});
|
|
17833
|
+
const remoteJsSupervised = generateCodeToSuperviseScriptWithSrc({
|
|
17834
|
+
type,
|
|
17835
|
+
src: inlineScriptSrc
|
|
17836
|
+
});
|
|
17837
|
+
mutations.push(() => {
|
|
17838
|
+
setHtmlNodeText(scriptNode, remoteJsSupervised);
|
|
17839
|
+
setHtmlNodeAttributes(scriptNode, {
|
|
17840
|
+
"jsenv-cooked-by": "jsenv:supervisor",
|
|
17841
|
+
"src": undefined,
|
|
17842
|
+
"inlined-from-src": inlineScriptSrc
|
|
17843
|
+
});
|
|
17844
|
+
});
|
|
17845
|
+
} else {
|
|
17846
|
+
scriptInfos.push({
|
|
17847
|
+
type,
|
|
17848
|
+
src: inlineScriptSrc,
|
|
17849
|
+
isInline: true
|
|
17850
|
+
});
|
|
17851
|
+
actions.push(async () => {
|
|
17852
|
+
try {
|
|
17853
|
+
const inlineJsSupervised = await injectSupervisorIntoJs({
|
|
17854
|
+
webServer,
|
|
17855
|
+
content: textContent,
|
|
17856
|
+
url: inlineScriptUrl,
|
|
17857
|
+
type,
|
|
17858
|
+
inlineSrc: inlineScriptSrc
|
|
17859
|
+
});
|
|
17860
|
+
mutations.push(() => {
|
|
17861
|
+
setHtmlNodeText(scriptNode, inlineJsSupervised);
|
|
17862
|
+
setHtmlNodeAttributes(scriptNode, {
|
|
17863
|
+
"jsenv-cooked-by": "jsenv:supervisor"
|
|
17864
|
+
});
|
|
17865
|
+
});
|
|
17866
|
+
} catch (e) {
|
|
17867
|
+
if (e.code === "PARSE_ERROR") {
|
|
17868
|
+
// mutations.push(() => {
|
|
17869
|
+
// setHtmlNodeAttributes(scriptNode, {
|
|
17870
|
+
// "jsenv-cooked-by": "jsenv:supervisor",
|
|
17871
|
+
// })
|
|
17872
|
+
// })
|
|
17873
|
+
// on touche a rien
|
|
17874
|
+
return;
|
|
17875
|
+
}
|
|
17876
|
+
throw e;
|
|
17877
|
+
}
|
|
17878
|
+
});
|
|
17879
|
+
}
|
|
17880
|
+
};
|
|
17881
|
+
const handleScriptWithSrc = (scriptNode, {
|
|
17882
|
+
type,
|
|
17883
|
+
src
|
|
17884
|
+
}) => {
|
|
17885
|
+
scriptInfos.push({
|
|
17886
|
+
type,
|
|
17887
|
+
src
|
|
17888
|
+
});
|
|
17889
|
+
const remoteJsSupervised = generateCodeToSuperviseScriptWithSrc({
|
|
17890
|
+
type,
|
|
17891
|
+
src
|
|
17892
|
+
});
|
|
17893
|
+
mutations.push(() => {
|
|
17894
|
+
setHtmlNodeText(scriptNode, remoteJsSupervised);
|
|
17895
|
+
setHtmlNodeAttributes(scriptNode, {
|
|
17896
|
+
"jsenv-cooked-by": "jsenv:supervisor",
|
|
17897
|
+
"src": undefined,
|
|
17898
|
+
"inlined-from-src": src
|
|
17899
|
+
});
|
|
17900
|
+
});
|
|
17901
|
+
};
|
|
17902
|
+
visitHtmlNodes(htmlAst, {
|
|
17903
|
+
script: scriptNode => {
|
|
17904
|
+
const {
|
|
17905
|
+
type,
|
|
17906
|
+
extension
|
|
17907
|
+
} = analyzeScriptNode(scriptNode);
|
|
17908
|
+
if (type !== "js_classic" && type !== "js_module") {
|
|
17909
|
+
return;
|
|
17910
|
+
}
|
|
17911
|
+
if (getHtmlNodeAttribute(scriptNode, "jsenv-injected-by")) {
|
|
17912
|
+
return;
|
|
17913
|
+
}
|
|
17914
|
+
const noSupervisor = getHtmlNodeAttribute(scriptNode, "no-supervisor");
|
|
17915
|
+
if (noSupervisor !== undefined) {
|
|
17916
|
+
return;
|
|
17917
|
+
}
|
|
17918
|
+
const scriptNodeText = getHtmlNodeText(scriptNode);
|
|
17919
|
+
if (scriptNodeText) {
|
|
17920
|
+
handleInlineScript(scriptNode, {
|
|
17921
|
+
type,
|
|
17922
|
+
extension,
|
|
17923
|
+
textContent: scriptNodeText
|
|
17924
|
+
});
|
|
17925
|
+
return;
|
|
17926
|
+
}
|
|
17927
|
+
const src = getHtmlNodeAttribute(scriptNode, "src");
|
|
17928
|
+
if (src) {
|
|
17929
|
+
handleScriptWithSrc(scriptNode, {
|
|
17930
|
+
type,
|
|
17931
|
+
src
|
|
17932
|
+
});
|
|
17933
|
+
return;
|
|
17934
|
+
}
|
|
17935
|
+
}
|
|
17936
|
+
});
|
|
17937
|
+
}
|
|
17938
|
+
// 2. Inject supervisor js file + setup call
|
|
17939
|
+
{
|
|
17940
|
+
const setupParamsSource = stringifyParams({
|
|
17941
|
+
...supervisorOptions,
|
|
17942
|
+
serverIsJsenvDevServer: webServer.isJsenvDevServer,
|
|
17943
|
+
rootDirectoryUrl: webServer.rootDirectoryUrl,
|
|
17944
|
+
scriptInfos
|
|
17945
|
+
}, " ");
|
|
17946
|
+
injectScriptNodeAsEarlyAsPossible(htmlAst, createHtmlNode({
|
|
17947
|
+
tagName: "script",
|
|
17948
|
+
textContent: `
|
|
17949
|
+
window.__supervisor__.setup({
|
|
17950
|
+
${setupParamsSource}
|
|
17951
|
+
})
|
|
17952
|
+
`
|
|
17953
|
+
}), "jsenv:supervisor");
|
|
17954
|
+
const supervisorScript = createHtmlNode({
|
|
17955
|
+
tagName: "script",
|
|
17956
|
+
src: supervisorScriptSrc
|
|
17957
|
+
});
|
|
17958
|
+
injectScriptNodeAsEarlyAsPossible(htmlAst, supervisorScript, "jsenv:supervisor");
|
|
17959
|
+
}
|
|
17960
|
+
// 3. Perform actions (transforming inline script content) and html mutations
|
|
17961
|
+
if (actions.length > 0) {
|
|
17962
|
+
await Promise.all(actions.map(action => action()));
|
|
17963
|
+
}
|
|
17964
|
+
mutations.forEach(mutation => mutation());
|
|
17965
|
+
const htmlModified = stringifyHtmlAst(htmlAst);
|
|
17966
|
+
return {
|
|
17967
|
+
content: htmlModified
|
|
17968
|
+
};
|
|
17969
|
+
};
|
|
17970
|
+
const stringifyParams = (params, prefix = "") => {
|
|
17971
|
+
const source = JSON.stringify(params, null, prefix);
|
|
17972
|
+
if (prefix.length) {
|
|
17973
|
+
// remove leading "{\n"
|
|
17974
|
+
// remove leading prefix
|
|
17975
|
+
// remove trailing "\n}"
|
|
17976
|
+
return source.slice(2 + prefix.length, -2);
|
|
17977
|
+
}
|
|
17978
|
+
// remove leading "{"
|
|
17979
|
+
// remove trailing "}"
|
|
17980
|
+
return source.slice(1, -1);
|
|
17981
|
+
};
|
|
17982
|
+
const generateCodeToSuperviseScriptWithSrc = ({
|
|
17983
|
+
type,
|
|
17984
|
+
src
|
|
17985
|
+
}) => {
|
|
17986
|
+
if (type === "js_module") {
|
|
17987
|
+
return `
|
|
17988
|
+
window.__supervisor__.superviseScriptTypeModule(${JSON.stringify(src)}, (url) => import(url));
|
|
17989
|
+
`;
|
|
17990
|
+
}
|
|
17991
|
+
return `
|
|
17992
|
+
window.__supervisor__.superviseScript(${JSON.stringify(src)});
|
|
17993
|
+
`;
|
|
17994
|
+
};
|
|
17995
|
+
|
|
17996
|
+
/*
|
|
17997
|
+
* This plugin provides a way for jsenv to know when js execution is done
|
|
17529
17998
|
*/
|
|
17999
|
+
const supervisorFileUrl = new URL("./js/supervisor.js", import.meta.url).href;
|
|
17530
18000
|
const jsenvPluginSupervisor = ({
|
|
17531
18001
|
logs = false,
|
|
17532
18002
|
measurePerf = false,
|
|
@@ -17534,8 +18004,6 @@ const jsenvPluginSupervisor = ({
|
|
|
17534
18004
|
openInEditor = true,
|
|
17535
18005
|
errorBaseUrl
|
|
17536
18006
|
}) => {
|
|
17537
|
-
const supervisorFileUrl = new URL("./js/supervisor.js", import.meta.url).href;
|
|
17538
|
-
const scriptTypeModuleSupervisorFileUrl = new URL("./js/script_type_module_supervisor.js", import.meta.url).href;
|
|
17539
18007
|
return {
|
|
17540
18008
|
name: "jsenv:supervisor",
|
|
17541
18009
|
appliesDuring: "dev",
|
|
@@ -17677,170 +18145,50 @@ const jsenvPluginSupervisor = ({
|
|
|
17677
18145
|
url,
|
|
17678
18146
|
content
|
|
17679
18147
|
}, context) => {
|
|
17680
|
-
const htmlAst = parseHtmlString(content);
|
|
17681
|
-
const scriptsToSupervise = [];
|
|
17682
|
-
const handleInlineScript = (node, htmlNodeText) => {
|
|
17683
|
-
const {
|
|
17684
|
-
type,
|
|
17685
|
-
extension
|
|
17686
|
-
} = analyzeScriptNode(node);
|
|
17687
|
-
const {
|
|
17688
|
-
line,
|
|
17689
|
-
column,
|
|
17690
|
-
lineEnd,
|
|
17691
|
-
columnEnd,
|
|
17692
|
-
isOriginal
|
|
17693
|
-
} = getHtmlNodePosition(node, {
|
|
17694
|
-
preferOriginal: true
|
|
17695
|
-
});
|
|
17696
|
-
let inlineScriptUrl = generateInlineContentUrl({
|
|
17697
|
-
url,
|
|
17698
|
-
extension: extension || ".js",
|
|
17699
|
-
line,
|
|
17700
|
-
column,
|
|
17701
|
-
lineEnd,
|
|
17702
|
-
columnEnd
|
|
17703
|
-
});
|
|
17704
|
-
const [inlineScriptReference] = context.referenceUtils.foundInline({
|
|
17705
|
-
type: "script",
|
|
17706
|
-
subtype: "inline",
|
|
17707
|
-
expectedType: type,
|
|
17708
|
-
isOriginalPosition: isOriginal,
|
|
17709
|
-
specifierLine: line - 1,
|
|
17710
|
-
specifierColumn: column,
|
|
17711
|
-
specifier: inlineScriptUrl,
|
|
17712
|
-
contentType: "text/javascript",
|
|
17713
|
-
content: htmlNodeText
|
|
17714
|
-
});
|
|
17715
|
-
removeHtmlNodeText(node);
|
|
17716
|
-
if (extension) {
|
|
17717
|
-
setHtmlNodeAttributes(node, {
|
|
17718
|
-
type: type === "js_module" ? "module" : undefined
|
|
17719
|
-
});
|
|
17720
|
-
}
|
|
17721
|
-
scriptsToSupervise.push({
|
|
17722
|
-
node,
|
|
17723
|
-
isInline: true,
|
|
17724
|
-
type,
|
|
17725
|
-
src: inlineScriptReference.generatedSpecifier
|
|
17726
|
-
});
|
|
17727
|
-
};
|
|
17728
|
-
const handleScriptWithSrc = (node, src) => {
|
|
17729
|
-
const {
|
|
17730
|
-
type
|
|
17731
|
-
} = analyzeScriptNode(node);
|
|
17732
|
-
const integrity = getHtmlNodeAttribute(node, "integrity");
|
|
17733
|
-
const crossorigin = getHtmlNodeAttribute(node, "crossorigin") !== undefined;
|
|
17734
|
-
const defer = getHtmlNodeAttribute(node, "defer") !== undefined;
|
|
17735
|
-
const async = getHtmlNodeAttribute(node, "async") !== undefined;
|
|
17736
|
-
scriptsToSupervise.push({
|
|
17737
|
-
node,
|
|
17738
|
-
type,
|
|
17739
|
-
src,
|
|
17740
|
-
defer,
|
|
17741
|
-
async,
|
|
17742
|
-
integrity,
|
|
17743
|
-
crossorigin
|
|
17744
|
-
});
|
|
17745
|
-
};
|
|
17746
|
-
visitHtmlNodes(htmlAst, {
|
|
17747
|
-
script: node => {
|
|
17748
|
-
const {
|
|
17749
|
-
type
|
|
17750
|
-
} = analyzeScriptNode(node);
|
|
17751
|
-
if (type !== "js_classic" && type !== "js_module") {
|
|
17752
|
-
return;
|
|
17753
|
-
}
|
|
17754
|
-
if (getHtmlNodeAttribute(node, "jsenv-cooked-by") || getHtmlNodeAttribute(node, "jsenv-inlined-by") || getHtmlNodeAttribute(node, "jsenv-injected-by")) {
|
|
17755
|
-
return;
|
|
17756
|
-
}
|
|
17757
|
-
const noSupervisor = getHtmlNodeAttribute(node, "no-supervisor");
|
|
17758
|
-
if (noSupervisor !== undefined) {
|
|
17759
|
-
return;
|
|
17760
|
-
}
|
|
17761
|
-
const htmlNodeText = getHtmlNodeText(node);
|
|
17762
|
-
if (htmlNodeText) {
|
|
17763
|
-
handleInlineScript(node, htmlNodeText);
|
|
17764
|
-
return;
|
|
17765
|
-
}
|
|
17766
|
-
const src = getHtmlNodeAttribute(node, "src");
|
|
17767
|
-
if (src) {
|
|
17768
|
-
handleScriptWithSrc(node, src);
|
|
17769
|
-
return;
|
|
17770
|
-
}
|
|
17771
|
-
}
|
|
17772
|
-
});
|
|
17773
|
-
const [scriptTypeModuleSupervisorFileReference] = context.referenceUtils.inject({
|
|
17774
|
-
type: "js_import",
|
|
17775
|
-
expectedType: "js_module",
|
|
17776
|
-
specifier: scriptTypeModuleSupervisorFileUrl
|
|
17777
|
-
});
|
|
17778
18148
|
const [supervisorFileReference] = context.referenceUtils.inject({
|
|
17779
18149
|
type: "script",
|
|
17780
18150
|
expectedType: "js_classic",
|
|
17781
18151
|
specifier: supervisorFileUrl
|
|
17782
18152
|
});
|
|
17783
|
-
|
|
17784
|
-
|
|
17785
|
-
|
|
17786
|
-
|
|
17787
|
-
|
|
18153
|
+
return injectSupervisorIntoHTML({
|
|
18154
|
+
content,
|
|
18155
|
+
url
|
|
18156
|
+
}, {
|
|
18157
|
+
supervisorScriptSrc: supervisorFileReference.generatedSpecifier,
|
|
18158
|
+
supervisorOptions: {
|
|
17788
18159
|
errorBaseUrl,
|
|
17789
18160
|
logs,
|
|
17790
18161
|
measurePerf,
|
|
17791
18162
|
errorOverlay,
|
|
17792
18163
|
openInEditor
|
|
17793
|
-
},
|
|
17794
|
-
|
|
17795
|
-
|
|
17796
|
-
|
|
17797
|
-
|
|
17798
|
-
|
|
17799
|
-
|
|
17800
|
-
|
|
17801
|
-
|
|
17802
|
-
|
|
17803
|
-
|
|
17804
|
-
|
|
17805
|
-
|
|
17806
|
-
|
|
17807
|
-
|
|
17808
|
-
|
|
17809
|
-
|
|
17810
|
-
|
|
17811
|
-
|
|
17812
|
-
|
|
17813
|
-
|
|
17814
|
-
|
|
17815
|
-
|
|
17816
|
-
|
|
17817
|
-
});
|
|
17818
|
-
if (type === "js_module") {
|
|
17819
|
-
setHtmlNodeText(node, `
|
|
17820
|
-
import { superviseScriptTypeModule } from ${scriptTypeModuleSupervisorFileReference.generatedSpecifier}
|
|
17821
|
-
superviseScriptTypeModule(${paramsAsJson})
|
|
17822
|
-
`);
|
|
17823
|
-
} else {
|
|
17824
|
-
setHtmlNodeText(node, `
|
|
17825
|
-
window.__supervisor__.superviseScript(${paramsAsJson})
|
|
17826
|
-
`);
|
|
17827
|
-
}
|
|
17828
|
-
if (src) {
|
|
17829
|
-
setHtmlNodeAttributes(node, {
|
|
17830
|
-
"jsenv-inlined-by": "jsenv:supervisor",
|
|
17831
|
-
"src": undefined,
|
|
17832
|
-
"inlined-from-src": src
|
|
17833
|
-
});
|
|
17834
|
-
} else {
|
|
17835
|
-
setHtmlNodeAttributes(node, {
|
|
17836
|
-
"jsenv-cooked-by": "jsenv:supervisor"
|
|
18164
|
+
},
|
|
18165
|
+
webServer: {
|
|
18166
|
+
rootDirectoryUrl: context.rootDirectoryUrl,
|
|
18167
|
+
isJsenvDevServer: true
|
|
18168
|
+
},
|
|
18169
|
+
inlineAsRemote: true,
|
|
18170
|
+
generateInlineScriptSrc: ({
|
|
18171
|
+
type,
|
|
18172
|
+
textContent,
|
|
18173
|
+
inlineScriptUrl,
|
|
18174
|
+
isOriginal,
|
|
18175
|
+
line,
|
|
18176
|
+
column
|
|
18177
|
+
}) => {
|
|
18178
|
+
const [inlineScriptReference] = context.referenceUtils.foundInline({
|
|
18179
|
+
type: "script",
|
|
18180
|
+
subtype: "inline",
|
|
18181
|
+
expectedType: type,
|
|
18182
|
+
isOriginalPosition: isOriginal,
|
|
18183
|
+
specifierLine: line - 1,
|
|
18184
|
+
specifierColumn: column,
|
|
18185
|
+
specifier: inlineScriptUrl,
|
|
18186
|
+
contentType: "text/javascript",
|
|
18187
|
+
content: textContent
|
|
17837
18188
|
});
|
|
18189
|
+
return inlineScriptReference.generatedSpecifier;
|
|
17838
18190
|
}
|
|
17839
18191
|
});
|
|
17840
|
-
const htmlModified = stringifyHtmlAst(htmlAst);
|
|
17841
|
-
return {
|
|
17842
|
-
content: htmlModified
|
|
17843
|
-
};
|
|
17844
18192
|
}
|
|
17845
18193
|
}
|
|
17846
18194
|
};
|
|
@@ -19451,7 +19799,12 @@ const jsenvPluginBabel = ({
|
|
|
19451
19799
|
map
|
|
19452
19800
|
} = await applyBabelPlugins({
|
|
19453
19801
|
babelPlugins,
|
|
19454
|
-
urlInfo
|
|
19802
|
+
urlInfo,
|
|
19803
|
+
options: {
|
|
19804
|
+
generatorOpts: {
|
|
19805
|
+
retainLines: context.dev
|
|
19806
|
+
}
|
|
19807
|
+
}
|
|
19455
19808
|
});
|
|
19456
19809
|
return {
|
|
19457
19810
|
content: code,
|
|
@@ -19539,7 +19892,7 @@ const babelPluginMetadataUsesTopLevelAwait = () => {
|
|
|
19539
19892
|
programPath.traverse({
|
|
19540
19893
|
AwaitExpression: path => {
|
|
19541
19894
|
const closestFunction = path.getFunctionParent();
|
|
19542
|
-
if (!closestFunction) {
|
|
19895
|
+
if (!closestFunction || closestFunction.type === "Program") {
|
|
19543
19896
|
usesTopLevelAwait = true;
|
|
19544
19897
|
path.stop();
|
|
19545
19898
|
}
|
|
@@ -20418,9 +20771,10 @@ const jsenvPluginRibbon = ({
|
|
|
20418
20771
|
tagName: "script",
|
|
20419
20772
|
type: "module",
|
|
20420
20773
|
textContent: `
|
|
20421
|
-
import { injectRibbon} from "${ribbonClientFileReference.generatedSpecifier}"
|
|
20774
|
+
import { injectRibbon } from "${ribbonClientFileReference.generatedSpecifier}"
|
|
20422
20775
|
|
|
20423
|
-
injectRibbon(${paramsJson})
|
|
20776
|
+
injectRibbon(${paramsJson})
|
|
20777
|
+
`
|
|
20424
20778
|
});
|
|
20425
20779
|
injectHtmlNode(htmlAst, scriptNode, "jsenv:ribbon");
|
|
20426
20780
|
return stringifyHtmlAst(htmlAst);
|
|
@@ -20468,13 +20822,13 @@ const getCorePlugins = ({
|
|
|
20468
20822
|
return [jsenvPluginUrlAnalysis({
|
|
20469
20823
|
rootDirectoryUrl,
|
|
20470
20824
|
...urlAnalysis
|
|
20471
|
-
}), jsenvPluginTranspilation(transpilation),
|
|
20472
|
-
// before inline as it turns inline <script> into <script src>
|
|
20473
|
-
jsenvPluginImportmap(),
|
|
20825
|
+
}), jsenvPluginTranspilation(transpilation), jsenvPluginImportmap(),
|
|
20474
20826
|
// before node esm to handle bare specifiers
|
|
20475
20827
|
// + before node esm to handle importmap before inline content
|
|
20476
20828
|
jsenvPluginInline(),
|
|
20477
20829
|
// before "file urls" to resolve and load inline urls
|
|
20830
|
+
...(supervisor ? [jsenvPluginSupervisor(supervisor)] : []),
|
|
20831
|
+
// after inline as it needs inline script to be cooked
|
|
20478
20832
|
jsenvPluginFileUrls({
|
|
20479
20833
|
directoryReferenceAllowed,
|
|
20480
20834
|
...fileSystemMagicRedirection
|
|
@@ -22387,7 +22741,7 @@ const createFileService = ({
|
|
|
22387
22741
|
const {
|
|
22388
22742
|
runtimeName,
|
|
22389
22743
|
runtimeVersion
|
|
22390
|
-
} = parseUserAgentHeader(request.headers["user-agent"]);
|
|
22744
|
+
} = parseUserAgentHeader(request.headers["user-agent"] || "");
|
|
22391
22745
|
const runtimeId = `${runtimeName}@${runtimeVersion}`;
|
|
22392
22746
|
const existingContext = contextCache.get(runtimeId);
|
|
22393
22747
|
if (existingContext) {
|
|
@@ -22846,7 +23200,7 @@ const startDevServer = async ({
|
|
|
22846
23200
|
stopOnExit: false,
|
|
22847
23201
|
stopOnSIGINT: handleSIGINT,
|
|
22848
23202
|
stopOnInternalError: false,
|
|
22849
|
-
keepProcessAlive,
|
|
23203
|
+
keepProcessAlive: process.env.IMPORTED_BY_TEST_PLAN ? false : keepProcessAlive,
|
|
22850
23204
|
logLevel: serverLogLevel,
|
|
22851
23205
|
startLog: false,
|
|
22852
23206
|
https,
|
|
@@ -22855,7 +23209,13 @@ const startDevServer = async ({
|
|
|
22855
23209
|
hostname,
|
|
22856
23210
|
port,
|
|
22857
23211
|
requestWaitingMs: 60_000,
|
|
22858
|
-
services: [
|
|
23212
|
+
services: [{
|
|
23213
|
+
injectResponseHeaders: () => {
|
|
23214
|
+
return {
|
|
23215
|
+
"x-server-name": "jsenv_dev_server"
|
|
23216
|
+
};
|
|
23217
|
+
}
|
|
23218
|
+
}, jsenvServiceCORS({
|
|
22859
23219
|
accessControlAllowRequestOrigin: true,
|
|
22860
23220
|
accessControlAllowRequestMethod: true,
|
|
22861
23221
|
accessControlAllowRequestHeaders: true,
|
|
@@ -22864,7 +23224,7 @@ const startDevServer = async ({
|
|
|
22864
23224
|
timingAllowOrigin: true
|
|
22865
23225
|
}), {
|
|
22866
23226
|
handleRequest: request => {
|
|
22867
|
-
if (request.pathname === "/
|
|
23227
|
+
if (request.pathname === "/__params__.json") {
|
|
22868
23228
|
const json = JSON.stringify({
|
|
22869
23229
|
sourceDirectoryUrl
|
|
22870
23230
|
});
|
|
@@ -22877,12 +23237,6 @@ const startDevServer = async ({
|
|
|
22877
23237
|
body: json
|
|
22878
23238
|
};
|
|
22879
23239
|
}
|
|
22880
|
-
if (request.pathname === "/__stop__") {
|
|
22881
|
-
server.stop();
|
|
22882
|
-
return {
|
|
22883
|
-
status: 200
|
|
22884
|
-
};
|
|
22885
|
-
}
|
|
22886
23240
|
return null;
|
|
22887
23241
|
}
|
|
22888
23242
|
}, ...services, {
|
|
@@ -23041,18 +23395,28 @@ const basicFetch = async (url, {
|
|
|
23041
23395
|
headers
|
|
23042
23396
|
});
|
|
23043
23397
|
req.on("response", response => {
|
|
23044
|
-
|
|
23045
|
-
|
|
23046
|
-
|
|
23047
|
-
|
|
23048
|
-
|
|
23049
|
-
|
|
23050
|
-
|
|
23051
|
-
|
|
23052
|
-
|
|
23053
|
-
|
|
23054
|
-
|
|
23055
|
-
|
|
23398
|
+
resolve({
|
|
23399
|
+
status: response.statusCode,
|
|
23400
|
+
headers: response.headers,
|
|
23401
|
+
json: () => {
|
|
23402
|
+
req.setTimeout(0);
|
|
23403
|
+
req.destroy();
|
|
23404
|
+
return new Promise(resolve => {
|
|
23405
|
+
if (response.headers["content-type"] !== "application/json") {
|
|
23406
|
+
console.warn("not json");
|
|
23407
|
+
}
|
|
23408
|
+
let responseBody = "";
|
|
23409
|
+
response.setEncoding("utf8");
|
|
23410
|
+
response.on("data", chunk => {
|
|
23411
|
+
responseBody += chunk;
|
|
23412
|
+
});
|
|
23413
|
+
response.on("end", () => {
|
|
23414
|
+
resolve(JSON.parse(responseBody));
|
|
23415
|
+
});
|
|
23416
|
+
response.on("error", e => {
|
|
23417
|
+
reject(e);
|
|
23418
|
+
});
|
|
23419
|
+
});
|
|
23056
23420
|
}
|
|
23057
23421
|
});
|
|
23058
23422
|
});
|
|
@@ -23061,6 +23425,57 @@ const basicFetch = async (url, {
|
|
|
23061
23425
|
});
|
|
23062
23426
|
};
|
|
23063
23427
|
|
|
23428
|
+
const assertAndNormalizeWebServer = async webServer => {
|
|
23429
|
+
if (!webServer) {
|
|
23430
|
+
throw new TypeError(`webServer is required when running tests on browser(s)`);
|
|
23431
|
+
}
|
|
23432
|
+
const unexpectedParamNames = Object.keys(webServer).filter(key => {
|
|
23433
|
+
return !["origin", "moduleUrl", "rootDirectoryUrl"].includes(key);
|
|
23434
|
+
});
|
|
23435
|
+
if (unexpectedParamNames.length > 0) {
|
|
23436
|
+
throw new TypeError(`${unexpectedParamNames.join(",")}: there is no such param to webServer`);
|
|
23437
|
+
}
|
|
23438
|
+
let aServerIsListening = await pingServer(webServer.origin);
|
|
23439
|
+
if (!aServerIsListening) {
|
|
23440
|
+
if (!webServer.moduleUrl) {
|
|
23441
|
+
throw new TypeError(`webServer.moduleUrl is required as there is no server listening "${webServer.origin}"`);
|
|
23442
|
+
}
|
|
23443
|
+
try {
|
|
23444
|
+
process.env.IMPORTED_BY_TEST_PLAN = "1";
|
|
23445
|
+
await import(webServer.moduleUrl);
|
|
23446
|
+
delete process.env.IMPORTED_BY_TEST_PLAN;
|
|
23447
|
+
} catch (e) {
|
|
23448
|
+
if (e.code === "ERR_MODULE_NOT_FOUND") {
|
|
23449
|
+
throw new Error(`webServer.moduleUrl does not lead to a file at "${webServer.moduleUrl}"`);
|
|
23450
|
+
}
|
|
23451
|
+
throw e;
|
|
23452
|
+
}
|
|
23453
|
+
aServerIsListening = await pingServer(webServer.origin);
|
|
23454
|
+
if (!aServerIsListening) {
|
|
23455
|
+
throw new Error(`webServer.moduleUrl did not start a server listening at "${webServer.origin}", check file at "${webServer.moduleUrl}"`);
|
|
23456
|
+
}
|
|
23457
|
+
}
|
|
23458
|
+
const {
|
|
23459
|
+
headers
|
|
23460
|
+
} = await basicFetch(webServer.origin);
|
|
23461
|
+
if (headers["x-server-name"] === "jsenv_dev_server") {
|
|
23462
|
+
webServer.isJsenvDevServer = true;
|
|
23463
|
+
const {
|
|
23464
|
+
json
|
|
23465
|
+
} = await basicFetch(`${webServer.origin}/__params__.json`, {
|
|
23466
|
+
rejectUnauthorized: false
|
|
23467
|
+
});
|
|
23468
|
+
if (webServer.rootDirectoryUrl === undefined) {
|
|
23469
|
+
const jsenvDevServerParams = await json();
|
|
23470
|
+
webServer.rootDirectoryUrl = jsenvDevServerParams.sourceDirectoryUrl;
|
|
23471
|
+
} else {
|
|
23472
|
+
webServer.rootDirectoryUrl = assertAndNormalizeDirectoryUrl(webServer.rootDirectoryUrl, "webServer.rootDirectoryUrl");
|
|
23473
|
+
}
|
|
23474
|
+
} else {
|
|
23475
|
+
webServer.rootDirectoryUrl = assertAndNormalizeDirectoryUrl(webServer.rootDirectoryUrl, "webServer.rootDirectoryUrl");
|
|
23476
|
+
}
|
|
23477
|
+
};
|
|
23478
|
+
|
|
23064
23479
|
const generateCoverageJsonFile = async ({
|
|
23065
23480
|
coverage,
|
|
23066
23481
|
coverageJsonFileUrl,
|
|
@@ -23887,29 +24302,49 @@ const createExecutionLog = ({
|
|
|
23887
24302
|
timeEllapsed,
|
|
23888
24303
|
memoryHeap
|
|
23889
24304
|
});
|
|
24305
|
+
let log;
|
|
23890
24306
|
if (completedExecutionLogAbbreviation && status === "completed") {
|
|
23891
|
-
|
|
24307
|
+
log = `${description}${summary}`;
|
|
24308
|
+
} else {
|
|
24309
|
+
const {
|
|
24310
|
+
consoleCalls = [],
|
|
24311
|
+
errors = []
|
|
24312
|
+
} = executionResult;
|
|
24313
|
+
const consoleOutput = formatConsoleCalls(consoleCalls);
|
|
24314
|
+
const errorsOutput = formatErrors(errors);
|
|
24315
|
+
log = formatExecution({
|
|
24316
|
+
label: `${description}${summary}`,
|
|
24317
|
+
details: {
|
|
24318
|
+
file: fileRelativeUrl,
|
|
24319
|
+
...(logRuntime ? {
|
|
24320
|
+
runtime: `${runtimeName}/${runtimeVersion}`
|
|
24321
|
+
} : {}),
|
|
24322
|
+
...(logEachDuration ? {
|
|
24323
|
+
duration: status === "executing" ? msAsEllapsedTime(Date.now() - startMs) : msAsDuration(endMs - startMs)
|
|
24324
|
+
} : {})
|
|
24325
|
+
},
|
|
24326
|
+
consoleOutput,
|
|
24327
|
+
errorsOutput
|
|
24328
|
+
});
|
|
23892
24329
|
}
|
|
23893
24330
|
const {
|
|
23894
|
-
|
|
23895
|
-
|
|
23896
|
-
|
|
23897
|
-
|
|
23898
|
-
|
|
23899
|
-
|
|
23900
|
-
label: `${description}${summary}`,
|
|
23901
|
-
details: {
|
|
23902
|
-
file: fileRelativeUrl,
|
|
23903
|
-
...(logRuntime ? {
|
|
23904
|
-
runtime: `${runtimeName}/${runtimeVersion}`
|
|
23905
|
-
} : {}),
|
|
23906
|
-
...(logEachDuration ? {
|
|
23907
|
-
duration: status === "executing" ? msAsEllapsedTime(Date.now() - startMs) : msAsDuration(endMs - startMs)
|
|
23908
|
-
} : {})
|
|
23909
|
-
},
|
|
23910
|
-
consoleOutput,
|
|
23911
|
-
errorsOutput
|
|
24331
|
+
columns = 80
|
|
24332
|
+
} = process.stdout;
|
|
24333
|
+
log = wrapAnsi(log, columns, {
|
|
24334
|
+
trim: false,
|
|
24335
|
+
hard: true,
|
|
24336
|
+
wordWrap: false
|
|
23912
24337
|
});
|
|
24338
|
+
if (endMs) {
|
|
24339
|
+
if (completedExecutionLogAbbreviation) {
|
|
24340
|
+
return `${log}\n`;
|
|
24341
|
+
}
|
|
24342
|
+
if (executionIndex === counters.total - 1) {
|
|
24343
|
+
return `${log}\n`;
|
|
24344
|
+
}
|
|
24345
|
+
return `${log}\n\n`;
|
|
24346
|
+
}
|
|
24347
|
+
return log;
|
|
23913
24348
|
};
|
|
23914
24349
|
const formatErrors = errors => {
|
|
23915
24350
|
if (errors.length === 0) {
|
|
@@ -24022,39 +24457,51 @@ const descriptionFormatters = {
|
|
|
24022
24457
|
index,
|
|
24023
24458
|
total
|
|
24024
24459
|
}) => {
|
|
24025
|
-
return ANSI.color(`executing ${index
|
|
24460
|
+
return ANSI.color(`executing ${padNumber(index, total)} of ${total}`, EXECUTION_COLORS.executing);
|
|
24026
24461
|
},
|
|
24027
24462
|
aborted: ({
|
|
24028
24463
|
index,
|
|
24029
24464
|
total
|
|
24030
24465
|
}) => {
|
|
24031
|
-
return ANSI.color(`${UNICODE.FAILURE_RAW} execution ${index
|
|
24466
|
+
return ANSI.color(`${UNICODE.FAILURE_RAW} execution ${padNumber(index, total)} of ${total} aborted`, EXECUTION_COLORS.aborted);
|
|
24032
24467
|
},
|
|
24033
24468
|
timedout: ({
|
|
24034
24469
|
index,
|
|
24035
24470
|
total,
|
|
24036
24471
|
executionParams
|
|
24037
24472
|
}) => {
|
|
24038
|
-
return ANSI.color(`${UNICODE.FAILURE_RAW} execution ${index
|
|
24473
|
+
return ANSI.color(`${UNICODE.FAILURE_RAW} execution ${padNumber(index, total)} of ${total} timeout after ${executionParams.allocatedMs}ms`, EXECUTION_COLORS.timedout);
|
|
24039
24474
|
},
|
|
24040
24475
|
failed: ({
|
|
24041
24476
|
index,
|
|
24042
24477
|
total
|
|
24043
24478
|
}) => {
|
|
24044
|
-
return ANSI.color(`${UNICODE.FAILURE_RAW} execution ${index
|
|
24479
|
+
return ANSI.color(`${UNICODE.FAILURE_RAW} execution ${padNumber(index, total)} of ${total} failed`, EXECUTION_COLORS.failed);
|
|
24045
24480
|
},
|
|
24046
24481
|
completed: ({
|
|
24047
24482
|
index,
|
|
24048
24483
|
total
|
|
24049
24484
|
}) => {
|
|
24050
|
-
return ANSI.color(`${UNICODE.OK_RAW} execution ${index
|
|
24485
|
+
return ANSI.color(`${UNICODE.OK_RAW} execution ${padNumber(index, total)} of ${total} completed`, EXECUTION_COLORS.completed);
|
|
24051
24486
|
},
|
|
24052
24487
|
cancelled: ({
|
|
24053
24488
|
index,
|
|
24054
24489
|
total
|
|
24055
24490
|
}) => {
|
|
24056
|
-
return ANSI.color(`${UNICODE.FAILURE_RAW} execution ${index
|
|
24491
|
+
return ANSI.color(`${UNICODE.FAILURE_RAW} execution ${padNumber(index, total)} of ${total} cancelled`, EXECUTION_COLORS.cancelled);
|
|
24492
|
+
}
|
|
24493
|
+
};
|
|
24494
|
+
const padNumber = (index, total) => {
|
|
24495
|
+
const number = index + 1;
|
|
24496
|
+
const numberWidth = String(number).length;
|
|
24497
|
+
const totalWith = String(total).length;
|
|
24498
|
+
let missingWidth = totalWith - numberWidth;
|
|
24499
|
+
let padded = "";
|
|
24500
|
+
while (missingWidth--) {
|
|
24501
|
+
padded += "0";
|
|
24057
24502
|
}
|
|
24503
|
+
padded += number;
|
|
24504
|
+
return padded;
|
|
24058
24505
|
};
|
|
24059
24506
|
const formatConsoleCalls = consoleCalls => {
|
|
24060
24507
|
if (consoleCalls.length === 0) {
|
|
@@ -24200,8 +24647,7 @@ const executeSteps = async (executionSteps, {
|
|
|
24200
24647
|
completedExecutionLogMerging,
|
|
24201
24648
|
completedExecutionLogAbbreviation,
|
|
24202
24649
|
rootDirectoryUrl,
|
|
24203
|
-
|
|
24204
|
-
sourceDirectoryUrl,
|
|
24650
|
+
webServer,
|
|
24205
24651
|
keepRunning,
|
|
24206
24652
|
defaultMsAllocatedPerExecution,
|
|
24207
24653
|
maxExecutionsInParallel,
|
|
@@ -24280,8 +24726,7 @@ const executeSteps = async (executionSteps, {
|
|
|
24280
24726
|
}
|
|
24281
24727
|
let runtimeParams = {
|
|
24282
24728
|
rootDirectoryUrl,
|
|
24283
|
-
|
|
24284
|
-
sourceDirectoryUrl,
|
|
24729
|
+
webServer,
|
|
24285
24730
|
coverageEnabled,
|
|
24286
24731
|
coverageConfig,
|
|
24287
24732
|
coverageMethodForBrowsers,
|
|
@@ -24425,7 +24870,7 @@ const executeSteps = async (executionSteps, {
|
|
|
24425
24870
|
global.gc();
|
|
24426
24871
|
}
|
|
24427
24872
|
if (executionLogsEnabled) {
|
|
24428
|
-
|
|
24873
|
+
const log = createExecutionLog(afterExecutionInfo, {
|
|
24429
24874
|
completedExecutionLogAbbreviation,
|
|
24430
24875
|
counters,
|
|
24431
24876
|
logRuntime,
|
|
@@ -24437,18 +24882,6 @@ const executeSteps = async (executionSteps, {
|
|
|
24437
24882
|
memoryHeap: memoryUsage().heapUsed
|
|
24438
24883
|
} : {})
|
|
24439
24884
|
});
|
|
24440
|
-
log = `${log}
|
|
24441
|
-
|
|
24442
|
-
`;
|
|
24443
|
-
const {
|
|
24444
|
-
columns = 80
|
|
24445
|
-
} = process.stdout;
|
|
24446
|
-
log = wrapAnsi(log, columns, {
|
|
24447
|
-
trim: false,
|
|
24448
|
-
hard: true,
|
|
24449
|
-
wordWrap: false
|
|
24450
|
-
});
|
|
24451
|
-
|
|
24452
24885
|
// replace spinner with this execution result
|
|
24453
24886
|
if (spinner) spinner.stop();
|
|
24454
24887
|
executionLog.write(log);
|
|
@@ -24466,7 +24899,12 @@ const executeSteps = async (executionSteps, {
|
|
|
24466
24899
|
});
|
|
24467
24900
|
}
|
|
24468
24901
|
}
|
|
24469
|
-
|
|
24902
|
+
const isLastExecutionLog = executionIndex === executionSteps.length - 1;
|
|
24903
|
+
const cancelRemaining = failFast && executionResult.status !== "completed" && counters.done < counters.total;
|
|
24904
|
+
if (isLastExecutionLog) {
|
|
24905
|
+
executionLog.write("\n");
|
|
24906
|
+
}
|
|
24907
|
+
if (cancelRemaining) {
|
|
24470
24908
|
logger.info(`"failFast" enabled -> cancel remaining executions`);
|
|
24471
24909
|
failFastAbortController.abort();
|
|
24472
24910
|
}
|
|
@@ -24571,7 +25009,7 @@ const executeInParallel = async ({
|
|
|
24571
25009
|
* Execute a list of files and log how it goes.
|
|
24572
25010
|
* @param {Object} testPlanParameters
|
|
24573
25011
|
* @param {string|url} testPlanParameters.rootDirectoryUrl Directory containing test files;
|
|
24574
|
-
* @param {
|
|
25012
|
+
* @param {Object} [testPlanParameters.webServer] Web server info; required when executing test on browsers
|
|
24575
25013
|
* @param {Object} testPlanParameters.testPlan Object associating files with runtimes where they will be executed
|
|
24576
25014
|
* @param {boolean} [testPlanParameters.completedExecutionLogAbbreviation=false] Abbreviate completed execution information to shorten terminal output
|
|
24577
25015
|
* @param {boolean} [testPlanParameters.completedExecutionLogMerging=false] Merge completed execution logs to shorten terminal output
|
|
@@ -24598,8 +25036,7 @@ const executeTestPlan = async ({
|
|
|
24598
25036
|
completedExecutionLogAbbreviation = false,
|
|
24599
25037
|
completedExecutionLogMerging = false,
|
|
24600
25038
|
rootDirectoryUrl,
|
|
24601
|
-
|
|
24602
|
-
devServerOrigin,
|
|
25039
|
+
webServer,
|
|
24603
25040
|
testPlan,
|
|
24604
25041
|
updateProcessExitCode = true,
|
|
24605
25042
|
maxExecutionsInParallel = 1,
|
|
@@ -24644,8 +25081,6 @@ const executeTestPlan = async ({
|
|
|
24644
25081
|
}) => {
|
|
24645
25082
|
let someNeedsServer = false;
|
|
24646
25083
|
let someNodeRuntime = false;
|
|
24647
|
-
let stopDevServerNeeded = false;
|
|
24648
|
-
let sourceDirectoryUrl;
|
|
24649
25084
|
const runtimes = {};
|
|
24650
25085
|
// param validation
|
|
24651
25086
|
{
|
|
@@ -24680,34 +25115,7 @@ const executeTestPlan = async ({
|
|
|
24680
25115
|
});
|
|
24681
25116
|
});
|
|
24682
25117
|
if (someNeedsServer) {
|
|
24683
|
-
|
|
24684
|
-
throw new TypeError(`devServerOrigin is required when running tests on browser(s)`);
|
|
24685
|
-
}
|
|
24686
|
-
let devServerStarted = await pingServer(devServerOrigin);
|
|
24687
|
-
if (!devServerStarted) {
|
|
24688
|
-
if (!devServerModuleUrl) {
|
|
24689
|
-
throw new TypeError(`devServerModuleUrl is required when dev server is not started in order to run tests on browser(s)`);
|
|
24690
|
-
}
|
|
24691
|
-
try {
|
|
24692
|
-
process.env.IMPORTED_BY_TEST_PLAN = "1";
|
|
24693
|
-
await import(devServerModuleUrl);
|
|
24694
|
-
delete process.env.IMPORTED_BY_TEST_PLAN;
|
|
24695
|
-
} catch (e) {
|
|
24696
|
-
if (e.code === "ERR_MODULE_NOT_FOUND") {
|
|
24697
|
-
throw new Error(`Cannot find file responsible to start dev server at "${devServerModuleUrl}"`);
|
|
24698
|
-
}
|
|
24699
|
-
throw e;
|
|
24700
|
-
}
|
|
24701
|
-
devServerStarted = await pingServer(devServerOrigin);
|
|
24702
|
-
if (!devServerStarted) {
|
|
24703
|
-
throw new Error(`dev server not started after importing "${devServerModuleUrl}", ensure this module file is starting a server at "${devServerOrigin}"`);
|
|
24704
|
-
}
|
|
24705
|
-
stopDevServerNeeded = true;
|
|
24706
|
-
}
|
|
24707
|
-
const devServerParams = await basicFetch(`${devServerOrigin}/__server_params__.json`, {
|
|
24708
|
-
rejectUnauthorized: false
|
|
24709
|
-
});
|
|
24710
|
-
sourceDirectoryUrl = devServerParams.sourceDirectoryUrl;
|
|
25118
|
+
await assertAndNormalizeWebServer(webServer);
|
|
24711
25119
|
}
|
|
24712
25120
|
if (coverageEnabled) {
|
|
24713
25121
|
if (typeof coverageConfig !== "object") {
|
|
@@ -24785,6 +25193,8 @@ const executeTestPlan = async ({
|
|
|
24785
25193
|
}
|
|
24786
25194
|
}
|
|
24787
25195
|
testPlan = {
|
|
25196
|
+
"file:///**/node_modules/": null,
|
|
25197
|
+
"**/*./": null,
|
|
24788
25198
|
...testPlan,
|
|
24789
25199
|
"**/.jsenv/": null
|
|
24790
25200
|
};
|
|
@@ -24809,8 +25219,7 @@ const executeTestPlan = async ({
|
|
|
24809
25219
|
completedExecutionLogMerging,
|
|
24810
25220
|
completedExecutionLogAbbreviation,
|
|
24811
25221
|
rootDirectoryUrl,
|
|
24812
|
-
|
|
24813
|
-
sourceDirectoryUrl,
|
|
25222
|
+
webServer,
|
|
24814
25223
|
maxExecutionsInParallel,
|
|
24815
25224
|
defaultMsAllocatedPerExecution,
|
|
24816
25225
|
failFast,
|
|
@@ -24825,17 +25234,6 @@ const executeTestPlan = async ({
|
|
|
24825
25234
|
coverageV8ConflictWarning,
|
|
24826
25235
|
coverageTempDirectoryUrl
|
|
24827
25236
|
});
|
|
24828
|
-
if (stopDevServerNeeded) {
|
|
24829
|
-
// we are expecting ECONNRESET because server will be stopped by the request
|
|
24830
|
-
basicFetch(`${devServerOrigin}/__stop__`, {
|
|
24831
|
-
rejectUnauthorized: false
|
|
24832
|
-
}).catch(e => {
|
|
24833
|
-
if (e.code === "ECONNRESET") {
|
|
24834
|
-
return;
|
|
24835
|
-
}
|
|
24836
|
-
throw e;
|
|
24837
|
-
});
|
|
24838
|
-
}
|
|
24839
25237
|
if (updateProcessExitCode && result.planSummary.counters.total !== result.planSummary.counters.completed) {
|
|
24840
25238
|
process.exitCode = 1;
|
|
24841
25239
|
}
|
|
@@ -24884,7 +25282,7 @@ const createRuntimeFromPlaywright = ({
|
|
|
24884
25282
|
browserName,
|
|
24885
25283
|
browserVersion,
|
|
24886
25284
|
coveragePlaywrightAPIAvailable = false,
|
|
24887
|
-
|
|
25285
|
+
shouldIgnoreError = () => false,
|
|
24888
25286
|
transformErrorHook = error => error,
|
|
24889
25287
|
isolatedTab = false
|
|
24890
25288
|
}) => {
|
|
@@ -24898,9 +25296,8 @@ const createRuntimeFromPlaywright = ({
|
|
|
24898
25296
|
signal = new AbortController().signal,
|
|
24899
25297
|
logger,
|
|
24900
25298
|
rootDirectoryUrl,
|
|
25299
|
+
webServer,
|
|
24901
25300
|
fileRelativeUrl,
|
|
24902
|
-
devServerOrigin,
|
|
24903
|
-
sourceDirectoryUrl,
|
|
24904
25301
|
// measurePerformance,
|
|
24905
25302
|
collectPerformance,
|
|
24906
25303
|
coverageEnabled = false,
|
|
@@ -24915,6 +25312,19 @@ const createRuntimeFromPlaywright = ({
|
|
|
24915
25312
|
playwrightLaunchOptions = {},
|
|
24916
25313
|
ignoreHTTPSErrors = true
|
|
24917
25314
|
}) => {
|
|
25315
|
+
const fileUrl = new URL(fileRelativeUrl, rootDirectoryUrl).href;
|
|
25316
|
+
if (!urlIsInsideOf(fileUrl, webServer.rootDirectoryUrl)) {
|
|
25317
|
+
throw new Error(`Cannot execute file that is outside web server root directory
|
|
25318
|
+
--- file ---
|
|
25319
|
+
${fileUrl}
|
|
25320
|
+
--- web server root directory url ---
|
|
25321
|
+
${webServer.rootDirectoryUrl}`);
|
|
25322
|
+
}
|
|
25323
|
+
const fileServerUrl = moveUrl({
|
|
25324
|
+
url: fileUrl,
|
|
25325
|
+
from: webServer.rootDirectoryUrl,
|
|
25326
|
+
to: `${webServer.origin}/`
|
|
25327
|
+
});
|
|
24918
25328
|
const cleanupCallbackList = createCallbackListNotifiedOnce();
|
|
24919
25329
|
const cleanup = memoize(async reason => {
|
|
24920
25330
|
await cleanupCallbackList.notify({
|
|
@@ -24978,6 +25388,13 @@ const createRuntimeFromPlaywright = ({
|
|
|
24978
25388
|
} : {})
|
|
24979
25389
|
}
|
|
24980
25390
|
});
|
|
25391
|
+
if (!webServer.isJsenvDevServer) {
|
|
25392
|
+
await initJsExecutionMiddleware(page, {
|
|
25393
|
+
webServer,
|
|
25394
|
+
fileUrl,
|
|
25395
|
+
fileServerUrl
|
|
25396
|
+
});
|
|
25397
|
+
}
|
|
24981
25398
|
const closePage = async () => {
|
|
24982
25399
|
try {
|
|
24983
25400
|
await page.close();
|
|
@@ -25006,8 +25423,8 @@ const createRuntimeFromPlaywright = ({
|
|
|
25006
25423
|
const v8CoveragesWithFsUrls = v8CoveragesWithWebUrls.map(v8CoveragesWithWebUrl => {
|
|
25007
25424
|
const fsUrl = moveUrl({
|
|
25008
25425
|
url: v8CoveragesWithWebUrl.url,
|
|
25009
|
-
from: `${
|
|
25010
|
-
to:
|
|
25426
|
+
from: `${webServer.origin}/`,
|
|
25427
|
+
to: webServer.rootDirectoryUrl
|
|
25011
25428
|
});
|
|
25012
25429
|
return {
|
|
25013
25430
|
...v8CoveragesWithWebUrl,
|
|
@@ -25069,19 +25486,7 @@ const createRuntimeFromPlaywright = ({
|
|
|
25069
25486
|
result.performance = performance;
|
|
25070
25487
|
});
|
|
25071
25488
|
}
|
|
25072
|
-
|
|
25073
|
-
if (!urlIsInsideOf(fileUrl, sourceDirectoryUrl)) {
|
|
25074
|
-
throw new Error(`Cannot execute file that is outside source directory
|
|
25075
|
-
--- file ---
|
|
25076
|
-
${fileUrl}
|
|
25077
|
-
--- source directory ---
|
|
25078
|
-
${sourceDirectoryUrl}`);
|
|
25079
|
-
}
|
|
25080
|
-
const fileDevServerUrl = moveUrl({
|
|
25081
|
-
url: fileUrl,
|
|
25082
|
-
from: sourceDirectoryUrl,
|
|
25083
|
-
to: `${devServerOrigin}/`
|
|
25084
|
-
});
|
|
25489
|
+
|
|
25085
25490
|
// https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-console
|
|
25086
25491
|
const removeConsoleListener = registerEvent({
|
|
25087
25492
|
object: page,
|
|
@@ -25109,7 +25514,7 @@ ${sourceDirectoryUrl}`);
|
|
|
25109
25514
|
object: page,
|
|
25110
25515
|
eventType: "error",
|
|
25111
25516
|
callback: error => {
|
|
25112
|
-
if (
|
|
25517
|
+
if (shouldIgnoreError(error, "error")) {
|
|
25113
25518
|
return;
|
|
25114
25519
|
}
|
|
25115
25520
|
cb(transformErrorHook(error));
|
|
@@ -25122,7 +25527,10 @@ ${sourceDirectoryUrl}`);
|
|
|
25122
25527
|
// object: page,
|
|
25123
25528
|
// eventType: "pageerror",
|
|
25124
25529
|
// callback: (error) => {
|
|
25125
|
-
// if (
|
|
25530
|
+
// if (
|
|
25531
|
+
// webServer.isJsenvDevServer ||
|
|
25532
|
+
// shouldIgnoreError(error, "pageerror")
|
|
25533
|
+
// ) {
|
|
25126
25534
|
// return
|
|
25127
25535
|
// }
|
|
25128
25536
|
// result.errors.push(transformErrorHook(error))
|
|
@@ -25164,16 +25572,28 @@ ${sourceDirectoryUrl}`);
|
|
|
25164
25572
|
},
|
|
25165
25573
|
response: async cb => {
|
|
25166
25574
|
try {
|
|
25167
|
-
await page.goto(
|
|
25575
|
+
await page.goto(fileServerUrl, {
|
|
25168
25576
|
timeout: 0
|
|
25169
25577
|
});
|
|
25170
25578
|
const returnValue = await page.evaluate( /* eslint-disable no-undef */
|
|
25171
25579
|
/* istanbul ignore next */
|
|
25172
|
-
() => {
|
|
25580
|
+
async () => {
|
|
25581
|
+
let startTime;
|
|
25582
|
+
try {
|
|
25583
|
+
startTime = window.performance.timing.navigationStart;
|
|
25584
|
+
} catch (e) {
|
|
25585
|
+
startTime = Date.now();
|
|
25586
|
+
}
|
|
25173
25587
|
if (!window.__supervisor__) {
|
|
25174
|
-
throw new Error(
|
|
25588
|
+
throw new Error("window.__supervisor__ is undefined");
|
|
25175
25589
|
}
|
|
25176
|
-
|
|
25590
|
+
const executionResultFromJsenvSupervisor = await window.__supervisor__.getDocumentExecutionResult();
|
|
25591
|
+
return {
|
|
25592
|
+
type: "window_supervisor",
|
|
25593
|
+
startTime,
|
|
25594
|
+
endTime: Date.now(),
|
|
25595
|
+
executionResults: executionResultFromJsenvSupervisor.executionResults
|
|
25596
|
+
};
|
|
25177
25597
|
}
|
|
25178
25598
|
/* eslint-enable no-undef */);
|
|
25179
25599
|
|
|
@@ -25196,12 +25616,18 @@ ${sourceDirectoryUrl}`);
|
|
|
25196
25616
|
result.errors.push(error);
|
|
25197
25617
|
return;
|
|
25198
25618
|
}
|
|
25619
|
+
if (winner.name === "pageerror") {
|
|
25620
|
+
let error = winner.data;
|
|
25621
|
+
result.status = "failed";
|
|
25622
|
+
result.errors.push(error);
|
|
25623
|
+
return;
|
|
25624
|
+
}
|
|
25199
25625
|
if (winner.name === "closed") {
|
|
25200
25626
|
result.status = "failed";
|
|
25201
25627
|
result.errors.push(isBrowserDedicatedToExecution ? new Error(`browser disconnected during execution`) : new Error(`page closed during execution`));
|
|
25202
25628
|
return;
|
|
25203
25629
|
}
|
|
25204
|
-
// winner.name
|
|
25630
|
+
// winner.name === "response"
|
|
25205
25631
|
const {
|
|
25206
25632
|
executionResults
|
|
25207
25633
|
} = winner.data;
|
|
@@ -25211,10 +25637,17 @@ ${sourceDirectoryUrl}`);
|
|
|
25211
25637
|
const executionResult = executionResults[key];
|
|
25212
25638
|
if (executionResult.status === "failed") {
|
|
25213
25639
|
result.status = "failed";
|
|
25214
|
-
|
|
25215
|
-
|
|
25216
|
-
|
|
25217
|
-
|
|
25640
|
+
if (executionResult.exception) {
|
|
25641
|
+
result.errors.push({
|
|
25642
|
+
...executionResult.exception,
|
|
25643
|
+
stack: executionResult.exception.text
|
|
25644
|
+
});
|
|
25645
|
+
} else {
|
|
25646
|
+
result.errors.push({
|
|
25647
|
+
...executionResult.error,
|
|
25648
|
+
stack: executionResult.error.stack
|
|
25649
|
+
});
|
|
25650
|
+
}
|
|
25218
25651
|
}
|
|
25219
25652
|
});
|
|
25220
25653
|
};
|
|
@@ -25243,7 +25676,7 @@ ${sourceDirectoryUrl}`);
|
|
|
25243
25676
|
browserName,
|
|
25244
25677
|
browserVersion,
|
|
25245
25678
|
coveragePlaywrightAPIAvailable,
|
|
25246
|
-
|
|
25679
|
+
shouldIgnoreError,
|
|
25247
25680
|
transformErrorHook,
|
|
25248
25681
|
isolatedTab: true
|
|
25249
25682
|
});
|
|
@@ -25361,6 +25794,106 @@ const extractTextFromConsoleMessage = consoleMessage => {
|
|
|
25361
25794
|
// return text
|
|
25362
25795
|
};
|
|
25363
25796
|
|
|
25797
|
+
const initJsExecutionMiddleware = async (page, {
|
|
25798
|
+
webServer,
|
|
25799
|
+
fileUrl,
|
|
25800
|
+
fileServerUrl
|
|
25801
|
+
}) => {
|
|
25802
|
+
const inlineScriptContents = new Map();
|
|
25803
|
+
const interceptHtmlToExecute = async ({
|
|
25804
|
+
route
|
|
25805
|
+
}) => {
|
|
25806
|
+
// Fetch original response.
|
|
25807
|
+
const response = await route.fetch();
|
|
25808
|
+
// Add a prefix to the title.
|
|
25809
|
+
const originalBody = await response.text();
|
|
25810
|
+
const injectionResult = await injectSupervisorIntoHTML({
|
|
25811
|
+
content: originalBody,
|
|
25812
|
+
url: fileUrl
|
|
25813
|
+
}, {
|
|
25814
|
+
supervisorScriptSrc: `/@fs/${supervisorFileUrl$1.slice("file:///".length)}`,
|
|
25815
|
+
supervisorOptions: {},
|
|
25816
|
+
inlineAsRemote: true,
|
|
25817
|
+
webServer,
|
|
25818
|
+
onInlineScript: ({
|
|
25819
|
+
src,
|
|
25820
|
+
textContent
|
|
25821
|
+
}) => {
|
|
25822
|
+
const inlineScriptWebUrl = new URL(src, `${webServer.origin}/`).href;
|
|
25823
|
+
inlineScriptContents.set(inlineScriptWebUrl, textContent);
|
|
25824
|
+
}
|
|
25825
|
+
});
|
|
25826
|
+
route.fulfill({
|
|
25827
|
+
response,
|
|
25828
|
+
body: injectionResult.content,
|
|
25829
|
+
headers: {
|
|
25830
|
+
...response.headers(),
|
|
25831
|
+
"content-length": Buffer.byteLength(injectionResult.content)
|
|
25832
|
+
}
|
|
25833
|
+
});
|
|
25834
|
+
};
|
|
25835
|
+
const interceptInlineScript = ({
|
|
25836
|
+
url,
|
|
25837
|
+
route
|
|
25838
|
+
}) => {
|
|
25839
|
+
const inlineScriptContent = inlineScriptContents.get(url);
|
|
25840
|
+
route.fulfill({
|
|
25841
|
+
status: 200,
|
|
25842
|
+
body: inlineScriptContent,
|
|
25843
|
+
headers: {
|
|
25844
|
+
"content-type": "text/javascript",
|
|
25845
|
+
"content-length": Buffer.byteLength(inlineScriptContent)
|
|
25846
|
+
}
|
|
25847
|
+
});
|
|
25848
|
+
};
|
|
25849
|
+
const interceptFileSystemUrl = ({
|
|
25850
|
+
url,
|
|
25851
|
+
route
|
|
25852
|
+
}) => {
|
|
25853
|
+
const relativeUrl = url.slice(webServer.origin.length);
|
|
25854
|
+
const fsPath = relativeUrl.slice("/@fs/".length);
|
|
25855
|
+
const fsUrl = `file:///${fsPath}`;
|
|
25856
|
+
const fileContent = readFileSync$1(new URL(fsUrl), "utf8");
|
|
25857
|
+
route.fulfill({
|
|
25858
|
+
status: 200,
|
|
25859
|
+
body: fileContent,
|
|
25860
|
+
headers: {
|
|
25861
|
+
"content-type": "text/javascript",
|
|
25862
|
+
"content-length": Buffer.byteLength(fileContent)
|
|
25863
|
+
}
|
|
25864
|
+
});
|
|
25865
|
+
};
|
|
25866
|
+
await page.route("**", async route => {
|
|
25867
|
+
const request = route.request();
|
|
25868
|
+
const url = request.url();
|
|
25869
|
+
if (url === fileServerUrl && urlToExtension$1(url) === ".html") {
|
|
25870
|
+
interceptHtmlToExecute({
|
|
25871
|
+
url,
|
|
25872
|
+
request,
|
|
25873
|
+
route
|
|
25874
|
+
});
|
|
25875
|
+
return;
|
|
25876
|
+
}
|
|
25877
|
+
if (inlineScriptContents.has(url)) {
|
|
25878
|
+
interceptInlineScript({
|
|
25879
|
+
url,
|
|
25880
|
+
request,
|
|
25881
|
+
route
|
|
25882
|
+
});
|
|
25883
|
+
return;
|
|
25884
|
+
}
|
|
25885
|
+
const fsServerUrl = new URL("/@fs/", webServer.origin);
|
|
25886
|
+
if (url.startsWith(fsServerUrl)) {
|
|
25887
|
+
interceptFileSystemUrl({
|
|
25888
|
+
url,
|
|
25889
|
+
request,
|
|
25890
|
+
route
|
|
25891
|
+
});
|
|
25892
|
+
return;
|
|
25893
|
+
}
|
|
25894
|
+
route.fallback();
|
|
25895
|
+
});
|
|
25896
|
+
};
|
|
25364
25897
|
const registerEvent = ({
|
|
25365
25898
|
object,
|
|
25366
25899
|
eventType,
|
|
@@ -25394,7 +25927,7 @@ const webkit = createRuntimeFromPlaywright({
|
|
|
25394
25927
|
// browserVersion will be set by "browser._initializer.version"
|
|
25395
25928
|
// see also https://github.com/microsoft/playwright/releases
|
|
25396
25929
|
browserVersion: "unset",
|
|
25397
|
-
|
|
25930
|
+
shouldIgnoreError: error => {
|
|
25398
25931
|
// we catch error during execution but safari throw unhandled rejection
|
|
25399
25932
|
// in a non-deterministic way.
|
|
25400
25933
|
// I suppose it's due to some race condition to decide if the promise is catched or not
|
|
@@ -26321,8 +26854,7 @@ const startBuildServer = async ({
|
|
|
26321
26854
|
stopOnExit: false,
|
|
26322
26855
|
stopOnSIGINT: false,
|
|
26323
26856
|
stopOnInternalError: false,
|
|
26324
|
-
|
|
26325
|
-
keepProcessAlive,
|
|
26857
|
+
keepProcessAlive: process.env.IMPORTED_BY_TEST_PLAN ? false : keepProcessAlive,
|
|
26326
26858
|
logLevel: serverLogLevel,
|
|
26327
26859
|
startLog: false,
|
|
26328
26860
|
https,
|
|
@@ -26406,8 +26938,7 @@ const execute = async ({
|
|
|
26406
26938
|
handleSIGINT = true,
|
|
26407
26939
|
logLevel,
|
|
26408
26940
|
rootDirectoryUrl,
|
|
26409
|
-
|
|
26410
|
-
devServerOrigin,
|
|
26941
|
+
webServer,
|
|
26411
26942
|
fileRelativeUrl,
|
|
26412
26943
|
allocatedMs,
|
|
26413
26944
|
mirrorConsole = true,
|
|
@@ -26433,23 +26964,16 @@ const execute = async ({
|
|
|
26433
26964
|
}, abort);
|
|
26434
26965
|
});
|
|
26435
26966
|
}
|
|
26967
|
+
if (runtime.type === "browser") {
|
|
26968
|
+
await assertAndNormalizeWebServer(webServer);
|
|
26969
|
+
}
|
|
26436
26970
|
let resultTransformer = result => result;
|
|
26437
26971
|
runtimeParams = {
|
|
26438
26972
|
rootDirectoryUrl,
|
|
26439
|
-
|
|
26440
|
-
devServerOrigin,
|
|
26973
|
+
webServer,
|
|
26441
26974
|
fileRelativeUrl,
|
|
26442
26975
|
...runtimeParams
|
|
26443
26976
|
};
|
|
26444
|
-
if (runtime.type === "browser") {
|
|
26445
|
-
if (!devServerOrigin) {
|
|
26446
|
-
throw new TypeError(`devServerOrigin is required to execute file on a browser`);
|
|
26447
|
-
}
|
|
26448
|
-
const devServerStarted = await pingServer(devServerOrigin);
|
|
26449
|
-
if (!devServerStarted) {
|
|
26450
|
-
throw new Error(`no server listening at ${devServerOrigin}. It is required to execute file`);
|
|
26451
|
-
}
|
|
26452
|
-
}
|
|
26453
26977
|
let result = await run({
|
|
26454
26978
|
signal: executeOperation.signal,
|
|
26455
26979
|
logger,
|