@utoo/lint 0.2.8 → 0.2.9

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 CHANGED
@@ -2,252 +2,226 @@
2
2
 
3
3
  ![utoo-lint logo](assets/utoo-lint-logo.svg)
4
4
 
5
- High performance linter for JavaScript and TypeScript written by Zig.
5
+ [![npm version](https://img.shields.io/npm/v/%40utoo%2Flint?logo=npm)](https://www.npmjs.com/package/@utoo/lint)
6
+ [![Node.js 20+](https://img.shields.io/badge/Node.js-%3E%3D20-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
7
+ [![CI](https://github.com/utooland/utoo-lint/actions/workflows/ci.yml/badge.svg)](https://github.com/utooland/utoo-lint/actions/workflows/ci.yml)
8
+ [![License](https://img.shields.io/github/license/utooland/utoo-lint)](https://github.com/utooland/utoo-lint/blob/main/LICENSE)
6
9
 
7
- ## Install
10
+ A high-performance linter for JavaScript and TypeScript, written in Zig and
11
+ powered by [Yuku](https://github.com/yuku-toolchain/yuku).
8
12
 
9
- ```bash
10
- pnpm add -D @utoo/lint
11
- ```
13
+ `@utoo/lint` combines a native lint engine with familiar configuration, CLI,
14
+ and JavaScript APIs.
12
15
 
13
- ## CLI
16
+ [Installation](#installation) · [Quick start](#quick-start) ·
17
+ [Rules](https://github.com/utooland/utoo-lint/blob/main/docs/rule-status.md) ·
18
+ [Configuration](https://github.com/utooland/utoo-lint/blob/main/docs/configuration.md) ·
19
+ [GitHub](https://github.com/utooland/utoo-lint)
14
20
 
15
- Run the linter with:
21
+ ## Why utoo-lint?
16
22
 
17
- ```bash
18
- pnpm exec utoo-lint src
19
- ```
23
+ - **Native performance** — Zig and Yuku power parsing, semantic analysis, and
24
+ parallel file linting.
25
+ - **Broad rule coverage** — More than 300 rules cover JavaScript, TypeScript,
26
+ React, JSX accessibility, and imports.
27
+ - **Familiar configuration** — Use typed `utlint.config.ts` or static
28
+ `utlint.config.json`, with ESLint-style rule names and severities.
29
+ - **Practical workflows** — Safe autofix, suppression comments, JSON output,
30
+ and ESM/CommonJS APIs are included.
31
+ - **Portable installation** — Prebuilt binaries are available for macOS,
32
+ Linux, and Windows on supported architectures.
20
33
 
21
- If no target is provided, `utoo-lint` uses `files` from the selected
22
- `utlint.config.ts` or `utlint.config.json`. When no config file or `files` entry
23
- exists, it scans the current directory. It skips `.git`, `.zig-cache`,
24
- `node_modules`, `vendor`, and `zig-out`.
34
+ ## Installation
25
35
 
26
- Use a config file:
36
+ `@utoo/lint` requires Node.js 20 or later.
27
37
 
28
- ```bash
29
- pnpm exec utoo-lint --config=utlint.config.json src
30
- ```
38
+ ### utoo
31
39
 
32
- Run a focused rule set:
40
+ [utoo](https://utoo.land/en/docs/utoo) is a fast, npm-compatible package
41
+ manager written in Rust. Install it once if it is not already available:
33
42
 
34
43
  ```bash
35
- pnpm exec utoo-lint --rules=no-debugger,no-console src
44
+ npm install -g utoo
36
45
  ```
37
46
 
38
- Use machine-readable output:
47
+ Add `@utoo/lint` as a development dependency:
39
48
 
40
49
  ```bash
41
- pnpm exec utoo-lint --format=json src
50
+ ut install @utoo/lint -D
42
51
  ```
43
52
 
44
- The default terminal output groups diagnostics by file, aligns their location
45
- and severity, and summarizes errors and warnings separately. Color is detected
46
- automatically; pass `--color` to force it or `--no-color` to disable it. JSON
47
- output remains stable for editor and CI integrations.
48
-
49
- ## Autofix
50
-
51
- Apply safe fixes from all enabled rules that support autofix:
53
+ In an existing project, run `ut install` without a package name to install all
54
+ dependencies declared in `package.json`.
52
55
 
53
- ```bash
54
- pnpm exec utoo-lint --fix src
55
- ```
56
-
57
- Preview the result without changing files:
56
+ ### Other package managers
58
57
 
59
58
  ```bash
60
- pnpm exec utoo-lint --fix-dry-run --format=json src
59
+ pnpm add -D @utoo/lint
60
+ npm install --save-dev @utoo/lint
61
61
  ```
62
62
 
63
- `--fix` writes fixed source back to disk. `--fix-dry-run` never writes files;
64
- with JSON output, changed sources are returned in the `outputs` array. Fixes run
65
- until the source is stable, subject to a safety pass limit. Diagnostics remain
66
- when a rule or a particular code shape cannot be fixed safely. The current
67
- [rule status](https://github.com/fireairforce/utoo-lint/blob/main/docs/rule-status.md)
68
- lists autofix coverage by rule.
63
+ The package selects the native binary for the current platform. No Zig
64
+ toolchain is required.
69
65
 
70
- ## Suppression comments
66
+ ## Quick start
71
67
 
72
- Suppress a rule for the next line of code with `utlint-ignore`:
68
+ ### Run with the default rules
73
69
 
74
- ```js
75
- // utlint-ignore no-debugger: generated breakpoint
76
- debugger;
70
+ ```bash
71
+ utx @utoo/lint src
77
72
  ```
78
73
 
79
- Omit the rule ID to suppress all lint rules for the next line of code. Use
80
- `utlint-ignore-all` before any code for a file-level suppression, or matching
81
- `utlint-ignore-start` and `utlint-ignore-end` comments for a range. Line and
82
- block comments are supported. Parse errors remain visible, and fixes from
83
- suppressed diagnostics are not applied.
74
+ Without a config file, `utoo-lint` uses its built-in default rules. It supports
75
+ `.js`, `.jsx`, `.ts`, `.tsx`, `.mjs`, `.cjs`, `.mts`, and `.cts` files.
84
76
 
85
- Native reports expose suppressed items in `suppressedDiagnostics`; the ESLint
86
- compatibility API exposes them in `suppressedMessages`.
77
+ ### Add a typed config
87
78
 
88
- ## Configuration
79
+ Create `utlint.config.ts`:
89
80
 
90
- `utoo-lint` supports two canonical project config formats:
81
+ ```ts
82
+ import { defineConfig, globalIgnores } from "@utoo/lint/config";
83
+ import frontend from "@utoo/lint/configs/frontend";
91
84
 
92
- | Config | Use it when | Raw native binary |
93
- | --- | --- | --- |
94
- | `utlint.config.ts` | You want typed authoring, imports, or computed values. | No |
95
- | `utlint.config.json` | You want a static, runtime-independent config. | Yes |
85
+ export default defineConfig(
86
+ globalIgnores(["dist/", "coverage/"]),
87
+ {
88
+ ...frontend,
89
+ files: ["src/**/*.{js,jsx,ts,tsx}"],
90
+ rules: {
91
+ ...frontend.rules,
92
+ "no-console": "warn"
93
+ }
94
+ }
95
+ );
96
+ ```
96
97
 
97
- The npm CLI discovers either format automatically. It can also select one
98
- explicitly:
98
+ The CLI discovers the config and uses its `files` patterns:
99
99
 
100
100
  ```bash
101
- pnpm exec utoo-lint --config=utlint.config.ts src
102
- pnpm exec utoo-lint --config=utlint.config.json src
101
+ utx @utoo/lint
103
102
  ```
104
103
 
105
- For a static config readable by both the npm CLI and raw native binary, create
106
- `utlint.config.json`:
104
+ See the
105
+ [configuration guide](https://github.com/utooland/utoo-lint/blob/main/docs/configuration.md)
106
+ for flat config arrays, global ignores, rule options, and config precedence.
107
+
108
+ ### Add package scripts
107
109
 
108
110
  ```json
109
111
  {
110
- "$schema": "https://raw.githubusercontent.com/fireairforce/utoo-lint/main/npm/utoo-lint/schema.json",
111
- "files": ["src/**/*.{js,jsx,ts,tsx}"],
112
- "ignores": ["dist", "node_modules"],
113
- "rules": {
114
- "no-console": "off",
115
- "no-debugger": "error"
112
+ "scripts": {
113
+ "lint": "utoo-lint src",
114
+ "lint:fix": "utoo-lint --fix src"
116
115
  }
117
116
  }
118
117
  ```
119
118
 
120
- For a typed config executed by the npm/Node CLI, create `utlint.config.ts`:
119
+ With utoo, run these scripts as `ut lint` and `ut lint:fix`.
121
120
 
122
- ```ts
123
- import { defineConfig } from "@utoo/lint/config";
124
-
125
- export default defineConfig({
126
- files: ["src/**/*.{js,jsx,ts,tsx}"],
127
- ignores: ["dist", "node_modules"],
128
- rules: {
129
- "no-console": "off",
130
- "no-debugger": "error"
131
- }
132
- });
133
- ```
121
+ ## Common commands
134
122
 
135
- The packaged frontend preset is also available as a typed config import:
123
+ | Task | Command |
124
+ | --- | --- |
125
+ | Lint files | `utx @utoo/lint src` |
126
+ | Apply safe fixes | `utx @utoo/lint --fix src` |
127
+ | Preview fixes as JSON | `utx @utoo/lint --fix-dry-run --format=json src` |
128
+ | Select rules for one run | `utx @utoo/lint --rules=no-debugger,no-unused-vars src` |
129
+ | Use an explicit config | `utx @utoo/lint --config=utlint.config.json src` |
136
130
 
137
- ```ts
138
- import { defineConfig } from "@utoo/lint/config";
139
- import frontend from "@utoo/lint/configs/frontend";
131
+ Run `utx @utoo/lint --help` for all CLI options.
140
132
 
141
- export default defineConfig({
142
- ...frontend,
143
- rules: {
144
- ...frontend.rules,
145
- "no-console": "off",
146
- },
147
- });
148
- ```
133
+ ## Configuration
149
134
 
150
- Use `globalIgnores()` as a separate config entry when files or directories
151
- must be excluded from every entry:
135
+ `utoo-lint` supports two canonical config formats. They are alternative
136
+ representations of one active config and are not merged together.
152
137
 
153
- ```ts
154
- import { defineConfig, globalIgnores } from "@utoo/lint/config";
138
+ | Config | Best for | Raw native binary |
139
+ | --- | --- | --- |
140
+ | `utlint.config.ts` | Typed authoring, imports, presets, and computed values | No |
141
+ | `utlint.config.json` | Static configuration and JSON Schema validation | Yes |
155
142
 
156
- export default defineConfig(
157
- globalIgnores(["dist/", ".next/", "**/generated/"]),
158
- {
159
- files: ["**/*.{js,jsx,ts,tsx}"],
160
- rules: {
161
- "no-debugger": "error"
162
- }
163
- }
164
- );
143
+ Rule severities follow ESLint conventions: `off`, `warn`, `error`, `0`, `1`,
144
+ and `2`. A selected config enables only the rules present in its resolved
145
+ `rules` map.
146
+
147
+ The npm CLI and JavaScript API load both formats. The raw native binary loads
148
+ JSON only. See the
149
+ [configuration guide](https://github.com/utooland/utoo-lint/blob/main/docs/configuration.md)
150
+ for the complete behavior.
151
+
152
+ ## Autofix
153
+
154
+ Apply safe fixes in place:
155
+
156
+ ```bash
157
+ utx @utoo/lint --fix src
165
158
  ```
166
159
 
167
- `globalIgnores()` returns an ignore-only object containing `ignores` and a
168
- generated `name`. Only such global entries prune directories during target
169
- discovery. By contrast, `ignores` beside `files`, `rules`, or another config
170
- key is entry-scoped: it prevents that entry from applying to matching files,
171
- but does not exclude them from other entries. With no CLI targets, global
172
- ignores filter the config's `files` patterns, or the current-directory scan
173
- when no `files` are configured. Directory patterns such as `dist/` and
174
- `.next/` are relative to the config; use `**/generated/` to match at any depth.
175
- This intentionally follows [ESLint flat config ignore semantics](https://eslint.org/docs/latest/use/configure/ignore).
176
-
177
- The two canonical names represent one active config and are not implicitly
178
- merged. For the npm/Node entry point, discovery is nearest-directory-first.
179
- Within the same directory, `utlint.config.ts` takes precedence over
180
- `utlint.config.json`; deprecated `utoo.json` and `utoo-lint.json` files are
181
- checked afterward for temporary backward compatibility. Rule-related CLI
182
- options such as `--rules`, individual rule toggles, and fishlint's `--rule` are
183
- applied after the selected config.
184
-
185
- Project-config `files` and `ignores` patterns are relative to the selected
186
- config file's directory. In a flat config array, they select the entries that
187
- apply to each file; matching entries are combined in order and later rule
188
- values override earlier values. The npm CLI, JavaScript API, and fishlint
189
- compatibility command perform this rule resolution per file.
190
-
191
- TypeScript config is trusted executable code. Its default export must be a
192
- JSON-serializable object or flat config array. The npm wrapper executes it and
193
- materializes the result as JSON before invoking the native binary. The raw
194
- binary does not execute or discover TypeScript; it searches for
195
- `utlint.config.json` and then the legacy JSON names. Direct native use therefore
196
- requires a JSON config. The raw binary applies only its `rules`;
197
- config-driven `files` and `ignores` filtering and default target selection are
198
- npm/Node wrapper features. Pass lint targets explicitly when invoking the raw
199
- binary. As in ESLint, selecting a config disables rules not present in its
200
- resolved `rules` map. When no config is selected, utoo-lint uses its built-in
201
- default rules.
202
-
203
- Rule severities use ESLint's exit behavior: warnings are reported with exit
204
- status 0, while errors return exit status 1. Use fishlint's `--max-warnings`
205
- option when warnings should fail CI.
206
-
207
- Start from the packaged frontend template:
160
+ Preview fixes without writing files:
208
161
 
209
162
  ```bash
210
- cp node_modules/@utoo/lint/configs/frontend.json utlint.config.json
163
+ utx @utoo/lint --fix-dry-run --format=json src
211
164
  ```
212
165
 
213
- ## ESLint Migration
166
+ Fixes run until the source is stable, subject to a safety pass limit. The
167
+ [rule status](https://github.com/utooland/utoo-lint/blob/main/docs/rule-status.md)
168
+ documents autofix support for each rule.
214
169
 
215
- Convert an existing ESLint config into the native utoo format:
170
+ ## Suppression comments
216
171
 
217
- ```bash
218
- pnpm exec utoo-lint migrate eslint --from eslint.config.js --output utlint.config.json
172
+ Use suppression comments for intentional exceptions without disabling a rule
173
+ in project configuration:
174
+
175
+ ```js
176
+ // utlint-ignore no-debugger: generated breakpoint
177
+ debugger;
219
178
  ```
220
179
 
221
- The package also provides compatibility bins for common replacement flows:
180
+ | Directive | Scope |
181
+ | --- | --- |
182
+ | `utlint-ignore [rule]` | The next line of code |
183
+ | `utlint-ignore-all [rule]` | The entire file when placed before any code |
184
+ | `utlint-ignore-start [rule]` | The start of a suppressed range |
185
+ | `utlint-ignore-end [rule]` | The end of a suppressed range |
186
+
187
+ A directive may target one rule ID. Omitting it creates or closes an all-rules
188
+ suppression. Parse errors are never suppressed, and suppressed fixes are not
189
+ applied.
190
+
191
+ See the
192
+ [suppression comments guide](https://github.com/utooland/utoo-lint/blob/main/docs/suppressions.md)
193
+ for range examples and API behavior.
194
+
195
+ ## Migrating from ESLint
196
+
197
+ Convert an existing ESLint config into a native utoo-lint config:
222
198
 
223
199
  ```bash
224
- pnpm exec eslint --config utlint.config.json src
225
- pnpm exec fishlint eslint --disable-setup --config utlint.config.json --glob src
200
+ utx @utoo/lint migrate eslint \
201
+ --from eslint.config.js \
202
+ --output utlint.config.json
226
203
  ```
227
204
 
205
+ The package also exposes `eslint` and `fishlint` compatibility commands for
206
+ incremental replacement workflows. See the
207
+ [migration guide](https://github.com/utooland/utoo-lint/blob/main/docs/eslint-migration.md)
208
+ for supported mappings and known differences.
209
+
228
210
  ## JavaScript API
229
211
 
212
+ The package provides ESM and CommonJS entry points:
213
+
230
214
  ```js
231
215
  import { lintFiles } from "@utoo/lint";
232
216
 
233
217
  const report = lintFiles(["src"], {
234
- config: "utlint.config.json",
235
- rules: ["no-debugger"]
218
+ config: "utlint.config.ts"
236
219
  });
237
220
 
238
221
  console.log(report.diagnostics);
239
222
  ```
240
223
 
241
- Calling `lintFiles()` without patterns follows the same default target behavior
242
- as the CLI, so migration scripts usually do not need to expand config `files`
243
- manually.
244
-
245
- CommonJS is supported through `require("@utoo/lint")`.
246
-
247
- Set `fix: true` to compute fixed output through the JavaScript API. Raw
248
- `lintFiles()` and `lintText()` reports expose changed sources in `outputs`
249
- without writing them. The ESLint-compatible API follows the usual two-step
250
- flow:
224
+ An ESLint-style API is also available:
251
225
 
252
226
  ```js
253
227
  import { ESLint } from "@utoo/lint";
@@ -257,8 +231,33 @@ const results = await eslint.lintFiles(["src"]);
257
231
  await ESLint.outputFixes(results);
258
232
  ```
259
233
 
260
- ## Binary Resolution
234
+ Raw reports include active diagnostics, suppressed diagnostics, fixed outputs,
235
+ and an exit code. The compatibility layer also exports `Linter`, `CLIEngine`,
236
+ `RuleTester`, and `SourceCode` APIs.
237
+
238
+ ## Supported platforms
239
+
240
+ The package installs a platform-specific optional dependency containing the
241
+ native binary.
242
+
243
+ | Operating system | Architectures |
244
+ | --- | --- |
245
+ | macOS | arm64, x64 |
246
+ | Linux | arm64, x64 |
247
+ | Windows | x64 |
248
+
249
+ Set `UTOO_LINT_BIN=/path/to/utoo-lint` to use a custom binary during local
250
+ development.
251
+
252
+ ## Documentation
253
+
254
+ - [Rule status](https://github.com/utooland/utoo-lint/blob/main/docs/rule-status.md)
255
+ - [Configuration](https://github.com/utooland/utoo-lint/blob/main/docs/configuration.md)
256
+ - [Suppression comments](https://github.com/utooland/utoo-lint/blob/main/docs/suppressions.md)
257
+ - [Migrating from ESLint](https://github.com/utooland/utoo-lint/blob/main/docs/eslint-migration.md)
258
+ - [Benchmarks](https://github.com/utooland/utoo-lint/tree/main/benchmarks)
259
+ - [Security policy](https://github.com/utooland/utoo-lint/blob/main/SECURITY.md)
260
+
261
+ ## License
261
262
 
262
- `@utoo/lint` installs a platform-specific optional dependency containing the
263
- native `utoo-lint` binary. Set `UTOO_LINT_BIN=/path/to/utoo-lint` to force a
264
- specific binary during local development.
263
+ [MIT](https://github.com/utooland/utoo-lint/blob/main/LICENSE)
package/bin/migrate.js CHANGED
@@ -16,7 +16,14 @@ const JAVASCRIPT_CONFIG_EXTENSIONS = new Set([".js", ".mjs", ".cjs"]);
16
16
  const IGNORED_RULES = new Map([
17
17
  ["prettier/prettier", "formatting is outside utoo-lint"]
18
18
  ]);
19
- const SCHEMA_URL = "https://raw.githubusercontent.com/fireairforce/utoo-lint/main/npm/utoo-lint/schema.json";
19
+ const RULE_ALIASES = new Map([
20
+ ["@eslint-react/no-array-index-key", "react/no-array-index-key"],
21
+ ["@eslint-react/dom-no-find-dom-node", "react/no-find-dom-node"],
22
+ ["@eslint-react/dom-no-render-return-value", "react/no-render-return-value"],
23
+ ["@eslint-react/dom-no-void-elements-with-children", "react/void-dom-elements-no-children"],
24
+ ["@eslint-react/rules-of-hooks", "react-hooks/rules-of-hooks"]
25
+ ]);
26
+ const SCHEMA_URL = "https://raw.githubusercontent.com/utooland/utoo-lint/main/npm/utoo-lint/schema.json";
20
27
  const JAVASCRIPT_CONFIG_LOADER_SCRIPT = `
21
28
  import { writeFileSync } from "node:fs";
22
29
  import { pathToFileURL } from "node:url";
@@ -178,43 +185,41 @@ function migrateEslintConfig(options) {
178
185
  const eslintConfig = loadEslintConfig(sourcePath, options.cwd);
179
186
  const supportedRuleIds = new Set(new Linter().getRules().keys());
180
187
  const entries = normalizeConfigEntries(eslintConfig);
181
- const rules = {};
182
- const files = new Set();
183
- const ignores = new Set();
188
+ const migratedEntries = [];
184
189
  const supportedRules = [];
185
190
  const unsupportedRules = [];
186
191
  const ignoredRules = [];
192
+ const translatedRules = [];
187
193
 
188
194
  for (const entry of entries) {
189
- addPatterns(files, entry.files);
190
- addPatterns(ignores, entry.ignores);
191
- if (!entry.rules || typeof entry.rules !== "object") {
192
- continue;
193
- }
194
- for (const [ruleId, ruleConfig] of Object.entries(entry.rules)) {
195
- if (IGNORED_RULES.has(ruleId)) {
196
- ignoredRules.push({ ruleId, reason: IGNORED_RULES.get(ruleId) });
197
- continue;
195
+ const rules = {};
196
+ if (entry.rules && typeof entry.rules === "object") {
197
+ for (const [ruleId, ruleConfig] of Object.entries(entry.rules)) {
198
+ const migratedRuleId = RULE_ALIASES.get(ruleId) ?? ruleId;
199
+ if (IGNORED_RULES.has(ruleId)) {
200
+ ignoredRules.push({ ruleId, reason: IGNORED_RULES.get(ruleId) });
201
+ continue;
202
+ }
203
+ if (!supportedRuleIds.has(migratedRuleId)) {
204
+ if (isRuleDisabled(ruleConfig)) {
205
+ continue;
206
+ }
207
+ unsupportedRules.push(ruleId);
208
+ continue;
209
+ }
210
+ rules[migratedRuleId] = migratableRuleConfig(ruleConfig);
211
+ supportedRules.push(migratedRuleId);
212
+ if (migratedRuleId !== ruleId) {
213
+ translatedRules.push({ sourceRuleId: ruleId, targetRuleId: migratedRuleId });
214
+ }
198
215
  }
199
- if (!supportedRuleIds.has(ruleId)) {
200
- unsupportedRules.push(ruleId);
201
- continue;
202
- }
203
- rules[ruleId] = migratableRuleConfig(ruleConfig);
204
- supportedRules.push(ruleId);
205
216
  }
217
+ migratedEntries.push(migratableConfigEntry(entry, rules));
206
218
  }
207
219
 
208
- const config = {
209
- $schema: SCHEMA_URL
210
- };
211
- if (files.size > 0) {
212
- config.files = [...files];
213
- }
214
- if (ignores.size > 0) {
215
- config.ignores = [...ignores];
216
- }
217
- config.rules = sortObjectByKey(rules);
220
+ const config = Array.isArray(eslintConfig)
221
+ ? [{ $schema: SCHEMA_URL }, ...migratedEntries]
222
+ : { $schema: SCHEMA_URL, ...(migratedEntries[0] ?? { rules: {} }) };
218
223
 
219
224
  return {
220
225
  config,
@@ -224,6 +229,7 @@ function migrateEslintConfig(options) {
224
229
  supportedRules: uniqueSorted(supportedRules),
225
230
  unsupportedRules: uniqueSorted(unsupportedRules),
226
231
  ignoredRules: uniqueByRuleId(ignoredRules),
232
+ translatedRules: uniqueTranslatedRules(translatedRules),
227
233
  optionDroppedRules: []
228
234
  }
229
235
  };
@@ -282,21 +288,54 @@ function normalizeConfigEntries(config) {
282
288
  return [];
283
289
  }
284
290
 
285
- function addPatterns(target, value) {
286
- if (!value) {
287
- return;
291
+ function migratableConfigEntry(entry, rules) {
292
+ const migrated = {};
293
+ if (typeof entry.name === "string") {
294
+ migrated.name = entry.name;
295
+ }
296
+ const files = migratablePatterns(entry.files);
297
+ if (files.length > 0) {
298
+ migrated.files = files;
299
+ }
300
+ const ignores = migratablePatterns(entry.ignores);
301
+ if (ignores.length > 0) {
302
+ migrated.ignores = ignores;
288
303
  }
304
+ if (!isGlobalIgnoreEntry(entry)) {
305
+ migrated.rules = sortObjectByKey(rules);
306
+ }
307
+ return migrated;
308
+ }
309
+
310
+ function migratablePatterns(value) {
311
+ const patterns = [];
312
+ collectMigratablePatterns(value, patterns);
313
+ return [...new Set(patterns)];
314
+ }
315
+
316
+ function collectMigratablePatterns(value, patterns) {
289
317
  for (const pattern of Array.isArray(value) ? value : [value]) {
290
- if (typeof pattern === "string") {
291
- target.add(pattern);
318
+ if (Array.isArray(pattern)) {
319
+ collectMigratablePatterns(pattern, patterns);
320
+ } else if (typeof pattern === "string") {
321
+ patterns.push(pattern);
292
322
  }
293
323
  }
294
324
  }
295
325
 
326
+ function isGlobalIgnoreEntry(entry) {
327
+ return Boolean(entry.ignores) && Object.keys(entry).every((key) => key === "name" || key === "ignores");
328
+ }
329
+
296
330
  function migratableRuleConfig(ruleConfig) {
297
331
  return Array.isArray(ruleConfig) ? [...ruleConfig] : ruleConfig;
298
332
  }
299
333
 
334
+ function isRuleDisabled(ruleConfig) {
335
+ const severity = Array.isArray(ruleConfig) ? ruleConfig[0] : ruleConfig;
336
+ return severity === "off" || severity === 0 || severity === false;
337
+ }
338
+
300
339
  function sortObjectByKey(object) {
301
340
  return Object.fromEntries(Object.entries(object).sort(([left], [right]) => left.localeCompare(right)));
302
341
  }
@@ -313,6 +352,14 @@ function uniqueByRuleId(values) {
313
352
  return [...result.values()].sort((left, right) => left.ruleId.localeCompare(right.ruleId));
314
353
  }
315
354
 
355
+ function uniqueTranslatedRules(values) {
356
+ const result = new Map();
357
+ for (const value of values) {
358
+ result.set(value.sourceRuleId, value);
359
+ }
360
+ return [...result.values()].sort((left, right) => left.sourceRuleId.localeCompare(right.sourceRuleId));
361
+ }
362
+
316
363
  function writeReport(report, options, stream) {
317
364
  if (options.report === "json") {
318
365
  stream.write(JSON.stringify(report, null, 2) + "\n");
@@ -324,6 +371,9 @@ function writeReport(report, options, stream) {
324
371
  stream.write(`utoo-lint migrate eslint: wrote ${report.output}\n`);
325
372
  }
326
373
  stream.write(`utoo-lint migrate eslint: migrated ${report.supportedRules.length} supported rule(s)\n`);
374
+ if (report.translatedRules.length > 0) {
375
+ stream.write(`utoo-lint migrate eslint: translated ${report.translatedRules.length} rule alias(es): ${report.translatedRules.map((rule) => `${rule.sourceRuleId} -> ${rule.targetRuleId}`).join(", ")}\n`);
376
+ }
327
377
  if (report.ignoredRules.length > 0) {
328
378
  stream.write(`utoo-lint migrate eslint: ignored ${report.ignoredRules.length} rule(s): ${report.ignoredRules.map((rule) => rule.ruleId).join(", ")}\n`);
329
379
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "https://raw.githubusercontent.com/fireairforce/utoo-lint/main/npm/utoo-lint/schema.json",
2
+ "$schema": "https://raw.githubusercontent.com/utooland/utoo-lint/main/npm/utoo-lint/schema.json",
3
3
  "rules": {
4
4
  "no-debugger": "error",
5
5
  "no-alert": "error",
package/index.cjs CHANGED
@@ -305,6 +305,11 @@ const BUILTIN_RULE_IDS = [
305
305
  "import/no-named-as-default-member",
306
306
  "import/no-unresolved",
307
307
  "import/no-self-import",
308
+ "promise/no-promise-in-callback",
309
+ "promise/no-return-in-finally",
310
+ "promise/no-return-wrap",
311
+ "promise/param-names",
312
+ "promise/valid-params",
308
313
  "jsx-a11y/alt-text",
309
314
  "jsx-a11y/anchor-has-content",
310
315
  "jsx-a11y/aria-props",
@@ -318,6 +323,11 @@ const BUILTIN_RULE_IDS = [
318
323
  "jsx-a11y/role-has-required-aria-props",
319
324
  "jsx-a11y/role-supports-aria-props",
320
325
  "jsx-a11y/scope",
326
+ "promise/always-return",
327
+ "promise/catch-or-return",
328
+ "promise/no-callback-in-promise",
329
+ "promise/no-nesting",
330
+ "promise/no-new-statics",
321
331
  "react/button-has-type",
322
332
  "react/default-props-match-prop-types",
323
333
  "react/display-name",
@@ -358,7 +368,9 @@ const BUILTIN_RULE_IDS = [
358
368
  "react/self-closing-comp",
359
369
  "react/style-prop-object",
360
370
  "react/void-dom-elements-no-children",
371
+ "react-hooks/exhaustive-deps",
361
372
  "react-hooks/rules-of-hooks",
373
+ "unused-imports/no-unused-imports",
362
374
  "@typescript-eslint/adjacent-overload-signatures",
363
375
  "@typescript-eslint/array-type",
364
376
  "@typescript-eslint/ban-ts-comment",
@@ -376,6 +388,7 @@ const BUILTIN_RULE_IDS = [
376
388
  "@typescript-eslint/no-dupe-class-members",
377
389
  "@typescript-eslint/no-empty-function",
378
390
  "@typescript-eslint/no-empty-interface",
391
+ "@typescript-eslint/no-empty-object-type",
379
392
  "@typescript-eslint/no-duplicate-enum-values",
380
393
  "@typescript-eslint/no-extra-semi",
381
394
  "@typescript-eslint/no-extra-non-null-assertion",
@@ -391,6 +404,7 @@ const BUILTIN_RULE_IDS = [
391
404
  "@typescript-eslint/no-shadow",
392
405
  "@typescript-eslint/no-this-alias",
393
406
  "@typescript-eslint/no-unsafe-declaration-merging",
407
+ "@typescript-eslint/no-unsafe-function-type",
394
408
  "@typescript-eslint/triple-slash-reference",
395
409
  "@typescript-eslint/typedef",
396
410
  "@typescript-eslint/unified-signatures",
@@ -434,7 +448,11 @@ const NATIVE_ONLY_RULE_IDS = [
434
448
  "semantic-errors"
435
449
  ];
436
450
  const NATIVE_RULE_IDS = [...BUILTIN_RULE_IDS, ...NATIVE_ONLY_RULE_IDS];
437
- const FIXABLE_BUILTIN_RULE_IDS = new Set(["no-extra-semi", "@typescript-eslint/no-extra-semi"]);
451
+ const FIXABLE_BUILTIN_RULE_IDS = new Set([
452
+ "no-extra-semi",
453
+ "@typescript-eslint/no-extra-semi",
454
+ "unused-imports/no-unused-imports"
455
+ ]);
438
456
  const BUILTIN_RULES = new Map(BUILTIN_RULE_IDS.map((ruleId) => [ruleId, createBuiltinRule(ruleId)]));
439
457
 
440
458
  class UtooLint {
package/index.js CHANGED
@@ -308,6 +308,11 @@ const BUILTIN_RULE_IDS = [
308
308
  "import/no-named-as-default-member",
309
309
  "import/no-unresolved",
310
310
  "import/no-self-import",
311
+ "promise/no-promise-in-callback",
312
+ "promise/no-return-in-finally",
313
+ "promise/no-return-wrap",
314
+ "promise/param-names",
315
+ "promise/valid-params",
311
316
  "jsx-a11y/alt-text",
312
317
  "jsx-a11y/anchor-has-content",
313
318
  "jsx-a11y/aria-props",
@@ -321,6 +326,11 @@ const BUILTIN_RULE_IDS = [
321
326
  "jsx-a11y/role-has-required-aria-props",
322
327
  "jsx-a11y/role-supports-aria-props",
323
328
  "jsx-a11y/scope",
329
+ "promise/always-return",
330
+ "promise/catch-or-return",
331
+ "promise/no-callback-in-promise",
332
+ "promise/no-nesting",
333
+ "promise/no-new-statics",
324
334
  "react/button-has-type",
325
335
  "react/default-props-match-prop-types",
326
336
  "react/display-name",
@@ -361,7 +371,9 @@ const BUILTIN_RULE_IDS = [
361
371
  "react/self-closing-comp",
362
372
  "react/style-prop-object",
363
373
  "react/void-dom-elements-no-children",
374
+ "react-hooks/exhaustive-deps",
364
375
  "react-hooks/rules-of-hooks",
376
+ "unused-imports/no-unused-imports",
365
377
  "@typescript-eslint/adjacent-overload-signatures",
366
378
  "@typescript-eslint/array-type",
367
379
  "@typescript-eslint/ban-ts-comment",
@@ -379,6 +391,7 @@ const BUILTIN_RULE_IDS = [
379
391
  "@typescript-eslint/no-dupe-class-members",
380
392
  "@typescript-eslint/no-empty-function",
381
393
  "@typescript-eslint/no-empty-interface",
394
+ "@typescript-eslint/no-empty-object-type",
382
395
  "@typescript-eslint/no-duplicate-enum-values",
383
396
  "@typescript-eslint/no-extra-semi",
384
397
  "@typescript-eslint/no-extra-non-null-assertion",
@@ -394,6 +407,7 @@ const BUILTIN_RULE_IDS = [
394
407
  "@typescript-eslint/no-shadow",
395
408
  "@typescript-eslint/no-this-alias",
396
409
  "@typescript-eslint/no-unsafe-declaration-merging",
410
+ "@typescript-eslint/no-unsafe-function-type",
397
411
  "@typescript-eslint/triple-slash-reference",
398
412
  "@typescript-eslint/typedef",
399
413
  "@typescript-eslint/unified-signatures",
@@ -437,7 +451,11 @@ const NATIVE_ONLY_RULE_IDS = [
437
451
  "semantic-errors"
438
452
  ];
439
453
  const NATIVE_RULE_IDS = [...BUILTIN_RULE_IDS, ...NATIVE_ONLY_RULE_IDS];
440
- const FIXABLE_BUILTIN_RULE_IDS = new Set(["no-extra-semi", "@typescript-eslint/no-extra-semi"]);
454
+ const FIXABLE_BUILTIN_RULE_IDS = new Set([
455
+ "no-extra-semi",
456
+ "@typescript-eslint/no-extra-semi",
457
+ "unused-imports/no-unused-imports"
458
+ ]);
441
459
  const BUILTIN_RULES = new Map(BUILTIN_RULE_IDS.map((ruleId) => [ruleId, createBuiltinRule(ruleId)]));
442
460
 
443
461
  export class UtooLint {
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@utoo/lint",
3
- "version": "0.2.8",
4
- "description": "Experimental JavaScript and TypeScript linter powered by Yuku",
3
+ "version": "0.2.9",
4
+ "description": "High-performance JavaScript and TypeScript linter powered by Yuku",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "git+https://github.com/fireairforce/utoo-lint.git"
8
+ "url": "git+https://github.com/utooland/utoo-lint.git"
9
9
  },
10
10
  "bugs": {
11
- "url": "https://github.com/fireairforce/utoo-lint/issues"
11
+ "url": "https://github.com/utooland/utoo-lint/issues"
12
12
  },
13
- "homepage": "https://github.com/fireairforce/utoo-lint#readme",
13
+ "homepage": "https://github.com/utooland/utoo-lint#readme",
14
14
  "type": "module",
15
15
  "engines": {
16
- "node": ">=18.0.0"
16
+ "node": ">=20.0.0"
17
17
  },
18
18
  "types": "./index.d.ts",
19
19
  "exports": {
@@ -76,11 +76,11 @@
76
76
  "use-at-your-own-risk.js"
77
77
  ],
78
78
  "optionalDependencies": {
79
- "@utoo/lint-darwin-arm64": "0.2.8",
80
- "@utoo/lint-darwin-x64": "0.2.8",
81
- "@utoo/lint-linux-arm64": "0.2.8",
82
- "@utoo/lint-linux-x64": "0.2.8",
83
- "@utoo/lint-win32-x64": "0.2.8"
79
+ "@utoo/lint-darwin-arm64": "0.2.9",
80
+ "@utoo/lint-darwin-x64": "0.2.9",
81
+ "@utoo/lint-linux-arm64": "0.2.9",
82
+ "@utoo/lint-linux-x64": "0.2.9",
83
+ "@utoo/lint-win32-x64": "0.2.9"
84
84
  },
85
85
  "dependencies": {
86
86
  "jiti": "2.7.0"
package/schema.json CHANGED
@@ -1,50 +1,63 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft-07/schema",
3
- "$id": "https://raw.githubusercontent.com/fireairforce/utoo-lint/main/npm/utoo-lint/schema.json",
3
+ "$id": "https://raw.githubusercontent.com/utooland/utoo-lint/main/npm/utoo-lint/schema.json",
4
4
  "title": "utoo-lint configuration",
5
- "type": "object",
6
- "additionalProperties": true,
7
- "properties": {
8
- "$schema": {
9
- "type": "string"
5
+ "oneOf": [
6
+ {
7
+ "$ref": "#/definitions/configObject"
10
8
  },
11
- "rules": {
12
- "description": "ESLint-compatible rule severity map. Unknown rule names are rejected by utoo-lint.",
13
- "type": "object",
14
- "additionalProperties": {
15
- "$ref": "#/definitions/ruleSeverity"
9
+ {
10
+ "type": "array",
11
+ "items": {
12
+ "$ref": "#/definitions/configObject"
16
13
  }
17
- },
18
- "files": {
19
- "description": "File globs this config entry applies to.",
20
- "oneOf": [
21
- {
14
+ }
15
+ ],
16
+ "definitions": {
17
+ "configObject": {
18
+ "type": "object",
19
+ "additionalProperties": true,
20
+ "properties": {
21
+ "$schema": {
22
22
  "type": "string"
23
23
  },
24
- {
25
- "type": "array",
26
- "items": {
27
- "type": "string"
24
+ "rules": {
25
+ "description": "ESLint-compatible rule severity map. Unknown rule names are rejected by utoo-lint.",
26
+ "type": "object",
27
+ "additionalProperties": {
28
+ "$ref": "#/definitions/ruleSeverity"
28
29
  }
29
- }
30
- ]
31
- },
32
- "ignores": {
33
- "description": "File globs ignored by this config entry.",
34
- "oneOf": [
35
- {
36
- "type": "string"
37
30
  },
38
- {
39
- "type": "array",
40
- "items": {
41
- "type": "string"
42
- }
31
+ "files": {
32
+ "description": "File globs this config entry applies to.",
33
+ "oneOf": [
34
+ {
35
+ "type": "string"
36
+ },
37
+ {
38
+ "type": "array",
39
+ "items": {
40
+ "type": "string"
41
+ }
42
+ }
43
+ ]
44
+ },
45
+ "ignores": {
46
+ "description": "File globs ignored by this config entry.",
47
+ "oneOf": [
48
+ {
49
+ "type": "string"
50
+ },
51
+ {
52
+ "type": "array",
53
+ "items": {
54
+ "type": "string"
55
+ }
56
+ }
57
+ ]
43
58
  }
44
- ]
45
- }
46
- },
47
- "definitions": {
59
+ }
60
+ },
48
61
  "ruleSeverity": {
49
62
  "oneOf": [
50
63
  {