@jsenv/test 3.5.45 → 3.6.0

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,607 +1,7 @@
1
+ import { normalizeImportMap, resolveImport } from "./vendors.js";
1
2
  import { readFileSync } from "node:fs";
2
3
  import { pathToFileURL } from "node:url";
3
4
 
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
- addressUrl,
458
- address,
459
- specifier,
460
- }),
461
- );
462
- return
463
- }
464
- mappingsNormalized[specifierResolved] = addressUrl;
465
- });
466
-
467
- return sortImports(mappingsNormalized)
468
- };
469
-
470
- const normalizeScopes = (scopes, baseUrl) => {
471
- const scopesNormalized = {};
472
-
473
- Object.keys(scopes).forEach((scopeSpecifier) => {
474
- const scopeMappings = scopes[scopeSpecifier];
475
- const scopeUrl = tryUrlResolution(scopeSpecifier, baseUrl);
476
- if (scopeUrl === null) {
477
- console.warn(
478
- formulateScopeResolutionFailed({
479
- scope: scopeSpecifier,
480
- baseUrl,
481
- }),
482
- );
483
- return
484
- }
485
- const scopeValueNormalized = normalizeMappings(scopeMappings, baseUrl);
486
- scopesNormalized[scopeUrl] = scopeValueNormalized;
487
- });
488
-
489
- return sortScopes(scopesNormalized)
490
- };
491
-
492
- const formulateBaseUrlMustBeStringOrUrl = ({
493
- baseUrl,
494
- }) => `baseUrl must be a string or an url.
495
- --- base url ---
496
- ${baseUrl}`;
497
-
498
- const formulateAddressMustBeAString = ({
499
- specifier,
500
- address,
501
- }) => `Address must be a string.
502
- --- address ---
503
- ${address}
504
- --- specifier ---
505
- ${specifier}`;
506
-
507
- const formulateAdressResolutionFailed = ({
508
- address,
509
- baseUrl,
510
- specifier,
511
- }) => `Address url resolution failed.
512
- --- address ---
513
- ${address}
514
- --- base url ---
515
- ${baseUrl}
516
- --- specifier ---
517
- ${specifier}`;
518
-
519
- const formulateAddressUrlRequiresTrailingSlash = ({
520
- addressURL,
521
- address,
522
- specifier,
523
- }) => `Address must end with /.
524
- --- address url ---
525
- ${addressURL}
526
- --- address ---
527
- ${address}
528
- --- specifier ---
529
- ${specifier}`;
530
-
531
- const formulateScopeResolutionFailed = ({
532
- scope,
533
- baseUrl,
534
- }) => `Scope url resolution failed.
535
- --- scope ---
536
- ${scope}
537
- --- base url ---
538
- ${baseUrl}`;
539
-
540
- const pathnameToExtension = (pathname) => {
541
- const slashLastIndex = pathname.lastIndexOf("/");
542
- if (slashLastIndex !== -1) {
543
- pathname = pathname.slice(slashLastIndex + 1);
544
- }
545
-
546
- const dotLastIndex = pathname.lastIndexOf(".");
547
- if (dotLastIndex === -1) return ""
548
- // if (dotLastIndex === pathname.length - 1) return ""
549
- return pathname.slice(dotLastIndex)
550
- };
551
-
552
- const resolveImport = ({
553
- specifier,
554
- importer,
555
- importMap,
556
- defaultExtension = false,
557
- createBareSpecifierError,
558
- onImportMapping = () => {},
559
- }) => {
560
- let url;
561
- if (importMap) {
562
- url = applyImportMap({
563
- importMap,
564
- specifier,
565
- importer,
566
- createBareSpecifierError,
567
- onImportMapping,
568
- });
569
- } else {
570
- url = resolveUrl(specifier, importer);
571
- }
572
-
573
- if (defaultExtension) {
574
- url = applyDefaultExtension({ url, importer, defaultExtension });
575
- }
576
-
577
- return url
578
- };
579
-
580
- const applyDefaultExtension = ({ url, importer, defaultExtension }) => {
581
- if (urlToPathname(url).endsWith("/")) {
582
- return url
583
- }
584
-
585
- if (typeof defaultExtension === "string") {
586
- const extension = pathnameToExtension(url);
587
- if (extension === "") {
588
- return `${url}${defaultExtension}`
589
- }
590
- return url
591
- }
592
-
593
- if (defaultExtension === true) {
594
- const extension = pathnameToExtension(url);
595
- if (extension === "" && importer) {
596
- const importerPathname = urlToPathname(importer);
597
- const importerExtension = pathnameToExtension(importerPathname);
598
- return `${url}${importerExtension}`
599
- }
600
- }
601
-
602
- return url
603
- };
604
-
605
5
  let importMap;
606
6
 
607
7
  const cwdUrl = `${String(pathToFileURL(process.cwd()))}/`;
@@ -0,0 +1,3 @@
1
+ import { register } from "node:module";
2
+
3
+ register(new URL("./importmap_hooks.mjs", import.meta.url));
@@ -1,6 +1,6 @@
1
1
  import { createException } from "@jsenv/exception";
2
2
  import { memoryUsage } from "node:process";
3
- import { executeUsingDynamicImport } from "./js/execute_using_dynamic_import.js";
3
+ import { executeUsingDynamicImport } from "./execute_using_dynamic_import.js";
4
4
  import "node:fs";
5
5
  import "node:perf_hooks";
6
6
  import "node:inspector";
@@ -3,7 +3,7 @@ import { memoryUsage } from "node:process";
3
3
  import { setFlagsFromString } from "node:v8";
4
4
  import { runInNewContext } from "node:vm";
5
5
  import { parentPort } from "node:worker_threads";
6
- import { executeUsingDynamicImport } from "./js/execute_using_dynamic_import.js";
6
+ import { executeUsingDynamicImport } from "./execute_using_dynamic_import.js";
7
7
  import "node:fs";
8
8
  import "node:perf_hooks";
9
9
  import "node:inspector";