@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.js CHANGED
@@ -358,6 +358,8 @@ var Builder = class {
358
358
  );
359
359
  this.#comments = void 0;
360
360
  return import_htmljs_parser.TagType.statement;
361
+ case "server":
362
+ case "client":
361
363
  case "static":
362
364
  this.#program.static.push(
363
365
  this.#staticNode = {
@@ -850,135 +852,197 @@ function sortBySourceThenGenerated(a, b) {
850
852
  return delta === 0 ? b.generatedStart - a.generatedStart : delta;
851
853
  }
852
854
 
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
- }
855
+ // src/extractors/html/keywords.ts
856
+ 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)$/;
857
+ function isHTMLTag(tag) {
858
+ return builtinTagsRegex.test(tag);
859
+ }
860
+ function getAttributeValueType(value) {
861
+ if (value === void 0 || value[0] !== "=") return void 0;
862
+ value = value.substring(1).trim();
863
+ switch (value) {
864
+ case "NaN":
865
+ case "Infinity":
866
+ case "-Infinity":
867
+ return 1 /* Literal */;
868
+ case "null":
869
+ case "false":
870
+ case "undefined":
871
+ return void 0;
872
+ case "true":
873
+ return 0 /* True */;
863
874
  }
864
- for (const node of program.body) {
865
- visit(node);
875
+ if (
876
+ // double quote string
877
+ /^"(?:[^"\\]+|\\.)*"$/.test(value) || // single quote string
878
+ /^'(?:[^'\\]+|\\.)*'$/.test(value) || // template literal without any interpolations
879
+ /^`(?:[^`\\$]+|\\.|\$(?!\{))*`$/.test(value)
880
+ ) {
881
+ return 2 /* QuotedString */;
882
+ } else if (
883
+ // octal literal
884
+ /^-?0[oO]?[0-7](?:_?[0-7]+)*n?$/.test(value) || // hex literal
885
+ /^-?0[xX][0-9a-fA-F](?:_?[0-9a-fA-F]+)*n?$/.test(value) || // binary literal
886
+ /^-?0[bB][01](?:_?[01]+)*n?$/.test(value) || // integer or float
887
+ /^-?\d(?:_?\d+)*(?:[.eE]\d(?:_?\d+)*|n?|\.?)$/.test(value)
888
+ ) {
889
+ return 1 /* Literal */;
866
890
  }
867
- const extractedByExt = /* @__PURE__ */ new Map();
868
- for (const [ext, extractor] of extractorsByExt) {
869
- extractedByExt.set(ext, extractor.end());
891
+ return 3 /* Dynamic */;
892
+ }
893
+
894
+ // src/extractors/html/index.ts
895
+ function extractHTML(parsed) {
896
+ return new HTMLExtractor(parsed).end();
897
+ }
898
+ var HTMLExtractor = class {
899
+ #extractor;
900
+ #read;
901
+ #nodeDetails;
902
+ #nodeIdCounter;
903
+ constructor(parsed) {
904
+ this.#extractor = new Extractor(parsed);
905
+ this.#read = parsed.read.bind(parsed);
906
+ this.#nodeDetails = {};
907
+ this.#nodeIdCounter = 0;
908
+ parsed.program.body.forEach((node) => this.#visitNode(node));
870
909
  }
871
- return extractedByExt;
872
- function visit(node) {
873
- var _a, _b;
910
+ end() {
911
+ return { extracted: this.#extractor.end(), nodeDetails: this.#nodeDetails };
912
+ }
913
+ #visitNode(node) {
914
+ var _a;
915
+ let hasDynamicBody = false, hasDynamicAttrs = false, isDynamic = false;
874
916
  switch (node.type) {
875
917
  case 16 /* AttrTag */:
876
- if (node.body) {
877
- for (const child of node.body) {
878
- visit(child);
879
- }
880
- }
918
+ (_a = node.body) == null ? void 0 : _a.forEach((child) => {
919
+ if (this.#visitNode(child)) hasDynamicBody = true;
920
+ });
881
921
  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
- }
922
+ case 1 /* Tag */: {
923
+ const nodeId = `${this.#nodeIdCounter++}`;
924
+ ({ isDynamic, hasDynamicAttrs, hasDynamicBody } = this.#writeTag(
925
+ node,
926
+ nodeId
927
+ ));
928
+ this.#nodeDetails[nodeId] = { hasDynamicAttrs, hasDynamicBody };
929
+ break;
930
+ }
931
+ case 17 /* Text */:
932
+ this.#extractor.copy(node);
933
+ break;
934
+ case 22 /* Placeholder */:
935
+ isDynamic = this.#read({
936
+ start: node.start + 1,
937
+ end: node.start + 2
938
+ }) === "!";
939
+ this.#extractor.write("placeholder");
918
940
  break;
919
941
  }
942
+ return isDynamic || hasDynamicBody;
920
943
  }
921
- function getExtractor(ext) {
922
- let extractor = extractorsByExt.get(ext);
923
- if (!extractor) {
924
- extractorsByExt.set(ext, extractor = new Extractor(parsed));
944
+ #writeTag(node, id) {
945
+ const isDynamic = !node.nameText || !isHTMLTag(node.nameText);
946
+ let hasDynamicAttrs = false, hasDynamicBody = false;
947
+ if (!isDynamic) {
948
+ ({ hasDynamicAttrs, hasDynamicBody } = this.#writeHTMLTag(node, id));
949
+ } else {
950
+ this.#writeCustomTag(node);
925
951
  }
926
- return extractor;
952
+ return { isDynamic, hasDynamicAttrs, hasDynamicBody };
927
953
  }
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, " ");
954
+ #writeHTMLTag(node, id) {
955
+ var _a, _b;
956
+ let hasDynamicAttrs = false, hasDynamicBody = false;
957
+ this.#extractor.write("<");
958
+ this.#extractor.copy(node.name);
959
+ this.#extractor.write(` data-marko-node-id="${id}"`);
960
+ (_a = node.attrs) == null ? void 0 : _a.forEach((attr) => {
961
+ if (attr.type === 10 /* AttrNamed */) this.#writeAttrNamed(attr);
962
+ else if (attr.type === 15 /* AttrSpread */) hasDynamicAttrs = true;
963
+ });
964
+ this.#extractor.write(">");
965
+ if (!isVoidTag(node.nameText)) {
966
+ (_b = node.body) == null ? void 0 : _b.forEach((child) => {
967
+ if (this.#visitNode(child)) hasDynamicBody = true;
968
+ });
969
+ this.#extractor.write(`</${node.nameText}>`);
970
+ }
971
+ return { hasDynamicAttrs, hasDynamicBody };
946
972
  }
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 [];
973
+ #writeCustomTag(node) {
974
+ if (node.body) {
975
+ this.#extractor.write("<div>");
976
+ node.body.forEach((node2) => this.#visitNode(node2));
977
+ this.#extractor.write("</div>");
962
978
  }
963
979
  }
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
980
+ #writeAttrNamed(attr) {
981
+ this.#extractor.write(" ");
982
+ const nameString = this.#read(attr.name);
983
+ if (/:(?:scoped|(?:no-update(?:-if)?))$/.test(nameString)) {
984
+ this.#extractor.copy({
985
+ start: attr.name.start,
986
+ end: attr.name.start + nameString.lastIndexOf(":")
976
987
  });
977
- } catch {
988
+ } else {
989
+ this.#extractor.copy(attr.name);
990
+ }
991
+ if (attr.value === void 0 || attr.name.start === attr.name.end || attr.value.type === 14 /* AttrMethod */) {
978
992
  return;
979
993
  }
994
+ const valueString = this.#read(attr.value);
995
+ const valueType = getAttributeValueType(valueString);
996
+ if (valueType === void 0) return;
997
+ switch (valueType) {
998
+ case 0 /* True */:
999
+ break;
1000
+ case 1 /* Literal */:
1001
+ this.#extractor.write('="');
1002
+ this.#extractor.copy({
1003
+ start: attr.value.start + valueString.search(/[^=\s]/g),
1004
+ end: attr.value.end
1005
+ });
1006
+ this.#extractor.write('"');
1007
+ break;
1008
+ case 2 /* QuotedString */:
1009
+ this.#extractor.write('="');
1010
+ this.#extractor.copy({
1011
+ start: attr.value.start + valueString.search(/[^=\s]/g) + 1,
1012
+ end: attr.value.end - 1
1013
+ });
1014
+ this.#extractor.write('"');
1015
+ break;
1016
+ case 3 /* Dynamic */:
1017
+ this.#extractor.write(`="dynamic"`);
1018
+ break;
1019
+ }
980
1020
  }
981
1021
  };
1022
+ function isVoidTag(tagName) {
1023
+ switch (tagName) {
1024
+ case "area":
1025
+ case "base":
1026
+ case "br":
1027
+ case "col":
1028
+ case "embed":
1029
+ case "hr":
1030
+ case "img":
1031
+ case "input":
1032
+ case "link":
1033
+ case "meta":
1034
+ case "param":
1035
+ case "source":
1036
+ case "track":
1037
+ case "wbr":
1038
+ return true;
1039
+ default:
1040
+ return false;
1041
+ }
1042
+ }
1043
+
1044
+ // src/extractors/script/index.ts
1045
+ var import_relative_import_path = require("relative-import-path");
982
1046
 
983
1047
  // src/extractors/script/util/attach-scopes.ts
984
1048
  var t = __toESM(require("@babel/types"));
@@ -1057,9 +1121,12 @@ function crawlProgramScope(parsed, scriptParser) {
1057
1121
  case 16 /* AttrTag */: {
1058
1122
  if (child.var) {
1059
1123
  parentScope.bindings ??= {};
1060
- const parsedFn = scriptParser.expressionAt(child.var.value.start - 6, `${read(child.var.value)}=0`);
1124
+ const parsedFn = scriptParser.expressionAt(
1125
+ child.var.value.start - 1,
1126
+ `(${read(child.var.value)})=>0`
1127
+ );
1061
1128
  if (parsedFn) {
1062
- const lVal = parsedFn.left;
1129
+ const lVal = parsedFn.params[0];
1063
1130
  checkForMutations(parentScope, lVal);
1064
1131
  for (const id of getVarIdentifiers(
1065
1132
  parsed,
@@ -1547,6 +1614,57 @@ function tryReaddirSync(dir) {
1547
1614
  }
1548
1615
  }
1549
1616
 
1617
+ // src/extractors/script/util/jsdoc-input-type.ts
1618
+ var MaybeInputTypedefReg = /@typedef\b[\s\S]*\bInput\b/;
1619
+ function getJSDocInputType(comment, ts) {
1620
+ var _a, _b, _c, _d;
1621
+ if (!MaybeInputTypedefReg.test(comment)) return;
1622
+ const sourceFile = ts.createSourceFile(
1623
+ "_.js",
1624
+ comment,
1625
+ ts.ScriptTarget.Latest,
1626
+ false,
1627
+ ts.ScriptKind.JS
1628
+ );
1629
+ const tags = (_b = (_a = sourceFile.endOfFileToken.jsDoc) == null ? void 0 : _a[0]) == null ? void 0 : _b.tags;
1630
+ if (!(tags && hasInputTypeDef(ts, sourceFile, tags))) return;
1631
+ let typeParameters;
1632
+ for (const tag of tags) {
1633
+ if (isTemplateTag(ts, tag)) {
1634
+ let constraint = (_c = tag.constraint) == null ? void 0 : _c.type.getText(sourceFile);
1635
+ for (const param of tag.typeParameters) {
1636
+ const value = {
1637
+ name: "" + param.name.escapedText,
1638
+ constraint,
1639
+ default: (_d = param.default) == null ? void 0 : _d.getText(sourceFile)
1640
+ };
1641
+ constraint = void 0;
1642
+ if (typeParameters) {
1643
+ typeParameters.push(value);
1644
+ } else {
1645
+ typeParameters = [value];
1646
+ }
1647
+ }
1648
+ }
1649
+ }
1650
+ return { typeParameters };
1651
+ }
1652
+ function hasInputTypeDef(ts, sourceFile, tags) {
1653
+ var _a;
1654
+ for (const tag of tags) {
1655
+ if (isTypeDefTag(ts, tag) && ((_a = tag.fullName) == null ? void 0 : _a.getText(sourceFile)) === "Input") {
1656
+ return true;
1657
+ }
1658
+ }
1659
+ return false;
1660
+ }
1661
+ function isTypeDefTag(ts, tag) {
1662
+ return tag.kind === ts.SyntaxKind.JSDocTypedefTag;
1663
+ }
1664
+ function isTemplateTag(ts, tag) {
1665
+ return tag.kind === ts.SyntaxKind.JSDocTemplateTag;
1666
+ }
1667
+
1550
1668
  // src/extractors/script/util/runtime-overrides.ts
1551
1669
  var RuntimeOverloads = /* @__PURE__ */ new Map();
1552
1670
  var commentsReg = /\/\*(?:[^*]+|\*[^/])*\*\//gm;
@@ -1600,56 +1718,61 @@ function getRuntimeOverrides(runtimeTypes, generics, applyGenerics) {
1600
1718
  return result;
1601
1719
  }
1602
1720
 
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
- }
1721
+ // src/extractors/script/util/script-parser.ts
1722
+ var import_parser5 = require("@babel/parser");
1723
+ var plugins = [
1724
+ "exportDefaultFrom",
1725
+ "importAssertions",
1726
+ "typescript"
1727
+ ];
1728
+ var ScriptParser = class {
1729
+ #parsed;
1730
+ constructor(parsed) {
1731
+ this.#parsed = parsed;
1732
+ }
1733
+ statementAt(startIndex, src) {
1734
+ const pos = this.#parsed.positionAt(startIndex);
1735
+ try {
1736
+ return (0, import_parser5.parse)(src, {
1737
+ plugins,
1738
+ startIndex,
1739
+ startLine: pos.line + 1,
1740
+ startColumn: pos.character,
1741
+ strictMode: true,
1742
+ errorRecovery: true,
1743
+ sourceType: "module",
1744
+ allowUndeclaredExports: true,
1745
+ allowSuperOutsideMethod: true,
1746
+ allowAwaitOutsideFunction: true,
1747
+ allowReturnOutsideFunction: true,
1748
+ sourceFilename: this.#parsed.filename
1749
+ }).program.body;
1750
+ } catch {
1751
+ return [];
1634
1752
  }
1635
1753
  }
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;
1754
+ expressionAt(startIndex, src) {
1755
+ const pos = this.#parsed.positionAt(startIndex);
1756
+ try {
1757
+ return (0, import_parser5.parseExpression)(src, {
1758
+ plugins,
1759
+ startIndex,
1760
+ startLine: pos.line + 1,
1761
+ startColumn: pos.character,
1762
+ strictMode: true,
1763
+ errorRecovery: true,
1764
+ sourceType: "module",
1765
+ allowUndeclaredExports: true,
1766
+ allowSuperOutsideMethod: true,
1767
+ allowAwaitOutsideFunction: true,
1768
+ allowReturnOutsideFunction: true,
1769
+ sourceFilename: this.#parsed.filename
1770
+ });
1771
+ } catch {
1772
+ return;
1643
1773
  }
1644
1774
  }
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
- }
1775
+ };
1653
1776
 
1654
1777
  // src/extractors/script/index.ts
1655
1778
  var SEP_EMPTY = "";
@@ -1703,7 +1826,7 @@ var ScriptExtractor = class {
1703
1826
  this.#ts = opts.ts;
1704
1827
  this.#runtimeTypes = opts.runtimeTypesCode;
1705
1828
  this.#extractor = new Extractor(parsed);
1706
- this.#scriptParser = new ScriptParser(parsed.filename, parsed.code);
1829
+ this.#scriptParser = new ScriptParser(parsed);
1707
1830
  this.#read = parsed.read.bind(parsed);
1708
1831
  this.#mutationOffsets = crawlProgramScope(this.#parsed, this.#scriptParser);
1709
1832
  this.#writeProgram(parsed.program);
@@ -2146,6 +2269,7 @@ constructor(_?: Return) {}
2146
2269
  #writeTag(tag) {
2147
2270
  const tagName = tag.nameText;
2148
2271
  const renderId = this.#getRenderId(tag);
2272
+ let nestedTagType;
2149
2273
  if (renderId) {
2150
2274
  this.#extractor.write(
2151
2275
  `${varShared("assertRendered")}(${varShared(
@@ -2157,6 +2281,10 @@ constructor(_?: Return) {}
2157
2281
  const def = this.#lookup.getTag(tagName);
2158
2282
  if (def) {
2159
2283
  const importPath = resolveTagImport(this.#filename, def);
2284
+ const isMarkoFile = importPath == null ? void 0 : importPath.endsWith(".marko");
2285
+ if (isMarkoFile) {
2286
+ nestedTagType = `import("${importPath}").Input`;
2287
+ }
2160
2288
  const renderer = (importPath == null ? void 0 : importPath.endsWith(".marko")) ? `renderTemplate(import("${importPath}"))` : def.html ? `renderNativeTag("${def.name}")` : "missingTag";
2161
2289
  if (!def.html && REG_TAG_NAME_IDENTIFIER.test(tagName)) {
2162
2290
  this.#extractor.write(
@@ -2169,6 +2297,7 @@ ${varShared(renderer)})`);
2169
2297
  this.#extractor.write(varShared(renderer));
2170
2298
  }
2171
2299
  } else if (REG_TAG_NAME_IDENTIFIER.test(tagName)) {
2300
+ nestedTagType = `Marko.Input<typeof ${this.#read(tag.name)}>`;
2172
2301
  this.#extractor.write(`${varShared("renderDynamicTag")}(
2173
2302
  `).copy(tag.name).write("\n)");
2174
2303
  } else {
@@ -2184,7 +2313,7 @@ ${varShared(renderer)})`);
2184
2313
  } else {
2185
2314
  this.#extractor.write("()()(");
2186
2315
  }
2187
- this.#writeTagInputObject(tag);
2316
+ this.#writeTagInputObject(tag, nestedTagType);
2188
2317
  if (renderId) {
2189
2318
  this.#extractor.write(`)`);
2190
2319
  }
@@ -2368,7 +2497,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2368
2497
  );
2369
2498
  return hasAttrs;
2370
2499
  }
2371
- #writeAttrTags({ staticAttrTags, dynamicAttrTagParents }, inMerge) {
2500
+ #writeAttrTags({ staticAttrTags, dynamicAttrTagParents }, inMerge, nestedTagType) {
2372
2501
  let wasMerge = false;
2373
2502
  if (dynamicAttrTagParents) {
2374
2503
  if (staticAttrTags) {
@@ -2384,7 +2513,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2384
2513
  }
2385
2514
  }
2386
2515
  if (staticAttrTags) {
2387
- this.#writeStaticAttrTags(staticAttrTags, inMerge);
2516
+ this.#writeStaticAttrTags(staticAttrTags, inMerge, nestedTagType);
2388
2517
  if (dynamicAttrTagParents)
2389
2518
  this.#extractor.write(`}${SEP_COMMA_NEW_LINE}`);
2390
2519
  }
@@ -2393,7 +2522,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2393
2522
  if (wasMerge) this.#extractor.write(`)${SEP_COMMA_NEW_LINE}`);
2394
2523
  }
2395
2524
  }
2396
- #writeStaticAttrTags(staticAttrTags, wasMerge) {
2525
+ #writeStaticAttrTags(staticAttrTags, wasMerge, nestedTagType) {
2397
2526
  if (!wasMerge) this.#extractor.write("...{");
2398
2527
  this.#extractor.write(
2399
2528
  `[${varShared("never")}](){
@@ -2423,6 +2552,15 @@ const attrTags = ${varShared(
2423
2552
  this.#extractor.write("]: ");
2424
2553
  if (isRepeated) {
2425
2554
  this.#extractor.write(`${varShared("repeatedAttrTag")}(
2555
+ ...
2556
+ `);
2557
+ if (nestedTagType && this.#scriptLang === "js" /* js */) {
2558
+ this.#extractor.write(
2559
+ `/** @satisfies {${nestedTagType}["${name}"][]} */
2560
+ `
2561
+ );
2562
+ }
2563
+ this.#extractor.write(`([
2426
2564
  `);
2427
2565
  }
2428
2566
  for (const childNode of attrTag) {
@@ -2430,6 +2568,10 @@ const attrTags = ${varShared(
2430
2568
  this.#extractor.write(SEP_COMMA_NEW_LINE);
2431
2569
  }
2432
2570
  if (isRepeated) {
2571
+ this.#extractor.write("])");
2572
+ if (nestedTagType && this.#scriptLang === "ts" /* ts */) {
2573
+ this.#extractor.write(` satisfies ${nestedTagType}["${name}"][]`);
2574
+ }
2433
2575
  this.#extractor.write(`)${SEP_COMMA_NEW_LINE}`);
2434
2576
  }
2435
2577
  }
@@ -2490,7 +2632,7 @@ const attrTags = ${varShared(
2490
2632
  this.#extractor.write(SEP_COMMA_NEW_LINE);
2491
2633
  }
2492
2634
  }
2493
- #writeTagInputObject(tag) {
2635
+ #writeTagInputObject(tag, nestedTagType) {
2494
2636
  if (!tag.params) this.#writeComments(tag);
2495
2637
  let hasInput = false;
2496
2638
  this.#extractor.write("{\n");
@@ -2511,7 +2653,7 @@ const attrTags = ${varShared(
2511
2653
  let hasRenderBody = false;
2512
2654
  if (body) {
2513
2655
  hasInput = true;
2514
- this.#writeAttrTags(body, false);
2656
+ this.#writeAttrTags(body, false, nestedTagType);
2515
2657
  hasRenderBody = body.renderBody !== void 0;
2516
2658
  } else if (tag.close) {
2517
2659
  hasRenderBody = true;
@@ -2883,33 +3025,127 @@ function getReturnTag(parent) {
2883
3025
  }
2884
3026
  }
2885
3027
  }
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;
3028
+ }
3029
+ function isValueAttribute(attr) {
3030
+ var _a;
3031
+ return attr.type === 10 /* AttrNamed */ && ((_a = attr.value) == null ? void 0 : _a.type) === 13 /* AttrValue */;
3032
+ }
3033
+ function resolveTagImport(from, def) {
3034
+ const filename = resolveTagFile(def);
3035
+ if (filename) {
3036
+ return from ? (0, import_relative_import_path.relativeImportPath)(from, filename) : filename;
3037
+ }
3038
+ }
3039
+ function resolveTagFile(def) {
3040
+ return def && (def.types || def.template || def.renderer);
3041
+ }
3042
+ function isWhitespaceCode(code) {
3043
+ return code <= 32;
3044
+ }
3045
+ function stripExt(filename) {
3046
+ return filename.replace(REG_EXT, "");
3047
+ }
3048
+ function removeNewLines(str) {
3049
+ return str.replace(REG_NEW_LINE, " ");
3050
+ }
3051
+ function isEmptyRange(range) {
3052
+ return range.start === range.end;
3053
+ }
3054
+
3055
+ // src/extractors/style/index.ts
3056
+ function extractStyle(opts) {
3057
+ const { parsed, lookup } = opts;
3058
+ const extractorsByExt = /* @__PURE__ */ new Map();
3059
+ const { read, program, code } = parsed;
3060
+ let placeholderId = 0;
3061
+ for (const node of program.static) {
3062
+ if (node.type === 27 /* Style */) {
3063
+ getExtractor(node.ext || ".css").copy(node.value);
3064
+ }
3065
+ }
3066
+ for (const node of program.body) {
3067
+ visit(node);
3068
+ }
3069
+ const extractedByExt = /* @__PURE__ */ new Map();
3070
+ for (const [ext, extractor] of extractorsByExt) {
3071
+ extractedByExt.set(ext, extractor.end());
3072
+ }
3073
+ return extractedByExt;
3074
+ function visit(node) {
3075
+ var _a, _b;
3076
+ switch (node.type) {
3077
+ case 16 /* AttrTag */:
3078
+ if (node.body) {
3079
+ for (const child of node.body) {
3080
+ visit(child);
3081
+ }
3082
+ }
3083
+ break;
3084
+ case 1 /* Tag */:
3085
+ if (node.body) {
3086
+ if (node.nameText === "style") {
3087
+ const ext = node.shorthandClassNames ? read(node.shorthandClassNames.at(-1)) : ".css";
3088
+ for (const child of node.body) {
3089
+ switch (child.type) {
3090
+ case 17 /* Text */:
3091
+ getExtractor(ext).copy(child);
3092
+ break;
3093
+ case 22 /* Placeholder */:
3094
+ getExtractor(ext).write(`var(--_${placeholderId++})`);
3095
+ break;
3096
+ }
3097
+ }
3098
+ } else {
3099
+ for (const child of node.body) {
3100
+ visit(child);
3101
+ }
3102
+ }
3103
+ }
3104
+ if (node.attrs) {
3105
+ for (const attr of node.attrs) {
3106
+ if (
3107
+ // Check for string literal attribute values.
3108
+ attr.type === 10 /* AttrNamed */ && ((_a = attr.value) == null ? void 0 : _a.type) === 13 /* AttrValue */ && /^['"]$/.test(code[attr.value.value.start])
3109
+ ) {
3110
+ const name = read(attr.name);
3111
+ if (name === "#style" || name === "style" && node.nameText && name === "style" && ((_b = lookup.getTag(node.nameText)) == null ? void 0 : _b.html)) {
3112
+ getExtractor("css").write(":root{").copy({
3113
+ start: attr.value.value.start + 1,
3114
+ end: attr.value.value.end - 1
3115
+ }).write("}");
3116
+ }
3117
+ }
3118
+ }
3119
+ }
3120
+ break;
3121
+ }
3122
+ }
3123
+ function getExtractor(ext) {
3124
+ let extractor = extractorsByExt.get(ext);
3125
+ if (!extractor) {
3126
+ extractorsByExt.set(ext, extractor = new Extractor(parsed));
3127
+ }
3128
+ return extractor;
2895
3129
  }
2896
3130
  }
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;
3131
+
3132
+ // src/processors/index.ts
3133
+ var processors_exports = {};
3134
+ __export(processors_exports, {
3135
+ create: () => create,
3136
+ extensions: () => extensions,
3137
+ has: () => has
3138
+ });
3139
+
3140
+ // src/util/get-ext.ts
3141
+ function getExt(fileName) {
3142
+ const extIndex = fileName.lastIndexOf(".");
3143
+ if (extIndex !== -1) return fileName.slice(extIndex);
2911
3144
  }
2912
3145
 
3146
+ // src/processors/marko.ts
3147
+ var import_path3 = __toESM(require("path"));
3148
+
2913
3149
  // src/util/project.ts
2914
3150
  var project_exports = {};
2915
3151
  __export(project_exports, {
@@ -2923,9 +3159,9 @@ __export(project_exports, {
2923
3159
  setDefaultCompilerMeta: () => setDefaultCompilerMeta,
2924
3160
  setDefaultTypePaths: () => setDefaultTypePaths
2925
3161
  });
2926
- var import_path2 = __toESM(require("path"));
2927
- var import_module = require("module");
2928
3162
  var import_strip_json_comments = require("@luxass/strip-json-comments");
3163
+ var import_module = require("module");
3164
+ var import_path2 = __toESM(require("path"));
2929
3165
  var defaultTypeLibs = {};
2930
3166
  var defaultMeta;
2931
3167
  var ignoreErrors = (_err) => {
@@ -3137,22 +3373,7 @@ function interopDefault(mod) {
3137
3373
  return mod.default || mod;
3138
3374
  }
3139
3375
 
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
3376
  // src/processors/marko.ts
3155
- var import_path3 = __toESM(require("path"));
3156
3377
  var isRemapExtensionReg = /\.ts$/;
3157
3378
  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
3379
  var marko_default = {
@@ -3362,195 +3583,6 @@ function has(fileName) {
3362
3583
  function isDefinitionFile(fileName) {
3363
3584
  return /\.d\.[^.]+$/.test(fileName);
3364
3585
  }
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
3586
  // Annotate the CommonJS export names for ESM import in node:
3555
3587
  0 && (module.exports = {
3556
3588
  NodeType,