@lucidkiln/core 1.1.0 → 1.2.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.
package/dist/node.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { f as Loader } from "./environment-DkfrPazl.js";
1
+ import { f as Loader } from "./environment-DBvRrOn0.js";
2
2
  //#region src/node.d.ts
3
3
  export type FileLoaderOptions = {
4
4
  /** Directory that template names are relative to. */
@@ -152,26 +152,14 @@ var Lexer = class {
152
152
  const standalone = applyStandaloneLines(this.#text, parts);
153
153
  const lines = textLines(this.#text, parts, standalone);
154
154
  const tokens = [];
155
- let line = 0;
156
- for (const part of parts) {
157
- const text = part.kind === "text";
158
- const at = text ? part.from : part.start;
159
- if (text && part.from < part.to) tokens.push({
155
+ for (const part of parts) if (part.kind === "text") {
156
+ if (part.from < part.to) tokens.push({
160
157
  type: "text",
161
158
  value: this.#text.slice(part.from, part.to),
162
159
  start: part.from,
163
160
  end: part.to
164
161
  });
165
- else if (lines[line]?.start === at) tokens.push({
166
- type: "text",
167
- value: "",
168
- start: at,
169
- end: at
170
- });
171
- if (!text && part.kind !== "comment") for (const token of part.tokens) tokens.push(token);
172
- const end = text ? part.to : part.start;
173
- while ((lines[line]?.start ?? Infinity) <= end) line++;
174
- }
162
+ } else if (part.kind !== "comment") for (const token of part.tokens) tokens.push(token);
175
163
  tokens.push({
176
164
  type: "eof",
177
165
  value: "",
@@ -701,9 +689,6 @@ const textLines = (text, parts, removed) => {
701
689
  indentEnd
702
690
  });
703
691
  };
704
- const first = parts[0];
705
- const start = contentStart(text);
706
- if (first && (first.kind !== "text" || first.from === start)) add(start);
707
692
  for (const part of parts) {
708
693
  if (part.kind !== "text") continue;
709
694
  for (let newline = newlineIn(text, part.start, part.end); newline !== -1; newline = newlineIn(text, newline + 1, part.end)) {
@@ -718,11 +703,10 @@ const textLines = (text, parts, removed) => {
718
703
  //#region src/parser/indent.ts
719
704
  /** The bodies a statement holds. Branches share their tag's body. A component's fills are bodies of their own, inside the component's. */
720
705
  const bodiesOf = (node) => {
721
- const own = (lists, full = false) => ({
706
+ const own = (lists) => ({
722
707
  open: node.span.start,
723
708
  end: node.span.end,
724
- lists,
725
- full
709
+ lists
726
710
  });
727
711
  switch (node.type) {
728
712
  case "If": return [own([...node.branches.map((branch) => branch.body), node.alternate ?? []])];
@@ -730,16 +714,15 @@ const bodiesOf = (node) => {
730
714
  case "Component": return [...node.fills.map((fill) => ({
731
715
  open: fill.span.start,
732
716
  end: fill.span.end,
733
- lists: [fill.body],
734
- full: true
735
- })), own([node.body, ...node.fills.map((fill) => fill.body)], true)];
717
+ lists: [fill.body]
718
+ })), own([node.body, ...node.fills.map((fill) => fill.body)])];
736
719
  case "Island": return [{
737
720
  ...own([node.body]),
738
721
  keep: true
739
722
  }];
740
723
  case "Block":
741
724
  case "Slot":
742
- case "Add": return [own([node.body], true)];
725
+ case "Add":
743
726
  case "SetBlock":
744
727
  case "Macro":
745
728
  case "Cache":
@@ -797,15 +780,16 @@ const preformatted = (text, textTokens) => {
797
780
  return ranges;
798
781
  };
799
782
  /**
800
- * Works out the indentation of a template's text, so content can be inserted elsewhere and still line up.
783
+ * Removes the extra indentation inside tags that stand on their own line: the spaces and tabs that every content line
784
+ * inside shares, beyond the tag's own indentation. It happens when compiling, so rendering is no slower.
801
785
  *
802
- * - Removes the extra indentation inside tags that stand on their own line: the spaces and tabs that every content line inside shares, beyond the tag's own. Blocks, slots and a component's content can be inserted where another tag stands, so they lose all of it and take the indentation of where they land.
803
- * - Sets `indent` on the tags in `placedNodes`, which insert content, when they stand on their own line: the line's indentation once that removal is done.
804
- * - Sets `lines` on text nodes: where each line of text starts, to take the indentation of where the text is inserted.
786
+ * Also sets the indentation of `islands` whose tags stand on lines of their own, since a deferred island writes its
787
+ * tags back as `<kiln-island>` lines. See `IslandStatement`.
805
788
  *
806
- * Lines inside `<pre>` and `<textarea>` keep their whitespace. Amounts come from the source alone, so nested tags each remove their own step whatever order they are visited in.
789
+ * Lines inside `<pre>` and `<textarea>` keep their whitespace. Amounts come from the source alone, so nested tags each
790
+ * remove their own step whatever order they are visited in. Then the text nodes holding those offsets are rebuilt.
807
791
  */
808
- const arrangeIndentation = (ast, text, { tokens, standalone, lines }, placedNodes) => {
792
+ const removeIndentation = (ast, text, { tokens, standalone, lines }, islands) => {
809
793
  const textTokens = tokens.filter((token) => token.type === "text");
810
794
  const pre = preformatted(text, textTokens);
811
795
  const inPre = (offset) => {
@@ -823,28 +807,19 @@ const arrangeIndentation = (ast, text, { tokens, standalone, lines }, placedNode
823
807
  indentEnd
824
808
  };
825
809
  };
826
- const placed = [];
827
- const islandEnds = [];
828
- for (const node of placedNodes) {
810
+ const islandLines = [];
811
+ for (const node of islands) {
829
812
  if (inPre(node.span.start)) continue;
830
- if (node.type === "Island") {
831
- const end = text.lastIndexOf("{%", node.span.end - 1);
832
- if (standalone.has(end)) islandEnds.push({
833
- node,
834
- line: lineOf(end)
835
- });
836
- }
837
- if (!standalone.has(node.span.start)) continue;
838
- placed.push({
813
+ const end = text.lastIndexOf("{%", node.span.end - 1);
814
+ const open = standalone.has(node.span.start) ? lineOf(node.span.start) : null;
815
+ const close = standalone.has(end) ? lineOf(end) : null;
816
+ if (open || close) islandLines.push({
839
817
  node,
840
- line: lineOf(node.span.start)
818
+ open,
819
+ end: close
841
820
  });
842
821
  }
843
- const contentLines = placed.length > 0 || islandEnds.length > 0 ? [
844
- ...textLines,
845
- ...placed.map((item) => item.line),
846
- ...islandEnds.map((item) => item.line)
847
- ].sort((a, b) => a.start - b.start) : textLines;
822
+ const contentLines = islandLines.length > 0 ? [...textLines, ...islandLines.flatMap((item) => [item.open, item.end].filter((line) => line !== null))].sort((a, b) => a.start - b.start) : textLines;
848
823
  const cuts = [];
849
824
  const indentOf = (offset) => {
850
825
  const line = lineOf(offset);
@@ -860,6 +835,7 @@ const arrangeIndentation = (ast, text, { tokens, standalone, lines }, placedNode
860
835
  const first = firstAtLeast(contentLines, newline + 1, (line) => line.start);
861
836
  const last = firstAtLeast(contentLines, to, (line) => line.start);
862
837
  if (first >= last) return;
838
+ const own = indentOf(body.open);
863
839
  let shared = Infinity;
864
840
  const firstLine = contentLines[first];
865
841
  for (let i = first; i < last; i++) {
@@ -868,16 +844,11 @@ const arrangeIndentation = (ast, text, { tokens, standalone, lines }, placedNode
868
844
  while (length < line.indentEnd - line.start && text[line.start + length] === text[firstLine.start + length]) length++;
869
845
  shared = Math.min(shared, length);
870
846
  }
871
- let keep = 0;
872
- if (!body.full) {
873
- const own = indentOf(body.open);
874
- if (shared <= own.length) return;
875
- if (text.slice(firstLine.start, firstLine.start + own.length) !== own) return;
876
- keep = own.length;
877
- } else if (shared === 0) return;
847
+ if (shared <= own.length) return;
848
+ if (text.slice(firstLine.start, firstLine.start + own.length) !== own) return;
878
849
  for (let i = first; i < last; i++) {
879
850
  const line = contentLines[i];
880
- cuts.push([line.start + keep, line.start + shared]);
851
+ cuts.push([line.start + own.length, line.start + shared]);
881
852
  }
882
853
  };
883
854
  const visit = (list) => {
@@ -908,37 +879,28 @@ const arrangeIndentation = (ast, text, { tokens, standalone, lines }, placedNode
908
879
  }
909
880
  return out + text.slice(Math.min(at, end), end);
910
881
  };
911
- for (const { node, line } of placed) node.indent = uncut(line.start, line.indentEnd);
912
- for (const { node, line } of islandEnds) node.closing = {
913
- indent: line.start === lineOf(node.span.start).start ? null : uncut(line.start, line.indentEnd),
914
- newline: text.includes("\n", node.span.end)
915
- };
882
+ for (const { node, open, end } of islandLines) {
883
+ if (open) node.indent = uncut(open.start, open.indentEnd);
884
+ if (end) node.closing = {
885
+ indent: open !== null && end.start === open.start ? null : uncut(end.start, end.indentEnd),
886
+ newline: text.includes("\n", node.span.end)
887
+ };
888
+ }
889
+ if (merged.length === 0) return;
916
890
  const hasCut = (start, end) => {
917
891
  const range = merged[firstAtLeast(merged, start, (range) => range[1] - 1)];
918
892
  return range !== void 0 && range[0] < end;
919
893
  };
920
894
  const rewrite = (list) => {
921
895
  for (const node of list) {
922
- if (node.type === "Text") {
923
- let line = firstAtLeast(textLines, node.span.start, (l) => l.start);
924
- const hasLines = (textLines[line]?.start ?? Infinity) <= node.span.end;
925
- const cut = hasCut(node.span.start, node.span.end);
926
- if (!hasLines && !cut) continue;
896
+ if (node.type === "Text" && hasCut(node.span.start, node.span.end)) {
927
897
  let value = "";
928
- let size = 0;
929
- const starts = [];
930
898
  for (let i = firstAtLeast(textTokens, node.span.start, (t) => t.start); i < textTokens.length; i++) {
931
899
  const token = textTokens[i];
932
900
  if (token.end > node.span.end) break;
933
- for (let next = textLines[line]; next !== void 0 && next.start <= token.end; next = textLines[++line]) starts.push(size + (cut ? uncut(token.start, next.start).length : next.start - token.start));
934
- if (cut) {
935
- const piece = uncut(token.start, token.end);
936
- value += piece;
937
- size += piece.length;
938
- } else size += token.end - token.start;
901
+ value += uncut(token.start, token.end);
939
902
  }
940
- if (cut) node.value = value;
941
- if (starts.length > 0) node.lines = starts;
903
+ node.value = value;
942
904
  }
943
905
  const bodies = bodiesOf(node);
944
906
  if (bodies.length > 0) for (const child of childLists(bodies)) rewrite(child);
@@ -967,7 +929,7 @@ const parse = (text, options) => {
967
929
  const lexed = lex(text);
968
930
  const parser = new Parser(lexed.tokens, options);
969
931
  const ast = parser.parseTemplate(text.length);
970
- arrangeIndentation(ast, text, lexed, parser.placed);
932
+ removeIndentation(ast, text, lexed, parser.islands);
971
933
  return {
972
934
  ast,
973
935
  problems: [...lexed.problems, ...parser.problems]
@@ -1018,8 +980,8 @@ const describeToken = (token) => {
1018
980
  };
1019
981
  var Parser = class {
1020
982
  problems = [];
1021
- /** Every tag that inserts content, for `arrangeIndentation`. */
1022
- placed = [];
983
+ /** Every island, for `removeIndentation`. */
984
+ islands = [];
1023
985
  #tokens;
1024
986
  #options;
1025
987
  #index = 0;
@@ -1275,14 +1237,14 @@ var Parser = class {
1275
1237
  case "outlet": {
1276
1238
  const name = this.#parseStringLiteral("an outlet name");
1277
1239
  const close = this.#expectTagEnd();
1278
- return this.#placed({
1240
+ return {
1279
1241
  type: "Outlet",
1280
1242
  name,
1281
1243
  span: {
1282
1244
  start: open.start,
1283
1245
  end: close.end
1284
1246
  }
1285
- });
1247
+ };
1286
1248
  }
1287
1249
  case "flush": {
1288
1250
  const close = this.#expectTagEnd();
@@ -1593,7 +1555,7 @@ var Parser = class {
1593
1555
  if (this.#peek().type !== "tagClose") {
1594
1556
  const expression = this.#parseExpression();
1595
1557
  const close = this.#expectTagEnd();
1596
- return this.#placed({
1558
+ return {
1597
1559
  type: "Block",
1598
1560
  name,
1599
1561
  body: [{
@@ -1608,13 +1570,13 @@ var Parser = class {
1608
1570
  start: open.start,
1609
1571
  end: close.end
1610
1572
  }
1611
- });
1573
+ };
1612
1574
  }
1613
1575
  this.#expectTagEnd();
1614
1576
  const { body, end } = this.#parseBody(["endblock"]);
1615
1577
  if (end) this.#finishEndTag(name);
1616
1578
  else this.#unclosed("block", open, "endblock");
1617
- return this.#placed({
1579
+ return {
1618
1580
  type: "Block",
1619
1581
  name,
1620
1582
  body,
@@ -1622,7 +1584,7 @@ var Parser = class {
1622
1584
  start: open.start,
1623
1585
  end: this.#lastEnd
1624
1586
  }
1625
- });
1587
+ };
1626
1588
  }
1627
1589
  #parseInclude(open) {
1628
1590
  const template = this.#parseExpression();
@@ -1638,7 +1600,7 @@ var Parser = class {
1638
1600
  ignoreMissing = true;
1639
1601
  }
1640
1602
  const close = this.#expectTagEnd();
1641
- return this.#placed({
1603
+ return {
1642
1604
  type: "Include",
1643
1605
  template,
1644
1606
  with: withValue,
@@ -1647,7 +1609,7 @@ var Parser = class {
1647
1609
  start: open.start,
1648
1610
  end: close.end
1649
1611
  }
1650
- });
1612
+ };
1651
1613
  }
1652
1614
  #parseComponent(open) {
1653
1615
  const header = this.#header(() => {
@@ -1696,7 +1658,7 @@ var Parser = class {
1696
1658
  }
1697
1659
  });
1698
1660
  }
1699
- return this.#placed({
1661
+ return {
1700
1662
  type: "Component",
1701
1663
  ...header,
1702
1664
  fills,
@@ -1705,11 +1667,7 @@ var Parser = class {
1705
1667
  start: open.start,
1706
1668
  end: this.#lastEnd
1707
1669
  }
1708
- });
1709
- }
1710
- #placed(node) {
1711
- this.placed.push(node);
1712
- return node;
1670
+ };
1713
1671
  }
1714
1672
  #parseStringLiteral(what) {
1715
1673
  const token = this.#peek();
@@ -1722,7 +1680,7 @@ var Parser = class {
1722
1680
  const { body, end } = this.#parseBody(["endslot"]);
1723
1681
  if (end) this.#finishEndTag();
1724
1682
  else this.#unclosed("slot", open, "endslot");
1725
- return this.#placed({
1683
+ return {
1726
1684
  type: "Slot",
1727
1685
  name,
1728
1686
  body,
@@ -1730,7 +1688,7 @@ var Parser = class {
1730
1688
  start: open.start,
1731
1689
  end: this.#lastEnd
1732
1690
  }
1733
- });
1691
+ };
1734
1692
  }
1735
1693
  #parseMacro(open) {
1736
1694
  const header = this.#header(() => {
@@ -1898,7 +1856,7 @@ var Parser = class {
1898
1856
  const { body, end } = this.#parseBody(["endisland"]);
1899
1857
  if (end) this.#finishEndTag();
1900
1858
  else this.#unclosed("island", open, "endisland");
1901
- return this.#placed({
1859
+ const island = {
1902
1860
  type: "Island",
1903
1861
  ...header,
1904
1862
  body,
@@ -1906,7 +1864,9 @@ var Parser = class {
1906
1864
  start: open.start,
1907
1865
  end: this.#lastEnd
1908
1866
  }
1909
- });
1867
+ };
1868
+ this.islands.push(island);
1869
+ return island;
1910
1870
  }
1911
1871
  #parseApply(open) {
1912
1872
  const filters = this.#header(() => {
@@ -2664,4 +2624,4 @@ var Parser = class {
2664
2624
  //#endregion
2665
2625
  export { ESCAPE_STRATEGIES as a, DEFAULT_SLOT as i, contentStart as n, scanParts as r, parse as t };
2666
2626
 
2667
- //# sourceMappingURL=parser-BU5dThuk.js.map
2627
+ //# sourceMappingURL=parser-isboTef0.js.map