@zgeoff/oxlint-config 0.0.1
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/LICENSE +21 -0
- package/README.md +41 -0
- package/index.js +1 -0
- package/oxlintrc.json +154 -0
- package/package.json +31 -0
- package/plugin.js +81 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Geoff Whatley
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# @zgeoff/oxlint-config
|
|
2
|
+
|
|
3
|
+
Shareable [oxlint](https://oxc.rs/docs/guide/usage/linter.html) config: every category at `error`,
|
|
4
|
+
restriction-rule cherry-picks, comment-style enforcement via `@stylistic`, and a bundled JS plugin
|
|
5
|
+
(`zgeoff/*` rules) banning AST shapes no native oxlint rule can express — top-level arrows, nested
|
|
6
|
+
function declarations, bare named exports, destructured and inline-typed parameters, ternary and
|
|
7
|
+
await arguments.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
bun add -d @zgeoff/oxlint-config oxlint
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Extend it from `.oxlintrc.json` — `extends` resolves file paths only, so point at the file inside
|
|
16
|
+
`node_modules`:
|
|
17
|
+
|
|
18
|
+
```jsonc
|
|
19
|
+
{
|
|
20
|
+
"extends": ["./node_modules/@zgeoff/oxlint-config/oxlintrc.json"],
|
|
21
|
+
// ignorePatterns don't propagate through extends — each consumer declares
|
|
22
|
+
// its own (oxlint also honors .gitignore)
|
|
23
|
+
"ignorePatterns": ["**/node_modules/**", "**/dist/**", "**/*.json"],
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Or import the config object in `oxlint.config.ts`:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import config from '@zgeoff/oxlint-config';
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The plugin is addressable on its own at `@zgeoff/oxlint-config/plugin` for configs that want the
|
|
34
|
+
`zgeoff/*` rules without the rest.
|
|
35
|
+
|
|
36
|
+
## Notes
|
|
37
|
+
|
|
38
|
+
- The config enables the `typescript`, `unicorn`, `oxc`, `import`, and `promise` plugins and loads
|
|
39
|
+
`@stylistic/eslint-plugin` (a dependency of this package) through `jsPlugins`.
|
|
40
|
+
- Type-aware rules activate under `oxlint --type-aware` (requires `oxlint-tsgolint`); the config
|
|
41
|
+
works without it.
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './oxlintrc.json' with { type: 'json' };
|
package/oxlintrc.json
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
|
|
3
|
+
"plugins": ["typescript", "unicorn", "oxc", "import", "promise"],
|
|
4
|
+
"jsPlugins": ["@zgeoff/oxlint-config/plugin", "@stylistic/eslint-plugin"],
|
|
5
|
+
"categories": {
|
|
6
|
+
"correctness": "error",
|
|
7
|
+
"suspicious": "error",
|
|
8
|
+
"perf": "error",
|
|
9
|
+
"pedantic": "error",
|
|
10
|
+
"style": "error"
|
|
11
|
+
},
|
|
12
|
+
"env": {
|
|
13
|
+
"node": true,
|
|
14
|
+
"es2024": true
|
|
15
|
+
},
|
|
16
|
+
"rules": {
|
|
17
|
+
// custom AST rules from plugin.js
|
|
18
|
+
"zgeoff/no-top-level-arrow": "error",
|
|
19
|
+
"zgeoff/no-nested-function-declaration": "error",
|
|
20
|
+
"zgeoff/no-bare-named-exports": "error",
|
|
21
|
+
"zgeoff/no-inline-param-object-types": "error",
|
|
22
|
+
"zgeoff/no-destructured-params": "error",
|
|
23
|
+
"zgeoff/no-pick-destructuring": "error",
|
|
24
|
+
"zgeoff/no-ternary-args": "error",
|
|
25
|
+
"zgeoff/no-await-args": "error",
|
|
26
|
+
|
|
27
|
+
// enforced comment style — line comments unless JSDoc or exclamation
|
|
28
|
+
"@stylistic/multiline-comment-style": [
|
|
29
|
+
"error",
|
|
30
|
+
"separate-lines",
|
|
31
|
+
{ "checkJSDoc": false, "checkExclamation": false }
|
|
32
|
+
],
|
|
33
|
+
|
|
34
|
+
// own-line comments get a blank line above so they don't crowd the
|
|
35
|
+
// previous statement; comments opening a block/object/class/etc are exempt
|
|
36
|
+
"@stylistic/lines-around-comment": [
|
|
37
|
+
"error",
|
|
38
|
+
{
|
|
39
|
+
"beforeLineComment": true,
|
|
40
|
+
"beforeBlockComment": true,
|
|
41
|
+
"allowBlockStart": true,
|
|
42
|
+
"allowObjectStart": true,
|
|
43
|
+
"allowArrayStart": true,
|
|
44
|
+
"allowClassStart": true,
|
|
45
|
+
"allowEnumStart": true,
|
|
46
|
+
"allowInterfaceStart": true,
|
|
47
|
+
"allowModuleStart": true,
|
|
48
|
+
"allowTypeStart": true
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
|
|
52
|
+
// restriction-category cherry-picks
|
|
53
|
+
"typescript/no-explicit-any": "error",
|
|
54
|
+
"typescript/no-non-null-assertion": "error",
|
|
55
|
+
"import/no-cycle": "error",
|
|
56
|
+
|
|
57
|
+
"no-shadow": "error",
|
|
58
|
+
|
|
59
|
+
// separate type-only imports from value imports
|
|
60
|
+
"typescript/consistent-type-imports": [
|
|
61
|
+
"error",
|
|
62
|
+
{ "prefer": "type-imports", "fixStyle": "separate-type-imports" }
|
|
63
|
+
],
|
|
64
|
+
"no-duplicate-imports": ["error", { "allowSeparateTypeImports": true }],
|
|
65
|
+
// oxfmt's sortImports owns import declaration order; lint only member order
|
|
66
|
+
"sort-imports": ["error", { "ignoreDeclarationSort": true }],
|
|
67
|
+
|
|
68
|
+
// allows destructuring for omitting properties
|
|
69
|
+
"no-unused-vars": ["error", { "ignoreRestSiblings": true }],
|
|
70
|
+
|
|
71
|
+
// arrays: destructure instead of indexing; objects: house style is direct
|
|
72
|
+
// member access, so never required (the omit-via-rest exception lives in
|
|
73
|
+
// zgeoff/no-pick-destructuring)
|
|
74
|
+
"prefer-destructuring": [
|
|
75
|
+
"error",
|
|
76
|
+
{
|
|
77
|
+
"VariableDeclarator": { "array": true, "object": false },
|
|
78
|
+
"AssignmentExpression": { "array": false, "object": false }
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
|
|
82
|
+
// consistent function syntax — declarations top level, arrows for inside
|
|
83
|
+
// blocks and callbacks
|
|
84
|
+
"func-style": ["error", "declaration", { "allowArrowFunctions": true }],
|
|
85
|
+
"no-inner-declarations": ["error", "functions"],
|
|
86
|
+
"prefer-arrow-callback": ["error", { "allowNamedFunctions": false, "allowUnboundThis": false }],
|
|
87
|
+
|
|
88
|
+
// stylistic
|
|
89
|
+
"curly": ["error", "all"],
|
|
90
|
+
"unicorn/prefer-ternary": ["error", "only-single-line"],
|
|
91
|
+
"unicorn/prefer-node-protocol": "error",
|
|
92
|
+
"unicorn/no-array-sort": "error",
|
|
93
|
+
"unicorn/no-nested-ternary": "off",
|
|
94
|
+
"unicorn/no-useless-undefined": ["error", { "checkArguments": false }],
|
|
95
|
+
"unicorn/no-null": "off",
|
|
96
|
+
"unicorn/number-literal-case": "off",
|
|
97
|
+
"unicorn/filename-case": "off",
|
|
98
|
+
|
|
99
|
+
// category-enabled rules that contradict this config's style: inline named
|
|
100
|
+
// export declarations are the convention (group-exports demands the exact
|
|
101
|
+
// form no-bare-named-exports bans), keys group by meaning not alphabet,
|
|
102
|
+
// comments stay lowercase, short identifiers and literal numbers are fine
|
|
103
|
+
"import/prefer-default-export": "off",
|
|
104
|
+
"import/no-named-export": "off",
|
|
105
|
+
"import/exports-last": "off",
|
|
106
|
+
"import/group-exports": "off",
|
|
107
|
+
"sort-keys": "off",
|
|
108
|
+
"capitalized-comments": "off",
|
|
109
|
+
"id-length": "off",
|
|
110
|
+
"no-magic-numbers": "off",
|
|
111
|
+
// prefer-ternary (only-single-line, above) wants ternaries where they fit
|
|
112
|
+
// on one line; no-ternary would ban the same code
|
|
113
|
+
"no-ternary": "off",
|
|
114
|
+
// this config targets node/bun tooling — node builtins are the platform
|
|
115
|
+
"import/no-nodejs-modules": "off",
|
|
116
|
+
|
|
117
|
+
// preload-style modules (matcher registration, type augmentation) are
|
|
118
|
+
// side-effect imports by design
|
|
119
|
+
"import/no-unassigned-import": [
|
|
120
|
+
"error",
|
|
121
|
+
{ "allow": ["jest-extended", "**/augment-bun-test.ts"] }
|
|
122
|
+
],
|
|
123
|
+
|
|
124
|
+
// disable import rules that duplicate work TypeScript's compiler already
|
|
125
|
+
// does
|
|
126
|
+
"import/namespace": "off",
|
|
127
|
+
"import/named": "off",
|
|
128
|
+
"import/default": "off",
|
|
129
|
+
"import/no-named-as-default-member": "off"
|
|
130
|
+
},
|
|
131
|
+
"overrides": [
|
|
132
|
+
{
|
|
133
|
+
"files": ["**/*.test.ts"],
|
|
134
|
+
// one co-located suite per module with inline test data: file length
|
|
135
|
+
// scales with coverage, not complexity
|
|
136
|
+
"rules": {
|
|
137
|
+
"max-lines": "off"
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"files": ["**/*.js", "**/*.cjs", "**/*.mjs"],
|
|
142
|
+
// untyped JS is `any` end to end, so the unsafe-* rules flag every
|
|
143
|
+
// expression
|
|
144
|
+
"rules": {
|
|
145
|
+
"typescript/no-unsafe-argument": "off",
|
|
146
|
+
"typescript/no-unsafe-assignment": "off",
|
|
147
|
+
"typescript/no-unsafe-call": "off",
|
|
148
|
+
"typescript/no-unsafe-member-access": "off",
|
|
149
|
+
"typescript/no-unsafe-return": "off",
|
|
150
|
+
"typescript/no-unsafe-type-assertion": "off"
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zgeoff/oxlint-config",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Shareable oxlint config: strict categories, house-style rules, and a custom JS plugin",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/zgeoff/tools.git",
|
|
9
|
+
"directory": "packages/oxlint-config"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"oxlintrc.json",
|
|
13
|
+
"plugin.js",
|
|
14
|
+
"index.js"
|
|
15
|
+
],
|
|
16
|
+
"type": "module",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": "./index.js",
|
|
19
|
+
"./oxlintrc.json": "./oxlintrc.json",
|
|
20
|
+
"./plugin": "./plugin.js"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@stylistic/eslint-plugin": "5.10.0"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"oxlint": ">=1.71.0"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/plugin.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Custom lint rules, loaded through oxlint's jsPlugins (ESLint v9 rule API).
|
|
2
|
+
// Each rule bans an AST shape that no native oxlint rule can express, using
|
|
3
|
+
// esquery selectors.
|
|
4
|
+
|
|
5
|
+
function banSelectors(type, message, selectors) {
|
|
6
|
+
return {
|
|
7
|
+
meta: { type, messages: { banned: message }, schema: [] },
|
|
8
|
+
create(context) {
|
|
9
|
+
const entries = selectors.map((selector) => [
|
|
10
|
+
selector,
|
|
11
|
+
(node) => context.report({ node, messageId: 'banned' }),
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
return Object.fromEntries(entries);
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const plugin = {
|
|
20
|
+
meta: { name: 'zgeoff' },
|
|
21
|
+
rules: {
|
|
22
|
+
'no-top-level-arrow': banSelectors(
|
|
23
|
+
'problem',
|
|
24
|
+
'Declare top-level functions with the `function` keyword instead of assigning an arrow function.',
|
|
25
|
+
[
|
|
26
|
+
"Program > VariableDeclaration > VariableDeclarator[init.type='ArrowFunctionExpression']",
|
|
27
|
+
"Program > ExportNamedDeclaration > VariableDeclaration > VariableDeclarator[init.type='ArrowFunctionExpression']",
|
|
28
|
+
'Program > ExportDefaultDeclaration > ArrowFunctionExpression',
|
|
29
|
+
"Program > ExpressionStatement > AssignmentExpression[right.type='ArrowFunctionExpression']",
|
|
30
|
+
],
|
|
31
|
+
),
|
|
32
|
+
'no-nested-function-declaration': banSelectors(
|
|
33
|
+
'problem',
|
|
34
|
+
'Use an arrow function here; reserve the `function` keyword for top-level declarations.',
|
|
35
|
+
[
|
|
36
|
+
'FunctionDeclaration:not(Program > FunctionDeclaration):not(Program > ExportNamedDeclaration > FunctionDeclaration):not(Program > ExportDefaultDeclaration > FunctionDeclaration)',
|
|
37
|
+
],
|
|
38
|
+
),
|
|
39
|
+
'no-bare-named-exports': banSelectors(
|
|
40
|
+
'problem',
|
|
41
|
+
'Export declarations inline instead of listing names in `export { ... }`.',
|
|
42
|
+
['ExportNamedDeclaration[specifiers.length>0][declaration=null][source=null]'],
|
|
43
|
+
),
|
|
44
|
+
'no-inline-param-object-types': banSelectors(
|
|
45
|
+
'problem',
|
|
46
|
+
'Give this parameter a named interface or type alias instead of an inline object-literal type.',
|
|
47
|
+
[
|
|
48
|
+
':matches(FunctionDeclaration, FunctionExpression, ArrowFunctionExpression, TSDeclareFunction, TSFunctionType) > Identifier.params > TSTypeAnnotation > TSTypeLiteral',
|
|
49
|
+
'TSMethodSignature > Identifier.params > TSTypeAnnotation > TSTypeLiteral',
|
|
50
|
+
],
|
|
51
|
+
),
|
|
52
|
+
'no-destructured-params': banSelectors(
|
|
53
|
+
'suggestion',
|
|
54
|
+
'Accept the parameter whole instead of destructuring it in the signature.',
|
|
55
|
+
[
|
|
56
|
+
':matches(FunctionDeclaration, FunctionExpression, ArrowFunctionExpression) > ObjectPattern.params',
|
|
57
|
+
':matches(FunctionDeclaration, FunctionExpression, ArrowFunctionExpression) > AssignmentPattern.params > ObjectPattern',
|
|
58
|
+
],
|
|
59
|
+
),
|
|
60
|
+
'no-pick-destructuring': banSelectors(
|
|
61
|
+
'suggestion',
|
|
62
|
+
'Use direct member access; destructure an object only with a rest element to omit properties.',
|
|
63
|
+
['VariableDeclarator > ObjectPattern:not(:has(> RestElement))'],
|
|
64
|
+
),
|
|
65
|
+
'no-ternary-args': banSelectors(
|
|
66
|
+
'suggestion',
|
|
67
|
+
'Extract this ternary to a named const instead of passing it as an argument.',
|
|
68
|
+
[
|
|
69
|
+
'CallExpression > ConditionalExpression.arguments',
|
|
70
|
+
'NewExpression > ConditionalExpression.arguments',
|
|
71
|
+
],
|
|
72
|
+
),
|
|
73
|
+
'no-await-args': banSelectors(
|
|
74
|
+
'suggestion',
|
|
75
|
+
'Await into a named const instead of awaiting inside an argument list.',
|
|
76
|
+
['CallExpression > AwaitExpression.arguments', 'NewExpression > AwaitExpression.arguments'],
|
|
77
|
+
),
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export default plugin;
|