@optionfactory/fml 8.0.0 → 8.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 (45) hide show
  1. package/dist/client-errors.iife.js +11 -8
  2. package/dist/client-errors.iife.js.map +1 -1
  3. package/dist/client-errors.iife.min.js +1 -1
  4. package/dist/client-errors.iife.min.js.map +1 -1
  5. package/dist/fml.css +10 -1
  6. package/dist/fml.css.map +1 -1
  7. package/dist/fml.d.mts +73 -13
  8. package/dist/fml.iife.js +598 -208
  9. package/dist/fml.iife.js.map +1 -1
  10. package/dist/fml.iife.min.js +1 -1
  11. package/dist/fml.iife.min.js.map +1 -1
  12. package/dist/fml.min.mjs +1 -1
  13. package/dist/fml.min.mjs.map +1 -1
  14. package/dist/fml.mjs +598 -208
  15. package/dist/fml.mjs.map +1 -1
  16. package/dist/ftl.d.mts +2 -1
  17. package/dist/ftl.iife.js +60 -33
  18. package/dist/ftl.iife.js.map +1 -1
  19. package/dist/ftl.iife.min.js +1 -1
  20. package/dist/ftl.iife.min.js.map +1 -1
  21. package/dist/ftl.min.mjs +1 -1
  22. package/dist/ftl.min.mjs.map +1 -1
  23. package/dist/ftl.mjs +60 -33
  24. package/dist/ftl.mjs.map +1 -1
  25. package/dist/ful.css +10 -1
  26. package/dist/ful.css.map +1 -1
  27. package/dist/ful.d.mts +22 -6
  28. package/dist/ful.iife.js +496 -166
  29. package/dist/ful.iife.js.map +1 -1
  30. package/dist/ful.iife.min.js +1 -1
  31. package/dist/ful.iife.min.js.map +1 -1
  32. package/dist/ful.min.mjs +1 -1
  33. package/dist/ful.min.mjs.map +1 -1
  34. package/dist/ful.mjs +496 -166
  35. package/dist/ful.mjs.map +1 -1
  36. package/dist/httpc.d.mts +49 -6
  37. package/dist/httpc.iife.js +40 -9
  38. package/dist/httpc.iife.js.map +1 -1
  39. package/dist/httpc.iife.min.js +1 -1
  40. package/dist/httpc.iife.min.js.map +1 -1
  41. package/dist/httpc.min.mjs +1 -1
  42. package/dist/httpc.min.mjs.map +1 -1
  43. package/dist/httpc.mjs +40 -9
  44. package/dist/httpc.mjs.map +1 -1
  45. package/package.json +6 -7
package/dist/ftl.mjs CHANGED
@@ -3457,7 +3457,7 @@ class EvaluatingVisitor {
3457
3457
  case '<=':
3458
3458
  return lhs <= rhs;
3459
3459
  default:
3460
- throw new Error('unknown cmp op ' + node.op);
3460
+ throw new Error(`unknown cmp op ${node.op}`);
3461
3461
  }
3462
3462
  }
3463
3463
  [nodes.call](node) {
@@ -3468,7 +3468,7 @@ class EvaluatingVisitor {
3468
3468
  }
3469
3469
  const fn = module[fnRef.value];
3470
3470
  if (!fn) {
3471
- throw new Error(`Function "#${fnRef.module === null ? '' : fnRef.module + ':'}${fnRef.value}" not found`);
3471
+ throw new Error(`Function "#${fnRef.module === null ? '' : `${fnRef.module}:`}${fnRef.value}" not found`);
3472
3472
  }
3473
3473
  const args = node.args.map((arg) => this.visit(arg));
3474
3474
  return fn.apply(this.#resolve_proxy(), args);
@@ -3504,14 +3504,14 @@ class EvaluatingVisitor {
3504
3504
  return cond ? cond : this.visit(node.ifFalse);
3505
3505
  }
3506
3506
  [nodes.access](node) {
3507
- let prev = undefined;
3507
+ let prev ;
3508
3508
  let cur = this.visit(node.lhs);
3509
3509
  for (let i = 0; i !== node.rhs.length; ++i) {
3510
3510
  const rhs = node.rhs[i];
3511
3511
  if (rhs.ns && cur == null) {
3512
3512
  return undefined;
3513
3513
  }
3514
- let value = undefined;
3514
+ let value ;
3515
3515
  switch (rhs.type) {
3516
3516
  case nodes.member: {
3517
3517
  value = cur[rhs.rhs];
@@ -3552,7 +3552,7 @@ class EvaluatingVisitor {
3552
3552
  case nodes.templated.ten:
3553
3553
  return { type: nodes.dom.n, value: this.visit(node.value) };
3554
3554
  default:
3555
- throw new Error('unknown node type ' + node.type.toString());
3555
+ throw new Error(`unknown node type ${node.type.toString()}`);
3556
3556
  }
3557
3557
  });
3558
3558
  }
@@ -3728,7 +3728,7 @@ class Attributes {
3728
3728
  .forEach((a) => {
3729
3729
  const target = a.substring(prefix.length);
3730
3730
  if (target === 'class') {
3731
- const classes = from.getAttribute(prefix + 'class')?.split(/\s+/).filter((a) => a.length) ?? [];
3731
+ const classes = from.getAttribute(`${prefix}class`)?.split(/\s+/).filter((a) => a.length) ?? [];
3732
3732
  to.classList.add(...classes);
3733
3733
  return;
3734
3734
  }
@@ -3820,8 +3820,7 @@ class Nodes {
3820
3820
  * @returns
3821
3821
  */
3822
3822
  static isParsed(el) {
3823
- //@ts-ignore
3824
- for (let c = el; c; c = c.parentNode) {
3823
+ for (let c = /** @type {Node | null} */ (el); c; c = c.parentNode) {
3825
3824
  if (c.nextSibling) {
3826
3825
  return true;
3827
3826
  }
@@ -3884,19 +3883,18 @@ class Nodes {
3884
3883
  }
3885
3884
 
3886
3885
  class NodeOperations {
3887
- #forRemoval;
3888
- constructor() {
3889
- this.#forRemoval = [];
3890
- }
3886
+ #forRemoval = new Set();
3891
3887
  removed(node) {
3892
- return this.#forRemoval.includes(node);
3888
+ return this.#forRemoval.has(node);
3893
3889
  }
3894
3890
  remove(node) {
3895
3891
  node.replaceChildren?.();
3896
3892
  Object.keys(node.dataset || {})
3897
3893
  .filter((k) => k.startsWith('tpl'))
3898
- .forEach((k) => delete node.dataset[k]);
3899
- this.#forRemoval.push(node);
3894
+ .forEach((k) => {
3895
+ delete node.dataset[k];
3896
+ });
3897
+ this.#forRemoval.add(node);
3900
3898
  }
3901
3899
  popData(node, key) {
3902
3900
  const v = node.dataset[key];
@@ -3914,9 +3912,10 @@ class NodeOperations {
3914
3912
  this.remove(ref);
3915
3913
  }
3916
3914
  cleanup() {
3917
- while (this.#forRemoval.length) {
3918
- this.#forRemoval.pop().remove();
3915
+ for (const node of this.#forRemoval) {
3916
+ node.remove();
3919
3917
  }
3918
+ this.#forRemoval.clear();
3920
3919
  }
3921
3920
  }
3922
3921
 
@@ -3971,7 +3970,7 @@ class CommandsHandler {
3971
3970
  }
3972
3971
  static tplRemove(node, value, ops, modules, dataStack) {
3973
3972
  switch (value.toLowerCase()) {
3974
- case 'tag':
3973
+ case 'tag': {
3975
3974
  const fragment = new DocumentFragment();
3976
3975
  while (node.firstChild) {
3977
3976
  fragment.appendChild(node.firstChild);
@@ -3985,6 +3984,7 @@ class CommandsHandler {
3985
3984
  ops.remove(node);
3986
3985
  }
3987
3986
  break;
3987
+ }
3988
3988
  case 'body':
3989
3989
  node.replaceChildren();
3990
3990
  break;
@@ -4050,8 +4050,10 @@ class CommandsHandler {
4050
4050
  }
4051
4051
  }
4052
4052
 
4053
- // Module-isolated string cache for dataset-to-attribute conversions
4053
+ // Module-isolated string cache for dataset-to-attribute conversions, bounded with
4054
+ // FIFO eviction like the expression ast cache
4054
4055
  const attributeCache = new Map();
4056
+ const ATTRIBUTE_CACHE_MAX_SIZE = 1000;
4055
4057
 
4056
4058
  /**
4057
4059
  * Converts a tpl camelCase dataset key into a kebab-case attribute name.
@@ -4062,6 +4064,9 @@ const attributeCache = new Map();
4062
4064
  function toAttr(dataSetKey) {
4063
4065
  let cached = attributeCache.get(dataSetKey);
4064
4066
  if (!cached) {
4067
+ if (attributeCache.size >= ATTRIBUTE_CACHE_MAX_SIZE) {
4068
+ attributeCache.delete(attributeCache.keys().next().value);
4069
+ }
4065
4070
  cached = dataSetKey
4066
4071
  .substring(3)
4067
4072
  .split(/(?=[A-Z])/)
@@ -4196,9 +4201,10 @@ class Template {
4196
4201
  * @param {string} expression
4197
4202
  * @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode]
4198
4203
  * @param {...*} data
4204
+ * @returns the evaluated expression result
4199
4205
  */
4200
4206
  evaluate(expression, mode, ...data) {
4201
- new ExpressionEvaluator(this.#modules, this.#dataStack).withOverlay(...data).evaluate(expression, mode);
4207
+ return new ExpressionEvaluator(this.#modules, this.#dataStack).withOverlay(...data).evaluate(expression, mode);
4202
4208
  }
4203
4209
  /**
4204
4210
  * Returns an expression evaluator with bound modules and dataStack.
@@ -4343,8 +4349,8 @@ class RenderError extends Error {
4343
4349
  if (node.nodeType === Node.TEXT_NODE) {
4344
4350
  node.nodeValue = node.nodeValue.trim();
4345
4351
  }
4346
- for (var n = 0; n < node.childNodes.length; n++) {
4347
- var child = node.childNodes[n];
4352
+ for (let n = 0; n < node.childNodes.length; n++) {
4353
+ const child = node.childNodes[n];
4348
4354
  if (child.nodeType === Node.TEXT_NODE) {
4349
4355
  child.nodeValue = child.nodeValue.trim();
4350
4356
  if (child.nodeValue.length === 0) {
@@ -4363,8 +4369,7 @@ class UpgradeQueue {
4363
4369
  #q = new Map();
4364
4370
  constructor() {
4365
4371
  document.addEventListener('DOMContentLoaded', async () => {
4366
- const pending = Array.from(this.entries).map(([child, promise]) => promise);
4367
- await Promise.all(pending);
4372
+ await this.settle();
4368
4373
  document.dispatchEvent(
4369
4374
  new CustomEvent('ftl:ready', {
4370
4375
  bubbles: false,
@@ -4384,6 +4389,16 @@ class UpgradeQueue {
4384
4389
  .finally(() => this.#q.delete(el));
4385
4390
  this.#q.set(el, promise);
4386
4391
  }
4392
+ /**
4393
+ * Waits for every queued upgrade, including the ones enqueued while waiting:
4394
+ * a component is only queued once its parent connects it, so a single pass would
4395
+ * miss everything nested.
4396
+ */
4397
+ async settle() {
4398
+ while (this.#q.size !== 0) {
4399
+ await Promise.all(Array.from(this.#q.values()));
4400
+ }
4401
+ }
4387
4402
  get entries() {
4388
4403
  return this.#q.entries();
4389
4404
  }
@@ -4578,18 +4593,30 @@ class Templates {
4578
4593
  }
4579
4594
  }
4580
4595
 
4581
- class Rendering {
4582
- static async waitFor(el) {
4596
+ /**
4597
+ * Waits for the queued upgrades matching the filter, including the ones enqueued while
4598
+ * waiting: a component is only queued once its parent connects it, so a single pass
4599
+ * would miss everything nested.
4600
+ * @param {(el: Element) => boolean} accept
4601
+ */
4602
+ const settle = async (accept) => {
4603
+ for (;;) {
4583
4604
  const pending = Array.from(registry.upgrades)
4584
- .filter(([child, promise]) => el.contains(child))
4585
- .map(([child, promise]) => promise);
4605
+ .filter(([child]) => accept(child))
4606
+ .map(([, promise]) => promise);
4607
+ if (pending.length === 0) {
4608
+ return;
4609
+ }
4586
4610
  await Promise.all(pending);
4587
4611
  }
4612
+ };
4613
+
4614
+ class Rendering {
4615
+ static async waitFor(el) {
4616
+ await settle((child) => el.contains(child));
4617
+ }
4588
4618
  static async waitForChildren(el) {
4589
- const pending = Array.from(registry.upgrades)
4590
- .filter(([child, promise]) => el !== child && el.contains(child))
4591
- .map(([child, promise]) => promise);
4592
- await Promise.all(pending);
4619
+ await settle((child) => el !== child && el.contains(child));
4593
4620
  }
4594
4621
  }
4595
4622
 
@@ -4631,7 +4658,7 @@ class ParsedElement extends HTMLElement {
4631
4658
  template(name) {
4632
4659
  const { modules, data } = registry.context();
4633
4660
  let t = this.#bits().TEMPLATES[name ?? 'default'].withData(data).withModules(modules);
4634
- for (let k of ['l10n', 'config']) {
4661
+ for (const k of ['l10n', 'config']) {
4635
4662
  const v = this.constructor[k];
4636
4663
  if (v) {
4637
4664
  t = t.withOverlay({ [k]: v });