@herb-tools/formatter 0.4.0 → 0.4.2
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/README.md +8 -5
- package/bin/herb-format +3 -0
- package/dist/herb-format.js +17444 -0
- package/dist/herb-format.js.map +1 -0
- package/dist/index.cjs +327 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +327 -37
- package/dist/index.esm.js.map +1 -1
- package/dist/types/printer.d.ts +15 -0
- package/package.json +4 -4
- package/src/cli.ts +175 -23
- package/src/printer.ts +393 -33
- package/bin/herb-formatter +0 -3
- package/dist/herb-formatter.js +0 -9070
- package/dist/herb-formatter.js.map +0 -1
- /package/dist/types/{herb-formatter.d.ts → herb-format.d.ts} +0 -0
- /package/src/{herb-formatter.ts → herb-format.ts} +0 -0
package/dist/index.esm.js
CHANGED
|
@@ -129,7 +129,7 @@ class Token {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
|
132
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release/templates/javascript/packages/core/src/errors.ts.erb
|
|
132
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-6/templates/javascript/packages/core/src/errors.ts.erb
|
|
133
133
|
class HerbError {
|
|
134
134
|
type;
|
|
135
135
|
message;
|
|
@@ -579,7 +579,7 @@ function convertToUTF8(string) {
|
|
|
579
579
|
}
|
|
580
580
|
|
|
581
581
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
|
582
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release/templates/javascript/packages/core/src/nodes.ts.erb
|
|
582
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-6/templates/javascript/packages/core/src/nodes.ts.erb
|
|
583
583
|
class Node {
|
|
584
584
|
type;
|
|
585
585
|
location;
|
|
@@ -2574,7 +2574,7 @@ function fromSerializedNode(node) {
|
|
|
2574
2574
|
}
|
|
2575
2575
|
|
|
2576
2576
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
|
2577
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release/templates/javascript/packages/core/src/visitor.ts.erb
|
|
2577
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-6/templates/javascript/packages/core/src/visitor.ts.erb
|
|
2578
2578
|
class Visitor {
|
|
2579
2579
|
visit(node) {
|
|
2580
2580
|
if (!node)
|
|
@@ -2689,6 +2689,8 @@ class Printer extends Visitor {
|
|
|
2689
2689
|
source;
|
|
2690
2690
|
lines = [];
|
|
2691
2691
|
indentLevel = 0;
|
|
2692
|
+
inlineMode = false;
|
|
2693
|
+
isInComplexNesting = false;
|
|
2692
2694
|
constructor(source, options) {
|
|
2693
2695
|
super();
|
|
2694
2696
|
this.source = source;
|
|
@@ -2702,6 +2704,7 @@ class Printer extends Visitor {
|
|
|
2702
2704
|
const node = object;
|
|
2703
2705
|
this.lines = [];
|
|
2704
2706
|
this.indentLevel = indentLevel;
|
|
2707
|
+
this.isInComplexNesting = false; // Reset for each top-level element
|
|
2705
2708
|
if (typeof node.accept === 'function') {
|
|
2706
2709
|
node.accept(this);
|
|
2707
2710
|
}
|
|
@@ -2751,6 +2754,8 @@ class Printer extends Visitor {
|
|
|
2751
2754
|
const tagName = open.tag_name?.value ?? "";
|
|
2752
2755
|
const indent = this.indent();
|
|
2753
2756
|
const attributes = open.children.filter((child) => child instanceof HTMLAttributeNode || child.type === 'AST_HTML_ATTRIBUTE_NODE');
|
|
2757
|
+
const inlineNodes = open.children.filter(child => !(child instanceof HTMLAttributeNode || child.type === 'AST_HTML_ATTRIBUTE_NODE') &&
|
|
2758
|
+
!(child instanceof WhitespaceNode || child.type === 'AST_WHITESPACE_NODE'));
|
|
2754
2759
|
const children = node.body.filter(child => !(child instanceof WhitespaceNode || child.type === 'AST_WHITESPACE_NODE') &&
|
|
2755
2760
|
!((child instanceof HTMLTextNode || child.type === 'AST_HTML_TEXT_NODE') && child?.content.trim() === ""));
|
|
2756
2761
|
const hasClosing = open.tag_closing?.value === ">" || open.tag_closing?.value === "/>";
|
|
@@ -2759,7 +2764,7 @@ class Printer extends Visitor {
|
|
|
2759
2764
|
this.push(indent + `<${tagName}`);
|
|
2760
2765
|
return;
|
|
2761
2766
|
}
|
|
2762
|
-
if (attributes.length === 0) {
|
|
2767
|
+
if (attributes.length === 0 && inlineNodes.length === 0) {
|
|
2763
2768
|
if (children.length === 0) {
|
|
2764
2769
|
if (isSelfClosing) {
|
|
2765
2770
|
this.push(indent + `<${tagName} />`);
|
|
@@ -2772,6 +2777,28 @@ class Printer extends Visitor {
|
|
|
2772
2777
|
}
|
|
2773
2778
|
return;
|
|
2774
2779
|
}
|
|
2780
|
+
if (children.length >= 1) {
|
|
2781
|
+
if (this.isInComplexNesting) {
|
|
2782
|
+
if (children.length === 1) {
|
|
2783
|
+
const child = children[0];
|
|
2784
|
+
if (child instanceof HTMLTextNode || child.type === 'AST_HTML_TEXT_NODE') {
|
|
2785
|
+
const textContent = child.content.trim();
|
|
2786
|
+
const singleLine = `<${tagName}>${textContent}</${tagName}>`;
|
|
2787
|
+
if (!textContent.includes('\n') && (indent.length + singleLine.length) <= this.maxLineLength) {
|
|
2788
|
+
this.push(indent + singleLine);
|
|
2789
|
+
return;
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
else {
|
|
2795
|
+
const inlineResult = this.tryRenderInline(children, tagName);
|
|
2796
|
+
if (inlineResult && (indent.length + inlineResult.length) <= this.maxLineLength) {
|
|
2797
|
+
this.push(indent + inlineResult);
|
|
2798
|
+
return;
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2775
2802
|
this.push(indent + `<${tagName}>`);
|
|
2776
2803
|
this.withIndent(() => {
|
|
2777
2804
|
children.forEach(child => this.visit(child));
|
|
@@ -2781,14 +2808,40 @@ class Printer extends Visitor {
|
|
|
2781
2808
|
}
|
|
2782
2809
|
return;
|
|
2783
2810
|
}
|
|
2784
|
-
|
|
2811
|
+
if (attributes.length === 0 && inlineNodes.length > 0) {
|
|
2812
|
+
const inline = this.renderInlineOpen(tagName, [], isSelfClosing, inlineNodes, open.children);
|
|
2813
|
+
if (children.length === 0) {
|
|
2814
|
+
if (isSelfClosing || node.is_void) {
|
|
2815
|
+
this.push(indent + inline);
|
|
2816
|
+
}
|
|
2817
|
+
else {
|
|
2818
|
+
this.push(indent + inline + `</${tagName}>`);
|
|
2819
|
+
}
|
|
2820
|
+
return;
|
|
2821
|
+
}
|
|
2822
|
+
this.push(indent + inline);
|
|
2823
|
+
this.withIndent(() => {
|
|
2824
|
+
children.forEach(child => this.visit(child));
|
|
2825
|
+
});
|
|
2826
|
+
if (!node.is_void && !isSelfClosing) {
|
|
2827
|
+
this.push(indent + `</${tagName}>`);
|
|
2828
|
+
}
|
|
2829
|
+
return;
|
|
2830
|
+
}
|
|
2831
|
+
const inline = this.renderInlineOpen(tagName, attributes, isSelfClosing, inlineNodes, open.children);
|
|
2785
2832
|
const singleAttribute = attributes[0];
|
|
2786
|
-
|
|
2833
|
+
singleAttribute &&
|
|
2787
2834
|
(singleAttribute.value instanceof HTMLAttributeValueNode || singleAttribute.value?.type === 'AST_HTML_ATTRIBUTE_VALUE_NODE') &&
|
|
2788
2835
|
singleAttribute.value?.children.length === 0;
|
|
2789
|
-
const
|
|
2790
|
-
|
|
2791
|
-
|
|
2836
|
+
const hasERBControlFlow = inlineNodes.some(node => node instanceof ERBIfNode || node.type === 'AST_ERB_IF_NODE' ||
|
|
2837
|
+
node instanceof ERBUnlessNode || node.type === 'AST_ERB_UNLESS_NODE' ||
|
|
2838
|
+
node instanceof ERBBlockNode || node.type === 'AST_ERB_BLOCK_NODE' ||
|
|
2839
|
+
node instanceof ERBCaseNode || node.type === 'AST_ERB_CASE_NODE' ||
|
|
2840
|
+
node instanceof ERBWhileNode || node.type === 'AST_ERB_WHILE_NODE' ||
|
|
2841
|
+
node instanceof ERBForNode || node.type === 'AST_ERB_FOR_NODE');
|
|
2842
|
+
const shouldKeepInline = (attributes.length <= 3 &&
|
|
2843
|
+
inline.length + indent.length <= this.maxLineLength) ||
|
|
2844
|
+
(inlineNodes.length > 0 && !hasERBControlFlow);
|
|
2792
2845
|
if (shouldKeepInline) {
|
|
2793
2846
|
if (children.length === 0) {
|
|
2794
2847
|
if (isSelfClosing) {
|
|
@@ -2816,27 +2869,67 @@ class Printer extends Visitor {
|
|
|
2816
2869
|
}
|
|
2817
2870
|
return;
|
|
2818
2871
|
}
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2872
|
+
if (inlineNodes.length > 0 && hasERBControlFlow) {
|
|
2873
|
+
this.push(indent + `<${tagName}`);
|
|
2874
|
+
this.withIndent(() => {
|
|
2875
|
+
open.children.forEach(child => {
|
|
2876
|
+
if (child instanceof HTMLAttributeNode || child.type === 'AST_HTML_ATTRIBUTE_NODE') {
|
|
2877
|
+
this.push(this.indent() + this.renderAttribute(child));
|
|
2878
|
+
}
|
|
2879
|
+
else if (!(child instanceof WhitespaceNode || child.type === 'AST_WHITESPACE_NODE')) {
|
|
2880
|
+
this.visit(child);
|
|
2881
|
+
}
|
|
2882
|
+
});
|
|
2823
2883
|
});
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2884
|
+
if (isSelfClosing) {
|
|
2885
|
+
this.push(indent + "/>");
|
|
2886
|
+
}
|
|
2887
|
+
else if (node.is_void) {
|
|
2888
|
+
this.push(indent + ">");
|
|
2889
|
+
}
|
|
2890
|
+
else if (children.length === 0) {
|
|
2891
|
+
this.push(indent + ">" + `</${tagName}>`);
|
|
2892
|
+
}
|
|
2893
|
+
else {
|
|
2894
|
+
this.push(indent + ">");
|
|
2895
|
+
this.withIndent(() => {
|
|
2896
|
+
children.forEach(child => this.visit(child));
|
|
2897
|
+
});
|
|
2898
|
+
this.push(indent + `</${tagName}>`);
|
|
2899
|
+
}
|
|
2830
2900
|
}
|
|
2831
|
-
else if (
|
|
2832
|
-
this.push(indent +
|
|
2901
|
+
else if (inlineNodes.length > 0) {
|
|
2902
|
+
this.push(indent + this.renderInlineOpen(tagName, attributes, isSelfClosing, inlineNodes, open.children));
|
|
2903
|
+
if (!isSelfClosing && !node.is_void && children.length > 0) {
|
|
2904
|
+
this.withIndent(() => {
|
|
2905
|
+
children.forEach(child => this.visit(child));
|
|
2906
|
+
});
|
|
2907
|
+
this.push(indent + `</${tagName}>`);
|
|
2908
|
+
}
|
|
2833
2909
|
}
|
|
2834
2910
|
else {
|
|
2835
|
-
this.push(indent +
|
|
2911
|
+
this.push(indent + `<${tagName}`);
|
|
2836
2912
|
this.withIndent(() => {
|
|
2837
|
-
|
|
2913
|
+
attributes.forEach(attribute => {
|
|
2914
|
+
this.push(this.indent() + this.renderAttribute(attribute));
|
|
2915
|
+
});
|
|
2838
2916
|
});
|
|
2839
|
-
|
|
2917
|
+
if (isSelfClosing) {
|
|
2918
|
+
this.push(indent + "/>");
|
|
2919
|
+
}
|
|
2920
|
+
else if (node.is_void) {
|
|
2921
|
+
this.push(indent + ">");
|
|
2922
|
+
}
|
|
2923
|
+
else if (children.length === 0) {
|
|
2924
|
+
this.push(indent + ">" + `</${tagName}>`);
|
|
2925
|
+
}
|
|
2926
|
+
else {
|
|
2927
|
+
this.push(indent + ">");
|
|
2928
|
+
this.withIndent(() => {
|
|
2929
|
+
children.forEach(child => this.visit(child));
|
|
2930
|
+
});
|
|
2931
|
+
this.push(indent + `</${tagName}>`);
|
|
2932
|
+
}
|
|
2840
2933
|
}
|
|
2841
2934
|
}
|
|
2842
2935
|
visitHTMLOpenTagNode(node) {
|
|
@@ -2867,11 +2960,10 @@ class Printer extends Visitor {
|
|
|
2867
2960
|
const attributes = node.attributes.filter((attribute) => attribute instanceof HTMLAttributeNode || attribute.type === 'AST_HTML_ATTRIBUTE_NODE');
|
|
2868
2961
|
const inline = this.renderInlineOpen(tagName, attributes, true);
|
|
2869
2962
|
const singleAttribute = attributes[0];
|
|
2870
|
-
|
|
2963
|
+
singleAttribute &&
|
|
2871
2964
|
(singleAttribute.value instanceof HTMLAttributeValueNode || singleAttribute.value?.type === 'AST_HTML_ATTRIBUTE_VALUE_NODE') &&
|
|
2872
2965
|
singleAttribute.value?.children.length === 0;
|
|
2873
2966
|
const shouldKeepInline = attributes.length <= 3 &&
|
|
2874
|
-
!hasEmptyValue &&
|
|
2875
2967
|
inline.length + indent.length <= this.maxLineLength;
|
|
2876
2968
|
if (shouldKeepInline) {
|
|
2877
2969
|
this.push(indent + inline);
|
|
@@ -3044,15 +3136,50 @@ class Printer extends Visitor {
|
|
|
3044
3136
|
}
|
|
3045
3137
|
}
|
|
3046
3138
|
visitERBIfNode(node) {
|
|
3047
|
-
this.
|
|
3048
|
-
|
|
3049
|
-
node.
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3139
|
+
if (this.inlineMode) {
|
|
3140
|
+
const open = node.tag_opening?.value ?? "";
|
|
3141
|
+
const content = node.content?.value ?? "";
|
|
3142
|
+
const close = node.tag_closing?.value ?? "";
|
|
3143
|
+
this.lines.push(open + content + close);
|
|
3144
|
+
if (node.statements.length > 0) {
|
|
3145
|
+
this.lines.push(" ");
|
|
3146
|
+
}
|
|
3147
|
+
node.statements.forEach((child, index) => {
|
|
3148
|
+
if (child instanceof HTMLAttributeNode || child.type === 'AST_HTML_ATTRIBUTE_NODE') {
|
|
3149
|
+
this.lines.push(this.renderAttribute(child));
|
|
3150
|
+
}
|
|
3151
|
+
else {
|
|
3152
|
+
this.visit(child);
|
|
3153
|
+
}
|
|
3154
|
+
if (index < node.statements.length - 1) {
|
|
3155
|
+
this.lines.push(" ");
|
|
3156
|
+
}
|
|
3157
|
+
});
|
|
3158
|
+
if (node.statements.length > 0) {
|
|
3159
|
+
this.lines.push(" ");
|
|
3160
|
+
}
|
|
3161
|
+
if (node.subsequent) {
|
|
3162
|
+
this.visit(node.subsequent);
|
|
3163
|
+
}
|
|
3164
|
+
if (node.end_node) {
|
|
3165
|
+
const endNode = node.end_node;
|
|
3166
|
+
const endOpen = endNode.tag_opening?.value ?? "";
|
|
3167
|
+
const endContent = endNode.content?.value ?? "";
|
|
3168
|
+
const endClose = endNode.tag_closing?.value ?? "";
|
|
3169
|
+
this.lines.push(endOpen + endContent + endClose);
|
|
3170
|
+
}
|
|
3053
3171
|
}
|
|
3054
|
-
|
|
3055
|
-
this.printERBNode(node
|
|
3172
|
+
else {
|
|
3173
|
+
this.printERBNode(node);
|
|
3174
|
+
this.withIndent(() => {
|
|
3175
|
+
node.statements.forEach(child => this.visit(child));
|
|
3176
|
+
});
|
|
3177
|
+
if (node.subsequent) {
|
|
3178
|
+
this.visit(node.subsequent);
|
|
3179
|
+
}
|
|
3180
|
+
if (node.end_node) {
|
|
3181
|
+
this.printERBNode(node.end_node);
|
|
3182
|
+
}
|
|
3056
3183
|
}
|
|
3057
3184
|
}
|
|
3058
3185
|
visitERBElseNode(node) {
|
|
@@ -3132,8 +3259,54 @@ class Printer extends Visitor {
|
|
|
3132
3259
|
this.visit(node.end_node);
|
|
3133
3260
|
}
|
|
3134
3261
|
// --- Utility methods ---
|
|
3135
|
-
renderInlineOpen(name, attributes, selfClose) {
|
|
3262
|
+
renderInlineOpen(name, attributes, selfClose, inlineNodes = [], allChildren = []) {
|
|
3136
3263
|
const parts = attributes.map(attribute => this.renderAttribute(attribute));
|
|
3264
|
+
if (inlineNodes.length > 0) {
|
|
3265
|
+
let result = `<${name}`;
|
|
3266
|
+
if (allChildren.length > 0) {
|
|
3267
|
+
const currentIndentLevel = this.indentLevel;
|
|
3268
|
+
this.indentLevel = 0;
|
|
3269
|
+
const tempLines = this.lines;
|
|
3270
|
+
this.lines = [];
|
|
3271
|
+
allChildren.forEach(child => {
|
|
3272
|
+
if (child instanceof HTMLAttributeNode || child.type === 'AST_HTML_ATTRIBUTE_NODE') {
|
|
3273
|
+
this.lines.push(" " + this.renderAttribute(child));
|
|
3274
|
+
}
|
|
3275
|
+
else if (!(child instanceof WhitespaceNode || child.type === 'AST_WHITESPACE_NODE')) {
|
|
3276
|
+
const wasInlineMode = this.inlineMode;
|
|
3277
|
+
this.inlineMode = true;
|
|
3278
|
+
this.lines.push(" ");
|
|
3279
|
+
this.visit(child);
|
|
3280
|
+
this.inlineMode = wasInlineMode;
|
|
3281
|
+
}
|
|
3282
|
+
});
|
|
3283
|
+
const inlineContent = this.lines.join("");
|
|
3284
|
+
this.lines = tempLines;
|
|
3285
|
+
this.indentLevel = currentIndentLevel;
|
|
3286
|
+
result += inlineContent;
|
|
3287
|
+
}
|
|
3288
|
+
else {
|
|
3289
|
+
if (parts.length > 0) {
|
|
3290
|
+
result += ` ${parts.join(" ")}`;
|
|
3291
|
+
}
|
|
3292
|
+
const currentIndentLevel = this.indentLevel;
|
|
3293
|
+
this.indentLevel = 0;
|
|
3294
|
+
const tempLines = this.lines;
|
|
3295
|
+
this.lines = [];
|
|
3296
|
+
inlineNodes.forEach(node => {
|
|
3297
|
+
const wasInlineMode = this.inlineMode;
|
|
3298
|
+
this.inlineMode = true;
|
|
3299
|
+
this.visit(node);
|
|
3300
|
+
this.inlineMode = wasInlineMode;
|
|
3301
|
+
});
|
|
3302
|
+
const inlineContent = this.lines.join("");
|
|
3303
|
+
this.lines = tempLines;
|
|
3304
|
+
this.indentLevel = currentIndentLevel;
|
|
3305
|
+
result += inlineContent;
|
|
3306
|
+
}
|
|
3307
|
+
result += selfClose ? " />" : ">";
|
|
3308
|
+
return result;
|
|
3309
|
+
}
|
|
3137
3310
|
return `<${name}${parts.length ? " " + parts.join(" ") : ""}${selfClose ? " /" : ""}>`;
|
|
3138
3311
|
}
|
|
3139
3312
|
renderAttribute(attribute) {
|
|
@@ -3145,8 +3318,7 @@ class Printer extends Visitor {
|
|
|
3145
3318
|
const open_quote = (attrValue.open_quote?.value ?? "");
|
|
3146
3319
|
const close_quote = (attrValue.close_quote?.value ?? "");
|
|
3147
3320
|
const attribute_value = attrValue.children.map((attr) => {
|
|
3148
|
-
if (attr instanceof HTMLTextNode || attr.type === 'AST_HTML_TEXT_NODE' ||
|
|
3149
|
-
attr instanceof LiteralNode || attr.type === 'AST_LITERAL_NODE') {
|
|
3321
|
+
if (attr instanceof HTMLTextNode || attr.type === 'AST_HTML_TEXT_NODE' || attr instanceof LiteralNode || attr.type === 'AST_LITERAL_NODE') {
|
|
3150
3322
|
return attr.content;
|
|
3151
3323
|
}
|
|
3152
3324
|
else if (attr instanceof ERBContentNode || attr.type === 'AST_ERB_CONTENT_NODE') {
|
|
@@ -3159,6 +3331,124 @@ class Printer extends Visitor {
|
|
|
3159
3331
|
}
|
|
3160
3332
|
return name + equals + value;
|
|
3161
3333
|
}
|
|
3334
|
+
/**
|
|
3335
|
+
* Try to render children inline if they are simple enough.
|
|
3336
|
+
* Returns the inline string if possible, null otherwise.
|
|
3337
|
+
*/
|
|
3338
|
+
tryRenderInline(children, tagName, depth = 0) {
|
|
3339
|
+
if (children.length > 10) {
|
|
3340
|
+
return null;
|
|
3341
|
+
}
|
|
3342
|
+
const maxNestingDepth = this.getMaxNestingDepth(children, 0);
|
|
3343
|
+
if (maxNestingDepth > 1) {
|
|
3344
|
+
this.isInComplexNesting = true;
|
|
3345
|
+
return null;
|
|
3346
|
+
}
|
|
3347
|
+
for (const child of children) {
|
|
3348
|
+
if (child instanceof HTMLTextNode || child.type === 'AST_HTML_TEXT_NODE') {
|
|
3349
|
+
const textContent = child.content;
|
|
3350
|
+
if (textContent.includes('\n')) {
|
|
3351
|
+
return null;
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
else if (child instanceof HTMLElementNode || child.type === 'AST_HTML_ELEMENT_NODE') {
|
|
3355
|
+
const element = child;
|
|
3356
|
+
const openTag = element.open_tag;
|
|
3357
|
+
const attributes = openTag.children.filter((child) => child instanceof HTMLAttributeNode || child.type === 'AST_HTML_ATTRIBUTE_NODE');
|
|
3358
|
+
if (attributes.length > 0) {
|
|
3359
|
+
return null;
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
else if (child instanceof ERBContentNode || child.type === 'AST_ERB_CONTENT_NODE') ;
|
|
3363
|
+
else {
|
|
3364
|
+
return null;
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
const oldLines = this.lines;
|
|
3368
|
+
const oldInlineMode = this.inlineMode;
|
|
3369
|
+
try {
|
|
3370
|
+
this.lines = [];
|
|
3371
|
+
this.inlineMode = true;
|
|
3372
|
+
let content = '';
|
|
3373
|
+
for (const child of children) {
|
|
3374
|
+
if (child instanceof HTMLTextNode || child.type === 'AST_HTML_TEXT_NODE') {
|
|
3375
|
+
content += child.content;
|
|
3376
|
+
}
|
|
3377
|
+
else if (child instanceof HTMLElementNode || child.type === 'AST_HTML_ELEMENT_NODE') {
|
|
3378
|
+
const element = child;
|
|
3379
|
+
const openTag = element.open_tag;
|
|
3380
|
+
const childTagName = openTag?.tag_name?.value || '';
|
|
3381
|
+
const attributes = openTag.children.filter((child) => child instanceof HTMLAttributeNode || child.type === 'AST_HTML_ATTRIBUTE_NODE');
|
|
3382
|
+
const attributesString = attributes.length > 0
|
|
3383
|
+
? ' ' + attributes.map(attr => this.renderAttribute(attr)).join(' ')
|
|
3384
|
+
: '';
|
|
3385
|
+
const elementContent = this.renderElementInline(element);
|
|
3386
|
+
content += `<${childTagName}${attributesString}>${elementContent}</${childTagName}>`;
|
|
3387
|
+
}
|
|
3388
|
+
else if (child instanceof ERBContentNode || child.type === 'AST_ERB_CONTENT_NODE') {
|
|
3389
|
+
const erbNode = child;
|
|
3390
|
+
const open = erbNode.tag_opening?.value ?? "";
|
|
3391
|
+
const erbContent = erbNode.content?.value ?? "";
|
|
3392
|
+
const close = erbNode.tag_closing?.value ?? "";
|
|
3393
|
+
content += `${open} ${erbContent.trim()} ${close}`;
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
content = content.replace(/\s+/g, ' ').trim();
|
|
3397
|
+
return `<${tagName}>${content}</${tagName}>`;
|
|
3398
|
+
}
|
|
3399
|
+
finally {
|
|
3400
|
+
this.lines = oldLines;
|
|
3401
|
+
this.inlineMode = oldInlineMode;
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
/**
|
|
3405
|
+
* Calculate the maximum nesting depth in a subtree of nodes.
|
|
3406
|
+
*/
|
|
3407
|
+
getMaxNestingDepth(children, currentDepth) {
|
|
3408
|
+
let maxDepth = currentDepth;
|
|
3409
|
+
for (const child of children) {
|
|
3410
|
+
if (child instanceof HTMLElementNode || child.type === 'AST_HTML_ELEMENT_NODE') {
|
|
3411
|
+
const element = child;
|
|
3412
|
+
const elementChildren = element.body.filter(child => !(child instanceof WhitespaceNode || child.type === 'AST_WHITESPACE_NODE') &&
|
|
3413
|
+
!((child instanceof HTMLTextNode || child.type === 'AST_HTML_TEXT_NODE') && child?.content.trim() === ""));
|
|
3414
|
+
const childDepth = this.getMaxNestingDepth(elementChildren, currentDepth + 1);
|
|
3415
|
+
maxDepth = Math.max(maxDepth, childDepth);
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
return maxDepth;
|
|
3419
|
+
}
|
|
3420
|
+
/**
|
|
3421
|
+
* Render an HTML element's content inline (without the wrapping tags).
|
|
3422
|
+
*/
|
|
3423
|
+
renderElementInline(element) {
|
|
3424
|
+
const children = element.body.filter(child => !(child instanceof WhitespaceNode || child.type === 'AST_WHITESPACE_NODE') &&
|
|
3425
|
+
!((child instanceof HTMLTextNode || child.type === 'AST_HTML_TEXT_NODE') && child?.content.trim() === ""));
|
|
3426
|
+
let content = '';
|
|
3427
|
+
for (const child of children) {
|
|
3428
|
+
if (child instanceof HTMLTextNode || child.type === 'AST_HTML_TEXT_NODE') {
|
|
3429
|
+
content += child.content;
|
|
3430
|
+
}
|
|
3431
|
+
else if (child instanceof HTMLElementNode || child.type === 'AST_HTML_ELEMENT_NODE') {
|
|
3432
|
+
const childElement = child;
|
|
3433
|
+
const openTag = childElement.open_tag;
|
|
3434
|
+
const childTagName = openTag?.tag_name?.value || '';
|
|
3435
|
+
const attributes = openTag.children.filter((child) => child instanceof HTMLAttributeNode || child.type === 'AST_HTML_ATTRIBUTE_NODE');
|
|
3436
|
+
const attributesString = attributes.length > 0
|
|
3437
|
+
? ' ' + attributes.map(attr => this.renderAttribute(attr)).join(' ')
|
|
3438
|
+
: '';
|
|
3439
|
+
const childContent = this.renderElementInline(childElement);
|
|
3440
|
+
content += `<${childTagName}${attributesString}>${childContent}</${childTagName}>`;
|
|
3441
|
+
}
|
|
3442
|
+
else if (child instanceof ERBContentNode || child.type === 'AST_ERB_CONTENT_NODE') {
|
|
3443
|
+
const erbNode = child;
|
|
3444
|
+
const open = erbNode.tag_opening?.value ?? "";
|
|
3445
|
+
const erbContent = erbNode.content?.value ?? "";
|
|
3446
|
+
const close = erbNode.tag_closing?.value ?? "";
|
|
3447
|
+
content += `${open} ${erbContent.trim()} ${close}`;
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
return content.replace(/\s+/g, ' ').trim();
|
|
3451
|
+
}
|
|
3162
3452
|
}
|
|
3163
3453
|
|
|
3164
3454
|
/**
|