@jsenv/core 33.0.2 → 34.0.1
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 +938 -370
- 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 +283 -0
- package/src/plugins/supervisor/jsenv_plugin_supervisor.js +48 -233
- package/src/plugins/transpilation/as_js_classic/convert_js_module_to_js_classic.js +67 -30
- 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/babel_plugin_transform_modules_systemjs.cjs +0 -392
- package/dist/js/script_type_module_supervisor.js +0 -109
- package/src/plugins/supervisor/client/script_type_module_supervisor.js +0 -98
- package/src/plugins/transpilation/as_js_classic/babel_plugin_transform_modules_systemjs.cjs +0 -608
|
@@ -1,68 +1,19 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Jsenv needs to wait for all js execution inside an HTML page before killing the browser.
|
|
3
|
-
* A naive approach would consider execution done when "load" event is dispatched on window but:
|
|
4
|
-
*
|
|
5
|
-
* scenario | covered by window "load"
|
|
6
|
-
* ------------------------------------------- | -------------------------
|
|
7
|
-
* js referenced by <script src> | yes
|
|
8
|
-
* js inlined into <script> | yes
|
|
9
|
-
* js referenced by <script type="module" src> | partially (not for import and top level await)
|
|
10
|
-
* js inlined into <script type="module"> | not at all
|
|
11
|
-
*
|
|
12
2
|
* This plugin provides a way for jsenv to know when js execution is done
|
|
13
|
-
* As a side effect this plugin enables ability to hot reload js inlined into <script hot-accept>
|
|
14
|
-
*
|
|
15
|
-
* <script src="file.js">
|
|
16
|
-
* becomes
|
|
17
|
-
* <script>
|
|
18
|
-
* window.__supervisor__.superviseScript({ src: 'file.js' })
|
|
19
|
-
* </script>
|
|
20
|
-
*
|
|
21
|
-
* <script>
|
|
22
|
-
* console.log(42)
|
|
23
|
-
* </script>
|
|
24
|
-
* becomes
|
|
25
|
-
* <script>
|
|
26
|
-
* window.__supervisor__.superviseScript({ src: 'main.html@L10-L13.js' })
|
|
27
|
-
* </script>
|
|
28
|
-
*
|
|
29
|
-
* <script type="module" src="module.js"></script>
|
|
30
|
-
* becomes
|
|
31
|
-
* <script type="module">
|
|
32
|
-
* import { superviseScriptTypeModule } from 'supervisor'
|
|
33
|
-
* superviseScriptTypeModule({ src: "module.js" })
|
|
34
|
-
* </script>
|
|
35
|
-
*
|
|
36
|
-
* <script type="module">
|
|
37
|
-
* console.log(42)
|
|
38
|
-
* </script>
|
|
39
|
-
* becomes
|
|
40
|
-
* <script type="module">
|
|
41
|
-
* import { superviseScriptTypeModule } from 'supervisor'
|
|
42
|
-
* superviseScriptTypeModule({ src: 'main.html@L10-L13.js' })
|
|
43
|
-
* </script>
|
|
44
3
|
*/
|
|
45
4
|
|
|
46
5
|
import { fileURLToPath } from "node:url"
|
|
47
|
-
import {
|
|
48
|
-
parseHtmlString,
|
|
49
|
-
stringifyHtmlAst,
|
|
50
|
-
visitHtmlNodes,
|
|
51
|
-
getHtmlNodeAttribute,
|
|
52
|
-
setHtmlNodeAttributes,
|
|
53
|
-
analyzeScriptNode,
|
|
54
|
-
injectScriptNodeAsEarlyAsPossible,
|
|
55
|
-
createHtmlNode,
|
|
56
|
-
getHtmlNodePosition,
|
|
57
|
-
getHtmlNodeText,
|
|
58
|
-
removeHtmlNodeText,
|
|
59
|
-
setHtmlNodeText,
|
|
60
|
-
} from "@jsenv/ast"
|
|
61
|
-
import { generateInlineContentUrl, stringifyUrlSite } from "@jsenv/urls"
|
|
62
6
|
import { getOriginalPosition } from "@jsenv/sourcemap"
|
|
7
|
+
import { stringifyUrlSite } from "@jsenv/urls"
|
|
63
8
|
|
|
9
|
+
import { injectSupervisorIntoHTML } from "./html_supervisor_injection.js"
|
|
64
10
|
import { requireFromJsenv } from "@jsenv/core/src/require_from_jsenv.js"
|
|
65
11
|
|
|
12
|
+
export const supervisorFileUrl = new URL(
|
|
13
|
+
"./client/supervisor.js",
|
|
14
|
+
import.meta.url,
|
|
15
|
+
).href
|
|
16
|
+
|
|
66
17
|
export const jsenvPluginSupervisor = ({
|
|
67
18
|
logs = false,
|
|
68
19
|
measurePerf = false,
|
|
@@ -70,13 +21,6 @@ export const jsenvPluginSupervisor = ({
|
|
|
70
21
|
openInEditor = true,
|
|
71
22
|
errorBaseUrl,
|
|
72
23
|
}) => {
|
|
73
|
-
const supervisorFileUrl = new URL("./client/supervisor.js", import.meta.url)
|
|
74
|
-
.href
|
|
75
|
-
const scriptTypeModuleSupervisorFileUrl = new URL(
|
|
76
|
-
"./client/script_type_module_supervisor.js",
|
|
77
|
-
import.meta.url,
|
|
78
|
-
).href
|
|
79
|
-
|
|
80
24
|
return {
|
|
81
25
|
name: "jsenv:supervisor",
|
|
82
26
|
appliesDuring: "dev",
|
|
@@ -216,184 +160,55 @@ export const jsenvPluginSupervisor = ({
|
|
|
216
160
|
},
|
|
217
161
|
transformUrlContent: {
|
|
218
162
|
html: ({ url, content }, context) => {
|
|
219
|
-
const htmlAst = parseHtmlString(content)
|
|
220
|
-
const scriptsToSupervise = []
|
|
221
|
-
|
|
222
|
-
const handleInlineScript = (node, htmlNodeText) => {
|
|
223
|
-
const { type, extension } = analyzeScriptNode(node)
|
|
224
|
-
const { line, column, lineEnd, columnEnd, isOriginal } =
|
|
225
|
-
getHtmlNodePosition(node, { preferOriginal: true })
|
|
226
|
-
let inlineScriptUrl = generateInlineContentUrl({
|
|
227
|
-
url,
|
|
228
|
-
extension: extension || ".js",
|
|
229
|
-
line,
|
|
230
|
-
column,
|
|
231
|
-
lineEnd,
|
|
232
|
-
columnEnd,
|
|
233
|
-
})
|
|
234
|
-
const [inlineScriptReference] = context.referenceUtils.foundInline({
|
|
235
|
-
type: "script",
|
|
236
|
-
subtype: "inline",
|
|
237
|
-
expectedType: type,
|
|
238
|
-
isOriginalPosition: isOriginal,
|
|
239
|
-
specifierLine: line - 1,
|
|
240
|
-
specifierColumn: column,
|
|
241
|
-
specifier: inlineScriptUrl,
|
|
242
|
-
contentType: "text/javascript",
|
|
243
|
-
content: htmlNodeText,
|
|
244
|
-
})
|
|
245
|
-
removeHtmlNodeText(node)
|
|
246
|
-
if (extension) {
|
|
247
|
-
setHtmlNodeAttributes(node, {
|
|
248
|
-
type: type === "js_module" ? "module" : undefined,
|
|
249
|
-
})
|
|
250
|
-
}
|
|
251
|
-
scriptsToSupervise.push({
|
|
252
|
-
node,
|
|
253
|
-
isInline: true,
|
|
254
|
-
type,
|
|
255
|
-
src: inlineScriptReference.generatedSpecifier,
|
|
256
|
-
})
|
|
257
|
-
}
|
|
258
|
-
const handleScriptWithSrc = (node, src) => {
|
|
259
|
-
const { type } = analyzeScriptNode(node)
|
|
260
|
-
const integrity = getHtmlNodeAttribute(node, "integrity")
|
|
261
|
-
const crossorigin =
|
|
262
|
-
getHtmlNodeAttribute(node, "crossorigin") !== undefined
|
|
263
|
-
const defer = getHtmlNodeAttribute(node, "defer") !== undefined
|
|
264
|
-
const async = getHtmlNodeAttribute(node, "async") !== undefined
|
|
265
|
-
scriptsToSupervise.push({
|
|
266
|
-
node,
|
|
267
|
-
type,
|
|
268
|
-
src,
|
|
269
|
-
defer,
|
|
270
|
-
async,
|
|
271
|
-
integrity,
|
|
272
|
-
crossorigin,
|
|
273
|
-
})
|
|
274
|
-
}
|
|
275
|
-
visitHtmlNodes(htmlAst, {
|
|
276
|
-
script: (node) => {
|
|
277
|
-
const { type } = analyzeScriptNode(node)
|
|
278
|
-
if (type !== "js_classic" && type !== "js_module") {
|
|
279
|
-
return
|
|
280
|
-
}
|
|
281
|
-
if (
|
|
282
|
-
getHtmlNodeAttribute(node, "jsenv-cooked-by") ||
|
|
283
|
-
getHtmlNodeAttribute(node, "jsenv-inlined-by") ||
|
|
284
|
-
getHtmlNodeAttribute(node, "jsenv-injected-by")
|
|
285
|
-
) {
|
|
286
|
-
return
|
|
287
|
-
}
|
|
288
|
-
const noSupervisor = getHtmlNodeAttribute(node, "no-supervisor")
|
|
289
|
-
if (noSupervisor !== undefined) {
|
|
290
|
-
return
|
|
291
|
-
}
|
|
292
|
-
const htmlNodeText = getHtmlNodeText(node)
|
|
293
|
-
if (htmlNodeText) {
|
|
294
|
-
handleInlineScript(node, htmlNodeText)
|
|
295
|
-
return
|
|
296
|
-
}
|
|
297
|
-
const src = getHtmlNodeAttribute(node, "src")
|
|
298
|
-
if (src) {
|
|
299
|
-
handleScriptWithSrc(node, src)
|
|
300
|
-
return
|
|
301
|
-
}
|
|
302
|
-
},
|
|
303
|
-
})
|
|
304
|
-
const [scriptTypeModuleSupervisorFileReference] =
|
|
305
|
-
context.referenceUtils.inject({
|
|
306
|
-
type: "js_import",
|
|
307
|
-
expectedType: "js_module",
|
|
308
|
-
specifier: scriptTypeModuleSupervisorFileUrl,
|
|
309
|
-
})
|
|
310
163
|
const [supervisorFileReference] = context.referenceUtils.inject({
|
|
311
164
|
type: "script",
|
|
312
165
|
expectedType: "js_classic",
|
|
313
166
|
specifier: supervisorFileUrl,
|
|
314
167
|
})
|
|
315
|
-
injectScriptNodeAsEarlyAsPossible(
|
|
316
|
-
htmlAst,
|
|
317
|
-
createHtmlNode({
|
|
318
|
-
tagName: "script",
|
|
319
|
-
textContent: `
|
|
320
|
-
window.__supervisor__.setup(${JSON.stringify(
|
|
321
|
-
{
|
|
322
|
-
rootDirectoryUrl: context.rootDirectoryUrl,
|
|
323
|
-
errorBaseUrl,
|
|
324
|
-
logs,
|
|
325
|
-
measurePerf,
|
|
326
|
-
errorOverlay,
|
|
327
|
-
openInEditor,
|
|
328
|
-
},
|
|
329
|
-
null,
|
|
330
|
-
" ",
|
|
331
|
-
)})
|
|
332
|
-
`,
|
|
333
|
-
}),
|
|
334
|
-
"jsenv:supervisor",
|
|
335
|
-
)
|
|
336
|
-
injectScriptNodeAsEarlyAsPossible(
|
|
337
|
-
htmlAst,
|
|
338
|
-
createHtmlNode({
|
|
339
|
-
tagName: "script",
|
|
340
|
-
src: supervisorFileReference.generatedSpecifier,
|
|
341
|
-
}),
|
|
342
|
-
"jsenv:supervisor",
|
|
343
|
-
)
|
|
344
168
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
} else {
|
|
387
|
-
setHtmlNodeAttributes(node, {
|
|
388
|
-
"jsenv-cooked-by": "jsenv:supervisor",
|
|
389
|
-
})
|
|
390
|
-
}
|
|
169
|
+
return injectSupervisorIntoHTML(
|
|
170
|
+
{
|
|
171
|
+
content,
|
|
172
|
+
url,
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
supervisorScriptSrc: supervisorFileReference.generatedSpecifier,
|
|
176
|
+
supervisorOptions: {
|
|
177
|
+
errorBaseUrl,
|
|
178
|
+
logs,
|
|
179
|
+
measurePerf,
|
|
180
|
+
errorOverlay,
|
|
181
|
+
openInEditor,
|
|
182
|
+
},
|
|
183
|
+
webServer: {
|
|
184
|
+
rootDirectoryUrl: context.rootDirectoryUrl,
|
|
185
|
+
isJsenvDevServer: true,
|
|
186
|
+
},
|
|
187
|
+
inlineAsRemote: true,
|
|
188
|
+
generateInlineScriptSrc: ({
|
|
189
|
+
type,
|
|
190
|
+
textContent,
|
|
191
|
+
inlineScriptUrl,
|
|
192
|
+
isOriginal,
|
|
193
|
+
line,
|
|
194
|
+
column,
|
|
195
|
+
}) => {
|
|
196
|
+
const [inlineScriptReference] =
|
|
197
|
+
context.referenceUtils.foundInline({
|
|
198
|
+
type: "script",
|
|
199
|
+
subtype: "inline",
|
|
200
|
+
expectedType: type,
|
|
201
|
+
isOriginalPosition: isOriginal,
|
|
202
|
+
specifierLine: line - 1,
|
|
203
|
+
specifierColumn: column,
|
|
204
|
+
specifier: inlineScriptUrl,
|
|
205
|
+
contentType: "text/javascript",
|
|
206
|
+
content: textContent,
|
|
207
|
+
})
|
|
208
|
+
return inlineScriptReference.generatedSpecifier
|
|
209
|
+
},
|
|
391
210
|
},
|
|
392
211
|
)
|
|
393
|
-
const htmlModified = stringifyHtmlAst(htmlAst)
|
|
394
|
-
return {
|
|
395
|
-
content: htmlModified,
|
|
396
|
-
}
|
|
397
212
|
},
|
|
398
213
|
},
|
|
399
214
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath } from "node:url"
|
|
2
1
|
import { urlToRelativeUrl } from "@jsenv/urls"
|
|
3
2
|
import { readFileSync } from "@jsenv/filesystem"
|
|
4
3
|
import {
|
|
@@ -17,24 +16,7 @@ import { babelPluginTransformImportMetaResolve } from "./helpers/babel_plugin_tr
|
|
|
17
16
|
// because of https://github.com/rpetrich/babel-plugin-transform-async-to-promises/issues/84
|
|
18
17
|
import customAsyncToPromises from "./async-to-promises.js"
|
|
19
18
|
|
|
20
|
-
/*
|
|
21
|
-
* When systemjs format is used by babel, it will generated UID based on
|
|
22
|
-
* the import specifier:
|
|
23
|
-
* https://github.com/babel/babel/blob/97d1967826077f15e766778c0d64711399e9a72a/packages/babel-plugin-transform-modules-systemjs/src/index.ts#L498
|
|
24
|
-
* But at this stage import specifier are absolute file urls
|
|
25
|
-
* So without minification these specifier are long and dependent
|
|
26
|
-
* on where the files are on the filesystem.
|
|
27
|
-
* This can be mitigated by minification that will rename them.
|
|
28
|
-
* But to fix this issue once and for all I have copy-pasted
|
|
29
|
-
* "@babel/plugin-transform-modules-systemjs" to introduce
|
|
30
|
-
* "generateIdentifierHint" options and prevent that from hapenning
|
|
31
|
-
*/
|
|
32
|
-
const TRANSFORM_MODULES_SYSTEMJS_PATH = fileURLToPath(
|
|
33
|
-
new URL("./babel_plugin_transform_modules_systemjs.cjs", import.meta.url),
|
|
34
|
-
)
|
|
35
|
-
|
|
36
19
|
export const convertJsModuleToJsClassic = async ({
|
|
37
|
-
rootDirectoryUrl,
|
|
38
20
|
systemJsInjection,
|
|
39
21
|
systemJsClientFileUrl,
|
|
40
22
|
urlInfo,
|
|
@@ -60,18 +42,8 @@ export const convertJsModuleToJsClassic = async ({
|
|
|
60
42
|
// proposal-dynamic-import required with systemjs for babel8:
|
|
61
43
|
// https://github.com/babel/babel/issues/10746
|
|
62
44
|
requireFromJsenv("@babel/plugin-proposal-dynamic-import"),
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
requireFromJsenv(TRANSFORM_MODULES_SYSTEMJS_PATH),
|
|
66
|
-
{
|
|
67
|
-
generateIdentifierHint: (key) => {
|
|
68
|
-
if (key.startsWith("file://")) {
|
|
69
|
-
return urlToRelativeUrl(key, rootDirectoryUrl)
|
|
70
|
-
}
|
|
71
|
-
return key
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
],
|
|
45
|
+
requireFromJsenv("@babel/plugin-transform-modules-systemjs"),
|
|
46
|
+
[babelPluginRelativeImports, { rootUrl: jsModuleUrlInfo.url }],
|
|
75
47
|
[
|
|
76
48
|
customAsyncToPromises,
|
|
77
49
|
{
|
|
@@ -91,6 +63,7 @@ export const convertJsModuleToJsClassic = async ({
|
|
|
91
63
|
babelPluginTransformImportMetaUrl,
|
|
92
64
|
babelPluginTransformImportMetaResolve,
|
|
93
65
|
requireFromJsenv("@babel/plugin-transform-modules-umd"),
|
|
66
|
+
[babelPluginRelativeImports, { rootUrl: jsModuleUrlInfo.url }],
|
|
94
67
|
]),
|
|
95
68
|
],
|
|
96
69
|
urlInfo: jsModuleUrlInfo,
|
|
@@ -132,3 +105,67 @@ export const convertJsModuleToJsClassic = async ({
|
|
|
132
105
|
sourcemap,
|
|
133
106
|
}
|
|
134
107
|
}
|
|
108
|
+
|
|
109
|
+
/*
|
|
110
|
+
* When systemjs or umd format is used by babel, it will generated UID based on
|
|
111
|
+
* the import specifier:
|
|
112
|
+
* https://github.com/babel/babel/blob/97d1967826077f15e766778c0d64711399e9a72a/packages/babel-plugin-transform-modules-systemjs/src/index.ts#L498
|
|
113
|
+
* But at this stage import specifier are absolute file urls
|
|
114
|
+
* This can be mitigated by minification that will rename them.
|
|
115
|
+
* But to fix this issue once and for all there is babelPluginRelativeImports below
|
|
116
|
+
*/
|
|
117
|
+
const babelPluginRelativeImports = (babel) => {
|
|
118
|
+
const t = babel.types
|
|
119
|
+
|
|
120
|
+
const replaceSpecifierAtPath = (path, state) => {
|
|
121
|
+
const specifier = path.node.value
|
|
122
|
+
if (specifier.startsWith("file://")) {
|
|
123
|
+
const specifierRelative = urlToRelativeUrl(specifier, state.opts.rootUrl)
|
|
124
|
+
path.replaceWith(t.stringLiteral(specifierRelative))
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
name: "relative-imports",
|
|
130
|
+
visitor: {
|
|
131
|
+
CallExpression: (path, state) => {
|
|
132
|
+
if (path.node.callee.type !== "Import") {
|
|
133
|
+
// Some other function call, not import();
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
if (path.node.arguments[0].type !== "StringLiteral") {
|
|
137
|
+
// Non-string argument, probably a variable or expression, e.g.
|
|
138
|
+
// import(moduleId)
|
|
139
|
+
// import('./' + moduleName)
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
const sourcePath = path.get("arguments")[0]
|
|
143
|
+
if (sourcePath.node.type === "StringLiteral") {
|
|
144
|
+
replaceSpecifierAtPath(sourcePath, state)
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
ImportDeclaration: (path, state) => {
|
|
148
|
+
const sourcePath = path.get("source")
|
|
149
|
+
replaceSpecifierAtPath(sourcePath, state)
|
|
150
|
+
},
|
|
151
|
+
ExportAllDeclaration: (path, state) => {
|
|
152
|
+
const sourcePath = path.get("source")
|
|
153
|
+
replaceSpecifierAtPath(sourcePath, state)
|
|
154
|
+
},
|
|
155
|
+
ExportNamedDeclaration: (path, state) => {
|
|
156
|
+
if (!path.node.source) {
|
|
157
|
+
// This export has no "source", so it's probably
|
|
158
|
+
// a local variable or function, e.g.
|
|
159
|
+
// export { varName }
|
|
160
|
+
// export const constName = ...
|
|
161
|
+
// export function funcName() {}
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
const sourcePath = path.get("source")
|
|
165
|
+
if (sourcePath.node.type === "StringLiteral") {
|
|
166
|
+
replaceSpecifierAtPath(sourcePath, state)
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -157,7 +157,7 @@ export const jsenvPluginAsJsClassicHtml = ({
|
|
|
157
157
|
break
|
|
158
158
|
}
|
|
159
159
|
} catch (e) {
|
|
160
|
-
if (context.dev) {
|
|
160
|
+
if (context.dev && e.code !== "PARSE_ERROR") {
|
|
161
161
|
needsSystemJs = true
|
|
162
162
|
// ignore cooking error, the browser will trigger it again on fetch
|
|
163
163
|
// + disable cache for this html file because when browser will reload
|
|
@@ -74,7 +74,7 @@ const babelPluginMetadataUsesTopLevelAwait = () => {
|
|
|
74
74
|
programPath.traverse({
|
|
75
75
|
AwaitExpression: (path) => {
|
|
76
76
|
const closestFunction = path.getFunctionParent()
|
|
77
|
-
if (!closestFunction) {
|
|
77
|
+
if (!closestFunction || closestFunction.type === "Program") {
|
|
78
78
|
usesTopLevelAwait = true
|
|
79
79
|
path.stop()
|
|
80
80
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs"
|
|
2
2
|
import { memoryUsage } from "node:process"
|
|
3
3
|
import { takeCoverage } from "node:v8"
|
|
4
|
-
import wrapAnsi from "wrap-ansi"
|
|
5
4
|
import stripAnsi from "strip-ansi"
|
|
6
5
|
|
|
7
6
|
import { urlToFileSystemPath } from "@jsenv/urls"
|
|
@@ -31,8 +30,7 @@ export const executeSteps = async (
|
|
|
31
30
|
completedExecutionLogMerging,
|
|
32
31
|
completedExecutionLogAbbreviation,
|
|
33
32
|
rootDirectoryUrl,
|
|
34
|
-
|
|
35
|
-
sourceDirectoryUrl,
|
|
33
|
+
webServer,
|
|
36
34
|
|
|
37
35
|
keepRunning,
|
|
38
36
|
defaultMsAllocatedPerExecution,
|
|
@@ -119,8 +117,7 @@ export const executeSteps = async (
|
|
|
119
117
|
|
|
120
118
|
let runtimeParams = {
|
|
121
119
|
rootDirectoryUrl,
|
|
122
|
-
|
|
123
|
-
sourceDirectoryUrl,
|
|
120
|
+
webServer,
|
|
124
121
|
|
|
125
122
|
coverageEnabled,
|
|
126
123
|
coverageConfig,
|
|
@@ -279,7 +276,7 @@ export const executeSteps = async (
|
|
|
279
276
|
global.gc()
|
|
280
277
|
}
|
|
281
278
|
if (executionLogsEnabled) {
|
|
282
|
-
|
|
279
|
+
const log = createExecutionLog(afterExecutionInfo, {
|
|
283
280
|
completedExecutionLogAbbreviation,
|
|
284
281
|
counters,
|
|
285
282
|
logRuntime,
|
|
@@ -293,16 +290,6 @@ export const executeSteps = async (
|
|
|
293
290
|
? { memoryHeap: memoryUsage().heapUsed }
|
|
294
291
|
: {}),
|
|
295
292
|
})
|
|
296
|
-
log = `${log}
|
|
297
|
-
|
|
298
|
-
`
|
|
299
|
-
const { columns = 80 } = process.stdout
|
|
300
|
-
log = wrapAnsi(log, columns, {
|
|
301
|
-
trim: false,
|
|
302
|
-
hard: true,
|
|
303
|
-
wordWrap: false,
|
|
304
|
-
})
|
|
305
|
-
|
|
306
293
|
// replace spinner with this execution result
|
|
307
294
|
if (spinner) spinner.stop()
|
|
308
295
|
executionLog.write(log)
|
|
@@ -319,11 +306,16 @@ export const executeSteps = async (
|
|
|
319
306
|
executionLog = createLog({ newLine: "" })
|
|
320
307
|
}
|
|
321
308
|
}
|
|
322
|
-
|
|
309
|
+
const isLastExecutionLog = executionIndex === executionSteps.length - 1
|
|
310
|
+
const cancelRemaining =
|
|
323
311
|
failFast &&
|
|
324
312
|
executionResult.status !== "completed" &&
|
|
325
313
|
counters.done < counters.total
|
|
326
|
-
) {
|
|
314
|
+
if (isLastExecutionLog) {
|
|
315
|
+
executionLog.write("\n")
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (cancelRemaining) {
|
|
327
319
|
logger.info(`"failFast" enabled -> cancel remaining executions`)
|
|
328
320
|
failFastAbortController.abort()
|
|
329
321
|
}
|
|
@@ -8,8 +8,7 @@ import {
|
|
|
8
8
|
} from "@jsenv/filesystem"
|
|
9
9
|
import { createLogger, createDetailedMessage } from "@jsenv/log"
|
|
10
10
|
|
|
11
|
-
import {
|
|
12
|
-
import { basicFetch } from "../basic_fetch.js"
|
|
11
|
+
import { assertAndNormalizeWebServer } from "../execute/web_server_param.js"
|
|
13
12
|
import { generateCoverageJsonFile } from "./coverage/coverage_reporter_json_file.js"
|
|
14
13
|
import { generateCoverageHtmlDirectory } from "./coverage/coverage_reporter_html_directory.js"
|
|
15
14
|
import { generateCoverageTextLog } from "./coverage/coverage_reporter_text_log.js"
|
|
@@ -20,7 +19,7 @@ import { executeSteps } from "./execute_steps.js"
|
|
|
20
19
|
* Execute a list of files and log how it goes.
|
|
21
20
|
* @param {Object} testPlanParameters
|
|
22
21
|
* @param {string|url} testPlanParameters.rootDirectoryUrl Directory containing test files;
|
|
23
|
-
* @param {
|
|
22
|
+
* @param {Object} [testPlanParameters.webServer] Web server info; required when executing test on browsers
|
|
24
23
|
* @param {Object} testPlanParameters.testPlan Object associating files with runtimes where they will be executed
|
|
25
24
|
* @param {boolean} [testPlanParameters.completedExecutionLogAbbreviation=false] Abbreviate completed execution information to shorten terminal output
|
|
26
25
|
* @param {boolean} [testPlanParameters.completedExecutionLogMerging=false] Merge completed execution logs to shorten terminal output
|
|
@@ -46,10 +45,9 @@ export const executeTestPlan = async ({
|
|
|
46
45
|
logFileRelativeUrl = ".jsenv/test_plan_debug.txt",
|
|
47
46
|
completedExecutionLogAbbreviation = false,
|
|
48
47
|
completedExecutionLogMerging = false,
|
|
49
|
-
rootDirectoryUrl,
|
|
50
|
-
devServerModuleUrl,
|
|
51
|
-
devServerOrigin,
|
|
52
48
|
|
|
49
|
+
rootDirectoryUrl,
|
|
50
|
+
webServer,
|
|
53
51
|
testPlan,
|
|
54
52
|
updateProcessExitCode = true,
|
|
55
53
|
maxExecutionsInParallel = 1,
|
|
@@ -96,8 +94,6 @@ export const executeTestPlan = async ({
|
|
|
96
94
|
}) => {
|
|
97
95
|
let someNeedsServer = false
|
|
98
96
|
let someNodeRuntime = false
|
|
99
|
-
let stopDevServerNeeded = false
|
|
100
|
-
let sourceDirectoryUrl
|
|
101
97
|
const runtimes = {}
|
|
102
98
|
// param validation
|
|
103
99
|
{
|
|
@@ -137,44 +133,7 @@ export const executeTestPlan = async ({
|
|
|
137
133
|
})
|
|
138
134
|
|
|
139
135
|
if (someNeedsServer) {
|
|
140
|
-
|
|
141
|
-
throw new TypeError(
|
|
142
|
-
`devServerOrigin is required when running tests on browser(s)`,
|
|
143
|
-
)
|
|
144
|
-
}
|
|
145
|
-
let devServerStarted = await pingServer(devServerOrigin)
|
|
146
|
-
if (!devServerStarted) {
|
|
147
|
-
if (!devServerModuleUrl) {
|
|
148
|
-
throw new TypeError(
|
|
149
|
-
`devServerModuleUrl is required when dev server is not started in order to run tests on browser(s)`,
|
|
150
|
-
)
|
|
151
|
-
}
|
|
152
|
-
try {
|
|
153
|
-
process.env.IMPORTED_BY_TEST_PLAN = "1"
|
|
154
|
-
await import(devServerModuleUrl)
|
|
155
|
-
delete process.env.IMPORTED_BY_TEST_PLAN
|
|
156
|
-
} catch (e) {
|
|
157
|
-
if (e.code === "ERR_MODULE_NOT_FOUND") {
|
|
158
|
-
throw new Error(
|
|
159
|
-
`Cannot find file responsible to start dev server at "${devServerModuleUrl}"`,
|
|
160
|
-
)
|
|
161
|
-
}
|
|
162
|
-
throw e
|
|
163
|
-
}
|
|
164
|
-
devServerStarted = await pingServer(devServerOrigin)
|
|
165
|
-
if (!devServerStarted) {
|
|
166
|
-
throw new Error(
|
|
167
|
-
`dev server not started after importing "${devServerModuleUrl}", ensure this module file is starting a server at "${devServerOrigin}"`,
|
|
168
|
-
)
|
|
169
|
-
}
|
|
170
|
-
stopDevServerNeeded = true
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const devServerParams = await basicFetch(
|
|
174
|
-
`${devServerOrigin}/__server_params__.json`,
|
|
175
|
-
{ rejectUnauthorized: false },
|
|
176
|
-
)
|
|
177
|
-
sourceDirectoryUrl = devServerParams.sourceDirectoryUrl
|
|
136
|
+
await assertAndNormalizeWebServer(webServer)
|
|
178
137
|
}
|
|
179
138
|
|
|
180
139
|
if (coverageEnabled) {
|
|
@@ -290,7 +249,12 @@ export const executeTestPlan = async ({
|
|
|
290
249
|
}
|
|
291
250
|
}
|
|
292
251
|
|
|
293
|
-
testPlan = {
|
|
252
|
+
testPlan = {
|
|
253
|
+
"file:///**/node_modules/": null,
|
|
254
|
+
"**/*./": null,
|
|
255
|
+
...testPlan,
|
|
256
|
+
"**/.jsenv/": null,
|
|
257
|
+
}
|
|
294
258
|
logger.debug(`Generate executions`)
|
|
295
259
|
const executionSteps = await executionStepsFromTestPlan({
|
|
296
260
|
signal,
|
|
@@ -313,8 +277,7 @@ export const executeTestPlan = async ({
|
|
|
313
277
|
completedExecutionLogMerging,
|
|
314
278
|
completedExecutionLogAbbreviation,
|
|
315
279
|
rootDirectoryUrl,
|
|
316
|
-
|
|
317
|
-
sourceDirectoryUrl,
|
|
280
|
+
webServer,
|
|
318
281
|
|
|
319
282
|
maxExecutionsInParallel,
|
|
320
283
|
defaultMsAllocatedPerExecution,
|
|
@@ -331,17 +294,6 @@ export const executeTestPlan = async ({
|
|
|
331
294
|
coverageV8ConflictWarning,
|
|
332
295
|
coverageTempDirectoryUrl,
|
|
333
296
|
})
|
|
334
|
-
if (stopDevServerNeeded) {
|
|
335
|
-
// we are expecting ECONNRESET because server will be stopped by the request
|
|
336
|
-
basicFetch(`${devServerOrigin}/__stop__`, {
|
|
337
|
-
rejectUnauthorized: false,
|
|
338
|
-
}).catch((e) => {
|
|
339
|
-
if (e.code === "ECONNRESET") {
|
|
340
|
-
return
|
|
341
|
-
}
|
|
342
|
-
throw e
|
|
343
|
-
})
|
|
344
|
-
}
|
|
345
297
|
if (
|
|
346
298
|
updateProcessExitCode &&
|
|
347
299
|
result.planSummary.counters.total !== result.planSummary.counters.completed
|