@marko/language-tools 2.4.5 → 2.4.7

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/index.mjs CHANGED
@@ -5,7 +5,7 @@ var __export = (target, all) => {
5
5
  };
6
6
 
7
7
  // src/parser.ts
8
- import { TagType, createParser } from "htmljs-parser";
8
+ import { createParser, TagType } from "htmljs-parser";
9
9
 
10
10
  // src/util/get-node-at-offset.ts
11
11
  function getNodeAtOffset(offset, program) {
@@ -91,8 +91,8 @@ function childAtOffset(offset, children) {
91
91
  // src/parser.ts
92
92
  import {
93
93
  getLines,
94
- getPosition,
95
- getLocation
94
+ getLocation,
95
+ getPosition
96
96
  } from "htmljs-parser";
97
97
  var styleBlockReg = /((?:\.[^\s\\/:*?"<>|({]+)*)\s*\{/y;
98
98
  var UNFINISHED = Number.MAX_SAFE_INTEGER;
@@ -319,6 +319,8 @@ var Builder = class {
319
319
  );
320
320
  this.#comments = void 0;
321
321
  return TagType.statement;
322
+ case "server":
323
+ case "client":
322
324
  case "static":
323
325
  this.#program.static.push(
324
326
  this.#staticNode = {
@@ -811,144 +813,203 @@ function sortBySourceThenGenerated(a, b) {
811
813
  return delta === 0 ? b.generatedStart - a.generatedStart : delta;
812
814
  }
813
815
 
814
- // src/extractors/style/index.ts
815
- function extractStyle(opts) {
816
- const { parsed, lookup } = opts;
817
- const extractorsByExt = /* @__PURE__ */ new Map();
818
- const { read, program, code } = parsed;
819
- let placeholderId = 0;
820
- for (const node of program.static) {
821
- if (node.type === 27 /* Style */) {
822
- getExtractor(node.ext || ".css").copy(node.value);
823
- }
816
+ // src/extractors/html/keywords.ts
817
+ var builtinTagsRegex = /^(?:a(?:(?:bbr|cronym|ddress|pplet|r(?:ea|ticle)|side|udio))?|b(?:(?:ase(?:font)?|d[io]|gsound|ig|l(?:ink|ockquote)|ody|r|utton))?|c(?:a(?:nvas|ption)|enter|ite|o(?:de|l(?:group)?|mmand|ntent))|d(?:ata(?:list)?|d|e(?:l|tails)|fn|i(?:alog|r|v)|l|t)|e(?:lement|m(?:bed)?)|f(?:i(?:eldset|g(?:caption|ure))|o(?:nt|oter|rm)|rame(?:set)?)|h(?:1|2|3|4|5|6|ead(?:er)?|group|r|tml)|i(?:(?:frame|m(?:age|g)|n(?:put|s)|sindex))?|k(?:bd|eygen)|l(?:abel|egend|i(?:(?:nk|sting))?)|m(?:a(?:in|p|r(?:k|quee)|th)|e(?:nu(?:item)?|t(?:a|er))|ulticol)|n(?:av|extid|o(?:br|embed|frames|script))|o(?:bject|l|pt(?:group|ion)|utput)|p(?:(?:aram|icture|laintext|r(?:e|ogress)))?|q|r(?:bc?|p|tc?|uby)|s(?:(?:amp|cript|e(?:ction|lect)|hadow|lot|mall|ource|pa(?:cer|n)|t(?:r(?:ike|ong)|yle)|u(?:b|mmary|p)|vg))?|t(?:able|body|d|e(?:mplate|xtarea)|foot|h(?:ead)?|i(?:me|tle)|r(?:ack)?|t)|ul?|v(?:ar|ideo)|wbr|xmp)$/;
818
+ function isHTMLTag(tag) {
819
+ return builtinTagsRegex.test(tag);
820
+ }
821
+ function getAttributeValueType(value) {
822
+ if (value === void 0 || value[0] !== "=") return void 0;
823
+ value = value.substring(1).trim();
824
+ switch (value) {
825
+ case "NaN":
826
+ case "Infinity":
827
+ case "-Infinity":
828
+ return 1 /* Literal */;
829
+ case "null":
830
+ case "false":
831
+ case "undefined":
832
+ return void 0;
833
+ case "true":
834
+ return 0 /* True */;
824
835
  }
825
- for (const node of program.body) {
826
- visit(node);
836
+ if (
837
+ // double quote string
838
+ /^"(?:[^"\\]+|\\.)*"$/.test(value) || // single quote string
839
+ /^'(?:[^'\\]+|\\.)*'$/.test(value) || // template literal without any interpolations
840
+ /^`(?:[^`\\$]+|\\.|\$(?!\{))*`$/.test(value)
841
+ ) {
842
+ return 2 /* QuotedString */;
843
+ } else if (
844
+ // octal literal
845
+ /^-?0[oO]?[0-7](?:_?[0-7]+)*n?$/.test(value) || // hex literal
846
+ /^-?0[xX][0-9a-fA-F](?:_?[0-9a-fA-F]+)*n?$/.test(value) || // binary literal
847
+ /^-?0[bB][01](?:_?[01]+)*n?$/.test(value) || // integer or float
848
+ /^-?\d(?:_?\d+)*(?:[.eE]\d(?:_?\d+)*|n?|\.?)$/.test(value)
849
+ ) {
850
+ return 1 /* Literal */;
827
851
  }
828
- const extractedByExt = /* @__PURE__ */ new Map();
829
- for (const [ext, extractor] of extractorsByExt) {
830
- extractedByExt.set(ext, extractor.end());
852
+ return 3 /* Dynamic */;
853
+ }
854
+
855
+ // src/extractors/html/index.ts
856
+ function extractHTML(parsed) {
857
+ return new HTMLExtractor(parsed).end();
858
+ }
859
+ var HTMLExtractor = class {
860
+ #extractor;
861
+ #read;
862
+ #nodeDetails;
863
+ #nodeIdCounter;
864
+ constructor(parsed) {
865
+ this.#extractor = new Extractor(parsed);
866
+ this.#read = parsed.read.bind(parsed);
867
+ this.#nodeDetails = {};
868
+ this.#nodeIdCounter = 0;
869
+ parsed.program.body.forEach((node) => this.#visitNode(node));
831
870
  }
832
- return extractedByExt;
833
- function visit(node) {
834
- var _a, _b;
871
+ end() {
872
+ return { extracted: this.#extractor.end(), nodeDetails: this.#nodeDetails };
873
+ }
874
+ #visitNode(node) {
875
+ var _a;
876
+ let hasDynamicBody = false, hasDynamicAttrs = false, isDynamic = false;
835
877
  switch (node.type) {
836
878
  case 16 /* AttrTag */:
837
- if (node.body) {
838
- for (const child of node.body) {
839
- visit(child);
840
- }
841
- }
879
+ (_a = node.body) == null ? void 0 : _a.forEach((child) => {
880
+ if (this.#visitNode(child)) hasDynamicBody = true;
881
+ });
842
882
  break;
843
- case 1 /* Tag */:
844
- if (node.body) {
845
- if (node.nameText === "style") {
846
- const ext = node.shorthandClassNames ? read(node.shorthandClassNames.at(-1)) : ".css";
847
- for (const child of node.body) {
848
- switch (child.type) {
849
- case 17 /* Text */:
850
- getExtractor(ext).copy(child);
851
- break;
852
- case 22 /* Placeholder */:
853
- getExtractor(ext).write(`var(--_${placeholderId++})`);
854
- break;
855
- }
856
- }
857
- } else {
858
- for (const child of node.body) {
859
- visit(child);
860
- }
861
- }
862
- }
863
- if (node.attrs) {
864
- for (const attr of node.attrs) {
865
- if (
866
- // Check for string literal attribute values.
867
- attr.type === 10 /* AttrNamed */ && ((_a = attr.value) == null ? void 0 : _a.type) === 13 /* AttrValue */ && /^['"]$/.test(code[attr.value.value.start])
868
- ) {
869
- const name = read(attr.name);
870
- if (name === "#style" || name === "style" && node.nameText && name === "style" && ((_b = lookup.getTag(node.nameText)) == null ? void 0 : _b.html)) {
871
- getExtractor("css").write(":root{").copy({
872
- start: attr.value.value.start + 1,
873
- end: attr.value.value.end - 1
874
- }).write("}");
875
- }
876
- }
877
- }
878
- }
883
+ case 1 /* Tag */: {
884
+ const nodeId = `${this.#nodeIdCounter++}`;
885
+ ({ isDynamic, hasDynamicAttrs, hasDynamicBody } = this.#writeTag(
886
+ node,
887
+ nodeId
888
+ ));
889
+ this.#nodeDetails[nodeId] = { hasDynamicAttrs, hasDynamicBody };
890
+ break;
891
+ }
892
+ case 17 /* Text */:
893
+ this.#extractor.copy(node);
894
+ break;
895
+ case 22 /* Placeholder */:
896
+ isDynamic = this.#read({
897
+ start: node.start + 1,
898
+ end: node.start + 2
899
+ }) === "!";
900
+ this.#extractor.write("placeholder");
879
901
  break;
880
902
  }
903
+ return isDynamic || hasDynamicBody;
881
904
  }
882
- function getExtractor(ext) {
883
- let extractor = extractorsByExt.get(ext);
884
- if (!extractor) {
885
- extractorsByExt.set(ext, extractor = new Extractor(parsed));
905
+ #writeTag(node, id) {
906
+ const isDynamic = !node.nameText || !isHTMLTag(node.nameText);
907
+ let hasDynamicAttrs = false, hasDynamicBody = false;
908
+ if (!isDynamic) {
909
+ ({ hasDynamicAttrs, hasDynamicBody } = this.#writeHTMLTag(node, id));
910
+ } else {
911
+ this.#writeCustomTag(node);
886
912
  }
887
- return extractor;
913
+ return { isDynamic, hasDynamicAttrs, hasDynamicBody };
888
914
  }
889
- }
890
-
891
- // src/extractors/script/index.ts
892
- import { relativeImportPath } from "relative-import-path";
893
-
894
- // src/extractors/script/util/script-parser.ts
895
- import {
896
- parseExpression,
897
- parse as parseStatement
898
- } from "@babel/parser";
899
- var plugins = [
900
- "exportDefaultFrom",
901
- "importAssertions",
902
- "typescript"
903
- ];
904
- var ScriptParser = class {
905
- #sourceFileName;
906
- #whitespace;
907
- constructor(sourceFileName, code) {
908
- this.#sourceFileName = sourceFileName;
909
- this.#whitespace = code.replace(/[^\s]/g, " ");
915
+ #writeHTMLTag(node, id) {
916
+ var _a, _b;
917
+ let hasDynamicAttrs = false, hasDynamicBody = false;
918
+ this.#extractor.write("<");
919
+ this.#extractor.copy(node.name);
920
+ this.#extractor.write(` data-marko-node-id="${id}"`);
921
+ (_a = node.attrs) == null ? void 0 : _a.forEach((attr) => {
922
+ if (attr.type === 10 /* AttrNamed */) this.#writeAttrNamed(attr);
923
+ else if (attr.type === 15 /* AttrSpread */) hasDynamicAttrs = true;
924
+ });
925
+ this.#extractor.write(">");
926
+ if (!isVoidTag(node.nameText)) {
927
+ (_b = node.body) == null ? void 0 : _b.forEach((child) => {
928
+ if (this.#visitNode(child)) hasDynamicBody = true;
929
+ });
930
+ this.#extractor.write(`</${node.nameText}>`);
931
+ }
932
+ return { hasDynamicAttrs, hasDynamicBody };
910
933
  }
911
- statementAt(offset, src) {
912
- try {
913
- return parseStatement(this.#whitespace.slice(0, offset) + src, {
914
- plugins,
915
- strictMode: true,
916
- errorRecovery: true,
917
- sourceType: "module",
918
- allowUndeclaredExports: true,
919
- allowSuperOutsideMethod: true,
920
- allowAwaitOutsideFunction: true,
921
- allowReturnOutsideFunction: true,
922
- sourceFilename: this.#sourceFileName
923
- }).program.body;
924
- } catch {
925
- return [];
934
+ #writeCustomTag(node) {
935
+ if (node.body) {
936
+ this.#extractor.write("<div>");
937
+ node.body.forEach((node2) => this.#visitNode(node2));
938
+ this.#extractor.write("</div>");
926
939
  }
927
940
  }
928
- expressionAt(offset, src) {
929
- try {
930
- return parseExpression(this.#whitespace.slice(0, offset) + src, {
931
- plugins,
932
- strictMode: true,
933
- errorRecovery: true,
934
- sourceType: "module",
935
- allowUndeclaredExports: true,
936
- allowSuperOutsideMethod: true,
937
- allowAwaitOutsideFunction: true,
938
- allowReturnOutsideFunction: true,
939
- sourceFilename: this.#sourceFileName
941
+ #writeAttrNamed(attr) {
942
+ this.#extractor.write(" ");
943
+ const nameString = this.#read(attr.name);
944
+ if (/:(?:scoped|(?:no-update(?:-if)?))$/.test(nameString)) {
945
+ this.#extractor.copy({
946
+ start: attr.name.start,
947
+ end: attr.name.start + nameString.lastIndexOf(":")
940
948
  });
941
- } catch {
949
+ } else {
950
+ this.#extractor.copy(attr.name);
951
+ }
952
+ if (attr.value === void 0 || attr.name.start === attr.name.end || attr.value.type === 14 /* AttrMethod */) {
942
953
  return;
943
954
  }
955
+ const valueString = this.#read(attr.value);
956
+ const valueType = getAttributeValueType(valueString);
957
+ if (valueType === void 0) return;
958
+ switch (valueType) {
959
+ case 0 /* True */:
960
+ break;
961
+ case 1 /* Literal */:
962
+ this.#extractor.write('="');
963
+ this.#extractor.copy({
964
+ start: attr.value.start + valueString.search(/[^=\s]/g),
965
+ end: attr.value.end
966
+ });
967
+ this.#extractor.write('"');
968
+ break;
969
+ case 2 /* QuotedString */:
970
+ this.#extractor.write('="');
971
+ this.#extractor.copy({
972
+ start: attr.value.start + valueString.search(/[^=\s]/g) + 1,
973
+ end: attr.value.end - 1
974
+ });
975
+ this.#extractor.write('"');
976
+ break;
977
+ case 3 /* Dynamic */:
978
+ this.#extractor.write(`="dynamic"`);
979
+ break;
980
+ }
944
981
  }
945
982
  };
946
-
947
- // src/extractors/script/util/attach-scopes.ts
948
- import * as t from "@babel/types";
949
- var ATTR_UNAMED = "value";
950
- var Scopes = /* @__PURE__ */ new WeakMap();
951
- var BoundAttrMemberExpressionStartOffsets = /* @__PURE__ */ new WeakMap();
983
+ function isVoidTag(tagName) {
984
+ switch (tagName) {
985
+ case "area":
986
+ case "base":
987
+ case "br":
988
+ case "col":
989
+ case "embed":
990
+ case "hr":
991
+ case "img":
992
+ case "input":
993
+ case "link":
994
+ case "meta":
995
+ case "param":
996
+ case "source":
997
+ case "track":
998
+ case "wbr":
999
+ return true;
1000
+ default:
1001
+ return false;
1002
+ }
1003
+ }
1004
+
1005
+ // src/extractors/script/index.ts
1006
+ import { relativeImportPath } from "relative-import-path";
1007
+
1008
+ // src/extractors/script/util/attach-scopes.ts
1009
+ import * as t from "@babel/types";
1010
+ var ATTR_UNAMED = "value";
1011
+ var Scopes = /* @__PURE__ */ new WeakMap();
1012
+ var BoundAttrMemberExpressionStartOffsets = /* @__PURE__ */ new WeakMap();
952
1013
  function crawlProgramScope(parsed, scriptParser) {
953
1014
  var _a;
954
1015
  const { program, read } = parsed;
@@ -1021,9 +1082,12 @@ function crawlProgramScope(parsed, scriptParser) {
1021
1082
  case 16 /* AttrTag */: {
1022
1083
  if (child.var) {
1023
1084
  parentScope.bindings ??= {};
1024
- const parsedFn = scriptParser.expressionAt(child.var.value.start - 6, `${read(child.var.value)}=0`);
1085
+ const parsedFn = scriptParser.expressionAt(
1086
+ child.var.value.start - 1,
1087
+ `(${read(child.var.value)})=>0`
1088
+ );
1025
1089
  if (parsedFn) {
1026
- const lVal = parsedFn.left;
1090
+ const lVal = parsedFn.params[0];
1027
1091
  checkForMutations(parentScope, lVal);
1028
1092
  for (const id of getVarIdentifiers(
1029
1093
  parsed,
@@ -1511,6 +1575,57 @@ function tryReaddirSync(dir) {
1511
1575
  }
1512
1576
  }
1513
1577
 
1578
+ // src/extractors/script/util/jsdoc-input-type.ts
1579
+ var MaybeInputTypedefReg = /@typedef\b[\s\S]*\bInput\b/;
1580
+ function getJSDocInputType(comment, ts) {
1581
+ var _a, _b, _c, _d;
1582
+ if (!MaybeInputTypedefReg.test(comment)) return;
1583
+ const sourceFile = ts.createSourceFile(
1584
+ "_.js",
1585
+ comment,
1586
+ ts.ScriptTarget.Latest,
1587
+ false,
1588
+ ts.ScriptKind.JS
1589
+ );
1590
+ const tags = (_b = (_a = sourceFile.endOfFileToken.jsDoc) == null ? void 0 : _a[0]) == null ? void 0 : _b.tags;
1591
+ if (!(tags && hasInputTypeDef(ts, sourceFile, tags))) return;
1592
+ let typeParameters;
1593
+ for (const tag of tags) {
1594
+ if (isTemplateTag(ts, tag)) {
1595
+ let constraint = (_c = tag.constraint) == null ? void 0 : _c.type.getText(sourceFile);
1596
+ for (const param of tag.typeParameters) {
1597
+ const value = {
1598
+ name: "" + param.name.escapedText,
1599
+ constraint,
1600
+ default: (_d = param.default) == null ? void 0 : _d.getText(sourceFile)
1601
+ };
1602
+ constraint = void 0;
1603
+ if (typeParameters) {
1604
+ typeParameters.push(value);
1605
+ } else {
1606
+ typeParameters = [value];
1607
+ }
1608
+ }
1609
+ }
1610
+ }
1611
+ return { typeParameters };
1612
+ }
1613
+ function hasInputTypeDef(ts, sourceFile, tags) {
1614
+ var _a;
1615
+ for (const tag of tags) {
1616
+ if (isTypeDefTag(ts, tag) && ((_a = tag.fullName) == null ? void 0 : _a.getText(sourceFile)) === "Input") {
1617
+ return true;
1618
+ }
1619
+ }
1620
+ return false;
1621
+ }
1622
+ function isTypeDefTag(ts, tag) {
1623
+ return tag.kind === ts.SyntaxKind.JSDocTypedefTag;
1624
+ }
1625
+ function isTemplateTag(ts, tag) {
1626
+ return tag.kind === ts.SyntaxKind.JSDocTemplateTag;
1627
+ }
1628
+
1514
1629
  // src/extractors/script/util/runtime-overrides.ts
1515
1630
  var RuntimeOverloads = /* @__PURE__ */ new Map();
1516
1631
  var commentsReg = /\/\*(?:[^*]+|\*[^/])*\*\//gm;
@@ -1564,56 +1679,64 @@ function getRuntimeOverrides(runtimeTypes, generics, applyGenerics) {
1564
1679
  return result;
1565
1680
  }
1566
1681
 
1567
- // src/extractors/script/util/jsdoc-input-type.ts
1568
- var MaybeInputTypedefReg = /@typedef\b[\s\S]*\bInput\b/;
1569
- function getJSDocInputType(comment, ts) {
1570
- var _a, _b, _c, _d;
1571
- if (!MaybeInputTypedefReg.test(comment)) return;
1572
- const sourceFile = ts.createSourceFile(
1573
- "_.js",
1574
- comment,
1575
- ts.ScriptTarget.Latest,
1576
- false,
1577
- ts.ScriptKind.JS
1578
- );
1579
- const tags = (_b = (_a = sourceFile.endOfFileToken.jsDoc) == null ? void 0 : _a[0]) == null ? void 0 : _b.tags;
1580
- if (!(tags && hasInputTypeDef(ts, sourceFile, tags))) return;
1581
- let typeParameters;
1582
- for (const tag of tags) {
1583
- if (isTemplateTag(ts, tag)) {
1584
- let constraint = (_c = tag.constraint) == null ? void 0 : _c.type.getText(sourceFile);
1585
- for (const param of tag.typeParameters) {
1586
- const value = {
1587
- name: "" + param.name.escapedText,
1588
- constraint,
1589
- default: (_d = param.default) == null ? void 0 : _d.getText(sourceFile)
1590
- };
1591
- constraint = void 0;
1592
- if (typeParameters) {
1593
- typeParameters.push(value);
1594
- } else {
1595
- typeParameters = [value];
1596
- }
1597
- }
1682
+ // src/extractors/script/util/script-parser.ts
1683
+ import {
1684
+ parse as parseStatement,
1685
+ parseExpression
1686
+ } from "@babel/parser";
1687
+ var plugins = [
1688
+ "exportDefaultFrom",
1689
+ "importAssertions",
1690
+ "typescript"
1691
+ ];
1692
+ var ScriptParser = class {
1693
+ #parsed;
1694
+ constructor(parsed) {
1695
+ this.#parsed = parsed;
1696
+ }
1697
+ statementAt(startIndex, src) {
1698
+ const pos = this.#parsed.positionAt(startIndex);
1699
+ try {
1700
+ return parseStatement(src, {
1701
+ plugins,
1702
+ startIndex,
1703
+ startLine: pos.line + 1,
1704
+ startColumn: pos.character,
1705
+ strictMode: true,
1706
+ errorRecovery: true,
1707
+ sourceType: "module",
1708
+ allowUndeclaredExports: true,
1709
+ allowSuperOutsideMethod: true,
1710
+ allowAwaitOutsideFunction: true,
1711
+ allowReturnOutsideFunction: true,
1712
+ sourceFilename: this.#parsed.filename
1713
+ }).program.body;
1714
+ } catch {
1715
+ return [];
1598
1716
  }
1599
1717
  }
1600
- return { typeParameters };
1601
- }
1602
- function hasInputTypeDef(ts, sourceFile, tags) {
1603
- var _a;
1604
- for (const tag of tags) {
1605
- if (isTypeDefTag(ts, tag) && ((_a = tag.fullName) == null ? void 0 : _a.getText(sourceFile)) === "Input") {
1606
- return true;
1718
+ expressionAt(startIndex, src) {
1719
+ const pos = this.#parsed.positionAt(startIndex);
1720
+ try {
1721
+ return parseExpression(src, {
1722
+ plugins,
1723
+ startIndex,
1724
+ startLine: pos.line + 1,
1725
+ startColumn: pos.character,
1726
+ strictMode: true,
1727
+ errorRecovery: true,
1728
+ sourceType: "module",
1729
+ allowUndeclaredExports: true,
1730
+ allowSuperOutsideMethod: true,
1731
+ allowAwaitOutsideFunction: true,
1732
+ allowReturnOutsideFunction: true,
1733
+ sourceFilename: this.#parsed.filename
1734
+ });
1735
+ } catch {
1736
+ return;
1607
1737
  }
1608
1738
  }
1609
- return false;
1610
- }
1611
- function isTypeDefTag(ts, tag) {
1612
- return tag.kind === ts.SyntaxKind.JSDocTypedefTag;
1613
- }
1614
- function isTemplateTag(ts, tag) {
1615
- return tag.kind === ts.SyntaxKind.JSDocTemplateTag;
1616
- }
1739
+ };
1617
1740
 
1618
1741
  // src/extractors/script/index.ts
1619
1742
  var SEP_EMPTY = "";
@@ -1667,7 +1790,7 @@ var ScriptExtractor = class {
1667
1790
  this.#ts = opts.ts;
1668
1791
  this.#runtimeTypes = opts.runtimeTypesCode;
1669
1792
  this.#extractor = new Extractor(parsed);
1670
- this.#scriptParser = new ScriptParser(parsed.filename, parsed.code);
1793
+ this.#scriptParser = new ScriptParser(parsed);
1671
1794
  this.#read = parsed.read.bind(parsed);
1672
1795
  this.#mutationOffsets = crawlProgramScope(this.#parsed, this.#scriptParser);
1673
1796
  this.#writeProgram(parsed.program);
@@ -2110,6 +2233,7 @@ constructor(_?: Return) {}
2110
2233
  #writeTag(tag) {
2111
2234
  const tagName = tag.nameText;
2112
2235
  const renderId = this.#getRenderId(tag);
2236
+ let nestedTagType;
2113
2237
  if (renderId) {
2114
2238
  this.#extractor.write(
2115
2239
  `${varShared("assertRendered")}(${varShared(
@@ -2121,6 +2245,10 @@ constructor(_?: Return) {}
2121
2245
  const def = this.#lookup.getTag(tagName);
2122
2246
  if (def) {
2123
2247
  const importPath = resolveTagImport(this.#filename, def);
2248
+ const isMarkoFile = importPath == null ? void 0 : importPath.endsWith(".marko");
2249
+ if (isMarkoFile) {
2250
+ nestedTagType = `import("${importPath}").Input`;
2251
+ }
2124
2252
  const renderer = (importPath == null ? void 0 : importPath.endsWith(".marko")) ? `renderTemplate(import("${importPath}"))` : def.html ? `renderNativeTag("${def.name}")` : "missingTag";
2125
2253
  if (!def.html && REG_TAG_NAME_IDENTIFIER.test(tagName)) {
2126
2254
  this.#extractor.write(
@@ -2133,6 +2261,7 @@ ${varShared(renderer)})`);
2133
2261
  this.#extractor.write(varShared(renderer));
2134
2262
  }
2135
2263
  } else if (REG_TAG_NAME_IDENTIFIER.test(tagName)) {
2264
+ nestedTagType = `Marko.Input<typeof ${this.#read(tag.name)}>`;
2136
2265
  this.#extractor.write(`${varShared("renderDynamicTag")}(
2137
2266
  `).copy(tag.name).write("\n)");
2138
2267
  } else {
@@ -2148,7 +2277,7 @@ ${varShared(renderer)})`);
2148
2277
  } else {
2149
2278
  this.#extractor.write("()()(");
2150
2279
  }
2151
- this.#writeTagInputObject(tag);
2280
+ this.#writeTagInputObject(tag, nestedTagType);
2152
2281
  if (renderId) {
2153
2282
  this.#extractor.write(`)`);
2154
2283
  }
@@ -2332,7 +2461,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2332
2461
  );
2333
2462
  return hasAttrs;
2334
2463
  }
2335
- #writeAttrTags({ staticAttrTags, dynamicAttrTagParents }, inMerge) {
2464
+ #writeAttrTags({ staticAttrTags, dynamicAttrTagParents }, inMerge, nestedTagType) {
2336
2465
  let wasMerge = false;
2337
2466
  if (dynamicAttrTagParents) {
2338
2467
  if (staticAttrTags) {
@@ -2348,7 +2477,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2348
2477
  }
2349
2478
  }
2350
2479
  if (staticAttrTags) {
2351
- this.#writeStaticAttrTags(staticAttrTags, inMerge);
2480
+ this.#writeStaticAttrTags(staticAttrTags, inMerge, nestedTagType);
2352
2481
  if (dynamicAttrTagParents)
2353
2482
  this.#extractor.write(`}${SEP_COMMA_NEW_LINE}`);
2354
2483
  }
@@ -2357,7 +2486,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2357
2486
  if (wasMerge) this.#extractor.write(`)${SEP_COMMA_NEW_LINE}`);
2358
2487
  }
2359
2488
  }
2360
- #writeStaticAttrTags(staticAttrTags, wasMerge) {
2489
+ #writeStaticAttrTags(staticAttrTags, wasMerge, nestedTagType) {
2361
2490
  if (!wasMerge) this.#extractor.write("...{");
2362
2491
  this.#extractor.write(
2363
2492
  `[${varShared("never")}](){
@@ -2387,6 +2516,15 @@ const attrTags = ${varShared(
2387
2516
  this.#extractor.write("]: ");
2388
2517
  if (isRepeated) {
2389
2518
  this.#extractor.write(`${varShared("repeatedAttrTag")}(
2519
+ ...
2520
+ `);
2521
+ if (nestedTagType && this.#scriptLang === "js" /* js */) {
2522
+ this.#extractor.write(
2523
+ `/** @satisfies {${nestedTagType}["${name}"][]} */
2524
+ `
2525
+ );
2526
+ }
2527
+ this.#extractor.write(`([
2390
2528
  `);
2391
2529
  }
2392
2530
  for (const childNode of attrTag) {
@@ -2394,6 +2532,10 @@ const attrTags = ${varShared(
2394
2532
  this.#extractor.write(SEP_COMMA_NEW_LINE);
2395
2533
  }
2396
2534
  if (isRepeated) {
2535
+ this.#extractor.write("])");
2536
+ if (nestedTagType && this.#scriptLang === "ts" /* ts */) {
2537
+ this.#extractor.write(` satisfies ${nestedTagType}["${name}"][]`);
2538
+ }
2397
2539
  this.#extractor.write(`)${SEP_COMMA_NEW_LINE}`);
2398
2540
  }
2399
2541
  }
@@ -2454,7 +2596,7 @@ const attrTags = ${varShared(
2454
2596
  this.#extractor.write(SEP_COMMA_NEW_LINE);
2455
2597
  }
2456
2598
  }
2457
- #writeTagInputObject(tag) {
2599
+ #writeTagInputObject(tag, nestedTagType) {
2458
2600
  if (!tag.params) this.#writeComments(tag);
2459
2601
  let hasInput = false;
2460
2602
  this.#extractor.write("{\n");
@@ -2475,7 +2617,7 @@ const attrTags = ${varShared(
2475
2617
  let hasRenderBody = false;
2476
2618
  if (body) {
2477
2619
  hasInput = true;
2478
- this.#writeAttrTags(body, false);
2620
+ this.#writeAttrTags(body, false, nestedTagType);
2479
2621
  hasRenderBody = body.renderBody !== void 0;
2480
2622
  } else if (tag.close) {
2481
2623
  hasRenderBody = true;
@@ -2874,6 +3016,100 @@ function isEmptyRange(range) {
2874
3016
  return range.start === range.end;
2875
3017
  }
2876
3018
 
3019
+ // src/extractors/style/index.ts
3020
+ function extractStyle(opts) {
3021
+ const { parsed, lookup } = opts;
3022
+ const extractorsByExt = /* @__PURE__ */ new Map();
3023
+ const { read, program, code } = parsed;
3024
+ let placeholderId = 0;
3025
+ for (const node of program.static) {
3026
+ if (node.type === 27 /* Style */) {
3027
+ getExtractor(node.ext || ".css").copy(node.value);
3028
+ }
3029
+ }
3030
+ for (const node of program.body) {
3031
+ visit(node);
3032
+ }
3033
+ const extractedByExt = /* @__PURE__ */ new Map();
3034
+ for (const [ext, extractor] of extractorsByExt) {
3035
+ extractedByExt.set(ext, extractor.end());
3036
+ }
3037
+ return extractedByExt;
3038
+ function visit(node) {
3039
+ var _a, _b;
3040
+ switch (node.type) {
3041
+ case 16 /* AttrTag */:
3042
+ if (node.body) {
3043
+ for (const child of node.body) {
3044
+ visit(child);
3045
+ }
3046
+ }
3047
+ break;
3048
+ case 1 /* Tag */:
3049
+ if (node.body) {
3050
+ if (node.nameText === "style") {
3051
+ const ext = node.shorthandClassNames ? read(node.shorthandClassNames.at(-1)) : ".css";
3052
+ for (const child of node.body) {
3053
+ switch (child.type) {
3054
+ case 17 /* Text */:
3055
+ getExtractor(ext).copy(child);
3056
+ break;
3057
+ case 22 /* Placeholder */:
3058
+ getExtractor(ext).write(`var(--_${placeholderId++})`);
3059
+ break;
3060
+ }
3061
+ }
3062
+ } else {
3063
+ for (const child of node.body) {
3064
+ visit(child);
3065
+ }
3066
+ }
3067
+ }
3068
+ if (node.attrs) {
3069
+ for (const attr of node.attrs) {
3070
+ if (
3071
+ // Check for string literal attribute values.
3072
+ attr.type === 10 /* AttrNamed */ && ((_a = attr.value) == null ? void 0 : _a.type) === 13 /* AttrValue */ && /^['"]$/.test(code[attr.value.value.start])
3073
+ ) {
3074
+ const name = read(attr.name);
3075
+ if (name === "#style" || name === "style" && node.nameText && name === "style" && ((_b = lookup.getTag(node.nameText)) == null ? void 0 : _b.html)) {
3076
+ getExtractor("css").write(":root{").copy({
3077
+ start: attr.value.value.start + 1,
3078
+ end: attr.value.value.end - 1
3079
+ }).write("}");
3080
+ }
3081
+ }
3082
+ }
3083
+ }
3084
+ break;
3085
+ }
3086
+ }
3087
+ function getExtractor(ext) {
3088
+ let extractor = extractorsByExt.get(ext);
3089
+ if (!extractor) {
3090
+ extractorsByExt.set(ext, extractor = new Extractor(parsed));
3091
+ }
3092
+ return extractor;
3093
+ }
3094
+ }
3095
+
3096
+ // src/processors/index.ts
3097
+ var processors_exports = {};
3098
+ __export(processors_exports, {
3099
+ create: () => create,
3100
+ extensions: () => extensions,
3101
+ has: () => has
3102
+ });
3103
+
3104
+ // src/util/get-ext.ts
3105
+ function getExt(fileName) {
3106
+ const extIndex = fileName.lastIndexOf(".");
3107
+ if (extIndex !== -1) return fileName.slice(extIndex);
3108
+ }
3109
+
3110
+ // src/processors/marko.ts
3111
+ import path3 from "path";
3112
+
2877
3113
  // src/util/project.ts
2878
3114
  var project_exports = {};
2879
3115
  __export(project_exports, {
@@ -2887,9 +3123,9 @@ __export(project_exports, {
2887
3123
  setDefaultCompilerMeta: () => setDefaultCompilerMeta,
2888
3124
  setDefaultTypePaths: () => setDefaultTypePaths
2889
3125
  });
2890
- import path2 from "path";
2891
- import { createRequire } from "module";
2892
3126
  import { strip as stripJSONComments } from "@luxass/strip-json-comments";
3127
+ import { createRequire } from "module";
3128
+ import path2 from "path";
2893
3129
  var defaultTypeLibs = {};
2894
3130
  var defaultMeta;
2895
3131
  var ignoreErrors = (_err) => {
@@ -3101,22 +3337,7 @@ function interopDefault(mod) {
3101
3337
  return mod.default || mod;
3102
3338
  }
3103
3339
 
3104
- // src/processors/index.ts
3105
- var processors_exports = {};
3106
- __export(processors_exports, {
3107
- create: () => create,
3108
- extensions: () => extensions,
3109
- has: () => has
3110
- });
3111
-
3112
- // src/util/get-ext.ts
3113
- function getExt(fileName) {
3114
- const extIndex = fileName.lastIndexOf(".");
3115
- if (extIndex !== -1) return fileName.slice(extIndex);
3116
- }
3117
-
3118
3340
  // src/processors/marko.ts
3119
- import path3 from "path";
3120
3341
  var isRemapExtensionReg = /\.ts$/;
3121
3342
  var skipRemapExtensionsReg = /\.(?:[cm]?jsx?|json|marko|css|less|sass|scss|styl|stylus|pcss|postcss|sss|a?png|jpe?g|jfif|pipeg|pjp|gif|svg|ico|web[pm]|avif|mp4|ogg|mp3|wav|flac|aac|opus|woff2?|eot|[ot]tf|webmanifest|pdf|txt)$/;
3122
3343
  var marko_default = {
@@ -3326,195 +3547,6 @@ function has(fileName) {
3326
3547
  function isDefinitionFile(fileName) {
3327
3548
  return /\.d\.[^.]+$/.test(fileName);
3328
3549
  }
3329
-
3330
- // src/extractors/html/keywords.ts
3331
- var builtinTagsRegex = /^(?:a(?:(?:bbr|cronym|ddress|pplet|r(?:ea|ticle)|side|udio))?|b(?:(?:ase(?:font)?|d[io]|gsound|ig|l(?:ink|ockquote)|ody|r|utton))?|c(?:a(?:nvas|ption)|enter|ite|o(?:de|l(?:group)?|mmand|ntent))|d(?:ata(?:list)?|d|e(?:l|tails)|fn|i(?:alog|r|v)|l|t)|e(?:lement|m(?:bed)?)|f(?:i(?:eldset|g(?:caption|ure))|o(?:nt|oter|rm)|rame(?:set)?)|h(?:1|2|3|4|5|6|ead(?:er)?|group|r|tml)|i(?:(?:frame|m(?:age|g)|n(?:put|s)|sindex))?|k(?:bd|eygen)|l(?:abel|egend|i(?:(?:nk|sting))?)|m(?:a(?:in|p|r(?:k|quee)|th)|e(?:nu(?:item)?|t(?:a|er))|ulticol)|n(?:av|extid|o(?:br|embed|frames|script))|o(?:bject|l|pt(?:group|ion)|utput)|p(?:(?:aram|icture|laintext|r(?:e|ogress)))?|q|r(?:bc?|p|tc?|uby)|s(?:(?:amp|cript|e(?:ction|lect)|hadow|lot|mall|ource|pa(?:cer|n)|t(?:r(?:ike|ong)|yle)|u(?:b|mmary|p)|vg))?|t(?:able|body|d|e(?:mplate|xtarea)|foot|h(?:ead)?|i(?:me|tle)|r(?:ack)?|t)|ul?|v(?:ar|ideo)|wbr|xmp)$/;
3332
- function isHTMLTag(tag) {
3333
- return builtinTagsRegex.test(tag);
3334
- }
3335
- function getAttributeValueType(value) {
3336
- if (value === void 0 || value[0] !== "=") return void 0;
3337
- value = value.substring(1).trim();
3338
- switch (value) {
3339
- case "NaN":
3340
- case "Infinity":
3341
- case "-Infinity":
3342
- return 1 /* Literal */;
3343
- case "null":
3344
- case "false":
3345
- case "undefined":
3346
- return void 0;
3347
- case "true":
3348
- return 0 /* True */;
3349
- }
3350
- if (
3351
- // double quote string
3352
- /^"(?:[^"\\]+|\\.)*"$/.test(value) || // single quote string
3353
- /^'(?:[^'\\]+|\\.)*'$/.test(value) || // template literal without any interpolations
3354
- /^`(?:[^`\\$]+|\\.|\$(?!\{))*`$/.test(value)
3355
- ) {
3356
- return 2 /* QuotedString */;
3357
- } else if (
3358
- // octal literal
3359
- /^-?0[oO]?[0-7](?:_?[0-7]+)*n?$/.test(value) || // hex literal
3360
- /^-?0[xX][0-9a-fA-F](?:_?[0-9a-fA-F]+)*n?$/.test(value) || // binary literal
3361
- /^-?0[bB][01](?:_?[01]+)*n?$/.test(value) || // integer or float
3362
- /^-?\d(?:_?\d+)*(?:[.eE]\d(?:_?\d+)*|n?|\.?)$/.test(value)
3363
- ) {
3364
- return 1 /* Literal */;
3365
- }
3366
- return 3 /* Dynamic */;
3367
- }
3368
-
3369
- // src/extractors/html/index.ts
3370
- function extractHTML(parsed) {
3371
- return new HTMLExtractor(parsed).end();
3372
- }
3373
- var HTMLExtractor = class {
3374
- #extractor;
3375
- #read;
3376
- #nodeDetails;
3377
- #nodeIdCounter;
3378
- constructor(parsed) {
3379
- this.#extractor = new Extractor(parsed);
3380
- this.#read = parsed.read.bind(parsed);
3381
- this.#nodeDetails = {};
3382
- this.#nodeIdCounter = 0;
3383
- parsed.program.body.forEach((node) => this.#visitNode(node));
3384
- }
3385
- end() {
3386
- return { extracted: this.#extractor.end(), nodeDetails: this.#nodeDetails };
3387
- }
3388
- #visitNode(node) {
3389
- var _a;
3390
- let hasDynamicBody = false, hasDynamicAttrs = false, isDynamic = false;
3391
- switch (node.type) {
3392
- case 16 /* AttrTag */:
3393
- (_a = node.body) == null ? void 0 : _a.forEach((child) => {
3394
- if (this.#visitNode(child)) hasDynamicBody = true;
3395
- });
3396
- break;
3397
- case 1 /* Tag */: {
3398
- const nodeId = `${this.#nodeIdCounter++}`;
3399
- ({ isDynamic, hasDynamicAttrs, hasDynamicBody } = this.#writeTag(
3400
- node,
3401
- nodeId
3402
- ));
3403
- this.#nodeDetails[nodeId] = { hasDynamicAttrs, hasDynamicBody };
3404
- break;
3405
- }
3406
- case 17 /* Text */:
3407
- this.#extractor.copy(node);
3408
- break;
3409
- case 22 /* Placeholder */:
3410
- isDynamic = this.#read({
3411
- start: node.start + 1,
3412
- end: node.start + 2
3413
- }) === "!";
3414
- this.#extractor.write("placeholder");
3415
- break;
3416
- }
3417
- return isDynamic || hasDynamicBody;
3418
- }
3419
- #writeTag(node, id) {
3420
- const isDynamic = !node.nameText || !isHTMLTag(node.nameText);
3421
- let hasDynamicAttrs = false, hasDynamicBody = false;
3422
- if (!isDynamic) {
3423
- ({ hasDynamicAttrs, hasDynamicBody } = this.#writeHTMLTag(node, id));
3424
- } else {
3425
- this.#writeCustomTag(node);
3426
- }
3427
- return { isDynamic, hasDynamicAttrs, hasDynamicBody };
3428
- }
3429
- #writeHTMLTag(node, id) {
3430
- var _a, _b;
3431
- let hasDynamicAttrs = false, hasDynamicBody = false;
3432
- this.#extractor.write("<");
3433
- this.#extractor.copy(node.name);
3434
- this.#extractor.write(` data-marko-node-id="${id}"`);
3435
- (_a = node.attrs) == null ? void 0 : _a.forEach((attr) => {
3436
- if (attr.type === 10 /* AttrNamed */) this.#writeAttrNamed(attr);
3437
- else if (attr.type === 15 /* AttrSpread */) hasDynamicAttrs = true;
3438
- });
3439
- this.#extractor.write(">");
3440
- if (!isVoidTag(node.nameText)) {
3441
- (_b = node.body) == null ? void 0 : _b.forEach((child) => {
3442
- if (this.#visitNode(child)) hasDynamicBody = true;
3443
- });
3444
- this.#extractor.write(`</${node.nameText}>`);
3445
- }
3446
- return { hasDynamicAttrs, hasDynamicBody };
3447
- }
3448
- #writeCustomTag(node) {
3449
- if (node.body) {
3450
- this.#extractor.write("<div>");
3451
- node.body.forEach((node2) => this.#visitNode(node2));
3452
- this.#extractor.write("</div>");
3453
- }
3454
- }
3455
- #writeAttrNamed(attr) {
3456
- this.#extractor.write(" ");
3457
- const nameString = this.#read(attr.name);
3458
- if (/:(?:scoped|(?:no-update(?:-if)?))$/.test(nameString)) {
3459
- this.#extractor.copy({
3460
- start: attr.name.start,
3461
- end: attr.name.start + nameString.lastIndexOf(":")
3462
- });
3463
- } else {
3464
- this.#extractor.copy(attr.name);
3465
- }
3466
- if (attr.value === void 0 || attr.name.start === attr.name.end || attr.value.type === 14 /* AttrMethod */) {
3467
- return;
3468
- }
3469
- const valueString = this.#read(attr.value);
3470
- const valueType = getAttributeValueType(valueString);
3471
- if (valueType === void 0) return;
3472
- switch (valueType) {
3473
- case 0 /* True */:
3474
- break;
3475
- case 1 /* Literal */:
3476
- this.#extractor.write('="');
3477
- this.#extractor.copy({
3478
- start: attr.value.start + valueString.search(/[^=\s]/g),
3479
- end: attr.value.end
3480
- });
3481
- this.#extractor.write('"');
3482
- break;
3483
- case 2 /* QuotedString */:
3484
- this.#extractor.write('="');
3485
- this.#extractor.copy({
3486
- start: attr.value.start + valueString.search(/[^=\s]/g) + 1,
3487
- end: attr.value.end - 1
3488
- });
3489
- this.#extractor.write('"');
3490
- break;
3491
- case 3 /* Dynamic */:
3492
- this.#extractor.write(`="dynamic"`);
3493
- break;
3494
- }
3495
- }
3496
- };
3497
- function isVoidTag(tagName) {
3498
- switch (tagName) {
3499
- case "area":
3500
- case "base":
3501
- case "br":
3502
- case "col":
3503
- case "embed":
3504
- case "hr":
3505
- case "img":
3506
- case "input":
3507
- case "link":
3508
- case "meta":
3509
- case "param":
3510
- case "source":
3511
- case "track":
3512
- case "wbr":
3513
- return true;
3514
- default:
3515
- return false;
3516
- }
3517
- }
3518
3550
  export {
3519
3551
  NodeType,
3520
3552
  processors_exports as Processors,