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