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.
Files changed (2) hide show
  1. package/dist/index.js +122 -13
  2. 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
- if (!modulePath.startsWith(".") && !modulePath.startsWith("/"))
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 (!specifier.startsWith(".") && !specifier.startsWith("/")) {
17982
- return void 0;
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
- const sourceDir = posixDirname(sourcePath);
17985
- const resolved = posixJoinAndNormalize(sourceDir, specifier);
17986
- return resolveInProject(resolved, project) ?? resolved;
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "technical-debt-radar",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Stop Node.js production crashes before merge. 47 detection patterns across 5 categories.",
5
5
  "bin": {
6
6
  "radar": "dist/index.js",