@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/src/omega/kitchen.js
CHANGED
|
@@ -86,6 +86,7 @@ export const createKitchen = ({
|
|
|
86
86
|
baseUrl,
|
|
87
87
|
isOriginalPosition,
|
|
88
88
|
shouldHandle,
|
|
89
|
+
isEntryPoint = false,
|
|
89
90
|
isInline = false,
|
|
90
91
|
injected = false,
|
|
91
92
|
isRessourceHint = false,
|
|
@@ -122,6 +123,7 @@ export const createKitchen = ({
|
|
|
122
123
|
baseUrl,
|
|
123
124
|
isOriginalPosition,
|
|
124
125
|
shouldHandle,
|
|
126
|
+
isEntryPoint,
|
|
125
127
|
isInline,
|
|
126
128
|
injected,
|
|
127
129
|
isRessourceHint,
|
|
@@ -138,6 +140,7 @@ export const createKitchen = ({
|
|
|
138
140
|
reference.next = newReference
|
|
139
141
|
newReference.prev = reference
|
|
140
142
|
newReference.original = reference.original || reference
|
|
143
|
+
// newReference.isEntryPoint = reference.isEntryPoint
|
|
141
144
|
}
|
|
142
145
|
const resolveReference = (reference) => {
|
|
143
146
|
try {
|
|
@@ -166,12 +169,16 @@ export const createKitchen = ({
|
|
|
166
169
|
},
|
|
167
170
|
)
|
|
168
171
|
|
|
169
|
-
const urlInfo = urlGraph.reuseOrCreateUrlInfo(reference.url)
|
|
170
|
-
applyReferenceEffectsOnUrlInfo(reference, urlInfo, kitchenContext)
|
|
171
|
-
|
|
172
172
|
const referenceUrlObject = new URL(reference.url)
|
|
173
173
|
reference.searchParams = referenceUrlObject.searchParams
|
|
174
174
|
reference.generatedUrl = reference.url
|
|
175
|
+
if (reference.searchParams.has("entry_point")) {
|
|
176
|
+
reference.isEntryPoint = true
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const urlInfo = urlGraph.reuseOrCreateUrlInfo(reference.url)
|
|
180
|
+
applyReferenceEffectsOnUrlInfo(reference, urlInfo, kitchenContext)
|
|
181
|
+
|
|
175
182
|
// This hook must touch reference.generatedUrl, NOT reference.url
|
|
176
183
|
// And this is because this hook inject query params used to:
|
|
177
184
|
// - bypass browser cache (?v)
|
|
@@ -286,6 +293,7 @@ export const createKitchen = ({
|
|
|
286
293
|
status = 200,
|
|
287
294
|
headers = {},
|
|
288
295
|
body,
|
|
296
|
+
isEntryPoint,
|
|
289
297
|
} = fetchUrlContentReturnValue
|
|
290
298
|
if (status !== 200) {
|
|
291
299
|
throw new Error(`unexpected status, ${status}`)
|
|
@@ -319,6 +327,9 @@ export const createKitchen = ({
|
|
|
319
327
|
if (data) {
|
|
320
328
|
Object.assign(urlInfo.data, data)
|
|
321
329
|
}
|
|
330
|
+
if (typeof isEntryPoint === "boolean") {
|
|
331
|
+
urlInfo.isEntryPoint = isEntryPoint
|
|
332
|
+
}
|
|
322
333
|
if (filename) {
|
|
323
334
|
urlInfo.filename = filename
|
|
324
335
|
}
|
|
@@ -620,6 +631,7 @@ export const createKitchen = ({
|
|
|
620
631
|
|
|
621
632
|
const prepareEntryPoint = (params) => {
|
|
622
633
|
const entryReference = createReference(params)
|
|
634
|
+
entryReference.isEntryPoint = true
|
|
623
635
|
const entryUrlInfo = resolveReference(entryReference)
|
|
624
636
|
return [entryReference, entryUrlInfo]
|
|
625
637
|
}
|
|
@@ -718,6 +730,10 @@ const applyReferenceEffectsOnUrlInfo = (reference, urlInfo, context) => {
|
|
|
718
730
|
}
|
|
719
731
|
urlInfo.originalUrl = urlInfo.originalUrl || reference.url
|
|
720
732
|
|
|
733
|
+
if (reference.isEntryPoint || isWebWorkerEntryPointReference(reference)) {
|
|
734
|
+
urlInfo.isEntryPoint = true
|
|
735
|
+
}
|
|
736
|
+
|
|
721
737
|
Object.assign(urlInfo.data, reference.data)
|
|
722
738
|
Object.assign(urlInfo.timing, reference.timing)
|
|
723
739
|
if (reference.injected) {
|
|
@@ -746,9 +762,6 @@ const applyReferenceEffectsOnUrlInfo = (reference, urlInfo, context) => {
|
|
|
746
762
|
: reference.content
|
|
747
763
|
urlInfo.content = reference.content
|
|
748
764
|
}
|
|
749
|
-
if (isWebWorkerEntryPointReference(reference)) {
|
|
750
|
-
urlInfo.data.isWebWorkerEntryPoint = true
|
|
751
|
-
}
|
|
752
765
|
}
|
|
753
766
|
|
|
754
767
|
const adjustUrlSite = (urlInfo, { urlGraph, url, line, column }) => {
|
|
@@ -50,10 +50,10 @@ export const createFileService = ({
|
|
|
50
50
|
reference = urlGraph.inferReference(request.ressource, parentUrl)
|
|
51
51
|
}
|
|
52
52
|
if (!reference) {
|
|
53
|
-
const entryPoint = kitchen.
|
|
53
|
+
const entryPoint = kitchen.injectReference({
|
|
54
54
|
trace: parentUrl || rootDirectoryUrl,
|
|
55
55
|
parentUrl: parentUrl || rootDirectoryUrl,
|
|
56
|
-
type: "
|
|
56
|
+
type: "http_request",
|
|
57
57
|
specifier: request.ressource,
|
|
58
58
|
})
|
|
59
59
|
reference = entryPoint[0]
|
package/src/omega/url_graph.js
CHANGED
|
@@ -94,7 +94,7 @@ const rollupPluginJsenv = ({
|
|
|
94
94
|
let previousNonEntryPointModuleId
|
|
95
95
|
jsModuleUrlInfos.forEach((jsModuleUrlInfo) => {
|
|
96
96
|
const id = jsModuleUrlInfo.url
|
|
97
|
-
if (jsModuleUrlInfo.
|
|
97
|
+
if (jsModuleUrlInfo.isEntryPoint) {
|
|
98
98
|
emitChunk({
|
|
99
99
|
id,
|
|
100
100
|
})
|
|
@@ -355,10 +355,7 @@ const willBeInsideJsDirectory = ({
|
|
|
355
355
|
// generated by rollup
|
|
356
356
|
return true
|
|
357
357
|
}
|
|
358
|
-
if (
|
|
359
|
-
!jsModuleUrlInfo.data.isEntryPoint &&
|
|
360
|
-
!jsModuleUrlInfo.data.isWebWorkerEntryPoint
|
|
361
|
-
) {
|
|
358
|
+
if (!jsModuleUrlInfo.isEntryPoint) {
|
|
362
359
|
// not an entry point, jsenv will put it inside js/ directory
|
|
363
360
|
return true
|
|
364
361
|
}
|
|
@@ -101,24 +101,24 @@ const jsenvPluginAsJsClassicConversion = ({
|
|
|
101
101
|
if (!originalUrlInfo) {
|
|
102
102
|
return null
|
|
103
103
|
}
|
|
104
|
-
const isJsEntryPoint =
|
|
105
|
-
// in general html files are entry points
|
|
106
|
-
// but during build js can be sepcified as an entry point
|
|
107
|
-
// (meaning there is no html file where we can inject systemjs)
|
|
108
|
-
// in that case we need to inject systemjs in the js file
|
|
109
|
-
originalUrlInfo.data.isEntryPoint ||
|
|
110
|
-
// In thoose case we need to inject systemjs the worker js file
|
|
111
|
-
originalUrlInfo.data.isWebWorkerEntryPoint
|
|
112
|
-
// if it's an entry point without dependency (it does not use import)
|
|
113
|
-
// then we can use UMD, otherwise we have to use systemjs
|
|
114
|
-
// because it is imported by systemjs
|
|
115
104
|
const jsClassicFormat =
|
|
116
|
-
|
|
105
|
+
// in general html file are entry points, but js can be entry point when:
|
|
106
|
+
// - passed in entryPoints to build
|
|
107
|
+
// - is used by web worker
|
|
108
|
+
// - the reference contains ?entry_point
|
|
109
|
+
// When js is entry point there can be no HTML to inject systemjs
|
|
110
|
+
// and systemjs must be injected into the js file
|
|
111
|
+
originalUrlInfo.isEntryPoint &&
|
|
112
|
+
// if it's an entry point without dependency (it does not use import)
|
|
113
|
+
// then we can use UMD, otherwise we have to use systemjs
|
|
114
|
+
// because it is imported by systemjs
|
|
115
|
+
!originalUrlInfo.data.usesImport
|
|
116
|
+
? "umd"
|
|
117
|
+
: "system"
|
|
117
118
|
const { content, sourcemap } = await convertJsModuleToJsClassic({
|
|
118
119
|
systemJsInjection,
|
|
119
120
|
systemJsClientFileUrl,
|
|
120
121
|
urlInfo: originalUrlInfo,
|
|
121
|
-
isJsEntryPoint,
|
|
122
122
|
jsClassicFormat,
|
|
123
123
|
})
|
|
124
124
|
urlInfo.data.jsClassicFormat = jsClassicFormat
|
|
@@ -160,7 +160,6 @@ const convertJsModuleToJsClassic = async ({
|
|
|
160
160
|
systemJsInjection,
|
|
161
161
|
systemJsClientFileUrl,
|
|
162
162
|
urlInfo,
|
|
163
|
-
isJsEntryPoint,
|
|
164
163
|
jsClassicFormat,
|
|
165
164
|
}) => {
|
|
166
165
|
const { code, map } = await applyBabelPlugins({
|
|
@@ -193,7 +192,11 @@ const convertJsModuleToJsClassic = async ({
|
|
|
193
192
|
})
|
|
194
193
|
let sourcemap = urlInfo.sourcemap
|
|
195
194
|
sourcemap = await composeTwoSourcemaps(sourcemap, map)
|
|
196
|
-
if (
|
|
195
|
+
if (
|
|
196
|
+
systemJsInjection &&
|
|
197
|
+
jsClassicFormat === "system" &&
|
|
198
|
+
urlInfo.isEntryPoint
|
|
199
|
+
) {
|
|
197
200
|
const magicSource = createMagicSource(code)
|
|
198
201
|
const systemjsCode = readFileSync(systemJsClientFileUrl, { as: "string" })
|
|
199
202
|
magicSource.prepend(`${systemjsCode}\n\n`)
|
|
@@ -9,7 +9,8 @@ export const jsenvPluginUrlResolution = () => {
|
|
|
9
9
|
name: "jsenv:url_resolution",
|
|
10
10
|
appliesDuring: "*",
|
|
11
11
|
resolveUrl: {
|
|
12
|
-
"
|
|
12
|
+
"http_request": urlResolver, // during dev
|
|
13
|
+
"entry_point": urlResolver, // during build
|
|
13
14
|
"link_href": urlResolver,
|
|
14
15
|
"script_src": urlResolver,
|
|
15
16
|
"a_href": urlResolver,
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync } from "node:fs"
|
|
2
2
|
import { Abort } from "@jsenv/abort"
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
filterV8Coverage,
|
|
7
|
-
} from "./v8_coverage_from_directory.js"
|
|
4
|
+
import { filterV8Coverage } from "./v8_coverage.js"
|
|
5
|
+
import { readNodeV8CoverageDirectory } from "./v8_coverage_node_directory.js"
|
|
8
6
|
import { composeTwoV8Coverages } from "./v8_coverage_composition.js"
|
|
9
7
|
import { composeTwoFileByFileIstanbulCoverages } from "./istanbul_coverage_composition.js"
|
|
10
8
|
import { v8CoverageToIstanbul } from "./v8_coverage_to_istanbul.js"
|
|
@@ -20,8 +18,7 @@ export const reportToCoverage = async (
|
|
|
20
18
|
rootDirectoryUrl,
|
|
21
19
|
coverageConfig,
|
|
22
20
|
coverageIncludeMissing,
|
|
23
|
-
|
|
24
|
-
coverageForceIstanbul,
|
|
21
|
+
coverageMethodForNodeJs,
|
|
25
22
|
coverageV8ConflictWarning,
|
|
26
23
|
},
|
|
27
24
|
) => {
|
|
@@ -49,24 +46,24 @@ export const reportToCoverage = async (
|
|
|
49
46
|
// that were suppose to be coverage but were not.
|
|
50
47
|
if (
|
|
51
48
|
executionResult.status === "completed" &&
|
|
52
|
-
executionResult.
|
|
53
|
-
|
|
49
|
+
executionResult.type === "node" &&
|
|
50
|
+
coverageMethodForNodeJs !== "NODE_V8_COVERAGE"
|
|
54
51
|
) {
|
|
55
52
|
logger.warn(
|
|
56
|
-
`No
|
|
53
|
+
`No "coverageFileUrl" from execution named "${executionName}" of ${file}`,
|
|
57
54
|
)
|
|
58
55
|
}
|
|
59
56
|
},
|
|
60
57
|
})
|
|
61
58
|
|
|
62
|
-
if (
|
|
63
|
-
await
|
|
59
|
+
if (coverageMethodForNodeJs === "NODE_V8_COVERAGE") {
|
|
60
|
+
await readNodeV8CoverageDirectory({
|
|
64
61
|
logger,
|
|
65
62
|
signal,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
onV8Coverage: async (nodeV8Coverage) => {
|
|
64
|
+
const nodeV8CoverageLight = await filterV8Coverage(nodeV8Coverage, {
|
|
65
|
+
rootDirectoryUrl,
|
|
66
|
+
coverageConfig,
|
|
70
67
|
})
|
|
71
68
|
v8Coverage = v8Coverage
|
|
72
69
|
? composeTwoV8Coverages(v8Coverage, nodeV8CoverageLight)
|
|
@@ -164,9 +161,9 @@ const getCoverageFromReport = async ({ signal, report, onMissing }) => {
|
|
|
164
161
|
return
|
|
165
162
|
}
|
|
166
163
|
|
|
167
|
-
const executionCoverage =
|
|
168
|
-
|
|
169
|
-
|
|
164
|
+
const executionCoverage = JSON.parse(
|
|
165
|
+
String(readFileSync(new URL(coverageFileUrl))),
|
|
166
|
+
)
|
|
170
167
|
if (isV8Coverage(executionCoverage)) {
|
|
171
168
|
v8Coverage = v8Coverage
|
|
172
169
|
? composeTwoV8Coverages(v8Coverage, executionCoverage)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { URL_META } from "@jsenv/url-meta"
|
|
2
|
+
|
|
3
|
+
export const filterV8Coverage = async (
|
|
4
|
+
v8Coverage,
|
|
5
|
+
{ rootDirectoryUrl, coverageConfig },
|
|
6
|
+
) => {
|
|
7
|
+
const associations = URL_META.resolveAssociations(
|
|
8
|
+
{ cover: coverageConfig },
|
|
9
|
+
rootDirectoryUrl,
|
|
10
|
+
)
|
|
11
|
+
const urlShouldBeCovered = (url) => {
|
|
12
|
+
const { cover } = URL_META.applyAssociations({
|
|
13
|
+
url: new URL(url, rootDirectoryUrl).href,
|
|
14
|
+
associations,
|
|
15
|
+
})
|
|
16
|
+
return cover
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const v8CoverageFiltered = {
|
|
20
|
+
...v8Coverage,
|
|
21
|
+
result: v8Coverage.result.filter((fileReport) =>
|
|
22
|
+
urlShouldBeCovered(fileReport.url),
|
|
23
|
+
),
|
|
24
|
+
}
|
|
25
|
+
return v8CoverageFiltered
|
|
26
|
+
}
|
|
@@ -1,27 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
readDirectory,
|
|
4
|
-
readFile,
|
|
5
|
-
} from "@jsenv/filesystem"
|
|
6
|
-
import { resolveUrl } from "@jsenv/urls"
|
|
1
|
+
import { readFileSync, readdirSync } from "node:fs"
|
|
2
|
+
import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem"
|
|
7
3
|
import { createDetailedMessage } from "@jsenv/log"
|
|
8
4
|
import { Abort } from "@jsenv/abort"
|
|
9
5
|
|
|
10
|
-
export const
|
|
6
|
+
export const readNodeV8CoverageDirectory = async ({
|
|
11
7
|
logger,
|
|
12
8
|
signal,
|
|
13
|
-
NODE_V8_COVERAGE,
|
|
14
9
|
onV8Coverage,
|
|
15
10
|
maxMsWaitingForNodeToWriteCoverageFile = 2000,
|
|
16
11
|
}) => {
|
|
12
|
+
const NODE_V8_COVERAGE = process.env.NODE_V8_COVERAGE
|
|
17
13
|
const operation = Abort.startOperation()
|
|
18
14
|
operation.addAbortSignal(signal)
|
|
19
15
|
|
|
16
|
+
let timeSpentTrying = 0
|
|
20
17
|
const tryReadDirectory = async () => {
|
|
21
|
-
const dirContent =
|
|
18
|
+
const dirContent = readdirSync(NODE_V8_COVERAGE)
|
|
22
19
|
if (dirContent.length > 0) {
|
|
23
20
|
return dirContent
|
|
24
21
|
}
|
|
22
|
+
if (timeSpentTrying < maxMsWaitingForNodeToWriteCoverageFile) {
|
|
23
|
+
await new Promise((resolve) => setTimeout(resolve, 200))
|
|
24
|
+
timeSpentTrying += 200
|
|
25
|
+
logger.debug("retry to read coverage directory")
|
|
26
|
+
return tryReadDirectory()
|
|
27
|
+
}
|
|
25
28
|
logger.warn(`v8 coverage directory is empty at ${NODE_V8_COVERAGE}`)
|
|
26
29
|
return dirContent
|
|
27
30
|
}
|
|
@@ -32,17 +35,19 @@ export const visitNodeV8Directory = async ({
|
|
|
32
35
|
|
|
33
36
|
const coverageDirectoryUrl =
|
|
34
37
|
assertAndNormalizeDirectoryUrl(NODE_V8_COVERAGE)
|
|
38
|
+
|
|
35
39
|
await dirContent.reduce(async (previous, dirEntry) => {
|
|
36
40
|
operation.throwIfAborted()
|
|
37
41
|
await previous
|
|
38
42
|
|
|
39
|
-
const dirEntryUrl =
|
|
40
|
-
const tryReadJsonFile = async (
|
|
41
|
-
const fileContent =
|
|
43
|
+
const dirEntryUrl = new URL(dirEntry, coverageDirectoryUrl)
|
|
44
|
+
const tryReadJsonFile = async () => {
|
|
45
|
+
const fileContent = String(readFileSync(dirEntryUrl))
|
|
42
46
|
if (fileContent === "") {
|
|
43
|
-
if (timeSpentTrying <
|
|
47
|
+
if (timeSpentTrying < maxMsWaitingForNodeToWriteCoverageFile) {
|
|
44
48
|
await new Promise((resolve) => setTimeout(resolve, 200))
|
|
45
|
-
|
|
49
|
+
timeSpentTrying += 200
|
|
50
|
+
return tryReadJsonFile()
|
|
46
51
|
}
|
|
47
52
|
console.warn(`Coverage JSON file is empty at ${dirEntryUrl}`)
|
|
48
53
|
return null
|
|
@@ -54,7 +59,8 @@ export const visitNodeV8Directory = async ({
|
|
|
54
59
|
} catch (e) {
|
|
55
60
|
if (timeSpentTrying < maxMsWaitingForNodeToWriteCoverageFile) {
|
|
56
61
|
await new Promise((resolve) => setTimeout(resolve, 200))
|
|
57
|
-
|
|
62
|
+
timeSpentTrying += 200
|
|
63
|
+
return tryReadJsonFile()
|
|
58
64
|
}
|
|
59
65
|
console.warn(
|
|
60
66
|
createDetailedMessage(`Error while reading coverage file`, {
|
|
@@ -68,20 +74,10 @@ export const visitNodeV8Directory = async ({
|
|
|
68
74
|
|
|
69
75
|
const fileContent = await tryReadJsonFile()
|
|
70
76
|
if (fileContent) {
|
|
71
|
-
onV8Coverage(fileContent)
|
|
77
|
+
await onV8Coverage(fileContent)
|
|
72
78
|
}
|
|
73
79
|
}, Promise.resolve())
|
|
74
80
|
} finally {
|
|
75
81
|
await operation.end()
|
|
76
82
|
}
|
|
77
83
|
}
|
|
78
|
-
|
|
79
|
-
export const filterV8Coverage = (v8Coverage, { urlShouldBeCovered }) => {
|
|
80
|
-
const v8CoverageFiltered = {
|
|
81
|
-
...v8Coverage,
|
|
82
|
-
result: v8Coverage.result.filter((fileReport) =>
|
|
83
|
-
urlShouldBeCovered(fileReport.url),
|
|
84
|
-
),
|
|
85
|
-
}
|
|
86
|
-
return v8CoverageFiltered
|
|
87
|
-
}
|