@pobammer-ts/small-rules 1.0.0 → 2.0.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 ADDED
@@ -0,0 +1,35 @@
1
+ # `@pobammer-ts/small-rules`
2
+
3
+ A collection of [Oxlint](https://oxc.rs)-native rules for linting [roblox-ts](https://roblox-ts.com/) projects.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ ni -D @pobammer-ts/small-rules
9
+ ```
10
+
11
+ This package is an [Oxlint plugin](https://oxc.rs/docs/guide/usage/plugins) and must be used with [Oxlint](https://oxc.rs) v1.69.0 or later. It also requires TypeScript 6 and Node.js `^20.19.0` / `>=22.12.0`.
12
+
13
+ ## Configuration
14
+
15
+ Register the plugin in your `.oxlintrc.json` and enable the rules you want:
16
+
17
+ ```json
18
+ {
19
+ "plugins": ["@pobammer-ts/small-rules"],
20
+ "rules": {
21
+ "small-rules/no-print": "error",
22
+ "small-rules/no-warn": "error"
23
+ }
24
+ }
25
+ ```
26
+
27
+ All rules are namespaced under the `small-rules/` prefix. Pick the subset that fits your project — there is no bulk opt-in.
28
+
29
+ ## Rules
30
+
31
+ Full rule documentation is forthcoming alongside the project docs site.
32
+
33
+ ## License
34
+
35
+ MIT © HowManySmall
package/dist/index.d.ts CHANGED
@@ -680,13 +680,20 @@ declare const smallRules: import("oxlint-plugin-utilities").Plugin<{
680
680
  readonly type: "object";
681
681
  }]>;
682
682
  "prefer-expect-assertions": import("oxlint-plugin-utilities").CreateRule<readonly [{
683
+ readonly additionalAssertionFunctions?: readonly string[];
683
684
  readonly additionalExpectCallNames?: readonly string[];
684
685
  readonly onlyFunctionsWithAsyncKeyword?: boolean;
685
686
  readonly onlyFunctionsWithExpectInCallback?: boolean;
686
687
  readonly onlyFunctionsWithExpectInLoop?: boolean;
687
- }], "assertionsRequiresNumberArgument" | "assertionsRequiresOneArgument" | "hasAssertionsTakesNoArguments" | "haveExpectAssertions" | "suggestAddingAssertions" | "suggestAddingHasAssertions" | "wrongAssertionCount", readonly [{
688
+ }], "assertionsRequiresNumberArgument" | "assertionsRequiresOneArgument" | "hasAssertionsTakesNoArguments" | "haveExpectAssertions" | "preferAssertionsCount" | "suggestAddingAssertions" | "suggestAddingHasAssertions" | "wrongAssertionCount", readonly [{
688
689
  readonly additionalProperties: false;
689
690
  readonly properties: {
691
+ readonly additionalAssertionFunctions: {
692
+ readonly items: {
693
+ readonly type: "string";
694
+ };
695
+ readonly type: "array";
696
+ };
690
697
  readonly additionalExpectCallNames: {
691
698
  readonly items: {
692
699
  readonly type: "string";
@@ -705,20 +712,6 @@ declare const smallRules: import("oxlint-plugin-utilities").Plugin<{
705
712
  };
706
713
  readonly type: "object";
707
714
  }]>;
708
- "prefer-expect-assertions-count": import("oxlint-plugin-utilities").CreateRule<readonly [{
709
- readonly additionalAssertionFunctions?: readonly string[];
710
- }], "preferAssertionsCount", readonly [{
711
- readonly additionalProperties: false;
712
- readonly properties: {
713
- readonly additionalAssertionFunctions: {
714
- readonly items: {
715
- readonly type: "string";
716
- };
717
- readonly type: "array";
718
- };
719
- };
720
- readonly type: "object";
721
- }]>;
722
715
  "prefer-hoisted-jsx-elements": import("oxlint-plugin-utilities").CreateRule<readonly [{
723
716
  readonly additionalHoistableComponents?: readonly string[];
724
717
  readonly additionalStaticFactories?: readonly string[];
package/dist/index.js CHANGED
@@ -1230,92 +1230,25 @@ function addScore(current, addition, config, ceiling) {
1230
1230
  if (!config.performanceMode) return nextScore;
1231
1231
  return Math.min(nextScore, ceiling);
1232
1232
  }
1233
+ function addStructuralScore(current, node, depth, config, cache, depthMultiplierCache, ceiling, bonus = 0) {
1234
+ return addScore(current, calculateStructuralComplexity(node, depth, config, cache, depthMultiplierCache, ceiling) + bonus, config, ceiling);
1235
+ }
1233
1236
  function addNestedTypeAnnotationScores(current, members, depth, config, cache, depthMultiplierCache, ceiling) {
1234
1237
  let score = current;
1235
1238
  for (const member of members) {
1236
1239
  const typeAnnotation = getNestedTypeAnnotation(member);
1237
1240
  if (typeAnnotation === void 0) continue;
1238
- score = addScore(score, calculateStructuralComplexity(typeAnnotation, depth, config, cache, depthMultiplierCache, ceiling), config, ceiling);
1241
+ score = addStructuralScore(score, typeAnnotation, depth, config, cache, depthMultiplierCache, ceiling);
1239
1242
  }
1240
1243
  return score;
1241
1244
  }
1242
1245
  function getDepthMultiplier(depth, cache) {
1243
1246
  const cached = cache.get(depth);
1244
1247
  if (cached !== void 0) return cached;
1245
- const computed = Math.max(1, Math.log2(depth + 1));
1248
+ const computed = Math.log2(depth + 1);
1246
1249
  cache.set(depth, computed);
1247
1250
  return computed;
1248
1251
  }
1249
- function calculateChildComplexity(node, depth, config, cache, depthMultiplierCache, ceiling) {
1250
- return calculateStructuralComplexity(node, depth, config, cache, depthMultiplierCache, ceiling);
1251
- }
1252
- function addChildComplexity(score, node, depth, config, cache, depthMultiplierCache, ceiling) {
1253
- return addScore(score, calculateChildComplexity(node, depth, config, cache, depthMultiplierCache, ceiling), config, ceiling);
1254
- }
1255
- function getDefinedNodes(nodes) {
1256
- return nodes.filter((node) => node !== void 0);
1257
- }
1258
- function addChildComplexities(score, nodes, depth, config, cache, depthMultiplierCache, ceiling) {
1259
- let nextScore = score;
1260
- for (const child of nodes) nextScore = addChildComplexity(nextScore, child, depth, config, cache, depthMultiplierCache, ceiling);
1261
- return nextScore;
1262
- }
1263
- function calculateArrayTypeComplexity(node, depth, config, cache, depthMultiplierCache, ceiling) {
1264
- if (!isRecord$1(node)) return 1;
1265
- const elementType = getNodeValue(node, "elementType");
1266
- if (elementType === void 0) return 1;
1267
- return addScore(calculateChildComplexity(elementType, depth, config, cache, depthMultiplierCache, ceiling), 1, config, ceiling);
1268
- }
1269
- function calculateConditionalTypeComplexity(node, depth, config, cache, depthMultiplierCache, ceiling) {
1270
- if (!isRecord$1(node)) return 3;
1271
- return addChildComplexities(3, getDefinedNodes([
1272
- getNodeValue(node, "checkType"),
1273
- getNodeValue(node, "extendsType"),
1274
- getNodeValue(node, "trueType"),
1275
- getNodeValue(node, "falseType")
1276
- ]), depth, config, cache, depthMultiplierCache, ceiling);
1277
- }
1278
- function calculateFunctionTypeComplexity(node, depth, config, cache, depthMultiplierCache, ceiling) {
1279
- if (!isRecord$1(node)) return 2;
1280
- return addChildComplexities(2, getDefinedNodes([...getNodeArrayValue(node, "params")?.map(getNestedTypeAnnotation) ?? [], getReturnTypeAnnotation$1(node)]), depth, config, cache, depthMultiplierCache, ceiling);
1281
- }
1282
- function calculateInterfaceComplexity(node, depth, config, cache, depthMultiplierCache, ceiling) {
1283
- if (!isRecord$1(node)) return config.interfacePenalty;
1284
- const extendsLength = getNodeArrayValue(node, "extends")?.length ?? 0;
1285
- const body = getNodeValue(node, "body");
1286
- const members = body !== void 0 && isRecord$1(body) ? getNodeArrayValue(body, "body") ?? [] : [];
1287
- let score = config.interfacePenalty;
1288
- if (extendsLength > 0) score = addScore(score, extendsLength * 5, config, ceiling);
1289
- score = addScore(score, members.length * 2, config, ceiling);
1290
- return addNestedTypeAnnotationScores(score, members, depth, config, cache, depthMultiplierCache, ceiling);
1291
- }
1292
- function calculateTypeListComplexity(node, depth, typePenalty, config, cache, depthMultiplierCache, ceiling) {
1293
- if (!isRecord$1(node)) return 0;
1294
- const types = getNodeArrayValue(node, "types") ?? [];
1295
- return addScore(addChildComplexities(0, types, depth, config, cache, depthMultiplierCache, ceiling), typePenalty(types.length), config, ceiling);
1296
- }
1297
- function calculateMappedTypeComplexity(node, depth, config, cache, depthMultiplierCache, ceiling) {
1298
- if (!isRecord$1(node)) return 5;
1299
- return addChildComplexities(5, getDefinedNodes([getNodeValue(node, "constraint"), getNodeValue(node, "typeAnnotation")]), depth, config, cache, depthMultiplierCache, ceiling);
1300
- }
1301
- function calculateTupleTypeComplexity(node, depth, config, cache, depthMultiplierCache, ceiling) {
1302
- if (!isRecord$1(node)) return 0;
1303
- const elementTypes = getNodeArrayValue(node, "elementTypes") ?? [];
1304
- return addScore(addChildComplexities(1, elementTypes.filter((element) => element.type !== "TSRestType" && element.type !== "TSOptionalType"), depth, config, cache, depthMultiplierCache, ceiling), 1.5 * elementTypes.length, config, ceiling);
1305
- }
1306
- function calculateTypeLiteralComplexity(node, depth, config, cache, depthMultiplierCache, ceiling) {
1307
- if (!isRecord$1(node)) return 0;
1308
- const members = getNodeArrayValue(node, "members") ?? [];
1309
- return addNestedTypeAnnotationScores(2 + members.length * .5, members, depth, config, cache, depthMultiplierCache, ceiling);
1310
- }
1311
- function calculateTypeReferenceComplexity(node, depth, config, cache, depthMultiplierCache, ceiling) {
1312
- if (!isRecord$1(node)) return 2;
1313
- const typeArguments = getNodeValue(node, "typeArguments");
1314
- const parameters = typeArguments !== void 0 && isRecord$1(typeArguments) ? getNodeArrayValue(typeArguments, "params") ?? [] : [];
1315
- let score = 2;
1316
- for (const parameter of parameters) score = addScore(score, calculateChildComplexity(parameter, depth, config, cache, depthMultiplierCache, ceiling) + 2, config, ceiling);
1317
- return score;
1318
- }
1319
1252
  function calculateStructuralComplexity(node, depth, config, cache, depthMultiplierCache, ceiling) {
1320
1253
  const cached = cache.nodeCache.get(node);
1321
1254
  if (cached !== void 0) return cached;
@@ -1328,7 +1261,14 @@ function calculateStructuralComplexity(node, depth, config, cache, depthMultipli
1328
1261
  case "TSNeverKeyword":
1329
1262
  case "TSUnknownKeyword": break;
1330
1263
  case "TSArrayType":
1331
- score = calculateArrayTypeComplexity(node, nextDepth, config, cache, depthMultiplierCache, ceiling);
1264
+ if (isRecord$1(node)) {
1265
+ const elementType = getNodeValue(node, "elementType");
1266
+ if (elementType !== void 0) {
1267
+ score = addScore(calculateStructuralComplexity(elementType, nextDepth, config, cache, depthMultiplierCache, ceiling), 1, config, ceiling);
1268
+ break;
1269
+ }
1270
+ }
1271
+ score = 1;
1332
1272
  break;
1333
1273
  case "TSBigIntKeyword":
1334
1274
  case "TSBooleanKeyword":
@@ -1341,32 +1281,91 @@ function calculateStructuralComplexity(node, depth, config, cache, depthMultipli
1341
1281
  score = 1;
1342
1282
  break;
1343
1283
  case "TSConditionalType":
1344
- score = calculateConditionalTypeComplexity(node, nextDepth, config, cache, depthMultiplierCache, ceiling);
1284
+ score = 3;
1285
+ if (isRecord$1(node)) {
1286
+ const checkType = getNodeValue(node, "checkType");
1287
+ const extendsType = getNodeValue(node, "extendsType");
1288
+ const trueType = getNodeValue(node, "trueType");
1289
+ const falseType = getNodeValue(node, "falseType");
1290
+ if (checkType !== void 0) score = addScore(score, calculateStructuralComplexity(checkType, nextDepth, config, cache, depthMultiplierCache, ceiling), config, ceiling);
1291
+ if (extendsType !== void 0) score = addScore(score, calculateStructuralComplexity(extendsType, nextDepth, config, cache, depthMultiplierCache, ceiling), config, ceiling);
1292
+ if (trueType !== void 0) score = addScore(score, calculateStructuralComplexity(trueType, nextDepth, config, cache, depthMultiplierCache, ceiling), config, ceiling);
1293
+ if (falseType !== void 0) score = addScore(score, calculateStructuralComplexity(falseType, nextDepth, config, cache, depthMultiplierCache, ceiling), config, ceiling);
1294
+ }
1345
1295
  break;
1346
1296
  case "TSFunctionType":
1347
1297
  case "TSMethodSignature":
1348
- score = calculateFunctionTypeComplexity(node, nextDepth, config, cache, depthMultiplierCache, ceiling);
1298
+ score = 2;
1299
+ if (isRecord$1(node)) {
1300
+ const parameters = getNodeArrayValue(node, "params") ?? [];
1301
+ for (const parameter of parameters) {
1302
+ const typeAnnotation = getNestedTypeAnnotation(parameter);
1303
+ if (typeAnnotation === void 0) continue;
1304
+ score = addScore(score, calculateStructuralComplexity(typeAnnotation, nextDepth, config, cache, depthMultiplierCache, ceiling), config, ceiling);
1305
+ }
1306
+ const returnType = getReturnTypeAnnotation$1(node);
1307
+ if (returnType !== void 0) score = addScore(score, calculateStructuralComplexity(returnType, nextDepth, config, cache, depthMultiplierCache, ceiling), config, ceiling);
1308
+ }
1349
1309
  break;
1350
1310
  case "TSInterfaceDeclaration":
1351
- score = calculateInterfaceComplexity(node, nextDepth, config, cache, depthMultiplierCache, ceiling);
1311
+ score = config.interfacePenalty;
1312
+ if (isRecord$1(node)) {
1313
+ const extendsLength = getNodeArrayValue(node, "extends")?.length;
1314
+ if (extendsLength !== void 0 && extendsLength > 0) score = addScore(score, extendsLength * 5, config, ceiling);
1315
+ const body = getNodeValue(node, "body");
1316
+ const members = body !== void 0 && isRecord$1(body) ? getNodeArrayValue(body, "body") ?? [] : [];
1317
+ score = addScore(score, members.length * 2, config, ceiling);
1318
+ score = addNestedTypeAnnotationScores(score, members, nextDepth, config, cache, depthMultiplierCache, ceiling);
1319
+ }
1352
1320
  break;
1353
1321
  case "TSIntersectionType":
1354
- score = calculateTypeListComplexity(node, nextDepth, (typeCount) => 3 * typeCount, config, cache, depthMultiplierCache, ceiling);
1322
+ if (isRecord$1(node)) {
1323
+ const types = getNodeArrayValue(node, "types") ?? [];
1324
+ for (const type of types) score = addStructuralScore(score, type, nextDepth, config, cache, depthMultiplierCache, ceiling);
1325
+ score = addScore(score, 3 * types.length, config, ceiling);
1326
+ }
1355
1327
  break;
1356
1328
  case "TSMappedType":
1357
- score = calculateMappedTypeComplexity(node, nextDepth, config, cache, depthMultiplierCache, ceiling);
1329
+ score = 5;
1330
+ if (isRecord$1(node)) {
1331
+ const constraint = getNodeValue(node, "constraint");
1332
+ if (constraint !== void 0) score = addStructuralScore(score, constraint, nextDepth, config, cache, depthMultiplierCache, ceiling);
1333
+ const typeAnnotation = getNodeValue(node, "typeAnnotation");
1334
+ if (typeAnnotation !== void 0) score = addStructuralScore(score, typeAnnotation, nextDepth, config, cache, depthMultiplierCache, ceiling);
1335
+ }
1358
1336
  break;
1359
1337
  case "TSTupleType":
1360
- score = calculateTupleTypeComplexity(node, nextDepth, config, cache, depthMultiplierCache, ceiling);
1338
+ if (isRecord$1(node)) {
1339
+ const elementTypes = getNodeArrayValue(node, "elementTypes") ?? [];
1340
+ score = 1;
1341
+ for (const element of elementTypes) {
1342
+ if (element.type === "TSRestType" || element.type === "TSOptionalType") continue;
1343
+ score = addStructuralScore(score, element, nextDepth, config, cache, depthMultiplierCache, ceiling);
1344
+ }
1345
+ score = addScore(score, 1.5 * elementTypes.length, config, ceiling);
1346
+ }
1361
1347
  break;
1362
1348
  case "TSTypeLiteral":
1363
- score = calculateTypeLiteralComplexity(node, nextDepth, config, cache, depthMultiplierCache, ceiling);
1349
+ if (isRecord$1(node)) {
1350
+ const members = getNodeArrayValue(node, "members") ?? [];
1351
+ score = 2 + members.length * .5;
1352
+ score = addNestedTypeAnnotationScores(score, members, nextDepth, config, cache, depthMultiplierCache, ceiling);
1353
+ }
1364
1354
  break;
1365
1355
  case "TSTypeReference":
1366
- score = calculateTypeReferenceComplexity(node, nextDepth, config, cache, depthMultiplierCache, ceiling);
1356
+ score = 2;
1357
+ if (isRecord$1(node)) {
1358
+ const typeArguments = getNodeValue(node, "typeArguments");
1359
+ const parameters = typeArguments !== void 0 && isRecord$1(typeArguments) ? getNodeArrayValue(typeArguments, "params") ?? [] : [];
1360
+ for (const parameter of parameters) score = addStructuralScore(score, parameter, nextDepth, config, cache, depthMultiplierCache, ceiling, 2);
1361
+ }
1367
1362
  break;
1368
1363
  case "TSUnionType":
1369
- score = calculateTypeListComplexity(node, nextDepth, (typeCount) => 2 * (typeCount - 1), config, cache, depthMultiplierCache, ceiling);
1364
+ if (isRecord$1(node)) {
1365
+ const types = getNodeArrayValue(node, "types") ?? [];
1366
+ for (const type of types) score = addStructuralScore(score, type, nextDepth, config, cache, depthMultiplierCache, ceiling);
1367
+ score = addScore(score, 2 * (types.length - 1), config, ceiling);
1368
+ }
1370
1369
  break;
1371
1370
  default: score = 1;
1372
1371
  }
@@ -20281,7 +20280,7 @@ function getExpectContext(currentParent, root) {
20281
20280
  };
20282
20281
  const ownContext = {
20283
20282
  hasCallback: isFunction(currentParent),
20284
- hasIndeterminate: isFunction(currentParent) || isIndeterminateLoopNode(currentParent) || currentParent.type === "ConditionalExpression" || currentParent.type === "IfStatement" || currentParent.type === "SwitchCase" || currentParent.type === "TryStatement",
20283
+ hasIndeterminate: isFunction(currentParent) || isIndeterminateLoopNode(currentParent) || currentParent.type === "ConditionalExpression" || currentParent.type === "IfStatement" || currentParent.type === "SwitchCase" || currentParent.type === "TryStatement" && currentParent.handler !== null,
20285
20284
  hasLoop: isIndeterminateLoopNode(currentParent)
20286
20285
  };
20287
20286
  if (currentParent.parent === null) return ownContext;
@@ -20335,20 +20334,29 @@ function countExpectCalls(body, additionalAssertionFunctions = []) {
20335
20334
  }
20336
20335
  //#endregion
20337
20336
  //#region src/rules/prefer-expect-assertions.ts
20337
+ function parseStringArray(value) {
20338
+ if (!Array.isArray(value)) return [];
20339
+ return value.filter((item) => typeof item === "string");
20340
+ }
20338
20341
  function parseOptions$1(rawOptions) {
20339
20342
  if (!isRecord$2(rawOptions)) return {
20343
+ additionalAssertionFunctions: [],
20340
20344
  additionalExpectCallNames: [],
20341
20345
  onlyFunctionsWithAsyncKeyword: false,
20342
20346
  onlyFunctionsWithExpectInCallback: false,
20343
20347
  onlyFunctionsWithExpectInLoop: false
20344
20348
  };
20345
20349
  return {
20346
- additionalExpectCallNames: Array.isArray(rawOptions.additionalExpectCallNames) ? rawOptions.additionalExpectCallNames.filter((name) => typeof name === "string") : [],
20350
+ additionalAssertionFunctions: parseStringArray(rawOptions.additionalAssertionFunctions),
20351
+ additionalExpectCallNames: parseStringArray(rawOptions.additionalExpectCallNames),
20347
20352
  onlyFunctionsWithAsyncKeyword: rawOptions.onlyFunctionsWithAsyncKeyword === true,
20348
20353
  onlyFunctionsWithExpectInCallback: rawOptions.onlyFunctionsWithExpectInCallback === true,
20349
20354
  onlyFunctionsWithExpectInLoop: rawOptions.onlyFunctionsWithExpectInLoop === true
20350
20355
  };
20351
20356
  }
20357
+ function getAdditionalExpectCallNames(options) {
20358
+ return [...new Set([...options.additionalExpectCallNames, ...options.additionalAssertionFunctions])];
20359
+ }
20352
20360
  function hasEnabledFilter(options) {
20353
20361
  return options.onlyFunctionsWithAsyncKeyword || options.onlyFunctionsWithExpectInCallback || options.onlyFunctionsWithExpectInLoop;
20354
20362
  }
@@ -20449,11 +20457,22 @@ const preferExpectAssertions = defineRule({
20449
20457
  if (callback === void 0) return;
20450
20458
  const body = getCallbackBody(callback);
20451
20459
  if (body === void 0) return;
20452
- const { deterministic, indeterminate, hasIndeterminate, hasExpectInCallback, hasExpectInLoop } = countExpectCalls(body, options.additionalExpectCallNames);
20460
+ const { deterministic, indeterminate, hasIndeterminate, hasExpectInCallback, hasExpectInLoop } = countExpectCalls(body, getAdditionalExpectCallNames(options));
20453
20461
  if (!shouldCheckTest(callback, options, deterministic, indeterminate, hasExpectInCallback, hasExpectInLoop)) return;
20454
20462
  const assertionCall = getFirstStatementCall(callback);
20455
20463
  if (assertionCall !== void 0 && (isExpectAssertionsCall(assertionCall) || isExpectHasAssertionsCall(assertionCall))) {
20456
20464
  validateAssertionCall(context, assertionCall, deterministic, hasIndeterminate);
20465
+ if (isExpectHasAssertionsCall(assertionCall) && assertionCall.arguments.length === 0 && deterministic > 0 && !hasIndeterminate) {
20466
+ const [firstStatement] = getCallbackBlockBody(callback)?.body ?? [];
20467
+ if (firstStatement !== void 0) context.report({
20468
+ data: { count: String(deterministic) },
20469
+ fix(fixer) {
20470
+ return fixer.replaceText(firstStatement, `expect.assertions(${deterministic});`);
20471
+ },
20472
+ messageId: "preferAssertionsCount",
20473
+ node: assertionCall
20474
+ });
20475
+ }
20457
20476
  return;
20458
20477
  }
20459
20478
  reportMissingAssertions(context, callback, node, deterministic, hasIndeterminate);
@@ -20461,15 +20480,17 @@ const preferExpectAssertions = defineRule({
20461
20480
  },
20462
20481
  meta: {
20463
20482
  docs: {
20464
- description: "Enforce expect assertion guards in Jest tests.",
20483
+ description: "Enforce expect assertion guards in Jest tests and prefer expect.assertions(n) over expect.hasAssertions() when the count is known.",
20465
20484
  recommended: true
20466
20485
  },
20486
+ fixable: "code",
20467
20487
  hasSuggestions: true,
20468
20488
  messages: {
20469
20489
  assertionsRequiresNumberArgument: "This argument should be a number",
20470
20490
  assertionsRequiresOneArgument: "`expect.assertions` expects a single argument of type number",
20471
20491
  hasAssertionsTakesNoArguments: "`expect.hasAssertions` expects no arguments",
20472
20492
  haveExpectAssertions: "Every test should have either `expect.assertions(<number of assertions>)` or `expect.hasAssertions()` as its first expression",
20493
+ preferAssertionsCount: "Use `expect.assertions({{count}})` instead of `expect.hasAssertions()` when the count is known",
20473
20494
  suggestAddingAssertions: "Add `expect.assertions({{count}})`",
20474
20495
  suggestAddingHasAssertions: "Add `expect.hasAssertions()`",
20475
20496
  wrongAssertionCount: "Expected {{expected}} assertions, but test has {{actual}} expect calls"
@@ -20477,6 +20498,10 @@ const preferExpectAssertions = defineRule({
20477
20498
  schema: [{
20478
20499
  additionalProperties: false,
20479
20500
  properties: {
20501
+ additionalAssertionFunctions: {
20502
+ items: { type: "string" },
20503
+ type: "array"
20504
+ },
20480
20505
  additionalExpectCallNames: {
20481
20506
  items: { type: "string" },
20482
20507
  type: "array"
@@ -20491,46 +20516,6 @@ const preferExpectAssertions = defineRule({
20491
20516
  }
20492
20517
  });
20493
20518
  //#endregion
20494
- //#region src/rules/prefer-expect-assertions-count.ts
20495
- const preferExpectAssertionsCount = defineRule({
20496
- create(context) {
20497
- const additionalAssertionFunctions = context.options[0]?.additionalAssertionFunctions ?? [];
20498
- return { CallExpression(node) {
20499
- if (!isTestCaseCall(node)) return;
20500
- const callback = getTestCallback(node);
20501
- if (callback?.body?.type !== "BlockStatement") return;
20502
- const [firstStatement] = callback.body.body;
20503
- if (firstStatement?.type !== "ExpressionStatement" || firstStatement.expression.type !== "CallExpression" || !isExpectHasAssertionsCall(firstStatement.expression)) return;
20504
- const { deterministic, indeterminate, hasIndeterminate } = countExpectCalls(callback.body, additionalAssertionFunctions);
20505
- if (hasIndeterminate || indeterminate > 0 || deterministic === 0) return;
20506
- context.report({
20507
- data: { count: String(deterministic) },
20508
- fix(fixer) {
20509
- return fixer.replaceText(firstStatement, `expect.assertions(${deterministic});`);
20510
- },
20511
- messageId: "preferAssertionsCount",
20512
- node: firstStatement.expression
20513
- });
20514
- } };
20515
- },
20516
- meta: {
20517
- docs: {
20518
- description: "Prefer expect.assertions(n) over expect.hasAssertions() when the assertion count is known.",
20519
- recommended: true
20520
- },
20521
- fixable: "code",
20522
- messages: { preferAssertionsCount: "Use `expect.assertions({{count}})` instead of `expect.hasAssertions()` when the count is known" },
20523
- schema: [{
20524
- additionalProperties: false,
20525
- properties: { additionalAssertionFunctions: {
20526
- items: { type: "string" },
20527
- type: "array"
20528
- } },
20529
- type: "object"
20530
- }]
20531
- }
20532
- });
20533
- //#endregion
20534
20519
  //#region src/rules/prefer-hoisted-jsx-elements.ts
20535
20520
  function normalizeAdditionalHoistableComponents(rawOptions) {
20536
20521
  if (!(isRecord$2(rawOptions) && "additionalHoistableComponents" in rawOptions)) return /* @__PURE__ */ new Set();
@@ -26043,7 +26028,6 @@ const smallRules = definePlugin({
26043
26028
  "prefer-context-stack": preferContextStack,
26044
26029
  "prefer-early-return": preferEarlyReturn,
26045
26030
  "prefer-expect-assertions": preferExpectAssertions,
26046
- "prefer-expect-assertions-count": preferExpectAssertionsCount,
26047
26031
  "prefer-hoisted-jsx-elements": preferHoistedJsxElements,
26048
26032
  "prefer-hoisted-jsx-object-properties": preferHoistedJsxObjectProperties,
26049
26033
  "prefer-idiv": preferIdiv,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pobammer-ts/small-rules",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "Various Oxlint-native rules for linting roblox-ts projects.",
5
5
  "keywords": [
6
6
  "lint",
@@ -106,7 +106,7 @@
106
106
  "jiti": "2.7.0",
107
107
  "jscpd": "5.0.7",
108
108
  "knip": "6.16.1",
109
- "oxfmt": "0.54.0",
109
+ "oxfmt": "0.55.0",
110
110
  "oxlint": "1.69.0",
111
111
  "oxlint-tsgolint": "0.23.0",
112
112
  "package-manager-detector": "1.6.0",
@@ -118,7 +118,7 @@
118
118
  "vitiate": "0.3.0"
119
119
  },
120
120
  "peerDependencies": {
121
- "typescript": "6"
121
+ "typescript": ">=5 <8"
122
122
  },
123
123
  "engines": {
124
124
  "node": "^20.19.0 || >=22.12.0"