@sun-asterisk/sunlint 1.3.34 → 1.3.36

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 (90) hide show
  1. package/core/architecture-integration.js +16 -7
  2. package/core/auto-performance-manager.js +1 -1
  3. package/core/cli-action-handler.js +102 -2
  4. package/core/cli-program.js +102 -138
  5. package/core/file-targeting-service.js +62 -4
  6. package/core/git-utils.js +19 -12
  7. package/core/github-annotate-service.js +326 -11
  8. package/core/html-report-generator.js +326 -731
  9. package/core/impact-integration.js +551 -0
  10. package/core/output-service.js +293 -21
  11. package/core/scoring-service.js +3 -2
  12. package/engines/arch-detect/core/analyzer.js +413 -0
  13. package/engines/arch-detect/core/index.js +22 -0
  14. package/engines/arch-detect/engine/hybrid-detector.js +176 -0
  15. package/engines/arch-detect/engine/index.js +24 -0
  16. package/engines/arch-detect/engine/rule-executor.js +228 -0
  17. package/engines/arch-detect/engine/score-calculator.js +214 -0
  18. package/engines/arch-detect/engine/violation-detector.js +616 -0
  19. package/engines/arch-detect/index.js +50 -0
  20. package/engines/arch-detect/rules/base-rule.js +187 -0
  21. package/engines/arch-detect/rules/index.js +35 -0
  22. package/engines/arch-detect/rules/layered/index.js +28 -0
  23. package/engines/arch-detect/rules/layered/l001-presentation-layer.js +237 -0
  24. package/engines/arch-detect/rules/layered/l002-business-layer.js +215 -0
  25. package/engines/arch-detect/rules/layered/l003-data-layer.js +229 -0
  26. package/engines/arch-detect/rules/layered/l004-model-layer.js +204 -0
  27. package/engines/arch-detect/rules/layered/l005-layer-separation.js +215 -0
  28. package/engines/arch-detect/rules/layered/l006-dependency-direction.js +221 -0
  29. package/engines/arch-detect/rules/layered/layered-rules-collection.js +445 -0
  30. package/engines/arch-detect/rules/modular/index.js +27 -0
  31. package/engines/arch-detect/rules/modular/m001-feature-modules.js +238 -0
  32. package/engines/arch-detect/rules/modular/m002-core-module.js +169 -0
  33. package/engines/arch-detect/rules/modular/m003-module-declaration.js +186 -0
  34. package/engines/arch-detect/rules/modular/m004-public-api.js +171 -0
  35. package/engines/arch-detect/rules/modular/m005-no-deep-imports.js +220 -0
  36. package/engines/arch-detect/rules/modular/modular-rules-collection.js +357 -0
  37. package/engines/arch-detect/rules/presentation/index.js +27 -0
  38. package/engines/arch-detect/rules/presentation/pr001-view-layer.js +221 -0
  39. package/engines/arch-detect/rules/presentation/pr002-presentation-logic.js +192 -0
  40. package/engines/arch-detect/rules/presentation/pr004-data-binding.js +187 -0
  41. package/engines/arch-detect/rules/presentation/pr006-router-layer.js +185 -0
  42. package/engines/arch-detect/rules/presentation/pr007-interactor-layer.js +181 -0
  43. package/engines/arch-detect/rules/presentation/presentation-rules-collection.js +507 -0
  44. package/engines/arch-detect/rules/project-scanner/index.js +31 -0
  45. package/engines/arch-detect/rules/project-scanner/ps001-project-root.js +213 -0
  46. package/engines/arch-detect/rules/project-scanner/ps002-language-detection.js +192 -0
  47. package/engines/arch-detect/rules/project-scanner/ps003-framework-detection.js +339 -0
  48. package/engines/arch-detect/rules/project-scanner/ps004-build-system.js +171 -0
  49. package/engines/arch-detect/rules/project-scanner/ps005-source-directory.js +163 -0
  50. package/engines/arch-detect/rules/project-scanner/ps006-test-directory.js +184 -0
  51. package/engines/arch-detect/rules/project-scanner/ps007-documentation.js +149 -0
  52. package/engines/arch-detect/rules/project-scanner/ps008-cicd-detection.js +163 -0
  53. package/engines/arch-detect/rules/project-scanner/ps009-code-quality.js +152 -0
  54. package/engines/arch-detect/rules/project-scanner/ps010-statistics.js +180 -0
  55. package/engines/arch-detect/rules/rule-registry.js +111 -0
  56. package/engines/arch-detect/types/context.types.js +60 -0
  57. package/engines/arch-detect/types/enums.js +161 -0
  58. package/engines/arch-detect/types/index.js +25 -0
  59. package/engines/arch-detect/types/result.types.js +7 -0
  60. package/engines/arch-detect/types/rule.types.js +7 -0
  61. package/engines/arch-detect/utils/file-scanner.js +411 -0
  62. package/engines/arch-detect/utils/index.js +23 -0
  63. package/engines/arch-detect/utils/pattern-matcher.js +328 -0
  64. package/engines/impact/cli.js +106 -0
  65. package/engines/impact/config/default-config.js +54 -0
  66. package/engines/impact/core/change-detector.js +258 -0
  67. package/engines/impact/core/detectors/database-detector.js +1317 -0
  68. package/engines/impact/core/detectors/endpoint-detector.js +55 -0
  69. package/engines/impact/core/impact-analyzer.js +124 -0
  70. package/engines/impact/core/report-generator.js +462 -0
  71. package/engines/impact/core/utils/ast-parser.js +241 -0
  72. package/engines/impact/core/utils/dependency-graph.js +159 -0
  73. package/engines/impact/core/utils/file-utils.js +116 -0
  74. package/engines/impact/core/utils/git-utils.js +203 -0
  75. package/engines/impact/core/utils/logger.js +13 -0
  76. package/engines/impact/core/utils/method-call-graph.js +1192 -0
  77. package/engines/impact/index.js +135 -0
  78. package/engines/impact/package.json +29 -0
  79. package/package.json +18 -43
  80. package/scripts/build-release.sh +0 -0
  81. package/scripts/copy-impact-analyzer.js +135 -0
  82. package/scripts/install.sh +0 -0
  83. package/scripts/manual-release.sh +0 -0
  84. package/scripts/pre-release-test.sh +0 -0
  85. package/scripts/prepare-release.sh +0 -0
  86. package/scripts/quick-performance-test.js +0 -0
  87. package/scripts/setup-github-registry.sh +0 -0
  88. package/scripts/trigger-release.sh +0 -0
  89. package/scripts/verify-install.sh +0 -0
  90. package/templates/combined-report.html +1418 -0
@@ -16,16 +16,15 @@ class ArchitectureIntegration {
16
16
 
17
17
  /**
18
18
  * Load architecture detection module
19
- * Tries bundled version first, then falls back to local development path
19
+ * Tries bundled (engines/) first, then npm package, then local dev paths
20
20
  */
21
21
  async loadArchitectureModule() {
22
22
  if (this.archModule) {
23
23
  return this.archModule;
24
24
  }
25
25
 
26
- // Try bundled version first (engines/arch-detect)
26
+ // Priority 1: Try bundled version (engines/arch-detect) - for published package
27
27
  const bundledPath = path.join(__dirname, '..', 'engines', 'arch-detect', 'index.js');
28
-
29
28
  if (fs.existsSync(bundledPath)) {
30
29
  try {
31
30
  this.archModule = require(bundledPath);
@@ -35,12 +34,23 @@ class ArchitectureIntegration {
35
34
  return this.archModule;
36
35
  } catch (error) {
37
36
  if (this.options.verbose) {
38
- console.log(chalk.yellow(`⚠️ Failed to load bundled module: ${error.message}`));
37
+ console.log(chalk.yellow(`⚠️ Failed to load bundled: ${error.message}`));
39
38
  }
40
39
  }
41
40
  }
42
41
 
43
- // Fallback: Try local development path
42
+ // Priority 2: Try npm package (workspace link) - for local development
43
+ try {
44
+ this.archModule = require('@sunlint/architecture-detection');
45
+ if (this.options.verbose) {
46
+ console.log(chalk.gray('📦 Loaded @sunlint/architecture-detection package'));
47
+ }
48
+ return this.archModule;
49
+ } catch (error) {
50
+ // Package not found, continue to fallback
51
+ }
52
+
53
+ // Priority 3: Try local development paths
44
54
  const devPaths = [
45
55
  path.join(__dirname, '..', '..', '..', '..', 'architecture-detection', 'dist', 'index.js'),
46
56
  path.join(__dirname, '..', '..', '..', 'architecture-detection', 'dist', 'index.js'),
@@ -63,8 +73,7 @@ class ArchitectureIntegration {
63
73
  }
64
74
 
65
75
  throw new Error(
66
- 'Architecture detection module not found. Run "npm run build" to bundle it, ' +
67
- 'or ensure architecture-detection is built in the parent directory.'
76
+ 'Architecture detection module not found. Run "npm run build" in sunlint directory.'
68
77
  );
69
78
  }
70
79
 
@@ -76,7 +76,7 @@ class AutoPerformanceManager {
76
76
  analyzeProject(options, targetFiles) {
77
77
  const fileCount = targetFiles.length;
78
78
  const inputPath = options.input || process.cwd();
79
-
79
+
80
80
  // Estimate project complexity
81
81
  const hasNodeModules = fs.existsSync(path.join(inputPath, 'node_modules'));
82
82
  const hasPackageJson = fs.existsSync(path.join(inputPath, 'package.json'));
@@ -15,6 +15,7 @@ const OutputService = require('./output-service');
15
15
  const GitUtils = require('./git-utils');
16
16
  const FileTargetingService = require('./file-targeting-service');
17
17
  const { ArchitectureIntegration } = require('./architecture-integration');
18
+ const { ImpactIntegration } = require('./impact-integration');
18
19
 
19
20
  // Legacy orchestrator for fallback
20
21
  // const LegacyOrchestrator = require('./legacy-analysis-orchestrator'); // Removed
@@ -83,8 +84,8 @@ class CliActionHandler {
83
84
  const startTime = Date.now();
84
85
  let results = null;
85
86
 
86
- // Run code quality analysis (unless --architecture is used alone)
87
- if (rulesToRun.length > 0 && !this.isArchitectureOnly()) {
87
+ // Run code quality analysis (unless --architecture or --impact is used alone)
88
+ if (rulesToRun.length > 0 && !this.isArchitectureOnly() && !this.isImpactOnly()) {
88
89
  results = await this.runModernAnalysis(rulesToRun, targetingResult.files, config);
89
90
  } else {
90
91
  results = { results: [], summary: { total: 0, errors: 0, warnings: 0 } };
@@ -96,6 +97,12 @@ class CliActionHandler {
96
97
  results.architecture = architectureResults;
97
98
  }
98
99
 
100
+ // Run impact analysis if requested
101
+ if (this.options.impact) {
102
+ const impactResults = await this.runImpactAnalysis();
103
+ results.impact = impactResults;
104
+ }
105
+
99
106
  const duration = Date.now() - startTime;
100
107
 
101
108
  // Output results
@@ -515,6 +522,22 @@ class CliActionHandler {
515
522
  */
516
523
  isArchitectureOnly() {
517
524
  return this.options.architecture &&
525
+ !this.options.impact &&
526
+ !this.options.all &&
527
+ !this.options.rule &&
528
+ !this.options.rules &&
529
+ !this.options.quality &&
530
+ !this.options.security &&
531
+ !this.options.category;
532
+ }
533
+
534
+ /**
535
+ * Check if only impact analysis was requested (no code quality rules)
536
+ * Following Rule C006: Verb-noun naming
537
+ */
538
+ isImpactOnly() {
539
+ return this.options.impact &&
540
+ !this.options.architecture &&
518
541
  !this.options.all &&
519
542
  !this.options.rule &&
520
543
  !this.options.rules &&
@@ -554,6 +577,83 @@ class CliActionHandler {
554
577
  return null;
555
578
  }
556
579
  }
580
+
581
+ /**
582
+ * Run impact analysis using impact-analyzer module
583
+ * Following Rule C006: Verb-noun naming
584
+ */
585
+ async runImpactAnalysis() {
586
+ if (!this.options.quiet) {
587
+ console.log(chalk.blue('\n🔍 Running impact analysis...'));
588
+ }
589
+
590
+ try {
591
+ const integration = new ImpactIntegration({
592
+ ...this.options,
593
+ impactBase: this.options.impactBase || 'HEAD~1',
594
+ impactHead: this.options.impactHead,
595
+ impactReport: this.options.impactReport,
596
+ impactMaxDepth: this.options.impactMaxDepth ? parseInt(this.options.impactMaxDepth, 10) : 3,
597
+ });
598
+
599
+ const projectPath = this.getProjectPath();
600
+ const results = await integration.analyze(projectPath);
601
+
602
+ // Display summary
603
+ if (!this.options.quiet && results.summary) {
604
+ const { summary } = results;
605
+ const severityColors = {
606
+ critical: chalk.red,
607
+ high: chalk.yellow,
608
+ medium: chalk.cyan,
609
+ low: chalk.green,
610
+ none: chalk.gray,
611
+ };
612
+ const colorFn = severityColors[summary.severity] || chalk.white;
613
+
614
+ console.log(chalk.gray(`\n Base: ${summary.baseRef}`));
615
+ console.log(chalk.gray(` Changes: ${summary.totalChanges} files`));
616
+ console.log(` Impact: ${colorFn(summary.impactScore + '/100')} (${colorFn(summary.severity.toUpperCase())})`);
617
+
618
+ if (summary.categories) {
619
+ const cats = Object.entries(summary.categories)
620
+ .filter(([, count]) => count > 0)
621
+ .map(([cat, count]) => `${cat}:${count}`)
622
+ .join(' · ');
623
+ if (cats) {
624
+ console.log(chalk.gray(` Categories: ${cats}`));
625
+ }
626
+ }
627
+ }
628
+
629
+ // Save markdown report if requested
630
+ if (this.options.impactReport && results.markdownReport) {
631
+ const reportPath = await integration.saveReport(
632
+ results.markdownReport,
633
+ this.options.impactReport
634
+ );
635
+ if (!this.options.quiet) {
636
+ console.log(chalk.green(`\n📄 Impact report saved: ${reportPath}`));
637
+ }
638
+ }
639
+
640
+ // Save JSON report if requested
641
+ if (this.options.impactJson) {
642
+ const jsonPath = await integration.saveJsonReport(results, this.options.impactJson);
643
+ if (!this.options.quiet) {
644
+ console.log(chalk.green(`📊 Impact JSON saved: ${jsonPath}`));
645
+ }
646
+ }
647
+
648
+ return results;
649
+ } catch (error) {
650
+ console.error(chalk.yellow(`⚠️ Impact analysis failed: ${error.message}`));
651
+ if (this.options.verbose || this.options.debug) {
652
+ console.error(error.stack);
653
+ }
654
+ return null;
655
+ }
656
+ }
557
657
  }
558
658
 
559
659
  module.exports = CliActionHandler;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * CLI Program Definition
3
- * Following Rule C005: Single responsibility - only handle CLI structure
3
+ * Defines all CLI options and help text for SunLint
4
4
  */
5
5
 
6
6
  const { Command } = require('commander');
@@ -11,169 +11,133 @@ function createCliProgram() {
11
11
 
12
12
  program
13
13
  .name('sunlint')
14
- .description('☀️ Sun Lint - Coding Standards Checker | Multi-rule Quality & Security Analysis')
14
+ .description('☀️ SunLint - Multi-language Static Analysis Tool | Code Quality & Security')
15
15
  .version(version);
16
16
 
17
- // Rule selection options
17
+ // ──────────────────────────────────────────────────────────────
18
+ // Rule Selection
19
+ // ──────────────────────────────────────────────────────────────
18
20
  program
19
21
  .option('-r, --rule <rule>', 'Run single rule (e.g., C019)')
20
- .option('--rules <rules>', 'Run multiple rules (comma-separated, e.g., C019,C006,S005)')
22
+ .option('--rules <rules>', 'Run multiple rules (comma-separated)')
21
23
  .option('-a, --all', 'Run all available rules')
22
- .option('-c, --category <category>', 'Run rules by category (quality,security,logging,naming)')
24
+ .option('-c, --category <category>', 'Run rules by category (quality, security, logging, naming)')
23
25
  .option('--quality', 'Run all code quality rules')
24
26
  .option('--security', 'Run all secure coding rules');
25
27
 
26
- // Architecture Analysis options
27
- program
28
- .option('--architecture', 'Enable architecture pattern detection (layered, modular, mvvm, viper)')
29
- .option('--arch-patterns <patterns>', 'Target specific architecture patterns (comma-separated)')
30
- .option('--arch-report', 'Generate separate architecture MD report');
31
-
32
- // TypeScript specific options (Phase 1 focus)
33
- program
34
- .option('--typescript', 'Enable TypeScript-specific analysis')
35
- .option('--typescript-engine <engine>', 'TypeScript analysis engine (eslint,heuristic,hybrid)', 'heuristic')
36
- .option('--ensure-deps', 'Ensure ESLint dependencies are installed');
37
-
38
- // Input/Output options (v1.x: explicit --input required)
28
+ // ──────────────────────────────────────────────────────────────
29
+ // Input/Output
30
+ // ──────────────────────────────────────────────────────────────
39
31
  program
40
32
  .option('-i, --input <path>', 'Input file or directory to analyze (REQUIRED)')
41
- .option('-f, --format <format>', 'Output format (eslint,json,summary,table)', 'eslint')
42
- .option('-o, --output <file>', 'Output file path')
43
- .option('--output-summary <file>', 'Output summary report file path (JSON format for CI/CD)')
44
- .option('--upload-report [url]', 'Upload summary report to API endpoint after analysis (default: Sun* Coding Standards API)')
45
- .option('--config <file>', 'Configuration file path (default: auto-discover)')
46
- .option('--github-annotate [mode]', 'Annotate GitHub PR with comments, summary & HTML report artifact (modes: annotate, summary, all)');
47
-
48
- // File targeting options
33
+ .option('-f, --format <format>', 'Output format: eslint, json, summary, table', 'eslint')
34
+ .option('-o, --output <file>', 'Write output to file')
35
+ .option('--output-summary <file>', 'Write JSON summary for CI/CD')
36
+ .option('--output-html [file]', 'Generate HTML report (default: sunlint-report.html)')
37
+ .option('--config <file>', 'Configuration file path');
38
+
39
+ // ──────────────────────────────────────────────────────────────
40
+ // File Targeting
41
+ // ──────────────────────────────────────────────────────────────
49
42
  program
50
43
  .option('--include <patterns>', 'Include file patterns (comma-separated globs)')
51
44
  .option('--exclude <patterns>', 'Exclude file patterns (comma-separated globs)')
52
- .option('--languages <languages>', 'Target specific languages (comma-separated: typescript,dart,kotlin)')
53
- .option('--include-tests', 'Include test files in analysis (default: true)')
45
+ .option('--languages <langs>', 'Target languages: typescript, dart, kotlin, swift, java')
54
46
  .option('--exclude-tests', 'Exclude test files from analysis')
55
- .option('--only-source', 'Only analyze source files (exclude tests, configs, etc.)');
47
+ .option('--only-source', 'Only analyze source files (exclude tests, configs)');
56
48
 
57
- // CI/CD and Git integration options
49
+ // ──────────────────────────────────────────────────────────────
50
+ // Git Integration
51
+ // ──────────────────────────────────────────────────────────────
58
52
  program
59
- .option('--changed-files', 'Only analyze files changed in current branch (git diff)')
53
+ .option('--changed-files', 'Only analyze changed files (git diff)')
60
54
  .option('--staged-files', 'Only analyze staged files (git diff --cached)')
61
- .option('--diff-base <ref>', 'Compare against specific git reference (e.g., origin/main)')
62
- .option('--since <commit>', 'Only analyze files changed since specific commit')
63
- .option('--pr-mode', 'Enable PR mode (changed files + baseline comparison)')
64
- .option('--baseline <file>', 'Load baseline results to compare against')
65
- .option('--save-baseline <file>', 'Save current results as baseline')
66
- .option('--fail-on-new-violations', 'Exit with error only on new violations (not existing)');
67
-
68
- // Performance options (SIMPLIFIED)
55
+ .option('--diff-base <ref>', 'Compare against git ref (e.g., origin/main)');
56
+
57
+ // ──────────────────────────────────────────────────────────────
58
+ // GitHub Actions
59
+ // ──────────────────────────────────────────────────────────────
69
60
  program
70
- .option('--timeout <milliseconds>', 'Analysis timeout in milliseconds (default: auto)', '0')
71
- .option('--max-files <count>', 'Maximum files to analyze (default: auto-detect)', '0')
72
- .option('--performance <mode>', 'Performance mode: auto, fast, careful (default: auto)', 'auto');
61
+ .option('--github-annotate [mode]', 'GitHub PR annotations (modes: annotate, summary, all)')
62
+ .option('--upload-report [url]', 'Upload report to API (default: Sun* Coding Standards)');
73
63
 
74
- // Advanced options
64
+ // ──────────────────────────────────────────────────────────────
65
+ // Analysis Options
66
+ // ──────────────────────────────────────────────────────────────
67
+ program
68
+ .option('--typescript', 'Enable TypeScript-specific analysis')
69
+ .option('--architecture', 'Enable architecture pattern detection')
70
+ .option('--arch-patterns <patterns>', 'Target architecture patterns (comma-separated)')
71
+ .option('--arch-report', 'Generate architecture markdown report')
72
+ .option('--impact', 'Enable impact analysis (analyze code changes)')
73
+ .option('--impact-base <ref>', 'Base git ref for impact analysis (default: HEAD~1)')
74
+ .option('--impact-head <ref>', 'Head git ref for impact analysis (default: working directory)')
75
+ .option('--impact-report <file>', 'Output impact report file (default: impact-report.md)')
76
+ .option('--impact-json <file>', 'Output impact analysis as JSON file')
77
+ .option('--impact-max-depth <n>', 'Maximum call graph depth for impact analysis (default: 3)')
78
+ .option('--engine <engine>', 'Analysis engine: auto, eslint, heuristic', 'auto')
79
+ .option('--eslint-integration', 'Merge with existing ESLint config')
80
+ .option('--no-eslint-integration', 'Disable ESLint integration');
81
+
82
+ // ──────────────────────────────────────────────────────────────
83
+ // Performance
84
+ // ──────────────────────────────────────────────────────────────
85
+ program
86
+ .option('--performance <mode>', 'Performance mode: auto, fast, careful', 'auto')
87
+ .option('--timeout <ms>', 'Analysis timeout in milliseconds', '0')
88
+ .option('--max-files <n>', 'Maximum files to analyze', '0')
89
+ .option('--max-semantic-files <n>', 'TypeScript symbol table limit (default: 1000)');
90
+
91
+ // ──────────────────────────────────────────────────────────────
92
+ // Output Control
93
+ // ──────────────────────────────────────────────────────────────
75
94
  program
76
- .option('--engine <engine>', 'Analysis engine (eslint,heuristic,auto)', 'auto')
77
- .option('--dry-run', 'Show what would be analyzed without running')
78
95
  .option('--verbose', 'Enable verbose logging')
79
96
  .option('--quiet', 'Suppress non-error output')
80
- .option('--debug', 'Enable debug mode')
81
97
  .option('--ai', 'Enable AI-powered analysis')
82
- .option('--no-ai', 'Force disable AI analysis')
83
- .option('--max-semantic-files <number>', 'Symbol table file limit for TypeScript analysis (default: 1000, -1 for unlimited)')
84
- .option('--list-engines', 'List available analysis engines');
85
-
86
- // ESLint Integration options
87
- program
88
- .option('--eslint-integration', 'Enable ESLint integration (merge with existing ESLint config)')
89
- .option('--no-eslint-integration', 'Disable ESLint integration')
90
- .option('--eslint-merge-rules', 'Merge SunLint and user ESLint rules (default: true)')
91
- .option('--eslint-preserve-config', 'Preserve user ESLint configuration (default: true)')
92
- .option('--eslint-run-after', 'Run ESLint after SunLint (instead of merged execution)');
98
+ .option('--no-ai', 'Disable AI analysis');
93
99
 
94
- // Help examples
100
+ // ──────────────────────────────────────────────────────────────
101
+ // Help Examples
102
+ // ──────────────────────────────────────────────────────────────
95
103
  program.addHelpText('after', `
96
104
  Examples:
97
- $ sunlint --rule=C019 --input=src
98
- $ sunlint --rule C019 --input src
99
- $ sunlint --rules=C019,C006,S005 --input=src
100
- $ sunlint --all --input=src
101
- $ sunlint --quality --input=src
102
- $ sunlint --security --input=src
103
- $ sunlint --category=logging --input=src
104
-
105
- File Targeting:
106
- $ sunlint --all --include="src/**/*.ts" --exclude="**/*.test.*" --input=.
107
-
108
- Performance (SIMPLIFIED):
109
- $ sunlint --all --input=src --performance=auto # Auto-detect best settings
110
- $ sunlint --all --input=src --performance=fast # Quick scan
111
- $ sunlint --all --input=src --performance=careful # Thorough analysis
112
- $ sunlint --all --input=src --timeout=60000 # Custom timeout (60s)
113
-
114
- File Limits (when needed):
115
- $ sunlint --all --input=src --max-files=500 # Limit total files analyzed
116
- $ sunlint --all --input=src --max-semantic-files=200 # Limit TypeScript symbol table
117
- $ sunlint --all --languages=typescript,dart --input=src
118
- $ sunlint --typescript --exclude-tests --input=src
119
- $ sunlint --all --only-source --include="src/**,lib/**" --input=.
120
-
121
- TypeScript Analysis (Phase 1):
122
- $ sunlint --typescript --input=src
123
- $ sunlint --rule=C006 --typescript --input=src
124
- $ sunlint --rules=C019,S005 --typescript --input=src
125
- $ sunlint --typescript-engine=eslint --input=src
126
-
127
- Version Strategy:
128
- v1.x: ESLint-first with SunLint fallback (current)
129
- v2.x: SunLint-first with ESLint integration (--eslint-integration)
130
-
131
- Engine Configuration:
132
- $ sunlint --all --input=src # Use config engine setting
133
- $ sunlint --all --input=src --engine=eslint # Force ESLint engine
134
- $ sunlint --all --input=src --engine=heuristic # Force Heuristic engine
135
-
136
- CI/CD Integration:
137
- $ sunlint --all --changed-files --format=summary --no-ai
138
- $ sunlint --all --changed-files --diff-base=origin/main --fail-on-new-violations
139
- $ sunlint --all --staged-files --format=summary
140
- $ sunlint --all --pr-mode --diff-base=origin/main
141
- $ sunlint --all --output-summary=report.json --upload-report
142
- $ sunlint --all --output-summary=report.json --upload-report=https://custom-api.com/reports
143
-
144
- GitHub Actions Integration:
145
- $ sunlint --all --input=src --github-annotate # Inline + summary + HTML artifact
146
- $ sunlint --all --input=src --github-annotate=annotate # Inline comments only
147
- $ sunlint --all --input=src --github-annotate=summary # Summary comment + HTML artifact
148
- $ sunlint --all --input=src --github-annotate=all # All features (default)
149
- $ sunlint --all --changed-files --github-annotate # With changed files only
150
-
151
- ESLint Integration:
152
- $ sunlint --typescript --eslint-integration --input=src
153
- $ sunlint --all --eslint-integration --eslint-merge-rules --input=src
154
- $ sunlint --all --eslint-integration --eslint-run-after --input=src
155
- $ sunlint --typescript --eslint-integration --changed-files
156
-
157
- Advanced File Targeting:
158
- $ sunlint --all --include="src/**/*.ts,lib/**/*.dart" --exclude="**/*.generated.*" --input=.
159
- $ sunlint --typescript --exclude="**/*.d.ts,**/*.test.*" --input=src
160
- $ sunlint --languages=typescript,dart --include="src/**,packages/**" --input=.
161
- $ sunlint --all --only-source --exclude-tests --languages=typescript --input=.
162
-
163
- Large Project Optimization:
164
- $ sunlint --all --input=. --max-semantic-files=500 # Conservative analysis
165
- $ sunlint --all --input=. --max-semantic-files=2000 # Comprehensive analysis
166
- $ sunlint --all --input=. --max-semantic-files=-1 # Unlimited (all files)
167
- $ sunlint --all --input=. --max-semantic-files=0 # Disable semantic analysis
168
- $ sunlint --all --changed-files --max-semantic-files=300 # Fast CI analysis
169
-
170
- Architecture Analysis:
171
- $ sunlint --all --architecture --input=src # Code quality + architecture
172
- $ sunlint --architecture --input=src # Architecture only
173
- $ sunlint --architecture --arch-report --input=src # Generate MD report
174
- $ sunlint --architecture --arch-patterns=mvvm,layered --input=src
175
-
176
- Sun* Engineering - Coding Standards Made Simple ☀️
105
+ sunlint --all --input=src # Analyze all rules
106
+ sunlint --rule=C019 --input=src # Single rule
107
+ sunlint --rules=C019,C006 --input=src # Multiple rules
108
+ sunlint --quality --input=src # Quality rules only
109
+ sunlint --security --input=src # Security rules only
110
+
111
+ Git Integration:
112
+ sunlint --all --changed-files --input=. # Changed files only
113
+ sunlint --all --staged-files --input=. # Staged files only
114
+ sunlint --all --diff-base=origin/main # Compare with main
115
+
116
+ GitHub Actions:
117
+ sunlint --all --github-annotate --input=src # PR annotations
118
+ sunlint --all --output-summary=report.json # JSON report for CI
119
+
120
+ Reports:
121
+ sunlint --all --output-html --input=src # Generate HTML report
122
+ sunlint --all --output-html=report.html # Custom HTML filename
123
+
124
+ Architecture:
125
+ sunlint --architecture --input=src # Detect patterns
126
+ sunlint --architecture --arch-report # Generate MD report
127
+
128
+ Impact Analysis:
129
+ sunlint --impact --input=src # Analyze code changes
130
+ sunlint --impact --impact-base=origin/main # Compare with main
131
+ sunlint --impact --impact-head=HEAD # Specify head ref
132
+ sunlint --impact --impact-report=report.md # Markdown report
133
+ sunlint --impact --impact-json=report.json # JSON report
134
+ sunlint --impact --impact-max-depth=5 # Call graph depth
135
+
136
+ Performance:
137
+ sunlint --all --performance=fast --input=. # Quick scan
138
+ sunlint --all --max-files=500 --input=. # Limit files
139
+
140
+ ☀️ Sun* Engineering - Coding Standards Made Simple
177
141
  `);
178
142
 
179
143
  return program;
@@ -616,22 +616,80 @@ class FileTargetingService {
616
616
  return files;
617
617
  }
618
618
 
619
+ /**
620
+ * Directories to always skip during file collection (performance + safety)
621
+ * These are known heavy/problematic directories in Flutter/mobile projects
622
+ */
623
+ static SKIP_DIRECTORIES = new Set([
624
+ 'node_modules',
625
+ '.git',
626
+ '.dart_tool',
627
+ 'build',
628
+ 'Pods',
629
+ '.symlinks',
630
+ '.pub-cache',
631
+ '.gradle',
632
+ 'DerivedData',
633
+ '__pycache__',
634
+ '.venv',
635
+ 'vendor',
636
+ '.bundle'
637
+ ]);
638
+
619
639
  /**
620
640
  * Collect files from directory recursively
621
641
  * Rule C006: collectFilesFromDirectory - verb-noun naming
642
+ * Enhanced: Skip symlinks and known heavy directories to prevent stack overflow
622
643
  */
623
- async collectFilesFromDirectory(dirPath) {
644
+ async collectFilesFromDirectory(dirPath, visitedPaths = new Set()) {
624
645
  const files = [];
625
- const entries = fs.readdirSync(dirPath);
646
+
647
+ // Resolve real path to detect circular symlinks
648
+ let realPath;
649
+ try {
650
+ realPath = fs.realpathSync(dirPath);
651
+ } catch {
652
+ return files; // Cannot resolve path, skip
653
+ }
654
+
655
+ // Prevent infinite loops from circular symlinks
656
+ if (visitedPaths.has(realPath)) {
657
+ return files;
658
+ }
659
+ visitedPaths.add(realPath);
660
+
661
+ let entries;
662
+ try {
663
+ entries = fs.readdirSync(dirPath);
664
+ } catch {
665
+ return files; // Cannot read directory, skip
666
+ }
626
667
 
627
668
  for (const entry of entries) {
669
+ // Skip known heavy directories early (before stat call)
670
+ if (FileTargetingService.SKIP_DIRECTORIES.has(entry)) {
671
+ continue;
672
+ }
673
+
628
674
  const fullPath = path.join(dirPath, entry);
629
- const stats = fs.statSync(fullPath);
675
+
676
+ let stats;
677
+ try {
678
+ // Use lstatSync to detect symlinks without following them
679
+ stats = fs.lstatSync(fullPath);
680
+ } catch {
681
+ continue; // Cannot stat, skip this entry
682
+ }
683
+
684
+ // Skip symlinks to prevent circular references
685
+ if (stats.isSymbolicLink()) {
686
+ continue;
687
+ }
630
688
 
631
689
  if (stats.isFile()) {
632
690
  files.push(path.resolve(fullPath));
633
691
  } else if (stats.isDirectory()) {
634
- const subFiles = await this.collectFilesFromDirectory(fullPath);
692
+ const subFiles = await this.collectFilesFromDirectory(fullPath, visitedPaths);
635
693
  files.push(...subFiles);
636
694
  }
637
695
  }