@timmo001/oxlint-rules 0.3.3 → 0.3.5

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 CHANGED
@@ -87,6 +87,17 @@ npm pack --dry-run
87
87
  bunx jsr publish --dry-run
88
88
  ```
89
89
 
90
+ `bun run lint` builds the local plugins before linting all maintained code,
91
+ including tests, tooling, the root config and packaged skill scripts. The root
92
+ config extends the local `recommended-effect` source config, enabling every
93
+ registered rule in `anti-slop`, `timmo`, `anti-slop-effect` and `timmo-effect`.
94
+ Plugin imports resolve through this package's exports to the fresh `dist` build.
95
+
96
+ Lint disables nested configs so the vendored submodule's config cannot override
97
+ the root exclusions. Generated output, vendored source and agent directories
98
+ remain excluded. RuleTester fixture strings retain their intentional violations;
99
+ the surrounding test code is linted with the full rule set.
100
+
90
101
  Advance `vendor/anti-slop` only by updating its Git submodule commit. Keep its
91
102
  source and MIT licence unchanged, then run the full package checks so both
92
103
  upstream exports and composed configs are verified.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timmo001/oxlint-rules",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Shared Oxlint plugins and configs with optional Effect rules.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -36,22 +36,22 @@
36
36
  ],
37
37
  "scripts": {
38
38
  "build": "bun run scripts/build.ts",
39
- "check": "bun run build && bun run test && bun run lint && bun run typecheck && bun run format:check && bun run skills:validate",
40
- "format": "prettier --write \"{src,scripts}/**/*.ts\" \"skills/**/*.{md,mjs}\" \"*.md\" \"*.json\"",
41
- "format:check": "prettier --check \"{src,scripts}/**/*.ts\" \"skills/**/*.{md,mjs}\" \"*.md\" \"*.json\"",
42
- "lint": "oxlint src scripts",
39
+ "check": "bun run lint && bun run test && bun run typecheck && bun run format:check && bun run skills:validate",
40
+ "format": "prettier --write \"{src,scripts}/**/*.ts\" \"skills/**/*.{md,mjs}\" \"*.ts\" \"*.md\" \"*.json\"",
41
+ "format:check": "prettier --check \"{src,scripts}/**/*.ts\" \"skills/**/*.{md,mjs}\" \"*.ts\" \"*.md\" \"*.json\"",
42
+ "lint": "bun run build && oxlint --disable-nested-config .",
43
43
  "skills:validate": "bunx skills-ref validate skills/install-timmo-oxlint-rules && bunx skills-ref validate skills/add-oxlint-rule && bunx skills-ref validate skills/release-oxlint-rules",
44
44
  "test": "node --experimental-strip-types scripts/test.ts",
45
45
  "typecheck": "tsc --noEmit"
46
46
  },
47
47
  "peerDependencies": {
48
- "@oxlint/plugins": "1.82.0",
49
- "oxlint": "1.82.0"
48
+ "@oxlint/plugins": "1.83.0",
49
+ "oxlint": "1.83.0"
50
50
  },
51
51
  "devDependencies": {
52
- "@oxlint/plugins": "1.82.0",
52
+ "@oxlint/plugins": "1.83.0",
53
53
  "@types/bun": "^1.3.14",
54
- "oxlint": "1.82.0",
54
+ "oxlint": "1.83.0",
55
55
  "prettier": "^3.9.5",
56
56
  "skills-ref": "0.1.5",
57
57
  "typescript": "^7.0.0"
@@ -43,6 +43,8 @@ description: >-
43
43
  output as authoritative rather than maintaining plugin or rule inventories
44
44
  in this skill.
45
45
 
46
- Run the target repository's normal lint, typecheck, tests, and build. Report
46
+ Run the target repository's lint command to verify the configuration loads, plus
47
+ any checks explicitly required by that repository. Run typechecks, tests, or a
48
+ build only when code or dependencies changed in a way those checks cover. Report
47
49
  package-manager changes, preserved local configuration, enabled rule groups,
48
50
  and checks.
@@ -2,7 +2,9 @@
2
2
  import { spawnSync } from "node:child_process";
3
3
 
4
4
  const [runner, ...copyArguments] = process.argv.slice(2);
5
+
5
6
  const packageName = "@timmo001/oxlint-rules";
7
+
6
8
  const commands = {
7
9
  bun: ["bunx", packageName, "copy", ...copyArguments],
8
10
  npm: [
@@ -20,11 +22,14 @@ const commands = {
20
22
  };
21
23
 
22
24
  const command = commands[runner];
25
+
23
26
  if (!command) {
24
27
  console.error("Usage: copy.mjs <bun|npm|pnpm|yarn> <destination> [--force]");
25
28
  process.exit(1);
26
29
  }
27
30
 
28
31
  const result = spawnSync(command[0], command.slice(1), { stdio: "inherit" });
32
+
29
33
  if (result.error) throw result.error;
34
+
30
35
  process.exit(result.status ?? 1);
@@ -9,16 +9,20 @@ function resolveVariable(
9
9
  identifier: ESTree.IdentifierReference,
10
10
  ): Variable | null {
11
11
  let scope: Scope | null = sourceCode.getScope(identifier);
12
+
12
13
  while (scope) {
13
14
  const variable = scope.set.get(identifier.name);
15
+
14
16
  if (variable) return variable;
15
17
  scope = scope.upper;
16
18
  }
19
+
17
20
  return null;
18
21
  }
19
22
 
20
23
  function nearestEnclosingFunction(node: ESTree.Node): FunctionNode | null {
21
24
  let current: ESTree.Node | null = node.parent;
25
+
22
26
  while (current) {
23
27
  if (
24
28
  current.type === "FunctionDeclaration" ||
@@ -27,13 +31,16 @@ function nearestEnclosingFunction(node: ESTree.Node): FunctionNode | null {
27
31
  ) {
28
32
  return current;
29
33
  }
34
+
30
35
  current = current.parent;
31
36
  }
37
+
32
38
  return null;
33
39
  }
34
40
 
35
41
  function staticMemberName(node: ESTree.Expression): string | null {
36
42
  if (node.type !== "MemberExpression" || node.computed) return null;
43
+
37
44
  return node.property.type === "Identifier" ? node.property.name : null;
38
45
  }
39
46
 
@@ -43,6 +50,7 @@ function isEffectImport(
43
50
  namespace: boolean,
44
51
  ): boolean {
45
52
  const variable = resolveVariable(sourceCode, identifier);
53
+
46
54
  return (
47
55
  variable?.defs.some((definition) => {
48
56
  if (
@@ -52,9 +60,12 @@ function isEffectImport(
52
60
  ) {
53
61
  return false;
54
62
  }
63
+
55
64
  if (namespace) return definition.node.type === "ImportNamespaceSpecifier";
65
+
56
66
  if (definition.node.type !== "ImportSpecifier") return false;
57
67
  const imported = definition.node.imported;
68
+
58
69
  return (
59
70
  (imported.type === "Identifier" ? imported.name : imported.value) ===
60
71
  "Effect"
@@ -71,10 +82,13 @@ function isEffectMethod(
71
82
  if (staticMemberName(node) !== method || node.type !== "MemberExpression") {
72
83
  return false;
73
84
  }
85
+
74
86
  const object = node.object;
87
+
75
88
  if (object.type === "Identifier") {
76
89
  return isEffectImport(sourceCode, object, false);
77
90
  }
91
+
78
92
  if (
79
93
  staticMemberName(object) !== "Effect" ||
80
94
  object.type !== "MemberExpression" ||
@@ -82,6 +96,7 @@ function isEffectMethod(
82
96
  ) {
83
97
  return false;
84
98
  }
99
+
85
100
  return isEffectImport(sourceCode, object.object, true);
86
101
  }
87
102
 
@@ -97,12 +112,15 @@ function isRecognisedEffectGenerator(
97
112
  owner: FunctionNode,
98
113
  ): boolean {
99
114
  const parent = owner.parent;
115
+
100
116
  if (parent.type !== "CallExpression" || !isDirectArgument(owner, parent)) {
101
117
  return false;
102
118
  }
119
+
103
120
  if (isEffectMethod(sourceCode, parent.callee, "gen")) return true;
104
121
 
105
122
  const factoryCall = parent.callee;
123
+
106
124
  return (
107
125
  factoryCall.type === "CallExpression" &&
108
126
  isEffectMethod(sourceCode, factoryCall.callee, "fn")
@@ -127,12 +145,14 @@ export const noTryCatchInEffectGeneratorsRule = defineRule({
127
145
  TryStatement(node) {
128
146
  if (!node.handler) return;
129
147
  const owner = nearestEnclosingFunction(node);
148
+
130
149
  if (
131
150
  !owner?.generator ||
132
151
  !isRecognisedEffectGenerator(context.sourceCode, owner)
133
152
  ) {
134
153
  return;
135
154
  }
155
+
136
156
  context.report({ node, messageId: "useEffectErrorChannel" });
137
157
  },
138
158
  };
@@ -3,10 +3,12 @@ import { defineRule } from "@oxlint/plugins";
3
3
  import type { ESTree } from "@oxlint/plugins";
4
4
 
5
5
  type FunctionNode = ESTree.Function | ESTree.ArrowFunctionExpression;
6
+
6
7
  type TypeAssertion = ESTree.TSAsExpression | ESTree.TSTypeAssertion;
7
8
 
8
9
  function nearestEnclosingFunction(node: ESTree.Node): FunctionNode | null {
9
10
  let current: ESTree.Node | null = node.parent;
11
+
10
12
  while (current) {
11
13
  if (
12
14
  current.type === "FunctionDeclaration" ||
@@ -15,8 +17,10 @@ function nearestEnclosingFunction(node: ESTree.Node): FunctionNode | null {
15
17
  ) {
16
18
  return current;
17
19
  }
20
+
18
21
  current = current.parent;
19
22
  }
23
+
20
24
  return null;
21
25
  }
22
26
 
@@ -25,6 +29,7 @@ function assertedEventParameter(node: TypeAssertion): {
25
29
  readonly property: "currentTarget" | "target";
26
30
  } | null {
27
31
  const expression = node.expression;
32
+
28
33
  if (
29
34
  expression.type !== "MemberExpression" ||
30
35
  expression.computed ||
@@ -35,9 +40,11 @@ function assertedEventParameter(node: TypeAssertion): {
35
40
  ) {
36
41
  return null;
37
42
  }
43
+
38
44
  const parameterName = expression.object.name;
39
45
  const property = expression.property.name;
40
46
  const owner = nearestEnclosingFunction(node);
47
+
41
48
  if (
42
49
  !owner?.params.some(
43
50
  (parameter) =>
@@ -46,6 +53,7 @@ function assertedEventParameter(node: TypeAssertion): {
46
53
  ) {
47
54
  return null;
48
55
  }
56
+
49
57
  return {
50
58
  parameter: parameterName,
51
59
  property,
@@ -68,6 +76,7 @@ export const preferEventParameterTypeRule = defineRule({
68
76
  create(context) {
69
77
  const checkAssertion = (node: TypeAssertion) => {
70
78
  const eventParameter = assertedEventParameter(node);
79
+
71
80
  if (!eventParameter) return;
72
81
  context.report({
73
82
  node,
@@ -75,6 +84,7 @@ export const preferEventParameterTypeRule = defineRule({
75
84
  data: eventParameter,
76
85
  });
77
86
  };
87
+
78
88
  return {
79
89
  TSAsExpression: checkAssertion,
80
90
  TSTypeAssertion: checkAssertion,