@marko/language-tools 2.4.4 → 2.4.6

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.js CHANGED
@@ -850,135 +850,197 @@ function sortBySourceThenGenerated(a, b) {
850
850
  return delta === 0 ? b.generatedStart - a.generatedStart : delta;
851
851
  }
852
852
 
853
- // src/extractors/style/index.ts
854
- function extractStyle(opts) {
855
- const { parsed, lookup } = opts;
856
- const extractorsByExt = /* @__PURE__ */ new Map();
857
- const { read, program, code } = parsed;
858
- let placeholderId = 0;
859
- for (const node of program.static) {
860
- if (node.type === 27 /* Style */) {
861
- getExtractor(node.ext || ".css").copy(node.value);
862
- }
853
+ // src/extractors/html/keywords.ts
854
+ 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)$/;
855
+ function isHTMLTag(tag) {
856
+ return builtinTagsRegex.test(tag);
857
+ }
858
+ function getAttributeValueType(value) {
859
+ if (value === void 0 || value[0] !== "=") return void 0;
860
+ value = value.substring(1).trim();
861
+ switch (value) {
862
+ case "NaN":
863
+ case "Infinity":
864
+ case "-Infinity":
865
+ return 1 /* Literal */;
866
+ case "null":
867
+ case "false":
868
+ case "undefined":
869
+ return void 0;
870
+ case "true":
871
+ return 0 /* True */;
863
872
  }
864
- for (const node of program.body) {
865
- visit(node);
873
+ if (
874
+ // double quote string
875
+ /^"(?:[^"\\]+|\\.)*"$/.test(value) || // single quote string
876
+ /^'(?:[^'\\]+|\\.)*'$/.test(value) || // template literal without any interpolations
877
+ /^`(?:[^`\\$]+|\\.|\$(?!\{))*`$/.test(value)
878
+ ) {
879
+ return 2 /* QuotedString */;
880
+ } else if (
881
+ // octal literal
882
+ /^-?0[oO]?[0-7](?:_?[0-7]+)*n?$/.test(value) || // hex literal
883
+ /^-?0[xX][0-9a-fA-F](?:_?[0-9a-fA-F]+)*n?$/.test(value) || // binary literal
884
+ /^-?0[bB][01](?:_?[01]+)*n?$/.test(value) || // integer or float
885
+ /^-?\d(?:_?\d+)*(?:[.eE]\d(?:_?\d+)*|n?|\.?)$/.test(value)
886
+ ) {
887
+ return 1 /* Literal */;
866
888
  }
867
- const extractedByExt = /* @__PURE__ */ new Map();
868
- for (const [ext, extractor] of extractorsByExt) {
869
- extractedByExt.set(ext, extractor.end());
889
+ return 3 /* Dynamic */;
890
+ }
891
+
892
+ // src/extractors/html/index.ts
893
+ function extractHTML(parsed) {
894
+ return new HTMLExtractor(parsed).end();
895
+ }
896
+ var HTMLExtractor = class {
897
+ #extractor;
898
+ #read;
899
+ #nodeDetails;
900
+ #nodeIdCounter;
901
+ constructor(parsed) {
902
+ this.#extractor = new Extractor(parsed);
903
+ this.#read = parsed.read.bind(parsed);
904
+ this.#nodeDetails = {};
905
+ this.#nodeIdCounter = 0;
906
+ parsed.program.body.forEach((node) => this.#visitNode(node));
870
907
  }
871
- return extractedByExt;
872
- function visit(node) {
873
- var _a, _b;
908
+ end() {
909
+ return { extracted: this.#extractor.end(), nodeDetails: this.#nodeDetails };
910
+ }
911
+ #visitNode(node) {
912
+ var _a;
913
+ let hasDynamicBody = false, hasDynamicAttrs = false, isDynamic = false;
874
914
  switch (node.type) {
875
915
  case 16 /* AttrTag */:
876
- if (node.body) {
877
- for (const child of node.body) {
878
- visit(child);
879
- }
880
- }
916
+ (_a = node.body) == null ? void 0 : _a.forEach((child) => {
917
+ if (this.#visitNode(child)) hasDynamicBody = true;
918
+ });
881
919
  break;
882
- case 1 /* Tag */:
883
- if (node.body) {
884
- if (node.nameText === "style") {
885
- const ext = node.shorthandClassNames ? read(node.shorthandClassNames.at(-1)) : ".css";
886
- for (const child of node.body) {
887
- switch (child.type) {
888
- case 17 /* Text */:
889
- getExtractor(ext).copy(child);
890
- break;
891
- case 22 /* Placeholder */:
892
- getExtractor(ext).write(`var(--_${placeholderId++})`);
893
- break;
894
- }
895
- }
896
- } else {
897
- for (const child of node.body) {
898
- visit(child);
899
- }
900
- }
901
- }
902
- if (node.attrs) {
903
- for (const attr of node.attrs) {
904
- if (
905
- // Check for string literal attribute values.
906
- attr.type === 10 /* AttrNamed */ && ((_a = attr.value) == null ? void 0 : _a.type) === 13 /* AttrValue */ && /^['"]$/.test(code[attr.value.value.start])
907
- ) {
908
- const name = read(attr.name);
909
- if (name === "#style" || name === "style" && node.nameText && name === "style" && ((_b = lookup.getTag(node.nameText)) == null ? void 0 : _b.html)) {
910
- getExtractor("css").write(":root{").copy({
911
- start: attr.value.value.start + 1,
912
- end: attr.value.value.end - 1
913
- }).write("}");
914
- }
915
- }
916
- }
917
- }
920
+ case 1 /* Tag */: {
921
+ const nodeId = `${this.#nodeIdCounter++}`;
922
+ ({ isDynamic, hasDynamicAttrs, hasDynamicBody } = this.#writeTag(
923
+ node,
924
+ nodeId
925
+ ));
926
+ this.#nodeDetails[nodeId] = { hasDynamicAttrs, hasDynamicBody };
927
+ break;
928
+ }
929
+ case 17 /* Text */:
930
+ this.#extractor.copy(node);
931
+ break;
932
+ case 22 /* Placeholder */:
933
+ isDynamic = this.#read({
934
+ start: node.start + 1,
935
+ end: node.start + 2
936
+ }) === "!";
937
+ this.#extractor.write("placeholder");
918
938
  break;
919
939
  }
940
+ return isDynamic || hasDynamicBody;
920
941
  }
921
- function getExtractor(ext) {
922
- let extractor = extractorsByExt.get(ext);
923
- if (!extractor) {
924
- extractorsByExt.set(ext, extractor = new Extractor(parsed));
942
+ #writeTag(node, id) {
943
+ const isDynamic = !node.nameText || !isHTMLTag(node.nameText);
944
+ let hasDynamicAttrs = false, hasDynamicBody = false;
945
+ if (!isDynamic) {
946
+ ({ hasDynamicAttrs, hasDynamicBody } = this.#writeHTMLTag(node, id));
947
+ } else {
948
+ this.#writeCustomTag(node);
925
949
  }
926
- return extractor;
950
+ return { isDynamic, hasDynamicAttrs, hasDynamicBody };
927
951
  }
928
- }
929
-
930
- // src/extractors/script/index.ts
931
- var import_relative_import_path = require("relative-import-path");
932
-
933
- // src/extractors/script/util/script-parser.ts
934
- var import_parser4 = require("@babel/parser");
935
- var plugins = [
936
- "exportDefaultFrom",
937
- "importAssertions",
938
- "typescript"
939
- ];
940
- var ScriptParser = class {
941
- #sourceFileName;
942
- #whitespace;
943
- constructor(sourceFileName, code) {
944
- this.#sourceFileName = sourceFileName;
945
- this.#whitespace = code.replace(/[^\s]/g, " ");
952
+ #writeHTMLTag(node, id) {
953
+ var _a, _b;
954
+ let hasDynamicAttrs = false, hasDynamicBody = false;
955
+ this.#extractor.write("<");
956
+ this.#extractor.copy(node.name);
957
+ this.#extractor.write(` data-marko-node-id="${id}"`);
958
+ (_a = node.attrs) == null ? void 0 : _a.forEach((attr) => {
959
+ if (attr.type === 10 /* AttrNamed */) this.#writeAttrNamed(attr);
960
+ else if (attr.type === 15 /* AttrSpread */) hasDynamicAttrs = true;
961
+ });
962
+ this.#extractor.write(">");
963
+ if (!isVoidTag(node.nameText)) {
964
+ (_b = node.body) == null ? void 0 : _b.forEach((child) => {
965
+ if (this.#visitNode(child)) hasDynamicBody = true;
966
+ });
967
+ this.#extractor.write(`</${node.nameText}>`);
968
+ }
969
+ return { hasDynamicAttrs, hasDynamicBody };
946
970
  }
947
- statementAt(offset, src) {
948
- try {
949
- return (0, import_parser4.parse)(this.#whitespace.slice(0, offset) + src, {
950
- plugins,
951
- strictMode: true,
952
- errorRecovery: true,
953
- sourceType: "module",
954
- allowUndeclaredExports: true,
955
- allowSuperOutsideMethod: true,
956
- allowAwaitOutsideFunction: true,
957
- allowReturnOutsideFunction: true,
958
- sourceFilename: this.#sourceFileName
959
- }).program.body;
960
- } catch {
961
- return [];
971
+ #writeCustomTag(node) {
972
+ if (node.body) {
973
+ this.#extractor.write("<div>");
974
+ node.body.forEach((node2) => this.#visitNode(node2));
975
+ this.#extractor.write("</div>");
962
976
  }
963
977
  }
964
- expressionAt(offset, src) {
965
- try {
966
- return (0, import_parser4.parseExpression)(this.#whitespace.slice(0, offset) + src, {
967
- plugins,
968
- strictMode: true,
969
- errorRecovery: true,
970
- sourceType: "module",
971
- allowUndeclaredExports: true,
972
- allowSuperOutsideMethod: true,
973
- allowAwaitOutsideFunction: true,
974
- allowReturnOutsideFunction: true,
975
- sourceFilename: this.#sourceFileName
978
+ #writeAttrNamed(attr) {
979
+ this.#extractor.write(" ");
980
+ const nameString = this.#read(attr.name);
981
+ if (/:(?:scoped|(?:no-update(?:-if)?))$/.test(nameString)) {
982
+ this.#extractor.copy({
983
+ start: attr.name.start,
984
+ end: attr.name.start + nameString.lastIndexOf(":")
976
985
  });
977
- } catch {
986
+ } else {
987
+ this.#extractor.copy(attr.name);
988
+ }
989
+ if (attr.value === void 0 || attr.name.start === attr.name.end || attr.value.type === 14 /* AttrMethod */) {
978
990
  return;
979
991
  }
992
+ const valueString = this.#read(attr.value);
993
+ const valueType = getAttributeValueType(valueString);
994
+ if (valueType === void 0) return;
995
+ switch (valueType) {
996
+ case 0 /* True */:
997
+ break;
998
+ case 1 /* Literal */:
999
+ this.#extractor.write('="');
1000
+ this.#extractor.copy({
1001
+ start: attr.value.start + valueString.search(/[^=\s]/g),
1002
+ end: attr.value.end
1003
+ });
1004
+ this.#extractor.write('"');
1005
+ break;
1006
+ case 2 /* QuotedString */:
1007
+ this.#extractor.write('="');
1008
+ this.#extractor.copy({
1009
+ start: attr.value.start + valueString.search(/[^=\s]/g) + 1,
1010
+ end: attr.value.end - 1
1011
+ });
1012
+ this.#extractor.write('"');
1013
+ break;
1014
+ case 3 /* Dynamic */:
1015
+ this.#extractor.write(`="dynamic"`);
1016
+ break;
1017
+ }
980
1018
  }
981
1019
  };
1020
+ function isVoidTag(tagName) {
1021
+ switch (tagName) {
1022
+ case "area":
1023
+ case "base":
1024
+ case "br":
1025
+ case "col":
1026
+ case "embed":
1027
+ case "hr":
1028
+ case "img":
1029
+ case "input":
1030
+ case "link":
1031
+ case "meta":
1032
+ case "param":
1033
+ case "source":
1034
+ case "track":
1035
+ case "wbr":
1036
+ return true;
1037
+ default:
1038
+ return false;
1039
+ }
1040
+ }
1041
+
1042
+ // src/extractors/script/index.ts
1043
+ var import_relative_import_path = require("relative-import-path");
982
1044
 
983
1045
  // src/extractors/script/util/attach-scopes.ts
984
1046
  var t = __toESM(require("@babel/types"));
@@ -1547,6 +1609,57 @@ function tryReaddirSync(dir) {
1547
1609
  }
1548
1610
  }
1549
1611
 
1612
+ // src/extractors/script/util/jsdoc-input-type.ts
1613
+ var MaybeInputTypedefReg = /@typedef\b[\s\S]*\bInput\b/;
1614
+ function getJSDocInputType(comment, ts) {
1615
+ var _a, _b, _c, _d;
1616
+ if (!MaybeInputTypedefReg.test(comment)) return;
1617
+ const sourceFile = ts.createSourceFile(
1618
+ "_.js",
1619
+ comment,
1620
+ ts.ScriptTarget.Latest,
1621
+ false,
1622
+ ts.ScriptKind.JS
1623
+ );
1624
+ const tags = (_b = (_a = sourceFile.endOfFileToken.jsDoc) == null ? void 0 : _a[0]) == null ? void 0 : _b.tags;
1625
+ if (!(tags && hasInputTypeDef(ts, sourceFile, tags))) return;
1626
+ let typeParameters;
1627
+ for (const tag of tags) {
1628
+ if (isTemplateTag(ts, tag)) {
1629
+ let constraint = (_c = tag.constraint) == null ? void 0 : _c.type.getText(sourceFile);
1630
+ for (const param of tag.typeParameters) {
1631
+ const value = {
1632
+ name: "" + param.name.escapedText,
1633
+ constraint,
1634
+ default: (_d = param.default) == null ? void 0 : _d.getText(sourceFile)
1635
+ };
1636
+ constraint = void 0;
1637
+ if (typeParameters) {
1638
+ typeParameters.push(value);
1639
+ } else {
1640
+ typeParameters = [value];
1641
+ }
1642
+ }
1643
+ }
1644
+ }
1645
+ return { typeParameters };
1646
+ }
1647
+ function hasInputTypeDef(ts, sourceFile, tags) {
1648
+ var _a;
1649
+ for (const tag of tags) {
1650
+ if (isTypeDefTag(ts, tag) && ((_a = tag.fullName) == null ? void 0 : _a.getText(sourceFile)) === "Input") {
1651
+ return true;
1652
+ }
1653
+ }
1654
+ return false;
1655
+ }
1656
+ function isTypeDefTag(ts, tag) {
1657
+ return tag.kind === ts.SyntaxKind.JSDocTypedefTag;
1658
+ }
1659
+ function isTemplateTag(ts, tag) {
1660
+ return tag.kind === ts.SyntaxKind.JSDocTemplateTag;
1661
+ }
1662
+
1550
1663
  // src/extractors/script/util/runtime-overrides.ts
1551
1664
  var RuntimeOverloads = /* @__PURE__ */ new Map();
1552
1665
  var commentsReg = /\/\*(?:[^*]+|\*[^/])*\*\//gm;
@@ -1600,56 +1713,61 @@ function getRuntimeOverrides(runtimeTypes, generics, applyGenerics) {
1600
1713
  return result;
1601
1714
  }
1602
1715
 
1603
- // src/extractors/script/util/jsdoc-input-type.ts
1604
- var MaybeInputTypedefReg = /@typedef\b[\s\S]*\bInput\b/;
1605
- function getJSDocInputType(comment, ts) {
1606
- var _a, _b, _c, _d;
1607
- if (!MaybeInputTypedefReg.test(comment)) return;
1608
- const sourceFile = ts.createSourceFile(
1609
- "_.js",
1610
- comment,
1611
- ts.ScriptTarget.Latest,
1612
- false,
1613
- ts.ScriptKind.JS
1614
- );
1615
- const tags = (_b = (_a = sourceFile.endOfFileToken.jsDoc) == null ? void 0 : _a[0]) == null ? void 0 : _b.tags;
1616
- if (!(tags && hasInputTypeDef(ts, sourceFile, tags))) return;
1617
- let typeParameters;
1618
- for (const tag of tags) {
1619
- if (isTemplateTag(ts, tag)) {
1620
- let constraint = (_c = tag.constraint) == null ? void 0 : _c.type.getText(sourceFile);
1621
- for (const param of tag.typeParameters) {
1622
- const value = {
1623
- name: "" + param.name.escapedText,
1624
- constraint,
1625
- default: (_d = param.default) == null ? void 0 : _d.getText(sourceFile)
1626
- };
1627
- constraint = void 0;
1628
- if (typeParameters) {
1629
- typeParameters.push(value);
1630
- } else {
1631
- typeParameters = [value];
1632
- }
1633
- }
1716
+ // src/extractors/script/util/script-parser.ts
1717
+ var import_parser5 = require("@babel/parser");
1718
+ var plugins = [
1719
+ "exportDefaultFrom",
1720
+ "importAssertions",
1721
+ "typescript"
1722
+ ];
1723
+ var ScriptParser = class {
1724
+ #parsed;
1725
+ constructor(parsed) {
1726
+ this.#parsed = parsed;
1727
+ }
1728
+ statementAt(startIndex, src) {
1729
+ const pos = this.#parsed.positionAt(startIndex);
1730
+ try {
1731
+ return (0, import_parser5.parse)(src, {
1732
+ plugins,
1733
+ startIndex,
1734
+ startLine: pos.line + 1,
1735
+ startColumn: pos.character,
1736
+ strictMode: true,
1737
+ errorRecovery: true,
1738
+ sourceType: "module",
1739
+ allowUndeclaredExports: true,
1740
+ allowSuperOutsideMethod: true,
1741
+ allowAwaitOutsideFunction: true,
1742
+ allowReturnOutsideFunction: true,
1743
+ sourceFilename: this.#parsed.filename
1744
+ }).program.body;
1745
+ } catch {
1746
+ return [];
1634
1747
  }
1635
1748
  }
1636
- return { typeParameters };
1637
- }
1638
- function hasInputTypeDef(ts, sourceFile, tags) {
1639
- var _a;
1640
- for (const tag of tags) {
1641
- if (isTypeDefTag(ts, tag) && ((_a = tag.fullName) == null ? void 0 : _a.getText(sourceFile)) === "Input") {
1642
- return true;
1749
+ expressionAt(startIndex, src) {
1750
+ const pos = this.#parsed.positionAt(startIndex);
1751
+ try {
1752
+ return (0, import_parser5.parseExpression)(src, {
1753
+ plugins,
1754
+ startIndex,
1755
+ startLine: pos.line + 1,
1756
+ startColumn: pos.character,
1757
+ strictMode: true,
1758
+ errorRecovery: true,
1759
+ sourceType: "module",
1760
+ allowUndeclaredExports: true,
1761
+ allowSuperOutsideMethod: true,
1762
+ allowAwaitOutsideFunction: true,
1763
+ allowReturnOutsideFunction: true,
1764
+ sourceFilename: this.#parsed.filename
1765
+ });
1766
+ } catch {
1767
+ return;
1643
1768
  }
1644
1769
  }
1645
- return false;
1646
- }
1647
- function isTypeDefTag(ts, tag) {
1648
- return tag.kind === ts.SyntaxKind.JSDocTypedefTag;
1649
- }
1650
- function isTemplateTag(ts, tag) {
1651
- return tag.kind === ts.SyntaxKind.JSDocTemplateTag;
1652
- }
1770
+ };
1653
1771
 
1654
1772
  // src/extractors/script/index.ts
1655
1773
  var SEP_EMPTY = "";
@@ -1703,7 +1821,7 @@ var ScriptExtractor = class {
1703
1821
  this.#ts = opts.ts;
1704
1822
  this.#runtimeTypes = opts.runtimeTypesCode;
1705
1823
  this.#extractor = new Extractor(parsed);
1706
- this.#scriptParser = new ScriptParser(parsed.filename, parsed.code);
1824
+ this.#scriptParser = new ScriptParser(parsed);
1707
1825
  this.#read = parsed.read.bind(parsed);
1708
1826
  this.#mutationOffsets = crawlProgramScope(this.#parsed, this.#scriptParser);
1709
1827
  this.#writeProgram(parsed.program);
@@ -2146,6 +2264,7 @@ constructor(_?: Return) {}
2146
2264
  #writeTag(tag) {
2147
2265
  const tagName = tag.nameText;
2148
2266
  const renderId = this.#getRenderId(tag);
2267
+ let nestedTagType;
2149
2268
  if (renderId) {
2150
2269
  this.#extractor.write(
2151
2270
  `${varShared("assertRendered")}(${varShared(
@@ -2157,6 +2276,10 @@ constructor(_?: Return) {}
2157
2276
  const def = this.#lookup.getTag(tagName);
2158
2277
  if (def) {
2159
2278
  const importPath = resolveTagImport(this.#filename, def);
2279
+ const isMarkoFile = importPath == null ? void 0 : importPath.endsWith(".marko");
2280
+ if (isMarkoFile) {
2281
+ nestedTagType = `import("${importPath}").Input`;
2282
+ }
2160
2283
  const renderer = (importPath == null ? void 0 : importPath.endsWith(".marko")) ? `renderTemplate(import("${importPath}"))` : def.html ? `renderNativeTag("${def.name}")` : "missingTag";
2161
2284
  if (!def.html && REG_TAG_NAME_IDENTIFIER.test(tagName)) {
2162
2285
  this.#extractor.write(
@@ -2169,6 +2292,7 @@ ${varShared(renderer)})`);
2169
2292
  this.#extractor.write(varShared(renderer));
2170
2293
  }
2171
2294
  } else if (REG_TAG_NAME_IDENTIFIER.test(tagName)) {
2295
+ nestedTagType = `Marko.Input<typeof ${this.#read(tag.name)}>`;
2172
2296
  this.#extractor.write(`${varShared("renderDynamicTag")}(
2173
2297
  `).copy(tag.name).write("\n)");
2174
2298
  } else {
@@ -2184,7 +2308,7 @@ ${varShared(renderer)})`);
2184
2308
  } else {
2185
2309
  this.#extractor.write("()()(");
2186
2310
  }
2187
- this.#writeTagInputObject(tag);
2311
+ this.#writeTagInputObject(tag, nestedTagType);
2188
2312
  if (renderId) {
2189
2313
  this.#extractor.write(`)`);
2190
2314
  }
@@ -2368,7 +2492,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2368
2492
  );
2369
2493
  return hasAttrs;
2370
2494
  }
2371
- #writeAttrTags({ staticAttrTags, dynamicAttrTagParents }, inMerge) {
2495
+ #writeAttrTags({ staticAttrTags, dynamicAttrTagParents }, inMerge, nestedTagType) {
2372
2496
  let wasMerge = false;
2373
2497
  if (dynamicAttrTagParents) {
2374
2498
  if (staticAttrTags) {
@@ -2384,7 +2508,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2384
2508
  }
2385
2509
  }
2386
2510
  if (staticAttrTags) {
2387
- this.#writeStaticAttrTags(staticAttrTags, inMerge);
2511
+ this.#writeStaticAttrTags(staticAttrTags, inMerge, nestedTagType);
2388
2512
  if (dynamicAttrTagParents)
2389
2513
  this.#extractor.write(`}${SEP_COMMA_NEW_LINE}`);
2390
2514
  }
@@ -2393,7 +2517,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2393
2517
  if (wasMerge) this.#extractor.write(`)${SEP_COMMA_NEW_LINE}`);
2394
2518
  }
2395
2519
  }
2396
- #writeStaticAttrTags(staticAttrTags, wasMerge) {
2520
+ #writeStaticAttrTags(staticAttrTags, wasMerge, nestedTagType) {
2397
2521
  if (!wasMerge) this.#extractor.write("...{");
2398
2522
  this.#extractor.write(
2399
2523
  `[${varShared("never")}](){
@@ -2423,6 +2547,15 @@ const attrTags = ${varShared(
2423
2547
  this.#extractor.write("]: ");
2424
2548
  if (isRepeated) {
2425
2549
  this.#extractor.write(`${varShared("repeatedAttrTag")}(
2550
+ ...
2551
+ `);
2552
+ if (nestedTagType && this.#scriptLang === "js" /* js */) {
2553
+ this.#extractor.write(
2554
+ `/** @satisfies {${nestedTagType}["${name}"][]} */
2555
+ `
2556
+ );
2557
+ }
2558
+ this.#extractor.write(`([
2426
2559
  `);
2427
2560
  }
2428
2561
  for (const childNode of attrTag) {
@@ -2430,6 +2563,10 @@ const attrTags = ${varShared(
2430
2563
  this.#extractor.write(SEP_COMMA_NEW_LINE);
2431
2564
  }
2432
2565
  if (isRepeated) {
2566
+ this.#extractor.write("])");
2567
+ if (nestedTagType && this.#scriptLang === "ts" /* ts */) {
2568
+ this.#extractor.write(` satisfies ${nestedTagType}["${name}"][]`);
2569
+ }
2433
2570
  this.#extractor.write(`)${SEP_COMMA_NEW_LINE}`);
2434
2571
  }
2435
2572
  }
@@ -2490,7 +2627,7 @@ const attrTags = ${varShared(
2490
2627
  this.#extractor.write(SEP_COMMA_NEW_LINE);
2491
2628
  }
2492
2629
  }
2493
- #writeTagInputObject(tag) {
2630
+ #writeTagInputObject(tag, nestedTagType) {
2494
2631
  if (!tag.params) this.#writeComments(tag);
2495
2632
  let hasInput = false;
2496
2633
  this.#extractor.write("{\n");
@@ -2511,7 +2648,7 @@ const attrTags = ${varShared(
2511
2648
  let hasRenderBody = false;
2512
2649
  if (body) {
2513
2650
  hasInput = true;
2514
- this.#writeAttrTags(body, false);
2651
+ this.#writeAttrTags(body, false, nestedTagType);
2515
2652
  hasRenderBody = body.renderBody !== void 0;
2516
2653
  } else if (tag.close) {
2517
2654
  hasRenderBody = true;
@@ -2883,33 +3020,127 @@ function getReturnTag(parent) {
2883
3020
  }
2884
3021
  }
2885
3022
  }
2886
- }
2887
- function isValueAttribute(attr) {
2888
- var _a;
2889
- return attr.type === 10 /* AttrNamed */ && ((_a = attr.value) == null ? void 0 : _a.type) === 13 /* AttrValue */;
2890
- }
2891
- function resolveTagImport(from, def) {
2892
- const filename = resolveTagFile(def);
2893
- if (filename) {
2894
- return from ? (0, import_relative_import_path.relativeImportPath)(from, filename) : filename;
3023
+ }
3024
+ function isValueAttribute(attr) {
3025
+ var _a;
3026
+ return attr.type === 10 /* AttrNamed */ && ((_a = attr.value) == null ? void 0 : _a.type) === 13 /* AttrValue */;
3027
+ }
3028
+ function resolveTagImport(from, def) {
3029
+ const filename = resolveTagFile(def);
3030
+ if (filename) {
3031
+ return from ? (0, import_relative_import_path.relativeImportPath)(from, filename) : filename;
3032
+ }
3033
+ }
3034
+ function resolveTagFile(def) {
3035
+ return def && (def.types || def.template || def.renderer);
3036
+ }
3037
+ function isWhitespaceCode(code) {
3038
+ return code <= 32;
3039
+ }
3040
+ function stripExt(filename) {
3041
+ return filename.replace(REG_EXT, "");
3042
+ }
3043
+ function removeNewLines(str) {
3044
+ return str.replace(REG_NEW_LINE, " ");
3045
+ }
3046
+ function isEmptyRange(range) {
3047
+ return range.start === range.end;
3048
+ }
3049
+
3050
+ // src/extractors/style/index.ts
3051
+ function extractStyle(opts) {
3052
+ const { parsed, lookup } = opts;
3053
+ const extractorsByExt = /* @__PURE__ */ new Map();
3054
+ const { read, program, code } = parsed;
3055
+ let placeholderId = 0;
3056
+ for (const node of program.static) {
3057
+ if (node.type === 27 /* Style */) {
3058
+ getExtractor(node.ext || ".css").copy(node.value);
3059
+ }
3060
+ }
3061
+ for (const node of program.body) {
3062
+ visit(node);
3063
+ }
3064
+ const extractedByExt = /* @__PURE__ */ new Map();
3065
+ for (const [ext, extractor] of extractorsByExt) {
3066
+ extractedByExt.set(ext, extractor.end());
3067
+ }
3068
+ return extractedByExt;
3069
+ function visit(node) {
3070
+ var _a, _b;
3071
+ switch (node.type) {
3072
+ case 16 /* AttrTag */:
3073
+ if (node.body) {
3074
+ for (const child of node.body) {
3075
+ visit(child);
3076
+ }
3077
+ }
3078
+ break;
3079
+ case 1 /* Tag */:
3080
+ if (node.body) {
3081
+ if (node.nameText === "style") {
3082
+ const ext = node.shorthandClassNames ? read(node.shorthandClassNames.at(-1)) : ".css";
3083
+ for (const child of node.body) {
3084
+ switch (child.type) {
3085
+ case 17 /* Text */:
3086
+ getExtractor(ext).copy(child);
3087
+ break;
3088
+ case 22 /* Placeholder */:
3089
+ getExtractor(ext).write(`var(--_${placeholderId++})`);
3090
+ break;
3091
+ }
3092
+ }
3093
+ } else {
3094
+ for (const child of node.body) {
3095
+ visit(child);
3096
+ }
3097
+ }
3098
+ }
3099
+ if (node.attrs) {
3100
+ for (const attr of node.attrs) {
3101
+ if (
3102
+ // Check for string literal attribute values.
3103
+ attr.type === 10 /* AttrNamed */ && ((_a = attr.value) == null ? void 0 : _a.type) === 13 /* AttrValue */ && /^['"]$/.test(code[attr.value.value.start])
3104
+ ) {
3105
+ const name = read(attr.name);
3106
+ if (name === "#style" || name === "style" && node.nameText && name === "style" && ((_b = lookup.getTag(node.nameText)) == null ? void 0 : _b.html)) {
3107
+ getExtractor("css").write(":root{").copy({
3108
+ start: attr.value.value.start + 1,
3109
+ end: attr.value.value.end - 1
3110
+ }).write("}");
3111
+ }
3112
+ }
3113
+ }
3114
+ }
3115
+ break;
3116
+ }
3117
+ }
3118
+ function getExtractor(ext) {
3119
+ let extractor = extractorsByExt.get(ext);
3120
+ if (!extractor) {
3121
+ extractorsByExt.set(ext, extractor = new Extractor(parsed));
3122
+ }
3123
+ return extractor;
2895
3124
  }
2896
3125
  }
2897
- function resolveTagFile(def) {
2898
- return def && (def.types || def.template || def.renderer);
2899
- }
2900
- function isWhitespaceCode(code) {
2901
- return code <= 32;
2902
- }
2903
- function stripExt(filename) {
2904
- return filename.replace(REG_EXT, "");
2905
- }
2906
- function removeNewLines(str) {
2907
- return str.replace(REG_NEW_LINE, " ");
2908
- }
2909
- function isEmptyRange(range) {
2910
- return range.start === range.end;
3126
+
3127
+ // src/processors/index.ts
3128
+ var processors_exports = {};
3129
+ __export(processors_exports, {
3130
+ create: () => create,
3131
+ extensions: () => extensions,
3132
+ has: () => has
3133
+ });
3134
+
3135
+ // src/util/get-ext.ts
3136
+ function getExt(fileName) {
3137
+ const extIndex = fileName.lastIndexOf(".");
3138
+ if (extIndex !== -1) return fileName.slice(extIndex);
2911
3139
  }
2912
3140
 
3141
+ // src/processors/marko.ts
3142
+ var import_path3 = __toESM(require("path"));
3143
+
2913
3144
  // src/util/project.ts
2914
3145
  var project_exports = {};
2915
3146
  __export(project_exports, {
@@ -2923,9 +3154,9 @@ __export(project_exports, {
2923
3154
  setDefaultCompilerMeta: () => setDefaultCompilerMeta,
2924
3155
  setDefaultTypePaths: () => setDefaultTypePaths
2925
3156
  });
2926
- var import_path2 = __toESM(require("path"));
2927
- var import_module = require("module");
2928
3157
  var import_strip_json_comments = require("@luxass/strip-json-comments");
3158
+ var import_module = require("module");
3159
+ var import_path2 = __toESM(require("path"));
2929
3160
  var defaultTypeLibs = {};
2930
3161
  var defaultMeta;
2931
3162
  var ignoreErrors = (_err) => {
@@ -3137,22 +3368,7 @@ function interopDefault(mod) {
3137
3368
  return mod.default || mod;
3138
3369
  }
3139
3370
 
3140
- // src/processors/index.ts
3141
- var processors_exports = {};
3142
- __export(processors_exports, {
3143
- create: () => create,
3144
- extensions: () => extensions,
3145
- has: () => has
3146
- });
3147
-
3148
- // src/util/get-ext.ts
3149
- function getExt(fileName) {
3150
- const extIndex = fileName.lastIndexOf(".");
3151
- if (extIndex !== -1) return fileName.slice(extIndex);
3152
- }
3153
-
3154
3371
  // src/processors/marko.ts
3155
- var import_path3 = __toESM(require("path"));
3156
3372
  var isRemapExtensionReg = /\.ts$/;
3157
3373
  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)$/;
3158
3374
  var marko_default = {
@@ -3362,195 +3578,6 @@ function has(fileName) {
3362
3578
  function isDefinitionFile(fileName) {
3363
3579
  return /\.d\.[^.]+$/.test(fileName);
3364
3580
  }
3365
-
3366
- // src/extractors/html/keywords.ts
3367
- 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)$/;
3368
- function isHTMLTag(tag) {
3369
- return builtinTagsRegex.test(tag);
3370
- }
3371
- function getAttributeValueType(value) {
3372
- if (value === void 0 || value[0] !== "=") return void 0;
3373
- value = value.substring(1).trim();
3374
- switch (value) {
3375
- case "NaN":
3376
- case "Infinity":
3377
- case "-Infinity":
3378
- return 1 /* Literal */;
3379
- case "null":
3380
- case "false":
3381
- case "undefined":
3382
- return void 0;
3383
- case "true":
3384
- return 0 /* True */;
3385
- }
3386
- if (
3387
- // double quote string
3388
- /^"(?:[^"\\]+|\\.)*"$/.test(value) || // single quote string
3389
- /^'(?:[^'\\]+|\\.)*'$/.test(value) || // template literal without any interpolations
3390
- /^`(?:[^`\\$]+|\\.|\$(?!\{))*`$/.test(value)
3391
- ) {
3392
- return 2 /* QuotedString */;
3393
- } else if (
3394
- // octal literal
3395
- /^-?0[oO]?[0-7](?:_?[0-7]+)*n?$/.test(value) || // hex literal
3396
- /^-?0[xX][0-9a-fA-F](?:_?[0-9a-fA-F]+)*n?$/.test(value) || // binary literal
3397
- /^-?0[bB][01](?:_?[01]+)*n?$/.test(value) || // integer or float
3398
- /^-?\d(?:_?\d+)*(?:[.eE]\d(?:_?\d+)*|n?|\.?)$/.test(value)
3399
- ) {
3400
- return 1 /* Literal */;
3401
- }
3402
- return 3 /* Dynamic */;
3403
- }
3404
-
3405
- // src/extractors/html/index.ts
3406
- function extractHTML(parsed) {
3407
- return new HTMLExtractor(parsed).end();
3408
- }
3409
- var HTMLExtractor = class {
3410
- #extractor;
3411
- #read;
3412
- #nodeDetails;
3413
- #nodeIdCounter;
3414
- constructor(parsed) {
3415
- this.#extractor = new Extractor(parsed);
3416
- this.#read = parsed.read.bind(parsed);
3417
- this.#nodeDetails = {};
3418
- this.#nodeIdCounter = 0;
3419
- parsed.program.body.forEach((node) => this.#visitNode(node));
3420
- }
3421
- end() {
3422
- return { extracted: this.#extractor.end(), nodeDetails: this.#nodeDetails };
3423
- }
3424
- #visitNode(node) {
3425
- var _a;
3426
- let hasDynamicBody = false, hasDynamicAttrs = false, isDynamic = false;
3427
- switch (node.type) {
3428
- case 16 /* AttrTag */:
3429
- (_a = node.body) == null ? void 0 : _a.forEach((child) => {
3430
- if (this.#visitNode(child)) hasDynamicBody = true;
3431
- });
3432
- break;
3433
- case 1 /* Tag */: {
3434
- const nodeId = `${this.#nodeIdCounter++}`;
3435
- ({ isDynamic, hasDynamicAttrs, hasDynamicBody } = this.#writeTag(
3436
- node,
3437
- nodeId
3438
- ));
3439
- this.#nodeDetails[nodeId] = { hasDynamicAttrs, hasDynamicBody };
3440
- break;
3441
- }
3442
- case 17 /* Text */:
3443
- this.#extractor.copy(node);
3444
- break;
3445
- case 22 /* Placeholder */:
3446
- isDynamic = this.#read({
3447
- start: node.start + 1,
3448
- end: node.start + 2
3449
- }) === "!";
3450
- this.#extractor.write("placeholder");
3451
- break;
3452
- }
3453
- return isDynamic || hasDynamicBody;
3454
- }
3455
- #writeTag(node, id) {
3456
- const isDynamic = !node.nameText || !isHTMLTag(node.nameText);
3457
- let hasDynamicAttrs = false, hasDynamicBody = false;
3458
- if (!isDynamic) {
3459
- ({ hasDynamicAttrs, hasDynamicBody } = this.#writeHTMLTag(node, id));
3460
- } else {
3461
- this.#writeCustomTag(node);
3462
- }
3463
- return { isDynamic, hasDynamicAttrs, hasDynamicBody };
3464
- }
3465
- #writeHTMLTag(node, id) {
3466
- var _a, _b;
3467
- let hasDynamicAttrs = false, hasDynamicBody = false;
3468
- this.#extractor.write("<");
3469
- this.#extractor.copy(node.name);
3470
- this.#extractor.write(` data-marko-node-id="${id}"`);
3471
- (_a = node.attrs) == null ? void 0 : _a.forEach((attr) => {
3472
- if (attr.type === 10 /* AttrNamed */) this.#writeAttrNamed(attr);
3473
- else if (attr.type === 15 /* AttrSpread */) hasDynamicAttrs = true;
3474
- });
3475
- this.#extractor.write(">");
3476
- if (!isVoidTag(node.nameText)) {
3477
- (_b = node.body) == null ? void 0 : _b.forEach((child) => {
3478
- if (this.#visitNode(child)) hasDynamicBody = true;
3479
- });
3480
- this.#extractor.write(`</${node.nameText}>`);
3481
- }
3482
- return { hasDynamicAttrs, hasDynamicBody };
3483
- }
3484
- #writeCustomTag(node) {
3485
- if (node.body) {
3486
- this.#extractor.write("<div>");
3487
- node.body.forEach((node2) => this.#visitNode(node2));
3488
- this.#extractor.write("</div>");
3489
- }
3490
- }
3491
- #writeAttrNamed(attr) {
3492
- this.#extractor.write(" ");
3493
- const nameString = this.#read(attr.name);
3494
- if (/:(?:scoped|(?:no-update(?:-if)?))$/.test(nameString)) {
3495
- this.#extractor.copy({
3496
- start: attr.name.start,
3497
- end: attr.name.start + nameString.lastIndexOf(":")
3498
- });
3499
- } else {
3500
- this.#extractor.copy(attr.name);
3501
- }
3502
- if (attr.value === void 0 || attr.name.start === attr.name.end || attr.value.type === 14 /* AttrMethod */) {
3503
- return;
3504
- }
3505
- const valueString = this.#read(attr.value);
3506
- const valueType = getAttributeValueType(valueString);
3507
- if (valueType === void 0) return;
3508
- switch (valueType) {
3509
- case 0 /* True */:
3510
- break;
3511
- case 1 /* Literal */:
3512
- this.#extractor.write('="');
3513
- this.#extractor.copy({
3514
- start: attr.value.start + valueString.search(/[^=\s]/g),
3515
- end: attr.value.end
3516
- });
3517
- this.#extractor.write('"');
3518
- break;
3519
- case 2 /* QuotedString */:
3520
- this.#extractor.write('="');
3521
- this.#extractor.copy({
3522
- start: attr.value.start + valueString.search(/[^=\s]/g) + 1,
3523
- end: attr.value.end - 1
3524
- });
3525
- this.#extractor.write('"');
3526
- break;
3527
- case 3 /* Dynamic */:
3528
- this.#extractor.write(`="dynamic"`);
3529
- break;
3530
- }
3531
- }
3532
- };
3533
- function isVoidTag(tagName) {
3534
- switch (tagName) {
3535
- case "area":
3536
- case "base":
3537
- case "br":
3538
- case "col":
3539
- case "embed":
3540
- case "hr":
3541
- case "img":
3542
- case "input":
3543
- case "link":
3544
- case "meta":
3545
- case "param":
3546
- case "source":
3547
- case "track":
3548
- case "wbr":
3549
- return true;
3550
- default:
3551
- return false;
3552
- }
3553
- }
3554
3581
  // Annotate the CommonJS export names for ESM import in node:
3555
3582
  0 && (module.exports = {
3556
3583
  NodeType,