@herb-tools/formatter 0.4.3 → 0.5.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.
- package/README.md +113 -15
- package/dist/herb-format.js +196 -69
- package/dist/herb-format.js.map +1 -1
- package/dist/index.cjs +188 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +188 -61
- package/dist/index.esm.js.map +1 -1
- package/dist/types/printer.d.ts +7 -0
- package/package.json +2 -2
- package/src/cli.ts +2 -2
- package/src/printer.ts +230 -62
package/dist/types/printer.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export declare class Printer extends Visitor {
|
|
|
13
13
|
private indentLevel;
|
|
14
14
|
private inlineMode;
|
|
15
15
|
private isInComplexNesting;
|
|
16
|
+
private currentTagName;
|
|
16
17
|
private static readonly INLINE_ELEMENTS;
|
|
17
18
|
constructor(source: string, options: Required<FormatOptions>);
|
|
18
19
|
print(object: Node | Token, indentLevel?: number): string;
|
|
@@ -48,6 +49,12 @@ export declare class Printer extends Visitor {
|
|
|
48
49
|
* Determine if a tag should be rendered inline based on attribute count and other factors
|
|
49
50
|
*/
|
|
50
51
|
private shouldRenderInline;
|
|
52
|
+
private hasMultilineAttributes;
|
|
53
|
+
private formatClassAttribute;
|
|
54
|
+
private isFormattableAttribute;
|
|
55
|
+
private formatMultilineAttribute;
|
|
56
|
+
private formatMultilineAttributeValue;
|
|
57
|
+
private breakTokensIntoLines;
|
|
51
58
|
/**
|
|
52
59
|
* Render multiline attributes for a tag
|
|
53
60
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herb-tools/formatter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
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,7 +35,7 @@
|
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@herb-tools/core": "0.
|
|
38
|
+
"@herb-tools/core": "0.5.0",
|
|
39
39
|
"glob": "^11.0.3"
|
|
40
40
|
},
|
|
41
41
|
"files": [
|
package/src/cli.ts
CHANGED
|
@@ -27,11 +27,11 @@ export class CLI {
|
|
|
27
27
|
|
|
28
28
|
Examples:
|
|
29
29
|
herb-format # Format all **/*.html.erb files in current directory
|
|
30
|
-
herb-format
|
|
30
|
+
herb-format templates/ # Format and **/*.html.erb within the given directory
|
|
31
31
|
herb-format templates/index.html.erb # Format and write single file
|
|
32
|
+
herb-format --check # Check if all **/*.html.erb files are formatted
|
|
32
33
|
herb-format --check templates/ # Check if all **/*.html.erb files in templates/ are formatted
|
|
33
34
|
cat template.html.erb | herb-format # Format from stdin to stdout
|
|
34
|
-
herb-format - < template.html.erb # Format from stdin to stdout
|
|
35
35
|
`
|
|
36
36
|
|
|
37
37
|
async run() {
|
package/src/printer.ts
CHANGED
|
@@ -57,6 +57,12 @@ type ERBNode =
|
|
|
57
57
|
|
|
58
58
|
import type { FormatOptions } from "./options.js"
|
|
59
59
|
|
|
60
|
+
// TODO: we can probably expand this list with more tags/attributes
|
|
61
|
+
const FORMATTABLE_ATTRIBUTES: Record<string, string[]> = {
|
|
62
|
+
'*': ['class'],
|
|
63
|
+
'img': ['srcset', 'sizes']
|
|
64
|
+
}
|
|
65
|
+
|
|
60
66
|
/**
|
|
61
67
|
* Printer traverses the Herb AST using the Visitor pattern
|
|
62
68
|
* and emits a formatted string with proper indentation, line breaks, and attribute wrapping.
|
|
@@ -69,6 +75,7 @@ export class Printer extends Visitor {
|
|
|
69
75
|
private indentLevel: number = 0
|
|
70
76
|
private inlineMode: boolean = false
|
|
71
77
|
private isInComplexNesting: boolean = false
|
|
78
|
+
private currentTagName: string = ""
|
|
72
79
|
|
|
73
80
|
private static readonly INLINE_ELEMENTS = new Set([
|
|
74
81
|
'a', 'abbr', 'acronym', 'b', 'bdo', 'big', 'br', 'cite', 'code',
|
|
@@ -196,22 +203,142 @@ export class Printer extends Visitor {
|
|
|
196
203
|
indentLength: number,
|
|
197
204
|
maxLineLength: number = this.maxLineLength,
|
|
198
205
|
hasComplexERB: boolean = false,
|
|
199
|
-
|
|
200
|
-
|
|
206
|
+
_nestingDepth: number = 0,
|
|
207
|
+
_inlineNodesLength: number = 0,
|
|
208
|
+
hasMultilineAttributes: boolean = false
|
|
201
209
|
): boolean {
|
|
202
|
-
if (hasComplexERB) return false
|
|
210
|
+
if (hasComplexERB || hasMultilineAttributes) return false
|
|
203
211
|
|
|
204
|
-
// Special case: no attributes at all, always inline if it fits
|
|
205
212
|
if (totalAttributeCount === 0) {
|
|
206
213
|
return inlineLength + indentLength <= maxLineLength
|
|
207
214
|
}
|
|
208
215
|
|
|
209
|
-
|
|
210
|
-
|
|
216
|
+
if (totalAttributeCount > 3 || inlineLength + indentLength > maxLineLength) {
|
|
217
|
+
return false
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return true
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private hasMultilineAttributes(attributes: HTMLAttributeNode[]): boolean {
|
|
224
|
+
return attributes.some(attribute => {
|
|
225
|
+
if (attribute.value && (attribute.value instanceof HTMLAttributeValueNode || (attribute.value as any)?.type === 'AST_HTML_ATTRIBUTE_VALUE_NODE')) {
|
|
226
|
+
const attributeValue = attribute.value as HTMLAttributeValueNode
|
|
227
|
+
|
|
228
|
+
const content = attributeValue.children.map((child: Node) => {
|
|
229
|
+
if (child instanceof HTMLTextNode || (child as any).type === 'AST_HTML_TEXT_NODE' || child instanceof LiteralNode || (child as any).type === 'AST_LITERAL_NODE') {
|
|
230
|
+
return (child as HTMLTextNode | LiteralNode).content
|
|
231
|
+
} else if (child instanceof ERBContentNode || (child as any).type === 'AST_ERB_CONTENT_NODE') {
|
|
232
|
+
const erbAttribute = child as ERBContentNode
|
|
233
|
+
|
|
234
|
+
return erbAttribute.tag_opening!.value + erbAttribute.content!.value + erbAttribute.tag_closing!.value
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return ""
|
|
238
|
+
}).join("")
|
|
239
|
+
|
|
240
|
+
if (/\r?\n/.test(content)) {
|
|
241
|
+
const name = (attribute.name as HTMLAttributeNameNode)!.name!.value ?? ""
|
|
242
|
+
|
|
243
|
+
if (name === 'class') {
|
|
244
|
+
const normalizedContent = content.replace(/\s+/g, ' ').trim()
|
|
245
|
+
|
|
246
|
+
return normalizedContent.length > 80
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const lines = content.split(/\r?\n/)
|
|
250
|
+
|
|
251
|
+
if (lines.length > 1) {
|
|
252
|
+
return lines.slice(1).some(line => /^\s+/.test(line))
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return false
|
|
258
|
+
})
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private formatClassAttribute(content: string, name: string, equals: string, open_quote: string, close_quote: string): string {
|
|
262
|
+
const normalizedContent = content.replace(/\s+/g, ' ').trim()
|
|
263
|
+
const hasActualNewlines = /\r?\n/.test(content)
|
|
264
|
+
|
|
265
|
+
if (hasActualNewlines && normalizedContent.length > 80) {
|
|
266
|
+
const lines = content.split(/\r?\n/).map(line => line.trim()).filter(line => line)
|
|
267
|
+
|
|
268
|
+
if (lines.length > 1) {
|
|
269
|
+
return open_quote + this.formatMultilineAttributeValue(lines) + close_quote
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const currentIndent = this.indentLevel * this.indentWidth
|
|
274
|
+
const attributeLine = `${name}${equals}${open_quote}${normalizedContent}${close_quote}`
|
|
275
|
+
|
|
276
|
+
if (currentIndent + attributeLine.length > this.maxLineLength && normalizedContent.length > 60) {
|
|
277
|
+
const classes = normalizedContent.split(' ')
|
|
278
|
+
const lines = this.breakTokensIntoLines(classes, currentIndent)
|
|
279
|
+
|
|
280
|
+
if (lines.length > 1) {
|
|
281
|
+
return open_quote + this.formatMultilineAttributeValue(lines) + close_quote
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return open_quote + normalizedContent + close_quote
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private isFormattableAttribute(attributeName: string, tagName: string): boolean {
|
|
289
|
+
const globalFormattable = FORMATTABLE_ATTRIBUTES['*'] || []
|
|
290
|
+
const tagSpecificFormattable = FORMATTABLE_ATTRIBUTES[tagName.toLowerCase()] || []
|
|
291
|
+
|
|
292
|
+
return globalFormattable.includes(attributeName) || tagSpecificFormattable.includes(attributeName)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private formatMultilineAttribute(content: string, name: string, equals: string, open_quote: string, close_quote: string): string {
|
|
296
|
+
if (name === 'srcset' || name === 'sizes') {
|
|
297
|
+
const normalizedContent = content.replace(/\s+/g, ' ').trim()
|
|
298
|
+
|
|
299
|
+
return open_quote + normalizedContent + close_quote
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const lines = content.split('\n')
|
|
303
|
+
|
|
304
|
+
if (lines.length <= 1) {
|
|
305
|
+
return open_quote + content + close_quote
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const formattedContent = this.formatMultilineAttributeValue(lines)
|
|
309
|
+
|
|
310
|
+
return open_quote + formattedContent + close_quote
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
private formatMultilineAttributeValue(lines: string[]): string {
|
|
314
|
+
const indent = " ".repeat((this.indentLevel + 1) * this.indentWidth)
|
|
315
|
+
const closeIndent = " ".repeat(this.indentLevel * this.indentWidth)
|
|
316
|
+
|
|
317
|
+
return "\n" + lines.map(line => indent + line).join("\n") + "\n" + closeIndent
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private breakTokensIntoLines(tokens: string[], currentIndent: number, separator: string = ' '): string[] {
|
|
321
|
+
const lines: string[] = []
|
|
322
|
+
let currentLine = ''
|
|
323
|
+
|
|
324
|
+
for (const token of tokens) {
|
|
325
|
+
const testLine = currentLine ? currentLine + separator + token : token
|
|
326
|
+
|
|
327
|
+
if (testLine.length > (this.maxLineLength - currentIndent - 6)) {
|
|
328
|
+
if (currentLine) {
|
|
329
|
+
lines.push(currentLine)
|
|
330
|
+
currentLine = token
|
|
331
|
+
} else {
|
|
332
|
+
lines.push(token)
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
currentLine = testLine
|
|
336
|
+
}
|
|
337
|
+
}
|
|
211
338
|
|
|
212
|
-
|
|
339
|
+
if (currentLine) lines.push(currentLine)
|
|
213
340
|
|
|
214
|
-
return
|
|
341
|
+
return lines
|
|
215
342
|
}
|
|
216
343
|
|
|
217
344
|
/**
|
|
@@ -219,8 +346,8 @@ export class Printer extends Visitor {
|
|
|
219
346
|
*/
|
|
220
347
|
private renderMultilineAttributes(
|
|
221
348
|
tagName: string,
|
|
222
|
-
|
|
223
|
-
|
|
349
|
+
_attributes: HTMLAttributeNode[],
|
|
350
|
+
_inlineNodes: Node[] = [],
|
|
224
351
|
allChildren: Node[] = [],
|
|
225
352
|
isSelfClosing: boolean = false,
|
|
226
353
|
isVoid: boolean = false,
|
|
@@ -230,7 +357,6 @@ export class Printer extends Visitor {
|
|
|
230
357
|
this.push(indent + `<${tagName}`)
|
|
231
358
|
|
|
232
359
|
this.withIndent(() => {
|
|
233
|
-
// Render children in order, handling both attributes and ERB nodes
|
|
234
360
|
allChildren.forEach(child => {
|
|
235
361
|
if (child instanceof HTMLAttributeNode || (child as any).type === 'AST_HTML_ATTRIBUTE_NODE') {
|
|
236
362
|
this.push(this.indent() + this.renderAttribute(child as HTMLAttributeNode))
|
|
@@ -310,6 +436,7 @@ export class Printer extends Visitor {
|
|
|
310
436
|
const tagName = open.tag_name?.value ?? ""
|
|
311
437
|
const indent = this.indent()
|
|
312
438
|
|
|
439
|
+
this.currentTagName = tagName
|
|
313
440
|
|
|
314
441
|
const attributes = this.extractAttributes(open.children)
|
|
315
442
|
const inlineNodes = this.extractInlineNodes(open.children)
|
|
@@ -498,11 +625,10 @@ export class Printer extends Visitor {
|
|
|
498
625
|
this.maxLineLength,
|
|
499
626
|
hasComplexERB,
|
|
500
627
|
nestingDepth,
|
|
501
|
-
inlineNodes.length
|
|
628
|
+
inlineNodes.length,
|
|
629
|
+
this.hasMultilineAttributes(attributes)
|
|
502
630
|
)
|
|
503
631
|
|
|
504
|
-
|
|
505
|
-
|
|
506
632
|
if (shouldKeepInline) {
|
|
507
633
|
if (children.length === 0) {
|
|
508
634
|
if (isSelfClosing) {
|
|
@@ -632,17 +758,32 @@ export class Printer extends Visitor {
|
|
|
632
758
|
}
|
|
633
759
|
|
|
634
760
|
if (isInlineElement && children.length === 0) {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
761
|
+
const inline = this.renderInlineOpen(tagName, attributes, isSelfClosing, inlineNodes, open.children)
|
|
762
|
+
const totalAttributeCount = this.getTotalAttributeCount(attributes, inlineNodes)
|
|
763
|
+
const shouldKeepInline = this.shouldRenderInline(
|
|
764
|
+
totalAttributeCount,
|
|
765
|
+
inline.length,
|
|
766
|
+
indent.length,
|
|
767
|
+
this.maxLineLength,
|
|
768
|
+
false,
|
|
769
|
+
0,
|
|
770
|
+
inlineNodes.length,
|
|
771
|
+
this.hasMultilineAttributes(attributes)
|
|
772
|
+
)
|
|
773
|
+
|
|
774
|
+
if (shouldKeepInline) {
|
|
775
|
+
let result = `<${tagName}`
|
|
776
|
+
result += this.renderAttributesString(attributes)
|
|
777
|
+
if (isSelfClosing) {
|
|
778
|
+
result += " />"
|
|
779
|
+
} else if (node.is_void) {
|
|
780
|
+
result += ">"
|
|
781
|
+
} else {
|
|
782
|
+
result += `></${tagName}>`
|
|
783
|
+
}
|
|
784
|
+
this.push(indent + result)
|
|
785
|
+
return
|
|
643
786
|
}
|
|
644
|
-
this.push(indent + result)
|
|
645
|
-
return
|
|
646
787
|
}
|
|
647
788
|
|
|
648
789
|
this.renderMultilineAttributes(tagName, attributes, inlineNodes, open.children, isSelfClosing, node.is_void, children.length > 0)
|
|
@@ -683,7 +824,8 @@ export class Printer extends Visitor {
|
|
|
683
824
|
this.maxLineLength,
|
|
684
825
|
false,
|
|
685
826
|
0,
|
|
686
|
-
inlineNodes.length
|
|
827
|
+
inlineNodes.length,
|
|
828
|
+
this.hasMultilineAttributes(attributes)
|
|
687
829
|
)
|
|
688
830
|
|
|
689
831
|
if (shouldKeepInline) {
|
|
@@ -711,7 +853,8 @@ export class Printer extends Visitor {
|
|
|
711
853
|
this.maxLineLength,
|
|
712
854
|
false,
|
|
713
855
|
0,
|
|
714
|
-
inlineNodes.length
|
|
856
|
+
inlineNodes.length,
|
|
857
|
+
this.hasMultilineAttributes(attributes)
|
|
715
858
|
)
|
|
716
859
|
|
|
717
860
|
if (shouldKeepInline) {
|
|
@@ -805,20 +948,25 @@ export class Printer extends Visitor {
|
|
|
805
948
|
const indent = this.indent()
|
|
806
949
|
const open = node.comment_start?.value ?? ""
|
|
807
950
|
const close = node.comment_end?.value ?? ""
|
|
951
|
+
|
|
808
952
|
let inner: string
|
|
809
953
|
|
|
810
|
-
if (node.
|
|
811
|
-
// TODO: use .value
|
|
812
|
-
const [_, startIndex] = node.comment_start.range.toArray()
|
|
813
|
-
const [endIndex] = node.comment_end.range.toArray()
|
|
814
|
-
const rawInner = this.source.slice(startIndex, endIndex)
|
|
815
|
-
inner = ` ${rawInner.trim()} `
|
|
816
|
-
} else {
|
|
954
|
+
if (node.children && node.children.length > 0) {
|
|
817
955
|
inner = node.children.map(child => {
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
956
|
+
if (child instanceof HTMLTextNode || (child as any).type === 'AST_HTML_TEXT_NODE') {
|
|
957
|
+
return (child as HTMLTextNode).content
|
|
958
|
+
} else if (child instanceof LiteralNode || (child as any).type === 'AST_LITERAL_NODE') {
|
|
959
|
+
return (child as LiteralNode).content
|
|
960
|
+
} else {
|
|
961
|
+
const prevLines = this.lines.length
|
|
962
|
+
this.visit(child)
|
|
963
|
+
return this.lines.slice(prevLines).join("")
|
|
964
|
+
}
|
|
821
965
|
}).join("")
|
|
966
|
+
|
|
967
|
+
inner = ` ${inner.trim()} `
|
|
968
|
+
} else {
|
|
969
|
+
inner = ""
|
|
822
970
|
}
|
|
823
971
|
|
|
824
972
|
this.push(indent + open + inner + close)
|
|
@@ -830,26 +978,28 @@ export class Printer extends Visitor {
|
|
|
830
978
|
const close = node.tag_closing?.value ?? ""
|
|
831
979
|
let inner: string
|
|
832
980
|
|
|
833
|
-
if (node.
|
|
834
|
-
const
|
|
835
|
-
const [closingStart] = node.tag_closing.range.toArray()
|
|
836
|
-
const rawInner = this.source.slice(openingEnd, closingStart)
|
|
981
|
+
if (node.content && node.content.value) {
|
|
982
|
+
const rawInner = node.content.value
|
|
837
983
|
const lines = rawInner.split("\n")
|
|
984
|
+
|
|
838
985
|
if (lines.length > 2) {
|
|
839
986
|
const childIndent = indent + " ".repeat(this.indentWidth)
|
|
840
987
|
const innerLines = lines.slice(1, -1).map(line => childIndent + line.trim())
|
|
988
|
+
|
|
841
989
|
inner = "\n" + innerLines.join("\n") + "\n"
|
|
842
990
|
} else {
|
|
843
991
|
inner = ` ${rawInner.trim()} `
|
|
844
992
|
}
|
|
993
|
+
} else if ((node as any).children) {
|
|
994
|
+
inner = (node as any).children.map((child: any) => {
|
|
995
|
+
const prevLines = this.lines.length
|
|
996
|
+
|
|
997
|
+
this.visit(child)
|
|
998
|
+
|
|
999
|
+
return this.lines.slice(prevLines).join("")
|
|
1000
|
+
}).join("")
|
|
845
1001
|
} else {
|
|
846
|
-
inner =
|
|
847
|
-
.map((child: any) => {
|
|
848
|
-
const prevLines = this.lines.length
|
|
849
|
-
this.visit(child)
|
|
850
|
-
return this.lines.slice(prevLines).join("")
|
|
851
|
-
})
|
|
852
|
-
.join("")
|
|
1002
|
+
inner = ""
|
|
853
1003
|
}
|
|
854
1004
|
|
|
855
1005
|
this.push(indent + open + inner + close)
|
|
@@ -858,22 +1008,30 @@ export class Printer extends Visitor {
|
|
|
858
1008
|
visitHTMLDoctypeNode(node: HTMLDoctypeNode): void {
|
|
859
1009
|
const indent = this.indent()
|
|
860
1010
|
const open = node.tag_opening?.value ?? ""
|
|
861
|
-
let innerDoctype: string
|
|
862
1011
|
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
.
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
1012
|
+
let innerDoctype = node.children.map(child => {
|
|
1013
|
+
if (child instanceof HTMLTextNode || (child as any).type === 'AST_HTML_TEXT_NODE') {
|
|
1014
|
+
return (child as HTMLTextNode).content
|
|
1015
|
+
} else if (child instanceof LiteralNode || (child as any).type === 'AST_LITERAL_NODE') {
|
|
1016
|
+
return (child as LiteralNode).content
|
|
1017
|
+
} else if (child instanceof ERBContentNode || (child as any).type === 'AST_ERB_CONTENT_NODE') {
|
|
1018
|
+
const erbNode = child as ERBContentNode
|
|
1019
|
+
const erbOpen = erbNode.tag_opening?.value ?? ""
|
|
1020
|
+
const erbContent = erbNode.content?.value ?? ""
|
|
1021
|
+
const erbClose = erbNode.tag_closing?.value ?? ""
|
|
1022
|
+
|
|
1023
|
+
return erbOpen + (erbContent ? ` ${erbContent.trim()} ` : "") + erbClose
|
|
1024
|
+
} else {
|
|
1025
|
+
const prevLines = this.lines.length
|
|
1026
|
+
|
|
1027
|
+
this.visit(child)
|
|
1028
|
+
|
|
1029
|
+
return this.lines.slice(prevLines).join("")
|
|
1030
|
+
}
|
|
1031
|
+
}).join("")
|
|
875
1032
|
|
|
876
1033
|
const close = node.tag_closing?.value ?? ""
|
|
1034
|
+
|
|
877
1035
|
this.push(indent + open + innerDoctype + close)
|
|
878
1036
|
}
|
|
879
1037
|
|
|
@@ -1175,6 +1333,8 @@ export class Printer extends Visitor {
|
|
|
1175
1333
|
currentLineContent += erbContent
|
|
1176
1334
|
|
|
1177
1335
|
if ((indent.length + currentLineContent.length) > Math.max(this.maxLineLength, 120)) {
|
|
1336
|
+
this.lines = oldLines
|
|
1337
|
+
this.inlineMode = oldInlineMode
|
|
1178
1338
|
this.visitTextFlowChildrenMultiline(children)
|
|
1179
1339
|
|
|
1180
1340
|
return
|
|
@@ -1359,7 +1519,15 @@ export class Printer extends Visitor {
|
|
|
1359
1519
|
close_quote = '"'
|
|
1360
1520
|
}
|
|
1361
1521
|
|
|
1362
|
-
|
|
1522
|
+
if (this.isFormattableAttribute(name, this.currentTagName)) {
|
|
1523
|
+
if (name === 'class') {
|
|
1524
|
+
value = this.formatClassAttribute(content, name, equals, open_quote, close_quote)
|
|
1525
|
+
} else {
|
|
1526
|
+
value = this.formatMultilineAttribute(content, name, equals, open_quote, close_quote)
|
|
1527
|
+
}
|
|
1528
|
+
} else {
|
|
1529
|
+
value = open_quote + content + close_quote
|
|
1530
|
+
}
|
|
1363
1531
|
}
|
|
1364
1532
|
|
|
1365
1533
|
return name + equals + value
|
|
@@ -1368,7 +1536,7 @@ export class Printer extends Visitor {
|
|
|
1368
1536
|
/**
|
|
1369
1537
|
* Try to render a complete element inline including opening tag, children, and closing tag
|
|
1370
1538
|
*/
|
|
1371
|
-
private tryRenderInlineFull(
|
|
1539
|
+
private tryRenderInlineFull(_node: HTMLElementNode, tagName: string, attributes: HTMLAttributeNode[], children: Node[]): string | null {
|
|
1372
1540
|
let result = `<${tagName}`
|
|
1373
1541
|
|
|
1374
1542
|
result += this.renderAttributesString(attributes)
|