technical-debt-radar 1.0.4 → 1.0.5
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.js +122 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -16578,6 +16578,31 @@ var require_reliability_detector = __commonJS({
|
|
|
16578
16578
|
});
|
|
16579
16579
|
}
|
|
16580
16580
|
var LOGGING_PATTERNS = /\b(logger|console\.(?:error|warn)|report|sentry|bugsnag|datadog|newrelic|winston|pino|bunyan)\b/i;
|
|
16581
|
+
var SAFE_FALLBACK_PATTERN = /^return\s+(true|false|null|undefined|void\s+0|0|\[\s*\]|\{\s*\})\s*;?$/;
|
|
16582
|
+
function isSafeFallbackReturn(block) {
|
|
16583
|
+
const statements = block.getStatements();
|
|
16584
|
+
if (statements.length === 0)
|
|
16585
|
+
return false;
|
|
16586
|
+
let returnStatement;
|
|
16587
|
+
let hasOtherSideEffects = false;
|
|
16588
|
+
for (const stmt of statements) {
|
|
16589
|
+
if (ts_morph_1.Node.isReturnStatement(stmt)) {
|
|
16590
|
+
returnStatement = stmt;
|
|
16591
|
+
} else if (ts_morph_1.Node.isVariableStatement(stmt)) {
|
|
16592
|
+
continue;
|
|
16593
|
+
} else {
|
|
16594
|
+
hasOtherSideEffects = true;
|
|
16595
|
+
}
|
|
16596
|
+
}
|
|
16597
|
+
if (!returnStatement || hasOtherSideEffects)
|
|
16598
|
+
return false;
|
|
16599
|
+
const returnText = returnStatement.getText().trim();
|
|
16600
|
+
if (SAFE_FALLBACK_PATTERN.test(returnText))
|
|
16601
|
+
return true;
|
|
16602
|
+
if (statements.length === 1 && returnText.startsWith("return "))
|
|
16603
|
+
return true;
|
|
16604
|
+
return false;
|
|
16605
|
+
}
|
|
16581
16606
|
function detectMissingErrorLogging(sourceFile, filePath, fns, policy, violations) {
|
|
16582
16607
|
sourceFile.forEachDescendant((node) => {
|
|
16583
16608
|
if (!ts_morph_1.Node.isCatchClause(node))
|
|
@@ -16596,6 +16621,8 @@ var require_reliability_detector = __commonJS({
|
|
|
16596
16621
|
});
|
|
16597
16622
|
if (hasThrow)
|
|
16598
16623
|
return;
|
|
16624
|
+
if (isSafeFallbackReturn(block))
|
|
16625
|
+
return;
|
|
16599
16626
|
const hasIncrement = blockText.includes("++") || blockText.includes("+=");
|
|
16600
16627
|
const hasPropertyMethodCall = /this\.\w+/.test(blockText);
|
|
16601
16628
|
const hasAwaitOrDelay = /\bawait\b|\bsetTimeout\b|\bsleep\b|\bdelay\b/i.test(blockText);
|
|
@@ -17715,6 +17742,7 @@ var require_dead_code_detector = __commonJS({
|
|
|
17715
17742
|
}
|
|
17716
17743
|
const project = createProject(input);
|
|
17717
17744
|
const sourceFiles = /* @__PURE__ */ new Map();
|
|
17745
|
+
const { aliases } = parsePathAliases(input);
|
|
17718
17746
|
for (const sf of project.getSourceFiles()) {
|
|
17719
17747
|
const filePath = normalizeFilePath(sf.getFilePath());
|
|
17720
17748
|
sourceFiles.set(filePath, sf);
|
|
@@ -17730,7 +17758,7 @@ var require_dead_code_detector = __commonJS({
|
|
|
17730
17758
|
}
|
|
17731
17759
|
const importRefs = /* @__PURE__ */ new Map();
|
|
17732
17760
|
for (const [filePath, sf] of sourceFiles) {
|
|
17733
|
-
const refs = extractImportReferences(sf, filePath, project);
|
|
17761
|
+
const refs = extractImportReferences(sf, filePath, project, aliases);
|
|
17734
17762
|
if (refs.size > 0) {
|
|
17735
17763
|
importRefs.set(filePath, refs);
|
|
17736
17764
|
}
|
|
@@ -17772,6 +17800,9 @@ var require_dead_code_detector = __commonJS({
|
|
|
17772
17800
|
continue;
|
|
17773
17801
|
if (cfg.excludeTypes && isTypeExport(exp.type))
|
|
17774
17802
|
continue;
|
|
17803
|
+
const sf = sourceFiles.get(filePath);
|
|
17804
|
+
if (sf && exp.type === "class" && isNestJSDIRegistered(sf, exp.name))
|
|
17805
|
+
continue;
|
|
17775
17806
|
unusedExports.push({
|
|
17776
17807
|
export: exp,
|
|
17777
17808
|
file: filePath,
|
|
@@ -17849,7 +17880,7 @@ var require_dead_code_detector = __commonJS({
|
|
|
17849
17880
|
}
|
|
17850
17881
|
return exports3;
|
|
17851
17882
|
}
|
|
17852
|
-
function extractImportReferences(sourceFile, filePath, project) {
|
|
17883
|
+
function extractImportReferences(sourceFile, filePath, project, aliases = []) {
|
|
17853
17884
|
const refs = /* @__PURE__ */ new Map();
|
|
17854
17885
|
function addRef(resolvedFile, name) {
|
|
17855
17886
|
if (!refs.has(resolvedFile)) {
|
|
@@ -17859,7 +17890,7 @@ var require_dead_code_detector = __commonJS({
|
|
|
17859
17890
|
}
|
|
17860
17891
|
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
17861
17892
|
const specifier = importDecl.getModuleSpecifierValue();
|
|
17862
|
-
const resolved = resolveModuleSpecifier(filePath, specifier, project);
|
|
17893
|
+
const resolved = resolveModuleSpecifier(filePath, specifier, project, aliases);
|
|
17863
17894
|
if (!resolved)
|
|
17864
17895
|
continue;
|
|
17865
17896
|
const defaultImport = importDecl.getDefaultImport();
|
|
@@ -17879,7 +17910,7 @@ var require_dead_code_detector = __commonJS({
|
|
|
17879
17910
|
const specifier = exportDecl.getModuleSpecifierValue();
|
|
17880
17911
|
if (!specifier)
|
|
17881
17912
|
continue;
|
|
17882
|
-
const resolved = resolveModuleSpecifier(filePath, specifier, project);
|
|
17913
|
+
const resolved = resolveModuleSpecifier(filePath, specifier, project, aliases);
|
|
17883
17914
|
if (!resolved)
|
|
17884
17915
|
continue;
|
|
17885
17916
|
if (exportDecl.isNamespaceExport()) {
|
|
@@ -17900,9 +17931,7 @@ var require_dead_code_detector = __commonJS({
|
|
|
17900
17931
|
if (args.length === 0 || !ts_morph_1.Node.isStringLiteral(args[0]))
|
|
17901
17932
|
return;
|
|
17902
17933
|
const modulePath = args[0].getLiteralValue();
|
|
17903
|
-
|
|
17904
|
-
return;
|
|
17905
|
-
const resolved = resolveModuleSpecifier(filePath, modulePath, project);
|
|
17934
|
+
const resolved = resolveModuleSpecifier(filePath, modulePath, project, aliases);
|
|
17906
17935
|
if (!resolved)
|
|
17907
17936
|
return;
|
|
17908
17937
|
const parent = node.getParent();
|
|
@@ -17955,6 +17984,83 @@ var require_dead_code_detector = __commonJS({
|
|
|
17955
17984
|
function isTypeExport(type) {
|
|
17956
17985
|
return type === "type" || type === "interface" || type === "enum";
|
|
17957
17986
|
}
|
|
17987
|
+
function parsePathAliases(input) {
|
|
17988
|
+
let tsconfigContent;
|
|
17989
|
+
const tsconfigFile = input.changedFiles.find((f) => f.status !== "deleted" && /tsconfig(?:\.build)?\.json$/.test(f.path));
|
|
17990
|
+
if (tsconfigFile) {
|
|
17991
|
+
tsconfigContent = tsconfigFile.content;
|
|
17992
|
+
}
|
|
17993
|
+
if (!tsconfigContent && input.projectRoot) {
|
|
17994
|
+
try {
|
|
17995
|
+
const fs9 = require("fs");
|
|
17996
|
+
const tsconfigPath = path9.join(input.projectRoot, "tsconfig.json");
|
|
17997
|
+
if (fs9.existsSync(tsconfigPath)) {
|
|
17998
|
+
tsconfigContent = fs9.readFileSync(tsconfigPath, "utf-8");
|
|
17999
|
+
}
|
|
18000
|
+
} catch {
|
|
18001
|
+
}
|
|
18002
|
+
}
|
|
18003
|
+
if (!tsconfigContent) {
|
|
18004
|
+
return { aliases: [], baseUrl: "." };
|
|
18005
|
+
}
|
|
18006
|
+
try {
|
|
18007
|
+
const stripped = tsconfigContent.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/,\s*([}\]])/g, "$1");
|
|
18008
|
+
const tsconfig = JSON.parse(stripped);
|
|
18009
|
+
const compilerOptions = tsconfig.compilerOptions ?? {};
|
|
18010
|
+
const baseUrl = compilerOptions.baseUrl ?? ".";
|
|
18011
|
+
const paths = compilerOptions.paths ?? {};
|
|
18012
|
+
const aliases = [];
|
|
18013
|
+
for (const [pattern, targets] of Object.entries(paths)) {
|
|
18014
|
+
const prefix = pattern.replace(/\/?\*$/, "");
|
|
18015
|
+
const resolvedTargets = targets.map((t) => {
|
|
18016
|
+
const target = t.replace(/\/?\*$/, "");
|
|
18017
|
+
if (baseUrl !== "." && !target.startsWith("/")) {
|
|
18018
|
+
return posixJoinAndNormalize(baseUrl, target);
|
|
18019
|
+
}
|
|
18020
|
+
return target;
|
|
18021
|
+
});
|
|
18022
|
+
aliases.push({ prefix, targets: resolvedTargets });
|
|
18023
|
+
}
|
|
18024
|
+
return { aliases, baseUrl };
|
|
18025
|
+
} catch {
|
|
18026
|
+
return { aliases: [], baseUrl: "." };
|
|
18027
|
+
}
|
|
18028
|
+
}
|
|
18029
|
+
function resolvePathAlias(specifier, aliases, project) {
|
|
18030
|
+
for (const alias of aliases) {
|
|
18031
|
+
if (alias.prefix === "" || specifier === alias.prefix || specifier.startsWith(alias.prefix + "/")) {
|
|
18032
|
+
const remainder = alias.prefix === "" ? specifier : specifier.slice(alias.prefix.length).replace(/^\//, "");
|
|
18033
|
+
for (const target of alias.targets) {
|
|
18034
|
+
const candidate = remainder ? target ? target + "/" + remainder : remainder : target;
|
|
18035
|
+
const resolved = resolveInProject(candidate, project);
|
|
18036
|
+
if (resolved)
|
|
18037
|
+
return resolved;
|
|
18038
|
+
}
|
|
18039
|
+
}
|
|
18040
|
+
}
|
|
18041
|
+
return void 0;
|
|
18042
|
+
}
|
|
18043
|
+
function isNestJSDIRegistered(sourceFile, exportName) {
|
|
18044
|
+
for (const cls of sourceFile.getClasses()) {
|
|
18045
|
+
const className = cls.getName();
|
|
18046
|
+
if (className !== exportName)
|
|
18047
|
+
continue;
|
|
18048
|
+
const decorators = cls.getDecorators();
|
|
18049
|
+
for (const dec of decorators) {
|
|
18050
|
+
const decName = dec.getName();
|
|
18051
|
+
if (decName === "Injectable" || decName === "Controller" || decName === "Guard" || decName === "Resolver" || decName === "Gateway") {
|
|
18052
|
+
return true;
|
|
18053
|
+
}
|
|
18054
|
+
}
|
|
18055
|
+
const extendsClause = cls.getExtends();
|
|
18056
|
+
if (extendsClause) {
|
|
18057
|
+
const extendsText = extendsClause.getText();
|
|
18058
|
+
if (extendsText.includes("PassportStrategy"))
|
|
18059
|
+
return true;
|
|
18060
|
+
}
|
|
18061
|
+
}
|
|
18062
|
+
return false;
|
|
18063
|
+
}
|
|
17958
18064
|
function createProject(input) {
|
|
17959
18065
|
const project = new ts_morph_1.Project({
|
|
17960
18066
|
useInMemoryFileSystem: true,
|
|
@@ -17977,13 +18083,16 @@ var require_dead_code_detector = __commonJS({
|
|
|
17977
18083
|
const normalized = filePath.replace(/\\/g, "/");
|
|
17978
18084
|
return normalized.startsWith("/") ? normalized.slice(1) : normalized;
|
|
17979
18085
|
}
|
|
17980
|
-
function resolveModuleSpecifier(sourcePath, specifier, project) {
|
|
17981
|
-
if (
|
|
17982
|
-
|
|
18086
|
+
function resolveModuleSpecifier(sourcePath, specifier, project, aliases = []) {
|
|
18087
|
+
if (specifier.startsWith(".") || specifier.startsWith("/")) {
|
|
18088
|
+
const sourceDir = posixDirname(sourcePath);
|
|
18089
|
+
const resolved = posixJoinAndNormalize(sourceDir, specifier);
|
|
18090
|
+
return resolveInProject(resolved, project) ?? resolved;
|
|
17983
18091
|
}
|
|
17984
|
-
|
|
17985
|
-
|
|
17986
|
-
|
|
18092
|
+
if (aliases.length > 0) {
|
|
18093
|
+
return resolvePathAlias(specifier, aliases, project);
|
|
18094
|
+
}
|
|
18095
|
+
return void 0;
|
|
17987
18096
|
}
|
|
17988
18097
|
function resolveInProject(resolved, project) {
|
|
17989
18098
|
const lookup = "/" + resolved;
|