eslint-plugin-unslop 0.2.0 → 0.2.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.
- package/dist/index.cjs +85 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +85 -24
- 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.2.
|
|
40
|
+
version: "0.2.1",
|
|
41
41
|
description: "ESLint plugin with rules for reducing AI-generated code smells",
|
|
42
42
|
repository: {
|
|
43
43
|
type: "git",
|
|
@@ -1163,10 +1163,18 @@ function reportTopLevelOrdering(program, context) {
|
|
|
1163
1163
|
const helpers = collectHelpers(body);
|
|
1164
1164
|
const refs = collectReferences(context.sourceCode.scopeManager.globalScope);
|
|
1165
1165
|
const cyclicNames = findCyclicHelperNames(body, helpers, refs);
|
|
1166
|
-
const
|
|
1166
|
+
const eagerHelperNames = findEagerHelperNames(body, helpers, refs);
|
|
1167
|
+
const fixRange = buildTopLevelFixRange({
|
|
1168
|
+
body,
|
|
1169
|
+
helpers,
|
|
1170
|
+
refs,
|
|
1171
|
+
cyclicNames,
|
|
1172
|
+
eagerHelperNames,
|
|
1173
|
+
context
|
|
1174
|
+
});
|
|
1167
1175
|
for (const helper of helpers) {
|
|
1168
1176
|
if (cyclicNames.has(helper.name)) continue;
|
|
1169
|
-
if (
|
|
1177
|
+
if (eagerHelperNames.has(helper.name)) continue;
|
|
1170
1178
|
const consumer = findFirstConsumerEntry(body, helper, refs)?.statement;
|
|
1171
1179
|
if (!consumer) continue;
|
|
1172
1180
|
context.report({
|
|
@@ -1182,19 +1190,26 @@ function reportTopLevelOrdering(program, context) {
|
|
|
1182
1190
|
}
|
|
1183
1191
|
}
|
|
1184
1192
|
function buildTopLevelFixRange(input) {
|
|
1185
|
-
const { body, helpers, refs, cyclicNames, context } = input;
|
|
1193
|
+
const { body, helpers, refs, cyclicNames, eagerHelperNames, context } = input;
|
|
1186
1194
|
if (body.length < 2) return void 0;
|
|
1187
|
-
const edges = collectTopLevelEdges(
|
|
1195
|
+
const edges = collectTopLevelEdges({
|
|
1196
|
+
body,
|
|
1197
|
+
helpers,
|
|
1198
|
+
refs,
|
|
1199
|
+
cyclicNames,
|
|
1200
|
+
eagerHelperNames
|
|
1201
|
+
});
|
|
1188
1202
|
if (edges.length === 0) return void 0;
|
|
1189
1203
|
const order = stableTopologicalOrder(body.length, edges);
|
|
1190
1204
|
if (!order || isIdentityOrder(order)) return void 0;
|
|
1191
1205
|
const orderedBody = order.map((index) => body[index]);
|
|
1192
1206
|
return createSafeReorderFix(context.sourceCode, body, orderedBody);
|
|
1193
1207
|
}
|
|
1194
|
-
function collectTopLevelEdges(
|
|
1208
|
+
function collectTopLevelEdges(input) {
|
|
1209
|
+
const { body, helpers, refs, cyclicNames, eagerHelperNames } = input;
|
|
1195
1210
|
const edges = [];
|
|
1196
1211
|
for (const helper of helpers) {
|
|
1197
|
-
if (cyclicNames.has(helper.name) ||
|
|
1212
|
+
if (cyclicNames.has(helper.name) || eagerHelperNames.has(helper.name)) continue;
|
|
1198
1213
|
const consumer = findFirstConsumerEntry(body, helper, refs);
|
|
1199
1214
|
if (!consumer) continue;
|
|
1200
1215
|
edges.push([consumer.index, helper.index]);
|
|
@@ -1322,32 +1337,78 @@ function collectReferences(scope) {
|
|
|
1322
1337
|
}
|
|
1323
1338
|
return refs;
|
|
1324
1339
|
}
|
|
1325
|
-
function
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1340
|
+
function findEagerHelperNames(body, helpers, refs) {
|
|
1341
|
+
const helperNames = new Set(helpers.map((helper) => helper.name));
|
|
1342
|
+
const deps = collectHelperRuntimeDependencies(body, helpers, refs, helperNames);
|
|
1343
|
+
const roots = collectEagerRootNames(body, refs, helperNames);
|
|
1344
|
+
return collectReachableNames(roots, deps);
|
|
1345
|
+
}
|
|
1346
|
+
function collectHelperRuntimeDependencies(body, helpers, refs, helperNames) {
|
|
1347
|
+
const deps = /* @__PURE__ */ new Map();
|
|
1348
|
+
for (const helper of helpers) {
|
|
1349
|
+
const statement = body[helper.index];
|
|
1350
|
+
if (!statement) continue;
|
|
1351
|
+
const names = collectRuntimeNamesInStatement(statement, refs, helperNames);
|
|
1352
|
+
names.delete(helper.name);
|
|
1353
|
+
deps.set(helper.name, names);
|
|
1330
1354
|
}
|
|
1331
|
-
return
|
|
1355
|
+
return deps;
|
|
1332
1356
|
}
|
|
1333
|
-
function
|
|
1334
|
-
const
|
|
1335
|
-
if (!range) return false;
|
|
1357
|
+
function collectEagerRootNames(body, refs, helperNames) {
|
|
1358
|
+
const roots = /* @__PURE__ */ new Set();
|
|
1336
1359
|
for (const ref of refs) {
|
|
1337
|
-
if (ref
|
|
1338
|
-
if (
|
|
1360
|
+
if (!isRuntimeHelperReadReference(ref, helperNames)) continue;
|
|
1361
|
+
if (!isEagerRefInTopLevelStatement(ref, body)) continue;
|
|
1362
|
+
roots.add(ref.identifier.name);
|
|
1339
1363
|
}
|
|
1340
|
-
return
|
|
1364
|
+
return roots;
|
|
1341
1365
|
}
|
|
1342
|
-
function
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
return
|
|
1366
|
+
function isEagerRefInTopLevelStatement(ref, body) {
|
|
1367
|
+
if (ref.from.type !== "module" && ref.from.type !== "global") return false;
|
|
1368
|
+
const statement = findTopLevelStatementForRef(body, ref);
|
|
1369
|
+
if (!statement) return false;
|
|
1370
|
+
return statement.type !== "ExportNamedDeclaration" || !!statement.declaration;
|
|
1371
|
+
}
|
|
1372
|
+
function findTopLevelStatementForRef(body, ref) {
|
|
1373
|
+
return body.find((statement) => isReferenceInsideStatement(ref, statement));
|
|
1374
|
+
}
|
|
1375
|
+
function collectRuntimeNamesInStatement(statement, refs, helperNames) {
|
|
1376
|
+
const names = /* @__PURE__ */ new Set();
|
|
1377
|
+
if (!statement.range) return names;
|
|
1378
|
+
for (const ref of refs) {
|
|
1379
|
+
if (!isRuntimeHelperReadReference(ref, helperNames)) continue;
|
|
1380
|
+
if (!isReferenceInsideStatement(ref, statement)) continue;
|
|
1381
|
+
names.add(ref.identifier.name);
|
|
1382
|
+
}
|
|
1383
|
+
return names;
|
|
1384
|
+
}
|
|
1385
|
+
function isReferenceInsideStatement(ref, statement) {
|
|
1386
|
+
const range = ref.identifier.range;
|
|
1387
|
+
if (!range || !statement.range) return false;
|
|
1388
|
+
return range[0] >= statement.range[0] && range[1] <= statement.range[1];
|
|
1389
|
+
}
|
|
1390
|
+
function collectReachableNames(roots, deps) {
|
|
1391
|
+
const reachable = /* @__PURE__ */ new Set();
|
|
1392
|
+
const queue = [...roots];
|
|
1393
|
+
while (queue.length > 0) {
|
|
1394
|
+
const current = queue.shift();
|
|
1395
|
+
if (!current || reachable.has(current)) continue;
|
|
1396
|
+
reachable.add(current);
|
|
1397
|
+
for (const dep of deps.get(current) ?? []) queue.push(dep);
|
|
1398
|
+
}
|
|
1399
|
+
return reachable;
|
|
1400
|
+
}
|
|
1401
|
+
function isRuntimeHelperReadReference(ref, helperNames) {
|
|
1402
|
+
if (isTypeReference(ref)) return false;
|
|
1403
|
+
if (!isReadReference(ref)) return false;
|
|
1404
|
+
return helperNames.has(ref.identifier.name);
|
|
1347
1405
|
}
|
|
1348
1406
|
function isTypeReference(ref) {
|
|
1349
1407
|
return "isTypeReference" in ref && ref.isTypeReference === true;
|
|
1350
1408
|
}
|
|
1409
|
+
function isReadReference(ref) {
|
|
1410
|
+
return typeof ref.isRead === "function" ? ref.isRead() : true;
|
|
1411
|
+
}
|
|
1351
1412
|
function getSymbolName(statement) {
|
|
1352
1413
|
if (statement.type === "ExportDefaultDeclaration") return "default export";
|
|
1353
1414
|
if (statement.type === "ExportNamedDeclaration") return getNamedExportName(statement);
|