@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/types/printer.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export declare class Printer extends Visitor {
|
|
|
11
11
|
private source;
|
|
12
12
|
private lines;
|
|
13
13
|
private indentLevel;
|
|
14
|
+
private inlineMode;
|
|
15
|
+
private isInComplexNesting;
|
|
14
16
|
constructor(source: string, options: Required<FormatOptions>);
|
|
15
17
|
print(object: Node | Token, indentLevel?: number): string;
|
|
16
18
|
private push;
|
|
@@ -52,4 +54,17 @@ export declare class Printer extends Visitor {
|
|
|
52
54
|
private visitERBGeneric;
|
|
53
55
|
private renderInlineOpen;
|
|
54
56
|
renderAttribute(attribute: HTMLAttributeNode): string;
|
|
57
|
+
/**
|
|
58
|
+
* Try to render children inline if they are simple enough.
|
|
59
|
+
* Returns the inline string if possible, null otherwise.
|
|
60
|
+
*/
|
|
61
|
+
private tryRenderInline;
|
|
62
|
+
/**
|
|
63
|
+
* Calculate the maximum nesting depth in a subtree of nodes.
|
|
64
|
+
*/
|
|
65
|
+
private getMaxNestingDepth;
|
|
66
|
+
/**
|
|
67
|
+
* Render an HTML element's content inline (without the wrapping tags).
|
|
68
|
+
*/
|
|
69
|
+
private renderElementInline;
|
|
55
70
|
}
|
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.2",
|
|
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.2",
|
|
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,163 @@ 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
|
+
const output = result.endsWith('\n') ? result : result + '\n'
|
|
109
|
+
|
|
110
|
+
if (output !== source) {
|
|
111
|
+
if (isCheckMode) {
|
|
112
|
+
unformattedFiles.push(filePath)
|
|
113
|
+
} else {
|
|
114
|
+
writeFileSync(filePath, output, "utf-8")
|
|
115
|
+
console.log(`Formatted: ${filePath}`)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
formattedCount++
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error(`Error formatting ${filePath}:`, error)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (isCheckMode) {
|
|
126
|
+
if (unformattedFiles.length > 0) {
|
|
127
|
+
console.log(`\nThe following ${pluralize(unformattedFiles.length, 'file is', 'files are')} not formatted:`)
|
|
128
|
+
unformattedFiles.forEach(file => console.log(` ${file}`))
|
|
129
|
+
console.log(`\nChecked ${files.length} ${pluralize(files.length, 'file')}, found ${unformattedFiles.length} unformatted ${pluralize(unformattedFiles.length, 'file')}`)
|
|
130
|
+
process.exit(1)
|
|
131
|
+
} else {
|
|
132
|
+
console.log(`\nChecked ${files.length} ${pluralize(files.length, 'file')}, all files are properly formatted`)
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
console.log(`\nChecked ${files.length} ${pluralize(files.length, 'file')}, formatted ${formattedCount} ${pluralize(formattedCount, 'file')}`)
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
const source = readFileSync(file, "utf-8")
|
|
139
|
+
const result = formatter.format(source)
|
|
140
|
+
const output = result.endsWith('\n') ? result : result + '\n'
|
|
141
|
+
|
|
142
|
+
if (output !== source) {
|
|
143
|
+
if (isCheckMode) {
|
|
144
|
+
console.log(`File is not formatted: ${file}`)
|
|
145
|
+
process.exit(1)
|
|
146
|
+
} else {
|
|
147
|
+
writeFileSync(file, output, "utf-8")
|
|
148
|
+
console.log(`Formatted: ${file}`)
|
|
149
|
+
}
|
|
150
|
+
} else if (isCheckMode) {
|
|
151
|
+
console.log(`File is properly formatted: ${file}`)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error(`Error: Cannot access '${file}':`, error)
|
|
157
|
+
|
|
158
|
+
process.exit(1)
|
|
159
|
+
}
|
|
48
160
|
} else {
|
|
49
|
-
|
|
50
|
-
}
|
|
161
|
+
const files = await glob("**/*.html.erb")
|
|
51
162
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
163
|
+
if (files.length === 0) {
|
|
164
|
+
console.log(`No files found matching pattern: ${resolve("**/*.html.erb")}`)
|
|
165
|
+
|
|
166
|
+
process.exit(0)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
let formattedCount = 0
|
|
170
|
+
let unformattedFiles: string[] = []
|
|
171
|
+
|
|
172
|
+
for (const filePath of files) {
|
|
173
|
+
try {
|
|
174
|
+
const source = readFileSync(filePath, "utf-8")
|
|
175
|
+
const result = formatter.format(source)
|
|
176
|
+
const output = result.endsWith('\n') ? result : result + '\n'
|
|
177
|
+
|
|
178
|
+
if (output !== source) {
|
|
179
|
+
if (isCheckMode) {
|
|
180
|
+
unformattedFiles.push(filePath)
|
|
181
|
+
} else {
|
|
182
|
+
writeFileSync(filePath, output, "utf-8")
|
|
183
|
+
console.log(`Formatted: ${filePath}`)
|
|
184
|
+
}
|
|
185
|
+
formattedCount++
|
|
186
|
+
}
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.error(`Error formatting ${filePath}:`, error)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (isCheckMode) {
|
|
193
|
+
if (unformattedFiles.length > 0) {
|
|
194
|
+
console.log(`\nThe following ${pluralize(unformattedFiles.length, 'file is', 'files are')} not formatted:`)
|
|
195
|
+
unformattedFiles.forEach(file => console.log(` ${file}`))
|
|
196
|
+
console.log(`\nChecked ${files.length} ${pluralize(files.length, 'file')}, found ${unformattedFiles.length} unformatted ${pluralize(unformattedFiles.length, 'file')}`)
|
|
197
|
+
|
|
198
|
+
process.exit(1)
|
|
199
|
+
} else {
|
|
200
|
+
console.log(`\nChecked ${files.length} ${pluralize(files.length, 'file')}, all files are properly formatted`)
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
console.log(`\nChecked ${files.length} ${pluralize(files.length, 'file')}, formatted ${formattedCount} ${pluralize(formattedCount, 'file')}`)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
55
206
|
} catch (error) {
|
|
56
207
|
console.error(error)
|
|
208
|
+
|
|
57
209
|
process.exit(1)
|
|
58
210
|
}
|
|
59
211
|
}
|