@bastidood/eslint-config 0.1.1 → 0.3.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.
@@ -13,12 +13,12 @@ var RestrictionLists = class RestrictionLists {
13
13
  #properties = ["error"];
14
14
  #syntax = ["error"];
15
15
  /** Append every list of an entry point's restrictions. */
16
- append(restrictions) {
17
- appendAll(this.#imports.paths, restrictions.importPaths);
18
- appendAll(this.#imports.patterns, restrictions.importPatterns);
19
- appendAll(this.#globals, restrictions.globals);
20
- appendAll(this.#properties, restrictions.properties);
21
- appendAll(this.#syntax, restrictions.syntax);
16
+ append({ importPaths, importPatterns, globals, properties, syntax }) {
17
+ appendAll(this.#imports.paths, importPaths);
18
+ appendAll(this.#imports.patterns, importPatterns);
19
+ appendAll(this.#globals, globals);
20
+ appendAll(this.#properties, properties);
21
+ appendAll(this.#syntax, syntax);
22
22
  }
23
23
  /** Copy these lists so that a scoped override can extend them without changing the top-level rules. */
24
24
  copy() {
@@ -10,6 +10,9 @@ const CORE = {
10
10
  jsPlugins: [{
11
11
  name: "eslint-js",
12
12
  specifier: "oxlint-plugin-eslint"
13
+ }, {
14
+ name: "slopstopper",
15
+ specifier: "@bastidood/eslint-plugin-slopstopper"
13
16
  }],
14
17
  categories: {
15
18
  correctness: "error",
@@ -271,6 +274,35 @@ const CORE = {
271
274
  radix: "error",
272
275
  "require-unicode-regexp": "error",
273
276
  "require-yield": "error",
277
+ "slopstopper/destructure-on-sight": "error",
278
+ "slopstopper/handle-or-rethrow": "error",
279
+ "slopstopper/merge-exclusive-branches": "error",
280
+ "slopstopper/needless-type-intersection": "error",
281
+ "slopstopper/no-alias-object-type": "error",
282
+ "slopstopper/no-array-filter-map": "error",
283
+ "slopstopper/no-conditional-empty-object-spread": "error",
284
+ "slopstopper/no-equality-chain": "error",
285
+ "slopstopper/no-inferrable-return-type": "error",
286
+ "slopstopper/no-known-value-widening": "error",
287
+ "slopstopper/no-multiple-optional-parameters": "error",
288
+ "slopstopper/no-negated-group": "error",
289
+ "slopstopper/no-negated-quantifier-callback": "error",
290
+ "slopstopper/no-plain-template-literal": "error",
291
+ "slopstopper/no-positional-scalars": "error",
292
+ "slopstopper/no-private-modifier": "error",
293
+ "slopstopper/no-reduce-accumulator-copy": "error",
294
+ "slopstopper/no-temporary-access": "error",
295
+ "slopstopper/no-type-predicates": "error",
296
+ "slopstopper/no-unknown-returns": "error",
297
+ "slopstopper/no-unknown-type-aliases": "error",
298
+ "slopstopper/no-unsafe-dictionary-type": "error",
299
+ "slopstopper/prefer-class-over-factory": "error",
300
+ "slopstopper/prefer-de-morgan": "error",
301
+ "slopstopper/prefer-prefix-increment": "error",
302
+ "slopstopper/prefer-switch": "error",
303
+ "slopstopper/prefer-typeof-undefined": "error",
304
+ "slopstopper/prefer-while-true": "error",
305
+ "slopstopper/stepdown-order": "error",
274
306
  "sort-keys": "off",
275
307
  "symbol-description": "error",
276
308
  "typescript/adjacent-overload-signatures": "error",
@@ -400,7 +432,7 @@ const CORE = {
400
432
  "typescript/switch-exhaustiveness-check": ["error", {
401
433
  allowDefaultCaseForExhaustiveSwitch: false,
402
434
  considerDefaultExhaustiveForUnions: false,
403
- requireDefaultForNonUnion: true
435
+ requireDefaultForNonUnion: false
404
436
  }],
405
437
  "typescript/triple-slash-reference": "error",
406
438
  "typescript/unbound-method": "error",
@@ -422,8 +454,11 @@ const CORE = {
422
454
  message: "Import through a path alias or package subpath import instead of a parent-relative path (`../`)."
423
455
  }],
424
456
  globals: [{
457
+ name: "Reflect",
458
+ message: "Call typed functions and read typed properties directly instead of through `Reflect`, and parse dynamic input into a domain type first. Suppress this diagnostic with a reason only where a typed access is impossible."
459
+ }, {
425
460
  name: "undefined",
426
- message: "Omit absent values instead of writing `undefined`: conditionally insert the property (as `exactOptionalPropertyTypes` requires) or leave out the argument, and use a `typeof` check to test for absence. Pass `void 0` only to a parameter that cannot be omitted and is explicitly typed to accept `undefined`."
461
+ message: "Omit absent values instead of writing `undefined`: add the property in an `if` after declaring the object (as `exactOptionalPropertyTypes` requires) or leave out the argument, and use a `typeof` check to test for absence. Pass `void 0` only to a parameter that cannot be omitted and is explicitly typed to accept `undefined`."
427
462
  }],
428
463
  properties: [{
429
464
  object: "process",
@@ -431,9 +466,6 @@ const CORE = {
431
466
  message: "Read environment variables only in the entry point: validate them there and pass the values as explicit parameters. Opt the entry point out with a file-level disable comment."
432
467
  }],
433
468
  syntax: [{
434
- selector: "[accessibility=\"private\"]:not(MethodDefinition[kind=\"constructor\"])",
435
- message: "Use an ECMAScript `#private` member instead of the TypeScript `private` modifier."
436
- }, {
437
469
  selector: "UnaryExpression[operator=\"void\"][argument.type=\"Literal\"]:not(:matches(CallExpression, NewExpression) > UnaryExpression.arguments)",
438
470
  message: "Do not write `void 0` to produce `undefined`: omit the absent value by conditionally inserting the property (as `exactOptionalPropertyTypes` requires). `void 0` is allowed only as an argument to a parameter that cannot be omitted and is explicitly typed to accept `undefined`."
439
471
  }]
@@ -1407,44 +1439,37 @@ const REACT_PERF = ["react-perf"];
1407
1439
  const TYPESCRIPT = ["@typescript-eslint"];
1408
1440
  const UNICORN = ["unicorn"];
1409
1441
  const VITEST = ["vitest"];
1410
- /** ESLint rule namespaces that an Oxlint plugin covers. */
1411
- function namespacesOf(plugin) {
1412
- switch (plugin) {
1413
- case "eslint": return ESLINT;
1414
- case "import": return IMPORT;
1415
- case "jsdoc": return JSDOC;
1416
- case "jsx-a11y": return JSX_A11Y;
1417
- case "node": return NODE;
1418
- case "promise": return PROMISE;
1419
- case "react": return REACT;
1420
- case "react-perf": return REACT_PERF;
1421
- case "typescript": return TYPESCRIPT;
1422
- case "unicorn": return UNICORN;
1423
- case "vitest": return VITEST;
1424
- default: return null;
1425
- }
1426
- }
1427
- /**
1428
- * Whether ESLint can run a rule, given its ESLint name. The conversion prunes unsupported rules: Oxlint-only rules,
1429
- * and rules or options that the ESLint 10 plugins lack.
1430
- */
1431
- function isSupportedInEslint(eslintName) {
1432
- switch (eslintName) {
1433
- case "import/consistent-type-specifier-style": return false;
1434
- default: return !eslintName.startsWith("oxc/");
1442
+ /** Write the ESLint equivalents of explicitly configured Oxlint rules, skipping rules that ESLint cannot run. */
1443
+ function writeEslintRules(target, rules) {
1444
+ for (const name in rules) {
1445
+ if (!Object.hasOwn(rules, name)) continue;
1446
+ const rule = rules[name];
1447
+ if (typeof rule === "undefined") continue;
1448
+ const eslintName = toEslintRuleName(name);
1449
+ if (isSupportedInEslint(eslintName)) target[eslintName] = toEslintRuleEntry(rule);
1435
1450
  }
1436
1451
  }
1437
- /** Rename the rules that an ESLint 10 plugin publishes under a different name than Oxlint's `eslint-plugin-oxlint` lists. */
1438
- function renamedInEslint(eslintName) {
1439
- switch (eslintName) {
1440
- case "unicorn/no-array-for-each": return "unicorn/no-for-each";
1441
- default: return eslintName;
1452
+ /** Translate an Oxlint rule name into the name of the same rule in ESLint and its plugins. */
1453
+ function toEslintRuleName(name) {
1454
+ const separator = name.indexOf("/");
1455
+ if (separator === -1) return name;
1456
+ const rule = name.slice(separator + 1);
1457
+ switch (name.slice(0, separator)) {
1458
+ case "eslint-js": return rule;
1459
+ case "typescript": return `@typescript-eslint/${rule}`;
1460
+ case "node": return `n/${rule}`;
1461
+ case "unicorn": return renamedInEslint(name);
1462
+ case "react": {
1463
+ const hook = `react-hooks/${rule}`;
1464
+ return Object.hasOwn(reactHooksRules, hook) ? hook : name;
1465
+ }
1466
+ default: return name;
1442
1467
  }
1443
1468
  }
1444
- function namespaceOf(eslintName) {
1445
- const separator = eslintName.lastIndexOf("/");
1446
- return separator === -1 ? "eslint" : eslintName.slice(0, separator);
1469
+ function toEslintRuleEntry(rule) {
1470
+ return Array.isArray(rule) ? [toEslintSeverity(rule[0]), ...rule.values().drop(1)] : toEslintSeverity(rule);
1447
1471
  }
1472
+ const CATEGORY_INDEX = indexCategories();
1448
1473
  /** Index the published category lists once: category, then ESLint namespace, then rule names. */
1449
1474
  function indexCategories() {
1450
1475
  const sources = [
@@ -1471,50 +1496,25 @@ function indexCategories() {
1471
1496
  }
1472
1497
  return index;
1473
1498
  }
1474
- const CATEGORY_INDEX = indexCategories();
1475
- /** Translate an Oxlint rule name into the name of the same rule in ESLint and its plugins. */
1476
- function toEslintRuleName(name) {
1477
- const separator = name.indexOf("/");
1478
- if (separator === -1) return name;
1479
- const rule = name.slice(separator + 1);
1480
- switch (name.slice(0, separator)) {
1481
- case "eslint-js": return rule;
1482
- case "typescript": return `@typescript-eslint/${rule}`;
1483
- case "node": return `n/${rule}`;
1484
- case "unicorn": return renamedInEslint(name);
1485
- case "react": {
1486
- const hook = `react-hooks/${rule}`;
1487
- return Object.hasOwn(reactHooksRules, hook) ? hook : name;
1488
- }
1489
- default: return name;
1490
- }
1491
- }
1492
- function toEslintSeverity(severity) {
1493
- switch (severity) {
1494
- case "deny":
1495
- case "error": return "error";
1496
- case "allow":
1497
- case "off": return "off";
1498
- case "warn": return "warn";
1499
- default: switch (severity) {
1500
- case 0:
1501
- case 1:
1502
- case 2: return severity;
1503
- default: throw new UnsupportedSeverityError(severity);
1504
- }
1499
+ /**
1500
+ * Whether ESLint can run a rule, given its ESLint name. The conversion prunes unsupported rules: Oxlint-only rules,
1501
+ * and rules or options that the ESLint 10 plugins lack.
1502
+ */
1503
+ function isSupportedInEslint(eslintName) {
1504
+ switch (eslintName) {
1505
+ case "import/consistent-type-specifier-style": return false;
1506
+ default: return !eslintName.startsWith("oxc/");
1505
1507
  }
1506
1508
  }
1507
- function toEslintRuleEntry(rule) {
1508
- return Array.isArray(rule) ? [toEslintSeverity(rule[0]), ...rule.values().drop(1)] : toEslintSeverity(rule);
1509
+ function namespaceOf(eslintName) {
1510
+ const separator = eslintName.lastIndexOf("/");
1511
+ return separator === -1 ? "eslint" : eslintName.slice(0, separator);
1509
1512
  }
1510
- /** Write the ESLint equivalents of explicitly configured Oxlint rules, skipping rules that ESLint cannot run. */
1511
- function writeEslintRules(target, rules) {
1512
- for (const name in rules) {
1513
- if (!Object.hasOwn(rules, name)) continue;
1514
- const rule = rules[name];
1515
- if (typeof rule === "undefined") continue;
1516
- const eslintName = toEslintRuleName(name);
1517
- if (isSupportedInEslint(eslintName)) target[eslintName] = toEslintRuleEntry(rule);
1513
+ /** Rename the rules that an ESLint 10 plugin publishes under a different name than Oxlint's `eslint-plugin-oxlint` lists. */
1514
+ function renamedInEslint(eslintName) {
1515
+ switch (eslintName) {
1516
+ case "unicorn/no-array-for-each": return "unicorn/no-for-each";
1517
+ default: return eslintName;
1518
1518
  }
1519
1519
  }
1520
1520
  /**
@@ -1558,4 +1558,36 @@ function writeNamespaceRules(target, byNamespace, plugins, severity) {
1558
1558
  }
1559
1559
  }
1560
1560
  }
1561
+ /** ESLint rule namespaces that an Oxlint plugin covers. */
1562
+ function namespacesOf(plugin) {
1563
+ switch (plugin) {
1564
+ case "eslint": return ESLINT;
1565
+ case "import": return IMPORT;
1566
+ case "jsdoc": return JSDOC;
1567
+ case "jsx-a11y": return JSX_A11Y;
1568
+ case "node": return NODE;
1569
+ case "promise": return PROMISE;
1570
+ case "react": return REACT;
1571
+ case "react-perf": return REACT_PERF;
1572
+ case "typescript": return TYPESCRIPT;
1573
+ case "unicorn": return UNICORN;
1574
+ case "vitest": return VITEST;
1575
+ default: return null;
1576
+ }
1577
+ }
1578
+ function toEslintSeverity(severity) {
1579
+ switch (severity) {
1580
+ case "deny":
1581
+ case "error": return "error";
1582
+ case "allow":
1583
+ case "off": return "off";
1584
+ case "warn": return "warn";
1585
+ default: switch (severity) {
1586
+ case 0:
1587
+ case 1:
1588
+ case 2: return severity;
1589
+ default: throw new UnsupportedSeverityError(severity);
1590
+ }
1591
+ }
1592
+ }
1561
1593
  export { CORE as i, writeEslintRules as n, writeExtensionRules as r, writeCategoryRules as t };
package/dist/core.mjs CHANGED
@@ -1,4 +1,5 @@
1
- import { i as CORE$1, n as writeEslintRules, r as writeExtensionRules, t as writeCategoryRules } from "./convert-B62YDKje.mjs";
1
+ import { i as CORE$1, n as writeEslintRules, r as writeExtensionRules, t as writeCategoryRules } from "./convert-Bc-edfKh.mjs";
2
+ import SLOPSTOPPER from "@bastidood/eslint-plugin-slopstopper";
2
3
  import { importX } from "eslint-plugin-import-x";
3
4
  import tseslint from "typescript-eslint";
4
5
  // src/core.ts
@@ -22,7 +23,8 @@ for (const { files, rules } of OVERRIDES) {
22
23
  }
23
24
  const PLUGIN_DEFINITIONS = {
24
25
  "@typescript-eslint": TYPESCRIPT_ESLINT,
25
- import: importX
26
+ import: importX,
27
+ slopstopper: SLOPSTOPPER
26
28
  };
27
29
  const RESTRICTIONS = {
28
30
  ...CORE$1.restrictions,
package/dist/jsdoc.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { i as CORE, n as writeEslintRules, t as writeCategoryRules } from "./convert-B62YDKje.mjs";
1
+ import { i as CORE, n as writeEslintRules, t as writeCategoryRules } from "./convert-Bc-edfKh.mjs";
2
2
  import jsdocPlugin from "eslint-plugin-jsdoc";
3
3
  // ../oxlint/src/jsdoc.ts
4
4
  const JSDOC$1 = { config: {
package/dist/node.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { i as CORE, n as writeEslintRules, t as writeCategoryRules } from "./convert-B62YDKje.mjs";
1
+ import { i as CORE, n as writeEslintRules, t as writeCategoryRules } from "./convert-Bc-edfKh.mjs";
2
2
  import nodePlugin from "eslint-plugin-n";
3
3
  // ../oxlint/src/node.ts
4
4
  /** Node.js rules for code that runs on Node.js. */
package/dist/promise.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { i as CORE, n as writeEslintRules, t as writeCategoryRules } from "./convert-B62YDKje.mjs";
1
+ import { i as CORE, n as writeEslintRules, t as writeCategoryRules } from "./convert-Bc-edfKh.mjs";
2
2
  import promisePlugin from "eslint-plugin-promise";
3
3
  // ../oxlint/src/promise.ts
4
4
  const PROMISE$1 = { config: {
package/dist/unicorn.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { i as CORE, n as writeEslintRules, t as writeCategoryRules } from "./convert-B62YDKje.mjs";
1
+ import { i as CORE, n as writeEslintRules, t as writeCategoryRules } from "./convert-Bc-edfKh.mjs";
2
2
  import unicornPlugin from "eslint-plugin-unicorn";
3
3
  // ../oxlint/src/unicorn.ts
4
4
  const UNICORN$1 = { config: {
@@ -105,7 +105,7 @@ const UNICORN$1 = { config: {
105
105
  "unicorn/prefer-optional-catch-binding": "off",
106
106
  "unicorn/prefer-prototype-methods": "error",
107
107
  "unicorn/prefer-query-selector": "error",
108
- "unicorn/prefer-reflect-apply": "error",
108
+ "unicorn/prefer-reflect-apply": "off",
109
109
  "unicorn/prefer-regexp-test": "error",
110
110
  "unicorn/prefer-response-static-json": "error",
111
111
  "unicorn/prefer-set-has": "error",
package/dist/vitest.mjs CHANGED
@@ -1,6 +1,8 @@
1
- import { i as CORE, n as writeEslintRules, t as writeCategoryRules } from "./convert-B62YDKje.mjs";
1
+ import { i as CORE, n as writeEslintRules, t as writeCategoryRules } from "./convert-Bc-edfKh.mjs";
2
2
  import vitestPlugin from "@vitest/eslint-plugin";
3
3
  // ../oxlint/src/vitest.ts
4
+ /** Steers module mocks toward dependency injection, as honest functions take their external collaborators as parameters. */
5
+ const MODULE_MOCK_MESSAGE = "A module mock swaps out a dependency that the code under test hides. Make the code honest instead: have it take the stateful or external collaborator (a database, network client, file system, or clock) as a parameter, inject the real one from the entry point, and pass a fake in the test. If a mock is unavoidable, suppress this diagnostic with a reason and mock through `import()` so that the path stays type-checked.";
4
6
  const VITEST$1 = { config: {
5
7
  plugins: ["vitest"],
6
8
  rules: {
@@ -36,7 +38,11 @@ const VITEST$1 = { config: {
36
38
  "vitest/no-large-snapshots": "error",
37
39
  "vitest/no-mocks-import": "error",
38
40
  "vitest/no-restricted-matchers": "off",
39
- "vitest/no-restricted-vi-methods": "off",
41
+ "vitest/no-restricted-vi-methods": ["error", {
42
+ mock: MODULE_MOCK_MESSAGE,
43
+ doMock: MODULE_MOCK_MESSAGE,
44
+ importMock: MODULE_MOCK_MESSAGE
45
+ }],
40
46
  "vitest/no-standalone-expect": "error",
41
47
  "vitest/no-test-prefixes": "error",
42
48
  "vitest/no-test-return-statement": "error",
@@ -83,7 +89,7 @@ const VITEST$1 = { config: {
83
89
  "vitest/valid-describe-callback": "error",
84
90
  "vitest/valid-expect": "error",
85
91
  "vitest/valid-expect-in-promise": "error",
86
- "vitest/valid-title": "error",
92
+ "vitest/valid-title": ["error", { ignoreTypeOfDescribeName: true }],
87
93
  "vitest/warn-todo": "error"
88
94
  }
89
95
  } };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastidood/eslint-config",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "Strict baseline ESLint configuration generated from @bastidood/oxlint-config.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/BastiDood/slopstopper",
@@ -26,6 +26,7 @@
26
26
  "access": "public"
27
27
  },
28
28
  "dependencies": {
29
+ "@bastidood/eslint-plugin-slopstopper": "^0.2.0",
29
30
  "@vitest/eslint-plugin": "^1.6.28",
30
31
  "eslint-plugin-import-x": "^4.17.1",
31
32
  "eslint-plugin-jsdoc": "^65.4.2",
@@ -35,12 +36,12 @@
35
36
  "typescript-eslint": "^8.72.1"
36
37
  },
37
38
  "devDependencies": {
38
- "@bastidood/oxlint-config": "0.1.1",
39
+ "@bastidood/oxlint-config": "0.3.0",
39
40
  "@bastidood/tsconfig": "0.1.1",
40
41
  "@typescript/native": "npm:typescript@^7.0.2",
41
42
  "eslint": "^10.13.0",
42
43
  "eslint-plugin-oxlint": "^1.90.0",
43
- "tsdown": "^0.23.2",
44
+ "tsdown": "^0.23.4",
44
45
  "typescript": "npm:@typescript/typescript6@^6.0.2"
45
46
  },
46
47
  "peerDependencies": {