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 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 html = readFileSync(file, "utf-8");
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
@@ -3,6 +3,9 @@ export interface LintDiagnostic {
3
3
  message: string;
4
4
  severity: "error" | "warning";
5
5
  line: number | null;
6
+ col: number | null;
7
+ endLine: number | null;
8
+ endCol: number | null;
6
9
  fixable: boolean;
7
10
  }
8
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rintenki",
3
- "version": "0.1.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
Binary file
Binary file
Binary file
Binary file
Binary file