@jsenv/core 27.0.3 → 27.2.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/controllable_child_process.mjs +139 -0
- package/dist/controllable_worker_thread.mjs +103 -0
- package/dist/js/execute_using_dynamic_import.js +169 -0
- package/dist/js/v8_coverage.js +539 -0
- package/dist/main.js +683 -818
- package/package.json +9 -8
- package/src/build/build.js +9 -12
- package/src/build/build_urls_generator.js +1 -1
- package/src/build/inject_global_version_mappings.js +3 -2
- package/src/build/inject_service_worker_urls.js +1 -2
- package/src/execute/run.js +50 -68
- package/src/execute/runtimes/browsers/chromium.js +1 -1
- package/src/execute/runtimes/browsers/firefox.js +1 -1
- package/src/execute/runtimes/browsers/from_playwright.js +13 -8
- package/src/execute/runtimes/browsers/webkit.js +1 -1
- package/src/execute/runtimes/node/{controllable_file.mjs → controllable_child_process.mjs} +18 -50
- package/src/execute/runtimes/node/controllable_worker_thread.mjs +103 -0
- package/src/execute/runtimes/node/execute_using_dynamic_import.js +49 -0
- package/src/execute/runtimes/node/exit_codes.js +9 -0
- package/src/execute/runtimes/node/{node_process.js → node_child_process.js} +56 -50
- package/src/execute/runtimes/node/node_worker_thread.js +268 -25
- package/src/execute/runtimes/node/profiler_v8_coverage.js +56 -0
- package/src/main.js +3 -1
- package/src/omega/kitchen.js +19 -6
- package/src/omega/server/file_service.js +2 -2
- package/src/omega/url_graph/url_graph_load.js +0 -1
- package/src/omega/url_graph.js +1 -0
- package/src/plugins/bundling/js_module/bundle_js_module.js +2 -5
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +18 -15
- package/src/plugins/url_resolution/jsenv_plugin_url_resolution.js +2 -1
- package/src/test/coverage/report_to_coverage.js +16 -19
- package/src/test/coverage/v8_coverage.js +26 -0
- package/src/test/coverage/{v8_coverage_from_directory.js → v8_coverage_node_directory.js} +22 -26
- package/src/test/execute_plan.js +98 -91
- package/src/test/execute_test_plan.js +19 -13
- package/src/test/logs_file_execution.js +90 -13
- package/dist/js/controllable_file.mjs +0 -227
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/core",
|
|
3
|
-
"version": "27.
|
|
3
|
+
"version": "27.2.1",
|
|
4
4
|
"description": "Tool to develop, test and build js projects",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"eslint": "npx eslint . --ext=.js,.mjs,.cjs,.html",
|
|
44
44
|
"dev": "node --conditions=development ./scripts/dev/dev.mjs",
|
|
45
45
|
"test": "node --conditions=development ./scripts/test/test.mjs",
|
|
46
|
-
"test:coverage": "npm run test -- --coverage",
|
|
46
|
+
"test:coverage": "cross-env NODE_V8_COVERAGE=.coverage/node npm run test -- --coverage",
|
|
47
|
+
"test:workspace": "npm run test --workspaces --if-present -- --workspace",
|
|
47
48
|
"build": "node --conditions=development ./scripts/build/build.mjs",
|
|
48
|
-
"workspace:test": "npm run test --workspaces --if-present -- --workspace",
|
|
49
49
|
"workspace:versions": "node ./scripts/publish/workspace_versions.mjs",
|
|
50
50
|
"workspace:publish": "node ./scripts/publish/workspace_publish.mjs",
|
|
51
51
|
"performances": "node --expose-gc ./scripts/performance/generate_performance_report.mjs --log --once",
|
|
@@ -65,20 +65,20 @@
|
|
|
65
65
|
"@babel/plugin-transform-modules-umd": "7.18.6",
|
|
66
66
|
"@c88/v8-coverage": "0.1.1",
|
|
67
67
|
"@financial-times/polyfill-useragent-normaliser": "2.0.1",
|
|
68
|
-
"@jsenv/ast": "1.1.1",
|
|
69
68
|
"@jsenv/abort": "4.2.3",
|
|
69
|
+
"@jsenv/ast": "1.1.1",
|
|
70
|
+
"@jsenv/babel-plugins": "1.0.5",
|
|
70
71
|
"@jsenv/filesystem": "4.1.0",
|
|
71
72
|
"@jsenv/importmap": "1.2.1",
|
|
72
73
|
"@jsenv/integrity": "0.0.1",
|
|
74
|
+
"@jsenv/log": "3.1.0",
|
|
73
75
|
"@jsenv/node-esm-resolution": "0.1.0",
|
|
74
|
-
"@jsenv/server": "12.7.
|
|
76
|
+
"@jsenv/server": "12.7.5",
|
|
77
|
+
"@jsenv/sourcemap": "1.0.1",
|
|
75
78
|
"@jsenv/uneval": "1.6.0",
|
|
76
79
|
"@jsenv/url-meta": "7.0.0",
|
|
77
80
|
"@jsenv/urls": "1.2.6",
|
|
78
81
|
"@jsenv/utils": "2.0.1",
|
|
79
|
-
"@jsenv/babel-plugins": "1.0.5",
|
|
80
|
-
"@jsenv/log": "3.0.2",
|
|
81
|
-
"@jsenv/sourcemap": "1.0.1",
|
|
82
82
|
"acorn-import-assertions": "1.8.0",
|
|
83
83
|
"cuid": "2.1.8",
|
|
84
84
|
"html-minifier": "4.0.0",
|
|
@@ -103,6 +103,7 @@
|
|
|
103
103
|
"@jsenv/https-local": "2.1.0",
|
|
104
104
|
"@jsenv/package-workspace": "0.4.1",
|
|
105
105
|
"@jsenv/performance-impact": "3.0.1",
|
|
106
|
+
"cross-env": "7.0.3",
|
|
106
107
|
"eslint": "8.19.0",
|
|
107
108
|
"eslint-plugin-html": "6.2.0",
|
|
108
109
|
"eslint-plugin-import": "2.26.0",
|
package/src/build/build.js
CHANGED
|
@@ -287,7 +287,7 @@ build ${entryPointKeys.length} entry points`)
|
|
|
287
287
|
}
|
|
288
288
|
}
|
|
289
289
|
GRAPH.forEach(rawGraph, (rawUrlInfo) => {
|
|
290
|
-
if (rawUrlInfo.
|
|
290
|
+
if (rawUrlInfo.isEntryPoint) {
|
|
291
291
|
addToBundlerIfAny(rawUrlInfo)
|
|
292
292
|
if (rawUrlInfo.type === "html") {
|
|
293
293
|
rawUrlInfo.dependencies.forEach((dependencyUrl) => {
|
|
@@ -370,6 +370,7 @@ build ${entryPointKeys.length} entry points`)
|
|
|
370
370
|
const bundleUrlInfo = {
|
|
371
371
|
type,
|
|
372
372
|
subtype: rawUrlInfo ? rawUrlInfo.subtype : undefined,
|
|
373
|
+
isEntryPoint: rawUrlInfo ? rawUrlInfo.isEntryPoint : undefined,
|
|
373
374
|
filename: rawUrlInfo ? rawUrlInfo.filename : undefined,
|
|
374
375
|
originalUrl: rawUrlInfo ? rawUrlInfo.originalUrl : undefined,
|
|
375
376
|
originalContent: rawUrlInfo
|
|
@@ -516,11 +517,10 @@ build ${entryPointKeys.length} entry points`)
|
|
|
516
517
|
// Here we just want to reserve an url for that file
|
|
517
518
|
const buildUrl = buildUrlsGenerator.generate(rawUrl, {
|
|
518
519
|
urlInfo: {
|
|
519
|
-
data:
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
},
|
|
520
|
+
data: reference.data,
|
|
521
|
+
isEntryPoint:
|
|
522
|
+
reference.isEntryPoint ||
|
|
523
|
+
isWebWorkerEntryPointReference(reference),
|
|
524
524
|
type: reference.expectedType,
|
|
525
525
|
subtype: reference.expectedSubtype,
|
|
526
526
|
filename: reference.filename,
|
|
@@ -801,7 +801,7 @@ ${Object.keys(finalGraph.urlInfos).join("\n")}`,
|
|
|
801
801
|
// - versioning update inline content
|
|
802
802
|
// - file converted for import assertion of js_classic conversion
|
|
803
803
|
if (
|
|
804
|
-
!urlInfo.
|
|
804
|
+
!urlInfo.isEntryPoint &&
|
|
805
805
|
urlInfo.type !== "sourcemap" &&
|
|
806
806
|
urlInfo.dependents.size === 0
|
|
807
807
|
) {
|
|
@@ -998,7 +998,7 @@ const applyUrlVersioning = async ({
|
|
|
998
998
|
if (!urlInfo.shouldHandle) {
|
|
999
999
|
return
|
|
1000
1000
|
}
|
|
1001
|
-
if (!urlInfo.
|
|
1001
|
+
if (!urlInfo.isEntryPoint && urlInfo.dependents.size === 0) {
|
|
1002
1002
|
return
|
|
1003
1003
|
}
|
|
1004
1004
|
|
|
@@ -1255,14 +1255,11 @@ const assertEntryPoints = ({ entryPoints }) => {
|
|
|
1255
1255
|
}
|
|
1256
1256
|
|
|
1257
1257
|
const canUseVersionedUrl = (urlInfo) => {
|
|
1258
|
-
if (urlInfo.
|
|
1258
|
+
if (urlInfo.isEntryPoint) {
|
|
1259
1259
|
return false
|
|
1260
1260
|
}
|
|
1261
1261
|
if (urlInfo.type === "webmanifest") {
|
|
1262
1262
|
return false
|
|
1263
1263
|
}
|
|
1264
|
-
if (urlInfo.subtype === "service_worker") {
|
|
1265
|
-
return !urlInfo.data.isWebWorkerEntryPoint
|
|
1266
|
-
}
|
|
1267
1264
|
return true
|
|
1268
1265
|
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
stringifyHtmlAst,
|
|
9
9
|
} from "@jsenv/ast"
|
|
10
10
|
|
|
11
|
+
import { isWebWorkerUrlInfo } from "@jsenv/core/src/omega/web_workers.js"
|
|
11
12
|
import { GRAPH } from "./graph_utils.js"
|
|
12
13
|
|
|
13
14
|
export const injectGlobalVersionMapping = async ({
|
|
@@ -17,7 +18,7 @@ export const injectGlobalVersionMapping = async ({
|
|
|
17
18
|
}) => {
|
|
18
19
|
await Promise.all(
|
|
19
20
|
GRAPH.map(finalGraph, async (urlInfo) => {
|
|
20
|
-
if (urlInfo.
|
|
21
|
+
if (urlInfo.isEntryPoint) {
|
|
21
22
|
await injectVersionMappings({
|
|
22
23
|
urlInfo,
|
|
23
24
|
kitchen: finalGraphKitchen,
|
|
@@ -43,7 +44,7 @@ const jsInjector = (urlInfo, { versionMappings }) => {
|
|
|
43
44
|
const magicSource = createMagicSource(urlInfo.content)
|
|
44
45
|
magicSource.prepend(
|
|
45
46
|
generateClientCodeForVersionMappings(versionMappings, {
|
|
46
|
-
globalName: urlInfo
|
|
47
|
+
globalName: isWebWorkerUrlInfo(urlInfo) ? "self" : "window",
|
|
47
48
|
}),
|
|
48
49
|
)
|
|
49
50
|
return magicSource.toContentAndSourcemap()
|
|
@@ -12,8 +12,7 @@ export const injectServiceWorkerUrls = async ({
|
|
|
12
12
|
finalGraph,
|
|
13
13
|
(finalUrlInfo) => {
|
|
14
14
|
return (
|
|
15
|
-
finalUrlInfo.subtype === "service_worker" &&
|
|
16
|
-
finalUrlInfo.data.isWebWorkerEntryPoint
|
|
15
|
+
finalUrlInfo.subtype === "service_worker" && finalUrlInfo.isEntryPoint
|
|
17
16
|
)
|
|
18
17
|
},
|
|
19
18
|
)
|
package/src/execute/run.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import cuid from "cuid"
|
|
2
2
|
import { Abort, raceCallbacks } from "@jsenv/abort"
|
|
3
|
-
import {
|
|
4
|
-
import { writeFile } from "@jsenv/filesystem"
|
|
3
|
+
import { writeFileSync } from "@jsenv/filesystem"
|
|
5
4
|
|
|
6
5
|
export const run = async ({
|
|
7
6
|
signal = new AbortController().signal,
|
|
@@ -10,17 +9,18 @@ export const run = async ({
|
|
|
10
9
|
keepRunning = false,
|
|
11
10
|
mirrorConsole = false,
|
|
12
11
|
collectConsole = false,
|
|
13
|
-
|
|
12
|
+
coverageEnabled = false,
|
|
14
13
|
coverageTempDirectoryUrl,
|
|
15
14
|
collectPerformance = false,
|
|
16
15
|
|
|
17
16
|
runtime,
|
|
18
17
|
runtimeParams,
|
|
19
18
|
}) => {
|
|
19
|
+
let result = {}
|
|
20
|
+
const callbacks = []
|
|
21
|
+
|
|
20
22
|
const onConsoleRef = { current: () => {} }
|
|
21
23
|
const stopSignal = { notify: () => {} }
|
|
22
|
-
|
|
23
|
-
let resultTransformer = (result) => result
|
|
24
24
|
const runtimeLabel = `${runtime.name}/${runtime.version}`
|
|
25
25
|
|
|
26
26
|
const runOperation = Abort.startOperation()
|
|
@@ -33,22 +33,24 @@ export const run = async ({
|
|
|
33
33
|
allocatedMs !== Infinity
|
|
34
34
|
) {
|
|
35
35
|
const timeoutAbortSource = runOperation.timeout(allocatedMs)
|
|
36
|
-
|
|
36
|
+
callbacks.push(() => {
|
|
37
37
|
if (
|
|
38
38
|
result.status === "errored" &&
|
|
39
39
|
Abort.isAbortError(result.error) &&
|
|
40
40
|
timeoutAbortSource.signal.aborted
|
|
41
41
|
) {
|
|
42
|
-
|
|
42
|
+
result = {
|
|
43
|
+
status: "timedout",
|
|
44
|
+
}
|
|
43
45
|
}
|
|
44
|
-
return result
|
|
45
46
|
})
|
|
46
47
|
}
|
|
47
|
-
|
|
48
|
+
callbacks.push(() => {
|
|
48
49
|
if (result.status === "errored" && Abort.isAbortError(result.error)) {
|
|
49
|
-
|
|
50
|
+
result = {
|
|
51
|
+
status: "aborted",
|
|
52
|
+
}
|
|
50
53
|
}
|
|
51
|
-
return result
|
|
52
54
|
})
|
|
53
55
|
const consoleCalls = []
|
|
54
56
|
onConsoleRef.current = ({ type, text }) => {
|
|
@@ -64,45 +66,14 @@ export const run = async ({
|
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
68
|
if (collectConsole) {
|
|
67
|
-
|
|
69
|
+
callbacks.push(() => {
|
|
68
70
|
result.consoleCalls = consoleCalls
|
|
69
|
-
return result
|
|
70
|
-
})
|
|
71
|
-
}
|
|
72
|
-
if (collectCoverage) {
|
|
73
|
-
resultTransformer = composeTransformer(
|
|
74
|
-
resultTransformer,
|
|
75
|
-
async (result) => {
|
|
76
|
-
// we do not keep coverage in memory, it can grow very big
|
|
77
|
-
// instead we store it on the filesystem
|
|
78
|
-
// and they can be read later at "coverageFileUrl"
|
|
79
|
-
const { coverage } = result
|
|
80
|
-
if (coverage) {
|
|
81
|
-
const coverageFileUrl = resolveUrl(
|
|
82
|
-
`./${runtime.name}/${cuid()}`,
|
|
83
|
-
coverageTempDirectoryUrl,
|
|
84
|
-
)
|
|
85
|
-
await writeFile(coverageFileUrl, JSON.stringify(coverage, null, " "))
|
|
86
|
-
result.coverageFileUrl = coverageFileUrl
|
|
87
|
-
delete result.coverage
|
|
88
|
-
}
|
|
89
|
-
return result
|
|
90
|
-
},
|
|
91
|
-
)
|
|
92
|
-
} else {
|
|
93
|
-
resultTransformer = composeTransformer(resultTransformer, (result) => {
|
|
94
|
-
// as collectCoverage is disabled
|
|
95
|
-
// executionResult.coverage is undefined or {}
|
|
96
|
-
// we delete it just to have a cleaner object
|
|
97
|
-
delete result.coverage
|
|
98
|
-
return result
|
|
99
71
|
})
|
|
100
72
|
}
|
|
101
73
|
|
|
102
74
|
const startMs = Date.now()
|
|
103
|
-
|
|
75
|
+
callbacks.push(() => {
|
|
104
76
|
result.duration = Date.now() - startMs
|
|
105
|
-
return result
|
|
106
77
|
})
|
|
107
78
|
|
|
108
79
|
try {
|
|
@@ -119,7 +90,7 @@ export const run = async ({
|
|
|
119
90
|
},
|
|
120
91
|
runned: async (cb) => {
|
|
121
92
|
try {
|
|
122
|
-
const
|
|
93
|
+
const runResult = await runtime.run({
|
|
123
94
|
signal: runOperation.signal,
|
|
124
95
|
logger,
|
|
125
96
|
...runtimeParams,
|
|
@@ -128,7 +99,7 @@ export const run = async ({
|
|
|
128
99
|
stopSignal,
|
|
129
100
|
onConsole: (log) => onConsoleRef.current(log),
|
|
130
101
|
})
|
|
131
|
-
cb(
|
|
102
|
+
cb(runResult)
|
|
132
103
|
} catch (e) {
|
|
133
104
|
cb({
|
|
134
105
|
status: "errored",
|
|
@@ -144,35 +115,46 @@ export const run = async ({
|
|
|
144
115
|
if (winner.name === "aborted") {
|
|
145
116
|
runOperation.throwIfAborted()
|
|
146
117
|
}
|
|
147
|
-
|
|
148
|
-
|
|
118
|
+
|
|
119
|
+
const { status, namespace, error, performance, coverage } = winner.data
|
|
120
|
+
result.status = status
|
|
121
|
+
if (status === "errored") {
|
|
122
|
+
result.error = error
|
|
123
|
+
} else {
|
|
124
|
+
result.namespace = namespace
|
|
125
|
+
}
|
|
126
|
+
if (collectPerformance) {
|
|
127
|
+
result.performance = performance
|
|
128
|
+
}
|
|
129
|
+
if (coverageEnabled) {
|
|
130
|
+
if (coverage) {
|
|
131
|
+
// we do not keep coverage in memory, it can grow very big
|
|
132
|
+
// instead we store it on the filesystem
|
|
133
|
+
// and they can be read later at "coverageFileUrl"
|
|
134
|
+
const coverageFileUrl = new URL(
|
|
135
|
+
`./${runtime.name}/${cuid()}.json`,
|
|
136
|
+
coverageTempDirectoryUrl,
|
|
137
|
+
)
|
|
138
|
+
writeFileSync(coverageFileUrl, JSON.stringify(coverage, null, " "))
|
|
139
|
+
result.coverageFileUrl = coverageFileUrl.href
|
|
140
|
+
} else {
|
|
141
|
+
// will eventually log a warning in report_to_coverage.js
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
callbacks.forEach((callback) => {
|
|
145
|
+
callback()
|
|
146
|
+
})
|
|
149
147
|
return result
|
|
150
148
|
} catch (e) {
|
|
151
|
-
|
|
149
|
+
result = {
|
|
152
150
|
status: "errored",
|
|
153
151
|
error: e,
|
|
154
152
|
}
|
|
155
|
-
|
|
153
|
+
callbacks.forEach((callback) => {
|
|
154
|
+
callback()
|
|
155
|
+
})
|
|
156
156
|
return result
|
|
157
157
|
} finally {
|
|
158
158
|
await runOperation.end()
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
|
-
|
|
162
|
-
const composeTransformer = (previousTransformer, transformer) => {
|
|
163
|
-
return async (value) => {
|
|
164
|
-
const transformedValue = await previousTransformer(value)
|
|
165
|
-
return transformer(transformedValue)
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const createAbortedResult = () => {
|
|
170
|
-
return {
|
|
171
|
-
status: "aborted",
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
const createTimedoutResult = () => {
|
|
175
|
-
return {
|
|
176
|
-
status: "timedout",
|
|
177
|
-
}
|
|
178
|
-
}
|
|
@@ -2,7 +2,7 @@ import { createRuntimeFromPlaywright } from "./from_playwright.js"
|
|
|
2
2
|
|
|
3
3
|
export const chromium = createRuntimeFromPlaywright({
|
|
4
4
|
browserName: "chromium",
|
|
5
|
-
browserVersion: "
|
|
5
|
+
browserVersion: "104.0.5112.20", // to update, check https://github.com/microsoft/playwright/releases
|
|
6
6
|
coveragePlaywrightAPIAvailable: true,
|
|
7
7
|
})
|
|
8
8
|
export const chromiumIsolatedTab = chromium.isolatedTab
|
|
@@ -2,6 +2,6 @@ import { createRuntimeFromPlaywright } from "./from_playwright.js"
|
|
|
2
2
|
|
|
3
3
|
export const firefox = createRuntimeFromPlaywright({
|
|
4
4
|
browserName: "firefox",
|
|
5
|
-
browserVersion: "
|
|
5
|
+
browserVersion: "100.0.2", // to update, check https://github.com/microsoft/playwright/releases
|
|
6
6
|
})
|
|
7
7
|
export const firefoxIsolatedTab = firefox.isolatedTab
|
|
@@ -11,7 +11,7 @@ import { moveUrl } from "@jsenv/urls"
|
|
|
11
11
|
import { memoize } from "@jsenv/utils/src/memoize/memoize.js"
|
|
12
12
|
import { escapeRegexpSpecialChars } from "@jsenv/utils/src/string/escape_regexp_special_chars.js"
|
|
13
13
|
|
|
14
|
-
import { filterV8Coverage } from "@jsenv/core/src/test/coverage/
|
|
14
|
+
import { filterV8Coverage } from "@jsenv/core/src/test/coverage/v8_coverage.js"
|
|
15
15
|
import { composeTwoFileByFileIstanbulCoverages } from "@jsenv/core/src/test/coverage/istanbul_coverage_composition.js"
|
|
16
16
|
|
|
17
17
|
export const createRuntimeFromPlaywright = ({
|
|
@@ -23,6 +23,7 @@ export const createRuntimeFromPlaywright = ({
|
|
|
23
23
|
isolatedTab = false,
|
|
24
24
|
}) => {
|
|
25
25
|
const runtime = {
|
|
26
|
+
type: "browser",
|
|
26
27
|
name: browserName,
|
|
27
28
|
version: browserVersion,
|
|
28
29
|
needsServer: true,
|
|
@@ -37,9 +38,9 @@ export const createRuntimeFromPlaywright = ({
|
|
|
37
38
|
|
|
38
39
|
// measurePerformance,
|
|
39
40
|
collectPerformance,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
coverageEnabled = false,
|
|
42
|
+
coverageConfig,
|
|
43
|
+
coverageMethodForBrowsers,
|
|
43
44
|
|
|
44
45
|
stopAfterAllSignal,
|
|
45
46
|
stopSignal,
|
|
@@ -108,8 +109,11 @@ export const createRuntimeFromPlaywright = ({
|
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
let resultTransformer = (result) => result
|
|
111
|
-
if (
|
|
112
|
-
if (
|
|
112
|
+
if (coverageEnabled) {
|
|
113
|
+
if (
|
|
114
|
+
coveragePlaywrightAPIAvailable &&
|
|
115
|
+
coverageMethodForBrowsers === "playwright_api"
|
|
116
|
+
) {
|
|
113
117
|
await page.coverage.startJSCoverage({
|
|
114
118
|
// reportAnonymousScripts: true,
|
|
115
119
|
})
|
|
@@ -133,10 +137,11 @@ export const createRuntimeFromPlaywright = ({
|
|
|
133
137
|
}
|
|
134
138
|
},
|
|
135
139
|
)
|
|
136
|
-
const coverage = filterV8Coverage(
|
|
140
|
+
const coverage = await filterV8Coverage(
|
|
137
141
|
{ result: v8CoveragesWithFsUrls },
|
|
138
142
|
{
|
|
139
|
-
|
|
143
|
+
rootDirectoryUrl,
|
|
144
|
+
coverageConfig,
|
|
140
145
|
},
|
|
141
146
|
)
|
|
142
147
|
return {
|
|
@@ -2,7 +2,7 @@ import { createRuntimeFromPlaywright } from "./from_playwright.js"
|
|
|
2
2
|
|
|
3
3
|
export const webkit = createRuntimeFromPlaywright({
|
|
4
4
|
browserName: "webkit",
|
|
5
|
-
browserVersion: "15.4",
|
|
5
|
+
browserVersion: "15.4", // to update, check https://github.com/microsoft/playwright/releases
|
|
6
6
|
ignoreErrorHook: (error) => {
|
|
7
7
|
// we catch error during execution but safari throw unhandled rejection
|
|
8
8
|
// in a non-deterministic way.
|
|
@@ -1,48 +1,23 @@
|
|
|
1
|
-
import v8 from "node:v8"
|
|
2
1
|
import { uneval } from "@jsenv/uneval"
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
import { executeUsingDynamicImport } from "./execute_using_dynamic_import.js"
|
|
4
4
|
|
|
5
5
|
const ACTIONS_AVAILABLE = {
|
|
6
|
-
"execute-using-dynamic-import":
|
|
7
|
-
const getNamespace = async () => {
|
|
8
|
-
const namespace = await import(fileUrl)
|
|
9
|
-
const namespaceResolved = {}
|
|
10
|
-
await Promise.all([
|
|
11
|
-
...Object.keys(namespace).map(async (key) => {
|
|
12
|
-
const value = await namespace[key]
|
|
13
|
-
namespaceResolved[key] = value
|
|
14
|
-
}),
|
|
15
|
-
])
|
|
16
|
-
return namespaceResolved
|
|
17
|
-
}
|
|
18
|
-
if (collectPerformance) {
|
|
19
|
-
const getPerformance = startObservingPerformances()
|
|
20
|
-
const namespace = await getNamespace()
|
|
21
|
-
const performance = await getPerformance()
|
|
22
|
-
return {
|
|
23
|
-
namespace,
|
|
24
|
-
performance,
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
const namespace = await getNamespace()
|
|
28
|
-
return {
|
|
29
|
-
namespace,
|
|
30
|
-
}
|
|
31
|
-
},
|
|
6
|
+
"execute-using-dynamic-import": executeUsingDynamicImport,
|
|
32
7
|
"execute-using-require": async ({ fileUrl }) => {
|
|
33
|
-
const { createRequire } = await import("module")
|
|
34
|
-
const { fileURLToPath } = await import("url")
|
|
8
|
+
const { createRequire } = await import("node:module")
|
|
9
|
+
const { fileURLToPath } = await import("node:url")
|
|
35
10
|
const filePath = fileURLToPath(fileUrl)
|
|
36
11
|
const require = createRequire(fileUrl)
|
|
37
12
|
// eslint-disable-next-line import/no-dynamic-require
|
|
38
13
|
const namespace = require(filePath)
|
|
39
14
|
const namespaceResolved = {}
|
|
40
|
-
await Promise.all(
|
|
41
|
-
|
|
15
|
+
await Promise.all(
|
|
16
|
+
Object.keys(namespace).map(async (key) => {
|
|
42
17
|
const value = await namespace[key]
|
|
43
18
|
namespaceResolved[key] = value
|
|
44
19
|
}),
|
|
45
|
-
|
|
20
|
+
)
|
|
46
21
|
return namespaceResolved
|
|
47
22
|
},
|
|
48
23
|
}
|
|
@@ -94,18 +69,17 @@ const sendToParent = (type, data) => {
|
|
|
94
69
|
// It means node process may stay alive longer than expected
|
|
95
70
|
// the time to send the data to the parent.
|
|
96
71
|
process.send({
|
|
72
|
+
jsenv: true,
|
|
97
73
|
type,
|
|
98
74
|
data,
|
|
99
75
|
})
|
|
100
76
|
}
|
|
101
77
|
|
|
102
|
-
const
|
|
103
|
-
const listener = (
|
|
104
|
-
if (
|
|
105
|
-
// commenting line
|
|
106
|
-
|
|
107
|
-
// eslint-disable-next-line no-eval
|
|
108
|
-
callback(eval(`(${event.data})`))
|
|
78
|
+
const onceParentMessage = (type, callback) => {
|
|
79
|
+
const listener = (message) => {
|
|
80
|
+
if (message && message.jsenv && message.type === type) {
|
|
81
|
+
removeListener() // commenting this line keep this process alive
|
|
82
|
+
callback(message.data)
|
|
109
83
|
}
|
|
110
84
|
}
|
|
111
85
|
const removeListener = () => {
|
|
@@ -115,7 +89,7 @@ const onceProcessMessage = (type, callback) => {
|
|
|
115
89
|
return removeListener
|
|
116
90
|
}
|
|
117
91
|
|
|
118
|
-
const removeActionRequestListener =
|
|
92
|
+
const removeActionRequestListener = onceParentMessage(
|
|
119
93
|
ACTION_REQUEST_EVENT_NAME,
|
|
120
94
|
async ({ actionType, actionParams }) => {
|
|
121
95
|
const action = ACTIONS_AVAILABLE[actionType]
|
|
@@ -133,13 +107,6 @@ const removeActionRequestListener = onceProcessMessage(
|
|
|
133
107
|
value = e
|
|
134
108
|
}
|
|
135
109
|
|
|
136
|
-
if (process.env.NODE_V8_COVERAGE) {
|
|
137
|
-
v8.takeCoverage()
|
|
138
|
-
// if (actionParams.stopCoverageAfterExecution) {
|
|
139
|
-
// v8.stopCoverage()
|
|
140
|
-
// }
|
|
141
|
-
}
|
|
142
|
-
|
|
143
110
|
// setTimeout(() => {}, 100)
|
|
144
111
|
|
|
145
112
|
if (failed) {
|
|
@@ -151,7 +118,6 @@ const removeActionRequestListener = onceProcessMessage(
|
|
|
151
118
|
// removeActionRequestListener()
|
|
152
119
|
if (actionParams.exitAfterAction) {
|
|
153
120
|
removeActionRequestListener()
|
|
154
|
-
|
|
155
121
|
// for some reason this fixes v8 coverage directory sometimes empty on Ubuntu
|
|
156
122
|
// process.exit()
|
|
157
123
|
}
|
|
@@ -164,4 +130,6 @@ const removeActionRequestListener = onceProcessMessage(
|
|
|
164
130
|
// process.once("SIGTERM", removeActionRequestListener)
|
|
165
131
|
// process.once("SIGINT", removeActionRequestListener)
|
|
166
132
|
|
|
167
|
-
setTimeout(() =>
|
|
133
|
+
setTimeout(() => {
|
|
134
|
+
sendToParent("ready")
|
|
135
|
+
})
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { parentPort } from "node:worker_threads"
|
|
2
|
+
import { uneval } from "@jsenv/uneval"
|
|
3
|
+
|
|
4
|
+
import { executeUsingDynamicImport } from "./execute_using_dynamic_import.js"
|
|
5
|
+
|
|
6
|
+
const ACTIONS_AVAILABLE = {
|
|
7
|
+
"execute-using-dynamic-import": executeUsingDynamicImport,
|
|
8
|
+
}
|
|
9
|
+
const ACTION_REQUEST_EVENT_NAME = "action"
|
|
10
|
+
const ACTION_RESPONSE_EVENT_NAME = "action-result"
|
|
11
|
+
const ACTION_RESPONSE_STATUS_FAILED = "action-failed"
|
|
12
|
+
const ACTION_RESPONSE_STATUS_COMPLETED = "action-completed"
|
|
13
|
+
|
|
14
|
+
const sendActionFailed = (error) => {
|
|
15
|
+
if (error.hasOwnProperty("toString")) {
|
|
16
|
+
delete error.toString
|
|
17
|
+
}
|
|
18
|
+
sendToParent(
|
|
19
|
+
ACTION_RESPONSE_EVENT_NAME,
|
|
20
|
+
// process.send algorithm does not send non enumerable values
|
|
21
|
+
// so use @jsenv/uneval
|
|
22
|
+
uneval(
|
|
23
|
+
{
|
|
24
|
+
status: ACTION_RESPONSE_STATUS_FAILED,
|
|
25
|
+
value: error,
|
|
26
|
+
},
|
|
27
|
+
{ ignoreSymbols: true },
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const sendActionCompleted = (value) => {
|
|
33
|
+
sendToParent(
|
|
34
|
+
ACTION_RESPONSE_EVENT_NAME,
|
|
35
|
+
// here we use JSON.stringify because we should not
|
|
36
|
+
// have non enumerable value (unlike there is on Error objects)
|
|
37
|
+
// otherwise uneval is quite slow to turn a giant object
|
|
38
|
+
// into a string (and value can be giant when using coverage)
|
|
39
|
+
JSON.stringify({
|
|
40
|
+
status: ACTION_RESPONSE_STATUS_COMPLETED,
|
|
41
|
+
value,
|
|
42
|
+
}),
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const sendToParent = (type, data) => {
|
|
47
|
+
// this can keep process alive longer than expected
|
|
48
|
+
// when source is a long string.
|
|
49
|
+
// It means node process may stay alive longer than expected
|
|
50
|
+
// the time to send the data to the parent.
|
|
51
|
+
parentPort.postMessage({
|
|
52
|
+
jsenv: true,
|
|
53
|
+
type,
|
|
54
|
+
data,
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const onceParentMessage = (type, callback) => {
|
|
59
|
+
const listener = (message) => {
|
|
60
|
+
if (message && message.jsenv && message.type === type) {
|
|
61
|
+
removeListener() // commenting this line keep this worker alive
|
|
62
|
+
callback(message.data)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const removeListener = () => {
|
|
66
|
+
parentPort.removeListener("message", listener)
|
|
67
|
+
}
|
|
68
|
+
parentPort.on("message", listener)
|
|
69
|
+
return removeListener
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const removeActionRequestListener = onceParentMessage(
|
|
73
|
+
ACTION_REQUEST_EVENT_NAME,
|
|
74
|
+
async ({ actionType, actionParams }) => {
|
|
75
|
+
const action = ACTIONS_AVAILABLE[actionType]
|
|
76
|
+
if (!action) {
|
|
77
|
+
sendActionFailed(new Error(`unknown action ${actionType}`))
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let value
|
|
82
|
+
let failed = false
|
|
83
|
+
try {
|
|
84
|
+
value = await action(actionParams)
|
|
85
|
+
} catch (e) {
|
|
86
|
+
failed = true
|
|
87
|
+
value = e
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (failed) {
|
|
91
|
+
sendActionFailed(value)
|
|
92
|
+
} else {
|
|
93
|
+
sendActionCompleted(value)
|
|
94
|
+
}
|
|
95
|
+
if (actionParams.exitAfterAction) {
|
|
96
|
+
removeActionRequestListener()
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
setTimeout(() => {
|
|
102
|
+
sendToParent("ready")
|
|
103
|
+
})
|