@jsenv/test 3.6.1 → 3.6.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.
@@ -1,7 +1,606 @@
1
- import { normalizeImportMap, resolveImport } from "./vendors.js";
2
1
  import { readFileSync } from "node:fs";
3
2
  import { pathToFileURL } from "node:url";
4
3
 
4
+ // duplicated from @jsenv/log to avoid the dependency
5
+ const createDetailedMessage = (message, details = {}) => {
6
+ let string = `${message}`;
7
+
8
+ Object.keys(details).forEach((key) => {
9
+ const value = details[key];
10
+ string += `
11
+ --- ${key} ---
12
+ ${
13
+ Array.isArray(value)
14
+ ? value.join(`
15
+ `)
16
+ : value
17
+ }`;
18
+ });
19
+
20
+ return string
21
+ };
22
+
23
+ const assertImportMap = (value) => {
24
+ if (value === null) {
25
+ throw new TypeError(`an importMap must be an object, got null`)
26
+ }
27
+
28
+ const type = typeof value;
29
+ if (type !== "object") {
30
+ throw new TypeError(`an importMap must be an object, received ${value}`)
31
+ }
32
+
33
+ if (Array.isArray(value)) {
34
+ throw new TypeError(
35
+ `an importMap must be an object, received array ${value}`,
36
+ )
37
+ }
38
+ };
39
+
40
+ const hasScheme = (string) => {
41
+ return /^[a-zA-Z]{2,}:/.test(string)
42
+ };
43
+
44
+ const urlToScheme = (urlString) => {
45
+ const colonIndex = urlString.indexOf(":");
46
+ if (colonIndex === -1) return ""
47
+ return urlString.slice(0, colonIndex)
48
+ };
49
+
50
+ const urlToPathname = (urlString) => {
51
+ return ressourceToPathname(urlToRessource(urlString))
52
+ };
53
+
54
+ const urlToRessource = (urlString) => {
55
+ const scheme = urlToScheme(urlString);
56
+
57
+ if (scheme === "file") {
58
+ return urlString.slice("file://".length)
59
+ }
60
+
61
+ if (scheme === "https" || scheme === "http") {
62
+ // remove origin
63
+ const afterProtocol = urlString.slice(scheme.length + "://".length);
64
+ const pathnameSlashIndex = afterProtocol.indexOf("/", "://".length);
65
+ return afterProtocol.slice(pathnameSlashIndex)
66
+ }
67
+
68
+ return urlString.slice(scheme.length + 1)
69
+ };
70
+
71
+ const ressourceToPathname = (ressource) => {
72
+ const searchSeparatorIndex = ressource.indexOf("?");
73
+ return searchSeparatorIndex === -1
74
+ ? ressource
75
+ : ressource.slice(0, searchSeparatorIndex)
76
+ };
77
+
78
+ const urlToOrigin = (urlString) => {
79
+ const scheme = urlToScheme(urlString);
80
+
81
+ if (scheme === "file") {
82
+ return "file://"
83
+ }
84
+
85
+ if (scheme === "http" || scheme === "https") {
86
+ const secondProtocolSlashIndex = scheme.length + "://".length;
87
+ const pathnameSlashIndex = urlString.indexOf("/", secondProtocolSlashIndex);
88
+
89
+ if (pathnameSlashIndex === -1) return urlString
90
+ return urlString.slice(0, pathnameSlashIndex)
91
+ }
92
+
93
+ return urlString.slice(0, scheme.length + 1)
94
+ };
95
+
96
+ const pathnameToParentPathname = (pathname) => {
97
+ const slashLastIndex = pathname.lastIndexOf("/");
98
+ if (slashLastIndex === -1) {
99
+ return "/"
100
+ }
101
+
102
+ return pathname.slice(0, slashLastIndex + 1)
103
+ };
104
+
105
+ // could be useful: https://url.spec.whatwg.org/#url-miscellaneous
106
+
107
+
108
+ const resolveUrl = (specifier, baseUrl) => {
109
+ if (baseUrl) {
110
+ if (typeof baseUrl !== "string") {
111
+ throw new TypeError(writeBaseUrlMustBeAString({ baseUrl, specifier }))
112
+ }
113
+ if (!hasScheme(baseUrl)) {
114
+ throw new Error(writeBaseUrlMustBeAbsolute({ baseUrl, specifier }))
115
+ }
116
+ }
117
+
118
+ if (hasScheme(specifier)) {
119
+ return specifier
120
+ }
121
+
122
+ if (!baseUrl) {
123
+ throw new Error(writeBaseUrlRequired({ baseUrl, specifier }))
124
+ }
125
+
126
+ // scheme relative
127
+ if (specifier.slice(0, 2) === "//") {
128
+ return `${urlToScheme(baseUrl)}:${specifier}`
129
+ }
130
+
131
+ // origin relative
132
+ if (specifier[0] === "/") {
133
+ return `${urlToOrigin(baseUrl)}${specifier}`
134
+ }
135
+
136
+ const baseOrigin = urlToOrigin(baseUrl);
137
+ const basePathname = urlToPathname(baseUrl);
138
+
139
+ if (specifier === ".") {
140
+ const baseDirectoryPathname = pathnameToParentPathname(basePathname);
141
+ return `${baseOrigin}${baseDirectoryPathname}`
142
+ }
143
+
144
+ // pathname relative inside
145
+ if (specifier.slice(0, 2) === "./") {
146
+ const baseDirectoryPathname = pathnameToParentPathname(basePathname);
147
+ return `${baseOrigin}${baseDirectoryPathname}${specifier.slice(2)}`
148
+ }
149
+
150
+ // pathname relative outside
151
+ if (specifier.slice(0, 3) === "../") {
152
+ let unresolvedPathname = specifier;
153
+ const importerFolders = basePathname.split("/");
154
+ importerFolders.pop();
155
+
156
+ while (unresolvedPathname.slice(0, 3) === "../") {
157
+ unresolvedPathname = unresolvedPathname.slice(3);
158
+ // when there is no folder left to resolved
159
+ // we just ignore '../'
160
+ if (importerFolders.length) {
161
+ importerFolders.pop();
162
+ }
163
+ }
164
+
165
+ const resolvedPathname = `${importerFolders.join(
166
+ "/",
167
+ )}/${unresolvedPathname}`;
168
+ return `${baseOrigin}${resolvedPathname}`
169
+ }
170
+
171
+ // bare
172
+ if (basePathname === "") {
173
+ return `${baseOrigin}/${specifier}`
174
+ }
175
+ if (basePathname[basePathname.length] === "/") {
176
+ return `${baseOrigin}${basePathname}${specifier}`
177
+ }
178
+ return `${baseOrigin}${pathnameToParentPathname(basePathname)}${specifier}`
179
+ };
180
+
181
+ const writeBaseUrlMustBeAString = ({
182
+ baseUrl,
183
+ specifier,
184
+ }) => `baseUrl must be a string.
185
+ --- base url ---
186
+ ${baseUrl}
187
+ --- specifier ---
188
+ ${specifier}`;
189
+
190
+ const writeBaseUrlMustBeAbsolute = ({
191
+ baseUrl,
192
+ specifier,
193
+ }) => `baseUrl must be absolute.
194
+ --- base url ---
195
+ ${baseUrl}
196
+ --- specifier ---
197
+ ${specifier}`;
198
+
199
+ const writeBaseUrlRequired = ({
200
+ baseUrl,
201
+ specifier,
202
+ }) => `baseUrl required to resolve relative specifier.
203
+ --- base url ---
204
+ ${baseUrl}
205
+ --- specifier ---
206
+ ${specifier}`;
207
+
208
+ const tryUrlResolution = (string, url) => {
209
+ const result = resolveUrl(string, url);
210
+ return hasScheme(result) ? result : null
211
+ };
212
+
213
+ const resolveSpecifier = (specifier, importer) => {
214
+ if (
215
+ specifier === "." ||
216
+ specifier[0] === "/" ||
217
+ specifier.startsWith("./") ||
218
+ specifier.startsWith("../")
219
+ ) {
220
+ return resolveUrl(specifier, importer)
221
+ }
222
+
223
+ if (hasScheme(specifier)) {
224
+ return specifier
225
+ }
226
+
227
+ return null
228
+ };
229
+
230
+ const applyImportMap = ({
231
+ importMap,
232
+ specifier,
233
+ importer,
234
+ createBareSpecifierError = ({ specifier, importer }) => {
235
+ return new Error(
236
+ createDetailedMessage(`Unmapped bare specifier.`, {
237
+ specifier,
238
+ importer,
239
+ }),
240
+ )
241
+ },
242
+ onImportMapping = () => {},
243
+ }) => {
244
+ assertImportMap(importMap);
245
+ if (typeof specifier !== "string") {
246
+ throw new TypeError(
247
+ createDetailedMessage("specifier must be a string.", {
248
+ specifier,
249
+ importer,
250
+ }),
251
+ )
252
+ }
253
+ if (importer) {
254
+ if (typeof importer !== "string") {
255
+ throw new TypeError(
256
+ createDetailedMessage("importer must be a string.", {
257
+ importer,
258
+ specifier,
259
+ }),
260
+ )
261
+ }
262
+ if (!hasScheme(importer)) {
263
+ throw new Error(
264
+ createDetailedMessage(`importer must be an absolute url.`, {
265
+ importer,
266
+ specifier,
267
+ }),
268
+ )
269
+ }
270
+ }
271
+
272
+ const specifierUrl = resolveSpecifier(specifier, importer);
273
+ const specifierNormalized = specifierUrl || specifier;
274
+
275
+ const { scopes } = importMap;
276
+ if (scopes && importer) {
277
+ const scopeSpecifierMatching = Object.keys(scopes).find(
278
+ (scopeSpecifier) => {
279
+ return (
280
+ scopeSpecifier === importer ||
281
+ specifierIsPrefixOf(scopeSpecifier, importer)
282
+ )
283
+ },
284
+ );
285
+ if (scopeSpecifierMatching) {
286
+ const scopeMappings = scopes[scopeSpecifierMatching];
287
+ const mappingFromScopes = applyMappings(
288
+ scopeMappings,
289
+ specifierNormalized,
290
+ scopeSpecifierMatching,
291
+ onImportMapping,
292
+ );
293
+ if (mappingFromScopes !== null) {
294
+ return mappingFromScopes
295
+ }
296
+ }
297
+ }
298
+
299
+ const { imports } = importMap;
300
+ if (imports) {
301
+ const mappingFromImports = applyMappings(
302
+ imports,
303
+ specifierNormalized,
304
+ undefined,
305
+ onImportMapping,
306
+ );
307
+ if (mappingFromImports !== null) {
308
+ return mappingFromImports
309
+ }
310
+ }
311
+
312
+ if (specifierUrl) {
313
+ return specifierUrl
314
+ }
315
+
316
+ throw createBareSpecifierError({ specifier, importer })
317
+ };
318
+
319
+ const applyMappings = (
320
+ mappings,
321
+ specifierNormalized,
322
+ scope,
323
+ onImportMapping,
324
+ ) => {
325
+ const specifierCandidates = Object.keys(mappings);
326
+
327
+ let i = 0;
328
+ while (i < specifierCandidates.length) {
329
+ const specifierCandidate = specifierCandidates[i];
330
+ i++;
331
+ if (specifierCandidate === specifierNormalized) {
332
+ const address = mappings[specifierCandidate];
333
+ onImportMapping({
334
+ scope,
335
+ from: specifierCandidate,
336
+ to: address,
337
+ before: specifierNormalized,
338
+ after: address,
339
+ });
340
+ return address
341
+ }
342
+ if (specifierIsPrefixOf(specifierCandidate, specifierNormalized)) {
343
+ const address = mappings[specifierCandidate];
344
+ const afterSpecifier = specifierNormalized.slice(
345
+ specifierCandidate.length,
346
+ );
347
+ const addressFinal = tryUrlResolution(afterSpecifier, address);
348
+ onImportMapping({
349
+ scope,
350
+ from: specifierCandidate,
351
+ to: address,
352
+ before: specifierNormalized,
353
+ after: addressFinal,
354
+ });
355
+ return addressFinal
356
+ }
357
+ }
358
+
359
+ return null
360
+ };
361
+
362
+ const specifierIsPrefixOf = (specifierHref, href) => {
363
+ return (
364
+ specifierHref[specifierHref.length - 1] === "/" &&
365
+ href.startsWith(specifierHref)
366
+ )
367
+ };
368
+
369
+ const sortImports = (imports) => {
370
+ const mappingsSorted = {};
371
+
372
+ Object.keys(imports)
373
+ .sort(compareLengthOrLocaleCompare)
374
+ .forEach((name) => {
375
+ mappingsSorted[name] = imports[name];
376
+ });
377
+
378
+ return mappingsSorted
379
+ };
380
+
381
+ const sortScopes = (scopes) => {
382
+ const scopesSorted = {};
383
+
384
+ Object.keys(scopes)
385
+ .sort(compareLengthOrLocaleCompare)
386
+ .forEach((scopeSpecifier) => {
387
+ scopesSorted[scopeSpecifier] = sortImports(scopes[scopeSpecifier]);
388
+ });
389
+
390
+ return scopesSorted
391
+ };
392
+
393
+ const compareLengthOrLocaleCompare = (a, b) => {
394
+ return b.length - a.length || a.localeCompare(b)
395
+ };
396
+
397
+ const normalizeImportMap = (importMap, baseUrl) => {
398
+ assertImportMap(importMap);
399
+
400
+ if (!isStringOrUrl(baseUrl)) {
401
+ throw new TypeError(formulateBaseUrlMustBeStringOrUrl({ baseUrl }))
402
+ }
403
+
404
+ const { imports, scopes } = importMap;
405
+
406
+ return {
407
+ imports: imports ? normalizeMappings(imports, baseUrl) : undefined,
408
+ scopes: scopes ? normalizeScopes(scopes, baseUrl) : undefined,
409
+ }
410
+ };
411
+
412
+ const isStringOrUrl = (value) => {
413
+ if (typeof value === "string") {
414
+ return true
415
+ }
416
+
417
+ if (typeof URL === "function" && value instanceof URL) {
418
+ return true
419
+ }
420
+
421
+ return false
422
+ };
423
+
424
+ const normalizeMappings = (mappings, baseUrl) => {
425
+ const mappingsNormalized = {};
426
+
427
+ Object.keys(mappings).forEach((specifier) => {
428
+ const address = mappings[specifier];
429
+
430
+ if (typeof address !== "string") {
431
+ console.warn(
432
+ formulateAddressMustBeAString({
433
+ address,
434
+ specifier,
435
+ }),
436
+ );
437
+ return
438
+ }
439
+
440
+ const specifierResolved = resolveSpecifier(specifier, baseUrl) || specifier;
441
+
442
+ const addressUrl = tryUrlResolution(address, baseUrl);
443
+ if (addressUrl === null) {
444
+ console.warn(
445
+ formulateAdressResolutionFailed({
446
+ address,
447
+ baseUrl,
448
+ specifier,
449
+ }),
450
+ );
451
+ return
452
+ }
453
+
454
+ if (specifier.endsWith("/") && !addressUrl.endsWith("/")) {
455
+ console.warn(
456
+ formulateAddressUrlRequiresTrailingSlash({
457
+ address,
458
+ specifier,
459
+ }),
460
+ );
461
+ return
462
+ }
463
+ mappingsNormalized[specifierResolved] = addressUrl;
464
+ });
465
+
466
+ return sortImports(mappingsNormalized)
467
+ };
468
+
469
+ const normalizeScopes = (scopes, baseUrl) => {
470
+ const scopesNormalized = {};
471
+
472
+ Object.keys(scopes).forEach((scopeSpecifier) => {
473
+ const scopeMappings = scopes[scopeSpecifier];
474
+ const scopeUrl = tryUrlResolution(scopeSpecifier, baseUrl);
475
+ if (scopeUrl === null) {
476
+ console.warn(
477
+ formulateScopeResolutionFailed({
478
+ scope: scopeSpecifier,
479
+ baseUrl,
480
+ }),
481
+ );
482
+ return
483
+ }
484
+ const scopeValueNormalized = normalizeMappings(scopeMappings, baseUrl);
485
+ scopesNormalized[scopeUrl] = scopeValueNormalized;
486
+ });
487
+
488
+ return sortScopes(scopesNormalized)
489
+ };
490
+
491
+ const formulateBaseUrlMustBeStringOrUrl = ({
492
+ baseUrl,
493
+ }) => `baseUrl must be a string or an url.
494
+ --- base url ---
495
+ ${baseUrl}`;
496
+
497
+ const formulateAddressMustBeAString = ({
498
+ specifier,
499
+ address,
500
+ }) => `Address must be a string.
501
+ --- address ---
502
+ ${address}
503
+ --- specifier ---
504
+ ${specifier}`;
505
+
506
+ const formulateAdressResolutionFailed = ({
507
+ address,
508
+ baseUrl,
509
+ specifier,
510
+ }) => `Address url resolution failed.
511
+ --- address ---
512
+ ${address}
513
+ --- base url ---
514
+ ${baseUrl}
515
+ --- specifier ---
516
+ ${specifier}`;
517
+
518
+ const formulateAddressUrlRequiresTrailingSlash = ({
519
+ addressURL,
520
+ address,
521
+ specifier,
522
+ }) => `Address must end with /.
523
+ --- address url ---
524
+ ${addressURL}
525
+ --- address ---
526
+ ${address}
527
+ --- specifier ---
528
+ ${specifier}`;
529
+
530
+ const formulateScopeResolutionFailed = ({
531
+ scope,
532
+ baseUrl,
533
+ }) => `Scope url resolution failed.
534
+ --- scope ---
535
+ ${scope}
536
+ --- base url ---
537
+ ${baseUrl}`;
538
+
539
+ const pathnameToExtension = (pathname) => {
540
+ const slashLastIndex = pathname.lastIndexOf("/");
541
+ if (slashLastIndex !== -1) {
542
+ pathname = pathname.slice(slashLastIndex + 1);
543
+ }
544
+
545
+ const dotLastIndex = pathname.lastIndexOf(".");
546
+ if (dotLastIndex === -1) return ""
547
+ // if (dotLastIndex === pathname.length - 1) return ""
548
+ return pathname.slice(dotLastIndex)
549
+ };
550
+
551
+ const resolveImport = ({
552
+ specifier,
553
+ importer,
554
+ importMap,
555
+ defaultExtension = false,
556
+ createBareSpecifierError,
557
+ onImportMapping = () => {},
558
+ }) => {
559
+ let url;
560
+ if (importMap) {
561
+ url = applyImportMap({
562
+ importMap,
563
+ specifier,
564
+ importer,
565
+ createBareSpecifierError,
566
+ onImportMapping,
567
+ });
568
+ } else {
569
+ url = resolveUrl(specifier, importer);
570
+ }
571
+
572
+ if (defaultExtension) {
573
+ url = applyDefaultExtension({ url, importer, defaultExtension });
574
+ }
575
+
576
+ return url
577
+ };
578
+
579
+ const applyDefaultExtension = ({ url, importer, defaultExtension }) => {
580
+ if (urlToPathname(url).endsWith("/")) {
581
+ return url
582
+ }
583
+
584
+ if (typeof defaultExtension === "string") {
585
+ const extension = pathnameToExtension(url);
586
+ if (extension === "") {
587
+ return `${url}${defaultExtension}`
588
+ }
589
+ return url
590
+ }
591
+
592
+ if (defaultExtension === true) {
593
+ const extension = pathnameToExtension(url);
594
+ if (extension === "" && importer) {
595
+ const importerPathname = urlToPathname(importer);
596
+ const importerExtension = pathnameToExtension(importerPathname);
597
+ return `${url}${importerExtension}`
598
+ }
599
+ }
600
+
601
+ return url
602
+ };
603
+
5
604
  let importMap;
6
605
 
7
606
  const cwdUrl = `${String(pathToFileURL(process.cwd()))}/`;
@@ -1,19 +1,19 @@
1
+ import { readGitHubWorkflowEnv, startGithubCheckRun } from "@jsenv/github-check-run";
1
2
  import process$1, { cpuUsage, memoryUsage } from "node:process";
2
- import os, { cpus, release, totalmem, availableParallelism, freemem } from "node:os";
3
+ import os, { cpus, totalmem, release, availableParallelism, freemem } from "node:os";
3
4
  import tty from "node:tty";
4
5
  import stringWidth from "string-width";
5
- import { pathToFileURL, fileURLToPath } from "node:url";
6
- import { readdir, chmod, stat, lstat, chmodSync, statSync, lstatSync, promises, readFile as readFile$1, unlinkSync, openSync, closeSync, readdirSync, rmdirSync, mkdirSync, readFileSync, writeFileSync as writeFileSync$1, unlink, rmdir, existsSync } from "node:fs";
7
- import { URL_META, filterV8Coverage } from "./js/v8_coverage.js";
8
- import { dirname } from "node:path";
9
- import crypto from "node:crypto";
10
- import { readGitHubWorkflowEnv, startGithubCheckRun } from "@jsenv/github-check-run";
11
6
  import { applyNodeEsmResolution } from "@jsenv/node-esm-resolution";
7
+ import { URL_META, filterV8Coverage } from "./js/v8_coverage.js";
8
+ import { readdir, chmod, stat, lstat, chmodSync, statSync, lstatSync, promises, readFile as readFile$1, readdirSync, openSync, closeSync, unlinkSync, rmdirSync, mkdirSync, readFileSync, writeFileSync as writeFileSync$1, unlink, rmdir, existsSync } from "node:fs";
12
9
  import { takeCoverage } from "node:v8";
13
10
  import stripAnsi from "strip-ansi";
11
+ import { pathToFileURL, fileURLToPath } from "node:url";
14
12
  import { createRequire } from "node:module";
15
13
  import { applyBabelPlugins } from "@jsenv/ast";
16
14
  import { createException } from "@jsenv/exception";
15
+ import crypto from "node:crypto";
16
+ import { dirname } from "node:path";
17
17
  import { createServer } from "node:net";
18
18
  import { spawn, spawnSync, fork } from "node:child_process";
19
19
  import { SOURCEMAP, generateSourcemapDataUrl } from "@jsenv/sourcemap";
@@ -3165,10 +3165,6 @@ const removeDirectoryNaive = (
3165
3165
  });
3166
3166
  };
3167
3167
 
3168
- process.platform === "win32";
3169
-
3170
- process.platform === "win32";
3171
-
3172
3168
  const ensureEmptyDirectorySync = (source) => {
3173
3169
  const stat = readEntryStatSync(source, {
3174
3170
  nullIfNotFound: true,
@@ -3223,14 +3219,6 @@ const ensureEmptyDirectory = async (source) => {
3223
3219
  );
3224
3220
  };
3225
3221
 
3226
- process.platform === "win32";
3227
-
3228
- process.platform === "linux";
3229
-
3230
- process.platform === "darwin";
3231
- process.platform === "linux";
3232
- process.platform === "freebsd";
3233
-
3234
3222
  const normalizeFileByFileCoveragePaths = (
3235
3223
  fileByFileCoverage,
3236
3224
  rootDirectoryUrl,
@@ -5976,7 +5964,6 @@ const ensureWebServerIsStarted = async (
5976
5964
  }
5977
5965
  if (webServer.command) {
5978
5966
  await startServerUsingCommand(webServer, {
5979
- ignoreProcessExecArgv,
5980
5967
  signal,
5981
5968
  allocatedMs,
5982
5969
  teardownCallbackSet,
@@ -7368,8 +7355,6 @@ const initJsSupervisorMiddleware = async (
7368
7355
  const url = request.url();
7369
7356
  if (url === fileServerUrl && urlToExtension(url) === ".html") {
7370
7357
  interceptHtmlToExecute({
7371
- url,
7372
- request,
7373
7358
  route,
7374
7359
  });
7375
7360
  return;
@@ -7377,14 +7362,13 @@ const initJsSupervisorMiddleware = async (
7377
7362
  if (inlineScriptContents.has(url)) {
7378
7363
  interceptInlineScript({
7379
7364
  url,
7380
- request,
7381
7365
  route,
7382
7366
  });
7383
7367
  return;
7384
7368
  }
7385
7369
  const fsServerUrl = new URL("/@fs/", webServer.origin);
7386
7370
  if (url.startsWith(fsServerUrl)) {
7387
- interceptFileSystemUrl({ url, request, route });
7371
+ interceptFileSystemUrl({ url, route });
7388
7372
  return;
7389
7373
  }
7390
7374
  route.fallback();
@@ -9868,7 +9852,6 @@ const execute = async ({
9868
9852
  measureMemoryUsage,
9869
9853
  onMeasureMemoryAvailable,
9870
9854
  collectPerformance,
9871
- collectCoverage,
9872
9855
  coverageTempDirectoryUrl,
9873
9856
  runtime,
9874
9857
  runtimeParams,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/test",
3
- "version": "3.6.1",
3
+ "version": "3.6.2",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,6 +27,10 @@
27
27
  "/dist/",
28
28
  "/src/"
29
29
  ],
30
+ "sideEffects": [
31
+ "./src/runtime_node/node_child_process_controlled.mjs",
32
+ "./src/runtime_node/node_worker_thread_controlled.mjs"
33
+ ],
30
34
  "scripts": {
31
35
  "build": "node --conditions=development ./scripts/build.mjs",
32
36
  "prepublishOnly": "npm run build"
@@ -37,17 +41,17 @@
37
41
  "dependencies": {
38
42
  "@c88/v8-coverage": "0.1.1",
39
43
  "@jsenv/abort": "4.3.1",
40
- "@jsenv/ast": "6.6.0",
44
+ "@jsenv/ast": "6.6.1",
41
45
  "@jsenv/exception": "1.1.5",
42
- "@jsenv/filesystem": "4.14.0",
43
- "@jsenv/github-check-run": "0.2.14",
46
+ "@jsenv/filesystem": "4.14.1",
47
+ "@jsenv/github-check-run": "0.2.15",
44
48
  "@jsenv/node-esm-resolution": "1.1.0",
45
- "@jsenv/humanize": "1.3.0",
46
- "@jsenv/plugin-supervisor": "1.6.7",
47
- "@jsenv/sourcemap": "1.3.0",
49
+ "@jsenv/humanize": "1.3.1",
50
+ "@jsenv/plugin-supervisor": "1.6.8",
51
+ "@jsenv/sourcemap": "1.3.1",
48
52
  "@jsenv/url-meta": "8.5.4",
49
- "@jsenv/urls": "2.6.1",
50
- "@jsenv/utils": "2.2.0",
53
+ "@jsenv/urls": "2.6.2",
54
+ "@jsenv/utils": "2.2.1",
51
55
  "he": "1.2.0",
52
56
  "istanbul-lib-coverage": "3.2.2",
53
57
  "istanbul-lib-instrument": "6.0.3",
@@ -1,602 +0,0 @@
1
- // duplicated from @jsenv/log to avoid the dependency
2
- const createDetailedMessage = (message, details = {}) => {
3
- let string = `${message}`;
4
-
5
- Object.keys(details).forEach((key) => {
6
- const value = details[key];
7
- string += `
8
- --- ${key} ---
9
- ${
10
- Array.isArray(value)
11
- ? value.join(`
12
- `)
13
- : value
14
- }`;
15
- });
16
-
17
- return string
18
- };
19
-
20
- const assertImportMap = (value) => {
21
- if (value === null) {
22
- throw new TypeError(`an importMap must be an object, got null`)
23
- }
24
-
25
- const type = typeof value;
26
- if (type !== "object") {
27
- throw new TypeError(`an importMap must be an object, received ${value}`)
28
- }
29
-
30
- if (Array.isArray(value)) {
31
- throw new TypeError(
32
- `an importMap must be an object, received array ${value}`,
33
- )
34
- }
35
- };
36
-
37
- const hasScheme = (string) => {
38
- return /^[a-zA-Z]{2,}:/.test(string)
39
- };
40
-
41
- const urlToScheme = (urlString) => {
42
- const colonIndex = urlString.indexOf(":");
43
- if (colonIndex === -1) return ""
44
- return urlString.slice(0, colonIndex)
45
- };
46
-
47
- const urlToPathname = (urlString) => {
48
- return ressourceToPathname(urlToRessource(urlString))
49
- };
50
-
51
- const urlToRessource = (urlString) => {
52
- const scheme = urlToScheme(urlString);
53
-
54
- if (scheme === "file") {
55
- return urlString.slice("file://".length)
56
- }
57
-
58
- if (scheme === "https" || scheme === "http") {
59
- // remove origin
60
- const afterProtocol = urlString.slice(scheme.length + "://".length);
61
- const pathnameSlashIndex = afterProtocol.indexOf("/", "://".length);
62
- return afterProtocol.slice(pathnameSlashIndex)
63
- }
64
-
65
- return urlString.slice(scheme.length + 1)
66
- };
67
-
68
- const ressourceToPathname = (ressource) => {
69
- const searchSeparatorIndex = ressource.indexOf("?");
70
- return searchSeparatorIndex === -1
71
- ? ressource
72
- : ressource.slice(0, searchSeparatorIndex)
73
- };
74
-
75
- const urlToOrigin = (urlString) => {
76
- const scheme = urlToScheme(urlString);
77
-
78
- if (scheme === "file") {
79
- return "file://"
80
- }
81
-
82
- if (scheme === "http" || scheme === "https") {
83
- const secondProtocolSlashIndex = scheme.length + "://".length;
84
- const pathnameSlashIndex = urlString.indexOf("/", secondProtocolSlashIndex);
85
-
86
- if (pathnameSlashIndex === -1) return urlString
87
- return urlString.slice(0, pathnameSlashIndex)
88
- }
89
-
90
- return urlString.slice(0, scheme.length + 1)
91
- };
92
-
93
- const pathnameToParentPathname = (pathname) => {
94
- const slashLastIndex = pathname.lastIndexOf("/");
95
- if (slashLastIndex === -1) {
96
- return "/"
97
- }
98
-
99
- return pathname.slice(0, slashLastIndex + 1)
100
- };
101
-
102
- // could be useful: https://url.spec.whatwg.org/#url-miscellaneous
103
-
104
-
105
- const resolveUrl = (specifier, baseUrl) => {
106
- if (baseUrl) {
107
- if (typeof baseUrl !== "string") {
108
- throw new TypeError(writeBaseUrlMustBeAString({ baseUrl, specifier }))
109
- }
110
- if (!hasScheme(baseUrl)) {
111
- throw new Error(writeBaseUrlMustBeAbsolute({ baseUrl, specifier }))
112
- }
113
- }
114
-
115
- if (hasScheme(specifier)) {
116
- return specifier
117
- }
118
-
119
- if (!baseUrl) {
120
- throw new Error(writeBaseUrlRequired({ baseUrl, specifier }))
121
- }
122
-
123
- // scheme relative
124
- if (specifier.slice(0, 2) === "//") {
125
- return `${urlToScheme(baseUrl)}:${specifier}`
126
- }
127
-
128
- // origin relative
129
- if (specifier[0] === "/") {
130
- return `${urlToOrigin(baseUrl)}${specifier}`
131
- }
132
-
133
- const baseOrigin = urlToOrigin(baseUrl);
134
- const basePathname = urlToPathname(baseUrl);
135
-
136
- if (specifier === ".") {
137
- const baseDirectoryPathname = pathnameToParentPathname(basePathname);
138
- return `${baseOrigin}${baseDirectoryPathname}`
139
- }
140
-
141
- // pathname relative inside
142
- if (specifier.slice(0, 2) === "./") {
143
- const baseDirectoryPathname = pathnameToParentPathname(basePathname);
144
- return `${baseOrigin}${baseDirectoryPathname}${specifier.slice(2)}`
145
- }
146
-
147
- // pathname relative outside
148
- if (specifier.slice(0, 3) === "../") {
149
- let unresolvedPathname = specifier;
150
- const importerFolders = basePathname.split("/");
151
- importerFolders.pop();
152
-
153
- while (unresolvedPathname.slice(0, 3) === "../") {
154
- unresolvedPathname = unresolvedPathname.slice(3);
155
- // when there is no folder left to resolved
156
- // we just ignore '../'
157
- if (importerFolders.length) {
158
- importerFolders.pop();
159
- }
160
- }
161
-
162
- const resolvedPathname = `${importerFolders.join(
163
- "/",
164
- )}/${unresolvedPathname}`;
165
- return `${baseOrigin}${resolvedPathname}`
166
- }
167
-
168
- // bare
169
- if (basePathname === "") {
170
- return `${baseOrigin}/${specifier}`
171
- }
172
- if (basePathname[basePathname.length] === "/") {
173
- return `${baseOrigin}${basePathname}${specifier}`
174
- }
175
- return `${baseOrigin}${pathnameToParentPathname(basePathname)}${specifier}`
176
- };
177
-
178
- const writeBaseUrlMustBeAString = ({
179
- baseUrl,
180
- specifier,
181
- }) => `baseUrl must be a string.
182
- --- base url ---
183
- ${baseUrl}
184
- --- specifier ---
185
- ${specifier}`;
186
-
187
- const writeBaseUrlMustBeAbsolute = ({
188
- baseUrl,
189
- specifier,
190
- }) => `baseUrl must be absolute.
191
- --- base url ---
192
- ${baseUrl}
193
- --- specifier ---
194
- ${specifier}`;
195
-
196
- const writeBaseUrlRequired = ({
197
- baseUrl,
198
- specifier,
199
- }) => `baseUrl required to resolve relative specifier.
200
- --- base url ---
201
- ${baseUrl}
202
- --- specifier ---
203
- ${specifier}`;
204
-
205
- const tryUrlResolution = (string, url) => {
206
- const result = resolveUrl(string, url);
207
- return hasScheme(result) ? result : null
208
- };
209
-
210
- const resolveSpecifier = (specifier, importer) => {
211
- if (
212
- specifier === "." ||
213
- specifier[0] === "/" ||
214
- specifier.startsWith("./") ||
215
- specifier.startsWith("../")
216
- ) {
217
- return resolveUrl(specifier, importer)
218
- }
219
-
220
- if (hasScheme(specifier)) {
221
- return specifier
222
- }
223
-
224
- return null
225
- };
226
-
227
- const applyImportMap = ({
228
- importMap,
229
- specifier,
230
- importer,
231
- createBareSpecifierError = ({ specifier, importer }) => {
232
- return new Error(
233
- createDetailedMessage(`Unmapped bare specifier.`, {
234
- specifier,
235
- importer,
236
- }),
237
- )
238
- },
239
- onImportMapping = () => {},
240
- }) => {
241
- assertImportMap(importMap);
242
- if (typeof specifier !== "string") {
243
- throw new TypeError(
244
- createDetailedMessage("specifier must be a string.", {
245
- specifier,
246
- importer,
247
- }),
248
- )
249
- }
250
- if (importer) {
251
- if (typeof importer !== "string") {
252
- throw new TypeError(
253
- createDetailedMessage("importer must be a string.", {
254
- importer,
255
- specifier,
256
- }),
257
- )
258
- }
259
- if (!hasScheme(importer)) {
260
- throw new Error(
261
- createDetailedMessage(`importer must be an absolute url.`, {
262
- importer,
263
- specifier,
264
- }),
265
- )
266
- }
267
- }
268
-
269
- const specifierUrl = resolveSpecifier(specifier, importer);
270
- const specifierNormalized = specifierUrl || specifier;
271
-
272
- const { scopes } = importMap;
273
- if (scopes && importer) {
274
- const scopeSpecifierMatching = Object.keys(scopes).find(
275
- (scopeSpecifier) => {
276
- return (
277
- scopeSpecifier === importer ||
278
- specifierIsPrefixOf(scopeSpecifier, importer)
279
- )
280
- },
281
- );
282
- if (scopeSpecifierMatching) {
283
- const scopeMappings = scopes[scopeSpecifierMatching];
284
- const mappingFromScopes = applyMappings(
285
- scopeMappings,
286
- specifierNormalized,
287
- scopeSpecifierMatching,
288
- onImportMapping,
289
- );
290
- if (mappingFromScopes !== null) {
291
- return mappingFromScopes
292
- }
293
- }
294
- }
295
-
296
- const { imports } = importMap;
297
- if (imports) {
298
- const mappingFromImports = applyMappings(
299
- imports,
300
- specifierNormalized,
301
- undefined,
302
- onImportMapping,
303
- );
304
- if (mappingFromImports !== null) {
305
- return mappingFromImports
306
- }
307
- }
308
-
309
- if (specifierUrl) {
310
- return specifierUrl
311
- }
312
-
313
- throw createBareSpecifierError({ specifier, importer })
314
- };
315
-
316
- const applyMappings = (
317
- mappings,
318
- specifierNormalized,
319
- scope,
320
- onImportMapping,
321
- ) => {
322
- const specifierCandidates = Object.keys(mappings);
323
-
324
- let i = 0;
325
- while (i < specifierCandidates.length) {
326
- const specifierCandidate = specifierCandidates[i];
327
- i++;
328
- if (specifierCandidate === specifierNormalized) {
329
- const address = mappings[specifierCandidate];
330
- onImportMapping({
331
- scope,
332
- from: specifierCandidate,
333
- to: address,
334
- before: specifierNormalized,
335
- after: address,
336
- });
337
- return address
338
- }
339
- if (specifierIsPrefixOf(specifierCandidate, specifierNormalized)) {
340
- const address = mappings[specifierCandidate];
341
- const afterSpecifier = specifierNormalized.slice(
342
- specifierCandidate.length,
343
- );
344
- const addressFinal = tryUrlResolution(afterSpecifier, address);
345
- onImportMapping({
346
- scope,
347
- from: specifierCandidate,
348
- to: address,
349
- before: specifierNormalized,
350
- after: addressFinal,
351
- });
352
- return addressFinal
353
- }
354
- }
355
-
356
- return null
357
- };
358
-
359
- const specifierIsPrefixOf = (specifierHref, href) => {
360
- return (
361
- specifierHref[specifierHref.length - 1] === "/" &&
362
- href.startsWith(specifierHref)
363
- )
364
- };
365
-
366
- const sortImports = (imports) => {
367
- const mappingsSorted = {};
368
-
369
- Object.keys(imports)
370
- .sort(compareLengthOrLocaleCompare)
371
- .forEach((name) => {
372
- mappingsSorted[name] = imports[name];
373
- });
374
-
375
- return mappingsSorted
376
- };
377
-
378
- const sortScopes = (scopes) => {
379
- const scopesSorted = {};
380
-
381
- Object.keys(scopes)
382
- .sort(compareLengthOrLocaleCompare)
383
- .forEach((scopeSpecifier) => {
384
- scopesSorted[scopeSpecifier] = sortImports(scopes[scopeSpecifier]);
385
- });
386
-
387
- return scopesSorted
388
- };
389
-
390
- const compareLengthOrLocaleCompare = (a, b) => {
391
- return b.length - a.length || a.localeCompare(b)
392
- };
393
-
394
- const normalizeImportMap = (importMap, baseUrl) => {
395
- assertImportMap(importMap);
396
-
397
- if (!isStringOrUrl(baseUrl)) {
398
- throw new TypeError(formulateBaseUrlMustBeStringOrUrl({ baseUrl }))
399
- }
400
-
401
- const { imports, scopes } = importMap;
402
-
403
- return {
404
- imports: imports ? normalizeMappings(imports, baseUrl) : undefined,
405
- scopes: scopes ? normalizeScopes(scopes, baseUrl) : undefined,
406
- }
407
- };
408
-
409
- const isStringOrUrl = (value) => {
410
- if (typeof value === "string") {
411
- return true
412
- }
413
-
414
- if (typeof URL === "function" && value instanceof URL) {
415
- return true
416
- }
417
-
418
- return false
419
- };
420
-
421
- const normalizeMappings = (mappings, baseUrl) => {
422
- const mappingsNormalized = {};
423
-
424
- Object.keys(mappings).forEach((specifier) => {
425
- const address = mappings[specifier];
426
-
427
- if (typeof address !== "string") {
428
- console.warn(
429
- formulateAddressMustBeAString({
430
- address,
431
- specifier,
432
- }),
433
- );
434
- return
435
- }
436
-
437
- const specifierResolved = resolveSpecifier(specifier, baseUrl) || specifier;
438
-
439
- const addressUrl = tryUrlResolution(address, baseUrl);
440
- if (addressUrl === null) {
441
- console.warn(
442
- formulateAdressResolutionFailed({
443
- address,
444
- baseUrl,
445
- specifier,
446
- }),
447
- );
448
- return
449
- }
450
-
451
- if (specifier.endsWith("/") && !addressUrl.endsWith("/")) {
452
- console.warn(
453
- formulateAddressUrlRequiresTrailingSlash({
454
- addressUrl,
455
- address,
456
- specifier,
457
- }),
458
- );
459
- return
460
- }
461
- mappingsNormalized[specifierResolved] = addressUrl;
462
- });
463
-
464
- return sortImports(mappingsNormalized)
465
- };
466
-
467
- const normalizeScopes = (scopes, baseUrl) => {
468
- const scopesNormalized = {};
469
-
470
- Object.keys(scopes).forEach((scopeSpecifier) => {
471
- const scopeMappings = scopes[scopeSpecifier];
472
- const scopeUrl = tryUrlResolution(scopeSpecifier, baseUrl);
473
- if (scopeUrl === null) {
474
- console.warn(
475
- formulateScopeResolutionFailed({
476
- scope: scopeSpecifier,
477
- baseUrl,
478
- }),
479
- );
480
- return
481
- }
482
- const scopeValueNormalized = normalizeMappings(scopeMappings, baseUrl);
483
- scopesNormalized[scopeUrl] = scopeValueNormalized;
484
- });
485
-
486
- return sortScopes(scopesNormalized)
487
- };
488
-
489
- const formulateBaseUrlMustBeStringOrUrl = ({
490
- baseUrl,
491
- }) => `baseUrl must be a string or an url.
492
- --- base url ---
493
- ${baseUrl}`;
494
-
495
- const formulateAddressMustBeAString = ({
496
- specifier,
497
- address,
498
- }) => `Address must be a string.
499
- --- address ---
500
- ${address}
501
- --- specifier ---
502
- ${specifier}`;
503
-
504
- const formulateAdressResolutionFailed = ({
505
- address,
506
- baseUrl,
507
- specifier,
508
- }) => `Address url resolution failed.
509
- --- address ---
510
- ${address}
511
- --- base url ---
512
- ${baseUrl}
513
- --- specifier ---
514
- ${specifier}`;
515
-
516
- const formulateAddressUrlRequiresTrailingSlash = ({
517
- addressURL,
518
- address,
519
- specifier,
520
- }) => `Address must end with /.
521
- --- address url ---
522
- ${addressURL}
523
- --- address ---
524
- ${address}
525
- --- specifier ---
526
- ${specifier}`;
527
-
528
- const formulateScopeResolutionFailed = ({
529
- scope,
530
- baseUrl,
531
- }) => `Scope url resolution failed.
532
- --- scope ---
533
- ${scope}
534
- --- base url ---
535
- ${baseUrl}`;
536
-
537
- const pathnameToExtension = (pathname) => {
538
- const slashLastIndex = pathname.lastIndexOf("/");
539
- if (slashLastIndex !== -1) {
540
- pathname = pathname.slice(slashLastIndex + 1);
541
- }
542
-
543
- const dotLastIndex = pathname.lastIndexOf(".");
544
- if (dotLastIndex === -1) return ""
545
- // if (dotLastIndex === pathname.length - 1) return ""
546
- return pathname.slice(dotLastIndex)
547
- };
548
-
549
- const resolveImport = ({
550
- specifier,
551
- importer,
552
- importMap,
553
- defaultExtension = false,
554
- createBareSpecifierError,
555
- onImportMapping = () => {},
556
- }) => {
557
- let url;
558
- if (importMap) {
559
- url = applyImportMap({
560
- importMap,
561
- specifier,
562
- importer,
563
- createBareSpecifierError,
564
- onImportMapping,
565
- });
566
- } else {
567
- url = resolveUrl(specifier, importer);
568
- }
569
-
570
- if (defaultExtension) {
571
- url = applyDefaultExtension({ url, importer, defaultExtension });
572
- }
573
-
574
- return url
575
- };
576
-
577
- const applyDefaultExtension = ({ url, importer, defaultExtension }) => {
578
- if (urlToPathname(url).endsWith("/")) {
579
- return url
580
- }
581
-
582
- if (typeof defaultExtension === "string") {
583
- const extension = pathnameToExtension(url);
584
- if (extension === "") {
585
- return `${url}${defaultExtension}`
586
- }
587
- return url
588
- }
589
-
590
- if (defaultExtension === true) {
591
- const extension = pathnameToExtension(url);
592
- if (extension === "" && importer) {
593
- const importerPathname = urlToPathname(importer);
594
- const importerExtension = pathnameToExtension(importerPathname);
595
- return `${url}${importerExtension}`
596
- }
597
- }
598
-
599
- return url
600
- };
601
-
602
- export { normalizeImportMap, resolveImport };