From eb915c3f7cba250db80f25ff77297f1aa89f67f6 Mon Sep 17 00:00:00 2001 From: "Jakub Mazurek (@jayu)" Date: Fri, 17 Jul 2026 13:54:52 +0000 Subject: [PATCH 1/2] refactor: centralise emojis --- internal/checks/circular.go | 4 +- internal/cli/config_init.go | 23 ++++++------ internal/cli/config_lint.go | 42 +++++++++------------ internal/cli/config_run.go | 75 +++++++++++++++++++------------------ internal/cli/prompt.go | 6 ++- internal/diag/diag.go | 8 +++- internal/emoji/emoji.go | 26 +++++++++++++ 7 files changed, 107 insertions(+), 77 deletions(-) create mode 100644 internal/emoji/emoji.go diff --git a/internal/checks/circular.go b/internal/checks/circular.go index dbaa74d..f7f3f50 100644 --- a/internal/checks/circular.go +++ b/internal/checks/circular.go @@ -4,6 +4,8 @@ import ( "fmt" "sort" "strings" + + "rev-dep-go/internal/emoji" ) // findCircularDependencies detects circular dependencies in the dependency tree @@ -234,7 +236,7 @@ func findDeterministicCycle(start string, adj map[string][]string, inSCC map[str func formatCircularDependencies(cycles [][]string, pathPrefix string, deps MinimalDependencyTree, includeHeader bool, baseIndentation int) string { if len(cycles) == 0 { if includeHeader { - return fmt.Sprintln("āœ… No circular dependencies found! ") + return fmt.Sprintln(emoji.Success + " No circular dependencies found! ") } return "" } diff --git a/internal/cli/config_init.go b/internal/cli/config_init.go index 8c3d8b9..2385af7 100644 --- a/internal/cli/config_init.go +++ b/internal/cli/config_init.go @@ -11,6 +11,7 @@ import ( "github.com/spf13/cobra" "rev-dep-go/internal/config" + "rev-dep-go/internal/emoji" globutil "rev-dep-go/internal/glob" "rev-dep-go/internal/monorepo" "rev-dep-go/internal/pathutil" @@ -774,33 +775,33 @@ func packageDirsToSortedRelPaths(cwd string, packageDirs []string) []string { // printInitConfigResults prints the results of config initialization func printInitConfigResults(result *initConfigResult) { - fmt.Printf("āœ… Created .rev-dep.config.jsonc at %s\n", result.configPath) + fmt.Printf("%s Created .rev-dep.config.jsonc at %s\n", emoji.Success, result.configPath) fmt.Println() switch { case result.createdForMonorepoSubPackage: - fmt.Printf("āš ļø Created config for monorepo sub-package. This file targets the current package only.\n") + fmt.Printf("%s Created config for monorepo sub-package. This file targets the current package only.\n", emoji.Warning) case result.isMonorepo: if len(result.workspacePackagePaths) > 0 { - fmt.Printf("šŸ“¦ Monorepo detected: discovered %d workspace %s and created a rule for each:\n", len(result.workspacePackagePaths), packagesWord(len(result.workspacePackagePaths))) + fmt.Printf("%s Monorepo detected: discovered %d workspace %s and created a rule for each:\n", emoji.Package, len(result.workspacePackagePaths), packagesWord(len(result.workspacePackagePaths))) for _, relPath := range result.workspacePackagePaths { fmt.Printf(" - %s\n", relPath) } } else { - fmt.Printf("šŸ“¦ Monorepo detected: no workspace packages found.\n") + fmt.Printf("%s Monorepo detected: no workspace packages found.\n", emoji.Package) } case result.rootHasPackageJson: - fmt.Printf("šŸ“ Created a rule for the root package.\n") + fmt.Printf("%s Created a rule for the root package.\n", emoji.Rule) case len(result.standalonePackagePaths) == 0: - fmt.Printf("šŸ“ No package.json found; created a single rule for the root directory.\n") + fmt.Printf("%s No package.json found; created a single rule for the root directory.\n", emoji.Rule) default: - fmt.Printf("šŸ“ No root package.json found; created rules for standalone packages only.\n") + fmt.Printf("%s No root package.json found; created rules for standalone packages only.\n", emoji.Rule) } // Separate section for standalone packages discovered in subdirectories. if len(result.standalonePackagePaths) > 0 { fmt.Println() - fmt.Printf("🧩 Discovered %d standalone %s in subdirectories (not part of a monorepo) and created a rule for each:\n", len(result.standalonePackagePaths), packagesWord(len(result.standalonePackagePaths))) + fmt.Printf("%s Discovered %d standalone %s in subdirectories (not part of a monorepo) and created a rule for each:\n", emoji.Standalone, len(result.standalonePackagePaths), packagesWord(len(result.standalonePackagePaths))) for _, relPath := range result.standalonePackagePaths { fmt.Printf(" - %s\n", relPath) } @@ -808,7 +809,7 @@ func printInitConfigResults(result *initConfigResult) { if result.entryPointsDetected { fmt.Println() - fmt.Printf("šŸ”Ž Auto-detected entry points for %d %s (production/development classified by path).\n", result.entryPointPackageCount, packagesWord(result.entryPointPackageCount)) + fmt.Printf("%s Auto-detected entry points for %d %s (production/development classified by path).\n", emoji.Search, result.entryPointPackageCount, packagesWord(result.entryPointPackageCount)) } fmt.Println() @@ -820,6 +821,6 @@ func printInitConfigResults(result *initConfigResult) { } fmt.Println() - fmt.Printf("šŸ“– Integration guide: %s\n", integrationGuide) - fmt.Printf("šŸ›Ÿ Troubleshooting: https://rev-dep.com/troubleshooting\n\n") + fmt.Printf("%s Integration guide: %s\n", emoji.Guide, integrationGuide) + fmt.Printf("%s Troubleshooting: https://rev-dep.com/troubleshooting\n\n", emoji.Troubleshooting) } diff --git a/internal/cli/config_lint.go b/internal/cli/config_lint.go index f17a767..68db99b 100644 --- a/internal/cli/config_lint.go +++ b/internal/cli/config_lint.go @@ -11,6 +11,7 @@ import ( "github.com/spf13/cobra" "rev-dep-go/internal/config" + "rev-dep-go/internal/emoji" "rev-dep-go/internal/pathutil" ) @@ -21,13 +22,6 @@ var ( lintConfigRules []string ) -// errorMark / warnMark are the finding bullets: colorful emoji (red āŒ, yellow āš ļø) -// consistent with the āœ…/āœļø markers used elsewhere in the output. -const ( - errorMark = "āŒ" - warnMark = "āš ļø" -) - var configLintCmd = &cobra.Command{ Use: "lint", Short: "Report (and optionally remove) config glob/path patterns that match nothing", @@ -75,7 +69,7 @@ them by hand.`, errorsRemaining, warnings := countLintFindings(result, lintConfigFix) printConfigLintStatus(errorsRemaining, warnings, lintConfigFix) - fmt.Printf("✨ Done in %dms.\n", time.Since(startTime).Milliseconds()) + fmt.Printf("%s Done in %dms.\n", emoji.Done, time.Since(startTime).Milliseconds()) if errorsRemaining > 0 { os.Exit(1) @@ -114,13 +108,13 @@ func printConfigLintStatus(errors, warnings int, fixed bool) { case errors == 0 && warnings == 0: // "all clean" already printed by the results section case errors == 0: - fmt.Printf("\nāš ļø %d warning(s), no errors — exit 0.\n", warnings) + fmt.Printf("\n%s %d warning(s), no errors — exit 0.\n", emoji.Warning, warnings) default: verb := "found" if fixed { verb = "remaining after --fix" } - fmt.Printf("\nāŒ %d error(s) %s, %d warning(s).\n", errors, verb, warnings) + fmt.Printf("\n%s %d error(s) %s, %d warning(s).\n", emoji.Error, errors, verb, warnings) } } @@ -144,7 +138,7 @@ func printConfigLintResults(result *config.LintResult, cwd string) { for i, r := range result.RulesRun { ruleNames[i] = string(r) } - fmt.Printf("šŸ” Config lint: %s [rules: %s]\n", configRel, strings.Join(ruleNames, ", ")) + fmt.Printf("%s Config lint: %s [rules: %s]\n", emoji.Search, configRel, strings.Join(ruleNames, ", ")) var errorDeads, warningDeads []config.DeadPattern for _, dp := range result.DeadPatterns { @@ -156,7 +150,7 @@ func printConfigLintResults(result *config.LintResult, cwd string) { } if len(errorDeads) == 0 && len(warningDeads) == 0 && len(result.Overlaps) == 0 && result.TrailingCommaCount == 0 && result.CompactableCount == 0 { - fmt.Printf("\nāœ… No issues found — every glob matches something, no patterns overlap, config is compact.\n") + fmt.Printf("\n%s No issues found — every glob matches something, no patterns overlap, config is compact.\n", emoji.Success) return } @@ -184,9 +178,9 @@ func (p *ruleHeaderPrinter) print(ruleIndex int, rulePath string) bool { return false } if ruleIndex < 0 { - fmt.Printf("\nšŸ“„ Top-level\n") + fmt.Printf("\n%s Top-level\n", emoji.File) } else { - fmt.Printf("\nšŸ“ Rule: %s\n", rulePath) + fmt.Printf("\n%s Rule: %s\n", emoji.Rule, rulePath) } p.ruleIndex, p.rulePath, p.first = ruleIndex, rulePath, false return true @@ -210,7 +204,7 @@ func printErrorSection(deads []config.DeadPattern) { if !dp.Removable { suffix += " [not auto-removed]" } - fmt.Printf(" %s %q%s\n", errorMark, dp.Value, suffix) + fmt.Printf(" %s %q%s\n", emoji.Error, dp.Value, suffix) } } @@ -234,12 +228,12 @@ func printWarningSection(warningDeads []config.DeadPattern, overlaps []config.Ov fmt.Printf("\n── Warnings ──\n") if trailingCommas > 0 || compactable > 0 { - fmt.Printf("\nšŸ“„ File\n") + fmt.Printf("\n%s File\n", emoji.File) if compactable > 0 { - fmt.Printf(" %s %d detector declaration(s) can be written more compactly — run --fix to simplify\n", warnMark, compactable) + fmt.Printf(" %s %d detector declaration(s) can be written more compactly — run --fix to simplify\n", emoji.Warning, compactable) } if trailingCommas > 0 { - fmt.Printf(" %s %d redundant trailing comma(s) — run --fix to remove\n", warnMark, trailingCommas) + fmt.Printf(" %s %d redundant trailing comma(s) — run --fix to remove\n", emoji.Warning, trailingCommas) } } @@ -306,7 +300,7 @@ func printWarningSection(warningDeads []config.DeadPattern, overlaps []config.Ov fmt.Printf(" %s\n", label) lastLabel = label } - fmt.Printf(" %s %s\n", warnMark, l.text) + fmt.Printf(" %s %s\n", emoji.Warning, l.text) } } @@ -325,19 +319,19 @@ func kindSuffix(kind config.PatternKind) string { func printConfigLintFixSummary(fix *config.FixResult) { if fix.RemovedCount > 0 { - fmt.Printf("\nāœļø Removed %d dead pattern(s).\n", fix.RemovedCount) + fmt.Printf("\n%s Removed %d dead pattern(s).\n", emoji.Fix, fix.RemovedCount) } if fix.CompactedCount > 0 { - fmt.Printf("āœļø Simplified %d detector declaration(s) to compact form.\n", fix.CompactedCount) + fmt.Printf("%s Simplified %d detector declaration(s) to compact form.\n", emoji.Fix, fix.CompactedCount) } if fix.TrailingCommasRemoved > 0 { - fmt.Printf("āœļø Removed %d redundant trailing comma(s).\n", fix.TrailingCommasRemoved) + fmt.Printf("%s Removed %d redundant trailing comma(s).\n", emoji.Fix, fix.TrailingCommasRemoved) } if fix.ReportOnlyKept > 0 { - fmt.Printf("āš ļø %d dead pattern(s) not auto-removed (removing them could change a check's behavior or make the config invalid) — review and remove manually.\n", fix.ReportOnlyKept) + fmt.Printf("%s %d dead pattern(s) not auto-removed (removing them could change a check's behavior or make the config invalid) — review and remove manually.\n", emoji.Warning, fix.ReportOnlyKept) } if fix.RemovedCount == 0 && fix.ReportOnlyKept == 0 && fix.TrailingCommasRemoved == 0 && fix.CompactedCount == 0 { - fmt.Printf("\nāœ… Nothing to remove.\n") + fmt.Printf("\n%s Nothing to remove.\n", emoji.Success) } } diff --git a/internal/cli/config_run.go b/internal/cli/config_run.go index 6947713..041a71d 100644 --- a/internal/cli/config_run.go +++ b/internal/cli/config_run.go @@ -12,6 +12,7 @@ import ( "rev-dep-go/internal/checks" "rev-dep-go/internal/config" + "rev-dep-go/internal/emoji" "rev-dep-go/internal/node" "rev-dep-go/internal/pathutil" "rev-dep-go/internal/telemetry" @@ -101,7 +102,7 @@ var configRunCmd = &cobra.Command{ } executionTime := time.Since(startTime) - fmt.Printf("\n✨ Done in %dms.\n", executionTime.Milliseconds()) + fmt.Printf("\n%s Done in %dms.\n", emoji.Done, executionTime.Milliseconds()) if shouldConfigRunExitNonZero(result, runConfigFix) || lintHasErrors { os.Exit(1) @@ -155,11 +156,11 @@ func runConfigLintSummary(cwd string, runResult *config.ConfigProcessingResult, errors, warnings := countLintFindings(lintResult, false) switch { case errors > 0: - fmt.Printf("\n%s Config lint: %d error(s), %d warning(s) — run `rev-dep config lint` for details (or --fix to apply).\n", errorMark, errors, warnings) + fmt.Printf("\n%s Config lint: %d error(s), %d warning(s) — run `rev-dep config lint` for details (or --fix to apply).\n", emoji.Error, errors, warnings) case warnings > 0: - fmt.Printf("\n%s Config lint: 0 errors, %d warning(s) — run `rev-dep config lint` for details (or --fix to apply).\n", warnMark, warnings) + fmt.Printf("\n%s Config lint: 0 errors, %d warning(s) — run `rev-dep config lint` for details (or --fix to apply).\n", emoji.Warning, warnings) default: - fmt.Printf("\nāœ… Config lint: no issues.\n") + fmt.Printf("\n%s Config lint: no issues.\n", emoji.Success) } return errors > 0, nil } @@ -267,7 +268,7 @@ func formatAndPrintConfigResults(result *config.ConfigProcessingResult, cwd stri shouldWarnAboutImportConventionWithPJsonImports = shouldWarnAboutImportConventionWithPJsonImports || ruleResult.ShouldWarnAboutImportConventionWithPJsonImports if ruleResult.RulePath != "" { - fmt.Printf("\nšŸ“ Rule: %s (%d files)\n", ruleResult.RulePath, ruleResult.FileCount) + fmt.Printf("\n%s Rule: %s (%d files)\n", emoji.Rule, ruleResult.RulePath, ruleResult.FileCount) } // Show enabled checks and their status with indentation @@ -275,7 +276,7 @@ func formatAndPrintConfigResults(result *config.ConfigProcessingResult, cwd stri switch check { case "circular-imports": if len(ruleResult.CircularDependencies) > 0 { - fmt.Printf(" āŒ Circular Dependencies Issues (%d):\n\n", len(ruleResult.CircularDependencies)) + fmt.Printf(" %s Circular Dependencies Issues (%d):\n\n", emoji.Error, len(ruleResult.CircularDependencies)) circularDepsToDisplay := ruleResult.CircularDependencies remaining := 0 @@ -291,11 +292,11 @@ func formatAndPrintConfigResults(result *config.ConfigProcessingResult, cwd stri fmt.Printf(" ... and %d more circular dependency issues\n", remaining) } } else { - fmt.Printf(" āœ… Circular Dependencies\n") + fmt.Printf(" %s Circular Dependencies\n", emoji.Success) } case "orphan-files": if len(ruleResult.OrphanFiles) > 0 { - fmt.Printf(" āŒ Orphan Files Issues (%d):\n", len(ruleResult.OrphanFiles)) + fmt.Printf(" %s Orphan Files Issues (%d):\n", emoji.Error, len(ruleResult.OrphanFiles)) orphanFilesToDisplay := ruleResult.OrphanFiles remaining := 0 @@ -312,11 +313,11 @@ func formatAndPrintConfigResults(result *config.ConfigProcessingResult, cwd stri fmt.Printf(" ... and %d more orphan file issues\n", remaining) } } else { - fmt.Printf(" āœ… Orphan Files\n") + fmt.Printf(" %s Orphan Files\n", emoji.Success) } case "module-boundaries": if len(ruleResult.ModuleBoundaryViolations) > 0 { - fmt.Printf(" āŒ Module Boundary Issues (%d):\n", len(ruleResult.ModuleBoundaryViolations)) + fmt.Printf(" %s Module Boundary Issues (%d):\n", emoji.Error, len(ruleResult.ModuleBoundaryViolations)) violationsToDisplay := ruleResult.ModuleBoundaryViolations remaining := 0 @@ -341,11 +342,11 @@ func formatAndPrintConfigResults(result *config.ConfigProcessingResult, cwd stri fmt.Printf(" ... and %d more module boundary issues\n", remaining) } } else { - fmt.Printf(" āœ… Module Boundaries\n") + fmt.Printf(" %s Module Boundaries\n", emoji.Success) } case "unused-node-modules": if len(ruleResult.UnusedNodeModules) > 0 { - fmt.Printf(" āŒ Unused Node Modules Issues (%d):\n", len(ruleResult.UnusedNodeModules)) + fmt.Printf(" %s Unused Node Modules Issues (%d):\n", emoji.Error, len(ruleResult.UnusedNodeModules)) modulesToDisplay := ruleResult.UnusedNodeModules remaining := 0 @@ -367,11 +368,11 @@ func formatAndPrintConfigResults(result *config.ConfigProcessingResult, cwd stri fmt.Printf(" ... and %d more unused node module issues\n", remaining) } } else { - fmt.Printf(" āœ… Unused Node Modules\n") + fmt.Printf(" %s Unused Node Modules\n", emoji.Success) } case "missing-node-modules": if len(ruleResult.MissingNodeModules) > 0 { - fmt.Printf(" āŒ Missing Node Modules Issues (%d):\n", len(ruleResult.MissingNodeModules)) + fmt.Printf(" %s Missing Node Modules Issues (%d):\n", emoji.Error, len(ruleResult.MissingNodeModules)) missingToDisplay := ruleResult.MissingNodeModules remaining := 0 @@ -417,11 +418,11 @@ func formatAndPrintConfigResults(result *config.ConfigProcessingResult, cwd stri fmt.Printf(" ... and %d more missing node module issues\n", remaining) } } else { - fmt.Printf(" āœ… Missing Node Modules\n") + fmt.Printf(" %s Missing Node Modules\n", emoji.Success) } case "import-conventions": if len(ruleResult.ImportConventionViolations) > 0 { - fmt.Printf(" āŒ Import Convention Issues (%d):\n", len(ruleResult.ImportConventionViolations)) + fmt.Printf(" %s Import Convention Issues (%d):\n", emoji.Error, len(ruleResult.ImportConventionViolations)) violationsToDisplay := ruleResult.ImportConventionViolations @@ -465,11 +466,11 @@ func formatAndPrintConfigResults(result *config.ConfigProcessingResult, cwd stri fmt.Printf(" ... and %d more import convention issues\n", remaining) } } else { - fmt.Printf(" āœ… Import Conventions\n") + fmt.Printf(" %s Import Conventions\n", emoji.Success) } case "unresolved-imports": if len(ruleResult.UnresolvedImports) > 0 { - fmt.Printf(" āŒ Unresolved Imports (%d):\n", len(ruleResult.UnresolvedImports)) + fmt.Printf(" %s Unresolved Imports (%d):\n", emoji.Error, len(ruleResult.UnresolvedImports)) // Sort all results before limiting unresolvedToDisplay := ruleResult.UnresolvedImports @@ -509,11 +510,11 @@ func formatAndPrintConfigResults(result *config.ConfigProcessingResult, cwd stri fmt.Printf(" ... and %d more unresolved import issues\n", remaining) } } else { - fmt.Printf(" āœ… Unresolved Imports\n") + fmt.Printf(" %s Unresolved Imports\n", emoji.Success) } case "unused-exports": if len(ruleResult.UnusedExports) > 0 { - fmt.Printf(" āŒ Unused Exports Issues (%d):\n", len(ruleResult.UnusedExports)) + fmt.Printf(" %s Unused Exports Issues (%d):\n", emoji.Error, len(ruleResult.UnusedExports)) exportsToDisplay := ruleResult.UnusedExports @@ -558,11 +559,11 @@ func formatAndPrintConfigResults(result *config.ConfigProcessingResult, cwd stri fmt.Printf(" ... and %d more unused export issues\n", remaining) } } else { - fmt.Printf(" āœ… Unused Exports\n") + fmt.Printf(" %s Unused Exports\n", emoji.Success) } case "dev-deps-usage-on-prod": if len(ruleResult.RestrictedDevDependenciesUsageViolations) > 0 { - fmt.Printf(" āŒ Dev Deps Usage On Prod Issues (%d):\n", len(ruleResult.RestrictedDevDependenciesUsageViolations)) + fmt.Printf(" %s Dev Deps Usage On Prod Issues (%d):\n", emoji.Error, len(ruleResult.RestrictedDevDependenciesUsageViolations)) violationsToDisplay := ruleResult.RestrictedDevDependenciesUsageViolations remaining := 0 @@ -596,11 +597,11 @@ func formatAndPrintConfigResults(result *config.ConfigProcessingResult, cwd stri fmt.Printf(" ... and %d more Dev Deps Usage On Prod Issues\n", remaining) } } else { - fmt.Printf(" āœ… Dev Deps Usage On Prod\n") + fmt.Printf(" %s Dev Deps Usage On Prod\n", emoji.Success) } case "restricted-imports": if len(ruleResult.RestrictedImportsViolations) > 0 { - fmt.Printf(" āŒ Restricted Imports Issues (%d):\n", len(ruleResult.RestrictedImportsViolations)) + fmt.Printf(" %s Restricted Imports Issues (%d):\n", emoji.Error, len(ruleResult.RestrictedImportsViolations)) violationsToDisplay := ruleResult.RestrictedImportsViolations slices.SortFunc(violationsToDisplay, func(a, b checks.RestrictedImportViolation) int { @@ -685,11 +686,11 @@ func formatAndPrintConfigResults(result *config.ConfigProcessingResult, cwd stri printRestrictedImportsResolveHint(ruleResult, cwd) } else { - fmt.Printf(" āœ… Restricted Imports\n") + fmt.Printf(" %s Restricted Imports\n", emoji.Success) } case "restricted-importers": if len(ruleResult.RestrictedImportersViolations) > 0 { - fmt.Printf(" āŒ Restricted Importers Issues (%d):\n", len(ruleResult.RestrictedImportersViolations)) + fmt.Printf(" %s Restricted Importers Issues (%d):\n", emoji.Error, len(ruleResult.RestrictedImportersViolations)) violationsToDisplay := ruleResult.RestrictedImportersViolations remaining := 0 @@ -736,11 +737,11 @@ func formatAndPrintConfigResults(result *config.ConfigProcessingResult, cwd stri printRestrictedImportersResolveHint(ruleResult, cwd) } else { - fmt.Printf(" āœ… Restricted Importers\n") + fmt.Printf(" %s Restricted Importers\n", emoji.Success) } case "restricted-direct-importers": if len(ruleResult.RestrictedDirectImportersViolations) > 0 { - fmt.Printf(" āŒ Restricted Direct Importers Issues (%d):\n", len(ruleResult.RestrictedDirectImportersViolations)) + fmt.Printf(" %s Restricted Direct Importers Issues (%d):\n", emoji.Error, len(ruleResult.RestrictedDirectImportersViolations)) violationsToDisplay := ruleResult.RestrictedDirectImportersViolations remaining := 0 @@ -786,28 +787,28 @@ func formatAndPrintConfigResults(result *config.ConfigProcessingResult, cwd stri fmt.Printf(" ... and %d more restricted direct importer issues\n", remaining) } } else { - fmt.Printf(" āœ… Restricted Direct Importers\n") + fmt.Printf(" %s Restricted Direct Importers\n", emoji.Success) } } } // Show warning if no files found for this rule if ruleResult.FileCount == 0 { - fmt.Printf(" āš ļø No files found for this rule - check if the path is correct\n") + fmt.Printf(" %s No files found for this rule - check if the path is correct\n", emoji.Warning) } // Show warning if package.json is missing in the rule path directory if ruleResult.MissingPackageJson { packageJsonPath := filepath.Join(cwd, ruleResult.RulePath, "package.json") - fmt.Printf(" āš ļø Warning: Rule path missing package.json - some features may not work (missing: %s)\n", packageJsonPath) + fmt.Printf(" %s Warning: Rule path missing package.json - some features may not work (missing: %s)\n", emoji.Warning, packageJsonPath) } } // Print final verdict if !result.HasFailures { - fmt.Printf("\nāœ… All checks passed!\n") + fmt.Printf("\n%s All checks passed!\n", emoji.Success) } else { - fmt.Printf("\nāŒ Checks failed! See details above.\n") + fmt.Printf("\n%s Checks failed! See details above.\n", emoji.Error) } // Print autofix summary if any fixes were applied or unfixable issues found @@ -823,19 +824,19 @@ func formatAndPrintConfigResults(result *config.ConfigProcessingResult, cwd stri if len(summary) > 0 { // Capitalize first letter of first summary part summary[0] = strings.ToUpper(summary[0][:1]) + summary[0][1:] - fmt.Printf("āœļø %s\n", strings.Join(summary, ", ")) + fmt.Printf("%s %s\n", emoji.Fix, strings.Join(summary, ", ")) } } if result.FixableIssuesCount > 0 { - fmt.Printf("šŸ’” Fixable issues: %d. Use '--fix' flag to autofix.\n", result.FixableIssuesCount) + fmt.Printf("%s Fixable issues: %d. Use '--fix' flag to autofix.\n", emoji.Tip, result.FixableIssuesCount) } if result.UnfixableAliasingCount > 0 { - fmt.Printf("āš ļø Warning: %d inter-domain relative imports could not be automatically fixed because target domains lack aliases or are not defined in config.\n", result.UnfixableAliasingCount) + fmt.Printf("%s Warning: %d inter-domain relative imports could not be automatically fixed because target domains lack aliases or are not defined in config.\n", emoji.Warning, result.UnfixableAliasingCount) } if shouldWarnAboutImportConventionWithPJsonImports { - fmt.Println("āš ļø Warning: Support for package.json imports map aliases is not yet implemented for import conventions checks") + fmt.Println(emoji.Warning + " Warning: Support for package.json imports map aliases is not yet implemented for import conventions checks") } } diff --git a/internal/cli/prompt.go b/internal/cli/prompt.go index cbcd60a..a065689 100644 --- a/internal/cli/prompt.go +++ b/internal/cli/prompt.go @@ -10,6 +10,8 @@ import ( "unicode/utf8" "golang.org/x/term" + + "rev-dep-go/internal/emoji" ) // Interactive terminal prompts. These are generic CLI helpers, independent of any particular @@ -193,13 +195,13 @@ func selectOne(in io.Reader, out io.Writer, prompt string, options []string, def fmt.Fprintln(out) return defaultIndex, options[defaultIndex], nil } - fmt.Fprintf(out, " āš ļø please choose a number between 1 and %d.\n\n", len(options)) + fmt.Fprintf(out, " %s please choose a number between 1 and %d.\n\n", emoji.Warning, len(options)) continue } n, convErr := strconv.Atoi(line) if convErr != nil || n < 1 || n > len(options) { - fmt.Fprintf(out, " āš ļø %q is not a valid choice (1-%d).\n\n", line, len(options)) + fmt.Fprintf(out, " %s %q is not a valid choice (1-%d).\n\n", emoji.Warning, line, len(options)) continue } fmt.Fprintln(out) diff --git a/internal/diag/diag.go b/internal/diag/diag.go index aaae637..6f3f1af 100644 --- a/internal/diag/diag.go +++ b/internal/diag/diag.go @@ -1,6 +1,10 @@ package diag -import "fmt" +import ( + "fmt" + + "rev-dep-go/internal/emoji" +) var verbose bool @@ -10,6 +14,6 @@ func SetVerbose(v bool) { func Warnf(format string, args ...interface{}) { if verbose { - fmt.Printf("āš ļø Warning: "+format+"\n", args...) + fmt.Printf(emoji.Warning+" Warning: "+format+"\n", args...) } } diff --git a/internal/emoji/emoji.go b/internal/emoji/emoji.go new file mode 100644 index 0000000..6f44490 --- /dev/null +++ b/internal/emoji/emoji.go @@ -0,0 +1,26 @@ +// Package emoji centralizes the emoji glyphs used in rev-dep's CLI output so they stay +// consistent and can be changed in one place. +// +// Constants hold the bare glyph only — no surrounding spaces. Spacing is intentionally +// inconsistent across call sites (wide glyphs are often followed by two spaces), so keep +// the spaces in the format strings, not here. +// +// Glyphs that default to text presentation carry the U+FE0F variation selector so they +// render in color (Warning āš ļø, Fix āœļø); dropping it makes them monochrome. +package emoji + +const ( + Success = "āœ…" // U+2705 white heavy check mark + Error = "āŒ" // U+274C cross mark + Warning = "āš ļø" // U+26A0 U+FE0F warning sign (keep the variation selector!) + Fix = "āœļø" // U+270D U+FE0F writing hand (keep the variation selector!) + Rule = "šŸ“" // U+1F4C1 file folder + File = "šŸ“„" // U+1F4C4 page facing up + Done = "✨" // U+2728 sparkles + Search = "šŸ”" // U+1F50D magnifying glass + Package = "šŸ“¦" // U+1F4E6 package + Tip = "šŸ’”" // U+1F4A1 light bulb + Standalone = "🧩" // U+1F9E9 jigsaw puzzle piece + Guide = "šŸ“–" // U+1F4D6 open book + Troubleshooting = "šŸ›Ÿ" // U+1F6DF ring buoy +) From 3f1f6b441652330c6bdb79245aa4c4ff7ccea361 Mon Sep 17 00:00:00 2001 From: "Jakub Mazurek (@jayu)" Date: Sat, 18 Jul 2026 11:49:23 +0200 Subject: [PATCH 2/2] refactor: centralise emojis --- internal/cli/config_summary_output_test.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/internal/cli/config_summary_output_test.go b/internal/cli/config_summary_output_test.go index 4ed0927..1c09a6d 100644 --- a/internal/cli/config_summary_output_test.go +++ b/internal/cli/config_summary_output_test.go @@ -2,12 +2,14 @@ package cli import ( "bytes" + "fmt" "io" "os" "strings" "testing" "rev-dep-go/internal/config" + "rev-dep-go/internal/emoji" ) func TestFormatAndPrintConfigResults_Summary(t *testing.T) { @@ -22,14 +24,14 @@ func TestFormatAndPrintConfigResults_Summary(t *testing.T) { FixedImportsCount: 5, FixedFilesCount: 2, }, - expected: "āœļø Fixed 5 imports in 2 files", + expected: fmt.Sprintf("%s Fixed 5 imports in 2 files", emoji.Fix), }, { name: "Only files removed", result: &config.ConfigProcessingResult{ DeletedFilesCount: 3, }, - expected: "āœļø Removed 3 orphan files", + expected: fmt.Sprintf("%s Removed 3 orphan files", emoji.Fix), }, { name: "Both fixed and removed", @@ -38,7 +40,7 @@ func TestFormatAndPrintConfigResults_Summary(t *testing.T) { FixedFilesCount: 2, DeletedFilesCount: 3, }, - expected: "āœļø Fixed 5 imports in 2 files, removed 3 orphan files", + expected: fmt.Sprintf("%s Fixed 5 imports in 2 files, removed 3 orphan files", emoji.Fix), }, { name: "Nothing fixed", @@ -69,7 +71,7 @@ func TestFormatAndPrintConfigResults_Summary(t *testing.T) { output := buf.String() if tt.expected == "" { - if strings.Contains(output, "āœļø") { + if strings.Contains(output, emoji.Fix) { t.Errorf("Expected no summary, but got: %q", output) } } else {