@timmo001/oxlint-rules 0.1.4 → 0.2.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.
@@ -102,11 +102,67 @@ var timmoEffectPlugin = eslintCompatPlugin({
102
102
  });
103
103
  var effect_default = timmoEffectPlugin;
104
104
 
105
- // vendor/anti-slop/src/index.ts
105
+ // vendor/anti-slop/src/effect/index.ts
106
106
  import { eslintCompatPlugin as eslintCompatPlugin2 } from "@oxlint/plugins";
107
107
 
108
- // vendor/anti-slop/src/rules/no-chained-type-assertions.ts
108
+ // vendor/anti-slop/src/effect/rules/no-service-constructor-imports.ts
109
109
  import { defineRule as defineRule2 } from "@oxlint/plugins";
110
+ var SERVICE_CONSTRUCTOR_NAME = /^make[A-Z]/u;
111
+ var TEST_FILE = /\.(?:test|spec)\.[cm]?[jt]sx?$/u;
112
+ function isProjectLocalImport(source) {
113
+ return source.startsWith("./") || source.startsWith("../");
114
+ }
115
+ function getImportedName(specifier) {
116
+ if (specifier.imported.type === "Identifier")
117
+ return specifier.imported.name;
118
+ return specifier.imported.value;
119
+ }
120
+ var noServiceConstructorImportsRule = defineRule2({
121
+ meta: {
122
+ type: "problem",
123
+ docs: {
124
+ description: "Disallow project-local make<CapabilityName> imports outside test and spec files."
125
+ },
126
+ messages: {
127
+ serviceConstructorImport: 'Do not import Effect service constructor "{{name}}" into runtime code. Import the owning Layer, yield the contextual service, and allow its requirements to propagate to the composition root.'
128
+ }
129
+ },
130
+ create(context) {
131
+ const isTestFile = TEST_FILE.test(context.filename.replaceAll("\\", "/"));
132
+ return {
133
+ ImportDeclaration(node) {
134
+ if (isTestFile || !isProjectLocalImport(node.source.value))
135
+ return;
136
+ for (const specifier of node.specifiers) {
137
+ if (specifier.type !== "ImportSpecifier")
138
+ continue;
139
+ const importedName = getImportedName(specifier);
140
+ if (!SERVICE_CONSTRUCTOR_NAME.test(importedName))
141
+ continue;
142
+ context.report({
143
+ node: specifier,
144
+ messageId: "serviceConstructorImport",
145
+ data: { name: importedName }
146
+ });
147
+ }
148
+ }
149
+ };
150
+ }
151
+ });
152
+
153
+ // vendor/anti-slop/src/effect/index.ts
154
+ var antiSlopEffectPlugin = eslintCompatPlugin2({
155
+ meta: { name: "anti-slop-effect" },
156
+ rules: {
157
+ "no-service-constructor-imports": noServiceConstructorImportsRule
158
+ }
159
+ });
160
+ var effect_default2 = antiSlopEffectPlugin;
161
+ // vendor/anti-slop/src/index.ts
162
+ import { eslintCompatPlugin as eslintCompatPlugin3 } from "@oxlint/plugins";
163
+
164
+ // vendor/anti-slop/src/rules/no-chained-type-assertions.ts
165
+ import { defineRule as defineRule3 } from "@oxlint/plugins";
110
166
  function isTypeAssertionExpression(node) {
111
167
  return node.type === "TSAsExpression" || node.type === "TSTypeAssertion";
112
168
  }
@@ -141,7 +197,7 @@ function isForbiddenAssertionChain(node) {
141
197
  }
142
198
  return assertionCount > 1 && hasNonConstAssertion;
143
199
  }
144
- var noChainedTypeAssertionsRule = defineRule2({
200
+ var noChainedTypeAssertionsRule = defineRule3({
145
201
  meta: {
146
202
  type: "problem",
147
203
  docs: {
@@ -165,7 +221,7 @@ var noChainedTypeAssertionsRule = defineRule2({
165
221
  });
166
222
 
167
223
  // vendor/anti-slop/src/rules/no-conditional-empty-object-spread.ts
168
- import { defineRule as defineRule3 } from "@oxlint/plugins";
224
+ import { defineRule as defineRule4 } from "@oxlint/plugins";
169
225
  function unwrapParentheses(node) {
170
226
  let current = node;
171
227
  while (current.type === "ParenthesizedExpression") {
@@ -180,7 +236,7 @@ function isConditionalEmptyObjectSpread(node) {
180
236
  const conditional = unwrapParentheses(node);
181
237
  return conditional.type === "ConditionalExpression" && (isEmptyObjectExpression(conditional.consequent) || isEmptyObjectExpression(conditional.alternate));
182
238
  }
183
- var noConditionalEmptyObjectSpreadRule = defineRule3({
239
+ var noConditionalEmptyObjectSpreadRule = defineRule4({
184
240
  meta: {
185
241
  type: "suggestion",
186
242
  docs: {
@@ -204,7 +260,7 @@ var noConditionalEmptyObjectSpreadRule = defineRule3({
204
260
  });
205
261
 
206
262
  // vendor/anti-slop/src/rules/no-known-value-widening.ts
207
- import { defineRule as defineRule4 } from "@oxlint/plugins";
263
+ import { defineRule as defineRule5 } from "@oxlint/plugins";
208
264
 
209
265
  // vendor/anti-slop/src/shared/lexical-type-parameters.ts
210
266
  function isNode(value) {
@@ -903,7 +959,7 @@ function isDictionaryAccumulatorTarget(destination) {
903
959
  function hasParentAssertion(node) {
904
960
  return node.parent?.type === "TSAsExpression" || node.parent?.type === "TSTypeAssertion";
905
961
  }
906
- var noKnownValueWideningRule = defineRule4({
962
+ var noKnownValueWideningRule = defineRule5({
907
963
  meta: {
908
964
  type: "problem",
909
965
  docs: {
@@ -1016,7 +1072,7 @@ var noKnownValueWideningRule = defineRule4({
1016
1072
  });
1017
1073
 
1018
1074
  // vendor/anti-slop/src/rules/no-module-mocking.ts
1019
- import { defineRule as defineRule5 } from "@oxlint/plugins";
1075
+ import { defineRule as defineRule6 } from "@oxlint/plugins";
1020
1076
  var moduleMockMethods = new Set(["doMock", "mock", "unstable_mockModule"]);
1021
1077
  function resolveVariable3(sourceCode, identifier) {
1022
1078
  let scope = sourceCode.getScope(identifier);
@@ -1061,7 +1117,7 @@ function moduleMockCall(sourceCode, callee) {
1061
1117
  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;
1062
1118
  return method !== null && moduleMockMethods.has(method);
1063
1119
  }
1064
- var noModuleMockingRule = defineRule5({
1120
+ var noModuleMockingRule = defineRule6({
1065
1121
  meta: {
1066
1122
  type: "problem",
1067
1123
  docs: {
@@ -1085,8 +1141,8 @@ var noModuleMockingRule = defineRule5({
1085
1141
  });
1086
1142
 
1087
1143
  // vendor/anti-slop/src/rules/no-object-parameters.ts
1088
- import { defineRule as defineRule6 } from "@oxlint/plugins";
1089
- var noObjectParametersRule = defineRule6({
1144
+ import { defineRule as defineRule7 } from "@oxlint/plugins";
1145
+ var noObjectParametersRule = defineRule7({
1090
1146
  meta: {
1091
1147
  type: "problem",
1092
1148
  docs: {
@@ -1139,7 +1195,7 @@ var noObjectParametersRule = defineRule6({
1139
1195
  });
1140
1196
 
1141
1197
  // vendor/anti-slop/src/rules/no-reflect-apply.ts
1142
- import { defineRule as defineRule7 } from "@oxlint/plugins";
1198
+ import { defineRule as defineRule8 } from "@oxlint/plugins";
1143
1199
 
1144
1200
  // vendor/anti-slop/src/shared/reflect-method.ts
1145
1201
  function resolveVariable4(sourceCode, identifier) {
@@ -1170,7 +1226,7 @@ function isGlobalReflectMethodCall(sourceCode, callee, methodName) {
1170
1226
  }
1171
1227
 
1172
1228
  // vendor/anti-slop/src/rules/no-reflect-apply.ts
1173
- var noReflectApplyRule = defineRule7({
1229
+ var noReflectApplyRule = defineRule8({
1174
1230
  meta: {
1175
1231
  type: "problem",
1176
1232
  docs: {
@@ -1194,8 +1250,8 @@ var noReflectApplyRule = defineRule7({
1194
1250
  });
1195
1251
 
1196
1252
  // vendor/anti-slop/src/rules/no-reflect-get.ts
1197
- import { defineRule as defineRule8 } from "@oxlint/plugins";
1198
- var noReflectGetRule = defineRule8({
1253
+ import { defineRule as defineRule9 } from "@oxlint/plugins";
1254
+ var noReflectGetRule = defineRule9({
1199
1255
  meta: {
1200
1256
  type: "problem",
1201
1257
  docs: {
@@ -1219,7 +1275,7 @@ var noReflectGetRule = defineRule8({
1219
1275
  });
1220
1276
 
1221
1277
  // vendor/anti-slop/src/rules/no-runtime-typeof.ts
1222
- import { defineRule as defineRule9 } from "@oxlint/plugins";
1278
+ import { defineRule as defineRule10 } from "@oxlint/plugins";
1223
1279
  function isRuntimeFunction(node) {
1224
1280
  return node.type === "ArrowFunctionExpression" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression";
1225
1281
  }
@@ -1242,7 +1298,7 @@ function isExistenceProbe(node) {
1242
1298
  const other = parent.left === node ? parent.right : parent.left;
1243
1299
  return other.type === "Literal" && other.value === "undefined";
1244
1300
  }
1245
- var noRuntimeTypeofRule = defineRule9({
1301
+ var noRuntimeTypeofRule = defineRule10({
1246
1302
  meta: {
1247
1303
  type: "problem",
1248
1304
  docs: {
@@ -1276,7 +1332,7 @@ var noRuntimeTypeofRule = defineRule9({
1276
1332
  });
1277
1333
 
1278
1334
  // vendor/anti-slop/src/rules/no-shape-in-symbol-names.ts
1279
- import { defineRule as defineRule10 } from "@oxlint/plugins";
1335
+ import { defineRule as defineRule11 } from "@oxlint/plugins";
1280
1336
  var FORBIDDEN_SYMBOL_NAME = "shape";
1281
1337
  function containsForbiddenSymbolName(name) {
1282
1338
  return name.toLowerCase().includes(FORBIDDEN_SYMBOL_NAME);
@@ -1287,7 +1343,7 @@ function isBorrowedMemberName(node) {
1287
1343
  return false;
1288
1344
  return parent.property === node && parent.computed === false;
1289
1345
  }
1290
- var noForbiddenTermInSymbolNamesRule = defineRule10({
1346
+ var noForbiddenTermInSymbolNamesRule = defineRule11({
1291
1347
  meta: {
1292
1348
  type: "problem",
1293
1349
  docs: {
@@ -1316,12 +1372,12 @@ var noForbiddenTermInSymbolNamesRule = defineRule10({
1316
1372
  });
1317
1373
 
1318
1374
  // vendor/anti-slop/src/rules/no-unknown-parameters.ts
1319
- import { defineRule as defineRule11 } from "@oxlint/plugins";
1375
+ import { defineRule as defineRule12 } from "@oxlint/plugins";
1320
1376
  function isTypePredicateSubject(owner, parameterName) {
1321
1377
  const predicate = owner.returnType?.typeAnnotation;
1322
1378
  return predicate?.type === "TSTypePredicate" && predicate.parameterName.type === "Identifier" && predicate.parameterName.name === parameterName;
1323
1379
  }
1324
- var noUnknownParametersRule = defineRule11({
1380
+ var noUnknownParametersRule = defineRule12({
1325
1381
  meta: {
1326
1382
  type: "problem",
1327
1383
  docs: {
@@ -1365,8 +1421,8 @@ var noUnknownParametersRule = defineRule11({
1365
1421
  });
1366
1422
 
1367
1423
  // vendor/anti-slop/src/rules/no-unknown-returns.ts
1368
- import { defineRule as defineRule12 } from "@oxlint/plugins";
1369
- var noUnknownReturnsRule = defineRule12({
1424
+ import { defineRule as defineRule13 } from "@oxlint/plugins";
1425
+ var noUnknownReturnsRule = defineRule13({
1370
1426
  meta: {
1371
1427
  type: "problem",
1372
1428
  docs: {
@@ -1419,8 +1475,8 @@ var noUnknownReturnsRule = defineRule12({
1419
1475
  });
1420
1476
 
1421
1477
  // vendor/anti-slop/src/rules/no-unknown-type-aliases.ts
1422
- import { defineRule as defineRule13 } from "@oxlint/plugins";
1423
- var noUnknownTypeAliasesRule = defineRule13({
1478
+ import { defineRule as defineRule14 } from "@oxlint/plugins";
1479
+ var noUnknownTypeAliasesRule = defineRule14({
1424
1480
  meta: {
1425
1481
  type: "problem",
1426
1482
  docs: {
@@ -1458,7 +1514,7 @@ var noUnknownTypeAliasesRule = defineRule13({
1458
1514
  });
1459
1515
 
1460
1516
  // vendor/anti-slop/src/rules/no-unsafe-dictionary-type.ts
1461
- import { defineRule as defineRule14 } from "@oxlint/plugins";
1517
+ import { defineRule as defineRule15 } from "@oxlint/plugins";
1462
1518
  var typeNodeKinds = new Set([
1463
1519
  "JSDocNonNullableType",
1464
1520
  "JSDocNullableType",
@@ -1545,7 +1601,7 @@ function shouldReportType(node, environment) {
1545
1601
  }
1546
1602
  return true;
1547
1603
  }
1548
- var noUnsafeDictionaryTypeRule = defineRule14({
1604
+ var noUnsafeDictionaryTypeRule = defineRule15({
1549
1605
  meta: {
1550
1606
  type: "problem",
1551
1607
  docs: {
@@ -1587,7 +1643,7 @@ var noUnsafeDictionaryTypeRule = defineRule14({
1587
1643
  });
1588
1644
 
1589
1645
  // vendor/anti-slop/src/rules/no-widen-then-assert.ts
1590
- import { defineRule as defineRule15 } from "@oxlint/plugins";
1646
+ import { defineRule as defineRule16 } from "@oxlint/plugins";
1591
1647
  var functionBoundaryTypes = new Set([
1592
1648
  "ArrowFunctionExpression",
1593
1649
  "FunctionDeclaration",
@@ -1783,7 +1839,7 @@ function assertionIsNarrower(sourceText, broadKind, evidence, assertedType) {
1783
1839
  return isDefinitelyObjectType(assertedType);
1784
1840
  return isDefinitelyNarrowerRecordType(assertedType);
1785
1841
  }
1786
- var noWidenThenAssertRule = defineRule15({
1842
+ var noWidenThenAssertRule = defineRule16({
1787
1843
  meta: {
1788
1844
  type: "problem",
1789
1845
  docs: {
@@ -1823,7 +1879,7 @@ var noWidenThenAssertRule = defineRule15({
1823
1879
  });
1824
1880
 
1825
1881
  // vendor/anti-slop/src/rules/require-safety-comment-for-type-assertion.ts
1826
- import { defineRule as defineRule16 } from "@oxlint/plugins";
1882
+ import { defineRule as defineRule17 } from "@oxlint/plugins";
1827
1883
  var DEFAULT_SAFETY_MARKERS = ["SAFETY"];
1828
1884
  var commentOwnerKinds = new Set([
1829
1885
  "ExpressionStatement",
@@ -1866,7 +1922,7 @@ function hasSafetyComment(sourceCode, node, pattern) {
1866
1922
  current = current.parent;
1867
1923
  }
1868
1924
  }
1869
- var requireSafetyCommentForTypeAssertionRule = defineRule16({
1925
+ var requireSafetyCommentForTypeAssertionRule = defineRule17({
1870
1926
  meta: {
1871
1927
  type: "problem",
1872
1928
  docs: {
@@ -1916,7 +1972,7 @@ var requireSafetyCommentForTypeAssertionRule = defineRule16({
1916
1972
  });
1917
1973
 
1918
1974
  // vendor/anti-slop/src/index.ts
1919
- var antiSlopPlugin = eslintCompatPlugin2({
1975
+ var antiSlopPlugin = eslintCompatPlugin3({
1920
1976
  meta: { name: "anti-slop" },
1921
1977
  rules: {
1922
1978
  "no-chained-type-assertions": noChainedTypeAssertionsRule,
@@ -1937,62 +1993,74 @@ var antiSlopPlugin = eslintCompatPlugin2({
1937
1993
  }
1938
1994
  });
1939
1995
  var src_default = antiSlopPlugin;
1940
- // vendor/anti-slop/src/effect/index.ts
1941
- import { eslintCompatPlugin as eslintCompatPlugin3 } from "@oxlint/plugins";
1996
+ // src/generic/index.ts
1997
+ import { eslintCompatPlugin as eslintCompatPlugin4 } from "@oxlint/plugins";
1942
1998
 
1943
- // vendor/anti-slop/src/effect/rules/no-service-constructor-imports.ts
1944
- import { defineRule as defineRule17 } from "@oxlint/plugins";
1945
- var SERVICE_CONSTRUCTOR_NAME = /^make[A-Z]/u;
1946
- var TEST_FILE = /\.(?:test|spec)\.[cm]?[jt]sx?$/u;
1947
- function isProjectLocalImport(source) {
1948
- return source.startsWith("./") || source.startsWith("../");
1999
+ // src/generic/rules/prefer-event-parameter-type.ts
2000
+ import { defineRule as defineRule18 } from "@oxlint/plugins";
2001
+ function nearestEnclosingFunction2(node) {
2002
+ let current = node.parent;
2003
+ while (current) {
2004
+ if (current.type === "FunctionDeclaration" || current.type === "FunctionExpression" || current.type === "ArrowFunctionExpression") {
2005
+ return current;
2006
+ }
2007
+ current = current.parent;
2008
+ }
2009
+ return null;
1949
2010
  }
1950
- function getImportedName(specifier) {
1951
- if (specifier.imported.type === "Identifier")
1952
- return specifier.imported.name;
1953
- return specifier.imported.value;
2011
+ function assertedEventParameter(node) {
2012
+ const expression = node.expression;
2013
+ if (expression.type !== "MemberExpression" || expression.computed || expression.object.type !== "Identifier" || expression.property.type !== "Identifier" || expression.property.name !== "currentTarget" && expression.property.name !== "target") {
2014
+ return null;
2015
+ }
2016
+ const parameterName = expression.object.name;
2017
+ const property = expression.property.name;
2018
+ const owner = nearestEnclosingFunction2(node);
2019
+ if (!owner?.params.some((parameter) => parameter.type === "Identifier" && parameter.name === parameterName)) {
2020
+ return null;
2021
+ }
2022
+ return {
2023
+ parameter: parameterName,
2024
+ property
2025
+ };
1954
2026
  }
1955
- var noServiceConstructorImportsRule = defineRule17({
2027
+ var preferEventParameterTypeRule = defineRule18({
1956
2028
  meta: {
1957
- type: "problem",
2029
+ type: "suggestion",
1958
2030
  docs: {
1959
- description: "Disallow project-local make<CapabilityName> imports outside test and spec files."
2031
+ description: "Prefer typing event target properties in the handler parameter instead of asserting them at use sites."
1960
2032
  },
1961
2033
  messages: {
1962
- serviceConstructorImport: 'Do not import Effect service constructor "{{name}}" into runtime code. Import the owning Layer, yield the contextual service, and allow its requirements to propagate to the composition root.'
2034
+ typeEventParameter: "Type `{{parameter}}.{{property}}` in the function signature instead of asserting it at the use site."
1963
2035
  }
1964
2036
  },
1965
2037
  create(context) {
1966
- const isTestFile = TEST_FILE.test(context.filename.replaceAll("\\", "/"));
2038
+ const checkAssertion = (node) => {
2039
+ const eventParameter = assertedEventParameter(node);
2040
+ if (!eventParameter)
2041
+ return;
2042
+ context.report({
2043
+ node,
2044
+ messageId: "typeEventParameter",
2045
+ data: eventParameter
2046
+ });
2047
+ };
1967
2048
  return {
1968
- ImportDeclaration(node) {
1969
- if (isTestFile || !isProjectLocalImport(node.source.value))
1970
- return;
1971
- for (const specifier of node.specifiers) {
1972
- if (specifier.type !== "ImportSpecifier")
1973
- continue;
1974
- const importedName2 = getImportedName(specifier);
1975
- if (!SERVICE_CONSTRUCTOR_NAME.test(importedName2))
1976
- continue;
1977
- context.report({
1978
- node: specifier,
1979
- messageId: "serviceConstructorImport",
1980
- data: { name: importedName2 }
1981
- });
1982
- }
1983
- }
2049
+ TSAsExpression: checkAssertion,
2050
+ TSTypeAssertion: checkAssertion
1984
2051
  };
1985
2052
  }
1986
2053
  });
1987
2054
 
1988
- // vendor/anti-slop/src/effect/index.ts
1989
- var antiSlopEffectPlugin = eslintCompatPlugin3({
1990
- meta: { name: "anti-slop-effect" },
2055
+ // src/generic/index.ts
2056
+ var timmoPlugin = eslintCompatPlugin4({
2057
+ meta: { name: "timmo" },
1991
2058
  rules: {
1992
- "no-service-constructor-imports": noServiceConstructorImportsRule
2059
+ "prefer-event-parameter-type": preferEventParameterTypeRule
1993
2060
  }
1994
2061
  });
1995
- var effect_default2 = antiSlopEffectPlugin;
2062
+ var generic_default = timmoPlugin;
2063
+
1996
2064
  // node_modules/oxlint/dist/index.js
1997
2065
  function defineConfig(config) {
1998
2066
  return config;
@@ -2009,9 +2077,13 @@ var recommended = defineConfig({
2009
2077
  {
2010
2078
  name: "anti-slop",
2011
2079
  specifier: "@timmo001/oxlint-rules/upstream/anti-slop"
2012
- }
2080
+ },
2081
+ { name: "timmo", specifier: "@timmo001/oxlint-rules/generic" }
2013
2082
  ],
2014
- rules: enablePluginRules("anti-slop", src_default)
2083
+ rules: {
2084
+ ...enablePluginRules("anti-slop", src_default),
2085
+ ...enablePluginRules("timmo", generic_default)
2086
+ }
2015
2087
  });
2016
2088
  var recommended_default = recommended;
2017
2089
 
@@ -1833,6 +1833,74 @@ var antiSlopPlugin = eslintCompatPlugin({
1833
1833
  }
1834
1834
  });
1835
1835
  var src_default = antiSlopPlugin;
1836
+ // src/generic/index.ts
1837
+ import { eslintCompatPlugin as eslintCompatPlugin2 } from "@oxlint/plugins";
1838
+
1839
+ // src/generic/rules/prefer-event-parameter-type.ts
1840
+ import { defineRule as defineRule16 } from "@oxlint/plugins";
1841
+ function nearestEnclosingFunction(node) {
1842
+ let current = node.parent;
1843
+ while (current) {
1844
+ if (current.type === "FunctionDeclaration" || current.type === "FunctionExpression" || current.type === "ArrowFunctionExpression") {
1845
+ return current;
1846
+ }
1847
+ current = current.parent;
1848
+ }
1849
+ return null;
1850
+ }
1851
+ function assertedEventParameter(node) {
1852
+ const expression = node.expression;
1853
+ if (expression.type !== "MemberExpression" || expression.computed || expression.object.type !== "Identifier" || expression.property.type !== "Identifier" || expression.property.name !== "currentTarget" && expression.property.name !== "target") {
1854
+ return null;
1855
+ }
1856
+ const parameterName = expression.object.name;
1857
+ const property = expression.property.name;
1858
+ const owner = nearestEnclosingFunction(node);
1859
+ if (!owner?.params.some((parameter) => parameter.type === "Identifier" && parameter.name === parameterName)) {
1860
+ return null;
1861
+ }
1862
+ return {
1863
+ parameter: parameterName,
1864
+ property
1865
+ };
1866
+ }
1867
+ var preferEventParameterTypeRule = defineRule16({
1868
+ meta: {
1869
+ type: "suggestion",
1870
+ docs: {
1871
+ description: "Prefer typing event target properties in the handler parameter instead of asserting them at use sites."
1872
+ },
1873
+ messages: {
1874
+ typeEventParameter: "Type `{{parameter}}.{{property}}` in the function signature instead of asserting it at the use site."
1875
+ }
1876
+ },
1877
+ create(context) {
1878
+ const checkAssertion = (node) => {
1879
+ const eventParameter = assertedEventParameter(node);
1880
+ if (!eventParameter)
1881
+ return;
1882
+ context.report({
1883
+ node,
1884
+ messageId: "typeEventParameter",
1885
+ data: eventParameter
1886
+ });
1887
+ };
1888
+ return {
1889
+ TSAsExpression: checkAssertion,
1890
+ TSTypeAssertion: checkAssertion
1891
+ };
1892
+ }
1893
+ });
1894
+
1895
+ // src/generic/index.ts
1896
+ var timmoPlugin = eslintCompatPlugin2({
1897
+ meta: { name: "timmo" },
1898
+ rules: {
1899
+ "prefer-event-parameter-type": preferEventParameterTypeRule
1900
+ }
1901
+ });
1902
+ var generic_default = timmoPlugin;
1903
+
1836
1904
  // node_modules/oxlint/dist/index.js
1837
1905
  function defineConfig(config) {
1838
1906
  return config;
@@ -1849,9 +1917,13 @@ var recommended = defineConfig({
1849
1917
  {
1850
1918
  name: "anti-slop",
1851
1919
  specifier: "@timmo001/oxlint-rules/upstream/anti-slop"
1852
- }
1920
+ },
1921
+ { name: "timmo", specifier: "@timmo001/oxlint-rules/generic" }
1853
1922
  ],
1854
- rules: enablePluginRules("anti-slop", src_default)
1923
+ rules: {
1924
+ ...enablePluginRules("anti-slop", src_default),
1925
+ ...enablePluginRules("timmo", generic_default)
1926
+ }
1855
1927
  });
1856
1928
  var recommended_default = recommended;
1857
1929
  export {
@@ -0,0 +1,70 @@
1
+ // src/generic/index.ts
2
+ import { eslintCompatPlugin } from "@oxlint/plugins";
3
+
4
+ // src/generic/rules/prefer-event-parameter-type.ts
5
+ import { defineRule } from "@oxlint/plugins";
6
+ function nearestEnclosingFunction(node) {
7
+ let current = node.parent;
8
+ while (current) {
9
+ if (current.type === "FunctionDeclaration" || current.type === "FunctionExpression" || current.type === "ArrowFunctionExpression") {
10
+ return current;
11
+ }
12
+ current = current.parent;
13
+ }
14
+ return null;
15
+ }
16
+ function assertedEventParameter(node) {
17
+ const expression = node.expression;
18
+ if (expression.type !== "MemberExpression" || expression.computed || expression.object.type !== "Identifier" || expression.property.type !== "Identifier" || expression.property.name !== "currentTarget" && expression.property.name !== "target") {
19
+ return null;
20
+ }
21
+ const parameterName = expression.object.name;
22
+ const property = expression.property.name;
23
+ const owner = nearestEnclosingFunction(node);
24
+ if (!owner?.params.some((parameter) => parameter.type === "Identifier" && parameter.name === parameterName)) {
25
+ return null;
26
+ }
27
+ return {
28
+ parameter: parameterName,
29
+ property
30
+ };
31
+ }
32
+ var preferEventParameterTypeRule = defineRule({
33
+ meta: {
34
+ type: "suggestion",
35
+ docs: {
36
+ description: "Prefer typing event target properties in the handler parameter instead of asserting them at use sites."
37
+ },
38
+ messages: {
39
+ typeEventParameter: "Type `{{parameter}}.{{property}}` in the function signature instead of asserting it at the use site."
40
+ }
41
+ },
42
+ create(context) {
43
+ const checkAssertion = (node) => {
44
+ const eventParameter = assertedEventParameter(node);
45
+ if (!eventParameter)
46
+ return;
47
+ context.report({
48
+ node,
49
+ messageId: "typeEventParameter",
50
+ data: eventParameter
51
+ });
52
+ };
53
+ return {
54
+ TSAsExpression: checkAssertion,
55
+ TSTypeAssertion: checkAssertion
56
+ };
57
+ }
58
+ });
59
+
60
+ // src/generic/index.ts
61
+ var timmoPlugin = eslintCompatPlugin({
62
+ meta: { name: "timmo" },
63
+ rules: {
64
+ "prefer-event-parameter-type": preferEventParameterTypeRule
65
+ }
66
+ });
67
+ var generic_default = timmoPlugin;
68
+ export {
69
+ generic_default as default
70
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timmo001/oxlint-rules",
3
- "version": "0.1.4",
3
+ "version": "0.2.1",
4
4
  "description": "Shared Oxlint plugins and configs with optional Effect rules.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -14,6 +14,7 @@
14
14
  "exports": {
15
15
  "./upstream/anti-slop": "./dist/upstream/anti-slop.js",
16
16
  "./upstream/effect": "./dist/upstream/effect.js",
17
+ "./generic": "./dist/generic/index.js",
17
18
  "./effect": "./dist/effect/index.js",
18
19
  "./configs/recommended": "./dist/configs/recommended.js",
19
20
  "./configs/effect": "./dist/configs/effect.js"
@@ -21,6 +22,8 @@
21
22
  "files": [
22
23
  "dist",
23
24
  "src/effect",
25
+ "src/generic",
26
+ "!src/generic/**/*.test.ts",
24
27
  "!src/effect/**/*.test.ts",
25
28
  "vendor/anti-slop/src",
26
29
  "vendor/anti-slop/LICENSE",
@@ -37,7 +40,7 @@
37
40
  "format": "prettier --write \"{src,scripts}/**/*.ts\" \"skills/**/*.{md,mjs}\" \"*.md\" \"*.json\"",
38
41
  "format:check": "prettier --check \"{src,scripts}/**/*.ts\" \"skills/**/*.{md,mjs}\" \"*.md\" \"*.json\"",
39
42
  "lint": "oxlint src scripts",
40
- "skills:validate": "bunx skills-ref validate skills/install-timmo-oxlint-rules && bunx skills-ref validate skills/add-oxlint-rule",
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",
41
44
  "test": "node --experimental-strip-types scripts/test.ts",
42
45
  "typecheck": "tsc --noEmit"
43
46
  },
@@ -23,9 +23,13 @@ description: >-
23
23
  separately when Dylan Mulroy's plugin should own the behaviour.
24
24
  6. Register the rule in its plugin. Verify the matching config and copy command
25
25
  discover it from the plugin's `rules` map, then update the README rule list
26
- and behaviour description. Do not add rule names to either skill.
26
+ and behaviour description. Keep skills workflow-only; do not duplicate rule
27
+ or plugin inventories or counts in them.
27
28
  7. Run `mise run check`, `mise run build`, `npm pack --dry-run`, and
28
- `bunx jsr@0.14.3 publish --dry-run` in the central checkout.
29
+ `bunx jsr@0.14.3 publish --dry-run --allow-dirty` in the central checkout.
30
+ 8. If the user also asks to publish the change, load `release-oxlint-rules` and
31
+ hand off the release after the rule checks pass. Do not duplicate or bypass
32
+ its release workflow.
29
33
 
30
34
  Report the fixture contract, registration and docs changed, checks, and any
31
35
  consumer rollout left for a separate stage. Do not publish or assume a local
@@ -23,9 +23,9 @@ description: >-
23
23
  2. Detect Bun, npm, pnpm, or Yarn from the target's package manager declaration
24
24
  and lockfile. Add an exact development dependency through that package
25
25
  manager.
26
- 3. Extend `@timmo001/oxlint-rules/configs/recommended`. Use `/configs/effect`
27
- instead only when `effect` is a direct dependency or the user explicitly
28
- requests it.
26
+ 3. Extend `@timmo001/oxlint-rules/configs/recommended`. The config owns plugin
27
+ registration and recommended severities. Use `/configs/effect` instead only
28
+ when `effect` is a direct dependency or the user explicitly requests it.
29
29
  4. Keep dependency and config edits visible. Do not delegate them to a script.
30
30
 
31
31
  ## Copy rules
@@ -36,11 +36,11 @@ description: >-
36
36
  meaningful differences before asking whether replacement is intended.
37
37
  3. Run `node scripts/copy.mjs <bun|npm|pnpm|yarn> <destination>`. Add `--force`
38
38
  only after explicit replacement approval.
39
- 4. Register the three entry points printed by the command as `anti-slop`,
40
- `anti-slop-effect`, and `timmo-effect`. Merge every printed general rule
41
- setting into the target config. Merge every printed Effect rule setting only
42
- for direct Effect use or an explicit request. Do not maintain a rule list in
43
- this skill.
39
+ 4. Register every plugin entry point printed by the command. Merge every printed
40
+ general rule setting into the target config. Merge every printed Effect rule
41
+ setting only for direct Effect use or an explicit request. Treat the command
42
+ output as authoritative rather than maintaining plugin or rule inventories
43
+ in this skill.
44
44
 
45
45
  Run the target repository's normal lint, typecheck, tests, and build. Report
46
46
  package-manager changes, preserved local configuration, enabled rule groups,