@herb-tools/formatter 0.8.1 → 0.8.3

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.
@@ -29,9 +29,6 @@ export declare const FORMATTABLE_ATTRIBUTES: Record<string, string[]>;
29
29
  export declare const INLINE_ELEMENTS: Set<string>;
30
30
  export declare const CONTENT_PRESERVING_ELEMENTS: Set<string>;
31
31
  export declare const SPACEABLE_CONTAINERS: Set<string>;
32
- export declare const TIGHT_GROUP_PARENTS: Set<string>;
33
- export declare const TIGHT_GROUP_CHILDREN: Set<string>;
34
- export declare const SPACING_THRESHOLD = 3;
35
32
  /**
36
33
  * Token list attributes that contain space-separated values and benefit from
37
34
  * spacing around ERB content for readability
@@ -0,0 +1,9 @@
1
+ import type { Node } from "@herb-tools/core";
2
+ /**
3
+ * Check if an ERB content node is a herb:formatter ignore comment
4
+ */
5
+ export declare function isHerbFormatterIgnoreComment(node: Node): boolean;
6
+ /**
7
+ * Check if the document contains a herb:formatter ignore directive anywhere.
8
+ */
9
+ export declare function hasFormatterIgnoreDirective(node: Node): boolean;
@@ -24,6 +24,10 @@ export declare class FormatPrinter extends Printer {
24
24
  private currentAttributeName;
25
25
  private elementStack;
26
26
  private elementFormattingAnalysis;
27
+ private nodeIsMultiline;
28
+ private stringLineCount;
29
+ private tagGroupsCache;
30
+ private allSingleLineCache;
27
31
  source: string;
28
32
  constructor(source: string, options: Required<FormatOptions>);
29
33
  print(input: Node | ParseResult | Token): string;
@@ -44,6 +48,10 @@ export declare class FormatPrinter extends Printer {
44
48
  * Useful for testing what output would be generated without affecting the main output
45
49
  */
46
50
  private capture;
51
+ /**
52
+ * Track a boundary node's multiline status by comparing line count before/after rendering.
53
+ */
54
+ private trackBoundary;
47
55
  /**
48
56
  * Capture all nodes that would be visited during a callback
49
57
  * Returns a flat list of all nodes without generating any output
@@ -72,6 +80,19 @@ export declare class FormatPrinter extends Printer {
72
80
  * Extract inline nodes (non-attribute, non-whitespace) from a list of nodes
73
81
  */
74
82
  private extractInlineNodes;
83
+ /**
84
+ * Check if a node will render as multiple lines when formatted.
85
+ */
86
+ private isMultilineElement;
87
+ /**
88
+ * Get a grouping key for a node (tag name for HTML, ERB type for ERB)
89
+ */
90
+ private getGroupingKey;
91
+ /**
92
+ * Detect groups of consecutive same-tag/same-type single-line elements
93
+ * Returns a map of index -> group info for efficient lookup
94
+ */
95
+ private detectTagGroups;
75
96
  /**
76
97
  * Determine if spacing should be added between sibling elements
77
98
  *
@@ -127,6 +148,8 @@ export declare class FormatPrinter extends Printer {
127
148
  visitHTMLElementBody(body: Node[], element: HTMLElementNode): void;
128
149
  /**
129
150
  * Visit element children with intelligent spacing logic
151
+ *
152
+ * Tracks line positions and immediately splices blank lines after rendering each child.
130
153
  */
131
154
  private visitElementChildren;
132
155
  visitHTMLOpenTagNode(node: HTMLOpenTagNode): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herb-tools/formatter",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "description": "Auto-formatter for HTML+ERB templates with intelligent indentation, line wrapping, and ERB-aware pretty-printing.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://herb-tools.dev",
@@ -35,10 +35,10 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@herb-tools/config": "0.8.1",
39
- "@herb-tools/core": "0.8.1",
40
- "@herb-tools/printer": "0.8.1",
41
- "@herb-tools/rewriter": "0.8.1"
38
+ "@herb-tools/config": "0.8.3",
39
+ "@herb-tools/core": "0.8.3",
40
+ "@herb-tools/printer": "0.8.3",
41
+ "@herb-tools/rewriter": "0.8.3"
42
42
  },
43
43
  "files": [
44
44
  "package.json",
@@ -56,17 +56,6 @@ export const SPACEABLE_CONTAINERS = new Set([
56
56
  'figure', 'details', 'summary', 'dialog', 'fieldset'
57
57
  ])
58
58
 
59
- export const TIGHT_GROUP_PARENTS = new Set([
60
- 'ul', 'ol', 'nav', 'select', 'datalist', 'optgroup', 'tr', 'thead',
61
- 'tbody', 'tfoot'
62
- ])
63
-
64
- export const TIGHT_GROUP_CHILDREN = new Set([
65
- 'li', 'option', 'td', 'th', 'dt', 'dd'
66
- ])
67
-
68
- export const SPACING_THRESHOLD = 3
69
-
70
59
  /**
71
60
  * Token list attributes that contain space-separated values and benefit from
72
61
  * spacing around ERB content for readability
@@ -0,0 +1,45 @@
1
+ import { isERBCommentNode } from "@herb-tools/core"
2
+ import { Visitor } from "@herb-tools/core"
3
+
4
+ import type { Node, ERBContentNode } from "@herb-tools/core"
5
+
6
+ const HERB_FORMATTER_PREFIX = "herb:formatter"
7
+ const HERB_FORMATTER_IGNORE_PREFIX = `${HERB_FORMATTER_PREFIX} ignore`
8
+
9
+ /**
10
+ * Check if an ERB content node is a herb:formatter ignore comment
11
+ */
12
+ export function isHerbFormatterIgnoreComment(node: Node): boolean {
13
+ if (!isERBCommentNode(node)) return false
14
+
15
+ const content = node?.content?.value || ""
16
+
17
+ return content.trim() === HERB_FORMATTER_IGNORE_PREFIX
18
+ }
19
+
20
+ /**
21
+ * Check if the document contains a herb:formatter ignore directive anywhere.
22
+ */
23
+ export function hasFormatterIgnoreDirective(node: Node): boolean {
24
+ const detector = new FormatterIgnoreDetector()
25
+ detector.visit(node)
26
+ return detector.hasIgnoreDirective
27
+ }
28
+
29
+ /**
30
+ * Visitor that detects if the AST contains a herb:formatter ignore directive.
31
+ */
32
+ class FormatterIgnoreDetector extends Visitor {
33
+ public hasIgnoreDirective = false
34
+
35
+ visitERBContentNode(node: ERBContentNode): void {
36
+ if (isHerbFormatterIgnoreComment(node)) {
37
+ this.hasIgnoreDirective = true
38
+ return
39
+ }
40
+
41
+ if (this.hasIgnoreDirective) return
42
+
43
+ this.visitChildNodes(node)
44
+ }
45
+ }