@jsenv/core 38.1.1 → 38.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,13 +1,9 @@
1
1
  # @jsenv/core [![npm package](https://img.shields.io/npm/v/@jsenv/core.svg?logo=npm&label=package)](https://www.npmjs.com/package/@jsenv/core)
2
2
 
3
- Jsenv is a tool to develop test and build projects using JavaScript. This tool makes a special effort to let you free to code as you want. Jsenv is simple, easy to understand and well documented.
3
+ Jsenv is a tool to develop test and build projects using JavaScript.
4
+ Jsenv is simple, easy to understand and well documented.
4
5
 
5
- [Documentation for users](<https://github.com/jsenv/core/wiki/A)-Introduction>) · [Changelog](./CHANGELOG.md)
6
-
7
- See also
8
-
9
- - [Documentation for contributors](./docs/contributors/README.md)
10
- - [Documentation for maintainers](./docs/maintainers/README.md)
6
+ [Documentation](<https://github.com/jsenv/core/wiki/A)-Introduction>)
11
7
 
12
8
  <!-- # Installation
13
9
 
@@ -0,0 +1,45 @@
1
+ /* @minVersion 7.22.0 */
2
+ function dispose_SuppressedError(suppressed, error) {
3
+ if (typeof SuppressedError !== "undefined") {
4
+ // eslint-disable-next-line no-undef
5
+ dispose_SuppressedError = SuppressedError;
6
+ } else {
7
+ dispose_SuppressedError = function SuppressedError(suppressed, error) {
8
+ this.suppressed = suppressed;
9
+ this.error = error;
10
+ this.stack = new Error().stack;
11
+ };
12
+ dispose_SuppressedError.prototype = Object.create(Error.prototype, {
13
+ constructor: {
14
+ value: dispose_SuppressedError,
15
+ writable: true,
16
+ configurable: true,
17
+ },
18
+ });
19
+ }
20
+ return new dispose_SuppressedError(suppressed, error);
21
+ }
22
+
23
+ export default function _dispose(stack, error, hasError) {
24
+ function next() {
25
+ while (stack.length > 0) {
26
+ try {
27
+ var r = stack.pop();
28
+ var p = r.d.call(r.v);
29
+ if (r.a) return Promise.resolve(p).then(next, err);
30
+ } catch (e) {
31
+ return err(e);
32
+ }
33
+ }
34
+ if (hasError) throw error;
35
+ }
36
+
37
+ function err(e) {
38
+ error = hasError ? new dispose_SuppressedError(e, error) : e;
39
+ hasError = true;
40
+
41
+ return next();
42
+ }
43
+
44
+ return next();
45
+ }
@@ -483,7 +483,7 @@ export default function _regeneratorRuntime() {
483
483
  };
484
484
  };
485
485
  function values(iterable) {
486
- if (iterable) {
486
+ if (iterable || iterable === "") {
487
487
  var iteratorMethod = iterable[iteratorSymbol];
488
488
  if (iteratorMethod) {
489
489
  return iteratorMethod.call(iterable);
@@ -508,11 +508,7 @@ export default function _regeneratorRuntime() {
508
508
  return (next.next = next);
509
509
  }
510
510
  }
511
-
512
- // Return an iterator with no values.
513
- return {
514
- next: doneResult,
515
- };
511
+ throw new TypeError(typeof iterable + " is not iterable");
516
512
  }
517
513
  exports.values = values;
518
514
  function doneResult() {
@@ -0,0 +1,23 @@
1
+ /* @minVersion 7.22.0 */
2
+
3
+ export default function _using(stack, value, isAwait) {
4
+ if (value === null || value === void 0) return value;
5
+ if (typeof value !== "object") {
6
+ throw new TypeError(
7
+ "using declarations can only be used with objects, null, or undefined."
8
+ );
9
+ }
10
+ // core-js-pure uses Symbol.for for polyfilling well-known symbols
11
+ if (isAwait) {
12
+ var dispose =
13
+ value[Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")];
14
+ }
15
+ if (dispose === null || dispose === void 0) {
16
+ dispose = value[Symbol.dispose || Symbol.for("Symbol.dispose")];
17
+ }
18
+ if (typeof dispose !== "function") {
19
+ throw new TypeError(`Property [Symbol.dispose] is not a function.`);
20
+ }
21
+ stack.push({ v: value, d: dispose, a: isAwait });
22
+ return value;
23
+ }
package/dist/js/ws.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import require$$0 from "stream";
2
2
  import require$$0$3 from "events";
3
3
  import require$$2 from "http";
4
- import require$$1 from "https";
5
- import require$$3 from "net";
6
- import require$$4 from "tls";
7
- import require$$5 from "crypto";
4
+ import require$$1 from "crypto";
8
5
  import require$$0$1 from "zlib";
9
6
  import require$$0$2 from "buffer";
7
+ import require$$1$1 from "https";
8
+ import require$$3 from "net";
9
+ import require$$4 from "tls";
10
10
  import require$$7 from "url";
11
11
 
12
12
  function getDefaultExportFromCjs (x) {
@@ -876,12 +876,15 @@ const { concat, toArrayBuffer, unmask } = bufferUtilExports;
876
876
  const { isValidStatusCode: isValidStatusCode$1, isValidUTF8 } = validationExports;
877
877
 
878
878
  const FastBuffer = Buffer[Symbol.species];
879
+ const promise = Promise.resolve();
880
+
879
881
  const GET_INFO = 0;
880
882
  const GET_PAYLOAD_LENGTH_16 = 1;
881
883
  const GET_PAYLOAD_LENGTH_64 = 2;
882
884
  const GET_MASK = 3;
883
885
  const GET_DATA = 4;
884
886
  const INFLATING = 5;
887
+ const WAIT_MICROTASK = 6;
885
888
 
886
889
  /**
887
890
  * HyBi Receiver implementation.
@@ -1020,9 +1023,23 @@ let Receiver$1 = class Receiver extends Writable {
1020
1023
  case GET_DATA:
1021
1024
  err = this.getData(cb);
1022
1025
  break;
1026
+ case INFLATING:
1027
+ this._loop = false;
1028
+ return;
1023
1029
  default:
1024
- // `INFLATING`
1030
+ //
1031
+ // `WAIT_MICROTASK`.
1032
+ //
1025
1033
  this._loop = false;
1034
+
1035
+ //
1036
+ // `queueMicrotask()` is not available in Node.js < 11 and is no
1037
+ // better anyway.
1038
+ //
1039
+ promise.then(() => {
1040
+ this._state = GET_INFO;
1041
+ this.startLoop(cb);
1042
+ });
1026
1043
  return;
1027
1044
  }
1028
1045
  } while (this._loop);
@@ -1405,7 +1422,7 @@ let Receiver$1 = class Receiver extends Writable {
1405
1422
  }
1406
1423
  }
1407
1424
 
1408
- this._state = GET_INFO;
1425
+ this._state = WAIT_MICROTASK;
1409
1426
  }
1410
1427
 
1411
1428
  /**
@@ -1422,6 +1439,8 @@ let Receiver$1 = class Receiver extends Writable {
1422
1439
  if (data.length === 0) {
1423
1440
  this.emit('conclude', 1005, EMPTY_BUFFER$2);
1424
1441
  this.end();
1442
+
1443
+ this._state = GET_INFO;
1425
1444
  } else {
1426
1445
  const code = data.readUInt16BE(0);
1427
1446
 
@@ -1453,14 +1472,16 @@ let Receiver$1 = class Receiver extends Writable {
1453
1472
 
1454
1473
  this.emit('conclude', code, buf);
1455
1474
  this.end();
1475
+
1476
+ this._state = GET_INFO;
1456
1477
  }
1457
1478
  } else if (this._opcode === 0x09) {
1458
1479
  this.emit('ping', data);
1480
+ this._state = WAIT_MICROTASK;
1459
1481
  } else {
1460
1482
  this.emit('pong', data);
1483
+ this._state = WAIT_MICROTASK;
1461
1484
  }
1462
-
1463
- this._state = GET_INFO;
1464
1485
  }
1465
1486
  };
1466
1487
 
@@ -1489,8 +1510,8 @@ function error(ErrorCtor, message, prefix, statusCode, errorCode) {
1489
1510
  return err;
1490
1511
  }
1491
1512
 
1492
- /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^net|tls$" }] */
1493
- const { randomFillSync } = require$$5;
1513
+ /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex" }] */
1514
+ const { randomFillSync } = require$$1;
1494
1515
 
1495
1516
  const PerMessageDeflate$2 = permessageDeflate;
1496
1517
  const { EMPTY_BUFFER: EMPTY_BUFFER$1 } = constants;
@@ -1507,7 +1528,7 @@ let Sender$1 = class Sender {
1507
1528
  /**
1508
1529
  * Creates a Sender instance.
1509
1530
  *
1510
- * @param {(net.Socket|tls.Socket)} socket The connection socket
1531
+ * @param {Duplex} socket The connection socket
1511
1532
  * @param {Object} [extensions] An object containing the negotiated extensions
1512
1533
  * @param {Function} [generateMask] The function used to generate the masking
1513
1534
  * key
@@ -2456,14 +2477,14 @@ function format$1(extensions) {
2456
2477
 
2457
2478
  var extension$1 = { format: format$1, parse: parse$2 };
2458
2479
 
2459
- /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Readable$" }] */
2480
+ /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex|Readable$" }] */
2460
2481
 
2461
2482
  const EventEmitter$1 = require$$0$3;
2462
- const https = require$$1;
2483
+ const https = require$$1$1;
2463
2484
  const http$1 = require$$2;
2464
2485
  const net = require$$3;
2465
2486
  const tls = require$$4;
2466
- const { randomBytes, createHash: createHash$1 } = require$$5;
2487
+ const { randomBytes, createHash: createHash$1 } = require$$1;
2467
2488
  const { URL } = require$$7;
2468
2489
 
2469
2490
  const PerMessageDeflate$1 = permessageDeflate;
@@ -2644,8 +2665,7 @@ let WebSocket$1 = class WebSocket extends EventEmitter$1 {
2644
2665
  /**
2645
2666
  * Set up the socket and the internal resources.
2646
2667
  *
2647
- * @param {(net.Socket|tls.Socket)} socket The network socket between the
2648
- * server and client
2668
+ * @param {Duplex} socket The network socket between the server and client
2649
2669
  * @param {Buffer} head The first packet of the upgraded stream
2650
2670
  * @param {Object} options Options object
2651
2671
  * @param {Function} [options.generateMask] The function used to generate the
@@ -2678,8 +2698,11 @@ let WebSocket$1 = class WebSocket extends EventEmitter$1 {
2678
2698
  receiver.on('ping', receiverOnPing);
2679
2699
  receiver.on('pong', receiverOnPong);
2680
2700
 
2681
- socket.setTimeout(0);
2682
- socket.setNoDelay();
2701
+ //
2702
+ // These methods may not be available if `socket` is just a `Duplex`.
2703
+ //
2704
+ if (socket.setTimeout) socket.setTimeout(0);
2705
+ if (socket.setNoDelay) socket.setNoDelay();
2683
2706
 
2684
2707
  if (head.length > 0) socket.unshift(head);
2685
2708
 
@@ -3122,24 +3145,30 @@ function initAsClient(websocket, address, protocols, options) {
3122
3145
 
3123
3146
  if (address instanceof URL) {
3124
3147
  parsedUrl = address;
3125
- websocket._url = address.href;
3126
3148
  } else {
3127
3149
  try {
3128
3150
  parsedUrl = new URL(address);
3129
3151
  } catch (e) {
3130
3152
  throw new SyntaxError(`Invalid URL: ${address}`);
3131
3153
  }
3154
+ }
3132
3155
 
3133
- websocket._url = address;
3156
+ if (parsedUrl.protocol === 'http:') {
3157
+ parsedUrl.protocol = 'ws:';
3158
+ } else if (parsedUrl.protocol === 'https:') {
3159
+ parsedUrl.protocol = 'wss:';
3134
3160
  }
3135
3161
 
3162
+ websocket._url = parsedUrl.href;
3163
+
3136
3164
  const isSecure = parsedUrl.protocol === 'wss:';
3137
3165
  const isIpcUrl = parsedUrl.protocol === 'ws+unix:';
3138
3166
  let invalidUrlMessage;
3139
3167
 
3140
3168
  if (parsedUrl.protocol !== 'ws:' && !isSecure && !isIpcUrl) {
3141
3169
  invalidUrlMessage =
3142
- 'The URL\'s protocol must be one of "ws:", "wss:", or "ws+unix:"';
3170
+ 'The URL\'s protocol must be one of "ws:", "wss:", ' +
3171
+ '"http:", "https", or "ws+unix:"';
3143
3172
  } else if (isIpcUrl && !parsedUrl.pathname) {
3144
3173
  invalidUrlMessage = "The URL's pathname is empty";
3145
3174
  } else if (parsedUrl.hash) {
@@ -3673,7 +3702,7 @@ function resume(stream) {
3673
3702
  }
3674
3703
 
3675
3704
  /**
3676
- * The listener of the `net.Socket` `'close'` event.
3705
+ * The listener of the socket `'close'` event.
3677
3706
  *
3678
3707
  * @private
3679
3708
  */
@@ -3724,7 +3753,7 @@ function socketOnClose() {
3724
3753
  }
3725
3754
 
3726
3755
  /**
3727
- * The listener of the `net.Socket` `'data'` event.
3756
+ * The listener of the socket `'data'` event.
3728
3757
  *
3729
3758
  * @param {Buffer} chunk A chunk of data
3730
3759
  * @private
@@ -3736,7 +3765,7 @@ function socketOnData(chunk) {
3736
3765
  }
3737
3766
 
3738
3767
  /**
3739
- * The listener of the `net.Socket` `'end'` event.
3768
+ * The listener of the socket `'end'` event.
3740
3769
  *
3741
3770
  * @private
3742
3771
  */
@@ -3749,7 +3778,7 @@ function socketOnEnd() {
3749
3778
  }
3750
3779
 
3751
3780
  /**
3752
- * The listener of the `net.Socket` `'error'` event.
3781
+ * The listener of the socket `'error'` event.
3753
3782
  *
3754
3783
  * @private
3755
3784
  */
@@ -3826,11 +3855,11 @@ function parse(header) {
3826
3855
 
3827
3856
  var subprotocol$1 = { parse };
3828
3857
 
3829
- /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^net|tls|https$" }] */
3858
+ /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex$" }] */
3830
3859
 
3831
3860
  const EventEmitter = require$$0$3;
3832
3861
  const http = require$$2;
3833
- const { createHash } = require$$5;
3862
+ const { createHash } = require$$1;
3834
3863
 
3835
3864
  const extension = extension$1;
3836
3865
  const PerMessageDeflate = permessageDeflate;
@@ -4044,8 +4073,7 @@ class WebSocketServer extends EventEmitter {
4044
4073
  * Handle a HTTP Upgrade request.
4045
4074
  *
4046
4075
  * @param {http.IncomingMessage} req The request object
4047
- * @param {(net.Socket|tls.Socket)} socket The network socket between the
4048
- * server and client
4076
+ * @param {Duplex} socket The network socket between the server and client
4049
4077
  * @param {Buffer} head The first packet of the upgraded stream
4050
4078
  * @param {Function} cb Callback
4051
4079
  * @public
@@ -4169,8 +4197,7 @@ class WebSocketServer extends EventEmitter {
4169
4197
  * @param {String} key The value of the `Sec-WebSocket-Key` header
4170
4198
  * @param {Set} protocols The subprotocols
4171
4199
  * @param {http.IncomingMessage} req The request object
4172
- * @param {(net.Socket|tls.Socket)} socket The network socket between the
4173
- * server and client
4200
+ * @param {Duplex} socket The network socket between the server and client
4174
4201
  * @param {Buffer} head The first packet of the upgraded stream
4175
4202
  * @param {Function} cb Callback
4176
4203
  * @throws {Error} If called more than once with the same socket
@@ -4300,7 +4327,7 @@ function socketOnError() {
4300
4327
  /**
4301
4328
  * Close the connection when preconditions are not fulfilled.
4302
4329
  *
4303
- * @param {(net.Socket|tls.Socket)} socket The socket of the upgrade request
4330
+ * @param {Duplex} socket The socket of the upgrade request
4304
4331
  * @param {Number} code The HTTP response status code
4305
4332
  * @param {String} [message] The HTTP response body
4306
4333
  * @param {Object} [headers] Additional HTTP response headers
@@ -4341,7 +4368,7 @@ function abortHandshake(socket, code, message, headers) {
4341
4368
  *
4342
4369
  * @param {WebSocketServer} server The WebSocket server
4343
4370
  * @param {http.IncomingMessage} req The request object
4344
- * @param {(net.Socket|tls.Socket)} socket The socket of the upgrade request
4371
+ * @param {Duplex} socket The socket of the upgrade request
4345
4372
  * @param {Number} code The HTTP response status code
4346
4373
  * @param {String} message The HTTP response body
4347
4374
  * @private
@@ -10579,9 +10579,13 @@ const jsenvPluginJsModuleFallbackOnWorkers = () => {
10579
10579
  name: `jsenv:js_module_fallback_on_${subtype}`,
10580
10580
  appliesDuring: "*",
10581
10581
  init: (context) => {
10582
+ if (Object.keys(context.runtimeCompat).toString() === "node") {
10583
+ return false;
10584
+ }
10582
10585
  if (context.isSupportedOnCurrentClients(`${subtype}_type_module`)) {
10583
10586
  return false;
10584
10587
  }
10588
+
10585
10589
  return true;
10586
10590
  },
10587
10591
  redirectReference: {
@@ -14797,214 +14801,6 @@ const createRepartitionMessage = ({ html, css, js, json, other, total }) => {
14797
14801
  - `)}`;
14798
14802
  };
14799
14803
 
14800
- const placeholderSymbol = Symbol.for("jsenv_placeholder");
14801
- const PLACEHOLDER = {
14802
- optional: (value) => {
14803
- return { [placeholderSymbol]: "optional", value };
14804
- },
14805
- };
14806
-
14807
- const replacePlaceholders = (content, replacements, urlInfo) => {
14808
- const magicSource = createMagicSource(content);
14809
- for (const key of Object.keys(replacements)) {
14810
- let index = content.indexOf(key);
14811
- const replacement = replacements[key];
14812
- let isOptional;
14813
- let value;
14814
- if (replacement && replacement[placeholderSymbol]) {
14815
- const valueBehindSymbol = replacement[placeholderSymbol];
14816
- isOptional = valueBehindSymbol === "optional";
14817
- value = replacement.value;
14818
- } else {
14819
- value = replacement;
14820
- }
14821
- if (index === -1) {
14822
- if (!isOptional) {
14823
- urlInfo.context.logger.warn(
14824
- `placeholder "${key}" not found in ${urlInfo.url}.
14825
- --- suggestion a ---
14826
- Add "${key}" in that file.
14827
- --- suggestion b ---
14828
- Fix eventual typo in "${key}"?
14829
- --- suggestion c ---
14830
- Mark injection as optional using PLACEHOLDER.optional()
14831
-
14832
- import { PLACEHOLDER } from "@jsenv/core"
14833
-
14834
- return {
14835
- "${key}": PLACEHOLDER.optional(${JSON.stringify(value)})
14836
- }`,
14837
- );
14838
- }
14839
- continue;
14840
- }
14841
-
14842
- while (index !== -1) {
14843
- const start = index;
14844
- const end = index + key.length;
14845
- magicSource.replace({
14846
- start,
14847
- end,
14848
- replacement:
14849
- urlInfo.type === "js_classic" ||
14850
- urlInfo.type === "js_module" ||
14851
- urlInfo.type === "html"
14852
- ? JSON.stringify(value, null, " ")
14853
- : value,
14854
- });
14855
- index = content.indexOf(key, end);
14856
- }
14857
- }
14858
- return magicSource.toContentAndSourcemap();
14859
- };
14860
-
14861
- const injectGlobals = (content, globals, urlInfo) => {
14862
- if (urlInfo.type === "html") {
14863
- return globalInjectorOnHtml(content, globals);
14864
- }
14865
- if (urlInfo.type === "js_classic" || urlInfo.type === "js_module") {
14866
- return globalsInjectorOnJs(content, globals, urlInfo);
14867
- }
14868
- throw new Error(`cannot inject globals into "${urlInfo.type}"`);
14869
- };
14870
-
14871
- const globalInjectorOnHtml = (content, globals) => {
14872
- // ideally we would inject an importmap but browser support is too low
14873
- // (even worse for worker/service worker)
14874
- // so for now we inject code into entry points
14875
- const htmlAst = parseHtmlString(content, {
14876
- storeOriginalPositions: false,
14877
- });
14878
- const clientCode = generateClientCodeForGlobals(globals, {
14879
- isWebWorker: false,
14880
- });
14881
- injectHtmlNodeAsEarlyAsPossible(
14882
- htmlAst,
14883
- createHtmlNode({
14884
- tagName: "script",
14885
- textContent: clientCode,
14886
- }),
14887
- "jsenv:inject_globals",
14888
- );
14889
- return stringifyHtmlAst(htmlAst);
14890
- };
14891
-
14892
- const globalsInjectorOnJs = (content, globals, urlInfo) => {
14893
- const clientCode = generateClientCodeForGlobals(globals, {
14894
- isWebWorker:
14895
- urlInfo.subtype === "worker" ||
14896
- urlInfo.subtype === "service_worker" ||
14897
- urlInfo.subtype === "shared_worker",
14898
- });
14899
- const magicSource = createMagicSource(content);
14900
- magicSource.prepend(clientCode);
14901
- return magicSource.toContentAndSourcemap();
14902
- };
14903
-
14904
- const generateClientCodeForGlobals = (globals, { isWebWorker = false }) => {
14905
- const globalName = isWebWorker ? "self" : "window";
14906
- return `Object.assign(${globalName}, ${JSON.stringify(
14907
- globals,
14908
- null,
14909
- " ",
14910
- )});`;
14911
- };
14912
-
14913
- const jsenvPluginInjections = (rawAssociations) => {
14914
- let resolvedAssociations;
14915
-
14916
- return {
14917
- name: "jsenv:injections",
14918
- appliesDuring: "*",
14919
- init: (context) => {
14920
- resolvedAssociations = URL_META.resolveAssociations(
14921
- { injectionsGetter: rawAssociations },
14922
- context.rootDirectoryUrl,
14923
- );
14924
- },
14925
- transformUrlContent: async (urlInfo) => {
14926
- const { injectionsGetter } = URL_META.applyAssociations({
14927
- url: asUrlWithoutSearch(urlInfo.url),
14928
- associations: resolvedAssociations,
14929
- });
14930
- if (!injectionsGetter) {
14931
- return null;
14932
- }
14933
- if (typeof injectionsGetter !== "function") {
14934
- throw new TypeError("injectionsGetter must be a function");
14935
- }
14936
- const injections = await injectionsGetter(urlInfo);
14937
- if (!injections) {
14938
- return null;
14939
- }
14940
- const keys = Object.keys(injections);
14941
- if (keys.length === 0) {
14942
- return null;
14943
- }
14944
- let someGlobal = false;
14945
- let someReplacement = false;
14946
- const globals = {};
14947
- const replacements = {};
14948
- for (const key of keys) {
14949
- const { type, name, value } = createInjection(injections[key], key);
14950
- if (type === "global") {
14951
- globals[name] = value;
14952
- someGlobal = true;
14953
- } else {
14954
- replacements[name] = value;
14955
- someReplacement = true;
14956
- }
14957
- }
14958
-
14959
- if (!someGlobal && !someReplacement) {
14960
- return null;
14961
- }
14962
-
14963
- let content = urlInfo.content;
14964
- let sourcemap;
14965
- if (someGlobal) {
14966
- const globalInjectionResult = injectGlobals(content, globals, urlInfo);
14967
- content = globalInjectionResult.content;
14968
- sourcemap = globalInjectionResult.sourcemap;
14969
- }
14970
- if (someReplacement) {
14971
- const replacementResult = replacePlaceholders(
14972
- content,
14973
- replacements,
14974
- urlInfo,
14975
- );
14976
- content = replacementResult.content;
14977
- sourcemap = sourcemap
14978
- ? composeTwoSourcemaps(sourcemap, replacementResult.sourcemap)
14979
- : replacementResult.sourcemap;
14980
- }
14981
- return {
14982
- content,
14983
- sourcemap,
14984
- };
14985
- },
14986
- };
14987
- };
14988
-
14989
- const wellKnowGlobalNames = ["window", "global", "globalThis", "self"];
14990
- const createInjection = (value, key) => {
14991
- for (const wellKnowGlobalName of wellKnowGlobalNames) {
14992
- const prefix = `${wellKnowGlobalName}.`;
14993
- if (key.startsWith(prefix)) {
14994
- return {
14995
- type: "global",
14996
- name: key.slice(prefix.length),
14997
- value,
14998
- };
14999
- }
15000
- }
15001
- return {
15002
- type: "replacement",
15003
- name: key,
15004
- value,
15005
- };
15006
- };
15007
-
15008
14804
  const jsenvPluginReferenceExpectedTypes = () => {
15009
14805
  const redirectJsReference = (reference) => {
15010
14806
  const urlObject = new URL(reference.url);
@@ -15690,6 +15486,8 @@ const parseAndTransformJsReferences = async (
15690
15486
  const magicSource = createMagicSource(urlInfo.content);
15691
15487
  const parallelActions = [];
15692
15488
  const sequentialActions = [];
15489
+ const isNodeJs =
15490
+ Object.keys(urlInfo.context.runtimeCompat).toString() === "node";
15693
15491
 
15694
15492
  const onInlineReference = (inlineReferenceInfo) => {
15695
15493
  const inlineUrl = getUrlForContentInsideJs(inlineReferenceInfo, {
@@ -15737,6 +15535,19 @@ const parseAndTransformJsReferences = async (
15737
15535
  ) {
15738
15536
  urlInfo.data.usesImport = true;
15739
15537
  }
15538
+ if (
15539
+ isNodeJs &&
15540
+ externalReferenceInfo.type === "js_url" &&
15541
+ externalReferenceInfo.expectedSubtype === "worker" &&
15542
+ externalReferenceInfo.expectedType === "js_classic" &&
15543
+ // TODO: it's true also if closest package.json
15544
+ // is type: module
15545
+ urlToExtension$1(
15546
+ new URL(externalReferenceInfo.specifier, urlInfo.url).href,
15547
+ ) === ".mjs"
15548
+ ) {
15549
+ externalReferenceInfo.expectedType = "js_module";
15550
+ }
15740
15551
  const reference = urlInfo.dependencies.found({
15741
15552
  type: externalReferenceInfo.type,
15742
15553
  subtype: externalReferenceInfo.subtype,
@@ -15779,6 +15590,7 @@ const parseAndTransformJsReferences = async (
15779
15590
  isJsModule: urlInfo.type === "js_module",
15780
15591
  isWebWorker: isWebWorkerUrlInfo(urlInfo),
15781
15592
  inlineContent,
15593
+ isNodeJs,
15782
15594
  });
15783
15595
  for (const jsReferenceInfo of jsReferenceInfos) {
15784
15596
  if (jsReferenceInfo.isInline) {
@@ -18353,6 +18165,104 @@ const jsenvPluginProtocolHttp = () => {
18353
18165
  };
18354
18166
  };
18355
18167
 
18168
+ const jsenvPluginInjections = (rawAssociations) => {
18169
+ let resolvedAssociations;
18170
+
18171
+ return {
18172
+ name: "jsenv:injections",
18173
+ appliesDuring: "*",
18174
+ init: (context) => {
18175
+ resolvedAssociations = URL_META.resolveAssociations(
18176
+ { injectionsGetter: rawAssociations },
18177
+ context.rootDirectoryUrl,
18178
+ );
18179
+ },
18180
+ transformUrlContent: async (urlInfo) => {
18181
+ const { injectionsGetter } = URL_META.applyAssociations({
18182
+ url: asUrlWithoutSearch(urlInfo.url),
18183
+ associations: resolvedAssociations,
18184
+ });
18185
+ if (!injectionsGetter) {
18186
+ return null;
18187
+ }
18188
+ if (typeof injectionsGetter !== "function") {
18189
+ throw new TypeError("injectionsGetter must be a function");
18190
+ }
18191
+ const injections = await injectionsGetter(urlInfo);
18192
+ if (!injections) {
18193
+ return null;
18194
+ }
18195
+ const keys = Object.keys(injections);
18196
+ if (keys.length === 0) {
18197
+ return null;
18198
+ }
18199
+ return replacePlaceholders(urlInfo.content, injections, urlInfo);
18200
+ },
18201
+ };
18202
+ };
18203
+
18204
+ const injectionSymbol = Symbol.for("jsenv_injection");
18205
+ const INJECTIONS = {
18206
+ optional: (value) => {
18207
+ return { [injectionSymbol]: "optional", value };
18208
+ },
18209
+ };
18210
+
18211
+ // we export this because it is imported by jsenv_plugin_placeholder.js and unit test
18212
+ const replacePlaceholders = (content, replacements, urlInfo) => {
18213
+ const magicSource = createMagicSource(content);
18214
+ for (const key of Object.keys(replacements)) {
18215
+ let index = content.indexOf(key);
18216
+ const replacement = replacements[key];
18217
+ let isOptional;
18218
+ let value;
18219
+ if (replacement && replacement[injectionSymbol]) {
18220
+ const valueBehindSymbol = replacement[injectionSymbol];
18221
+ isOptional = valueBehindSymbol === "optional";
18222
+ value = replacement.value;
18223
+ } else {
18224
+ value = replacement;
18225
+ }
18226
+ if (index === -1) {
18227
+ if (!isOptional) {
18228
+ urlInfo.context.logger.warn(
18229
+ `placeholder "${key}" not found in ${urlInfo.url}.
18230
+ --- suggestion a ---
18231
+ Add "${key}" in that file.
18232
+ --- suggestion b ---
18233
+ Fix eventual typo in "${key}"?
18234
+ --- suggestion c ---
18235
+ Mark injection as optional using INJECTIONS.optional()
18236
+
18237
+ import { INJECTIONS } from "@jsenv/core";
18238
+
18239
+ return {
18240
+ "${key}": INJECTIONS.optional(${JSON.stringify(value)}),
18241
+ }`,
18242
+ );
18243
+ }
18244
+ continue;
18245
+ }
18246
+
18247
+ while (index !== -1) {
18248
+ const start = index;
18249
+ const end = index + key.length;
18250
+ magicSource.replace({
18251
+ start,
18252
+ end,
18253
+ replacement:
18254
+ urlInfo.type === "js_classic" ||
18255
+ urlInfo.type === "js_module" ||
18256
+ urlInfo.type === "html"
18257
+ ? JSON.stringify(value, null, " ")
18258
+ : value,
18259
+ });
18260
+ index = content.indexOf(key, end);
18261
+ }
18262
+ }
18263
+ return magicSource.toContentAndSourcemap();
18264
+ };
18265
+
18356
18266
  const jsenvPluginInliningAsDataUrl = () => {
18357
18267
  return {
18358
18268
  name: "jsenv:inlining_as_data_url",
@@ -18885,9 +18795,9 @@ const babelPluginMetadataImportMetaScenarios = () => {
18885
18795
 
18886
18796
  /*
18887
18797
  * Source code can contain the following
18888
- * - __dev__
18889
- * - __build__
18890
- * A global will be injected with true/false when needed
18798
+ * - __DEV__
18799
+ * - __BUILD__
18800
+ * That will be replaced with true/false
18891
18801
  */
18892
18802
 
18893
18803
 
@@ -18896,8 +18806,8 @@ const jsenvPluginGlobalScenarios = () => {
18896
18806
  return replacePlaceholders(
18897
18807
  urlInfo.content,
18898
18808
  {
18899
- __DEV__: PLACEHOLDER.optional(urlInfo.context.dev),
18900
- __BUILD__: PLACEHOLDER.optional(urlInfo.context.build),
18809
+ __DEV__: INJECTIONS.optional(urlInfo.context.dev),
18810
+ __BUILD__: INJECTIONS.optional(urlInfo.context.build),
18901
18811
  },
18902
18812
  urlInfo,
18903
18813
  );
@@ -22982,4 +22892,4 @@ const createBuildFilesService = ({ buildDirectoryUrl, buildMainFilePath }) => {
22982
22892
 
22983
22893
  const SECONDS_IN_30_DAYS = 60 * 60 * 24 * 30;
22984
22894
 
22985
- export { build, startBuildServer, startDevServer };
22895
+ export { INJECTIONS, build, startBuildServer, startDevServer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "38.1.1",
3
+ "version": "38.2.1",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -61,27 +61,26 @@
61
61
  "dependencies": {
62
62
  "@financial-times/polyfill-useragent-normaliser": "1.10.2",
63
63
  "@jsenv/abort": "4.2.4",
64
- "@jsenv/ast": "5.1.2",
64
+ "@jsenv/ast": "5.1.3",
65
65
  "@jsenv/filesystem": "4.2.6",
66
66
  "@jsenv/importmap": "1.2.1",
67
67
  "@jsenv/integrity": "0.0.1",
68
68
  "@jsenv/log": "3.4.0",
69
69
  "@jsenv/node-esm-resolution": "1.0.1",
70
- "@jsenv/js-module-fallback": "1.3.3",
70
+ "@jsenv/js-module-fallback": "1.3.4",
71
71
  "@jsenv/runtime-compat": "1.2.0",
72
- "@jsenv/server": "15.1.0",
73
- "@jsenv/sourcemap": "1.2.1",
74
- "@jsenv/plugin-injections": "1.0.0",
75
- "@jsenv/plugin-bundling": "2.5.1",
76
- "@jsenv/plugin-minification": "1.5.0",
77
- "@jsenv/plugin-transpilation": "1.3.2",
78
- "@jsenv/plugin-supervisor": "1.3.2",
72
+ "@jsenv/server": "15.1.1",
73
+ "@jsenv/sourcemap": "1.2.2",
74
+ "@jsenv/plugin-bundling": "2.5.3",
75
+ "@jsenv/plugin-minification": "1.5.1",
76
+ "@jsenv/plugin-transpilation": "1.3.3",
77
+ "@jsenv/plugin-supervisor": "1.3.3",
79
78
  "@jsenv/url-meta": "8.1.0",
80
79
  "@jsenv/urls": "2.2.1",
81
80
  "@jsenv/utils": "2.0.1"
82
81
  },
83
82
  "devDependencies": {
84
- "@babel/eslint-parser": "7.22.9",
83
+ "@babel/eslint-parser": "7.22.15",
85
84
  "@babel/plugin-syntax-import-assertions": "7.22.5",
86
85
  "babel-plugin-transform-async-to-promises": "0.8.18",
87
86
  "@jsenv/assert": "./packages/independent/assert/",
@@ -93,12 +92,12 @@
93
92
  "@jsenv/performance-impact": "4.1.1",
94
93
  "@jsenv/plugin-as-js-classic": "./packages/related/plugin-as-js-classic/",
95
94
  "@jsenv/test": "./packages/related/test/",
96
- "eslint": "8.46.0",
95
+ "eslint": "8.49.0",
97
96
  "eslint-plugin-html": "7.1.0",
98
- "eslint-plugin-import": "2.28.0",
99
- "eslint-plugin-react": "7.33.1",
97
+ "eslint-plugin-import": "2.28.1",
98
+ "eslint-plugin-react": "7.33.2",
100
99
  "open": "9.1.0",
101
- "playwright": "1.36.2",
102
- "prettier": "3.0.0"
100
+ "playwright": "1.37.1",
101
+ "prettier": "3.0.3"
103
102
  }
104
103
  }
package/src/main.js CHANGED
@@ -3,3 +3,6 @@ export { startDevServer } from "./dev/start_dev_server.js";
3
3
  // build
4
4
  export { build } from "./build/build.js";
5
5
  export { startBuildServer } from "./build/start_build_server.js";
6
+
7
+ // others
8
+ export { INJECTIONS } from "./plugins/injections/jsenv_plugin_injections.js";
@@ -1,19 +1,22 @@
1
1
  /*
2
2
  * Source code can contain the following
3
- * - __dev__
4
- * - __build__
5
- * A global will be injected with true/false when needed
3
+ * - __DEV__
4
+ * - __BUILD__
5
+ * That will be replaced with true/false
6
6
  */
7
7
 
8
- import { replacePlaceholders, PLACEHOLDER } from "@jsenv/plugin-injections";
8
+ import {
9
+ replacePlaceholders,
10
+ INJECTIONS,
11
+ } from "../injections/jsenv_plugin_injections.js";
9
12
 
10
13
  export const jsenvPluginGlobalScenarios = () => {
11
14
  const transformIfNeeded = (urlInfo) => {
12
15
  return replacePlaceholders(
13
16
  urlInfo.content,
14
17
  {
15
- __DEV__: PLACEHOLDER.optional(urlInfo.context.dev),
16
- __BUILD__: PLACEHOLDER.optional(urlInfo.context.build),
18
+ __DEV__: INJECTIONS.optional(urlInfo.context.dev),
19
+ __BUILD__: INJECTIONS.optional(urlInfo.context.build),
17
20
  },
18
21
  urlInfo,
19
22
  );
@@ -0,0 +1,59 @@
1
+ import { createMagicSource } from "@jsenv/sourcemap";
2
+ import {
3
+ parseHtmlString,
4
+ injectHtmlNodeAsEarlyAsPossible,
5
+ createHtmlNode,
6
+ stringifyHtmlAst,
7
+ } from "@jsenv/ast";
8
+
9
+ export const injectGlobals = (content, globals, urlInfo) => {
10
+ if (urlInfo.type === "html") {
11
+ return globalInjectorOnHtml(content, globals, urlInfo);
12
+ }
13
+ if (urlInfo.type === "js_classic" || urlInfo.type === "js_module") {
14
+ return globalsInjectorOnJs(content, globals, urlInfo);
15
+ }
16
+ throw new Error(`cannot inject globals into "${urlInfo.type}"`);
17
+ };
18
+
19
+ const globalInjectorOnHtml = (content, globals) => {
20
+ // ideally we would inject an importmap but browser support is too low
21
+ // (even worse for worker/service worker)
22
+ // so for now we inject code into entry points
23
+ const htmlAst = parseHtmlString(content, {
24
+ storeOriginalPositions: false,
25
+ });
26
+ const clientCode = generateClientCodeForGlobals(globals, {
27
+ isWebWorker: false,
28
+ });
29
+ injectHtmlNodeAsEarlyAsPossible(
30
+ htmlAst,
31
+ createHtmlNode({
32
+ tagName: "script",
33
+ textContent: clientCode,
34
+ }),
35
+ "jsenv:inject_globals",
36
+ );
37
+ return stringifyHtmlAst(htmlAst);
38
+ };
39
+
40
+ const globalsInjectorOnJs = (content, globals, urlInfo) => {
41
+ const clientCode = generateClientCodeForGlobals(globals, {
42
+ isWebWorker:
43
+ urlInfo.subtype === "worker" ||
44
+ urlInfo.subtype === "service_worker" ||
45
+ urlInfo.subtype === "shared_worker",
46
+ });
47
+ const magicSource = createMagicSource(content);
48
+ magicSource.prepend(clientCode);
49
+ return magicSource.toContentAndSourcemap();
50
+ };
51
+
52
+ const generateClientCodeForGlobals = (globals, { isWebWorker = false }) => {
53
+ const globalName = isWebWorker ? "self" : "window";
54
+ return `Object.assign(${globalName}, ${JSON.stringify(
55
+ globals,
56
+ null,
57
+ " ",
58
+ )});`;
59
+ };
@@ -0,0 +1,101 @@
1
+ import { URL_META } from "@jsenv/url-meta";
2
+ import { asUrlWithoutSearch } from "@jsenv/urls";
3
+ import { createMagicSource } from "@jsenv/sourcemap";
4
+
5
+ export const jsenvPluginInjections = (rawAssociations) => {
6
+ let resolvedAssociations;
7
+
8
+ return {
9
+ name: "jsenv:injections",
10
+ appliesDuring: "*",
11
+ init: (context) => {
12
+ resolvedAssociations = URL_META.resolveAssociations(
13
+ { injectionsGetter: rawAssociations },
14
+ context.rootDirectoryUrl,
15
+ );
16
+ },
17
+ transformUrlContent: async (urlInfo) => {
18
+ const { injectionsGetter } = URL_META.applyAssociations({
19
+ url: asUrlWithoutSearch(urlInfo.url),
20
+ associations: resolvedAssociations,
21
+ });
22
+ if (!injectionsGetter) {
23
+ return null;
24
+ }
25
+ if (typeof injectionsGetter !== "function") {
26
+ throw new TypeError("injectionsGetter must be a function");
27
+ }
28
+ const injections = await injectionsGetter(urlInfo);
29
+ if (!injections) {
30
+ return null;
31
+ }
32
+ const keys = Object.keys(injections);
33
+ if (keys.length === 0) {
34
+ return null;
35
+ }
36
+ return replacePlaceholders(urlInfo.content, injections, urlInfo);
37
+ },
38
+ };
39
+ };
40
+
41
+ const injectionSymbol = Symbol.for("jsenv_injection");
42
+ export const INJECTIONS = {
43
+ optional: (value) => {
44
+ return { [injectionSymbol]: "optional", value };
45
+ },
46
+ };
47
+
48
+ // we export this because it is imported by jsenv_plugin_placeholder.js and unit test
49
+ export const replacePlaceholders = (content, replacements, urlInfo) => {
50
+ const magicSource = createMagicSource(content);
51
+ for (const key of Object.keys(replacements)) {
52
+ let index = content.indexOf(key);
53
+ const replacement = replacements[key];
54
+ let isOptional;
55
+ let value;
56
+ if (replacement && replacement[injectionSymbol]) {
57
+ const valueBehindSymbol = replacement[injectionSymbol];
58
+ isOptional = valueBehindSymbol === "optional";
59
+ value = replacement.value;
60
+ } else {
61
+ value = replacement;
62
+ }
63
+ if (index === -1) {
64
+ if (!isOptional) {
65
+ urlInfo.context.logger.warn(
66
+ `placeholder "${key}" not found in ${urlInfo.url}.
67
+ --- suggestion a ---
68
+ Add "${key}" in that file.
69
+ --- suggestion b ---
70
+ Fix eventual typo in "${key}"?
71
+ --- suggestion c ---
72
+ Mark injection as optional using INJECTIONS.optional()
73
+
74
+ import { INJECTIONS } from "@jsenv/core";
75
+
76
+ return {
77
+ "${key}": INJECTIONS.optional(${JSON.stringify(value)}),
78
+ }`,
79
+ );
80
+ }
81
+ continue;
82
+ }
83
+
84
+ while (index !== -1) {
85
+ const start = index;
86
+ const end = index + key.length;
87
+ magicSource.replace({
88
+ start,
89
+ end,
90
+ replacement:
91
+ urlInfo.type === "js_classic" ||
92
+ urlInfo.type === "js_module" ||
93
+ urlInfo.type === "html"
94
+ ? JSON.stringify(value, null, " ")
95
+ : value,
96
+ });
97
+ index = content.indexOf(key, end);
98
+ }
99
+ }
100
+ return magicSource.toContentAndSourcemap();
101
+ };
@@ -1,5 +1,4 @@
1
1
  import { jsenvPluginSupervisor } from "@jsenv/plugin-supervisor";
2
- import { jsenvPluginInjections } from "@jsenv/plugin-injections";
3
2
  import { jsenvPluginTranspilation } from "@jsenv/plugin-transpilation";
4
3
 
5
4
  import { jsenvPluginReferenceAnalysis } from "./reference_analysis/jsenv_plugin_reference_analysis.js";
@@ -9,6 +8,7 @@ import { jsenvPluginWebResolution } from "./resolution_web/jsenv_plugin_web_reso
9
8
  import { jsenvPluginVersionSearchParam } from "./version_search_param/jsenv_plugin_version_search_param.js";
10
9
  import { jsenvPluginProtocolFile } from "./protocol_file/jsenv_plugin_protocol_file.js";
11
10
  import { jsenvPluginProtocolHttp } from "./protocol_http/jsenv_plugin_protocol_http.js";
11
+ import { jsenvPluginInjections } from "./injections/jsenv_plugin_injections.js";
12
12
  import { jsenvPluginInlining } from "./inlining/jsenv_plugin_inlining.js";
13
13
  import { jsenvPluginCommonJsGlobals } from "./commonjs_globals/jsenv_plugin_commonjs_globals.js";
14
14
  import { jsenvPluginImportMetaScenarios } from "./import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js";
@@ -1,5 +1,6 @@
1
1
  import { createMagicSource } from "@jsenv/sourcemap";
2
2
  import { parseJsUrls, getUrlForContentInsideJs } from "@jsenv/ast";
3
+ import { urlToExtension } from "@jsenv/urls";
3
4
  import { JS_QUOTES } from "@jsenv/utils/src/string/js_quotes.js";
4
5
 
5
6
  import { isWebWorkerUrlInfo } from "@jsenv/core/src/kitchen/web_workers.js";
@@ -34,6 +35,8 @@ const parseAndTransformJsReferences = async (
34
35
  const magicSource = createMagicSource(urlInfo.content);
35
36
  const parallelActions = [];
36
37
  const sequentialActions = [];
38
+ const isNodeJs =
39
+ Object.keys(urlInfo.context.runtimeCompat).toString() === "node";
37
40
 
38
41
  const onInlineReference = (inlineReferenceInfo) => {
39
42
  const inlineUrl = getUrlForContentInsideJs(inlineReferenceInfo, {
@@ -81,6 +84,19 @@ const parseAndTransformJsReferences = async (
81
84
  ) {
82
85
  urlInfo.data.usesImport = true;
83
86
  }
87
+ if (
88
+ isNodeJs &&
89
+ externalReferenceInfo.type === "js_url" &&
90
+ externalReferenceInfo.expectedSubtype === "worker" &&
91
+ externalReferenceInfo.expectedType === "js_classic" &&
92
+ // TODO: it's true also if closest package.json
93
+ // is type: module
94
+ urlToExtension(
95
+ new URL(externalReferenceInfo.specifier, urlInfo.url).href,
96
+ ) === ".mjs"
97
+ ) {
98
+ externalReferenceInfo.expectedType = "js_module";
99
+ }
84
100
  const reference = urlInfo.dependencies.found({
85
101
  type: externalReferenceInfo.type,
86
102
  subtype: externalReferenceInfo.subtype,
@@ -123,6 +139,7 @@ const parseAndTransformJsReferences = async (
123
139
  isJsModule: urlInfo.type === "js_module",
124
140
  isWebWorker: isWebWorkerUrlInfo(urlInfo),
125
141
  inlineContent,
142
+ isNodeJs,
126
143
  });
127
144
  for (const jsReferenceInfo of jsReferenceInfos) {
128
145
  if (jsReferenceInfo.isInline) {