@anatolykoptev/krolik-cli 0.15.1 → 0.16.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 CHANGED
@@ -58,7 +58,7 @@ var KROLIK_VERSION, TEMPLATE_VERSION;
58
58
  var init_version = __esm({
59
59
  "src/version.ts"() {
60
60
  init_esm_shims();
61
- KROLIK_VERSION = "0.15.1";
61
+ KROLIK_VERSION = "0.16.0";
62
62
  TEMPLATE_VERSION = "6.1.0";
63
63
  }
64
64
  });
@@ -20779,6 +20779,53 @@ var init_context4 = __esm({
20779
20779
  });
20780
20780
 
20781
20781
  // src/commands/fix/fixers/console/analyzer.ts
20782
+ function isErrorHandlingContext(lines, lineIndex) {
20783
+ const startIdx = Math.max(0, lineIndex - 5);
20784
+ const endIdx = Math.min(lines.length - 1, lineIndex + 2);
20785
+ for (let i = startIdx; i <= endIdx; i++) {
20786
+ const line = lines[i] ?? "";
20787
+ for (const pattern of ERROR_CONTEXT_PATTERNS) {
20788
+ if (pattern.test(line)) {
20789
+ return true;
20790
+ }
20791
+ }
20792
+ }
20793
+ return false;
20794
+ }
20795
+ function isStructuredLogging(line) {
20796
+ return STRUCTURED_LOGGING_PATTERNS.some((pattern) => pattern.test(line));
20797
+ }
20798
+ function classifyConsole(method, lines, lineIndex) {
20799
+ const isDebugMethod = DEBUG_METHODS.includes(method);
20800
+ const isErrorMethod = ERROR_METHODS.includes(method);
20801
+ const inErrorContext = isErrorHandlingContext(lines, lineIndex);
20802
+ if (isErrorMethod && inErrorContext) {
20803
+ return {
20804
+ severity: "info",
20805
+ shouldFix: false,
20806
+ reason: "Error logging in error handler - likely intentional"
20807
+ };
20808
+ }
20809
+ if (isDebugMethod) {
20810
+ return {
20811
+ severity: "warning",
20812
+ shouldFix: true,
20813
+ reason: "Debug statement should be removed before production"
20814
+ };
20815
+ }
20816
+ if (isErrorMethod) {
20817
+ return {
20818
+ severity: "info",
20819
+ shouldFix: true,
20820
+ reason: "Consider using structured logging instead"
20821
+ };
20822
+ }
20823
+ return {
20824
+ severity: "warning",
20825
+ shouldFix: true,
20826
+ reason: "Remove console statement before production"
20827
+ };
20828
+ }
20782
20829
  function analyzeConsole(content, file) {
20783
20830
  const issues = [];
20784
20831
  const lines = content.split("\n");
@@ -20786,33 +20833,76 @@ function analyzeConsole(content, file) {
20786
20833
  const line = lines[i] ?? "";
20787
20834
  const trimmed = line.trim();
20788
20835
  if (trimmed.startsWith("//") || trimmed.startsWith("*")) continue;
20836
+ if (isStructuredLogging(line)) continue;
20789
20837
  CONSOLE_PATTERN.lastIndex = 0;
20790
20838
  let match;
20791
20839
  while ((match = CONSOLE_PATTERN.exec(line)) !== null) {
20792
20840
  if (isInsideLineComment(line, match.index)) continue;
20793
20841
  if (isInsideStringLine(line, match.index)) continue;
20794
20842
  const method = match[1] ?? "log";
20795
- issues.push({
20843
+ const classification = classifyConsole(method, lines, i);
20844
+ const issue = {
20796
20845
  file,
20797
20846
  line: i + 1,
20798
- severity: "warning",
20847
+ severity: classification.severity,
20799
20848
  category: "lint",
20800
- message: `Unexpected console statement: console.${method}`,
20801
- suggestion: "Remove console statement before production",
20802
- snippet: trimmed.slice(0, 60),
20803
- fixerId: "console"
20804
- });
20849
+ message: `console.${method}: ${classification.reason}`,
20850
+ suggestion: classification.shouldFix ? "Remove or replace with structured logging" : "Review if this logging is necessary",
20851
+ snippet: trimmed.slice(0, 60)
20852
+ };
20853
+ if (classification.shouldFix) {
20854
+ issue.fixerId = "console";
20855
+ }
20856
+ issues.push(issue);
20805
20857
  }
20806
20858
  }
20807
20859
  return issues;
20808
20860
  }
20809
- var CONSOLE_METHODS, CONSOLE_PATTERN;
20861
+ var DEBUG_METHODS, ERROR_METHODS, ERROR_CONTEXT_PATTERNS, STRUCTURED_LOGGING_PATTERNS, CONSOLE_PATTERN;
20810
20862
  var init_analyzer2 = __esm({
20811
20863
  "src/commands/fix/fixers/console/analyzer.ts"() {
20812
20864
  init_esm_shims();
20813
20865
  init_swc();
20814
- CONSOLE_METHODS = ["log", "warn", "error", "info", "debug", "trace", "dir", "table"];
20815
- CONSOLE_PATTERN = new RegExp(`console\\.(${CONSOLE_METHODS.join("|")})\\s*\\(`, "g");
20866
+ DEBUG_METHODS = ["log", "debug", "trace", "dir", "table", "info", "count", "time", "timeEnd"];
20867
+ ERROR_METHODS = ["error", "warn"];
20868
+ ERROR_CONTEXT_PATTERNS = [
20869
+ /\bcatch\s*\(/i,
20870
+ // catch block
20871
+ /\.catch\s*\(/i,
20872
+ // promise catch
20873
+ /\bonError\b/i,
20874
+ // error callback
20875
+ /\bonReject\b/i,
20876
+ // rejection handler
20877
+ /\berror\s*[=:]/i,
20878
+ // error variable assignment
20879
+ /\bhandleError\b/i,
20880
+ // error handler function
20881
+ /\bif\s*\(\s*error\b/i,
20882
+ // error check
20883
+ /\bif\s*\(\s*err\b/i,
20884
+ // err check
20885
+ /throw\s+/i
20886
+ // near throw statement
20887
+ ];
20888
+ STRUCTURED_LOGGING_PATTERNS = [
20889
+ /\blogger\./i,
20890
+ // logger.info, logger.error
20891
+ /\blog\./i,
20892
+ // log.info, log.error
20893
+ /(?<!console\.)\bdebug\(/i,
20894
+ // debug() from debug package (not console.debug)
20895
+ /\bwinston\b/i,
20896
+ // winston logger
20897
+ /\bpino\b/i,
20898
+ // pino logger
20899
+ /\bbunyan\b/i
20900
+ // bunyan logger
20901
+ ];
20902
+ CONSOLE_PATTERN = new RegExp(
20903
+ `console\\.(${[...DEBUG_METHODS, ...ERROR_METHODS].join("|")})\\s*\\(`,
20904
+ "g"
20905
+ );
20816
20906
  }
20817
20907
  });
20818
20908
 
@@ -24766,6 +24856,27 @@ var init_long_functions = __esm({
24766
24856
  };
24767
24857
  }
24768
24858
  });
24859
+ function suggestConstantName(value, lineContent) {
24860
+ const knownName = NUMBER_NAME_SUGGESTIONS.get(value);
24861
+ if (knownName) return knownName;
24862
+ for (const { pattern, nameTemplate } of CONTEXT_PATTERNS) {
24863
+ if (pattern.test(lineContent)) {
24864
+ return nameTemplate(value);
24865
+ }
24866
+ }
24867
+ if (value >= 1e3 && value % 1e3 === 0) {
24868
+ const seconds = value / 1e3;
24869
+ if (seconds < 60) return `${seconds}_SECONDS_MS`;
24870
+ if (seconds < 3600) return `${Math.round(seconds / 60)}_MINUTES_MS`;
24871
+ return `${Math.round(seconds / 3600)}_HOURS_MS`;
24872
+ }
24873
+ if (value >= 1024 && value % 1024 === 0) {
24874
+ const kb = value / 1024;
24875
+ if (kb < 1024) return `${kb}_KB`;
24876
+ return `${Math.round(kb / 1024)}_MB`;
24877
+ }
24878
+ return `CONST_${value}`;
24879
+ }
24769
24880
  function analyzeMagicNumbersAST(content, file) {
24770
24881
  if (file.includes(".config.") || file.includes(".test.") || file.includes(".spec.") || file.endsWith(".d.ts")) {
24771
24882
  return [];
@@ -24789,13 +24900,14 @@ function analyzeMagicNumbersAST(content, file) {
24789
24900
  if (shouldSkipNumber(value)) continue;
24790
24901
  if (isInAllowedContext2(literal)) continue;
24791
24902
  const lineContent = lines[line - 1] ?? "";
24903
+ const suggestedName = suggestConstantName(value, lineContent);
24792
24904
  issues.push({
24793
24905
  file,
24794
24906
  line,
24795
24907
  severity: "warning",
24796
24908
  category: "hardcoded",
24797
24909
  message: `Hardcoded number: ${value}`,
24798
- suggestion: "Extract to a named constant",
24910
+ suggestion: `Extract to constant: const ${suggestedName} = ${value}`,
24799
24911
  snippet: lineContent.trim().slice(0, 60),
24800
24912
  fixerId: "magic-numbers"
24801
24913
  });
@@ -24892,13 +25004,14 @@ function analyzeMagicNumbersFallback(content, file) {
24892
25004
  while ((match = MAGIC_NUMBER_PATTERN.exec(codeOnly)) !== null) {
24893
25005
  const num = parseInt(match[1] ?? "0", 10);
24894
25006
  if (shouldSkipNumber(num)) continue;
25007
+ const suggestedName = suggestConstantName(num, line);
24895
25008
  issues.push({
24896
25009
  file,
24897
25010
  line: i + 1,
24898
25011
  severity: "warning",
24899
25012
  category: "hardcoded",
24900
25013
  message: `Hardcoded number: ${num}`,
24901
- suggestion: "Extract to a named constant",
25014
+ suggestion: `Extract to constant: const ${suggestedName} = ${num}`,
24902
25015
  snippet: trimmed.slice(0, 60),
24903
25016
  fixerId: "magic-numbers"
24904
25017
  });
@@ -24906,7 +25019,7 @@ function analyzeMagicNumbersFallback(content, file) {
24906
25019
  }
24907
25020
  return issues;
24908
25021
  }
24909
- var ALLOWED_NUMBERS, HTTP_STATUS_CODES2, PORT_NUMBERS, TIME_CONSTANTS;
25022
+ var ALLOWED_NUMBERS, HTTP_STATUS_CODES2, PORT_NUMBERS, TIME_CONSTANTS, NUMBER_NAME_SUGGESTIONS, CONTEXT_PATTERNS;
24910
25023
  var init_ast_analyzer4 = __esm({
24911
25024
  "src/commands/fix/fixers/magic-numbers/ast-analyzer.ts"() {
24912
25025
  init_esm_shims();
@@ -24944,6 +25057,75 @@ var init_ast_analyzer4 = __esm({
24944
25057
  864e5
24945
25058
  // 1 day
24946
25059
  ]);
25060
+ NUMBER_NAME_SUGGESTIONS = /* @__PURE__ */ new Map([
25061
+ // Time (milliseconds)
25062
+ [500, "HALF_SECOND_MS"],
25063
+ [1e3, "ONE_SECOND_MS"],
25064
+ [1500, "ONE_AND_HALF_SECONDS_MS"],
25065
+ [2e3, "TWO_SECONDS_MS"],
25066
+ [3e3, "THREE_SECONDS_MS"],
25067
+ [5e3, "FIVE_SECONDS_MS"],
25068
+ [1e4, "TEN_SECONDS_MS"],
25069
+ [15e3, "FIFTEEN_SECONDS_MS"],
25070
+ [3e4, "THIRTY_SECONDS_MS"],
25071
+ [6e4, "ONE_MINUTE_MS"],
25072
+ [12e4, "TWO_MINUTES_MS"],
25073
+ [3e5, "FIVE_MINUTES_MS"],
25074
+ [6e5, "TEN_MINUTES_MS"],
25075
+ [9e5, "FIFTEEN_MINUTES_MS"],
25076
+ [18e5, "THIRTY_MINUTES_MS"],
25077
+ [36e5, "ONE_HOUR_MS"],
25078
+ [72e5, "TWO_HOURS_MS"],
25079
+ [864e5, "ONE_DAY_MS"],
25080
+ [6048e5, "ONE_WEEK_MS"],
25081
+ // Time (seconds)
25082
+ [60, "ONE_MINUTE_SECS"],
25083
+ [120, "TWO_MINUTES_SECS"],
25084
+ [300, "FIVE_MINUTES_SECS"],
25085
+ [600, "TEN_MINUTES_SECS"],
25086
+ [3600, "ONE_HOUR_SECS"],
25087
+ [86400, "ONE_DAY_SECS"],
25088
+ // File sizes
25089
+ [1024, "ONE_KB"],
25090
+ [1048576, "ONE_MB"],
25091
+ [1073741824, "ONE_GB"],
25092
+ [5242880, "FIVE_MB"],
25093
+ [10485760, "TEN_MB"],
25094
+ [52428800, "FIFTY_MB"],
25095
+ [104857600, "HUNDRED_MB"],
25096
+ // Pagination / limits
25097
+ [10, "DEFAULT_PAGE_SIZE"],
25098
+ [20, "DEFAULT_PAGE_SIZE"],
25099
+ [25, "DEFAULT_PAGE_SIZE"],
25100
+ [50, "DEFAULT_PAGE_SIZE"],
25101
+ [100, "DEFAULT_LIMIT"],
25102
+ // Retry / timeout
25103
+ [3, "MAX_RETRIES"],
25104
+ [5, "MAX_RETRIES"],
25105
+ // Animation / UI
25106
+ [150, "ANIMATION_DURATION_MS"],
25107
+ [200, "ANIMATION_DURATION_MS"],
25108
+ [250, "ANIMATION_DURATION_MS"],
25109
+ [300, "ANIMATION_DURATION_MS"],
25110
+ [350, "ANIMATION_DURATION_MS"],
25111
+ [400, "ANIMATION_DURATION_MS"],
25112
+ [500, "ANIMATION_DURATION_MS"]
25113
+ ]);
25114
+ CONTEXT_PATTERNS = [
25115
+ // setTimeout/setInterval
25116
+ { pattern: /setTimeout|setInterval|delay|sleep/, nameTemplate: (v) => `DELAY_${v}MS` },
25117
+ // Width/height
25118
+ { pattern: /width|height|size/i, nameTemplate: (v) => `SIZE_${v}PX` },
25119
+ // Padding/margin
25120
+ { pattern: /padding|margin|gap|spacing/i, nameTemplate: (v) => `SPACING_${v}` },
25121
+ // Max/min
25122
+ { pattern: /max|maximum/i, nameTemplate: (v) => `MAX_${v}` },
25123
+ { pattern: /min|minimum/i, nameTemplate: (v) => `MIN_${v}` },
25124
+ // Threshold/limit
25125
+ { pattern: /threshold|limit/i, nameTemplate: (v) => `THRESHOLD_${v}` },
25126
+ // Percentage
25127
+ { pattern: /percent|opacity|alpha/i, nameTemplate: (v) => `PERCENT_${v}` }
25128
+ ];
24947
25129
  }
24948
25130
  });
24949
25131
  function fixMagicNumberAST(issue, content) {
@@ -27042,7 +27224,7 @@ function analyzeContentSignals(filePath, content) {
27042
27224
  const exportsJSX = matchesAnyPattern(fileContent, JSX_PATTERNS);
27043
27225
  const usesReactHooks = matchesAnyPattern(fileContent, REACT_HOOK_PATTERNS);
27044
27226
  const exportsValidationSchema = matchesAnyPattern(fileContent, SCHEMA_PATTERNS);
27045
- const createsReactContext = matchesAnyPattern(fileContent, CONTEXT_PATTERNS);
27227
+ const createsReactContext = matchesAnyPattern(fileContent, CONTEXT_PATTERNS2);
27046
27228
  const hasAsyncOperations = matchesAnyPattern(fileContent, ASYNC_PATTERNS);
27047
27229
  const hasPureIndicators = matchesAnyPattern(fileContent, PURE_FUNCTION_INDICATORS);
27048
27230
  const hasSideEffects = matchesAnyPattern(fileContent, SIDE_EFFECT_PATTERNS);
@@ -27094,7 +27276,7 @@ function detectContentType(signals) {
27094
27276
  }
27095
27277
  return null;
27096
27278
  }
27097
- var SCORES, REACT_HOOK_PATTERNS, JSX_PATTERNS, SCHEMA_PATTERNS, CONTEXT_PATTERNS, PURE_FUNCTION_INDICATORS, SIDE_EFFECT_PATTERNS, ASYNC_PATTERNS;
27279
+ var SCORES, REACT_HOOK_PATTERNS, JSX_PATTERNS, SCHEMA_PATTERNS, CONTEXT_PATTERNS2, PURE_FUNCTION_INDICATORS, SIDE_EFFECT_PATTERNS, ASYNC_PATTERNS;
27098
27280
  var init_content = __esm({
27099
27281
  "src/lib/@discovery/reusables/signals/content.ts"() {
27100
27282
  init_esm_shims();
@@ -27163,7 +27345,7 @@ var init_content = __esm({
27163
27345
  /export\s+(?:const|type)\s+\w*Schema/
27164
27346
  // export const/type XxxSchema
27165
27347
  ];
27166
- CONTEXT_PATTERNS = [
27348
+ CONTEXT_PATTERNS2 = [
27167
27349
  /createContext\s*\(/,
27168
27350
  // React.createContext
27169
27351
  /React\.createContext\s*\(/,