@herb-tools/formatter 0.4.0 → 0.4.1
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 +17254 -0
- package/dist/herb-format.js.map +1 -0
- package/dist/index.cjs +138 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +138 -35
- package/dist/index.esm.js.map +1 -1
- package/dist/types/printer.d.ts +1 -0
- package/package.json +4 -4
- package/src/cli.ts +170 -23
- package/src/printer.ts +158 -32
- 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/types/printer.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare class Printer extends Visitor {
|
|
|
11
11
|
private source;
|
|
12
12
|
private lines;
|
|
13
13
|
private indentLevel;
|
|
14
|
+
private inlineMode;
|
|
14
15
|
constructor(source: string, options: Required<FormatOptions>);
|
|
15
16
|
print(object: Node | Token, indentLevel?: number): string;
|
|
16
17
|
private push;
|
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herb-tools/formatter",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "0.4.1",
|
|
5
4
|
"license": "MIT",
|
|
6
5
|
"homepage": "https://herb-tools.dev",
|
|
7
6
|
"bugs": "https://github.com/marcoroth/herb/issues/new?title=Package%20%60@herb-tools/formatter%60:%20",
|
|
@@ -15,7 +14,7 @@
|
|
|
15
14
|
"require": "./dist/index.cjs",
|
|
16
15
|
"types": "./dist/types/index.d.ts",
|
|
17
16
|
"bin": {
|
|
18
|
-
"herb-
|
|
17
|
+
"herb-format": "bin/herb-format"
|
|
19
18
|
},
|
|
20
19
|
"scripts": {
|
|
21
20
|
"build": "yarn clean && rollup -c rollup.config.mjs",
|
|
@@ -35,7 +34,8 @@
|
|
|
35
34
|
}
|
|
36
35
|
},
|
|
37
36
|
"dependencies": {
|
|
38
|
-
"@herb-tools/core": "0.4.
|
|
37
|
+
"@herb-tools/core": "0.4.1",
|
|
38
|
+
"glob": "^11.0.3"
|
|
39
39
|
},
|
|
40
40
|
"files": [
|
|
41
41
|
"package.json",
|
package/src/cli.ts
CHANGED
|
@@ -1,30 +1,45 @@
|
|
|
1
|
-
import
|
|
1
|
+
import dedent from "dedent"
|
|
2
|
+
import { readFileSync, writeFileSync, statSync } from "fs"
|
|
3
|
+
import { glob } from "glob"
|
|
4
|
+
import { join, resolve } from "path"
|
|
5
|
+
|
|
2
6
|
import { Herb } from "@herb-tools/node-wasm"
|
|
3
7
|
import { Formatter } from "./formatter.js"
|
|
8
|
+
|
|
4
9
|
import { name, version } from "../package.json"
|
|
5
10
|
|
|
11
|
+
const pluralize = (count: number, singular: string, plural: string = singular + 's'): string => {
|
|
12
|
+
return count === 1 ? singular : plural
|
|
13
|
+
}
|
|
14
|
+
|
|
6
15
|
export class CLI {
|
|
7
|
-
private usage = `
|
|
8
|
-
|
|
16
|
+
private usage = dedent`
|
|
17
|
+
Usage: herb-format [file|directory] [options]
|
|
9
18
|
|
|
10
|
-
|
|
11
|
-
|
|
19
|
+
Arguments:
|
|
20
|
+
file|directory File to format, directory to format all **/*.html.erb files within,
|
|
21
|
+
or '-' for stdin (omit to format all **/*.html.erb files in current directory)
|
|
12
22
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
23
|
+
Options:
|
|
24
|
+
-c, --check check if files are formatted without modifying them
|
|
25
|
+
-h, --help show help
|
|
26
|
+
-v, --version show version
|
|
16
27
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
Examples:
|
|
29
|
+
herb-format # Format all **/*.html.erb files in current directory
|
|
30
|
+
herb-format --check # Check if all **/*.html.erb files are formatted
|
|
31
|
+
herb-format templates/index.html.erb # Format and write single file
|
|
32
|
+
herb-format --check templates/ # Check if all **/*.html.erb files in templates/ are formatted
|
|
33
|
+
cat template.html.erb | herb-format # Format from stdin to stdout
|
|
34
|
+
herb-format - < template.html.erb # Format from stdin to stdout
|
|
35
|
+
`
|
|
22
36
|
|
|
23
37
|
async run() {
|
|
24
38
|
const args = process.argv.slice(2)
|
|
25
39
|
|
|
26
40
|
if (args.includes("--help") || args.includes("-h")) {
|
|
27
41
|
console.log(this.usage)
|
|
42
|
+
|
|
28
43
|
process.exit(0)
|
|
29
44
|
}
|
|
30
45
|
|
|
@@ -34,26 +49,158 @@ export class CLI {
|
|
|
34
49
|
if (args.includes("--version") || args.includes("-v")) {
|
|
35
50
|
console.log("Versions:")
|
|
36
51
|
console.log(` ${name}@${version}, ${Herb.version}`.split(", ").join("\n "))
|
|
52
|
+
|
|
37
53
|
process.exit(0)
|
|
38
54
|
}
|
|
39
55
|
|
|
40
|
-
|
|
56
|
+
console.log("⚠️ Experimental Preview: The formatter is in early development. Please report any unexpected behavior or bugs to https://github.com/marcoroth/herb/issues")
|
|
57
|
+
console.log()
|
|
58
|
+
|
|
59
|
+
const formatter = new Formatter(Herb)
|
|
60
|
+
const isCheckMode = args.includes("--check") || args.includes("-c")
|
|
41
61
|
|
|
42
|
-
// Find the first non-flag argument (the file)
|
|
43
62
|
const file = args.find(arg => !arg.startsWith("-"))
|
|
44
63
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
64
|
+
if (!file && !process.stdin.isTTY) {
|
|
65
|
+
if (isCheckMode) {
|
|
66
|
+
console.error("Error: --check mode is not supported with stdin")
|
|
67
|
+
|
|
68
|
+
process.exit(1)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const source = await this.readStdin()
|
|
72
|
+
const result = formatter.format(source)
|
|
73
|
+
const output = result.endsWith('\n') ? result : result + '\n'
|
|
74
|
+
|
|
75
|
+
process.stdout.write(output)
|
|
76
|
+
} else if (file === "-") {
|
|
77
|
+
if (isCheckMode) {
|
|
78
|
+
console.error("Error: --check mode is not supported with stdin")
|
|
79
|
+
|
|
80
|
+
process.exit(1)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const source = await this.readStdin()
|
|
84
|
+
const result = formatter.format(source)
|
|
85
|
+
const output = result.endsWith('\n') ? result : result + '\n'
|
|
86
|
+
|
|
87
|
+
process.stdout.write(output)
|
|
88
|
+
} else if (file) {
|
|
89
|
+
try {
|
|
90
|
+
const stats = statSync(file)
|
|
91
|
+
|
|
92
|
+
if (stats.isDirectory()) {
|
|
93
|
+
const pattern = join(file, "**/*.html.erb")
|
|
94
|
+
const files = await glob(pattern)
|
|
95
|
+
|
|
96
|
+
if (files.length === 0) {
|
|
97
|
+
console.log(`No files found matching pattern: ${resolve(pattern)}`)
|
|
98
|
+
process.exit(0)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let formattedCount = 0
|
|
102
|
+
let unformattedFiles: string[] = []
|
|
103
|
+
|
|
104
|
+
for (const filePath of files) {
|
|
105
|
+
try {
|
|
106
|
+
const source = readFileSync(filePath, "utf-8")
|
|
107
|
+
const result = formatter.format(source)
|
|
108
|
+
if (result !== source) {
|
|
109
|
+
if (isCheckMode) {
|
|
110
|
+
unformattedFiles.push(filePath)
|
|
111
|
+
} else {
|
|
112
|
+
writeFileSync(filePath, result, "utf-8")
|
|
113
|
+
console.log(`Formatted: ${filePath}`)
|
|
114
|
+
}
|
|
115
|
+
formattedCount++
|
|
116
|
+
}
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.error(`Error formatting ${filePath}:`, error)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (isCheckMode) {
|
|
123
|
+
if (unformattedFiles.length > 0) {
|
|
124
|
+
console.log(`\nThe following ${pluralize(unformattedFiles.length, 'file is', 'files are')} not formatted:`)
|
|
125
|
+
unformattedFiles.forEach(file => console.log(` ${file}`))
|
|
126
|
+
console.log(`\nChecked ${files.length} ${pluralize(files.length, 'file')}, found ${unformattedFiles.length} unformatted ${pluralize(unformattedFiles.length, 'file')}`)
|
|
127
|
+
process.exit(1)
|
|
128
|
+
} else {
|
|
129
|
+
console.log(`\nChecked ${files.length} ${pluralize(files.length, 'file')}, all files are properly formatted`)
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
console.log(`\nChecked ${files.length} ${pluralize(files.length, 'file')}, formatted ${formattedCount} ${pluralize(formattedCount, 'file')}`)
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
const source = readFileSync(file, "utf-8")
|
|
136
|
+
const result = formatter.format(source)
|
|
137
|
+
|
|
138
|
+
if (result !== source) {
|
|
139
|
+
if (isCheckMode) {
|
|
140
|
+
console.log(`File is not formatted: ${file}`)
|
|
141
|
+
process.exit(1)
|
|
142
|
+
} else {
|
|
143
|
+
writeFileSync(file, result, "utf-8")
|
|
144
|
+
console.log(`Formatted: ${file}`)
|
|
145
|
+
}
|
|
146
|
+
} else if (isCheckMode) {
|
|
147
|
+
console.log(`File is properly formatted: ${file}`)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error(`Error: Cannot access '${file}':`, error)
|
|
153
|
+
|
|
154
|
+
process.exit(1)
|
|
155
|
+
}
|
|
48
156
|
} else {
|
|
49
|
-
|
|
50
|
-
}
|
|
157
|
+
const files = await glob("**/*.html.erb")
|
|
51
158
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
159
|
+
if (files.length === 0) {
|
|
160
|
+
console.log(`No files found matching pattern: ${resolve("**/*.html.erb")}`)
|
|
161
|
+
|
|
162
|
+
process.exit(0)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
let formattedCount = 0
|
|
166
|
+
let unformattedFiles: string[] = []
|
|
167
|
+
|
|
168
|
+
for (const filePath of files) {
|
|
169
|
+
try {
|
|
170
|
+
const source = readFileSync(filePath, "utf-8")
|
|
171
|
+
const result = formatter.format(source)
|
|
172
|
+
|
|
173
|
+
if (result !== source) {
|
|
174
|
+
if (isCheckMode) {
|
|
175
|
+
unformattedFiles.push(filePath)
|
|
176
|
+
} else {
|
|
177
|
+
writeFileSync(filePath, result, "utf-8")
|
|
178
|
+
console.log(`Formatted: ${filePath}`)
|
|
179
|
+
}
|
|
180
|
+
formattedCount++
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.error(`Error formatting ${filePath}:`, error)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (isCheckMode) {
|
|
188
|
+
if (unformattedFiles.length > 0) {
|
|
189
|
+
console.log(`\nThe following ${pluralize(unformattedFiles.length, 'file is', 'files are')} not formatted:`)
|
|
190
|
+
unformattedFiles.forEach(file => console.log(` ${file}`))
|
|
191
|
+
console.log(`\nChecked ${files.length} ${pluralize(files.length, 'file')}, found ${unformattedFiles.length} unformatted ${pluralize(unformattedFiles.length, 'file')}`)
|
|
192
|
+
|
|
193
|
+
process.exit(1)
|
|
194
|
+
} else {
|
|
195
|
+
console.log(`\nChecked ${files.length} ${pluralize(files.length, 'file')}, all files are properly formatted`)
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
console.log(`\nChecked ${files.length} ${pluralize(files.length, 'file')}, formatted ${formattedCount} ${pluralize(formattedCount, 'file')}`)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
55
201
|
} catch (error) {
|
|
56
202
|
console.error(error)
|
|
203
|
+
|
|
57
204
|
process.exit(1)
|
|
58
205
|
}
|
|
59
206
|
}
|
package/src/printer.ts
CHANGED
|
@@ -67,6 +67,7 @@ export class Printer extends Visitor {
|
|
|
67
67
|
private source: string
|
|
68
68
|
private lines: string[] = []
|
|
69
69
|
private indentLevel: number = 0
|
|
70
|
+
private inlineMode: boolean = false
|
|
70
71
|
|
|
71
72
|
constructor(source: string, options: Required<FormatOptions>) {
|
|
72
73
|
super()
|
|
@@ -143,6 +144,11 @@ export class Printer extends Visitor {
|
|
|
143
144
|
const attributes = open.children.filter((child): child is HTMLAttributeNode =>
|
|
144
145
|
child instanceof HTMLAttributeNode || (child as any).type === 'AST_HTML_ATTRIBUTE_NODE'
|
|
145
146
|
)
|
|
147
|
+
const inlineNodes = open.children.filter(child =>
|
|
148
|
+
!(child instanceof HTMLAttributeNode || (child as any).type === 'AST_HTML_ATTRIBUTE_NODE') &&
|
|
149
|
+
!(child instanceof WhitespaceNode || (child as any).type === 'AST_WHITESPACE_NODE')
|
|
150
|
+
)
|
|
151
|
+
|
|
146
152
|
const children = node.body.filter(
|
|
147
153
|
child =>
|
|
148
154
|
!(child instanceof WhitespaceNode || (child as any).type === 'AST_WHITESPACE_NODE') &&
|
|
@@ -158,7 +164,7 @@ export class Printer extends Visitor {
|
|
|
158
164
|
return
|
|
159
165
|
}
|
|
160
166
|
|
|
161
|
-
if (attributes.length === 0) {
|
|
167
|
+
if (attributes.length === 0 && inlineNodes.length === 0) {
|
|
162
168
|
if (children.length === 0) {
|
|
163
169
|
if (isSelfClosing) {
|
|
164
170
|
this.push(indent + `<${tagName} />`)
|
|
@@ -167,6 +173,7 @@ export class Printer extends Visitor {
|
|
|
167
173
|
} else {
|
|
168
174
|
this.push(indent + `<${tagName}></${tagName}>`)
|
|
169
175
|
}
|
|
176
|
+
|
|
170
177
|
return
|
|
171
178
|
}
|
|
172
179
|
|
|
@@ -183,16 +190,41 @@ export class Printer extends Visitor {
|
|
|
183
190
|
return
|
|
184
191
|
}
|
|
185
192
|
|
|
186
|
-
|
|
193
|
+
if (attributes.length === 0 && inlineNodes.length > 0) {
|
|
194
|
+
const inline = this.renderInlineOpen(tagName, [], isSelfClosing, inlineNodes, open.children)
|
|
195
|
+
|
|
196
|
+
if (children.length === 0) {
|
|
197
|
+
if (isSelfClosing || node.is_void) {
|
|
198
|
+
this.push(indent + inline)
|
|
199
|
+
} else {
|
|
200
|
+
this.push(indent + inline + `</${tagName}>`)
|
|
201
|
+
}
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
this.push(indent + inline)
|
|
206
|
+
this.withIndent(() => {
|
|
207
|
+
children.forEach(child => this.visit(child))
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
if (!node.is_void && !isSelfClosing) {
|
|
211
|
+
this.push(indent + `</${tagName}>`)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const inline = this.renderInlineOpen(tagName, attributes, isSelfClosing, inlineNodes, open.children)
|
|
187
218
|
const singleAttribute = attributes[0]
|
|
188
219
|
const hasEmptyValue =
|
|
189
220
|
singleAttribute &&
|
|
190
221
|
(singleAttribute.value instanceof HTMLAttributeValueNode || (singleAttribute.value as any)?.type === 'AST_HTML_ATTRIBUTE_VALUE_NODE') &&
|
|
191
222
|
(singleAttribute.value as any)?.children.length === 0
|
|
192
223
|
|
|
193
|
-
const shouldKeepInline = attributes.length <= 3 &&
|
|
224
|
+
const shouldKeepInline = (attributes.length <= 3 &&
|
|
194
225
|
!hasEmptyValue &&
|
|
195
|
-
inline.length + indent.length <= this.maxLineLength
|
|
226
|
+
inline.length + indent.length <= this.maxLineLength) ||
|
|
227
|
+
inlineNodes.length > 0
|
|
196
228
|
|
|
197
229
|
if (shouldKeepInline) {
|
|
198
230
|
if (children.length === 0) {
|
|
@@ -223,27 +255,38 @@ export class Printer extends Visitor {
|
|
|
223
255
|
return
|
|
224
256
|
}
|
|
225
257
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
attributes.forEach(attribute => {
|
|
229
|
-
this.push(this.indent() + this.renderAttribute(attribute))
|
|
230
|
-
})
|
|
231
|
-
})
|
|
258
|
+
if (inlineNodes.length > 0) {
|
|
259
|
+
this.push(indent + this.renderInlineOpen(tagName, attributes, isSelfClosing, inlineNodes, open.children))
|
|
232
260
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
261
|
+
if (!isSelfClosing && !node.is_void && children.length > 0) {
|
|
262
|
+
this.withIndent(() => {
|
|
263
|
+
children.forEach(child => this.visit(child))
|
|
264
|
+
})
|
|
265
|
+
this.push(indent + `</${tagName}>`)
|
|
266
|
+
}
|
|
239
267
|
} else {
|
|
240
|
-
this.push(indent +
|
|
241
|
-
|
|
268
|
+
this.push(indent + `<${tagName}`)
|
|
242
269
|
this.withIndent(() => {
|
|
243
|
-
|
|
270
|
+
attributes.forEach(attribute => {
|
|
271
|
+
this.push(this.indent() + this.renderAttribute(attribute))
|
|
272
|
+
})
|
|
244
273
|
})
|
|
245
274
|
|
|
246
|
-
|
|
275
|
+
if (isSelfClosing) {
|
|
276
|
+
this.push(indent + "/>")
|
|
277
|
+
} else if (node.is_void) {
|
|
278
|
+
this.push(indent + ">")
|
|
279
|
+
} else if (children.length === 0) {
|
|
280
|
+
this.push(indent + ">" + `</${tagName}>`)
|
|
281
|
+
} else {
|
|
282
|
+
this.push(indent + ">")
|
|
283
|
+
|
|
284
|
+
this.withIndent(() => {
|
|
285
|
+
children.forEach(child => this.visit(child))
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
this.push(indent + `</${tagName}>`)
|
|
289
|
+
}
|
|
247
290
|
}
|
|
248
291
|
}
|
|
249
292
|
|
|
@@ -492,18 +535,41 @@ export class Printer extends Visitor {
|
|
|
492
535
|
}
|
|
493
536
|
|
|
494
537
|
visitERBIfNode(node: ERBIfNode): void {
|
|
495
|
-
this.
|
|
538
|
+
if (this.inlineMode) {
|
|
539
|
+
const open = node.tag_opening?.value ?? ""
|
|
540
|
+
const content = node.content?.value ?? ""
|
|
541
|
+
const close = node.tag_closing?.value ?? ""
|
|
542
|
+
this.lines.push(open + content + close)
|
|
543
|
+
|
|
544
|
+
node.statements.forEach(child => {
|
|
545
|
+
if (child instanceof HTMLAttributeNode || (child as any).type === 'AST_HTML_ATTRIBUTE_NODE') {
|
|
546
|
+
this.lines.push(" " + this.renderAttribute(child as HTMLAttributeNode) + " ")
|
|
547
|
+
} else {
|
|
548
|
+
this.visit(child)
|
|
549
|
+
}
|
|
550
|
+
})
|
|
496
551
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
552
|
+
if (node.end_node) {
|
|
553
|
+
const endNode = node.end_node as any
|
|
554
|
+
const endOpen = endNode.tag_opening?.value ?? ""
|
|
555
|
+
const endContent = endNode.content?.value ?? ""
|
|
556
|
+
const endClose = endNode.tag_closing?.value ?? ""
|
|
557
|
+
this.lines.push(endOpen + endContent + endClose)
|
|
558
|
+
}
|
|
559
|
+
} else {
|
|
560
|
+
this.printERBNode(node)
|
|
500
561
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
562
|
+
this.withIndent(() => {
|
|
563
|
+
node.statements.forEach(child => this.visit(child))
|
|
564
|
+
})
|
|
504
565
|
|
|
505
|
-
|
|
506
|
-
|
|
566
|
+
if (node.subsequent) {
|
|
567
|
+
this.visit(node.subsequent)
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (node.end_node) {
|
|
571
|
+
this.printERBNode(node.end_node as any)
|
|
572
|
+
}
|
|
507
573
|
}
|
|
508
574
|
}
|
|
509
575
|
|
|
@@ -601,15 +667,73 @@ export class Printer extends Visitor {
|
|
|
601
667
|
|
|
602
668
|
// --- Utility methods ---
|
|
603
669
|
|
|
604
|
-
private renderInlineOpen(name: string, attributes: HTMLAttributeNode[], selfClose: boolean): string {
|
|
670
|
+
private renderInlineOpen(name: string, attributes: HTMLAttributeNode[], selfClose: boolean, inlineNodes: Node[] = [], allChildren: Node[] = []): string {
|
|
605
671
|
const parts = attributes.map(attribute => this.renderAttribute(attribute))
|
|
606
672
|
|
|
673
|
+
if (inlineNodes.length > 0) {
|
|
674
|
+
let result = `<${name}`
|
|
675
|
+
|
|
676
|
+
if (allChildren.length > 0) {
|
|
677
|
+
const currentIndentLevel = this.indentLevel
|
|
678
|
+
this.indentLevel = 0
|
|
679
|
+
const tempLines = this.lines
|
|
680
|
+
this.lines = []
|
|
681
|
+
|
|
682
|
+
allChildren.forEach(child => {
|
|
683
|
+
if (child instanceof HTMLAttributeNode || (child as any).type === 'AST_HTML_ATTRIBUTE_NODE') {
|
|
684
|
+
this.lines.push(" " + this.renderAttribute(child as HTMLAttributeNode))
|
|
685
|
+
} else if (!(child instanceof WhitespaceNode || (child as any).type === 'AST_WHITESPACE_NODE')) {
|
|
686
|
+
const wasInlineMode = this.inlineMode
|
|
687
|
+
this.inlineMode = true
|
|
688
|
+
|
|
689
|
+
this.lines.push(" ")
|
|
690
|
+
|
|
691
|
+
this.visit(child)
|
|
692
|
+
this.inlineMode = wasInlineMode
|
|
693
|
+
}
|
|
694
|
+
})
|
|
695
|
+
|
|
696
|
+
const inlineContent = this.lines.join("")
|
|
697
|
+
this.lines = tempLines
|
|
698
|
+
this.indentLevel = currentIndentLevel
|
|
699
|
+
|
|
700
|
+
result += inlineContent
|
|
701
|
+
} else {
|
|
702
|
+
if (parts.length > 0) {
|
|
703
|
+
result += ` ${parts.join(" ")}`
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
const currentIndentLevel = this.indentLevel
|
|
707
|
+
this.indentLevel = 0
|
|
708
|
+
const tempLines = this.lines
|
|
709
|
+
this.lines = []
|
|
710
|
+
|
|
711
|
+
inlineNodes.forEach(node => {
|
|
712
|
+
const wasInlineMode = this.inlineMode
|
|
713
|
+
this.inlineMode = true
|
|
714
|
+
this.visit(node)
|
|
715
|
+
this.inlineMode = wasInlineMode
|
|
716
|
+
})
|
|
717
|
+
|
|
718
|
+
const inlineContent = this.lines.join("")
|
|
719
|
+
this.lines = tempLines
|
|
720
|
+
this.indentLevel = currentIndentLevel
|
|
721
|
+
|
|
722
|
+
result += inlineContent
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
result += selfClose ? " />" : ">"
|
|
726
|
+
|
|
727
|
+
return result
|
|
728
|
+
}
|
|
729
|
+
|
|
607
730
|
return `<${name}${parts.length ? " " + parts.join(" ") : ""}${selfClose ? " /" : ""}>`
|
|
608
731
|
}
|
|
609
732
|
|
|
610
733
|
renderAttribute(attribute: HTMLAttributeNode): string {
|
|
611
734
|
const name = (attribute.name as HTMLAttributeNameNode)!.name!.value ?? ""
|
|
612
735
|
const equals = attribute.equals?.value ?? ""
|
|
736
|
+
|
|
613
737
|
let value = ""
|
|
614
738
|
|
|
615
739
|
if (attribute.value && (attribute.value instanceof HTMLAttributeValueNode || (attribute.value as any)?.type === 'AST_HTML_ATTRIBUTE_VALUE_NODE')) {
|
|
@@ -617,13 +741,15 @@ export class Printer extends Visitor {
|
|
|
617
741
|
const open_quote = (attrValue.open_quote?.value ?? "")
|
|
618
742
|
const close_quote = (attrValue.close_quote?.value ?? "")
|
|
619
743
|
const attribute_value = attrValue.children.map((attr: any) => {
|
|
620
|
-
if (attr instanceof HTMLTextNode || (attr as any).type === 'AST_HTML_TEXT_NODE' ||
|
|
621
|
-
|
|
744
|
+
if (attr instanceof HTMLTextNode || (attr as any).type === 'AST_HTML_TEXT_NODE' || attr instanceof LiteralNode || (attr as any).type === 'AST_LITERAL_NODE') {
|
|
745
|
+
|
|
622
746
|
return (attr as HTMLTextNode | LiteralNode).content
|
|
623
747
|
} else if (attr instanceof ERBContentNode || (attr as any).type === 'AST_ERB_CONTENT_NODE') {
|
|
624
748
|
const erbAttr = attr as ERBContentNode
|
|
749
|
+
|
|
625
750
|
return (erbAttr.tag_opening!.value + erbAttr.content!.value + erbAttr.tag_closing!.value)
|
|
626
751
|
}
|
|
752
|
+
|
|
627
753
|
return ""
|
|
628
754
|
}).join("")
|
|
629
755
|
|
package/bin/herb-formatter
DELETED