@rdjperron/lint 0.2.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.
Files changed (53) hide show
  1. package/README.md +54 -53
  2. package/dist/config.d.mts +13 -4
  3. package/dist/config.mjs +70 -70
  4. package/dist/config.mjs.map +1 -1
  5. package/dist/index.d.mts +2 -2
  6. package/dist/index.mjs +50 -16
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/verify.d.mts +2 -2
  9. package/dist/verify.mjs +4 -4
  10. package/dist/verify.mjs.map +1 -1
  11. package/fixtures/README.md +4 -4
  12. package/fixtures/no-array-filter-map.fixture.ts +1 -1
  13. package/fixtures/no-chained-type-assertions.fixture.ts +1 -1
  14. package/fixtures/no-conditional-empty-object-spread.fixture.ts +1 -1
  15. package/fixtures/no-hardcoded-jsx-text.fixture.tsx +1 -1
  16. package/fixtures/no-known-value-widening.fixture.ts +1 -1
  17. package/fixtures/no-module-mocking.fixture.ts +1 -1
  18. package/fixtures/no-object-parameters.fixture.ts +1 -1
  19. package/fixtures/no-reduce-accumulator-copy.fixture.ts +1 -1
  20. package/fixtures/no-reflect-apply.fixture.ts +1 -1
  21. package/fixtures/no-reflect-get.fixture.ts +1 -1
  22. package/fixtures/no-runtime-typeof.fixture.ts +1 -1
  23. package/fixtures/no-shape-in-symbol-names.fixture.ts +1 -1
  24. package/fixtures/no-unknown-parameters.fixture.ts +1 -1
  25. package/fixtures/no-unknown-returns.fixture.ts +1 -1
  26. package/fixtures/no-unknown-type-aliases.fixture.ts +1 -1
  27. package/fixtures/no-unsafe-dictionary-type.fixture.ts +1 -1
  28. package/fixtures/no-use-effect.fixture.tsx +1 -1
  29. package/fixtures/no-widen-then-assert.fixture.ts +1 -1
  30. package/fixtures/require-safety-comment-for-type-assertion.fixture.ts +1 -1
  31. package/fixtures/static-public-env.fixture.ts +1 -1
  32. package/package.json +3 -4
  33. package/docs/rules/README.md +0 -43
  34. package/docs/rules/no-array-filter-map.md +0 -60
  35. package/docs/rules/no-chained-type-assertions.md +0 -52
  36. package/docs/rules/no-conditional-empty-object-spread.md +0 -50
  37. package/docs/rules/no-hardcoded-jsx-text.md +0 -52
  38. package/docs/rules/no-known-value-widening.md +0 -62
  39. package/docs/rules/no-module-mocking.md +0 -53
  40. package/docs/rules/no-object-parameters.md +0 -55
  41. package/docs/rules/no-reduce-accumulator-copy.md +0 -57
  42. package/docs/rules/no-reflect-apply.md +0 -56
  43. package/docs/rules/no-reflect-get.md +0 -51
  44. package/docs/rules/no-runtime-typeof.md +0 -61
  45. package/docs/rules/no-shape-in-symbol-names.md +0 -53
  46. package/docs/rules/no-unknown-parameters.md +0 -55
  47. package/docs/rules/no-unknown-returns.md +0 -61
  48. package/docs/rules/no-unknown-type-aliases.md +0 -52
  49. package/docs/rules/no-unsafe-dictionary-type.md +0 -56
  50. package/docs/rules/no-use-effect.md +0 -55
  51. package/docs/rules/no-widen-then-assert.md +0 -53
  52. package/docs/rules/require-safety-comment-for-type-assertion.md +0 -59
  53. package/docs/rules/static-public-env.md +0 -55
package/README.md CHANGED
@@ -1,20 +1,20 @@
1
1
  # @rdjperron/lint
2
2
 
3
- One Oxlint JS plugin, `rdj`, with twenty rules against low-evidence code, the config data a
3
+ One Oxlint JS plugin, `rdjperron`, with twenty rules against low-evidence code, the config data a
4
4
  project spreads into its own `vite.config.ts`, and the check that proves every rule still fires.
5
5
 
6
- | Entry | What it gives you |
7
- | ---------------------------- | ------------------------------------------------------------------------------------ |
8
- | `@rdjperron/lint` | The plugin. One `jsPlugins` entry, one prefix, twenty rules. |
9
- | `@rdjperron/lint/config` | `recommended`, the override builders, and the `fmt`, `ignore` and `staged` blocks. |
10
- | `@rdjperron/lint/verify` | `verifySuites`, `packageSuite`, `verifyPluginApiPin`, and the `rdj-lint-verify` bin. |
11
- | `@rdjperron/lint/fixtures/*` | One deliberately violating file per rule, the input the verify command lints. |
6
+ | Entry | What it gives you |
7
+ | ---------------------------- | ------------------------------------------------------------------------------------------ |
8
+ | `@rdjperron/lint` | The plugin. One `jsPlugins` entry, one prefix, twenty rules. |
9
+ | `@rdjperron/lint/config` | `recommended`, the override builders, and the `fmt`, `ignore` and `staged` blocks. |
10
+ | `@rdjperron/lint/verify` | `verifySuites`, `packageSuite`, `verifyPluginApiPin`, and the `rdjperron-lint-verify` bin. |
11
+ | `@rdjperron/lint/fixtures/*` | One deliberately violating file per rule, the input the verify command lints. |
12
12
 
13
13
  Seventeen of the rules are a copy of [dmmulroy/anti-slop](https://github.com/dmmulroy/anti-slop),
14
14
  owned and edited here (`src/anti-slop/VENDORED.md` in the source repo records the commit). The
15
15
  other three are written here.
16
16
 
17
- Tested on vite-plus 0.3.1, oxlint 1.81.0 and `@oxlint/plugins` 1.79.0 (2026-09-17).
17
+ Tested on vite-plus 0.3.1, oxlint 1.81.0 and `@oxlint/plugins` 1.79.0 (2026-09-18).
18
18
 
19
19
  ## Install
20
20
 
@@ -33,7 +33,7 @@ vp why @oxlint/plugins
33
33
  ```
34
34
 
35
35
  Pin `@oxlint/plugins` exactly to that number. The peer range here is a caret, so an exact pin
36
- anywhere in the same major satisfies it. Move the pin every time `vp` moves; `rdj-lint-verify`
36
+ anywhere in the same major satisfies it. Move the pin every time `vp` moves; `rdjperron-lint-verify`
37
37
  fails when the two drift apart.
38
38
 
39
39
  ## Use it
@@ -67,7 +67,7 @@ form, because the plugin declares its own `meta.name`:
67
67
 
68
68
  ```ts
69
69
  jsPlugins: ["@rdjperron/lint"];
70
- jsPlugins: [{ name: "rdj", specifier: "@rdjperron/lint" }];
70
+ jsPlugins: [{ name: "rdjperron", specifier: "@rdjperron/lint" }];
71
71
  ```
72
72
 
73
73
  ### What `extends` carries, and what it does not
@@ -91,19 +91,19 @@ refused; a package name is what works.
91
91
 
92
92
  ### The config exports
93
93
 
94
- | Export | What it is |
95
- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
96
- | `plugin` | `{ name: "rdj", specifier: "@rdjperron/lint" }`. |
97
- | `RULE_IDS`, `RuleId` | The twenty prefixed ids, as a readonly tuple and as a type. |
98
- | `recommended` | The config object for `lint.extends`: the plugin entry, the rules below, and the built-in choices. |
99
- | `react(files, options?)` | Browser globals, the hooks rules and the compiler's rules. `options.useEffectAllowedIn` names the one file allowed to call `useEffect`, puts its name in every finding, and switches the rule off for it. |
100
- | `i18n(files, options?)` | `rdj/no-hardcoded-jsx-text` at error. `options.pattern` sets the word pattern. |
101
- | `expo(files)` | `rdj/static-public-env` at error. |
102
- | `tests(files)` | `any`, `!`, `no-runtime-typeof` and the safety-comment rule off, with the reason on each. |
103
- | `fixtures(view?)` | Every rule at error on the verify view, `scripts/.lint-fixtures/**` by default. |
104
- | `fmt` | `printWidth` 100, double quotes, semicolons, two spaces. |
105
- | `ignore` | Dependencies, build output, the AI agent directories and the Vitest JSON report. |
106
- | `staged` | `vp check --fix` on JS and TS sources. |
94
+ | Export | What it is |
95
+ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
96
+ | `plugin` | `{ name: "rdjperron", specifier: "@rdjperron/lint" }`. |
97
+ | `RULE_IDS`, `RuleId` | The twenty prefixed ids, as a readonly tuple and as a type. |
98
+ | `recommended` | The config object for `lint.extends`: the plugin entry, the rules below, and the built-in choices. |
99
+ | `react(files, options?)` | The hooks rules, the compiler's rules and the globals. `options.env` is the environment those files run in, `{ browser: true }` by default, so a React Native consumer names its own. `options.useEffectAllowedIn` names the one file allowed to call `useEffect`, puts its name in every finding, and switches the rule off for it. |
100
+ | `i18n(files, options?)` | `rdjperron/no-hardcoded-jsx-text` at error. `options.pattern` sets the word pattern. |
101
+ | `expo(files)` | `rdjperron/static-public-env` at error. |
102
+ | `tests(files)` | `any`, `!`, `no-runtime-typeof` and the safety-comment rule off, with the reason on each. |
103
+ | `fixtures(view?)` | Every rule at error on the verify view, `scripts/.lint-fixtures/**` by default. |
104
+ | `fmt` | `printWidth` 100, double quotes, semicolons, two spaces. |
105
+ | `ignore` | Dependencies, build output, the AI agent directories and the Vitest JSON report. |
106
+ | `staged` | `vp check --fix` on JS and TS sources. |
107
107
 
108
108
  `recommended` puts every rule at `error` except `no-hardcoded-jsx-text` and `static-public-env`,
109
109
  which belong to a translated app and to an Expo app and are left to `i18n()` and `expo()`. The
@@ -112,7 +112,7 @@ measurements behind those severities are in the comments beside them.
112
112
  ## Prove the rules still fire
113
113
 
114
114
  ```bash
115
- npx rdj-lint-verify
115
+ npx rdjperron-lint-verify
116
116
  ```
117
117
 
118
118
  The fixtures ship in the package: `fixtures/<rule>.fixture.ts(x)` is one deliberately violating
@@ -122,7 +122,7 @@ go silently dead after a linter upgrade or a plugin API skew, and "reports nothi
122
122
  like "no violations". Nothing else notices.
123
123
 
124
124
  Your config has to carry `fixtures()` in its overrides, on the view path the command uses
125
- (`scripts/.lint-fixtures/rdj` sits under the default `scripts/.lint-fixtures/**`). This is not a
125
+ (`scripts/.lint-fixtures/rdjperron` sits under the default `scripts/.lint-fixtures/**`). This is not a
126
126
  convenience: `-A`, `-W` and `-D` reach built-in rules only and cannot switch a JS plugin rule on,
127
127
  so the override is the only way the rules you leave off on your own code get proven.
128
128
 
@@ -134,41 +134,42 @@ source repo's `scripts/verify-lint-fixtures.mjs` is the example.
134
134
 
135
135
  ## The rules
136
136
 
137
- Each page explains what the rule rejects, what to write instead, and what it deliberately allows.
138
-
139
- | Rule | What it rejects |
140
- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
141
- | [`rdj/no-array-filter-map`](docs/rules/no-array-filter-map.md) | `filter().map()`, two passes and an intermediate array. |
142
- | [`rdj/no-chained-type-assertions`](docs/rules/no-chained-type-assertions.md) | `x as unknown as T`, an assertion laundered through a second one. |
143
- | [`rdj/no-conditional-empty-object-spread`](docs/rules/no-conditional-empty-object-spread.md) | `...(cond ? { a } : {})`, omission hidden behind an empty object. |
144
- | [`rdj/no-hardcoded-jsx-text`](docs/rules/no-hardcoded-jsx-text.md) | Words written straight into JSX children. |
145
- | [`rdj/no-known-value-widening`](docs/rules/no-known-value-widening.md) | An annotation that throws away what inference already knew. |
146
- | [`rdj/no-module-mocking`](docs/rules/no-module-mocking.md) | `vi.mock` and friends, a test of the mock rather than the module. |
147
- | [`rdj/no-object-parameters`](docs/rules/no-object-parameters.md) | The bare `object` type on a function input. |
148
- | [`rdj/no-reduce-accumulator-copy`](docs/rules/no-reduce-accumulator-copy.md) | Spreading the accumulator inside `reduce`, quadratic by construction. |
149
- | [`rdj/no-reflect-apply`](docs/rules/no-reflect-apply.md) | `Reflect.apply`, a call the types cannot follow. |
150
- | [`rdj/no-reflect-get`](docs/rules/no-reflect-get.md) | `Reflect.get`, a property read the types cannot follow. |
151
- | [`rdj/no-runtime-typeof`](docs/rules/no-runtime-typeof.md) | `typeof` as a substitute for parsing input at its boundary. |
152
- | [`rdj/no-shape-in-symbol-names`](docs/rules/no-shape-in-symbol-names.md) | "shape" in a symbol name, structure in place of a domain role. |
153
- | [`rdj/no-unknown-parameters`](docs/rules/no-unknown-parameters.md) | `unknown` on a function input. |
154
- | [`rdj/no-unknown-returns`](docs/rules/no-unknown-returns.md) | `unknown` on a function output. |
155
- | [`rdj/no-unknown-type-aliases`](docs/rules/no-unknown-type-aliases.md) | A type alias that resolves to `unknown`. |
156
- | [`rdj/no-unsafe-dictionary-type`](docs/rules/no-unsafe-dictionary-type.md) | A dictionary whose values are `unknown`, `any` or `object`. |
157
- | [`rdj/no-use-effect`](docs/rules/no-use-effect.md) | Every `useEffect` and `useLayoutEffect` call or import. |
158
- | [`rdj/no-widen-then-assert`](docs/rules/no-widen-then-assert.md) | Widening a value and asserting it back. |
159
- | [`rdj/require-safety-comment-for-type-assertion`](docs/rules/require-safety-comment-for-type-assertion.md) | An assertion with no `SAFETY:` line naming the invariant. |
160
- | [`rdj/static-public-env`](docs/rules/static-public-env.md) | Any `process.env` read but `process.env.NAME`, the one form Expo inlines. |
137
+ Each rule file opens with the reason the rule exists and the case where it is legitimate to
138
+ scope it off; the messages, the options and the cases are in the rule and its tests.
139
+
140
+ | Rule | What it rejects |
141
+ | ----------------------------------------------------- | --------------------------------------------------------------------- |
142
+ | `rdjperron/no-array-filter-map` | `filter().map()`, two passes and an intermediate array. |
143
+ | `rdjperron/no-chained-type-assertions` | `x as unknown as T`, an assertion laundered through a second one. |
144
+ | `rdjperron/no-conditional-empty-object-spread` | `...(cond ? { a } : {})`, omission hidden behind an empty object. |
145
+ | `rdjperron/no-hardcoded-jsx-text` | Words written straight into JSX children. |
146
+ | `rdjperron/no-known-value-widening` | An annotation that throws away what inference already knew. |
147
+ | `rdjperron/no-module-mocking` | `vi.mock` and friends, a test of the mock rather than the module. |
148
+ | `rdjperron/no-object-parameters` | The bare `object` type on a function input. |
149
+ | `rdjperron/no-reduce-accumulator-copy` | Spreading the accumulator inside `reduce`, quadratic by construction. |
150
+ | `rdjperron/no-reflect-apply` | `Reflect.apply`, a call the types cannot follow. |
151
+ | `rdjperron/no-reflect-get` | `Reflect.get`, a property read the types cannot follow. |
152
+ | `rdjperron/no-runtime-typeof` | `typeof` as a substitute for parsing input at its boundary. |
153
+ | `rdjperron/no-shape-in-symbol-names` | The word "shape" in a symbol name, structure in place of a role. |
154
+ | `rdjperron/no-unknown-parameters` | `unknown` on a function input. |
155
+ | `rdjperron/no-unknown-returns` | `unknown` on a function output. |
156
+ | `rdjperron/no-unknown-type-aliases` | A type alias that resolves to `unknown`. |
157
+ | `rdjperron/no-unsafe-dictionary-type` | A dictionary whose values are `unknown`, `any` or `object`. |
158
+ | `rdjperron/no-use-effect` | Every `useEffect` and `useLayoutEffect` call or import. |
159
+ | `rdjperron/no-widen-then-assert` | Widening a value and asserting it back. |
160
+ | `rdjperron/require-safety-comment-for-type-assertion` | An assertion with no `SAFETY:` line naming the invariant. |
161
+ | `rdjperron/static-public-env` | A `process.env` read Expo's bundler does not replace at build time. |
161
162
 
162
163
  ### Options
163
164
 
164
- - `rdj/no-use-effect` takes `{ hint?: string }`, appended to every finding, so a project can name
165
+ - `rdjperron/no-use-effect` takes `{ hint?: string }`, appended to every finding, so a project can name
165
166
  its one sanctioned wrapper. `react(files, { useEffectAllowedIn })` sets it and exempts that file.
166
- - `rdj/no-hardcoded-jsx-text` takes `{ pattern?: string }`, a regex source compiled with `u`. The
167
+ - `rdjperron/no-hardcoded-jsx-text` takes `{ pattern?: string }`, a regex source compiled with `u`. The
167
168
  default `\p{L}{3,}` says three letters in a row is a word; digits, separators and bullets are
168
169
  not. It looks at JSX text and at string literals placed as children, never at attribute values.
169
- - `rdj/no-runtime-typeof` takes `{ allowInTypeGuards?: boolean }`. `recommended` turns it on: a
170
+ - `rdjperron/no-runtime-typeof` takes `{ allowInTypeGuards?: boolean }`. `recommended` turns it on: a
170
171
  `typeof` inside a type predicate is the decode the rule asks for, named and reusable.
171
- - `rdj/static-public-env` has no options.
172
+ - `rdjperron/static-public-env` has no options.
172
173
 
173
174
  ## Severities are measurements
174
175
 
package/dist/config.d.mts CHANGED
@@ -40,13 +40,13 @@ export interface Config {
40
40
  overrides?: Override[];
41
41
  }
42
42
  /**
43
- * The plugin entry. The alias is `rdj`, which is also the prefix in every
43
+ * The plugin entry. The alias is `rdjperron`, which is also the prefix in every
44
44
  * diagnostic and every disable comment, so a consumer that renames it renames
45
45
  * its own rule keys too.
46
46
  */
47
47
  export declare const plugin: JsPlugin;
48
48
  /** Every rule the plugin registers, prefixed. `fixtures()` and the verify bin read this list. */
49
- export declare const RULE_IDS: readonly ["rdj/no-array-filter-map", "rdj/no-chained-type-assertions", "rdj/no-conditional-empty-object-spread", "rdj/no-hardcoded-jsx-text", "rdj/no-known-value-widening", "rdj/no-module-mocking", "rdj/no-object-parameters", "rdj/no-reduce-accumulator-copy", "rdj/no-reflect-apply", "rdj/no-reflect-get", "rdj/no-runtime-typeof", "rdj/no-shape-in-symbol-names", "rdj/no-unknown-parameters", "rdj/no-unknown-returns", "rdj/no-unknown-type-aliases", "rdj/no-unsafe-dictionary-type", "rdj/no-use-effect", "rdj/no-widen-then-assert", "rdj/require-safety-comment-for-type-assertion", "rdj/static-public-env"];
49
+ export declare const RULE_IDS: readonly ["rdjperron/no-array-filter-map", "rdjperron/no-chained-type-assertions", "rdjperron/no-conditional-empty-object-spread", "rdjperron/no-hardcoded-jsx-text", "rdjperron/no-known-value-widening", "rdjperron/no-module-mocking", "rdjperron/no-object-parameters", "rdjperron/no-reduce-accumulator-copy", "rdjperron/no-reflect-apply", "rdjperron/no-reflect-get", "rdjperron/no-runtime-typeof", "rdjperron/no-shape-in-symbol-names", "rdjperron/no-unknown-parameters", "rdjperron/no-unknown-returns", "rdjperron/no-unknown-type-aliases", "rdjperron/no-unsafe-dictionary-type", "rdjperron/no-use-effect", "rdjperron/no-widen-then-assert", "rdjperron/require-safety-comment-for-type-assertion", "rdjperron/static-public-env"];
50
50
  export type RuleId = (typeof RULE_IDS)[number];
51
51
  /**
52
52
  * The base config, for `lint: { extends: [recommended] }`.
@@ -59,6 +59,15 @@ export type RuleId = (typeof RULE_IDS)[number];
59
59
  */
60
60
  export declare const recommended: Config;
61
61
  export interface ReactOptions {
62
+ /**
63
+ * The globals these files run with, as oxlint's `env`, defaulting to
64
+ * `{ browser: true }`. A React Native app has no DOM: oxlint ships no
65
+ * `react-native` environment, and `{ "shared-node-browser": true }` is the
66
+ * closest one, the timers and the console both runtimes agree on.
67
+ */
68
+ env?: {
69
+ [name: string]: boolean;
70
+ };
62
71
  /**
63
72
  * The one file allowed to call `useEffect`, as a path. Its name goes into the
64
73
  * `no-use-effect` hint for every React file, and the rule is off for it.
@@ -66,7 +75,7 @@ export interface ReactOptions {
66
75
  useEffectAllowedIn?: string;
67
76
  }
68
77
  /**
69
- * The React file set: browser globals, the hooks rules, and the compiler's own
78
+ * The React file set: the globals, the hooks rules, and the compiler's own
70
79
  * judgement. A consumer appends its own entries after these, because an override
71
80
  * that changes a `react/*` rule must repeat `plugins: ["react"]` or oxlint
72
81
  * silently drops the entry.
@@ -81,7 +90,7 @@ export interface I18nOptions {
81
90
  }
82
91
  /** Files whose JSX text belongs in a catalogue rather than in the source. */
83
92
  export declare function i18n(files: string[], options?: I18nOptions): Override[];
84
- /** Files Expo's bundler inlines `process.env` into, where only `process.env.NAME` survives. */
93
+ /** Files Expo's bundler inlines `process.env` into, where only the reads it replaces survive. */
85
94
  export declare function expo(files: string[]): Override[];
86
95
  /** Test files: four rules whose premise a test does not hold. */
87
96
  export declare function tests(files: string[]): Override[];
package/dist/config.mjs CHANGED
@@ -1,35 +1,35 @@
1
1
  //#region src/config.ts
2
2
  /**
3
- * The plugin entry. The alias is `rdj`, which is also the prefix in every
3
+ * The plugin entry. The alias is `rdjperron`, which is also the prefix in every
4
4
  * diagnostic and every disable comment, so a consumer that renames it renames
5
5
  * its own rule keys too.
6
6
  */
7
7
  const plugin = {
8
- name: "rdj",
8
+ name: "rdjperron",
9
9
  specifier: "@rdjperron/lint"
10
10
  };
11
11
  /** Every rule the plugin registers, prefixed. `fixtures()` and the verify bin read this list. */
12
12
  const RULE_IDS = [
13
- "rdj/no-array-filter-map",
14
- "rdj/no-chained-type-assertions",
15
- "rdj/no-conditional-empty-object-spread",
16
- "rdj/no-hardcoded-jsx-text",
17
- "rdj/no-known-value-widening",
18
- "rdj/no-module-mocking",
19
- "rdj/no-object-parameters",
20
- "rdj/no-reduce-accumulator-copy",
21
- "rdj/no-reflect-apply",
22
- "rdj/no-reflect-get",
23
- "rdj/no-runtime-typeof",
24
- "rdj/no-shape-in-symbol-names",
25
- "rdj/no-unknown-parameters",
26
- "rdj/no-unknown-returns",
27
- "rdj/no-unknown-type-aliases",
28
- "rdj/no-unsafe-dictionary-type",
29
- "rdj/no-use-effect",
30
- "rdj/no-widen-then-assert",
31
- "rdj/require-safety-comment-for-type-assertion",
32
- "rdj/static-public-env"
13
+ "rdjperron/no-array-filter-map",
14
+ "rdjperron/no-chained-type-assertions",
15
+ "rdjperron/no-conditional-empty-object-spread",
16
+ "rdjperron/no-hardcoded-jsx-text",
17
+ "rdjperron/no-known-value-widening",
18
+ "rdjperron/no-module-mocking",
19
+ "rdjperron/no-object-parameters",
20
+ "rdjperron/no-reduce-accumulator-copy",
21
+ "rdjperron/no-reflect-apply",
22
+ "rdjperron/no-reflect-get",
23
+ "rdjperron/no-runtime-typeof",
24
+ "rdjperron/no-shape-in-symbol-names",
25
+ "rdjperron/no-unknown-parameters",
26
+ "rdjperron/no-unknown-returns",
27
+ "rdjperron/no-unknown-type-aliases",
28
+ "rdjperron/no-unsafe-dictionary-type",
29
+ "rdjperron/no-use-effect",
30
+ "rdjperron/no-widen-then-assert",
31
+ "rdjperron/require-safety-comment-for-type-assertion",
32
+ "rdjperron/static-public-env"
33
33
  ];
34
34
  /**
35
35
  * The base config, for `lint: { extends: [recommended] }`.
@@ -43,24 +43,24 @@ const RULE_IDS = [
43
43
  const recommended = {
44
44
  jsPlugins: [plugin],
45
45
  rules: {
46
- "rdj/no-array-filter-map": "error",
47
- "rdj/no-chained-type-assertions": "error",
48
- "rdj/no-conditional-empty-object-spread": "error",
49
- "rdj/no-known-value-widening": "error",
50
- "rdj/no-module-mocking": "error",
51
- "rdj/no-object-parameters": "error",
52
- "rdj/no-reduce-accumulator-copy": "error",
53
- "rdj/no-reflect-apply": "error",
54
- "rdj/no-reflect-get": "error",
55
- "rdj/no-runtime-typeof": ["error", { allowInTypeGuards: true }],
56
- "rdj/no-shape-in-symbol-names": "error",
57
- "rdj/no-unknown-parameters": "error",
58
- "rdj/no-unknown-returns": "error",
59
- "rdj/no-unknown-type-aliases": "error",
60
- "rdj/no-unsafe-dictionary-type": "error",
61
- "rdj/no-widen-then-assert": "error",
62
- "rdj/require-safety-comment-for-type-assertion": "error",
63
- "rdj/no-use-effect": "error",
46
+ "rdjperron/no-array-filter-map": "error",
47
+ "rdjperron/no-chained-type-assertions": "error",
48
+ "rdjperron/no-conditional-empty-object-spread": "error",
49
+ "rdjperron/no-known-value-widening": "error",
50
+ "rdjperron/no-module-mocking": "error",
51
+ "rdjperron/no-object-parameters": "error",
52
+ "rdjperron/no-reduce-accumulator-copy": "error",
53
+ "rdjperron/no-reflect-apply": "error",
54
+ "rdjperron/no-reflect-get": "error",
55
+ "rdjperron/no-runtime-typeof": ["error", { allowInTypeGuards: true }],
56
+ "rdjperron/no-shape-in-symbol-names": "error",
57
+ "rdjperron/no-unknown-parameters": "error",
58
+ "rdjperron/no-unknown-returns": "error",
59
+ "rdjperron/no-unknown-type-aliases": "error",
60
+ "rdjperron/no-unsafe-dictionary-type": "error",
61
+ "rdjperron/no-widen-then-assert": "error",
62
+ "rdjperron/require-safety-comment-for-type-assertion": "error",
63
+ "rdjperron/no-use-effect": "error",
64
64
  "typescript/no-floating-promises": "error",
65
65
  "typescript/no-misused-promises": "error",
66
66
  "typescript/await-thenable": "error",
@@ -86,13 +86,13 @@ const recommended = {
86
86
  }
87
87
  };
88
88
  /**
89
- * The React file set: browser globals, the hooks rules, and the compiler's own
89
+ * The React file set: the globals, the hooks rules, and the compiler's own
90
90
  * judgement. A consumer appends its own entries after these, because an override
91
91
  * that changes a `react/*` rule must repeat `plugins: ["react"]` or oxlint
92
92
  * silently drops the entry.
93
93
  */
94
94
  function react(files, options = {}) {
95
- const { useEffectAllowedIn } = options;
95
+ const { env = { browser: true }, useEffectAllowedIn } = options;
96
96
  const rules = {
97
97
  "react/rules-of-hooks": "error",
98
98
  "react/exhaustive-deps": "warn",
@@ -105,14 +105,14 @@ function react(files, options = {}) {
105
105
  const overrides = [{
106
106
  files,
107
107
  plugins: ["react"],
108
- env: { browser: true },
108
+ env,
109
109
  rules
110
110
  }];
111
111
  if (useEffectAllowedIn !== void 0) {
112
- rules["rdj/no-use-effect"] = ["error", { hint: `The one sanctioned wrapper is ${useEffectAllowedIn}.` }];
112
+ rules["rdjperron/no-use-effect"] = ["error", { hint: `The one sanctioned wrapper is ${useEffectAllowedIn}.` }];
113
113
  overrides.push({
114
114
  files: [useEffectAllowedIn],
115
- rules: { "rdj/no-use-effect": "off" }
115
+ rules: { "rdjperron/no-use-effect": "off" }
116
116
  });
117
117
  }
118
118
  return overrides;
@@ -122,14 +122,14 @@ function i18n(files, options = {}) {
122
122
  const { pattern } = options;
123
123
  return [{
124
124
  files,
125
- rules: { "rdj/no-hardcoded-jsx-text": pattern === void 0 ? "error" : ["error", { pattern }] }
125
+ rules: { "rdjperron/no-hardcoded-jsx-text": pattern === void 0 ? "error" : ["error", { pattern }] }
126
126
  }];
127
127
  }
128
- /** Files Expo's bundler inlines `process.env` into, where only `process.env.NAME` survives. */
128
+ /** Files Expo's bundler inlines `process.env` into, where only the reads it replaces survive. */
129
129
  function expo(files) {
130
130
  return [{
131
131
  files,
132
- rules: { "rdj/static-public-env": "error" }
132
+ rules: { "rdjperron/static-public-env": "error" }
133
133
  }];
134
134
  }
135
135
  /** Test files: four rules whose premise a test does not hold. */
@@ -139,33 +139,33 @@ function tests(files) {
139
139
  rules: {
140
140
  "typescript/no-explicit-any": "off",
141
141
  "typescript/no-non-null-assertion": "off",
142
- "rdj/no-runtime-typeof": "off",
143
- "rdj/require-safety-comment-for-type-assertion": "off"
142
+ "rdjperron/no-runtime-typeof": "off",
143
+ "rdjperron/require-safety-comment-for-type-assertion": "off"
144
144
  }
145
145
  }];
146
146
  }
147
147
  /** Every rule of the plugin at `error`, so none of them can be missing from the list. */
148
148
  const FIXTURE_RULES = {
149
- "rdj/no-array-filter-map": "error",
150
- "rdj/no-chained-type-assertions": "error",
151
- "rdj/no-conditional-empty-object-spread": "error",
152
- "rdj/no-hardcoded-jsx-text": "error",
153
- "rdj/no-known-value-widening": "error",
154
- "rdj/no-module-mocking": "error",
155
- "rdj/no-object-parameters": "error",
156
- "rdj/no-reduce-accumulator-copy": "error",
157
- "rdj/no-reflect-apply": "error",
158
- "rdj/no-reflect-get": "error",
159
- "rdj/no-runtime-typeof": "error",
160
- "rdj/no-shape-in-symbol-names": "error",
161
- "rdj/no-unknown-parameters": "error",
162
- "rdj/no-unknown-returns": "error",
163
- "rdj/no-unknown-type-aliases": "error",
164
- "rdj/no-unsafe-dictionary-type": "error",
165
- "rdj/no-use-effect": "error",
166
- "rdj/no-widen-then-assert": "error",
167
- "rdj/require-safety-comment-for-type-assertion": "error",
168
- "rdj/static-public-env": "error"
149
+ "rdjperron/no-array-filter-map": "error",
150
+ "rdjperron/no-chained-type-assertions": "error",
151
+ "rdjperron/no-conditional-empty-object-spread": "error",
152
+ "rdjperron/no-hardcoded-jsx-text": "error",
153
+ "rdjperron/no-known-value-widening": "error",
154
+ "rdjperron/no-module-mocking": "error",
155
+ "rdjperron/no-object-parameters": "error",
156
+ "rdjperron/no-reduce-accumulator-copy": "error",
157
+ "rdjperron/no-reflect-apply": "error",
158
+ "rdjperron/no-reflect-get": "error",
159
+ "rdjperron/no-runtime-typeof": "error",
160
+ "rdjperron/no-shape-in-symbol-names": "error",
161
+ "rdjperron/no-unknown-parameters": "error",
162
+ "rdjperron/no-unknown-returns": "error",
163
+ "rdjperron/no-unknown-type-aliases": "error",
164
+ "rdjperron/no-unsafe-dictionary-type": "error",
165
+ "rdjperron/no-use-effect": "error",
166
+ "rdjperron/no-widen-then-assert": "error",
167
+ "rdjperron/require-safety-comment-for-type-assertion": "error",
168
+ "rdjperron/static-public-env": "error"
169
169
  };
170
170
  /**
171
171
  * The view the verify command lints the shipped fixtures through. Without this
@@ -1 +1 @@
1
- {"version":3,"file":"config.mjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * Config data for a consumer's `vite.config.ts` (or `.oxlintrc.ts`).\n *\n * The types below are declared locally rather than imported from `oxlint`:\n * `oxlint` is never a dependency of this package (`vp migrate` strips one, and a\n * direct dependency installs a second linter beside the Vite+ IDE stub), and a\n * consumer that installs only `vite-plus` and `@oxlint/plugins` has no\n * guaranteed path to oxlint's own `.d.ts`. They are structurally compatible with\n * `OxlintConfig` and `OxlintOverride`, checked by a consumer-shaped config in\n * `tests/config.test.ts`: arrays are mutable (oxlint's are), and `plugins` is\n * oxlint's own literal union, because a bare `string[]` is not assignable to it.\n */\n\n/** A severity as oxlint spells it in a config; the numeric forms are not used here. */\ntype Severity = \"off\" | \"warn\" | \"error\";\n\n/** A rule entry: a severity, or a severity followed by the rule's own options. */\ntype RuleEntry = Severity | [Severity, ...unknown[]];\n\ninterface Rules {\n [ruleId: string]: RuleEntry;\n}\n\n/** oxlint's built-in plugins, the values its `plugins` field accepts. */\ntype BuiltinPlugin =\n | \"eslint\"\n | \"import\"\n | \"jest\"\n | \"jsdoc\"\n | \"jsx-a11y\"\n | \"nextjs\"\n | \"node\"\n | \"oxc\"\n | \"promise\"\n | \"react\"\n | \"react-perf\"\n | \"typescript\"\n | \"unicorn\"\n | \"vitest\"\n | \"vue\";\n\ninterface JsPlugin {\n name: string;\n specifier: string;\n}\n\nexport interface Override {\n files: string[];\n excludeFiles?: string[];\n plugins?: BuiltinPlugin[];\n env?: { [name: string]: boolean };\n jsPlugins?: JsPlugin[];\n rules?: Rules;\n}\n\nexport interface Config {\n jsPlugins?: JsPlugin[];\n rules?: Rules;\n overrides?: Override[];\n}\n\n/**\n * The plugin entry. The alias is `rdj`, which is also the prefix in every\n * diagnostic and every disable comment, so a consumer that renames it renames\n * its own rule keys too.\n */\nexport const plugin: JsPlugin = { name: \"rdj\", specifier: \"@rdjperron/lint\" };\n\n/** Every rule the plugin registers, prefixed. `fixtures()` and the verify bin read this list. */\nexport const RULE_IDS = [\n \"rdj/no-array-filter-map\",\n \"rdj/no-chained-type-assertions\",\n \"rdj/no-conditional-empty-object-spread\",\n \"rdj/no-hardcoded-jsx-text\",\n \"rdj/no-known-value-widening\",\n \"rdj/no-module-mocking\",\n \"rdj/no-object-parameters\",\n \"rdj/no-reduce-accumulator-copy\",\n \"rdj/no-reflect-apply\",\n \"rdj/no-reflect-get\",\n \"rdj/no-runtime-typeof\",\n \"rdj/no-shape-in-symbol-names\",\n \"rdj/no-unknown-parameters\",\n \"rdj/no-unknown-returns\",\n \"rdj/no-unknown-type-aliases\",\n \"rdj/no-unsafe-dictionary-type\",\n \"rdj/no-use-effect\",\n \"rdj/no-widen-then-assert\",\n \"rdj/require-safety-comment-for-type-assertion\",\n \"rdj/static-public-env\",\n] as const;\n\nexport type RuleId = (typeof RULE_IDS)[number];\n\n/**\n * The base config, for `lint: { extends: [recommended] }`.\n *\n * Measured through `extends` on vite-plus 0.3.1 / oxlint 1.81.0 (2026-09-17):\n * `jsPlugins`, `rules`, `overrides`, `plugins`, `categories` and `options` are\n * inherited by the consumer's config, and the consumer's own entries win.\n * `env`, `settings` and `ignorePatterns` are not inherited, which is why this\n * object carries none of them and the consumer keeps writing its own.\n */\nexport const recommended: Config = {\n jsPlugins: [plugin],\n rules: {\n // The anti-slop rules. Measured on stack-lab 2026-08-20 and 2026-09-13, and\n // on rummy 2026-09-16 (vite-plus 0.3.1, oxlint 1.81.0): on real hand-written\n // code they fire only on the patterns they exist to reject, so a hit is the\n // finding. Land a new one at `warn` in the consumer, fix or scope what\n // fires, then let this default take over.\n \"rdj/no-array-filter-map\": \"error\",\n \"rdj/no-chained-type-assertions\": \"error\",\n \"rdj/no-conditional-empty-object-spread\": \"error\",\n \"rdj/no-known-value-widening\": \"error\",\n \"rdj/no-module-mocking\": \"error\",\n \"rdj/no-object-parameters\": \"error\",\n \"rdj/no-reduce-accumulator-copy\": \"error\",\n \"rdj/no-reflect-apply\": \"error\",\n \"rdj/no-reflect-get\": \"error\",\n // A `typeof` inside a type predicate is the decode the rule asks for, named\n // and reusable, and the pack already exempts a predicate's subject from\n // `no-unknown-parameters` (rummy, 2026-09-16).\n \"rdj/no-runtime-typeof\": [\"error\", { allowInTypeGuards: true }],\n \"rdj/no-shape-in-symbol-names\": \"error\",\n \"rdj/no-unknown-parameters\": \"error\",\n \"rdj/no-unknown-returns\": \"error\",\n \"rdj/no-unknown-type-aliases\": \"error\",\n \"rdj/no-unsafe-dictionary-type\": \"error\",\n \"rdj/no-widen-then-assert\": \"error\",\n \"rdj/require-safety-comment-for-type-assertion\": \"error\",\n // Derived state, event handlers and a reactive query cover every case; the\n // one sanctioned wrapper is named through `react(files, { useEffectAllowedIn })`.\n \"rdj/no-use-effect\": \"error\",\n\n // `no-hardcoded-jsx-text` and `static-public-env` are absent on purpose:\n // they belong to a translated app and to an Expo app. `i18n()` and `expo()`\n // switch them on for the files that want them.\n\n // Type-aware rules; they need `options.typeAware` in the consumer's config.\n \"typescript/no-floating-promises\": \"error\",\n \"typescript/no-misused-promises\": \"error\",\n \"typescript/await-thenable\": \"error\",\n // Measured on rummy 2026-09-16 before the cleanup: 5, 5 and 1 hits, zero after.\n \"typescript/no-unnecessary-type-assertion\": \"error\",\n \"typescript/no-misused-spread\": \"error\",\n \"typescript/require-array-sort-compare\": \"error\",\n \"typescript/no-explicit-any\": \"warn\",\n\n \"prefer-const\": [\"error\", { destructuring: \"all\" }],\n \"no-unused-vars\": [\"error\", { argsIgnorePattern: \"^_\", varsIgnorePattern: \"^_\" }],\n // oxlint 1.77 moved this into `suspicious`, where it contradicts the `^_`\n // convention above. The prefix wins.\n \"no-underscore-dangle\": \"off\",\n eqeqeq: [\"error\", \"always\", { null: \"ignore\" }],\n\n // Deliberate off-switches, held by both consumers since the 2026-08-20 setup.\n \"typescript/no-unsafe-type-assertion\": \"off\",\n \"no-await-in-loop\": \"off\",\n \"unicorn/consistent-function-scoping\": \"off\",\n \"promise/always-return\": \"off\",\n },\n};\n\nexport interface ReactOptions {\n /**\n * The one file allowed to call `useEffect`, as a path. Its name goes into the\n * `no-use-effect` hint for every React file, and the rule is off for it.\n */\n useEffectAllowedIn?: string;\n}\n\n/**\n * The React file set: browser globals, the hooks rules, and the compiler's own\n * judgement. A consumer appends its own entries after these, because an override\n * that changes a `react/*` rule must repeat `plugins: [\"react\"]` or oxlint\n * silently drops the entry.\n */\nexport function react(files: string[], options: ReactOptions = {}): Override[] {\n const { useEffectAllowedIn } = options;\n const rules: Rules = {\n \"react/rules-of-hooks\": \"error\",\n \"react/exhaustive-deps\": \"warn\",\n // React 19 and the automatic JSX runtime: nothing has to be in scope.\n \"react/react-in-jsx-scope\": \"off\",\n // The React Compiler's own rules: a component it silently skips is a\n // component that never memoizes.\n \"react/immutability\": \"error\",\n \"react/refs\": \"error\",\n \"react/purity\": \"error\",\n // A module that exports only components refreshes in place; any other\n // export re-runs every importer.\n \"react/only-export-components\": \"error\",\n };\n const overrides: Override[] = [{ files, plugins: [\"react\"], env: { browser: true }, rules }];\n\n if (useEffectAllowedIn !== undefined) {\n rules[\"rdj/no-use-effect\"] = [\n \"error\",\n { hint: `The one sanctioned wrapper is ${useEffectAllowedIn}.` },\n ];\n overrides.push({\n // The sanctioned wrapper exists precisely so every other file can be clean.\n files: [useEffectAllowedIn],\n rules: { \"rdj/no-use-effect\": \"off\" },\n });\n }\n\n return overrides;\n}\n\nexport interface I18nOptions {\n /**\n * The word pattern, a regex source compiled with `u`. The rule's default,\n * `\\p{L}{3,}`, reads three letters in a row as a word.\n */\n pattern?: string;\n}\n\n/** Files whose JSX text belongs in a catalogue rather than in the source. */\nexport function i18n(files: string[], options: I18nOptions = {}): Override[] {\n const { pattern } = options;\n return [\n {\n files,\n rules: {\n \"rdj/no-hardcoded-jsx-text\": pattern === undefined ? \"error\" : [\"error\", { pattern }],\n },\n },\n ];\n}\n\n/** Files Expo's bundler inlines `process.env` into, where only `process.env.NAME` survives. */\nexport function expo(files: string[]): Override[] {\n return [{ files, rules: { \"rdj/static-public-env\": \"error\" } }];\n}\n\n/** Test files: four rules whose premise a test does not hold. */\nexport function tests(files: string[]): Override[] {\n return [\n {\n files,\n rules: {\n // A fixture is allowed to be the wrong type on purpose.\n \"typescript/no-explicit-any\": \"off\",\n \"typescript/no-non-null-assertion\": \"off\",\n // `expect(typeof x).toBe(\"function\")` reads as an unparsed boundary, but\n // asserting the shape of your own public API is the point of the assertion.\n \"rdj/no-runtime-typeof\": \"off\",\n // A justification on every assertion in a test is noise (53 measured on\n // rummy, 2026-09-16).\n \"rdj/require-safety-comment-for-type-assertion\": \"off\",\n },\n },\n ];\n}\n\n/** Every rule of the plugin at `error`, so none of them can be missing from the list. */\nconst FIXTURE_RULES = {\n \"rdj/no-array-filter-map\": \"error\",\n \"rdj/no-chained-type-assertions\": \"error\",\n \"rdj/no-conditional-empty-object-spread\": \"error\",\n \"rdj/no-hardcoded-jsx-text\": \"error\",\n \"rdj/no-known-value-widening\": \"error\",\n \"rdj/no-module-mocking\": \"error\",\n \"rdj/no-object-parameters\": \"error\",\n \"rdj/no-reduce-accumulator-copy\": \"error\",\n \"rdj/no-reflect-apply\": \"error\",\n \"rdj/no-reflect-get\": \"error\",\n \"rdj/no-runtime-typeof\": \"error\",\n \"rdj/no-shape-in-symbol-names\": \"error\",\n \"rdj/no-unknown-parameters\": \"error\",\n \"rdj/no-unknown-returns\": \"error\",\n \"rdj/no-unknown-type-aliases\": \"error\",\n \"rdj/no-unsafe-dictionary-type\": \"error\",\n \"rdj/no-use-effect\": \"error\",\n \"rdj/no-widen-then-assert\": \"error\",\n \"rdj/require-safety-comment-for-type-assertion\": \"error\",\n \"rdj/static-public-env\": \"error\",\n} satisfies Record<RuleId, \"error\">;\n\n/**\n * The view the verify command lints the shipped fixtures through. Without this\n * override every rule the consumer leaves off reports nothing there, and \"no\n * findings\" is exactly what a dead rule looks like.\n */\nexport function fixtures(view = \"scripts/.lint-fixtures/**\"): Override[] {\n return [{ files: [view], rules: { ...FIXTURE_RULES } }];\n}\n\n/** The formatting both consumers settled on. Spread it and add what is yours. */\nexport const fmt = {\n printWidth: 100,\n singleQuote: false,\n semi: true,\n tabWidth: 2,\n useTabs: false,\n};\n\n/**\n * Paths no linter and no formatter should walk: build output, dependencies, the\n * directories AI coding agents scribble in, and the Vitest JSON report. Spread\n * it into `ignorePatterns` and append the repo's own.\n */\nexport const ignore: string[] = [\n \"node_modules/**\",\n \"**/node_modules/**\",\n \"dist/**\",\n \"**/dist/**\",\n ...[\n \".agents\",\n \".claude\",\n \".codex\",\n \".continue\",\n \".cursor\",\n \".gemini\",\n \".opencode\",\n \".pi\",\n \".roo\",\n \".windsurf\",\n ].flatMap((dir) => [`${dir}/**`, `**/${dir}/**`]),\n \"**/test-results.json\",\n];\n\n/** The pre-commit entry: `vp check --fix` is defined for JS and TS sources, nothing else. */\nexport const staged = {\n \"*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}\": \"vp check --fix\",\n};\n"],"mappings":";;;;;;AAkEA,MAAa,SAAmB;CAAE,MAAM;CAAO,WAAW;AAAkB;;AAG5E,MAAa,WAAW;CACtB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;AAaA,MAAa,cAAsB;CACjC,WAAW,CAAC,MAAM;CAClB,OAAO;EAML,2BAA2B;EAC3B,kCAAkC;EAClC,0CAA0C;EAC1C,+BAA+B;EAC/B,yBAAyB;EACzB,4BAA4B;EAC5B,kCAAkC;EAClC,wBAAwB;EACxB,sBAAsB;EAItB,yBAAyB,CAAC,SAAS,EAAE,mBAAmB,KAAK,CAAC;EAC9D,gCAAgC;EAChC,6BAA6B;EAC7B,0BAA0B;EAC1B,+BAA+B;EAC/B,iCAAiC;EACjC,4BAA4B;EAC5B,iDAAiD;EAGjD,qBAAqB;EAOrB,mCAAmC;EACnC,kCAAkC;EAClC,6BAA6B;EAE7B,4CAA4C;EAC5C,gCAAgC;EAChC,yCAAyC;EACzC,8BAA8B;EAE9B,gBAAgB,CAAC,SAAS,EAAE,eAAe,MAAM,CAAC;EAClD,kBAAkB,CAAC,SAAS;GAAE,mBAAmB;GAAM,mBAAmB;EAAK,CAAC;EAGhF,wBAAwB;EACxB,QAAQ;GAAC;GAAS;GAAU,EAAE,MAAM,SAAS;EAAC;EAG9C,uCAAuC;EACvC,oBAAoB;EACpB,uCAAuC;EACvC,yBAAyB;CAC3B;AACF;;;;;;;AAgBA,SAAgB,MAAM,OAAiB,UAAwB,CAAC,GAAe;CAC7E,MAAM,EAAE,uBAAuB;CAC/B,MAAM,QAAe;EACnB,wBAAwB;EACxB,yBAAyB;EAEzB,4BAA4B;EAG5B,sBAAsB;EACtB,cAAc;EACd,gBAAgB;EAGhB,gCAAgC;CAClC;CACA,MAAM,YAAwB,CAAC;EAAE;EAAO,SAAS,CAAC,OAAO;EAAG,KAAK,EAAE,SAAS,KAAK;EAAG;CAAM,CAAC;CAE3F,IAAI,uBAAuB,KAAA,GAAW;EACpC,MAAM,uBAAuB,CAC3B,SACA,EAAE,MAAM,iCAAiC,mBAAmB,GAAG,CACjE;EACA,UAAU,KAAK;GAEb,OAAO,CAAC,kBAAkB;GAC1B,OAAO,EAAE,qBAAqB,MAAM;EACtC,CAAC;CACH;CAEA,OAAO;AACT;;AAWA,SAAgB,KAAK,OAAiB,UAAuB,CAAC,GAAe;CAC3E,MAAM,EAAE,YAAY;CACpB,OAAO,CACL;EACE;EACA,OAAO,EACL,6BAA6B,YAAY,KAAA,IAAY,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,EACtF;CACF,CACF;AACF;;AAGA,SAAgB,KAAK,OAA6B;CAChD,OAAO,CAAC;EAAE;EAAO,OAAO,EAAE,yBAAyB,QAAQ;CAAE,CAAC;AAChE;;AAGA,SAAgB,MAAM,OAA6B;CACjD,OAAO,CACL;EACE;EACA,OAAO;GAEL,8BAA8B;GAC9B,oCAAoC;GAGpC,yBAAyB;GAGzB,iDAAiD;EACnD;CACF,CACF;AACF;;AAGA,MAAM,gBAAgB;CACpB,2BAA2B;CAC3B,kCAAkC;CAClC,0CAA0C;CAC1C,6BAA6B;CAC7B,+BAA+B;CAC/B,yBAAyB;CACzB,4BAA4B;CAC5B,kCAAkC;CAClC,wBAAwB;CACxB,sBAAsB;CACtB,yBAAyB;CACzB,gCAAgC;CAChC,6BAA6B;CAC7B,0BAA0B;CAC1B,+BAA+B;CAC/B,iCAAiC;CACjC,qBAAqB;CACrB,4BAA4B;CAC5B,iDAAiD;CACjD,yBAAyB;AAC3B;;;;;;AAOA,SAAgB,SAAS,OAAO,6BAAyC;CACvE,OAAO,CAAC;EAAE,OAAO,CAAC,IAAI;EAAG,OAAO,EAAE,GAAG,cAAc;CAAE,CAAC;AACxD;;AAGA,MAAa,MAAM;CACjB,YAAY;CACZ,aAAa;CACb,MAAM;CACN,UAAU;CACV,SAAS;AACX;;;;;;AAOA,MAAa,SAAmB;CAC9B;CACA;CACA;CACA;CACA,GAAG;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,SAAS,QAAQ,CAAC,GAAG,IAAI,MAAM,MAAM,IAAI,IAAI,CAAC;CAChD;AACF;;AAGA,MAAa,SAAS,EACpB,qCAAqC,iBACvC"}
1
+ {"version":3,"file":"config.mjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * Config data for a consumer's `vite.config.ts` (or `.oxlintrc.ts`).\n *\n * The types below are declared locally rather than imported from `oxlint`:\n * `oxlint` is never a dependency of this package (`vp migrate` strips one, and a\n * direct dependency installs a second linter beside the Vite+ IDE stub), and a\n * consumer that installs only `vite-plus` and `@oxlint/plugins` has no\n * guaranteed path to oxlint's own `.d.ts`. They are structurally compatible with\n * `OxlintConfig` and `OxlintOverride`, checked by a consumer-shaped config in\n * `tests/config.test.ts`: arrays are mutable (oxlint's are), and `plugins` is\n * oxlint's own literal union, because a bare `string[]` is not assignable to it.\n */\n\n/** A severity as oxlint spells it in a config; the numeric forms are not used here. */\ntype Severity = \"off\" | \"warn\" | \"error\";\n\n/** A rule entry: a severity, or a severity followed by the rule's own options. */\ntype RuleEntry = Severity | [Severity, ...unknown[]];\n\ninterface Rules {\n [ruleId: string]: RuleEntry;\n}\n\n/** oxlint's built-in plugins, the values its `plugins` field accepts. */\ntype BuiltinPlugin =\n | \"eslint\"\n | \"import\"\n | \"jest\"\n | \"jsdoc\"\n | \"jsx-a11y\"\n | \"nextjs\"\n | \"node\"\n | \"oxc\"\n | \"promise\"\n | \"react\"\n | \"react-perf\"\n | \"typescript\"\n | \"unicorn\"\n | \"vitest\"\n | \"vue\";\n\ninterface JsPlugin {\n name: string;\n specifier: string;\n}\n\nexport interface Override {\n files: string[];\n excludeFiles?: string[];\n plugins?: BuiltinPlugin[];\n env?: { [name: string]: boolean };\n jsPlugins?: JsPlugin[];\n rules?: Rules;\n}\n\nexport interface Config {\n jsPlugins?: JsPlugin[];\n rules?: Rules;\n overrides?: Override[];\n}\n\n/**\n * The plugin entry. The alias is `rdjperron`, which is also the prefix in every\n * diagnostic and every disable comment, so a consumer that renames it renames\n * its own rule keys too.\n */\nexport const plugin: JsPlugin = { name: \"rdjperron\", specifier: \"@rdjperron/lint\" };\n\n/** Every rule the plugin registers, prefixed. `fixtures()` and the verify bin read this list. */\nexport const RULE_IDS = [\n \"rdjperron/no-array-filter-map\",\n \"rdjperron/no-chained-type-assertions\",\n \"rdjperron/no-conditional-empty-object-spread\",\n \"rdjperron/no-hardcoded-jsx-text\",\n \"rdjperron/no-known-value-widening\",\n \"rdjperron/no-module-mocking\",\n \"rdjperron/no-object-parameters\",\n \"rdjperron/no-reduce-accumulator-copy\",\n \"rdjperron/no-reflect-apply\",\n \"rdjperron/no-reflect-get\",\n \"rdjperron/no-runtime-typeof\",\n \"rdjperron/no-shape-in-symbol-names\",\n \"rdjperron/no-unknown-parameters\",\n \"rdjperron/no-unknown-returns\",\n \"rdjperron/no-unknown-type-aliases\",\n \"rdjperron/no-unsafe-dictionary-type\",\n \"rdjperron/no-use-effect\",\n \"rdjperron/no-widen-then-assert\",\n \"rdjperron/require-safety-comment-for-type-assertion\",\n \"rdjperron/static-public-env\",\n] as const;\n\nexport type RuleId = (typeof RULE_IDS)[number];\n\n/**\n * The base config, for `lint: { extends: [recommended] }`.\n *\n * Measured through `extends` on vite-plus 0.3.1 / oxlint 1.81.0 (2026-09-17):\n * `jsPlugins`, `rules`, `overrides`, `plugins`, `categories` and `options` are\n * inherited by the consumer's config, and the consumer's own entries win.\n * `env`, `settings` and `ignorePatterns` are not inherited, which is why this\n * object carries none of them and the consumer keeps writing its own.\n */\nexport const recommended: Config = {\n jsPlugins: [plugin],\n rules: {\n // The anti-slop rules. Measured on stack-lab 2026-08-20 and 2026-09-13, and\n // on rummy 2026-09-16 (vite-plus 0.3.1, oxlint 1.81.0): on real hand-written\n // code they fire only on the patterns they exist to reject, so a hit is the\n // finding. Land a new one at `warn` in the consumer, fix or scope what\n // fires, then let this default take over.\n \"rdjperron/no-array-filter-map\": \"error\",\n \"rdjperron/no-chained-type-assertions\": \"error\",\n \"rdjperron/no-conditional-empty-object-spread\": \"error\",\n \"rdjperron/no-known-value-widening\": \"error\",\n \"rdjperron/no-module-mocking\": \"error\",\n \"rdjperron/no-object-parameters\": \"error\",\n \"rdjperron/no-reduce-accumulator-copy\": \"error\",\n \"rdjperron/no-reflect-apply\": \"error\",\n \"rdjperron/no-reflect-get\": \"error\",\n // A `typeof` inside a type predicate is the decode the rule asks for, named\n // and reusable, and the pack already exempts a predicate's subject from\n // `no-unknown-parameters` (rummy, 2026-09-16).\n \"rdjperron/no-runtime-typeof\": [\"error\", { allowInTypeGuards: true }],\n \"rdjperron/no-shape-in-symbol-names\": \"error\",\n \"rdjperron/no-unknown-parameters\": \"error\",\n \"rdjperron/no-unknown-returns\": \"error\",\n \"rdjperron/no-unknown-type-aliases\": \"error\",\n \"rdjperron/no-unsafe-dictionary-type\": \"error\",\n \"rdjperron/no-widen-then-assert\": \"error\",\n \"rdjperron/require-safety-comment-for-type-assertion\": \"error\",\n // Derived state, event handlers and a reactive query cover every case; the\n // one sanctioned wrapper is named through `react(files, { useEffectAllowedIn })`.\n \"rdjperron/no-use-effect\": \"error\",\n\n // `no-hardcoded-jsx-text` and `static-public-env` are absent on purpose:\n // they belong to a translated app and to an Expo app. `i18n()` and `expo()`\n // switch them on for the files that want them.\n\n // Type-aware rules; they need `options.typeAware` in the consumer's config.\n \"typescript/no-floating-promises\": \"error\",\n \"typescript/no-misused-promises\": \"error\",\n \"typescript/await-thenable\": \"error\",\n // Measured on rummy 2026-09-16 before the cleanup: 5, 5 and 1 hits, zero after.\n \"typescript/no-unnecessary-type-assertion\": \"error\",\n \"typescript/no-misused-spread\": \"error\",\n \"typescript/require-array-sort-compare\": \"error\",\n \"typescript/no-explicit-any\": \"warn\",\n\n \"prefer-const\": [\"error\", { destructuring: \"all\" }],\n \"no-unused-vars\": [\"error\", { argsIgnorePattern: \"^_\", varsIgnorePattern: \"^_\" }],\n // oxlint 1.77 moved this into `suspicious`, where it contradicts the `^_`\n // convention above. The prefix wins.\n \"no-underscore-dangle\": \"off\",\n eqeqeq: [\"error\", \"always\", { null: \"ignore\" }],\n\n // Deliberate off-switches, held by both consumers since the 2026-08-20 setup.\n \"typescript/no-unsafe-type-assertion\": \"off\",\n \"no-await-in-loop\": \"off\",\n \"unicorn/consistent-function-scoping\": \"off\",\n \"promise/always-return\": \"off\",\n },\n};\n\nexport interface ReactOptions {\n /**\n * The globals these files run with, as oxlint's `env`, defaulting to\n * `{ browser: true }`. A React Native app has no DOM: oxlint ships no\n * `react-native` environment, and `{ \"shared-node-browser\": true }` is the\n * closest one, the timers and the console both runtimes agree on.\n */\n env?: { [name: string]: boolean };\n /**\n * The one file allowed to call `useEffect`, as a path. Its name goes into the\n * `no-use-effect` hint for every React file, and the rule is off for it.\n */\n useEffectAllowedIn?: string;\n}\n\n/**\n * The React file set: the globals, the hooks rules, and the compiler's own\n * judgement. A consumer appends its own entries after these, because an override\n * that changes a `react/*` rule must repeat `plugins: [\"react\"]` or oxlint\n * silently drops the entry.\n */\nexport function react(files: string[], options: ReactOptions = {}): Override[] {\n const { env = { browser: true }, useEffectAllowedIn } = options;\n const rules: Rules = {\n \"react/rules-of-hooks\": \"error\",\n \"react/exhaustive-deps\": \"warn\",\n // React 19 and the automatic JSX runtime: nothing has to be in scope.\n \"react/react-in-jsx-scope\": \"off\",\n // The React Compiler's own rules: a component it silently skips is a\n // component that never memoizes.\n \"react/immutability\": \"error\",\n \"react/refs\": \"error\",\n \"react/purity\": \"error\",\n // A module that exports only components refreshes in place; any other\n // export re-runs every importer.\n \"react/only-export-components\": \"error\",\n };\n const overrides: Override[] = [{ files, plugins: [\"react\"], env, rules }];\n\n if (useEffectAllowedIn !== undefined) {\n rules[\"rdjperron/no-use-effect\"] = [\n \"error\",\n { hint: `The one sanctioned wrapper is ${useEffectAllowedIn}.` },\n ];\n overrides.push({\n // The sanctioned wrapper exists precisely so every other file can be clean.\n files: [useEffectAllowedIn],\n rules: { \"rdjperron/no-use-effect\": \"off\" },\n });\n }\n\n return overrides;\n}\n\nexport interface I18nOptions {\n /**\n * The word pattern, a regex source compiled with `u`. The rule's default,\n * `\\p{L}{3,}`, reads three letters in a row as a word.\n */\n pattern?: string;\n}\n\n/** Files whose JSX text belongs in a catalogue rather than in the source. */\nexport function i18n(files: string[], options: I18nOptions = {}): Override[] {\n const { pattern } = options;\n return [\n {\n files,\n rules: {\n \"rdjperron/no-hardcoded-jsx-text\": pattern === undefined ? \"error\" : [\"error\", { pattern }],\n },\n },\n ];\n}\n\n/** Files Expo's bundler inlines `process.env` into, where only the reads it replaces survive. */\nexport function expo(files: string[]): Override[] {\n return [{ files, rules: { \"rdjperron/static-public-env\": \"error\" } }];\n}\n\n/** Test files: four rules whose premise a test does not hold. */\nexport function tests(files: string[]): Override[] {\n return [\n {\n files,\n rules: {\n // A fixture is allowed to be the wrong type on purpose.\n \"typescript/no-explicit-any\": \"off\",\n \"typescript/no-non-null-assertion\": \"off\",\n // `expect(typeof x).toBe(\"function\")` reads as an unparsed boundary, but\n // asserting the shape of your own public API is the point of the assertion.\n \"rdjperron/no-runtime-typeof\": \"off\",\n // A justification on every assertion in a test is noise (53 measured on\n // rummy, 2026-09-16).\n \"rdjperron/require-safety-comment-for-type-assertion\": \"off\",\n },\n },\n ];\n}\n\n/** Every rule of the plugin at `error`, so none of them can be missing from the list. */\nconst FIXTURE_RULES = {\n \"rdjperron/no-array-filter-map\": \"error\",\n \"rdjperron/no-chained-type-assertions\": \"error\",\n \"rdjperron/no-conditional-empty-object-spread\": \"error\",\n \"rdjperron/no-hardcoded-jsx-text\": \"error\",\n \"rdjperron/no-known-value-widening\": \"error\",\n \"rdjperron/no-module-mocking\": \"error\",\n \"rdjperron/no-object-parameters\": \"error\",\n \"rdjperron/no-reduce-accumulator-copy\": \"error\",\n \"rdjperron/no-reflect-apply\": \"error\",\n \"rdjperron/no-reflect-get\": \"error\",\n \"rdjperron/no-runtime-typeof\": \"error\",\n \"rdjperron/no-shape-in-symbol-names\": \"error\",\n \"rdjperron/no-unknown-parameters\": \"error\",\n \"rdjperron/no-unknown-returns\": \"error\",\n \"rdjperron/no-unknown-type-aliases\": \"error\",\n \"rdjperron/no-unsafe-dictionary-type\": \"error\",\n \"rdjperron/no-use-effect\": \"error\",\n \"rdjperron/no-widen-then-assert\": \"error\",\n \"rdjperron/require-safety-comment-for-type-assertion\": \"error\",\n \"rdjperron/static-public-env\": \"error\",\n} satisfies Record<RuleId, \"error\">;\n\n/**\n * The view the verify command lints the shipped fixtures through. Without this\n * override every rule the consumer leaves off reports nothing there, and \"no\n * findings\" is exactly what a dead rule looks like.\n */\nexport function fixtures(view = \"scripts/.lint-fixtures/**\"): Override[] {\n return [{ files: [view], rules: { ...FIXTURE_RULES } }];\n}\n\n/** The formatting both consumers settled on. Spread it and add what is yours. */\nexport const fmt = {\n printWidth: 100,\n singleQuote: false,\n semi: true,\n tabWidth: 2,\n useTabs: false,\n};\n\n/**\n * Paths no linter and no formatter should walk: build output, dependencies, the\n * directories AI coding agents scribble in, and the Vitest JSON report. Spread\n * it into `ignorePatterns` and append the repo's own.\n */\nexport const ignore: string[] = [\n \"node_modules/**\",\n \"**/node_modules/**\",\n \"dist/**\",\n \"**/dist/**\",\n ...[\n \".agents\",\n \".claude\",\n \".codex\",\n \".continue\",\n \".cursor\",\n \".gemini\",\n \".opencode\",\n \".pi\",\n \".roo\",\n \".windsurf\",\n ].flatMap((dir) => [`${dir}/**`, `**/${dir}/**`]),\n \"**/test-results.json\",\n];\n\n/** The pre-commit entry: `vp check --fix` is defined for JS and TS sources, nothing else. */\nexport const staged = {\n \"*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}\": \"vp check --fix\",\n};\n"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region src/index.d.ts
2
- declare const rdjPlugin: import("@oxlint/plugins").Plugin;
2
+ declare const rdjperronPlugin: import("@oxlint/plugins").Plugin;
3
3
  //#endregion
4
- export { rdjPlugin as default };
4
+ export { rdjperronPlugin as default };
5
5
  //# sourceMappingURL=index.d.mts.map