Herb Linter
Package: @herb-tools/linter
The Herb Linter provides comprehensive HTML+ERB validation with a set of configurable rules to enforce best practices and catch common errors.
Installation
Global Installation
npm install -g @herb-tools/linterpnpm add -g @herb-tools/linteryarn global add @herb-tools/linterbun add -g @herb-tools/linterOne-time Usage
For occasional use without installing:
npx @herb-tools/linter template.html.erbProject Installation
npm add -D @herb-tools/linterpnpm add -D @herb-tools/linteryarn add -D @herb-tools/linterbun add -D @herb-tools/linterAfter installing as a dev dependency, initialize the configuration:
npx herb-lint --initpnpm herb-lint --inityarn herb-lint --initbunx herb-lint --initThen add a lint NPM script to your package.json:
{
"scripts": {
"herb:lint": "herb-lint"
}
}Then run the scripts:
npm run herb:lintpnpm herb:lintyarn herb:lintbun run herb:lintUsage
Command Line
Basic usage:
npx @herb-tools/linter
npx @herb-tools/linter app/views/
npx @herb-tools/linter template.html.erb
npx @herb-tools/linter "**/*.rhtml"
npx @herb-tools/linter "**/*.xml.erb"Initialize configuration:
# Create a .herb.yml configuration file
npx @herb-tools/linter --initOptions
Output Format:
# Use detailed output (default)
npx @herb-tools/linter template.html.erb --format detailed
# Use simple output format
npx @herb-tools/linter template.html.erb --simple
# or
npx @herb-tools/linter template.html.erb --format simple
# Use JSON output format
npx @herb-tools/linter template.html.erb --json
# or
npx @herb-tools/linter template.html.erb --format json
# Use GitHub Actions output format with detailed preview (default)
# (This is enabled automatically when the GITHUB_ACTIONS environment variable is set)
npx @herb-tools/linter template.html.erb --github
# Combine GitHub Actions output with simple format
npx @herb-tools/linter template.html.erb --format=simple --github
# Combine GitHub Actions output with detailed format (explicit)
npx @herb-tools/linter template.html.erb --format=detailed --githubDisplay Options:
# Disable colored output
npx @herb-tools/linter template.html.erb --no-color
# Set syntax highlighting theme
npx @herb-tools/linter template.html.erb --theme github-dark
# Disable timing information
npx @herb-tools/linter template.html.erb --no-timing
# Disable line wrapping
npx @herb-tools/linter template.html.erb --no-wrap-lines
# Enable line truncation (mutually exclusive with line wrapping)
npx @herb-tools/linter template.html.erb --truncate-lines --no-wrap-lines
# Combine GitHub Actions annotations with different formats
npx @herb-tools/linter template.html.erb --format=simple --github
# Disable GitHub Actions annotations (even in GitHub Actions environment)
npx @herb-tools/linter template.html.erb --no-githubHelp and Version:
# Show help
npx @herb-tools/linter --help
# Show version information
npx @herb-tools/linter --versionGitHub Actions Output Format
The linter supports GitHub Actions annotation format with the --github flag, which can be combined with --format=simple or --format=detailed. The --github flag adds GitHub Actions annotations that GitHub can parse to create inline annotations in pull requests, while also showing the regular format output for local debugging.
Tip: Running in GitHub Actions
When the GITHUB_ACTIONS environment variable is set (as in GitHub Actions), GitHub Actions annotations are enabled by default. You can disable them with --no-github if needed.
# GitHub Actions annotations + detailed format (default)
npx @herb-tools/linter --github
# GitHub Actions annotations + simple format (minimal local output)
npx @herb-tools/linter --format=simple --githubExample: --github (GitHub annotations + detailed format)
::error file=template.html.erb,line=3,col=3,title=html-img-require-alt • @herb-tools/linter@0.7.5::Missing required `alt` attribute on `<img>` tag [html-img-require-alt]%0A%0A%0Atemplate.html.erb:3:3%0A%0A 1 │ <div>%0A 2 │ <span>Test content</span>%0A → 3 │ <img src="test.jpg">%0A │ ~~~%0A 4 │ </div>%0A
[error] Missing required `alt` attribute on `<img>` tag [html-img-require-alt]
template.html.erb:3:3
1 │ <div>
2 │ <span>Test content</span>
→ 3 │ <img src="test.jpg">
│ ~~~
4 │ </div>Example: --format=simple --github (GitHub annotations + simple format)
::error file=template.html.erb,line=3,col=3,title=html-img-require-alt • @herb-tools/linter@0.7.5::Missing required `alt` attribute on `<img>` tag [html-img-require-alt]%0A%0A%0Atemplate.html.erb:3:3%0A%0A 1 │ <div>%0A 2 │ <span>Test content</span>%0A → 3 │ <img src="test.jpg">%0A │ ~~~%0A 4 │ </div>%0A
template.html.erb:
3:3 ✗ Missing required `alt` attribute on `<img>` tag [html-img-require-alt]The GitHub Actions annotations include:
- Embedded plain-text code previews (with
%0Afor newlines) that GitHub renders in PR comments titleproperty showing the rule code and linter version for better traceability- Full error context for debugging directly in GitHub's UI
The regular format output provides colorful syntax highlighting for local terminal debugging.
This approach is ideal for CI/CD workflows as it provides both GitHub integration and local debugging:
name: Herb Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Herb Linter
run: npx @herb-tools/linterJSON Output Format
The linter supports structured JSON output with the --json flag, useful for programmatic consumption:
npx @herb-tools/linter template.html.erb --jsonExample output:
{
"offenses": [
{
"filename": "template.html.erb",
"message": "File must end with trailing newline",
"location": {
"start": { "line": 1, "column": 21 },
"end": { "line": 1, "column": 22 }
},
"severity": "error",
"code": "erb-require-trailing-newline",
"source": "Herb Linter"
}
],
"summary": {
"filesChecked": 1,
"filesWithOffenses": 1,
"totalErrors": 1,
"totalWarnings": 0,
"totalOffenses": 1,
"ruleCount": 21
},
"timing": {
"startTime": "2025-08-14T16:00:48.845Z",
"duration": 27
},
"completed": true,
"clean": false,
"message": null
}JSON output fields:
offenses: Array of linting offenses with location and severitysummary: Statistics about the linting run (nullon errors)timing: Timing information with ISO timestamp (nullwith--no-timing)completed: Whether the linter ran successfully on filesclean: Whether there were no offenses (nullwhencompleted=false)message: Error or informational message (nullon success)
Disabling Rules Inline v0.8.0+
You can disable linting rules for specific lines using inline comments. This is useful when you need to allow certain code that would otherwise trigger a linting offense.
Disabling a Single Rule
Add a comment at the end of the line with herb:disable followed by the rule name:
<DIV>test</DIV> <%# herb:disable html-tag-name-lowercase %>Disabling Multiple Rules
You can disable multiple rules on the same line by separating rule names with commas:
<DIV id='test' class="<%= "hello" %>">test</DIV> <%# herb:disable html-tag-name-lowercase, html-attribute-double-quotes %>Disabling All Rules
To disable all linting rules for a specific line, use all:
<DIV id='test' class="<%= "hello" %>">test</DIV> <%# herb:disable all %>Important
Inline disable comments only affect the line they appear on. Each line that needs linting disabled must have its own disable comment.
Configuration
Create a .herb.yml file in your project root to configure the linter:
npx @herb-tools/linter --initBasic Configuration
linter:
enabled: true
# Additional glob patterns to include (additive to defaults)
include:
- '**/*.xml.erb'
# Glob patterns to exclude from linting
exclude:
- 'vendor/**/*'
- 'node_modules/**/*'
rules:
# Disable a rule
erb-no-extra-newline:
enabled: false
# Change rule severity
html-tag-name-lowercase:
severity: warning # error, warning, info, or hintRule-Level File Patterns
Apply rules to specific files using include, only, and exclude patterns:
linter:
rules:
# Apply rule only to component files
html-img-require-alt:
include:
- 'app/components/**/*'
exclude:
- 'app/components/legacy/**/*'
# Restrict rule to specific directory (overrides include)
html-tag-name-lowercase:
only:
- 'app/views/**/*'
exclude:
- 'app/views/admin/**/*'Pattern precedence:
only- When present, rule applies ONLY to these files (ignores allinclude)include- Whenonlyis absent, rule applies only to these files (additive)exclude- Always applied regardless ofincludeoronly
Force Flag
Process files even when excluded or when linter is disabled:
# Force linting when disabled in config
herb-lint --force
# Force linting on an excluded file
herb-lint --force app/views/excluded-file.html.erbWhen using --force on an excluded file, the linter will show a warning but proceed with linting.
Language Server Integration
The linter is automatically integrated into the Herb Language Server, providing real-time validation in supported editors like VS Code, Zed, and Neovim.