eslint-plugin-unslop 0.6.1 → 0.7.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 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.6.1",
40
+ version: "0.7.1",
41
41
  description: "ESLint plugin with rules for reducing AI-generated code smells",
42
42
  repository: {
43
43
  type: "git",
@@ -301,7 +301,7 @@ var WRAPPER_UNSAFE = {
301
301
 
302
302
  // src/rules/no-false-sharing/index.ts
303
303
  var import_node_path4 = __toESM(require("path"), 1);
304
- var import_typescript3 = __toESM(require("typescript"), 1);
304
+ var import_typescript4 = __toESM(require("typescript"), 1);
305
305
 
306
306
  // src/utils/architecture-policy.ts
307
307
  var import_node_path3 = __toESM(require("path"), 1);
@@ -764,6 +764,61 @@ function hasStringName(value) {
764
764
  return typeof id.name === "string";
765
765
  }
766
766
 
767
+ // src/utils/project-symbol-usage.ts
768
+ var import_typescript3 = __toESM(require("typescript"), 1);
769
+ function findTsSourceFile(program, filename) {
770
+ const normalized = normalizeResolvedPath2(filename);
771
+ return program.getSourceFiles().find((sf) => normalizeResolvedPath2(sf.fileName) === normalized);
772
+ }
773
+ function countProjectWideUses(targetSymbol, checker, program) {
774
+ let total = 0;
775
+ for (const sf of program.getSourceFiles()) {
776
+ if (sf.isDeclarationFile) continue;
777
+ total += countUsesInFile(sf, targetSymbol, checker);
778
+ }
779
+ return total;
780
+ }
781
+ function countUsesInFile(sourceFile, targetSymbol, checker) {
782
+ let count = 0;
783
+ function walk(node) {
784
+ if (import_typescript3.default.isIdentifier(node) && isCountableUse(node, targetSymbol, checker)) {
785
+ count++;
786
+ }
787
+ import_typescript3.default.forEachChild(node, walk);
788
+ }
789
+ walk(sourceFile);
790
+ return count;
791
+ }
792
+ function isCountableUse(node, targetSymbol, checker) {
793
+ if (!isRealUsagePosition(node)) return false;
794
+ const sym = checker.getSymbolAtLocation(node);
795
+ if (sym === void 0) return false;
796
+ return resolveCanonicalSymbol(sym, checker) === targetSymbol;
797
+ }
798
+ function isRealUsagePosition(node) {
799
+ const parent = node.parent;
800
+ if (isDeclarationBinding(parent, node)) return false;
801
+ if (isImportPosition(parent, node)) return false;
802
+ if (import_typescript3.default.isExportSpecifier(parent)) return false;
803
+ if (import_typescript3.default.isExportAssignment(parent)) return false;
804
+ return true;
805
+ }
806
+ function isDeclarationBinding(parent, node) {
807
+ return isNamedDeclParent(parent) && parent.name === node;
808
+ }
809
+ function isNamedDeclParent(parent) {
810
+ return import_typescript3.default.isVariableDeclaration(parent) || import_typescript3.default.isTypeAliasDeclaration(parent) || import_typescript3.default.isInterfaceDeclaration(parent) || import_typescript3.default.isFunctionDeclaration(parent) || import_typescript3.default.isClassDeclaration(parent);
811
+ }
812
+ function isImportPosition(parent, node) {
813
+ if (import_typescript3.default.isImportSpecifier(parent)) return true;
814
+ if (import_typescript3.default.isImportClause(parent) && parent.name === node) return true;
815
+ return import_typescript3.default.isNamespaceImport(parent) && parent.name === node;
816
+ }
817
+ function resolveCanonicalSymbol(symbol, checker) {
818
+ if ((symbol.flags & import_typescript3.default.SymbolFlags.Alias) === 0) return symbol;
819
+ return checker.getAliasedSymbol(symbol);
820
+ }
821
+
767
822
  // src/rules/no-false-sharing/index.ts
768
823
  var no_false_sharing_default = {
769
824
  meta: {
@@ -954,11 +1009,11 @@ function isProjectSourceFile(filePath, options) {
954
1009
  function collectImportedSymbolUsages(sourceFile, projectContext) {
955
1010
  const usages = [];
956
1011
  for (const statement of sourceFile.statements) {
957
- if (import_typescript3.default.isImportDeclaration(statement)) {
1012
+ if (import_typescript4.default.isImportDeclaration(statement)) {
958
1013
  addImportDeclarationUsages(statement, sourceFile, projectContext, usages);
959
1014
  continue;
960
1015
  }
961
- if (import_typescript3.default.isExportDeclaration(statement)) {
1016
+ if (import_typescript4.default.isExportDeclaration(statement)) {
962
1017
  addExportDeclarationUsages(statement, sourceFile, projectContext, usages);
963
1018
  }
964
1019
  }
@@ -972,7 +1027,7 @@ function addImportDeclarationUsages(statement, sourceFile, projectContext, usage
972
1027
  addIdentifierUsage(statement.importClause.name, targetFile, projectContext, usages);
973
1028
  }
974
1029
  const namedBindings = statement.importClause.namedBindings;
975
- if (namedBindings === void 0 || !import_typescript3.default.isNamedImports(namedBindings)) return;
1030
+ if (namedBindings === void 0 || !import_typescript4.default.isNamedImports(namedBindings)) return;
976
1031
  for (const element of namedBindings.elements) {
977
1032
  addIdentifierUsage(element.name, targetFile, projectContext, usages);
978
1033
  }
@@ -980,15 +1035,15 @@ function addImportDeclarationUsages(statement, sourceFile, projectContext, usage
980
1035
  function addExportDeclarationUsages(statement, sourceFile, projectContext, usages) {
981
1036
  const targetFile = getResolvedTargetFile(statement.moduleSpecifier, sourceFile, projectContext);
982
1037
  if (targetFile === void 0) return;
983
- if (statement.exportClause === void 0 || !import_typescript3.default.isNamedExports(statement.exportClause)) return;
1038
+ if (statement.exportClause === void 0 || !import_typescript4.default.isNamedExports(statement.exportClause)) return;
984
1039
  for (const element of statement.exportClause.elements) {
985
1040
  const reference = element.propertyName ?? element.name;
986
- if (!import_typescript3.default.isIdentifier(reference)) continue;
1041
+ if (!import_typescript4.default.isIdentifier(reference)) continue;
987
1042
  addIdentifierUsage(reference, targetFile, projectContext, usages);
988
1043
  }
989
1044
  }
990
1045
  function getResolvedTargetFile(moduleSpecifier, sourceFile, projectContext) {
991
- if (moduleSpecifier === void 0 || !import_typescript3.default.isStringLiteral(moduleSpecifier)) return void 0;
1046
+ if (moduleSpecifier === void 0 || !import_typescript4.default.isStringLiteral(moduleSpecifier)) return void 0;
992
1047
  return resolveImportTarget(sourceFile.fileName, projectContext, moduleSpecifier.text);
993
1048
  }
994
1049
  function addIdentifierUsage(identifier, targetFile, projectContext, usages) {
@@ -1000,7 +1055,7 @@ function addIdentifierUsage(identifier, targetFile, projectContext, usages) {
1000
1055
  usages.push({ canonicalKey, targetFile });
1001
1056
  }
1002
1057
  function getCanonicalSymbol(checker, symbol) {
1003
- if ((symbol.flags & import_typescript3.default.SymbolFlags.Alias) === 0) return symbol;
1058
+ if ((symbol.flags & import_typescript4.default.SymbolFlags.Alias) === 0) return symbol;
1004
1059
  return checker.getAliasedSymbol(symbol);
1005
1060
  }
1006
1061
  function getCanonicalSymbolKey(symbol) {
@@ -1244,6 +1299,7 @@ function walkMemberIds(node, ids, skip) {
1244
1299
  function walkPropertyIds(node, ids, skip) {
1245
1300
  if (prop(node, "computed")) walkIds(prop(node, "key"), ids, skip);
1246
1301
  walkIds(prop(node, "value"), ids, skip);
1302
+ walkIds(prop(node, "typeAnnotation"), ids, skip);
1247
1303
  }
1248
1304
  function isPropertyLike(t) {
1249
1305
  return t === "Property" || t === "MethodDefinition" || t === "PropertyDefinition";
@@ -1285,17 +1341,23 @@ function visitValue(val, visit) {
1285
1341
 
1286
1342
  // src/rules/read-friendly-order/kahns-sort.ts
1287
1343
  function kahnsTopologicalSort(items, priority) {
1288
- const byName = new Map(items.filter((e) => e.name).map((e) => [e.name, e]));
1289
- const inDeg = buildInDegrees(items, byName);
1344
+ const byName = /* @__PURE__ */ new Map();
1345
+ for (const e of items) {
1346
+ if (e.name) byName.set(e.name, e);
1347
+ }
1348
+ const { inDeg, eagerDependents } = buildInDegrees(items, byName);
1290
1349
  const queue = items.filter((e) => !e.name || inDeg.get(e.name) === 0);
1291
1350
  const result = [];
1292
- const state = { placed: /* @__PURE__ */ new Set(), inDeg, byName };
1351
+ const state = { placed: /* @__PURE__ */ new Set(), inDeg, byName, eagerDependents };
1293
1352
  drainQueue(queue, result, state, priority);
1294
1353
  appendRemaining(items, result, state.placed);
1295
1354
  return result;
1296
1355
  }
1297
1356
  function findCyclicNames(items) {
1298
- const byName = new Map(items.filter((e) => e.name).map((e) => [e.name, e]));
1357
+ const byName = /* @__PURE__ */ new Map();
1358
+ for (const e of items) {
1359
+ if (e.name) byName.set(e.name, e);
1360
+ }
1299
1361
  const inCycle = /* @__PURE__ */ new Set();
1300
1362
  for (const [name] of byName) {
1301
1363
  if (reachesSelf(name, name, byName, /* @__PURE__ */ new Set())) {
@@ -1306,33 +1368,67 @@ function findCyclicNames(items) {
1306
1368
  }
1307
1369
  function buildInDegrees(items, byName) {
1308
1370
  const inDeg = /* @__PURE__ */ new Map();
1309
- for (const [name] of byName) inDeg.set(name, 0);
1371
+ const eagerDependents = /* @__PURE__ */ new Map();
1372
+ for (const [name] of byName) {
1373
+ inDeg.set(name, 0);
1374
+ eagerDependents.set(name, []);
1375
+ }
1310
1376
  for (const item of items) {
1311
- for (const d of item.deps) {
1312
- if (inDeg.has(d)) inDeg.set(d, inDeg.get(d) + 1);
1377
+ processItemDeps(item, inDeg, eagerDependents);
1378
+ }
1379
+ return { inDeg, eagerDependents };
1380
+ }
1381
+ function processItemDeps(item, inDeg, eagerDependents) {
1382
+ for (const d of item.deps) {
1383
+ if (!inDeg.has(d)) continue;
1384
+ if (item.eager && item.name && inDeg.has(item.name)) {
1385
+ addEagerDependent(item.name, d, inDeg, eagerDependents);
1386
+ } else {
1387
+ inDeg.set(d, (inDeg.get(d) ?? 0) + 1);
1313
1388
  }
1314
1389
  }
1315
- return inDeg;
1390
+ }
1391
+ function addEagerDependent(consumerName, dep, inDeg, eagerDependents) {
1392
+ inDeg.set(consumerName, (inDeg.get(consumerName) ?? 0) + 1);
1393
+ const list = eagerDependents.get(dep) ?? [];
1394
+ list.push(consumerName);
1395
+ eagerDependents.set(dep, list);
1316
1396
  }
1317
1397
  function drainQueue(queue, result, state, priority) {
1318
1398
  while (queue.length > 0) {
1319
1399
  queue.sort((a, b) => (priority ? priority(a) - priority(b) : 0) || a.idx - b.idx);
1320
1400
  const item = queue.shift();
1401
+ if (item === void 0) break;
1321
1402
  result.push(item);
1322
1403
  if (item.name) state.placed.add(item.name);
1323
1404
  decrementDeps(item, queue, state);
1324
1405
  }
1325
1406
  }
1326
1407
  function decrementDeps(item, queue, state) {
1408
+ decrementNonEager(item, queue, state);
1409
+ decrementEager(item, queue, state);
1410
+ }
1411
+ function decrementNonEager(item, queue, state) {
1327
1412
  for (const d of item.deps) {
1328
1413
  if (state.placed.has(d) || !state.inDeg.has(d)) continue;
1329
- state.inDeg.set(d, state.inDeg.get(d) - 1);
1330
- if (state.inDeg.get(d) === 0) {
1331
- const dm = state.byName.get(d);
1332
- if (dm && !state.placed.has(d)) queue.push(dm);
1333
- }
1414
+ const current = state.inDeg.get(d) ?? 0;
1415
+ state.inDeg.set(d, current - 1);
1416
+ if (current - 1 === 0) enqueueByName(d, queue, state);
1334
1417
  }
1335
1418
  }
1419
+ function decrementEager(item, queue, state) {
1420
+ if (!item.name) return;
1421
+ for (const consumerName of state.eagerDependents.get(item.name) ?? []) {
1422
+ if (state.placed.has(consumerName) || !state.inDeg.has(consumerName)) continue;
1423
+ const current = state.inDeg.get(consumerName) ?? 0;
1424
+ state.inDeg.set(consumerName, current - 1);
1425
+ if (current - 1 === 0) enqueueByName(consumerName, queue, state);
1426
+ }
1427
+ }
1428
+ function enqueueByName(name, queue, state) {
1429
+ const entry = state.byName.get(name);
1430
+ if (entry && !state.placed.has(name)) queue.push(entry);
1431
+ }
1336
1432
  function appendRemaining(items, result, placed) {
1337
1433
  for (const item of items) {
1338
1434
  if (item.name && !placed.has(item.name)) result.push(item);
@@ -2115,7 +2211,7 @@ function isEntrypointFile(filePath, entrypoints) {
2115
2211
  }
2116
2212
 
2117
2213
  // src/rules/no-single-use-constants/index.ts
2118
- var import_typescript4 = __toESM(require("typescript"), 1);
2214
+ var import_typescript5 = __toESM(require("typescript"), 1);
2119
2215
  var no_single_use_constants_default = {
2120
2216
  meta: {
2121
2217
  type: "suggestion",
@@ -2198,11 +2294,7 @@ function extractConstDeclarators(stmt) {
2198
2294
  function isExcludedInitializer(declarator) {
2199
2295
  const init = declarator.init;
2200
2296
  if (init === null || init === void 0) return true;
2201
- return init.type === "ObjectExpression" || init.type === "NewExpression" || init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression" || init.type === "ClassExpression" || hasExplicitTypeArguments(init);
2202
- }
2203
- function hasExplicitTypeArguments(node) {
2204
- if (node.type !== "CallExpression") return false;
2205
- return node.typeArguments !== void 0 || node.typeParameters !== void 0;
2297
+ return init.type === "ObjectExpression" || init.type === "NewExpression" || init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression" || init.type === "ClassExpression" || init.type === "CallExpression";
2206
2298
  }
2207
2299
  function collectExportedNames(program, names) {
2208
2300
  for (const stmt of program.body) {
@@ -2242,65 +2334,124 @@ function countExportedUses(name, filename, tsContext) {
2242
2334
  if (targetSymbol === void 0) return void 0;
2243
2335
  return countProjectWideUses(targetSymbol, tsContext.checker, tsContext.program);
2244
2336
  }
2245
- function findTsSourceFile(program, filename) {
2246
- const normalized = normalizeResolvedPath2(filename);
2247
- return program.getSourceFiles().find((sf) => normalizeResolvedPath2(sf.fileName) === normalized);
2248
- }
2249
2337
  function findDeclarationSymbol(sourceFile, name, checker) {
2250
2338
  for (const stmt of sourceFile.statements) {
2251
- if (!import_typescript4.default.isVariableStatement(stmt)) continue;
2252
- if ((stmt.declarationList.flags & import_typescript4.default.NodeFlags.Const) === 0) continue;
2339
+ if (!import_typescript5.default.isVariableStatement(stmt)) continue;
2340
+ if ((stmt.declarationList.flags & import_typescript5.default.NodeFlags.Const) === 0) continue;
2253
2341
  for (const decl of stmt.declarationList.declarations) {
2254
- if (!import_typescript4.default.isIdentifier(decl.name) || decl.name.text !== name) continue;
2342
+ if (!import_typescript5.default.isIdentifier(decl.name) || decl.name.text !== name) continue;
2255
2343
  return checker.getSymbolAtLocation(decl.name);
2256
2344
  }
2257
2345
  }
2258
2346
  return void 0;
2259
2347
  }
2260
- function countProjectWideUses(targetSymbol, checker, program) {
2261
- let total = 0;
2262
- for (const sf of program.getSourceFiles()) {
2263
- if (sf.isDeclarationFile) continue;
2264
- total += countUsesInFile(sf, targetSymbol, checker);
2265
- }
2266
- return total;
2267
- }
2268
- function countUsesInFile(sourceFile, targetSymbol, checker) {
2269
- let count = 0;
2270
- function walk(node) {
2271
- if (import_typescript4.default.isIdentifier(node) && isCountableUse(node, targetSymbol, checker)) {
2272
- count++;
2348
+
2349
+ // src/rules/no-unused-types/index.ts
2350
+ var import_typescript6 = __toESM(require("typescript"), 1);
2351
+ var no_unused_types_default = {
2352
+ meta: {
2353
+ type: "suggestion",
2354
+ docs: {
2355
+ description: "Disallow exported type aliases and interfaces with zero project-wide uses",
2356
+ recommended: false
2357
+ },
2358
+ schema: [],
2359
+ messages: {
2360
+ configurationError: "Configuration error: {{details}}",
2361
+ zeroUses: 'type "{{name}}" has no real uses across the project; remove it'
2273
2362
  }
2274
- import_typescript4.default.forEachChild(node, walk);
2363
+ },
2364
+ create(context) {
2365
+ const filename = context.filename;
2366
+ return {
2367
+ "Program:exit"(node) {
2368
+ const exportedTypeNames = collectExportedTypeNames(node);
2369
+ if (filename.length === 0 || exportedTypeNames.size === 0) return;
2370
+ const projectContext = getRequiredTypeScriptProjectContext(filename);
2371
+ if (projectContext.kind === "context-error") {
2372
+ context.report({
2373
+ node,
2374
+ messageId: "configurationError",
2375
+ data: {
2376
+ details: formatProjectContextError(projectContext.error)
2377
+ }
2378
+ });
2379
+ return;
2380
+ }
2381
+ const tsContext = projectContext.projectContext;
2382
+ for (const name of exportedTypeNames) {
2383
+ checkType(name, filename, tsContext, context);
2384
+ }
2385
+ }
2386
+ };
2275
2387
  }
2276
- walk(sourceFile);
2277
- return count;
2388
+ };
2389
+ function collectExportedTypeNames(program) {
2390
+ const names = /* @__PURE__ */ new Set();
2391
+ for (const stmt of program.body) {
2392
+ const name = getExportedTypeName(stmt);
2393
+ if (name !== void 0) names.add(name);
2394
+ }
2395
+ return names;
2278
2396
  }
2279
- function isCountableUse(node, targetSymbol, checker) {
2280
- if (!isRealUsagePosition(node)) return false;
2281
- const sym = checker.getSymbolAtLocation(node);
2282
- if (sym === void 0) return false;
2283
- return resolveCanonicalSymbol(sym, checker) === targetSymbol;
2397
+ function getExportedTypeName(stmt) {
2398
+ if (stmt.type !== "ExportNamedDeclaration") return void 0;
2399
+ const decl = stmt.declaration;
2400
+ if (decl === null || decl === void 0) return void 0;
2401
+ const declType = strProp2(decl, "type");
2402
+ if (declType !== "TSTypeAliasDeclaration" && declType !== "TSInterfaceDeclaration")
2403
+ return void 0;
2404
+ const idNode = prop2(decl, "id");
2405
+ return strProp2(idNode, "name");
2284
2406
  }
2285
- function isRealUsagePosition(node) {
2286
- const parent = node.parent;
2287
- if (isDeclarationBinding(parent, node)) return false;
2288
- if (isImportPosition(parent, node)) return false;
2289
- if (import_typescript4.default.isExportSpecifier(parent)) return false;
2290
- if (import_typescript4.default.isExportAssignment(parent)) return false;
2291
- return true;
2407
+ function checkType(name, filename, tsContext, context) {
2408
+ const sourceFile = findTsSourceFile(tsContext.program, filename);
2409
+ if (sourceFile === void 0) return;
2410
+ const targetSymbol = findTypeDeclarationSymbol(sourceFile, name, tsContext.checker);
2411
+ if (targetSymbol === void 0) return;
2412
+ const count = countProjectWideUses(targetSymbol, tsContext.checker, tsContext.program);
2413
+ if (count > 0) return;
2414
+ const decl = targetSymbol.declarations?.[0];
2415
+ if (decl === void 0) return;
2416
+ context.report({
2417
+ node: context.sourceCode.ast,
2418
+ loc: {
2419
+ start: {
2420
+ line: import_typescript6.default.getLineAndCharacterOfPosition(sourceFile, decl.getStart(sourceFile)).line + 1,
2421
+ column: import_typescript6.default.getLineAndCharacterOfPosition(sourceFile, decl.getStart(sourceFile)).character
2422
+ },
2423
+ end: {
2424
+ line: import_typescript6.default.getLineAndCharacterOfPosition(sourceFile, decl.getEnd()).line + 1,
2425
+ column: import_typescript6.default.getLineAndCharacterOfPosition(sourceFile, decl.getEnd()).character
2426
+ }
2427
+ },
2428
+ messageId: "zeroUses",
2429
+ data: { name }
2430
+ });
2292
2431
  }
2293
- function isDeclarationBinding(parent, node) {
2294
- return import_typescript4.default.isVariableDeclaration(parent) && parent.name === node;
2432
+ function findTypeDeclarationSymbol(sourceFile, name, checker) {
2433
+ for (const stmt of sourceFile.statements) {
2434
+ const decl = getTypedDeclaration(stmt);
2435
+ if (decl === void 0) continue;
2436
+ if (!import_typescript6.default.isIdentifier(decl.name) || decl.name.text !== name) continue;
2437
+ const sym = checker.getSymbolAtLocation(decl.name);
2438
+ if (sym !== void 0) return sym;
2439
+ }
2440
+ return void 0;
2295
2441
  }
2296
- function isImportPosition(parent, node) {
2297
- if (import_typescript4.default.isImportSpecifier(parent)) return true;
2298
- if (import_typescript4.default.isImportClause(parent) && parent.name === node) return true;
2299
- return import_typescript4.default.isNamespaceImport(parent) && parent.name === node;
2442
+ function getTypedDeclaration(stmt) {
2443
+ if (import_typescript6.default.isTypeAliasDeclaration(stmt) || import_typescript6.default.isInterfaceDeclaration(stmt)) {
2444
+ return stmt;
2445
+ }
2446
+ return void 0;
2300
2447
  }
2301
- function resolveCanonicalSymbol(symbol, checker) {
2302
- if ((symbol.flags & import_typescript4.default.SymbolFlags.Alias) === 0) return symbol;
2303
- return checker.getAliasedSymbol(symbol);
2448
+ function strProp2(obj, key) {
2449
+ const v = prop2(obj, key);
2450
+ return typeof v === "string" ? v : void 0;
2451
+ }
2452
+ function prop2(obj, key) {
2453
+ if (typeof obj !== "object" || obj === null) return void 0;
2454
+ return Reflect.get(obj, key);
2304
2455
  }
2305
2456
 
2306
2457
  // src/rules/index.ts
@@ -2312,7 +2463,8 @@ var rules_default = {
2312
2463
  "import-control": import_control_default,
2313
2464
  "no-whitebox-testing": no_whitebox_testing_default,
2314
2465
  "export-control": export_control_default,
2315
- "no-single-use-constants": no_single_use_constants_default
2466
+ "no-single-use-constants": no_single_use_constants_default,
2467
+ "no-unused-types": no_unused_types_default
2316
2468
  };
2317
2469
 
2318
2470
  // src/index.ts
@@ -2343,6 +2495,7 @@ var full = {
2343
2495
  "unslop/export-control": "error",
2344
2496
  "unslop/no-false-sharing": "error",
2345
2497
  "unslop/no-single-use-constants": "error",
2498
+ "unslop/no-unused-types": "error",
2346
2499
  "unslop/read-friendly-order": "error"
2347
2500
  }
2348
2501
  };