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 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.1",
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
- const safe = isFixSafe(ctx, entries);
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, safe, p, entries) {
1346
- doReportAll({ ctx, violations, safe, p, entries });
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: args.safe && i === 0 ? buildTopFix(args.ctx, args.p, args.entries) : null
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) => src.getText(e.node)).join("\n\n");
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);