eslint-plugin-ts-data-forge 0.5.4 → 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.
Files changed (48) hide show
  1. package/README.md +1 -0
  2. package/dist/plugin.d.mts +2 -0
  3. package/dist/plugin.d.mts.map +1 -1
  4. package/dist/plugin.mjs +1 -0
  5. package/dist/plugin.mjs.map +1 -1
  6. package/dist/rule-types.d.mts +15 -0
  7. package/dist/rule-types.d.mts.map +1 -1
  8. package/dist/rules/ast-utils.mjs.map +1 -1
  9. package/dist/rules/branded-number-types.mjs.map +1 -1
  10. package/dist/rules/constants.mjs.map +1 -1
  11. package/dist/rules/import-utils.mjs.map +1 -1
  12. package/dist/rules/no-side-effect-import.d.mts +17 -0
  13. package/dist/rules/no-side-effect-import.d.mts.map +1 -0
  14. package/dist/rules/no-side-effect-import.mjs +41 -0
  15. package/dist/rules/no-side-effect-import.mjs.map +1 -0
  16. package/dist/rules/no-unnecessary-type-guard.mjs.map +1 -1
  17. package/dist/rules/prefer-arr-is-array.mjs.map +1 -1
  18. package/dist/rules/prefer-arr-is-bounded-length-array.mjs.map +1 -1
  19. package/dist/rules/prefer-arr-is-fixed-length-array.mjs.map +1 -1
  20. package/dist/rules/prefer-arr-is-max-length-array.mjs.map +1 -1
  21. package/dist/rules/prefer-arr-is-min-length-array.mjs.map +1 -1
  22. package/dist/rules/prefer-arr-is-non-empty.mjs.map +1 -1
  23. package/dist/rules/prefer-arr-sum.mjs.map +1 -1
  24. package/dist/rules/prefer-as-int.mjs.map +1 -1
  25. package/dist/rules/prefer-canonical-array-slicing.mjs.map +1 -1
  26. package/dist/rules/prefer-canonical-length-cast.mjs +1 -1
  27. package/dist/rules/prefer-canonical-length-cast.mjs.map +1 -1
  28. package/dist/rules/prefer-canonical-length-guard.mjs.map +1 -1
  29. package/dist/rules/prefer-comparison-over-nullish-guard.mjs.map +1 -1
  30. package/dist/rules/prefer-is-non-null-object.mjs.map +1 -1
  31. package/dist/rules/prefer-is-record-and-has-key.mjs.map +1 -1
  32. package/dist/rules/prefer-num-safe-parse-float.mjs.map +1 -1
  33. package/dist/rules/prefer-num-safe-parse-int.mjs.map +1 -1
  34. package/dist/rules/prefer-obj-over-entries-round-trip.mjs.map +1 -1
  35. package/dist/rules/prefer-range-for-loop.mjs +1 -1
  36. package/dist/rules/prefer-range-for-loop.mjs.map +1 -1
  37. package/dist/rules/rules.d.mts +1 -0
  38. package/dist/rules/rules.d.mts.map +1 -1
  39. package/dist/rules/rules.mjs +2 -0
  40. package/dist/rules/rules.mjs.map +1 -1
  41. package/package.json +5 -5
  42. package/src/plugin.mts +1 -0
  43. package/src/rule-types.mts +16 -0
  44. package/src/rules/no-side-effect-import.mts +71 -0
  45. package/src/rules/no-side-effect-import.test.mts +85 -0
  46. package/src/rules/prefer-canonical-length-cast.mts +9 -2
  47. package/src/rules/prefer-range-for-loop.mts +1 -1
  48. package/src/rules/rules.mts +2 -0
package/README.md CHANGED
@@ -82,6 +82,7 @@ export default [
82
82
  | `no-unnecessary-type-guard` | Flag `ts-data-forge` type-guard calls that do no narrowing (type-aware). |
83
83
  | `prefer-comparison-over-nullish-guard` | Prefer direct `=== null` / `!== undefined` over `isNull` / `isNotUndefined` calls. |
84
84
  | `prefer-obj-over-entries-round-trip` | Replace an `Object.fromEntries(Object.entries(o).map/filter/flatMap(…))` round trip with `Obj.map` / `Obj.filter` / `Obj.filterMap`. |
85
+ | `no-side-effect-import` | Remove a side-effect-only `import 'ts-data-forge';`; the package declares `sideEffects: false`, so it binds no name and does nothing. |
85
86
 
86
87
  ## License
87
88
 
package/dist/plugin.d.mts CHANGED
@@ -15,6 +15,7 @@ export declare const eslintPluginTsDataForge: {
15
15
  readonly 'prefer-is-record-and-has-key': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useHasKey" | "useIsRecordAndHasKey", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
16
16
  readonly 'prefer-num-safe-parse-int': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useSafeParseInt", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
17
17
  readonly 'prefer-num-safe-parse-float': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useSafeParseFloat", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
18
+ readonly 'no-side-effect-import': import("@typescript-eslint/utils/ts-eslint").RuleModule<"removeSideEffectImport", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
18
19
  readonly 'no-unnecessary-type-guard': import("@typescript-eslint/utils/ts-eslint").RuleModule<"alwaysFalse" | "alwaysTrue" | "replaceTypeGuard", readonly [(Readonly<{
19
20
  ignore?: readonly string[];
20
21
  }> | undefined)?], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
@@ -40,6 +41,7 @@ export declare const eslintPluginTsDataForge: {
40
41
  readonly 'ts-data-forge/prefer-is-record-and-has-key': 'error';
41
42
  readonly 'ts-data-forge/prefer-num-safe-parse-int': 'error';
42
43
  readonly 'ts-data-forge/prefer-num-safe-parse-float': 'error';
44
+ readonly 'ts-data-forge/no-side-effect-import': 'error';
43
45
  readonly 'ts-data-forge/no-unnecessary-type-guard': 'error';
44
46
  readonly 'ts-data-forge/prefer-comparison-over-nullish-guard': 'error';
45
47
  readonly 'ts-data-forge/prefer-obj-over-entries-round-trip': 'error';
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.mts","sourceRoot":"","sources":["../src/plugin.mts"],"names":[],"mappings":"AAEA,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AA4CvE,eAAO,MAAM,uBAAuB;;iBAEhC,IAAI,EAAE,6BAA6B;;;;;;;;;;;;;;;;;;;;;QAInC,oDAAoD;iBACpD,WAAW;2BAtBP,2BAA2B;;yBAQ3B,eAAe,EAAI,YAAY;;;yEA5BW,OAAO;uEACT,OAAO;wEACN,OAAO;8DACjB,OAAO;yDACZ,OAAO;wDACR,OAAO;oEACK,OAAO;gEACX,OAAO;uEACA,OAAO;oEACV,OAAO;sEACL,OAAO;oEACT,OAAO;+EACI,OAAO;6EACT,OAAO;;;;CA+B5B,CAAC"}
1
+ {"version":3,"file":"plugin.d.mts","sourceRoot":"","sources":["../src/plugin.mts"],"names":[],"mappings":"AAEA,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AA6CvE,eAAO,MAAM,uBAAuB;;iBAEhC,IAAI,EAAE,6BAA6B;;;;;;;;;;;;;;;;;;;;;;QAInC,oDAAoD;iBACpD,WAAW;2BAtBP,2BAA2B;;yBAQ3B,eAAe,EAAI,YAAY;;;yEA7BW,OAAO;uEACT,OAAO;wEACN,OAAO;8DACjB,OAAO;yDACZ,OAAO;wDACR,OAAO;oEACK,OAAO;gEACX,OAAO;uEACA,OAAO;oEACV,OAAO;sEACL,OAAO;gEACb,OAAO;oEACH,OAAO;+EACI,OAAO;6EACT,OAAO;;;;CA+B5B,CAAC"}
package/dist/plugin.mjs CHANGED
@@ -12,6 +12,7 @@ const recommendedRules = {
12
12
  "ts-data-forge/prefer-is-record-and-has-key": "error",
13
13
  "ts-data-forge/prefer-num-safe-parse-int": "error",
14
14
  "ts-data-forge/prefer-num-safe-parse-float": "error",
15
+ "ts-data-forge/no-side-effect-import": "error",
15
16
  "ts-data-forge/no-unnecessary-type-guard": "error",
16
17
  "ts-data-forge/prefer-comparison-over-nullish-guard": "error",
17
18
  "ts-data-forge/prefer-obj-over-entries-round-trip": "error"
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.mjs","sources":["../src/plugin.mts"],"sourcesContent":["import { type ReadonlyRecord } from 'ts-type-forge';\nimport { tsDataForgeRules } from './rules/index.mjs';\nimport { type ESLintFlatConfig, type ESLintPlugin } from './types.mjs';\n\n/**\n * Every rule this plugin ships, at `error`.\n *\n * The `satisfies` clause below is keyed off {@link tsDataForgeRules}, so adding\n * a rule without listing it here fails to type-check.\n */\nconst recommendedRules = {\n 'ts-data-forge/prefer-canonical-array-slicing': 'error',\n 'ts-data-forge/prefer-canonical-length-cast': 'error',\n 'ts-data-forge/prefer-canonical-length-guard': 'error',\n 'ts-data-forge/prefer-arr-is-array': 'error',\n 'ts-data-forge/prefer-arr-sum': 'error',\n 'ts-data-forge/prefer-as-int': 'error',\n 'ts-data-forge/prefer-is-non-null-object': 'error',\n 'ts-data-forge/prefer-range-for-loop': 'error',\n 'ts-data-forge/prefer-is-record-and-has-key': 'error',\n 'ts-data-forge/prefer-num-safe-parse-int': 'error',\n 'ts-data-forge/prefer-num-safe-parse-float': 'error',\n 'ts-data-forge/no-unnecessary-type-guard': 'error',\n 'ts-data-forge/prefer-comparison-over-nullish-guard': 'error',\n 'ts-data-forge/prefer-obj-over-entries-round-trip': 'error',\n} as const satisfies ReadonlyRecord<\n `ts-data-forge/${keyof typeof tsDataForgeRules}`,\n 'error'\n>;\n\nconst recommendedConfig = {\n name: 'ts-data-forge/recommended',\n plugins: {\n // Resolved lazily so that this config registers the *same* object that\n // `eslintPluginTsDataForge` refers to. ESLint rejects a plugin name that\n // maps to two different objects with `Cannot redefine plugin`, which is\n // what a user combining this preset with their own\n // `plugins: { 'ts-data-forge': eslintPluginTsDataForge }` entry would hit\n // if the preset embedded a separate copy of the plugin.\n get 'ts-data-forge'(): ESLintPlugin {\n return eslintPluginTsDataForge;\n },\n },\n rules: recommendedRules,\n} as const satisfies ESLintFlatConfig;\n\nexport const eslintPluginTsDataForge = {\n meta: {\n name: 'eslint-plugin-ts-data-forge',\n },\n rules: tsDataForgeRules,\n configs: {\n /** Enables every rule of this plugin at `error`. */\n recommended: recommendedConfig,\n },\n} as const satisfies ESLintPlugin;\n"],"names":[],"mappings":";;AAUA,MAAM,gBAAA,GAAmB;AAAA,EACvB,8CAAA,EAAgD,OAAA;AAAA,EAChD,4CAAA,EAA8C,OAAA;AAAA,EAC9C,6CAAA,EAA+C,OAAA;AAAA,EAC/C,mCAAA,EAAqC,OAAA;AAAA,EACrC,8BAAA,EAAgC,OAAA;AAAA,EAChC,6BAAA,EAA+B,OAAA;AAAA,EAC/B,yCAAA,EAA2C,OAAA;AAAA,EAC3C,qCAAA,EAAuC,OAAA;AAAA,EACvC,4CAAA,EAA8C,OAAA;AAAA,EAC9C,yCAAA,EAA2C,OAAA;AAAA,EAC3C,2CAAA,EAA6C,OAAA;AAAA,EAC7C,yCAAA,EAA2C,OAAA;AAAA,EAC3C,oDAAA,EAAsD,OAAA;AAAA,EACtD,kDAAA,EAAoD;AACtD,CAAA;AAKA,MAAM,iBAAA,GAAoB;AAAA,EACxB,IAAA,EAAM,2BAAA;AAAA,EACN,OAAA,EAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOP,IAAI,eAAA,GAAgC;AAClC,MAAA,OAAO,uBAAA;AAAA,IACT;AAAA,GACF;AAAA,EACA,KAAA,EAAO;AACT,CAAA;AAEO,MAAM,uBAAA,GAA0B;AAAA,EACrC,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM;AAAA,GACR;AAAA,EACA,KAAA,EAAO,gBAAA;AAAA,EACP,OAAA,EAAS;AAAA;AAAA,IAEP,WAAA,EAAa;AAAA;AAEjB;;;;"}
1
+ {"version":3,"file":"plugin.mjs","sources":["../src/plugin.mts"],"sourcesContent":["import { type ReadonlyRecord } from 'ts-type-forge';\nimport { tsDataForgeRules } from './rules/index.mjs';\nimport { type ESLintFlatConfig, type ESLintPlugin } from './types.mjs';\n\n/**\n * Every rule this plugin ships, at `error`.\n *\n * The `satisfies` clause below is keyed off {@link tsDataForgeRules}, so adding\n * a rule without listing it here fails to type-check.\n */\nconst recommendedRules = {\n 'ts-data-forge/prefer-canonical-array-slicing': 'error',\n 'ts-data-forge/prefer-canonical-length-cast': 'error',\n 'ts-data-forge/prefer-canonical-length-guard': 'error',\n 'ts-data-forge/prefer-arr-is-array': 'error',\n 'ts-data-forge/prefer-arr-sum': 'error',\n 'ts-data-forge/prefer-as-int': 'error',\n 'ts-data-forge/prefer-is-non-null-object': 'error',\n 'ts-data-forge/prefer-range-for-loop': 'error',\n 'ts-data-forge/prefer-is-record-and-has-key': 'error',\n 'ts-data-forge/prefer-num-safe-parse-int': 'error',\n 'ts-data-forge/prefer-num-safe-parse-float': 'error',\n 'ts-data-forge/no-side-effect-import': 'error',\n 'ts-data-forge/no-unnecessary-type-guard': 'error',\n 'ts-data-forge/prefer-comparison-over-nullish-guard': 'error',\n 'ts-data-forge/prefer-obj-over-entries-round-trip': 'error',\n} as const satisfies ReadonlyRecord<\n `ts-data-forge/${keyof typeof tsDataForgeRules}`,\n 'error'\n>;\n\nconst recommendedConfig = {\n name: 'ts-data-forge/recommended',\n plugins: {\n // Resolved lazily so that this config registers the *same* object that\n // `eslintPluginTsDataForge` refers to. ESLint rejects a plugin name that\n // maps to two different objects with `Cannot redefine plugin`, which is\n // what a user combining this preset with their own\n // `plugins: { 'ts-data-forge': eslintPluginTsDataForge }` entry would hit\n // if the preset embedded a separate copy of the plugin.\n get 'ts-data-forge'(): ESLintPlugin {\n return eslintPluginTsDataForge;\n },\n },\n rules: recommendedRules,\n} as const satisfies ESLintFlatConfig;\n\nexport const eslintPluginTsDataForge = {\n meta: {\n name: 'eslint-plugin-ts-data-forge',\n },\n rules: tsDataForgeRules,\n configs: {\n /** Enables every rule of this plugin at `error`. */\n recommended: recommendedConfig,\n },\n} as const satisfies ESLintPlugin;\n"],"names":[],"mappings":";;AAUA,MAAM,gBAAA,GAAmB;AAAA,EACvB,8CAAA,EAAgD,OAAA;AAAA,EAChD,4CAAA,EAA8C,OAAA;AAAA,EAC9C,6CAAA,EAA+C,OAAA;AAAA,EAC/C,mCAAA,EAAqC,OAAA;AAAA,EACrC,8BAAA,EAAgC,OAAA;AAAA,EAChC,6BAAA,EAA+B,OAAA;AAAA,EAC/B,yCAAA,EAA2C,OAAA;AAAA,EAC3C,qCAAA,EAAuC,OAAA;AAAA,EACvC,4CAAA,EAA8C,OAAA;AAAA,EAC9C,yCAAA,EAA2C,OAAA;AAAA,EAC3C,2CAAA,EAA6C,OAAA;AAAA,EAC7C,qCAAA,EAAuC,OAAA;AAAA,EACvC,yCAAA,EAA2C,OAAA;AAAA,EAC3C,oDAAA,EAAsD,OAAA;AAAA,EACtD,kDAAA,EAAoD;AACtD,CAAA;AAKA,MAAM,iBAAA,GAAoB;AAAA,EACxB,IAAA,EAAM,2BAAA;AAAA,EACN,OAAA,EAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOP,IAAI,eAAA,GAAgC;AAClC,MAAA,OAAO,uBAAA;AAAA,IACT;AAAA,GACF;AAAA,EACA,KAAA,EAAO;AACT,CAAA;AAEO,MAAM,uBAAA,GAA0B;AAAA,EACrC,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM;AAAA,GACR;AAAA,EACA,KAAA,EAAO,gBAAA;AAAA,EACP,OAAA,EAAS;AAAA;AAAA,IAEP,WAAA,EAAa;AAAA;AAEjB;;"}
@@ -162,6 +162,20 @@ declare namespace PreferNumSafeParseInt {
162
162
  declare namespace PreferNumSafeParseFloat {
163
163
  type RuleEntry = Linter.StringSeverity;
164
164
  }
165
+ /**
166
+ * @description Remove a side-effect-only `import 'ts-data-forge';`, which binds no name and, the package being `sideEffects: false`, does nothing.
167
+ *
168
+ * ```md
169
+ * | key | value |
170
+ * | :--------- | :--------- |
171
+ * | type | suggestion |
172
+ * | deprecated | false |
173
+ * | fixable | code |
174
+ * ```
175
+ */
176
+ declare namespace NoSideEffectImport {
177
+ type RuleEntry = Linter.StringSeverity;
178
+ }
165
179
  /**
166
180
  * @description Detect ts-data-forge type guard calls that perform no narrowing (the argument type already satisfies, or can never satisfy, the guard).
167
181
  *
@@ -238,6 +252,7 @@ export type EslintTsDataForgeRules = Readonly<{
238
252
  'ts-data-forge/prefer-is-record-and-has-key': PreferIsRecordAndHasKey.RuleEntry;
239
253
  'ts-data-forge/prefer-num-safe-parse-int': PreferNumSafeParseInt.RuleEntry;
240
254
  'ts-data-forge/prefer-num-safe-parse-float': PreferNumSafeParseFloat.RuleEntry;
255
+ 'ts-data-forge/no-side-effect-import': NoSideEffectImport.RuleEntry;
241
256
  'ts-data-forge/no-unnecessary-type-guard': NoUnnecessaryTypeGuard.RuleEntry;
242
257
  'ts-data-forge/prefer-comparison-over-nullish-guard': PreferComparisonOverNullishGuard.RuleEntry;
243
258
  'ts-data-forge/prefer-obj-over-entries-round-trip': PreferObjOverEntriesRoundTrip.RuleEntry;
@@ -1 +1 @@
1
- {"version":3,"file":"rule-types.d.mts","sourceRoot":"","sources":["../src/rule-types.mts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rule-types.d.mts","sourceRoot":"","sources":["../src/rule-types.mts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ast-utils.mjs","sources":["../../src/rules/ast-utils.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { type DeepReadonly } from 'ts-type-forge';\n\nconst isIntegerLiteral = (node: DeepReadonly<TSESTree.Expression>): boolean =>\n node.type === AST_NODE_TYPES.Literal &&\n typeof node.value === 'number' &&\n Number.isInteger(node.value);\n\nexport const isIntegerLiteralOrConstant = (\n node: DeepReadonly<TSESTree.Expression>,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n sourceCode: TSESLint.SourceCode,\n): boolean => {\n // Direct integer literal (e.g., 3)\n if (isIntegerLiteral(node)) return true;\n\n // Identifier referencing a const variable initialized with an integer literal\n if (node.type !== AST_NODE_TYPES.Identifier) return false;\n\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n const scope = sourceCode.getScope(node as TSESTree.Node);\n\n const variable = findVariable(scope, node.name);\n\n if (variable === undefined) return false;\n\n // Must have exactly one definition (const)\n if (variable.defs.length !== 1) return false;\n\n const def = variable.defs[0];\n\n if (def === undefined) return false;\n\n if (!isVariableDefinition(def)) return false;\n\n if (def.parent.kind !== 'const') return false;\n\n if (def.node.init == null) return false;\n\n // Reject if there is an explicit type annotation (e.g., `const n: number = 3`)\n // because the literal type is widened and type guard cannot narrow it.\n if (def.node.id.typeAnnotation !== undefined) return false;\n\n return isIntegerLiteral(def.node.init);\n};\n\nexport const isLengthAccess = (\n node: DeepReadonly<TSESTree.Expression>,\n): node is TSESTree.MemberExpression =>\n node.type === AST_NODE_TYPES.MemberExpression &&\n node.property.type === AST_NODE_TYPES.Identifier &&\n node.property.name === 'length';\n\ntype LengthComparison = Readonly<{\n array: TSESTree.Expression;\n bound: TSESTree.Expression;\n kind: 'max' | 'min';\n}>;\n\n/**\n * Parses a `<array>.length <op> <bound>` comparison (in either operand order)\n * into its array expression, integer-literal/`const` `bound`, and whether it is\n * a lower (`min`) or upper (`max`) length bound. Returns `undefined` for\n * anything else.\n *\n * - `xs.length >= n` / `n <= xs.length` → `min`\n * - `xs.length <= n` / `n >= xs.length` → `max`\n */\nexport const parseLengthComparison = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n node: TSESTree.Expression,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n sourceCode: TSESLint.SourceCode,\n): LengthComparison | undefined => {\n if (\n node.type !== AST_NODE_TYPES.BinaryExpression ||\n (node.operator !== '>=' && node.operator !== '<=')\n ) {\n return undefined;\n }\n\n const { left, right, operator } = node;\n\n const lengthOnLeft = isLengthAccess(left);\n\n const lengthSide = lengthOnLeft ? left : right;\n\n const boundSide = lengthOnLeft ? right : left;\n\n if (!isLengthAccess(lengthSide)) return undefined;\n\n if (!isIntegerLiteralOrConstant(boundSide, sourceCode)) return undefined;\n\n const kind: LengthComparison['kind'] = (\n lengthOnLeft ? operator === '>=' : operator === '<='\n )\n ? 'min'\n : 'max';\n\n return { array: lengthSide.object, bound: boundSide, kind };\n};\n\n/**\n * `true` when `node` is one operand of an `a && b` whose other operand is the\n * complementary length bound (`min` vs `max`) on the same array — i.e. the two\n * together form the `Arr.isBoundedLengthArray` pattern handled by\n * `prefer-arr-is-bounded-length-array`. The single-bound rules skip such\n * operands so the bounded rule rewrites the whole `&&` instead of the parts.\n */\nexport const isPartOfBoundedLengthCheck = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n node: TSESTree.BinaryExpression,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n sourceCode: TSESLint.SourceCode,\n): boolean => {\n const { parent } = node;\n\n if (\n parent.type !== AST_NODE_TYPES.LogicalExpression ||\n parent.operator !== '&&'\n ) {\n return false;\n }\n\n const self = parseLengthComparison(node, sourceCode);\n\n if (self === undefined) return false;\n\n const sibling = parent.left === node ? parent.right : parent.left;\n\n const other = parseLengthComparison(sibling, sourceCode);\n\n if (other === undefined) return false;\n\n return (\n self.kind !== other.kind &&\n sourceCode.getText(self.array) === sourceCode.getText(other.array)\n );\n};\n\ntype VariableDefinition = Readonly<{\n type: string;\n parent: TSESTree.VariableDeclaration;\n node: TSESTree.VariableDeclarator;\n}>;\n\nconst isVariableDefinition = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n def: TSESLint.Scope.Definition,\n): def is TSESLint.Scope.Definition & VariableDefinition =>\n (def.type as string) === 'Variable';\n\nconst findVariable = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n scope: TSESLint.Scope.Scope,\n name: string,\n): TSESLint.Scope.Variable | undefined => {\n const variable = scope.set.get(name);\n\n if (variable !== undefined) return variable;\n\n if (scope.upper === null) return undefined;\n\n return findVariable(scope.upper, name);\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ast-utils.mjs","sources":["../../src/rules/ast-utils.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { type DeepReadonly } from 'ts-type-forge';\n\nconst isIntegerLiteral = (node: DeepReadonly<TSESTree.Expression>): boolean =>\n node.type === AST_NODE_TYPES.Literal &&\n typeof node.value === 'number' &&\n Number.isInteger(node.value);\n\nexport const isIntegerLiteralOrConstant = (\n node: DeepReadonly<TSESTree.Expression>,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n sourceCode: TSESLint.SourceCode,\n): boolean => {\n // Direct integer literal (e.g., 3)\n if (isIntegerLiteral(node)) return true;\n\n // Identifier referencing a const variable initialized with an integer literal\n if (node.type !== AST_NODE_TYPES.Identifier) return false;\n\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n const scope = sourceCode.getScope(node as TSESTree.Node);\n\n const variable = findVariable(scope, node.name);\n\n if (variable === undefined) return false;\n\n // Must have exactly one definition (const)\n if (variable.defs.length !== 1) return false;\n\n const def = variable.defs[0];\n\n if (def === undefined) return false;\n\n if (!isVariableDefinition(def)) return false;\n\n if (def.parent.kind !== 'const') return false;\n\n if (def.node.init == null) return false;\n\n // Reject if there is an explicit type annotation (e.g., `const n: number = 3`)\n // because the literal type is widened and type guard cannot narrow it.\n if (def.node.id.typeAnnotation !== undefined) return false;\n\n return isIntegerLiteral(def.node.init);\n};\n\nexport const isLengthAccess = (\n node: DeepReadonly<TSESTree.Expression>,\n): node is TSESTree.MemberExpression =>\n node.type === AST_NODE_TYPES.MemberExpression &&\n node.property.type === AST_NODE_TYPES.Identifier &&\n node.property.name === 'length';\n\ntype LengthComparison = Readonly<{\n array: TSESTree.Expression;\n bound: TSESTree.Expression;\n kind: 'max' | 'min';\n}>;\n\n/**\n * Parses a `<array>.length <op> <bound>` comparison (in either operand order)\n * into its array expression, integer-literal/`const` `bound`, and whether it is\n * a lower (`min`) or upper (`max`) length bound. Returns `undefined` for\n * anything else.\n *\n * - `xs.length >= n` / `n <= xs.length` → `min`\n * - `xs.length <= n` / `n >= xs.length` → `max`\n */\nexport const parseLengthComparison = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n node: TSESTree.Expression,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n sourceCode: TSESLint.SourceCode,\n): LengthComparison | undefined => {\n if (\n node.type !== AST_NODE_TYPES.BinaryExpression ||\n (node.operator !== '>=' && node.operator !== '<=')\n ) {\n return undefined;\n }\n\n const { left, right, operator } = node;\n\n const lengthOnLeft = isLengthAccess(left);\n\n const lengthSide = lengthOnLeft ? left : right;\n\n const boundSide = lengthOnLeft ? right : left;\n\n if (!isLengthAccess(lengthSide)) return undefined;\n\n if (!isIntegerLiteralOrConstant(boundSide, sourceCode)) return undefined;\n\n const kind: LengthComparison['kind'] = (\n lengthOnLeft ? operator === '>=' : operator === '<='\n )\n ? 'min'\n : 'max';\n\n return { array: lengthSide.object, bound: boundSide, kind };\n};\n\n/**\n * `true` when `node` is one operand of an `a && b` whose other operand is the\n * complementary length bound (`min` vs `max`) on the same array — i.e. the two\n * together form the `Arr.isBoundedLengthArray` pattern handled by\n * `prefer-arr-is-bounded-length-array`. The single-bound rules skip such\n * operands so the bounded rule rewrites the whole `&&` instead of the parts.\n */\nexport const isPartOfBoundedLengthCheck = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n node: TSESTree.BinaryExpression,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n sourceCode: TSESLint.SourceCode,\n): boolean => {\n const { parent } = node;\n\n if (\n parent.type !== AST_NODE_TYPES.LogicalExpression ||\n parent.operator !== '&&'\n ) {\n return false;\n }\n\n const self = parseLengthComparison(node, sourceCode);\n\n if (self === undefined) return false;\n\n const sibling = parent.left === node ? parent.right : parent.left;\n\n const other = parseLengthComparison(sibling, sourceCode);\n\n if (other === undefined) return false;\n\n return (\n self.kind !== other.kind &&\n sourceCode.getText(self.array) === sourceCode.getText(other.array)\n );\n};\n\ntype VariableDefinition = Readonly<{\n type: string;\n parent: TSESTree.VariableDeclaration;\n node: TSESTree.VariableDeclarator;\n}>;\n\nconst isVariableDefinition = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n def: TSESLint.Scope.Definition,\n): def is TSESLint.Scope.Definition & VariableDefinition =>\n (def.type as string) === 'Variable';\n\nconst findVariable = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n scope: TSESLint.Scope.Scope,\n name: string,\n): TSESLint.Scope.Variable | undefined => {\n const variable = scope.set.get(name);\n\n if (variable !== undefined) return variable;\n\n if (scope.upper === null) return undefined;\n\n return findVariable(scope.upper, name);\n};\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"branded-number-types.mjs","sources":["../../src/rules/branded-number-types.mts"],"sourcesContent":["// Keep this list in sync with the `as<Type>` cast functions exported by\n// ts-data-forge. `pnpm run check:branded-number-types` (also run during the\n// build) fails if a branded number type is missing here or no longer exists.\nconst brandedNumberTypes = [\n 'FiniteNumber',\n 'Int',\n 'Int16',\n 'Int32',\n 'Int8',\n 'NegativeFiniteNumber',\n 'NegativeInt',\n 'NegativeSafeInt',\n 'NonNegativeFiniteNumber',\n 'NonNegativeInt16',\n 'NonNegativeInt32',\n 'NonPositiveFiniteNumber',\n 'NonPositiveInt',\n 'NonPositiveSafeInt',\n 'NonZeroFiniteNumber',\n 'NonZeroInt',\n 'NonZeroInt16',\n 'NonZeroInt32',\n 'NonZeroSafeInt',\n 'NonZeroUint16',\n 'NonZeroUint32',\n 'PositiveFiniteNumber',\n 'PositiveInt',\n 'PositiveInt16',\n 'PositiveInt32',\n 'PositiveSafeInt',\n 'PositiveUint16',\n 'PositiveUint32',\n 'SafeInt',\n 'SafeUint',\n 'Uint',\n 'Uint16',\n 'Uint32',\n 'Uint8',\n] as const;\n\n/**\n * Branded number types from ts-data-forge and their corresponding as* functions\n */\nexport const brandedNumberTypeNameToFunctionName: ReadonlyMap<\n string,\n `as${string}`\n> = new Map(brandedNumberTypes.map((t) => [t, `as${t}`]));\n"],"names":[],"mappings":"AAGA,MAAM,kBAAA,GAAqB;AAAA,EACzB,cAAA;AAAA,EACA,KAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,sBAAA;AAAA,EACA,aAAA;AAAA,EACA,iBAAA;AAAA,EACA,yBAAA;AAAA,EACA,kBAAA;AAAA,EACA,kBAAA;AAAA,EACA,yBAAA;AAAA,EACA,gBAAA;AAAA,EACA,oBAAA;AAAA,EACA,qBAAA;AAAA,EACA,YAAA;AAAA,EACA,cAAA;AAAA,EACA,cAAA;AAAA,EACA,gBAAA;AAAA,EACA,eAAA;AAAA,EACA,eAAA;AAAA,EACA,sBAAA;AAAA,EACA,aAAA;AAAA,EACA,eAAA;AAAA,EACA,eAAA;AAAA,EACA,iBAAA;AAAA,EACA,gBAAA;AAAA,EACA,gBAAA;AAAA,EACA,SAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA;AAKO,MAAM,mCAAA,GAGT,IAAI,GAAA,CAAI,kBAAA,CAAmB,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,EAAG,CAAA,EAAA,EAAK,CAAC,CAAA,CAAE,CAAC,CAAC;;;;"}
1
+ {"version":3,"file":"branded-number-types.mjs","sources":["../../src/rules/branded-number-types.mts"],"sourcesContent":["// Keep this list in sync with the `as<Type>` cast functions exported by\n// ts-data-forge. `pnpm run check:branded-number-types` (also run during the\n// build) fails if a branded number type is missing here or no longer exists.\nconst brandedNumberTypes = [\n 'FiniteNumber',\n 'Int',\n 'Int16',\n 'Int32',\n 'Int8',\n 'NegativeFiniteNumber',\n 'NegativeInt',\n 'NegativeSafeInt',\n 'NonNegativeFiniteNumber',\n 'NonNegativeInt16',\n 'NonNegativeInt32',\n 'NonPositiveFiniteNumber',\n 'NonPositiveInt',\n 'NonPositiveSafeInt',\n 'NonZeroFiniteNumber',\n 'NonZeroInt',\n 'NonZeroInt16',\n 'NonZeroInt32',\n 'NonZeroSafeInt',\n 'NonZeroUint16',\n 'NonZeroUint32',\n 'PositiveFiniteNumber',\n 'PositiveInt',\n 'PositiveInt16',\n 'PositiveInt32',\n 'PositiveSafeInt',\n 'PositiveUint16',\n 'PositiveUint32',\n 'SafeInt',\n 'SafeUint',\n 'Uint',\n 'Uint16',\n 'Uint32',\n 'Uint8',\n] as const;\n\n/**\n * Branded number types from ts-data-forge and their corresponding as* functions\n */\nexport const brandedNumberTypeNameToFunctionName: ReadonlyMap<\n string,\n `as${string}`\n> = new Map(brandedNumberTypes.map((t) => [t, `as${t}`]));\n"],"names":[],"mappings":"AAGA,MAAM,kBAAA,GAAqB;AAAA,EACzB,cAAA;AAAA,EACA,KAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,sBAAA;AAAA,EACA,aAAA;AAAA,EACA,iBAAA;AAAA,EACA,yBAAA;AAAA,EACA,kBAAA;AAAA,EACA,kBAAA;AAAA,EACA,yBAAA;AAAA,EACA,gBAAA;AAAA,EACA,oBAAA;AAAA,EACA,qBAAA;AAAA,EACA,YAAA;AAAA,EACA,cAAA;AAAA,EACA,cAAA;AAAA,EACA,gBAAA;AAAA,EACA,eAAA;AAAA,EACA,eAAA;AAAA,EACA,sBAAA;AAAA,EACA,aAAA;AAAA,EACA,eAAA;AAAA,EACA,eAAA;AAAA,EACA,iBAAA;AAAA,EACA,gBAAA;AAAA,EACA,gBAAA;AAAA,EACA,SAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA;AAKO,MAAM,mCAAA,GAGT,IAAI,GAAA,CAAI,kBAAA,CAAmB,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,EAAG,CAAA,EAAA,EAAK,CAAC,CAAA,CAAE,CAAC,CAAC;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"constants.mjs","sources":["../../src/rules/constants.mts"],"sourcesContent":["export const TS_DATA_FORGE_MODULE = 'ts-data-forge';\n"],"names":[],"mappings":"AAAO,MAAM,oBAAA,GAAuB;;;;"}
1
+ {"version":3,"file":"constants.mjs","sources":["../../src/rules/constants.mts"],"sourcesContent":["export const TS_DATA_FORGE_MODULE = 'ts-data-forge';\n"],"names":[],"mappings":"AAAO,MAAM,oBAAA,GAAuB;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"import-utils.mjs","sources":["../../src/rules/import-utils.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { type DeepReadonly } from 'ts-type-forge';\nimport { TS_DATA_FORGE_MODULE } from './constants.mjs';\n\n/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */\n\nexport const getTsDataForgeImport = (\n program: TSESTree.Program,\n): TSESTree.ImportDeclaration | undefined =>\n program.body.find(\n (node): node is TSESTree.ImportDeclaration =>\n node.type === AST_NODE_TYPES.ImportDeclaration &&\n node.source.value === TS_DATA_FORGE_MODULE,\n );\n\nexport const getNamedImports = (\n node: DeepReadonly<TSESTree.ImportDeclaration> | undefined,\n): readonly string[] => {\n if (node === undefined) return [];\n\n return node.specifiers.flatMap((specifier) =>\n specifier.type === AST_NODE_TYPES.ImportSpecifier\n ? (() => {\n const importedName =\n specifier.imported.type === AST_NODE_TYPES.Identifier\n ? specifier.imported.name\n : specifier.imported.value;\n\n return typeof importedName === 'string' ? [importedName] : [];\n })()\n : [],\n );\n};\n\n/**\n * Returns the in-scope local name a given canonical export is imported as\n * (handling aliases such as `import { isX as y }` → `'y'`), or `undefined` when\n * it is not imported. Used so autofixes reference the binding that actually\n * exists rather than the canonical name.\n */\nexport const getImportedLocalName = (\n node: DeepReadonly<TSESTree.ImportDeclaration> | undefined,\n importedName: string,\n): string | undefined =>\n node?.specifiers.find(\n (specifier) =>\n specifier.type === AST_NODE_TYPES.ImportSpecifier &&\n (specifier.imported.type === AST_NODE_TYPES.Identifier\n ? specifier.imported.name\n : specifier.imported.value) === importedName,\n )?.local.name;\n\ntype ResolvedCallee = Readonly<{\n /** The canonical (imported) ts-data-forge name, regardless of local alias. */\n canonicalName: string;\n /** The identifier node to rewrite when replacing the callee name. */\n propertyNode: TSESTree.Node;\n /** Whether the call was made through a namespace import (`tf.isX(...)`). */\n isNamespace: boolean;\n}>;\n\n/**\n * Builds a resolver that maps a call expression's callee to the canonical\n * ts-data-forge function it refers to. Handles named imports (including aliases\n * such as `import { isX as y }`) and namespace imports (`import * as tf` →\n * `tf.isX(...)`). Returns `undefined` for any callee that does not reference the\n * given ts-data-forge import.\n */\nexport const buildCalleeResolver = (\n importDecl: TSESTree.ImportDeclaration | undefined,\n): ((\n callee: DeepReadonly<TSESTree.Expression>,\n) => ResolvedCallee | undefined) => {\n const specifiers = importDecl?.specifiers ?? [];\n\n const namespaceName = specifiers.find(\n (specifier) => specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier,\n )?.local.name;\n\n const localToCanonical = new Map<string, string>(\n specifiers.flatMap((specifier) => {\n if (specifier.type !== AST_NODE_TYPES.ImportSpecifier) return [];\n\n const importedName =\n specifier.imported.type === AST_NODE_TYPES.Identifier\n ? specifier.imported.name\n : specifier.imported.value;\n\n return typeof importedName === 'string'\n ? [[specifier.local.name, importedName] as const]\n : [];\n }),\n );\n\n return (callee) => {\n if (callee.type === AST_NODE_TYPES.Identifier) {\n const canonicalName = localToCanonical.get(callee.name);\n\n return canonicalName === undefined\n ? undefined\n : {\n canonicalName,\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n propertyNode: callee as TSESTree.Node,\n isNamespace: false,\n };\n }\n\n if (\n namespaceName !== undefined &&\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.object.type === AST_NODE_TYPES.Identifier &&\n callee.object.name === namespaceName &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ) {\n return {\n canonicalName: callee.property.name,\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n propertyNode: callee.property as TSESTree.Node,\n isNamespace: true,\n };\n }\n\n return undefined;\n };\n};\n\nexport const buildImportFixes = (\n fixer: TSESLint.RuleFixer,\n program: TSESTree.Program,\n _tsDataForgeImport: TSESTree.ImportDeclaration | undefined,\n requiredNames: readonly string[],\n): readonly TSESLint.RuleFix[] => {\n const specifierText = requiredNames.join(', ');\n\n const importStatement =\n `import { ${specifierText} } from '${TS_DATA_FORGE_MODULE}';` as const;\n\n const newLine = '\\n';\n\n const insertionText = `${importStatement}${newLine}` as const;\n\n // Always insert at the beginning of the file\n // (organize-imports will handle merging/deduplication)\n return [fixer.insertTextBefore(program, insertionText)];\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"import-utils.mjs","sources":["../../src/rules/import-utils.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { type DeepReadonly } from 'ts-type-forge';\nimport { TS_DATA_FORGE_MODULE } from './constants.mjs';\n\n/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */\n\nexport const getTsDataForgeImport = (\n program: TSESTree.Program,\n): TSESTree.ImportDeclaration | undefined =>\n program.body.find(\n (node): node is TSESTree.ImportDeclaration =>\n node.type === AST_NODE_TYPES.ImportDeclaration &&\n node.source.value === TS_DATA_FORGE_MODULE,\n );\n\nexport const getNamedImports = (\n node: DeepReadonly<TSESTree.ImportDeclaration> | undefined,\n): readonly string[] => {\n if (node === undefined) return [];\n\n return node.specifiers.flatMap((specifier) =>\n specifier.type === AST_NODE_TYPES.ImportSpecifier\n ? (() => {\n const importedName =\n specifier.imported.type === AST_NODE_TYPES.Identifier\n ? specifier.imported.name\n : specifier.imported.value;\n\n return typeof importedName === 'string' ? [importedName] : [];\n })()\n : [],\n );\n};\n\n/**\n * Returns the in-scope local name a given canonical export is imported as\n * (handling aliases such as `import { isX as y }` → `'y'`), or `undefined` when\n * it is not imported. Used so autofixes reference the binding that actually\n * exists rather than the canonical name.\n */\nexport const getImportedLocalName = (\n node: DeepReadonly<TSESTree.ImportDeclaration> | undefined,\n importedName: string,\n): string | undefined =>\n node?.specifiers.find(\n (specifier) =>\n specifier.type === AST_NODE_TYPES.ImportSpecifier &&\n (specifier.imported.type === AST_NODE_TYPES.Identifier\n ? specifier.imported.name\n : specifier.imported.value) === importedName,\n )?.local.name;\n\ntype ResolvedCallee = Readonly<{\n /** The canonical (imported) ts-data-forge name, regardless of local alias. */\n canonicalName: string;\n /** The identifier node to rewrite when replacing the callee name. */\n propertyNode: TSESTree.Node;\n /** Whether the call was made through a namespace import (`tf.isX(...)`). */\n isNamespace: boolean;\n}>;\n\n/**\n * Builds a resolver that maps a call expression's callee to the canonical\n * ts-data-forge function it refers to. Handles named imports (including aliases\n * such as `import { isX as y }`) and namespace imports (`import * as tf` →\n * `tf.isX(...)`). Returns `undefined` for any callee that does not reference the\n * given ts-data-forge import.\n */\nexport const buildCalleeResolver = (\n importDecl: TSESTree.ImportDeclaration | undefined,\n): ((\n callee: DeepReadonly<TSESTree.Expression>,\n) => ResolvedCallee | undefined) => {\n const specifiers = importDecl?.specifiers ?? [];\n\n const namespaceName = specifiers.find(\n (specifier) => specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier,\n )?.local.name;\n\n const localToCanonical = new Map<string, string>(\n specifiers.flatMap((specifier) => {\n if (specifier.type !== AST_NODE_TYPES.ImportSpecifier) return [];\n\n const importedName =\n specifier.imported.type === AST_NODE_TYPES.Identifier\n ? specifier.imported.name\n : specifier.imported.value;\n\n return typeof importedName === 'string'\n ? [[specifier.local.name, importedName] as const]\n : [];\n }),\n );\n\n return (callee) => {\n if (callee.type === AST_NODE_TYPES.Identifier) {\n const canonicalName = localToCanonical.get(callee.name);\n\n return canonicalName === undefined\n ? undefined\n : {\n canonicalName,\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n propertyNode: callee as TSESTree.Node,\n isNamespace: false,\n };\n }\n\n if (\n namespaceName !== undefined &&\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.object.type === AST_NODE_TYPES.Identifier &&\n callee.object.name === namespaceName &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ) {\n return {\n canonicalName: callee.property.name,\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n propertyNode: callee.property as TSESTree.Node,\n isNamespace: true,\n };\n }\n\n return undefined;\n };\n};\n\nexport const buildImportFixes = (\n fixer: TSESLint.RuleFixer,\n program: TSESTree.Program,\n _tsDataForgeImport: TSESTree.ImportDeclaration | undefined,\n requiredNames: readonly string[],\n): readonly TSESLint.RuleFix[] => {\n const specifierText = requiredNames.join(', ');\n\n const importStatement =\n `import { ${specifierText} } from '${TS_DATA_FORGE_MODULE}';` as const;\n\n const newLine = '\\n';\n\n const insertionText = `${importStatement}${newLine}` as const;\n\n // Always insert at the beginning of the file\n // (organize-imports will handle merging/deduplication)\n return [fixer.insertTextBefore(program, insertionText)];\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { type TSESLint } from '@typescript-eslint/utils';
2
+ type Options = readonly [];
3
+ type MessageIds = 'removeSideEffectImport';
4
+ /**
5
+ * `import 'ts-data-forge';` binds nothing and runs nothing: every export is a
6
+ * pure value or function, and the package declares `sideEffects: false`, which
7
+ * is a promise that evaluating it changes nothing observable. TypeScript keeps
8
+ * a side-effect import in the emitted JavaScript — that is the one kind it
9
+ * never elides — so what is left is a module loaded for no reason, and a
10
+ * bundler that trusts the flag drops it anyway.
11
+ *
12
+ * The rule matches the bare specifier exactly; an import of some other module
13
+ * is none of its business.
14
+ */
15
+ export declare const noSideEffectImport: TSESLint.RuleModule<MessageIds, Options>;
16
+ export {};
17
+ //# sourceMappingURL=no-side-effect-import.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-side-effect-import.d.mts","sourceRoot":"","sources":["../../src/rules/no-side-effect-import.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAiB,MAAM,0BAA0B,CAAC;AAIxE,KAAK,OAAO,GAAG,SAAS,EAAE,CAAC;AAE3B,KAAK,UAAU,GAAG,wBAAwB,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,UAAU,CAAC,UAAU,EAAE,OAAO,CA8B9D,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { Arr } from 'ts-data-forge';
2
+ import { TS_DATA_FORGE_MODULE } from './constants.mjs';
3
+
4
+ const noSideEffectImport = {
5
+ meta: {
6
+ type: "suggestion",
7
+ docs: {
8
+ description: `Remove a side-effect-only \`import '${TS_DATA_FORGE_MODULE}';\`, which binds no name and, the package being \`sideEffects: false\`, does nothing.`
9
+ },
10
+ fixable: "code",
11
+ schema: [],
12
+ messages: {
13
+ removeSideEffectImport: `\`import '${TS_DATA_FORGE_MODULE}';\` binds no name, and \`${TS_DATA_FORGE_MODULE}\` declares \`sideEffects: false\`, so loading it does nothing. Remove it, and import what you need by name.`
14
+ }
15
+ },
16
+ create: (context) => ({
17
+ ImportDeclaration: (node) => {
18
+ if (node.source.value !== TS_DATA_FORGE_MODULE || !Arr.isEmpty(node.specifiers)) {
19
+ return;
20
+ }
21
+ context.report({
22
+ node,
23
+ messageId: "removeSideEffectImport",
24
+ fix: (fixer) => removeStatement(fixer, context.sourceCode, node)
25
+ });
26
+ }
27
+ }),
28
+ defaultOptions: []
29
+ };
30
+ const removeStatement = (fixer, sourceCode, node) => {
31
+ const restOfLine = /^[^\S\n]*\n/u.exec(
32
+ sourceCode.getText().slice(node.range[1])
33
+ );
34
+ return fixer.removeRange([
35
+ node.range[0],
36
+ node.range[1] + (restOfLine?.[0].length ?? 0)
37
+ ]);
38
+ };
39
+
40
+ export { noSideEffectImport };
41
+ //# sourceMappingURL=no-side-effect-import.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-side-effect-import.mjs","sources":["../../src/rules/no-side-effect-import.mts"],"sourcesContent":["import { type TSESLint, type TSESTree } from '@typescript-eslint/utils';\nimport { Arr } from 'ts-data-forge';\nimport { TS_DATA_FORGE_MODULE } from './constants.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'removeSideEffectImport';\n\n/**\n * `import 'ts-data-forge';` binds nothing and runs nothing: every export is a\n * pure value or function, and the package declares `sideEffects: false`, which\n * is a promise that evaluating it changes nothing observable. TypeScript keeps\n * a side-effect import in the emitted JavaScript — that is the one kind it\n * never elides — so what is left is a module loaded for no reason, and a\n * bundler that trusts the flag drops it anyway.\n *\n * The rule matches the bare specifier exactly; an import of some other module\n * is none of its business.\n */\nexport const noSideEffectImport: TSESLint.RuleModule<MessageIds, Options> = {\n meta: {\n type: 'suggestion',\n docs: {\n description: `Remove a side-effect-only \\`import '${TS_DATA_FORGE_MODULE}';\\`, which binds no name and, the package being \\`sideEffects: false\\`, does nothing.`,\n },\n fixable: 'code',\n schema: [],\n messages: {\n removeSideEffectImport: `\\`import '${TS_DATA_FORGE_MODULE}';\\` binds no name, and \\`${TS_DATA_FORGE_MODULE}\\` declares \\`sideEffects: false\\`, so loading it does nothing. Remove it, and import what you need by name.`,\n },\n },\n\n create: (context) => ({\n ImportDeclaration: (node) => {\n if (\n node.source.value !== TS_DATA_FORGE_MODULE ||\n !Arr.isEmpty(node.specifiers)\n ) {\n return;\n }\n\n context.report({\n node,\n messageId: 'removeSideEffectImport',\n fix: (fixer) => removeStatement(fixer, context.sourceCode, node),\n });\n },\n }),\n defaultOptions: [],\n} as const;\n\n/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */\n\n/**\n * Removes a statement along with the rest of its line, so that deleting it does\n * not leave a blank line where it was.\n */\nconst removeStatement = (\n fixer: TSESLint.RuleFixer,\n sourceCode: TSESLint.SourceCode,\n node: TSESTree.Node,\n): TSESLint.RuleFix => {\n const restOfLine = /^[^\\S\\n]*\\n/u.exec(\n sourceCode.getText().slice(node.range[1]),\n );\n\n return fixer.removeRange([\n node.range[0],\n node.range[1] + (restOfLine?.[0].length ?? 0),\n ]);\n};\n"],"names":[],"mappings":";;;AAmBO,MAAM,kBAAA,GAA+D;AAAA,EAC1E,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,YAAA;AAAA,IACN,IAAA,EAAM;AAAA,MACJ,WAAA,EAAa,uCAAuC,oBAAoB,CAAA,sFAAA;AAAA,KAC1E;AAAA,IACA,OAAA,EAAS,MAAA;AAAA,IACT,QAAQ,EAAC;AAAA,IACT,QAAA,EAAU;AAAA,MACR,sBAAA,EAAwB,CAAA,UAAA,EAAa,oBAAoB,CAAA,0BAAA,EAA6B,oBAAoB,CAAA,4GAAA;AAAA;AAC5G,GACF;AAAA,EAEA,MAAA,EAAQ,CAAC,OAAA,MAAa;AAAA,IACpB,iBAAA,EAAmB,CAAC,IAAA,KAAS;AAC3B,MAAA,IACE,IAAA,CAAK,OAAO,KAAA,KAAU,oBAAA,IACtB,CAAC,GAAA,CAAI,OAAA,CAAQ,IAAA,CAAK,UAAU,CAAA,EAC5B;AACA,QAAA;AAAA,MACF;AAEA,MAAA,OAAA,CAAQ,MAAA,CAAO;AAAA,QACb,IAAA;AAAA,QACA,SAAA,EAAW,wBAAA;AAAA,QACX,KAAK,CAAC,KAAA,KAAU,gBAAgB,KAAA,EAAO,OAAA,CAAQ,YAAY,IAAI;AAAA,OAChE,CAAA;AAAA,IACH;AAAA,GACF,CAAA;AAAA,EACA,gBAAgB;AAClB;AAQA,MAAM,eAAA,GAAkB,CACtB,KAAA,EACA,UAAA,EACA,IAAA,KACqB;AACrB,EAAA,MAAM,aAAa,cAAA,CAAe,IAAA;AAAA,IAChC,WAAW,OAAA,EAAQ,CAAE,MAAM,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC;AAAA,GAC1C;AAEA,EAAA,OAAO,MAAM,WAAA,CAAY;AAAA,IACvB,IAAA,CAAK,MAAM,CAAC,CAAA;AAAA,IACZ,KAAK,KAAA,CAAM,CAAC,KAAK,UAAA,GAAa,CAAC,EAAE,MAAA,IAAU,CAAA;AAAA,GAC5C,CAAA;AACH,CAAA;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"no-unnecessary-type-guard.mjs","sources":["../../src/rules/no-unnecessary-type-guard.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n ESLintUtils,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { type DeepReadonly, type ReadonlyRecord } from 'ts-type-forge';\nimport * as ts from 'typescript';\nimport {\n buildCalleeResolver,\n buildImportFixes,\n getImportedLocalName,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [\n Readonly<{\n /** Names of ts-data-forge guard functions to skip checking. */\n ignore?: readonly string[];\n }>?,\n];\n\ntype MessageIds = 'alwaysTrue' | 'alwaysFalse' | 'replaceTypeGuard';\n\n/**\n * The primitive categories a (non-`null`/`undefined`) union member can fall\n * into. `other` covers anything the rule does not reason about (objects,\n * functions, `void`, …).\n */\ntype AtomKey =\n 'undefined' | 'null' | 'boolean' | 'number' | 'string' | 'bigint' | 'symbol';\n\ntype Atom = AtomKey | 'other';\n\ntype GuardSpec = DeepReadonly<\n | {\n kind: 'narrowTo';\n atoms: AtomKey[];\n replacements?: ReadonlyRecord<string, string>;\n }\n | {\n kind: 'excludeFrom';\n atoms: AtomKey[];\n replacements?: ReadonlyRecord<string, string>;\n }\n | { kind: 'nonEmptyString' }\n | { kind: 'record' }\n>;\n\n/**\n * Internal marker property attached to every `ts-type-forge` branded type. Its\n * presence (together with the `MinLength` brand key) lets us recognize a\n * `NonEmptyString` without holding a reference to the type itself.\n */\nconst BRAND_MARKER_PROPERTY =\n 'TSTypeForgeInternals--edd2f9ce-7ca5-45b0-9d1a-bd61b9b5d9c3';\n\n/**\n * Brand key carried by `MinLengthString<N>` — and therefore by\n * `NonEmptyString = MinLengthString<1>`. Its value is `MinLengthTuple<N, 0>`, a\n * tuple of `0`s with at least `N` elements, so a required first element means\n * `N >= 1` (the string cannot be empty).\n */\nconst MIN_LENGTH_BRAND_KEY = 'MinLength';\n\nconst NULLISH_REPLACEMENTS_EXCLUDE: ReadonlyRecord<string, string> = {\n undefined: 'isNotUndefined',\n null: 'isNotNull',\n} as const;\n\nconst NULLISH_REPLACEMENTS_NARROW: ReadonlyRecord<string, string> = {\n undefined: 'isUndefined',\n null: 'isNull',\n} as const;\n\nconst GUARD_SPECS: DeepReadonly<ReadonlyRecord<string, GuardSpec>> = {\n isUndefined: { kind: 'narrowTo', atoms: ['undefined'] },\n isNotUndefined: { kind: 'excludeFrom', atoms: ['undefined'] },\n isNull: { kind: 'narrowTo', atoms: ['null'] },\n isNotNull: { kind: 'excludeFrom', atoms: ['null'] },\n isBoolean: { kind: 'narrowTo', atoms: ['boolean'] },\n isNotBoolean: { kind: 'excludeFrom', atoms: ['boolean'] },\n isNumber: { kind: 'narrowTo', atoms: ['number'] },\n isNotNumber: { kind: 'excludeFrom', atoms: ['number'] },\n isString: { kind: 'narrowTo', atoms: ['string'] },\n isNotString: { kind: 'excludeFrom', atoms: ['string'] },\n isBigint: { kind: 'narrowTo', atoms: ['bigint'] },\n isNotBigint: { kind: 'excludeFrom', atoms: ['bigint'] },\n isSymbol: { kind: 'narrowTo', atoms: ['symbol'] },\n isNotSymbol: { kind: 'excludeFrom', atoms: ['symbol'] },\n isNullish: {\n kind: 'narrowTo',\n atoms: ['null', 'undefined'],\n replacements: NULLISH_REPLACEMENTS_NARROW,\n },\n isNonNullish: {\n kind: 'excludeFrom',\n atoms: ['null', 'undefined'],\n replacements: NULLISH_REPLACEMENTS_EXCLUDE,\n },\n isNonEmptyString: { kind: 'nonEmptyString' },\n isRecord: { kind: 'record' },\n} as const;\n\nconst DEFERRED_OR_OPAQUE_FLAGS =\n ts.TypeFlags.Any |\n ts.TypeFlags.Unknown |\n ts.TypeFlags.Never |\n ts.TypeFlags.TypeParameter |\n ts.TypeFlags.IndexedAccess |\n ts.TypeFlags.Conditional |\n ts.TypeFlags.Substitution;\n\nexport const noUnnecessaryTypeGuard: TSESLint.RuleModule<MessageIds, Options> =\n {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Detect ts-data-forge type guard calls that perform no narrowing (the argument type already satisfies, or can never satisfy, the guard).',\n },\n fixable: 'code',\n schema: [\n {\n type: 'object',\n properties: {\n ignore: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Names of ts-data-forge guard functions to skip checking.',\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n alwaysTrue:\n 'Unnecessary `{{name}}` call: the argument type guarantees it always returns `true`. Replace the call with `true`.',\n alwaysFalse:\n 'Unnecessary `{{name}}` call: the argument type guarantees it always returns `false`. Replace the call with `false`.',\n replaceTypeGuard:\n 'Unnecessary `{{from}}` call: the argument can only be narrowed by `{{to}}`. Use `{{to}}` instead.',\n },\n },\n\n create: (context) => {\n const options = context.options[0];\n\n const parserServices = ESLintUtils.getParserServices(context);\n\n const compilerOptions = parserServices.program.getCompilerOptions();\n\n // Without `strictNullChecks`, `null` / `undefined` are erased from the type\n // system, so reasoning about nullish narrowing is unsound. Disable entirely.\n const strictNullChecks =\n compilerOptions.strictNullChecks ?? compilerOptions.strict ?? false;\n\n if (!strictNullChecks) return {};\n\n const checker = parserServices.program.getTypeChecker();\n\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const ignored = new Set(options?.ignore);\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const resolveGuard = buildCalleeResolver(tsDataForgeImport);\n\n const buildReplaceFix =\n (\n isNamespace: boolean,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n propertyNode: TSESTree.Node,\n replacement: string,\n ): TSESLint.ReportFixFunction =>\n (fixer) => {\n // A namespace member access (`tf.isX`) exposes every export, so the\n // canonical name is always in scope.\n if (isNamespace) {\n return [fixer.replaceText(propertyNode, replacement)];\n }\n\n // If the replacement guard is already imported (possibly under an\n // alias), rename to its in-scope local name. Otherwise add a new\n // import and rename to the canonical name.\n const localName = getImportedLocalName(\n tsDataForgeImport,\n replacement,\n );\n\n if (localName !== undefined) {\n return [fixer.replaceText(propertyNode, localName)];\n }\n\n return [\n ...buildImportFixes(fixer, program, tsDataForgeImport, [\n replacement,\n ]),\n fixer.replaceText(propertyNode, replacement),\n ];\n };\n\n const reportConstant = (\n node: DeepReadonly<TSESTree.CallExpression>,\n name: string,\n value: boolean,\n argument: DeepReadonly<TSESTree.Expression>,\n ): void => {\n // Replacing the call with a literal drops the argument expression, so only\n // autofix when evaluating it cannot have observable side effects.\n const canFix = isSideEffectFreeArg(argument);\n\n context.report({\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n node: node as TSESTree.Node,\n messageId: value ? 'alwaysTrue' : 'alwaysFalse',\n data: { name },\n fix: canFix\n ? (fixer) =>\n fixer.replaceText(\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n node as TSESTree.Node,\n value ? 'true' : 'false',\n )\n : undefined,\n });\n };\n\n const reportReplace = (\n node: DeepReadonly<TSESTree.CallExpression>,\n from: string,\n to: string,\n isNamespace: boolean,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n propertyNode: TSESTree.Node,\n ): void => {\n context.report({\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n node: node as TSESTree.Node,\n messageId: 'replaceTypeGuard',\n data: { from, to },\n fix: buildReplaceFix(isNamespace, propertyNode, to),\n });\n };\n\n return {\n CallExpression: (node) => {\n if (node.arguments.length !== 1) return;\n\n const argument = node.arguments[0];\n\n if (\n argument === undefined ||\n argument.type === AST_NODE_TYPES.SpreadElement\n ) {\n return;\n }\n\n const resolved = resolveGuard(node.callee);\n\n if (resolved === undefined) return;\n\n const { canonicalName, propertyNode, isNamespace } = resolved;\n\n if (ignored.has(canonicalName)) return;\n\n const spec = GUARD_SPECS[canonicalName];\n\n if (spec === undefined) return;\n\n const argTsNode = parserServices.esTreeNodeToTSNodeMap.get(argument);\n\n const argType = checker.getTypeAtLocation(argTsNode);\n\n const parts = collectUnionParts(argType);\n\n if (parts === undefined) return; // opaque / generic → bail conservatively\n\n const inputAtoms = new Set(parts.map(classifyAtom));\n\n const hasMemberOutsideTarget = (\n atoms: readonly AtomKey[],\n ): boolean => {\n const targetSet = new Set<Atom>(atoms);\n\n return parts.some((p) => !targetSet.has(classifyAtom(p)));\n };\n\n switch (spec.kind) {\n case 'excludeFrom': {\n const removable = spec.atoms.filter((a) => inputAtoms.has(a));\n\n if (removable.length === 0) {\n // Nothing to exclude → the guard always holds.\n reportConstant(node, canonicalName, true, argument);\n } else if (!hasMemberOutsideTarget(spec.atoms)) {\n // Every member is excluded → the guard never holds.\n reportConstant(node, canonicalName, false, argument);\n } else if (removable.length < spec.atoms.length) {\n const replacement = spec.replacements?.[joinAtoms(removable)];\n\n if (replacement !== undefined) {\n reportReplace(\n node,\n canonicalName,\n replacement,\n isNamespace,\n propertyNode,\n );\n }\n }\n\n break;\n }\n\n case 'narrowTo': {\n const present = spec.atoms.filter((a) => inputAtoms.has(a));\n\n if (present.length === 0) {\n // No member is in the target set → the guard never holds.\n reportConstant(node, canonicalName, false, argument);\n } else if (!hasMemberOutsideTarget(spec.atoms)) {\n // Input ⊆ target → the guard always holds and narrows nothing.\n reportConstant(node, canonicalName, true, argument);\n } else if (present.length < spec.atoms.length) {\n const replacement = spec.replacements?.[joinAtoms(present)];\n\n if (replacement !== undefined) {\n reportReplace(\n node,\n canonicalName,\n replacement,\n isNamespace,\n propertyNode,\n );\n }\n }\n\n break;\n }\n\n case 'record': {\n if (parts.every((p) => isGuaranteedRecord(p, checker))) {\n // Every member already satisfies `UnknownRecord`.\n reportConstant(node, canonicalName, true, argument);\n } else if (\n parts.every((p) => isGuaranteedNotRecord(p, checker))\n ) {\n // No member can ever be a non-null, non-array object.\n reportConstant(node, canonicalName, false, argument);\n }\n\n break;\n }\n\n case 'nonEmptyString': {\n const hasNullish =\n inputAtoms.has('undefined') || inputAtoms.has('null');\n\n const nonNullishParts = parts.filter((p) => {\n const atom = classifyAtom(p);\n\n return atom !== 'undefined' && atom !== 'null';\n });\n\n if (!nonNullishParts.some(couldBeNonEmptyString)) {\n // No member can be a non-empty string → the guard never holds.\n reportConstant(node, canonicalName, false, argument);\n\n break;\n }\n\n if (\n nonNullishParts.some(\n (p) => !isGuaranteedNonEmptyString(p, checker),\n )\n ) {\n break; // still does real string/empty work\n }\n\n if (hasNullish) {\n reportReplace(\n node,\n canonicalName,\n 'isNonNullish',\n isNamespace,\n propertyNode,\n );\n } else {\n reportConstant(node, canonicalName, true, argument);\n }\n\n break;\n }\n }\n },\n };\n },\n defaultOptions: [{}],\n } as const;\n\n/**\n * Decomposes a type into its union members. Returns `undefined` when any member\n * is opaque (`any` / `unknown` / `never`) or deferred (type parameter, indexed\n * access, …), signalling that the rule should not draw conclusions.\n */\n// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\nconst collectUnionParts = (type: ts.Type): readonly ts.Type[] | undefined => {\n const parts = type.isUnion() ? type.types : ([type] as const);\n\n for (const part of parts) {\n if ((part.flags & DEFERRED_OR_OPAQUE_FLAGS) !== 0) return undefined;\n }\n\n return parts;\n};\n\n/**\n * Whether `isRecord` is guaranteed to accept this (non-union) type.\n *\n * `isRecord` narrows to `UnknownRecord` (`ReadonlyRecord<string, unknown>`), so\n * a string index signature is exactly what it asks for and its value type is\n * `unknown`, which anything satisfies. Arrays and callables are excluded even\n * when they carry one, because `isRecord` rejects them at runtime\n * (`Array.isArray` is true; `typeof fn === 'object'` is false).\n *\n * Deliberately conservative: an object type without an index signature of its\n * own is reported as neither guaranteed-accepted nor guaranteed-rejected, even\n * where TypeScript would accept it through an implicit index signature.\n */\nconst isGuaranteedRecord = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n type: ts.Type,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n checker: ts.TypeChecker,\n): boolean =>\n checker.getIndexInfoOfType(type, ts.IndexKind.String) !== undefined &&\n !isCallableType(type, checker) &&\n !checker.isArrayType(type) &&\n !checker.isTupleType(type);\n\n/**\n * Whether `isRecord` is guaranteed to reject this (non-union) type: every\n * primitive (`null` included), every array or tuple, and every callable fails\n * its `typeof x === 'object' && x !== null && !Array.isArray(x)` check.\n */\nconst isGuaranteedNotRecord = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n type: ts.Type,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n checker: ts.TypeChecker,\n): boolean =>\n classifyAtom(type) !== 'other' ||\n checker.isArrayType(type) ||\n checker.isTupleType(type) ||\n isCallableType(type, checker);\n\nconst isCallableType = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n type: ts.Type,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n checker: ts.TypeChecker,\n): boolean =>\n checker.getSignaturesOfType(type, ts.SignatureKind.Call).length > 0 ||\n checker.getSignaturesOfType(type, ts.SignatureKind.Construct).length > 0;\n\n/** Classifies a single (non-union) type into a primitive {@link Atom}. */\n// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\nconst classifyAtom = (type: ts.Type): Atom => {\n const { flags } = type;\n\n if ((flags & ts.TypeFlags.Undefined) !== 0) return 'undefined';\n\n if ((flags & ts.TypeFlags.Null) !== 0) return 'null';\n\n if ((flags & ts.TypeFlags.BooleanLike) !== 0) return 'boolean';\n\n if ((flags & ts.TypeFlags.NumberLike) !== 0) return 'number';\n\n if ((flags & ts.TypeFlags.StringLike) !== 0) return 'string';\n\n if ((flags & ts.TypeFlags.BigIntLike) !== 0) return 'bigint';\n\n if ((flags & ts.TypeFlags.ESSymbolLike) !== 0) return 'symbol';\n\n // Branded primitives (e.g. `NonEmptyString = string & {...}`) are\n // intersections; classify them by their underlying primitive constituent.\n if (type.isIntersection()) {\n for (const constituent of type.types) {\n const atom = classifyAtom(constituent);\n\n if (atom !== 'other') return atom;\n }\n }\n\n return 'other';\n};\n\nconst ATOM_ORDER: readonly AtomKey[] = [\n 'undefined',\n 'null',\n 'boolean',\n 'number',\n 'string',\n 'bigint',\n 'symbol',\n] as const;\n\n/** A stable key for a set of atoms, used to look up replacement guards. */\nconst joinAtoms = (atoms: readonly AtomKey[]): string =>\n ATOM_ORDER.filter((atom) => atoms.includes(atom)).join('|');\n\n/**\n * Returns `true` when the type is guaranteed to be a non-empty string: either a\n * non-empty string literal or a value branded as `NonEmptyString`\n * (`MinLengthString<N>` with `N >= 1`).\n */\nconst isGuaranteedNonEmptyString = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n type: ts.Type,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n checker: ts.TypeChecker,\n): boolean => {\n if (type.isStringLiteral()) return type.value !== '';\n\n if (type.getProperty(BRAND_MARKER_PROPERTY) === undefined) return false;\n\n const minLengthSymbol = type.getProperty(MIN_LENGTH_BRAND_KEY);\n\n if (minLengthSymbol === undefined) return false;\n\n // The `MinLength` brand value is `MinLengthTuple<N, 0>`; it has a required\n // first element (`'0'`) exactly when `N >= 1`, i.e. the string is non-empty.\n // `MinLengthString<0>` brands with a plain array and has no such element.\n return (\n checker.getTypeOfSymbol(minLengthSymbol).getProperty('0') !== undefined\n );\n};\n\n/**\n * Returns `true` when the type can possibly be a non-empty string (a general\n * `string`, a branded string, or a non-empty string literal). The empty string\n * literal and non-string types cannot.\n */\n// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\nconst couldBeNonEmptyString = (type: ts.Type): boolean => {\n if (type.isStringLiteral()) return type.value !== '';\n\n return classifyAtom(type) === 'string';\n};\n\n/**\n * Returns `true` when evaluating the expression cannot have observable side\n * effects, so replacing the surrounding call with a literal is safe. Limited to\n * simple references (identifiers, `this`, literals, and member-access chains).\n */\n\nconst isSideEffectFreeArg = (node: DeepReadonly<TSESTree.Node>): boolean => {\n // `node.type` is one of 130+ AST kinds; only a handful are relevant here.\n // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check\n switch (node.type) {\n case AST_NODE_TYPES.Identifier:\n case AST_NODE_TYPES.ThisExpression:\n case AST_NODE_TYPES.Literal:\n return true;\n\n case AST_NODE_TYPES.MemberExpression:\n return (\n isSideEffectFreeArg(node.object) &&\n (!node.computed || isSideEffectFreeArg(node.property))\n );\n\n default:\n return false;\n }\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"no-unnecessary-type-guard.mjs","sources":["../../src/rules/no-unnecessary-type-guard.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n ESLintUtils,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { type DeepReadonly, type ReadonlyRecord } from 'ts-type-forge';\nimport * as ts from 'typescript';\nimport {\n buildCalleeResolver,\n buildImportFixes,\n getImportedLocalName,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [\n Readonly<{\n /** Names of ts-data-forge guard functions to skip checking. */\n ignore?: readonly string[];\n }>?,\n];\n\ntype MessageIds = 'alwaysTrue' | 'alwaysFalse' | 'replaceTypeGuard';\n\n/**\n * The primitive categories a (non-`null`/`undefined`) union member can fall\n * into. `other` covers anything the rule does not reason about (objects,\n * functions, `void`, …).\n */\ntype AtomKey =\n 'undefined' | 'null' | 'boolean' | 'number' | 'string' | 'bigint' | 'symbol';\n\ntype Atom = AtomKey | 'other';\n\ntype GuardSpec = DeepReadonly<\n | {\n kind: 'narrowTo';\n atoms: AtomKey[];\n replacements?: ReadonlyRecord<string, string>;\n }\n | {\n kind: 'excludeFrom';\n atoms: AtomKey[];\n replacements?: ReadonlyRecord<string, string>;\n }\n | { kind: 'nonEmptyString' }\n | { kind: 'record' }\n>;\n\n/**\n * Internal marker property attached to every `ts-type-forge` branded type. Its\n * presence (together with the `MinLength` brand key) lets us recognize a\n * `NonEmptyString` without holding a reference to the type itself.\n */\nconst BRAND_MARKER_PROPERTY =\n 'TSTypeForgeInternals--edd2f9ce-7ca5-45b0-9d1a-bd61b9b5d9c3';\n\n/**\n * Brand key carried by `MinLengthString<N>` — and therefore by\n * `NonEmptyString = MinLengthString<1>`. Its value is `MinLengthTuple<N, 0>`, a\n * tuple of `0`s with at least `N` elements, so a required first element means\n * `N >= 1` (the string cannot be empty).\n */\nconst MIN_LENGTH_BRAND_KEY = 'MinLength';\n\nconst NULLISH_REPLACEMENTS_EXCLUDE: ReadonlyRecord<string, string> = {\n undefined: 'isNotUndefined',\n null: 'isNotNull',\n} as const;\n\nconst NULLISH_REPLACEMENTS_NARROW: ReadonlyRecord<string, string> = {\n undefined: 'isUndefined',\n null: 'isNull',\n} as const;\n\nconst GUARD_SPECS: DeepReadonly<ReadonlyRecord<string, GuardSpec>> = {\n isUndefined: { kind: 'narrowTo', atoms: ['undefined'] },\n isNotUndefined: { kind: 'excludeFrom', atoms: ['undefined'] },\n isNull: { kind: 'narrowTo', atoms: ['null'] },\n isNotNull: { kind: 'excludeFrom', atoms: ['null'] },\n isBoolean: { kind: 'narrowTo', atoms: ['boolean'] },\n isNotBoolean: { kind: 'excludeFrom', atoms: ['boolean'] },\n isNumber: { kind: 'narrowTo', atoms: ['number'] },\n isNotNumber: { kind: 'excludeFrom', atoms: ['number'] },\n isString: { kind: 'narrowTo', atoms: ['string'] },\n isNotString: { kind: 'excludeFrom', atoms: ['string'] },\n isBigint: { kind: 'narrowTo', atoms: ['bigint'] },\n isNotBigint: { kind: 'excludeFrom', atoms: ['bigint'] },\n isSymbol: { kind: 'narrowTo', atoms: ['symbol'] },\n isNotSymbol: { kind: 'excludeFrom', atoms: ['symbol'] },\n isNullish: {\n kind: 'narrowTo',\n atoms: ['null', 'undefined'],\n replacements: NULLISH_REPLACEMENTS_NARROW,\n },\n isNonNullish: {\n kind: 'excludeFrom',\n atoms: ['null', 'undefined'],\n replacements: NULLISH_REPLACEMENTS_EXCLUDE,\n },\n isNonEmptyString: { kind: 'nonEmptyString' },\n isRecord: { kind: 'record' },\n} as const;\n\nconst DEFERRED_OR_OPAQUE_FLAGS =\n ts.TypeFlags.Any |\n ts.TypeFlags.Unknown |\n ts.TypeFlags.Never |\n ts.TypeFlags.TypeParameter |\n ts.TypeFlags.IndexedAccess |\n ts.TypeFlags.Conditional |\n ts.TypeFlags.Substitution;\n\nexport const noUnnecessaryTypeGuard: TSESLint.RuleModule<MessageIds, Options> =\n {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Detect ts-data-forge type guard calls that perform no narrowing (the argument type already satisfies, or can never satisfy, the guard).',\n },\n fixable: 'code',\n schema: [\n {\n type: 'object',\n properties: {\n ignore: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Names of ts-data-forge guard functions to skip checking.',\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n alwaysTrue:\n 'Unnecessary `{{name}}` call: the argument type guarantees it always returns `true`. Replace the call with `true`.',\n alwaysFalse:\n 'Unnecessary `{{name}}` call: the argument type guarantees it always returns `false`. Replace the call with `false`.',\n replaceTypeGuard:\n 'Unnecessary `{{from}}` call: the argument can only be narrowed by `{{to}}`. Use `{{to}}` instead.',\n },\n },\n\n create: (context) => {\n const options = context.options[0];\n\n const parserServices = ESLintUtils.getParserServices(context);\n\n const compilerOptions = parserServices.program.getCompilerOptions();\n\n // Without `strictNullChecks`, `null` / `undefined` are erased from the type\n // system, so reasoning about nullish narrowing is unsound. Disable entirely.\n const strictNullChecks =\n compilerOptions.strictNullChecks ?? compilerOptions.strict ?? false;\n\n if (!strictNullChecks) return {};\n\n const checker = parserServices.program.getTypeChecker();\n\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const ignored = new Set(options?.ignore);\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const resolveGuard = buildCalleeResolver(tsDataForgeImport);\n\n const buildReplaceFix =\n (\n isNamespace: boolean,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n propertyNode: TSESTree.Node,\n replacement: string,\n ): TSESLint.ReportFixFunction =>\n (fixer) => {\n // A namespace member access (`tf.isX`) exposes every export, so the\n // canonical name is always in scope.\n if (isNamespace) {\n return [fixer.replaceText(propertyNode, replacement)];\n }\n\n // If the replacement guard is already imported (possibly under an\n // alias), rename to its in-scope local name. Otherwise add a new\n // import and rename to the canonical name.\n const localName = getImportedLocalName(\n tsDataForgeImport,\n replacement,\n );\n\n if (localName !== undefined) {\n return [fixer.replaceText(propertyNode, localName)];\n }\n\n return [\n ...buildImportFixes(fixer, program, tsDataForgeImport, [\n replacement,\n ]),\n fixer.replaceText(propertyNode, replacement),\n ];\n };\n\n const reportConstant = (\n node: DeepReadonly<TSESTree.CallExpression>,\n name: string,\n value: boolean,\n argument: DeepReadonly<TSESTree.Expression>,\n ): void => {\n // Replacing the call with a literal drops the argument expression, so only\n // autofix when evaluating it cannot have observable side effects.\n const canFix = isSideEffectFreeArg(argument);\n\n context.report({\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n node: node as TSESTree.Node,\n messageId: value ? 'alwaysTrue' : 'alwaysFalse',\n data: { name },\n fix: canFix\n ? (fixer) =>\n fixer.replaceText(\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n node as TSESTree.Node,\n value ? 'true' : 'false',\n )\n : undefined,\n });\n };\n\n const reportReplace = (\n node: DeepReadonly<TSESTree.CallExpression>,\n from: string,\n to: string,\n isNamespace: boolean,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n propertyNode: TSESTree.Node,\n ): void => {\n context.report({\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n node: node as TSESTree.Node,\n messageId: 'replaceTypeGuard',\n data: { from, to },\n fix: buildReplaceFix(isNamespace, propertyNode, to),\n });\n };\n\n return {\n CallExpression: (node) => {\n if (node.arguments.length !== 1) return;\n\n const argument = node.arguments[0];\n\n if (\n argument === undefined ||\n argument.type === AST_NODE_TYPES.SpreadElement\n ) {\n return;\n }\n\n const resolved = resolveGuard(node.callee);\n\n if (resolved === undefined) return;\n\n const { canonicalName, propertyNode, isNamespace } = resolved;\n\n if (ignored.has(canonicalName)) return;\n\n const spec = GUARD_SPECS[canonicalName];\n\n if (spec === undefined) return;\n\n const argTsNode = parserServices.esTreeNodeToTSNodeMap.get(argument);\n\n const argType = checker.getTypeAtLocation(argTsNode);\n\n const parts = collectUnionParts(argType);\n\n if (parts === undefined) return; // opaque / generic → bail conservatively\n\n const inputAtoms = new Set(parts.map(classifyAtom));\n\n const hasMemberOutsideTarget = (\n atoms: readonly AtomKey[],\n ): boolean => {\n const targetSet = new Set<Atom>(atoms);\n\n return parts.some((p) => !targetSet.has(classifyAtom(p)));\n };\n\n switch (spec.kind) {\n case 'excludeFrom': {\n const removable = spec.atoms.filter((a) => inputAtoms.has(a));\n\n if (removable.length === 0) {\n // Nothing to exclude → the guard always holds.\n reportConstant(node, canonicalName, true, argument);\n } else if (!hasMemberOutsideTarget(spec.atoms)) {\n // Every member is excluded → the guard never holds.\n reportConstant(node, canonicalName, false, argument);\n } else if (removable.length < spec.atoms.length) {\n const replacement = spec.replacements?.[joinAtoms(removable)];\n\n if (replacement !== undefined) {\n reportReplace(\n node,\n canonicalName,\n replacement,\n isNamespace,\n propertyNode,\n );\n }\n }\n\n break;\n }\n\n case 'narrowTo': {\n const present = spec.atoms.filter((a) => inputAtoms.has(a));\n\n if (present.length === 0) {\n // No member is in the target set → the guard never holds.\n reportConstant(node, canonicalName, false, argument);\n } else if (!hasMemberOutsideTarget(spec.atoms)) {\n // Input ⊆ target → the guard always holds and narrows nothing.\n reportConstant(node, canonicalName, true, argument);\n } else if (present.length < spec.atoms.length) {\n const replacement = spec.replacements?.[joinAtoms(present)];\n\n if (replacement !== undefined) {\n reportReplace(\n node,\n canonicalName,\n replacement,\n isNamespace,\n propertyNode,\n );\n }\n }\n\n break;\n }\n\n case 'record': {\n if (parts.every((p) => isGuaranteedRecord(p, checker))) {\n // Every member already satisfies `UnknownRecord`.\n reportConstant(node, canonicalName, true, argument);\n } else if (\n parts.every((p) => isGuaranteedNotRecord(p, checker))\n ) {\n // No member can ever be a non-null, non-array object.\n reportConstant(node, canonicalName, false, argument);\n }\n\n break;\n }\n\n case 'nonEmptyString': {\n const hasNullish =\n inputAtoms.has('undefined') || inputAtoms.has('null');\n\n const nonNullishParts = parts.filter((p) => {\n const atom = classifyAtom(p);\n\n return atom !== 'undefined' && atom !== 'null';\n });\n\n if (!nonNullishParts.some(couldBeNonEmptyString)) {\n // No member can be a non-empty string → the guard never holds.\n reportConstant(node, canonicalName, false, argument);\n\n break;\n }\n\n if (\n nonNullishParts.some(\n (p) => !isGuaranteedNonEmptyString(p, checker),\n )\n ) {\n break; // still does real string/empty work\n }\n\n if (hasNullish) {\n reportReplace(\n node,\n canonicalName,\n 'isNonNullish',\n isNamespace,\n propertyNode,\n );\n } else {\n reportConstant(node, canonicalName, true, argument);\n }\n\n break;\n }\n }\n },\n };\n },\n defaultOptions: [{}],\n } as const;\n\n/**\n * Decomposes a type into its union members. Returns `undefined` when any member\n * is opaque (`any` / `unknown` / `never`) or deferred (type parameter, indexed\n * access, …), signalling that the rule should not draw conclusions.\n */\n// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\nconst collectUnionParts = (type: ts.Type): readonly ts.Type[] | undefined => {\n const parts = type.isUnion() ? type.types : ([type] as const);\n\n for (const part of parts) {\n if ((part.flags & DEFERRED_OR_OPAQUE_FLAGS) !== 0) return undefined;\n }\n\n return parts;\n};\n\n/**\n * Whether `isRecord` is guaranteed to accept this (non-union) type.\n *\n * `isRecord` narrows to `UnknownRecord` (`ReadonlyRecord<string, unknown>`), so\n * a string index signature is exactly what it asks for and its value type is\n * `unknown`, which anything satisfies. Arrays and callables are excluded even\n * when they carry one, because `isRecord` rejects them at runtime\n * (`Array.isArray` is true; `typeof fn === 'object'` is false).\n *\n * Deliberately conservative: an object type without an index signature of its\n * own is reported as neither guaranteed-accepted nor guaranteed-rejected, even\n * where TypeScript would accept it through an implicit index signature.\n */\nconst isGuaranteedRecord = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n type: ts.Type,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n checker: ts.TypeChecker,\n): boolean =>\n checker.getIndexInfoOfType(type, ts.IndexKind.String) !== undefined &&\n !isCallableType(type, checker) &&\n !checker.isArrayType(type) &&\n !checker.isTupleType(type);\n\n/**\n * Whether `isRecord` is guaranteed to reject this (non-union) type: every\n * primitive (`null` included), every array or tuple, and every callable fails\n * its `typeof x === 'object' && x !== null && !Array.isArray(x)` check.\n */\nconst isGuaranteedNotRecord = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n type: ts.Type,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n checker: ts.TypeChecker,\n): boolean =>\n classifyAtom(type) !== 'other' ||\n checker.isArrayType(type) ||\n checker.isTupleType(type) ||\n isCallableType(type, checker);\n\nconst isCallableType = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n type: ts.Type,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n checker: ts.TypeChecker,\n): boolean =>\n checker.getSignaturesOfType(type, ts.SignatureKind.Call).length > 0 ||\n checker.getSignaturesOfType(type, ts.SignatureKind.Construct).length > 0;\n\n/** Classifies a single (non-union) type into a primitive {@link Atom}. */\n// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\nconst classifyAtom = (type: ts.Type): Atom => {\n const { flags } = type;\n\n if ((flags & ts.TypeFlags.Undefined) !== 0) return 'undefined';\n\n if ((flags & ts.TypeFlags.Null) !== 0) return 'null';\n\n if ((flags & ts.TypeFlags.BooleanLike) !== 0) return 'boolean';\n\n if ((flags & ts.TypeFlags.NumberLike) !== 0) return 'number';\n\n if ((flags & ts.TypeFlags.StringLike) !== 0) return 'string';\n\n if ((flags & ts.TypeFlags.BigIntLike) !== 0) return 'bigint';\n\n if ((flags & ts.TypeFlags.ESSymbolLike) !== 0) return 'symbol';\n\n // Branded primitives (e.g. `NonEmptyString = string & {...}`) are\n // intersections; classify them by their underlying primitive constituent.\n if (type.isIntersection()) {\n for (const constituent of type.types) {\n const atom = classifyAtom(constituent);\n\n if (atom !== 'other') return atom;\n }\n }\n\n return 'other';\n};\n\nconst ATOM_ORDER: readonly AtomKey[] = [\n 'undefined',\n 'null',\n 'boolean',\n 'number',\n 'string',\n 'bigint',\n 'symbol',\n] as const;\n\n/** A stable key for a set of atoms, used to look up replacement guards. */\nconst joinAtoms = (atoms: readonly AtomKey[]): string =>\n ATOM_ORDER.filter((atom) => atoms.includes(atom)).join('|');\n\n/**\n * Returns `true` when the type is guaranteed to be a non-empty string: either a\n * non-empty string literal or a value branded as `NonEmptyString`\n * (`MinLengthString<N>` with `N >= 1`).\n */\nconst isGuaranteedNonEmptyString = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n type: ts.Type,\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n checker: ts.TypeChecker,\n): boolean => {\n if (type.isStringLiteral()) return type.value !== '';\n\n if (type.getProperty(BRAND_MARKER_PROPERTY) === undefined) return false;\n\n const minLengthSymbol = type.getProperty(MIN_LENGTH_BRAND_KEY);\n\n if (minLengthSymbol === undefined) return false;\n\n // The `MinLength` brand value is `MinLengthTuple<N, 0>`; it has a required\n // first element (`'0'`) exactly when `N >= 1`, i.e. the string is non-empty.\n // `MinLengthString<0>` brands with a plain array and has no such element.\n return (\n checker.getTypeOfSymbol(minLengthSymbol).getProperty('0') !== undefined\n );\n};\n\n/**\n * Returns `true` when the type can possibly be a non-empty string (a general\n * `string`, a branded string, or a non-empty string literal). The empty string\n * literal and non-string types cannot.\n */\n// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\nconst couldBeNonEmptyString = (type: ts.Type): boolean => {\n if (type.isStringLiteral()) return type.value !== '';\n\n return classifyAtom(type) === 'string';\n};\n\n/**\n * Returns `true` when evaluating the expression cannot have observable side\n * effects, so replacing the surrounding call with a literal is safe. Limited to\n * simple references (identifiers, `this`, literals, and member-access chains).\n */\n\nconst isSideEffectFreeArg = (node: DeepReadonly<TSESTree.Node>): boolean => {\n // `node.type` is one of 130+ AST kinds; only a handful are relevant here.\n // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check\n switch (node.type) {\n case AST_NODE_TYPES.Identifier:\n case AST_NODE_TYPES.ThisExpression:\n case AST_NODE_TYPES.Literal:\n return true;\n\n case AST_NODE_TYPES.MemberExpression:\n return (\n isSideEffectFreeArg(node.object) &&\n (!node.computed || isSideEffectFreeArg(node.property))\n );\n\n default:\n return false;\n }\n};\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-arr-is-array.mjs","sources":["../../src/rules/prefer-arr-is-array.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { type DeepReadonly } from 'ts-type-forge';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useArrIsArray';\n\nexport const preferArrIsArray: TSESLint.RuleModule<MessageIds, Options> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `Array.isArray` with `Arr.isArray` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useArrIsArray:\n 'Replace `Array.isArray` with `Arr.isArray` from ts-data-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const mut_nodesToFix: TSESTree.CallExpression[] = [];\n\n return {\n CallExpression: (node) => {\n if (!isArrayIsArrayCall(node)) return;\n\n mut_nodesToFix.push(node);\n },\n 'Program:exit': () => {\n // Check if Arr is already imported\n const hasArrImport = getNamedImports(tsDataForgeImport).includes('Arr');\n\n // Note: We add import only for the first node to avoid conflicts when\n // multiple fixes try to insert at the same position. This means if the\n // first node is disabled via eslint-disable comment, no import will be\n // added.\n for (const [index, node] of mut_nodesToFix.entries()) {\n context.report({\n node,\n messageId: 'useArrIsArray',\n fix: (fixer) => {\n const callee = node.callee;\n\n if (callee.type !== AST_NODE_TYPES.MemberExpression) return [];\n\n const replacement = `Arr.isArray${sourceCode.getText(node).slice(sourceCode.getText(callee).length)}`;\n\n // Add import only for the first node and only if not already imported\n const importFixes =\n index === 0 && !hasArrImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n\nconst isArrayIsArrayCall = (\n node: DeepReadonly<TSESTree.CallExpression>,\n): boolean => {\n if (node.callee.type !== AST_NODE_TYPES.MemberExpression) return false;\n\n const { object, property } = node.callee;\n\n if (object.type !== AST_NODE_TYPES.Identifier) return false;\n\n if (object.name !== 'Array') return false;\n\n if (property.type !== AST_NODE_TYPES.Identifier) return false;\n\n if (property.name !== 'isArray') return false;\n\n return true;\n};\n"],"names":[],"mappings":";;;AAgBO,MAAM,gBAAA,GAA6D;AAAA,EACxE,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,YAAA;AAAA,IACN,IAAA,EAAM;AAAA,MACJ,WAAA,EACE;AAAA,KACJ;AAAA,IACA,OAAA,EAAS,MAAA;AAAA,IACT,QAAQ,EAAC;AAAA,IACT,QAAA,EAAU;AAAA,MACR,aAAA,EACE;AAAA;AACJ,GACF;AAAA,EAEA,MAAA,EAAQ,CAAC,OAAA,KAAY;AACnB,IAAA,MAAM,aAAa,OAAA,CAAQ,UAAA;AAE3B,IAAA,MAAM,UAAU,UAAA,CAAW,GAAA;AAE3B,IAAA,MAAM,iBAAA,GAAoB,qBAAqB,OAAO,CAAA;AAEtD,IAAA,MAAM,iBAA4C,EAAC;AAEnD,IAAA,OAAO;AAAA,MACL,cAAA,EAAgB,CAAC,IAAA,KAAS;AACxB,QAAA,IAAI,CAAC,kBAAA,CAAmB,IAAI,CAAA,EAAG;AAE/B,QAAA,cAAA,CAAe,KAAK,IAAI,CAAA;AAAA,MAC1B,CAAA;AAAA,MACA,gBAAgB,MAAM;AAEpB,QAAA,MAAM,YAAA,GAAe,eAAA,CAAgB,iBAAiB,CAAA,CAAE,SAAS,KAAK,CAAA;AAMtE,QAAA,KAAA,MAAW,CAAC,KAAA,EAAO,IAAI,CAAA,IAAK,cAAA,CAAe,SAAQ,EAAG;AACpD,UAAA,OAAA,CAAQ,MAAA,CAAO;AAAA,YACb,IAAA;AAAA,YACA,SAAA,EAAW,eAAA;AAAA,YACX,GAAA,EAAK,CAAC,KAAA,KAAU;AACd,cAAA,MAAM,SAAS,IAAA,CAAK,MAAA;AAEpB,cAAA,IAAI,MAAA,CAAO,IAAA,KAAS,cAAA,CAAe,gBAAA,SAAyB,EAAC;AAE7D,cAAA,MAAM,WAAA,GAAc,CAAA,WAAA,EAAc,UAAA,CAAW,OAAA,CAAQ,IAAI,CAAA,CAAE,KAAA,CAAM,UAAA,CAAW,OAAA,CAAQ,MAAM,CAAA,CAAE,MAAM,CAAC,CAAA,CAAA;AAGnG,cAAA,MAAM,WAAA,GACJ,KAAA,KAAU,CAAA,IAAK,CAAC,YAAA,GACZ,gBAAA,CAAiB,KAAA,EAAO,OAAA,EAAS,iBAAA,EAAmB,CAAC,KAAK,CAAC,IAC3D,EAAC;AAEP,cAAA,OAAO,CAAC,GAAG,WAAA,EAAa,MAAM,WAAA,CAAY,IAAA,EAAM,WAAW,CAAC,CAAA;AAAA,YAC9D;AAAA,WACD,CAAA;AAAA,QACH;AAAA,MACF;AAAA,KACF;AAAA,EACF,CAAA;AAAA,EACA,gBAAgB;AAClB;AAEA,MAAM,kBAAA,GAAqB,CACzB,IAAA,KACY;AACZ,EAAA,IAAI,IAAA,CAAK,MAAA,CAAO,IAAA,KAAS,cAAA,CAAe,kBAAkB,OAAO,KAAA;AAEjE,EAAA,MAAM,EAAE,MAAA,EAAQ,QAAA,EAAS,GAAI,IAAA,CAAK,MAAA;AAElC,EAAA,IAAI,MAAA,CAAO,IAAA,KAAS,cAAA,CAAe,UAAA,EAAY,OAAO,KAAA;AAEtD,EAAA,IAAI,MAAA,CAAO,IAAA,KAAS,OAAA,EAAS,OAAO,KAAA;AAEpC,EAAA,IAAI,QAAA,CAAS,IAAA,KAAS,cAAA,CAAe,UAAA,EAAY,OAAO,KAAA;AAExD,EAAA,IAAI,QAAA,CAAS,IAAA,KAAS,SAAA,EAAW,OAAO,KAAA;AAExC,EAAA,OAAO,IAAA;AACT,CAAA;;;;"}
1
+ {"version":3,"file":"prefer-arr-is-array.mjs","sources":["../../src/rules/prefer-arr-is-array.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { type DeepReadonly } from 'ts-type-forge';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useArrIsArray';\n\nexport const preferArrIsArray: TSESLint.RuleModule<MessageIds, Options> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `Array.isArray` with `Arr.isArray` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useArrIsArray:\n 'Replace `Array.isArray` with `Arr.isArray` from ts-data-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const mut_nodesToFix: TSESTree.CallExpression[] = [];\n\n return {\n CallExpression: (node) => {\n if (!isArrayIsArrayCall(node)) return;\n\n mut_nodesToFix.push(node);\n },\n 'Program:exit': () => {\n // Check if Arr is already imported\n const hasArrImport = getNamedImports(tsDataForgeImport).includes('Arr');\n\n // Note: We add import only for the first node to avoid conflicts when\n // multiple fixes try to insert at the same position. This means if the\n // first node is disabled via eslint-disable comment, no import will be\n // added.\n for (const [index, node] of mut_nodesToFix.entries()) {\n context.report({\n node,\n messageId: 'useArrIsArray',\n fix: (fixer) => {\n const callee = node.callee;\n\n if (callee.type !== AST_NODE_TYPES.MemberExpression) return [];\n\n const replacement = `Arr.isArray${sourceCode.getText(node).slice(sourceCode.getText(callee).length)}`;\n\n // Add import only for the first node and only if not already imported\n const importFixes =\n index === 0 && !hasArrImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n\nconst isArrayIsArrayCall = (\n node: DeepReadonly<TSESTree.CallExpression>,\n): boolean => {\n if (node.callee.type !== AST_NODE_TYPES.MemberExpression) return false;\n\n const { object, property } = node.callee;\n\n if (object.type !== AST_NODE_TYPES.Identifier) return false;\n\n if (object.name !== 'Array') return false;\n\n if (property.type !== AST_NODE_TYPES.Identifier) return false;\n\n if (property.name !== 'isArray') return false;\n\n return true;\n};\n"],"names":[],"mappings":";;;AAgBO,MAAM,gBAAA,GAA6D;AAAA,EACxE,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,YAAA;AAAA,IACN,IAAA,EAAM;AAAA,MACJ,WAAA,EACE;AAAA,KACJ;AAAA,IACA,OAAA,EAAS,MAAA;AAAA,IACT,QAAQ,EAAC;AAAA,IACT,QAAA,EAAU;AAAA,MACR,aAAA,EACE;AAAA;AACJ,GACF;AAAA,EAEA,MAAA,EAAQ,CAAC,OAAA,KAAY;AACnB,IAAA,MAAM,aAAa,OAAA,CAAQ,UAAA;AAE3B,IAAA,MAAM,UAAU,UAAA,CAAW,GAAA;AAE3B,IAAA,MAAM,iBAAA,GAAoB,qBAAqB,OAAO,CAAA;AAEtD,IAAA,MAAM,iBAA4C,EAAC;AAEnD,IAAA,OAAO;AAAA,MACL,cAAA,EAAgB,CAAC,IAAA,KAAS;AACxB,QAAA,IAAI,CAAC,kBAAA,CAAmB,IAAI,CAAA,EAAG;AAE/B,QAAA,cAAA,CAAe,KAAK,IAAI,CAAA;AAAA,MAC1B,CAAA;AAAA,MACA,gBAAgB,MAAM;AAEpB,QAAA,MAAM,YAAA,GAAe,eAAA,CAAgB,iBAAiB,CAAA,CAAE,SAAS,KAAK,CAAA;AAMtE,QAAA,KAAA,MAAW,CAAC,KAAA,EAAO,IAAI,CAAA,IAAK,cAAA,CAAe,SAAQ,EAAG;AACpD,UAAA,OAAA,CAAQ,MAAA,CAAO;AAAA,YACb,IAAA;AAAA,YACA,SAAA,EAAW,eAAA;AAAA,YACX,GAAA,EAAK,CAAC,KAAA,KAAU;AACd,cAAA,MAAM,SAAS,IAAA,CAAK,MAAA;AAEpB,cAAA,IAAI,MAAA,CAAO,IAAA,KAAS,cAAA,CAAe,gBAAA,SAAyB,EAAC;AAE7D,cAAA,MAAM,WAAA,GAAc,CAAA,WAAA,EAAc,UAAA,CAAW,OAAA,CAAQ,IAAI,CAAA,CAAE,KAAA,CAAM,UAAA,CAAW,OAAA,CAAQ,MAAM,CAAA,CAAE,MAAM,CAAC,CAAA,CAAA;AAGnG,cAAA,MAAM,WAAA,GACJ,KAAA,KAAU,CAAA,IAAK,CAAC,YAAA,GACZ,gBAAA,CAAiB,KAAA,EAAO,OAAA,EAAS,iBAAA,EAAmB,CAAC,KAAK,CAAC,IAC3D,EAAC;AAEP,cAAA,OAAO,CAAC,GAAG,WAAA,EAAa,MAAM,WAAA,CAAY,IAAA,EAAM,WAAW,CAAC,CAAA;AAAA,YAC9D;AAAA,WACD,CAAA;AAAA,QACH;AAAA,MACF;AAAA,KACF;AAAA,EACF,CAAA;AAAA,EACA,gBAAgB;AAClB;AAEA,MAAM,kBAAA,GAAqB,CACzB,IAAA,KACY;AACZ,EAAA,IAAI,IAAA,CAAK,MAAA,CAAO,IAAA,KAAS,cAAA,CAAe,kBAAkB,OAAO,KAAA;AAEjE,EAAA,MAAM,EAAE,MAAA,EAAQ,QAAA,EAAS,GAAI,IAAA,CAAK,MAAA;AAElC,EAAA,IAAI,MAAA,CAAO,IAAA,KAAS,cAAA,CAAe,UAAA,EAAY,OAAO,KAAA;AAEtD,EAAA,IAAI,MAAA,CAAO,IAAA,KAAS,OAAA,EAAS,OAAO,KAAA;AAEpC,EAAA,IAAI,QAAA,CAAS,IAAA,KAAS,cAAA,CAAe,UAAA,EAAY,OAAO,KAAA;AAExD,EAAA,IAAI,QAAA,CAAS,IAAA,KAAS,SAAA,EAAW,OAAO,KAAA;AAExC,EAAA,OAAO,IAAA;AACT,CAAA;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-arr-is-bounded-length-array.mjs","sources":["../../src/rules/prefer-arr-is-bounded-length-array.mts"],"sourcesContent":["import { type TSESLint, type TSESTree } from '@typescript-eslint/utils';\nimport { parseLengthComparison } from './ast-utils.mjs';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useIsBoundedLengthArray';\n\nexport const preferArrIsBoundedLengthArray: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `xs.length >= min && xs.length <= max` with `Arr.isBoundedLengthArray(xs, min, max)` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useIsBoundedLengthArray:\n 'Replace `{{original}}` with `Arr.isBoundedLengthArray({{arrayName}}, {{min}}, {{max}})` from ts-data-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const services = context.sourceCode.parserServices;\n\n const mut_nodesToFix: {\n node: TSESTree.LogicalExpression;\n arrayExpression: TSESTree.Expression;\n minExpression: TSESTree.Expression;\n maxExpression: TSESTree.Expression;\n }[] = [];\n\n return {\n LogicalExpression: (node) => {\n // Only `a && b` can encode a `min && max` length range.\n if (node.operator !== '&&') return;\n\n const left = parseLengthComparison(node.left, sourceCode);\n\n const right = parseLengthComparison(node.right, sourceCode);\n\n if (left === undefined || right === undefined) return;\n\n // Need one lower bound and one upper bound on the same array.\n if (left.kind === right.kind) return;\n\n if (\n sourceCode.getText(left.array) !== sourceCode.getText(right.array)\n ) {\n return;\n }\n\n const min = left.kind === 'min' ? left : right;\n\n const max = left.kind === 'min' ? right : left;\n\n const arrayExpression = min.array;\n\n // Check if arrayExpression is actually an array type\n if (services?.program !== undefined && services.program !== null) {\n const checker = services.program.getTypeChecker();\n\n const tsNode = services.esTreeNodeToTSNodeMap?.get(arrayExpression);\n\n if (tsNode !== undefined) {\n const type = checker.getTypeAtLocation(tsNode);\n\n // Check if it's an array type or tuple type\n const isArrayType =\n checker.isArrayType(type) || checker.isTupleType(type);\n\n if (!isArrayType) return;\n } else {\n return;\n }\n } else {\n return;\n }\n\n mut_nodesToFix.push({\n node,\n arrayExpression,\n minExpression: min.bound,\n maxExpression: max.bound,\n });\n },\n 'Program:exit': () => {\n const namedImports = getNamedImports(tsDataForgeImport);\n\n const hasArrImport = namedImports.includes('Arr');\n\n for (const [\n index,\n { node, arrayExpression, minExpression, maxExpression },\n ] of mut_nodesToFix.entries()) {\n const arrayText = sourceCode.getText(arrayExpression);\n\n const minText = sourceCode.getText(minExpression);\n\n const maxText = sourceCode.getText(maxExpression);\n\n const originalText = sourceCode.getText(node);\n\n context.report({\n node,\n messageId: 'useIsBoundedLengthArray',\n data: {\n original: originalText,\n arrayName: arrayText,\n min: minText,\n max: maxText,\n },\n fix: (fixer) => {\n const replacement = `Arr.isBoundedLengthArray(${minText}, ${maxText}, ${arrayText})`;\n\n const importFixes =\n index === 0 && !hasArrImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prefer-arr-is-bounded-length-array.mjs","sources":["../../src/rules/prefer-arr-is-bounded-length-array.mts"],"sourcesContent":["import { type TSESLint, type TSESTree } from '@typescript-eslint/utils';\nimport { parseLengthComparison } from './ast-utils.mjs';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useIsBoundedLengthArray';\n\nexport const preferArrIsBoundedLengthArray: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `xs.length >= min && xs.length <= max` with `Arr.isBoundedLengthArray(xs, min, max)` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useIsBoundedLengthArray:\n 'Replace `{{original}}` with `Arr.isBoundedLengthArray({{arrayName}}, {{min}}, {{max}})` from ts-data-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const services = context.sourceCode.parserServices;\n\n const mut_nodesToFix: {\n node: TSESTree.LogicalExpression;\n arrayExpression: TSESTree.Expression;\n minExpression: TSESTree.Expression;\n maxExpression: TSESTree.Expression;\n }[] = [];\n\n return {\n LogicalExpression: (node) => {\n // Only `a && b` can encode a `min && max` length range.\n if (node.operator !== '&&') return;\n\n const left = parseLengthComparison(node.left, sourceCode);\n\n const right = parseLengthComparison(node.right, sourceCode);\n\n if (left === undefined || right === undefined) return;\n\n // Need one lower bound and one upper bound on the same array.\n if (left.kind === right.kind) return;\n\n if (\n sourceCode.getText(left.array) !== sourceCode.getText(right.array)\n ) {\n return;\n }\n\n const min = left.kind === 'min' ? left : right;\n\n const max = left.kind === 'min' ? right : left;\n\n const arrayExpression = min.array;\n\n // Check if arrayExpression is actually an array type\n if (services?.program !== undefined && services.program !== null) {\n const checker = services.program.getTypeChecker();\n\n const tsNode = services.esTreeNodeToTSNodeMap?.get(arrayExpression);\n\n if (tsNode !== undefined) {\n const type = checker.getTypeAtLocation(tsNode);\n\n // Check if it's an array type or tuple type\n const isArrayType =\n checker.isArrayType(type) || checker.isTupleType(type);\n\n if (!isArrayType) return;\n } else {\n return;\n }\n } else {\n return;\n }\n\n mut_nodesToFix.push({\n node,\n arrayExpression,\n minExpression: min.bound,\n maxExpression: max.bound,\n });\n },\n 'Program:exit': () => {\n const namedImports = getNamedImports(tsDataForgeImport);\n\n const hasArrImport = namedImports.includes('Arr');\n\n for (const [\n index,\n { node, arrayExpression, minExpression, maxExpression },\n ] of mut_nodesToFix.entries()) {\n const arrayText = sourceCode.getText(arrayExpression);\n\n const minText = sourceCode.getText(minExpression);\n\n const maxText = sourceCode.getText(maxExpression);\n\n const originalText = sourceCode.getText(node);\n\n context.report({\n node,\n messageId: 'useIsBoundedLengthArray',\n data: {\n original: originalText,\n arrayName: arrayText,\n min: minText,\n max: maxText,\n },\n fix: (fixer) => {\n const replacement = `Arr.isBoundedLengthArray(${minText}, ${maxText}, ${arrayText})`;\n\n const importFixes =\n index === 0 && !hasArrImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-arr-is-fixed-length-array.mjs","sources":["../../src/rules/prefer-arr-is-fixed-length-array.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { type DeepReadonly } from 'ts-type-forge';\nimport { isIntegerLiteralOrConstant } from './ast-utils.mjs';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useIsFixedLengthArray';\n\nexport const preferArrIsFixedLengthArray: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `xs.length === n` with `Arr.isFixedLengthArray(xs, n)` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useIsFixedLengthArray:\n 'Replace `{{original}}` with `Arr.isFixedLengthArray({{arrayName}}, {{length}})` from ts-data-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const services = context.sourceCode.parserServices;\n\n const mut_nodesToFix: {\n node: TSESTree.BinaryExpression;\n arrayExpression: TSESTree.Expression;\n lengthExpression: TSESTree.Expression;\n isNegated: boolean;\n }[] = [];\n\n return {\n BinaryExpression: (node) => {\n // Check for `xs.length === n` or `n === xs.length` or `xs.length !== n`\n if (node.operator !== '===' && node.operator !== '!==') return;\n\n const isNegated = node.operator === '!==';\n\n const isLengthOnLeft = isLengthAccess(node.left);\n\n const lengthSide = node[isLengthOnLeft ? 'left' : 'right'];\n\n const valueSide = node[isLengthOnLeft ? 'right' : 'left'];\n\n if (!isLengthAccess(lengthSide)) return;\n\n // Only match integer literals or const variables initialized with integer literals\n if (!isIntegerLiteralOrConstant(valueSide, sourceCode)) return;\n\n // lengthSide is MemberExpression accessing .length\n const arrayExpression = lengthSide.object;\n\n // Check if arrayExpression is actually an array type\n if (services?.program !== undefined && services.program !== null) {\n const checker = services.program.getTypeChecker();\n\n const tsNode = services.esTreeNodeToTSNodeMap?.get(arrayExpression);\n\n if (tsNode !== undefined) {\n const type = checker.getTypeAtLocation(tsNode);\n\n // Check if it's an array type or tuple type\n const isArrayType =\n checker.isArrayType(type) || checker.isTupleType(type);\n\n if (!isArrayType) return;\n } else {\n return;\n }\n } else {\n return;\n }\n\n mut_nodesToFix.push({\n node,\n arrayExpression,\n lengthExpression: valueSide,\n isNegated,\n });\n },\n 'Program:exit': () => {\n const namedImports = getNamedImports(tsDataForgeImport);\n\n const hasArrImport = namedImports.includes('Arr');\n\n for (const [\n index,\n { node, arrayExpression, lengthExpression, isNegated },\n ] of mut_nodesToFix.entries()) {\n const arrayText = sourceCode.getText(arrayExpression);\n\n const lengthText = sourceCode.getText(lengthExpression);\n\n const originalText = sourceCode.getText(node);\n\n context.report({\n node,\n messageId: 'useIsFixedLengthArray',\n data: {\n original: originalText,\n arrayName: arrayText,\n length: lengthText,\n },\n fix: (fixer) => {\n const baseCall = `Arr.isFixedLengthArray(${lengthText}, ${arrayText})`;\n\n const replacement = isNegated ? `!${baseCall}` : baseCall;\n\n const importFixes =\n index === 0 && !hasArrImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n\nconst isLengthAccess = (\n node: DeepReadonly<TSESTree.Expression>,\n): node is TSESTree.MemberExpression =>\n node.type === AST_NODE_TYPES.MemberExpression &&\n node.property.type === AST_NODE_TYPES.Identifier &&\n node.property.name === 'length';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prefer-arr-is-fixed-length-array.mjs","sources":["../../src/rules/prefer-arr-is-fixed-length-array.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { type DeepReadonly } from 'ts-type-forge';\nimport { isIntegerLiteralOrConstant } from './ast-utils.mjs';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useIsFixedLengthArray';\n\nexport const preferArrIsFixedLengthArray: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `xs.length === n` with `Arr.isFixedLengthArray(xs, n)` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useIsFixedLengthArray:\n 'Replace `{{original}}` with `Arr.isFixedLengthArray({{arrayName}}, {{length}})` from ts-data-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const services = context.sourceCode.parserServices;\n\n const mut_nodesToFix: {\n node: TSESTree.BinaryExpression;\n arrayExpression: TSESTree.Expression;\n lengthExpression: TSESTree.Expression;\n isNegated: boolean;\n }[] = [];\n\n return {\n BinaryExpression: (node) => {\n // Check for `xs.length === n` or `n === xs.length` or `xs.length !== n`\n if (node.operator !== '===' && node.operator !== '!==') return;\n\n const isNegated = node.operator === '!==';\n\n const isLengthOnLeft = isLengthAccess(node.left);\n\n const lengthSide = node[isLengthOnLeft ? 'left' : 'right'];\n\n const valueSide = node[isLengthOnLeft ? 'right' : 'left'];\n\n if (!isLengthAccess(lengthSide)) return;\n\n // Only match integer literals or const variables initialized with integer literals\n if (!isIntegerLiteralOrConstant(valueSide, sourceCode)) return;\n\n // lengthSide is MemberExpression accessing .length\n const arrayExpression = lengthSide.object;\n\n // Check if arrayExpression is actually an array type\n if (services?.program !== undefined && services.program !== null) {\n const checker = services.program.getTypeChecker();\n\n const tsNode = services.esTreeNodeToTSNodeMap?.get(arrayExpression);\n\n if (tsNode !== undefined) {\n const type = checker.getTypeAtLocation(tsNode);\n\n // Check if it's an array type or tuple type\n const isArrayType =\n checker.isArrayType(type) || checker.isTupleType(type);\n\n if (!isArrayType) return;\n } else {\n return;\n }\n } else {\n return;\n }\n\n mut_nodesToFix.push({\n node,\n arrayExpression,\n lengthExpression: valueSide,\n isNegated,\n });\n },\n 'Program:exit': () => {\n const namedImports = getNamedImports(tsDataForgeImport);\n\n const hasArrImport = namedImports.includes('Arr');\n\n for (const [\n index,\n { node, arrayExpression, lengthExpression, isNegated },\n ] of mut_nodesToFix.entries()) {\n const arrayText = sourceCode.getText(arrayExpression);\n\n const lengthText = sourceCode.getText(lengthExpression);\n\n const originalText = sourceCode.getText(node);\n\n context.report({\n node,\n messageId: 'useIsFixedLengthArray',\n data: {\n original: originalText,\n arrayName: arrayText,\n length: lengthText,\n },\n fix: (fixer) => {\n const baseCall = `Arr.isFixedLengthArray(${lengthText}, ${arrayText})`;\n\n const replacement = isNegated ? `!${baseCall}` : baseCall;\n\n const importFixes =\n index === 0 && !hasArrImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n\nconst isLengthAccess = (\n node: DeepReadonly<TSESTree.Expression>,\n): node is TSESTree.MemberExpression =>\n node.type === AST_NODE_TYPES.MemberExpression &&\n node.property.type === AST_NODE_TYPES.Identifier &&\n node.property.name === 'length';\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-arr-is-max-length-array.mjs","sources":["../../src/rules/prefer-arr-is-max-length-array.mts"],"sourcesContent":["import { type TSESLint, type TSESTree } from '@typescript-eslint/utils';\nimport {\n isIntegerLiteralOrConstant,\n isLengthAccess,\n isPartOfBoundedLengthCheck,\n} from './ast-utils.mjs';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useIsMaxLengthArray';\n\nexport const preferArrIsMaxLengthArray: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `xs.length <= n` with `Arr.isMaxLengthArray(xs, n)` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useIsMaxLengthArray:\n 'Replace `{{original}}` with `Arr.isMaxLengthArray({{arrayName}}, {{length}})` from ts-data-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const services = context.sourceCode.parserServices;\n\n const mut_nodesToFix: {\n node: TSESTree.BinaryExpression;\n arrayExpression: TSESTree.Expression;\n lengthExpression: TSESTree.Expression;\n }[] = [];\n\n return {\n BinaryExpression: (node) => {\n // Check for `xs.length <= n` or `n >= xs.length`\n if (node.operator !== '>=' && node.operator !== '<=') return;\n\n // Defer to `prefer-arr-is-bounded-length-array` when this comparison is\n // the upper bound of a `min && max` pair on the same array.\n if (isPartOfBoundedLengthCheck(node, sourceCode)) return;\n\n // xs.length <= n or n >= xs.length\n const isLengthOnLeft = node.operator === '<=';\n\n const lengthSide = node[isLengthOnLeft ? 'left' : 'right'];\n\n const valueSide = node[isLengthOnLeft ? 'right' : 'left'];\n\n // Check if lengthSide is accessing .length\n if (!isLengthAccess(lengthSide)) return;\n\n // Only match integer literals or const variables initialized with integer literals\n if (!isIntegerLiteralOrConstant(valueSide, sourceCode)) return;\n\n // lengthSide is MemberExpression accessing .length\n const arrayExpression = lengthSide.object;\n\n // Check if arrayExpression is actually an array type\n if (services?.program !== undefined && services.program !== null) {\n const checker = services.program.getTypeChecker();\n\n const tsNode = services.esTreeNodeToTSNodeMap?.get(arrayExpression);\n\n if (tsNode !== undefined) {\n const type = checker.getTypeAtLocation(tsNode);\n\n // Check if it's an array type or tuple type\n const isArrayType =\n checker.isArrayType(type) || checker.isTupleType(type);\n\n if (!isArrayType) return;\n } else {\n return;\n }\n } else {\n return;\n }\n\n mut_nodesToFix.push({\n node,\n arrayExpression,\n lengthExpression: valueSide,\n });\n },\n 'Program:exit': () => {\n const namedImports = getNamedImports(tsDataForgeImport);\n\n const hasArrImport = namedImports.includes('Arr');\n\n for (const [\n index,\n { node, arrayExpression, lengthExpression },\n ] of mut_nodesToFix.entries()) {\n const arrayText = sourceCode.getText(arrayExpression);\n\n const lengthText = sourceCode.getText(lengthExpression);\n\n const originalText = sourceCode.getText(node);\n\n context.report({\n node,\n messageId: 'useIsMaxLengthArray',\n data: {\n original: originalText,\n arrayName: arrayText,\n length: lengthText,\n },\n fix: (fixer) => {\n const replacement = `Arr.isMaxLengthArray(${lengthText}, ${arrayText})`;\n\n const importFixes =\n index === 0 && !hasArrImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prefer-arr-is-max-length-array.mjs","sources":["../../src/rules/prefer-arr-is-max-length-array.mts"],"sourcesContent":["import { type TSESLint, type TSESTree } from '@typescript-eslint/utils';\nimport {\n isIntegerLiteralOrConstant,\n isLengthAccess,\n isPartOfBoundedLengthCheck,\n} from './ast-utils.mjs';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useIsMaxLengthArray';\n\nexport const preferArrIsMaxLengthArray: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `xs.length <= n` with `Arr.isMaxLengthArray(xs, n)` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useIsMaxLengthArray:\n 'Replace `{{original}}` with `Arr.isMaxLengthArray({{arrayName}}, {{length}})` from ts-data-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const services = context.sourceCode.parserServices;\n\n const mut_nodesToFix: {\n node: TSESTree.BinaryExpression;\n arrayExpression: TSESTree.Expression;\n lengthExpression: TSESTree.Expression;\n }[] = [];\n\n return {\n BinaryExpression: (node) => {\n // Check for `xs.length <= n` or `n >= xs.length`\n if (node.operator !== '>=' && node.operator !== '<=') return;\n\n // Defer to `prefer-arr-is-bounded-length-array` when this comparison is\n // the upper bound of a `min && max` pair on the same array.\n if (isPartOfBoundedLengthCheck(node, sourceCode)) return;\n\n // xs.length <= n or n >= xs.length\n const isLengthOnLeft = node.operator === '<=';\n\n const lengthSide = node[isLengthOnLeft ? 'left' : 'right'];\n\n const valueSide = node[isLengthOnLeft ? 'right' : 'left'];\n\n // Check if lengthSide is accessing .length\n if (!isLengthAccess(lengthSide)) return;\n\n // Only match integer literals or const variables initialized with integer literals\n if (!isIntegerLiteralOrConstant(valueSide, sourceCode)) return;\n\n // lengthSide is MemberExpression accessing .length\n const arrayExpression = lengthSide.object;\n\n // Check if arrayExpression is actually an array type\n if (services?.program !== undefined && services.program !== null) {\n const checker = services.program.getTypeChecker();\n\n const tsNode = services.esTreeNodeToTSNodeMap?.get(arrayExpression);\n\n if (tsNode !== undefined) {\n const type = checker.getTypeAtLocation(tsNode);\n\n // Check if it's an array type or tuple type\n const isArrayType =\n checker.isArrayType(type) || checker.isTupleType(type);\n\n if (!isArrayType) return;\n } else {\n return;\n }\n } else {\n return;\n }\n\n mut_nodesToFix.push({\n node,\n arrayExpression,\n lengthExpression: valueSide,\n });\n },\n 'Program:exit': () => {\n const namedImports = getNamedImports(tsDataForgeImport);\n\n const hasArrImport = namedImports.includes('Arr');\n\n for (const [\n index,\n { node, arrayExpression, lengthExpression },\n ] of mut_nodesToFix.entries()) {\n const arrayText = sourceCode.getText(arrayExpression);\n\n const lengthText = sourceCode.getText(lengthExpression);\n\n const originalText = sourceCode.getText(node);\n\n context.report({\n node,\n messageId: 'useIsMaxLengthArray',\n data: {\n original: originalText,\n arrayName: arrayText,\n length: lengthText,\n },\n fix: (fixer) => {\n const replacement = `Arr.isMaxLengthArray(${lengthText}, ${arrayText})`;\n\n const importFixes =\n index === 0 && !hasArrImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n"],"names":[],"mappings":"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"}