eslint-plugin-ts-type-forge 0.7.3 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +31 -8
  2. package/dist/index.mjs +2 -2
  3. package/dist/index.mjs.map +1 -1
  4. package/dist/plugin.d.mts +2 -0
  5. package/dist/plugin.d.mts.map +1 -1
  6. package/dist/plugin.mjs +34 -30
  7. package/dist/plugin.mjs.map +1 -1
  8. package/dist/rule-types.d.mts +15 -0
  9. package/dist/rule-types.d.mts.map +1 -1
  10. package/dist/rule-types.mjs +2 -1
  11. package/dist/rule-types.mjs.map +1 -1
  12. package/dist/rules/ast-utils.mjs +224 -103
  13. package/dist/rules/ast-utils.mjs.map +1 -1
  14. package/dist/rules/constants.mjs +14 -11
  15. package/dist/rules/constants.mjs.map +1 -1
  16. package/dist/rules/import-style.mjs +31 -26
  17. package/dist/rules/import-style.mjs.map +1 -1
  18. package/dist/rules/import-utils.mjs +49 -29
  19. package/dist/rules/import-utils.mjs.map +1 -1
  20. package/dist/rules/index.mjs +2 -2
  21. package/dist/rules/index.mjs.map +1 -1
  22. package/dist/rules/no-side-effect-import.d.mts +19 -0
  23. package/dist/rules/no-side-effect-import.d.mts.map +1 -0
  24. package/dist/rules/no-side-effect-import.mjs +55 -0
  25. package/dist/rules/no-side-effect-import.mjs.map +1 -0
  26. package/dist/rules/prefer-canonical-length-constrained-tuple.mjs +93 -73
  27. package/dist/rules/prefer-canonical-length-constrained-tuple.mjs.map +1 -1
  28. package/dist/rules/prefer-canonical-mutable-record.mjs +88 -75
  29. package/dist/rules/prefer-canonical-mutable-record.mjs.map +1 -1
  30. package/dist/rules/prefer-readonly-or-mutable-record.mjs +41 -45
  31. package/dist/rules/prefer-readonly-or-mutable-record.mjs.map +1 -1
  32. package/dist/rules/prefer-strict-or-relaxed-utility-type.mjs +48 -46
  33. package/dist/rules/prefer-strict-or-relaxed-utility-type.mjs.map +1 -1
  34. package/dist/rules/rewrite-rule-utils.mjs +31 -28
  35. package/dist/rules/rewrite-rule-utils.mjs.map +1 -1
  36. package/dist/rules/rules.d.mts +1 -0
  37. package/dist/rules/rules.d.mts.map +1 -1
  38. package/dist/rules/rules.mjs +8 -9
  39. package/dist/rules/rules.mjs.map +1 -1
  40. package/dist/rules/std-type-rule-utils.mjs +47 -30
  41. package/dist/rules/std-type-rule-utils.mjs.map +1 -1
  42. package/dist/types.mjs +2 -2
  43. package/dist/types.mjs.map +1 -1
  44. package/package.json +6 -7
  45. package/src/plugin.mts +1 -0
  46. package/src/rule-types.mts +16 -0
  47. package/src/rules/no-side-effect-import.mts +73 -0
  48. package/src/rules/no-side-effect-import.test.mts +83 -0
  49. package/src/rules/rules.mts +2 -0
@@ -1,11 +1,14 @@
1
- const TS_TYPE_FORGE_MODULE = "ts-type-forge";
2
- const NON_EMPTY_TUPLE_TYPE_NAME = "NonEmptyTuple";
3
- const MUTABLE_NON_EMPTY_TUPLE_TYPE_NAME = "MutableNonEmptyTuple";
4
- const MIN_LENGTH_TUPLE_TYPE_NAME = "MinLengthTuple";
5
- const MUTABLE_MIN_LENGTH_TUPLE_TYPE_NAME = "MutableMinLengthTuple";
6
- const FIXED_LENGTH_TUPLE_TYPE_NAME = "FixedLengthTuple";
7
- const MUTABLE_FIXED_LENGTH_TUPLE_TYPE_NAME = "MutableFixedLengthTuple";
8
- const DEFAULT_MAX_TUPLE_LENGTH = 10;
9
-
10
- export { DEFAULT_MAX_TUPLE_LENGTH, FIXED_LENGTH_TUPLE_TYPE_NAME, MIN_LENGTH_TUPLE_TYPE_NAME, MUTABLE_FIXED_LENGTH_TUPLE_TYPE_NAME, MUTABLE_MIN_LENGTH_TUPLE_TYPE_NAME, MUTABLE_NON_EMPTY_TUPLE_TYPE_NAME, NON_EMPTY_TUPLE_TYPE_NAME, TS_TYPE_FORGE_MODULE };
11
- //# sourceMappingURL=constants.mjs.map
1
+ export const TS_TYPE_FORGE_MODULE = 'ts-type-forge';
2
+ export const NON_EMPTY_TUPLE_TYPE_NAME = 'NonEmptyTuple';
3
+ export const MUTABLE_NON_EMPTY_TUPLE_TYPE_NAME = 'MutableNonEmptyTuple';
4
+ export const MIN_LENGTH_TUPLE_TYPE_NAME = 'MinLengthTuple';
5
+ export const MUTABLE_MIN_LENGTH_TUPLE_TYPE_NAME = 'MutableMinLengthTuple';
6
+ export const FIXED_LENGTH_TUPLE_TYPE_NAME = 'FixedLengthTuple';
7
+ export const MUTABLE_FIXED_LENGTH_TUPLE_TYPE_NAME = 'MutableFixedLengthTuple';
8
+
9
+
10
+
11
+
12
+
13
+ export const DEFAULT_MAX_TUPLE_LENGTH = 10;
14
+ //# sourceMappingURL=constants.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.mjs","sources":["../../src/rules/constants.mts"],"sourcesContent":["export const TS_TYPE_FORGE_MODULE = 'ts-type-forge';\n\nexport const NON_EMPTY_TUPLE_TYPE_NAME = 'NonEmptyTuple';\n\nexport const MUTABLE_NON_EMPTY_TUPLE_TYPE_NAME = 'MutableNonEmptyTuple';\n\nexport const MIN_LENGTH_TUPLE_TYPE_NAME = 'MinLengthTuple';\n\nexport const MUTABLE_MIN_LENGTH_TUPLE_TYPE_NAME = 'MutableMinLengthTuple';\n\nexport const FIXED_LENGTH_TUPLE_TYPE_NAME = 'FixedLengthTuple';\n\nexport const MUTABLE_FIXED_LENGTH_TUPLE_TYPE_NAME = 'MutableFixedLengthTuple';\n\n/**\n * Upper bound for the `maxLength` option of `prefer-length-constrained-tuple`.\n * Beyond this, spelling the tuple out is usually intentional (e.g. a matrix\n * row), and the named form stops being more readable.\n */\nexport const DEFAULT_MAX_TUPLE_LENGTH = 10;\n"],"names":[],"mappings":"AAAO,MAAM,oBAAA,GAAuB;AAE7B,MAAM,yBAAA,GAA4B;AAElC,MAAM,iCAAA,GAAoC;AAE1C,MAAM,0BAAA,GAA6B;AAEnC,MAAM,kCAAA,GAAqC;AAE3C,MAAM,4BAAA,GAA+B;AAErC,MAAM,oCAAA,GAAuC;AAO7C,MAAM,wBAAA,GAA2B;;;;"}
1
+ {"version":3,"file":"constants.mjs","sourceRoot":"","sources":["../../src/rules/constants.mts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,eAAe,CAAC;AAEpD,MAAM,CAAC,MAAM,yBAAyB,GAAG,eAAe,CAAC;AAEzD,MAAM,CAAC,MAAM,iCAAiC,GAAG,sBAAsB,CAAC;AAExE,MAAM,CAAC,MAAM,0BAA0B,GAAG,gBAAgB,CAAC;AAE3D,MAAM,CAAC,MAAM,kCAAkC,GAAG,uBAAuB,CAAC;AAE1E,MAAM,CAAC,MAAM,4BAA4B,GAAG,kBAAkB,CAAC;AAE/D,MAAM,CAAC,MAAM,oCAAoC,GAAG,yBAAyB,CAAC;AAE9E;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC"}
@@ -1,29 +1,34 @@
1
1
  import { TS_TYPE_FORGE_MODULE } from './constants.mjs';
2
- import { getImportedLocalName, getTsTypeForgeImports, hasConflictingDeclaration } from './import-utils.mjs';
3
-
4
- const DEFAULT_IMPORT_STYLE = "named";
5
- const IMPORT_STYLE_SCHEMA_PROPERTY = {
6
- type: "string",
7
- enum: ["global", "named"],
8
- description: [
9
- "How the ts-type-forge type is brought into scope.",
10
- "'named' (default) makes the autofix add the corresponding",
11
- `\`import { type \u2026 } from '${TS_TYPE_FORGE_MODULE}';\``,
12
- "when the name is not imported yet; 'global' assumes the",
13
- "ambient globals of `ts-type-forge/global` and never touches",
14
- "imports."
15
- ].join(" ")
16
- };
17
- const resolveTypeName = (program, canonicalName, importStyle) => {
18
- const importedLocalName = getImportedLocalName(
19
- getTsTypeForgeImports(program),
20
- canonicalName
21
- );
22
- if (importedLocalName !== void 0) {
23
- return { localName: importedLocalName, needsImport: false };
24
- }
25
- return hasConflictingDeclaration(program, canonicalName) ? void 0 : { localName: canonicalName, needsImport: importStyle === "named" };
2
+ import { getImportedLocalName, getTsTypeForgeImports, hasConflictingDeclaration, } from './import-utils.mjs';
3
+ export const DEFAULT_IMPORT_STYLE = 'named';
4
+ export const IMPORT_STYLE_SCHEMA_PROPERTY = {
5
+ type: 'string',
6
+ enum: ['global', 'named'],
7
+ description: [
8
+ 'How the ts-type-forge type is brought into scope.',
9
+ "'named' (default) makes the autofix add the corresponding",
10
+ `\`import { type … } from '${TS_TYPE_FORGE_MODULE}';\``,
11
+ "when the name is not imported yet; 'global' assumes the",
12
+ 'ambient globals of `ts-type-forge/global` and never touches',
13
+ 'imports.',
14
+ ].join(' '),
26
15
  };
27
16
 
28
- export { DEFAULT_IMPORT_STYLE, IMPORT_STYLE_SCHEMA_PROPERTY, resolveTypeName };
29
- //# sourceMappingURL=import-style.mjs.map
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+ export const resolveTypeName = (program, canonicalName, importStyle) => {
26
+ const importedLocalName = getImportedLocalName(getTsTypeForgeImports(program), canonicalName);
27
+ if (importedLocalName !== undefined) {
28
+ return { localName: importedLocalName, needsImport: false };
29
+ }
30
+ return hasConflictingDeclaration(program, canonicalName)
31
+ ? undefined
32
+ : { localName: canonicalName, needsImport: importStyle === 'named' };
33
+ };
34
+ //# sourceMappingURL=import-style.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"import-style.mjs","sources":["../../src/rules/import-style.mts"],"sourcesContent":["import { type TSESLint, type TSESTree } from '@typescript-eslint/utils';\nimport { TS_TYPE_FORGE_MODULE } from './constants.mjs';\nimport {\n getImportedLocalName,\n getTsTypeForgeImports,\n hasConflictingDeclaration,\n} from './import-utils.mjs';\n\n/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */\n\n/** `JSONSchema4`, reached through the public rule-module types. */\ntype JsonSchema = Exclude<\n TSESLint.RuleModule<string, readonly unknown[]>['meta']['schema'],\n readonly unknown[]\n>;\n\nexport type ImportStyle = 'global' | 'named';\n\nexport const DEFAULT_IMPORT_STYLE: ImportStyle = 'named';\n\nexport const IMPORT_STYLE_SCHEMA_PROPERTY: JsonSchema = {\n type: 'string',\n enum: ['global', 'named'],\n description: [\n 'How the ts-type-forge type is brought into scope.',\n \"'named' (default) makes the autofix add the corresponding\",\n `\\`import { type … } from '${TS_TYPE_FORGE_MODULE}';\\``,\n \"when the name is not imported yet; 'global' assumes the\",\n 'ambient globals of `ts-type-forge/global` and never touches',\n 'imports.',\n ].join(' '),\n};\n\n/**\n * Resolves how a ts-type-forge type name should be written in this file, and\n * whether the fix has to add an import for it.\n *\n * Returns `undefined` when the name is already bound to something else (a local\n * alias, or an import from another module) — rewriting to it would then silently\n * resolve to the wrong declaration, which is exactly what happens inside\n * ts-type-forge's own sources.\n */\nexport const resolveTypeName = (\n program: TSESTree.Program,\n canonicalName: string,\n importStyle: ImportStyle,\n): Readonly<{ localName: string; needsImport: boolean }> | undefined => {\n const importedLocalName = getImportedLocalName(\n getTsTypeForgeImports(program),\n canonicalName,\n );\n\n if (importedLocalName !== undefined) {\n return { localName: importedLocalName, needsImport: false };\n }\n\n return hasConflictingDeclaration(program, canonicalName)\n ? undefined\n : { localName: canonicalName, needsImport: importStyle === 'named' };\n};\n"],"names":[],"mappings":";;;AAkBO,MAAM,oBAAA,GAAoC;AAE1C,MAAM,4BAAA,GAA2C;AAAA,EACtD,IAAA,EAAM,QAAA;AAAA,EACN,IAAA,EAAM,CAAC,QAAA,EAAU,OAAO,CAAA;AAAA,EACxB,WAAA,EAAa;AAAA,IACX,mDAAA;AAAA,IACA,2DAAA;AAAA,IACA,kCAA6B,oBAAoB,CAAA,IAAA,CAAA;AAAA,IACjD,yDAAA;AAAA,IACA,6DAAA;AAAA,IACA;AAAA,GACF,CAAE,KAAK,GAAG;AACZ;AAWO,MAAM,eAAA,GAAkB,CAC7B,OAAA,EACA,aAAA,EACA,WAAA,KACsE;AACtE,EAAA,MAAM,iBAAA,GAAoB,oBAAA;AAAA,IACxB,sBAAsB,OAAO,CAAA;AAAA,IAC7B;AAAA,GACF;AAEA,EAAA,IAAI,sBAAsB,MAAA,EAAW;AACnC,IAAA,OAAO,EAAE,SAAA,EAAW,iBAAA,EAAmB,WAAA,EAAa,KAAA,EAAM;AAAA,EAC5D;AAEA,EAAA,OAAO,yBAAA,CAA0B,OAAA,EAAS,aAAa,CAAA,GACnD,MAAA,GACA,EAAE,SAAA,EAAW,aAAA,EAAe,WAAA,EAAa,WAAA,KAAgB,OAAA,EAAQ;AACvE;;;;"}
1
+ {"version":3,"file":"import-style.mjs","sourceRoot":"","sources":["../../src/rules/import-style.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,oBAAoB,CAAC;AAY5B,MAAM,CAAC,MAAM,oBAAoB,GAAgB,OAAO,CAAC;AAEzD,MAAM,CAAC,MAAM,4BAA4B,GAAe;IACtD,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;IACzB,WAAW,EAAE;QACX,mDAAmD;QACnD,2DAA2D;QAC3D,6BAA6B,oBAAoB,MAAM;QACvD,yDAAyD;QACzD,6DAA6D;QAC7D,UAAU;KACX,CAAC,IAAI,CAAC,GAAG,CAAC;CACZ,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,OAAyB,EACzB,aAAqB,EACrB,WAAwB,EAC2C,EAAE;IACrE,MAAM,iBAAiB,GAAG,oBAAoB,CAC5C,qBAAqB,CAAC,OAAO,CAAC,EAC9B,aAAa,CACd,CAAC;IAEF,IAAI,iBAAiB,KAAK,SAAS,EAAE,CAAC;QACpC,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;IAC9D,CAAC;IAED,OAAO,yBAAyB,CAAC,OAAO,EAAE,aAAa,CAAC;QACtD,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,KAAK,OAAO,EAAE,CAAC;AACzE,CAAC,CAAC"}
@@ -1,35 +1,55 @@
1
- import { AST_NODE_TYPES } from '@typescript-eslint/utils';
1
+ import { AST_NODE_TYPES, } from '@typescript-eslint/utils';
2
2
  import { TS_TYPE_FORGE_MODULE } from './constants.mjs';
3
3
 
4
- const getTsTypeForgeImports = (program) => program.body.filter(
5
- (node) => node.type === AST_NODE_TYPES.ImportDeclaration && node.source.value === TS_TYPE_FORGE_MODULE
6
- );
7
- const getImportedLocalName = (importDeclarations, importedName) => importDeclarations.flatMap((declaration) => declaration.specifiers).find(
8
- (specifier) => specifier.type === AST_NODE_TYPES.ImportSpecifier && (specifier.imported.type === AST_NODE_TYPES.Identifier ? specifier.imported.name : specifier.imported.value) === importedName
9
- )?.local.name;
10
- const hasConflictingDeclaration = (program, name) => program.body.some((node) => {
11
- if (node.type === AST_NODE_TYPES.ImportDeclaration) {
12
- return node.source.value !== TS_TYPE_FORGE_MODULE && node.specifiers.some((specifier) => specifier.local.name === name);
13
- }
14
- return declaresName(
15
- node.type === AST_NODE_TYPES.ExportNamedDeclaration || node.type === AST_NODE_TYPES.ExportDefaultDeclaration ? node.declaration : node,
16
- name
17
- );
4
+
5
+ export const getTsTypeForgeImports = (program) => program.body.filter((node) => node.type === AST_NODE_TYPES.ImportDeclaration &&
6
+ node.source.value === TS_TYPE_FORGE_MODULE);
7
+
8
+
9
+
10
+
11
+
12
+
13
+ export const getImportedLocalName = (importDeclarations, importedName) => importDeclarations
14
+ .flatMap((declaration) => declaration.specifiers)
15
+ .find((specifier) => specifier.type === AST_NODE_TYPES.ImportSpecifier &&
16
+ (specifier.imported.type === AST_NODE_TYPES.Identifier
17
+ ? specifier.imported.name
18
+ : specifier.imported.value) === importedName)?.local.name;
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+ export const hasConflictingDeclaration = (program, name) => program.body.some((node) => {
28
+ if (node.type === AST_NODE_TYPES.ImportDeclaration) {
29
+ return (node.source.value !== TS_TYPE_FORGE_MODULE &&
30
+ node.specifiers.some((specifier) => specifier.local.name === name));
31
+ }
32
+ return declaresName(node.type === AST_NODE_TYPES.ExportNamedDeclaration ||
33
+ node.type === AST_NODE_TYPES.ExportDefaultDeclaration
34
+ ? node.declaration
35
+ : node, name);
18
36
  });
19
- const buildTypeImportFix = (fixer, program, name) => [
20
- fixer.insertTextBefore(
21
- program,
22
- `import { type ${name} } from '${TS_TYPE_FORGE_MODULE}';
23
- `
24
- )
37
+
38
+
39
+
40
+
41
+
42
+ export const buildTypeImportFix = (fixer, program, name) => [
43
+ fixer.insertTextBefore(program, `import { type ${name} } from '${TS_TYPE_FORGE_MODULE}';\n`),
25
44
  ];
26
45
  const declaresName = (node, name) => {
27
- if (node === null || node === void 0) return false;
28
- if (node.type === AST_NODE_TYPES.TSTypeAliasDeclaration || node.type === AST_NODE_TYPES.TSInterfaceDeclaration || node.type === AST_NODE_TYPES.TSEnumDeclaration) {
29
- return node.id.name === name;
30
- }
31
- return node.type === AST_NODE_TYPES.ClassDeclaration && node.id?.name === name;
46
+ if (node === null || node === undefined)
47
+ return false;
48
+ if (node.type === AST_NODE_TYPES.TSTypeAliasDeclaration ||
49
+ node.type === AST_NODE_TYPES.TSInterfaceDeclaration ||
50
+ node.type === AST_NODE_TYPES.TSEnumDeclaration) {
51
+ return node.id.name === name;
52
+ }
53
+ return (node.type === AST_NODE_TYPES.ClassDeclaration && node.id?.name === name);
32
54
  };
33
-
34
- export { buildTypeImportFix, getImportedLocalName, getTsTypeForgeImports, hasConflictingDeclaration };
35
- //# sourceMappingURL=import-utils.mjs.map
55
+ //# sourceMappingURL=import-utils.mjs.map
@@ -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 { TS_TYPE_FORGE_MODULE } from './constants.mjs';\n\n/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */\n\n/** Every `import ... from 'ts-type-forge'` declaration in the file. */\nexport const getTsTypeForgeImports = (\n program: TSESTree.Program,\n): readonly TSESTree.ImportDeclaration[] =>\n program.body.filter(\n (node): node is TSESTree.ImportDeclaration =>\n node.type === AST_NODE_TYPES.ImportDeclaration &&\n node.source.value === TS_TYPE_FORGE_MODULE,\n );\n\n/**\n * Returns the in-scope local name a given ts-type-forge export is imported as\n * (handling aliases such as `import type { NonEmptyArray as NEA }` → `'NEA'`),\n * or `undefined` when it is not imported. Used so autofixes reference the\n * binding that actually exists rather than the canonical name.\n */\nexport const getImportedLocalName = (\n importDeclarations: readonly TSESTree.ImportDeclaration[],\n importedName: string,\n): string | undefined =>\n importDeclarations\n .flatMap((declaration) => declaration.specifiers)\n .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\n/**\n * Whether the file already binds `name` to something that is not the\n * ts-type-forge export of the same name — a local type alias / interface /\n * class / enum, or an import from another module. Autofixes that introduce\n * `name` must bail out in that case, because the rewritten type would resolve\n * to the wrong declaration. (ts-type-forge's own sources, which declare\n * `NonEmptyArray` themselves, are the motivating example.)\n */\nexport const hasConflictingDeclaration = (\n program: TSESTree.Program,\n name: string,\n): boolean =>\n program.body.some((node) => {\n if (node.type === AST_NODE_TYPES.ImportDeclaration) {\n return (\n node.source.value !== TS_TYPE_FORGE_MODULE &&\n node.specifiers.some((specifier) => specifier.local.name === name)\n );\n }\n\n return declaresName(\n node.type === AST_NODE_TYPES.ExportNamedDeclaration ||\n node.type === AST_NODE_TYPES.ExportDefaultDeclaration\n ? node.declaration\n : node,\n name,\n );\n });\n\n/**\n * Inserts `import { type <name> } from 'ts-type-forge';` at the top of the\n * file. Placement is deliberately naive: prettier-plugin-organize-imports (or\n * any equivalent) merges and sorts it afterwards.\n */\nexport const buildTypeImportFix = (\n fixer: TSESLint.RuleFixer,\n program: TSESTree.Program,\n name: string,\n): readonly TSESLint.RuleFix[] =>\n [\n fixer.insertTextBefore(\n program,\n `import { type ${name} } from '${TS_TYPE_FORGE_MODULE}';\\n`,\n ),\n ] as const;\n\nconst declaresName = (\n node: TSESTree.Node | null | undefined,\n name: string,\n): boolean => {\n if (node === null || node === undefined) return false;\n\n if (\n node.type === AST_NODE_TYPES.TSTypeAliasDeclaration ||\n node.type === AST_NODE_TYPES.TSInterfaceDeclaration ||\n node.type === AST_NODE_TYPES.TSEnumDeclaration\n ) {\n return node.id.name === name;\n }\n\n return (\n node.type === AST_NODE_TYPES.ClassDeclaration && node.id?.name === name\n );\n};\n"],"names":[],"mappings":";;;AAUO,MAAM,qBAAA,GAAwB,CACnC,OAAA,KAEA,OAAA,CAAQ,IAAA,CAAK,MAAA;AAAA,EACX,CAAC,SACC,IAAA,CAAK,IAAA,KAAS,eAAe,iBAAA,IAC7B,IAAA,CAAK,OAAO,KAAA,KAAU;AAC1B;AAQK,MAAM,oBAAA,GAAuB,CAClC,kBAAA,EACA,YAAA,KAEA,kBAAA,CACG,QAAQ,CAAC,WAAA,KAAgB,WAAA,CAAY,UAAU,CAAA,CAC/C,IAAA;AAAA,EACC,CAAC,SAAA,KACC,SAAA,CAAU,IAAA,KAAS,cAAA,CAAe,oBACjC,SAAA,CAAU,QAAA,CAAS,IAAA,KAAS,cAAA,CAAe,aACxC,SAAA,CAAU,QAAA,CAAS,IAAA,GACnB,SAAA,CAAU,SAAS,KAAA,MAAW;AACtC,CAAA,EAAG,KAAA,CAAM;AAUN,MAAM,yBAAA,GAA4B,CACvC,OAAA,EACA,IAAA,KAEA,QAAQ,IAAA,CAAK,IAAA,CAAK,CAAC,IAAA,KAAS;AAC1B,EAAA,IAAI,IAAA,CAAK,IAAA,KAAS,cAAA,CAAe,iBAAA,EAAmB;AAClD,IAAA,OACE,IAAA,CAAK,MAAA,CAAO,KAAA,KAAU,oBAAA,IACtB,IAAA,CAAK,UAAA,CAAW,IAAA,CAAK,CAAC,SAAA,KAAc,SAAA,CAAU,KAAA,CAAM,IAAA,KAAS,IAAI,CAAA;AAAA,EAErE;AAEA,EAAA,OAAO,YAAA;AAAA,IACL,IAAA,CAAK,SAAS,cAAA,CAAe,sBAAA,IAC3B,KAAK,IAAA,KAAS,cAAA,CAAe,wBAAA,GAC3B,IAAA,CAAK,WAAA,GACL,IAAA;AAAA,IACJ;AAAA,GACF;AACF,CAAC;AAOI,MAAM,kBAAA,GAAqB,CAChC,KAAA,EACA,OAAA,EACA,IAAA,KAEA;AAAA,EACE,KAAA,CAAM,gBAAA;AAAA,IACJ,OAAA;AAAA,IACA,CAAA,cAAA,EAAiB,IAAI,CAAA,SAAA,EAAY,oBAAoB,CAAA;AAAA;AAAA;AAEzD;AAEF,MAAM,YAAA,GAAe,CACnB,IAAA,EACA,IAAA,KACY;AACZ,EAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,IAAA,KAAS,MAAA,EAAW,OAAO,KAAA;AAEhD,EAAA,IACE,IAAA,CAAK,IAAA,KAAS,cAAA,CAAe,sBAAA,IAC7B,IAAA,CAAK,IAAA,KAAS,cAAA,CAAe,sBAAA,IAC7B,IAAA,CAAK,IAAA,KAAS,cAAA,CAAe,iBAAA,EAC7B;AACA,IAAA,OAAO,IAAA,CAAK,GAAG,IAAA,KAAS,IAAA;AAAA,EAC1B;AAEA,EAAA,OACE,KAAK,IAAA,KAAS,cAAA,CAAe,gBAAA,IAAoB,IAAA,CAAK,IAAI,IAAA,KAAS,IAAA;AAEvE,CAAA;;;;"}
1
+ {"version":3,"file":"import-utils.mjs","sourceRoot":"","sources":["../../src/rules/import-utils.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAGf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEvD,uEAAuE;AAEvE,uEAAuE;AACvE,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,OAAyB,EACc,EAAE,CACzC,OAAO,CAAC,IAAI,CAAC,MAAM,CACjB,CAAC,IAAI,EAAsC,EAAE,CAC3C,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,iBAAiB;IAC9C,IAAI,CAAC,MAAM,CAAC,KAAK,KAAK,oBAAoB,CAC7C,CAAC;AAEJ;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,kBAAyD,EACzD,YAAoB,EACA,EAAE,CACtB,kBAAkB;KACf,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC;KAChD,IAAI,CACH,CAAC,SAAS,EAAE,EAAE,CACZ,SAAS,CAAC,IAAI,KAAK,cAAc,CAAC,eAAe;IACjD,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU;QACpD,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI;QACzB,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,YAAY,CACjD,EAAE,KAAK,CAAC,IAAI,CAAC;AAElB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACvC,OAAyB,EACzB,IAAY,EACH,EAAE,CACX,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;IACzB,IAAI,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,iBAAiB,EAAE,CAAC;QACnD,OAAO,CACL,IAAI,CAAC,MAAM,CAAC,KAAK,KAAK,oBAAoB;YAC1C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CACnE,CAAC;IACJ,CAAC;IAED,OAAO,YAAY,CACjB,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,sBAAsB;QACjD,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,wBAAwB;QACrD,CAAC,CAAC,IAAI,CAAC,WAAW;QAClB,CAAC,CAAC,IAAI,EACR,IAAI,CACL,CAAC;AACJ,CAAC,CAAC,CAAC;AAEL;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,KAAyB,EACzB,OAAyB,EACzB,IAAY,EACiB,EAAE,CAC/B;IACE,KAAK,CAAC,gBAAgB,CACpB,OAAO,EACP,iBAAiB,IAAI,YAAY,oBAAoB,MAAM,CAC5D;CACO,CAAC;AAEb,MAAM,YAAY,GAAG,CACnB,IAAsC,EACtC,IAAY,EACH,EAAE;IACX,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAEtD,IACE,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,sBAAsB;QACnD,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,sBAAsB;QACnD,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,iBAAiB,EAC9C,CAAC;QACD,OAAO,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC;IAC/B,CAAC;IAED,OAAO,CACL,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,gBAAgB,IAAI,IAAI,CAAC,EAAE,EAAE,IAAI,KAAK,IAAI,CACxE,CAAC;AACJ,CAAC,CAAC"}
@@ -1,2 +1,2 @@
1
- export { tsTypeForgeRules } from './rules.mjs';
2
- //# sourceMappingURL=index.mjs.map
1
+ export * from './rules.mjs';
2
+ //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../src/rules/index.mts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { type TSESLint } from '@typescript-eslint/utils';
2
+ type Options = readonly [];
3
+ type MessageIds = 'removeSideEffectImport';
4
+ /**
5
+ * `import 'ts-type-forge';` binds nothing, and there is nothing for it to run:
6
+ * the package ships declarations only, and its `exports` map offers no runtime
7
+ * condition at all, so the import fails to resolve the moment the module graph
8
+ * is loaded. TypeScript keeps a side-effect import in the emitted JavaScript —
9
+ * that is the one kind it never elides — so this is a runtime error waiting in
10
+ * a file that type-checks.
11
+ *
12
+ * The ambient globals are a separate entry point, `ts-type-forge/global`,
13
+ * usually brought in through `compilerOptions.types` or a triple-slash
14
+ * reference. This rule matches the bare specifier exactly, so that subpath is
15
+ * left alone.
16
+ */
17
+ export declare const noSideEffectImport: TSESLint.RuleModule<MessageIds, Options>;
18
+ export {};
19
+ //# 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;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,UAAU,CAAC,UAAU,EAAE,OAAO,CA8B9D,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { Arr } from 'ts-data-forge';
2
+ import { TS_TYPE_FORGE_MODULE } from './constants.mjs';
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+ export const noSideEffectImport = {
17
+ meta: {
18
+ type: 'problem',
19
+ docs: {
20
+ description: `Remove a side-effect-only \`import '${TS_TYPE_FORGE_MODULE}';\`, which binds no name and has no runtime entry point to resolve to. \`${TS_TYPE_FORGE_MODULE}/global\` is left alone.`,
21
+ },
22
+ fixable: 'code',
23
+ schema: [],
24
+ messages: {
25
+ removeSideEffectImport: `\`import '${TS_TYPE_FORGE_MODULE}';\` binds no name, and \`${TS_TYPE_FORGE_MODULE}\` ships types only — it has no runtime entry point, so the import throws when the module is loaded. Remove it; the ambient globals come from \`${TS_TYPE_FORGE_MODULE}/global\`, not from this.`,
26
+ },
27
+ },
28
+ create: (context) => ({
29
+ ImportDeclaration: (node) => {
30
+ if (node.source.value !== TS_TYPE_FORGE_MODULE ||
31
+ !Arr.isEmpty(node.specifiers)) {
32
+ return;
33
+ }
34
+ context.report({
35
+ node,
36
+ messageId: 'removeSideEffectImport',
37
+ fix: (fixer) => removeStatement(fixer, context.sourceCode, node),
38
+ });
39
+ },
40
+ }),
41
+ defaultOptions: [],
42
+ };
43
+
44
+
45
+
46
+
47
+
48
+ const removeStatement = (fixer, sourceCode, node) => {
49
+ const restOfLine = /^[^\S\n]*\n/u.exec(sourceCode.getText().slice(node.range[1]));
50
+ return fixer.removeRange([
51
+ node.range[0],
52
+ node.range[1] + (restOfLine?.[0].length ?? 0),
53
+ ]);
54
+ };
55
+ //# sourceMappingURL=no-side-effect-import.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-side-effect-import.mjs","sourceRoot":"","sources":["../../src/rules/no-side-effect-import.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAMvD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAA6C;IAC1E,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,uCAAuC,oBAAoB,6EAA6E,oBAAoB,0BAA0B;SACpM;QACD,OAAO,EAAE,MAAM;QACf,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,sBAAsB,EAAE,aAAa,oBAAoB,6BAA6B,oBAAoB,mJAAmJ,oBAAoB,2BAA2B;SAC7S;KACF;IAED,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACpB,iBAAiB,EAAE,CAAC,IAAI,EAAE,EAAE;YAC1B,IACE,IAAI,CAAC,MAAM,CAAC,KAAK,KAAK,oBAAoB;gBAC1C,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAC7B,CAAC;gBACD,OAAO;YACT,CAAC;YAED,OAAO,CAAC,MAAM,CAAC;gBACb,IAAI;gBACJ,SAAS,EAAE,wBAAwB;gBACnC,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC;aACjE,CAAC,CAAC;QACL,CAAC;KACF,CAAC;IACF,cAAc,EAAE,EAAE;CACV,CAAC;AAEX,uEAAuE;AAEvE;;;GAGG;AACH,MAAM,eAAe,GAAG,CACtB,KAAyB,EACzB,UAA+B,EAC/B,IAAmB,EACD,EAAE;IACpB,MAAM,UAAU,GAAG,cAAc,CAAC,IAAI,CACpC,UAAU,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAC1C,CAAC;IAEF,OAAO,KAAK,CAAC,WAAW,CAAC;QACvB,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QACb,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC;KAC9C,CAAC,CAAC;AACL,CAAC,CAAC"}
@@ -1,82 +1,102 @@
1
1
  import { analyzeUniformTuple } from './ast-utils.mjs';
2
- import { DEFAULT_MAX_TUPLE_LENGTH, NON_EMPTY_TUPLE_TYPE_NAME, MUTABLE_NON_EMPTY_TUPLE_TYPE_NAME, MIN_LENGTH_TUPLE_TYPE_NAME, MUTABLE_MIN_LENGTH_TUPLE_TYPE_NAME, FIXED_LENGTH_TUPLE_TYPE_NAME, MUTABLE_FIXED_LENGTH_TUPLE_TYPE_NAME } from './constants.mjs';
3
- import { DEFAULT_IMPORT_STYLE, IMPORT_STYLE_SCHEMA_PROPERTY } from './import-style.mjs';
2
+ import { DEFAULT_MAX_TUPLE_LENGTH, FIXED_LENGTH_TUPLE_TYPE_NAME, MIN_LENGTH_TUPLE_TYPE_NAME, MUTABLE_FIXED_LENGTH_TUPLE_TYPE_NAME, MUTABLE_MIN_LENGTH_TUPLE_TYPE_NAME, MUTABLE_NON_EMPTY_TUPLE_TYPE_NAME, NON_EMPTY_TUPLE_TYPE_NAME, } from './constants.mjs';
3
+ import { DEFAULT_IMPORT_STYLE, IMPORT_STYLE_SCHEMA_PROPERTY, } from './import-style.mjs';
4
4
  import { reportTypeRewrites } from './rewrite-rule-utils.mjs';
5
5
 
6
+
7
+
8
+
6
9
  const MIN_FIXED_LENGTH = 2;
7
- const preferCanonicalLengthConstrainedTuple = {
8
- meta: {
9
- type: "suggestion",
10
- docs: {
11
- description: "Replace uniform tuple spellings such as `[V, ...V[]]` / `[V, V, ...V[]]` / `[V, V, V]` with the canonical ts-type-forge tuple type."
12
- },
13
- fixable: "code",
14
- schema: [
15
- {
16
- type: "object",
17
- properties: {
18
- importStyle: IMPORT_STYLE_SCHEMA_PROPERTY,
19
- maxLength: {
20
- type: "integer",
21
- minimum: MIN_FIXED_LENGTH,
22
- description: [
23
- "Longest tuple the rule rewrites (default",
24
- `${DEFAULT_MAX_TUPLE_LENGTH}).`,
25
- "Longer tuples are usually spelled out on purpose, and the named",
26
- "form stops being more readable."
27
- ].join(" ")
28
- }
10
+ export const preferCanonicalLengthConstrainedTuple = {
11
+ meta: {
12
+ type: 'suggestion',
13
+ docs: {
14
+ description: 'Replace uniform tuple spellings such as `[V, ...V[]]` / `[V, V, ...V[]]` / `[V, V, V]` with the canonical ts-type-forge tuple type.',
15
+ },
16
+ fixable: 'code',
17
+ schema: [
18
+ {
19
+ type: 'object',
20
+ properties: {
21
+ importStyle: IMPORT_STYLE_SCHEMA_PROPERTY,
22
+ maxLength: {
23
+ type: 'integer',
24
+ minimum: MIN_FIXED_LENGTH,
25
+ description: [
26
+ 'Longest tuple the rule rewrites (default',
27
+ `${DEFAULT_MAX_TUPLE_LENGTH}).`,
28
+ 'Longer tuples are usually spelled out on purpose, and the named',
29
+ 'form stops being more readable.',
30
+ ].join(' '),
31
+ },
32
+ },
33
+ additionalProperties: false,
34
+ },
35
+ ],
36
+ messages: {
37
+ useCanonicalTuple: 'Replace `{{original}}` with `{{replacement}}` from ts-type-forge.',
29
38
  },
30
- additionalProperties: false
31
- }
39
+ },
40
+ create: (context) => {
41
+ const sourceCode = context.sourceCode;
42
+ const options = context.options[0];
43
+ const importStyle = options?.importStyle ?? DEFAULT_IMPORT_STYLE;
44
+ const maxLength = options?.maxLength ?? DEFAULT_MAX_TUPLE_LENGTH;
45
+ const mut_rewrites = [];
46
+ return {
47
+ TSTupleType: (node) => {
48
+ const shape = analyzeUniformTuple(node, sourceCode);
49
+ if (shape === undefined || shape.fixedCount > maxLength)
50
+ return;
51
+ const canonicalName = canonicalNameFor(shape);
52
+ if (canonicalName === undefined)
53
+ return;
54
+ mut_rewrites.push({
55
+ node: shape.node,
56
+ canonicalName,
57
+ typeArgs: shape.fixedCount === 1
58
+ ? shape.elementText
59
+ : `${shape.fixedCount}, ${shape.elementText}`,
60
+ });
61
+ },
62
+ 'Program:exit': () => {
63
+ reportTypeRewrites(context, mut_rewrites, 'useCanonicalTuple', importStyle);
64
+ },
65
+ };
66
+ },
67
+ defaultOptions: [
68
+ { importStyle: DEFAULT_IMPORT_STYLE, maxLength: DEFAULT_MAX_TUPLE_LENGTH },
32
69
  ],
33
- messages: {
34
- useCanonicalTuple: "Replace `{{original}}` with `{{replacement}}` from ts-type-forge."
35
- }
36
- },
37
- create: (context) => {
38
- const sourceCode = context.sourceCode;
39
- const options = context.options[0];
40
- const importStyle = options?.importStyle ?? DEFAULT_IMPORT_STYLE;
41
- const maxLength = options?.maxLength ?? DEFAULT_MAX_TUPLE_LENGTH;
42
- const mut_rewrites = [];
43
- return {
44
- TSTupleType: (node) => {
45
- const shape = analyzeUniformTuple(node, sourceCode);
46
- if (shape === void 0 || shape.fixedCount > maxLength) return;
47
- const canonicalName = canonicalNameFor(shape);
48
- if (canonicalName === void 0) return;
49
- mut_rewrites.push({
50
- node: shape.node,
51
- canonicalName,
52
- typeArgs: shape.fixedCount === 1 ? shape.elementText : `${shape.fixedCount}, ${shape.elementText}`
53
- });
54
- },
55
- "Program:exit": () => {
56
- reportTypeRewrites(
57
- context,
58
- mut_rewrites,
59
- "useCanonicalTuple",
60
- importStyle
61
- );
62
- }
63
- };
64
- },
65
- defaultOptions: [
66
- { importStyle: DEFAULT_IMPORT_STYLE, maxLength: DEFAULT_MAX_TUPLE_LENGTH }
67
- ]
68
70
  };
69
- const canonicalNameFor = (shape) => {
70
- const { isReadonly, hasRest, fixedCount } = shape;
71
- if (hasRest) {
72
- if (fixedCount === 1) {
73
- return isReadonly ? NON_EMPTY_TUPLE_TYPE_NAME : MUTABLE_NON_EMPTY_TUPLE_TYPE_NAME;
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+ const canonicalNameFor = (
82
+
83
+
84
+ shape) => {
85
+ const { isReadonly, hasRest, fixedCount } = shape;
86
+ if (hasRest) {
87
+ if (fixedCount === 1) {
88
+ return isReadonly
89
+ ? NON_EMPTY_TUPLE_TYPE_NAME
90
+ : MUTABLE_NON_EMPTY_TUPLE_TYPE_NAME;
91
+ }
92
+ return isReadonly
93
+ ? MIN_LENGTH_TUPLE_TYPE_NAME
94
+ : MUTABLE_MIN_LENGTH_TUPLE_TYPE_NAME;
74
95
  }
75
- return isReadonly ? MIN_LENGTH_TUPLE_TYPE_NAME : MUTABLE_MIN_LENGTH_TUPLE_TYPE_NAME;
76
- }
77
- if (fixedCount < MIN_FIXED_LENGTH) return void 0;
78
- return isReadonly ? FIXED_LENGTH_TUPLE_TYPE_NAME : MUTABLE_FIXED_LENGTH_TUPLE_TYPE_NAME;
96
+ if (fixedCount < MIN_FIXED_LENGTH)
97
+ return undefined;
98
+ return isReadonly
99
+ ? FIXED_LENGTH_TUPLE_TYPE_NAME
100
+ : MUTABLE_FIXED_LENGTH_TUPLE_TYPE_NAME;
79
101
  };
80
-
81
- export { preferCanonicalLengthConstrainedTuple };
82
- //# sourceMappingURL=prefer-canonical-length-constrained-tuple.mjs.map
102
+ //# sourceMappingURL=prefer-canonical-length-constrained-tuple.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-canonical-length-constrained-tuple.mjs","sources":["../../src/rules/prefer-canonical-length-constrained-tuple.mts"],"sourcesContent":["import { type TSESLint } from '@typescript-eslint/utils';\nimport { analyzeUniformTuple, type UniformTupleShape } from './ast-utils.mjs';\nimport {\n DEFAULT_MAX_TUPLE_LENGTH,\n FIXED_LENGTH_TUPLE_TYPE_NAME,\n MIN_LENGTH_TUPLE_TYPE_NAME,\n MUTABLE_FIXED_LENGTH_TUPLE_TYPE_NAME,\n MUTABLE_MIN_LENGTH_TUPLE_TYPE_NAME,\n MUTABLE_NON_EMPTY_TUPLE_TYPE_NAME,\n NON_EMPTY_TUPLE_TYPE_NAME,\n} from './constants.mjs';\nimport {\n DEFAULT_IMPORT_STYLE,\n IMPORT_STYLE_SCHEMA_PROPERTY,\n} from './import-style.mjs';\nimport { reportTypeRewrites, type TypeRewrite } from './rewrite-rule-utils.mjs';\n\ntype Options = readonly [\n Readonly<{\n importStyle?: 'global' | 'named';\n maxLength?: number;\n }>?,\n];\n\ntype MessageIds = 'useCanonicalTuple';\n\n/**\n * A single fixed element without a rest (`[V]`) is left alone: it reads better\n * than `FixedLengthTuple<1, V>`.\n */\nconst MIN_FIXED_LENGTH = 2;\n\nexport const preferCanonicalLengthConstrainedTuple: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace uniform tuple spellings such as `[V, ...V[]]` / `[V, V, ...V[]]` / `[V, V, V]` with the canonical ts-type-forge tuple type.',\n },\n fixable: 'code',\n schema: [\n {\n type: 'object',\n properties: {\n importStyle: IMPORT_STYLE_SCHEMA_PROPERTY,\n maxLength: {\n type: 'integer',\n minimum: MIN_FIXED_LENGTH,\n description: [\n 'Longest tuple the rule rewrites (default',\n `${DEFAULT_MAX_TUPLE_LENGTH}).`,\n 'Longer tuples are usually spelled out on purpose, and the named',\n 'form stops being more readable.',\n ].join(' '),\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n useCanonicalTuple:\n 'Replace `{{original}}` with `{{replacement}}` from ts-type-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const options = context.options[0];\n\n const importStyle = options?.importStyle ?? DEFAULT_IMPORT_STYLE;\n\n const maxLength = options?.maxLength ?? DEFAULT_MAX_TUPLE_LENGTH;\n\n const mut_rewrites: TypeRewrite[] = [];\n\n return {\n TSTupleType: (node) => {\n const shape = analyzeUniformTuple(node, sourceCode);\n\n if (shape === undefined || shape.fixedCount > maxLength) return;\n\n const canonicalName = canonicalNameFor(shape);\n\n if (canonicalName === undefined) return;\n\n mut_rewrites.push({\n node: shape.node,\n canonicalName,\n typeArgs:\n shape.fixedCount === 1\n ? shape.elementText\n : `${shape.fixedCount}, ${shape.elementText}`,\n });\n },\n 'Program:exit': () => {\n reportTypeRewrites(\n context,\n mut_rewrites,\n 'useCanonicalTuple',\n importStyle,\n );\n },\n };\n },\n defaultOptions: [\n { importStyle: DEFAULT_IMPORT_STYLE, maxLength: DEFAULT_MAX_TUPLE_LENGTH },\n ],\n} as const;\n\n/**\n * The ts-type-forge type a given tuple shape spells out, or `undefined` when the\n * shape is deliberately left alone.\n *\n * | shape | readonly | mutable |\n * | :-------------------- | :------------------ | :------------------------- |\n * | `[V, ...V[]]` | `NonEmptyTuple` | `MutableNonEmptyTuple` |\n * | `[V, …×N, ...V[]]` | `MinLengthTuple` | `MutableMinLengthTuple` |\n * | `[V, …×N]` (N >= 2) | `FixedLengthTuple` | `MutableFixedLengthTuple` |\n */\nconst canonicalNameFor = (\n // `UniformTupleShape` carries a `TSESTree.Node`, which is not deeply readonly.\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n shape: UniformTupleShape,\n): string | undefined => {\n const { isReadonly, hasRest, fixedCount } = shape;\n\n if (hasRest) {\n if (fixedCount === 1) {\n return isReadonly\n ? NON_EMPTY_TUPLE_TYPE_NAME\n : MUTABLE_NON_EMPTY_TUPLE_TYPE_NAME;\n }\n\n return isReadonly\n ? MIN_LENGTH_TUPLE_TYPE_NAME\n : MUTABLE_MIN_LENGTH_TUPLE_TYPE_NAME;\n }\n\n if (fixedCount < MIN_FIXED_LENGTH) return undefined;\n\n return isReadonly\n ? FIXED_LENGTH_TUPLE_TYPE_NAME\n : MUTABLE_FIXED_LENGTH_TUPLE_TYPE_NAME;\n};\n"],"names":[],"mappings":";;;;;AA8BA,MAAM,gBAAA,GAAmB,CAAA;AAElB,MAAM,qCAAA,GAGT;AAAA,EACF,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,YAAA;AAAA,IACN,IAAA,EAAM;AAAA,MACJ,WAAA,EACE;AAAA,KACJ;AAAA,IACA,OAAA,EAAS,MAAA;AAAA,IACT,MAAA,EAAQ;AAAA,MACN;AAAA,QACE,IAAA,EAAM,QAAA;AAAA,QACN,UAAA,EAAY;AAAA,UACV,WAAA,EAAa,4BAAA;AAAA,UACb,SAAA,EAAW;AAAA,YACT,IAAA,EAAM,SAAA;AAAA,YACN,OAAA,EAAS,gBAAA;AAAA,YACT,WAAA,EAAa;AAAA,cACX,0CAAA;AAAA,cACA,GAAG,wBAAwB,CAAA,EAAA,CAAA;AAAA,cAC3B,iEAAA;AAAA,cACA;AAAA,aACF,CAAE,KAAK,GAAG;AAAA;AACZ,SACF;AAAA,QACA,oBAAA,EAAsB;AAAA;AACxB,KACF;AAAA,IACA,QAAA,EAAU;AAAA,MACR,iBAAA,EACE;AAAA;AACJ,GACF;AAAA,EAEA,MAAA,EAAQ,CAAC,OAAA,KAAY;AACnB,IAAA,MAAM,aAAa,OAAA,CAAQ,UAAA;AAE3B,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAA;AAEjC,IAAA,MAAM,WAAA,GAAc,SAAS,WAAA,IAAe,oBAAA;AAE5C,IAAA,MAAM,SAAA,GAAY,SAAS,SAAA,IAAa,wBAAA;AAExC,IAAA,MAAM,eAA8B,EAAC;AAErC,IAAA,OAAO;AAAA,MACL,WAAA,EAAa,CAAC,IAAA,KAAS;AACrB,QAAA,MAAM,KAAA,GAAQ,mBAAA,CAAoB,IAAA,EAAM,UAAU,CAAA;AAElD,QAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,CAAM,UAAA,GAAa,SAAA,EAAW;AAEzD,QAAA,MAAM,aAAA,GAAgB,iBAAiB,KAAK,CAAA;AAE5C,QAAA,IAAI,kBAAkB,MAAA,EAAW;AAEjC,QAAA,YAAA,CAAa,IAAA,CAAK;AAAA,UAChB,MAAM,KAAA,CAAM,IAAA;AAAA,UACZ,aAAA;AAAA,UACA,QAAA,EACE,KAAA,CAAM,UAAA,KAAe,CAAA,GACjB,KAAA,CAAM,WAAA,GACN,CAAA,EAAG,KAAA,CAAM,UAAU,CAAA,EAAA,EAAK,KAAA,CAAM,WAAW,CAAA;AAAA,SAChD,CAAA;AAAA,MACH,CAAA;AAAA,MACA,gBAAgB,MAAM;AACpB,QAAA,kBAAA;AAAA,UACE,OAAA;AAAA,UACA,YAAA;AAAA,UACA,mBAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF;AAAA,KACF;AAAA,EACF,CAAA;AAAA,EACA,cAAA,EAAgB;AAAA,IACd,EAAE,WAAA,EAAa,oBAAA,EAAsB,SAAA,EAAW,wBAAA;AAAyB;AAE7E;AAYA,MAAM,gBAAA,GAAmB,CAGvB,KAAA,KACuB;AACvB,EAAA,MAAM,EAAE,UAAA,EAAY,OAAA,EAAS,UAAA,EAAW,GAAI,KAAA;AAE5C,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,IAAI,eAAe,CAAA,EAAG;AACpB,MAAA,OAAO,aACH,yBAAA,GACA,iCAAA;AAAA,IACN;AAEA,IAAA,OAAO,aACH,0BAAA,GACA,kCAAA;AAAA,EACN;AAEA,EAAA,IAAI,UAAA,GAAa,kBAAkB,OAAO,MAAA;AAE1C,EAAA,OAAO,aACH,4BAAA,GACA,oCAAA;AACN,CAAA;;;;"}
1
+ {"version":3,"file":"prefer-canonical-length-constrained-tuple.mjs","sourceRoot":"","sources":["../../src/rules/prefer-canonical-length-constrained-tuple.mts"],"names":[],"mappings":"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"}