Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 69 additions & 12 deletions javascript/packages/formatter/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { join, resolve } from "path"

import { Herb } from "@herb-tools/node-wasm"
import { Formatter } from "./formatter.js"
import { parseArgs } from "util"
import { resolveFormatOptions } from "./options.js"

import { name, version, dependencies } from "../package.json"

Expand All @@ -21,9 +23,11 @@ export class CLI {
glob pattern to match files, or '-' for stdin (omit to format all **/*.html.erb files in current directory)

Options:
-c, --check check if files are formatted without modifying them
-h, --help show help
-v, --version show version
-c, --check check if files are formatted without modifying them
-h, --help show help
-v, --version show version
--indent-width <number> number of spaces per indentation level (default: 2)
--max-line-length <number> maximum line length before wrapping (default: 80)

Examples:
herb-format # Format all **/*.html.erb files in current directory
Expand All @@ -35,22 +39,71 @@ export class CLI {
herb-format "**/*.xml.erb" # Format all .xml.erb files using glob pattern
herb-format --check # Check if all **/*.html.erb files are formatted
herb-format --check templates/ # Check if all **/*.html.erb files in templates/ are formatted
herb-format --indent-width 4 # Format with 4-space indentation
herb-format --max-line-length 100 # Format with 100-character line limit
cat template.html.erb | herb-format # Format from stdin to stdout
`

async run() {
const args = process.argv.slice(2)

if (args.includes("--help") || args.includes("-h")) {
private parseArguments() {
const { values, positionals } = parseArgs({
args: process.argv.slice(2),
options: {
help: { type: "boolean", short: "h" },
version: { type: "boolean", short: "v" },
check: { type: "boolean", short: "c" },
"indent-width": { type: "string" },
"max-line-length": { type: "string" }
},
allowPositionals: true
})

if (values.help) {
console.log(this.usage)

process.exit(0)
}

let indentWidth: number | undefined

if (values["indent-width"]) {
const parsed = parseInt(values["indent-width"], 10)
if (isNaN(parsed) || parsed < 1) {
console.error(
`Invalid indent-width: ${values["indent-width"]}. Must be a positive integer.`,
)
process.exit(1)
}
indentWidth = parsed
}

let maxLineLength: number | undefined

if (values["max-line-length"]) {
const parsed = parseInt(values["max-line-length"], 10)
if (isNaN(parsed) || parsed < 1) {
console.error(
`Invalid max-line-length: ${values["max-line-length"]}. Must be a positive integer.`,
)
process.exit(1)
}
maxLineLength = parsed
}

return {
positionals,
isCheckMode: values.check,
isVersionMode: values.version,
indentWidth,
maxLineLength
}
}

async run() {
const { positionals, isCheckMode, isVersionMode, indentWidth, maxLineLength } = this.parseArguments()

try {
await Herb.load()

if (args.includes("--version") || args.includes("-v")) {
if (isVersionMode) {
console.log("Versions:")
console.log(` ${name}@${version}`)
console.log(` @herb-tools/printer@${dependencies['@herb-tools/printer']}`)
Expand All @@ -62,10 +115,14 @@ export class CLI {
console.log("⚠️ Experimental Preview: The formatter is in early development. Please report any unexpected behavior or bugs to https://github.com/marcoroth/herb/issues/new?template=formatting-issue.md")
console.log()

const formatter = new Formatter(Herb)
const isCheckMode = args.includes("--check") || args.includes("-c")
const formatOptions = resolveFormatOptions({
indentWidth,
maxLineLength
})

const formatter = new Formatter(Herb, formatOptions)

const file = args.find(arg => !arg.startsWith("-"))
const file = positionals[0]

if (!file && !process.stdin.isTTY) {
if (isCheckMode) {
Expand Down
33 changes: 33 additions & 0 deletions javascript/packages/formatter/test/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,39 @@ describe("CLI Binary", () => {
expect(formattedLines.join('\n')).toBe('\n')
})

it("should show --indent-width option in help", async () => {
const result = await execBinary(["--help"])

expectExitCode(result, 0)
expect(result.stdout).toContain("herb-format --indent-width")
expect(result.stdout).toContain("number of spaces per indentation level")
})

it("should accept valid --indent-width", async () => {
const input = '<div>\n<p>Test</p>\n</div>'
const result = await execBinary(["--indent-width", "4"], input)

expectExitCode(result, 0)
expect(result.stdout).toContain(" <p>Test</p>") // 4 spaces instead of 2
})

it("should show --max-line-length option in help", async () => {
const result = await execBinary(["--help"])

expectExitCode(result, 0)
expect(result.stdout).toContain("herb-format --max-line-length")
expect(result.stdout).toContain("maximum line length before wrapping")
})

it("should accept valid --max-line-length", async () => {
const input = '<p>Short text that wraps</p>'
const result = await execBinary(["--max-line-length", "20"], input)

expectExitCode(result, 0)
expect(result.stdout).toContain("Short text that")
expect(result.stdout).toContain("wraps")
})

describe("Glob Pattern Support", () => {
beforeEach(async () => {
await mkdir("test-fixtures", { recursive: true })
Expand Down
Loading