style(tools): unify console output with [I/W/E] [Module] color-coded format
- Add ANSI color coding: [I]=green, [W]=yellow, [E]=red - Format: [Level] [Module] message with 4-char indent for data rows - Error detail lines use |-> tree connector - Remove extra blank lines between outputs (except list) - Modules: CLI, Preview, Builder, Server, Watcher Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
d46f702664
commit
da8bb2e1a2
8 files changed
+46
-40
No files matched your search
+2
-2
@@ -49,7 +49,7 @@ func runCreate(c *cli.Context) error {
|
|||||||
|
|
||||||
if info, err := os.Stat(styleDir); err == nil {
|
if info, err := os.Stat(styleDir); err == nil {
|
||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
fmt.Printf(" [skip] '%s' already exists\n", styleName)
|
fmt.Printf("\033[32m[I]\033[0m [CLI] '%s' already exists, skipped\n", styleName)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return fmt.Errorf("'%s' exists but is not a directory", styleName)
|
return fmt.Errorf("'%s' exists but is not a directory", styleName)
|
||||||
@@ -79,7 +79,7 @@ func runCreate(c *cli.Context) error {
|
|||||||
created++
|
created++
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf(" Created style '%s' with %d template files\n", styleName, created)
|
fmt.Printf("\033[32m[I]\033[0m [CLI] Created style '%s' with %d template files\n", styleName, created)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+2
-2
@@ -37,14 +37,14 @@ func runDelete(c *cli.Context) error {
|
|||||||
styleDir := filepath.Join(folder, styleName)
|
styleDir := filepath.Join(folder, styleName)
|
||||||
|
|
||||||
if _, err := os.Stat(styleDir); os.IsNotExist(err) {
|
if _, err := os.Stat(styleDir); os.IsNotExist(err) {
|
||||||
fmt.Printf(" [skip] '%s' does not exist\n", styleName)
|
fmt.Printf("\033[33m[W]\033[0m [CLI] '%s' does not exist, skipped\n", styleName)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.RemoveAll(styleDir); err != nil {
|
if err := os.RemoveAll(styleDir); err != nil {
|
||||||
return fmt.Errorf("cannot delete %s: %w", styleDir, err)
|
return fmt.Errorf("cannot delete %s: %w", styleDir, err)
|
||||||
}
|
}
|
||||||
fmt.Printf(" Deleted style '%s'\n", styleName)
|
fmt.Printf("\033[32m[I]\033[0m [CLI] Deleted style '%s'\n", styleName)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+5
-5
@@ -48,7 +48,7 @@ func runDev(c *cli.Context) error {
|
|||||||
|
|
||||||
// Verify node_modules are installed
|
// Verify node_modules are installed
|
||||||
if _, err := os.Stat(filepath.Join(serverDir, "node_modules")); os.IsNotExist(err) {
|
if _, err := os.Stat(filepath.Join(serverDir, "node_modules")); os.IsNotExist(err) {
|
||||||
fmt.Println("Installing Node.js dependencies...")
|
fmt.Println("\033[32m[I]\033[0m [Builder] Installing Node.js dependencies...")
|
||||||
cmd := exec.Command("npm", "install")
|
cmd := exec.Command("npm", "install")
|
||||||
cmd.Dir = serverDir
|
cmd.Dir = serverDir
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
@@ -64,7 +64,7 @@ func runDev(c *cli.Context) error {
|
|||||||
initCmd.Stdout = os.Stdout
|
initCmd.Stdout = os.Stdout
|
||||||
initCmd.Stderr = os.Stderr
|
initCmd.Stderr = os.Stderr
|
||||||
if err := initCmd.Run(); err != nil {
|
if err := initCmd.Run(); err != nil {
|
||||||
fmt.Println("Warning: initial preview generation failed, starting anyway...")
|
fmt.Println("\033[33m[W]\033[0m [Builder] Initial preview generation failed, starting anyway")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the Node.js dev server
|
// Start the Node.js dev server
|
||||||
@@ -78,8 +78,8 @@ func runDev(c *cli.Context) error {
|
|||||||
return fmt.Errorf("failed to start dev server: %w", err)
|
return fmt.Errorf("failed to start dev server: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("\nDev server running at http://localhost:%d\n", port)
|
fmt.Printf("\033[32m[I]\033[0m [Server] Dev server running at http://localhost:%d\n", port)
|
||||||
fmt.Print("Press Ctrl+C to stop.\n\n")
|
fmt.Print("\033[32m[I]\033[0m [Server] Press Ctrl+C to stop\n")
|
||||||
|
|
||||||
// Handle graceful shutdown
|
// Handle graceful shutdown
|
||||||
sigCh := make(chan os.Signal, 1)
|
sigCh := make(chan os.Signal, 1)
|
||||||
@@ -87,7 +87,7 @@ func runDev(c *cli.Context) error {
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
<-sigCh
|
<-sigCh
|
||||||
fmt.Println("\nShutting down...")
|
fmt.Println("\033[32m[I]\033[0m [Server] Shutting down")
|
||||||
if err := serverCmd.Process.Signal(os.Interrupt); err != nil {
|
if err := serverCmd.Process.Signal(os.Interrupt); err != nil {
|
||||||
serverCmd.Process.Kill()
|
serverCmd.Process.Kill()
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -37,7 +37,7 @@ func runList(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absDir, _ := filepath.Abs(folder)
|
absDir, _ := filepath.Abs(folder)
|
||||||
fmt.Printf("Available styles in '%s' (%d):\n", filepath.Base(absDir), len(themes))
|
fmt.Printf("\033[32m[I]\033[0m [Preview] Available styles in '%s' (%d):\n", filepath.Base(absDir), len(themes))
|
||||||
|
|
||||||
sort.Strings(themes)
|
sort.Strings(themes)
|
||||||
for _, t := range themes {
|
for _, t := range themes {
|
||||||
@@ -51,7 +51,7 @@ func runList(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
fmt.Printf(" %-16s (%d .tmpl files)\n", t, count)
|
fmt.Printf("\033[32m[I]\033[0m [Preview] %-16s (%d .tmpl files)\n", t, count)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
@@ -78,7 +77,7 @@ func runPreview(c *cli.Context) error {
|
|||||||
|
|
||||||
preview.PrintDetailedSummary(result, folder, cfg)
|
preview.PrintDetailedSummary(result, folder, cfg)
|
||||||
|
|
||||||
log.Printf("Wrote %d themes x %d templates to %s",
|
fmt.Printf("\033[32m[I]\033[0m [Preview] Wrote %d themes x %d templates to %s\n",
|
||||||
len(result.Summaries), len(cfg.Templates), outputPath)
|
len(result.Summaries), len(cfg.Templates), outputPath)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+9
-11
@@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -67,7 +66,7 @@ func DiscoverThemes(themesDir string) ([]string, error) {
|
|||||||
func RenderAll(themesDir string, cfg *config.TemplatesConfig, themeFilter map[string]bool) *PreviewResult {
|
func RenderAll(themesDir string, cfg *config.TemplatesConfig, themeFilter map[string]bool) *PreviewResult {
|
||||||
themes, err := DiscoverThemes(themesDir)
|
themes, err := DiscoverThemes(themesDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERROR: %v", err)
|
fmt.Fprintf(os.Stderr, "\033[31m[E]\033[0m [Preview] Error discovering themes: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +80,7 @@ func RenderAll(themesDir string, cfg *config.TemplatesConfig, themeFilter map[st
|
|||||||
}
|
}
|
||||||
themes = filtered
|
themes = filtered
|
||||||
if len(themes) == 0 {
|
if len(themes) == 0 {
|
||||||
log.Printf("WARN: no themes matched the requested filter")
|
fmt.Println("\033[33m[W]\033[0m [Preview] No themes matched the requested filter")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -249,36 +248,35 @@ func WriteRenderedJS(result *PreviewResult, outputPath string) error {
|
|||||||
// PrintDetailedSummary prints a human-readable summary of the preview results.
|
// PrintDetailedSummary prints a human-readable summary of the preview results.
|
||||||
func PrintDetailedSummary(result *PreviewResult, themesDir string, cfg *config.TemplatesConfig) {
|
func PrintDetailedSummary(result *PreviewResult, themesDir string, cfg *config.TemplatesConfig) {
|
||||||
absDir, _ := filepath.Abs(themesDir)
|
absDir, _ := filepath.Abs(themesDir)
|
||||||
fmt.Printf("\nfound %d styles of possible Gitea mail template in '%s' folder:\n\n",
|
fmt.Printf("\033[32m[I]\033[0m [Preview] Found %d styles in '%s':\n",
|
||||||
len(result.Summaries), filepath.Base(absDir))
|
len(result.Summaries), filepath.Base(absDir))
|
||||||
|
|
||||||
for _, s := range result.Summaries {
|
for _, s := range result.Summaries {
|
||||||
if s.MissingCount == 0 && s.ErrorCount == 0 {
|
if s.MissingCount == 0 && s.ErrorCount == 0 {
|
||||||
fmt.Printf(" %-16s all required mail templates are properly rendered, (total %d .tmpl files, %s after, %v)\n",
|
fmt.Printf("\033[32m[I]\033[0m [Preview] %-16s all required templates OK (total %d .tmpl, %s, %v)\n",
|
||||||
s.Name, s.TotalFiles,
|
s.Name, s.TotalFiles,
|
||||||
formatSize(s.RenderedSize),
|
formatSize(s.RenderedSize),
|
||||||
s.RenderDuration.Round(time.Millisecond))
|
s.RenderDuration.Round(time.Millisecond))
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(" %-16s only %d required mail templates are properly rendered:\n",
|
fmt.Printf("\033[33m[W]\033[0m [Preview] %-16s only %d required templates rendered:\n",
|
||||||
s.Name, s.RenderedCount)
|
s.Name, s.RenderedCount)
|
||||||
for _, tplID := range s.Missing {
|
for _, tplID := range s.Missing {
|
||||||
if t, ok := cfg.Templates[tplID]; ok {
|
if t, ok := cfg.Templates[tplID]; ok {
|
||||||
fmt.Printf(" [%s] not found, expected at 'themes/%s/%s'\n",
|
fmt.Printf("\033[31m[E]\033[0m [Preview] |-> %s not found, expected at 'themes/%s/%s'\n",
|
||||||
filepath.Base(t.PathStr()), s.Name, t.PathStr())
|
filepath.Base(t.PathStr()), s.Name, t.PathStr())
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(" [%s] not found\n", tplID)
|
fmt.Printf("\033[31m[E]\033[0m [Preview] |-> %s not found\n", tplID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, tplID := range s.Errors {
|
for _, tplID := range s.Errors {
|
||||||
if rr, ok := s.Results[tplID]; ok && rr.Error != "" {
|
if rr, ok := s.Results[tplID]; ok && rr.Error != "" {
|
||||||
fmt.Printf(" [%s] render error: %s\n", tplID, rr.Error)
|
fmt.Printf("\033[31m[E]\033[0m [Preview] |-> %s render error: %s\n", tplID, rr.Error)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(" [%s] render error\n", tplID)
|
fmt.Printf("\033[31m[E]\033[0m [Preview] |-> %s render error\n", tplID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// markSafeHTML recursively converts known HTML-containing string fields to
|
// markSafeHTML recursively converts known HTML-containing string fields to
|
||||||
|
|||||||
+24
-15
@@ -8,6 +8,15 @@ import expressWs from 'express-ws';
|
|||||||
|
|
||||||
import { inlineCSS, stripGmail, stripOutlook } from './inliner.mjs';
|
import { inlineCSS, stripGmail, stripOutlook } from './inliner.mjs';
|
||||||
|
|
||||||
|
// ANSI color helpers
|
||||||
|
const G = '\x1b[32m'; // green (Info)
|
||||||
|
const Y = '\x1b[33m'; // yellow (Warn)
|
||||||
|
const R = '\x1b[31m'; // red (Error)
|
||||||
|
const Z = '\x1b[0m'; // reset
|
||||||
|
const I = `${G}[I]${Z}`;
|
||||||
|
const W = `${Y}[W]${Z}`;
|
||||||
|
const E = `${R}[E]${Z}`;
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
const ROOT = resolve(__dirname, '..', '..'); // project root
|
const ROOT = resolve(__dirname, '..', '..'); // project root
|
||||||
const TOOLS = join(ROOT, 'tools'); // tools/ (for go run)
|
const TOOLS = join(ROOT, 'tools'); // tools/ (for go run)
|
||||||
@@ -55,13 +64,13 @@ async function rebuild() {
|
|||||||
CHANGED_THEMES.clear();
|
CHANGED_THEMES.clear();
|
||||||
|
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
console.log(`\n[rebuild] ${themes.length > 0 ? themes.join(', ') : 'all'} changed`);
|
console.log(`${I} [Builder] Rebuilding: ${themes.length > 0 ? themes.join(', ') : 'all'} changed`);
|
||||||
|
|
||||||
// Step 1: Go render
|
// Step 1: Go render
|
||||||
try {
|
try {
|
||||||
await goPreview(themes.length > 0 ? themes : ['all']);
|
await goPreview(themes.length > 0 ? themes : ['all']);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[rebuild] go render failed:', err.message);
|
console.error(`${E} [Builder] Go render failed: ${err.message}`);
|
||||||
building = false;
|
building = false;
|
||||||
broadcast('error', { message: err.message });
|
broadcast('error', { message: err.message });
|
||||||
return;
|
return;
|
||||||
@@ -73,11 +82,11 @@ async function rebuild() {
|
|||||||
const juiced = await juiceRenderedOutput(renderedJS);
|
const juiced = await juiceRenderedOutput(renderedJS);
|
||||||
writeFileSync(RENDERED_JS, juiced, 'utf8');
|
writeFileSync(RENDERED_JS, juiced, 'utf8');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[rebuild] juice step failed:', err.message);
|
console.error(`${E} [Builder] Juice step failed: ${err.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const elapsed = Date.now() - start;
|
const elapsed = Date.now() - start;
|
||||||
console.log(`[rebuild] done in ${elapsed}ms`);
|
console.log(`${I} [Builder] Rebuild done in ${elapsed}ms`);
|
||||||
|
|
||||||
building = false;
|
building = false;
|
||||||
|
|
||||||
@@ -190,19 +199,19 @@ try {
|
|||||||
const fullPath = join(THEMES, fname);
|
const fullPath = join(THEMES, fname);
|
||||||
// event is 'rename' for both create and delete; 'change' for modifications
|
// event is 'rename' for both create and delete; 'change' for modifications
|
||||||
if (event === 'change') {
|
if (event === 'change') {
|
||||||
console.log('[watch] ' + relPath(fullPath) + ' edited');
|
console.log(`${I} [Watcher] ${relPath(fullPath)} changed`);
|
||||||
scheduleRebuild(fullPath);
|
scheduleRebuild(fullPath);
|
||||||
} else if (event === 'rename') {
|
} else if (event === 'rename') {
|
||||||
if (existsSync(fullPath)) {
|
if (existsSync(fullPath)) {
|
||||||
console.log('[watch] ' + relPath(fullPath) + ' created');
|
console.log(`${I} [Watcher] ${relPath(fullPath)} created`);
|
||||||
} else {
|
} else {
|
||||||
console.log('[watch] ' + relPath(fullPath) + ' deleted');
|
console.log(`${I} [Watcher] ${relPath(fullPath)} deleted`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
console.log('[watch] themes/ (recursive)');
|
console.log(`${I} [Watcher] Watching themes/ (recursive)`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[watch] failed:', err.message);
|
console.error(`${E} [Watcher] File watch failed: ${err.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initial Juice pass
|
// Initial Juice pass
|
||||||
@@ -213,9 +222,9 @@ try {
|
|||||||
const js = readFileSync(RENDERED_JS, 'utf8');
|
const js = readFileSync(RENDERED_JS, 'utf8');
|
||||||
const juiced = await juiceRenderedOutput(js);
|
const juiced = await juiceRenderedOutput(js);
|
||||||
writeFileSync(RENDERED_JS, juiced, 'utf8');
|
writeFileSync(RENDERED_JS, juiced, 'utf8');
|
||||||
console.log('[init] juice inlining applied');
|
console.log(`${I} [Builder] CSS inlining applied`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[init] juice failed:', err.message);
|
console.error(`${E} [Builder] CSS inlining failed: ${err.message}`);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@@ -223,8 +232,8 @@ try {
|
|||||||
app.get('/health', (_req, res) => res.json({ status: 'ok', port: PORT }));
|
app.get('/health', (_req, res) => res.json({ status: 'ok', port: PORT }));
|
||||||
|
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
console.log(`\n Gitea Mail Templates — Dev Server`);
|
console.log(`${I} [Server] Gitea Mail Templates — Dev Server`);
|
||||||
console.log(` http://localhost:${PORT}\n`);
|
console.log(`${I} [Server] http://localhost:${PORT}`);
|
||||||
console.log(` Watching themes/ for changes...`);
|
console.log(`${I} [Watcher] Watching themes/ for changes`);
|
||||||
console.log(` WebSocket ws://localhost:${PORT}/ws`);
|
console.log(`${I} [Server] WebSocket ws://localhost:${PORT}/ws\n`);
|
||||||
});
|
});
|
||||||
+1
-1
@@ -24,7 +24,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := app.Run(os.Args); err != nil {
|
if err := app.Run(os.Args); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
fmt.Fprintf(os.Stderr, "\033[31m[E]\033[0m [CLI] %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user