@anatolykoptev/krolik-cli 0.5.0 → 0.7.0
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/bin/cli.js +1140 -503
- package/dist/bin/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import { SyntaxKind, Project, Node, DiagnosticCategory } from 'ts-morph';
|
|
|
8
8
|
import * as crypto from 'crypto';
|
|
9
9
|
import { createHash } from 'crypto';
|
|
10
10
|
import { parseSync } from '@swc/core';
|
|
11
|
-
import { spawnSync, spawn, execSync } from 'child_process';
|
|
11
|
+
import { spawnSync, spawn, execFileSync, execSync } from 'child_process';
|
|
12
12
|
import { defaultOptions, minify } from 'minify-xml';
|
|
13
13
|
import { homedir } from 'os';
|
|
14
14
|
import Database from 'better-sqlite3';
|
|
@@ -56,7 +56,7 @@ var KROLIK_VERSION, TEMPLATE_VERSION;
|
|
|
56
56
|
var init_version = __esm({
|
|
57
57
|
"src/version.ts"() {
|
|
58
58
|
init_esm_shims();
|
|
59
|
-
KROLIK_VERSION = "0.
|
|
59
|
+
KROLIK_VERSION = "0.7.0";
|
|
60
60
|
TEMPLATE_VERSION = "6.0.0";
|
|
61
61
|
}
|
|
62
62
|
});
|
|
@@ -1394,6 +1394,279 @@ var init_signatures = __esm({
|
|
|
1394
1394
|
}
|
|
1395
1395
|
});
|
|
1396
1396
|
|
|
1397
|
+
// src/lib/@ast/fingerprint/normalize.ts
|
|
1398
|
+
function normalizeAst(node, options = {}, depth = 0) {
|
|
1399
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
1400
|
+
if (depth > opts.maxDepth) {
|
|
1401
|
+
return { type: "$MAX_DEPTH" };
|
|
1402
|
+
}
|
|
1403
|
+
if (node === null || node === void 0) {
|
|
1404
|
+
return null;
|
|
1405
|
+
}
|
|
1406
|
+
if (typeof node !== "object") {
|
|
1407
|
+
return node;
|
|
1408
|
+
}
|
|
1409
|
+
if (Array.isArray(node)) {
|
|
1410
|
+
return node.map((item) => normalizeAst(item, opts, depth + 1));
|
|
1411
|
+
}
|
|
1412
|
+
const obj = node;
|
|
1413
|
+
const nodeType = obj.type;
|
|
1414
|
+
if (nodeType) {
|
|
1415
|
+
return normalizeNodeByType(obj, nodeType, opts, depth);
|
|
1416
|
+
}
|
|
1417
|
+
return normalizeGenericObject(obj, opts, depth);
|
|
1418
|
+
}
|
|
1419
|
+
function normalizeNodeByType(node, nodeType, opts, depth) {
|
|
1420
|
+
if (nodeType === "Identifier" && opts.normalizeIdentifiers) {
|
|
1421
|
+
return { type: "Identifier", name: PLACEHOLDERS.IDENTIFIER };
|
|
1422
|
+
}
|
|
1423
|
+
if (nodeType === "StringLiteral" && opts.normalizeStrings) {
|
|
1424
|
+
return { type: "StringLiteral", value: PLACEHOLDERS.STRING };
|
|
1425
|
+
}
|
|
1426
|
+
if ((nodeType === "NumericLiteral" || nodeType === "NumberLiteral") && opts.normalizeNumbers) {
|
|
1427
|
+
return { type: "NumericLiteral", value: PLACEHOLDERS.NUMBER };
|
|
1428
|
+
}
|
|
1429
|
+
if (nodeType === "BigIntLiteral" && opts.normalizeNumbers) {
|
|
1430
|
+
return { type: "BigIntLiteral", value: PLACEHOLDERS.NUMBER };
|
|
1431
|
+
}
|
|
1432
|
+
if (nodeType === "RegExpLiteral") {
|
|
1433
|
+
const flags = node.flags;
|
|
1434
|
+
return { type: "RegExpLiteral", pattern: PLACEHOLDERS.REGEX, flags };
|
|
1435
|
+
}
|
|
1436
|
+
if (nodeType === "TemplateLiteral" && opts.normalizeStrings) {
|
|
1437
|
+
const quasis = node.quasis;
|
|
1438
|
+
const expressions = node.expressions;
|
|
1439
|
+
return {
|
|
1440
|
+
type: "TemplateLiteral",
|
|
1441
|
+
quasis: quasis?.map(() => ({ type: "TemplateElement", value: PLACEHOLDERS.TEMPLATE })),
|
|
1442
|
+
expressions: expressions?.map((e) => normalizeAst(e, opts, depth + 1))
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
if (nodeType === "JSXText" && opts.normalizeStrings) {
|
|
1446
|
+
return { type: "JSXText", value: PLACEHOLDERS.STRING };
|
|
1447
|
+
}
|
|
1448
|
+
return normalizeGenericObject(node, opts, depth);
|
|
1449
|
+
}
|
|
1450
|
+
function normalizeGenericObject(obj, opts, depth) {
|
|
1451
|
+
const result = {};
|
|
1452
|
+
for (const key of Object.keys(obj)) {
|
|
1453
|
+
if (SKIP_KEYS.has(key)) continue;
|
|
1454
|
+
const value = obj[key];
|
|
1455
|
+
if (key === "type") {
|
|
1456
|
+
if (opts.includeTypes) {
|
|
1457
|
+
result[key] = value;
|
|
1458
|
+
}
|
|
1459
|
+
continue;
|
|
1460
|
+
}
|
|
1461
|
+
result[key] = normalizeAst(value, opts, depth + 1);
|
|
1462
|
+
}
|
|
1463
|
+
return result;
|
|
1464
|
+
}
|
|
1465
|
+
function astToTokens(node, options = {}, tokens = [], depth = 0) {
|
|
1466
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
1467
|
+
if (depth > opts.maxDepth) {
|
|
1468
|
+
tokens.push("$MAX");
|
|
1469
|
+
return tokens;
|
|
1470
|
+
}
|
|
1471
|
+
if (node === null || node === void 0) {
|
|
1472
|
+
return tokens;
|
|
1473
|
+
}
|
|
1474
|
+
if (typeof node !== "object") {
|
|
1475
|
+
if (typeof node === "string" && opts.normalizeStrings) {
|
|
1476
|
+
tokens.push(PLACEHOLDERS.STRING);
|
|
1477
|
+
} else if (typeof node === "number" && opts.normalizeNumbers) {
|
|
1478
|
+
tokens.push(PLACEHOLDERS.NUMBER);
|
|
1479
|
+
} else if (typeof node === "boolean") {
|
|
1480
|
+
tokens.push(node ? "TRUE" : "FALSE");
|
|
1481
|
+
}
|
|
1482
|
+
return tokens;
|
|
1483
|
+
}
|
|
1484
|
+
if (Array.isArray(node)) {
|
|
1485
|
+
tokens.push("[");
|
|
1486
|
+
for (const item of node) {
|
|
1487
|
+
astToTokens(item, opts, tokens, depth + 1);
|
|
1488
|
+
}
|
|
1489
|
+
tokens.push("]");
|
|
1490
|
+
return tokens;
|
|
1491
|
+
}
|
|
1492
|
+
const obj = node;
|
|
1493
|
+
const nodeType = obj.type;
|
|
1494
|
+
if (nodeType) {
|
|
1495
|
+
tokens.push(nodeType);
|
|
1496
|
+
if (nodeType === "Identifier" && opts.normalizeIdentifiers) {
|
|
1497
|
+
tokens.push(PLACEHOLDERS.IDENTIFIER);
|
|
1498
|
+
return tokens;
|
|
1499
|
+
}
|
|
1500
|
+
if (nodeType === "StringLiteral" && opts.normalizeStrings) {
|
|
1501
|
+
tokens.push(PLACEHOLDERS.STRING);
|
|
1502
|
+
return tokens;
|
|
1503
|
+
}
|
|
1504
|
+
if ((nodeType === "NumericLiteral" || nodeType === "NumberLiteral") && opts.normalizeNumbers) {
|
|
1505
|
+
tokens.push(PLACEHOLDERS.NUMBER);
|
|
1506
|
+
return tokens;
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
tokens.push("{");
|
|
1510
|
+
for (const key of Object.keys(obj)) {
|
|
1511
|
+
if (SKIP_KEYS.has(key) || key === "type") continue;
|
|
1512
|
+
const value = obj[key];
|
|
1513
|
+
if (value !== null && value !== void 0) {
|
|
1514
|
+
tokens.push(`${key}:`);
|
|
1515
|
+
astToTokens(value, opts, tokens, depth + 1);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
tokens.push("}");
|
|
1519
|
+
return tokens;
|
|
1520
|
+
}
|
|
1521
|
+
var DEFAULT_OPTIONS, PLACEHOLDERS, SKIP_KEYS;
|
|
1522
|
+
var init_normalize = __esm({
|
|
1523
|
+
"src/lib/@ast/fingerprint/normalize.ts"() {
|
|
1524
|
+
init_esm_shims();
|
|
1525
|
+
DEFAULT_OPTIONS = {
|
|
1526
|
+
normalizeIdentifiers: true,
|
|
1527
|
+
normalizeStrings: true,
|
|
1528
|
+
normalizeNumbers: true,
|
|
1529
|
+
includeTypes: true,
|
|
1530
|
+
maxDepth: 50
|
|
1531
|
+
};
|
|
1532
|
+
PLACEHOLDERS = {
|
|
1533
|
+
IDENTIFIER: "$ID",
|
|
1534
|
+
STRING: "$STR",
|
|
1535
|
+
NUMBER: "$NUM",
|
|
1536
|
+
REGEX: "$RGX",
|
|
1537
|
+
TEMPLATE: "$TPL"
|
|
1538
|
+
};
|
|
1539
|
+
SKIP_KEYS = /* @__PURE__ */ new Set([
|
|
1540
|
+
"span",
|
|
1541
|
+
"start",
|
|
1542
|
+
"end",
|
|
1543
|
+
"loc",
|
|
1544
|
+
"range",
|
|
1545
|
+
"comments",
|
|
1546
|
+
"leadingComments",
|
|
1547
|
+
"trailingComments",
|
|
1548
|
+
"innerComments",
|
|
1549
|
+
"extra",
|
|
1550
|
+
"raw",
|
|
1551
|
+
"rawValue"
|
|
1552
|
+
]);
|
|
1553
|
+
}
|
|
1554
|
+
});
|
|
1555
|
+
function generateFingerprint(code, options = {}) {
|
|
1556
|
+
const opts = { ...DEFAULT_OPTIONS2, ...options };
|
|
1557
|
+
try {
|
|
1558
|
+
const ast = parseSync(code, {
|
|
1559
|
+
syntax: "typescript",
|
|
1560
|
+
tsx: true
|
|
1561
|
+
});
|
|
1562
|
+
return generateFingerprintFromAst(ast, opts);
|
|
1563
|
+
} catch {
|
|
1564
|
+
return {
|
|
1565
|
+
fingerprint: "",
|
|
1566
|
+
tokenCount: 0,
|
|
1567
|
+
complexity: 0,
|
|
1568
|
+
tooSmall: true
|
|
1569
|
+
};
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
function generateFingerprintFromAst(ast, options = {}) {
|
|
1573
|
+
const opts = { ...DEFAULT_OPTIONS2, ...options };
|
|
1574
|
+
if (opts.tokenBased) {
|
|
1575
|
+
const tokens2 = astToTokens(ast, opts);
|
|
1576
|
+
const tokenCount2 = tokens2.length;
|
|
1577
|
+
if (tokenCount2 < (opts.minTokens ?? 10)) {
|
|
1578
|
+
return {
|
|
1579
|
+
fingerprint: "",
|
|
1580
|
+
tokenCount: tokenCount2,
|
|
1581
|
+
complexity: calculateComplexity(tokens2),
|
|
1582
|
+
tooSmall: true
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
const tokenString = tokens2.join(" ");
|
|
1586
|
+
const fingerprint2 = crypto.createHash("md5").update(tokenString).digest("hex");
|
|
1587
|
+
return {
|
|
1588
|
+
fingerprint: fingerprint2,
|
|
1589
|
+
tokenCount: tokenCount2,
|
|
1590
|
+
complexity: calculateComplexity(tokens2),
|
|
1591
|
+
tooSmall: false
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
const normalized = normalizeAst(ast, opts);
|
|
1595
|
+
const serialized = JSON.stringify(normalized, null, 0);
|
|
1596
|
+
const tokens = astToTokens(ast, opts);
|
|
1597
|
+
const tokenCount = tokens.length;
|
|
1598
|
+
if (tokenCount < (opts.minTokens ?? 10)) {
|
|
1599
|
+
return {
|
|
1600
|
+
fingerprint: "",
|
|
1601
|
+
tokenCount,
|
|
1602
|
+
complexity: calculateComplexity(tokens),
|
|
1603
|
+
tooSmall: true
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
const fingerprint = crypto.createHash("md5").update(serialized).digest("hex");
|
|
1607
|
+
return {
|
|
1608
|
+
fingerprint,
|
|
1609
|
+
tokenCount,
|
|
1610
|
+
complexity: calculateComplexity(tokens),
|
|
1611
|
+
tooSmall: false
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1614
|
+
function calculateComplexity(tokens) {
|
|
1615
|
+
let complexity = 0;
|
|
1616
|
+
complexity += Math.log2(tokens.length + 1) * 10;
|
|
1617
|
+
const controlFlow = [
|
|
1618
|
+
"IfStatement",
|
|
1619
|
+
"ForStatement",
|
|
1620
|
+
"WhileStatement",
|
|
1621
|
+
"SwitchStatement",
|
|
1622
|
+
"TryStatement"
|
|
1623
|
+
];
|
|
1624
|
+
for (const token of tokens) {
|
|
1625
|
+
if (controlFlow.includes(token)) {
|
|
1626
|
+
complexity += 5;
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
const callCount = tokens.filter((t) => t === "CallExpression").length;
|
|
1630
|
+
complexity += callCount * 2;
|
|
1631
|
+
let maxDepth = 0;
|
|
1632
|
+
let currentDepth = 0;
|
|
1633
|
+
for (const token of tokens) {
|
|
1634
|
+
if (token === "{" || token === "[") {
|
|
1635
|
+
currentDepth++;
|
|
1636
|
+
maxDepth = Math.max(maxDepth, currentDepth);
|
|
1637
|
+
} else if (token === "}" || token === "]") {
|
|
1638
|
+
currentDepth--;
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
complexity += maxDepth * 3;
|
|
1642
|
+
return Math.round(complexity);
|
|
1643
|
+
}
|
|
1644
|
+
var DEFAULT_OPTIONS2;
|
|
1645
|
+
var init_fingerprint = __esm({
|
|
1646
|
+
"src/lib/@ast/fingerprint/fingerprint.ts"() {
|
|
1647
|
+
init_esm_shims();
|
|
1648
|
+
init_normalize();
|
|
1649
|
+
DEFAULT_OPTIONS2 = {
|
|
1650
|
+
normalizeIdentifiers: true,
|
|
1651
|
+
normalizeStrings: true,
|
|
1652
|
+
normalizeNumbers: true,
|
|
1653
|
+
includeTypes: true,
|
|
1654
|
+
maxDepth: 50,
|
|
1655
|
+
tokenBased: false,
|
|
1656
|
+
minTokens: 10
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
});
|
|
1660
|
+
|
|
1661
|
+
// src/lib/@ast/fingerprint/index.ts
|
|
1662
|
+
var init_fingerprint2 = __esm({
|
|
1663
|
+
"src/lib/@ast/fingerprint/index.ts"() {
|
|
1664
|
+
init_esm_shims();
|
|
1665
|
+
init_fingerprint();
|
|
1666
|
+
init_normalize();
|
|
1667
|
+
}
|
|
1668
|
+
});
|
|
1669
|
+
|
|
1397
1670
|
// src/lib/@ast/index.ts
|
|
1398
1671
|
var init_ast = __esm({
|
|
1399
1672
|
"src/lib/@ast/index.ts"() {
|
|
@@ -1401,6 +1674,7 @@ var init_ast = __esm({
|
|
|
1401
1674
|
init_ts_morph();
|
|
1402
1675
|
init_analysis2();
|
|
1403
1676
|
init_signatures();
|
|
1677
|
+
init_fingerprint2();
|
|
1404
1678
|
}
|
|
1405
1679
|
});
|
|
1406
1680
|
|
|
@@ -1686,6 +1960,85 @@ var init_imports = __esm({
|
|
|
1686
1960
|
});
|
|
1687
1961
|
|
|
1688
1962
|
// src/lib/@ast/swc/string-context.ts
|
|
1963
|
+
function isInsideComment2(content, offset) {
|
|
1964
|
+
let inSingleQuote = false;
|
|
1965
|
+
let inDoubleQuote = false;
|
|
1966
|
+
let inTemplate = false;
|
|
1967
|
+
let templateBraceDepth = 0;
|
|
1968
|
+
let inLineComment = false;
|
|
1969
|
+
let inBlockComment = false;
|
|
1970
|
+
let escaped = false;
|
|
1971
|
+
for (let i = 0; i < offset && i < content.length; i++) {
|
|
1972
|
+
const char = content[i];
|
|
1973
|
+
const nextChar = content[i + 1];
|
|
1974
|
+
if (escaped) {
|
|
1975
|
+
escaped = false;
|
|
1976
|
+
continue;
|
|
1977
|
+
}
|
|
1978
|
+
if (char === "\\" && (inSingleQuote || inDoubleQuote || inTemplate)) {
|
|
1979
|
+
escaped = true;
|
|
1980
|
+
continue;
|
|
1981
|
+
}
|
|
1982
|
+
if (inLineComment) {
|
|
1983
|
+
if (char === "\n") {
|
|
1984
|
+
inLineComment = false;
|
|
1985
|
+
}
|
|
1986
|
+
continue;
|
|
1987
|
+
}
|
|
1988
|
+
if (inBlockComment) {
|
|
1989
|
+
if (char === "*" && nextChar === "/") {
|
|
1990
|
+
inBlockComment = false;
|
|
1991
|
+
i++;
|
|
1992
|
+
}
|
|
1993
|
+
continue;
|
|
1994
|
+
}
|
|
1995
|
+
if (!inLineComment && !inBlockComment) {
|
|
1996
|
+
if (inTemplate && templateBraceDepth === 0 && char === "$" && nextChar === "{") {
|
|
1997
|
+
templateBraceDepth = 1;
|
|
1998
|
+
i++;
|
|
1999
|
+
continue;
|
|
2000
|
+
}
|
|
2001
|
+
if (inTemplate && templateBraceDepth > 0) {
|
|
2002
|
+
if (char === "{") {
|
|
2003
|
+
templateBraceDepth++;
|
|
2004
|
+
} else if (char === "}") {
|
|
2005
|
+
templateBraceDepth--;
|
|
2006
|
+
}
|
|
2007
|
+
if (templateBraceDepth > 0 && char === "/" && nextChar === "/") {
|
|
2008
|
+
inLineComment = true;
|
|
2009
|
+
i++;
|
|
2010
|
+
continue;
|
|
2011
|
+
}
|
|
2012
|
+
if (templateBraceDepth > 0 && char === "/" && nextChar === "*") {
|
|
2013
|
+
inBlockComment = true;
|
|
2014
|
+
i++;
|
|
2015
|
+
continue;
|
|
2016
|
+
}
|
|
2017
|
+
continue;
|
|
2018
|
+
}
|
|
2019
|
+
if (char === "'" && !inDoubleQuote && !inTemplate) {
|
|
2020
|
+
inSingleQuote = !inSingleQuote;
|
|
2021
|
+
} else if (char === '"' && !inSingleQuote && !inTemplate) {
|
|
2022
|
+
inDoubleQuote = !inDoubleQuote;
|
|
2023
|
+
} else if (char === "`" && !inSingleQuote && !inDoubleQuote) {
|
|
2024
|
+
inTemplate = !inTemplate;
|
|
2025
|
+
if (!inTemplate) templateBraceDepth = 0;
|
|
2026
|
+
}
|
|
2027
|
+
if (!inSingleQuote && !inDoubleQuote && !inTemplate) {
|
|
2028
|
+
if (char === "/" && nextChar === "/") {
|
|
2029
|
+
inLineComment = true;
|
|
2030
|
+
i++;
|
|
2031
|
+
continue;
|
|
2032
|
+
}
|
|
2033
|
+
if (char === "/" && nextChar === "*") {
|
|
2034
|
+
inBlockComment = true;
|
|
2035
|
+
i++;
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
return inLineComment || inBlockComment;
|
|
2041
|
+
}
|
|
1689
2042
|
function isInsideStringOrComment(content, offset) {
|
|
1690
2043
|
let inSingleQuote = false;
|
|
1691
2044
|
let inDoubleQuote = false;
|
|
@@ -3107,92 +3460,103 @@ var init_browser_apis = __esm({
|
|
|
3107
3460
|
});
|
|
3108
3461
|
|
|
3109
3462
|
// src/lib/@detectors/lint/lint-detector.ts
|
|
3110
|
-
function
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3463
|
+
function detectDebuggerStatement(nodeType, offset) {
|
|
3464
|
+
if (nodeType === "DebuggerStatement") {
|
|
3465
|
+
return { type: "debugger", offset };
|
|
3466
|
+
}
|
|
3467
|
+
return null;
|
|
3468
|
+
}
|
|
3469
|
+
function detectEmptyCatchBlock(node, nodeType, offset) {
|
|
3470
|
+
if (nodeType !== "CatchClause") {
|
|
3114
3471
|
return null;
|
|
3115
3472
|
}
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
};
|
|
3473
|
+
const catchClause = node;
|
|
3474
|
+
const body = catchClause.body;
|
|
3475
|
+
if (!body || body.type !== "BlockStatement") {
|
|
3476
|
+
return null;
|
|
3121
3477
|
}
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
if (body && body.type === "BlockStatement") {
|
|
3126
|
-
const stmts = body.stmts ?? [];
|
|
3127
|
-
if (stmts.length === 0) {
|
|
3128
|
-
return {
|
|
3129
|
-
type: "empty-catch",
|
|
3130
|
-
offset: span.start
|
|
3131
|
-
};
|
|
3132
|
-
}
|
|
3133
|
-
if (stmts.length === 1) {
|
|
3134
|
-
const stmt = stmts[0];
|
|
3135
|
-
if (stmt.type === "ReturnStatement") {
|
|
3136
|
-
return {
|
|
3137
|
-
type: "empty-catch",
|
|
3138
|
-
offset: span.start
|
|
3139
|
-
};
|
|
3140
|
-
}
|
|
3141
|
-
}
|
|
3142
|
-
}
|
|
3478
|
+
const stmts = body.stmts ?? [];
|
|
3479
|
+
if (stmts.length === 0) {
|
|
3480
|
+
return { type: "empty-catch", offset };
|
|
3143
3481
|
}
|
|
3144
|
-
if (
|
|
3145
|
-
const
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
return null;
|
|
3149
|
-
}
|
|
3150
|
-
const calleeType = callee.type;
|
|
3151
|
-
if (calleeType === "MemberExpression") {
|
|
3152
|
-
const memberExpr = callee;
|
|
3153
|
-
const object = memberExpr.object;
|
|
3154
|
-
const property = memberExpr.property;
|
|
3155
|
-
if (!object || !property) {
|
|
3156
|
-
return null;
|
|
3157
|
-
}
|
|
3158
|
-
const objectType = object.type;
|
|
3159
|
-
const propertyType = property.type;
|
|
3160
|
-
if (objectType === "Identifier" && propertyType === "Identifier") {
|
|
3161
|
-
const objectValue = object.value;
|
|
3162
|
-
const propertyValue = property.value;
|
|
3163
|
-
if (objectValue && propertyValue && isConsoleMember(propertyValue, objectValue)) {
|
|
3164
|
-
return {
|
|
3165
|
-
type: "console",
|
|
3166
|
-
offset: span.start,
|
|
3167
|
-
method: propertyValue
|
|
3168
|
-
};
|
|
3169
|
-
}
|
|
3170
|
-
}
|
|
3171
|
-
}
|
|
3172
|
-
if (calleeType === "Identifier") {
|
|
3173
|
-
const identifier = callee;
|
|
3174
|
-
const identifierValue = identifier.value;
|
|
3175
|
-
if (!identifierValue) {
|
|
3176
|
-
return null;
|
|
3177
|
-
}
|
|
3178
|
-
if (isDialogFunction(identifierValue)) {
|
|
3179
|
-
return {
|
|
3180
|
-
type: "alert",
|
|
3181
|
-
offset: span.start,
|
|
3182
|
-
method: identifierValue
|
|
3183
|
-
};
|
|
3184
|
-
}
|
|
3185
|
-
if (isEvalFunction(identifierValue)) {
|
|
3186
|
-
return {
|
|
3187
|
-
type: "eval",
|
|
3188
|
-
offset: span.start,
|
|
3189
|
-
method: identifierValue
|
|
3190
|
-
};
|
|
3191
|
-
}
|
|
3482
|
+
if (stmts.length === 1) {
|
|
3483
|
+
const stmt = stmts[0];
|
|
3484
|
+
if (stmt.type === "ReturnStatement") {
|
|
3485
|
+
return { type: "empty-catch", offset };
|
|
3192
3486
|
}
|
|
3193
3487
|
}
|
|
3194
3488
|
return null;
|
|
3195
3489
|
}
|
|
3490
|
+
function extractCallee(node, nodeType) {
|
|
3491
|
+
if (nodeType !== "CallExpression" && nodeType !== "OptionalChainingExpression") {
|
|
3492
|
+
return null;
|
|
3493
|
+
}
|
|
3494
|
+
const callExpr = node;
|
|
3495
|
+
return callExpr.callee ?? callExpr.base ?? null;
|
|
3496
|
+
}
|
|
3497
|
+
function detectConsoleCall(callee, offset) {
|
|
3498
|
+
const calleeType = callee.type;
|
|
3499
|
+
if (calleeType !== "MemberExpression") {
|
|
3500
|
+
return null;
|
|
3501
|
+
}
|
|
3502
|
+
const memberExpr = callee;
|
|
3503
|
+
const object = memberExpr.object;
|
|
3504
|
+
const property = memberExpr.property;
|
|
3505
|
+
if (!object || !property) {
|
|
3506
|
+
return null;
|
|
3507
|
+
}
|
|
3508
|
+
const objectNode = object;
|
|
3509
|
+
const propertyNode = property;
|
|
3510
|
+
if (objectNode.type !== "Identifier" || propertyNode.type !== "Identifier") {
|
|
3511
|
+
return null;
|
|
3512
|
+
}
|
|
3513
|
+
const objectValue = objectNode.value;
|
|
3514
|
+
const propertyValue = propertyNode.value;
|
|
3515
|
+
if (!objectValue || !propertyValue) {
|
|
3516
|
+
return null;
|
|
3517
|
+
}
|
|
3518
|
+
if (isConsoleMember(propertyValue, objectValue)) {
|
|
3519
|
+
return { type: "console", offset, method: propertyValue };
|
|
3520
|
+
}
|
|
3521
|
+
return null;
|
|
3522
|
+
}
|
|
3523
|
+
function detectGlobalFunctionCall(callee, offset) {
|
|
3524
|
+
const calleeType = callee.type;
|
|
3525
|
+
if (calleeType !== "Identifier") {
|
|
3526
|
+
return null;
|
|
3527
|
+
}
|
|
3528
|
+
const identifier = callee;
|
|
3529
|
+
const name = identifier.value;
|
|
3530
|
+
if (!name) {
|
|
3531
|
+
return null;
|
|
3532
|
+
}
|
|
3533
|
+
if (isDialogFunction(name)) {
|
|
3534
|
+
return { type: "alert", offset, method: name };
|
|
3535
|
+
}
|
|
3536
|
+
if (isEvalFunction(name)) {
|
|
3537
|
+
return { type: "eval", offset, method: name };
|
|
3538
|
+
}
|
|
3539
|
+
return null;
|
|
3540
|
+
}
|
|
3541
|
+
function detectLintIssue(node) {
|
|
3542
|
+
const nodeType = node.type;
|
|
3543
|
+
const span = node.span;
|
|
3544
|
+
if (!span || !nodeType) {
|
|
3545
|
+
return null;
|
|
3546
|
+
}
|
|
3547
|
+
const offset = span.start;
|
|
3548
|
+
const debuggerResult = detectDebuggerStatement(nodeType, offset);
|
|
3549
|
+
if (debuggerResult) return debuggerResult;
|
|
3550
|
+
const emptyCatchResult = detectEmptyCatchBlock(node, nodeType, offset);
|
|
3551
|
+
if (emptyCatchResult) return emptyCatchResult;
|
|
3552
|
+
const callee = extractCallee(node, nodeType);
|
|
3553
|
+
if (!callee) return null;
|
|
3554
|
+
const consoleResult = detectConsoleCall(callee, offset);
|
|
3555
|
+
if (consoleResult) return consoleResult;
|
|
3556
|
+
const globalResult = detectGlobalFunctionCall(callee, offset);
|
|
3557
|
+
if (globalResult) return globalResult;
|
|
3558
|
+
return null;
|
|
3559
|
+
}
|
|
3196
3560
|
var init_lint_detector = __esm({
|
|
3197
3561
|
"src/lib/@detectors/lint/lint-detector.ts"() {
|
|
3198
3562
|
init_esm_shims();
|
|
@@ -3202,117 +3566,119 @@ var init_lint_detector = __esm({
|
|
|
3202
3566
|
|
|
3203
3567
|
// src/lib/@detectors/lint/type-safety-detector.ts
|
|
3204
3568
|
function isAnyType(node) {
|
|
3205
|
-
const
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3569
|
+
const typedNode = node;
|
|
3570
|
+
return typedNode.type === "TsKeywordType" && typedNode.kind === "any";
|
|
3571
|
+
}
|
|
3572
|
+
function createDetection(type, offset) {
|
|
3573
|
+
return { type, offset };
|
|
3574
|
+
}
|
|
3575
|
+
function detectTypeAnnotation(node, span) {
|
|
3576
|
+
if (node.type !== "TsTypeAnnotation") return null;
|
|
3577
|
+
const typeAnnotation = node.typeAnnotation;
|
|
3578
|
+
if (!typeAnnotation || !isAnyType(typeAnnotation)) return null;
|
|
3579
|
+
return createDetection("any-annotation", span.start);
|
|
3580
|
+
}
|
|
3581
|
+
function detectAsAnyAssertion(node, span) {
|
|
3582
|
+
if (node.type !== "TsAsExpression") return null;
|
|
3583
|
+
const typeAnnotation = node.typeAnnotation;
|
|
3584
|
+
if (!typeAnnotation || !isAnyType(typeAnnotation)) return null;
|
|
3585
|
+
return createDetection("any-assertion", span.start);
|
|
3586
|
+
}
|
|
3587
|
+
function detectDoubleAssertionPattern(node, span) {
|
|
3588
|
+
if (node.type !== "TsAsExpression") return null;
|
|
3589
|
+
const expression = node.expression;
|
|
3590
|
+
if (!expression || expression.type !== "TsAsExpression") return null;
|
|
3591
|
+
const innerTypeAnnotation = expression.typeAnnotation;
|
|
3592
|
+
if (!innerTypeAnnotation) return null;
|
|
3593
|
+
const isUnknownAssertion = innerTypeAnnotation.type === "TsKeywordType" && innerTypeAnnotation.kind === "unknown";
|
|
3594
|
+
if (!isUnknownAssertion) return null;
|
|
3595
|
+
return createDetection("double-assertion", span.start);
|
|
3596
|
+
}
|
|
3597
|
+
function detectNonNullAssertionHandler(node, span) {
|
|
3598
|
+
if (node.type !== "TsNonNullExpression") return null;
|
|
3599
|
+
return createDetection("non-null", span.start);
|
|
3600
|
+
}
|
|
3601
|
+
function detectAnyInTypeParams(node, span) {
|
|
3602
|
+
if (node.type !== "TsTypeReference") return null;
|
|
3603
|
+
const params = node.typeParams?.params;
|
|
3604
|
+
if (!params) return null;
|
|
3605
|
+
for (const param of params) {
|
|
3606
|
+
if (isAnyType(param)) {
|
|
3607
|
+
return createDetection("any-annotation", span.start);
|
|
3608
|
+
}
|
|
3209
3609
|
}
|
|
3210
|
-
return
|
|
3610
|
+
return null;
|
|
3611
|
+
}
|
|
3612
|
+
function detectAnyInReturnType(node, span) {
|
|
3613
|
+
const functionTypes = ["TsFunctionType", "TsConstructorType", "TsMethodSignature"];
|
|
3614
|
+
if (!functionTypes.includes(node.type ?? "")) return null;
|
|
3615
|
+
const typeAnnotation = node.typeAnnotation;
|
|
3616
|
+
if (!typeAnnotation) return null;
|
|
3617
|
+
const returnType = typeAnnotation.typeAnnotation;
|
|
3618
|
+
if (!returnType || !isAnyType(returnType)) return null;
|
|
3619
|
+
return createDetection("any-annotation", span.start);
|
|
3620
|
+
}
|
|
3621
|
+
function detectAnyInParamType(node, span) {
|
|
3622
|
+
if (node.type !== "Parameter") return null;
|
|
3623
|
+
const typeAnnotation = node.typeAnnotation;
|
|
3624
|
+
if (!typeAnnotation) return null;
|
|
3625
|
+
const paramType = typeAnnotation.typeAnnotation;
|
|
3626
|
+
if (!paramType || !isAnyType(paramType)) return null;
|
|
3627
|
+
return createDetection("any-param", span.start);
|
|
3628
|
+
}
|
|
3629
|
+
function detectAnyInArrayType(node, span) {
|
|
3630
|
+
if (node.type !== "TsArrayType") return null;
|
|
3631
|
+
const elemType = node.elemType;
|
|
3632
|
+
if (!elemType || !isAnyType(elemType)) return null;
|
|
3633
|
+
return createDetection("any-array", span.start);
|
|
3211
3634
|
}
|
|
3212
3635
|
function detectTypeSafetyIssue(node) {
|
|
3213
|
-
const
|
|
3214
|
-
const span =
|
|
3215
|
-
if (!span) return null;
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
offset: span.start
|
|
3222
|
-
};
|
|
3223
|
-
}
|
|
3224
|
-
}
|
|
3225
|
-
if (nodeType === "TsAsExpression") {
|
|
3226
|
-
const typeAnnotation = node.typeAnnotation;
|
|
3227
|
-
const expression = node.expression;
|
|
3228
|
-
if (typeAnnotation && isAnyType(typeAnnotation)) {
|
|
3229
|
-
return {
|
|
3230
|
-
type: "any-assertion",
|
|
3231
|
-
offset: span.start
|
|
3232
|
-
};
|
|
3233
|
-
}
|
|
3234
|
-
if (expression && expression.type === "TsAsExpression") {
|
|
3235
|
-
const innerTypeAnnotation = expression.typeAnnotation;
|
|
3236
|
-
if (innerTypeAnnotation) {
|
|
3237
|
-
const innerType = innerTypeAnnotation.type;
|
|
3238
|
-
const innerKind = innerTypeAnnotation.kind;
|
|
3239
|
-
if (innerType === "TsKeywordType" && innerKind === "unknown") {
|
|
3240
|
-
return {
|
|
3241
|
-
type: "double-assertion",
|
|
3242
|
-
offset: span.start
|
|
3243
|
-
};
|
|
3244
|
-
}
|
|
3245
|
-
}
|
|
3246
|
-
}
|
|
3247
|
-
}
|
|
3248
|
-
if (nodeType === "TsNonNullExpression") {
|
|
3249
|
-
return {
|
|
3250
|
-
type: "non-null",
|
|
3251
|
-
offset: span.start
|
|
3252
|
-
};
|
|
3253
|
-
}
|
|
3254
|
-
if (nodeType === "TsTypeReference") {
|
|
3255
|
-
const typeParams = node.typeParams;
|
|
3256
|
-
if (typeParams?.params) {
|
|
3257
|
-
for (const param of typeParams.params) {
|
|
3258
|
-
if (isAnyType(param)) {
|
|
3259
|
-
return {
|
|
3260
|
-
type: "any-annotation",
|
|
3261
|
-
offset: span.start
|
|
3262
|
-
};
|
|
3263
|
-
}
|
|
3264
|
-
}
|
|
3265
|
-
}
|
|
3266
|
-
}
|
|
3267
|
-
if (nodeType === "TsFunctionType" || nodeType === "TsConstructorType" || nodeType === "TsMethodSignature") {
|
|
3268
|
-
const typeAnnotation = node.typeAnnotation;
|
|
3269
|
-
if (typeAnnotation) {
|
|
3270
|
-
const returnType = typeAnnotation.typeAnnotation;
|
|
3271
|
-
if (returnType && isAnyType(returnType)) {
|
|
3272
|
-
return {
|
|
3273
|
-
type: "any-annotation",
|
|
3274
|
-
offset: span.start
|
|
3275
|
-
};
|
|
3276
|
-
}
|
|
3277
|
-
}
|
|
3278
|
-
}
|
|
3279
|
-
if (nodeType === "Parameter") {
|
|
3280
|
-
const typeAnnotation = node.typeAnnotation;
|
|
3281
|
-
if (typeAnnotation) {
|
|
3282
|
-
const paramType = typeAnnotation.typeAnnotation;
|
|
3283
|
-
if (paramType && isAnyType(paramType)) {
|
|
3284
|
-
return {
|
|
3285
|
-
type: "any-param",
|
|
3286
|
-
offset: span.start
|
|
3287
|
-
};
|
|
3288
|
-
}
|
|
3289
|
-
}
|
|
3290
|
-
}
|
|
3291
|
-
if (nodeType === "TsArrayType") {
|
|
3292
|
-
const elemType = node.elemType;
|
|
3293
|
-
if (elemType && isAnyType(elemType)) {
|
|
3294
|
-
return {
|
|
3295
|
-
type: "any-array",
|
|
3296
|
-
offset: span.start
|
|
3297
|
-
};
|
|
3298
|
-
}
|
|
3636
|
+
const typedNode = node;
|
|
3637
|
+
const { type: nodeType, span } = typedNode;
|
|
3638
|
+
if (!span || !nodeType) return null;
|
|
3639
|
+
const handlers = detectionStrategies.get(nodeType);
|
|
3640
|
+
if (!handlers) return null;
|
|
3641
|
+
for (const handler of handlers) {
|
|
3642
|
+
const result = handler(typedNode, span);
|
|
3643
|
+
if (result) return result;
|
|
3299
3644
|
}
|
|
3300
3645
|
return null;
|
|
3301
3646
|
}
|
|
3647
|
+
var detectionStrategies;
|
|
3302
3648
|
var init_type_safety_detector = __esm({
|
|
3303
3649
|
"src/lib/@detectors/lint/type-safety-detector.ts"() {
|
|
3304
3650
|
init_esm_shims();
|
|
3651
|
+
detectionStrategies = /* @__PURE__ */ new Map([
|
|
3652
|
+
["TsTypeAnnotation", [detectTypeAnnotation]],
|
|
3653
|
+
["TsAsExpression", [detectAsAnyAssertion, detectDoubleAssertionPattern]],
|
|
3654
|
+
["TsNonNullExpression", [detectNonNullAssertionHandler]],
|
|
3655
|
+
["TsTypeReference", [detectAnyInTypeParams]],
|
|
3656
|
+
["TsFunctionType", [detectAnyInReturnType]],
|
|
3657
|
+
["TsConstructorType", [detectAnyInReturnType]],
|
|
3658
|
+
["TsMethodSignature", [detectAnyInReturnType]],
|
|
3659
|
+
["Parameter", [detectAnyInParamType]],
|
|
3660
|
+
["TsArrayType", [detectAnyInArrayType]]
|
|
3661
|
+
]);
|
|
3305
3662
|
}
|
|
3306
3663
|
});
|
|
3307
3664
|
|
|
3308
3665
|
// src/lib/@detectors/security/security-detector.ts
|
|
3309
|
-
function
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3666
|
+
function getNodeType2(node) {
|
|
3667
|
+
return node?.type;
|
|
3668
|
+
}
|
|
3669
|
+
function getNodeSpan2(node) {
|
|
3670
|
+
return node.span;
|
|
3671
|
+
}
|
|
3672
|
+
function getIdentifierValue(node) {
|
|
3673
|
+
return node.value;
|
|
3674
|
+
}
|
|
3675
|
+
function getArgumentExpression(arg) {
|
|
3676
|
+
return arg.expression;
|
|
3677
|
+
}
|
|
3678
|
+
function parseCallExpression(node) {
|
|
3679
|
+
const nodeType = getNodeType2(node);
|
|
3680
|
+
const span = getNodeSpan2(node);
|
|
3681
|
+
if (!span || nodeType !== "CallExpression") {
|
|
3316
3682
|
return null;
|
|
3317
3683
|
}
|
|
3318
3684
|
const callExpr = node;
|
|
@@ -3320,82 +3686,132 @@ function detectSecurityIssue(node) {
|
|
|
3320
3686
|
if (!callee) {
|
|
3321
3687
|
return null;
|
|
3322
3688
|
}
|
|
3323
|
-
const calleeType = callee
|
|
3324
|
-
if (calleeType
|
|
3325
|
-
|
|
3326
|
-
const identifierValue = identifier.value;
|
|
3327
|
-
if (identifierValue && COMMAND_EXEC_METHODS.includes(identifierValue)) {
|
|
3328
|
-
const args = callExpr.arguments ?? [];
|
|
3329
|
-
const firstArg = args[0];
|
|
3330
|
-
if (firstArg) {
|
|
3331
|
-
const argExpr = firstArg.expression;
|
|
3332
|
-
if (argExpr) {
|
|
3333
|
-
const argType = argExpr.type;
|
|
3334
|
-
if (argType === "TemplateLiteral") {
|
|
3335
|
-
const templateLiteral = argExpr;
|
|
3336
|
-
const expressions = templateLiteral.expressions ?? [];
|
|
3337
|
-
if (expressions.length > 0) {
|
|
3338
|
-
return {
|
|
3339
|
-
type: "command-injection",
|
|
3340
|
-
offset: span.start,
|
|
3341
|
-
method: identifierValue
|
|
3342
|
-
};
|
|
3343
|
-
}
|
|
3344
|
-
}
|
|
3345
|
-
}
|
|
3346
|
-
}
|
|
3347
|
-
}
|
|
3689
|
+
const calleeType = getNodeType2(callee);
|
|
3690
|
+
if (!calleeType) {
|
|
3691
|
+
return null;
|
|
3348
3692
|
}
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3693
|
+
return {
|
|
3694
|
+
callee,
|
|
3695
|
+
calleeType,
|
|
3696
|
+
args: callExpr.arguments ?? [],
|
|
3697
|
+
span
|
|
3698
|
+
};
|
|
3699
|
+
}
|
|
3700
|
+
function hasTemplateInterpolation(argExpr) {
|
|
3701
|
+
const argType = getNodeType2(argExpr);
|
|
3702
|
+
if (argType !== "TemplateLiteral") {
|
|
3703
|
+
return false;
|
|
3704
|
+
}
|
|
3705
|
+
const templateLiteral = argExpr;
|
|
3706
|
+
const expressions = templateLiteral.expressions ?? [];
|
|
3707
|
+
return expressions.length > 0;
|
|
3708
|
+
}
|
|
3709
|
+
function detectCommandInjectionFromCall(parsed) {
|
|
3710
|
+
if (parsed.calleeType !== "Identifier") {
|
|
3711
|
+
return null;
|
|
3712
|
+
}
|
|
3713
|
+
const methodName = getIdentifierValue(parsed.callee);
|
|
3714
|
+
if (!methodName || !COMMAND_EXEC_METHODS.has(methodName)) {
|
|
3715
|
+
return null;
|
|
3716
|
+
}
|
|
3717
|
+
const firstArg = parsed.args[0];
|
|
3718
|
+
if (!firstArg) {
|
|
3719
|
+
return null;
|
|
3720
|
+
}
|
|
3721
|
+
const argExpr = getArgumentExpression(firstArg);
|
|
3722
|
+
if (!argExpr) {
|
|
3723
|
+
return null;
|
|
3724
|
+
}
|
|
3725
|
+
if (hasTemplateInterpolation(argExpr)) {
|
|
3726
|
+
return {
|
|
3727
|
+
type: "command-injection",
|
|
3728
|
+
offset: parsed.span.start,
|
|
3729
|
+
method: methodName
|
|
3730
|
+
};
|
|
3731
|
+
}
|
|
3732
|
+
return null;
|
|
3733
|
+
}
|
|
3734
|
+
function isUntrustedArgument(arg) {
|
|
3735
|
+
const argExpr = getArgumentExpression(arg);
|
|
3736
|
+
if (!argExpr) {
|
|
3737
|
+
return false;
|
|
3738
|
+
}
|
|
3739
|
+
const argType = getNodeType2(argExpr);
|
|
3740
|
+
if (!argType) {
|
|
3741
|
+
return false;
|
|
3742
|
+
}
|
|
3743
|
+
if (UNTRUSTED_ARG_TYPES.has(argType)) {
|
|
3744
|
+
return true;
|
|
3745
|
+
}
|
|
3746
|
+
if (argType === "TemplateLiteral") {
|
|
3747
|
+
return hasTemplateInterpolation(argExpr);
|
|
3748
|
+
}
|
|
3749
|
+
return false;
|
|
3750
|
+
}
|
|
3751
|
+
function extractPathMethod(callee) {
|
|
3752
|
+
const memberExpr = callee;
|
|
3753
|
+
const { object, property } = memberExpr;
|
|
3754
|
+
if (!object || !property) {
|
|
3755
|
+
return null;
|
|
3756
|
+
}
|
|
3757
|
+
if (getNodeType2(object) !== "Identifier") {
|
|
3758
|
+
return null;
|
|
3759
|
+
}
|
|
3760
|
+
if (getIdentifierValue(object) !== "path") {
|
|
3761
|
+
return null;
|
|
3762
|
+
}
|
|
3763
|
+
if (getNodeType2(property) !== "Identifier") {
|
|
3764
|
+
return null;
|
|
3765
|
+
}
|
|
3766
|
+
const methodName = getIdentifierValue(property);
|
|
3767
|
+
if (!methodName || !PATH_METHODS.has(methodName)) {
|
|
3768
|
+
return null;
|
|
3769
|
+
}
|
|
3770
|
+
return methodName;
|
|
3771
|
+
}
|
|
3772
|
+
function detectPathTraversalFromCall(parsed) {
|
|
3773
|
+
if (parsed.calleeType !== "MemberExpression") {
|
|
3774
|
+
return null;
|
|
3775
|
+
}
|
|
3776
|
+
const methodName = extractPathMethod(parsed.callee);
|
|
3777
|
+
if (!methodName) {
|
|
3778
|
+
return null;
|
|
3779
|
+
}
|
|
3780
|
+
const pathSegmentArgs = parsed.args.slice(1);
|
|
3781
|
+
const hasUntrustedSegment = pathSegmentArgs.some(isUntrustedArgument);
|
|
3782
|
+
if (hasUntrustedSegment) {
|
|
3783
|
+
return {
|
|
3784
|
+
type: "path-traversal",
|
|
3785
|
+
offset: parsed.span.start,
|
|
3786
|
+
method: `path.${methodName}`
|
|
3787
|
+
};
|
|
3788
|
+
}
|
|
3789
|
+
return null;
|
|
3790
|
+
}
|
|
3791
|
+
function detectSecurityIssue(node) {
|
|
3792
|
+
const parsed = parseCallExpression(node);
|
|
3793
|
+
if (!parsed) {
|
|
3794
|
+
return null;
|
|
3795
|
+
}
|
|
3796
|
+
for (const detector of SECURITY_DETECTORS) {
|
|
3797
|
+
const result = detector(parsed);
|
|
3798
|
+
if (result) {
|
|
3799
|
+
return result;
|
|
3389
3800
|
}
|
|
3390
3801
|
}
|
|
3391
3802
|
return null;
|
|
3392
3803
|
}
|
|
3393
|
-
var COMMAND_EXEC_METHODS, PATH_METHODS;
|
|
3804
|
+
var COMMAND_EXEC_METHODS, PATH_METHODS, UNTRUSTED_ARG_TYPES, SECURITY_DETECTORS;
|
|
3394
3805
|
var init_security_detector = __esm({
|
|
3395
3806
|
"src/lib/@detectors/security/security-detector.ts"() {
|
|
3396
3807
|
init_esm_shims();
|
|
3397
|
-
COMMAND_EXEC_METHODS = ["execSync", "exec", "spawn", "spawnSync"];
|
|
3398
|
-
PATH_METHODS = ["join", "resolve"];
|
|
3808
|
+
COMMAND_EXEC_METHODS = /* @__PURE__ */ new Set(["execSync", "exec", "spawn", "spawnSync"]);
|
|
3809
|
+
PATH_METHODS = /* @__PURE__ */ new Set(["join", "resolve"]);
|
|
3810
|
+
UNTRUSTED_ARG_TYPES = /* @__PURE__ */ new Set(["Identifier", "MemberExpression", "CallExpression"]);
|
|
3811
|
+
SECURITY_DETECTORS = [
|
|
3812
|
+
detectCommandInjectionFromCall,
|
|
3813
|
+
detectPathTraversalFromCall
|
|
3814
|
+
];
|
|
3399
3815
|
}
|
|
3400
3816
|
});
|
|
3401
3817
|
|
|
@@ -5413,12 +5829,17 @@ var init_issue_factory = __esm({
|
|
|
5413
5829
|
});
|
|
5414
5830
|
|
|
5415
5831
|
// src/lib/@detectors/patterns/context.ts
|
|
5416
|
-
function
|
|
5417
|
-
const
|
|
5418
|
-
const
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5832
|
+
function isRealTsDirective(line, matchIndex) {
|
|
5833
|
+
const beforeMatch = line.slice(0, matchIndex);
|
|
5834
|
+
const lineCommentMatch = beforeMatch.match(/\/\/\s*$/);
|
|
5835
|
+
if (lineCommentMatch) {
|
|
5836
|
+
return true;
|
|
5837
|
+
}
|
|
5838
|
+
const blockCommentMatch = beforeMatch.match(/\/\*+\s*$/);
|
|
5839
|
+
if (blockCommentMatch) {
|
|
5840
|
+
return true;
|
|
5841
|
+
}
|
|
5842
|
+
return false;
|
|
5422
5843
|
}
|
|
5423
5844
|
function createSnippet(line) {
|
|
5424
5845
|
const trimmed = line.trim();
|
|
@@ -5427,24 +5848,27 @@ function createSnippet(line) {
|
|
|
5427
5848
|
function checkTsDirectives(content, filepath) {
|
|
5428
5849
|
const issues = [];
|
|
5429
5850
|
const lines = content.split("\n");
|
|
5851
|
+
let charOffset = 0;
|
|
5430
5852
|
for (let i = 0; i < lines.length; i++) {
|
|
5431
5853
|
const line = lines[i] ?? "";
|
|
5432
|
-
const
|
|
5433
|
-
if (!trimmed) {
|
|
5434
|
-
continue;
|
|
5435
|
-
}
|
|
5854
|
+
const lineStartOffset = charOffset;
|
|
5436
5855
|
for (const { pattern, type } of DIRECTIVE_PATTERNS) {
|
|
5437
|
-
const match = pattern.exec(
|
|
5856
|
+
const match = pattern.exec(line);
|
|
5438
5857
|
if (!match) {
|
|
5439
5858
|
continue;
|
|
5440
5859
|
}
|
|
5441
|
-
|
|
5860
|
+
const absoluteOffset = lineStartOffset + match.index;
|
|
5861
|
+
if (!isInsideComment2(content, absoluteOffset)) {
|
|
5862
|
+
continue;
|
|
5863
|
+
}
|
|
5864
|
+
if (!isRealTsDirective(line, match.index)) {
|
|
5442
5865
|
continue;
|
|
5443
5866
|
}
|
|
5444
|
-
const snippet = createSnippet(
|
|
5867
|
+
const snippet = createSnippet(line);
|
|
5445
5868
|
const issue = createTsDirectiveIssue(type, i + 1, snippet, filepath);
|
|
5446
5869
|
issues.push(issue);
|
|
5447
5870
|
}
|
|
5871
|
+
charOffset = lineStartOffset + line.length + 1;
|
|
5448
5872
|
}
|
|
5449
5873
|
return issues;
|
|
5450
5874
|
}
|
|
@@ -5536,6 +5960,7 @@ var MAX_SNIPPET_LENGTH, DIRECTIVE_PATTERNS, VERB_ENDINGS, VERB_STEM_PATTERNS, VO
|
|
|
5536
5960
|
var init_context = __esm({
|
|
5537
5961
|
"src/lib/@detectors/patterns/context.ts"() {
|
|
5538
5962
|
init_esm_shims();
|
|
5963
|
+
init_swc();
|
|
5539
5964
|
init_issue_factory();
|
|
5540
5965
|
MAX_SNIPPET_LENGTH = 80;
|
|
5541
5966
|
DIRECTIVE_PATTERNS = [
|
|
@@ -16111,6 +16536,132 @@ var init_registry5 = __esm({
|
|
|
16111
16536
|
}
|
|
16112
16537
|
});
|
|
16113
16538
|
|
|
16539
|
+
// src/lib/@core/text/morphology.ts
|
|
16540
|
+
function getVowelRatio2(str) {
|
|
16541
|
+
const vowels = str.match(/[aeiou]/gi);
|
|
16542
|
+
return vowels ? vowels.length / str.length : 0;
|
|
16543
|
+
}
|
|
16544
|
+
function isAbbreviation(name) {
|
|
16545
|
+
if (name.length > 5) return false;
|
|
16546
|
+
const vowelRatio = getVowelRatio2(name);
|
|
16547
|
+
return vowelRatio < 0.2;
|
|
16548
|
+
}
|
|
16549
|
+
function estimateSyllables(word) {
|
|
16550
|
+
const lowerWord = word.toLowerCase();
|
|
16551
|
+
const vowelClusters = lowerWord.match(/[aeiouy]+/g);
|
|
16552
|
+
if (!vowelClusters) return 1;
|
|
16553
|
+
let count = vowelClusters.length;
|
|
16554
|
+
if (lowerWord.endsWith("e") && count > 1) {
|
|
16555
|
+
count--;
|
|
16556
|
+
}
|
|
16557
|
+
if (/le$/.test(lowerWord) && count > 1) {
|
|
16558
|
+
const beforeLe = lowerWord.slice(-3, -2);
|
|
16559
|
+
if (!/[aeiouy]/.test(beforeLe)) {
|
|
16560
|
+
count++;
|
|
16561
|
+
}
|
|
16562
|
+
}
|
|
16563
|
+
return Math.max(1, count);
|
|
16564
|
+
}
|
|
16565
|
+
function hasNounSuffix(word) {
|
|
16566
|
+
const lowerWord = word.toLowerCase();
|
|
16567
|
+
const nounPatterns = [
|
|
16568
|
+
/er$/,
|
|
16569
|
+
// handler, listener, helper, manager
|
|
16570
|
+
/or$/,
|
|
16571
|
+
// iterator, selector, constructor
|
|
16572
|
+
/tion$/,
|
|
16573
|
+
// function, action, collection
|
|
16574
|
+
/sion$/,
|
|
16575
|
+
// session, version
|
|
16576
|
+
/ment$/,
|
|
16577
|
+
// element, argument
|
|
16578
|
+
/ness$/,
|
|
16579
|
+
// readiness
|
|
16580
|
+
/ity$/,
|
|
16581
|
+
// utility, entity
|
|
16582
|
+
/ure$/,
|
|
16583
|
+
// structure, closure
|
|
16584
|
+
/ance$/,
|
|
16585
|
+
// instance
|
|
16586
|
+
/ence$/,
|
|
16587
|
+
// reference, sequence
|
|
16588
|
+
/ing$/,
|
|
16589
|
+
// string, thing (as nouns)
|
|
16590
|
+
/ist$/,
|
|
16591
|
+
// list (and -ist words)
|
|
16592
|
+
/ata$/,
|
|
16593
|
+
// data, metadata
|
|
16594
|
+
/xt$/,
|
|
16595
|
+
// context, text
|
|
16596
|
+
/que$/,
|
|
16597
|
+
// queue
|
|
16598
|
+
/ay$/,
|
|
16599
|
+
// array, display
|
|
16600
|
+
/ch$/,
|
|
16601
|
+
// cache, batch
|
|
16602
|
+
/ck$/,
|
|
16603
|
+
// callback, stack
|
|
16604
|
+
/se$/,
|
|
16605
|
+
// response, case
|
|
16606
|
+
/te$/,
|
|
16607
|
+
// state, template
|
|
16608
|
+
/de$/,
|
|
16609
|
+
// node, code
|
|
16610
|
+
/ue$/,
|
|
16611
|
+
// value, queue
|
|
16612
|
+
/pe$/,
|
|
16613
|
+
// type, pipe
|
|
16614
|
+
/me$/,
|
|
16615
|
+
// name, frame
|
|
16616
|
+
/ms$/,
|
|
16617
|
+
// params, items
|
|
16618
|
+
/gs$/,
|
|
16619
|
+
// args, flags
|
|
16620
|
+
/ps$/,
|
|
16621
|
+
// props
|
|
16622
|
+
/ns$/,
|
|
16623
|
+
// options, actions
|
|
16624
|
+
/ts$/,
|
|
16625
|
+
// results, events
|
|
16626
|
+
/rd$/,
|
|
16627
|
+
// record
|
|
16628
|
+
/ry$/,
|
|
16629
|
+
// factory, entry
|
|
16630
|
+
/ol$/,
|
|
16631
|
+
// control, protocol
|
|
16632
|
+
/et$/,
|
|
16633
|
+
// object, set
|
|
16634
|
+
/ap$/,
|
|
16635
|
+
// map
|
|
16636
|
+
/lt$/,
|
|
16637
|
+
// result
|
|
16638
|
+
/ig$/,
|
|
16639
|
+
// config
|
|
16640
|
+
/fo$/
|
|
16641
|
+
// info
|
|
16642
|
+
];
|
|
16643
|
+
return nounPatterns.some((pattern) => pattern.test(lowerWord));
|
|
16644
|
+
}
|
|
16645
|
+
function splitIntoSegments(name) {
|
|
16646
|
+
if (name.includes("_")) {
|
|
16647
|
+
return name.split("_").filter((s) => s.length > 0);
|
|
16648
|
+
}
|
|
16649
|
+
return name.split(/(?=[A-Z])/).filter((s) => s.length > 0);
|
|
16650
|
+
}
|
|
16651
|
+
var init_morphology = __esm({
|
|
16652
|
+
"src/lib/@core/text/morphology.ts"() {
|
|
16653
|
+
init_esm_shims();
|
|
16654
|
+
}
|
|
16655
|
+
});
|
|
16656
|
+
|
|
16657
|
+
// src/lib/@core/text/index.ts
|
|
16658
|
+
var init_text3 = __esm({
|
|
16659
|
+
"src/lib/@core/text/index.ts"() {
|
|
16660
|
+
init_esm_shims();
|
|
16661
|
+
init_morphology();
|
|
16662
|
+
}
|
|
16663
|
+
});
|
|
16664
|
+
|
|
16114
16665
|
// src/lib/@core/utils/grouping.ts
|
|
16115
16666
|
function groupBy(items, keyFn) {
|
|
16116
16667
|
const grouped = /* @__PURE__ */ new Map();
|
|
@@ -16170,6 +16721,7 @@ var init_core2 = __esm({
|
|
|
16170
16721
|
init_logger2();
|
|
16171
16722
|
init_registry5();
|
|
16172
16723
|
init_shell2();
|
|
16724
|
+
init_text3();
|
|
16173
16725
|
init_time();
|
|
16174
16726
|
init_utils2();
|
|
16175
16727
|
}
|
|
@@ -16527,7 +17079,8 @@ var init_alert = __esm({
|
|
|
16527
17079
|
init_utils3();
|
|
16528
17080
|
metadata = createFixerMetadata("alert", "Alert Statements", "lint", {
|
|
16529
17081
|
description: "Remove alert() calls",
|
|
16530
|
-
difficulty: "
|
|
17082
|
+
difficulty: "risky",
|
|
17083
|
+
// TODO: not production-ready
|
|
16531
17084
|
cliFlag: "--fix-alert",
|
|
16532
17085
|
tags: ["trivial", "safe-to-autofix", "debugging"]
|
|
16533
17086
|
});
|
|
@@ -16613,7 +17166,8 @@ var init_any_type = __esm({
|
|
|
16613
17166
|
init_utils3();
|
|
16614
17167
|
metadata2 = createFixerMetadata("any-type", "Any Type Usage", "type-safety", {
|
|
16615
17168
|
description: "Replace `any` with `unknown`",
|
|
16616
|
-
difficulty: "
|
|
17169
|
+
difficulty: "risky",
|
|
17170
|
+
// TODO: not production-ready
|
|
16617
17171
|
cliFlag: "--fix-any",
|
|
16618
17172
|
negateFlag: "--no-any",
|
|
16619
17173
|
tags: ["safe", "type-safety"]
|
|
@@ -16900,7 +17454,8 @@ var init_backwards_compat2 = __esm({
|
|
|
16900
17454
|
"backwards-compat",
|
|
16901
17455
|
{
|
|
16902
17456
|
description: "Delete deprecated shim files and update imports",
|
|
16903
|
-
difficulty: "
|
|
17457
|
+
difficulty: "risky",
|
|
17458
|
+
// TODO: not production-ready
|
|
16904
17459
|
cliFlag: "--cleanup-deprecated",
|
|
16905
17460
|
tags: ["safe", "refactoring", "cleanup"]
|
|
16906
17461
|
}
|
|
@@ -16917,7 +17472,7 @@ var init_backwards_compat2 = __esm({
|
|
|
16917
17472
|
});
|
|
16918
17473
|
|
|
16919
17474
|
// src/commands/fix/fixers/complexity/index.ts
|
|
16920
|
-
function
|
|
17475
|
+
function calculateComplexity2(lines) {
|
|
16921
17476
|
let complexity = 1;
|
|
16922
17477
|
for (const line of lines) {
|
|
16923
17478
|
for (const pattern of COMPLEXITY_PATTERNS) {
|
|
@@ -16941,7 +17496,7 @@ function findFunctionsWithComplexity(content, filePath) {
|
|
|
16941
17496
|
name: f.name,
|
|
16942
17497
|
startLine: f.startLine,
|
|
16943
17498
|
endLine: f.endLine,
|
|
16944
|
-
complexity:
|
|
17499
|
+
complexity: calculateComplexity2(funcLines),
|
|
16945
17500
|
isAsync: f.isAsync
|
|
16946
17501
|
};
|
|
16947
17502
|
});
|
|
@@ -16978,7 +17533,7 @@ function findFunctionsRegex(content) {
|
|
|
16978
17533
|
if (currentFunction && braceCount <= functionStartBrace && line.includes("}")) {
|
|
16979
17534
|
currentFunction.endLine = i + 1;
|
|
16980
17535
|
const funcLines = lines.slice(currentFunction.startLine - 1, currentFunction.endLine);
|
|
16981
|
-
currentFunction.complexity =
|
|
17536
|
+
currentFunction.complexity = calculateComplexity2(funcLines);
|
|
16982
17537
|
functions.push(currentFunction);
|
|
16983
17538
|
currentFunction = null;
|
|
16984
17539
|
}
|
|
@@ -17228,7 +17783,8 @@ var init_console = __esm({
|
|
|
17228
17783
|
init_fixer();
|
|
17229
17784
|
metadata5 = createFixerMetadata("console", "Console Statements", "lint", {
|
|
17230
17785
|
description: "Remove console.log/warn/error statements",
|
|
17231
|
-
difficulty: "
|
|
17786
|
+
difficulty: "risky",
|
|
17787
|
+
// TODO: not production-ready
|
|
17232
17788
|
cliFlag: "--fix-console",
|
|
17233
17789
|
negateFlag: "--no-console",
|
|
17234
17790
|
tags: ["trivial", "safe-to-autofix", "debugging"]
|
|
@@ -17301,7 +17857,8 @@ var init_debugger = __esm({
|
|
|
17301
17857
|
init_utils3();
|
|
17302
17858
|
metadata6 = createFixerMetadata("debugger", "Debugger Statements", "lint", {
|
|
17303
17859
|
description: "Remove debugger statements",
|
|
17304
|
-
difficulty: "
|
|
17860
|
+
difficulty: "risky",
|
|
17861
|
+
// TODO: not production-ready
|
|
17305
17862
|
cliFlag: "--fix-debugger",
|
|
17306
17863
|
negateFlag: "--no-debugger",
|
|
17307
17864
|
tags: ["trivial", "safe-to-autofix", "debugging"]
|
|
@@ -17375,7 +17932,8 @@ var init_duplicate = __esm({
|
|
|
17375
17932
|
init_registry6();
|
|
17376
17933
|
metadata7 = createFixerMetadata("duplicate", "Duplicate Functions", "lint", {
|
|
17377
17934
|
description: "Merge duplicate functions from refactor analysis",
|
|
17378
|
-
difficulty: "
|
|
17935
|
+
difficulty: "risky",
|
|
17936
|
+
// TODO: not production-ready
|
|
17379
17937
|
cliFlag: "--fix-duplicate",
|
|
17380
17938
|
tags: ["safe", "refactoring", "deduplication"]
|
|
17381
17939
|
});
|
|
@@ -17491,7 +18049,8 @@ var init_equality = __esm({
|
|
|
17491
18049
|
init_fixer2();
|
|
17492
18050
|
metadata8 = createFixerMetadata("equality", "Strict Equality", "type-safety", {
|
|
17493
18051
|
description: "Replace == with === and != with !==",
|
|
17494
|
-
difficulty: "
|
|
18052
|
+
difficulty: "risky",
|
|
18053
|
+
// TODO: not production-ready
|
|
17495
18054
|
cliFlag: "--fix-equality",
|
|
17496
18055
|
negateFlag: "--no-equality",
|
|
17497
18056
|
tags: ["safe", "type-safety", "eslint"]
|
|
@@ -17637,7 +18196,8 @@ var init_eval = __esm({
|
|
|
17637
18196
|
init_fixer3();
|
|
17638
18197
|
metadata9 = createFixerMetadata("eval", "Eval Security", "type-safety", {
|
|
17639
18198
|
description: "Detect and fix eval() security risks",
|
|
17640
|
-
difficulty: "
|
|
18199
|
+
difficulty: "risky",
|
|
18200
|
+
// TODO: not production-ready
|
|
17641
18201
|
cliFlag: "--fix-eval",
|
|
17642
18202
|
negateFlag: "--no-eval",
|
|
17643
18203
|
tags: ["security", "safe", "eval"]
|
|
@@ -17741,7 +18301,8 @@ var init_hardcoded_urls = __esm({
|
|
|
17741
18301
|
init_registry6();
|
|
17742
18302
|
metadata10 = createFixerMetadata("hardcoded-urls", "Hardcoded URLs", "hardcoded", {
|
|
17743
18303
|
description: "Extract hardcoded URLs to constants",
|
|
17744
|
-
difficulty: "
|
|
18304
|
+
difficulty: "risky",
|
|
18305
|
+
// TODO: not production-ready
|
|
17745
18306
|
cliFlag: "--fix-urls",
|
|
17746
18307
|
tags: ["safe", "hardcoded", "refactoring"]
|
|
17747
18308
|
});
|
|
@@ -20000,6 +20561,33 @@ var init_ast_transformer = __esm({
|
|
|
20000
20561
|
"src/lib/@i18n/ast-transformer.ts"() {
|
|
20001
20562
|
init_esm_shims();
|
|
20002
20563
|
init_key_resolver();
|
|
20564
|
+
/* @__PURE__ */ new Set([
|
|
20565
|
+
SyntaxKind.ImportDeclaration,
|
|
20566
|
+
SyntaxKind.ExportDeclaration,
|
|
20567
|
+
SyntaxKind.ImportSpecifier,
|
|
20568
|
+
SyntaxKind.ExportSpecifier
|
|
20569
|
+
]);
|
|
20570
|
+
/* @__PURE__ */ new Set([
|
|
20571
|
+
SyntaxKind.TypeLiteral,
|
|
20572
|
+
SyntaxKind.TypeAliasDeclaration,
|
|
20573
|
+
SyntaxKind.InterfaceDeclaration,
|
|
20574
|
+
SyntaxKind.LiteralType
|
|
20575
|
+
]);
|
|
20576
|
+
/* @__PURE__ */ new Set([
|
|
20577
|
+
SyntaxKind.BinaryExpression,
|
|
20578
|
+
SyntaxKind.ConditionalExpression
|
|
20579
|
+
]);
|
|
20580
|
+
/* @__PURE__ */ new Map([
|
|
20581
|
+
[SyntaxKind.JsxAttribute, "jsx-attribute"],
|
|
20582
|
+
[SyntaxKind.JsxExpression, "jsx-expression"],
|
|
20583
|
+
[SyntaxKind.PropertyAssignment, "object-property"],
|
|
20584
|
+
[SyntaxKind.ShorthandPropertyAssignment, "object-property"],
|
|
20585
|
+
[SyntaxKind.CallExpression, "function-argument"],
|
|
20586
|
+
[SyntaxKind.ArrayLiteralExpression, "array-element"],
|
|
20587
|
+
[SyntaxKind.VariableDeclaration, "variable"],
|
|
20588
|
+
[SyntaxKind.TemplateSpan, "template-literal"],
|
|
20589
|
+
[SyntaxKind.TemplateExpression, "template-literal"]
|
|
20590
|
+
]);
|
|
20003
20591
|
}
|
|
20004
20592
|
});
|
|
20005
20593
|
|
|
@@ -20390,7 +20978,8 @@ var init_i18n2 = __esm({
|
|
|
20390
20978
|
init_replacer();
|
|
20391
20979
|
metadata11 = createFixerMetadata("i18n", "I18n Hardcoded Strings", "i18n", {
|
|
20392
20980
|
description: "Extract hardcoded Russian text to i18n translation keys",
|
|
20393
|
-
difficulty: "
|
|
20981
|
+
difficulty: "risky",
|
|
20982
|
+
// TODO: not production-ready
|
|
20394
20983
|
cliFlag: "--fix-i18n",
|
|
20395
20984
|
negateFlag: "--no-i18n",
|
|
20396
20985
|
tags: ["i18n", "localization", "russian", "catalog-first"]
|
|
@@ -20739,7 +21328,8 @@ var init_magic_numbers = __esm({
|
|
|
20739
21328
|
init_registry6();
|
|
20740
21329
|
metadata13 = createFixerMetadata("magic-numbers", "Magic Numbers", "hardcoded", {
|
|
20741
21330
|
description: "Extract magic numbers to named constants",
|
|
20742
|
-
difficulty: "
|
|
21331
|
+
difficulty: "risky",
|
|
21332
|
+
// TODO: not production-ready
|
|
20743
21333
|
cliFlag: "--fix-magic-numbers",
|
|
20744
21334
|
tags: ["safe", "hardcoded", "refactoring"]
|
|
20745
21335
|
});
|
|
@@ -21098,7 +21688,8 @@ var init_ts_ignore = __esm({
|
|
|
21098
21688
|
init_registry6();
|
|
21099
21689
|
metadata16 = createFixerMetadata("ts-ignore", "TS-Ignore Comments", "type-safety", {
|
|
21100
21690
|
description: "Remove @ts-ignore/@ts-nocheck comments",
|
|
21101
|
-
difficulty: "
|
|
21691
|
+
difficulty: "risky",
|
|
21692
|
+
// TODO: not production-ready
|
|
21102
21693
|
cliFlag: "--fix-ts-ignore",
|
|
21103
21694
|
tags: ["safe", "type-safety"]
|
|
21104
21695
|
});
|
|
@@ -21336,7 +21927,8 @@ var init_unused_imports = __esm({
|
|
|
21336
21927
|
init_fixer5();
|
|
21337
21928
|
metadata17 = createFixerMetadata("unused-imports", "Unused Imports", "lint", {
|
|
21338
21929
|
description: "Remove unused imports",
|
|
21339
|
-
difficulty: "
|
|
21930
|
+
difficulty: "risky",
|
|
21931
|
+
// TODO: not production-ready
|
|
21340
21932
|
cliFlag: "--fix-imports",
|
|
21341
21933
|
negateFlag: "--no-imports",
|
|
21342
21934
|
tags: ["safe", "imports", "cleanup"]
|
|
@@ -26369,117 +26961,6 @@ var init_constants10 = __esm({
|
|
|
26369
26961
|
});
|
|
26370
26962
|
|
|
26371
26963
|
// src/commands/refactor/analyzers/core/duplicates/linguistic.ts
|
|
26372
|
-
function splitIntoSegments(name) {
|
|
26373
|
-
if (name.includes("_")) {
|
|
26374
|
-
return name.split("_").filter((s) => s.length > 0);
|
|
26375
|
-
}
|
|
26376
|
-
return name.split(/(?=[A-Z])/).filter((s) => s.length > 0);
|
|
26377
|
-
}
|
|
26378
|
-
function getVowelRatio2(str) {
|
|
26379
|
-
const vowels = str.match(/[aeiou]/gi);
|
|
26380
|
-
return vowels ? vowels.length / str.length : 0;
|
|
26381
|
-
}
|
|
26382
|
-
function isAbbreviation(name) {
|
|
26383
|
-
if (name.length > 5) return false;
|
|
26384
|
-
const vowelRatio = getVowelRatio2(name);
|
|
26385
|
-
return vowelRatio < 0.2;
|
|
26386
|
-
}
|
|
26387
|
-
function estimateSyllables(word) {
|
|
26388
|
-
const lowerWord = word.toLowerCase();
|
|
26389
|
-
const vowelClusters = lowerWord.match(/[aeiouy]+/g);
|
|
26390
|
-
if (!vowelClusters) return 1;
|
|
26391
|
-
let count = vowelClusters.length;
|
|
26392
|
-
if (lowerWord.endsWith("e") && count > 1) {
|
|
26393
|
-
count--;
|
|
26394
|
-
}
|
|
26395
|
-
if (/le$/.test(lowerWord) && count > 1) {
|
|
26396
|
-
const beforeLe = lowerWord.slice(-3, -2);
|
|
26397
|
-
if (!/[aeiouy]/.test(beforeLe)) {
|
|
26398
|
-
count++;
|
|
26399
|
-
}
|
|
26400
|
-
}
|
|
26401
|
-
return Math.max(1, count);
|
|
26402
|
-
}
|
|
26403
|
-
function hasNounSuffix(word) {
|
|
26404
|
-
const lowerWord = word.toLowerCase();
|
|
26405
|
-
const nounPatterns = [
|
|
26406
|
-
/er$/,
|
|
26407
|
-
// handler, listener, helper, manager
|
|
26408
|
-
/or$/,
|
|
26409
|
-
// iterator, selector, constructor
|
|
26410
|
-
/tion$/,
|
|
26411
|
-
// function, action, collection
|
|
26412
|
-
/sion$/,
|
|
26413
|
-
// session, version
|
|
26414
|
-
/ment$/,
|
|
26415
|
-
// element, argument
|
|
26416
|
-
/ness$/,
|
|
26417
|
-
// readiness
|
|
26418
|
-
/ity$/,
|
|
26419
|
-
// utility, entity
|
|
26420
|
-
/ure$/,
|
|
26421
|
-
// structure, closure
|
|
26422
|
-
/ance$/,
|
|
26423
|
-
// instance
|
|
26424
|
-
/ence$/,
|
|
26425
|
-
// reference, sequence
|
|
26426
|
-
/ing$/,
|
|
26427
|
-
// string, thing (as nouns)
|
|
26428
|
-
/ist$/,
|
|
26429
|
-
// list (and -ist words)
|
|
26430
|
-
/ata$/,
|
|
26431
|
-
// data, metadata
|
|
26432
|
-
/xt$/,
|
|
26433
|
-
// context, text
|
|
26434
|
-
/que$/,
|
|
26435
|
-
// queue
|
|
26436
|
-
/ay$/,
|
|
26437
|
-
// array, display
|
|
26438
|
-
/ch$/,
|
|
26439
|
-
// cache, batch
|
|
26440
|
-
/ck$/,
|
|
26441
|
-
// callback, stack
|
|
26442
|
-
/se$/,
|
|
26443
|
-
// response, case
|
|
26444
|
-
/te$/,
|
|
26445
|
-
// state, template
|
|
26446
|
-
/de$/,
|
|
26447
|
-
// node, code
|
|
26448
|
-
/ue$/,
|
|
26449
|
-
// value, queue
|
|
26450
|
-
/pe$/,
|
|
26451
|
-
// type, pipe
|
|
26452
|
-
/me$/,
|
|
26453
|
-
// name, frame
|
|
26454
|
-
/ms$/,
|
|
26455
|
-
// params, items
|
|
26456
|
-
/gs$/,
|
|
26457
|
-
// args, flags
|
|
26458
|
-
/ps$/,
|
|
26459
|
-
// props
|
|
26460
|
-
/ns$/,
|
|
26461
|
-
// options, actions
|
|
26462
|
-
/ts$/,
|
|
26463
|
-
// results, events
|
|
26464
|
-
/rd$/,
|
|
26465
|
-
// record
|
|
26466
|
-
/ry$/,
|
|
26467
|
-
// factory, entry
|
|
26468
|
-
/ol$/,
|
|
26469
|
-
// control, protocol
|
|
26470
|
-
/et$/,
|
|
26471
|
-
// object, set
|
|
26472
|
-
/ap$/,
|
|
26473
|
-
// map
|
|
26474
|
-
/lt$/,
|
|
26475
|
-
// result
|
|
26476
|
-
/ig$/,
|
|
26477
|
-
// config
|
|
26478
|
-
/fo$/
|
|
26479
|
-
// info
|
|
26480
|
-
];
|
|
26481
|
-
return nounPatterns.some((pattern) => pattern.test(lowerWord));
|
|
26482
|
-
}
|
|
26483
26964
|
function hasVerbPrefix(word) {
|
|
26484
26965
|
return extractVerbPrefix(word) !== null;
|
|
26485
26966
|
}
|
|
@@ -26487,6 +26968,7 @@ var init_linguistic = __esm({
|
|
|
26487
26968
|
"src/commands/refactor/analyzers/core/duplicates/linguistic.ts"() {
|
|
26488
26969
|
init_esm_shims();
|
|
26489
26970
|
init_detectors3();
|
|
26971
|
+
init_text3();
|
|
26490
26972
|
}
|
|
26491
26973
|
});
|
|
26492
26974
|
|
|
@@ -26538,6 +27020,58 @@ function isSuffixOnlyName(name) {
|
|
|
26538
27020
|
}
|
|
26539
27021
|
return false;
|
|
26540
27022
|
}
|
|
27023
|
+
function isCommonVariableName(name) {
|
|
27024
|
+
const lowerName = name.toLowerCase();
|
|
27025
|
+
const commonCollectionNames = [
|
|
27026
|
+
// File system
|
|
27027
|
+
/^(files|dirs|directories|folders|paths|sources)$/i,
|
|
27028
|
+
// Array operations
|
|
27029
|
+
/^(items|entries|elements|records|rows|lines|parts|chunks|segments|tokens)$/i,
|
|
27030
|
+
// Results
|
|
27031
|
+
/^(results|matches|hits|findings|issues|errors|warnings)$/i,
|
|
27032
|
+
// Data
|
|
27033
|
+
/^(keys|values|pairs|fields|props|attrs|args|params)$/i,
|
|
27034
|
+
// Strings
|
|
27035
|
+
/^(words|chars|names|labels|tags|ids)$/i
|
|
27036
|
+
];
|
|
27037
|
+
for (const pattern of commonCollectionNames) {
|
|
27038
|
+
if (pattern.test(lowerName)) {
|
|
27039
|
+
return true;
|
|
27040
|
+
}
|
|
27041
|
+
}
|
|
27042
|
+
const transformationPatterns = [
|
|
27043
|
+
/^(sorted|filtered|mapped|reduced|grouped|merged|joined|split|parsed|formatted)$/i,
|
|
27044
|
+
/^(processed|transformed|converted|normalized|validated|sanitized|cleaned)$/i,
|
|
27045
|
+
/^(matched|found|selected|picked|extracted|collected|gathered)$/i,
|
|
27046
|
+
/^(updated|modified|changed|fixed|patched|adjusted)$/i
|
|
27047
|
+
];
|
|
27048
|
+
for (const pattern of transformationPatterns) {
|
|
27049
|
+
if (pattern.test(lowerName)) {
|
|
27050
|
+
return true;
|
|
27051
|
+
}
|
|
27052
|
+
}
|
|
27053
|
+
const iterationPatterns = [
|
|
27054
|
+
/^(line|path|file|dir|item|entry|key|value|name|index|offset)$/i,
|
|
27055
|
+
/^(current|next|prev|first|last|head|tail)$/i,
|
|
27056
|
+
/^(left|right|start|end|begin|stop)$/i
|
|
27057
|
+
];
|
|
27058
|
+
for (const pattern of iterationPatterns) {
|
|
27059
|
+
if (pattern.test(lowerName)) {
|
|
27060
|
+
return true;
|
|
27061
|
+
}
|
|
27062
|
+
}
|
|
27063
|
+
const tempPatterns = [
|
|
27064
|
+
/^(temp|tmp|buf|buffer|acc|accumulator|memo|cache)$/i,
|
|
27065
|
+
/^(output|input|source|target|dest|destination)$/i,
|
|
27066
|
+
/^(raw|clean|final|base|root|parent|child)$/i
|
|
27067
|
+
];
|
|
27068
|
+
for (const pattern of tempPatterns) {
|
|
27069
|
+
if (pattern.test(lowerName)) {
|
|
27070
|
+
return true;
|
|
27071
|
+
}
|
|
27072
|
+
}
|
|
27073
|
+
return false;
|
|
27074
|
+
}
|
|
26541
27075
|
function isPlaceholderName(name) {
|
|
26542
27076
|
const lowerName = name.toLowerCase();
|
|
26543
27077
|
if (/^(foo|bar|baz|qux|quux|corge|grault|garply|waldo|fred|plugh|xyzzy|thud)$/i.test(lowerName)) {
|
|
@@ -26662,6 +27196,7 @@ function isMeaningfulFunctionName(name) {
|
|
|
26662
27196
|
if (isGenericFunctionName(name)) return false;
|
|
26663
27197
|
if (name.length < 4) return false;
|
|
26664
27198
|
if (isCommonCallbackPattern(name)) return false;
|
|
27199
|
+
if (isCommonVariableName(name)) return false;
|
|
26665
27200
|
const namedPattern = detectNamingPattern(name);
|
|
26666
27201
|
if (namedPattern) {
|
|
26667
27202
|
return true;
|
|
@@ -26700,13 +27235,16 @@ var init_name_detection = __esm({
|
|
|
26700
27235
|
function extractFunctionsSwc(filePath, content) {
|
|
26701
27236
|
const functions = [];
|
|
26702
27237
|
try {
|
|
26703
|
-
const ast =
|
|
26704
|
-
syntax: "typescript",
|
|
26705
|
-
tsx: filePath.endsWith(".tsx")
|
|
26706
|
-
});
|
|
26707
|
-
const lineOffsets = calculateLineOffsets2(content);
|
|
27238
|
+
const { ast, lineOffsets, baseOffset } = parseFile(filePath, content);
|
|
26708
27239
|
visitNode2(ast, (node, context) => {
|
|
26709
|
-
const funcInfo = extractFunctionInfo(
|
|
27240
|
+
const funcInfo = extractFunctionInfo(
|
|
27241
|
+
node,
|
|
27242
|
+
filePath,
|
|
27243
|
+
content,
|
|
27244
|
+
lineOffsets,
|
|
27245
|
+
context,
|
|
27246
|
+
baseOffset
|
|
27247
|
+
);
|
|
26710
27248
|
if (funcInfo) {
|
|
26711
27249
|
functions.push(funcInfo);
|
|
26712
27250
|
}
|
|
@@ -26718,13 +27256,9 @@ function extractFunctionsSwc(filePath, content) {
|
|
|
26718
27256
|
function extractTypesSwc(filePath, content) {
|
|
26719
27257
|
const types = [];
|
|
26720
27258
|
try {
|
|
26721
|
-
const ast =
|
|
26722
|
-
syntax: "typescript",
|
|
26723
|
-
tsx: filePath.endsWith(".tsx")
|
|
26724
|
-
});
|
|
26725
|
-
const lineOffsets = calculateLineOffsets2(content);
|
|
27259
|
+
const { ast, lineOffsets, baseOffset } = parseFile(filePath, content);
|
|
26726
27260
|
visitNode2(ast, (node, context) => {
|
|
26727
|
-
const typeInfo = extractTypeInfo(node, filePath, content, lineOffsets, context);
|
|
27261
|
+
const typeInfo = extractTypeInfo(node, filePath, content, lineOffsets, context, baseOffset);
|
|
26728
27262
|
if (typeInfo) {
|
|
26729
27263
|
types.push(typeInfo);
|
|
26730
27264
|
}
|
|
@@ -26733,23 +27267,37 @@ function extractTypesSwc(filePath, content) {
|
|
|
26733
27267
|
}
|
|
26734
27268
|
return types;
|
|
26735
27269
|
}
|
|
26736
|
-
function extractTypeInfo(node, filePath, content, lineOffsets, context) {
|
|
27270
|
+
function extractTypeInfo(node, filePath, content, lineOffsets, context, baseOffset) {
|
|
26737
27271
|
const nodeType = node.type;
|
|
26738
27272
|
if (nodeType === "TsInterfaceDeclaration") {
|
|
26739
|
-
return extractInterfaceInfo(
|
|
27273
|
+
return extractInterfaceInfo(
|
|
27274
|
+
node,
|
|
27275
|
+
filePath,
|
|
27276
|
+
content,
|
|
27277
|
+
lineOffsets,
|
|
27278
|
+
context.isExported,
|
|
27279
|
+
baseOffset
|
|
27280
|
+
);
|
|
26740
27281
|
}
|
|
26741
27282
|
if (nodeType === "TsTypeAliasDeclaration") {
|
|
26742
|
-
return extractTypeAliasInfo(
|
|
27283
|
+
return extractTypeAliasInfo(
|
|
27284
|
+
node,
|
|
27285
|
+
filePath,
|
|
27286
|
+
content,
|
|
27287
|
+
lineOffsets,
|
|
27288
|
+
context.isExported,
|
|
27289
|
+
baseOffset
|
|
27290
|
+
);
|
|
26743
27291
|
}
|
|
26744
27292
|
return null;
|
|
26745
27293
|
}
|
|
26746
|
-
function extractInterfaceInfo(node, filePath, content, lineOffsets, isExported) {
|
|
27294
|
+
function extractInterfaceInfo(node, filePath, content, lineOffsets, isExported, baseOffset) {
|
|
26747
27295
|
const iface = node;
|
|
26748
27296
|
const name = iface.id?.value;
|
|
26749
27297
|
if (!name) return null;
|
|
26750
27298
|
const span = iface.span;
|
|
26751
|
-
const start = span?.start ??
|
|
26752
|
-
const end = span?.end ?? content.length;
|
|
27299
|
+
const start = Math.max(0, (span?.start ?? 1) - baseOffset - 1);
|
|
27300
|
+
const end = Math.min(content.length, (span?.end ?? content.length + 1) - baseOffset - 1);
|
|
26753
27301
|
const position = offsetToPosition2(start, lineOffsets);
|
|
26754
27302
|
const fields = [];
|
|
26755
27303
|
const fieldDefs = [];
|
|
@@ -26758,7 +27306,7 @@ function extractInterfaceInfo(node, filePath, content, lineOffsets, isExported)
|
|
|
26758
27306
|
const propName = member.key.value;
|
|
26759
27307
|
if (propName) {
|
|
26760
27308
|
fields.push(propName);
|
|
26761
|
-
const typeText = extractTypeText(member.typeAnnotation, content);
|
|
27309
|
+
const typeText = extractTypeText(member.typeAnnotation, content, baseOffset);
|
|
26762
27310
|
fieldDefs.push(`${propName}:${normalizeTypeText(typeText)}`);
|
|
26763
27311
|
}
|
|
26764
27312
|
} else if (member.type === "TsMethodSignature" && member.key) {
|
|
@@ -26783,15 +27331,15 @@ function extractInterfaceInfo(node, filePath, content, lineOffsets, isExported)
|
|
|
26783
27331
|
definition
|
|
26784
27332
|
};
|
|
26785
27333
|
}
|
|
26786
|
-
function extractTypeAliasInfo(node, filePath, content, lineOffsets, isExported) {
|
|
27334
|
+
function extractTypeAliasInfo(node, filePath, content, lineOffsets, isExported, baseOffset) {
|
|
26787
27335
|
const typeAlias = node;
|
|
26788
27336
|
const name = typeAlias.id?.value;
|
|
26789
27337
|
if (!name) return null;
|
|
26790
27338
|
const span = typeAlias.span;
|
|
26791
|
-
const start = span?.start ??
|
|
26792
|
-
const end = span?.end ?? content.length;
|
|
27339
|
+
const start = Math.max(0, (span?.start ?? 1) - baseOffset - 1);
|
|
27340
|
+
const end = Math.min(content.length, (span?.end ?? content.length + 1) - baseOffset - 1);
|
|
26793
27341
|
const position = offsetToPosition2(start, lineOffsets);
|
|
26794
|
-
const typeText = extractTypeText(typeAlias.typeAnnotation, content);
|
|
27342
|
+
const typeText = extractTypeText(typeAlias.typeAnnotation, content, baseOffset);
|
|
26795
27343
|
const normalizedStructure = normalizeTypeText(typeText);
|
|
26796
27344
|
const definition = content.slice(start, Math.min(end, start + 500));
|
|
26797
27345
|
return {
|
|
@@ -26805,24 +27353,17 @@ function extractTypeAliasInfo(node, filePath, content, lineOffsets, isExported)
|
|
|
26805
27353
|
definition
|
|
26806
27354
|
};
|
|
26807
27355
|
}
|
|
26808
|
-
function extractTypeText(typeAnnotation, content) {
|
|
27356
|
+
function extractTypeText(typeAnnotation, content, baseOffset) {
|
|
26809
27357
|
if (!typeAnnotation || typeof typeAnnotation !== "object") return "unknown";
|
|
26810
27358
|
const span = typeAnnotation.span;
|
|
26811
27359
|
if (!span) return "unknown";
|
|
26812
|
-
|
|
27360
|
+
const start = Math.max(0, span.start - baseOffset - 1);
|
|
27361
|
+
const end = Math.min(content.length, span.end - baseOffset - 1);
|
|
27362
|
+
return content.slice(start, end);
|
|
26813
27363
|
}
|
|
26814
27364
|
function normalizeTypeText(typeText) {
|
|
26815
27365
|
return typeText.replace(/import\([^)]+\)\./g, "").replace(/\s+/g, "").split(/[|&]/).map((t) => t.trim()).sort().join("|");
|
|
26816
27366
|
}
|
|
26817
|
-
function calculateLineOffsets2(content) {
|
|
26818
|
-
const offsets = [0];
|
|
26819
|
-
for (let i = 0; i < content.length; i++) {
|
|
26820
|
-
if (content[i] === "\n") {
|
|
26821
|
-
offsets.push(i + 1);
|
|
26822
|
-
}
|
|
26823
|
-
}
|
|
26824
|
-
return offsets;
|
|
26825
|
-
}
|
|
26826
27367
|
function offsetToPosition2(offset, lineOffsets) {
|
|
26827
27368
|
let line = 0;
|
|
26828
27369
|
for (let i = 0; i < lineOffsets.length; i++) {
|
|
@@ -26849,7 +27390,7 @@ function visitNode2(node, callback, context = { isExported: false }) {
|
|
|
26849
27390
|
currentContext.variableName = decl.id.value;
|
|
26850
27391
|
}
|
|
26851
27392
|
}
|
|
26852
|
-
if (nodeType === "ObjectExpression" || nodeType === "ArrayExpression") {
|
|
27393
|
+
if (nodeType === "ObjectExpression" || nodeType === "ArrayExpression" || nodeType === "CallExpression") {
|
|
26853
27394
|
currentContext.variableName = void 0;
|
|
26854
27395
|
}
|
|
26855
27396
|
callback(node, currentContext);
|
|
@@ -26866,17 +27407,33 @@ function visitNode2(node, callback, context = { isExported: false }) {
|
|
|
26866
27407
|
}
|
|
26867
27408
|
}
|
|
26868
27409
|
}
|
|
26869
|
-
function extractFunctionInfo(node, filePath, content, lineOffsets, context) {
|
|
27410
|
+
function extractFunctionInfo(node, filePath, content, lineOffsets, context, baseOffset) {
|
|
26870
27411
|
const nodeType = node.type;
|
|
26871
27412
|
if (nodeType === "FunctionDeclaration") {
|
|
26872
27413
|
const func = node;
|
|
26873
27414
|
const name = func.identifier?.value ?? "anonymous";
|
|
26874
|
-
return createFunctionInfo(
|
|
27415
|
+
return createFunctionInfo(
|
|
27416
|
+
func,
|
|
27417
|
+
name,
|
|
27418
|
+
filePath,
|
|
27419
|
+
content,
|
|
27420
|
+
lineOffsets,
|
|
27421
|
+
context.isExported,
|
|
27422
|
+
baseOffset
|
|
27423
|
+
);
|
|
26875
27424
|
}
|
|
26876
27425
|
if (nodeType === "FunctionExpression") {
|
|
26877
27426
|
const func = node;
|
|
26878
27427
|
const name = func.identifier?.value ?? "anonymous";
|
|
26879
|
-
return createFunctionInfo(
|
|
27428
|
+
return createFunctionInfo(
|
|
27429
|
+
func,
|
|
27430
|
+
name,
|
|
27431
|
+
filePath,
|
|
27432
|
+
content,
|
|
27433
|
+
lineOffsets,
|
|
27434
|
+
context.isExported,
|
|
27435
|
+
baseOffset
|
|
27436
|
+
);
|
|
26880
27437
|
}
|
|
26881
27438
|
if (nodeType === "ArrowFunctionExpression") {
|
|
26882
27439
|
const func = node;
|
|
@@ -26884,14 +27441,22 @@ function extractFunctionInfo(node, filePath, content, lineOffsets, context) {
|
|
|
26884
27441
|
if (!name) {
|
|
26885
27442
|
return null;
|
|
26886
27443
|
}
|
|
26887
|
-
return createFunctionInfo(
|
|
27444
|
+
return createFunctionInfo(
|
|
27445
|
+
func,
|
|
27446
|
+
name,
|
|
27447
|
+
filePath,
|
|
27448
|
+
content,
|
|
27449
|
+
lineOffsets,
|
|
27450
|
+
context.isExported,
|
|
27451
|
+
baseOffset
|
|
27452
|
+
);
|
|
26888
27453
|
}
|
|
26889
27454
|
return null;
|
|
26890
27455
|
}
|
|
26891
|
-
function createFunctionInfo(func, name, filePath, content, lineOffsets, isExported) {
|
|
27456
|
+
function createFunctionInfo(func, name, filePath, content, lineOffsets, isExported, baseOffset) {
|
|
26892
27457
|
const span = func.span;
|
|
26893
|
-
const start = span?.start ??
|
|
26894
|
-
const end = span?.end ?? content.length;
|
|
27458
|
+
const start = Math.max(0, (span?.start ?? 1) - baseOffset - 1);
|
|
27459
|
+
const end = Math.min(content.length, (span?.end ?? content.length + 1) - baseOffset - 1);
|
|
26895
27460
|
const position = offsetToPosition2(start, lineOffsets);
|
|
26896
27461
|
const bodyContent = content.slice(start, end);
|
|
26897
27462
|
const bodyHash = crypto.createHash("md5").update(bodyContent).digest("hex");
|
|
@@ -26912,6 +27477,7 @@ function createFunctionInfo(func, name, filePath, content, lineOffsets, isExport
|
|
|
26912
27477
|
var init_swc_parser = __esm({
|
|
26913
27478
|
"src/commands/refactor/analyzers/core/swc-parser.ts"() {
|
|
26914
27479
|
init_esm_shims();
|
|
27480
|
+
init_parser();
|
|
26915
27481
|
}
|
|
26916
27482
|
});
|
|
26917
27483
|
|
|
@@ -27006,6 +27572,7 @@ function parseFileWithSwc(file, projectRoot, verbose) {
|
|
|
27006
27572
|
const bodyText = content.slice(swcFunc.bodyStart, swcFunc.bodyEnd);
|
|
27007
27573
|
const normalizedBodyText = normalizeBody(bodyText);
|
|
27008
27574
|
const tokens = new Set(normalizedBodyText.split(/\s+/).filter((t) => t.length > 0));
|
|
27575
|
+
const fpResult = generateFingerprint(bodyText);
|
|
27009
27576
|
functions.push({
|
|
27010
27577
|
name: swcFunc.name,
|
|
27011
27578
|
file: relPath,
|
|
@@ -27016,7 +27583,10 @@ function parseFileWithSwc(file, projectRoot, verbose) {
|
|
|
27016
27583
|
exported: swcFunc.isExported,
|
|
27017
27584
|
bodyHash: swcFunc.bodyHash,
|
|
27018
27585
|
normalizedBody: normalizedBodyText,
|
|
27019
|
-
tokens
|
|
27586
|
+
tokens,
|
|
27587
|
+
// Fingerprint catches renamed clones (same structure, different identifiers)
|
|
27588
|
+
...fpResult.fingerprint && { fingerprint: fpResult.fingerprint },
|
|
27589
|
+
...fpResult.complexity > 0 && { complexity: fpResult.complexity }
|
|
27020
27590
|
});
|
|
27021
27591
|
}
|
|
27022
27592
|
} catch (error) {
|
|
@@ -27198,6 +27768,7 @@ async function findDuplicates(targetPath, projectRoot, options = {}) {
|
|
|
27198
27768
|
existing.push(func);
|
|
27199
27769
|
byHash.set(func.bodyHash, existing);
|
|
27200
27770
|
}
|
|
27771
|
+
const reportedLocations = /* @__PURE__ */ new Set();
|
|
27201
27772
|
for (const [, funcs] of byHash) {
|
|
27202
27773
|
if (funcs.length < 2) continue;
|
|
27203
27774
|
const { locations, uniqueFileCount } = deduplicateLocations(funcs);
|
|
@@ -27212,6 +27783,9 @@ async function findDuplicates(targetPath, projectRoot, options = {}) {
|
|
|
27212
27783
|
if (!aExported && bExported) return 1;
|
|
27213
27784
|
return a.localeCompare(b);
|
|
27214
27785
|
});
|
|
27786
|
+
for (const loc of locations) {
|
|
27787
|
+
reportedLocations.add(`${loc.file}:${loc.line}`);
|
|
27788
|
+
}
|
|
27215
27789
|
duplicates.push({
|
|
27216
27790
|
name: `[identical body] ${sortedNames.join(" / ")}`,
|
|
27217
27791
|
locations,
|
|
@@ -27219,6 +27793,38 @@ async function findDuplicates(targetPath, projectRoot, options = {}) {
|
|
|
27219
27793
|
recommendation: "merge"
|
|
27220
27794
|
});
|
|
27221
27795
|
}
|
|
27796
|
+
const byFingerprint = /* @__PURE__ */ new Map();
|
|
27797
|
+
for (const func of allFunctions) {
|
|
27798
|
+
if (!isLargeEnoughForDuplication(func)) continue;
|
|
27799
|
+
if (!func.fingerprint) continue;
|
|
27800
|
+
const locKey = `${func.file}:${func.line}`;
|
|
27801
|
+
if (reportedLocations.has(locKey)) continue;
|
|
27802
|
+
const existing = byFingerprint.get(func.fingerprint) ?? [];
|
|
27803
|
+
existing.push(func);
|
|
27804
|
+
byFingerprint.set(func.fingerprint, existing);
|
|
27805
|
+
}
|
|
27806
|
+
for (const [, funcs] of byFingerprint) {
|
|
27807
|
+
if (funcs.length < 2) continue;
|
|
27808
|
+
const { locations, uniqueFileCount } = deduplicateLocations(funcs);
|
|
27809
|
+
if (uniqueFileCount < 2) continue;
|
|
27810
|
+
if (locations.length < 2) continue;
|
|
27811
|
+
const uniqueNames = new Set(funcs.map((f) => f.name));
|
|
27812
|
+
if (uniqueNames.size < 2) continue;
|
|
27813
|
+
const sortedNames = [...uniqueNames].sort((a, b) => {
|
|
27814
|
+
const aExported = funcs.some((f) => f.name === a && f.exported);
|
|
27815
|
+
const bExported = funcs.some((f) => f.name === b && f.exported);
|
|
27816
|
+
if (aExported && !bExported) return -1;
|
|
27817
|
+
if (!aExported && bExported) return 1;
|
|
27818
|
+
return a.localeCompare(b);
|
|
27819
|
+
});
|
|
27820
|
+
duplicates.push({
|
|
27821
|
+
name: `[structural clone] ${sortedNames.join(" / ")}`,
|
|
27822
|
+
locations,
|
|
27823
|
+
similarity: 0.95,
|
|
27824
|
+
// Slightly less than identical body to differentiate
|
|
27825
|
+
recommendation: "merge"
|
|
27826
|
+
});
|
|
27827
|
+
}
|
|
27222
27828
|
return duplicates;
|
|
27223
27829
|
}
|
|
27224
27830
|
async function quickScanDuplicates(targetPath) {
|
|
@@ -34648,7 +35254,7 @@ function formatMarkdown4(context) {
|
|
|
34648
35254
|
return lines.join("\n");
|
|
34649
35255
|
}
|
|
34650
35256
|
var MAX_BODY_LENGTH;
|
|
34651
|
-
var
|
|
35257
|
+
var init_text4 = __esm({
|
|
34652
35258
|
"src/commands/context/formatters/text.ts"() {
|
|
34653
35259
|
init_esm_shims();
|
|
34654
35260
|
init_format();
|
|
@@ -34661,7 +35267,7 @@ var init_formatters2 = __esm({
|
|
|
34661
35267
|
"src/commands/context/formatters/index.ts"() {
|
|
34662
35268
|
init_esm_shims();
|
|
34663
35269
|
init_ai2();
|
|
34664
|
-
|
|
35270
|
+
init_text4();
|
|
34665
35271
|
}
|
|
34666
35272
|
});
|
|
34667
35273
|
|
|
@@ -39672,7 +40278,7 @@ function parseBiomeOutput(output) {
|
|
|
39672
40278
|
function getBiomeVersion(projectRoot) {
|
|
39673
40279
|
try {
|
|
39674
40280
|
const biome = getBiomePath(projectRoot);
|
|
39675
|
-
const result =
|
|
40281
|
+
const result = execFileSync(biome, ["--version"], {
|
|
39676
40282
|
cwd: projectRoot,
|
|
39677
40283
|
encoding: "utf8",
|
|
39678
40284
|
stdio: "pipe"
|
|
@@ -46515,11 +47121,11 @@ var init_duplicates_section = __esm({
|
|
|
46515
47121
|
const sortedFunctions = [...data.functions].sort((a, b) => b.similarity - a.similarity);
|
|
46516
47122
|
lines.push(" <!-- FUNCTION DUPLICATES - Consider merging or extracting -->");
|
|
46517
47123
|
lines.push(` <function-duplicates count="${data.functions.length}">`);
|
|
46518
|
-
for (const dup of sortedFunctions.slice(0,
|
|
47124
|
+
for (const dup of sortedFunctions.slice(0, 100)) {
|
|
46519
47125
|
formatFunctionDuplicate(lines, dup, " ");
|
|
46520
47126
|
}
|
|
46521
|
-
if (sortedFunctions.length >
|
|
46522
|
-
lines.push(` <!-- +${sortedFunctions.length -
|
|
47127
|
+
if (sortedFunctions.length > 100) {
|
|
47128
|
+
lines.push(` <!-- +${sortedFunctions.length - 100} more function duplicates -->`);
|
|
46523
47129
|
}
|
|
46524
47130
|
lines.push(" </function-duplicates>");
|
|
46525
47131
|
}
|
|
@@ -46527,11 +47133,11 @@ var init_duplicates_section = __esm({
|
|
|
46527
47133
|
const sortedTypes = [...data.types].sort((a, b) => b.similarity - a.similarity);
|
|
46528
47134
|
lines.push(" <!-- TYPE DUPLICATES - Consider consolidating -->");
|
|
46529
47135
|
lines.push(` <type-duplicates count="${data.types.length}">`);
|
|
46530
|
-
for (const dup of sortedTypes.slice(0,
|
|
47136
|
+
for (const dup of sortedTypes.slice(0, 100)) {
|
|
46531
47137
|
formatTypeDuplicate(lines, dup, " ");
|
|
46532
47138
|
}
|
|
46533
|
-
if (sortedTypes.length >
|
|
46534
|
-
lines.push(` <!-- +${sortedTypes.length -
|
|
47139
|
+
if (sortedTypes.length > 100) {
|
|
47140
|
+
lines.push(` <!-- +${sortedTypes.length - 100} more type duplicates -->`);
|
|
46535
47141
|
}
|
|
46536
47142
|
lines.push(" </type-duplicates>");
|
|
46537
47143
|
}
|
|
@@ -47730,7 +48336,7 @@ function formatMigrationPreview(plan) {
|
|
|
47730
48336
|
lines.push("");
|
|
47731
48337
|
return lines.join("\n");
|
|
47732
48338
|
}
|
|
47733
|
-
var
|
|
48339
|
+
var init_text5 = __esm({
|
|
47734
48340
|
"src/commands/refactor/output/text.ts"() {
|
|
47735
48341
|
init_esm_shims();
|
|
47736
48342
|
}
|
|
@@ -47814,7 +48420,7 @@ function formatRefactor(analysis, format2 = "text") {
|
|
|
47814
48420
|
return formatRefactorJson2(analysis);
|
|
47815
48421
|
}
|
|
47816
48422
|
default: {
|
|
47817
|
-
const { formatRefactorText: formatRefactorText2 } = (
|
|
48423
|
+
const { formatRefactorText: formatRefactorText2 } = (init_text5(), __toCommonJS(text_exports));
|
|
47818
48424
|
return formatRefactorText2(analysis);
|
|
47819
48425
|
}
|
|
47820
48426
|
}
|
|
@@ -47824,7 +48430,7 @@ var init_output7 = __esm({
|
|
|
47824
48430
|
init_esm_shims();
|
|
47825
48431
|
init_ai_native();
|
|
47826
48432
|
init_json2();
|
|
47827
|
-
|
|
48433
|
+
init_text5();
|
|
47828
48434
|
init_xml3();
|
|
47829
48435
|
}
|
|
47830
48436
|
});
|
|
@@ -50026,86 +50632,117 @@ __export(setup_exports, {
|
|
|
50026
50632
|
printDiagnostics: () => printDiagnostics,
|
|
50027
50633
|
runSetup: () => runSetup
|
|
50028
50634
|
});
|
|
50029
|
-
|
|
50030
|
-
|
|
50031
|
-
|
|
50032
|
-
|
|
50033
|
-
|
|
50034
|
-
|
|
50035
|
-
|
|
50036
|
-
}
|
|
50037
|
-
|
|
50038
|
-
|
|
50039
|
-
|
|
50040
|
-
|
|
50041
|
-
|
|
50042
|
-
|
|
50043
|
-
|
|
50044
|
-
|
|
50045
|
-
|
|
50046
|
-
|
|
50047
|
-
|
|
50048
|
-
|
|
50049
|
-
|
|
50635
|
+
function handleCheck(ctx) {
|
|
50636
|
+
if (!ctx.options.check) return false;
|
|
50637
|
+
printDiagnostics();
|
|
50638
|
+
return true;
|
|
50639
|
+
}
|
|
50640
|
+
async function handleUpdate2(ctx) {
|
|
50641
|
+
if (!ctx.options.update) return false;
|
|
50642
|
+
await runUpdate({ dryRun: ctx.options.dryRun ?? false, logger: ctx.logger });
|
|
50643
|
+
return true;
|
|
50644
|
+
}
|
|
50645
|
+
async function handleI18n(ctx) {
|
|
50646
|
+
if (!ctx.options.i18n) return false;
|
|
50647
|
+
logHeader(ctx.logger, ctx.options.dryRun);
|
|
50648
|
+
await installI18nextCli(ctx.projectRoot, {
|
|
50649
|
+
dryRun: ctx.options.dryRun ?? false,
|
|
50650
|
+
force: ctx.options.force ?? false,
|
|
50651
|
+
logger: ctx.logger
|
|
50652
|
+
});
|
|
50653
|
+
return true;
|
|
50654
|
+
}
|
|
50655
|
+
async function handleMcp(ctx) {
|
|
50656
|
+
if (!ctx.options.mcp) return false;
|
|
50657
|
+
logHeader(ctx.logger, ctx.options.dryRun);
|
|
50658
|
+
if (typeof ctx.options.mcp === "string") {
|
|
50659
|
+
await installMcpServer(ctx.options.mcp, {
|
|
50660
|
+
dryRun: ctx.options.dryRun ?? false,
|
|
50661
|
+
logger: ctx.logger
|
|
50662
|
+
});
|
|
50663
|
+
} else {
|
|
50664
|
+
await installAllMcpServers({
|
|
50665
|
+
dryRun: ctx.options.dryRun ?? false,
|
|
50666
|
+
logger: ctx.logger
|
|
50050
50667
|
});
|
|
50051
|
-
return;
|
|
50052
|
-
}
|
|
50053
|
-
if (mcp) {
|
|
50054
|
-
logger2.info("\u{1F430} Krolik Setup\n");
|
|
50055
|
-
if (dryRun) {
|
|
50056
|
-
logger2.info(" [DRY RUN] No changes will be made\n");
|
|
50057
|
-
}
|
|
50058
|
-
if (typeof mcp === "string") {
|
|
50059
|
-
await installMcpServer(mcp, { dryRun: dryRun ?? false, logger: logger2 });
|
|
50060
|
-
} else {
|
|
50061
|
-
await installAllMcpServers({ dryRun: dryRun ?? false, logger: logger2 });
|
|
50062
|
-
}
|
|
50063
|
-
return;
|
|
50064
50668
|
}
|
|
50669
|
+
return true;
|
|
50670
|
+
}
|
|
50671
|
+
function determineInstallTargets(options) {
|
|
50672
|
+
const { all, plugins, agents, mem } = options;
|
|
50065
50673
|
const noSelection = !all && !plugins && !agents && !mem;
|
|
50066
|
-
|
|
50067
|
-
|
|
50068
|
-
|
|
50674
|
+
return {
|
|
50675
|
+
shouldInstallPlugins: all || plugins || mem || noSelection,
|
|
50676
|
+
shouldInstallAgents: all || agents || noSelection,
|
|
50677
|
+
shouldInstallMcp: all ?? false
|
|
50678
|
+
};
|
|
50679
|
+
}
|
|
50680
|
+
function logHeader(logger2, dryRun) {
|
|
50069
50681
|
logger2.info("\u{1F430} Krolik Setup\n");
|
|
50070
50682
|
if (dryRun) {
|
|
50071
50683
|
logger2.info(" [DRY RUN] No changes will be made\n");
|
|
50072
50684
|
}
|
|
50073
|
-
|
|
50074
|
-
|
|
50075
|
-
|
|
50076
|
-
|
|
50077
|
-
|
|
50078
|
-
|
|
50079
|
-
|
|
50080
|
-
|
|
50081
|
-
|
|
50082
|
-
|
|
50083
|
-
|
|
50084
|
-
}
|
|
50085
|
-
}
|
|
50086
|
-
if (shouldInstallAgents) {
|
|
50087
|
-
logger2.info("\n\u{1F916} Installing AI agents...\n");
|
|
50088
|
-
await installAgentsRepo({
|
|
50089
|
-
dryRun: dryRun ?? false,
|
|
50090
|
-
force: force ?? false,
|
|
50091
|
-
logger: logger2
|
|
50685
|
+
}
|
|
50686
|
+
async function installPlugins(ctx, targets) {
|
|
50687
|
+
if (!targets.shouldInstallPlugins) return;
|
|
50688
|
+
const { all, mem } = ctx.options;
|
|
50689
|
+
const noSelection = !all && !ctx.options.plugins && !ctx.options.agents && !mem;
|
|
50690
|
+
ctx.logger.info("\u{1F4E6} Installing Claude Code plugins...\n");
|
|
50691
|
+
if (mem || all || noSelection) {
|
|
50692
|
+
await installMcpPlugin("claude-mem", {
|
|
50693
|
+
dryRun: ctx.options.dryRun ?? false,
|
|
50694
|
+
force: ctx.options.force ?? false,
|
|
50695
|
+
logger: ctx.logger
|
|
50092
50696
|
});
|
|
50093
50697
|
}
|
|
50094
|
-
|
|
50095
|
-
|
|
50096
|
-
|
|
50097
|
-
}
|
|
50698
|
+
}
|
|
50699
|
+
async function installAgents(ctx, targets) {
|
|
50700
|
+
if (!targets.shouldInstallAgents) return;
|
|
50701
|
+
ctx.logger.info("\n\u{1F916} Installing AI agents...\n");
|
|
50702
|
+
await installAgentsRepo({
|
|
50703
|
+
dryRun: ctx.options.dryRun ?? false,
|
|
50704
|
+
force: ctx.options.force ?? false,
|
|
50705
|
+
logger: ctx.logger
|
|
50706
|
+
});
|
|
50707
|
+
}
|
|
50708
|
+
async function installMcpServers(ctx, targets) {
|
|
50709
|
+
if (!targets.shouldInstallMcp) return;
|
|
50710
|
+
ctx.logger.info("\n");
|
|
50711
|
+
await installAllMcpServers({
|
|
50712
|
+
dryRun: ctx.options.dryRun ?? false,
|
|
50713
|
+
logger: ctx.logger
|
|
50714
|
+
});
|
|
50715
|
+
}
|
|
50716
|
+
function printSuccessMessage(logger2, targets) {
|
|
50098
50717
|
logger2.info("\n\u2705 Setup complete!");
|
|
50099
50718
|
logger2.info("\n\u{1F4DD} Next steps:");
|
|
50100
50719
|
logger2.info(" 1. Restart Claude Code to activate plugins");
|
|
50101
|
-
if (shouldInstallPlugins) {
|
|
50720
|
+
if (targets.shouldInstallPlugins) {
|
|
50102
50721
|
logger2.info(" 2. claude-mem Web UI: http://localhost:37777");
|
|
50103
50722
|
}
|
|
50104
|
-
if (shouldInstallAgents) {
|
|
50723
|
+
if (targets.shouldInstallAgents) {
|
|
50105
50724
|
logger2.info(" 3. Run: krolik agent --list");
|
|
50106
50725
|
}
|
|
50107
50726
|
logger2.info("\n\u{1F4A1} Run: krolik setup --check to see full status");
|
|
50108
50727
|
}
|
|
50728
|
+
async function runSetup(ctx) {
|
|
50729
|
+
const { logger: logger2, options, config } = ctx;
|
|
50730
|
+
const projectRoot = config.projectRoot ?? process.cwd();
|
|
50731
|
+
const setupCtx = { logger: logger2, projectRoot, options };
|
|
50732
|
+
if (handleCheck(setupCtx)) return;
|
|
50733
|
+
if (await handleUpdate2(setupCtx)) return;
|
|
50734
|
+
if (await handleI18n(setupCtx)) return;
|
|
50735
|
+
if (await handleMcp(setupCtx)) return;
|
|
50736
|
+
const targets = determineInstallTargets(options);
|
|
50737
|
+
logHeader(logger2, options.dryRun);
|
|
50738
|
+
if (!options.dryRun) {
|
|
50739
|
+
ensureDirectories();
|
|
50740
|
+
}
|
|
50741
|
+
await installPlugins(setupCtx, targets);
|
|
50742
|
+
await installAgents(setupCtx, targets);
|
|
50743
|
+
await installMcpServers(setupCtx, targets);
|
|
50744
|
+
printSuccessMessage(logger2, targets);
|
|
50745
|
+
}
|
|
50109
50746
|
async function runUpdate(opts) {
|
|
50110
50747
|
const { dryRun, logger: logger2 } = opts;
|
|
50111
50748
|
logger2.info("\u{1F504} Updating installed components...\n");
|