@reteps/tree-sitter-htmlmustache 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +1 -1
  2. package/browser/out/browser/index.d.ts +43 -0
  3. package/browser/out/browser/index.d.ts.map +1 -0
  4. package/browser/out/browser/index.mjs +3612 -0
  5. package/browser/out/browser/index.mjs.map +7 -0
  6. package/browser/out/core/collectErrors.d.ts +36 -0
  7. package/browser/out/core/collectErrors.d.ts.map +1 -0
  8. package/browser/out/core/configSchema.d.ts +63 -0
  9. package/browser/out/core/configSchema.d.ts.map +1 -0
  10. package/browser/out/core/customCodeTags.d.ts +34 -0
  11. package/browser/out/core/customCodeTags.d.ts.map +1 -0
  12. package/browser/out/core/diagnostic.d.ts +24 -0
  13. package/browser/out/core/diagnostic.d.ts.map +1 -0
  14. package/browser/out/core/embeddedRegions.d.ts +12 -0
  15. package/browser/out/core/embeddedRegions.d.ts.map +1 -0
  16. package/browser/out/core/formatting/classifier.d.ts +68 -0
  17. package/browser/out/core/formatting/classifier.d.ts.map +1 -0
  18. package/browser/out/core/formatting/embedded.d.ts +19 -0
  19. package/browser/out/core/formatting/embedded.d.ts.map +1 -0
  20. package/browser/out/core/formatting/formatters.d.ts +85 -0
  21. package/browser/out/core/formatting/formatters.d.ts.map +1 -0
  22. package/browser/out/core/formatting/index.d.ts +44 -0
  23. package/browser/out/core/formatting/index.d.ts.map +1 -0
  24. package/browser/out/core/formatting/ir.d.ts +100 -0
  25. package/browser/out/core/formatting/ir.d.ts.map +1 -0
  26. package/browser/out/core/formatting/mergeOptions.d.ts +18 -0
  27. package/browser/out/core/formatting/mergeOptions.d.ts.map +1 -0
  28. package/browser/out/core/formatting/printer.d.ts +18 -0
  29. package/browser/out/core/formatting/printer.d.ts.map +1 -0
  30. package/browser/out/core/formatting/utils.d.ts +39 -0
  31. package/browser/out/core/formatting/utils.d.ts.map +1 -0
  32. package/browser/out/core/grammar.d.ts +3 -0
  33. package/browser/out/core/grammar.d.ts.map +1 -0
  34. package/browser/out/core/htmlBalanceChecker.d.ts +23 -0
  35. package/browser/out/core/htmlBalanceChecker.d.ts.map +1 -0
  36. package/browser/out/core/mustacheChecks.d.ts +24 -0
  37. package/browser/out/core/mustacheChecks.d.ts.map +1 -0
  38. package/browser/out/core/nodeHelpers.d.ts +54 -0
  39. package/browser/out/core/nodeHelpers.d.ts.map +1 -0
  40. package/browser/out/core/ruleMetadata.d.ts +12 -0
  41. package/browser/out/core/ruleMetadata.d.ts.map +1 -0
  42. package/browser/out/core/selectorMatcher.d.ts +74 -0
  43. package/browser/out/core/selectorMatcher.d.ts.map +1 -0
  44. package/cli/out/main.js +133 -115
  45. package/package.json +21 -3
  46. package/src/browser/browser.test.ts +207 -0
  47. package/src/browser/index.ts +128 -0
  48. package/src/browser/tsconfig.json +18 -0
  49. package/src/core/collectErrors.ts +233 -0
  50. package/src/core/configSchema.ts +273 -0
  51. package/src/core/customCodeTags.ts +159 -0
  52. package/src/core/diagnostic.ts +45 -0
  53. package/src/core/embeddedRegions.ts +70 -0
  54. package/src/core/formatting/classifier.ts +549 -0
  55. package/src/core/formatting/embedded.ts +56 -0
  56. package/src/core/formatting/formatters.ts +1272 -0
  57. package/src/core/formatting/index.ts +185 -0
  58. package/src/core/formatting/ir.ts +202 -0
  59. package/src/core/formatting/mergeOptions.ts +34 -0
  60. package/src/core/formatting/printer.ts +242 -0
  61. package/src/core/formatting/utils.ts +193 -0
  62. package/src/core/grammar.ts +2 -0
  63. package/src/core/htmlBalanceChecker.ts +382 -0
  64. package/src/core/mustacheChecks.ts +504 -0
  65. package/src/core/nodeHelpers.ts +126 -0
  66. package/src/core/ruleMetadata.ts +63 -0
  67. package/src/core/selectorMatcher.ts +719 -0
@@ -0,0 +1,23 @@
1
+ export interface BalanceNode {
2
+ type: string;
3
+ text: string;
4
+ startPosition: {
5
+ row: number;
6
+ column: number;
7
+ };
8
+ endPosition: {
9
+ row: number;
10
+ column: number;
11
+ };
12
+ startIndex: number;
13
+ endIndex: number;
14
+ children: BalanceNode[];
15
+ }
16
+ export interface BalanceError {
17
+ node: BalanceNode;
18
+ message: string;
19
+ }
20
+ export { getSectionName } from './nodeHelpers.js';
21
+ export declare function checkUnclosedTags(rootNode: BalanceNode): BalanceError[];
22
+ export declare function checkHtmlBalance(rootNode: BalanceNode): BalanceError[];
23
+ //# sourceMappingURL=htmlBalanceChecker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"htmlBalanceChecker.d.ts","sourceRoot":"","sources":["../../../src/core/htmlBalanceChecker.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,WAAW,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAsBD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AA+QlD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,WAAW,GAAG,YAAY,EAAE,CA2BvE;AAID,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,WAAW,GAAG,YAAY,EAAE,CAoCtE"}
@@ -0,0 +1,24 @@
1
+ import type { BalanceNode, BalanceError } from './htmlBalanceChecker.js';
2
+ export interface TextReplacement {
3
+ startIndex: number;
4
+ endIndex: number;
5
+ newText: string;
6
+ }
7
+ export interface FixableError extends BalanceError {
8
+ severity?: 'error' | 'warning';
9
+ fix?: TextReplacement[];
10
+ fixDescription?: string;
11
+ }
12
+ export declare function checkNestedSameNameSections(rootNode: BalanceNode): FixableError[];
13
+ export declare function checkUnquotedMustacheAttributes(rootNode: BalanceNode): FixableError[];
14
+ export declare function checkConsecutiveSameNameSections(rootNode: BalanceNode, sourceText: string): FixableError[];
15
+ export declare function checkSelfClosingNonVoidTags(rootNode: BalanceNode): FixableError[];
16
+ export declare function checkUnescapedEntities(rootNode: BalanceNode): FixableError[];
17
+ export declare function checkHtmlComments(rootNode: BalanceNode): FixableError[];
18
+ export declare function checkUnrecognizedHtmlTags(rootNode: BalanceNode, customTagNames?: string[]): FixableError[];
19
+ export declare function checkDuplicateAttributes(rootNode: BalanceNode): FixableError[];
20
+ export declare function checkElementContentTooLong(rootNode: BalanceNode, elements: ReadonlyArray<{
21
+ tag: string;
22
+ maxBytes: number;
23
+ }>): FixableError[];
24
+ //# sourceMappingURL=mustacheChecks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mustacheChecks.d.ts","sourceRoot":"","sources":["../../../src/core/mustacheChecks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAIzE,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAa,SAAQ,YAAY;IAChD,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B,GAAG,CAAC,EAAE,eAAe,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAGD,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,WAAW,GAAG,YAAY,EAAE,CAgCjF;AAGD,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,WAAW,GAAG,YAAY,EAAE,CA4BrF;AAGD,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,YAAY,EAAE,CA6D1G;AAUD,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,WAAW,GAAG,YAAY,EAAE,CA6BjF;AAiED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,WAAW,GAAG,YAAY,EAAE,CAsD5E;AAGD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,WAAW,GAAG,YAAY,EAAE,CAiCvE;AAgCD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,YAAY,EAAE,CAsC1G;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,WAAW,GAAG,YAAY,EAAE,CAkD9E;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,WAAW,EACrB,QAAQ,EAAE,aAAa,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,GACzD,YAAY,EAAE,CAsChB"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Shared node helper functions and type constants for working with
3
+ * tree-sitter syntax nodes across the LSP and CLI.
4
+ *
5
+ * Uses a minimal interface compatible with both web-tree-sitter's SyntaxNode
6
+ * and the BalanceNode interface from htmlBalanceChecker.
7
+ */
8
+ /** Minimal node interface compatible with both SyntaxNode and BalanceNode. */
9
+ export interface MinimalNode {
10
+ type: string;
11
+ text: string;
12
+ children: MinimalNode[];
13
+ }
14
+ export declare const MUSTACHE_SECTION_TYPES: Set<string>;
15
+ export declare const INTERPOLATION_TYPES: Set<string>;
16
+ export declare const RAW_CONTENT_ELEMENT_TYPES: Set<string>;
17
+ export declare const HTML_ELEMENT_TYPES: Set<string>;
18
+ export declare function isMustacheSection(node: MinimalNode): boolean;
19
+ export declare function isRawContentElement(node: MinimalNode): boolean;
20
+ export declare function isHtmlElementType(node: MinimalNode): boolean;
21
+ /**
22
+ * Get the tag name from an HTML element node.
23
+ * Works with any node that has children with type 'html_start_tag' or
24
+ * 'html_self_closing_tag' containing an 'html_tag_name' child.
25
+ */
26
+ export declare function getTagName(node: MinimalNode): string | null;
27
+ /**
28
+ * Get the section name from a mustache section node.
29
+ * Looks for mustache_tag_name inside mustache_section_begin or
30
+ * mustache_inverted_section_begin.
31
+ */
32
+ export declare function getSectionName(node: MinimalNode): string | null;
33
+ /**
34
+ * Get the tag name from an erroneous end tag node.
35
+ */
36
+ export declare function getErroneousEndTagName(node: MinimalNode): string | null;
37
+ /**
38
+ * Get the dotted path text from a mustache_interpolation or mustache_triple
39
+ * node — e.g. `{{data.foo}}` → `"data.foo"`. Returns `"."` for the
40
+ * context-marker interpolation `{{.}}`. Returns null if the node has no
41
+ * recognisable expression child.
42
+ */
43
+ export declare function getInterpolationPath(node: MinimalNode): string | null;
44
+ /**
45
+ * Get the content text of a mustache_comment node (`{{!foo}}` → `"foo"`).
46
+ * Returns null if no content child is present.
47
+ */
48
+ export declare function getCommentContent(node: MinimalNode): string | null;
49
+ /**
50
+ * Get the partial name of a mustache_partial node (`{{>header}}` → `"header"`).
51
+ * Returns null if no content child is present.
52
+ */
53
+ export declare function getPartialName(node: MinimalNode): string | null;
54
+ //# sourceMappingURL=nodeHelpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeHelpers.d.ts","sourceRoot":"","sources":["../../../src/core/nodeHelpers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,8EAA8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB;AAID,eAAO,MAAM,sBAAsB,aAGjC,CAAC;AAEH,eAAO,MAAM,mBAAmB,aAG9B,CAAC;AAEH,eAAO,MAAM,yBAAyB,aAIpC,CAAC;AAEH,eAAO,MAAM,kBAAkB,aAK7B,CAAC;AAIH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAE5D;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAE9D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAE5D;AAID;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAQ3D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAO/D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAGvE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAQrE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAGlE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAG/D"}
@@ -0,0 +1,12 @@
1
+ import type { RuleSeverity } from './configSchema.js';
2
+ export interface RuleDefinition {
3
+ name: string;
4
+ defaultSeverity: RuleSeverity;
5
+ description: string;
6
+ }
7
+ export declare const RULES: RuleDefinition[];
8
+ /** Set of all known rule names (for config validation). */
9
+ export declare const KNOWN_RULE_NAMES: Set<string>;
10
+ /** Default severity for each rule (for runtime resolution). */
11
+ export declare const RULE_DEFAULTS: Record<string, RuleSeverity>;
12
+ //# sourceMappingURL=ruleMetadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ruleMetadata.d.ts","sourceRoot":"","sources":["../../../src/core/ruleMetadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,YAAY,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,KAAK,EAAE,cAAc,EA8CjC,CAAC;AAEF,2DAA2D;AAC3D,eAAO,MAAM,gBAAgB,aAA0C,CAAC;AAExE,+DAA+D;AAC/D,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAEtD,CAAC"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * CSS-like selector parser and tree matcher for custom lint rules.
3
+ *
4
+ * Mustache constructs are written literally in selectors — `{{foo}}`,
5
+ * `{{{foo}}}`, `{{#foo}}`, `{{^foo}}`, `{{!foo}}`, `{{>foo}}`. A preprocessor
6
+ * substitutes each form into an internal `:m-*` pseudo-class marker, then the
7
+ * rewritten string is handed to parsel-js. This keeps users in Mustache
8
+ * vocabulary without reinventing a selector parser.
9
+ *
10
+ * Supported user-facing syntax:
11
+ * - Tag names (`div`), universal (`*`), classes (`.foo`), ids (`#foo`)
12
+ * - Attributes: `[attr]`, `[attr=v]`, `[attr^=v]`, `[attr*=v]`, `[attr$=v]`, `[attr~=v]`
13
+ * - Descendant (space) and child (`>`) combinators
14
+ * - Mustache variables: `{{path}}` and `{{{path}}}` (raw)
15
+ * - Mustache sections: `{{#name}}` and `{{^name}}` (inverted)
16
+ * - Mustache comments: `{{!content}}`
17
+ * - Mustache partials: `{{>name}}`
18
+ * - Glob wildcard `*` inside the argument: `{{options.*}}`, `{{*.deprecated}}`, `{{*}}`
19
+ * - `:has(selector)` — element has a matching descendant
20
+ * - `:not(...)` over any attribute/class/id/:has form
21
+ * - `:root` — the tree-sitter fragment root (the whole document). Unlike
22
+ * browser CSS where `:root` matches `<html>`, this matches the parse-tree
23
+ * root so it works on partials/fragments too. Useful as a document-scoped
24
+ * anchor, e.g. `:root:has(pl-question-panel):not(:has(pl-answer-panel))`
25
+ * matches the root iff a `pl-question-panel` is present anywhere but no
26
+ * `pl-answer-panel` is. Cannot combine with tag/class/id/attribute in the
27
+ * same compound (only with `:has` / `:not(:has(...))`). Inside `:has(...)`,
28
+ * `:root` refers to the element being checked, not the document.
29
+ * - Comma-separated alternatives
30
+ *
31
+ * Unsupported (parseSelector returns null, rule is skipped):
32
+ * - Sibling combinators (`+`, `~`)
33
+ * - `[attr|=v]`, case-insensitive `i` flag
34
+ * - Mixed HTML + Mustache kinds in one compound (e.g. `img{{foo}}`)
35
+ * - `{{/end}}` (end tags aren't standalone nodes)
36
+ * - `{{=<% %>=}}` (delimiter changes aren't grammar-tracked)
37
+ * - Mustache literals inside `:not(...)` (only attribute/class/id/:has)
38
+ */
39
+ import type { BalanceNode } from './htmlBalanceChecker.js';
40
+ export type AttributeOperator = '=' | '^=' | '*=' | '$=' | '~=';
41
+ export type SegmentKind = 'html' | 'section' | 'inverted' | 'variable' | 'raw' | 'comment' | 'partial';
42
+ export interface AttributeConstraint {
43
+ name: string;
44
+ op: AttributeOperator;
45
+ value?: string;
46
+ negated: boolean;
47
+ }
48
+ export interface DescendantCheck {
49
+ selector: ParsedSelector;
50
+ negated: boolean;
51
+ }
52
+ export interface Segment {
53
+ kind: SegmentKind;
54
+ rootOnly: boolean;
55
+ name: string | null;
56
+ pathRegex?: RegExp;
57
+ attributes: AttributeConstraint[];
58
+ descendantChecks: DescendantCheck[];
59
+ combinator: 'descendant' | 'child';
60
+ }
61
+ /** A parsed selector is a list of alternatives (from comma-separated parts). */
62
+ export type ParsedSelector = Segment[][];
63
+ /**
64
+ * Rewrite Mustache-literal tokens (`{{...}}` forms) in the selector string into
65
+ * internal `:m-*` pseudo-class markers so parsel-js can handle them. Returns
66
+ * null if the string contains an unsupported or malformed Mustache token.
67
+ *
68
+ * Skips content inside `"..."` and `'...'` so that literal `{{...}}` embedded
69
+ * in CSS attribute-value strings is preserved unchanged.
70
+ */
71
+ export declare function preprocessMustacheLiterals(raw: string): string | null;
72
+ export declare function parseSelector(raw: string): ParsedSelector | null;
73
+ export declare function matchSelector(rootNode: BalanceNode, selector: ParsedSelector): BalanceNode[];
74
+ //# sourceMappingURL=selectorMatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectorMatcher.d.ts","sourceRoot":"","sources":["../../../src/core/selectorMatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAY3D,MAAM,MAAM,iBAAiB,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhE,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,SAAS,GACT,UAAU,GACV,UAAU,GACV,KAAK,GACL,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,iBAAiB,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,UAAU,EAAE,YAAY,GAAG,OAAO,CAAC;CACpC;AAED,gFAAgF;AAChF,MAAM,MAAM,cAAc,GAAG,OAAO,EAAE,EAAE,CAAC;AAQzC;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuFrE;AAID,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAuBhE;AA0eD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,GAAG,WAAW,EAAE,CAY5F"}
package/cli/out/main.js CHANGED
@@ -525,11 +525,16 @@ var import_node_path = __toESM(require("node:path"));
525
525
  // cli/src/wasm.ts
526
526
  var path = __toESM(require("node:path"));
527
527
  var import_web_tree_sitter = require("web-tree-sitter");
528
+
529
+ // src/core/grammar.ts
530
+ var GRAMMAR_WASM_FILENAME = "tree-sitter-htmlmustache.wasm";
531
+
532
+ // cli/src/wasm.ts
528
533
  var parser;
529
534
  async function initializeParser() {
530
535
  await import_web_tree_sitter.Parser.init();
531
536
  parser = new import_web_tree_sitter.Parser();
532
- const wasmPath = path.resolve(__dirname, "..", "..", "tree-sitter-htmlmustache.wasm");
537
+ const wasmPath = path.resolve(__dirname, "..", "..", GRAMMAR_WASM_FILENAME);
533
538
  const language = await import_web_tree_sitter.Language.load(wasmPath);
534
539
  parser.setLanguage(language);
535
540
  }
@@ -545,7 +550,7 @@ function parseDocument(source) {
545
550
  var fs = __toESM(require("fs"), 1);
546
551
  var path2 = __toESM(require("path"), 1);
547
552
 
548
- // lsp/server/src/ruleMetadata.ts
553
+ // src/core/ruleMetadata.ts
549
554
  var RULES = [
550
555
  {
551
556
  name: "nestedDuplicateSections",
@@ -598,7 +603,7 @@ var RULE_DEFAULTS = Object.fromEntries(
598
603
  RULES.map((r2) => [r2.name, r2.defaultSeverity])
599
604
  );
600
605
 
601
- // lsp/server/src/configFile.ts
606
+ // src/core/configSchema.ts
602
607
  var VALID_CSS_DISPLAY_VALUES = /* @__PURE__ */ new Set([
603
608
  "block",
604
609
  "inline",
@@ -648,7 +653,6 @@ function parseRuleEntry(key, value) {
648
653
  if (!options) return { severity };
649
654
  return { severity, ...options };
650
655
  }
651
- var CONFIG_FILENAME = ".htmlmustache.jsonc";
652
656
  function parseJsonc(text2) {
653
657
  let result = "";
654
658
  let i2 = 0;
@@ -687,21 +691,6 @@ function parseJsonc(text2) {
687
691
  result = result.replace(/,\s*([}\]])/g, "$1");
688
692
  return JSON.parse(result);
689
693
  }
690
- function findConfigFile(startDir) {
691
- let dir = path2.resolve(startDir);
692
- const root = path2.parse(dir).root;
693
- while (true) {
694
- const candidate = path2.join(dir, CONFIG_FILENAME);
695
- try {
696
- fs.accessSync(candidate, fs.constants.R_OK);
697
- return candidate;
698
- } catch {
699
- }
700
- const parent = path2.dirname(dir);
701
- if (parent === dir || dir === root) return null;
702
- dir = parent;
703
- }
704
- }
705
694
  var VALID_INDENT_MODES = /* @__PURE__ */ new Set(["never", "always", "attribute"]);
706
695
  function parseCustomTagArray(arr) {
707
696
  if (!Array.isArray(arr)) return [];
@@ -801,6 +790,24 @@ function validateConfig(raw) {
801
790
  }
802
791
  return config;
803
792
  }
793
+
794
+ // lsp/server/src/configFile.ts
795
+ var CONFIG_FILENAME = ".htmlmustache.jsonc";
796
+ function findConfigFile(startDir) {
797
+ let dir = path2.resolve(startDir);
798
+ const root = path2.parse(dir).root;
799
+ while (true) {
800
+ const candidate = path2.join(dir, CONFIG_FILENAME);
801
+ try {
802
+ fs.accessSync(candidate, fs.constants.R_OK);
803
+ return candidate;
804
+ } catch {
805
+ }
806
+ const parent = path2.dirname(dir);
807
+ if (parent === dir || dir === root) return null;
808
+ dir = parent;
809
+ }
810
+ }
804
811
  function loadConfigFileForPath(filePath) {
805
812
  const dir = path2.dirname(path2.resolve(filePath));
806
813
  const configPath = findConfigFile(dir);
@@ -814,7 +821,7 @@ function loadConfigFileForPath(filePath) {
814
821
  }
815
822
  }
816
823
 
817
- // lsp/server/src/nodeHelpers.ts
824
+ // src/core/nodeHelpers.ts
818
825
  var MUSTACHE_SECTION_TYPES = /* @__PURE__ */ new Set([
819
826
  "mustache_section",
820
827
  "mustache_inverted_section"
@@ -878,7 +885,7 @@ function getPartialName(node) {
878
885
  return child ? child.text.trim() : null;
879
886
  }
880
887
 
881
- // lsp/server/src/htmlBalanceChecker.ts
888
+ // src/core/htmlBalanceChecker.ts
882
889
  function getTagNameLower(element) {
883
890
  return getTagName(element)?.toLowerCase() ?? null;
884
891
  }
@@ -1175,7 +1182,7 @@ function checkHtmlBalance(rootNode) {
1175
1182
  return allErrors;
1176
1183
  }
1177
1184
 
1178
- // lsp/server/src/mustacheChecks.ts
1185
+ // src/core/mustacheChecks.ts
1179
1186
  function checkNestedSameNameSections(rootNode) {
1180
1187
  const errors = [];
1181
1188
  function visit(node, ancestors) {
@@ -1834,7 +1841,7 @@ function m(e2, { recursive: t2 = true, list: s2 = true } = {}) {
1834
1841
  return c2;
1835
1842
  }
1836
1843
 
1837
- // lsp/server/src/selectorMatcher.ts
1844
+ // src/core/selectorMatcher.ts
1838
1845
  var MUSTACHE_KIND_PSEUDO = /* @__PURE__ */ new Set([
1839
1846
  "m-section",
1840
1847
  "m-inverted",
@@ -2376,7 +2383,15 @@ function matchSelector(rootNode, selector) {
2376
2383
  return allResults;
2377
2384
  }
2378
2385
 
2379
- // lsp/server/src/collectErrors.ts
2386
+ // src/core/collectErrors.ts
2387
+ var selectorCache = /* @__PURE__ */ new Map();
2388
+ function parseSelectorCached(raw) {
2389
+ const hit = selectorCache.get(raw);
2390
+ if (hit !== void 0) return hit;
2391
+ const parsed = parseSelector(raw);
2392
+ selectorCache.set(raw, parsed);
2393
+ return parsed;
2394
+ }
2380
2395
  var ERROR_NODE_TYPES = /* @__PURE__ */ new Set([
2381
2396
  "ERROR",
2382
2397
  "mustache_erroneous_section_end",
@@ -2507,7 +2522,7 @@ function collectErrors(tree, rules, customTagNames, customRules) {
2507
2522
  if (disabledRules.has(rule.id)) continue;
2508
2523
  const severity = rule.severity ?? "error";
2509
2524
  if (severity === "off") continue;
2510
- const parsed = parseSelector(rule.selector);
2525
+ const parsed = parseSelectorCached(rule.selector);
2511
2526
  if (!parsed) continue;
2512
2527
  const matches = matchSelector(tree.rootNode, parsed);
2513
2528
  for (const node of matches) {
@@ -2520,20 +2535,32 @@ function collectErrors(tree, rules, customTagNames, customRules) {
2520
2535
  );
2521
2536
  }
2522
2537
 
2538
+ // src/core/diagnostic.ts
2539
+ function toFix(r2) {
2540
+ return { range: [r2.startIndex, r2.endIndex], newText: r2.newText };
2541
+ }
2542
+ function toDiagnostic(err) {
2543
+ const { node } = err;
2544
+ return {
2545
+ line: node.startPosition.row + 1,
2546
+ column: node.startPosition.column + 1,
2547
+ endLine: node.endPosition.row + 1,
2548
+ endColumn: node.endPosition.column + 1,
2549
+ message: err.message,
2550
+ severity: err.severity ?? "error",
2551
+ ruleName: err.ruleName,
2552
+ fix: err.fix && err.fix.length > 0 ? err.fix.map(toFix) : void 0,
2553
+ fixDescription: err.fixDescription
2554
+ };
2555
+ }
2556
+
2523
2557
  // cli/src/check.ts
2524
2558
  function collectErrors2(tree, file, rules, customTagNames, customRules) {
2525
2559
  const errors = collectErrors(tree, rules, customTagNames, customRules);
2526
2560
  return errors.map((error) => ({
2527
2561
  file,
2528
- line: error.node.startPosition.row + 1,
2529
- column: error.node.startPosition.column + 1,
2530
- endLine: error.node.endPosition.row + 1,
2531
- endColumn: error.node.endPosition.column + 1,
2532
- message: error.message,
2533
2562
  nodeText: error.node.text,
2534
- severity: error.severity,
2535
- fix: error.fix,
2536
- fixDescription: error.fixDescription
2563
+ ...toDiagnostic(error)
2537
2564
  }));
2538
2565
  }
2539
2566
  function formatError(error, source) {
@@ -2645,8 +2672,9 @@ function resolveFiles(cliPatterns) {
2645
2672
  function applyFixes(source, errors) {
2646
2673
  const replacements = [];
2647
2674
  for (const error of errors) {
2648
- if (error.fix) {
2649
- replacements.push(...error.fix);
2675
+ if (!error.fix) continue;
2676
+ for (const edit of error.fix) {
2677
+ replacements.push({ startIndex: edit.range[0], endIndex: edit.range[1], newText: edit.newText });
2650
2678
  }
2651
2679
  }
2652
2680
  if (replacements.length === 0) return source;
@@ -2974,7 +3002,7 @@ function getWellformedEdit(textEdit) {
2974
3002
  return textEdit;
2975
3003
  }
2976
3004
 
2977
- // lsp/server/src/formatting/printer.ts
3005
+ // src/core/formatting/printer.ts
2978
3006
  function print(doc, options) {
2979
3007
  const output = [];
2980
3008
  const state = { indentLevel: 0, mode: "break", groupModes: /* @__PURE__ */ new Map() };
@@ -3124,7 +3152,7 @@ function makeIndent(level, options) {
3124
3152
  return options.indentUnit.repeat(level);
3125
3153
  }
3126
3154
 
3127
- // lsp/server/src/formatting/ir.ts
3155
+ // src/core/formatting/ir.ts
3128
3156
  var hardline = { type: "hardline" };
3129
3157
  var softline = { type: "softline" };
3130
3158
  var line = { type: "line" };
@@ -3176,7 +3204,7 @@ function isLine(doc) {
3176
3204
  return typeof doc === "object" && doc.type === "line";
3177
3205
  }
3178
3206
 
3179
- // lsp/server/src/formatting/utils.ts
3207
+ // src/core/formatting/utils.ts
3180
3208
  function normalizeText(text2) {
3181
3209
  return text2.split("\n").map((line2) => line2.replace(/[ \t]+/g, " ").trim()).filter((line2, i2, arr) => line2 || i2 > 0 && i2 < arr.length - 1).join("\n");
3182
3210
  }
@@ -3252,7 +3280,7 @@ function getIgnoreDirective(node) {
3252
3280
  return null;
3253
3281
  }
3254
3282
 
3255
- // lsp/server/src/customCodeTags.ts
3283
+ // src/core/customCodeTags.ts
3256
3284
  function isCodeTag(config) {
3257
3285
  return !!(config.languageAttribute || config.languageDefault);
3258
3286
  }
@@ -3281,7 +3309,7 @@ function getAttributeValue(node, attrName) {
3281
3309
  return null;
3282
3310
  }
3283
3311
 
3284
- // lsp/server/src/formatting/classifier.ts
3312
+ // src/core/formatting/classifier.ts
3285
3313
  var EMPTY_MAP = /* @__PURE__ */ new Map();
3286
3314
  var CSS_DISPLAY_MAP = {
3287
3315
  // Block elements
@@ -3583,7 +3611,7 @@ function shouldTreatAsBlock(node, index, nodes, customTags = EMPTY_MAP) {
3583
3611
  return isHtmlEl && !shouldHtmlElementStayInline(node, index, nodes, customTags) || isMustacheSec && !isInTextFlow(node, index, nodes) || isBlockLevel(node, customTags) && !isInTextFlow(node, index, nodes);
3584
3612
  }
3585
3613
 
3586
- // lsp/server/src/formatting/formatters.ts
3614
+ // src/core/formatting/formatters.ts
3587
3615
  function isAttributeTruthy(value) {
3588
3616
  if (value === null || value === "" || value === "false" || value === "0") {
3589
3617
  return false;
@@ -4475,46 +4503,12 @@ function trimDoc(doc) {
4475
4503
  return doc;
4476
4504
  }
4477
4505
 
4478
- // lsp/server/src/formatting/editorconfig.ts
4479
- var import_editorconfig = require("editorconfig");
4480
- var import_url = require("url");
4481
- function getEditorConfigOptions(uri) {
4482
- try {
4483
- if (!uri.startsWith("file://")) {
4484
- return {};
4485
- }
4486
- const filePath = (0, import_url.fileURLToPath)(uri);
4487
- const config = (0, import_editorconfig.parseSync)(filePath);
4488
- const result = {};
4489
- if (config.indent_style === "space") {
4490
- result.insertSpaces = true;
4491
- } else if (config.indent_style === "tab") {
4492
- result.insertSpaces = false;
4493
- }
4494
- if (typeof config.indent_size === "number") {
4495
- result.tabSize = config.indent_size;
4496
- } else if (config.indent_size === "tab" && typeof config.tab_width === "number") {
4497
- result.tabSize = config.tab_width;
4498
- }
4499
- return result;
4500
- } catch {
4501
- return {};
4502
- }
4503
- }
4504
- function mergeOptions(lspOptions, uri, configFile) {
4505
- let tabSize = lspOptions.tabSize;
4506
- let insertSpaces = lspOptions.insertSpaces;
4507
- if (configFile?.indentSize !== void 0) tabSize = configFile.indentSize;
4508
- const ec = getEditorConfigOptions(uri);
4509
- if (ec.tabSize !== void 0) tabSize = ec.tabSize;
4510
- if (ec.insertSpaces !== void 0) insertSpaces = ec.insertSpaces;
4511
- return { tabSize, insertSpaces };
4512
- }
4506
+ // src/core/formatting/mergeOptions.ts
4513
4507
  function createIndentUnit(options) {
4514
4508
  return options.insertSpaces ? " ".repeat(options.tabSize) : " ";
4515
4509
  }
4516
4510
 
4517
- // lsp/server/src/formatting/index.ts
4511
+ // src/core/formatting/index.ts
4518
4512
  function buildCustomTagMap(customTags) {
4519
4513
  if (!customTags || customTags.length === 0) return void 0;
4520
4514
  const map = /* @__PURE__ */ new Map();
@@ -4524,12 +4518,9 @@ function buildCustomTagMap(customTags) {
4524
4518
  return map;
4525
4519
  }
4526
4520
  function formatDocument2(tree, document, options, params = {}) {
4527
- const { printWidth = 80, embeddedFormatted, mustacheSpaces, noBreakDelimiters, configFile } = params;
4528
- const mergedOptions = mergeOptions(options, document.uri, configFile);
4529
- const indentUnit = createIndentUnit(mergedOptions);
4530
- if (tree.rootNode.hasError) {
4531
- return [];
4532
- }
4521
+ const { printWidth = 80, embeddedFormatted, mustacheSpaces, noBreakDelimiters } = params;
4522
+ const indentUnit = createIndentUnit(options);
4523
+ if (tree.rootNode.hasError) return [];
4533
4524
  const customTagMap = buildCustomTagMap(params.customTags);
4534
4525
  const context = {
4535
4526
  document,
@@ -4547,7 +4538,7 @@ function formatDocument2(tree, document, options, params = {}) {
4547
4538
  return [{ range: fullRange, newText: formatted }];
4548
4539
  }
4549
4540
 
4550
- // lsp/server/src/embeddedRegions.ts
4541
+ // src/core/embeddedRegions.ts
4551
4542
  function getEmbeddedLanguageId(node) {
4552
4543
  if (node.type === "html_style_element") {
4553
4544
  return "css";
@@ -4600,6 +4591,62 @@ function collectEmbeddedRegions(rootNode) {
4600
4591
  return regions;
4601
4592
  }
4602
4593
 
4594
+ // src/core/formatting/embedded.ts
4595
+ var LANGUAGE_TO_PRETTIER_PARSER = {
4596
+ javascript: "babel",
4597
+ typescript: "typescript",
4598
+ css: "css"
4599
+ };
4600
+ async function formatEmbeddedRegions(rootNode, options, prettier) {
4601
+ const result = /* @__PURE__ */ new Map();
4602
+ if (!prettier) return result;
4603
+ const regions = collectEmbeddedRegions(rootNode);
4604
+ if (regions.length === 0) return result;
4605
+ await Promise.all(
4606
+ regions.map(async (region) => {
4607
+ const parser2 = LANGUAGE_TO_PRETTIER_PARSER[region.languageId];
4608
+ if (!parser2) return;
4609
+ try {
4610
+ const formatted = await prettier.format(region.content, {
4611
+ parser: parser2,
4612
+ tabWidth: options.tabSize,
4613
+ useTabs: !options.insertSpaces
4614
+ });
4615
+ result.set(region.startIndex, formatted);
4616
+ } catch {
4617
+ }
4618
+ })
4619
+ );
4620
+ return result;
4621
+ }
4622
+
4623
+ // lsp/server/src/formatting/editorconfig.ts
4624
+ var import_editorconfig = require("editorconfig");
4625
+ var import_url = require("url");
4626
+ function getEditorConfigOptions(uri) {
4627
+ try {
4628
+ if (!uri.startsWith("file://")) {
4629
+ return {};
4630
+ }
4631
+ const filePath = (0, import_url.fileURLToPath)(uri);
4632
+ const config = (0, import_editorconfig.parseSync)(filePath);
4633
+ const result = {};
4634
+ if (config.indent_style === "space") {
4635
+ result.insertSpaces = true;
4636
+ } else if (config.indent_style === "tab") {
4637
+ result.insertSpaces = false;
4638
+ }
4639
+ if (typeof config.indent_size === "number") {
4640
+ result.tabSize = config.indent_size;
4641
+ } else if (config.indent_size === "tab" && typeof config.tab_width === "number") {
4642
+ result.tabSize = config.tab_width;
4643
+ }
4644
+ return result;
4645
+ } catch {
4646
+ return {};
4647
+ }
4648
+ }
4649
+
4603
4650
  // cli/src/format.ts
4604
4651
  var USAGE2 = `Usage: htmlmustache format [options] [patterns...]
4605
4652
 
@@ -4703,15 +4750,9 @@ function resolveSettings(flags, filePath) {
4703
4750
  printWidth,
4704
4751
  mustacheSpaces,
4705
4752
  noBreakDelimiters,
4706
- customTags,
4707
- configFile
4753
+ customTags
4708
4754
  };
4709
4755
  }
4710
- var LANGUAGE_TO_PRETTIER_PARSER = {
4711
- javascript: "babel",
4712
- typescript: "typescript",
4713
- css: "css"
4714
- };
4715
4756
  var prettierModule;
4716
4757
  async function getPrettier() {
4717
4758
  if (prettierModule !== void 0) return prettierModule;
@@ -4723,32 +4764,9 @@ async function getPrettier() {
4723
4764
  return null;
4724
4765
  }
4725
4766
  }
4726
- async function formatEmbeddedRegions(tree, options) {
4727
- const result = /* @__PURE__ */ new Map();
4728
- const prettier = await getPrettier();
4729
- if (!prettier) return result;
4730
- const regions = collectEmbeddedRegions(tree.rootNode);
4731
- if (regions.length === 0) return result;
4732
- await Promise.all(
4733
- regions.map(async (region) => {
4734
- const parser2 = LANGUAGE_TO_PRETTIER_PARSER[region.languageId];
4735
- if (!parser2) return;
4736
- try {
4737
- const formatted = await prettier.format(region.content, {
4738
- parser: parser2,
4739
- tabWidth: options.tabSize,
4740
- useTabs: !options.insertSpaces
4741
- });
4742
- result.set(region.startIndex, formatted);
4743
- } catch {
4744
- }
4745
- })
4746
- );
4747
- return result;
4748
- }
4749
4767
  async function formatSource(source, options, params = {}) {
4750
4768
  const tree = parseDocument(source);
4751
- const embeddedFormatted = await formatEmbeddedRegions(tree, options);
4769
+ const embeddedFormatted = await formatEmbeddedRegions(tree.rootNode, options, await getPrettier());
4752
4770
  const document = TextDocument.create("file:///stdin", "htmlmustache", 1, source);
4753
4771
  const edits = formatDocument2(tree, document, options, {
4754
4772
  ...params,