@himynameisdave/oxlint-config 1.0.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/dist/vitest.js ADDED
@@ -0,0 +1,229 @@
1
+ import { defineConfig } from 'oxlint';
2
+ /**
3
+ * Vitest add-on: every rule from oxlint's `vitest` plugin, decided explicitly.
4
+ * Opt in alongside base if your tests run under Vitest:
5
+ *
6
+ * ```ts
7
+ * export default defineConfig({ extends: [base, vitest] });
8
+ * ```
9
+ *
10
+ * These rules only fire on test-shaped syntax (describe/it/test/expect), so
11
+ * they are inert in app code; no `files` scoping needed.
12
+ *
13
+ * Known limitation: oxlint resolves test functions by import source (`vitest`,
14
+ * `@jest/globals`) or bare globals. `import { it } from 'bun:test'` is NOT
15
+ * recognized, so none of these rules fire on bun:test-style suites. Bun users
16
+ * get no coverage from this add-on until oxlint supports bun:test upstream.
17
+ */
18
+ export default defineConfig({
19
+ plugins: ['vitest'],
20
+ rules: {
21
+ /* ================================================================== *
22
+ * vitest — correctness
23
+ * ================================================================== */
24
+ // A test with no assertion passes vacuously; it can never fail.
25
+ 'vitest/expect-expect': 'error',
26
+ // vi.mock/vi.hoisted run before imports no matter where they're written;
27
+ // placing them lower misleads readers about execution order.
28
+ 'vitest/hoisted-apis-on-top': 'error',
29
+ // An expect inside if/catch can silently never run; the test still passes.
30
+ 'vitest/no-conditional-expect': 'error',
31
+ // Conditionally-registered tests mean different environments run different
32
+ // suites, so CI green stops meaning the same thing everywhere.
33
+ 'vitest/no-conditional-tests': 'error',
34
+ // .skip rots invisibly and keeps CI green; use .todo for planned work
35
+ // (allowed, see warn-todo) or delete the test.
36
+ 'vitest/no-disabled-tests': 'error',
37
+ // .only committed to a suite makes CI silently green while skipping every
38
+ // other test: the exact quiet failure this config exists to prevent.
39
+ 'vitest/no-focused-tests': 'error',
40
+ // An expect in a describe body runs at collection time; its failure isn't
41
+ // attributed to any test.
42
+ 'vitest/no-standalone-expect': 'error',
43
+ // Auto-numbered snapshots shift identity when assertions are added or
44
+ // reordered; a hint pins each one (fires only with multiple per test).
45
+ 'vitest/prefer-snapshot-hint': 'error',
46
+ // An unawaited expect.poll()/expect.element() resolves after the test
47
+ // ends, so the assertion never gets a chance to fail.
48
+ 'vitest/require-awaited-expect-poll': 'error',
49
+ // Under test.concurrent, the global expect's snapshot state races; the
50
+ // local context expect is the only reliable one.
51
+ 'vitest/require-local-test-context-for-concurrent-snapshots': 'error',
52
+ // Untyped vi.fn() is (...args: any[]) => any, so wrong call shapes in
53
+ // assertions pass silently.
54
+ 'vitest/require-mock-type-parameters': 'error',
55
+ // Bare toThrow() passes on ANY error; asserting the message pins which
56
+ // failure you actually meant.
57
+ 'vitest/require-to-throw-message': 'error',
58
+ // A malformed describe callback (async, params, non-function) breaks
59
+ // collection quietly.
60
+ 'vitest/valid-describe-callback': 'error',
61
+ // expect() with no matcher, or a matcher misuse, asserts nothing at all.
62
+ 'vitest/valid-expect': 'error',
63
+ // A promise-chained expect must be awaited/returned or it asserts after
64
+ // the test has already passed.
65
+ 'vitest/valid-expect-in-promise': 'error',
66
+ // Empty or dynamic-garbage titles break -t filtering and failure reports.
67
+ 'vitest/valid-title': 'error',
68
+ // .todo is the sanctioned marker for planned tests; banning it would push
69
+ // people back to .skip, which no-disabled-tests rightly forbids.
70
+ 'vitest/warn-todo': 'off',
71
+ /* ================================================================== *
72
+ * vitest — suspicious
73
+ * ================================================================== */
74
+ // Commented-out tests rot silently; delete them or mark .todo.
75
+ 'vitest/no-commented-out-tests': 'error',
76
+ /* ================================================================== *
77
+ * vitest — pedantic
78
+ * ================================================================== */
79
+ // Branching in fixture setup (platform switches, parameterization) is
80
+ // legitimate; the real hazards are already errors via no-conditional-tests
81
+ // and no-conditional-expect.
82
+ 'vitest/no-conditional-in-test': 'off',
83
+ /* ================================================================== *
84
+ * vitest — style
85
+ * ================================================================== */
86
+ // .each and .for differ semantically (.for passes the test context), so
87
+ // the choice is per-case, not a consistency to police.
88
+ 'vitest/consistent-each-for': 'off',
89
+ // The base test-file override deliberately supports both .test and .spec
90
+ // conventions; picking one is repo policy, not lint.
91
+ 'vitest/consistent-test-filename': 'off',
92
+ // One word per scope: the default (test at top level, it inside describe)
93
+ // matches how each reads grammatically.
94
+ 'vitest/consistent-test-it': 'error',
95
+ // vi and vitest are the same object; one accessor (vi, the documented one)
96
+ // keeps greps and reviews single-spelling.
97
+ 'vitest/consistent-vitest-vi': 'error',
98
+ // Assertion-count caps are arbitrary (same stance as max-lines).
99
+ 'vitest/max-expects': 'off',
100
+ // Nesting-depth caps are arbitrary (same stance as max-lines).
101
+ 'vitest/max-nested-describe': 'off',
102
+ // toBeCalled/toHaveBeenCalled are aliases; one canonical spelling.
103
+ 'vitest/no-alias-methods': 'error',
104
+ // Two beforeEach in one describe both run; almost always a merge mistake.
105
+ 'vitest/no-duplicate-hooks': 'error',
106
+ // Hooks are the normal setup mechanism; banning them outright is a niche
107
+ // philosophy this config doesn't share.
108
+ 'vitest/no-hooks': 'off',
109
+ // Duplicate titles make failures ambiguous and -t filtering grab both.
110
+ 'vitest/no-identical-title': 'error',
111
+ // import from node:test in a Vitest suite runs under a different runner's
112
+ // semantics; assertions quietly diverge.
113
+ 'vitest/no-import-node-test': 'error',
114
+ // Twin of prefer-importing-vitest-globals; explicit imports win (below),
115
+ // so the globals-only counterpart stays off.
116
+ 'vitest/no-importing-vitest-globals': 'off',
117
+ // Interpolated inline snapshots can't be updated by the runner (-u).
118
+ 'vitest/no-interpolation-in-snapshots': 'error',
119
+ // Snapshot size caps are arbitrary (same stance as max-lines).
120
+ 'vitest/no-large-snapshots': 'off',
121
+ // Importing from __mocks__ directly bypasses the mocking system the
122
+ // directory exists to feed.
123
+ 'vitest/no-mocks-import': 'error',
124
+ // Inert without options; restricting matchers is a consumer decision.
125
+ 'vitest/no-restricted-matchers': 'off',
126
+ // Inert without options; restricting vi methods is a consumer decision.
127
+ 'vitest/no-restricted-vi-methods': 'off',
128
+ // xit/fdescribe are the sneaky spellings of .skip/.only; force the dotted
129
+ // forms so no-disabled-tests/no-focused-tests can see them.
130
+ 'vitest/no-test-prefixes': 'error',
131
+ // A return in a test body usually hides an unawaited promise; await it.
132
+ 'vitest/no-test-return-statement': 'error',
133
+ // expect(async () => { await f(); }) wraps a promise in a function for no
134
+ // reason; pass the promise straight to .rejects.
135
+ 'vitest/no-unneeded-async-expect-function': 'error',
136
+ // Blank-line layout is oxfmt's job.
137
+ 'vitest/padding-around-after-all-blocks': 'off',
138
+ // Blank-line layout is oxfmt's job.
139
+ 'vitest/padding-around-test-blocks': 'off',
140
+ // One matcher stating the full contract (count + args) beats two that can
141
+ // drift apart.
142
+ 'vitest/prefer-called-exactly-once-with': 'error',
143
+ // toHaveBeenCalledOnce reads as the assertion it is; twin of
144
+ // prefer-called-times, and exactly one direction can be on.
145
+ 'vitest/prefer-called-once': 'error',
146
+ // Opposite twin of prefer-called-once (enabled above).
147
+ 'vitest/prefer-called-times': 'off',
148
+ // Asserting the arguments catches wrong-payload bugs that a bare
149
+ // "was it called" never will.
150
+ 'vitest/prefer-called-with': 'error',
151
+ // toBeGreaterThan(x) reports both values on failure; toBe(a > x) reports
152
+ // only true/false.
153
+ 'vitest/prefer-comparison-matcher': 'error',
154
+ // describe(fn) survives renames; describe("fn") silently goes stale.
155
+ 'vitest/prefer-describe-function-title': 'error',
156
+ // Hand-rolled for-loops around test() hide per-case titles that .each
157
+ // reports individually.
158
+ 'vitest/prefer-each': 'error',
159
+ // expect(a === b).toBe(true) hides both operands in the failure output.
160
+ 'vitest/prefer-equality-matcher': 'error',
161
+ // expect.assertions() in every test is ceremony; expect-expect and
162
+ // no-conditional-expect already cover the vacuous-pass risk.
163
+ 'vitest/prefer-expect-assertions': 'off',
164
+ // await expect(p).resolves fails as an assertion; expect(await p) fails as
165
+ // an unhandled rejection with a worse trace.
166
+ 'vitest/prefer-expect-resolves': 'error',
167
+ // toBeTypeOf("string") reports the actual value; expect(typeof x) reports
168
+ // two strings.
169
+ 'vitest/prefer-expect-type-of': 'error',
170
+ // Lifecycle order (beforeAll, beforeEach, afterEach, afterAll) makes
171
+ // suite setup scannable.
172
+ 'vitest/prefer-hooks-in-order': 'error',
173
+ // Hooks buried below tests are easy to miss when reading a suite.
174
+ 'vitest/prefer-hooks-on-top': 'error',
175
+ // vi.mock(import("./m")) is typed and refactor-safe; the string form
176
+ // silently goes stale.
177
+ 'vitest/prefer-import-in-mock': 'error',
178
+ // Explicit imports type-check and work whether or not globals: true is
179
+ // set; twin no-importing-vitest-globals stays off.
180
+ 'vitest/prefer-importing-vitest-globals': 'error',
181
+ // Title casing is cosmetic.
182
+ 'vitest/prefer-lowercase-title': 'off',
183
+ // mockResolvedValue(x) states intent; mockImplementation(() =>
184
+ // Promise.resolve(x)) buries it.
185
+ 'vitest/prefer-mock-promise-shorthand': 'error',
186
+ // mockReturnValue(x) states intent; mockImplementation(() => x) buries it.
187
+ 'vitest/prefer-mock-return-shorthand': 'error',
188
+ // vi.spyOn restores on mockRestore; assigning vi.fn() onto an object
189
+ // clobbers the method for every later test.
190
+ 'vitest/prefer-spy-on': 'error',
191
+ // toBeTruthy passes on 1, "x", {}; toBe(true) asserts the actual boolean.
192
+ 'vitest/prefer-strict-boolean-matchers': 'error',
193
+ // toStrictEqual catches undefined-vs-missing keys and class mismatches
194
+ // that toEqual ignores.
195
+ 'vitest/prefer-strict-equal': 'error',
196
+ // toBe uses Object.is and prints a focused diff for primitives.
197
+ 'vitest/prefer-to-be': 'error',
198
+ // Opposite of prefer-strict-boolean-matchers (enabled); loose truthiness
199
+ // is exactly what we ban.
200
+ 'vitest/prefer-to-be-falsy': 'off',
201
+ // Targets jest-extended's toBeObject, which vanilla Vitest doesn't ship.
202
+ 'vitest/prefer-to-be-object': 'off',
203
+ // Opposite of prefer-strict-boolean-matchers (enabled); loose truthiness
204
+ // is exactly what we ban.
205
+ 'vitest/prefer-to-be-truthy': 'off',
206
+ // toContain prints the array on failure; .includes() toBe(true) prints a
207
+ // boolean.
208
+ 'vitest/prefer-to-contain': 'error',
209
+ // toHaveBeenCalledTimes keeps the mock's call log in the failure output;
210
+ // asserting mock.calls.length loses it.
211
+ 'vitest/prefer-to-have-been-called-times': 'error',
212
+ // toHaveLength reports the actual length; .length toBe(n) reports two
213
+ // bare numbers.
214
+ 'vitest/prefer-to-have-length': 'error',
215
+ // An empty test() body silently passes forever; .todo marks the intent
216
+ // honestly (and warn-todo stays off to allow it).
217
+ 'vitest/prefer-todo': 'error',
218
+ // Top-level fixture constants are idiomatic; hooks-only setup is ceremony.
219
+ 'vitest/require-hook': 'off',
220
+ // Flat test files are fine; a mandatory describe wrapper is boilerplate.
221
+ 'vitest/require-top-level-describe': 'off',
222
+ /* ================================================================== *
223
+ * vitest — restriction
224
+ * ================================================================== */
225
+ // The runner's default timeout already bounds every test; repeating it
226
+ // per-test is noise.
227
+ 'vitest/require-test-timeout': 'off'
228
+ }
229
+ });
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@himynameisdave/oxlint-config",
3
+ "version": "1.0.0",
4
+ "description": "An opinionated oxlint config, by and for himynameisdave.",
5
+ "keywords": [
6
+ "config",
7
+ "himynameisdave",
8
+ "lint",
9
+ "linter",
10
+ "oxc",
11
+ "oxlint",
12
+ "oxlint-config"
13
+ ],
14
+ "homepage": "https://github.com/himynameisdave/oxlint-config",
15
+ "bugs": {
16
+ "url": "https://github.com/himynameisdave/oxlint-config/issues"
17
+ },
18
+ "license": "MIT",
19
+ "author": "Dave Lunny <d@velunny.com>",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/himynameisdave/oxlint-config.git"
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "type": "module",
28
+ "main": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "default": "./dist/index.js"
34
+ },
35
+ "./base": {
36
+ "types": "./dist/base.d.ts",
37
+ "default": "./dist/base.js"
38
+ },
39
+ "./svelte": {
40
+ "types": "./dist/svelte.d.ts",
41
+ "default": "./dist/svelte.js"
42
+ },
43
+ "./type-aware": {
44
+ "types": "./dist/type-aware.d.ts",
45
+ "default": "./dist/type-aware.js"
46
+ },
47
+ "./vitest": {
48
+ "types": "./dist/vitest.d.ts",
49
+ "default": "./dist/vitest.js"
50
+ },
51
+ "./package.json": "./package.json"
52
+ },
53
+ "publishConfig": {
54
+ "access": "public"
55
+ },
56
+ "scripts": {
57
+ "build": "tsc",
58
+ "lint": "tsc && oxlint -c oxlint.config.ts --deny-warnings",
59
+ "check-coverage": "tsc && bun scripts/check-coverage.ts",
60
+ "test:consumer": "tsc && bun scripts/consumer-smoke.ts",
61
+ "prepublishOnly": "npm run build",
62
+ "format": "oxfmt --write",
63
+ "format:check": "oxfmt --check",
64
+ "test": "bun run lint && bun run format:check && bun run check-coverage && bun run test:consumer",
65
+ "release": "np"
66
+ },
67
+ "devDependencies": {
68
+ "@types/bun": "^1.3.14",
69
+ "np": "^12.0.0",
70
+ "oxfmt": "0.61.0",
71
+ "oxlint": "1.76.0",
72
+ "oxlint-tsgolint": "^7.0.2001",
73
+ "typescript": "^7.0.0"
74
+ },
75
+ "peerDependencies": {
76
+ "oxlint": ">=1.76.0 <2",
77
+ "oxlint-tsgolint": ">=7.0.2001"
78
+ },
79
+ "peerDependenciesMeta": {
80
+ "oxlint-tsgolint": {
81
+ "optional": true
82
+ }
83
+ },
84
+ "packageManager": "bun@1.3.13"
85
+ }