Skip to content

Commit 7a67ab1

Browse files
authored
Merge pull request #37 from cawpea/feature/#36_unify-style-prompts
Feature/#36 unify style prompts
2 parents d4ce158 + 5ff35b3 commit 7a67ab1

13 files changed

Lines changed: 881 additions & 181 deletions

docs/user-guide/cli-usage.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# CLI Usage
22

3+
Both `validate` and `fix` commands provide color-coded output with consistent formatting to help you quickly identify issues and track progress. Messages use standardized colors (errors in red, warnings in yellow, success in green, etc.) and contextual emoji indicators for improved readability.
4+
35
## Validate Command
46

57
### Basic Usage

src/cli/fix.ts

Lines changed: 32 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { formatFixOptions } from '@/utils/styles';
2020
import type { CodeRefError, FixOptions, FixResult } from '@/utils/types';
2121
import { extractCodeRefs, findMarkdownFiles, validateCodeRef } from '@/core/validate';
2222
import { loadFixConfig, getDocsPath, type CodeRefFixConfig } from '@/config';
23+
import { msg } from '@/utils/message-formatter';
2324

2425
// Parse command line arguments
2526
function parseArgs(): FixOptions {
@@ -86,23 +87,28 @@ export async function main(): Promise<void> {
8687
verbose: options.verbose,
8788
});
8889

89-
console.log('🔧 Starting CODE_REF error fixes...\n');
90+
// Set verbose mode for message formatter
91+
msg.setVerbose(options.verbose);
92+
93+
console.log(`${msg.startFix('Starting CODE_REF error fixes...')}\n`);
9094

9195
if (options.dryRun) {
92-
console.log('⚠️ DRY RUN mode: No actual changes will be made\n');
96+
console.log(`${msg.warning('DRY RUN mode: No actual changes will be made')}\n`);
9397
}
9498

9599
// Collect errors
96100
const errorGroups = collectErrors(config);
97101

98102
if (errorGroups.length === 0) {
99-
console.log('✅ No fixable errors found');
103+
console.log(msg.success('No fixable errors found'));
100104
process.exit(0);
101105
}
102106

103107
// Statistics
104108
const totalErrors = errorGroups.reduce((sum, g) => sum + g.errors.length, 0);
105-
console.log(`📊 Detected ${totalErrors} fixable error(s) in ${errorGroups.length} file(s)\n`);
109+
console.log(
110+
`${msg.info(`Detected ${totalErrors} fixable error(s) in ${errorGroups.length} file(s)`)}\n`
111+
);
106112

107113
// Interactive interface
108114
const rl = createPromptInterface();
@@ -111,8 +117,8 @@ export async function main(): Promise<void> {
111117

112118
try {
113119
for (const group of errorGroups) {
114-
console.log(`\n📄 ${path.relative(config.projectRoot, group.docFile)}`);
115-
console.log(` ${group.errors.length} error(s)\n`);
120+
console.log(`\n${msg.file(path.relative(config.projectRoot, group.docFile))}`);
121+
console.log(`${msg.context(`${group.errors.length} error(s)`)}\n`);
116122

117123
// Sort errors in descending order by docLineNumber (bottom to top)
118124
// To prevent fixes at the bottom from affecting line numbers at the top
@@ -125,10 +131,8 @@ export async function main(): Promise<void> {
125131
let _lineOffset = 0; // Track cumulative offset (for future edge case handling)
126132

127133
for (const error of sortedErrors) {
128-
console.log(`\n❌ ${error.type}: ${error.message}`);
129-
console.log(
130-
` Reference: ${path.relative(config.projectRoot, error.ref.docFile)}${error.ref.docLineNumber ? `:${error.ref.docLineNumber}` : ''}`
131-
);
134+
const location = `${path.relative(config.projectRoot, error.ref.docFile)}${error.ref.docLineNumber ? `:${error.ref.docLineNumber}` : ''}`;
135+
console.log(`\n${msg.errorDetail(error.type, error.message, location)}`);
132136

133137
// Create fix action
134138
let action;
@@ -141,12 +145,12 @@ export async function main(): Promise<void> {
141145

142146
// If there are multiple options, let the user choose
143147
if (Array.isArray(fixActionResult)) {
144-
console.log('\n🛠️ Please select a fix method:\n');
148+
console.log(`\n${msg.info('Please select a fix method:')}\n`);
145149
console.log(formatFixOptions(fixActionResult));
146150

147151
if (options.auto) {
148152
// Auto-select first option in auto mode
149-
console.log(' ℹ️ Auto-selecting option 1 in auto mode\n');
153+
console.log(`${msg.context('Auto-selecting option 1 in auto mode')}\n`);
150154
action = fixActionResult[0];
151155
} else {
152156
// Let user choose
@@ -156,14 +160,14 @@ export async function main(): Promise<void> {
156160
if (num >= 1 && num <= fixActionResult.length) {
157161
resolve(num - 1);
158162
} else {
159-
console.log(' ⚠️ Invalid selection. Skipping.');
163+
console.log(msg.context('Invalid selection. Skipping.'));
160164
resolve(-1);
161165
}
162166
});
163167
});
164168

165169
if (selection === -1) {
166-
console.log(' ⏭️ Skipped');
170+
console.log(msg.context('Skipped'));
167171
continue;
168172
}
169173

@@ -175,7 +179,7 @@ export async function main(): Promise<void> {
175179
}
176180

177181
if (!action) {
178-
console.log(' ⚠️ This error cannot be fixed');
182+
console.log(msg.context('This error cannot be fixed'));
179183
continue;
180184
}
181185

@@ -191,13 +195,13 @@ export async function main(): Promise<void> {
191195
}
192196

193197
if (!shouldFix) {
194-
console.log(' ⏭️ Skipped');
198+
console.log(msg.context('Skipped'));
195199
continue;
196200
}
197201

198202
// Dry run check
199203
if (options.dryRun) {
200-
console.log(' ✅ [DRY RUN] Simulated fix');
204+
console.log(msg.context('[DRY RUN] Simulated fix'));
201205
fixResults.push({ success: true, action });
202206
continue;
203207
}
@@ -207,7 +211,7 @@ export async function main(): Promise<void> {
207211
if (!options.noBackup && !backupFiles.has(group.docFile)) {
208212
backupPath = createBackup(group.docFile);
209213
backupFiles.add(group.docFile);
210-
console.log(` 💾 Backup created: ${path.basename(backupPath)}`);
214+
console.log(msg.context(`Backup created: ${path.basename(backupPath)}`));
211215
}
212216

213217
// Apply fix
@@ -216,15 +220,15 @@ export async function main(): Promise<void> {
216220
_lineOffset += lineDelta; // Accumulate offset
217221

218222
// Debug log
219-
if (lineDelta !== 0) {
220-
console.log(` 📊 Line delta: ${lineDelta > 0 ? '+' : ''}${lineDelta}`);
223+
if (lineDelta !== 0 && options.verbose) {
224+
console.log(msg.debug(` Line delta: ${lineDelta > 0 ? '+' : ''}${lineDelta}`));
221225
}
222226

223-
console.log(' ✅ Fix applied');
227+
console.log(msg.context('Fix applied'));
224228
fixResults.push({ success: true, action, backupPath });
225229
} catch (err) {
226230
const errorMsg = err instanceof Error ? err.message : String(err);
227-
console.log(` ❌ Fix failed: ${errorMsg}`);
231+
console.log(msg.context(`Fix failed: ${errorMsg}`));
228232
fixResults.push({ success: false, action, error: errorMsg, backupPath });
229233
}
230234
}
@@ -234,30 +238,23 @@ export async function main(): Promise<void> {
234238
}
235239

236240
// Result summary
237-
console.log(`\n${'='.repeat(60)}`);
238-
console.log('📊 Fix Results Summary\n');
239-
240241
const successful = fixResults.filter((r) => r.success).length;
241242
const failed = fixResults.filter((r) => !r.success).length;
242243

243-
console.log(`✅ Successful: ${successful}`);
244-
console.log(`❌ Failed: ${failed}`);
244+
const backupPaths =
245+
!options.noBackup && backupFiles.size > 0
246+
? Array.from(backupFiles).map((file) => path.relative(config.projectRoot, `${file}.backup`))
247+
: [];
245248

246-
if (backupFiles.size > 0 && !options.noBackup) {
247-
console.log(`\n💾 Backup files: ${backupFiles.size}`);
248-
for (const file of backupFiles) {
249-
const backupPath = `${file}.backup`;
250-
console.log(` ${path.relative(config.projectRoot, backupPath)}`);
251-
}
252-
}
249+
console.log(msg.summary(successful, failed, backupPaths));
253250

254251
process.exit(failed > 0 ? 1 : 0);
255252
}
256253

257254
// When executed as a script
258255
if (require.main === module) {
259256
main().catch((error) => {
260-
console.error('\n❌ An error occurred:', error);
257+
console.error(`\n${msg.error(`An error occurred: ${error}`)}`);
261258
process.exit(1);
262259
});
263260
}

src/cli/validate.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { findMarkdownFiles, extractCodeRefs, validateCodeRef } from '@/core/vali
1818
import { isIgnored, loadDocsignorePatterns } from '@/utils/ignore-pattern';
1919
import { displayLineRangeDiff } from '@/utils/diff-display';
2020
import { extractLinesFromFile } from '@/utils/code-comparison';
21+
import { msg } from '@/utils/message-formatter';
2122

2223
/**
2324
* CLI options
@@ -74,7 +75,7 @@ function resolveTargetFiles(targets: string[], projectRoot: string, docsPath: st
7475
resolvedFiles.add(absolutePath);
7576
}
7677
} else {
77-
console.warn(`⚠️ File not found: ${target}`);
78+
console.warn(msg.warning(`File not found: ${target}`));
7879
}
7980
}
8081

@@ -87,7 +88,9 @@ function resolveTargetFiles(targets: string[], projectRoot: string, docsPath: st
8788
export async function main(args: string[] = process.argv.slice(2)): Promise<void> {
8889
const options = parseCliArgs(args);
8990

90-
console.log('🔍 Validating CODE_REF references in documentation...\n');
91+
msg.setVerbose(options.verbose);
92+
93+
console.log(`${msg.startValidation('Validating CODE_REF references in documentation...')}\n`);
9194

9295
// Load configuration
9396
const config = loadConfig({
@@ -100,14 +103,14 @@ export async function main(args: string[] = process.argv.slice(2)): Promise<void
100103
const allMarkdownFiles = resolveTargetFiles(options.files, config.projectRoot, docsPath);
101104

102105
if (options.files.length > 0 && options.verbose) {
103-
console.log(`📋 Specified files/directories: ${options.files.join(', ')}\n`);
106+
console.log(`${msg.debug(`Specified files/directories: ${options.files.join(', ')}`)}\n`);
104107
}
105108

106109
// Load ignore patterns
107110
const ignoreFilePath = getIgnoreFilePath(config);
108111
const ignorePatterns = ignoreFilePath ? loadDocsignorePatterns(ignoreFilePath) : [];
109112
if (options.verbose) {
110-
console.log(`📋 Loaded ${ignorePatterns.length} ignore patterns\n`);
113+
console.log(`${msg.debug(`Loaded ${ignorePatterns.length} ignore patterns`)}\n`);
111114
}
112115

113116
// Filter files not excluded by ignore patterns
@@ -118,11 +121,11 @@ export async function main(args: string[] = process.argv.slice(2)): Promise<void
118121

119122
if (options.verbose && allMarkdownFiles.length > markdownFiles.length) {
120123
console.log(
121-
`📋 ${allMarkdownFiles.length - markdownFiles.length} files excluded by ignore patterns\n`
124+
`${msg.debug(`${allMarkdownFiles.length - markdownFiles.length} files excluded by ignore patterns`)}\n`
122125
);
123126
}
124127

125-
console.log(`📄 Found ${markdownFiles.length} markdown files\n`);
128+
console.log(`${msg.file(`Found ${markdownFiles.length} markdown files`)}\n`);
126129

127130
// Extract all CODE_REF references
128131
let totalRefs = 0;
@@ -137,15 +140,15 @@ export async function main(args: string[] = process.argv.slice(2)): Promise<void
137140
refs.forEach((ref) => allRefs.push({ ref, file }));
138141

139142
if (options.verbose) {
140-
console.log(` ${path.relative(docsPath, file)}: ${refs.length} references`);
143+
console.log(msg.debug(` ${path.relative(docsPath, file)}: ${refs.length} references`));
141144
}
142145
}
143146
}
144147

145-
console.log(`\n📌 Found ${totalRefs} CODE_REF references\n`);
148+
console.log(`\n${msg.info(`Found ${totalRefs} CODE_REF references`)}\n`);
146149

147150
if (totalRefs === 0) {
148-
console.log('✅ No CODE_REF references found (no validation needed)');
151+
console.log(msg.success('No CODE_REF references found (no validation needed)'));
149152
process.exit(0);
150153
}
151154

@@ -156,10 +159,10 @@ export async function main(args: string[] = process.argv.slice(2)): Promise<void
156159

157160
// Display results
158161
if (allErrors.length === 0) {
159-
console.log('✅ All CODE_REF references are valid!');
162+
console.log(msg.success('All CODE_REF references are valid!'));
160163
process.exit(0);
161164
} else {
162-
console.log(`Found ${allErrors.length} errors:\n`);
165+
console.log(`${msg.error(`Found ${allErrors.length} errors:`)}\n`);
163166

164167
// Group errors by document
165168
const errorsByDoc: Record<string, any[]> = {};
@@ -176,15 +179,15 @@ export async function main(args: string[] = process.argv.slice(2)): Promise<void
176179

177180
// Display error details
178181
for (const [docFile, errors] of Object.entries(errorsByDoc)) {
179-
console.log(`📄 ${docFile}:`);
182+
console.log(`${msg.file(docFile)}:`);
180183

181184
for (const error of errors) {
182-
console.log(` ${error.type}: ${error.message}`);
185+
console.log(` ${msg.error(`${error.type}: ${error.message}`)}`);
183186

184187
// Display line number in document
185188
const filePath = path.relative(config.projectRoot, error.ref.docFile);
186189
const lineInfo = error.ref.docLineNumber ? `:${error.ref.docLineNumber}` : '';
187-
console.log(` ${filePath}${lineInfo}: ${error.ref.fullMatch}`);
190+
console.log(msg.context(` ${filePath}${lineInfo}: ${error.ref.fullMatch}`));
188191

189192
// Display diff for CODE_LOCATION_MISMATCH in verbose mode
190193
if (error.type === 'CODE_LOCATION_MISMATCH' && error.suggestedLines && options.verbose) {
@@ -212,7 +215,7 @@ export async function main(args: string[] = process.argv.slice(2)): Promise<void
212215
// Run CLI if this file is executed directly
213216
if (require.main === module) {
214217
main().catch((error) => {
215-
console.error('Error:', error);
218+
console.error(msg.error(`Error: ${error}`));
216219
process.exit(1);
217220
});
218221
}

src/utils/backup.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ describe('backup', () => {
6969
mockedFs.existsSync.mockReturnValue(false);
7070

7171
expect(() => restoreBackup(backupPath, originalPath)).toThrow(
72-
'バックアップファイルが見つかりません'
72+
'Backup file not found: /test/file.txt.backup'
7373
);
7474
});
7575
});

src/utils/backup.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
/**
2-
* バックアップ管理ユーティリティ
2+
* Backup management utilities
33
*/
44

55
import * as fs from 'fs';
66
import * as path from 'path';
77

88
/**
9-
* ファイルのバックアップを作成
10-
* @returns バックアップファイルのパス
9+
* Create a backup of a file
10+
* @param filePath Path to the file to backup
11+
* @returns Path to the backup file
1112
*/
1213
export function createBackup(filePath: string): string {
1314
let backupPath = `${filePath}.backup`;
1415
let counter = 1;
1516

16-
// ユニークなバックアップファイル名を検索
17+
// Find a unique backup filename
1718
while (fs.existsSync(backupPath)) {
1819
backupPath = `${filePath}.backup.${counter}`;
1920
counter++;
@@ -24,18 +25,21 @@ export function createBackup(filePath: string): string {
2425
}
2526

2627
/**
27-
* バックアップから復元
28+
* Restore a file from backup
29+
* @param backupPath Path to the backup file
30+
* @param originalPath Path where the file should be restored
2831
*/
2932
export function restoreBackup(backupPath: string, originalPath: string): void {
3033
if (!fs.existsSync(backupPath)) {
31-
throw new Error(`バックアップファイルが見つかりません: ${backupPath}`);
34+
throw new Error(`Backup file not found: ${backupPath}`);
3235
}
3336

3437
fs.copyFileSync(backupPath, originalPath);
3538
}
3639

3740
/**
38-
* バックアップファイルを削除
41+
* Delete a backup file
42+
* @param backupPath Path to the backup file to delete
3943
*/
4044
export function deleteBackup(backupPath: string): void {
4145
if (fs.existsSync(backupPath)) {
@@ -44,7 +48,9 @@ export function deleteBackup(backupPath: string): void {
4448
}
4549

4650
/**
47-
* 指定ファイルの全バックアップファイルをリスト
51+
* List all backup files for a given file
52+
* @param filePath Path to the original file
53+
* @returns Array of backup file paths
4854
*/
4955
export function listBackups(filePath: string): string[] {
5056
const dir = path.dirname(filePath);

0 commit comments

Comments
 (0)