eslint-plugin-unslop 0.4.1 → 0.4.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/dist/index.cjs +52 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +52 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -37,7 +37,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
37
37
|
// package.json
|
|
38
38
|
var package_default = {
|
|
39
39
|
name: "eslint-plugin-unslop",
|
|
40
|
-
version: "0.4.
|
|
40
|
+
version: "0.4.2",
|
|
41
41
|
description: "ESLint plugin with rules for reducing AI-generated code smells",
|
|
42
42
|
repository: {
|
|
43
43
|
type: "git",
|
|
@@ -945,6 +945,10 @@ function isLocalPublicExport(node) {
|
|
|
945
945
|
function isEagerInit(node) {
|
|
946
946
|
const t = node.type;
|
|
947
947
|
if (t === "ExpressionStatement" || t === "IfStatement") return true;
|
|
948
|
+
if (t === "ExportDefaultDeclaration") {
|
|
949
|
+
const declType = strProp(prop(node, "declaration"), "type");
|
|
950
|
+
return declType === "CallExpression" || declType === "NewExpression";
|
|
951
|
+
}
|
|
948
952
|
const inner = t === "ExportNamedDeclaration" ? prop(node, "declaration") : node;
|
|
949
953
|
if (strProp(inner, "type") !== "VariableDeclaration") return false;
|
|
950
954
|
const decls = prop(inner, "declarations");
|
|
@@ -1009,14 +1013,37 @@ function checkFieldOrder(ctx, members, classBody, hasComputed) {
|
|
|
1009
1013
|
}
|
|
1010
1014
|
function checkMethodOrder(ctx, members, classBody, hasComputed) {
|
|
1011
1015
|
const methods = members.filter((m) => m.kind === "method");
|
|
1016
|
+
const cyclic = findCyclicMethods(methods);
|
|
1012
1017
|
for (const m of methods) {
|
|
1013
|
-
if (!m.name) continue;
|
|
1018
|
+
if (!m.name || cyclic.has(m.name)) continue;
|
|
1014
1019
|
if (methods.some((o) => o !== m && o.thisDeps.has(m.name) && o.idx > m.idx)) {
|
|
1015
1020
|
report(ctx, members, classBody, hasComputed, "moveMemberBelow", m);
|
|
1016
1021
|
return;
|
|
1017
1022
|
}
|
|
1018
1023
|
}
|
|
1019
1024
|
}
|
|
1025
|
+
function findCyclicMethods(methods) {
|
|
1026
|
+
const byName = new Map(methods.filter((m) => m.name).map((m) => [m.name, m]));
|
|
1027
|
+
const inCycle = /* @__PURE__ */ new Set();
|
|
1028
|
+
for (const [name] of byName) {
|
|
1029
|
+
if (methodReachesSelf(name, name, byName, /* @__PURE__ */ new Set())) {
|
|
1030
|
+
inCycle.add(name);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
return inCycle;
|
|
1034
|
+
}
|
|
1035
|
+
function methodReachesSelf(target, current, byName, visited) {
|
|
1036
|
+
const entry = byName.get(current);
|
|
1037
|
+
if (!entry) return false;
|
|
1038
|
+
for (const dep of entry.thisDeps) {
|
|
1039
|
+
if (!byName.has(dep)) continue;
|
|
1040
|
+
if (dep === target) return true;
|
|
1041
|
+
if (visited.has(dep)) continue;
|
|
1042
|
+
visited.add(dep);
|
|
1043
|
+
if (methodReachesSelf(target, dep, byName, visited)) return true;
|
|
1044
|
+
}
|
|
1045
|
+
return false;
|
|
1046
|
+
}
|
|
1020
1047
|
function report(ctx, members, classBody, hasComputed, messageId, target) {
|
|
1021
1048
|
const input = { ctx, members, classBody, hasComputed, messageId, target };
|
|
1022
1049
|
doReport(input);
|
|
@@ -1227,8 +1254,7 @@ function checkTopLevel(ctx, p) {
|
|
|
1227
1254
|
const cyclic = findCyclic(decls);
|
|
1228
1255
|
const violations = findViolations(decls, eager, cyclic);
|
|
1229
1256
|
if (violations.length === 0) return;
|
|
1230
|
-
|
|
1231
|
-
reportAll(ctx, violations, safe, p, entries);
|
|
1257
|
+
reportAll(ctx, violations, p, entries);
|
|
1232
1258
|
}
|
|
1233
1259
|
function collectEntries(p) {
|
|
1234
1260
|
const entries = [];
|
|
@@ -1342,8 +1368,8 @@ function firstConsumer(name, decls) {
|
|
|
1342
1368
|
}
|
|
1343
1369
|
return best;
|
|
1344
1370
|
}
|
|
1345
|
-
function reportAll(ctx, violations,
|
|
1346
|
-
doReportAll({ ctx, violations,
|
|
1371
|
+
function reportAll(ctx, violations, p, entries) {
|
|
1372
|
+
doReportAll({ ctx, violations, p, entries });
|
|
1347
1373
|
}
|
|
1348
1374
|
function doReportAll(args) {
|
|
1349
1375
|
for (let i = 0; i < args.violations.length; i++) {
|
|
@@ -1352,28 +1378,17 @@ function doReportAll(args) {
|
|
|
1352
1378
|
node: v.node,
|
|
1353
1379
|
messageId: isConst(v.name) ? "moveConstantBelow" : "moveHelperBelow",
|
|
1354
1380
|
data: { name: v.name },
|
|
1355
|
-
fix:
|
|
1381
|
+
fix: i === 0 ? buildTopFix(args.ctx, args.p, args.entries) : null
|
|
1356
1382
|
});
|
|
1357
1383
|
}
|
|
1358
1384
|
}
|
|
1359
|
-
function isFixSafe(ctx, entries) {
|
|
1360
|
-
const src = ctx.sourceCode;
|
|
1361
|
-
const stmts = entries.filter((e) => !e.isImport);
|
|
1362
|
-
for (let i = 0; i < stmts.length - 1; i++) {
|
|
1363
|
-
const cur = stmts[i].node;
|
|
1364
|
-
const nxt = stmts[i + 1].node;
|
|
1365
|
-
for (const c of src.getCommentsBefore(nxt)) {
|
|
1366
|
-
if (c.range[0] > cur.range[1]) return false;
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
return true;
|
|
1370
|
-
}
|
|
1371
1385
|
function isConst(name) {
|
|
1372
1386
|
return /^[A-Z][A-Z_0-9]+$/.test(name);
|
|
1373
1387
|
}
|
|
1374
1388
|
function buildTopFix(ctx, p, entries) {
|
|
1375
1389
|
return (fixer) => {
|
|
1376
1390
|
const src = ctx.sourceCode;
|
|
1391
|
+
const nodeTexts = buildNodeTexts(src, entries);
|
|
1377
1392
|
const imports = entries.filter((e) => e.isImport);
|
|
1378
1393
|
const externalReexports = entries.filter((e) => e.isExternalReexport);
|
|
1379
1394
|
const localPublicApi = entries.filter(
|
|
@@ -1388,10 +1403,27 @@ function buildTopFix(ctx, p, entries) {
|
|
|
1388
1403
|
);
|
|
1389
1404
|
const sortedPrivate = kahnsSort(privateDecls);
|
|
1390
1405
|
const all = [...imports, ...externalReexports, ...prioritizedPublicApi, ...sortedPrivate];
|
|
1391
|
-
const text = all.map((e) =>
|
|
1406
|
+
const text = all.map((e) => nodeTexts.get(e)).join("\n\n");
|
|
1392
1407
|
return fixer.replaceTextRange([p.range[0], p.range[1]], text);
|
|
1393
1408
|
};
|
|
1394
1409
|
}
|
|
1410
|
+
function buildNodeTexts(src, entries) {
|
|
1411
|
+
const result = /* @__PURE__ */ new Map();
|
|
1412
|
+
for (let i = 0; i < entries.length; i++) {
|
|
1413
|
+
const e = entries[i];
|
|
1414
|
+
const comments = src.getCommentsBefore(e.node);
|
|
1415
|
+
const prevEnd = i > 0 ? entries[i - 1].node.range[1] : e.node.range[0];
|
|
1416
|
+
const leadingComments = comments.filter((c) => c.range[0] >= prevEnd);
|
|
1417
|
+
if (leadingComments.length > 0) {
|
|
1418
|
+
const commentStart = leadingComments[0].range[0];
|
|
1419
|
+
const fullText = src.getText().slice(commentStart, e.node.range[1]);
|
|
1420
|
+
result.set(e, fullText);
|
|
1421
|
+
} else {
|
|
1422
|
+
result.set(e, src.getText(e.node));
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
return result;
|
|
1426
|
+
}
|
|
1395
1427
|
function kahnsSort(decls) {
|
|
1396
1428
|
const byName = new Map(decls.filter((e) => e.name).map((e) => [e.name, e]));
|
|
1397
1429
|
const inDeg = buildInDegrees(decls, byName);
|