@timmo001/oxlint-rules 0.5.0 → 0.6.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 +20 -2
- package/dist/cli.js +78 -14
- package/dist/configs/recommended-effect.js +125 -60
- package/dist/effect/index.js +69 -4
- package/package.json +10 -9
- package/skills/install-timmo-oxlint-rules/SKILL.md +18 -5
- package/src/effect/index.ts +2 -0
- package/src/effect/rules/no-raw-gh.ts +126 -0
package/README.md
CHANGED
|
@@ -69,6 +69,19 @@ the package configs to merge into the target Oxlint config. Effect settings
|
|
|
69
69
|
remain opt-in. It excludes tests and repository metadata, and refuses to
|
|
70
70
|
replace an existing destination unless `--force` is passed.
|
|
71
71
|
|
|
72
|
+
## Skills
|
|
73
|
+
|
|
74
|
+
[`install-timmo-oxlint-rules`](skills/install-timmo-oxlint-rules/SKILL.md)
|
|
75
|
+
guides agents through installing or copying these rules:
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
npx skills add timmo001/oxlint-rules --skill install-timmo-oxlint-rules
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
[`add-oxlint-rule`](skills/add-oxlint-rule/SKILL.md) and
|
|
82
|
+
[`release-oxlint-rules`](skills/release-oxlint-rules/SKILL.md) are for
|
|
83
|
+
maintaining this package.
|
|
84
|
+
|
|
72
85
|
## Rules
|
|
73
86
|
|
|
74
87
|
### `anti-slop` and `anti-slop-effect`
|
|
@@ -122,6 +135,12 @@ they only run when the consumer enables `typeAware`.
|
|
|
122
135
|
|
|
123
136
|
### `timmo-effect`
|
|
124
137
|
|
|
138
|
+
- `no-raw-gh`: reports the raw `execute`, `json` and `stream` methods called on
|
|
139
|
+
a `yield* Gh` binding from `@timmo001/effect-gh`, `Api.raw` from that package,
|
|
140
|
+
and any call whose first argument is `"gh"` or an array starting with `"gh"`,
|
|
141
|
+
such as `executor.run("gh", ...)` or `Bun.spawn(["gh", ...])`. Use the
|
|
142
|
+
package's typed operations, and add one to effect-gh when a command has none.
|
|
143
|
+
effect-gh's own relative imports are not reported.
|
|
125
144
|
- `no-try-catch-in-effect-generators`: diagnoses synchronous `try/catch` owned
|
|
126
145
|
by generators passed directly to `Effect.gen` or the curried `Effect.fn`
|
|
127
146
|
form. It leaves ordinary async polling, nested callback boundaries,
|
|
@@ -139,8 +158,7 @@ Initialise the upstream source and run the package checks:
|
|
|
139
158
|
|
|
140
159
|
```sh
|
|
141
160
|
git submodule update --init --recursive
|
|
142
|
-
mise run check
|
|
143
|
-
mise run build
|
|
161
|
+
mise run check ::: build
|
|
144
162
|
mise run test:package
|
|
145
163
|
npm pack --dry-run
|
|
146
164
|
bunx jsr@0.14.3 publish --dry-run --allow-dirty
|
package/dist/cli.js
CHANGED
|
@@ -2985,8 +2985,71 @@ var recommended_default = recommended;
|
|
|
2985
2985
|
// src/effect/index.ts
|
|
2986
2986
|
import { eslintCompatPlugin as eslintCompatPlugin3 } from "@oxlint/plugins";
|
|
2987
2987
|
|
|
2988
|
-
// src/effect/rules/no-
|
|
2988
|
+
// src/effect/rules/no-raw-gh.ts
|
|
2989
2989
|
import { defineRule as defineRule24 } from "@oxlint/plugins";
|
|
2990
|
+
var PACKAGE = "@timmo001/effect-gh";
|
|
2991
|
+
var rawMethods = new Set(["execute", "json", "stream"]);
|
|
2992
|
+
function isEffectGhImport(sourceCode, identifier, name) {
|
|
2993
|
+
return resolveVariable(sourceCode, identifier)?.defs.some((definition) => {
|
|
2994
|
+
if (definition.type !== "ImportBinding" || definition.parent?.type !== "ImportDeclaration" || definition.parent.source.value !== PACKAGE || definition.node.type !== "ImportSpecifier") {
|
|
2995
|
+
return false;
|
|
2996
|
+
}
|
|
2997
|
+
const imported = definition.node.imported;
|
|
2998
|
+
return (imported.type === "Identifier" ? imported.name : imported.value) === name;
|
|
2999
|
+
}) ?? false;
|
|
3000
|
+
}
|
|
3001
|
+
function isYieldGh(sourceCode, node) {
|
|
3002
|
+
return node?.type === "YieldExpression" && node.delegate && node.argument?.type === "Identifier" && isEffectGhImport(sourceCode, node.argument, "Gh");
|
|
3003
|
+
}
|
|
3004
|
+
function isGhBinding(sourceCode, identifier) {
|
|
3005
|
+
return resolveVariable(sourceCode, identifier)?.defs.some((definition) => definition.type === "Variable" && definition.node.type === "VariableDeclarator" && isYieldGh(sourceCode, definition.node.init)) ?? false;
|
|
3006
|
+
}
|
|
3007
|
+
function isGhArgument(node) {
|
|
3008
|
+
if (node?.type === "Literal")
|
|
3009
|
+
return node.value === "gh";
|
|
3010
|
+
if (node?.type !== "ArrayExpression")
|
|
3011
|
+
return false;
|
|
3012
|
+
const first = node.elements[0];
|
|
3013
|
+
return first?.type === "Literal" && first.value === "gh";
|
|
3014
|
+
}
|
|
3015
|
+
var noRawGhRule = defineRule24({
|
|
3016
|
+
meta: {
|
|
3017
|
+
type: "problem",
|
|
3018
|
+
docs: {
|
|
3019
|
+
description: "Use @timmo001/effect-gh typed operations instead of the raw Gh methods, Api.raw, or spawning gh directly."
|
|
3020
|
+
},
|
|
3021
|
+
messages: {
|
|
3022
|
+
rawGh: "Use a typed effect-gh operation instead of `Gh.{{method}}` with raw argv. Add one to effect-gh if the command has none.",
|
|
3023
|
+
rawApi: "Use `Api.json`, `Api.pages`, `Api.empty`, `Api.text` or `Api.headers` instead of `Api.raw`, so the response is typed.",
|
|
3024
|
+
spawnGh: "Run gh through a typed @timmo001/effect-gh operation instead of spawning it directly."
|
|
3025
|
+
}
|
|
3026
|
+
},
|
|
3027
|
+
create(context) {
|
|
3028
|
+
return {
|
|
3029
|
+
CallExpression(node) {
|
|
3030
|
+
const callee = node.callee;
|
|
3031
|
+
const method = staticMemberName(callee);
|
|
3032
|
+
if (callee.type === "MemberExpression" && method !== null) {
|
|
3033
|
+
const object = callee.object;
|
|
3034
|
+
if (rawMethods.has(method) && (isYieldGh(context.sourceCode, object) || object.type === "Identifier" && isGhBinding(context.sourceCode, object))) {
|
|
3035
|
+
context.report({ node, messageId: "rawGh", data: { method } });
|
|
3036
|
+
return;
|
|
3037
|
+
}
|
|
3038
|
+
if (method === "raw" && object.type === "Identifier" && isEffectGhImport(context.sourceCode, object, "Api")) {
|
|
3039
|
+
context.report({ node, messageId: "rawApi" });
|
|
3040
|
+
return;
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
3043
|
+
if (isGhArgument(node.arguments[0]))
|
|
3044
|
+
context.report({ node, messageId: "spawnGh" });
|
|
3045
|
+
}
|
|
3046
|
+
};
|
|
3047
|
+
}
|
|
3048
|
+
});
|
|
3049
|
+
|
|
3050
|
+
// src/effect/rules/no-try-catch-in-effect-generators.ts
|
|
3051
|
+
import { defineRule as defineRule25 } from "@oxlint/plugins";
|
|
3052
|
+
|
|
2990
3053
|
// src/shared/effect.ts
|
|
2991
3054
|
function isEffectImport(sourceCode, identifier, namespace) {
|
|
2992
3055
|
const variable = resolveVariable(sourceCode, identifier);
|
|
@@ -3033,7 +3096,7 @@ function isRecognisedEffectGenerator(sourceCode, owner) {
|
|
|
3033
3096
|
const factoryCall = parent.callee;
|
|
3034
3097
|
return factoryCall.type === "CallExpression" && isEffectMethod(sourceCode, factoryCall.callee, "fn");
|
|
3035
3098
|
}
|
|
3036
|
-
var noTryCatchInEffectGeneratorsRule =
|
|
3099
|
+
var noTryCatchInEffectGeneratorsRule = defineRule25({
|
|
3037
3100
|
meta: {
|
|
3038
3101
|
type: "problem",
|
|
3039
3102
|
docs: {
|
|
@@ -3059,7 +3122,7 @@ var noTryCatchInEffectGeneratorsRule = defineRule24({
|
|
|
3059
3122
|
});
|
|
3060
3123
|
|
|
3061
3124
|
// src/effect/rules/prefer-platform-services.ts
|
|
3062
|
-
import { defineRule as
|
|
3125
|
+
import { defineRule as defineRule26 } from "@oxlint/plugins";
|
|
3063
3126
|
var moduleServices = new Map([
|
|
3064
3127
|
["child_process", "childProcess"],
|
|
3065
3128
|
["fs", "fileSystem"],
|
|
@@ -3072,7 +3135,7 @@ var bunServices = new Map([
|
|
|
3072
3135
|
["spawnSync", "childProcess"],
|
|
3073
3136
|
["write", "fileSystem"]
|
|
3074
3137
|
]);
|
|
3075
|
-
var preferPlatformServicesRule =
|
|
3138
|
+
var preferPlatformServicesRule = defineRule26({
|
|
3076
3139
|
meta: {
|
|
3077
3140
|
type: "suggestion",
|
|
3078
3141
|
docs: {
|
|
@@ -3135,6 +3198,7 @@ var preferPlatformServicesRule = defineRule25({
|
|
|
3135
3198
|
var timmoEffectPlugin = eslintCompatPlugin3({
|
|
3136
3199
|
meta: { name: "timmo-effect" },
|
|
3137
3200
|
rules: {
|
|
3201
|
+
"no-raw-gh": noRawGhRule,
|
|
3138
3202
|
"no-try-catch-in-effect-generators": noTryCatchInEffectGeneratorsRule,
|
|
3139
3203
|
"prefer-platform-services": preferPlatformServicesRule
|
|
3140
3204
|
}
|
|
@@ -3145,7 +3209,7 @@ var effect_default = timmoEffectPlugin;
|
|
|
3145
3209
|
import { eslintCompatPlugin as eslintCompatPlugin4 } from "@oxlint/plugins";
|
|
3146
3210
|
|
|
3147
3211
|
// vendor/anti-slop/src/effect/rules/no-manual-effect-error-tag.ts
|
|
3148
|
-
import { defineRule as
|
|
3212
|
+
import { defineRule as defineRule27 } from "@oxlint/plugins";
|
|
3149
3213
|
|
|
3150
3214
|
// vendor/anti-slop/src/effect/shared/tagged-values.ts
|
|
3151
3215
|
var equalityOperators = new Set(["==", "===", "!=", "!=="]);
|
|
@@ -3192,7 +3256,7 @@ var isMatchPatternObject = (node) => {
|
|
|
3192
3256
|
};
|
|
3193
3257
|
|
|
3194
3258
|
// vendor/anti-slop/src/effect/rules/no-manual-effect-error-tag.ts
|
|
3195
|
-
var noManualEffectErrorTagRule =
|
|
3259
|
+
var noManualEffectErrorTagRule = defineRule27({
|
|
3196
3260
|
meta: {
|
|
3197
3261
|
type: "problem",
|
|
3198
3262
|
docs: {
|
|
@@ -3229,8 +3293,8 @@ var noManualEffectErrorTagRule = defineRule26({
|
|
|
3229
3293
|
});
|
|
3230
3294
|
|
|
3231
3295
|
// vendor/anti-slop/src/effect/rules/no-manual-tag-comparison.ts
|
|
3232
|
-
import { defineRule as
|
|
3233
|
-
var noManualTagComparisonRule =
|
|
3296
|
+
import { defineRule as defineRule28 } from "@oxlint/plugins";
|
|
3297
|
+
var noManualTagComparisonRule = defineRule28({
|
|
3234
3298
|
meta: {
|
|
3235
3299
|
type: "problem",
|
|
3236
3300
|
docs: {
|
|
@@ -3260,8 +3324,8 @@ var noManualTagComparisonRule = defineRule27({
|
|
|
3260
3324
|
});
|
|
3261
3325
|
|
|
3262
3326
|
// vendor/anti-slop/src/effect/rules/no-manual-tagged-construction.ts
|
|
3263
|
-
import { defineRule as
|
|
3264
|
-
var noManualTaggedConstructionRule =
|
|
3327
|
+
import { defineRule as defineRule29 } from "@oxlint/plugins";
|
|
3328
|
+
var noManualTaggedConstructionRule = defineRule29({
|
|
3265
3329
|
meta: {
|
|
3266
3330
|
type: "problem",
|
|
3267
3331
|
docs: {
|
|
@@ -3286,7 +3350,7 @@ var noManualTaggedConstructionRule = defineRule28({
|
|
|
3286
3350
|
});
|
|
3287
3351
|
|
|
3288
3352
|
// vendor/anti-slop/src/effect/rules/no-service-constructor-imports.ts
|
|
3289
|
-
import { defineRule as
|
|
3353
|
+
import { defineRule as defineRule30 } from "@oxlint/plugins";
|
|
3290
3354
|
var SERVICE_CONSTRUCTOR_NAME = /^make[A-Z]/u;
|
|
3291
3355
|
var TEST_FILE = /\.(?:test|spec)\.[cm]?[jt]sx?$/u;
|
|
3292
3356
|
function isProjectLocalImport(source) {
|
|
@@ -3297,7 +3361,7 @@ function getImportedName(specifier) {
|
|
|
3297
3361
|
return specifier.imported.name;
|
|
3298
3362
|
return specifier.imported.value;
|
|
3299
3363
|
}
|
|
3300
|
-
var noServiceConstructorImportsRule =
|
|
3364
|
+
var noServiceConstructorImportsRule = defineRule30({
|
|
3301
3365
|
meta: {
|
|
3302
3366
|
type: "problem",
|
|
3303
3367
|
docs: {
|
|
@@ -3331,9 +3395,9 @@ var noServiceConstructorImportsRule = defineRule29({
|
|
|
3331
3395
|
});
|
|
3332
3396
|
|
|
3333
3397
|
// vendor/anti-slop/src/effect/rules/prefer-effect-match.ts
|
|
3334
|
-
import { defineRule as
|
|
3398
|
+
import { defineRule as defineRule31 } from "@oxlint/plugins";
|
|
3335
3399
|
var equalityOperators2 = new Set(["==", "===", "!=", "!=="]);
|
|
3336
|
-
var preferEffectMatchRule =
|
|
3400
|
+
var preferEffectMatchRule = defineRule31({
|
|
3337
3401
|
meta: {
|
|
3338
3402
|
type: "problem",
|
|
3339
3403
|
docs: {
|
|
@@ -6,7 +6,7 @@ function defineConfig(config) {
|
|
|
6
6
|
// src/effect/index.ts
|
|
7
7
|
import { eslintCompatPlugin } from "@oxlint/plugins";
|
|
8
8
|
|
|
9
|
-
// src/effect/rules/no-
|
|
9
|
+
// src/effect/rules/no-raw-gh.ts
|
|
10
10
|
import { defineRule } from "@oxlint/plugins";
|
|
11
11
|
|
|
12
12
|
// src/shared/ast.ts
|
|
@@ -37,6 +37,70 @@ function resolveVariable(sourceCode, identifier) {
|
|
|
37
37
|
}
|
|
38
38
|
return null;
|
|
39
39
|
}
|
|
40
|
+
// src/effect/rules/no-raw-gh.ts
|
|
41
|
+
var PACKAGE = "@timmo001/effect-gh";
|
|
42
|
+
var rawMethods = new Set(["execute", "json", "stream"]);
|
|
43
|
+
function isEffectGhImport(sourceCode, identifier, name) {
|
|
44
|
+
return resolveVariable(sourceCode, identifier)?.defs.some((definition) => {
|
|
45
|
+
if (definition.type !== "ImportBinding" || definition.parent?.type !== "ImportDeclaration" || definition.parent.source.value !== PACKAGE || definition.node.type !== "ImportSpecifier") {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const imported = definition.node.imported;
|
|
49
|
+
return (imported.type === "Identifier" ? imported.name : imported.value) === name;
|
|
50
|
+
}) ?? false;
|
|
51
|
+
}
|
|
52
|
+
function isYieldGh(sourceCode, node) {
|
|
53
|
+
return node?.type === "YieldExpression" && node.delegate && node.argument?.type === "Identifier" && isEffectGhImport(sourceCode, node.argument, "Gh");
|
|
54
|
+
}
|
|
55
|
+
function isGhBinding(sourceCode, identifier) {
|
|
56
|
+
return resolveVariable(sourceCode, identifier)?.defs.some((definition) => definition.type === "Variable" && definition.node.type === "VariableDeclarator" && isYieldGh(sourceCode, definition.node.init)) ?? false;
|
|
57
|
+
}
|
|
58
|
+
function isGhArgument(node) {
|
|
59
|
+
if (node?.type === "Literal")
|
|
60
|
+
return node.value === "gh";
|
|
61
|
+
if (node?.type !== "ArrayExpression")
|
|
62
|
+
return false;
|
|
63
|
+
const first = node.elements[0];
|
|
64
|
+
return first?.type === "Literal" && first.value === "gh";
|
|
65
|
+
}
|
|
66
|
+
var noRawGhRule = defineRule({
|
|
67
|
+
meta: {
|
|
68
|
+
type: "problem",
|
|
69
|
+
docs: {
|
|
70
|
+
description: "Use @timmo001/effect-gh typed operations instead of the raw Gh methods, Api.raw, or spawning gh directly."
|
|
71
|
+
},
|
|
72
|
+
messages: {
|
|
73
|
+
rawGh: "Use a typed effect-gh operation instead of `Gh.{{method}}` with raw argv. Add one to effect-gh if the command has none.",
|
|
74
|
+
rawApi: "Use `Api.json`, `Api.pages`, `Api.empty`, `Api.text` or `Api.headers` instead of `Api.raw`, so the response is typed.",
|
|
75
|
+
spawnGh: "Run gh through a typed @timmo001/effect-gh operation instead of spawning it directly."
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
create(context) {
|
|
79
|
+
return {
|
|
80
|
+
CallExpression(node) {
|
|
81
|
+
const callee = node.callee;
|
|
82
|
+
const method = staticMemberName(callee);
|
|
83
|
+
if (callee.type === "MemberExpression" && method !== null) {
|
|
84
|
+
const object = callee.object;
|
|
85
|
+
if (rawMethods.has(method) && (isYieldGh(context.sourceCode, object) || object.type === "Identifier" && isGhBinding(context.sourceCode, object))) {
|
|
86
|
+
context.report({ node, messageId: "rawGh", data: { method } });
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (method === "raw" && object.type === "Identifier" && isEffectGhImport(context.sourceCode, object, "Api")) {
|
|
90
|
+
context.report({ node, messageId: "rawApi" });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (isGhArgument(node.arguments[0]))
|
|
95
|
+
context.report({ node, messageId: "spawnGh" });
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// src/effect/rules/no-try-catch-in-effect-generators.ts
|
|
102
|
+
import { defineRule as defineRule2 } from "@oxlint/plugins";
|
|
103
|
+
|
|
40
104
|
// src/shared/effect.ts
|
|
41
105
|
function isEffectImport(sourceCode, identifier, namespace) {
|
|
42
106
|
const variable = resolveVariable(sourceCode, identifier);
|
|
@@ -83,7 +147,7 @@ function isRecognisedEffectGenerator(sourceCode, owner) {
|
|
|
83
147
|
const factoryCall = parent.callee;
|
|
84
148
|
return factoryCall.type === "CallExpression" && isEffectMethod(sourceCode, factoryCall.callee, "fn");
|
|
85
149
|
}
|
|
86
|
-
var noTryCatchInEffectGeneratorsRule =
|
|
150
|
+
var noTryCatchInEffectGeneratorsRule = defineRule2({
|
|
87
151
|
meta: {
|
|
88
152
|
type: "problem",
|
|
89
153
|
docs: {
|
|
@@ -109,7 +173,7 @@ var noTryCatchInEffectGeneratorsRule = defineRule({
|
|
|
109
173
|
});
|
|
110
174
|
|
|
111
175
|
// src/effect/rules/prefer-platform-services.ts
|
|
112
|
-
import { defineRule as
|
|
176
|
+
import { defineRule as defineRule3 } from "@oxlint/plugins";
|
|
113
177
|
var moduleServices = new Map([
|
|
114
178
|
["child_process", "childProcess"],
|
|
115
179
|
["fs", "fileSystem"],
|
|
@@ -122,7 +186,7 @@ var bunServices = new Map([
|
|
|
122
186
|
["spawnSync", "childProcess"],
|
|
123
187
|
["write", "fileSystem"]
|
|
124
188
|
]);
|
|
125
|
-
var preferPlatformServicesRule =
|
|
189
|
+
var preferPlatformServicesRule = defineRule3({
|
|
126
190
|
meta: {
|
|
127
191
|
type: "suggestion",
|
|
128
192
|
docs: {
|
|
@@ -185,6 +249,7 @@ var preferPlatformServicesRule = defineRule2({
|
|
|
185
249
|
var timmoEffectPlugin = eslintCompatPlugin({
|
|
186
250
|
meta: { name: "timmo-effect" },
|
|
187
251
|
rules: {
|
|
252
|
+
"no-raw-gh": noRawGhRule,
|
|
188
253
|
"no-try-catch-in-effect-generators": noTryCatchInEffectGeneratorsRule,
|
|
189
254
|
"prefer-platform-services": preferPlatformServicesRule
|
|
190
255
|
}
|
|
@@ -195,7 +260,7 @@ var effect_default = timmoEffectPlugin;
|
|
|
195
260
|
import { eslintCompatPlugin as eslintCompatPlugin2 } from "@oxlint/plugins";
|
|
196
261
|
|
|
197
262
|
// vendor/anti-slop/src/effect/rules/no-manual-effect-error-tag.ts
|
|
198
|
-
import { defineRule as
|
|
263
|
+
import { defineRule as defineRule4 } from "@oxlint/plugins";
|
|
199
264
|
|
|
200
265
|
// vendor/anti-slop/src/effect/shared/tagged-values.ts
|
|
201
266
|
var equalityOperators = new Set(["==", "===", "!=", "!=="]);
|
|
@@ -242,7 +307,7 @@ var isMatchPatternObject = (node) => {
|
|
|
242
307
|
};
|
|
243
308
|
|
|
244
309
|
// vendor/anti-slop/src/effect/rules/no-manual-effect-error-tag.ts
|
|
245
|
-
var noManualEffectErrorTagRule =
|
|
310
|
+
var noManualEffectErrorTagRule = defineRule4({
|
|
246
311
|
meta: {
|
|
247
312
|
type: "problem",
|
|
248
313
|
docs: {
|
|
@@ -279,8 +344,8 @@ var noManualEffectErrorTagRule = defineRule3({
|
|
|
279
344
|
});
|
|
280
345
|
|
|
281
346
|
// vendor/anti-slop/src/effect/rules/no-manual-tag-comparison.ts
|
|
282
|
-
import { defineRule as
|
|
283
|
-
var noManualTagComparisonRule =
|
|
347
|
+
import { defineRule as defineRule5 } from "@oxlint/plugins";
|
|
348
|
+
var noManualTagComparisonRule = defineRule5({
|
|
284
349
|
meta: {
|
|
285
350
|
type: "problem",
|
|
286
351
|
docs: {
|
|
@@ -310,8 +375,8 @@ var noManualTagComparisonRule = defineRule4({
|
|
|
310
375
|
});
|
|
311
376
|
|
|
312
377
|
// vendor/anti-slop/src/effect/rules/no-manual-tagged-construction.ts
|
|
313
|
-
import { defineRule as
|
|
314
|
-
var noManualTaggedConstructionRule =
|
|
378
|
+
import { defineRule as defineRule6 } from "@oxlint/plugins";
|
|
379
|
+
var noManualTaggedConstructionRule = defineRule6({
|
|
315
380
|
meta: {
|
|
316
381
|
type: "problem",
|
|
317
382
|
docs: {
|
|
@@ -336,7 +401,7 @@ var noManualTaggedConstructionRule = defineRule5({
|
|
|
336
401
|
});
|
|
337
402
|
|
|
338
403
|
// vendor/anti-slop/src/effect/rules/no-service-constructor-imports.ts
|
|
339
|
-
import { defineRule as
|
|
404
|
+
import { defineRule as defineRule7 } from "@oxlint/plugins";
|
|
340
405
|
var SERVICE_CONSTRUCTOR_NAME = /^make[A-Z]/u;
|
|
341
406
|
var TEST_FILE = /\.(?:test|spec)\.[cm]?[jt]sx?$/u;
|
|
342
407
|
function isProjectLocalImport(source) {
|
|
@@ -347,7 +412,7 @@ function getImportedName(specifier) {
|
|
|
347
412
|
return specifier.imported.name;
|
|
348
413
|
return specifier.imported.value;
|
|
349
414
|
}
|
|
350
|
-
var noServiceConstructorImportsRule =
|
|
415
|
+
var noServiceConstructorImportsRule = defineRule7({
|
|
351
416
|
meta: {
|
|
352
417
|
type: "problem",
|
|
353
418
|
docs: {
|
|
@@ -381,9 +446,9 @@ var noServiceConstructorImportsRule = defineRule6({
|
|
|
381
446
|
});
|
|
382
447
|
|
|
383
448
|
// vendor/anti-slop/src/effect/rules/prefer-effect-match.ts
|
|
384
|
-
import { defineRule as
|
|
449
|
+
import { defineRule as defineRule8 } from "@oxlint/plugins";
|
|
385
450
|
var equalityOperators2 = new Set(["==", "===", "!=", "!=="]);
|
|
386
|
-
var preferEffectMatchRule =
|
|
451
|
+
var preferEffectMatchRule = defineRule8({
|
|
387
452
|
meta: {
|
|
388
453
|
type: "problem",
|
|
389
454
|
docs: {
|
|
@@ -449,7 +514,7 @@ function enablePluginRules(namespace, plugin) {
|
|
|
449
514
|
import { eslintCompatPlugin as eslintCompatPlugin3 } from "@oxlint/plugins";
|
|
450
515
|
|
|
451
516
|
// vendor/anti-slop/src/rules/no-array-filter-map.ts
|
|
452
|
-
import { defineRule as
|
|
517
|
+
import { defineRule as defineRule9 } from "@oxlint/plugins";
|
|
453
518
|
|
|
454
519
|
// vendor/anti-slop/src/shared/array-method.ts
|
|
455
520
|
function unwrapArrayExpression(node) {
|
|
@@ -524,7 +589,7 @@ function isKnownArrayExpression(sourceCode, node, visited = new Set) {
|
|
|
524
589
|
}
|
|
525
590
|
|
|
526
591
|
// vendor/anti-slop/src/rules/no-array-filter-map.ts
|
|
527
|
-
var noArrayFilterMapRule =
|
|
592
|
+
var noArrayFilterMapRule = defineRule9({
|
|
528
593
|
meta: {
|
|
529
594
|
type: "suggestion",
|
|
530
595
|
docs: { description: "Disallow adjacent array filter/map passes in favor of lazy iterator helpers or a single transformation." },
|
|
@@ -553,7 +618,7 @@ var noArrayFilterMapRule = defineRule8({
|
|
|
553
618
|
});
|
|
554
619
|
|
|
555
620
|
// vendor/anti-slop/src/rules/no-reduce-accumulator-copy.ts
|
|
556
|
-
import { defineRule as
|
|
621
|
+
import { defineRule as defineRule10 } from "@oxlint/plugins";
|
|
557
622
|
function enclosingReducer(node) {
|
|
558
623
|
let parent = node.parent;
|
|
559
624
|
while (parent !== null) {
|
|
@@ -603,7 +668,7 @@ function isGlobalCopyOwner(sourceCode, node, name) {
|
|
|
603
668
|
const variable = resolveArrayBinding(sourceCode, node);
|
|
604
669
|
return variable === null || variable.defs.length === 0;
|
|
605
670
|
}
|
|
606
|
-
var noReduceAccumulatorCopyRule =
|
|
671
|
+
var noReduceAccumulatorCopyRule = defineRule10({
|
|
607
672
|
meta: {
|
|
608
673
|
type: "problem",
|
|
609
674
|
docs: { description: "Disallow copying growing reducer accumulators with Object.assign, Array.from, or array copy methods." },
|
|
@@ -644,7 +709,7 @@ var noReduceAccumulatorCopyRule = defineRule9({
|
|
|
644
709
|
});
|
|
645
710
|
|
|
646
711
|
// vendor/anti-slop/src/rules/no-chained-type-assertions.ts
|
|
647
|
-
import { defineRule as
|
|
712
|
+
import { defineRule as defineRule11 } from "@oxlint/plugins";
|
|
648
713
|
function isTypeAssertionExpression(node) {
|
|
649
714
|
return node.type === "TSAsExpression" || node.type === "TSTypeAssertion";
|
|
650
715
|
}
|
|
@@ -679,7 +744,7 @@ function isForbiddenAssertionChain(node) {
|
|
|
679
744
|
}
|
|
680
745
|
return assertionCount > 1 && hasNonConstAssertion;
|
|
681
746
|
}
|
|
682
|
-
var noChainedTypeAssertionsRule =
|
|
747
|
+
var noChainedTypeAssertionsRule = defineRule11({
|
|
683
748
|
meta: {
|
|
684
749
|
type: "problem",
|
|
685
750
|
docs: {
|
|
@@ -703,7 +768,7 @@ var noChainedTypeAssertionsRule = defineRule10({
|
|
|
703
768
|
});
|
|
704
769
|
|
|
705
770
|
// vendor/anti-slop/src/rules/no-conditional-empty-object-spread.ts
|
|
706
|
-
import { defineRule as
|
|
771
|
+
import { defineRule as defineRule12 } from "@oxlint/plugins";
|
|
707
772
|
function unwrapParentheses(node) {
|
|
708
773
|
let current = node;
|
|
709
774
|
while (current.type === "ParenthesizedExpression") {
|
|
@@ -718,7 +783,7 @@ function isConditionalEmptyObjectSpread(node) {
|
|
|
718
783
|
const conditional = unwrapParentheses(node);
|
|
719
784
|
return conditional.type === "ConditionalExpression" && (isEmptyObjectExpression(conditional.consequent) || isEmptyObjectExpression(conditional.alternate));
|
|
720
785
|
}
|
|
721
|
-
var noConditionalEmptyObjectSpreadRule =
|
|
786
|
+
var noConditionalEmptyObjectSpreadRule = defineRule12({
|
|
722
787
|
meta: {
|
|
723
788
|
type: "suggestion",
|
|
724
789
|
docs: {
|
|
@@ -742,7 +807,7 @@ var noConditionalEmptyObjectSpreadRule = defineRule11({
|
|
|
742
807
|
});
|
|
743
808
|
|
|
744
809
|
// vendor/anti-slop/src/rules/no-known-value-widening.ts
|
|
745
|
-
import { defineRule as
|
|
810
|
+
import { defineRule as defineRule13 } from "@oxlint/plugins";
|
|
746
811
|
|
|
747
812
|
// vendor/anti-slop/src/shared/lexical-type-parameters.ts
|
|
748
813
|
function isNode(value) {
|
|
@@ -1431,7 +1496,7 @@ function isDictionaryAccumulatorTarget(destination) {
|
|
|
1431
1496
|
function hasParentAssertion(node) {
|
|
1432
1497
|
return node.parent?.type === "TSAsExpression" || node.parent?.type === "TSTypeAssertion";
|
|
1433
1498
|
}
|
|
1434
|
-
var noKnownValueWideningRule =
|
|
1499
|
+
var noKnownValueWideningRule = defineRule13({
|
|
1435
1500
|
meta: {
|
|
1436
1501
|
type: "problem",
|
|
1437
1502
|
docs: {
|
|
@@ -1544,7 +1609,7 @@ var noKnownValueWideningRule = defineRule12({
|
|
|
1544
1609
|
});
|
|
1545
1610
|
|
|
1546
1611
|
// vendor/anti-slop/src/rules/no-module-mocking.ts
|
|
1547
|
-
import { defineRule as
|
|
1612
|
+
import { defineRule as defineRule14 } from "@oxlint/plugins";
|
|
1548
1613
|
var moduleMockMethods = new Set(["doMock", "mock", "unstable_mockModule"]);
|
|
1549
1614
|
function importedName(node) {
|
|
1550
1615
|
if (node.type !== "ImportSpecifier")
|
|
@@ -1579,7 +1644,7 @@ function moduleMockCall(sourceCode, callee) {
|
|
|
1579
1644
|
const method = callee.computed ? property.type === "Literal" && (property.value === "doMock" || property.value === "mock" || property.value === "unstable_mockModule") ? property.value : null : property.type === "Identifier" ? property.name : null;
|
|
1580
1645
|
return method !== null && moduleMockMethods.has(method);
|
|
1581
1646
|
}
|
|
1582
|
-
var noModuleMockingRule =
|
|
1647
|
+
var noModuleMockingRule = defineRule14({
|
|
1583
1648
|
meta: {
|
|
1584
1649
|
type: "problem",
|
|
1585
1650
|
docs: {
|
|
@@ -1603,8 +1668,8 @@ var noModuleMockingRule = defineRule13({
|
|
|
1603
1668
|
});
|
|
1604
1669
|
|
|
1605
1670
|
// vendor/anti-slop/src/rules/no-object-parameters.ts
|
|
1606
|
-
import { defineRule as
|
|
1607
|
-
var noObjectParametersRule =
|
|
1671
|
+
import { defineRule as defineRule15 } from "@oxlint/plugins";
|
|
1672
|
+
var noObjectParametersRule = defineRule15({
|
|
1608
1673
|
meta: {
|
|
1609
1674
|
type: "problem",
|
|
1610
1675
|
docs: {
|
|
@@ -1657,7 +1722,7 @@ var noObjectParametersRule = defineRule14({
|
|
|
1657
1722
|
});
|
|
1658
1723
|
|
|
1659
1724
|
// vendor/anti-slop/src/rules/no-reflect-apply.ts
|
|
1660
|
-
import { defineRule as
|
|
1725
|
+
import { defineRule as defineRule16 } from "@oxlint/plugins";
|
|
1661
1726
|
|
|
1662
1727
|
// vendor/anti-slop/src/shared/reflect-method.ts
|
|
1663
1728
|
function isGlobalReflect(sourceCode, expression) {
|
|
@@ -1678,7 +1743,7 @@ function isGlobalReflectMethodCall(sourceCode, callee, methodName) {
|
|
|
1678
1743
|
}
|
|
1679
1744
|
|
|
1680
1745
|
// vendor/anti-slop/src/rules/no-reflect-apply.ts
|
|
1681
|
-
var noReflectApplyRule =
|
|
1746
|
+
var noReflectApplyRule = defineRule16({
|
|
1682
1747
|
meta: {
|
|
1683
1748
|
type: "problem",
|
|
1684
1749
|
docs: {
|
|
@@ -1702,8 +1767,8 @@ var noReflectApplyRule = defineRule15({
|
|
|
1702
1767
|
});
|
|
1703
1768
|
|
|
1704
1769
|
// vendor/anti-slop/src/rules/no-reflect-get.ts
|
|
1705
|
-
import { defineRule as
|
|
1706
|
-
var noReflectGetRule =
|
|
1770
|
+
import { defineRule as defineRule17 } from "@oxlint/plugins";
|
|
1771
|
+
var noReflectGetRule = defineRule17({
|
|
1707
1772
|
meta: {
|
|
1708
1773
|
type: "problem",
|
|
1709
1774
|
docs: {
|
|
@@ -1727,7 +1792,7 @@ var noReflectGetRule = defineRule16({
|
|
|
1727
1792
|
});
|
|
1728
1793
|
|
|
1729
1794
|
// vendor/anti-slop/src/rules/no-runtime-typeof.ts
|
|
1730
|
-
import { defineRule as
|
|
1795
|
+
import { defineRule as defineRule18 } from "@oxlint/plugins";
|
|
1731
1796
|
function isRuntimeFunction(node) {
|
|
1732
1797
|
return node.type === "ArrowFunctionExpression" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression";
|
|
1733
1798
|
}
|
|
@@ -1750,7 +1815,7 @@ function isExistenceProbe(node) {
|
|
|
1750
1815
|
const other = parent.left === node ? parent.right : parent.left;
|
|
1751
1816
|
return other.type === "Literal" && other.value === "undefined";
|
|
1752
1817
|
}
|
|
1753
|
-
var noRuntimeTypeofRule =
|
|
1818
|
+
var noRuntimeTypeofRule = defineRule18({
|
|
1754
1819
|
meta: {
|
|
1755
1820
|
type: "problem",
|
|
1756
1821
|
docs: {
|
|
@@ -1784,7 +1849,7 @@ var noRuntimeTypeofRule = defineRule17({
|
|
|
1784
1849
|
});
|
|
1785
1850
|
|
|
1786
1851
|
// vendor/anti-slop/src/rules/no-shape-in-symbol-names.ts
|
|
1787
|
-
import { defineRule as
|
|
1852
|
+
import { defineRule as defineRule19 } from "@oxlint/plugins";
|
|
1788
1853
|
var FORBIDDEN_SYMBOL_NAME = "shape";
|
|
1789
1854
|
function containsForbiddenSymbolName(name) {
|
|
1790
1855
|
return name.toLowerCase().includes(FORBIDDEN_SYMBOL_NAME);
|
|
@@ -1795,7 +1860,7 @@ function isBorrowedMemberName(node) {
|
|
|
1795
1860
|
return false;
|
|
1796
1861
|
return parent.property === node && parent.computed === false;
|
|
1797
1862
|
}
|
|
1798
|
-
var noForbiddenTermInSymbolNamesRule =
|
|
1863
|
+
var noForbiddenTermInSymbolNamesRule = defineRule19({
|
|
1799
1864
|
meta: {
|
|
1800
1865
|
type: "problem",
|
|
1801
1866
|
docs: {
|
|
@@ -1824,12 +1889,12 @@ var noForbiddenTermInSymbolNamesRule = defineRule18({
|
|
|
1824
1889
|
});
|
|
1825
1890
|
|
|
1826
1891
|
// vendor/anti-slop/src/rules/no-unknown-parameters.ts
|
|
1827
|
-
import { defineRule as
|
|
1892
|
+
import { defineRule as defineRule20 } from "@oxlint/plugins";
|
|
1828
1893
|
function isTypePredicateSubject(owner, parameterName) {
|
|
1829
1894
|
const predicate = owner.returnType?.typeAnnotation;
|
|
1830
1895
|
return predicate?.type === "TSTypePredicate" && predicate.parameterName.type === "Identifier" && predicate.parameterName.name === parameterName;
|
|
1831
1896
|
}
|
|
1832
|
-
var noUnknownParametersRule =
|
|
1897
|
+
var noUnknownParametersRule = defineRule20({
|
|
1833
1898
|
meta: {
|
|
1834
1899
|
type: "problem",
|
|
1835
1900
|
docs: {
|
|
@@ -1873,8 +1938,8 @@ var noUnknownParametersRule = defineRule19({
|
|
|
1873
1938
|
});
|
|
1874
1939
|
|
|
1875
1940
|
// vendor/anti-slop/src/rules/no-unknown-returns.ts
|
|
1876
|
-
import { defineRule as
|
|
1877
|
-
var noUnknownReturnsRule =
|
|
1941
|
+
import { defineRule as defineRule21 } from "@oxlint/plugins";
|
|
1942
|
+
var noUnknownReturnsRule = defineRule21({
|
|
1878
1943
|
meta: {
|
|
1879
1944
|
type: "problem",
|
|
1880
1945
|
docs: {
|
|
@@ -1927,8 +1992,8 @@ var noUnknownReturnsRule = defineRule20({
|
|
|
1927
1992
|
});
|
|
1928
1993
|
|
|
1929
1994
|
// vendor/anti-slop/src/rules/no-unknown-type-aliases.ts
|
|
1930
|
-
import { defineRule as
|
|
1931
|
-
var noUnknownTypeAliasesRule =
|
|
1995
|
+
import { defineRule as defineRule22 } from "@oxlint/plugins";
|
|
1996
|
+
var noUnknownTypeAliasesRule = defineRule22({
|
|
1932
1997
|
meta: {
|
|
1933
1998
|
type: "problem",
|
|
1934
1999
|
docs: {
|
|
@@ -1966,7 +2031,7 @@ var noUnknownTypeAliasesRule = defineRule21({
|
|
|
1966
2031
|
});
|
|
1967
2032
|
|
|
1968
2033
|
// vendor/anti-slop/src/rules/no-unsafe-dictionary-type.ts
|
|
1969
|
-
import { defineRule as
|
|
2034
|
+
import { defineRule as defineRule23 } from "@oxlint/plugins";
|
|
1970
2035
|
var typeNodeKinds = new Set([
|
|
1971
2036
|
"JSDocNonNullableType",
|
|
1972
2037
|
"JSDocNullableType",
|
|
@@ -2053,7 +2118,7 @@ function shouldReportType(node, environment) {
|
|
|
2053
2118
|
}
|
|
2054
2119
|
return true;
|
|
2055
2120
|
}
|
|
2056
|
-
var noUnsafeDictionaryTypeRule =
|
|
2121
|
+
var noUnsafeDictionaryTypeRule = defineRule23({
|
|
2057
2122
|
meta: {
|
|
2058
2123
|
type: "problem",
|
|
2059
2124
|
docs: {
|
|
@@ -2095,7 +2160,7 @@ var noUnsafeDictionaryTypeRule = defineRule22({
|
|
|
2095
2160
|
});
|
|
2096
2161
|
|
|
2097
2162
|
// vendor/anti-slop/src/rules/no-widen-then-assert.ts
|
|
2098
|
-
import { defineRule as
|
|
2163
|
+
import { defineRule as defineRule24 } from "@oxlint/plugins";
|
|
2099
2164
|
var functionBoundaryTypes = new Set([
|
|
2100
2165
|
"ArrowFunctionExpression",
|
|
2101
2166
|
"FunctionDeclaration",
|
|
@@ -2291,7 +2356,7 @@ function assertionIsNarrower(sourceText, broadKind, evidence, assertedType) {
|
|
|
2291
2356
|
return isDefinitelyObjectType(assertedType);
|
|
2292
2357
|
return isDefinitelyNarrowerRecordType(assertedType);
|
|
2293
2358
|
}
|
|
2294
|
-
var noWidenThenAssertRule =
|
|
2359
|
+
var noWidenThenAssertRule = defineRule24({
|
|
2295
2360
|
meta: {
|
|
2296
2361
|
type: "problem",
|
|
2297
2362
|
docs: {
|
|
@@ -2853,7 +2918,7 @@ var requireReadableSpacingRule = {
|
|
|
2853
2918
|
};
|
|
2854
2919
|
|
|
2855
2920
|
// vendor/anti-slop/src/rules/require-safety-comment-for-type-assertion.ts
|
|
2856
|
-
import { defineRule as
|
|
2921
|
+
import { defineRule as defineRule25 } from "@oxlint/plugins";
|
|
2857
2922
|
var DEFAULT_SAFETY_MARKERS = ["SAFETY"];
|
|
2858
2923
|
var commentOwnerKinds = new Set([
|
|
2859
2924
|
"ExpressionStatement",
|
|
@@ -2896,7 +2961,7 @@ function hasSafetyComment(sourceCode, node, pattern) {
|
|
|
2896
2961
|
current = current.parent;
|
|
2897
2962
|
}
|
|
2898
2963
|
}
|
|
2899
|
-
var requireSafetyCommentForTypeAssertionRule =
|
|
2964
|
+
var requireSafetyCommentForTypeAssertionRule = defineRule25({
|
|
2900
2965
|
meta: {
|
|
2901
2966
|
type: "problem",
|
|
2902
2967
|
docs: {
|
|
@@ -2974,7 +3039,7 @@ var src_default = antiSlopPlugin;
|
|
|
2974
3039
|
import { eslintCompatPlugin as eslintCompatPlugin4 } from "@oxlint/plugins";
|
|
2975
3040
|
|
|
2976
3041
|
// src/generic/rules/no-explicit-inferable-type-predicate.ts
|
|
2977
|
-
import { defineRule as
|
|
3042
|
+
import { defineRule as defineRule26 } from "@oxlint/plugins";
|
|
2978
3043
|
function isNamed(node, name) {
|
|
2979
3044
|
return node.type === "Identifier" && node.name === name;
|
|
2980
3045
|
}
|
|
@@ -3014,7 +3079,7 @@ function isPublicSignature(owner) {
|
|
|
3014
3079
|
}
|
|
3015
3080
|
return parent.type === "VariableDeclarator" && parent.parent.parent?.type === "ExportNamedDeclaration";
|
|
3016
3081
|
}
|
|
3017
|
-
var noExplicitInferableTypePredicateRule =
|
|
3082
|
+
var noExplicitInferableTypePredicateRule = defineRule26({
|
|
3018
3083
|
meta: {
|
|
3019
3084
|
type: "suggestion",
|
|
3020
3085
|
docs: {
|
|
@@ -3050,7 +3115,7 @@ var noExplicitInferableTypePredicateRule = defineRule25({
|
|
|
3050
3115
|
});
|
|
3051
3116
|
|
|
3052
3117
|
// src/generic/rules/no-unchecked-query-selector-type.ts
|
|
3053
|
-
import { defineRule as
|
|
3118
|
+
import { defineRule as defineRule27 } from "@oxlint/plugins";
|
|
3054
3119
|
|
|
3055
3120
|
// src/shared/json.ts
|
|
3056
3121
|
function jsonObject(value) {
|
|
@@ -3151,7 +3216,7 @@ function isLitRenderRootQuery(node, bases) {
|
|
|
3151
3216
|
|
|
3152
3217
|
// src/generic/rules/no-unchecked-query-selector-type.ts
|
|
3153
3218
|
var queryMethods = new Set(["closest", "querySelector", "querySelectorAll"]);
|
|
3154
|
-
var noUncheckedQuerySelectorTypeRule =
|
|
3219
|
+
var noUncheckedQuerySelectorTypeRule = defineRule27({
|
|
3155
3220
|
meta: {
|
|
3156
3221
|
type: "problem",
|
|
3157
3222
|
docs: {
|
|
@@ -3180,8 +3245,8 @@ var noUncheckedQuerySelectorTypeRule = defineRule26({
|
|
|
3180
3245
|
});
|
|
3181
3246
|
|
|
3182
3247
|
// src/generic/rules/no-untyped-custom-event.ts
|
|
3183
|
-
import { defineRule as
|
|
3184
|
-
var noUntypedCustomEventRule =
|
|
3248
|
+
import { defineRule as defineRule28 } from "@oxlint/plugins";
|
|
3249
|
+
var noUntypedCustomEventRule = defineRule28({
|
|
3185
3250
|
meta: {
|
|
3186
3251
|
type: "problem",
|
|
3187
3252
|
docs: {
|
|
@@ -3204,7 +3269,7 @@ var noUntypedCustomEventRule = defineRule27({
|
|
|
3204
3269
|
});
|
|
3205
3270
|
|
|
3206
3271
|
// src/generic/rules/prefer-event-parameter-type.ts
|
|
3207
|
-
import { defineRule as
|
|
3272
|
+
import { defineRule as defineRule29 } from "@oxlint/plugins";
|
|
3208
3273
|
function staticMemberPath(node) {
|
|
3209
3274
|
const path = [];
|
|
3210
3275
|
let current = node.type === "ChainExpression" ? node.expression : node;
|
|
@@ -3236,7 +3301,7 @@ function assertedEventParameter(node) {
|
|
|
3236
3301
|
property: member.path.join(".")
|
|
3237
3302
|
};
|
|
3238
3303
|
}
|
|
3239
|
-
var preferEventParameterTypeRule =
|
|
3304
|
+
var preferEventParameterTypeRule = defineRule29({
|
|
3240
3305
|
meta: {
|
|
3241
3306
|
type: "suggestion",
|
|
3242
3307
|
docs: {
|
|
@@ -3265,14 +3330,14 @@ var preferEventParameterTypeRule = defineRule28({
|
|
|
3265
3330
|
});
|
|
3266
3331
|
|
|
3267
3332
|
// src/generic/rules/prefer-lit-query-decorators.ts
|
|
3268
|
-
import { defineRule as
|
|
3333
|
+
import { defineRule as defineRule30 } from "@oxlint/plugins";
|
|
3269
3334
|
var messageIds = {
|
|
3270
3335
|
assignedElements: "preferQueryAssignedElements",
|
|
3271
3336
|
assignedNodes: "preferQueryAssignedNodes",
|
|
3272
3337
|
querySelector: "preferQuery",
|
|
3273
3338
|
querySelectorAll: "preferQueryAll"
|
|
3274
3339
|
};
|
|
3275
|
-
var preferLitQueryDecoratorsRule =
|
|
3340
|
+
var preferLitQueryDecoratorsRule = defineRule30({
|
|
3276
3341
|
meta: {
|
|
3277
3342
|
type: "suggestion",
|
|
3278
3343
|
docs: {
|
|
@@ -3325,8 +3390,8 @@ var preferLitQueryDecoratorsRule = defineRule29({
|
|
|
3325
3390
|
});
|
|
3326
3391
|
|
|
3327
3392
|
// src/generic/rules/prefer-types-over-assertions.ts
|
|
3328
|
-
import { defineRule as
|
|
3329
|
-
var preferTypesOverAssertionsRule =
|
|
3393
|
+
import { defineRule as defineRule31 } from "@oxlint/plugins";
|
|
3394
|
+
var preferTypesOverAssertionsRule = defineRule31({
|
|
3330
3395
|
meta: {
|
|
3331
3396
|
type: "problem",
|
|
3332
3397
|
docs: {
|
package/dist/effect/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/effect/index.ts
|
|
2
2
|
import { eslintCompatPlugin } from "@oxlint/plugins";
|
|
3
3
|
|
|
4
|
-
// src/effect/rules/no-
|
|
4
|
+
// src/effect/rules/no-raw-gh.ts
|
|
5
5
|
import { defineRule } from "@oxlint/plugins";
|
|
6
6
|
|
|
7
7
|
// src/shared/ast.ts
|
|
@@ -32,6 +32,70 @@ function resolveVariable(sourceCode, identifier) {
|
|
|
32
32
|
}
|
|
33
33
|
return null;
|
|
34
34
|
}
|
|
35
|
+
// src/effect/rules/no-raw-gh.ts
|
|
36
|
+
var PACKAGE = "@timmo001/effect-gh";
|
|
37
|
+
var rawMethods = new Set(["execute", "json", "stream"]);
|
|
38
|
+
function isEffectGhImport(sourceCode, identifier, name) {
|
|
39
|
+
return resolveVariable(sourceCode, identifier)?.defs.some((definition) => {
|
|
40
|
+
if (definition.type !== "ImportBinding" || definition.parent?.type !== "ImportDeclaration" || definition.parent.source.value !== PACKAGE || definition.node.type !== "ImportSpecifier") {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const imported = definition.node.imported;
|
|
44
|
+
return (imported.type === "Identifier" ? imported.name : imported.value) === name;
|
|
45
|
+
}) ?? false;
|
|
46
|
+
}
|
|
47
|
+
function isYieldGh(sourceCode, node) {
|
|
48
|
+
return node?.type === "YieldExpression" && node.delegate && node.argument?.type === "Identifier" && isEffectGhImport(sourceCode, node.argument, "Gh");
|
|
49
|
+
}
|
|
50
|
+
function isGhBinding(sourceCode, identifier) {
|
|
51
|
+
return resolveVariable(sourceCode, identifier)?.defs.some((definition) => definition.type === "Variable" && definition.node.type === "VariableDeclarator" && isYieldGh(sourceCode, definition.node.init)) ?? false;
|
|
52
|
+
}
|
|
53
|
+
function isGhArgument(node) {
|
|
54
|
+
if (node?.type === "Literal")
|
|
55
|
+
return node.value === "gh";
|
|
56
|
+
if (node?.type !== "ArrayExpression")
|
|
57
|
+
return false;
|
|
58
|
+
const first = node.elements[0];
|
|
59
|
+
return first?.type === "Literal" && first.value === "gh";
|
|
60
|
+
}
|
|
61
|
+
var noRawGhRule = defineRule({
|
|
62
|
+
meta: {
|
|
63
|
+
type: "problem",
|
|
64
|
+
docs: {
|
|
65
|
+
description: "Use @timmo001/effect-gh typed operations instead of the raw Gh methods, Api.raw, or spawning gh directly."
|
|
66
|
+
},
|
|
67
|
+
messages: {
|
|
68
|
+
rawGh: "Use a typed effect-gh operation instead of `Gh.{{method}}` with raw argv. Add one to effect-gh if the command has none.",
|
|
69
|
+
rawApi: "Use `Api.json`, `Api.pages`, `Api.empty`, `Api.text` or `Api.headers` instead of `Api.raw`, so the response is typed.",
|
|
70
|
+
spawnGh: "Run gh through a typed @timmo001/effect-gh operation instead of spawning it directly."
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
create(context) {
|
|
74
|
+
return {
|
|
75
|
+
CallExpression(node) {
|
|
76
|
+
const callee = node.callee;
|
|
77
|
+
const method = staticMemberName(callee);
|
|
78
|
+
if (callee.type === "MemberExpression" && method !== null) {
|
|
79
|
+
const object = callee.object;
|
|
80
|
+
if (rawMethods.has(method) && (isYieldGh(context.sourceCode, object) || object.type === "Identifier" && isGhBinding(context.sourceCode, object))) {
|
|
81
|
+
context.report({ node, messageId: "rawGh", data: { method } });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (method === "raw" && object.type === "Identifier" && isEffectGhImport(context.sourceCode, object, "Api")) {
|
|
85
|
+
context.report({ node, messageId: "rawApi" });
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (isGhArgument(node.arguments[0]))
|
|
90
|
+
context.report({ node, messageId: "spawnGh" });
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// src/effect/rules/no-try-catch-in-effect-generators.ts
|
|
97
|
+
import { defineRule as defineRule2 } from "@oxlint/plugins";
|
|
98
|
+
|
|
35
99
|
// src/shared/effect.ts
|
|
36
100
|
function isEffectImport(sourceCode, identifier, namespace) {
|
|
37
101
|
const variable = resolveVariable(sourceCode, identifier);
|
|
@@ -78,7 +142,7 @@ function isRecognisedEffectGenerator(sourceCode, owner) {
|
|
|
78
142
|
const factoryCall = parent.callee;
|
|
79
143
|
return factoryCall.type === "CallExpression" && isEffectMethod(sourceCode, factoryCall.callee, "fn");
|
|
80
144
|
}
|
|
81
|
-
var noTryCatchInEffectGeneratorsRule =
|
|
145
|
+
var noTryCatchInEffectGeneratorsRule = defineRule2({
|
|
82
146
|
meta: {
|
|
83
147
|
type: "problem",
|
|
84
148
|
docs: {
|
|
@@ -104,7 +168,7 @@ var noTryCatchInEffectGeneratorsRule = defineRule({
|
|
|
104
168
|
});
|
|
105
169
|
|
|
106
170
|
// src/effect/rules/prefer-platform-services.ts
|
|
107
|
-
import { defineRule as
|
|
171
|
+
import { defineRule as defineRule3 } from "@oxlint/plugins";
|
|
108
172
|
var moduleServices = new Map([
|
|
109
173
|
["child_process", "childProcess"],
|
|
110
174
|
["fs", "fileSystem"],
|
|
@@ -117,7 +181,7 @@ var bunServices = new Map([
|
|
|
117
181
|
["spawnSync", "childProcess"],
|
|
118
182
|
["write", "fileSystem"]
|
|
119
183
|
]);
|
|
120
|
-
var preferPlatformServicesRule =
|
|
184
|
+
var preferPlatformServicesRule = defineRule3({
|
|
121
185
|
meta: {
|
|
122
186
|
type: "suggestion",
|
|
123
187
|
docs: {
|
|
@@ -180,6 +244,7 @@ var preferPlatformServicesRule = defineRule2({
|
|
|
180
244
|
var timmoEffectPlugin = eslintCompatPlugin({
|
|
181
245
|
meta: { name: "timmo-effect" },
|
|
182
246
|
rules: {
|
|
247
|
+
"no-raw-gh": noRawGhRule,
|
|
183
248
|
"no-try-catch-in-effect-generators": noTryCatchInEffectGeneratorsRule,
|
|
184
249
|
"prefer-platform-services": preferPlatformServicesRule
|
|
185
250
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timmo001/oxlint-rules",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Shared Oxlint plugins and configs with optional Effect rules.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -38,24 +38,25 @@
|
|
|
38
38
|
],
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "bun run scripts/build.ts",
|
|
41
|
-
"check": "bun run
|
|
41
|
+
"check": "bun run --parallel --no-exit-on-error lint test typecheck format:check skills:validate",
|
|
42
42
|
"format": "prettier --write \"{src,scripts}/**/*.ts\" \"skills/**/*.{md,mjs}\" \"*.ts\" \"*.md\" \"*.json\"",
|
|
43
43
|
"format:check": "prettier --check \"{src,scripts}/**/*.ts\" \"skills/**/*.{md,mjs}\" \"*.ts\" \"*.md\" \"*.json\"",
|
|
44
|
-
"lint": "bun run build &&
|
|
45
|
-
"
|
|
44
|
+
"lint": "bun run build && bun run lint:oxlint",
|
|
45
|
+
"lint:oxlint": "oxlint --disable-nested-config .",
|
|
46
|
+
"skills:validate": "printf '%s\\n' skills/*/ | xargs -P 0 -n 1 skills-ref validate",
|
|
46
47
|
"test": "node --experimental-strip-types scripts/test.ts",
|
|
47
48
|
"test:package": "node --experimental-strip-types scripts/test-package.ts",
|
|
48
49
|
"typecheck": "tsc --noEmit"
|
|
49
50
|
},
|
|
50
51
|
"peerDependencies": {
|
|
51
|
-
"@oxlint/plugins": "1.
|
|
52
|
-
"oxlint": "1.
|
|
52
|
+
"@oxlint/plugins": "1.87.0",
|
|
53
|
+
"oxlint": "1.87.0"
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
55
|
-
"@oxlint/plugins": "1.
|
|
56
|
+
"@oxlint/plugins": "1.87.0",
|
|
56
57
|
"@types/bun": "^1.3.14",
|
|
57
|
-
"oxlint": "1.
|
|
58
|
-
"oxlint-tsgolint": "7.0.
|
|
58
|
+
"oxlint": "1.87.0",
|
|
59
|
+
"oxlint-tsgolint": "7.0.2004",
|
|
59
60
|
"prettier": "^3.9.5",
|
|
60
61
|
"skills-ref": "0.1.5",
|
|
61
62
|
"typescript": "^7.0.0"
|
|
@@ -32,10 +32,8 @@ description: >-
|
|
|
32
32
|
instead only when `effect` is a direct dependency or the user explicitly
|
|
33
33
|
requests it.
|
|
34
34
|
4. Keep dependency and config edits visible. Do not delegate them to a script.
|
|
35
|
-
5.
|
|
36
|
-
|
|
37
|
-
Enable `options.typeAware` in the target config and verify type-aware linting
|
|
38
|
-
alongside the shared rules.
|
|
35
|
+
5. Explicitly pin the latest `oxlint-tsgolint` that satisfies Oxlint's peer
|
|
36
|
+
requirement and was validated with the rules release.
|
|
39
37
|
|
|
40
38
|
## Copy rules
|
|
41
39
|
|
|
@@ -51,8 +49,23 @@ description: >-
|
|
|
51
49
|
output as authoritative rather than maintaining plugin or rule inventories
|
|
52
50
|
in this skill.
|
|
53
51
|
|
|
52
|
+
## Strictness
|
|
53
|
+
|
|
54
|
+
1. Start with every strictness option in the target config's `options`:
|
|
55
|
+
`typeAware: true`, `typeCheck: true`, and `maxWarnings: 0`. Keep them in the
|
|
56
|
+
config rather than as lint script flags, and remove any existing
|
|
57
|
+
`--type-aware`, `--type-check`, `--deny-warnings`, or `--max-warnings` flags
|
|
58
|
+
so the config is the single source.
|
|
59
|
+
2. Run the target's lint command. If it passes, keep every option.
|
|
60
|
+
3. If it fails, group the failures by cause and the option that raises them,
|
|
61
|
+
then ask one structured question: fix the failures, or drop the smallest set
|
|
62
|
+
of options that makes lint pass (`typeCheck` first, then `maxWarnings`, then
|
|
63
|
+
`typeAware`). Recommend the reduction that keeps the most strictness, and name
|
|
64
|
+
any shared cause, such as a missing declaration in an upstream package, that
|
|
65
|
+
a follow-up could fix for every consumer.
|
|
66
|
+
|
|
54
67
|
Run the target repository's lint command to verify the configuration loads, plus
|
|
55
68
|
any checks explicitly required by that repository. Run typechecks, tests, or a
|
|
56
69
|
build only when code or dependencies changed in a way those checks cover. Report
|
|
57
70
|
package-manager changes, preserved local configuration, enabled rule groups,
|
|
58
|
-
and checks.
|
|
71
|
+
strictness options kept or dropped, and checks.
|
package/src/effect/index.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { eslintCompatPlugin } from "@oxlint/plugins";
|
|
2
2
|
|
|
3
|
+
import { noRawGhRule } from "./rules/no-raw-gh.ts";
|
|
3
4
|
import { noTryCatchInEffectGeneratorsRule } from "./rules/no-try-catch-in-effect-generators.ts";
|
|
4
5
|
import { preferPlatformServicesRule } from "./rules/prefer-platform-services.ts";
|
|
5
6
|
|
|
6
7
|
const timmoEffectPlugin = eslintCompatPlugin({
|
|
7
8
|
meta: { name: "timmo-effect" },
|
|
8
9
|
rules: {
|
|
10
|
+
"no-raw-gh": noRawGhRule,
|
|
9
11
|
"no-try-catch-in-effect-generators": noTryCatchInEffectGeneratorsRule,
|
|
10
12
|
"prefer-platform-services": preferPlatformServicesRule,
|
|
11
13
|
},
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { defineRule } from "@oxlint/plugins";
|
|
2
|
+
|
|
3
|
+
import { staticMemberName } from "../../shared/ast.ts";
|
|
4
|
+
import { resolveVariable } from "../../shared/scope.ts";
|
|
5
|
+
|
|
6
|
+
import type { ESTree, SourceCode } from "@oxlint/plugins";
|
|
7
|
+
|
|
8
|
+
const PACKAGE = "@timmo001/effect-gh";
|
|
9
|
+
|
|
10
|
+
const rawMethods = new Set(["execute", "json", "stream"]);
|
|
11
|
+
|
|
12
|
+
/** Whether an identifier is bound by a named import of `name` from effect-gh. */
|
|
13
|
+
function isEffectGhImport(
|
|
14
|
+
sourceCode: SourceCode,
|
|
15
|
+
identifier: ESTree.IdentifierReference,
|
|
16
|
+
name: string,
|
|
17
|
+
): boolean {
|
|
18
|
+
return (
|
|
19
|
+
resolveVariable(sourceCode, identifier)?.defs.some((definition) => {
|
|
20
|
+
if (
|
|
21
|
+
definition.type !== "ImportBinding" ||
|
|
22
|
+
definition.parent?.type !== "ImportDeclaration" ||
|
|
23
|
+
definition.parent.source.value !== PACKAGE ||
|
|
24
|
+
definition.node.type !== "ImportSpecifier"
|
|
25
|
+
) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const imported = definition.node.imported;
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
(imported.type === "Identifier" ? imported.name : imported.value) ===
|
|
33
|
+
name
|
|
34
|
+
);
|
|
35
|
+
}) ?? false
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Whether an expression is `yield* Gh` with Gh from effect-gh. */
|
|
40
|
+
function isYieldGh(sourceCode: SourceCode, node: ESTree.Node | null): boolean {
|
|
41
|
+
return (
|
|
42
|
+
node?.type === "YieldExpression" &&
|
|
43
|
+
node.delegate &&
|
|
44
|
+
node.argument?.type === "Identifier" &&
|
|
45
|
+
isEffectGhImport(sourceCode, node.argument, "Gh")
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Whether an identifier is a variable initialised with `yield* Gh`. */
|
|
50
|
+
function isGhBinding(
|
|
51
|
+
sourceCode: SourceCode,
|
|
52
|
+
identifier: ESTree.IdentifierReference,
|
|
53
|
+
): boolean {
|
|
54
|
+
return (
|
|
55
|
+
resolveVariable(sourceCode, identifier)?.defs.some(
|
|
56
|
+
(definition) =>
|
|
57
|
+
definition.type === "Variable" &&
|
|
58
|
+
definition.node.type === "VariableDeclarator" &&
|
|
59
|
+
isYieldGh(sourceCode, definition.node.init),
|
|
60
|
+
) ?? false
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isGhArgument(node: ESTree.Node | undefined): boolean {
|
|
65
|
+
if (node?.type === "Literal") return node.value === "gh";
|
|
66
|
+
|
|
67
|
+
if (node?.type !== "ArrayExpression") return false;
|
|
68
|
+
const first = node.elements[0];
|
|
69
|
+
|
|
70
|
+
return first?.type === "Literal" && first.value === "gh";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Route gh through effect-gh's typed operations instead of raw argv or direct spawns. */
|
|
74
|
+
export const noRawGhRule = defineRule({
|
|
75
|
+
meta: {
|
|
76
|
+
type: "problem",
|
|
77
|
+
docs: {
|
|
78
|
+
description:
|
|
79
|
+
"Use @timmo001/effect-gh typed operations instead of the raw Gh methods, Api.raw, or spawning gh directly.",
|
|
80
|
+
},
|
|
81
|
+
messages: {
|
|
82
|
+
rawGh:
|
|
83
|
+
"Use a typed effect-gh operation instead of `Gh.{{method}}` with raw argv. Add one to effect-gh if the command has none.",
|
|
84
|
+
rawApi:
|
|
85
|
+
"Use `Api.json`, `Api.pages`, `Api.empty`, `Api.text` or `Api.headers` instead of `Api.raw`, so the response is typed.",
|
|
86
|
+
spawnGh:
|
|
87
|
+
"Run gh through a typed @timmo001/effect-gh operation instead of spawning it directly.",
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
create(context) {
|
|
91
|
+
return {
|
|
92
|
+
CallExpression(node) {
|
|
93
|
+
const callee = node.callee;
|
|
94
|
+
const method = staticMemberName(callee);
|
|
95
|
+
|
|
96
|
+
if (callee.type === "MemberExpression" && method !== null) {
|
|
97
|
+
const object = callee.object;
|
|
98
|
+
|
|
99
|
+
if (
|
|
100
|
+
rawMethods.has(method) &&
|
|
101
|
+
(isYieldGh(context.sourceCode, object) ||
|
|
102
|
+
(object.type === "Identifier" &&
|
|
103
|
+
isGhBinding(context.sourceCode, object)))
|
|
104
|
+
) {
|
|
105
|
+
context.report({ node, messageId: "rawGh", data: { method } });
|
|
106
|
+
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (
|
|
111
|
+
method === "raw" &&
|
|
112
|
+
object.type === "Identifier" &&
|
|
113
|
+
isEffectGhImport(context.sourceCode, object, "Api")
|
|
114
|
+
) {
|
|
115
|
+
context.report({ node, messageId: "rawApi" });
|
|
116
|
+
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (isGhArgument(node.arguments[0]))
|
|
122
|
+
context.report({ node, messageId: "spawnGh" });
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
});
|