rintenki 0.1.0 → 0.3.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 +251 -0
- package/bin/rintenki.js +28 -1
- package/index.d.ts +3 -0
- package/package.json +3 -2
- package/rintenki.darwin-arm64.node +0 -0
- package/rintenki.darwin-x64.node +0 -0
- package/rintenki.linux-arm64-gnu.node +0 -0
- package/rintenki.linux-x64-gnu.node +0 -0
- package/rintenki.win32-arm64-msvc.node +0 -0
- package/rintenki.win32-x64-msvc.node +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# rintenki
|
|
2
|
+
|
|
3
|
+
A fast HTML linter powered by html5ever + napi-rs.
|
|
4
|
+
|
|
5
|
+
The name "rintenki" comes from the Japanese word "輪転機" (rintenki), meaning a rotary printing press — a machine that prints large volumes at high speed. The name was chosen because "lint" and "rint" (輪転) sound alike, and like a rotary press that rapidly inspects and produces printed pages, rintenki quickly scans and checks your HTML.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Parsing by html5ever (Rust)
|
|
10
|
+
- Node.js native binding via napi-rs
|
|
11
|
+
- 38 built-in rules
|
|
12
|
+
- CLI / API / VS Code extension / LSP server
|
|
13
|
+
- Auto-fix with `--fix`
|
|
14
|
+
- JSON output for CI integration
|
|
15
|
+
- Per-rule severity customization (error / warning / off)
|
|
16
|
+
- Vue / JSX / eRuby support via parser plugins
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install rintenki
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## CLI
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx rintenki "src/**/*.html"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Options
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
rintenki [options] <files...>
|
|
34
|
+
|
|
35
|
+
-c, --config <path> Path to config file (default: .rintenkirc.json)
|
|
36
|
+
-f, --format <format> Output format: stylish (default), json
|
|
37
|
+
--fix Auto-fix fixable rules
|
|
38
|
+
--max-warnings <number> Exit with error if warnings exceed this number
|
|
39
|
+
-h, --help Show help
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Examples
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Basic usage
|
|
46
|
+
rintenki "src/**/*.html"
|
|
47
|
+
|
|
48
|
+
# JSON output
|
|
49
|
+
rintenki --format json "src/**/*.html"
|
|
50
|
+
|
|
51
|
+
# Auto-fix
|
|
52
|
+
rintenki --fix "src/**/*.html"
|
|
53
|
+
|
|
54
|
+
# Custom config
|
|
55
|
+
rintenki --config custom.json "src/**/*.html"
|
|
56
|
+
|
|
57
|
+
# Vue files
|
|
58
|
+
rintenki "src/**/*.vue"
|
|
59
|
+
|
|
60
|
+
# JSX/TSX files
|
|
61
|
+
rintenki "src/**/*.tsx"
|
|
62
|
+
|
|
63
|
+
# eRuby files
|
|
64
|
+
rintenki "app/views/**/*.erb"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## API
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
const { lint, fix } = require("rintenki");
|
|
71
|
+
|
|
72
|
+
const result = lint("<html><body><img></body></html>");
|
|
73
|
+
console.log(result.diagnostics);
|
|
74
|
+
|
|
75
|
+
const fixed = fix('<DIV Class="foo">text</DIV>');
|
|
76
|
+
console.log(fixed.output); // <div class="foo">text</div>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Configuration
|
|
80
|
+
|
|
81
|
+
Place `.rintenkirc.json` in your project root:
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"parser": {
|
|
86
|
+
".vue": "@rintenki/vue-parser"
|
|
87
|
+
},
|
|
88
|
+
"rules": {
|
|
89
|
+
"doctype": "error",
|
|
90
|
+
"no-consecutive-br": "warning",
|
|
91
|
+
"no-hard-code-id": "off",
|
|
92
|
+
"required-attr": true,
|
|
93
|
+
"end-tag": false
|
|
94
|
+
},
|
|
95
|
+
"ignore": ["dist/**", "vendor/**"]
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Severity
|
|
100
|
+
|
|
101
|
+
| Value | Description |
|
|
102
|
+
|-------|-------------|
|
|
103
|
+
| `"error"` | Report as error (exit code 1) |
|
|
104
|
+
| `"warning"` / `"warn"` | Report as warning |
|
|
105
|
+
| `"off"` / `false` | Disable the rule |
|
|
106
|
+
| `true` | Use default severity |
|
|
107
|
+
|
|
108
|
+
### Ignore
|
|
109
|
+
|
|
110
|
+
Use a `.rintenkiignore` file or the `ignore` field in config to exclude files by glob pattern. `node_modules` is always excluded automatically.
|
|
111
|
+
|
|
112
|
+
## Parser Plugins
|
|
113
|
+
|
|
114
|
+
rintenki supports non-HTML files via optional parser plugins.
|
|
115
|
+
|
|
116
|
+
| Plugin | Syntax | Install |
|
|
117
|
+
|--------|--------|---------|
|
|
118
|
+
| `@rintenki/vue-parser` | Vue SFC (`.vue`) | `npm install @rintenki/vue-parser` |
|
|
119
|
+
| `@rintenki/jsx-parser` | JSX/TSX (`.jsx`, `.tsx`) | `npm install @rintenki/jsx-parser` |
|
|
120
|
+
| `@rintenki/erb-parser` | eRuby (`.erb`) | `npm install @rintenki/erb-parser` |
|
|
121
|
+
|
|
122
|
+
Parsers are auto-detected from installed packages, or can be explicitly configured in `.rintenkirc.json`.
|
|
123
|
+
|
|
124
|
+
### How It Works
|
|
125
|
+
|
|
126
|
+
- **Vue**: Extracts `<template>` block via `@vue/compiler-sfc`, masks `{{ }}` interpolations
|
|
127
|
+
- **JSX/TSX**: Parses AST via `oxc-parser` (Rust), extracts HTML elements, maps React attributes (`className` → `class`)
|
|
128
|
+
- **eRuby**: Masks `<% %>` tags with same-length placeholders, preserving line numbers
|
|
129
|
+
|
|
130
|
+
## Rules
|
|
131
|
+
|
|
132
|
+
### Conformance Checking
|
|
133
|
+
|
|
134
|
+
| Rule | Default | Description |
|
|
135
|
+
|------|---------|-------------|
|
|
136
|
+
| `attr-duplication` | error | Detect duplicate attributes |
|
|
137
|
+
| `deprecated-attr` | error | Detect deprecated or obsolete attributes |
|
|
138
|
+
| `deprecated-element` | error | Detect deprecated or obsolete elements |
|
|
139
|
+
| `disallowed-element` | off | Detect disallowed elements |
|
|
140
|
+
| `doctype` | error | Detect missing DOCTYPE declaration |
|
|
141
|
+
| `heading-levels` | error | Detect skipped heading levels |
|
|
142
|
+
| `id-duplication` | error | Detect duplicate id attribute values |
|
|
143
|
+
| `invalid-attr` | error | Detect attributes not in the spec |
|
|
144
|
+
| `no-duplicate-dt` | error | Detect duplicate dt names in dl |
|
|
145
|
+
| `no-empty-palpable-content` | warning | Detect empty palpable content elements |
|
|
146
|
+
| `no-orphaned-end-tag` | error | Detect end tags without matching start tags |
|
|
147
|
+
| `permitted-contents` | error | Detect children not permitted by the spec |
|
|
148
|
+
| `placeholder-label-option` | warning | Detect missing placeholder option in required select |
|
|
149
|
+
| `require-datetime` | error | Detect missing datetime attribute on time element |
|
|
150
|
+
| `required-attr` | error | Detect missing required attributes |
|
|
151
|
+
| `required-element` | error | Detect missing required child elements |
|
|
152
|
+
|
|
153
|
+
### Accessibility
|
|
154
|
+
|
|
155
|
+
| Rule | Default | Description |
|
|
156
|
+
|------|---------|-------------|
|
|
157
|
+
| `label-has-control` | error | Detect label elements without associated control |
|
|
158
|
+
| `landmark-roles` | warning | Detect nested landmark roles |
|
|
159
|
+
| `neighbor-popovers` | off | Detect non-adjacent popover triggers and targets |
|
|
160
|
+
| `no-ambiguous-navigable-target-names` | warning | Detect invalid target name keywords |
|
|
161
|
+
| `no-consecutive-br` | warning | Detect consecutive br elements |
|
|
162
|
+
| `no-refer-to-non-existent-id` | error | Detect references to non-existent ids |
|
|
163
|
+
| `require-accessible-name` | error | Detect missing accessible names |
|
|
164
|
+
| `required-h1` | error | Detect missing h1 element |
|
|
165
|
+
| `table-row-column-alignment` | warning | Detect inconsistent table column counts |
|
|
166
|
+
| `use-list` | warning | Suggest list elements for bullet-prefixed text |
|
|
167
|
+
| `wai-aria` | error | Detect invalid WAI-ARIA roles and attributes |
|
|
168
|
+
|
|
169
|
+
### Naming Convention
|
|
170
|
+
|
|
171
|
+
| Rule | Default | Description |
|
|
172
|
+
|------|---------|-------------|
|
|
173
|
+
| `class-naming` | off | Enforce class name conventions |
|
|
174
|
+
|
|
175
|
+
### Maintainability
|
|
176
|
+
|
|
177
|
+
| Rule | Default | Description |
|
|
178
|
+
|------|---------|-------------|
|
|
179
|
+
| `no-hard-code-id` | off | Detect hardcoded id attributes |
|
|
180
|
+
| `no-use-event-handler-attr` | warning | Detect event handler attributes |
|
|
181
|
+
|
|
182
|
+
### Style
|
|
183
|
+
|
|
184
|
+
| Rule | Default | Description |
|
|
185
|
+
|------|---------|-------------|
|
|
186
|
+
| `attr-value-quotes` | warning | Detect unquoted attribute values |
|
|
187
|
+
| `case-sensitive-attr-name` | warning | Detect uppercase attribute names |
|
|
188
|
+
| `case-sensitive-tag-name` | warning | Detect uppercase tag names |
|
|
189
|
+
| `character-reference` | warning | Detect unescaped `&` characters |
|
|
190
|
+
| `end-tag` | warning | Detect missing end tags |
|
|
191
|
+
| `ineffective-attr` | warning | Detect attributes with no effect on element |
|
|
192
|
+
| `no-boolean-attr-value` | warning | Detect values on boolean attributes |
|
|
193
|
+
| `no-default-value` | warning | Detect attributes set to their default value |
|
|
194
|
+
|
|
195
|
+
### Auto-fixable Rules
|
|
196
|
+
|
|
197
|
+
The following rules can be auto-fixed with `--fix`:
|
|
198
|
+
|
|
199
|
+
- `case-sensitive-tag-name`
|
|
200
|
+
- `case-sensitive-attr-name`
|
|
201
|
+
- `no-boolean-attr-value`
|
|
202
|
+
- `no-default-value`
|
|
203
|
+
|
|
204
|
+
## VS Code Extension
|
|
205
|
+
|
|
206
|
+
The `packages/rintenki-vscode` package provides a VS Code extension with real-time linting and Quick Fix support via the LSP server.
|
|
207
|
+
|
|
208
|
+
### Development
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
pnpm build # Build all packages
|
|
212
|
+
code . # Open project root in VS Code
|
|
213
|
+
# Press F5 to launch Extension Development Host
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Settings
|
|
217
|
+
|
|
218
|
+
```json
|
|
219
|
+
{
|
|
220
|
+
"rintenki.rules": {
|
|
221
|
+
"case-sensitive-attr-name": "off",
|
|
222
|
+
"no-hard-code-id": "warning"
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Packages
|
|
228
|
+
|
|
229
|
+
| Package | Description |
|
|
230
|
+
|---------|-------------|
|
|
231
|
+
| `rintenki` | Linter core (Rust + napi-rs) |
|
|
232
|
+
| `rintenki-lsp-server` | LSP server |
|
|
233
|
+
| `rintenki-vscode` | VS Code extension |
|
|
234
|
+
| `@rintenki/parser-utils` | Shared parser interface |
|
|
235
|
+
| `@rintenki/vue-parser` | Vue SFC parser plugin |
|
|
236
|
+
| `@rintenki/jsx-parser` | JSX/TSX parser plugin |
|
|
237
|
+
| `@rintenki/erb-parser` | eRuby parser plugin |
|
|
238
|
+
|
|
239
|
+
## Supported Platforms
|
|
240
|
+
|
|
241
|
+
- macOS (arm64, x64)
|
|
242
|
+
- Linux (x64, arm64)
|
|
243
|
+
- Windows (x64, arm64)
|
|
244
|
+
|
|
245
|
+
## Inspired by
|
|
246
|
+
|
|
247
|
+
- [markuplint](https://markuplint.dev/) — The rule set design is inspired by markuplint.
|
|
248
|
+
|
|
249
|
+
## License
|
|
250
|
+
|
|
251
|
+
MIT
|
package/bin/rintenki.js
CHANGED
|
@@ -5,6 +5,13 @@ const { resolve, relative } = require("path");
|
|
|
5
5
|
const { glob } = require("tinyglobby");
|
|
6
6
|
const { lint, fix } = require("../index");
|
|
7
7
|
|
|
8
|
+
let loadParser;
|
|
9
|
+
try {
|
|
10
|
+
loadParser = require("@rintenki/parser-utils").loadParser;
|
|
11
|
+
} catch {
|
|
12
|
+
loadParser = () => undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
const COLORS = {
|
|
9
16
|
red: "\x1b[31m",
|
|
10
17
|
yellow: "\x1b[33m",
|
|
@@ -144,7 +151,19 @@ Examples:
|
|
|
144
151
|
let totalFixed = 0;
|
|
145
152
|
|
|
146
153
|
for (const file of files) {
|
|
147
|
-
let
|
|
154
|
+
let source = readFileSync(file, "utf-8");
|
|
155
|
+
let lineOffset = 0;
|
|
156
|
+
|
|
157
|
+
// Preprocess non-HTML files (Vue, ERB, etc.)
|
|
158
|
+
const parser = loadParser(file, config?.parser);
|
|
159
|
+
if (parser) {
|
|
160
|
+
const preprocessed = parser.preprocess(source);
|
|
161
|
+
source = preprocessed.html;
|
|
162
|
+
lineOffset = preprocessed.lineOffset;
|
|
163
|
+
if (!source) continue;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let html = source;
|
|
148
167
|
|
|
149
168
|
if (args.fix) {
|
|
150
169
|
const fixResult = fix(html, config);
|
|
@@ -158,6 +177,14 @@ Examples:
|
|
|
158
177
|
const result = lint(html, config);
|
|
159
178
|
const rel = relative(process.cwd(), file);
|
|
160
179
|
|
|
180
|
+
// Remap line numbers to original file positions
|
|
181
|
+
if (lineOffset > 0) {
|
|
182
|
+
for (const d of result.diagnostics) {
|
|
183
|
+
if (d.line != null) d.line += lineOffset;
|
|
184
|
+
if (d.endLine != null) d.endLine += lineOffset;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
161
188
|
for (const d of result.diagnostics) {
|
|
162
189
|
if (d.severity === "error") totalErrors++;
|
|
163
190
|
else totalWarnings++;
|
package/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rintenki",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A fast HTML linter powered by html5ever + napi-rs",
|
|
5
5
|
"author": "Kazuhiro Kobayashi <https://github.com/kzhrk>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"index.js",
|
|
33
33
|
"index.d.ts",
|
|
34
34
|
"bin/rintenki.js",
|
|
35
|
-
"rintenki.*.node"
|
|
35
|
+
"rintenki.*.node",
|
|
36
|
+
"README.md"
|
|
36
37
|
],
|
|
37
38
|
"napi": {
|
|
38
39
|
"binaryName": "rintenki",
|
|
Binary file
|
package/rintenki.darwin-x64.node
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|