@zgeoff/oxlint-config 0.0.3 → 0.0.4
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 +3 -2
- package/oxlintrc.json +3 -1
- package/package.json +8 -1
- package/plugin.js +80 -2
package/README.md
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Shareable [oxlint](https://oxc.rs/docs/guide/usage/linter.html) config: every category at `error`,
|
|
4
4
|
restriction-rule cherry-picks, comment-style enforcement via `@stylistic`, and a bundled JS plugin
|
|
5
|
-
(`zgeoff/*` rules) banning
|
|
5
|
+
(`zgeoff/*` rules) banning shapes no native oxlint rule can express — top-level arrows, nested
|
|
6
6
|
function declarations, bare named exports, destructured and inline-typed parameters, ternary and
|
|
7
|
-
await arguments,
|
|
7
|
+
await arguments, awaits hidden inside a control-flow condition or a `&&`/`||`/`??` chain, and
|
|
8
|
+
single-line `/** … */` blocks (auto-fixed to multi-line; inline `@type`/`@lends` casts exempt).
|
|
8
9
|
|
|
9
10
|
## Usage
|
|
10
11
|
|
package/oxlintrc.json
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"zgeoff/no-await-args": "error",
|
|
26
26
|
"zgeoff/no-await-in-condition": "error",
|
|
27
27
|
"zgeoff/no-await-in-logical": "error",
|
|
28
|
+
"zgeoff/no-single-line-jsdoc": "error",
|
|
28
29
|
|
|
29
30
|
// enforced comment style — line comments unless JSDoc or exclamation
|
|
30
31
|
"@stylistic/multiline-comment-style": [
|
|
@@ -142,8 +143,9 @@
|
|
|
142
143
|
{
|
|
143
144
|
"files": ["**/*.js", "**/*.cjs", "**/*.mjs"],
|
|
144
145
|
// untyped JS is `any` end to end, so the unsafe-* rules flag every
|
|
145
|
-
// expression
|
|
146
|
+
// expression and strict-boolean-expressions every conditional
|
|
146
147
|
"rules": {
|
|
148
|
+
"typescript/strict-boolean-expressions": "off",
|
|
147
149
|
"typescript/no-unsafe-argument": "off",
|
|
148
150
|
"typescript/no-unsafe-assignment": "off",
|
|
149
151
|
"typescript/no-unsafe-call": "off",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zgeoff/oxlint-config",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Shareable oxlint config: strict categories, house-style rules, and a custom JS plugin",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -22,9 +22,16 @@
|
|
|
22
22
|
"publishConfig": {
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"typecheck": "tsc"
|
|
27
|
+
},
|
|
25
28
|
"dependencies": {
|
|
26
29
|
"@stylistic/eslint-plugin": "5.10.0"
|
|
27
30
|
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/bun": "1.3.14",
|
|
33
|
+
"typescript": "7.0.1-rc"
|
|
34
|
+
},
|
|
28
35
|
"peerDependencies": {
|
|
29
36
|
"oxlint": ">=1.71.0"
|
|
30
37
|
}
|
package/plugin.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Custom lint rules, loaded through oxlint's jsPlugins (ESLint v9 rule API).
|
|
2
|
-
// Each rule bans
|
|
3
|
-
//
|
|
2
|
+
// Each rule bans a shape no native oxlint rule can express — esquery selectors
|
|
3
|
+
// for AST shapes, a source-comment scan where the target isn't
|
|
4
|
+
// esquery-selectable.
|
|
4
5
|
|
|
5
6
|
function banSelectors(type, message, selectors) {
|
|
6
7
|
return {
|
|
@@ -16,6 +17,82 @@ function banSelectors(type, message, selectors) {
|
|
|
16
17
|
};
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
// mirrors eslint-plugin-jsdoc's default singleLineTags: blocks that exist to
|
|
21
|
+
// cast inline (`/** @type {Foo} */ (bar)`) stay single-line by design
|
|
22
|
+
const inlineTagPattern = /^@(?:type|lends)\b/u;
|
|
23
|
+
|
|
24
|
+
function isSingleLineJSDoc(comment) {
|
|
25
|
+
return (
|
|
26
|
+
comment.type === 'Block' &&
|
|
27
|
+
comment.value.startsWith('*') &&
|
|
28
|
+
comment.loc.start.line === comment.loc.end.line
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Builds the multi-line replacement for a single-line JSDoc comment, or null
|
|
34
|
+
* when the comment shares its line with code — expanding those in place would
|
|
35
|
+
* scramble the surrounding statement, so they are reported without a fix.
|
|
36
|
+
*/
|
|
37
|
+
function planExpansion(text, comment) {
|
|
38
|
+
if (!isOwnLine(text, comment)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const lineStart = text.lastIndexOf('\n', comment.range[0] - 1) + 1;
|
|
43
|
+
const indent = text.slice(lineStart, comment.range[0]);
|
|
44
|
+
const body = comment.value.slice(1).trim();
|
|
45
|
+
const bodyLine = body === '' ? `${indent} *` : `${indent} * ${body}`;
|
|
46
|
+
|
|
47
|
+
return `/**\n${bodyLine}\n${indent} */`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isOwnLine(text, comment) {
|
|
51
|
+
const [start, end] = comment.range;
|
|
52
|
+
const lineStart = text.lastIndexOf('\n', start - 1) + 1;
|
|
53
|
+
const lineEnd = text.indexOf('\n', end);
|
|
54
|
+
const sliceEnd = lineEnd === -1 ? text.length : lineEnd;
|
|
55
|
+
|
|
56
|
+
return /^[ \t]*$/u.test(text.slice(lineStart, start)) && text.slice(end, sliceEnd).trim() === '';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const noSingleLineJSDoc = {
|
|
60
|
+
meta: {
|
|
61
|
+
type: 'layout',
|
|
62
|
+
fixable: 'whitespace',
|
|
63
|
+
messages: {
|
|
64
|
+
singleLine:
|
|
65
|
+
'Write JSDoc blocks multi-line: `/**` alone, one `*`-prefixed line per point, `*/` alone.',
|
|
66
|
+
},
|
|
67
|
+
schema: [],
|
|
68
|
+
},
|
|
69
|
+
create(context) {
|
|
70
|
+
return {
|
|
71
|
+
Program() {
|
|
72
|
+
const offenders = context.sourceCode
|
|
73
|
+
.getAllComments()
|
|
74
|
+
.filter(
|
|
75
|
+
(comment) =>
|
|
76
|
+
isSingleLineJSDoc(comment) && !inlineTagPattern.test(comment.value.slice(1).trim()),
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
for (const comment of offenders) {
|
|
80
|
+
const replacement = planExpansion(context.sourceCode.text, comment);
|
|
81
|
+
|
|
82
|
+
context.report({
|
|
83
|
+
loc: comment.loc,
|
|
84
|
+
messageId: 'singleLine',
|
|
85
|
+
fix:
|
|
86
|
+
replacement === null
|
|
87
|
+
? undefined
|
|
88
|
+
: (fixer) => fixer.replaceTextRange(comment.range, replacement),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
|
|
19
96
|
const plugin = {
|
|
20
97
|
meta: { name: 'zgeoff' },
|
|
21
98
|
rules: {
|
|
@@ -88,6 +165,7 @@ const plugin = {
|
|
|
88
165
|
'Await into a named const instead of chaining it after a && / || / ?? operator.',
|
|
89
166
|
['LogicalExpression > AwaitExpression'],
|
|
90
167
|
),
|
|
168
|
+
'no-single-line-jsdoc': noSingleLineJSDoc,
|
|
91
169
|
},
|
|
92
170
|
};
|
|
93
171
|
|