@jameswyse/oxc-config 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -163
- package/REFERENCE.md +105 -132
- package/dist/format.d.ts +5 -3
- package/dist/index.js +26 -0
- package/dist/lint.d.ts +3 -1
- package/dist/lint.js +153 -14
- package/dist/rules/explicit-api-states.js +7 -1
- package/dist/rules/import-boundaries.js +41 -13
- package/dist/rules/no-derived-test-expectation.d.ts +1 -0
- package/dist/rules/no-derived-test-expectation.js +136 -0
- package/dist/rules/no-disabled-tls-verification.d.ts +1 -0
- package/dist/rules/no-disabled-tls-verification.js +42 -0
- package/dist/rules/no-fixed-test-delay.js +46 -5
- package/dist/rules/no-generic-object-guard.d.ts +1 -0
- package/dist/rules/no-generic-object-guard.js +279 -0
- package/dist/rules/no-get-password-form.d.ts +1 -0
- package/dist/rules/no-get-password-form.js +113 -0
- package/dist/rules/no-hardcoded-credential.d.ts +1 -0
- package/dist/rules/no-hardcoded-credential.js +45 -0
- package/dist/rules/no-import-time-effects.d.ts +1 -0
- package/dist/rules/no-import-time-effects.js +90 -0
- package/dist/rules/no-optional-test-action.d.ts +1 -0
- package/dist/rules/no-optional-test-action.js +45 -0
- package/dist/rules/no-secret-fallback.d.ts +1 -0
- package/dist/rules/no-secret-fallback.js +64 -0
- package/dist/rules/no-swallowed-errors.js +131 -16
- package/dist/rules/no-test-timeout-overrides.js +22 -9
- package/dist/rules/no-unchecked-fetch-response.d.ts +1 -0
- package/dist/rules/no-unchecked-fetch-response.js +91 -0
- package/dist/rules/no-unchecked-index-search.d.ts +1 -0
- package/dist/rules/no-unchecked-index-search.js +41 -0
- package/dist/rules/no-uncontrolled-test-clock.d.ts +1 -0
- package/dist/rules/no-uncontrolled-test-clock.js +112 -0
- package/dist/rules/no-unsettled-promise.d.ts +1 -0
- package/dist/rules/no-unsettled-promise.js +64 -0
- package/dist/rules/require-suppression-reason.d.ts +1 -0
- package/dist/rules/require-suppression-reason.js +32 -0
- package/dist/shared/index.d.ts +9 -0
- package/dist/shared/index.js +70 -0
- package/dist/vendor/anti-slop/effect/rules/no-service-constructor-imports.js +10 -2
- package/dist/vendor/anti-slop/rules/no-known-value-widening.js +1 -1
- package/dist/vendor/anti-slop/rules/no-module-mocking.js +56 -1
- package/dist/vendor/anti-slop/rules/no-runtime-typeof.js +30 -1
- package/dist/vendor/anti-slop/rules/no-shape-in-symbol-names.js +28 -1
- package/dist/vendor/anti-slop/rules/no-unknown-returns.js +1 -1
- package/dist/vendor/anti-slop/rules/no-unsafe-dictionary-type.js +2 -2
- package/dist/vendor/anti-slop/shared/dictionary-types.js +72 -89
- package/dist/vendor/anti-slop/shared/type-alias-resolution.d.ts +8 -2
- package/dist/vendor/anti-slop/shared/type-alias-resolution.js +2 -2
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -2,204 +2,71 @@
|
|
|
2
2
|
|
|
3
3
|
Opinionated [Oxlint](https://oxc.rs/docs/guide/usage/linter.html) and [Oxfmt](https://oxc.rs/docs/guide/usage/formatter.html) configs for TypeScript and JavaScript projects. They combine Oxlint's native rules with custom rules for type and data safety, comment-free code, configuration ownership, import boundaries, server-action guards, test reliability and statement spacing.
|
|
4
4
|
|
|
5
|
-
The rules aim to keep both people and coding agents on the same standards: parse external data at the seam, use named exports, never ship console logging, and let names, types and tests carry intent instead of comments. [The reference](REFERENCE.md) maps each standard to its rules
|
|
5
|
+
The rules aim to keep both people and coding agents on the same standards: parse external data at the seam, use named exports, never ship console logging, and let names, types and tests carry intent instead of comments. [The reference](REFERENCE.md) maps each standard to its rules.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Example: a test that can't fail
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
```sh
|
|
12
|
-
pnpm add -D @jameswyse/oxc-config oxlint oxfmt oxlint-tsgolint
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
The React profile uses `eslint-plugin-react-you-might-not-need-an-effect`, which declares an ESLint peer even though it runs under Oxlint. To skip installing ESLint with pnpm, add this to the project's `pnpm-workspace.yaml` before installing:
|
|
16
|
-
|
|
17
|
-
```yaml
|
|
18
|
-
autoInstallPeers: false
|
|
19
|
-
peerDependencyRules:
|
|
20
|
-
ignoreMissing:
|
|
21
|
-
- eslint
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
`autoInstallPeers` affects every dependency in the workspace. No ESLint configuration is needed either way.
|
|
25
|
-
|
|
26
|
-
## Configure
|
|
27
|
-
|
|
28
|
-
Create `oxlint.config.ts` in the project root:
|
|
9
|
+
Coding agents often write tests that pass whatever the code does. This one passes as long as `parseUser` is exported:
|
|
29
10
|
|
|
30
11
|
```ts
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Type-aware checking is on by default. It needs the project's `tsconfig.json` and `oxlint-tsgolint`, which uses TypeScript 7. Configurations that rely on removed options such as `baseUrl` need migrating first. For plain JavaScript or a TypeScript project that is not yet compatible, pass `typeAware: false`. Syntax and scope rules still run, and explicit `any` is still rejected.
|
|
37
|
-
|
|
38
|
-
Create `oxfmt.config.ts` beside it:
|
|
39
|
-
|
|
40
|
-
```ts
|
|
41
|
-
import { formatConfig } from "@jameswyse/oxc-config/oxfmt";
|
|
42
|
-
|
|
43
|
-
export default formatConfig;
|
|
12
|
+
test("parseUser", () => {
|
|
13
|
+
expect(parseUser).toBeDefined();
|
|
14
|
+
});
|
|
44
15
|
```
|
|
45
16
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
```json
|
|
49
|
-
{
|
|
50
|
-
"scripts": {
|
|
51
|
-
"lint": "oxlint .",
|
|
52
|
-
"lint:fix": "oxlint --fix . && oxfmt . && oxlint .",
|
|
53
|
-
"format": "oxfmt .",
|
|
54
|
-
"format:check": "oxfmt --check ."
|
|
55
|
-
}
|
|
56
|
-
}
|
|
17
|
+
```text
|
|
18
|
+
error wyse(no-edit-proof-assertions): This assertion only proves that an import exists or that a value is a function. Call it and assert the behaviour it provides.
|
|
57
19
|
```
|
|
58
20
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
## Select framework and runtime profiles
|
|
62
|
-
|
|
63
|
-
Enable only the tools the project uses. Each profile accepts `true` for its default scope or an array of project-relative globs. `nextjs` takes application root directories instead of globs.
|
|
21
|
+
Calling the function and checking its result passes:
|
|
64
22
|
|
|
65
23
|
```ts
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
export default createLintConfig({ react: true, vitest: true });
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
For a monorepo with separate server and browser packages:
|
|
72
|
-
|
|
73
|
-
```ts
|
|
74
|
-
import { createLintConfig } from "@jameswyse/oxc-config/oxlint";
|
|
75
|
-
|
|
76
|
-
export default createLintConfig({
|
|
77
|
-
nextjs: ["apps/web"],
|
|
78
|
-
react: ["packages/ui/**/*.{ts,tsx}"],
|
|
79
|
-
node: ["packages/server/**/*.ts"],
|
|
80
|
-
vitest: ["**/*.{test,spec}.{ts,tsx}"],
|
|
24
|
+
test("parseUser reads the name from JSON", () => {
|
|
25
|
+
expect(parseUser('{"name":"Ada"}')).toEqual({ name: "Ada" });
|
|
81
26
|
});
|
|
82
27
|
```
|
|
83
28
|
|
|
84
|
-
|
|
85
|
-
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
86
|
-
| `react` | React and React Compiler rules, accessibility rules, effect-misuse rules, and e18e performance rules outside tests |
|
|
87
|
-
| `nextjs` | The React profile plus Next.js rules, with default exports allowed for `app` and `pages` entrypoints under each root |
|
|
88
|
-
| `node` | Node.js environment and rules |
|
|
89
|
-
| `vitest` | Vitest rules; its globs join the shared test scope |
|
|
90
|
-
| `jest` | Jest rules; keep its scope separate from Vitest when both exist |
|
|
91
|
-
| `effect` | Effect error-tag and service-import rules, for modules that use [Effect](https://effect.website) |
|
|
92
|
-
| `tests` | Extra test globs, such as Playwright or Node tests, for the custom test rules |
|
|
93
|
-
| `electron` | Electron security rules everywhere, and an import boundary keeping `electron` and `node:*` out of the renderer (`src/renderer/**` by default, or `{ renderer: [...] }`) |
|
|
94
|
-
|
|
95
|
-
Default exports are also accepted where a tool loads them: config files, Storybook stories and `.storybook`, `sanity.cli`, GraphQL Codegen entrypoints and declaration files.
|
|
29
|
+
This is one rule of many. [The reference](REFERENCE.md) lists the rest, from unparsed external data and scattered `process.env` reads to server actions that skip their auth check.
|
|
96
30
|
|
|
97
|
-
|
|
31
|
+
## Set up with a coding agent
|
|
98
32
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
These options need the project to name its owners, so each is off until configured. Globs are relative to the directory Oxlint runs from.
|
|
102
|
-
|
|
103
|
-
```ts
|
|
104
|
-
import { createLintConfig } from "@jameswyse/oxc-config/oxlint";
|
|
33
|
+
Give the agent this prompt:
|
|
105
34
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
boundaries: [
|
|
110
|
-
{
|
|
111
|
-
files: ["src/renderer/**"],
|
|
112
|
-
deny: ["electron", "node:*", "@main/**", "src/main/**"],
|
|
113
|
-
message: "Use the typed contextBridge API exposed by the preload process.",
|
|
114
|
-
},
|
|
115
|
-
],
|
|
116
|
-
serverActionGuards: ["requireStaffPermission", "requireMember"],
|
|
117
|
-
clock: ["src/platform/clock.ts"],
|
|
118
|
-
random: ["src/platform/ids.ts"],
|
|
119
|
-
});
|
|
35
|
+
```text
|
|
36
|
+
Set up @jameswyse/oxc-config in this repository by following
|
|
37
|
+
https://github.com/jameswyse/oxc-config/blob/main/docs/agent-setup.md
|
|
120
38
|
```
|
|
121
39
|
|
|
122
|
-
|
|
123
|
-
- `boundaries` rejects imports from `files` that match a `deny` glob. Bare specifiers match as written. Relative specifiers resolve to a project path first, so `../main/ipc` matches `src/main/**`. Type-only imports are allowed because they are erased. Use `ignore` to exclude files from a boundary's scope.
|
|
124
|
-
- `serverActionGuards` requires every exported function in a `"use server"` module, and every inline `"use server"` function, to start by awaiting one of the named guards. Server-action modules must export each action where it is declared. A guard's name cannot prove it authorises the operation, so review the guard itself.
|
|
125
|
-
- `clock` and `random` list the modules that may read the current time (`Date.now()`, argument-less `new Date()`) or generate random values and IDs (`Math.random()`, `crypto.randomUUID()`, `randomUUID`/`randomInt` from `node:crypto`). Other production code must receive them, so tests can control them without faking globals. Test files, parameter defaults such as `now = new Date()` and passing `Date.now` uncalled are allowed. Durations from `performance.now()` and secret generation are out of scope.
|
|
40
|
+
## Set up by hand
|
|
126
41
|
|
|
127
|
-
|
|
42
|
+
Install the package and its peers. It requires Node.js 22.23 or later.
|
|
128
43
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
```json
|
|
132
|
-
{
|
|
133
|
-
"extends": "@jameswyse/oxc-config/tsconfig"
|
|
134
|
-
}
|
|
44
|
+
```sh
|
|
45
|
+
pnpm add -D @jameswyse/oxc-config oxlint oxfmt oxlint-tsgolint
|
|
135
46
|
```
|
|
136
47
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
## Add project-specific exceptions
|
|
140
|
-
|
|
141
|
-
Append overrides to the returned config so its framework and test overrides stay active. Oxlint applies file overrides after top-level `rules`, so type-aware and framework rules can only be changed by a later override.
|
|
48
|
+
Create `oxlint.config.ts` and `oxfmt.config.ts` in the project root:
|
|
142
49
|
|
|
143
50
|
```ts
|
|
144
51
|
import { createLintConfig } from "@jameswyse/oxc-config/oxlint";
|
|
145
52
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
export default {
|
|
149
|
-
...base,
|
|
150
|
-
ignorePatterns: [...base.ignorePatterns, "src/routeTree.gen.ts"],
|
|
151
|
-
overrides: [
|
|
152
|
-
...base.overrides,
|
|
153
|
-
{
|
|
154
|
-
files: ["src/validation/**/*.ts"],
|
|
155
|
-
rules: {
|
|
156
|
-
"wyse/no-runtime-typeof": "off",
|
|
157
|
-
"wyse/no-unsafe-dictionary-type": "off",
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
],
|
|
161
|
-
};
|
|
53
|
+
export default createLintConfig();
|
|
162
54
|
```
|
|
163
55
|
|
|
164
|
-
Use this for validated input seams, real timer tests, external module mocks, framework-required default exports and generated files. Keep each exception to the smallest scope. Suppression comments that no longer suppress anything are errors.
|
|
165
|
-
|
|
166
|
-
## Adjust the formatter
|
|
167
|
-
|
|
168
|
-
The formatter uses 100 columns, two spaces, double quotes, semicolons and trailing commas. It sorts imports into value and type groups and sorts `package.json` scripts. To use single quotes or add a workspace import prefix:
|
|
169
|
-
|
|
170
56
|
```ts
|
|
171
57
|
import { formatConfig } from "@jameswyse/oxc-config/oxfmt";
|
|
172
58
|
|
|
173
|
-
export default
|
|
174
|
-
...formatConfig,
|
|
175
|
-
singleQuote: true,
|
|
176
|
-
sortImports: {
|
|
177
|
-
...formatConfig.sortImports,
|
|
178
|
-
internalPattern: ["@/", "#", "@my-workspace/"],
|
|
179
|
-
},
|
|
180
|
-
};
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
Tailwind class sorting depends on the project's Tailwind version and stylesheet, so configure Oxfmt's `sortTailwindcss` option in the project.
|
|
184
|
-
|
|
185
|
-
## Develop
|
|
186
|
-
|
|
187
|
-
```sh
|
|
188
|
-
pnpm install --frozen-lockfile
|
|
189
|
-
pnpm check
|
|
59
|
+
export default formatConfig;
|
|
190
60
|
```
|
|
191
61
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
## Release
|
|
62
|
+
Type-aware checking is on by default and needs a `tsconfig.json` that TypeScript 7 accepts. The defaults enable no framework profiles or project boundaries. The [configuration guide](docs/configuration.md) covers supported versions, scripts, framework profiles, project boundaries, the shared tsconfig, exceptions and formatter options.
|
|
195
63
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
```sh
|
|
199
|
-
pnpm changeset
|
|
200
|
-
```
|
|
64
|
+
## Documentation
|
|
201
65
|
|
|
202
|
-
|
|
66
|
+
- [Configuration guide](docs/configuration.md)
|
|
67
|
+
- [Agent setup instructions](docs/agent-setup.md)
|
|
68
|
+
- [Rule reference](REFERENCE.md)
|
|
69
|
+
- [Contributing](CONTRIBUTING.md)
|
|
203
70
|
|
|
204
71
|
## Vendored code
|
|
205
72
|
|
package/REFERENCE.md
CHANGED
|
@@ -1,134 +1,107 @@
|
|
|
1
1
|
# Rule reference
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
no-
|
|
86
|
-
no-
|
|
87
|
-
no-
|
|
88
|
-
no-
|
|
89
|
-
no-
|
|
90
|
-
no-
|
|
91
|
-
no-
|
|
92
|
-
|
|
93
|
-
no-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
no-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
`no-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
These policies are enforced once a project names the actual owners. They are intentionally not universal defaults:
|
|
110
|
-
|
|
111
|
-
| Policy | Mechanism | Required project information |
|
|
112
|
-
| --------------------------------------------- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
113
|
-
| Parse configuration at startup | `env` option | Modules that own configuration parsing |
|
|
114
|
-
| Keep privileged modules out of other runtimes | `boundaries` option | Each runtime's file scope, and the packages, aliases and paths it must not import |
|
|
115
|
-
| Require access checks before server actions | `serverActionGuards` option | The guard functions that perform authorisation |
|
|
116
|
-
| Control time and randomness | `clock` and `random` options | Modules that own the clock and ID or random generation |
|
|
117
|
-
| Harden Electron apps | `electron` option | Renderer globs when they are not `src/renderer/**` |
|
|
118
|
-
| Generated assets and style imports | Shared ignores plus local patterns; Oxfmt Tailwind configuration | Actual generators, CSS preprocessors, Tailwind config or stylesheet |
|
|
119
|
-
|
|
120
|
-
## Compiler settings and review work
|
|
121
|
-
|
|
122
|
-
For new TypeScript projects, use `strict`, `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, `noImplicitOverride` and `noFallthroughCasesInSwitch`. The package exports them as `@jameswyse/oxc-config/tsconfig` for a project's `tsconfig.json` to extend. Lint configuration never applies them, because changing them in an existing project is a deliberate migration.
|
|
123
|
-
|
|
124
|
-
Lint cannot establish correct authorisation, tenant isolation, schema validation, secret redaction, transaction boundaries, idempotency, crash recovery, concurrency ownership, dependency justification, useful module depth, constructive domain modelling or independent test oracles. These remain review and verification work. File naming, positional parameters, boolean inputs, `Partial<T>`, wrappers and abstraction counts have legitimate domain/framework exceptions and are not rejected through universal heuristics.
|
|
125
|
-
|
|
126
|
-
## Oxc API references
|
|
127
|
-
|
|
128
|
-
The implementation follows the current [Oxlint configuration API](https://oxc.rs/docs/guide/usage/linter/config.html), [JS plugin API](https://oxc.rs/docs/guide/usage/linter/writing-js-plugins.html), [type-aware setup](https://oxc.rs/docs/guide/usage/linter/type-aware.html), and [Oxfmt options](https://oxc.rs/docs/guide/usage/formatter/config-file-reference.html). Absolute plugin specifiers keep imported configs independent of the consumer's working directory. Side-effect import sorting is explicitly disabled.
|
|
129
|
-
|
|
130
|
-
## Rules considered and dropped
|
|
131
|
-
|
|
132
|
-
`no-call-count-assertions` would have rejected `toHaveBeenCalledTimes`, `toHaveBeenNthCalledWith` and `mock.calls` counting. On three codebases, 36 of 40 sampled findings were legitimate: the count was the behaviour under test, such as caching, retry, deduplication or firing once. No syntax separated them from incidental counts, so it was dropped.
|
|
133
|
-
|
|
134
|
-
Rules for safety comments on type assertions, a blank line between every top-level statement, and rejecting `unknown` parameters are not included. The first conflicts with `no-comments`, the second split deliberate groups of related declarations, and the third rejected legitimate unparsed input seams. Effect rules that reject `_tag` comparisons, prefer `Match` or reject literal tagged construction are not included either, because discriminant checks are the preferred way to narrow a union and TypeScript already checks literal variants.
|
|
3
|
+
Rules are errors, which fail the run, unless marked as warnings. Warnings flag patterns that have legitimate uses, so they report without failing. Custom rules use the `wyse/` prefix. Each one's tests sit beside it in `src/rules` or `src/vendor/anti-slop/rules` and show exactly what it reports.
|
|
4
|
+
|
|
5
|
+
Framework profiles and project options add rules only when enabled. The [configuration guide](docs/configuration.md) explains how to turn them on.
|
|
6
|
+
|
|
7
|
+
## Base rules
|
|
8
|
+
|
|
9
|
+
Oxlint's correctness and suspicious categories are errors. Unused variables are errors unless their names start with `_`, and suppression comments that no longer suppress anything are errors too. `no-underscore-dangle` is off, so fields such as `_id` and `__typename` stay valid.
|
|
10
|
+
|
|
11
|
+
Native rules also catch legacy and error-prone built-ins: `no-var`, `no-proto`, `no-new-func`, `no-new-wrappers`, `no-array-constructor`, `no-prototype-builtins`, `prefer-object-has-own`, `prefer-rest-params`, `prefer-regex-literals`, `import/no-mutable-exports`, `oxc/no-const-enum`, and Unicorn's `new-for-builtins`, `no-instanceof-array`, `no-new-buffer`, `prefer-node-protocol`, `prefer-import-meta-properties`, `prefer-response-static-json`, `prefer-string-trim-start-end`, `prefer-array-find`, `prefer-array-flat-map` and `prefer-array-some`.
|
|
12
|
+
|
|
13
|
+
## Types and external data
|
|
14
|
+
|
|
15
|
+
Native rules: `typescript/no-explicit-any`, `no-non-null-assertion`, `no-unsafe-function-type` and `no-import-type-side-effects`. `consistent-type-assertions` allows only `as` assertions and rejects them on object and array literals, and `method-signature-style` requires property signatures such as `run: () => void`. `ban-ts-comment` rejects `@ts-ignore` and `@ts-nocheck`, and requires a reason after `--` on `@ts-expect-error`.
|
|
16
|
+
|
|
17
|
+
Type-aware checking, which is on by default, adds the `no-unsafe-*` rules, `no-unsafe-type-assertion`, `no-unnecessary-type-assertion`, `switch-exhaustiveness-check`, `prefer-reduce-type-parameter`, `restrict-plus-operands` with every mixed-type allowance turned off, and `no-unnecessary-condition`, a nursery rule in Oxlint 1.86. `typescript/no-deprecated` is a warning outside tests.
|
|
18
|
+
|
|
19
|
+
- `wyse/no-runtime-typeof` (warning): `typeof` outside a type guard. `typeof x === "undefined"` passes, as does `typeof` on a variable or parameter declared in the same file with a union type that has no `unknown` or `any` member. Turn it off in modules that validate external input.
|
|
20
|
+
- `wyse/no-generic-object-guard`: a named helper such as `isRecord` whose whole body proves only that a value is a non-null object, through `typeof`, `instanceof Object`, prototype or `Object.prototype.toString` checks. Guards that also check a field pass, as do inline callbacks. It also reports any guard that takes `unknown` or `any` and claims a specific type while only checking that the value exists, or always returns `true`.
|
|
21
|
+
- `wyse/no-hardcoded-credential`: a string containing a private key or a token in a well-known format, such as an AWS access key or a GitHub, Stripe live, Slack, Google, Anthropic or OpenAI key. Placeholders containing `example`, `fake`, `dummy`, `placeholder`, `redacted` or a run such as `abcdefgh` pass.
|
|
22
|
+
- `wyse/no-disabled-tls-verification`: `rejectUnauthorized: false`, or setting `NODE_TLS_REJECT_UNAUTHORIZED` to `"0"`.
|
|
23
|
+
- `wyse/no-secret-fallback`: a string fallback for an environment variable whose name contains `SECRET`, `TOKEN`, `PASSWORD`, `PRIVATE`, `CREDENTIAL`, `SALT`, `SIGNING` or `API_KEY`, or ends in `KEY`, such as `process.env.SESSION_SECRET ?? "dev"` or a destructuring default.
|
|
24
|
+
- `wyse/type-guard-name`: a named type predicate such as `checkUser(value): value is User`. Names such as `isUser` and `hasId` pass, as do assertion functions and anonymous callbacks.
|
|
25
|
+
- `wyse/no-unknown-returns`: a declared return type of `unknown` or `Promise<unknown>`.
|
|
26
|
+
- `wyse/no-unknown-type-aliases`: a type alias that resolves to `unknown`.
|
|
27
|
+
- `wyse/no-unsafe-dictionary-type`: a dictionary type whose values are `unknown`, `any`, `object` or `{}`.
|
|
28
|
+
- `wyse/no-object-parameters`: a parameter typed `object`, directly or through an alias.
|
|
29
|
+
- `wyse/no-known-value-widening`: a known value stored under a broader declared type, such as `const value: unknown = {}`.
|
|
30
|
+
- `wyse/no-widen-then-assert`: widening a known value, then asserting it to a narrower type.
|
|
31
|
+
- `wyse/no-chained-type-assertions`: chained assertions such as `value as unknown as User`.
|
|
32
|
+
- `wyse/no-reflect-apply` and `wyse/no-reflect-get`: `Reflect.apply` and `Reflect.get`.
|
|
33
|
+
|
|
34
|
+
These custom rules check one file at a time, without type information from other files. None of them bans `unknown` outright or rejects `as const`.
|
|
35
|
+
|
|
36
|
+
## Control flow and errors
|
|
37
|
+
|
|
38
|
+
Native rules: `curly`, `eqeqeq`, `no-empty`, `no-useless-catch`, Oxlint's promise checks, `array-callback-return`, `no-case-declarations`, `no-constructor-return`, `no-fallthrough`, `no-loop-func`, `no-self-compare`, `no-unreachable-loop`, `no-useless-assignment` and `unicorn/error-message`. JavaScript files also get `no-throw-literal` and `prefer-promise-reject-errors`. Type-aware checking adds floating and misused promise checks, `await-thenable`, `return-await`, `only-throw-error`, `prefer-promise-reject-errors` and `use-unknown-in-catch-callback-variable`.
|
|
39
|
+
|
|
40
|
+
- `wyse/no-deep-ternary`: a ternary nested inside two others. It replaces Oxlint's nested-ternary rule, which conflicts with Oxfmt over parentheses.
|
|
41
|
+
- `wyse/no-swallowed-errors` (warning): a `catch` that only returns a fallback literal, a `.catch` callback or `.then` rejection callback that is empty or only returns one, and `Promise.allSettled` results that are never read. Effect's `orElseSucceed`, `orElse`, `catchAll` and `catchAllCause` count when they recover to a fallback literal or `Effect.void`. Built-in parser and filesystem-probe `try` blocks, `response.json()`, `.catch` on a promise another caller also receives, and Effect's `catchTag` all pass.
|
|
42
|
+
- `wyse/explicit-api-states` (warning): a boolean parameter on an exported function, exported class constructor or public method. Destructured parameters, accessors, private members, overloads, the subject of an `asserts` or `is` return type, and the final boolean of a `set*` or `update*` function pass.
|
|
43
|
+
- `wyse/no-unsettled-promise`: a `new Promise` executor that runs code but never calls `resolve` or `reject`, or calls them only inside an optional call such as `server?.close(resolve)`. Executors that do nothing, such as `() => {}`, pass. Test files aren't checked.
|
|
44
|
+
- `wyse/no-unchecked-index-search`: an `indexOf`, `lastIndexOf`, `findIndex` or `findLastIndex` result passed straight to `splice`, `toSpliced`, `with`, `at` or `slice`, where -1 counts from the end.
|
|
45
|
+
- `wyse/no-unchecked-fetch-response`: reading a `fetch` response body with `json()`, `text()` or a similar method without checking `ok` or `status`. A response passed to another function passes. Test files aren't checked.
|
|
46
|
+
|
|
47
|
+
## Modules, files and names
|
|
48
|
+
|
|
49
|
+
Native rules: `import/first`, `import/no-default-export`, `import/no-duplicates`, `import/no-self-import` and `import/no-unassigned-import`. `import/no-cycle` and `oxc/no-barrel-file` are warnings. Default exports are allowed in the files that tools load them from, listed in the [configuration guide](docs/configuration.md#select-framework-and-runtime-profiles).
|
|
50
|
+
|
|
51
|
+
- `wyse/no-forwarding-module` (warning): a module made only of re-exports, or one that re-exports its imports unchanged. Package entrypoints, Next.js `app` special files and `pages`, root `middleware`, `proxy` and `instrumentation` files, and modules with directives or side-effect imports pass.
|
|
52
|
+
- `wyse/no-import-time-effects` (warning): starting a timer, `fetch`, top-level `await`, opening a `BroadcastChannel`, `WebSocket`, `EventSource` or `Worker`, calling `addEventListener` or `listen`, or registering `process.on` handlers when a module is imported, including inside immediately invoked functions and static class members. Config files, scripts, tests, and entrypoints such as `main`, `server`, `worker` and `instrumentation` files and `bin`, `cli`, `deploy`, `tools`, `test`, `e2e` and `fixtures` directories pass.
|
|
53
|
+
- `wyse/no-pass-through-function` (warning): a named function whose body only calls another function with its own parameters, in order. Zero-parameter functions, method calls, type predicates and `useEffectEvent` callees pass.
|
|
54
|
+
- `wyse/no-generic-file-suffix`: a file named `*Contract`, `*Policy`, `policy` or `contract`, in any case style. Test and story files pass, as do header policies such as a content security policy and files that export a binding with the file's name, such as `agentPolicy.ts` exporting `AgentPolicy`.
|
|
55
|
+
- `wyse/no-shape-in-symbol-names`: a name containing "shape", in any letter case.
|
|
56
|
+
|
|
57
|
+
## Comments, suppressions and logging
|
|
58
|
+
|
|
59
|
+
- `wyse/no-comments`: line, block and JSDoc comments. Shebangs, JSDoc type tags in JavaScript files and tool directives pass. The directives include lint suppressions, `@ts-expect-error`, triple-slash references, `#__PURE__`, formatter and coverage ignores, bundler magic comments, and test-environment and JSX pragmas. The `allow` option adds more patterns.
|
|
60
|
+
- `wyse/require-suppression-reason`: an `oxlint-disable` or `eslint-disable` directive that names no rule, or gives no reason after `--`. Oxlint applies a blanket `oxlint-disable` or `oxlint-disable-line` to this rule too, so it can't report those two forms. It does report a blanket `oxlint-disable-next-line`.
|
|
61
|
+
- `eslint/no-console`: console calls outside config files and scripts.
|
|
62
|
+
|
|
63
|
+
## Mutation and copying
|
|
64
|
+
|
|
65
|
+
Native rules: `prefer-const`, `no-param-reassign` and `oxc/no-accumulating-spread`. Type-aware checking adds `prefer-readonly`.
|
|
66
|
+
|
|
67
|
+
- `wyse/no-array-filter-map`: adjacent `filter` and `map` passes over an array.
|
|
68
|
+
- `wyse/no-reduce-accumulator-copy`: copying a reducer's accumulator with `Object.assign`, `Array.from` or an array copy method.
|
|
69
|
+
- `wyse/no-conditional-empty-object-spread`: spreading a conditional empty object to omit fields, such as `...(id ? { id } : {})`.
|
|
70
|
+
|
|
71
|
+
## Project ownership
|
|
72
|
+
|
|
73
|
+
These rules run only once the project names its owners through the [project boundary options](docs/configuration.md#enforce-project-boundaries).
|
|
74
|
+
|
|
75
|
+
- `wyse/no-env-access` (`env`): reading `process.env` or `import.meta.env`, or destructuring `env` from either, outside the owners, config files and scripts. It doesn't check that the owner validates what it reads.
|
|
76
|
+
- `wyse/import-boundaries` (`boundaries`): a value import, re-export, literal dynamic import, or `require` call, including a loader made with `createRequire` from `node:module`, that matches a `deny` glob. Type-only imports and `import("…")` types pass unless the boundary sets `types: true`. It doesn't check computed imports.
|
|
77
|
+
- `wyse/server-action-guard` (`serverActionGuards`): a `"use server"` action that doesn't start by awaiting a guard, and indirect or default exports from server-action modules. Non-exported helpers and test files pass.
|
|
78
|
+
- `wyse/no-ambient-clock` (`clock`): `Date.now()` and argument-less `new Date()`, including through `globalThis` or `window`. Parameter defaults, uncalled `Date.now`, provider functions that only return the read, and test files pass.
|
|
79
|
+
- `wyse/no-ambient-random` (`random`): `Math.random()`, `crypto.randomUUID()`, and `randomUUID` or `randomInt` from `node:crypto`. The same exceptions apply. `randomBytes` and `getRandomValues` pass because they generate secrets.
|
|
80
|
+
|
|
81
|
+
## Tests
|
|
82
|
+
|
|
83
|
+
These rules run on the default test globs, the `tests` paths and the Jest and Vitest profile scopes. The `vitest` and `jest` profiles add native checks for focused, disabled and conditional tests, standalone expectations, valid `expect` calls, interpolated snapshots and redundant async `expect` callbacks. They also require the specific matcher, such as `toStrictEqual` over `toEqual`, `toHaveLength`, `toContain`, and comparison or equality matchers over `toBe(true)`. The `jest` profile rejects `done` callbacks.
|
|
84
|
+
|
|
85
|
+
- `wyse/no-fixed-test-delay`: an awaited literal timer delay, Playwright's `waitForTimeout`, or `yield* Effect.sleep` with a fixed duration. Genuine timer tests need a scoped exception.
|
|
86
|
+
- `wyse/no-vacuous-test-assertions`: an expectation that compares two literals, or the same identifier on both sides of `toBe`, `toEqual` or `toStrictEqual`.
|
|
87
|
+
- `wyse/no-edit-proof-assertions`: an assertion that only proves an import exists or is a function, such as `toBeDefined` on an import, `toBeTypeOf("function")` or `toHaveProperty` on a namespace.
|
|
88
|
+
- `wyse/no-optional-test-action`: a statement directly in a test body that an optional call can skip, such as `button?.click()` or `await dismiss?.click()`. Hooks and callbacks inside the test pass.
|
|
89
|
+
- `wyse/no-derived-test-expectation` (warning): an expected value that calls a function imported from production code, such as `toBe(formatLabel(input))`, directly, through a namespace import or through a `const` such as `expected`. `.not` assertions, functions the actual value also calls, and imports from test, fixture, e2e and helper paths pass.
|
|
90
|
+
- `wyse/no-uncontrolled-test-clock`: `Date.now()` or argument-less `new Date()` in a test file that never calls `useFakeTimers`, `setSystemTime`, `mock.timers.enable`, `clock.install` or `clock.setFixedTime`. Fake timers set up with `apis`, `toFake` or `doNotFake` options that leave `Date` real don't count, and neither does Effect's `TestClock`, which doesn't control `Date`. Playwright and end-to-end files pass.
|
|
91
|
+
- `wyse/no-test-timeout-overrides` (warning): timeout and retry overrides such as `test.setTimeout`, `test.slow()`, `describe.configure`, `vi.setConfig`, `jest.setTimeout` and `jest.retryTimes`. Setting retries to 0 passes. End-to-end files under `e2e/` or named `*.e2e.*` pass.
|
|
92
|
+
- `wyse/companion-has-owner` (warning): a `.test`, `.spec` or `.bench` file with no matching sibling module or directory. Files with an `integration` or `e2e` segment, and files under `tests/`, `test/`, `__tests__/` or `e2e/`, pass.
|
|
93
|
+
- `wyse/no-module-mocking`: Vitest, Jest and `node:test` module mocking. Replace dependencies through real interfaces, or add a file-scoped exception for an external system.
|
|
94
|
+
|
|
95
|
+
## Framework profiles
|
|
96
|
+
|
|
97
|
+
- `react` adds React and React Compiler rules, `jsx-a11y` correctness and suspicious rules, `react-you-might-not-need-an-effect`, and e18e performance rules outside tests. It includes `react/rules-of-hooks` outside end-to-end and `tests` paths, `no-unknown-property`, `checked-requires-onchange-or-readonly` and `jsx-no-useless-fragment`. It also adds `wyse/no-get-password-form`: a `<form>` with a password or one-time-code input that can submit by GET. An input belongs to the form it sits inside, or to the form its `form` attribute names. The form submits by GET when its `action` is missing or a string and its `method` is missing or anything but `post` or `dialog`, or when one of its submit buttons sets `formMethod` to anything but `post` or `dialog`.
|
|
98
|
+
- `nextjs` adds the React rules plus Next.js rules.
|
|
99
|
+
- `effect` adds `wyse/no-manual-effect-error-tag` and `wyse/no-service-constructor-imports`. The second reports a project-local `make*` import used as an uncalled value, such as `yield* makeIssueService`. Calling a `make*` factory with arguments passes.
|
|
100
|
+
- `electron` adds a renderer import boundary and these rules:
|
|
101
|
+
- `wyse/electron-no-remote-module`: `remote` from `electron`, or `@electron/remote`, by import, `require`, dynamic import or re-export.
|
|
102
|
+
- `wyse/electron-secure-web-preferences`: `webPreferences` without literal `contextIsolation: true`, `sandbox: true` and `nodeIntegration: false`, or with web security disabled, insecure content, or experimental or Blink features enabled. Later keys, spreads and computed keys that override these settings are reported too. Preferences passed from a variable the rule can't resolve pass.
|
|
103
|
+
- `wyse/electron-no-raw-ipc-renderer-exposure`: exposing `ipcRenderer` or its methods through `contextBridge`, forwarding renderer-chosen channels or arguments, or passing renderer callbacks straight to `on` or `once`.
|
|
104
|
+
|
|
105
|
+
## Formatting
|
|
106
|
+
|
|
107
|
+
Oxfmt formats the code. `wyse/statement-spacing` requires a blank line after the imports, around multiline blocks and declarations, and before `return`. Consecutive imports, consecutive one-line statements, and a type alias directly after the multiline declaration it derives from stay together. It's the only custom rule with an autofix.
|
package/dist/format.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type { OxfmtConfig
|
|
2
|
-
|
|
1
|
+
import type { OxfmtConfig } from "oxfmt";
|
|
2
|
+
type SortImportsOptions = Exclude<OxfmtConfig["sortImports"], boolean | undefined>;
|
|
3
|
+
export declare const formatConfig: Omit<OxfmtConfig, "ignorePatterns" | "sortImports"> & {
|
|
3
4
|
ignorePatterns: string[];
|
|
4
|
-
sortImports:
|
|
5
|
+
sortImports: SortImportsOptions;
|
|
5
6
|
};
|
|
7
|
+
export {};
|