@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 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;