exadev-eslint-config 2.20.1 → 2.20.2
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 +1 -1
- package/dist/index.cjs +22 -5
- package/dist/index.js +22 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -273,7 +273,7 @@ Bundled into `exadevConfig()`'s default output the same way React/Next.js auto-d
|
|
|
273
273
|
| `no-side-effects-in-index` | | **A barrel may contain only re-export statements** — nothing that could execute at import time. Self-scopes to any index file. |
|
|
274
274
|
| `barrel-direct-siblings-only` | | **A barrel may re-export only from a direct sibling** (`./module`), never a nested path, parent, or bare package specifier (mode 3). |
|
|
275
275
|
| `no-control-flow` | | **Bans `if`/`switch`/loops/the ternary operator outright.** Not part of `recommended` or `barrel` — ordinary code legitimately needs control flow, so this is opt-in, wired via a consumer's own `files` glob for the specific packages that want it (a composition-root package selecting an adapter/strategy by a validated key, say): a lookup table replaces a branch, a declarative array method (`map`/`filter`/`some`/`every`/...) replaces a loop. Requires no type information. |
|
|
276
|
-
| `no-pointless-reassignment` | ✓ | **Flags a `const` alias that adds no transformation** (`const foo = bar` where both sides are plain identifiers). Autofix rewrites every read to the original name and deletes the declaration
|
|
276
|
+
| `no-pointless-reassignment` | ✓ | **Flags a `const` alias that adds no transformation** (`const foo = bar` where both sides are plain identifiers). Autofix rewrites every read to the original name and deletes the declaration. Still reported but deliberately not auto-fixable where collapsing the alias would change meaning: an explicit type annotation (`const exhaustive: never = item` — the annotation is the point), a read where the original name is shadowed, a read as a shorthand object property, more than one declarator in the statement, or a source that is written to anywhere. An alias that is itself part of the module's exported surface (`export const alias = original;`, a later `export { alias }`/`export { alias as other }`, or `export default alias;`) is neither reported nor fixed at all, since collapsing it would rename or delete a binding every importer of this module depends on. |
|
|
277
277
|
| `no-object-assign` | ✓/suggestion | **`Object.assign` skips the type-checking object spread gets** — it doesn't check a source object's properties against the target's declared types. A fresh object-literal target autofixes to `{ ...target, ...source }`; mutating an existing reassignable binding offers a suggestion only (changes the object's identity); a `const` binding or a non-statement call site gets a plain report with no fix. |
|
|
278
278
|
| `no-mutable-union-array-param` | ✓ | **A union-typed array parameter can be mutated with a value the caller's narrower array never declared.** A function parameter typed as an array of a union (`(string \| number)[]`) accepts a narrower caller array (`number[]`) by covariance; calling `push`/`unshift`/`splice`/`fill`/`copyWithin` on it can then insert a value the caller's own array was never declared to hold. Autofix marks the parameter `readonly`, turning the mutating call into a real compile error to resolve deliberately. Requires no type information. |
|
|
279
279
|
| `prefer-readonly-array-param` | ✓ | **Every non-readonly array/tuple parameter should be `readonly`.** A narrower, safely-autofixable sibling of [`@typescript-eslint/prefer-readonly-parameter-types`](https://typescript-eslint.io/rules/prefer-readonly-parameter-types/) scoped to array/tuple parameter shapes only: fires unconditionally on every non-readonly array or tuple parameter, regardless of whether the function body mutates it, in any parameter position (a plain identifier, a rest parameter, a default-valued parameter, or a constructor parameter property) and any function-like shape (a concrete function/arrow/method, or a declaration-only ambient function, interface method, function type alias, call/construct signature, or abstract/ambient class method). A union containing an array/tuple member is fixed on that member alone. Autofix prepends `readonly ` (or renames `Array<T>` to `ReadonlyArray<T>`), turning any resulting mutation into a real compile error to resolve deliberately. Requires no type information — registered in both `plugin.configs.recommended` and the default (type-checked) export. |
|
package/dist/index.cjs
CHANGED
|
@@ -295,7 +295,7 @@ function buildNextjsConfig(options = {}) {
|
|
|
295
295
|
}
|
|
296
296
|
//#endregion
|
|
297
297
|
//#region package.json
|
|
298
|
-
var version = "2.20.
|
|
298
|
+
var version = "2.20.2";
|
|
299
299
|
//#endregion
|
|
300
300
|
//#region src/react.ts
|
|
301
301
|
const JSX_FILE_PATTERNS = ["**/*.jsx", "**/*.tsx"];
|
|
@@ -1274,6 +1274,13 @@ function isConstDeclarator(declarator) {
|
|
|
1274
1274
|
if (declarator.parent.type !== "VariableDeclaration") throw new Error(`Unreachable: expected a VariableDeclarator's own parent to be a VariableDeclaration, got "${declarator.parent.type}" instead.`);
|
|
1275
1275
|
return declarator.parent.kind === "const";
|
|
1276
1276
|
}
|
|
1277
|
+
function asVariableDeclaration(node) {
|
|
1278
|
+
if (node.type !== "VariableDeclaration") throw new Error(`Unreachable: expected a VariableDeclarator's own parent to be a VariableDeclaration, got "${node.type}" instead.`);
|
|
1279
|
+
return node;
|
|
1280
|
+
}
|
|
1281
|
+
function findTopLevelScope(globalScope) {
|
|
1282
|
+
return globalScope.childScopes.find((child) => child.type === "module") ?? globalScope;
|
|
1283
|
+
}
|
|
1277
1284
|
function variableInScope(scope, name) {
|
|
1278
1285
|
const variable = scope.set.get(name);
|
|
1279
1286
|
if (variable === void 0) throw new Error(`Unreachable: expected scope.set to hold a variable named "${name}".`);
|
|
@@ -1297,6 +1304,14 @@ function isShorthandPropertyRead(sourceCode, identifier) {
|
|
|
1297
1304
|
if (afterToken?.value !== "}" && afterToken?.value !== ",") return false;
|
|
1298
1305
|
return hasEnclosingShorthandBoundary(sourceCode.getTokensBefore(identifier));
|
|
1299
1306
|
}
|
|
1307
|
+
function isExportedAlias(declarationParentType, programBody, resolve, aliasVariable) {
|
|
1308
|
+
if (declarationParentType === "ExportNamedDeclaration") return true;
|
|
1309
|
+
return programBody.some((statement) => {
|
|
1310
|
+
if (statement.type === "ExportNamedDeclaration" && statement.source == null) return (statement.specifiers ?? []).some((specifier) => specifier.local.type === "Identifier" && specifier.local.name !== void 0 && resolve(specifier.local.name) === aliasVariable);
|
|
1311
|
+
const target = statement.type === "ExportDefaultDeclaration" ? statement.declaration : void 0;
|
|
1312
|
+
return target?.type === "Identifier" && target.name !== void 0 && resolve(target.name) === aliasVariable;
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1300
1315
|
const noPointlessReassignment = {
|
|
1301
1316
|
meta: {
|
|
1302
1317
|
type: "problem",
|
|
@@ -1305,6 +1320,7 @@ const noPointlessReassignment = {
|
|
|
1305
1320
|
messages: { pointlessReassignment: "Pointless reassignment: '{{ name }}' is just an alias for '{{ value }}'. Use the original directly." }
|
|
1306
1321
|
},
|
|
1307
1322
|
create(context) {
|
|
1323
|
+
const moduleScope = findTopLevelScope(context.sourceCode.getScope(context.sourceCode.ast));
|
|
1308
1324
|
return { VariableDeclarator(node) {
|
|
1309
1325
|
if (node.id.type !== "Identifier" || node.init?.type !== "Identifier" || node.id.name.startsWith("_")) return;
|
|
1310
1326
|
if (!isConstDeclarator(node)) return;
|
|
@@ -1314,6 +1330,9 @@ const noPointlessReassignment = {
|
|
|
1314
1330
|
const aliasName = node.id.name;
|
|
1315
1331
|
const originalName = node.init.name;
|
|
1316
1332
|
const aliasIsAnnotated = hasTypeAnnotation(node.id);
|
|
1333
|
+
const variable = variableInScope(scope, aliasName);
|
|
1334
|
+
const declaration = asVariableDeclaration(node.parent);
|
|
1335
|
+
if (isExportedAlias(declaration.parent.type, context.sourceCode.ast.body, (name) => resolveFrom(moduleScope, name), variable)) return;
|
|
1317
1336
|
context.report({
|
|
1318
1337
|
node,
|
|
1319
1338
|
messageId: "pointlessReassignment",
|
|
@@ -1322,7 +1341,6 @@ const noPointlessReassignment = {
|
|
|
1322
1341
|
value: originalName
|
|
1323
1342
|
},
|
|
1324
1343
|
fix(fixer) {
|
|
1325
|
-
const variable = variableInScope(scope, aliasName);
|
|
1326
1344
|
if (aliasIsAnnotated) return null;
|
|
1327
1345
|
if (variable.references.filter((reference) => reference.isWrite() && reference.identifier !== node.id).length > 0) return null;
|
|
1328
1346
|
const readReferences = variable.references.filter((reference) => reference.isRead());
|
|
@@ -1331,9 +1349,8 @@ const noPointlessReassignment = {
|
|
|
1331
1349
|
if (readRefs.some((reference) => isShorthandPropertyRead(context.sourceCode, reference.identifier))) return null;
|
|
1332
1350
|
if (readRefs.some((reference) => resolveFrom(reference.from, originalName) !== sourceVariable)) return null;
|
|
1333
1351
|
const fixes = readRefs.map((reference) => fixer.replaceText(reference.identifier, originalName));
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
fixes.push(fixer.remove(declaration.parent.type === "ExportNamedDeclaration" ? declaration.parent : declaration));
|
|
1352
|
+
if (declaration.declarations.length !== 1) return null;
|
|
1353
|
+
fixes.push(fixer.remove(declaration));
|
|
1337
1354
|
return fixes;
|
|
1338
1355
|
}
|
|
1339
1356
|
});
|
package/dist/index.js
CHANGED
|
@@ -261,7 +261,7 @@ function buildNextjsConfig(options = {}) {
|
|
|
261
261
|
}
|
|
262
262
|
//#endregion
|
|
263
263
|
//#region package.json
|
|
264
|
-
var version = "2.20.
|
|
264
|
+
var version = "2.20.2";
|
|
265
265
|
//#endregion
|
|
266
266
|
//#region src/react.ts
|
|
267
267
|
const JSX_FILE_PATTERNS = ["**/*.jsx", "**/*.tsx"];
|
|
@@ -1240,6 +1240,13 @@ function isConstDeclarator(declarator) {
|
|
|
1240
1240
|
if (declarator.parent.type !== "VariableDeclaration") throw new Error(`Unreachable: expected a VariableDeclarator's own parent to be a VariableDeclaration, got "${declarator.parent.type}" instead.`);
|
|
1241
1241
|
return declarator.parent.kind === "const";
|
|
1242
1242
|
}
|
|
1243
|
+
function asVariableDeclaration(node) {
|
|
1244
|
+
if (node.type !== "VariableDeclaration") throw new Error(`Unreachable: expected a VariableDeclarator's own parent to be a VariableDeclaration, got "${node.type}" instead.`);
|
|
1245
|
+
return node;
|
|
1246
|
+
}
|
|
1247
|
+
function findTopLevelScope(globalScope) {
|
|
1248
|
+
return globalScope.childScopes.find((child) => child.type === "module") ?? globalScope;
|
|
1249
|
+
}
|
|
1243
1250
|
function variableInScope(scope, name) {
|
|
1244
1251
|
const variable = scope.set.get(name);
|
|
1245
1252
|
if (variable === void 0) throw new Error(`Unreachable: expected scope.set to hold a variable named "${name}".`);
|
|
@@ -1263,6 +1270,14 @@ function isShorthandPropertyRead(sourceCode, identifier) {
|
|
|
1263
1270
|
if (afterToken?.value !== "}" && afterToken?.value !== ",") return false;
|
|
1264
1271
|
return hasEnclosingShorthandBoundary(sourceCode.getTokensBefore(identifier));
|
|
1265
1272
|
}
|
|
1273
|
+
function isExportedAlias(declarationParentType, programBody, resolve, aliasVariable) {
|
|
1274
|
+
if (declarationParentType === "ExportNamedDeclaration") return true;
|
|
1275
|
+
return programBody.some((statement) => {
|
|
1276
|
+
if (statement.type === "ExportNamedDeclaration" && statement.source == null) return (statement.specifiers ?? []).some((specifier) => specifier.local.type === "Identifier" && specifier.local.name !== void 0 && resolve(specifier.local.name) === aliasVariable);
|
|
1277
|
+
const target = statement.type === "ExportDefaultDeclaration" ? statement.declaration : void 0;
|
|
1278
|
+
return target?.type === "Identifier" && target.name !== void 0 && resolve(target.name) === aliasVariable;
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1266
1281
|
const noPointlessReassignment = {
|
|
1267
1282
|
meta: {
|
|
1268
1283
|
type: "problem",
|
|
@@ -1271,6 +1286,7 @@ const noPointlessReassignment = {
|
|
|
1271
1286
|
messages: { pointlessReassignment: "Pointless reassignment: '{{ name }}' is just an alias for '{{ value }}'. Use the original directly." }
|
|
1272
1287
|
},
|
|
1273
1288
|
create(context) {
|
|
1289
|
+
const moduleScope = findTopLevelScope(context.sourceCode.getScope(context.sourceCode.ast));
|
|
1274
1290
|
return { VariableDeclarator(node) {
|
|
1275
1291
|
if (node.id.type !== "Identifier" || node.init?.type !== "Identifier" || node.id.name.startsWith("_")) return;
|
|
1276
1292
|
if (!isConstDeclarator(node)) return;
|
|
@@ -1280,6 +1296,9 @@ const noPointlessReassignment = {
|
|
|
1280
1296
|
const aliasName = node.id.name;
|
|
1281
1297
|
const originalName = node.init.name;
|
|
1282
1298
|
const aliasIsAnnotated = hasTypeAnnotation(node.id);
|
|
1299
|
+
const variable = variableInScope(scope, aliasName);
|
|
1300
|
+
const declaration = asVariableDeclaration(node.parent);
|
|
1301
|
+
if (isExportedAlias(declaration.parent.type, context.sourceCode.ast.body, (name) => resolveFrom(moduleScope, name), variable)) return;
|
|
1283
1302
|
context.report({
|
|
1284
1303
|
node,
|
|
1285
1304
|
messageId: "pointlessReassignment",
|
|
@@ -1288,7 +1307,6 @@ const noPointlessReassignment = {
|
|
|
1288
1307
|
value: originalName
|
|
1289
1308
|
},
|
|
1290
1309
|
fix(fixer) {
|
|
1291
|
-
const variable = variableInScope(scope, aliasName);
|
|
1292
1310
|
if (aliasIsAnnotated) return null;
|
|
1293
1311
|
if (variable.references.filter((reference) => reference.isWrite() && reference.identifier !== node.id).length > 0) return null;
|
|
1294
1312
|
const readReferences = variable.references.filter((reference) => reference.isRead());
|
|
@@ -1297,9 +1315,8 @@ const noPointlessReassignment = {
|
|
|
1297
1315
|
if (readRefs.some((reference) => isShorthandPropertyRead(context.sourceCode, reference.identifier))) return null;
|
|
1298
1316
|
if (readRefs.some((reference) => resolveFrom(reference.from, originalName) !== sourceVariable)) return null;
|
|
1299
1317
|
const fixes = readRefs.map((reference) => fixer.replaceText(reference.identifier, originalName));
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
fixes.push(fixer.remove(declaration.parent.type === "ExportNamedDeclaration" ? declaration.parent : declaration));
|
|
1318
|
+
if (declaration.declarations.length !== 1) return null;
|
|
1319
|
+
fixes.push(fixer.remove(declaration));
|
|
1303
1320
|
return fixes;
|
|
1304
1321
|
}
|
|
1305
1322
|
});
|
package/package.json
CHANGED