eslint-plugin-ts-data-forge 0.1.7 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -19
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/plugin.d.mts +46 -1
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +35 -1
- package/dist/plugin.mjs.map +1 -1
- package/dist/rule-types.d.mts +6 -51
- package/dist/rule-types.d.mts.map +1 -1
- package/dist/rules/import-utils.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-bounded-length-array.mjs +1 -1
- package/dist/rules/prefer-arr-is-bounded-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-fixed-length-array.mjs +1 -1
- package/dist/rules/prefer-arr-is-fixed-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-max-length-array.mjs +1 -1
- package/dist/rules/prefer-arr-is-max-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-min-length-array.mjs +1 -1
- package/dist/rules/prefer-arr-is-min-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-sum.mjs +2 -2
- package/dist/rules/prefer-arr-sum.mjs.map +1 -1
- package/dist/rules/prefer-canonical-array-slicing.mjs.map +1 -1
- package/dist/rules/prefer-canonical-length-cast.d.mts +6 -0
- package/dist/rules/prefer-canonical-length-cast.d.mts.map +1 -0
- package/dist/rules/prefer-canonical-length-cast.mjs +155 -0
- package/dist/rules/prefer-canonical-length-cast.mjs.map +1 -0
- package/dist/rules/prefer-canonical-length-guard.d.mts +6 -0
- package/dist/rules/prefer-canonical-length-guard.d.mts.map +1 -0
- package/dist/rules/prefer-canonical-length-guard.mjs +129 -0
- package/dist/rules/prefer-canonical-length-guard.mjs.map +1 -0
- package/dist/rules/prefer-range-for-loop.mjs +1 -1
- package/dist/rules/prefer-range-for-loop.mjs.map +1 -1
- package/dist/rules/rules.d.mts +2 -5
- package/dist/rules/rules.d.mts.map +1 -1
- package/dist/rules/rules.mjs +4 -10
- package/dist/rules/rules.mjs.map +1 -1
- package/dist/types.d.mts +1 -0
- package/dist/types.d.mts.map +1 -1
- package/package.json +7 -3
- package/src/index.mts +1 -1
- package/src/plugin.mts +47 -3
- package/src/plugin.test.mts +24 -0
- package/src/rule-types.mts +6 -54
- package/src/rules/import-utils.mts +0 -1
- package/src/rules/prefer-arr-is-bounded-length-array.mts +1 -1
- package/src/rules/prefer-arr-is-bounded-length-array.test.mts +5 -5
- package/src/rules/prefer-arr-is-fixed-length-array.mts +1 -1
- package/src/rules/prefer-arr-is-fixed-length-array.test.mts +11 -11
- package/src/rules/prefer-arr-is-max-length-array.mts +1 -1
- package/src/rules/prefer-arr-is-max-length-array.test.mts +8 -8
- package/src/rules/prefer-arr-is-min-length-array.mts +1 -1
- package/src/rules/prefer-arr-is-min-length-array.test.mts +9 -9
- package/src/rules/prefer-arr-sum.mts +2 -2
- package/src/rules/prefer-canonical-array-slicing.mts +0 -2
- package/src/rules/prefer-canonical-length-cast.mts +290 -0
- package/src/rules/prefer-canonical-length-cast.test.mts +305 -0
- package/src/rules/prefer-canonical-length-guard.mts +250 -0
- package/src/rules/prefer-canonical-length-guard.test.mts +307 -0
- package/src/rules/prefer-range-for-loop.mts +1 -1
- package/src/rules/rules.mts +4 -10
- package/src/types.mts +2 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefer-canonical-length-guard.mjs","sources":["../../src/rules/prefer-canonical-length-guard.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { getImportedLocalName, getTsDataForgeImport } from './import-utils.mjs';\nimport { preferArrIsBoundedLengthArray } from './prefer-arr-is-bounded-length-array.mjs';\nimport { preferArrIsFixedLengthArray } from './prefer-arr-is-fixed-length-array.mjs';\nimport { preferArrIsMaxLengthArray } from './prefer-arr-is-max-length-array.mjs';\nimport { preferArrIsMinLengthArray } from './prefer-arr-is-min-length-array.mjs';\nimport { preferArrIsNonEmpty } from './prefer-arr-is-non-empty.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds =\n | 'useCanonicalGuard'\n | 'useIsBoundedLengthArray'\n | 'useIsFixedLengthArray'\n | 'useIsMaxLengthArray'\n | 'useIsMinLengthArray'\n | 'useIsNonEmpty';\n\n/**\n * The `xs.length <op> n` → `Arr.is*` rules folded into this one.\n *\n * Their implementations are reused as-is rather than rewritten: this rule\n * merges their visitors, so every case they already cover (type-aware array\n * checks, `Arr` import insertion, the `&&`-bounded-range special case) keeps\n * behaving exactly as before, and their co-located tests keep exercising them\n * directly.\n */\nconst COMPARISON_RULES = [\n preferArrIsNonEmpty,\n preferArrIsMinLengthArray,\n preferArrIsMaxLengthArray,\n preferArrIsBoundedLengthArray,\n preferArrIsFixedLengthArray,\n] as const;\n\nconst MESSAGES = {\n useCanonicalGuard:\n 'Replace `{{arrName}}.{{guard}}({{boundsText}})` with `{{arrName}}.{{replacement}}(...)`: the bound makes the two guards equivalent.',\n useIsNonEmpty: preferArrIsNonEmpty.meta.messages.useIsNonEmpty,\n useIsMinLengthArray:\n preferArrIsMinLengthArray.meta.messages.useIsMinLengthArray,\n useIsMaxLengthArray:\n preferArrIsMaxLengthArray.meta.messages.useIsMaxLengthArray,\n useIsBoundedLengthArray:\n preferArrIsBoundedLengthArray.meta.messages.useIsBoundedLengthArray,\n useIsFixedLengthArray:\n preferArrIsFixedLengthArray.meta.messages.useIsFixedLengthArray,\n} as const satisfies Record<MessageIds, string>;\n\n/**\n * Length guards whose bound makes them redundant with a named degenerate\n * guard, keyed by guard name.\n *\n * Every entry stays inside its own family rather than crossing the\n * branded/structural divide:\n *\n * - the branded `*Array` guards go to `isEmpty` / `isNonEmpty`, which narrow to\n * `FixedLengthArray<0, E> & Xs` / `MinLengthArray<1, E> & Xs`;\n * - the structural `*Tuple` guards go to `isEmptyTuple` / `isNonEmptyTuple`,\n * which narrow to `readonly []` / `MinLengthTuple<1, E>`.\n *\n * Most entries are then **type-identical** — `isFixedLengthArray(0, xs)` and\n * `isMinLengthArray(1, xs)` produce exactly what `isEmpty` / `isNonEmpty`\n * narrow to, and `FixedLengthTuple<0, E>`, `MaxLengthTuple<0, E>` and\n * `BoundedLengthTuple<0, 0, E>` are all `readonly []`. Two *strengthen*:\n * `MaxLengthArray<0, E>` and `BoundedLengthArray<0, 0, E>` do not include the\n * `readonly []` structural part that `FixedLengthArray<0, E>` intersects on, so\n * rewriting them to `isEmpty` adds it. That is sound for a guard — the narrowed\n * type only flows outwards, and the stronger type is assignable everywhere the\n * old one was — and it stays within the branded family.\n *\n * Sending a `*Tuple` guard to the branded `isEmpty` would also be sound, but it\n * would silently change which family the value belongs to, which is why each\n * family keeps its own lane.\n */\nconst GUARD_REWRITES = [\n { guard: 'isFixedLengthArray', bounds: [0], replacement: 'isEmpty' },\n { guard: 'isMaxLengthArray', bounds: [0], replacement: 'isEmpty' },\n { guard: 'isBoundedLengthArray', bounds: [0, 0], replacement: 'isEmpty' },\n { guard: 'isMinLengthArray', bounds: [1], replacement: 'isNonEmpty' },\n { guard: 'isFixedLengthTuple', bounds: [0], replacement: 'isEmptyTuple' },\n { guard: 'isMaxLengthTuple', bounds: [0], replacement: 'isEmptyTuple' },\n {\n guard: 'isBoundedLengthTuple',\n bounds: [0, 0],\n replacement: 'isEmptyTuple',\n },\n { guard: 'isMinLengthTuple', bounds: [1], replacement: 'isNonEmptyTuple' },\n] as const satisfies readonly Readonly<{\n guard: string;\n bounds: readonly number[];\n replacement: string;\n}>[];\n\nexport const preferCanonicalLengthGuard: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Normalize array-length checks to their canonical `Arr` guard: `xs.length <op> n` becomes the matching `Arr.is*` guard, and a degenerate guard becomes the named one for its family (`Arr.isFixedLengthArray(0, xs)` → `Arr.isEmpty`, `Arr.isFixedLengthTuple(0, xs)` → `Arr.isEmptyTuple`).',\n },\n fixable: 'code',\n schema: [],\n messages: MESSAGES,\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const arrLocalName = getImportedLocalName(\n getTsDataForgeImport(sourceCode.ast),\n 'Arr',\n );\n\n const comparisonVisitors = COMPARISON_RULES.map((rule) =>\n rule.create(context),\n );\n\n if (arrLocalName === undefined) return mergeVisitors(comparisonVisitors);\n\n return mergeVisitors([\n ...comparisonVisitors,\n {\n CallExpression: (node) => {\n const guardName = getGuardName(node, arrLocalName);\n\n if (guardName === undefined) return;\n\n const rewrite = GUARD_REWRITES.find(\n (entry) => entry.guard === guardName,\n );\n\n if (rewrite === undefined || !matchesBounds(node, rewrite.bounds)) {\n return;\n }\n\n // The array is the last argument; the bounds precede it.\n const array = node.arguments[rewrite.bounds.length];\n\n if (array === undefined) return;\n\n const arrayText = sourceCode.getText(array);\n\n context.report({\n node,\n messageId: 'useCanonicalGuard',\n data: {\n arrName: arrLocalName,\n guard: guardName,\n boundsText: [...rewrite.bounds.map(String), '…'].join(', '),\n replacement: rewrite.replacement,\n },\n fix: (fixer) =>\n fixer.replaceText(\n node,\n `${arrLocalName}.${rewrite.replacement}(${arrayText})`,\n ),\n });\n },\n },\n ]);\n },\n defaultOptions: [],\n} as const;\n\n/**\n * Combines several rule listeners into one, calling every handler registered\n * for a given selector in order.\n */\nconst mergeVisitors = (\n // `RuleListener` holds mutable AST handler signatures, so it is not deeply\n // readonly.\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n visitors: readonly TSESLint.RuleListener[],\n): TSESLint.RuleListener => {\n const mut_merged: Record<string, ((node: never) => void)[]> = {};\n\n for (const visitor of visitors) {\n for (const [selector, handler] of Object.entries(visitor)) {\n if (typeof handler !== 'function') continue;\n\n const mut_handlers = mut_merged[selector] ?? [];\n\n mut_handlers.push(handler);\n\n mut_merged[selector] = mut_handlers;\n }\n }\n\n return Object.fromEntries(\n Object.entries(mut_merged).map(([selector, handlers]) => [\n selector,\n (node: never) => {\n for (const handler of handlers) {\n handler(node);\n }\n },\n ]),\n );\n};\n\n/** The `Arr.<guard>` method name of `node`, or `undefined` for other calls. */\nconst getGuardName = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n node: TSESTree.CallExpression,\n arrLocalName: string,\n): string | undefined => {\n const { callee } = node;\n\n return callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.object.type === AST_NODE_TYPES.Identifier &&\n callee.object.name === arrLocalName &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ? callee.property.name\n : undefined;\n};\n\n/**\n * Whether the call is `guard(...bounds, array)` with every bound written as the\n * exact numeric literal the rewrite requires.\n *\n * The arity check also excludes the curried form (`guard(...bounds)`): the\n * degenerate guards take only the array, so there is nothing to rewrite it to.\n */\nconst matchesBounds = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n node: TSESTree.CallExpression,\n bounds: readonly number[],\n): boolean => {\n const args = node.arguments;\n\n if (args.length !== bounds.length + 1 || node.typeArguments !== undefined) {\n return false;\n }\n\n return args.every(\n (arg, index) =>\n arg.type !== AST_NODE_TYPES.SpreadElement &&\n (index === bounds.length ||\n (arg.type === AST_NODE_TYPES.Literal && arg.value === bounds[index])),\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -21,7 +21,7 @@ const preferRangeForLoop = {
|
|
|
21
21
|
const mut_nodesToFix = [];
|
|
22
22
|
return {
|
|
23
23
|
ForStatement: (node) => {
|
|
24
|
-
if (node.init?.type !== AST_NODE_TYPES.VariableDeclaration || node.init.kind !== "let" || !Arr.isFixedLengthTuple(node.init.declarations
|
|
24
|
+
if (node.init?.type !== AST_NODE_TYPES.VariableDeclaration || node.init.kind !== "let" || !Arr.isFixedLengthTuple(1, node.init.declarations)) {
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
27
|
const declaration = node.init.declarations[0];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-range-for-loop.mjs","sources":["../../src/rules/prefer-range-for-loop.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { Arr } from 'ts-data-forge';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useRangeForLoop';\n\nexport const preferRangeForLoop: TSESLint.RuleModule<MessageIds, Options> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `for (let i = begin; i < end; ++i)` with `for (const i of range(begin, end))` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useRangeForLoop:\n 'Replace with `for (const {{varName}} of range({{begin}}, {{end}}))` 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: {\n node: TSESTree.ForStatement;\n varName: string;\n begin: string;\n end: string;\n step?: string;\n }[] = [];\n\n return {\n ForStatement: (node) => {\n // Check init: let i = begin\n if (\n node.init?.type !== AST_NODE_TYPES.VariableDeclaration ||\n node.init.kind !== 'let' ||\n !Arr.isFixedLengthTuple(node.init.declarations
|
|
1
|
+
{"version":3,"file":"prefer-range-for-loop.mjs","sources":["../../src/rules/prefer-range-for-loop.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { Arr } from 'ts-data-forge';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useRangeForLoop';\n\nexport const preferRangeForLoop: TSESLint.RuleModule<MessageIds, Options> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `for (let i = begin; i < end; ++i)` with `for (const i of range(begin, end))` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useRangeForLoop:\n 'Replace with `for (const {{varName}} of range({{begin}}, {{end}}))` 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: {\n node: TSESTree.ForStatement;\n varName: string;\n begin: string;\n end: string;\n step?: string;\n }[] = [];\n\n return {\n ForStatement: (node) => {\n // Check init: let i = begin\n if (\n node.init?.type !== AST_NODE_TYPES.VariableDeclaration ||\n node.init.kind !== 'let' ||\n !Arr.isFixedLengthTuple(1, node.init.declarations)\n ) {\n return;\n }\n\n const declaration = node.init.declarations[0];\n\n if (\n declaration.id.type !== AST_NODE_TYPES.Identifier ||\n declaration.init === null\n ) {\n return;\n }\n\n const varName = declaration.id.name;\n\n const beginExpr = declaration.init;\n\n // Check test: i < end\n if (\n node.test?.type !== AST_NODE_TYPES.BinaryExpression ||\n node.test.operator !== '<' ||\n node.test.left.type !== AST_NODE_TYPES.Identifier ||\n node.test.left.name !== varName\n ) {\n return;\n }\n\n const endExpr = node.test.right;\n\n // Check update: ++i, i++, i += 1, or i += step\n if (node.update === null) return;\n\n let mut_step: string | undefined;\n\n if (node.update.type === AST_NODE_TYPES.UpdateExpression) {\n // ++i or i++\n if (\n node.update.operator === '++' &&\n node.update.argument.type === AST_NODE_TYPES.Identifier &&\n node.update.argument.name === varName\n ) {\n mut_step = undefined; // step defaults to 1\n } else {\n return;\n }\n } else if (\n node.update.type === AST_NODE_TYPES.AssignmentExpression &&\n node.update.operator === '+=' &&\n node.update.left.type === AST_NODE_TYPES.Identifier &&\n node.update.left.name === varName\n ) {\n // i += step\n const stepValue = node.update.right;\n\n if (stepValue.type === AST_NODE_TYPES.Literal) {\n const stepNum = stepValue.value;\n\n // Only support positive integer steps\n if (typeof stepNum === 'number' && stepNum > 0) {\n mut_step = stepNum === 1 ? undefined : String(stepNum);\n } else {\n return;\n }\n } else {\n // Variable step like i += step\n mut_step = sourceCode.getText(stepValue);\n }\n } else {\n return;\n }\n\n const beginText = sourceCode.getText(beginExpr);\n\n const endText = sourceCode.getText(endExpr);\n\n mut_nodesToFix.push({\n node,\n varName,\n begin: beginText,\n end: endText,\n step: mut_step,\n });\n },\n 'Program:exit': () => {\n const namedImports = getNamedImports(tsDataForgeImport);\n\n const hasRangeImport = namedImports.includes('range');\n\n for (const [\n index,\n { node, varName, begin, end, step },\n ] of mut_nodesToFix.entries()) {\n context.report({\n node: node.init ?? node,\n messageId: 'useRangeForLoop',\n data: {\n varName,\n begin,\n end,\n },\n fix: (fixer) => {\n const bodyText = sourceCode.getText(node.body);\n\n const rangeArgs =\n step === undefined\n ? `${begin}, ${end}`\n : `${begin}, ${end}, ${step}`;\n\n const replacement = `for (const ${varName} of range(${rangeArgs})) ${bodyText}`;\n\n const importFixes =\n index === 0 && !hasRangeImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, [\n 'range',\n ])\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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"}
|
package/dist/rules/rules.d.mts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
export declare const tsDataForgeRules: {
|
|
2
2
|
readonly 'prefer-canonical-array-slicing': import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferCanonicalArraySlicing", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
3
|
-
readonly 'prefer-
|
|
4
|
-
readonly 'prefer-
|
|
5
|
-
readonly 'prefer-arr-is-bounded-length-array': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsBoundedLengthArray", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
6
|
-
readonly 'prefer-arr-is-fixed-length-array': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsFixedLengthArray", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
3
|
+
readonly 'prefer-canonical-length-guard': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useCanonicalGuard" | "useIsBoundedLengthArray" | "useIsFixedLengthArray" | "useIsMaxLengthArray" | "useIsMinLengthArray" | "useIsNonEmpty", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
4
|
+
readonly 'prefer-canonical-length-cast': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useCanonicalCast", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
7
5
|
readonly 'prefer-arr-is-array': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useArrIsArray", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
8
|
-
readonly 'prefer-arr-is-non-empty': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNonEmpty", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
9
6
|
readonly 'prefer-arr-sum': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useArrSum" | "useArrSumBy", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
10
7
|
readonly 'prefer-as-int': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useBrandedNumberCastFunction", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
11
8
|
readonly 'prefer-is-non-null-object': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNonNullObject", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.d.mts","sourceRoot":"","sources":["../../src/rules/rules.mts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rules.d.mts","sourceRoot":"","sources":["../../src/rules/rules.mts"],"names":[],"mappings":"AAeA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;CAca,CAAC"}
|
package/dist/rules/rules.mjs
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { noUnnecessaryTypeGuard } from './no-unnecessary-type-guard.mjs';
|
|
2
2
|
import { preferArrIsArray } from './prefer-arr-is-array.mjs';
|
|
3
|
-
import { preferArrIsBoundedLengthArray } from './prefer-arr-is-bounded-length-array.mjs';
|
|
4
|
-
import { preferArrIsFixedLengthArray } from './prefer-arr-is-fixed-length-array.mjs';
|
|
5
|
-
import { preferArrIsMaxLengthArray } from './prefer-arr-is-max-length-array.mjs';
|
|
6
|
-
import { preferArrIsMinLengthArray } from './prefer-arr-is-min-length-array.mjs';
|
|
7
|
-
import { preferArrIsNonEmpty } from './prefer-arr-is-non-empty.mjs';
|
|
8
3
|
import { preferArrSum } from './prefer-arr-sum.mjs';
|
|
9
4
|
import { preferAsInt } from './prefer-as-int.mjs';
|
|
10
5
|
import { preferCanonicalArraySlicing } from './prefer-canonical-array-slicing.mjs';
|
|
6
|
+
import { preferCanonicalLengthCast } from './prefer-canonical-length-cast.mjs';
|
|
7
|
+
import { preferCanonicalLengthGuard } from './prefer-canonical-length-guard.mjs';
|
|
11
8
|
import { preferComparisonOverNullishGuard } from './prefer-comparison-over-nullish-guard.mjs';
|
|
12
9
|
import { preferIsNonNullObject } from './prefer-is-non-null-object.mjs';
|
|
13
10
|
import { preferIsRecordAndHasKey } from './prefer-is-record-and-has-key.mjs';
|
|
@@ -17,12 +14,9 @@ import { preferRangeForLoop } from './prefer-range-for-loop.mjs';
|
|
|
17
14
|
|
|
18
15
|
const tsDataForgeRules = {
|
|
19
16
|
"prefer-canonical-array-slicing": preferCanonicalArraySlicing,
|
|
20
|
-
"prefer-
|
|
21
|
-
"prefer-
|
|
22
|
-
"prefer-arr-is-bounded-length-array": preferArrIsBoundedLengthArray,
|
|
23
|
-
"prefer-arr-is-fixed-length-array": preferArrIsFixedLengthArray,
|
|
17
|
+
"prefer-canonical-length-guard": preferCanonicalLengthGuard,
|
|
18
|
+
"prefer-canonical-length-cast": preferCanonicalLengthCast,
|
|
24
19
|
"prefer-arr-is-array": preferArrIsArray,
|
|
25
|
-
"prefer-arr-is-non-empty": preferArrIsNonEmpty,
|
|
26
20
|
"prefer-arr-sum": preferArrSum,
|
|
27
21
|
"prefer-as-int": preferAsInt,
|
|
28
22
|
"prefer-is-non-null-object": preferIsNonNullObject,
|
package/dist/rules/rules.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.mjs","sources":["../../src/rules/rules.mts"],"sourcesContent":["import { type ESLintPlugin } from '../types.mjs';\nimport { noUnnecessaryTypeGuard } from './no-unnecessary-type-guard.mjs';\nimport { preferArrIsArray } from './prefer-arr-is-array.mjs';\nimport {
|
|
1
|
+
{"version":3,"file":"rules.mjs","sources":["../../src/rules/rules.mts"],"sourcesContent":["import { type ESLintPlugin } from '../types.mjs';\nimport { noUnnecessaryTypeGuard } from './no-unnecessary-type-guard.mjs';\nimport { preferArrIsArray } from './prefer-arr-is-array.mjs';\nimport { preferArrSum } from './prefer-arr-sum.mjs';\nimport { preferAsInt } from './prefer-as-int.mjs';\nimport { preferCanonicalArraySlicing } from './prefer-canonical-array-slicing.mjs';\nimport { preferCanonicalLengthCast } from './prefer-canonical-length-cast.mjs';\nimport { preferCanonicalLengthGuard } from './prefer-canonical-length-guard.mjs';\nimport { preferComparisonOverNullishGuard } from './prefer-comparison-over-nullish-guard.mjs';\nimport { preferIsNonNullObject } from './prefer-is-non-null-object.mjs';\nimport { preferIsRecordAndHasKey } from './prefer-is-record-and-has-key.mjs';\nimport { preferNumSafeParseFloat } from './prefer-num-safe-parse-float.mjs';\nimport { preferNumSafeParseInt } from './prefer-num-safe-parse-int.mjs';\nimport { preferRangeForLoop } from './prefer-range-for-loop.mjs';\n\nexport const tsDataForgeRules = {\n 'prefer-canonical-array-slicing': preferCanonicalArraySlicing,\n 'prefer-canonical-length-guard': preferCanonicalLengthGuard,\n 'prefer-canonical-length-cast': preferCanonicalLengthCast,\n 'prefer-arr-is-array': preferArrIsArray,\n 'prefer-arr-sum': preferArrSum,\n 'prefer-as-int': preferAsInt,\n 'prefer-is-non-null-object': preferIsNonNullObject,\n 'prefer-range-for-loop': preferRangeForLoop,\n 'prefer-is-record-and-has-key': preferIsRecordAndHasKey,\n 'prefer-num-safe-parse-int': preferNumSafeParseInt,\n 'prefer-num-safe-parse-float': preferNumSafeParseFloat,\n 'no-unnecessary-type-guard': noUnnecessaryTypeGuard,\n 'prefer-comparison-over-nullish-guard': preferComparisonOverNullishGuard,\n} as const satisfies ESLintPlugin['rules'];\n"],"names":[],"mappings":";;;;;;;;;;;;;;AAeO,MAAM,gBAAA,GAAmB;AAAA,EAC9B,gCAAA,EAAkC,2BAAA;AAAA,EAClC,+BAAA,EAAiC,0BAAA;AAAA,EACjC,8BAAA,EAAgC,yBAAA;AAAA,EAChC,qBAAA,EAAuB,gBAAA;AAAA,EACvB,gBAAA,EAAkB,YAAA;AAAA,EAClB,eAAA,EAAiB,WAAA;AAAA,EACjB,2BAAA,EAA6B,qBAAA;AAAA,EAC7B,uBAAA,EAAyB,kBAAA;AAAA,EACzB,8BAAA,EAAgC,uBAAA;AAAA,EAChC,2BAAA,EAA6B,qBAAA;AAAA,EAC7B,6BAAA,EAA+B,uBAAA;AAAA,EAC/B,2BAAA,EAA6B,sBAAA;AAAA,EAC7B,sCAAA,EAAwC;AAC1C;;;;"}
|
package/dist/types.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type TSESLint } from '@typescript-eslint/utils';
|
|
2
2
|
import { type DeepReadonly } from 'ts-type-forge';
|
|
3
3
|
export type ESLintPlugin = DeepReadonly<TSESLint.FlatConfig.Plugin>;
|
|
4
|
+
export type ESLintFlatConfig = DeepReadonly<TSESLint.FlatConfig.Config>;
|
|
4
5
|
//# sourceMappingURL=types.d.mts.map
|
package/dist/types.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","sourceRoot":"","sources":["../src/types.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.d.mts","sourceRoot":"","sources":["../src/types.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AAEpE,MAAM,MAAM,gBAAgB,GAAG,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-ts-data-forge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "ESLint rules that steer code toward ts-data-forge idioms.",
|
|
6
6
|
"keywords": [
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@typescript-eslint/utils": "8.64.0",
|
|
38
|
-
"ts-type-forge": "
|
|
39
|
-
"ts-data-forge": "
|
|
38
|
+
"ts-type-forge": "^9.1.1",
|
|
39
|
+
"ts-data-forge": "14.0.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@typescript-eslint/parser": "8.64.0",
|
|
@@ -51,6 +51,10 @@
|
|
|
51
51
|
"node": ">=22.22.2",
|
|
52
52
|
"pnpm": ">=9.0.0"
|
|
53
53
|
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public",
|
|
56
|
+
"provenance": true
|
|
57
|
+
},
|
|
54
58
|
"scripts": {
|
|
55
59
|
"build": "tsx ./scripts/cmd/build.mts",
|
|
56
60
|
"build:min": "tsx ./scripts/cmd/build.mts --skip-check",
|
package/src/index.mts
CHANGED
package/src/plugin.mts
CHANGED
|
@@ -1,9 +1,53 @@
|
|
|
1
1
|
import { tsDataForgeRules } from './rules/index.mjs';
|
|
2
|
-
import { type ESLintPlugin } from './types.mjs';
|
|
2
|
+
import { type ESLintFlatConfig, type ESLintPlugin } from './types.mjs';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Every rule this plugin ships, at `error`.
|
|
6
|
+
*
|
|
7
|
+
* The `satisfies` clause below is keyed off {@link tsDataForgeRules}, so adding
|
|
8
|
+
* a rule without listing it here fails to type-check.
|
|
9
|
+
*/
|
|
10
|
+
const recommendedRules = {
|
|
11
|
+
'ts-data-forge/prefer-canonical-array-slicing': 'error',
|
|
12
|
+
'ts-data-forge/prefer-canonical-length-cast': 'error',
|
|
13
|
+
'ts-data-forge/prefer-canonical-length-guard': 'error',
|
|
14
|
+
'ts-data-forge/prefer-arr-is-array': 'error',
|
|
15
|
+
'ts-data-forge/prefer-arr-sum': 'error',
|
|
16
|
+
'ts-data-forge/prefer-as-int': 'error',
|
|
17
|
+
'ts-data-forge/prefer-is-non-null-object': 'error',
|
|
18
|
+
'ts-data-forge/prefer-range-for-loop': 'error',
|
|
19
|
+
'ts-data-forge/prefer-is-record-and-has-key': 'error',
|
|
20
|
+
'ts-data-forge/prefer-num-safe-parse-int': 'error',
|
|
21
|
+
'ts-data-forge/prefer-num-safe-parse-float': 'error',
|
|
22
|
+
'ts-data-forge/no-unnecessary-type-guard': 'error',
|
|
23
|
+
'ts-data-forge/prefer-comparison-over-nullish-guard': 'error',
|
|
24
|
+
} as const satisfies Readonly<
|
|
25
|
+
Record<`ts-data-forge/${keyof typeof tsDataForgeRules}`, 'error'>
|
|
26
|
+
>;
|
|
27
|
+
|
|
28
|
+
const recommendedConfig = {
|
|
29
|
+
name: 'ts-data-forge/recommended',
|
|
30
|
+
plugins: {
|
|
31
|
+
// Resolved lazily so that this config registers the *same* object that
|
|
32
|
+
// `eslintPluginTsDataForge` refers to. ESLint rejects a plugin name that
|
|
33
|
+
// maps to two different objects with `Cannot redefine plugin`, which is
|
|
34
|
+
// what a user combining this preset with their own
|
|
35
|
+
// `plugins: { 'ts-data-forge': eslintPluginTsDataForge }` entry would hit
|
|
36
|
+
// if the preset embedded a separate copy of the plugin.
|
|
37
|
+
get 'ts-data-forge'(): ESLintPlugin {
|
|
38
|
+
return eslintPluginTsDataForge;
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
rules: recommendedRules,
|
|
42
|
+
} as const satisfies ESLintFlatConfig;
|
|
43
|
+
|
|
44
|
+
export const eslintPluginTsDataForge = {
|
|
5
45
|
meta: {
|
|
6
46
|
name: 'eslint-plugin-ts-data-forge',
|
|
7
47
|
},
|
|
8
48
|
rules: tsDataForgeRules,
|
|
9
|
-
|
|
49
|
+
configs: {
|
|
50
|
+
/** Enables every rule of this plugin at `error`. */
|
|
51
|
+
recommended: recommendedConfig,
|
|
52
|
+
},
|
|
53
|
+
} as const satisfies ESLintPlugin;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { eslintPluginTsDataForge } from './plugin.mjs';
|
|
2
|
+
import { tsDataForgeRules } from './rules/index.mjs';
|
|
3
|
+
|
|
4
|
+
describe('eslintPluginTsDataForge.configs.recommended', () => {
|
|
5
|
+
const recommended = eslintPluginTsDataForge.configs.recommended;
|
|
6
|
+
|
|
7
|
+
test('enables every rule of the plugin, and nothing else, at "error"', () => {
|
|
8
|
+
assert.deepStrictEqual(
|
|
9
|
+
recommended.rules,
|
|
10
|
+
Object.fromEntries(
|
|
11
|
+
Object.keys(tsDataForgeRules).map((name) => [
|
|
12
|
+
`ts-data-forge/${name}`,
|
|
13
|
+
'error',
|
|
14
|
+
]),
|
|
15
|
+
),
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('registers the exported plugin object itself', () => {
|
|
20
|
+
// Registering a *copy* would make `Cannot redefine plugin` errors possible
|
|
21
|
+
// for users who also list the plugin in their own `plugins` record.
|
|
22
|
+
expect(recommended.plugins['ts-data-forge']).toBe(eslintPluginTsDataForge);
|
|
23
|
+
});
|
|
24
|
+
});
|
package/src/rule-types.mts
CHANGED
|
@@ -43,7 +43,7 @@ namespace PreferCanonicalArraySlicing {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
|
-
* @description
|
|
46
|
+
* @description Normalize array-length checks to their canonical `Arr` guard: `xs.length <op> n` becomes the matching `Arr.is*` guard, and a degenerate guard becomes the named one for its family (`Arr.isFixedLengthArray(0, xs)` → `Arr.isEmpty`, `Arr.isFixedLengthTuple(0, xs)` → `Arr.isEmptyTuple`).
|
|
47
47
|
*
|
|
48
48
|
* ```md
|
|
49
49
|
* | key | value |
|
|
@@ -53,12 +53,12 @@ namespace PreferCanonicalArraySlicing {
|
|
|
53
53
|
* | fixable | code |
|
|
54
54
|
* ```
|
|
55
55
|
*/
|
|
56
|
-
namespace
|
|
56
|
+
namespace PreferCanonicalLengthGuard {
|
|
57
57
|
export type RuleEntry = Linter.StringSeverity;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
|
-
* @description
|
|
61
|
+
* @description Normalize an `Arr` length cast whose bounds are degenerate (e.g. `Arr.asMinLengthArray(1, xs)`, `Arr.asFixedLengthTuple(0, xs)`) to the cast that names that constraint directly, staying within the branded `*Array` or structural `*Tuple` family.
|
|
62
62
|
*
|
|
63
63
|
* ```md
|
|
64
64
|
* | key | value |
|
|
@@ -68,37 +68,7 @@ namespace PreferArrIsMinLengthArray {
|
|
|
68
68
|
* | fixable | code |
|
|
69
69
|
* ```
|
|
70
70
|
*/
|
|
71
|
-
namespace
|
|
72
|
-
export type RuleEntry = Linter.StringSeverity;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* @description Replace `xs.length >= min && xs.length <= max` with `Arr.isBoundedLengthArray(xs, min, max)` from ts-data-forge.
|
|
77
|
-
*
|
|
78
|
-
* ```md
|
|
79
|
-
* | key | value |
|
|
80
|
-
* | :--------- | :--------- |
|
|
81
|
-
* | type | suggestion |
|
|
82
|
-
* | deprecated | false |
|
|
83
|
-
* | fixable | code |
|
|
84
|
-
* ```
|
|
85
|
-
*/
|
|
86
|
-
namespace PreferArrIsBoundedLengthArray {
|
|
87
|
-
export type RuleEntry = Linter.StringSeverity;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* @description Replace `xs.length === n` with `Arr.isFixedLengthArray(xs, n)` from ts-data-forge.
|
|
92
|
-
*
|
|
93
|
-
* ```md
|
|
94
|
-
* | key | value |
|
|
95
|
-
* | :--------- | :--------- |
|
|
96
|
-
* | type | suggestion |
|
|
97
|
-
* | deprecated | false |
|
|
98
|
-
* | fixable | code |
|
|
99
|
-
* ```
|
|
100
|
-
*/
|
|
101
|
-
namespace PreferArrIsFixedLengthArray {
|
|
71
|
+
namespace PreferCanonicalLengthCast {
|
|
102
72
|
export type RuleEntry = Linter.StringSeverity;
|
|
103
73
|
}
|
|
104
74
|
|
|
@@ -117,21 +87,6 @@ namespace PreferArrIsArray {
|
|
|
117
87
|
export type RuleEntry = Linter.StringSeverity;
|
|
118
88
|
}
|
|
119
89
|
|
|
120
|
-
/**
|
|
121
|
-
* @description Replace `xs.length > 0` with `Arr.isNonEmpty(xs)` from ts-data-forge.
|
|
122
|
-
*
|
|
123
|
-
* ```md
|
|
124
|
-
* | key | value |
|
|
125
|
-
* | :--------- | :--------- |
|
|
126
|
-
* | type | suggestion |
|
|
127
|
-
* | deprecated | false |
|
|
128
|
-
* | fixable | code |
|
|
129
|
-
* ```
|
|
130
|
-
*/
|
|
131
|
-
namespace PreferArrIsNonEmpty {
|
|
132
|
-
export type RuleEntry = Linter.StringSeverity;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
90
|
/**
|
|
136
91
|
* @description Replace `xs.reduce((a, b) => a + b, 0)` with `Arr.sum(xs)` or `Arr.sumBy(xs, fn)` from ts-data-forge.
|
|
137
92
|
*
|
|
@@ -297,12 +252,9 @@ namespace PreferComparisonOverNullishGuard {
|
|
|
297
252
|
|
|
298
253
|
export type EslintTsDataForgeRules = Readonly<{
|
|
299
254
|
'ts-data-forge/prefer-canonical-array-slicing': PreferCanonicalArraySlicing.RuleEntry;
|
|
300
|
-
'ts-data-forge/prefer-
|
|
301
|
-
'ts-data-forge/prefer-
|
|
302
|
-
'ts-data-forge/prefer-arr-is-bounded-length-array': PreferArrIsBoundedLengthArray.RuleEntry;
|
|
303
|
-
'ts-data-forge/prefer-arr-is-fixed-length-array': PreferArrIsFixedLengthArray.RuleEntry;
|
|
255
|
+
'ts-data-forge/prefer-canonical-length-guard': PreferCanonicalLengthGuard.RuleEntry;
|
|
256
|
+
'ts-data-forge/prefer-canonical-length-cast': PreferCanonicalLengthCast.RuleEntry;
|
|
304
257
|
'ts-data-forge/prefer-arr-is-array': PreferArrIsArray.RuleEntry;
|
|
305
|
-
'ts-data-forge/prefer-arr-is-non-empty': PreferArrIsNonEmpty.RuleEntry;
|
|
306
258
|
'ts-data-forge/prefer-arr-sum': PreferArrSum.RuleEntry;
|
|
307
259
|
'ts-data-forge/prefer-as-int': PreferAsInt.RuleEntry;
|
|
308
260
|
'ts-data-forge/prefer-is-non-null-object': PreferIsNonNullObject.RuleEntry;
|
|
@@ -125,7 +125,7 @@ export const preferArrIsBoundedLengthArray: TSESLint.RuleModule<
|
|
|
125
125
|
max: maxText,
|
|
126
126
|
},
|
|
127
127
|
fix: (fixer) => {
|
|
128
|
-
const replacement = `Arr.isBoundedLengthArray(${
|
|
128
|
+
const replacement = `Arr.isBoundedLengthArray(${minText}, ${maxText}, ${arrayText})`;
|
|
129
129
|
|
|
130
130
|
const importFixes =
|
|
131
131
|
index === 0 && !hasArrImport
|
|
@@ -85,7 +85,7 @@ describe('prefer-arr-is-bounded-length-array', () => {
|
|
|
85
85
|
output: dedent`
|
|
86
86
|
import { Arr } from 'ts-data-forge';
|
|
87
87
|
const xs: readonly number[] = [1, 2, 3];
|
|
88
|
-
const ok = Arr.isBoundedLengthArray(
|
|
88
|
+
const ok = Arr.isBoundedLengthArray(1, 5, xs);
|
|
89
89
|
`,
|
|
90
90
|
errors: [{ messageId: 'useIsBoundedLengthArray' }],
|
|
91
91
|
},
|
|
@@ -98,7 +98,7 @@ describe('prefer-arr-is-bounded-length-array', () => {
|
|
|
98
98
|
output: dedent`
|
|
99
99
|
import { Arr } from 'ts-data-forge';
|
|
100
100
|
const xs: readonly number[] = [1, 2, 3];
|
|
101
|
-
const ok = Arr.isBoundedLengthArray(
|
|
101
|
+
const ok = Arr.isBoundedLengthArray(1, 5, xs);
|
|
102
102
|
`,
|
|
103
103
|
errors: [{ messageId: 'useIsBoundedLengthArray' }],
|
|
104
104
|
},
|
|
@@ -111,7 +111,7 @@ describe('prefer-arr-is-bounded-length-array', () => {
|
|
|
111
111
|
output: dedent`
|
|
112
112
|
import { Arr } from 'ts-data-forge';
|
|
113
113
|
const xs: readonly number[] = [1, 2, 3];
|
|
114
|
-
const ok = Arr.isBoundedLengthArray(
|
|
114
|
+
const ok = Arr.isBoundedLengthArray(1, 5, xs);
|
|
115
115
|
`,
|
|
116
116
|
errors: [{ messageId: 'useIsBoundedLengthArray' }],
|
|
117
117
|
},
|
|
@@ -128,7 +128,7 @@ describe('prefer-arr-is-bounded-length-array', () => {
|
|
|
128
128
|
const xs = [1, 2, 3];
|
|
129
129
|
const lo = 1;
|
|
130
130
|
const hi = 5;
|
|
131
|
-
const ok = Arr.isBoundedLengthArray(
|
|
131
|
+
const ok = Arr.isBoundedLengthArray(lo, hi, xs);
|
|
132
132
|
`,
|
|
133
133
|
errors: [{ messageId: 'useIsBoundedLengthArray' }],
|
|
134
134
|
},
|
|
@@ -144,7 +144,7 @@ describe('prefer-arr-is-bounded-length-array', () => {
|
|
|
144
144
|
import { Arr } from 'ts-data-forge';
|
|
145
145
|
|
|
146
146
|
const xs = [1, 2, 3];
|
|
147
|
-
const ok = Arr.isBoundedLengthArray(
|
|
147
|
+
const ok = Arr.isBoundedLengthArray(1, 5, xs);
|
|
148
148
|
`,
|
|
149
149
|
errors: [{ messageId: 'useIsBoundedLengthArray' }],
|
|
150
150
|
},
|
|
@@ -122,7 +122,7 @@ export const preferArrIsFixedLengthArray: TSESLint.RuleModule<
|
|
|
122
122
|
length: lengthText,
|
|
123
123
|
},
|
|
124
124
|
fix: (fixer) => {
|
|
125
|
-
const baseCall = `Arr.isFixedLengthArray(${
|
|
125
|
+
const baseCall = `Arr.isFixedLengthArray(${lengthText}, ${arrayText})`;
|
|
126
126
|
|
|
127
127
|
const replacement = isNegated ? `!${baseCall}` : baseCall;
|
|
128
128
|
|
|
@@ -76,7 +76,7 @@ describe('prefer-arr-is-fixed-length-array', () => {
|
|
|
76
76
|
output: dedent`
|
|
77
77
|
import { Arr } from 'ts-data-forge';
|
|
78
78
|
const xs: readonly number[] = [1, 2, 3];
|
|
79
|
-
const ok = Arr.isFixedLengthArray(
|
|
79
|
+
const ok = Arr.isFixedLengthArray(3, xs);
|
|
80
80
|
`,
|
|
81
81
|
errors: [{ messageId: 'useIsFixedLengthArray' }],
|
|
82
82
|
},
|
|
@@ -89,7 +89,7 @@ describe('prefer-arr-is-fixed-length-array', () => {
|
|
|
89
89
|
output: dedent`
|
|
90
90
|
import { Arr } from 'ts-data-forge';
|
|
91
91
|
const xs: readonly number[] = [1, 2, 3];
|
|
92
|
-
const ok = Arr.isFixedLengthArray(
|
|
92
|
+
const ok = Arr.isFixedLengthArray(3, xs);
|
|
93
93
|
`,
|
|
94
94
|
errors: [{ messageId: 'useIsFixedLengthArray' }],
|
|
95
95
|
},
|
|
@@ -102,7 +102,7 @@ describe('prefer-arr-is-fixed-length-array', () => {
|
|
|
102
102
|
output: dedent`
|
|
103
103
|
import { Arr } from 'ts-data-forge';
|
|
104
104
|
const xs = [1, 2, 3];
|
|
105
|
-
const ok = Arr.isFixedLengthArray(
|
|
105
|
+
const ok = Arr.isFixedLengthArray(3, xs);
|
|
106
106
|
`,
|
|
107
107
|
errors: [{ messageId: 'useIsFixedLengthArray' }],
|
|
108
108
|
},
|
|
@@ -115,7 +115,7 @@ describe('prefer-arr-is-fixed-length-array', () => {
|
|
|
115
115
|
output: dedent`
|
|
116
116
|
import { Arr } from 'ts-data-forge';
|
|
117
117
|
const xs = [1, 2, 3] as const;
|
|
118
|
-
const ok = Arr.isFixedLengthArray(
|
|
118
|
+
const ok = Arr.isFixedLengthArray(3, xs);
|
|
119
119
|
`,
|
|
120
120
|
errors: [{ messageId: 'useIsFixedLengthArray' }],
|
|
121
121
|
},
|
|
@@ -130,7 +130,7 @@ describe('prefer-arr-is-fixed-length-array', () => {
|
|
|
130
130
|
import { Arr } from 'ts-data-forge';
|
|
131
131
|
const xs = [1, 2, 3];
|
|
132
132
|
const n = 3;
|
|
133
|
-
const ok = Arr.isFixedLengthArray(
|
|
133
|
+
const ok = Arr.isFixedLengthArray(n, xs);
|
|
134
134
|
`,
|
|
135
135
|
errors: [{ messageId: 'useIsFixedLengthArray' }],
|
|
136
136
|
},
|
|
@@ -146,7 +146,7 @@ describe('prefer-arr-is-fixed-length-array', () => {
|
|
|
146
146
|
import { Arr } from 'ts-data-forge';
|
|
147
147
|
|
|
148
148
|
const xs = [1, 2, 3];
|
|
149
|
-
const ok = Arr.isFixedLengthArray(
|
|
149
|
+
const ok = Arr.isFixedLengthArray(3, xs);
|
|
150
150
|
`,
|
|
151
151
|
errors: [{ messageId: 'useIsFixedLengthArray' }],
|
|
152
152
|
},
|
|
@@ -162,8 +162,8 @@ describe('prefer-arr-is-fixed-length-array', () => {
|
|
|
162
162
|
import { Arr } from 'ts-data-forge';
|
|
163
163
|
const xs = [1, 2, 3];
|
|
164
164
|
const ys = [4, 5];
|
|
165
|
-
const ok1 = Arr.isFixedLengthArray(
|
|
166
|
-
const ok2 = Arr.isFixedLengthArray(
|
|
165
|
+
const ok1 = Arr.isFixedLengthArray(3, xs);
|
|
166
|
+
const ok2 = Arr.isFixedLengthArray(2, ys);
|
|
167
167
|
`,
|
|
168
168
|
errors: [
|
|
169
169
|
{ messageId: 'useIsFixedLengthArray' },
|
|
@@ -179,7 +179,7 @@ describe('prefer-arr-is-fixed-length-array', () => {
|
|
|
179
179
|
output: dedent`
|
|
180
180
|
import { Arr } from 'ts-data-forge';
|
|
181
181
|
const xs: readonly number[] = [1, 2, 3];
|
|
182
|
-
const ok = !Arr.isFixedLengthArray(
|
|
182
|
+
const ok = !Arr.isFixedLengthArray(3, xs);
|
|
183
183
|
`,
|
|
184
184
|
errors: [{ messageId: 'useIsFixedLengthArray' }],
|
|
185
185
|
},
|
|
@@ -192,7 +192,7 @@ describe('prefer-arr-is-fixed-length-array', () => {
|
|
|
192
192
|
output: dedent`
|
|
193
193
|
import { Arr } from 'ts-data-forge';
|
|
194
194
|
const xs: readonly number[] = [1, 2, 3];
|
|
195
|
-
const ok = !Arr.isFixedLengthArray(
|
|
195
|
+
const ok = !Arr.isFixedLengthArray(3, xs);
|
|
196
196
|
`,
|
|
197
197
|
errors: [{ messageId: 'useIsFixedLengthArray' }],
|
|
198
198
|
},
|
|
@@ -205,7 +205,7 @@ describe('prefer-arr-is-fixed-length-array', () => {
|
|
|
205
205
|
output: dedent`
|
|
206
206
|
import { Arr } from 'ts-data-forge';
|
|
207
207
|
const xs = [1, 2, 3];
|
|
208
|
-
const ok = !Arr.isFixedLengthArray(
|
|
208
|
+
const ok = !Arr.isFixedLengthArray(5, xs);
|
|
209
209
|
`,
|
|
210
210
|
errors: [{ messageId: 'useIsFixedLengthArray' }],
|
|
211
211
|
},
|
|
@@ -123,7 +123,7 @@ export const preferArrIsMaxLengthArray: TSESLint.RuleModule<
|
|
|
123
123
|
length: lengthText,
|
|
124
124
|
},
|
|
125
125
|
fix: (fixer) => {
|
|
126
|
-
const replacement = `Arr.isMaxLengthArray(${
|
|
126
|
+
const replacement = `Arr.isMaxLengthArray(${lengthText}, ${arrayText})`;
|
|
127
127
|
|
|
128
128
|
const importFixes =
|
|
129
129
|
index === 0 && !hasArrImport
|
|
@@ -89,7 +89,7 @@ describe('prefer-arr-is-max-length-array', () => {
|
|
|
89
89
|
output: dedent`
|
|
90
90
|
import { Arr } from 'ts-data-forge';
|
|
91
91
|
const xs: readonly number[] = [1, 2, 3];
|
|
92
|
-
const ok = Arr.isMaxLengthArray(
|
|
92
|
+
const ok = Arr.isMaxLengthArray(3, xs);
|
|
93
93
|
`,
|
|
94
94
|
errors: [{ messageId: 'useIsMaxLengthArray' }],
|
|
95
95
|
},
|
|
@@ -102,7 +102,7 @@ describe('prefer-arr-is-max-length-array', () => {
|
|
|
102
102
|
output: dedent`
|
|
103
103
|
import { Arr } from 'ts-data-forge';
|
|
104
104
|
const xs: readonly number[] = [1, 2, 3];
|
|
105
|
-
const ok = Arr.isMaxLengthArray(
|
|
105
|
+
const ok = Arr.isMaxLengthArray(3, xs);
|
|
106
106
|
`,
|
|
107
107
|
errors: [{ messageId: 'useIsMaxLengthArray' }],
|
|
108
108
|
},
|
|
@@ -115,7 +115,7 @@ describe('prefer-arr-is-max-length-array', () => {
|
|
|
115
115
|
output: dedent`
|
|
116
116
|
import { Arr } from 'ts-data-forge';
|
|
117
117
|
const xs = [1, 2, 3];
|
|
118
|
-
const ok = Arr.isMaxLengthArray(
|
|
118
|
+
const ok = Arr.isMaxLengthArray(1, xs);
|
|
119
119
|
`,
|
|
120
120
|
errors: [{ messageId: 'useIsMaxLengthArray' }],
|
|
121
121
|
},
|
|
@@ -128,7 +128,7 @@ describe('prefer-arr-is-max-length-array', () => {
|
|
|
128
128
|
output: dedent`
|
|
129
129
|
import { Arr } from 'ts-data-forge';
|
|
130
130
|
const xs = [1, 2, 3] as const;
|
|
131
|
-
const ok = Arr.isMaxLengthArray(
|
|
131
|
+
const ok = Arr.isMaxLengthArray(2, xs);
|
|
132
132
|
`,
|
|
133
133
|
errors: [{ messageId: 'useIsMaxLengthArray' }],
|
|
134
134
|
},
|
|
@@ -143,7 +143,7 @@ describe('prefer-arr-is-max-length-array', () => {
|
|
|
143
143
|
import { Arr } from 'ts-data-forge';
|
|
144
144
|
const xs = [1, 2, 3];
|
|
145
145
|
const n = 2;
|
|
146
|
-
const ok = Arr.isMaxLengthArray(
|
|
146
|
+
const ok = Arr.isMaxLengthArray(n, xs);
|
|
147
147
|
`,
|
|
148
148
|
errors: [{ messageId: 'useIsMaxLengthArray' }],
|
|
149
149
|
},
|
|
@@ -159,7 +159,7 @@ describe('prefer-arr-is-max-length-array', () => {
|
|
|
159
159
|
import { Arr } from 'ts-data-forge';
|
|
160
160
|
|
|
161
161
|
const xs = [1, 2, 3];
|
|
162
|
-
const ok = Arr.isMaxLengthArray(
|
|
162
|
+
const ok = Arr.isMaxLengthArray(1, xs);
|
|
163
163
|
`,
|
|
164
164
|
errors: [{ messageId: 'useIsMaxLengthArray' }],
|
|
165
165
|
},
|
|
@@ -175,8 +175,8 @@ describe('prefer-arr-is-max-length-array', () => {
|
|
|
175
175
|
import { Arr } from 'ts-data-forge';
|
|
176
176
|
const xs = [1, 2, 3];
|
|
177
177
|
const ys = [4, 5];
|
|
178
|
-
const ok1 = Arr.isMaxLengthArray(
|
|
179
|
-
const ok2 = Arr.isMaxLengthArray(
|
|
178
|
+
const ok1 = Arr.isMaxLengthArray(2, xs);
|
|
179
|
+
const ok2 = Arr.isMaxLengthArray(1, ys);
|
|
180
180
|
`,
|
|
181
181
|
errors: [
|
|
182
182
|
{ messageId: 'useIsMaxLengthArray' },
|