@marko/language-tools 2.4.5 → 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.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;
@@ -811,144 +811,203 @@ function sortBySourceThenGenerated(a, b) {
811
811
  return delta === 0 ? b.generatedStart - a.generatedStart : delta;
812
812
  }
813
813
 
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
- }
814
+ // src/extractors/html/keywords.ts
815
+ 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)$/;
816
+ function isHTMLTag(tag) {
817
+ return builtinTagsRegex.test(tag);
818
+ }
819
+ function getAttributeValueType(value) {
820
+ if (value === void 0 || value[0] !== "=") return void 0;
821
+ value = value.substring(1).trim();
822
+ switch (value) {
823
+ case "NaN":
824
+ case "Infinity":
825
+ case "-Infinity":
826
+ return 1 /* Literal */;
827
+ case "null":
828
+ case "false":
829
+ case "undefined":
830
+ return void 0;
831
+ case "true":
832
+ return 0 /* True */;
824
833
  }
825
- for (const node of program.body) {
826
- visit(node);
834
+ if (
835
+ // double quote string
836
+ /^"(?:[^"\\]+|\\.)*"$/.test(value) || // single quote string
837
+ /^'(?:[^'\\]+|\\.)*'$/.test(value) || // template literal without any interpolations
838
+ /^`(?:[^`\\$]+|\\.|\$(?!\{))*`$/.test(value)
839
+ ) {
840
+ return 2 /* QuotedString */;
841
+ } else if (
842
+ // octal literal
843
+ /^-?0[oO]?[0-7](?:_?[0-7]+)*n?$/.test(value) || // hex literal
844
+ /^-?0[xX][0-9a-fA-F](?:_?[0-9a-fA-F]+)*n?$/.test(value) || // binary literal
845
+ /^-?0[bB][01](?:_?[01]+)*n?$/.test(value) || // integer or float
846
+ /^-?\d(?:_?\d+)*(?:[.eE]\d(?:_?\d+)*|n?|\.?)$/.test(value)
847
+ ) {
848
+ return 1 /* Literal */;
827
849
  }
828
- const extractedByExt = /* @__PURE__ */ new Map();
829
- for (const [ext, extractor] of extractorsByExt) {
830
- extractedByExt.set(ext, extractor.end());
850
+ return 3 /* Dynamic */;
851
+ }
852
+
853
+ // src/extractors/html/index.ts
854
+ function extractHTML(parsed) {
855
+ return new HTMLExtractor(parsed).end();
856
+ }
857
+ var HTMLExtractor = class {
858
+ #extractor;
859
+ #read;
860
+ #nodeDetails;
861
+ #nodeIdCounter;
862
+ constructor(parsed) {
863
+ this.#extractor = new Extractor(parsed);
864
+ this.#read = parsed.read.bind(parsed);
865
+ this.#nodeDetails = {};
866
+ this.#nodeIdCounter = 0;
867
+ parsed.program.body.forEach((node) => this.#visitNode(node));
831
868
  }
832
- return extractedByExt;
833
- function visit(node) {
834
- var _a, _b;
869
+ end() {
870
+ return { extracted: this.#extractor.end(), nodeDetails: this.#nodeDetails };
871
+ }
872
+ #visitNode(node) {
873
+ var _a;
874
+ let hasDynamicBody = false, hasDynamicAttrs = false, isDynamic = false;
835
875
  switch (node.type) {
836
876
  case 16 /* AttrTag */:
837
- if (node.body) {
838
- for (const child of node.body) {
839
- visit(child);
840
- }
841
- }
877
+ (_a = node.body) == null ? void 0 : _a.forEach((child) => {
878
+ if (this.#visitNode(child)) hasDynamicBody = true;
879
+ });
842
880
  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
- }
881
+ case 1 /* Tag */: {
882
+ const nodeId = `${this.#nodeIdCounter++}`;
883
+ ({ isDynamic, hasDynamicAttrs, hasDynamicBody } = this.#writeTag(
884
+ node,
885
+ nodeId
886
+ ));
887
+ this.#nodeDetails[nodeId] = { hasDynamicAttrs, hasDynamicBody };
888
+ break;
889
+ }
890
+ case 17 /* Text */:
891
+ this.#extractor.copy(node);
892
+ break;
893
+ case 22 /* Placeholder */:
894
+ isDynamic = this.#read({
895
+ start: node.start + 1,
896
+ end: node.start + 2
897
+ }) === "!";
898
+ this.#extractor.write("placeholder");
879
899
  break;
880
900
  }
901
+ return isDynamic || hasDynamicBody;
881
902
  }
882
- function getExtractor(ext) {
883
- let extractor = extractorsByExt.get(ext);
884
- if (!extractor) {
885
- extractorsByExt.set(ext, extractor = new Extractor(parsed));
903
+ #writeTag(node, id) {
904
+ const isDynamic = !node.nameText || !isHTMLTag(node.nameText);
905
+ let hasDynamicAttrs = false, hasDynamicBody = false;
906
+ if (!isDynamic) {
907
+ ({ hasDynamicAttrs, hasDynamicBody } = this.#writeHTMLTag(node, id));
908
+ } else {
909
+ this.#writeCustomTag(node);
886
910
  }
887
- return extractor;
911
+ return { isDynamic, hasDynamicAttrs, hasDynamicBody };
888
912
  }
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, " ");
913
+ #writeHTMLTag(node, id) {
914
+ var _a, _b;
915
+ let hasDynamicAttrs = false, hasDynamicBody = false;
916
+ this.#extractor.write("<");
917
+ this.#extractor.copy(node.name);
918
+ this.#extractor.write(` data-marko-node-id="${id}"`);
919
+ (_a = node.attrs) == null ? void 0 : _a.forEach((attr) => {
920
+ if (attr.type === 10 /* AttrNamed */) this.#writeAttrNamed(attr);
921
+ else if (attr.type === 15 /* AttrSpread */) hasDynamicAttrs = true;
922
+ });
923
+ this.#extractor.write(">");
924
+ if (!isVoidTag(node.nameText)) {
925
+ (_b = node.body) == null ? void 0 : _b.forEach((child) => {
926
+ if (this.#visitNode(child)) hasDynamicBody = true;
927
+ });
928
+ this.#extractor.write(`</${node.nameText}>`);
929
+ }
930
+ return { hasDynamicAttrs, hasDynamicBody };
910
931
  }
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 [];
932
+ #writeCustomTag(node) {
933
+ if (node.body) {
934
+ this.#extractor.write("<div>");
935
+ node.body.forEach((node2) => this.#visitNode(node2));
936
+ this.#extractor.write("</div>");
926
937
  }
927
938
  }
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
939
+ #writeAttrNamed(attr) {
940
+ this.#extractor.write(" ");
941
+ const nameString = this.#read(attr.name);
942
+ if (/:(?:scoped|(?:no-update(?:-if)?))$/.test(nameString)) {
943
+ this.#extractor.copy({
944
+ start: attr.name.start,
945
+ end: attr.name.start + nameString.lastIndexOf(":")
940
946
  });
941
- } catch {
947
+ } else {
948
+ this.#extractor.copy(attr.name);
949
+ }
950
+ if (attr.value === void 0 || attr.name.start === attr.name.end || attr.value.type === 14 /* AttrMethod */) {
942
951
  return;
943
952
  }
953
+ const valueString = this.#read(attr.value);
954
+ const valueType = getAttributeValueType(valueString);
955
+ if (valueType === void 0) return;
956
+ switch (valueType) {
957
+ case 0 /* True */:
958
+ break;
959
+ case 1 /* Literal */:
960
+ this.#extractor.write('="');
961
+ this.#extractor.copy({
962
+ start: attr.value.start + valueString.search(/[^=\s]/g),
963
+ end: attr.value.end
964
+ });
965
+ this.#extractor.write('"');
966
+ break;
967
+ case 2 /* QuotedString */:
968
+ this.#extractor.write('="');
969
+ this.#extractor.copy({
970
+ start: attr.value.start + valueString.search(/[^=\s]/g) + 1,
971
+ end: attr.value.end - 1
972
+ });
973
+ this.#extractor.write('"');
974
+ break;
975
+ case 3 /* Dynamic */:
976
+ this.#extractor.write(`="dynamic"`);
977
+ break;
978
+ }
944
979
  }
945
980
  };
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();
981
+ function isVoidTag(tagName) {
982
+ switch (tagName) {
983
+ case "area":
984
+ case "base":
985
+ case "br":
986
+ case "col":
987
+ case "embed":
988
+ case "hr":
989
+ case "img":
990
+ case "input":
991
+ case "link":
992
+ case "meta":
993
+ case "param":
994
+ case "source":
995
+ case "track":
996
+ case "wbr":
997
+ return true;
998
+ default:
999
+ return false;
1000
+ }
1001
+ }
1002
+
1003
+ // src/extractors/script/index.ts
1004
+ import { relativeImportPath } from "relative-import-path";
1005
+
1006
+ // src/extractors/script/util/attach-scopes.ts
1007
+ import * as t from "@babel/types";
1008
+ var ATTR_UNAMED = "value";
1009
+ var Scopes = /* @__PURE__ */ new WeakMap();
1010
+ var BoundAttrMemberExpressionStartOffsets = /* @__PURE__ */ new WeakMap();
952
1011
  function crawlProgramScope(parsed, scriptParser) {
953
1012
  var _a;
954
1013
  const { program, read } = parsed;
@@ -1511,6 +1570,57 @@ function tryReaddirSync(dir) {
1511
1570
  }
1512
1571
  }
1513
1572
 
1573
+ // src/extractors/script/util/jsdoc-input-type.ts
1574
+ var MaybeInputTypedefReg = /@typedef\b[\s\S]*\bInput\b/;
1575
+ function getJSDocInputType(comment, ts) {
1576
+ var _a, _b, _c, _d;
1577
+ if (!MaybeInputTypedefReg.test(comment)) return;
1578
+ const sourceFile = ts.createSourceFile(
1579
+ "_.js",
1580
+ comment,
1581
+ ts.ScriptTarget.Latest,
1582
+ false,
1583
+ ts.ScriptKind.JS
1584
+ );
1585
+ const tags = (_b = (_a = sourceFile.endOfFileToken.jsDoc) == null ? void 0 : _a[0]) == null ? void 0 : _b.tags;
1586
+ if (!(tags && hasInputTypeDef(ts, sourceFile, tags))) return;
1587
+ let typeParameters;
1588
+ for (const tag of tags) {
1589
+ if (isTemplateTag(ts, tag)) {
1590
+ let constraint = (_c = tag.constraint) == null ? void 0 : _c.type.getText(sourceFile);
1591
+ for (const param of tag.typeParameters) {
1592
+ const value = {
1593
+ name: "" + param.name.escapedText,
1594
+ constraint,
1595
+ default: (_d = param.default) == null ? void 0 : _d.getText(sourceFile)
1596
+ };
1597
+ constraint = void 0;
1598
+ if (typeParameters) {
1599
+ typeParameters.push(value);
1600
+ } else {
1601
+ typeParameters = [value];
1602
+ }
1603
+ }
1604
+ }
1605
+ }
1606
+ return { typeParameters };
1607
+ }
1608
+ function hasInputTypeDef(ts, sourceFile, tags) {
1609
+ var _a;
1610
+ for (const tag of tags) {
1611
+ if (isTypeDefTag(ts, tag) && ((_a = tag.fullName) == null ? void 0 : _a.getText(sourceFile)) === "Input") {
1612
+ return true;
1613
+ }
1614
+ }
1615
+ return false;
1616
+ }
1617
+ function isTypeDefTag(ts, tag) {
1618
+ return tag.kind === ts.SyntaxKind.JSDocTypedefTag;
1619
+ }
1620
+ function isTemplateTag(ts, tag) {
1621
+ return tag.kind === ts.SyntaxKind.JSDocTemplateTag;
1622
+ }
1623
+
1514
1624
  // src/extractors/script/util/runtime-overrides.ts
1515
1625
  var RuntimeOverloads = /* @__PURE__ */ new Map();
1516
1626
  var commentsReg = /\/\*(?:[^*]+|\*[^/])*\*\//gm;
@@ -1564,56 +1674,64 @@ function getRuntimeOverrides(runtimeTypes, generics, applyGenerics) {
1564
1674
  return result;
1565
1675
  }
1566
1676
 
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
- }
1677
+ // src/extractors/script/util/script-parser.ts
1678
+ import {
1679
+ parse as parseStatement,
1680
+ parseExpression
1681
+ } from "@babel/parser";
1682
+ var plugins = [
1683
+ "exportDefaultFrom",
1684
+ "importAssertions",
1685
+ "typescript"
1686
+ ];
1687
+ var ScriptParser = class {
1688
+ #parsed;
1689
+ constructor(parsed) {
1690
+ this.#parsed = parsed;
1691
+ }
1692
+ statementAt(startIndex, src) {
1693
+ const pos = this.#parsed.positionAt(startIndex);
1694
+ try {
1695
+ return parseStatement(src, {
1696
+ plugins,
1697
+ startIndex,
1698
+ startLine: pos.line + 1,
1699
+ startColumn: pos.character,
1700
+ strictMode: true,
1701
+ errorRecovery: true,
1702
+ sourceType: "module",
1703
+ allowUndeclaredExports: true,
1704
+ allowSuperOutsideMethod: true,
1705
+ allowAwaitOutsideFunction: true,
1706
+ allowReturnOutsideFunction: true,
1707
+ sourceFilename: this.#parsed.filename
1708
+ }).program.body;
1709
+ } catch {
1710
+ return [];
1598
1711
  }
1599
1712
  }
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;
1713
+ expressionAt(startIndex, src) {
1714
+ const pos = this.#parsed.positionAt(startIndex);
1715
+ try {
1716
+ return parseExpression(src, {
1717
+ plugins,
1718
+ startIndex,
1719
+ startLine: pos.line + 1,
1720
+ startColumn: pos.character,
1721
+ strictMode: true,
1722
+ errorRecovery: true,
1723
+ sourceType: "module",
1724
+ allowUndeclaredExports: true,
1725
+ allowSuperOutsideMethod: true,
1726
+ allowAwaitOutsideFunction: true,
1727
+ allowReturnOutsideFunction: true,
1728
+ sourceFilename: this.#parsed.filename
1729
+ });
1730
+ } catch {
1731
+ return;
1607
1732
  }
1608
1733
  }
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
- }
1734
+ };
1617
1735
 
1618
1736
  // src/extractors/script/index.ts
1619
1737
  var SEP_EMPTY = "";
@@ -1667,7 +1785,7 @@ var ScriptExtractor = class {
1667
1785
  this.#ts = opts.ts;
1668
1786
  this.#runtimeTypes = opts.runtimeTypesCode;
1669
1787
  this.#extractor = new Extractor(parsed);
1670
- this.#scriptParser = new ScriptParser(parsed.filename, parsed.code);
1788
+ this.#scriptParser = new ScriptParser(parsed);
1671
1789
  this.#read = parsed.read.bind(parsed);
1672
1790
  this.#mutationOffsets = crawlProgramScope(this.#parsed, this.#scriptParser);
1673
1791
  this.#writeProgram(parsed.program);
@@ -2110,6 +2228,7 @@ constructor(_?: Return) {}
2110
2228
  #writeTag(tag) {
2111
2229
  const tagName = tag.nameText;
2112
2230
  const renderId = this.#getRenderId(tag);
2231
+ let nestedTagType;
2113
2232
  if (renderId) {
2114
2233
  this.#extractor.write(
2115
2234
  `${varShared("assertRendered")}(${varShared(
@@ -2121,6 +2240,10 @@ constructor(_?: Return) {}
2121
2240
  const def = this.#lookup.getTag(tagName);
2122
2241
  if (def) {
2123
2242
  const importPath = resolveTagImport(this.#filename, def);
2243
+ const isMarkoFile = importPath == null ? void 0 : importPath.endsWith(".marko");
2244
+ if (isMarkoFile) {
2245
+ nestedTagType = `import("${importPath}").Input`;
2246
+ }
2124
2247
  const renderer = (importPath == null ? void 0 : importPath.endsWith(".marko")) ? `renderTemplate(import("${importPath}"))` : def.html ? `renderNativeTag("${def.name}")` : "missingTag";
2125
2248
  if (!def.html && REG_TAG_NAME_IDENTIFIER.test(tagName)) {
2126
2249
  this.#extractor.write(
@@ -2133,6 +2256,7 @@ ${varShared(renderer)})`);
2133
2256
  this.#extractor.write(varShared(renderer));
2134
2257
  }
2135
2258
  } else if (REG_TAG_NAME_IDENTIFIER.test(tagName)) {
2259
+ nestedTagType = `Marko.Input<typeof ${this.#read(tag.name)}>`;
2136
2260
  this.#extractor.write(`${varShared("renderDynamicTag")}(
2137
2261
  `).copy(tag.name).write("\n)");
2138
2262
  } else {
@@ -2148,7 +2272,7 @@ ${varShared(renderer)})`);
2148
2272
  } else {
2149
2273
  this.#extractor.write("()()(");
2150
2274
  }
2151
- this.#writeTagInputObject(tag);
2275
+ this.#writeTagInputObject(tag, nestedTagType);
2152
2276
  if (renderId) {
2153
2277
  this.#extractor.write(`)`);
2154
2278
  }
@@ -2332,7 +2456,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2332
2456
  );
2333
2457
  return hasAttrs;
2334
2458
  }
2335
- #writeAttrTags({ staticAttrTags, dynamicAttrTagParents }, inMerge) {
2459
+ #writeAttrTags({ staticAttrTags, dynamicAttrTagParents }, inMerge, nestedTagType) {
2336
2460
  let wasMerge = false;
2337
2461
  if (dynamicAttrTagParents) {
2338
2462
  if (staticAttrTags) {
@@ -2348,7 +2472,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2348
2472
  }
2349
2473
  }
2350
2474
  if (staticAttrTags) {
2351
- this.#writeStaticAttrTags(staticAttrTags, inMerge);
2475
+ this.#writeStaticAttrTags(staticAttrTags, inMerge, nestedTagType);
2352
2476
  if (dynamicAttrTagParents)
2353
2477
  this.#extractor.write(`}${SEP_COMMA_NEW_LINE}`);
2354
2478
  }
@@ -2357,7 +2481,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2357
2481
  if (wasMerge) this.#extractor.write(`)${SEP_COMMA_NEW_LINE}`);
2358
2482
  }
2359
2483
  }
2360
- #writeStaticAttrTags(staticAttrTags, wasMerge) {
2484
+ #writeStaticAttrTags(staticAttrTags, wasMerge, nestedTagType) {
2361
2485
  if (!wasMerge) this.#extractor.write("...{");
2362
2486
  this.#extractor.write(
2363
2487
  `[${varShared("never")}](){
@@ -2387,6 +2511,15 @@ const attrTags = ${varShared(
2387
2511
  this.#extractor.write("]: ");
2388
2512
  if (isRepeated) {
2389
2513
  this.#extractor.write(`${varShared("repeatedAttrTag")}(
2514
+ ...
2515
+ `);
2516
+ if (nestedTagType && this.#scriptLang === "js" /* js */) {
2517
+ this.#extractor.write(
2518
+ `/** @satisfies {${nestedTagType}["${name}"][]} */
2519
+ `
2520
+ );
2521
+ }
2522
+ this.#extractor.write(`([
2390
2523
  `);
2391
2524
  }
2392
2525
  for (const childNode of attrTag) {
@@ -2394,6 +2527,10 @@ const attrTags = ${varShared(
2394
2527
  this.#extractor.write(SEP_COMMA_NEW_LINE);
2395
2528
  }
2396
2529
  if (isRepeated) {
2530
+ this.#extractor.write("])");
2531
+ if (nestedTagType && this.#scriptLang === "ts" /* ts */) {
2532
+ this.#extractor.write(` satisfies ${nestedTagType}["${name}"][]`);
2533
+ }
2397
2534
  this.#extractor.write(`)${SEP_COMMA_NEW_LINE}`);
2398
2535
  }
2399
2536
  }
@@ -2454,7 +2591,7 @@ const attrTags = ${varShared(
2454
2591
  this.#extractor.write(SEP_COMMA_NEW_LINE);
2455
2592
  }
2456
2593
  }
2457
- #writeTagInputObject(tag) {
2594
+ #writeTagInputObject(tag, nestedTagType) {
2458
2595
  if (!tag.params) this.#writeComments(tag);
2459
2596
  let hasInput = false;
2460
2597
  this.#extractor.write("{\n");
@@ -2475,7 +2612,7 @@ const attrTags = ${varShared(
2475
2612
  let hasRenderBody = false;
2476
2613
  if (body) {
2477
2614
  hasInput = true;
2478
- this.#writeAttrTags(body, false);
2615
+ this.#writeAttrTags(body, false, nestedTagType);
2479
2616
  hasRenderBody = body.renderBody !== void 0;
2480
2617
  } else if (tag.close) {
2481
2618
  hasRenderBody = true;
@@ -2874,6 +3011,100 @@ function isEmptyRange(range) {
2874
3011
  return range.start === range.end;
2875
3012
  }
2876
3013
 
3014
+ // src/extractors/style/index.ts
3015
+ function extractStyle(opts) {
3016
+ const { parsed, lookup } = opts;
3017
+ const extractorsByExt = /* @__PURE__ */ new Map();
3018
+ const { read, program, code } = parsed;
3019
+ let placeholderId = 0;
3020
+ for (const node of program.static) {
3021
+ if (node.type === 27 /* Style */) {
3022
+ getExtractor(node.ext || ".css").copy(node.value);
3023
+ }
3024
+ }
3025
+ for (const node of program.body) {
3026
+ visit(node);
3027
+ }
3028
+ const extractedByExt = /* @__PURE__ */ new Map();
3029
+ for (const [ext, extractor] of extractorsByExt) {
3030
+ extractedByExt.set(ext, extractor.end());
3031
+ }
3032
+ return extractedByExt;
3033
+ function visit(node) {
3034
+ var _a, _b;
3035
+ switch (node.type) {
3036
+ case 16 /* AttrTag */:
3037
+ if (node.body) {
3038
+ for (const child of node.body) {
3039
+ visit(child);
3040
+ }
3041
+ }
3042
+ break;
3043
+ case 1 /* Tag */:
3044
+ if (node.body) {
3045
+ if (node.nameText === "style") {
3046
+ const ext = node.shorthandClassNames ? read(node.shorthandClassNames.at(-1)) : ".css";
3047
+ for (const child of node.body) {
3048
+ switch (child.type) {
3049
+ case 17 /* Text */:
3050
+ getExtractor(ext).copy(child);
3051
+ break;
3052
+ case 22 /* Placeholder */:
3053
+ getExtractor(ext).write(`var(--_${placeholderId++})`);
3054
+ break;
3055
+ }
3056
+ }
3057
+ } else {
3058
+ for (const child of node.body) {
3059
+ visit(child);
3060
+ }
3061
+ }
3062
+ }
3063
+ if (node.attrs) {
3064
+ for (const attr of node.attrs) {
3065
+ if (
3066
+ // Check for string literal attribute values.
3067
+ attr.type === 10 /* AttrNamed */ && ((_a = attr.value) == null ? void 0 : _a.type) === 13 /* AttrValue */ && /^['"]$/.test(code[attr.value.value.start])
3068
+ ) {
3069
+ const name = read(attr.name);
3070
+ if (name === "#style" || name === "style" && node.nameText && name === "style" && ((_b = lookup.getTag(node.nameText)) == null ? void 0 : _b.html)) {
3071
+ getExtractor("css").write(":root{").copy({
3072
+ start: attr.value.value.start + 1,
3073
+ end: attr.value.value.end - 1
3074
+ }).write("}");
3075
+ }
3076
+ }
3077
+ }
3078
+ }
3079
+ break;
3080
+ }
3081
+ }
3082
+ function getExtractor(ext) {
3083
+ let extractor = extractorsByExt.get(ext);
3084
+ if (!extractor) {
3085
+ extractorsByExt.set(ext, extractor = new Extractor(parsed));
3086
+ }
3087
+ return extractor;
3088
+ }
3089
+ }
3090
+
3091
+ // src/processors/index.ts
3092
+ var processors_exports = {};
3093
+ __export(processors_exports, {
3094
+ create: () => create,
3095
+ extensions: () => extensions,
3096
+ has: () => has
3097
+ });
3098
+
3099
+ // src/util/get-ext.ts
3100
+ function getExt(fileName) {
3101
+ const extIndex = fileName.lastIndexOf(".");
3102
+ if (extIndex !== -1) return fileName.slice(extIndex);
3103
+ }
3104
+
3105
+ // src/processors/marko.ts
3106
+ import path3 from "path";
3107
+
2877
3108
  // src/util/project.ts
2878
3109
  var project_exports = {};
2879
3110
  __export(project_exports, {
@@ -2887,9 +3118,9 @@ __export(project_exports, {
2887
3118
  setDefaultCompilerMeta: () => setDefaultCompilerMeta,
2888
3119
  setDefaultTypePaths: () => setDefaultTypePaths
2889
3120
  });
2890
- import path2 from "path";
2891
- import { createRequire } from "module";
2892
3121
  import { strip as stripJSONComments } from "@luxass/strip-json-comments";
3122
+ import { createRequire } from "module";
3123
+ import path2 from "path";
2893
3124
  var defaultTypeLibs = {};
2894
3125
  var defaultMeta;
2895
3126
  var ignoreErrors = (_err) => {
@@ -3101,22 +3332,7 @@ function interopDefault(mod) {
3101
3332
  return mod.default || mod;
3102
3333
  }
3103
3334
 
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
3335
  // src/processors/marko.ts
3119
- import path3 from "path";
3120
3336
  var isRemapExtensionReg = /\.ts$/;
3121
3337
  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
3338
  var marko_default = {
@@ -3326,195 +3542,6 @@ function has(fileName) {
3326
3542
  function isDefinitionFile(fileName) {
3327
3543
  return /\.d\.[^.]+$/.test(fileName);
3328
3544
  }
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
3545
  export {
3519
3546
  NodeType,
3520
3547
  processors_exports as Processors,