@jsenv/core 34.3.0 → 35.0.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.
Files changed (74) hide show
  1. package/README.md +2 -9
  2. package/dist/js/ws.js +1 -145
  3. package/dist/{jsenv.js → jsenv_core.js} +1056 -3850
  4. package/package.json +8 -23
  5. package/src/build/build.js +2 -2
  6. package/src/dev/file_service.js +8 -8
  7. package/src/dev/start_dev_server.js +3 -3
  8. package/src/dev/user_agent.js +1 -1
  9. package/src/main.js +0 -23
  10. package/src/plugins/supervisor/jsenv_plugin_supervisor.js +1 -1
  11. package/src/plugins/transpilation/babel/require_babel_plugin.js +1 -1
  12. package/src/plugins/transpilation/js_module_fallback/convert_js_module_to_js_classic.js +1 -1
  13. package/dist/controllable_child_process.mjs +0 -129
  14. package/dist/controllable_worker_thread.mjs +0 -91
  15. package/dist/importmap_node_loader.mjs +0 -49
  16. package/dist/js/execute_using_dynamic_import.js +0 -850
  17. package/dist/js/resolveImport.js +0 -504
  18. package/dist/js/v8_coverage.js +0 -508
  19. package/dist/no_experimental_warnings.cjs +0 -8
  20. package/src/execute/execute.js +0 -111
  21. package/src/execute/run.js +0 -161
  22. package/src/execute/runtimes/browsers/chromium.js +0 -10
  23. package/src/execute/runtimes/browsers/firefox.js +0 -9
  24. package/src/execute/runtimes/browsers/from_playwright.js +0 -574
  25. package/src/execute/runtimes/browsers/middleware_istanbul.js +0 -65
  26. package/src/execute/runtimes/browsers/middleware_js_supervisor.js +0 -100
  27. package/src/execute/runtimes/browsers/webkit.js +0 -26
  28. package/src/execute/runtimes/node/child_exec_options.js +0 -166
  29. package/src/execute/runtimes/node/controllable_child_process.mjs +0 -135
  30. package/src/execute/runtimes/node/controllable_worker_thread.mjs +0 -103
  31. package/src/execute/runtimes/node/exec_options.js +0 -57
  32. package/src/execute/runtimes/node/execute_using_dynamic_import.js +0 -55
  33. package/src/execute/runtimes/node/exit_codes.js +0 -9
  34. package/src/execute/runtimes/node/importmap_node_loader.mjs +0 -51
  35. package/src/execute/runtimes/node/importmap_node_loader_file_url.js +0 -4
  36. package/src/execute/runtimes/node/kill_process_tree.js +0 -76
  37. package/src/execute/runtimes/node/no_experimental_warnings.cjs +0 -12
  38. package/src/execute/runtimes/node/no_experimental_warnings_file_url.js +0 -4
  39. package/src/execute/runtimes/node/node_child_process.js +0 -363
  40. package/src/execute/runtimes/node/node_execution_performance.js +0 -67
  41. package/src/execute/runtimes/node/node_worker_thread.js +0 -295
  42. package/src/execute/runtimes/node/profiler_v8_coverage.js +0 -56
  43. package/src/execute/runtimes/readme.md +0 -13
  44. package/src/execute/web_server_param.js +0 -74
  45. package/src/test/coverage/babel_plugin_instrument.js +0 -48
  46. package/src/test/coverage/coverage_reporter_html_directory.js +0 -32
  47. package/src/test/coverage/coverage_reporter_json_file.js +0 -17
  48. package/src/test/coverage/coverage_reporter_text_log.js +0 -19
  49. package/src/test/coverage/empty_coverage_factory.js +0 -52
  50. package/src/test/coverage/file_by_file_coverage.js +0 -25
  51. package/src/test/coverage/istanbul_coverage_composition.js +0 -28
  52. package/src/test/coverage/istanbul_coverage_map_from_coverage.js +0 -16
  53. package/src/test/coverage/list_files_not_covered.js +0 -15
  54. package/src/test/coverage/missing_coverage.js +0 -41
  55. package/src/test/coverage/report_to_coverage.js +0 -198
  56. package/src/test/coverage/v8_and_istanbul.js +0 -37
  57. package/src/test/coverage/v8_coverage.js +0 -26
  58. package/src/test/coverage/v8_coverage_composition.js +0 -24
  59. package/src/test/coverage/v8_coverage_node_directory.js +0 -85
  60. package/src/test/coverage/v8_coverage_to_istanbul.js +0 -99
  61. package/src/test/execute_steps.js +0 -425
  62. package/src/test/execute_test_plan.js +0 -372
  63. package/src/test/execution_colors.js +0 -10
  64. package/src/test/execution_steps.js +0 -65
  65. package/src/test/gc.js +0 -9
  66. package/src/test/logs_file_execution.js +0 -427
  67. package/src/test/logs_file_execution.test.mjs +0 -41
  68. package/src/test/readme.md +0 -3
  69. /package/src/{basic_fetch.js → helpers/basic_fetch.js} +0 -0
  70. /package/src/{lookup_package_directory.js → helpers/lookup_package_directory.js} +0 -0
  71. /package/src/{ping_server.js → helpers/ping_server.js} +0 -0
  72. /package/src/{require_from_jsenv.js → helpers/require_from_jsenv.js} +0 -0
  73. /package/src/{watch_source_files.js → helpers/watch_source_files.js} +0 -0
  74. /package/src/{web_url_converter.js → helpers/web_url_converter.js} +0 -0
@@ -1,850 +0,0 @@
1
- import { writeFileSync } from "node:fs";
2
- import { Session } from "node:inspector";
3
- import { PerformanceObserver, performance } from "node:perf_hooks";
4
-
5
- // https://developer.mozilla.org/en-US/docs/Glossary/Primitive
6
-
7
- const isComposite = value => {
8
- if (value === null) {
9
- return false;
10
- }
11
- const type = typeof value;
12
- if (type === "object") {
13
- return true;
14
- }
15
- if (type === "function") {
16
- return true;
17
- }
18
- return false;
19
- };
20
-
21
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap
22
- const compositeWellKnownMap = new WeakMap();
23
- const primitiveWellKnownMap = new Map();
24
- const getCompositeGlobalPath = value => compositeWellKnownMap.get(value);
25
- const getPrimitiveGlobalPath = value => primitiveWellKnownMap.get(value);
26
- const visitGlobalObject = value => {
27
- const visitValue = (value, path) => {
28
- if (isComposite(value)) {
29
- // prevent infinite recursion
30
- if (compositeWellKnownMap.has(value)) {
31
- return;
32
- }
33
- compositeWellKnownMap.set(value, path);
34
- const visitProperty = property => {
35
- let descriptor;
36
- try {
37
- descriptor = Object.getOwnPropertyDescriptor(value, property);
38
- } catch (e) {
39
- if (e.name === "SecurityError") {
40
- return;
41
- }
42
- throw e;
43
- }
44
- if (!descriptor) {
45
- // it's apparently possible to have getOwnPropertyNames returning
46
- // a property that later returns a null descriptor
47
- // for instance window.showModalDialog in webkit 13.0
48
- return;
49
- }
50
-
51
- // do not trigger getter/setter
52
- if ("value" in descriptor) {
53
- const propertyValue = descriptor.value;
54
- visitValue(propertyValue, [...path, property]);
55
- }
56
- };
57
- Object.getOwnPropertyNames(value).forEach(name => visitProperty(name));
58
- Object.getOwnPropertySymbols(value).forEach(symbol => visitProperty(symbol));
59
- }
60
- primitiveWellKnownMap.set(value, path);
61
- return;
62
- };
63
- visitValue(value, []);
64
- };
65
- if (typeof window === "object") visitGlobalObject(window);
66
- if (typeof global === "object") visitGlobalObject(global);
67
-
68
- /**
69
- * transforms a javascript value into an object describing it.
70
- *
71
- */
72
- const decompose = (mainValue, {
73
- functionAllowed,
74
- prototypeStrict,
75
- ignoreSymbols
76
- }) => {
77
- const valueMap = {};
78
- const recipeArray = [];
79
- const valueToIdentifier = (value, path = []) => {
80
- if (!isComposite(value)) {
81
- const existingIdentifier = identifierForPrimitive(value);
82
- if (existingIdentifier !== undefined) {
83
- return existingIdentifier;
84
- }
85
- const identifier = identifierForNewValue(value);
86
- recipeArray[identifier] = primitiveToRecipe(value);
87
- return identifier;
88
- }
89
- if (typeof Promise === "function" && value instanceof Promise) {
90
- throw new Error(createPromiseAreNotSupportedMessage({
91
- path
92
- }));
93
- }
94
- if (typeof WeakSet === "function" && value instanceof WeakSet) {
95
- throw new Error(createWeakSetAreNotSupportedMessage({
96
- path
97
- }));
98
- }
99
- if (typeof WeakMap === "function" && value instanceof WeakMap) {
100
- throw new Error(createWeakMapAreNotSupportedMessage({
101
- path
102
- }));
103
- }
104
- if (typeof value === "function" && !functionAllowed) {
105
- throw new Error(createForbiddenFunctionMessage({
106
- path
107
- }));
108
- }
109
- const existingIdentifier = identifierForComposite(value);
110
- if (existingIdentifier !== undefined) {
111
- return existingIdentifier;
112
- }
113
- const identifier = identifierForNewValue(value);
114
- const compositeGlobalPath = getCompositeGlobalPath(value);
115
- if (compositeGlobalPath) {
116
- recipeArray[identifier] = createGlobalReferenceRecipe(compositeGlobalPath);
117
- return identifier;
118
- }
119
- const propertyDescriptionArray = [];
120
- Object.getOwnPropertyNames(value).forEach(propertyName => {
121
- const propertyDescriptor = Object.getOwnPropertyDescriptor(value, propertyName);
122
- const propertyNameIdentifier = valueToIdentifier(propertyName, [...path, propertyName]);
123
- const propertyDescription = computePropertyDescription(propertyDescriptor, propertyName, path);
124
- propertyDescriptionArray.push({
125
- propertyNameIdentifier,
126
- propertyDescription
127
- });
128
- });
129
- const symbolDescriptionArray = [];
130
- if (!ignoreSymbols) {
131
- Object.getOwnPropertySymbols(value).forEach(symbol => {
132
- const propertyDescriptor = Object.getOwnPropertyDescriptor(value, symbol);
133
- const symbolIdentifier = valueToIdentifier(symbol, [...path, `[${symbol.toString()}]`]);
134
- const propertyDescription = computePropertyDescription(propertyDescriptor, symbol, path);
135
- symbolDescriptionArray.push({
136
- symbolIdentifier,
137
- propertyDescription
138
- });
139
- });
140
- }
141
- const methodDescriptionArray = computeMethodDescriptionArray(value, path);
142
- const extensible = Object.isExtensible(value);
143
- recipeArray[identifier] = createCompositeRecipe({
144
- propertyDescriptionArray,
145
- symbolDescriptionArray,
146
- methodDescriptionArray,
147
- extensible
148
- });
149
- return identifier;
150
- };
151
- const computePropertyDescription = (propertyDescriptor, propertyNameOrSymbol, path) => {
152
- if (propertyDescriptor.set && !functionAllowed) {
153
- throw new Error(createForbiddenPropertySetterMessage({
154
- path,
155
- propertyNameOrSymbol
156
- }));
157
- }
158
- if (propertyDescriptor.get && !functionAllowed) {
159
- throw new Error(createForbiddenPropertyGetterMessage({
160
- path,
161
- propertyNameOrSymbol
162
- }));
163
- }
164
- return {
165
- configurable: propertyDescriptor.configurable,
166
- writable: propertyDescriptor.writable,
167
- enumerable: propertyDescriptor.enumerable,
168
- getIdentifier: "get" in propertyDescriptor ? valueToIdentifier(propertyDescriptor.get, [...path, String(propertyNameOrSymbol), "[[descriptor:get]]"]) : undefined,
169
- setIdentifier: "set" in propertyDescriptor ? valueToIdentifier(propertyDescriptor.set, [...path, String(propertyNameOrSymbol), "[[descriptor:set]]"]) : undefined,
170
- valueIdentifier: "value" in propertyDescriptor ? valueToIdentifier(propertyDescriptor.value, [...path, String(propertyNameOrSymbol), "[[descriptor:value]]"]) : undefined
171
- };
172
- };
173
- const computeMethodDescriptionArray = (value, path) => {
174
- const methodDescriptionArray = [];
175
- if (typeof Set === "function" && value instanceof Set) {
176
- const callArray = [];
177
- value.forEach((entryValue, index) => {
178
- const entryValueIdentifier = valueToIdentifier(entryValue, [...path, `[[SetEntryValue]]`, index]);
179
- callArray.push([entryValueIdentifier]);
180
- });
181
- methodDescriptionArray.push({
182
- methodNameIdentifier: valueToIdentifier("add"),
183
- callArray
184
- });
185
- }
186
- if (typeof Map === "function" && value instanceof Map) {
187
- const callArray = [];
188
- value.forEach((entryValue, entryKey) => {
189
- const entryKeyIdentifier = valueToIdentifier(entryKey, [...path, "[[MapEntryKey]]", entryKey]);
190
- const entryValueIdentifier = valueToIdentifier(entryValue, [...path, "[[MapEntryValue]]", entryValue]);
191
- callArray.push([entryKeyIdentifier, entryValueIdentifier]);
192
- });
193
- methodDescriptionArray.push({
194
- methodNameIdentifier: valueToIdentifier("set"),
195
- callArray
196
- });
197
- }
198
- return methodDescriptionArray;
199
- };
200
- const identifierForPrimitive = value => {
201
- return Object.keys(valueMap).find(existingIdentifier => {
202
- const existingValue = valueMap[existingIdentifier];
203
- if (Object.is(value, existingValue)) return true;
204
- return value === existingValue;
205
- });
206
- };
207
- const identifierForComposite = value => {
208
- return Object.keys(valueMap).find(existingIdentifier => {
209
- const existingValue = valueMap[existingIdentifier];
210
- return value === existingValue;
211
- });
212
- };
213
- const identifierForNewValue = value => {
214
- const identifier = nextIdentifier();
215
- valueMap[identifier] = value;
216
- return identifier;
217
- };
218
- let currentIdentifier = -1;
219
- const nextIdentifier = () => {
220
- const identifier = String(parseInt(currentIdentifier) + 1);
221
- currentIdentifier = identifier;
222
- return identifier;
223
- };
224
- const mainIdentifier = valueToIdentifier(mainValue);
225
-
226
- // prototype, important to keep after the whole structure was visited
227
- // so that we discover if any prototype is part of the value
228
- const prototypeValueToIdentifier = prototypeValue => {
229
- // prototype is null
230
- if (prototypeValue === null) {
231
- return valueToIdentifier(prototypeValue);
232
- }
233
-
234
- // prototype found somewhere already
235
- const prototypeExistingIdentifier = identifierForComposite(prototypeValue);
236
- if (prototypeExistingIdentifier !== undefined) {
237
- return prototypeExistingIdentifier;
238
- }
239
-
240
- // mark prototype as visited
241
- const prototypeIdentifier = identifierForNewValue(prototypeValue);
242
-
243
- // prototype is a global reference ?
244
- const prototypeGlobalPath = getCompositeGlobalPath(prototypeValue);
245
- if (prototypeGlobalPath) {
246
- recipeArray[prototypeIdentifier] = createGlobalReferenceRecipe(prototypeGlobalPath);
247
- return prototypeIdentifier;
248
- }
249
-
250
- // otherwise prototype is unknown
251
- if (prototypeStrict) {
252
- throw new Error(createUnknownPrototypeMessage({
253
- prototypeValue
254
- }));
255
- }
256
- return prototypeValueToIdentifier(Object.getPrototypeOf(prototypeValue));
257
- };
258
- const identifierForValueOf = (value, path = []) => {
259
- if (value instanceof Array) {
260
- return valueToIdentifier(value.length, [...path, "length"]);
261
- }
262
- if ("valueOf" in value === false) {
263
- return undefined;
264
- }
265
- if (typeof value.valueOf !== "function") {
266
- return undefined;
267
- }
268
- const valueOfReturnValue = value.valueOf();
269
- if (!isComposite(valueOfReturnValue)) {
270
- return valueToIdentifier(valueOfReturnValue, [...path, "valueOf()"]);
271
- }
272
- if (valueOfReturnValue === value) {
273
- return undefined;
274
- }
275
- throw new Error(createUnexpectedValueOfReturnValueMessage());
276
- };
277
- recipeArray.slice().forEach((recipe, index) => {
278
- if (recipe.type === "composite") {
279
- const value = valueMap[index];
280
- if (typeof value === "function") {
281
- const valueOfIdentifier = nextIdentifier();
282
- recipeArray[valueOfIdentifier] = {
283
- type: "primitive",
284
- value
285
- };
286
- recipe.valueOfIdentifier = valueOfIdentifier;
287
- return;
288
- }
289
- if (value instanceof RegExp) {
290
- const valueOfIdentifier = nextIdentifier();
291
- recipeArray[valueOfIdentifier] = {
292
- type: "primitive",
293
- value
294
- };
295
- recipe.valueOfIdentifier = valueOfIdentifier;
296
- return;
297
- }
298
-
299
- // valueOf, mandatory to uneval new Date(10) for instance.
300
- recipe.valueOfIdentifier = identifierForValueOf(value);
301
- const prototypeValue = Object.getPrototypeOf(value);
302
- recipe.prototypeIdentifier = prototypeValueToIdentifier(prototypeValue);
303
- }
304
- });
305
- return {
306
- recipeArray,
307
- mainIdentifier,
308
- valueMap
309
- };
310
- };
311
- const primitiveToRecipe = value => {
312
- if (typeof value === "symbol") {
313
- return symbolToRecipe(value);
314
- }
315
- return createPimitiveRecipe(value);
316
- };
317
- const symbolToRecipe = symbol => {
318
- const globalSymbolKey = Symbol.keyFor(symbol);
319
- if (globalSymbolKey !== undefined) {
320
- return createGlobalSymbolRecipe(globalSymbolKey);
321
- }
322
- const symbolGlobalPath = getPrimitiveGlobalPath(symbol);
323
- if (!symbolGlobalPath) {
324
- throw new Error(createUnknownSymbolMessage({
325
- symbol
326
- }));
327
- }
328
- return createGlobalReferenceRecipe(symbolGlobalPath);
329
- };
330
- const createPimitiveRecipe = value => {
331
- return {
332
- type: "primitive",
333
- value
334
- };
335
- };
336
- const createGlobalReferenceRecipe = path => {
337
- const recipe = {
338
- type: "global-reference",
339
- path
340
- };
341
- return recipe;
342
- };
343
- const createGlobalSymbolRecipe = key => {
344
- return {
345
- type: "global-symbol",
346
- key
347
- };
348
- };
349
- const createCompositeRecipe = ({
350
- prototypeIdentifier,
351
- valueOfIdentifier,
352
- propertyDescriptionArray,
353
- symbolDescriptionArray,
354
- methodDescriptionArray,
355
- extensible
356
- }) => {
357
- return {
358
- type: "composite",
359
- prototypeIdentifier,
360
- valueOfIdentifier,
361
- propertyDescriptionArray,
362
- symbolDescriptionArray,
363
- methodDescriptionArray,
364
- extensible
365
- };
366
- };
367
- const createPromiseAreNotSupportedMessage = ({
368
- path
369
- }) => {
370
- if (path.length === 0) {
371
- return `promise are not supported.`;
372
- }
373
- return `promise are not supported.
374
- promise found at: ${path.join("")}`;
375
- };
376
- const createWeakSetAreNotSupportedMessage = ({
377
- path
378
- }) => {
379
- if (path.length === 0) {
380
- return `weakSet are not supported.`;
381
- }
382
- return `weakSet are not supported.
383
- weakSet found at: ${path.join("")}`;
384
- };
385
- const createWeakMapAreNotSupportedMessage = ({
386
- path
387
- }) => {
388
- if (path.length === 0) {
389
- return `weakMap are not supported.`;
390
- }
391
- return `weakMap are not supported.
392
- weakMap found at: ${path.join("")}`;
393
- };
394
- const createForbiddenFunctionMessage = ({
395
- path
396
- }) => {
397
- if (path.length === 0) {
398
- return `function are not allowed.`;
399
- }
400
- return `function are not allowed.
401
- function found at: ${path.join("")}`;
402
- };
403
- const createForbiddenPropertyGetterMessage = ({
404
- path,
405
- propertyNameOrSymbol
406
- }) => `property getter are not allowed.
407
- getter found on property: ${String(propertyNameOrSymbol)}
408
- at: ${path.join("")}`;
409
- const createForbiddenPropertySetterMessage = ({
410
- path,
411
- propertyNameOrSymbol
412
- }) => `property setter are not allowed.
413
- setter found on property: ${String(propertyNameOrSymbol)}
414
- at: ${path.join("")}`;
415
- const createUnexpectedValueOfReturnValueMessage = () => `valueOf() must return a primitive of the object itself.`;
416
- const createUnknownSymbolMessage = ({
417
- symbol
418
- }) => `symbol must be global, like Symbol.iterator, or created using Symbol.for().
419
- symbol: ${symbol.toString()}`;
420
- const createUnknownPrototypeMessage = ({
421
- prototypeValue
422
- }) => `prototype must be global, like Object.prototype, or somewhere in the value.
423
- prototype constructor name: ${prototypeValue.constructor.name}`;
424
-
425
- // be carefull because this function is mutating recipe objects inside the recipeArray.
426
- // this is not an issue because each recipe object is not accessible from the outside
427
- // when used internally by uneval
428
- const sortRecipe = recipeArray => {
429
- const findInRecipePrototypeChain = (recipe, callback) => {
430
- let currentRecipe = recipe;
431
- // eslint-disable-next-line no-constant-condition
432
- while (true) {
433
- if (currentRecipe.type !== "composite") {
434
- break;
435
- }
436
- const prototypeIdentifier = currentRecipe.prototypeIdentifier;
437
- if (prototypeIdentifier === undefined) {
438
- break;
439
- }
440
- currentRecipe = recipeArray[prototypeIdentifier];
441
- if (callback(currentRecipe, prototypeIdentifier)) {
442
- return prototypeIdentifier;
443
- }
444
- }
445
- return undefined;
446
- };
447
- const recipeArrayOrdered = recipeArray.slice();
448
- recipeArrayOrdered.sort((leftRecipe, rightRecipe) => {
449
- const leftType = leftRecipe.type;
450
- const rightType = rightRecipe.type;
451
- if (leftType === "composite" && rightType === "composite") {
452
- const rightRecipeIsInLeftRecipePrototypeChain = findInRecipePrototypeChain(leftRecipe, recipeCandidate => recipeCandidate === rightRecipe);
453
- // if left recipe requires right recipe, left must be after right
454
- if (rightRecipeIsInLeftRecipePrototypeChain) {
455
- return 1;
456
- }
457
- const leftRecipeIsInRightRecipePrototypeChain = findInRecipePrototypeChain(rightRecipe, recipeCandidate => recipeCandidate === leftRecipe);
458
- // if right recipe requires left recipe, right must be after left
459
- if (leftRecipeIsInRightRecipePrototypeChain) {
460
- return -1;
461
- }
462
- }
463
- if (leftType !== rightType) {
464
- // if left is a composite, left must be after right
465
- if (leftType === "composite") {
466
- return 1;
467
- }
468
-
469
- // if right is a composite, right must be after left
470
- if (rightType === "composite") {
471
- return -1;
472
- }
473
- }
474
- const leftIndex = recipeArray.indexOf(leftRecipe);
475
- const rightIndex = recipeArray.indexOf(rightRecipe);
476
- // left was before right, don't change that
477
- if (leftIndex < rightIndex) {
478
- return -1;
479
- }
480
-
481
- // right was after left, don't change that
482
- return 1;
483
- });
484
- return recipeArrayOrdered;
485
- };
486
-
487
- // https://github.com/joliss/js-string-escape/blob/master/index.js
488
- // http://javascript.crockford.com/remedial.html
489
- const escapeString = value => {
490
- const string = String(value);
491
- let i = 0;
492
- const j = string.length;
493
- var escapedString = "";
494
- while (i < j) {
495
- const char = string[i];
496
- let escapedChar;
497
- if (char === '"' || char === "'" || char === "\\") {
498
- escapedChar = `\\${char}`;
499
- } else if (char === "\n") {
500
- escapedChar = "\\n";
501
- } else if (char === "\r") {
502
- escapedChar = "\\r";
503
- } else if (char === "\u2028") {
504
- escapedChar = "\\u2028";
505
- } else if (char === "\u2029") {
506
- escapedChar = "\\u2029";
507
- } else {
508
- escapedChar = char;
509
- }
510
- escapedString += escapedChar;
511
- i++;
512
- }
513
- return escapedString;
514
- };
515
-
516
- const uneval = (value, {
517
- functionAllowed = false,
518
- prototypeStrict = false,
519
- ignoreSymbols = false
520
- } = {}) => {
521
- const {
522
- recipeArray,
523
- mainIdentifier,
524
- valueMap
525
- } = decompose(value, {
526
- functionAllowed,
527
- prototypeStrict,
528
- ignoreSymbols
529
- });
530
- const recipeArraySorted = sortRecipe(recipeArray);
531
- let source = `(function () {
532
- var globalObject
533
- try {
534
- globalObject = Function('return this')() || (42, eval)('this');
535
- } catch(e) {
536
- globalObject = window;
537
- }
538
-
539
- function safeDefineProperty(object, propertyNameOrSymbol, descriptor) {
540
- var currentDescriptor = Object.getOwnPropertyDescriptor(object, propertyNameOrSymbol);
541
- if (currentDescriptor && !currentDescriptor.configurable) return
542
- Object.defineProperty(object, propertyNameOrSymbol, descriptor)
543
- };
544
- `;
545
- const variableNameMap = {};
546
- recipeArray.forEach((recipe, index) => {
547
- const indexSorted = recipeArraySorted.indexOf(recipe);
548
- variableNameMap[index] = `_${indexSorted}`;
549
- });
550
- const identifierToVariableName = identifier => variableNameMap[identifier];
551
- const recipeToSetupSource = recipe => {
552
- if (recipe.type === "primitive") return primitiveRecipeToSetupSource(recipe);
553
- if (recipe.type === "global-symbol") return globalSymbolRecipeToSetupSource(recipe);
554
- if (recipe.type === "global-reference") return globalReferenceRecipeToSetupSource(recipe);
555
- return compositeRecipeToSetupSource(recipe);
556
- };
557
- const primitiveRecipeToSetupSource = ({
558
- value
559
- }) => {
560
- const type = typeof value;
561
- if (type === "string") {
562
- return `"${escapeString(value)}";`;
563
- }
564
- if (type === "bigint") {
565
- return `${value.toString()}n`;
566
- }
567
- if (Object.is(value, -0)) {
568
- return "-0;";
569
- }
570
- return `${String(value)};`;
571
- };
572
- const globalSymbolRecipeToSetupSource = recipe => {
573
- return `Symbol.for("${escapeString(recipe.key)}");`;
574
- };
575
- const globalReferenceRecipeToSetupSource = recipe => {
576
- const pathSource = recipe.path.map(part => `["${escapeString(part)}"]`).join("");
577
- return `globalObject${pathSource};`;
578
- };
579
- const compositeRecipeToSetupSource = ({
580
- prototypeIdentifier,
581
- valueOfIdentifier
582
- }) => {
583
- if (prototypeIdentifier === undefined) {
584
- return identifierToVariableName(valueOfIdentifier);
585
- }
586
- const prototypeValue = valueMap[prototypeIdentifier];
587
- if (prototypeValue === null) {
588
- return `Object.create(null);`;
589
- }
590
- const prototypeConstructor = prototypeValue.constructor;
591
- if (prototypeConstructor === Object) {
592
- return `Object.create(${identifierToVariableName(prototypeIdentifier)});`;
593
- }
594
- if (valueOfIdentifier === undefined) {
595
- return `new ${prototypeConstructor.name}();`;
596
- }
597
- if (prototypeConstructor.name === "BigInt") {
598
- return `Object(${identifierToVariableName(valueOfIdentifier)})`;
599
- }
600
- return `new ${prototypeConstructor.name}(${identifierToVariableName(valueOfIdentifier)});`;
601
- };
602
- recipeArraySorted.forEach(recipe => {
603
- const recipeVariableName = identifierToVariableName(recipeArray.indexOf(recipe));
604
- source += `var ${recipeVariableName} = ${recipeToSetupSource(recipe)}
605
- `;
606
- });
607
- const recipeToMutateSource = (recipe, recipeVariableName) => {
608
- if (recipe.type === "composite") {
609
- return compositeRecipeToMutateSource(recipe, recipeVariableName);
610
- }
611
- return ``;
612
- };
613
- const compositeRecipeToMutateSource = ({
614
- propertyDescriptionArray,
615
- symbolDescriptionArray,
616
- methodDescriptionArray,
617
- extensible
618
- }, recipeVariableName) => {
619
- let mutateSource = ``;
620
- propertyDescriptionArray.forEach(({
621
- propertyNameIdentifier,
622
- propertyDescription
623
- }) => {
624
- mutateSource += generateDefinePropertySource(recipeVariableName, propertyNameIdentifier, propertyDescription);
625
- });
626
- symbolDescriptionArray.forEach(({
627
- symbolIdentifier,
628
- propertyDescription
629
- }) => {
630
- mutateSource += generateDefinePropertySource(recipeVariableName, symbolIdentifier, propertyDescription);
631
- });
632
- methodDescriptionArray.forEach(({
633
- methodNameIdentifier,
634
- callArray
635
- }) => {
636
- mutateSource += generateMethodCallSource(recipeVariableName, methodNameIdentifier, callArray);
637
- });
638
- if (!extensible) {
639
- mutateSource += generatePreventExtensionSource(recipeVariableName);
640
- }
641
- return mutateSource;
642
- };
643
- const generateDefinePropertySource = (recipeVariableName, propertyNameOrSymbolIdentifier, propertyDescription) => {
644
- const propertyOrSymbolVariableName = identifierToVariableName(propertyNameOrSymbolIdentifier);
645
- const propertyDescriptorSource = generatePropertyDescriptorSource(propertyDescription);
646
- return `safeDefineProperty(${recipeVariableName}, ${propertyOrSymbolVariableName}, ${propertyDescriptorSource});`;
647
- };
648
- const generatePropertyDescriptorSource = ({
649
- configurable,
650
- writable,
651
- enumerable,
652
- getIdentifier,
653
- setIdentifier,
654
- valueIdentifier
655
- }) => {
656
- if (valueIdentifier === undefined) {
657
- return `{
658
- configurable: ${configurable},
659
- enumerable: ${enumerable},
660
- get: ${getIdentifier === undefined ? undefined : identifierToVariableName(getIdentifier)},
661
- set: ${setIdentifier === undefined ? undefined : identifierToVariableName(setIdentifier)},
662
- }`;
663
- }
664
- return `{
665
- configurable: ${configurable},
666
- writable: ${writable},
667
- enumerable: ${enumerable},
668
- value: ${valueIdentifier === undefined ? undefined : identifierToVariableName(valueIdentifier)}
669
- }`;
670
- };
671
- const generateMethodCallSource = (recipeVariableName, methodNameIdentifier, callArray) => {
672
- let methodCallSource = ``;
673
- const methodVariableName = identifierToVariableName(methodNameIdentifier);
674
- callArray.forEach(argumentIdentifiers => {
675
- const argumentVariableNames = argumentIdentifiers.map(argumentIdentifier => identifierToVariableName(argumentIdentifier));
676
- methodCallSource += `${recipeVariableName}[${methodVariableName}](${argumentVariableNames.join(",")});`;
677
- });
678
- return methodCallSource;
679
- };
680
- const generatePreventExtensionSource = recipeVariableName => {
681
- return `Object.preventExtensions(${recipeVariableName});`;
682
- };
683
- recipeArraySorted.forEach(recipe => {
684
- const recipeVariableName = identifierToVariableName(recipeArray.indexOf(recipe));
685
- source += `${recipeToMutateSource(recipe, recipeVariableName)}`;
686
- });
687
- source += `return ${identifierToVariableName(mainIdentifier)}; })()`;
688
- return source;
689
- };
690
-
691
- /*
692
- * Calling Profiler.startPreciseCoverage DO NOT propagate to
693
- * subprocesses (new Worker or child_process.fork())
694
- * So the best solution remains NODE_V8_COVERAGE
695
- * This profiler strategy remains useful when:
696
- * - As fallback when NODE_V8_COVERAGE is not configured
697
- * - If explicitely enabled with coverageMethodForNodeJs: 'Profiler'
698
- * - Used by jsenv during automated tests about coverage
699
- * - Anyone prefering this approach over NODE_V8_COVERAGE and assuming
700
- * it will not fork subprocess or don't care if coverage is missed for this code
701
- * - https://v8.dev/blog/javascript-code-coverage#for-embedders
702
- * - https://github.com/nodejs/node/issues/28283
703
- * - https://vanilla.aslushnikov.com/?Profiler.startPreciseCoverage
704
- */
705
- const startJsCoverage = async ({
706
- callCount = true,
707
- detailed = true
708
- } = {}) => {
709
- const session = new Session();
710
- session.connect();
711
- const postSession = (action, options) => {
712
- const promise = new Promise((resolve, reject) => {
713
- session.post(action, options, (error, data) => {
714
- if (error) {
715
- reject(error);
716
- } else {
717
- resolve(data);
718
- }
719
- });
720
- });
721
- return promise;
722
- };
723
- await postSession("Profiler.enable");
724
- await postSession("Profiler.startPreciseCoverage", {
725
- callCount,
726
- detailed
727
- });
728
- const takeJsCoverage = async () => {
729
- const coverage = await postSession("Profiler.takePreciseCoverage");
730
- return coverage;
731
- };
732
- const stopJsCoverage = async () => {
733
- const coverage = await takeJsCoverage();
734
- await postSession("Profiler.stopPreciseCoverage");
735
- await postSession("Profiler.disable");
736
- return coverage;
737
- };
738
- return {
739
- takeJsCoverage,
740
- stopJsCoverage
741
- };
742
- };
743
-
744
- const startObservingPerformances = () => {
745
- const measureEntries = [];
746
- // https://nodejs.org/dist/latest-v16.x/docs/api/perf_hooks.html
747
- const perfObserver = new PerformanceObserver((
748
- // https://nodejs.org/dist/latest-v16.x/docs/api/perf_hooks.html#perf_hooks_class_performanceobserverentrylist
749
- list) => {
750
- const perfMeasureEntries = list.getEntriesByType("measure");
751
- measureEntries.push(...perfMeasureEntries);
752
- });
753
- perfObserver.observe({
754
- entryTypes: ["measure"]
755
- });
756
- return async () => {
757
- // wait for node to call the performance observer
758
- await new Promise(resolve => {
759
- setTimeout(resolve);
760
- });
761
- performance.clearMarks();
762
- perfObserver.disconnect();
763
- return {
764
- ...readNodePerformance(),
765
- measures: measuresFromMeasureEntries(measureEntries)
766
- };
767
- };
768
- };
769
- const readNodePerformance = () => {
770
- const nodePerformance = {
771
- nodeTiming: asPlainObject(performance.nodeTiming),
772
- timeOrigin: performance.timeOrigin,
773
- eventLoopUtilization: performance.eventLoopUtilization()
774
- };
775
- return nodePerformance;
776
- };
777
-
778
- // remove getters that cannot be stringified
779
- const asPlainObject = objectWithGetters => {
780
- const objectWithoutGetters = {};
781
- Object.keys(objectWithGetters).forEach(key => {
782
- objectWithoutGetters[key] = objectWithGetters[key];
783
- });
784
- return objectWithoutGetters;
785
- };
786
- const measuresFromMeasureEntries = measureEntries => {
787
- const measures = {};
788
- // Sort to ensure measures order is predictable
789
- // It seems to be already predictable on Node 16+ but
790
- // it's not the case on Node 14.
791
- measureEntries.sort((a, b) => {
792
- return a.startTime - b.startTime;
793
- });
794
- measureEntries.forEach((
795
- // https://nodejs.org/dist/latest-v16.x/docs/api/perf_hooks.html#perf_hooks_class_performanceentry
796
- perfMeasureEntry) => {
797
- measures[perfMeasureEntry.name] = perfMeasureEntry.duration;
798
- });
799
- return measures;
800
- };
801
-
802
- const executeUsingDynamicImport = async ({
803
- rootDirectoryUrl,
804
- fileUrl,
805
- collectPerformance,
806
- coverageEnabled,
807
- coverageConfig,
808
- coverageMethodForNodeJs,
809
- coverageFileUrl
810
- }) => {
811
- const result = {};
812
- const afterImportCallbacks = [];
813
- if (coverageEnabled && coverageMethodForNodeJs === "Profiler") {
814
- const {
815
- filterV8Coverage
816
- } = await import("./v8_coverage.js").then(n => n.v8_coverage);
817
- const {
818
- stopJsCoverage
819
- } = await startJsCoverage();
820
- afterImportCallbacks.push(async () => {
821
- const coverage = await stopJsCoverage();
822
- const coverageLight = await filterV8Coverage(coverage, {
823
- rootDirectoryUrl,
824
- coverageConfig
825
- });
826
- writeFileSync(new URL(coverageFileUrl), JSON.stringify(coverageLight, null, " "));
827
- });
828
- }
829
- if (collectPerformance) {
830
- const getPerformance = startObservingPerformances();
831
- afterImportCallbacks.push(async () => {
832
- const performance = await getPerformance();
833
- result.performance = performance;
834
- });
835
- }
836
- const namespace = await import(fileUrl);
837
- const namespaceResolved = {};
838
- await Promise.all(Object.keys(namespace).map(async key => {
839
- const value = await namespace[key];
840
- namespaceResolved[key] = value;
841
- }));
842
- result.namespace = namespaceResolved;
843
- await afterImportCallbacks.reduce(async (previous, afterImportCallback) => {
844
- await previous;
845
- await afterImportCallback();
846
- }, Promise.resolve());
847
- return result;
848
- };
849
-
850
- export { executeUsingDynamicImport, uneval };