eslint-rule-forge 0.3.2 → 0.4.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.
@@ -1,6 +1,7 @@
1
1
  import { AST_NODE_TYPES, } from '@typescript-eslint/utils';
2
2
  import * as ts from 'typescript';
3
- import { buildNamedImportFix, getImportDeclarations, getNamedImports, } from '../imports.mjs';
3
+ import { hasForeignBinding } from '../bindings.mjs';
4
+ import { bindValueImports } from '../imports.mjs';
4
5
  import { getArgumentText } from '../parentheses.mjs';
5
6
  import { isIdentifierNamed, skipTypeWrappers, typeHoldsAtEveryLayer, } from '../type-wrappers.mjs';
6
7
 
@@ -24,8 +25,6 @@ export const createPreferIsRecordAndHasKey = ({ moduleName, }) => ({
24
25
  },
25
26
  create: (context) => {
26
27
  const sourceCode = context.sourceCode;
27
- const program = sourceCode.ast;
28
- const moduleImports = getImportDeclarations(program, moduleName);
29
28
 
30
29
 
31
30
 
@@ -100,59 +99,48 @@ export const createPreferIsRecordAndHasKey = ({ moduleName, }) => ({
100
99
  });
101
100
  },
102
101
  'Program:exit': () => {
103
- const namedImports = getNamedImports(moduleImports);
104
- const rewrites = mut_nodesToFix.map((entry) => {
105
102
 
106
103
 
107
- const needsIsRecord = !isKnownRecord(entry.objExpression);
108
- return {
109
- ...entry,
110
- needsIsRecord,
104
+ const withIsRecord = bindValueImports(sourceCode, moduleName, [
105
+ 'isRecord',
106
+ 'hasKey',
107
+ ]);
108
+ const hasKeyOnly = bindValueImports(sourceCode, moduleName, [
109
+ 'hasKey',
110
+ ]);
111
+ for (const { node, objExpression, keyExpression } of mut_nodesToFix) {
111
112
 
112
- fixable: !needsIsRecord || isSideEffectFree(entry.objExpression),
113
- };
114
- });
115
- const fixableRewrites = rewrites.filter(({ fixable }) => fixable);
116
- const importsToAdd = fixableRewrites.length === 0
117
- ? []
118
- : [
119
- ...(fixableRewrites.some(({ needsIsRecord }) => needsIsRecord) && !namedImports.includes('isRecord')
120
- ? ['isRecord']
121
- : []),
122
- ...(namedImports.includes('hasKey') ? [] : ['hasKey']),
123
- ];
124
- const importCarrier = fixableRewrites[0];
125
- for (const rewrite of rewrites) {
126
- const { node, objExpression, keyExpression, needsIsRecord } = rewrite;
113
+
114
+ const needsIsRecord = !isKnownRecord(objExpression);
115
+ const binding = needsIsRecord ? withIsRecord : hasKeyOnly;
116
+ const hasKeyName = binding?.localNames.at(-1);
117
+ const isRecordName = needsIsRecord
118
+ ? binding?.localNames[0]
119
+ : undefined;
127
120
  const objText = getArgumentText(sourceCode, objExpression);
128
121
  const keyText = getArgumentText(sourceCode, keyExpression);
129
- const originalText = sourceCode.getText(node);
122
+ const fixable = binding !== undefined &&
123
+ hasKeyName !== undefined &&
124
+ (!needsIsRecord || isRecordName !== undefined) &&
125
+
126
+ (!needsIsRecord || isSideEffectFree(objExpression)) &&
127
+ binding.localNames.every((name) => !hasForeignBinding(sourceCode, node, name, moduleName));
130
128
  context.report({
131
129
  node,
132
130
  messageId: needsIsRecord ? 'useIsRecordAndHasKey' : 'useHasKey',
133
131
  data: {
134
- original: originalText,
132
+ original: sourceCode.getText(node),
135
133
  objName: objText,
136
134
  keyName: keyText,
137
135
  },
138
- fix: !rewrite.fixable
139
- ? undefined
140
- : (fixer) => {
141
- const replacement = needsIsRecord
142
- ? `(isRecord(${objText}) && hasKey(${objText}, ${keyText}))`
143
- : `hasKey(${objText}, ${keyText})`;
144
-
145
-
146
-
147
-
148
- const importFixes = rewrite === importCarrier && importsToAdd.length > 0
149
- ? buildNamedImportFix(fixer, program, moduleName, importsToAdd)
150
- : [];
151
- return [
152
- ...importFixes,
153
- fixer.replaceText(node, replacement),
154
- ];
155
- },
136
+ fix: fixable
137
+ ? (fixer) => [
138
+ ...binding.edits(fixer),
139
+ fixer.replaceText(node, needsIsRecord
140
+ ? `(${isRecordName}(${objText}) && ${hasKeyName}(${objText}, ${keyText}))`
141
+ : `${hasKeyName}(${objText}, ${keyText})`),
142
+ ]
143
+ : null,
156
144
  });
157
145
  }
158
146
  },
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-is-record-and-has-key.mjs","sourceRoot":"","sources":["../../src/rules/prefer-is-record-and-has-key.mts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prefer-is-record-and-has-key.mjs","sourceRoot":"","sources":["../../src/rules/prefer-is-record-and-has-key.mts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-safe-parse.d.mts","sourceRoot":"","sources":["../../src/rules/prefer-safe-parse.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAelC,yEAAyE;AACzE,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAC;IAEnB,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,WAC3B,mBAAmB,KAC1B,QAAQ,CAAC,UAAU,CAAC,iBAAiB,EAAE,SAAS,EAAE,CAsBjD,CAAC;AAEL;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,WAC7B,mBAAmB,KAC1B,QAAQ,CAAC,UAAU,CAAC,mBAAmB,EAAE,SAAS,EAAE,CAUnD,CAAC"}
1
+ {"version":3,"file":"prefer-safe-parse.d.mts","sourceRoot":"","sources":["../../src/rules/prefer-safe-parse.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAYlC,yEAAyE;AACzE,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAC;IAEnB,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,WAC3B,mBAAmB,KAC1B,QAAQ,CAAC,UAAU,CAAC,iBAAiB,EAAE,SAAS,EAAE,CAsBjD,CAAC;AAEL;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,WAC7B,mBAAmB,KAC1B,QAAQ,CAAC,UAAU,CAAC,mBAAmB,EAAE,SAAS,EAAE,CAUnD,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { AST_NODE_TYPES, } from '@typescript-eslint/utils';
2
2
  import * as ts from 'typescript';
3
- import { buildNamedImportFix, getImportDeclarations, getNamedImports, } from '../imports.mjs';
3
+ import { hasForeignBinding } from '../bindings.mjs';
4
+ import { bindValueImports } from '../imports.mjs';
4
5
  import { getArgumentText } from '../parentheses.mjs';
5
6
  import { isIdentifierNamed, skipTypeWrappers, typeHoldsAtEveryLayer, } from '../type-wrappers.mjs';
6
7
 
@@ -44,7 +45,6 @@ export const createPreferSafeParseFloat = (config) => createSafeParseRule(config
44
45
  });
45
46
 
46
47
  const createSafeParseRule = ({ moduleName, namespace, method }, messageId, { messages, description, isTarget, }) => {
47
- const parseFunction = `${namespace}.${method}`;
48
48
 
49
49
  const requiredImports = [namespace, 'Result'].toSorted();
50
50
  return {
@@ -58,8 +58,6 @@ const createSafeParseRule = ({ moduleName, namespace, method }, messageId, { mes
58
58
  },
59
59
  create: (context) => {
60
60
  const sourceCode = context.sourceCode;
61
- const program = sourceCode.ast;
62
- const moduleImports = getImportDeclarations(program, moduleName);
63
61
  const mut_nodesToFix = [];
64
62
  return {
65
63
  CallExpression: (node) => {
@@ -86,12 +84,13 @@ const createSafeParseRule = ({ moduleName, namespace, method }, messageId, { mes
86
84
  mut_nodesToFix.push({ node, argExpression: firstArg });
87
85
  },
88
86
  'Program:exit': () => {
89
- const namedImports = getNamedImports(moduleImports);
90
- const missingImports = requiredImports.filter((name) => !namedImports.includes(name));
87
+ const binding = bindValueImports(sourceCode, moduleName, requiredImports);
88
+ const localNameOf = (name) => binding?.localNames[requiredImports.indexOf(name)];
89
+ const namespaceName = localNameOf(namespace);
90
+ const resultName = localNameOf('Result');
91
91
 
92
92
 
93
-
94
- for (const [index, { node, argExpression },] of mut_nodesToFix.entries()) {
93
+ for (const { node, argExpression } of mut_nodesToFix) {
95
94
  const argText = getArgumentText(sourceCode, argExpression);
96
95
  context.report({
97
96
  node,
@@ -100,13 +99,15 @@ const createSafeParseRule = ({ moduleName, namespace, method }, messageId, { mes
100
99
  original: sourceCode.getText(node),
101
100
  argName: argText,
102
101
  },
103
- fix: (fixer) => {
104
- const core = `Result.unwrapOkOr(${parseFunction}(${argText}), Number.NaN)`;
105
- const importFixes = index === 0 && missingImports.length > 0
106
- ? buildNamedImportFix(fixer, program, moduleName, missingImports)
107
- : [];
108
- return [...importFixes, fixer.replaceText(node, core)];
109
- },
102
+ fix: binding === undefined ||
103
+ namespaceName === undefined ||
104
+ resultName === undefined ||
105
+ [namespaceName, resultName].some((name) => hasForeignBinding(sourceCode, node, name, moduleName))
106
+ ? null
107
+ : (fixer) => [
108
+ ...binding.edits(fixer),
109
+ fixer.replaceText(node, `${resultName}.unwrapOkOr(${namespaceName}.${method}(${argText}), Number.NaN)`),
110
+ ],
110
111
  });
111
112
  }
112
113
  },
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-safe-parse.mjs","sourceRoot":"","sources":["../../src/rules/prefer-safe-parse.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAGf,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAiB9B;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACtC,MAA2B,EAC0B,EAAE,CACvD,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,EAAE;IAC7C,QAAQ,EAAE,EAAE,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE;IAClD,WAAW,EAAE,kFAAkF,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,MAAM,2BAA2B,MAAM,CAAC,UAAU,GAAG;IAC/K,QAAQ,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE;QACzB,IACE,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC;YACjC,CAAC,cAAc,CAAC,MAAM,EAAE,UAAU,CAAC,EACnC,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,oEAAoE;QACpE,kEAAkE;QAClE,MAAM,KAAK,GACT,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAEhE,OAAO,CACL,KAAK,KAAK,SAAS;YACnB,CAAC,KAAK,CAAC,IAAI,KAAK,cAAc,CAAC,OAAO,IAAI,KAAK,CAAC,KAAK,KAAK,EAAE,CAAC,CAC9D,CAAC;IACJ,CAAC;CACF,CAAC,CAAC;AAEL;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACxC,MAA2B,EAC4B,EAAE,CACzD,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,EAAE;IAC/C,QAAQ,EAAE,EAAE,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE;IACpD,WAAW,EAAE,uHAAuH,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,MAAM,2BAA2B,MAAM,CAAC,UAAU,GAAG;IACpN,kEAAkE;IAClE,iDAAiD;IACjD,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CACnB,YAAY,CAAC,MAAM,EAAE,YAAY,CAAC;QAClC,cAAc,CAAC,MAAM,EAAE,YAAY,CAAC;QACpC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC;CACjC,CAAC,CAAC;AAEL,uEAAuE;AAEvE,MAAM,mBAAmB,GAAG,CAC1B,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAuB,EACtD,SAAoB,EACpB,EACE,QAAQ,EACR,WAAW,EACX,QAAQ,GAcR,EAC2C,EAAE;IAC/C,MAAM,aAAa,GAAG,GAAG,SAAS,IAAI,MAAM,EAAW,CAAC;IAExD,gDAAgD;IAChD,MAAM,eAAe,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;IAEzD,OAAO;QACL,IAAI,EAAE;YACJ,IAAI,EAAE,YAAY;YAClB,IAAI,EAAE,EAAE,WAAW,EAAE;YACrB,OAAO,EAAE,MAAM;YACf,MAAM,EAAE,EAAE;YACV,QAAQ;YACR,cAAc,EAAE,EAAE;SACnB;QAED,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE;YAClB,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;YAEtC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC;YAE/B,MAAM,aAAa,GAAG,qBAAqB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;YAEjE,MAAM,cAAc,GAGd,EAAE,CAAC;YAET,OAAO;gBACL,cAAc,EAAE,CAAC,IAAI,EAAE,EAAE;oBACvB,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;wBAC7D,OAAO;oBACT,CAAC;oBAED,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;oBAEnC,8DAA8D;oBAC9D,UAAU;oBACV,IACE,QAAQ,KAAK,SAAS;wBACtB,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,aAAa,EAC9C,CAAC;wBACD,OAAO;oBACT,CAAC;oBAED,8DAA8D;oBAC9D,kEAAkE;oBAClE,gEAAgE;oBAChE,oEAAoE;oBACpE,iEAAiE;oBACjE,kEAAkE;oBAClE,0BAA0B;oBAC1B,IAAI,CAAC,qBAAqB,CAAC,UAAU,EAAE,QAAQ,EAAE,YAAY,CAAC,EAAE,CAAC;wBAC/D,OAAO;oBACT,CAAC;oBAED,cAAc,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACzD,CAAC;gBACD,cAAc,EAAE,GAAG,EAAE;oBACnB,MAAM,YAAY,GAAG,eAAe,CAAC,aAAa,CAAC,CAAC;oBAEpD,MAAM,cAAc,GAAG,eAAe,CAAC,MAAM,CAC3C,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,CACvC,CAAC;oBAEF,iDAAiD;oBACjD,6DAA6D;oBAC7D,uDAAuD;oBACvD,KAAK,MAAM,CACT,KAAK,EACL,EAAE,IAAI,EAAE,aAAa,EAAE,EACxB,IAAI,cAAc,CAAC,OAAO,EAAE,EAAE,CAAC;wBAC9B,MAAM,OAAO,GAAG,eAAe,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;wBAE3D,OAAO,CAAC,MAAM,CAAC;4BACb,IAAI;4BACJ,SAAS;4BACT,IAAI,EAAE;gCACJ,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC;gCAClC,OAAO,EAAE,OAAO;6BACjB;4BACD,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE;gCACb,MAAM,IAAI,GACR,qBAAqB,aAAa,IAAI,OAAO,gBAAyB,CAAC;gCAEzE,MAAM,WAAW,GACf,KAAK,KAAK,CAAC,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC;oCACtC,CAAC,CAAC,mBAAmB,CACjB,KAAK,EACL,OAAO,EACP,UAAU,EACV,cAAc,CACf;oCACH,CAAC,CAAE,EAAY,CAAC;gCAEpB,OAAO,CAAC,GAAG,WAAW,EAAE,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;4BACzD,CAAC;yBACF,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;aACF,CAAC;QACJ,CAAC;KACO,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,CAAC,EACnB,UAAU,EACV,SAAS,EACT,MAAM,GACc,EAAU,EAAE,CAChC,qDAAqD,SAAS,IAAI,MAAM,qCAAqC,UAAU,GAAY,CAAC;AAEtI,6EAA6E;AAC7E,MAAM,YAAY,GAAG,CAAC,MAA2B,EAAE,IAAY,EAAW,EAAE,CAC1E,MAAM,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC;AAEpE;;;GAGG;AACH,MAAM,cAAc,GAAG,CAAC,MAA2B,EAAE,IAAY,EAAW,EAAE,CAC5E,MAAM,CAAC,IAAI,KAAK,cAAc,CAAC,gBAAgB;IAC/C,CAAC,MAAM,CAAC,QAAQ;IAChB,iBAAiB,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1C,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU;IAClD,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,IAAI,CAAC;AAEhC;;;;GAIG;AACH,MAAM,YAAY,GAAG,CAAC,IAAa,EAAW,EAAE;IAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,CAAW,CAAC;IAE9D,OAAO,CACL,KAAK,CAAC,MAAM,GAAG,CAAC;QAChB,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAC9D,CAAC;AACJ,CAAC,CAAC"}
1
+ {"version":3,"file":"prefer-safe-parse.mjs","sourceRoot":"","sources":["../../src/rules/prefer-safe-parse.mts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-rule-forge",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "description": "Helpers for writing typescript-eslint rules that see through type wrappers and parentheses, and resolve what a file imports.",
6
6
  "keywords": [
@@ -41,9 +41,9 @@
41
41
  "@typescript-eslint/rule-tester": "8.72.0",
42
42
  "dedent": "1.7.2",
43
43
  "eslint": "10.13.0",
44
- "eslint-config-typed": "5.20.1",
45
- "eslint-plugin-ts-fortress": "1.3.9",
46
- "eslint-plugin-ts-type-forge": "0.8.14",
44
+ "eslint-config-typed": "7.1.0",
45
+ "eslint-plugin-ts-fortress": "1.3.10",
46
+ "eslint-plugin-ts-type-forge": "0.8.15",
47
47
  "prettier": "3.9.10",
48
48
  "prettier-plugin-organize-imports": "4.3.0",
49
49
  "prettier-plugin-packagejson": "3.0.2",
@@ -0,0 +1,23 @@
1
+ import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
2
+ import type { DeepReadonly } from 'ts-type-forge';
3
+
4
+ /* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */
5
+
6
+ /**
7
+ * Whether a comment inside `node` lies within one of `ranges`, which a fix
8
+ * would delete along with it. Such a fix is dropped instead of the comment.
9
+ * Only comments inside `node` are looked at, so each range has to lie within
10
+ * it.
11
+ */
12
+ export const hasCommentWithin = (
13
+ sourceCode: TSESLint.SourceCode,
14
+ node: TSESTree.Node,
15
+ ranges: DeepReadonly<TSESTree.Range[]>,
16
+ ): boolean =>
17
+ sourceCode
18
+ .getCommentsInside(node)
19
+ .some((comment) =>
20
+ ranges.some(
21
+ ([start, end]) => start <= comment.range[0] && comment.range[1] <= end,
22
+ ),
23
+ );
package/src/imports.mts CHANGED
@@ -140,33 +140,233 @@ export const buildCalleeResolver = (
140
140
 
141
141
  /**
142
142
  * A fix that inserts `import { <specifiers> } from '<moduleName>';` at the top
143
- * of the file, or no fix when `specifiers` is empty. Each specifier is written
143
+ * of the file, after its directive prologue (`'use client';` stays a
144
+ * directive), or no fix when `specifiers` is empty. Each specifier is written
144
145
  * as given, so `type T` imports a type.
145
146
  *
146
- * The placement is deliberately naive: an import sorter
147
- * (prettier-plugin-organize-imports or any equivalent) merges and orders it
148
- * afterwards. Put every name one report needs into a single call: ESLint
149
- * merges the fixes of one report into one edit, and two fixes inserting at the
150
- * start of the file conflict, so that one of them is dropped.
147
+ * Use it only when the file does not import from `moduleName` by name;
148
+ * {@link bindValueImports} adds to an existing declaration instead, which
149
+ * keeps `import-x/no-duplicates` satisfied, and falls back to this. An import
150
+ * sorter orders the declaration afterwards. Put every name one report needs
151
+ * into a single call: ESLint merges the fixes of one report into one edit,
152
+ * and two fixes inserting at the same place conflict, so that one of them is
153
+ * dropped.
151
154
  */
152
155
  export const buildNamedImportFix = (
153
156
  fixer: TSESLint.RuleFixer,
154
157
  program: TSESTree.Program,
155
158
  moduleName: string,
156
159
  specifiers: readonly string[],
157
- ): readonly TSESLint.RuleFix[] =>
158
- specifiers.length === 0
159
- ? ([] as const)
160
- : ([
161
- fixer.insertTextBefore(
162
- program,
163
- `import { ${specifiers.join(', ')} } from '${moduleName}';\n`,
164
- ),
165
- ] as const);
166
-
167
- const importedNameOf = (
160
+ ): readonly TSESLint.RuleFix[] => {
161
+ if (specifiers.length === 0) {
162
+ return [] as const;
163
+ }
164
+
165
+ const declaration =
166
+ `import { ${specifiers.join(', ')} } from '${moduleName}';` as const;
167
+
168
+ const lastDirective = program.body
169
+ .filter(
170
+ (statement): statement is TSESTree.ExpressionStatement =>
171
+ statement.type === AST_NODE_TYPES.ExpressionStatement &&
172
+ statement.directive !== undefined,
173
+ )
174
+ .at(-1);
175
+
176
+ if (lastDirective === undefined) {
177
+ return [fixer.insertTextBefore(program, `${declaration}\n`)] as const;
178
+ }
179
+
180
+ // After the comments that start on the directive's last line as well: one
181
+ // such as `// eslint-disable-line` is about that line.
182
+ const anchor =
183
+ (program.comments ?? [])
184
+ .filter(
185
+ (comment) =>
186
+ comment.range[0] >= lastDirective.range[1] &&
187
+ comment.loc.start.line === lastDirective.loc.end.line,
188
+ )
189
+ .at(-1) ?? lastDirective;
190
+
191
+ return [fixer.insertTextAfter(anchor, `\n${declaration}`)] as const;
192
+ };
193
+
194
+ /**
195
+ * The local names `moduleName`'s exports `exportNames` are bound to as values
196
+ * once `edits` are made, in the same order, or `undefined` when one of them
197
+ * cannot be bound.
198
+ *
199
+ * - Imported as a value: that local name, and no edit.
200
+ * - Imported by `import { type x }`: the edit drops `type`, keeping a comment
201
+ * after it.
202
+ * - Not imported: the edit adds it to the first named import from the module
203
+ * that has a value specifier, or else to any named import from it that is
204
+ * not `import type`. Adding it there rather than in a new declaration keeps
205
+ * `import-x/no-duplicates` satisfied. Failing that, it goes into braces
206
+ * after a lone default import (`import D, { x }`), and when the module has
207
+ * neither, a new declaration goes after the directive prologue
208
+ * ({@link buildNamedImportFix}).
209
+ * - Refused when an import already binds the name to something else
210
+ * (`import type { x }`, `import { y as x }`, `import { x } from 'other'`),
211
+ * where adding it would be a duplicate binding.
212
+ *
213
+ * ESLint's default parser leaves `importKind` out, which counts as a value
214
+ * import. Every name a fix writes is bound by that fix's own edits rather than
215
+ * left to another report's: an editor applies one fix at a time, and a fix
216
+ * that loses a conflict with another in the same pass is dropped. Fixes that
217
+ * all add a name overlap, so `--fix` applies one per pass, and the rest in the
218
+ * next, by when the file imports it. Whether something other than an import
219
+ * binds the name where it is written is the caller's to check
220
+ * ({@link hasForeignBinding}).
221
+ */
222
+ export const bindValueImports = (
223
+ sourceCode: TSESLint.SourceCode,
224
+ moduleName: string,
225
+ exportNames: readonly string[],
226
+ ): ValueImportBinding | undefined => {
227
+ const program = sourceCode.ast;
228
+
229
+ const allImports = program.body.filter(
230
+ (node): node is TSESTree.ImportDeclaration =>
231
+ node.type === AST_NODE_TYPES.ImportDeclaration,
232
+ );
233
+
234
+ const declarations = getImportDeclarations(program, moduleName);
235
+
236
+ const named = declarations.flatMap((declaration) =>
237
+ declaration.specifiers.flatMap((specifier) =>
238
+ specifier.type === AST_NODE_TYPES.ImportSpecifier
239
+ ? [{ declaration, specifier }]
240
+ : [],
241
+ ),
242
+ );
243
+
244
+ const bindings = exportNames.map((exportName) =>
245
+ bindingOf(sourceCode, allImports, named, exportName),
246
+ );
247
+
248
+ if (bindings.includes(undefined)) {
249
+ return undefined;
250
+ }
251
+
252
+ const defined = bindings.filter((b) => b !== undefined);
253
+
254
+ const toAdd = defined.flatMap((b) => (b.isAdded ? [b.localName] : []));
255
+
256
+ const host = (
257
+ named.find(isValueImport) ?? named.find(isNotTypeOnly)
258
+ )?.declaration.specifiers.at(-1);
259
+
260
+ // `import D from 'm'` takes `, { x }`: a second declaration beside it would
261
+ // be a duplicate.
262
+ const defaultHost = declarations.find(
263
+ (declaration) =>
264
+ declaration.importKind !== 'type' &&
265
+ declaration.specifiers.length === 1 &&
266
+ declaration.specifiers[0]?.type === AST_NODE_TYPES.ImportDefaultSpecifier,
267
+ )?.specifiers[0];
268
+
269
+ return {
270
+ localNames: defined.map((b) => b.localName),
271
+ edits: (fixer) => [
272
+ ...defined.flatMap((b) => b.edits(fixer)),
273
+ ...(toAdd.length === 0
274
+ ? []
275
+ : host !== undefined
276
+ ? [fixer.insertTextAfter(host, `, ${toAdd.join(', ')}`)]
277
+ : defaultHost !== undefined
278
+ ? [fixer.insertTextAfter(defaultHost, `, { ${toAdd.join(', ')} }`)]
279
+ : buildNamedImportFix(fixer, program, moduleName, toAdd)),
280
+ ],
281
+ };
282
+ };
283
+
284
+ /** The exported name `specifier` imports: `x` for `import { x as y }`. */
285
+ export const importedNameOf = (
168
286
  specifier: DeepReadonly<TSESTree.ImportSpecifier>,
169
287
  ): string =>
170
288
  specifier.imported.type === AST_NODE_TYPES.Identifier
171
289
  ? specifier.imported.name
172
290
  : specifier.imported.value;
291
+
292
+ /** What {@link bindValueImports} gives. */
293
+ export type ValueImportBinding = Readonly<{
294
+ /** The names to write, in the order of the export names asked for. */
295
+ localNames: readonly string[];
296
+
297
+ /** The edits that bring every one of them into scope. */
298
+ edits: (fixer: TSESLint.RuleFixer) => readonly TSESLint.RuleFix[];
299
+ }>;
300
+
301
+ type NamedSpecifier = Readonly<{
302
+ declaration: TSESTree.ImportDeclaration;
303
+ specifier: TSESTree.ImportSpecifier;
304
+ }>;
305
+
306
+ /** Neither `specifier` nor its declaration says `type`. */
307
+ const isValueImport = ({ declaration, specifier }: NamedSpecifier): boolean =>
308
+ isNotTypeOnly({ declaration, specifier }) && specifier.importKind !== 'type';
309
+
310
+ /** The declaration is not `import type`. */
311
+ const isNotTypeOnly = ({ declaration }: NamedSpecifier): boolean =>
312
+ declaration.importKind !== 'type';
313
+
314
+ const bindingOf = (
315
+ sourceCode: TSESLint.SourceCode,
316
+ allImports: readonly TSESTree.ImportDeclaration[],
317
+ named: readonly NamedSpecifier[],
318
+ exportName: string,
319
+ ):
320
+ | Readonly<{
321
+ localName: string;
322
+ isAdded: boolean;
323
+ edits: (fixer: TSESLint.RuleFixer) => readonly TSESLint.RuleFix[];
324
+ }>
325
+ | undefined => {
326
+ const matching = named.filter(
327
+ ({ specifier }) => importedNameOf(specifier) === exportName,
328
+ );
329
+
330
+ const value = matching.find(isValueImport);
331
+
332
+ if (value !== undefined) {
333
+ return {
334
+ localName: value.specifier.local.name,
335
+ isAdded: false,
336
+ edits: () => [],
337
+ };
338
+ }
339
+
340
+ const inlineType = matching.find(isNotTypeOnly)?.specifier;
341
+
342
+ if (inlineType !== undefined) {
343
+ const typeKeyword = sourceCode.getFirstToken(inlineType);
344
+
345
+ // Up to whatever follows `type`, a comment included, so that the comment
346
+ // stays.
347
+ const next =
348
+ typeKeyword === null
349
+ ? null
350
+ : sourceCode.getTokenAfter(typeKeyword, { includeComments: true });
351
+
352
+ return typeKeyword === null || next === null
353
+ ? undefined
354
+ : {
355
+ localName: inlineType.local.name,
356
+ isAdded: false,
357
+ edits: (fixer) => [
358
+ fixer.removeRange([typeKeyword.range[0], next.range[0]]),
359
+ ],
360
+ };
361
+ }
362
+
363
+ const isTaken = allImports.some((declaration) =>
364
+ declaration.specifiers.some(
365
+ (specifier) => specifier.local.name === exportName,
366
+ ),
367
+ );
368
+
369
+ return isTaken
370
+ ? undefined
371
+ : { localName: exportName, isAdded: true, edits: () => [] };
372
+ };
package/src/index.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './bindings.mjs';
2
+ export * from './comments.mjs';
2
3
  export * from './imports.mjs';
3
4
  export * from './negation.mjs';
4
5
  export * from './parentheses.mjs';
@@ -3,11 +3,8 @@ import {
3
3
  type TSESLint,
4
4
  type TSESTree,
5
5
  } from '@typescript-eslint/utils';
6
- import {
7
- buildNamedImportFix,
8
- getImportDeclarations,
9
- getNamedImports,
10
- } from '../imports.mjs';
6
+ import { hasForeignBinding } from '../bindings.mjs';
7
+ import { bindValueImports } from '../imports.mjs';
11
8
  import { getRangeWithParens } from '../parentheses.mjs';
12
9
  import { isIdentifierNamed, skipTypeWrappers } from '../type-wrappers.mjs';
13
10
 
@@ -44,10 +41,6 @@ export const createPreferArrayIsArray = ({
44
41
  create: (context) => {
45
42
  const sourceCode = context.sourceCode;
46
43
 
47
- const program = sourceCode.ast;
48
-
49
- const moduleImports = getImportDeclarations(program, moduleName);
50
-
51
44
  const mut_nodesToFix: TSESTree.CallExpression[] = [];
52
45
 
53
46
  return {
@@ -59,37 +52,31 @@ export const createPreferArrayIsArray = ({
59
52
  mut_nodesToFix.push(node);
60
53
  },
61
54
  'Program:exit': () => {
62
- const hasNamespaceImport =
63
- getNamedImports(moduleImports).includes(namespace);
55
+ const binding = bindValueImports(sourceCode, moduleName, [namespace]);
64
56
 
65
- // The import fix lives on the first report only:
66
- // `insertTextBefore(program, …)` produces overlapping ranges
67
- // otherwise and only one of them would survive a pass. A first
68
- // report silenced by an `eslint-disable` comment therefore takes
69
- // the import with it.
70
- for (const [index, node] of mut_nodesToFix.entries()) {
57
+ const localName = binding?.localNames[0];
58
+
59
+ // Every fix brings the namespace into scope itself, so that one
60
+ // applied alone (an editor's quick fix) is complete.
61
+ for (const node of mut_nodesToFix) {
71
62
  context.report({
72
63
  node,
73
64
  messageId: 'useIsArray',
74
- fix: (fixer) => {
75
- const importFixes =
76
- index === 0 && !hasNamespaceImport
77
- ? buildNamedImportFix(fixer, program, moduleName, [
78
- namespace,
79
- ])
80
- : ([] as const);
81
-
82
- // Only the callee is replaced, type wrappers included, along
83
- // with the parentheses around it, which its range leaves out;
84
- // the arguments stay as written.
85
- return [
86
- ...importFixes,
87
- fixer.replaceTextRange(
88
- getRangeWithParens(sourceCode, node.callee),
89
- replacement,
90
- ),
91
- ];
92
- },
65
+ fix:
66
+ binding === undefined ||
67
+ localName === undefined ||
68
+ hasForeignBinding(sourceCode, node, localName, moduleName)
69
+ ? null
70
+ : (fixer) => [
71
+ ...binding.edits(fixer),
72
+ // Only the callee is replaced, type wrappers included,
73
+ // along with the parentheses around it, which its range
74
+ // leaves out; the arguments stay as written.
75
+ fixer.replaceTextRange(
76
+ getRangeWithParens(sourceCode, node.callee),
77
+ `${localName}.isArray`,
78
+ ),
79
+ ],
93
80
  });
94
81
  }
95
82
  },