@jsenv/core 23.1.3 → 23.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/dist/jsenv_browser_system.js +36 -99
  2. package/dist/jsenv_browser_system.js.map +12 -21
  3. package/dist/jsenv_compile_proxy.js +18 -82
  4. package/dist/jsenv_compile_proxy.js.map +11 -21
  5. package/dist/jsenv_exploring_index.js +127 -274
  6. package/dist/jsenv_exploring_index.js.map +76 -90
  7. package/dist/jsenv_exploring_redirector.js +21 -89
  8. package/dist/jsenv_exploring_redirector.js.map +13 -25
  9. package/dist/jsenv_toolbar.js +81 -149
  10. package/dist/jsenv_toolbar.js.map +50 -61
  11. package/dist/jsenv_toolbar_injector.js +185 -231
  12. package/dist/jsenv_toolbar_injector.js.map +30 -42
  13. package/package.json +8 -9
  14. package/src/abort/abortable.js +172 -0
  15. package/src/abort/callback_list.js +64 -0
  16. package/src/abort/callback_race.js +34 -0
  17. package/src/abort/cleaner.js +22 -0
  18. package/src/abort/main.js +32 -0
  19. package/src/abort/process_teardown_events.js +59 -0
  20. package/src/buildProject.js +132 -123
  21. package/src/execute.js +108 -107
  22. package/src/executeTestPlan.js +107 -125
  23. package/src/importUsingChildProcess.js +2 -1
  24. package/src/internal/browser-launcher/executeHtmlFile.js +33 -12
  25. package/src/internal/browser-utils/fetch-browser.js +4 -29
  26. package/src/internal/browser-utils/fetchUsingXHR.js +5 -7
  27. package/src/internal/building/buildUsingRollup.js +60 -24
  28. package/src/internal/building/createJsenvRollupPlugin.js +13 -31
  29. package/src/internal/building/ressource_builder.js +3 -6
  30. package/src/internal/building/sourcemap_loader.js +4 -5
  31. package/src/internal/building/url_fetcher.js +2 -5
  32. package/src/internal/building/url_loader.js +3 -6
  33. package/src/internal/compiling/compileFile.js +1 -2
  34. package/src/internal/compiling/createCompiledFileService.js +8 -9
  35. package/src/internal/compiling/startCompileServer.js +74 -135
  36. package/src/internal/executing/coverage/generateCoverageJsonFile.js +20 -3
  37. package/src/internal/executing/coverage/relativeUrlToEmptyCoverage.js +19 -30
  38. package/src/internal/executing/coverage/reportToCoverage.js +44 -24
  39. package/src/internal/executing/coverage/v8CoverageFromNodeV8Directory.js +2 -15
  40. package/src/internal/executing/createSummaryLog.js +50 -37
  41. package/src/internal/executing/executeConcurrently.js +89 -47
  42. package/src/internal/executing/executePlan.js +33 -7
  43. package/src/internal/executing/executionLogs.js +25 -28
  44. package/src/internal/executing/execution_colors.js +15 -0
  45. package/src/internal/executing/generateExecutionSteps.js +3 -2
  46. package/src/internal/executing/launchAndExecute.js +217 -261
  47. package/src/internal/exploring/fetchExploringJson.js +3 -4
  48. package/src/internal/fetchUrl.js +6 -2
  49. package/src/internal/logs/log_style.js +16 -28
  50. package/src/internal/logs/msAsDuration.js +1 -1
  51. package/src/internal/node-launcher/createChildProcessOptions.js +4 -5
  52. package/src/internal/node-launcher/createControllableNodeProcess.js +117 -229
  53. package/src/internal/node-launcher/kill_process_tree.js +76 -0
  54. package/src/internal/node-launcher/nodeControllableFile.mjs +16 -10
  55. package/src/internal/{promise_track_race.js → promise_race.js} +2 -2
  56. package/src/internal/runtime/s.js +25 -24
  57. package/src/internal/toolbar/toolbar.html +157 -61
  58. package/src/internal/toolbar/toolbar.injector.js +8 -0
  59. package/src/internal/toolbar/util/animation.js +3 -7
  60. package/src/internal/toolbar/util/fetching.js +1 -30
  61. package/src/jsenvServiceWorkerFinalizer.js +1 -2
  62. package/src/launchBrowser.js +131 -127
  63. package/src/launchNode.js +29 -17
  64. package/src/playwright_browser_versions.js +3 -3
  65. package/src/requireUsingChildProcess.js +2 -1
  66. package/src/signal/signal.js +65 -0
  67. package/src/startExploring.js +70 -72
  68. package/src/internal/executeJsenvAsyncFunction.js +0 -34
  69. package/src/internal/toolbar/animation/toolbar-movie-icon.svg +0 -15
  70. package/src/internal/toolbar/compilation/flask.svg +0 -7
  71. package/src/internal/toolbar/compilation/info.svg +0 -9
  72. package/src/internal/toolbar/compilation/loupe.svg +0 -11
  73. package/src/internal/toolbar/compilation/toolbar_compilation.svg +0 -11
  74. package/src/internal/toolbar/eventsource/toolbar-power-icon.svg +0 -10
  75. package/src/internal/toolbar/eventsource/toolbar-power-off-icon.svg +0 -10
  76. package/src/internal/toolbar/responsive/toolbar-dots-icon.svg +0 -10
  77. package/src/internal/toolbar/settings/toolbar-settings-icon.svg +0 -9
  78. package/src/internal/toolbar/theme/toolbar-palette-icon.svg +0 -10
  79. package/src/internal/toolbar/toolbar-cross-icon.svg +0 -10
  80. package/src/internal/toolbar/toolbar-loading-icon.svg +0 -102
  81. package/src/internal/toolbar/toolbar-notif-icon.svg +0 -9
  82. package/src/internal/trackRessources.js +0 -23
@@ -0,0 +1,64 @@
1
+ export const createCallbackList = () => {
2
+ let callbacks = []
3
+
4
+ const add = (callback) => {
5
+ if (typeof callback !== "function") {
6
+ throw new Error(`callback must be a function, got ${callback}`)
7
+ }
8
+
9
+ // don't register twice
10
+ const existingCallback = callbacks.find((callbackCandidate) => {
11
+ return callbackCandidate === callback
12
+ })
13
+ if (existingCallback) {
14
+ if (typeof process.emitWarning === "object") {
15
+ process.emitWarning(`Trying to register same callback twice`, {
16
+ CODE: "CALLBACK_DUPLICATION",
17
+ detail: `It's often the sign that code is executd more than once`,
18
+ })
19
+ } else {
20
+ console.warn(`Trying to add same callback twice`)
21
+ }
22
+ } else {
23
+ callbacks = [...callbacks, callback]
24
+ }
25
+
26
+ return () => {
27
+ remove(callback)
28
+ }
29
+ }
30
+
31
+ const remove = (callback) => {
32
+ callbacks = arrayWithout(callbacks, callback)
33
+ }
34
+
35
+ const clear = () => {
36
+ callbacks = []
37
+ }
38
+
39
+ const copy = () => {
40
+ return callbacks.slice()
41
+ }
42
+
43
+ return {
44
+ add,
45
+ remove,
46
+ clear,
47
+ copy,
48
+ }
49
+ }
50
+
51
+ const arrayWithout = (array, item) => {
52
+ if (array.length === 0) return array
53
+ const arrayWithoutItem = []
54
+ let i = 0
55
+ while (i < array.length) {
56
+ const value = array[i]
57
+ i++
58
+ if (value === item) {
59
+ continue
60
+ }
61
+ arrayWithoutItem.push(value)
62
+ }
63
+ return arrayWithoutItem
64
+ }
@@ -0,0 +1,34 @@
1
+ /*
2
+ * See callback_race.md
3
+ */
4
+
5
+ export const raceCallbacks = (raceDescription, winnerCallback) => {
6
+ const cleanCallbacks = []
7
+ let done = false
8
+
9
+ const cleanup = () => {
10
+ const cleanCallbacksCopy = cleanCallbacks.slice()
11
+ cleanCallbacks.length = 0
12
+ cleanCallbacksCopy.forEach((clean) => {
13
+ clean()
14
+ })
15
+ }
16
+
17
+ Object.keys(raceDescription).forEach((candidateName) => {
18
+ const register = raceDescription[candidateName]
19
+ const returnValue = register((data) => {
20
+ if (done) return
21
+ done = true
22
+ cleanup()
23
+ winnerCallback({
24
+ name: candidateName,
25
+ data,
26
+ })
27
+ })
28
+ if (typeof returnValue === "function") {
29
+ cleanCallbacks.push(returnValue)
30
+ }
31
+ })
32
+
33
+ return cleanup
34
+ }
@@ -0,0 +1,22 @@
1
+ import { createCallbackList } from "./callback_list.js"
2
+
3
+ export const createCleaner = () => {
4
+ const callbackList = createCallbackList()
5
+
6
+ const addCallback = (callback) => {
7
+ return callbackList.add(callback)
8
+ }
9
+
10
+ const clean = async (reason) => {
11
+ const callbacks = callbackList.copy()
12
+ callbackList.clear()
13
+
14
+ await Promise.all(
15
+ callbacks.map(async (callback) => {
16
+ await callback(reason)
17
+ }),
18
+ )
19
+ }
20
+
21
+ return { addCallback, clean }
22
+ }
@@ -0,0 +1,32 @@
1
+ /*
2
+ * When starting an http server there is two distinct things
3
+ * that code may want to do:
4
+ * 1. Abort the server while it's starting
5
+ * 2. Stop server onces its started
6
+ *
7
+ * This can be achieved with the following code where
8
+ * server is aborted if it takes more than 1s to start
9
+ * and immediatly stopped once started
10
+ *
11
+ * const abortController = new AbortController()
12
+ * setTimeout(() => { abortController.abort() }, 1000)
13
+ * const server = await startServer({
14
+ * abortSignal: abortController.signal
15
+ * })
16
+ * await server.stop()
17
+ *
18
+ * In order to implement this kind of API two helpers are exported
19
+ * here:
20
+ * 1. "Abort" which can be used to throw an abort error
21
+ * while server is starting
22
+ * 2. "Cleanup" which can be used to track how to cleanup all the things
23
+ * done to start the server
24
+ *
25
+ * Same concepts could be reused when spwaning a child process, a worker, etc..
26
+ *
27
+ */
28
+
29
+ export { Abortable } from "./abortable.js"
30
+
31
+ // when will be a package this should be a Node.js export only
32
+ export { raceProcessTeardownEvents } from "./process_teardown_events.js"
@@ -0,0 +1,59 @@
1
+ import { raceCallbacks } from "./callback_race.js"
2
+
3
+ export const raceProcessTeardownEvents = (processTeardownEvents, callback) => {
4
+ return raceCallbacks(
5
+ {
6
+ ...(processTeardownEvents.SIGHUP ? SIGHUP_CALLBACK : {}),
7
+ ...(processTeardownEvents.SIGTERM ? SIGTERM_CALLBACK : {}),
8
+ ...(processTeardownEvents.SIGINT ? SIGINT_CALLBACK : {}),
9
+ ...(processTeardownEvents.beforeExit ? BEFORE_EXIT_CALLBACK : {}),
10
+ ...(processTeardownEvents.exit ? EXIT_CALLBACK : {}),
11
+ },
12
+ callback,
13
+ )
14
+ }
15
+
16
+ const SIGHUP_CALLBACK = {
17
+ SIGHUP: (cb) => {
18
+ process.on("SIGHUP", cb)
19
+ return () => {
20
+ process.removeListener("SIGHUP", cb)
21
+ }
22
+ },
23
+ }
24
+
25
+ const SIGTERM_CALLBACK = {
26
+ SIGTERM: (cb) => {
27
+ process.on("SIGTERM", cb)
28
+ return () => {
29
+ process.removeListener("SIGTERM", cb)
30
+ }
31
+ },
32
+ }
33
+
34
+ const BEFORE_EXIT_CALLBACK = {
35
+ beforeExit: (cb) => {
36
+ process.on("beforeExit", cb)
37
+ return () => {
38
+ process.removeListener("beforeExit", cb)
39
+ }
40
+ },
41
+ }
42
+
43
+ const EXIT_CALLBACK = {
44
+ exit: (cb) => {
45
+ process.on("exit", cb)
46
+ return () => {
47
+ process.removeListener("exit", cb)
48
+ }
49
+ },
50
+ }
51
+
52
+ const SIGINT_CALLBACK = {
53
+ SIGINT: (cb) => {
54
+ process.on("SIGINT", cb)
55
+ return () => {
56
+ process.removeListener("SIGINT", cb)
57
+ }
58
+ },
59
+ }
@@ -1,11 +1,10 @@
1
1
  import { createLogger, createDetailedMessage } from "@jsenv/logger"
2
- import {
3
- createCancellationToken,
4
- composeCancellationToken,
5
- } from "@jsenv/cancellation"
6
2
  import { resolveDirectoryUrl } from "@jsenv/filesystem"
7
3
 
8
- import { executeJsenvAsyncFunction } from "./internal/executeJsenvAsyncFunction.js"
4
+ import {
5
+ Abortable,
6
+ raceProcessTeardownEvents,
7
+ } from "@jsenv/core/src/abort/main.js"
9
8
  import { COMPILE_ID_BEST } from "./internal/CONSTANTS.js"
10
9
  import {
11
10
  assertProjectDirectoryUrl,
@@ -19,8 +18,8 @@ import {
19
18
  } from "./internal/generateGroupMap/jsenvRuntimeSupport.js"
20
19
 
21
20
  export const buildProject = async ({
22
- cancellationToken = createCancellationToken(),
23
- cancelOnSIGINT = false,
21
+ signal = new AbortController().signal,
22
+ handleSIGINT = true,
24
23
  logLevel = "info",
25
24
  compileServerLogLevel = "warn",
26
25
  logger,
@@ -103,141 +102,151 @@ export const buildProject = async ({
103
102
  // (to fix that sourcemap could be inlined)
104
103
  filesystemCache = true,
105
104
  }) => {
106
- const jsenvBuildFunction = async ({ jsenvCancellationToken }) => {
107
- cancellationToken = composeCancellationToken(
108
- cancellationToken,
109
- jsenvCancellationToken,
105
+ logger = logger || createLogger({ logLevel })
106
+ if (!["esmodule", "systemjs", "commonjs", "global"].includes(format)) {
107
+ throw new TypeError(
108
+ `unexpected format: ${format}. Must be "esmodule", "systemjs", "commonjs" or "global".`,
110
109
  )
110
+ }
111
+ if (typeof runtimeSupport !== "object" || runtimeSupport === null) {
112
+ throw new TypeError(
113
+ `runtimeSupport must be an object, got ${runtimeSupport}`,
114
+ )
115
+ }
111
116
 
112
- logger = logger || createLogger({ logLevel })
113
- if (!["esmodule", "systemjs", "commonjs", "global"].includes(format)) {
114
- throw new TypeError(
115
- `unexpected format: ${format}. Must be "esmodule", "systemjs", "commonjs" or "global".`,
116
- )
117
- }
118
- if (typeof runtimeSupport !== "object" || runtimeSupport === null) {
119
- throw new TypeError(
120
- `runtimeSupport must be an object, got ${runtimeSupport}`,
121
- )
122
- }
123
-
124
- projectDirectoryUrl = assertProjectDirectoryUrl({ projectDirectoryUrl })
125
- await assertProjectDirectoryExists({ projectDirectoryUrl })
117
+ projectDirectoryUrl = assertProjectDirectoryUrl({ projectDirectoryUrl })
118
+ await assertProjectDirectoryExists({ projectDirectoryUrl })
126
119
 
127
- assertEntryPointMap({ entryPointMap })
120
+ assertEntryPointMap({ entryPointMap })
128
121
 
129
- if (Object.keys(entryPointMap).length === 0) {
130
- logger.error(`entryPointMap is an empty object`)
131
- return {
132
- rollupBuilds: {},
133
- }
122
+ if (Object.keys(entryPointMap).length === 0) {
123
+ logger.error(`entryPointMap is an empty object`)
124
+ return {
125
+ rollupBuilds: {},
134
126
  }
127
+ }
135
128
 
136
- assertBuildDirectoryRelativeUrl({ buildDirectoryRelativeUrl })
137
- const buildDirectoryUrl = resolveDirectoryUrl(
138
- buildDirectoryRelativeUrl,
139
- projectDirectoryUrl,
129
+ assertBuildDirectoryRelativeUrl({ buildDirectoryRelativeUrl })
130
+ const buildDirectoryUrl = resolveDirectoryUrl(
131
+ buildDirectoryRelativeUrl,
132
+ projectDirectoryUrl,
133
+ )
134
+ assertBuildDirectoryInsideProject({
135
+ buildDirectoryUrl,
136
+ projectDirectoryUrl,
137
+ })
138
+
139
+ const buildOperation = Abortable.fromSignal(signal)
140
+ if (handleSIGINT) {
141
+ Abortable.effect(buildOperation, (cb) =>
142
+ raceProcessTeardownEvents(
143
+ {
144
+ SIGINT: true,
145
+ },
146
+ cb,
147
+ ),
140
148
  )
141
- assertBuildDirectoryInsideProject({
142
- buildDirectoryUrl,
143
- projectDirectoryUrl,
144
- })
149
+ }
145
150
 
146
- const compileServer = await startCompileServer({
147
- cancellationToken,
148
- compileServerLogLevel,
151
+ const compileServer = await startCompileServer({
152
+ signal: buildOperation.signal,
153
+ compileServerLogLevel,
154
+
155
+ projectDirectoryUrl,
156
+ jsenvDirectoryRelativeUrl,
157
+ jsenvDirectoryClean,
158
+ // build compiled files are written into a different directory
159
+ // than exploring-server. This is because here we compile for rollup
160
+ // that is expecting esmodule format, not systemjs
161
+ // + some more differences like import.meta.dev
162
+ outDirectoryName: "out-build",
163
+ importDefaultExtension,
164
+ moduleOutFormat: "esmodule", // rollup will transform into systemjs
165
+ importMetaFormat: format, // but ensure import.meta are correctly transformed into the right format
166
+
167
+ compileServerProtocol,
168
+ compileServerPrivateKey,
169
+ compileServerCertificate,
170
+ compileServerIp,
171
+ compileServerPort,
172
+ env,
173
+ babelPluginMap,
174
+ transformTopLevelAwait,
175
+ customCompilers,
176
+ runtimeSupport,
177
+
178
+ compileServerCanReadFromFileSystem: filesystemCache,
179
+ compileServerCanWriteOnFilesystem: filesystemCache,
180
+ // keep source html untouched
181
+ // here we don't need to inline importmap
182
+ // nor to inject jsenv script
183
+ transformHtmlSourceFiles: false,
184
+ })
185
+
186
+ buildOperation.cleaner.addCallback(async () => {
187
+ await compileServer.stop(`build cleanup`)
188
+ })
149
189
 
190
+ const { outDirectoryRelativeUrl, origin: compileServerOrigin } = compileServer
191
+
192
+ try {
193
+ const result = await buildUsingRollup({
194
+ buildOperation,
195
+ logger,
196
+
197
+ entryPointMap,
150
198
  projectDirectoryUrl,
151
- jsenvDirectoryRelativeUrl,
152
- jsenvDirectoryClean,
153
- // build compiled files are written into a different directory
154
- // than exploring-server. This is because here we compile for rollup
155
- // that is expecting esmodule format, not systemjs
156
- // + some more differences like import.meta.dev
157
- outDirectoryName: "out-build",
199
+ compileServerOrigin,
200
+ compileDirectoryRelativeUrl: `${outDirectoryRelativeUrl}${COMPILE_ID_BEST}/`,
201
+ buildDirectoryUrl,
202
+ buildDirectoryClean,
203
+ assetManifestFile,
204
+ assetManifestFileRelativeUrl,
205
+
206
+ urlMappings,
207
+ importResolutionMethod,
208
+ importMapFileRelativeUrl,
158
209
  importDefaultExtension,
159
- moduleOutFormat: "esmodule", // rollup will transform into systemjs
160
- importMetaFormat: format, // but ensure import.meta are correctly transformed into the right format
161
-
162
- compileServerProtocol,
163
- compileServerPrivateKey,
164
- compileServerCertificate,
165
- compileServerIp,
166
- compileServerPort,
167
- env,
210
+ externalImportSpecifiers,
211
+ externalImportUrlPatterns,
212
+ importPaths,
213
+
214
+ format,
215
+ systemJsUrl,
216
+ globalName,
217
+ globals,
168
218
  babelPluginMap,
169
219
  transformTopLevelAwait,
170
- customCompilers,
171
220
  runtimeSupport,
172
221
 
173
- compileServerCanReadFromFileSystem: filesystemCache,
174
- compileServerCanWriteOnFilesystem: filesystemCache,
175
- // keep source html untouched
176
- // here we don't need to inline importmap
177
- // nor to inject jsenv script
178
- transformHtmlSourceFiles: false,
222
+ urlVersioning,
223
+ lineBreakNormalization,
224
+ useImportMapToMaximizeCacheReuse,
225
+ preserveEntrySignatures,
226
+ jsConcatenation,
227
+
228
+ minify,
229
+ minifyHtmlOptions,
230
+ minifyJsOptions,
231
+ minifyCssOptions,
232
+
233
+ serviceWorkers,
234
+ serviceWorkerFinalizer,
235
+
236
+ writeOnFileSystem,
237
+ sourcemapExcludeSources,
179
238
  })
180
239
 
181
- const { outDirectoryRelativeUrl, origin: compileServerOrigin } =
182
- compileServer
183
-
184
- try {
185
- const result = await buildUsingRollup({
186
- cancellationToken,
187
- logger,
188
-
189
- entryPointMap,
190
- projectDirectoryUrl,
191
- compileServerOrigin,
192
- compileDirectoryRelativeUrl: `${outDirectoryRelativeUrl}${COMPILE_ID_BEST}/`,
193
- buildDirectoryUrl,
194
- buildDirectoryClean,
195
- assetManifestFile,
196
- assetManifestFileRelativeUrl,
197
-
198
- urlMappings,
199
- importResolutionMethod,
200
- importMapFileRelativeUrl,
201
- importDefaultExtension,
202
- externalImportSpecifiers,
203
- externalImportUrlPatterns,
204
- importPaths,
205
-
206
- format,
207
- systemJsUrl,
208
- globalName,
209
- globals,
210
- babelPluginMap,
211
- transformTopLevelAwait,
212
- runtimeSupport,
213
-
214
- urlVersioning,
215
- lineBreakNormalization,
216
- useImportMapToMaximizeCacheReuse,
217
- preserveEntrySignatures,
218
- jsConcatenation,
219
-
220
- minify,
221
- minifyHtmlOptions,
222
- minifyJsOptions,
223
- minifyCssOptions,
224
-
225
- serviceWorkers,
226
- serviceWorkerFinalizer,
227
-
228
- writeOnFileSystem,
229
- sourcemapExcludeSources,
230
- })
231
-
232
- return result
233
- } finally {
234
- compileServer.stop("build generated")
240
+ return result
241
+ } catch (e) {
242
+ if (Abortable.isAbortError(e)) {
243
+ logger.info("build aborted")
244
+ return null
235
245
  }
246
+ throw e
247
+ } finally {
248
+ await buildOperation.cleaner.clean()
236
249
  }
237
-
238
- return executeJsenvAsyncFunction(jsenvBuildFunction, {
239
- cancelOnSIGINT,
240
- })
241
250
  }
242
251
 
243
252
  const assertEntryPointMap = ({ entryPointMap }) => {