@ls-stack/fast-lint 0.0.0-stage → 0.2.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 +185 -2
- package/bin/fast-lint.mjs +182 -0
- package/dist/darwin-arm64/fast-lint +0 -0
- package/dist/darwin-x64/fast-lint +0 -0
- package/dist/linux-arm64/fast-lint +0 -0
- package/dist/linux-x64/fast-lint +0 -0
- package/dist/win32-x64/fast-lint.exe +0 -0
- package/lib/index.d.ts +51 -0
- package/lib/index.mjs +3 -0
- package/package.json +33 -3
package/README.md
CHANGED
|
@@ -1,3 +1,186 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ls-stack/fast-lint
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Type-aware lint rules running natively on [tsrs](https://github.com/maschwenk/tsrs), the Rust port of the TypeScript 7
|
|
4
|
+
checker, through [tsrslint](https://github.com/maschwenk/tsrslint)'s rule framework. No ESLint, no Node type checker:
|
|
5
|
+
only these rules run, against one shared program per tsconfig. Type-aware rules live here, not in
|
|
6
|
+
[`@ls-stack/extended-lint`](../extended-lint).
|
|
7
|
+
|
|
8
|
+
## Rules
|
|
9
|
+
|
|
10
|
+
| rule | origin |
|
|
11
|
+
| --- | --- |
|
|
12
|
+
| `improved-no-unnecessary-condition` | moved from `@ls-stack/extended-lint` |
|
|
13
|
+
| `strict-casting` | moved from `@ls-stack/extended-lint` (see below) |
|
|
14
|
+
| `no-unnecessary-void-on-promise` | moved from `@ls-stack/extended-lint` |
|
|
15
|
+
| `no-leaked-conditional-rendering` | port of `@eslint-react/no-leaked-conditional-rendering` (v2.3.13); option `reactVersion` (default `"19.0.0"`, also used for `""` and `"detect"`: the installed version isn't read) |
|
|
16
|
+
|
|
17
|
+
Options are the same as the former ESLint rules' (see `lib/index.d.ts`). Rule names may carry a plugin prefix, so
|
|
18
|
+
existing `@ls-stack/strict-casting` configs and disable comments keep working.
|
|
19
|
+
|
|
20
|
+
### `strict-casting`
|
|
21
|
+
|
|
22
|
+
Enforces safe and necessary `String()`/`Number()` casts.
|
|
23
|
+
|
|
24
|
+
Checks performed:
|
|
25
|
+
|
|
26
|
+
- **Unnecessary casts** (auto-fixable): casting a value that is already of the target type
|
|
27
|
+
- **Nullish casts**: `String(value)` produces the strings `"null"`/`"undefined"`, and `Number(value)` produces `0` for `null` and `NaN` for `undefined`. A suggestion is provided to add a `?? ''`/`?? 0` fallback
|
|
28
|
+
- **Invalid `Number()` casts**: casting a `symbol` (throws at runtime) or an object without a numeric `valueOf` (always results in `NaN`)
|
|
29
|
+
- **`any`/`unknown` casts**: casting untyped values is unsafe, type or narrow the value before casting
|
|
30
|
+
|
|
31
|
+
Bad:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
declare const str: string
|
|
35
|
+
declare const nullable: string | null
|
|
36
|
+
declare const obj: { a: string }
|
|
37
|
+
|
|
38
|
+
String(str) // Error: unnecessary cast
|
|
39
|
+
String(nullable) // Error: may produce "null"
|
|
40
|
+
Number(obj) // Error: results in NaN
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Good:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
String(num)
|
|
47
|
+
String(nullable ?? '')
|
|
48
|
+
Number(date) // Date has a numeric valueOf
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Options:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
type Options = [
|
|
55
|
+
{
|
|
56
|
+
/** Extra functions checked for unnecessary casts */
|
|
57
|
+
additionalCastFunctions?: { name: string; expectedType: 'string' | 'number' }[]
|
|
58
|
+
/** Allow casting possibly nullish values (default: false) */
|
|
59
|
+
allowNullishCasting?: boolean
|
|
60
|
+
/** Allow symbol/object Number() casts (default: false) */
|
|
61
|
+
allowInvalidCasting?: boolean
|
|
62
|
+
/** Allow casting `any` values (default: false) */
|
|
63
|
+
allowAnyCasting?: boolean
|
|
64
|
+
/** Allow casting `unknown` values, e.g. `String(err)` in catch blocks (default: false) */
|
|
65
|
+
allowUnknownCasting?: boolean
|
|
66
|
+
},
|
|
67
|
+
]
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
For `String(obj)` producing `"[object Object]"`, use `@typescript-eslint/no-base-to-string`, which covers that case well.
|
|
71
|
+
|
|
72
|
+
## Usage
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
fast-lint # every file of ./tsconfig.json
|
|
76
|
+
fast-lint src/feature # only files under these paths
|
|
77
|
+
fast-lint --fix # apply autofixes (strict-casting's unnecessary casts)
|
|
78
|
+
fast-lint --format json # stylish (default), compact or json, in ESLint's formats
|
|
79
|
+
fast-lint --max-warnings 0 --timing # --max-warnings -1: no limit
|
|
80
|
+
fast-lint -p packages/app # a tsconfig, or a directory with a tsconfig.json
|
|
81
|
+
fast-lint --color -- -odd-name.ts # force colors; `--` ends the options
|
|
82
|
+
fast-lint --report-unused-disable-directives # warn about fast-lint-disable comments that suppress nothing
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
A solution-style tsconfig (empty `files` plus `references`) lints its referenced projects; a file in several
|
|
86
|
+
projects is linted once.
|
|
87
|
+
|
|
88
|
+
Paths are printed relative to the current directory. A file with a syntax error gets a single `Parsing error`
|
|
89
|
+
instead of rule diagnostics. `--quiet` hides warnings and doesn't apply their fixes; like ESLint, it skips `warn` rules
|
|
90
|
+
entirely unless `--max-warnings` is given, in which case warnings still count. `--format json` prints ESLint's JSON
|
|
91
|
+
result shape, listing only files with problems or fixes, with the file's `source` when it has problems and wasn't
|
|
92
|
+
fixed.
|
|
93
|
+
|
|
94
|
+
Exit code: `0` clean, `1` errors (or more warnings than `--max-warnings`), `2` fatal error: a tsconfig with a missing
|
|
95
|
+
`files` entry or `extends` target, no files to lint, a path argument that matches no project file, a directory
|
|
96
|
+
argument whose files are all ignored, or a fixed file that couldn't be written (reported after the results). A file
|
|
97
|
+
argument that is ignored, a declaration file or in `node_modules` is skipped with a warning, and so is fixing a file
|
|
98
|
+
that isn't UTF-8 (its problems are still reported).
|
|
99
|
+
|
|
100
|
+
## Config
|
|
101
|
+
|
|
102
|
+
`fast-lint.config.ts` (or `.mts`/`.mjs`/`.js`, evaluated by the Node launcher) or `fast-lint.config.json`, in the
|
|
103
|
+
current directory or passed with `--config`. Without a config every rule runs as `error` with default options.
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import { defineConfig } from '@ls-stack/fast-lint'
|
|
107
|
+
|
|
108
|
+
export default defineConfig({
|
|
109
|
+
projects: ['tsconfig.json'],
|
|
110
|
+
ignores: ['dist/**'],
|
|
111
|
+
rules: {
|
|
112
|
+
'improved-no-unnecessary-condition': ['error', { alternativeForInCondition: 'use objectHasKey' }],
|
|
113
|
+
'strict-casting': ['warn', { additionalCastFunctions: [{ name: 'castToString', expectedType: 'string' }] }],
|
|
114
|
+
'no-unnecessary-void-on-promise': 'error',
|
|
115
|
+
},
|
|
116
|
+
overrides: [
|
|
117
|
+
{
|
|
118
|
+
files: ['src/**'],
|
|
119
|
+
ignores: ['**/*.test.ts'],
|
|
120
|
+
rules: { 'strict-casting': 'error', 'no-leaked-conditional-rendering': 'error' },
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
reportUnusedDisableDirectives: true,
|
|
124
|
+
})
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Overrides apply in order, like ESLint flat config blocks: a severity-only entry keeps the options set by an earlier
|
|
128
|
+
block. Globs are relative to the config file's directory (`../` for files outside it). Global `ignores` follow ESLint's
|
|
129
|
+
semantics: a pattern matching a directory (`src/gen`, `**/out`, `dist/`, `dist/**`) ignores everything in it, and
|
|
130
|
+
`!pattern` un-ignores files ignored by earlier patterns, but not files inside an ignored directory: to keep a file of
|
|
131
|
+
an ignored directory, ignore its contents with `dir/**/*` instead. An override's `ignores` also accept `!pattern`,
|
|
132
|
+
applied in order. Of the extglobs, only `@(a|b)` is supported (write the others with `{a,b}` or several globs). Rule
|
|
133
|
+
names may carry a plugin prefix (`@ls-stack/strict-casting`), but not both forms of one rule in one block.
|
|
134
|
+
|
|
135
|
+
A script config's default export must be the config object, or a promise of it.
|
|
136
|
+
|
|
137
|
+
## Disable comments
|
|
138
|
+
|
|
139
|
+
`eslint-disable`, `eslint-enable`, `eslint-disable-line` and `eslint-disable-next-line` comments (and their
|
|
140
|
+
`oxlint-` and `fast-lint-` forms) are honored, with or without a rule list and a `-- description`, so existing
|
|
141
|
+
directives such as `// eslint-disable-next-line @ls-stack/improved-no-unnecessary-condition` keep working. They are
|
|
142
|
+
parsed like ESLint does: the comment text must start with the directive, `disable` and `enable` need a block comment
|
|
143
|
+
(`/* */`), and a multi-line `disable-next-line` block comment applies to the line after the comment ends. Inline
|
|
144
|
+
config comments (`/* eslint rule: "off" */`) are ignored.
|
|
145
|
+
|
|
146
|
+
`--report-unused-disable-directives` (or `reportUnusedDisableDirectives: true` in the config) warns about
|
|
147
|
+
`fast-lint-disable*` directives that suppressed nothing, like ESLint's option of the same name: a directive without a
|
|
148
|
+
rule list when no problem was suppressed, else each listed rule that ran on the file and reported nothing there. Only
|
|
149
|
+
`fast-lint-` directives are checked, since `eslint-` and `oxlint-` ones may belong to rules of those linters. The
|
|
150
|
+
warnings have no rule id and no fix. Under `--quiet` they are hidden, and they aren't computed unless
|
|
151
|
+
`--max-warnings` is given (warn rules don't run then, so their directives would look unused).
|
|
152
|
+
|
|
153
|
+
### Running alongside ESLint
|
|
154
|
+
|
|
155
|
+
When these rules are moved out of ESLint (set to `off` there, or their plugin removed), ESLint sees their
|
|
156
|
+
`eslint-disable` comments as unused (`reportUnusedDisableDirectives`, a warning by default in ESLint 9), and
|
|
157
|
+
`eslint --fix` deletes them, or as errors for an unknown rule when the plugin isn't loaded. Then fast-lint reports the
|
|
158
|
+
problems again. Either write these rules' directives as `fast-lint-disable*` (or `oxlint-disable*`), which ESLint
|
|
159
|
+
ignores, or set `linterOptions: { reportUnusedDisableDirectives: 'off' }` in the ESLint config, which also stops
|
|
160
|
+
reporting directives that are unused for the remaining ESLint rules.
|
|
161
|
+
|
|
162
|
+
## Differences from ESLint
|
|
163
|
+
|
|
164
|
+
- `--fix` applies one pass of non-overlapping fixes (ESLint repeats up to 10 passes).
|
|
165
|
+
- `improved-no-unnecessary-condition` reports nothing in a project without `strictNullChecks` (or `strict`), where
|
|
166
|
+
every type may be nullish.
|
|
167
|
+
- Types come from tsrs (TypeScript 7.1-dev semantics); printed types in messages can differ slightly from TS 5.x.
|
|
168
|
+
- Prebuilt binaries ship for macOS (arm64, x64), Linux (x64, arm64; glibc >= 2.17, not musl/Alpine) and Windows
|
|
169
|
+
x64 (also used on Windows arm64, through emulation). Elsewhere, set `FAST_LINT_BINARY` to a `cargo build --release`
|
|
170
|
+
binary. The launcher prints the package version for `--version`; the binary itself prints the crate version.
|
|
171
|
+
|
|
172
|
+
## Development
|
|
173
|
+
|
|
174
|
+
```sh
|
|
175
|
+
cargo test # rule tests (tests/*.rs)
|
|
176
|
+
cargo clippy --all-targets
|
|
177
|
+
node scripts/build.mjs # release binaries for every platform into dist/<platform>-<arch>/
|
|
178
|
+
node scripts/build.mjs --clean linux-x64 win32-x64 # some platforms, deleting their build dirs afterwards
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Cross-compiling from macOS needs the Rust targets (`rustup target add x86_64-apple-darwin
|
|
182
|
+
x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu x86_64-pc-windows-msvc`), `zig` + `cargo-zigbuild` for Linux, and
|
|
183
|
+
`cargo-xwin` + Homebrew `llvm` (for `llvm-lib`) for Windows. Each platform takes about 2 minutes (fat LTO).
|
|
184
|
+
|
|
185
|
+
The first build fetches tsrslint's git dependency, including its `typescript-go` submodule (~2 GB, cached in
|
|
186
|
+
`~/.cargo/git`). tsrslint is pinned by commit in `Cargo.toml`; `Cargo.lock` pins the tsrs commit it was tested with.
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from 'node:child_process'
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
4
|
+
import { constants } from 'node:os'
|
|
5
|
+
import { dirname, resolve } from 'node:path'
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
7
|
+
|
|
8
|
+
const scriptConfigNames = [
|
|
9
|
+
'fast-lint.config.ts',
|
|
10
|
+
'fast-lint.config.mts',
|
|
11
|
+
'fast-lint.config.mjs',
|
|
12
|
+
'fast-lint.config.js',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
const scriptConfigExtensions = ['.ts', '.mts', '.cts', '.mjs', '.cjs', '.js']
|
|
16
|
+
|
|
17
|
+
function binaryPath() {
|
|
18
|
+
if (process.env.FAST_LINT_BINARY) return process.env.FAST_LINT_BINARY
|
|
19
|
+
|
|
20
|
+
const platform = `${process.platform}-${process.arch}`
|
|
21
|
+
const exe = process.platform === 'win32' ? 'fast-lint.exe' : 'fast-lint'
|
|
22
|
+
const candidates =
|
|
23
|
+
platform === 'win32-arm64' ? [platform, 'win32-x64'] : [platform]
|
|
24
|
+
|
|
25
|
+
for (const candidate of candidates) {
|
|
26
|
+
const path = fileURLToPath(
|
|
27
|
+
new URL(`../dist/${candidate}/${exe}`, import.meta.url),
|
|
28
|
+
)
|
|
29
|
+
if (existsSync(path)) return path
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
throw new Error(
|
|
33
|
+
`no prebuilt binary for ${platform}. Build one with \`cargo build --release\` in packages/fast-lint and point FAST_LINT_BINARY at it`,
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function findConfigFlag(args) {
|
|
38
|
+
let found
|
|
39
|
+
|
|
40
|
+
for (let i = 0; i < args.length; i++) {
|
|
41
|
+
const arg = args[i]
|
|
42
|
+
if (arg === undefined) continue
|
|
43
|
+
|
|
44
|
+
if (arg === '-c' || arg === '--config') {
|
|
45
|
+
found = { index: i, length: 2, path: args[i + 1] }
|
|
46
|
+
i++
|
|
47
|
+
} else if (arg.startsWith('--config=')) {
|
|
48
|
+
found = { index: i, length: 1, path: arg.slice('--config='.length) }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return found
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isScriptConfig(path) {
|
|
56
|
+
return scriptConfigExtensions.some((ext) => path.endsWith(ext))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function loadScriptConfig(path) {
|
|
60
|
+
const module = await import(pathToFileURL(path).href)
|
|
61
|
+
|
|
62
|
+
if (!('default' in module)) {
|
|
63
|
+
throw new Error(`${path} has no default export`)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const config = await module.default
|
|
67
|
+
|
|
68
|
+
if (typeof config !== 'object' || config === null || Array.isArray(config)) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`${path}: the default export must be a config object or a promise of one`,
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return JSON.stringify(config)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function hasInlineConfig(args) {
|
|
78
|
+
return args.some(
|
|
79
|
+
(arg) => arg === '--config-json' || arg.startsWith('--config-json='),
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function packageVersion() {
|
|
84
|
+
const packageJson = JSON.parse(
|
|
85
|
+
readFileSync(new URL('../package.json', import.meta.url), 'utf8'),
|
|
86
|
+
)
|
|
87
|
+
return packageJson.version
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function resolveArgs(args) {
|
|
91
|
+
if (hasInlineConfig(args)) return args
|
|
92
|
+
|
|
93
|
+
const flag = findConfigFlag(args)
|
|
94
|
+
|
|
95
|
+
if (flag) {
|
|
96
|
+
if (!flag.path || !isScriptConfig(flag.path)) return args
|
|
97
|
+
|
|
98
|
+
const path = resolve(flag.path)
|
|
99
|
+
const rest = [...args]
|
|
100
|
+
rest.splice(flag.index, flag.length)
|
|
101
|
+
|
|
102
|
+
return [
|
|
103
|
+
...rest,
|
|
104
|
+
'--config-json',
|
|
105
|
+
await loadScriptConfig(path),
|
|
106
|
+
'--config-root',
|
|
107
|
+
dirname(path),
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const found = scriptConfigNames
|
|
112
|
+
.map((name) => resolve(name))
|
|
113
|
+
.find((path) => existsSync(path))
|
|
114
|
+
|
|
115
|
+
if (!found) return args
|
|
116
|
+
|
|
117
|
+
return [
|
|
118
|
+
...args,
|
|
119
|
+
'--config-json',
|
|
120
|
+
await loadScriptConfig(found),
|
|
121
|
+
'--config-root',
|
|
122
|
+
dirname(found),
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const rawArgs = process.argv.slice(2)
|
|
127
|
+
|
|
128
|
+
if (rawArgs.includes('-v') || rawArgs.includes('--version')) {
|
|
129
|
+
console.log(packageVersion())
|
|
130
|
+
process.exit(0)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const isHelp = rawArgs.includes('-h') || rawArgs.includes('--help')
|
|
134
|
+
|
|
135
|
+
let exe
|
|
136
|
+
let args
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
exe = binaryPath()
|
|
140
|
+
args = isHelp ? rawArgs : await resolveArgs(rawArgs)
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.error(
|
|
143
|
+
`fast-lint: ${error instanceof Error ? error.message : String(error)}`,
|
|
144
|
+
)
|
|
145
|
+
process.exit(2)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const child = spawn(exe, args, { stdio: 'inherit' })
|
|
149
|
+
|
|
150
|
+
const signals = ['SIGINT', 'SIGTERM', 'SIGHUP']
|
|
151
|
+
|
|
152
|
+
for (const signal of signals) {
|
|
153
|
+
process.on(signal, () => child.kill(signal))
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
child.on('error', (error) => {
|
|
157
|
+
const hint =
|
|
158
|
+
process.platform === 'linux' &&
|
|
159
|
+
!process.env.FAST_LINT_BINARY &&
|
|
160
|
+
'code' in error &&
|
|
161
|
+
error.code === 'ENOENT'
|
|
162
|
+
? ' (the prebuilt Linux binaries need glibc >= 2.17; on musl/Alpine point FAST_LINT_BINARY at a binary built there)'
|
|
163
|
+
: ''
|
|
164
|
+
console.error(`fast-lint: failed to run ${exe}: ${error.message}${hint}`)
|
|
165
|
+
process.exit(2)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
child.on('exit', (code, signal) => {
|
|
169
|
+
if (signal) {
|
|
170
|
+
process.exitCode = 128 + (constants.signals[signal] ?? 0)
|
|
171
|
+
|
|
172
|
+
if (signals.includes(signal)) {
|
|
173
|
+
process.removeAllListeners(signal)
|
|
174
|
+
process.kill(process.pid, signal)
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
process.exit()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
process.exit(code ?? 2)
|
|
182
|
+
})
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type Severity = 'off' | 'warn' | 'error' | 0 | 1 | 2
|
|
2
|
+
|
|
3
|
+
type RuleEntry<Options> = Severity | [Severity] | [Severity, Options]
|
|
4
|
+
|
|
5
|
+
export type ImprovedNoUnnecessaryConditionOptions = {
|
|
6
|
+
alternativeForInCondition?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type StrictCastingOptions = {
|
|
10
|
+
additionalCastFunctions?: {
|
|
11
|
+
name: string
|
|
12
|
+
expectedType: 'string' | 'number'
|
|
13
|
+
}[]
|
|
14
|
+
allowNullishCasting?: boolean
|
|
15
|
+
allowInvalidCasting?: boolean
|
|
16
|
+
allowAnyCasting?: boolean
|
|
17
|
+
allowUnknownCasting?: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type NoLeakedConditionalRenderingOptions = {
|
|
21
|
+
reactVersion?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type RuleOptions = {
|
|
25
|
+
'improved-no-unnecessary-condition': ImprovedNoUnnecessaryConditionOptions
|
|
26
|
+
'strict-casting': StrictCastingOptions
|
|
27
|
+
'no-unnecessary-void-on-promise': Record<string, never>
|
|
28
|
+
'no-leaked-conditional-rendering': NoLeakedConditionalRenderingOptions
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type RulesConfig = {
|
|
32
|
+
[Name in keyof RuleOptions as Name | `${string}/${Name}`]?: RuleEntry<
|
|
33
|
+
RuleOptions[Name]
|
|
34
|
+
>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type Override = {
|
|
38
|
+
files: string[]
|
|
39
|
+
ignores?: string[]
|
|
40
|
+
rules?: RulesConfig
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type FastLintConfig = {
|
|
44
|
+
projects?: string[]
|
|
45
|
+
ignores?: string[]
|
|
46
|
+
rules?: RulesConfig
|
|
47
|
+
overrides?: Override[]
|
|
48
|
+
reportUnusedDisableDirectives?: boolean
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function defineConfig(config: FastLintConfig): FastLintConfig
|
package/lib/index.mjs
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ls-stack/fast-lint",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Type-aware lint rules of @ls-stack/extended-lint running natively on tsrs, the Rust port of the TypeScript checker",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"fast-lint": "./bin/fast-lint.mjs"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./lib/index.d.ts",
|
|
13
|
+
"default": "./lib/index.mjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"bin",
|
|
18
|
+
"lib",
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "github:lucaslos/eslint-plugin-extended-lint"
|
|
24
|
+
},
|
|
25
|
+
"author": "Lucas Santos",
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=22.18.0"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"test": "cargo test",
|
|
31
|
+
"lint": "cargo fmt --check && cargo clippy --all-targets -- -D warnings",
|
|
32
|
+
"build": "pnpm test && pnpm lint && pnpm run build:no-test",
|
|
33
|
+
"build:no-test": "node scripts/build.mjs",
|
|
34
|
+
"pre-publish": "pnpm build"
|
|
35
|
+
}
|
|
6
36
|
}
|