helm-env-delta 1.15.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +279 -95
  2. package/config.schema.json +496 -0
  3. package/dist/commandLine.d.ts +9 -11
  4. package/dist/commandLine.js +288 -101
  5. package/dist/config/ZodError.d.ts +2 -2
  6. package/dist/config/configFile.d.ts +1 -1
  7. package/dist/config/configFile.js +87 -41
  8. package/dist/config/configLoader.d.ts +2 -1
  9. package/dist/config/configMerger.d.ts +2 -1
  10. package/dist/consoleFormatter.d.ts +1 -1
  11. package/dist/consoleFormatter.js +12 -12
  12. package/dist/exitCodes.d.ts +5 -0
  13. package/dist/exitCodes.js +8 -0
  14. package/dist/index.js +82 -77
  15. package/dist/logger.d.ts +3 -3
  16. package/dist/pipeline/fileDiff.d.ts +6 -5
  17. package/dist/pipeline/fileLoader.d.ts +2 -1
  18. package/dist/pipeline/fileLoader.js +2 -2
  19. package/dist/pipeline/fileUpdater.d.ts +4 -4
  20. package/dist/pipeline/fileUpdater.js +1 -1
  21. package/dist/pipeline/stopRulesValidator.d.ts +2 -1
  22. package/dist/pipeline/stopRulesValidator.js +2 -4
  23. package/dist/pipeline/yamlFormatter.d.ts +1 -1
  24. package/dist/pipeline/yamlFormatter.js +9 -9
  25. package/dist/reporters/browserLauncher.js +1 -34
  26. package/dist/reporters/consoleDiffReporter.d.ts +2 -2
  27. package/dist/reporters/consoleDiffReporter.js +26 -26
  28. package/dist/reporters/htmlReporter.d.ts +4 -3
  29. package/dist/reporters/htmlReporter.js +20 -10
  30. package/dist/reporters/htmlTemplate.d.ts +1 -1
  31. package/dist/reporters/jsonReporter.d.ts +2 -2
  32. package/dist/reporters/treeRenderer.d.ts +1 -1
  33. package/dist/suggestionEngine.d.ts +2 -2
  34. package/dist/suggestionEngine.js +2 -2
  35. package/dist/utils/arrayMerger.d.ts +1 -1
  36. package/dist/utils/patternMatcher.d.ts +1 -1
  37. package/dist/utils/versionChecker.js +3 -3
  38. package/package.json +19 -17
@@ -1,4 +1,4 @@
1
- import { StopRuleViolation } from './pipeline/stopRulesValidator';
1
+ import { type StopRuleViolation } from './pipeline/stopRulesValidator';
2
2
  export type BoxStyle = 'success' | 'warning' | 'error' | 'info';
3
3
  export type ProgressStyle = 'loading' | 'success' | 'info';
4
4
  export type ViolationMode = 'error' | 'warning' | 'force';
@@ -4,18 +4,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.formatProgressMessage = exports.colorizeFileOperation = exports.formatStopRuleViolation = exports.formatBox = void 0;
7
- const chalk_1 = __importDefault(require("chalk"));
7
+ const ansi_colors_1 = __importDefault(require("ansi-colors"));
8
8
  const formatBox = (title, content, style = 'info', width = 60) => {
9
9
  const getColorFunction = (boxStyle) => {
10
10
  switch (boxStyle) {
11
11
  case 'success':
12
- return chalk_1.default.green;
12
+ return ansi_colors_1.default.green;
13
13
  case 'warning':
14
- return chalk_1.default.yellow;
14
+ return ansi_colors_1.default.yellow;
15
15
  case 'error':
16
- return chalk_1.default.red;
16
+ return ansi_colors_1.default.red;
17
17
  default:
18
- return chalk_1.default.cyan;
18
+ return ansi_colors_1.default.cyan;
19
19
  }
20
20
  };
21
21
  const colorFunction = getColorFunction(style);
@@ -44,7 +44,7 @@ const formatStopRuleViolation = (violation, mode) => {
44
44
  return violationMode === 'error' ? 'error' : 'warning';
45
45
  };
46
46
  const labelWidth = 10;
47
- const formatLabel = (label) => chalk_1.default.dim(label.padEnd(labelWidth));
47
+ const formatLabel = (label) => ansi_colors_1.default.dim(label.padEnd(labelWidth));
48
48
  const content = [
49
49
  `${formatLabel('File:')} ${violation.file}`,
50
50
  `${formatLabel('Path:')} ${violation.path}`,
@@ -61,13 +61,13 @@ const colorizeFileOperation = (operation, filePath, isDryRun, alreadyDeleted = f
61
61
  const getOperationDisplay = (op) => {
62
62
  switch (op) {
63
63
  case 'add':
64
- return { symbol: '+', verb: 'add', colorFn: chalk_1.default.green };
64
+ return { symbol: '+', verb: 'add', colorFn: ansi_colors_1.default.green };
65
65
  case 'update':
66
- return { symbol: '~', verb: 'update', colorFn: chalk_1.default.yellow };
66
+ return { symbol: '~', verb: 'update', colorFn: ansi_colors_1.default.yellow };
67
67
  case 'delete':
68
- return { symbol: '-', verb: 'delete', colorFn: chalk_1.default.red };
68
+ return { symbol: '-', verb: 'delete', colorFn: ansi_colors_1.default.red };
69
69
  case 'format':
70
- return { symbol: '≈', verb: 'format', colorFn: chalk_1.default.cyan };
70
+ return { symbol: '≈', verb: 'format', colorFn: ansi_colors_1.default.cyan };
71
71
  }
72
72
  };
73
73
  const { symbol, verb, colorFn } = getOperationDisplay(operation);
@@ -91,9 +91,9 @@ const formatProgressMessage = (message, style) => {
91
91
  const getColorFunction = (progressStyle) => {
92
92
  switch (progressStyle) {
93
93
  case 'success':
94
- return chalk_1.default.green;
94
+ return ansi_colors_1.default.green;
95
95
  default:
96
- return chalk_1.default.cyan;
96
+ return ansi_colors_1.default.cyan;
97
97
  }
98
98
  };
99
99
  const icon = getIcon(style);
@@ -0,0 +1,5 @@
1
+ export declare const EXIT_NO_CHANGES = 0;
2
+ export declare const EXIT_CHANGES_SYNCED = 1;
3
+ export declare const EXIT_STOP_RULE_VIOLATION = 2;
4
+ export declare const EXIT_CONFIG_ERROR = 3;
5
+ export declare const EXIT_VALIDATION_WARNINGS = 4;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EXIT_VALIDATION_WARNINGS = exports.EXIT_CONFIG_ERROR = exports.EXIT_STOP_RULE_VIOLATION = exports.EXIT_CHANGES_SYNCED = exports.EXIT_NO_CHANGES = void 0;
4
+ exports.EXIT_NO_CHANGES = 0;
5
+ exports.EXIT_CHANGES_SYNCED = 1;
6
+ exports.EXIT_STOP_RULE_VIOLATION = 2;
7
+ exports.EXIT_CONFIG_ERROR = 3;
8
+ exports.EXIT_VALIDATION_WARNINGS = 4;
package/dist/index.js CHANGED
@@ -40,12 +40,13 @@ const node_fs_1 = require("node:fs");
40
40
  const promises_1 = require("node:fs/promises");
41
41
  const node_os_1 = require("node:os");
42
42
  const node_path_1 = __importDefault(require("node:path"));
43
- const chalk_1 = __importDefault(require("chalk"));
43
+ const ansi_colors_1 = __importDefault(require("ansi-colors"));
44
44
  const YAML = __importStar(require("yaml"));
45
45
  const package_json_1 = __importDefault(require("../package.json"));
46
46
  const commandLine_1 = require("./commandLine");
47
47
  const config_1 = require("./config");
48
48
  const consoleFormatter_1 = require("./consoleFormatter");
49
+ const exitCodes_1 = require("./exitCodes");
49
50
  const logger_1 = require("./logger");
50
51
  const pipeline_1 = require("./pipeline");
51
52
  const reporters_1 = require("./reporters");
@@ -60,44 +61,46 @@ const versionChecker_1 = require("./utils/versionChecker");
60
61
  const main = async () => {
61
62
  const command = (0, commandLine_1.parseCommandLine)();
62
63
  if (command.noColor)
63
- chalk_1.default.level = 0;
64
+ ansi_colors_1.default.enabled = false;
64
65
  const verbosityLevel = command.verbose ? 'verbose' : command.quiet ? 'quiet' : 'normal';
65
- const logger = new logger_1.Logger({ level: verbosityLevel, isDiffJson: command.diffJson });
66
+ const logger = new logger_1.Logger({ level: verbosityLevel, isDiffJson: command.json });
66
67
  logger.log(`Now you run ${package_json_1.default.name} v${package_json_1.default.version}...`);
67
68
  const configDirectory = node_path_1.default.join((0, node_os_1.homedir)(), '.helm-env-delta');
68
69
  const firstRunMarker = node_path_1.default.join(configDirectory, 'first-run');
69
70
  const isFirstRun = !(0, node_fs_1.existsSync)(firstRunMarker);
70
71
  if (isFirstRun && !command.quiet) {
71
- console.log(chalk_1.default.cyan('\n👋 First time using helm-env-delta?\n'));
72
- console.log(chalk_1.default.dim(' Tips:'));
73
- console.log(chalk_1.default.dim(' • Always use --dry-run first to preview changes'));
74
- console.log(chalk_1.default.dim(' • Use --diff-html to review diffs in your browser'));
75
- console.log(chalk_1.default.dim(' • See examples: https://github.com/balazscsaba2006/helm-env-delta/tree/main/example'));
76
- console.log(chalk_1.default.dim(' • Run with --help to see all options\n'));
72
+ console.log(ansi_colors_1.default.cyan('\n👋 First time using helm-env-delta?\n'));
73
+ console.log(ansi_colors_1.default.dim(' Tips:'));
74
+ console.log(ansi_colors_1.default.dim(' • Always use --dry-run first to preview changes'));
75
+ console.log(ansi_colors_1.default.dim(' • Use the diff command to review changes before syncing'));
76
+ console.log(ansi_colors_1.default.dim(' • See examples: https://github.com/balazscsaba2006/helm-env-delta/tree/main/example'));
77
+ console.log(ansi_colors_1.default.dim(' • Run with --help to see all options\n'));
77
78
  (0, node_fs_1.mkdirSync)(configDirectory, { recursive: true });
78
79
  (0, node_fs_1.writeFileSync)(firstRunMarker, new Date().toISOString());
79
80
  }
80
- const config = (0, config_1.loadConfigFile)(command.config, command.quiet, logger, { formatOnly: command.formatOnly });
81
+ const config = (0, config_1.loadConfigFile)(command.config, command.quiet, logger, {
82
+ formatOnly: command.commandName === 'format'
83
+ });
81
84
  if (config.requiredVersion)
82
85
  configHasRequiredVersion = true;
83
- if (command.showConfig) {
84
- console.log(chalk_1.default.cyan('\n⚙️ Resolved Configuration:\n'));
86
+ if (command.commandName === 'show-config') {
87
+ console.log(ansi_colors_1.default.cyan('\n⚙️ Resolved Configuration:\n'));
85
88
  console.log(YAML.stringify(config, { indent: 2 }));
86
89
  return;
87
90
  }
88
- if (command.validate) {
91
+ if (command.commandName === 'validate') {
89
92
  if (!config.source) {
90
- logger.error('\nSource folder is required for validation mode.', 'critical');
91
- process.exit(1);
93
+ logger.error('\nSource folder is required for the validate command.', 'critical');
94
+ process.exit(exitCodes_1.EXIT_CONFIG_ERROR);
92
95
  }
93
96
  const validationConfig = config;
94
97
  logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Validating configuration...', 'info'));
95
98
  const warningResult = (0, config_1.validateConfigWarnings)(validationConfig);
96
99
  let hasAnyWarnings = warningResult.hasWarnings;
97
100
  if (warningResult.hasWarnings) {
98
- console.warn(chalk_1.default.yellow('\n⚠️ Configuration Warnings (non-fatal):\n'));
101
+ console.warn(ansi_colors_1.default.yellow('\n⚠️ Configuration Warnings (non-fatal):\n'));
99
102
  for (const warning of warningResult.warnings)
100
- console.warn(chalk_1.default.yellow(` • ${warning}`));
103
+ console.warn(ansi_colors_1.default.yellow(` • ${warning}`));
101
104
  }
102
105
  logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Loading files for validation...', 'loading'));
103
106
  const sourceResult = await (0, pipeline_1.loadFiles)({
@@ -135,16 +138,21 @@ const main = async () => {
135
138
  const usageResult = (0, pipeline_1.validatePatternUsage)(validationConfig, sourceFiles, destinationFiles);
136
139
  hasAnyWarnings = hasAnyWarnings || usageResult.hasWarnings;
137
140
  if (usageResult.hasWarnings) {
138
- console.warn(chalk_1.default.yellow('\n⚠️ Pattern Usage Warnings (non-fatal):\n'));
141
+ console.warn(ansi_colors_1.default.yellow('\n⚠️ Pattern Usage Warnings (non-fatal):\n'));
139
142
  for (const warning of usageResult.warnings) {
140
- const contextString = warning.context ? chalk_1.default.dim(` (${warning.context})`) : '';
141
- console.warn(chalk_1.default.yellow(` • ${warning.message}${contextString}`));
143
+ const contextString = warning.context ? ansi_colors_1.default.dim(` (${warning.context})`) : '';
144
+ console.warn(ansi_colors_1.default.yellow(` • ${warning.message}${contextString}`));
142
145
  if (warning.hint)
143
- console.warn(chalk_1.default.dim(` Hint: ${warning.hint}`));
146
+ console.warn(ansi_colors_1.default.dim(` Hint: ${warning.hint}`));
144
147
  }
145
148
  }
146
149
  if (hasAnyWarnings)
147
- logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Configuration has warnings but is usable', 'info'));
150
+ if (command.strict) {
151
+ logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Configuration has warnings — strict mode: failing', 'info'));
152
+ process.exitCode = exitCodes_1.EXIT_VALIDATION_WARNINGS;
153
+ }
154
+ else
155
+ logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Configuration has warnings but is usable', 'info'));
148
156
  else
149
157
  logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Configuration is valid', 'success'));
150
158
  return;
@@ -159,11 +167,7 @@ const main = async () => {
159
167
  logger.debug(` Transforms: ${Object.keys(config.transforms || {}).length} pattern(s)`);
160
168
  logger.debug(` Prune enabled: ${config.prune}`);
161
169
  }
162
- if (command.formatOnly) {
163
- if (!config.outputFormat) {
164
- logger.log(chalk_1.default.yellow('\n⚠️ No outputFormat configured. Nothing to format.'));
165
- return;
166
- }
170
+ if (command.commandName === 'format') {
167
171
  logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Loading destination files...', 'loading'));
168
172
  const destinationResult = await (0, pipeline_1.loadFiles)({
169
173
  baseDirectory: config.destination,
@@ -174,14 +178,6 @@ const main = async () => {
174
178
  if (command.filter)
175
179
  destinationFiles = (0, fileFilter_1.filterFileMap)(destinationFiles, command.filter);
176
180
  logger.progress(`Loaded ${destinationFiles.size} destination file(s)`, 'success');
177
- if (command.listFiles) {
178
- const filesList = [...destinationFiles.keys()].toSorted();
179
- console.log(chalk_1.default.cyan('\n📋 Files to be formatted:\n'));
180
- console.log(chalk_1.default.yellow(`Destination files: ${filesList.length}`));
181
- for (const file of filesList)
182
- console.log(` ${chalk_1.default.dim(file)}`);
183
- return;
184
- }
185
181
  logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Formatting files...', 'info'));
186
182
  let formattedCount = 0;
187
183
  const errors = [];
@@ -215,13 +211,15 @@ const main = async () => {
215
211
  logger.error(`\n❌ Encountered ${errors.length} error(s):`, 'critical');
216
212
  for (const { path: errorPath, error } of errors)
217
213
  logger.error(` ${errorPath}: ${error.message}`, 'critical');
218
- process.exit(1);
214
+ process.exit(exitCodes_1.EXIT_CHANGES_SYNCED);
219
215
  }
216
+ if (formattedCount > 0)
217
+ process.exitCode = exitCodes_1.EXIT_CHANGES_SYNCED;
220
218
  return;
221
219
  }
222
220
  if (!config.source) {
223
- logger.error('\nSource folder is required for sync operations.', 'critical');
224
- process.exit(1);
221
+ logger.error('\nSource folder is required for this command.', 'critical');
222
+ process.exit(exitCodes_1.EXIT_CONFIG_ERROR);
225
223
  }
226
224
  const syncConfig = config;
227
225
  logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Loading files...', 'loading'));
@@ -262,16 +260,16 @@ const main = async () => {
262
260
  destinationFiles = filtered.destinationFiles;
263
261
  logger.progress(`--my filter (${command.myDays} days, author: "${author}") matched ${sourceFiles.size} source, ${destinationFiles.size} destination file(s)`, 'info');
264
262
  }
265
- if (command.listFiles) {
263
+ if (command.commandName === 'list-files') {
266
264
  const sourceFilesList = [...sourceFiles.keys()].toSorted();
267
265
  const destinationFilesList = [...destinationFiles.keys()].toSorted();
268
- console.log(chalk_1.default.cyan('\n📋 Files to be synced:\n'));
269
- console.log(chalk_1.default.green(`Source files: ${sourceFilesList.length}`));
266
+ console.log(ansi_colors_1.default.cyan('\n📋 Files to be synced:\n'));
267
+ console.log(ansi_colors_1.default.green(`Source files: ${sourceFilesList.length}`));
270
268
  for (const file of sourceFilesList)
271
- console.log(` ${chalk_1.default.dim(file)}`);
272
- console.log(chalk_1.default.yellow(`\nDestination files: ${destinationFilesList.length}`));
269
+ console.log(` ${ansi_colors_1.default.dim(file)}`);
270
+ console.log(ansi_colors_1.default.yellow(`\nDestination files: ${destinationFilesList.length}`));
273
271
  for (const file of destinationFilesList)
274
- console.log(` ${chalk_1.default.dim(file)}`);
272
+ console.log(` ${ansi_colors_1.default.dim(file)}`);
275
273
  return;
276
274
  }
277
275
  logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Computing differences...', 'info'));
@@ -279,33 +277,38 @@ const main = async () => {
279
277
  const diffResult = (0, fileFilter_1.filterDiffResultByMode)(rawDiffResult, command.mode);
280
278
  if (logger.shouldShow('debug'))
281
279
  logger.debug('Diff pipeline: parse → transforms → skipPath → normalize → compare');
282
- if (command.diff && !command.quiet)
283
- (0, reporters_1.showConsoleDiff)(diffResult, syncConfig);
284
- else {
285
- logger.log(` New files: ${diffResult.addedFiles.length}`);
286
- logger.log(` Deleted files: ${diffResult.deletedFiles.length}`);
287
- logger.log(` Changed files: ${diffResult.changedFiles.length}`);
288
- logger.log(` Unchanged files: ${diffResult.unchangedFiles.length}`);
280
+ if (command.commandName === 'diff') {
281
+ if (!command.quiet && !command.html && !command.json)
282
+ (0, reporters_1.showConsoleDiff)(diffResult, syncConfig);
283
+ if ((command.html || command.reportOutput) && !command.quiet)
284
+ await (0, reporters_1.generateHtmlReport)(diffResult, [], syncConfig, true, logger, undefined, command.reportOutput);
285
+ if (command.json)
286
+ (0, reporters_1.generateJsonReport)(diffResult, [], { violations: [], isValid: true }, syncConfig, true, package_json_1.default.version);
287
+ return;
289
288
  }
290
- if (command.suggest) {
289
+ logger.log(` New files: ${diffResult.addedFiles.length}`);
290
+ logger.log(` Deleted files: ${diffResult.deletedFiles.length}`);
291
+ logger.log(` Changed files: ${diffResult.changedFiles.length}`);
292
+ logger.log(` Unchanged files: ${diffResult.unchangedFiles.length}`);
293
+ if (command.commandName === 'suggest') {
291
294
  logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Analyzing differences for suggestions...', 'info'));
292
295
  try {
293
296
  const suggestions = (0, suggestionEngine_1.analyzeDifferencesForSuggestions)(diffResult, syncConfig, command.suggestThreshold);
294
297
  const yaml = (0, suggestionEngine_1.formatSuggestionsAsYaml)(suggestions);
295
- console.log(chalk_1.default.cyan('\n💡 Suggested Configuration:\n'));
298
+ console.log(ansi_colors_1.default.cyan('\n💡 Suggested Configuration:\n'));
296
299
  console.log(yaml);
297
300
  if (suggestions.metadata.changedFiles === 0)
298
- console.log(chalk_1.default.yellow('\nℹ️ No changes detected. Files are already in sync.'));
301
+ console.log(ansi_colors_1.default.yellow('\nℹ️ No changes detected. Files are already in sync.'));
299
302
  else {
300
- console.log(chalk_1.default.dim('\n---'));
301
- console.log(chalk_1.default.dim('💡 Tip: Copy relevant sections to your config.yaml and test with --dry-run'));
303
+ console.log(ansi_colors_1.default.dim('\n---'));
304
+ console.log(ansi_colors_1.default.dim('💡 Tip: Copy relevant sections to your config.yaml and test with run --dry-run'));
302
305
  }
303
306
  return;
304
307
  }
305
308
  catch (error) {
306
309
  if ((0, suggestionEngine_1.isSuggestionEngineError)(error)) {
307
310
  logger.error('\nFailed to generate suggestions: ' + error.message, 'critical');
308
- process.exit(1);
311
+ process.exit(exitCodes_1.EXIT_CHANGES_SYNCED);
309
312
  }
310
313
  throw error;
311
314
  }
@@ -323,41 +326,40 @@ const main = async () => {
323
326
  for (const violation of validationResult.violations)
324
327
  logger.stopRule(violation, 'error');
325
328
  logger.error('\nUse --force to override stop rules or --dry-run to preview changes.', 'critical');
326
- process.exit(1);
329
+ process.exit(exitCodes_1.EXIT_STOP_RULE_VIOLATION);
327
330
  }
328
331
  if (!command.dryRun && !command.quiet) {
329
- console.log(chalk_1.default.cyan('\n📊 Sync Summary:'));
330
- console.log(chalk_1.default.dim('─'.repeat(60)));
331
- console.log(` ${chalk_1.default.green('Added:')} ${diffResult.addedFiles.length} files`);
332
- console.log(` ${chalk_1.default.yellow('Changed:')} ${diffResult.changedFiles.length} files`);
333
- console.log(` ${chalk_1.default.red('Deleted:')} ${diffResult.deletedFiles.length} files (${syncConfig.prune ? 'prune enabled' : 'prune disabled'})`);
334
- console.log(` ${chalk_1.default.blue('Unchanged:')} ${diffResult.unchangedFiles.length} files`);
335
- console.log(chalk_1.default.dim('─'.repeat(60)));
332
+ console.log(ansi_colors_1.default.cyan('\n📊 Sync Summary:'));
333
+ console.log(ansi_colors_1.default.dim('─'.repeat(60)));
334
+ console.log(` ${ansi_colors_1.default.green('Added:')} ${diffResult.addedFiles.length} files`);
335
+ console.log(` ${ansi_colors_1.default.yellow('Changed:')} ${diffResult.changedFiles.length} files`);
336
+ console.log(` ${ansi_colors_1.default.red('Deleted:')} ${diffResult.deletedFiles.length} files (${syncConfig.prune ? 'prune enabled' : 'prune disabled'})`);
337
+ console.log(` ${ansi_colors_1.default.blue('Unchanged:')} ${diffResult.unchangedFiles.length} files`);
338
+ console.log(ansi_colors_1.default.dim('─'.repeat(60)));
336
339
  if (diffResult.deletedFiles.length > 0 && syncConfig.prune) {
337
- console.warn(chalk_1.default.red('⚠️ Warning: Prune is enabled. The following files will be permanently deleted:'));
340
+ console.warn(ansi_colors_1.default.red('⚠️ Warning: Prune is enabled. The following files will be permanently deleted:'));
338
341
  for (const f of diffResult.deletedFiles)
339
- console.warn(chalk_1.default.red(` - ${f}`));
342
+ console.warn(ansi_colors_1.default.red(` - ${f}`));
340
343
  }
341
344
  if (syncConfig.confirmationDelay > 0) {
342
345
  const totalSeconds = Math.ceil(syncConfig.confirmationDelay / 1000);
343
- console.log(chalk_1.default.dim('\nPress Ctrl+C to cancel.\n'));
346
+ console.log(ansi_colors_1.default.dim('\nPress Ctrl+C to cancel.\n'));
344
347
  for (let remaining = totalSeconds; remaining > 0; remaining--) {
345
- process.stdout.write(chalk_1.default.dim(` Proceeding in ${remaining}s...\r`));
348
+ process.stdout.write(ansi_colors_1.default.dim(` Proceeding in ${remaining}s...\r`));
346
349
  await new Promise((resolve) => setTimeout(resolve, 1000));
347
350
  }
348
351
  process.stdout.write(' '.repeat(40) + '\r');
349
352
  }
350
353
  else
351
- console.log(chalk_1.default.dim('\nPress Ctrl+C to cancel, or use --dry-run to preview changes first.\n'));
354
+ console.log(ansi_colors_1.default.dim('\nPress Ctrl+C to cancel, or use --dry-run to preview changes first.\n'));
352
355
  }
353
- const formattedFiles = await (0, pipeline_1.updateFiles)(diffResult, sourceFiles, destinationFiles, syncConfig, command.dryRun, command.skipFormat, logger);
354
- if (command.diffHtml && !command.quiet)
355
- await (0, reporters_1.generateHtmlReport)(diffResult, formattedFiles, syncConfig, command.dryRun, logger, command.dryRun ? validationResult : undefined);
356
- if (command.diffJson)
357
- (0, reporters_1.generateJsonReport)(diffResult, formattedFiles, validationResult, syncConfig, command.dryRun, package_json_1.default.version);
356
+ await (0, pipeline_1.updateFiles)(diffResult, sourceFiles, destinationFiles, syncConfig, command.dryRun, command.skipFormat, logger);
357
+ const hasChanges = diffResult.addedFiles.length > 0 || diffResult.deletedFiles.length > 0 || diffResult.changedFiles.length > 0;
358
+ if (hasChanges)
359
+ process.exitCode = exitCodes_1.EXIT_CHANGES_SYNCED;
358
360
  };
359
361
  let configHasRequiredVersion = false;
360
- (async () => {
362
+ void (async () => {
361
363
  try {
362
364
  await main();
363
365
  }
@@ -392,7 +394,10 @@ let configHasRequiredVersion = false;
392
394
  console.error('Unexpected error:', error.message);
393
395
  else
394
396
  console.error('Unexpected error:', error);
395
- process.exit(1);
397
+ if ((0, config_1.isConfigMergerError)(error) || (0, config_1.isConfigLoaderError)(error) || (0, config_1.isZodValidationError)(error))
398
+ process.exit(exitCodes_1.EXIT_CONFIG_ERROR);
399
+ else
400
+ process.exit(exitCodes_1.EXIT_CHANGES_SYNCED);
396
401
  }
397
402
  finally {
398
403
  const command = (0, commandLine_1.parseCommandLine)();
package/dist/logger.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { FileOperation, ProgressStyle, ViolationMode } from './consoleFormatter';
2
- import { StopRuleViolation } from './pipeline/stopRulesValidator';
1
+ import { type FileOperation, type ProgressStyle, type ViolationMode } from './consoleFormatter';
2
+ import { type StopRuleViolation } from './pipeline/stopRulesValidator';
3
3
  export type VerbosityLevel = 'quiet' | 'normal' | 'verbose';
4
4
  export type OutputCategory = 'critical' | 'normal' | 'debug' | 'special';
5
5
  export type LoggerOptions = {
@@ -7,7 +7,7 @@ export type LoggerOptions = {
7
7
  isDiffJson: boolean;
8
8
  };
9
9
  export declare class Logger {
10
- private level;
10
+ private readonly level;
11
11
  constructor(options: LoggerOptions);
12
12
  shouldShow(category: OutputCategory): boolean;
13
13
  log(message: string, category?: OutputCategory): void;
@@ -1,5 +1,6 @@
1
- import { Config, FixedValueConfig, FixedValueRule, TransformConfig } from '../config';
2
- import { FileMap } from './fileLoader';
1
+ import { type Config, type FixedValueConfig, type FixedValueRule, type TransformConfig } from '../config';
2
+ import type { Logger } from '../logger';
3
+ import { type FileMap } from './fileLoader';
3
4
  export interface FileDiffResult {
4
5
  addedFiles: AddedFile[];
5
6
  deletedFiles: string[];
@@ -15,8 +16,8 @@ export interface ChangedFile {
15
16
  processedDestContent: unknown;
16
17
  rawParsedSource: unknown;
17
18
  rawParsedDest: unknown;
18
- skipPaths: string[];
19
- fixedValueRules: FixedValueRule[];
19
+ skipPaths?: string[];
20
+ fixedValueRules?: FixedValueRule[];
20
21
  normalizedSource?: unknown;
21
22
  normalizedDest?: unknown;
22
23
  parsedSource?: unknown;
@@ -55,5 +56,5 @@ export declare class FileDiffError extends FileDiffErrorClass {
55
56
  }
56
57
  export declare const isFileDiffError: (error: unknown) => error is FileDiffError;
57
58
  export declare const getSkipPathsForFile: (filePath: string, skipPath?: Record<string, string[]>) => string[];
58
- export declare const computeFileDiff: (sourceFiles: FileMap, destinationFiles: FileMap, config: Config, logger?: import("../logger").Logger, originalPaths?: Map<string, string>) => FileDiffResult;
59
+ export declare const computeFileDiff: (sourceFiles: FileMap, destinationFiles: FileMap, config: Config, logger?: Logger, originalPaths?: Map<string, string>) => FileDiffResult;
59
60
  export {};
@@ -1,4 +1,5 @@
1
1
  import type { TransformConfig } from '../config';
2
+ import type { Logger } from '../logger';
2
3
  export interface FileLoaderOptions {
3
4
  baseDirectory: string;
4
5
  include: string[];
@@ -29,5 +30,5 @@ declare const FileLoaderErrorClass: {
29
30
  export declare class FileLoaderError extends FileLoaderErrorClass {
30
31
  }
31
32
  export declare const isFileLoaderError: (error: unknown) => error is FileLoaderError;
32
- export declare const loadFiles: (options: FileLoaderOptions, logger?: import("../logger").Logger) => Promise<FileLoaderResult>;
33
+ export declare const loadFiles: (options: FileLoaderOptions, logger?: Logger) => Promise<FileLoaderResult>;
33
34
  export {};
@@ -155,8 +155,8 @@ const readFilesIntoMap = async (baseDirectory, absoluteFilePaths) => {
155
155
  };
156
156
  const loadFiles = async (options, logger) => {
157
157
  const absoluteBaseDirectory = await validateAndResolveBaseDirectory(options.baseDirectory);
158
- const includePatterns = options.include ?? ['**/*'];
159
- const excludePatterns = options.exclude ?? [];
158
+ const includePatterns = options.include;
159
+ const excludePatterns = options.exclude;
160
160
  const files = await findMatchingFiles(absoluteBaseDirectory, includePatterns, excludePatterns, options.transforms, options.skipExclude);
161
161
  if (logger?.shouldShow('debug')) {
162
162
  logger.debug('Glob matching:');
@@ -1,7 +1,7 @@
1
- import { Config } from '../config';
2
- import { Logger } from '../logger';
3
- import { ChangedFile, FileDiffResult } from './fileDiff';
4
- import { FileMap } from './fileLoader';
1
+ import { type Config } from '../config';
2
+ import { type Logger } from '../logger';
3
+ import { type ChangedFile, type FileDiffResult } from './fileDiff';
4
+ import { type FileMap } from './fileLoader';
5
5
  export interface FileUpdateError {
6
6
  operation: 'add' | 'update' | 'delete';
7
7
  path: string;
@@ -113,7 +113,7 @@ const deepMerge = (fullTarget, filteredSource, filteredTarget, currentPath = [],
113
113
  if (typeof filteredSource === 'object' && typeof fullTarget === 'object') {
114
114
  const sourceObject = filteredSource;
115
115
  const fullTargetObject = fullTarget;
116
- const filteredTargetObject = filteredTarget || {};
116
+ const filteredTargetObject = (typeof filteredTarget === 'object' && filteredTarget ? filteredTarget : {});
117
117
  const result = { ...sourceObject };
118
118
  const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
119
119
  for (const [key, value] of Object.entries(fullTargetObject))
@@ -1,4 +1,5 @@
1
1
  import type { StopRule } from '../config';
2
+ import type { Logger } from '../logger';
2
3
  import type { FileDiffResult } from './fileDiff';
3
4
  declare const StopRulesValidatorErrorClass: {
4
5
  new (message: string, options?: import("../utils").ErrorOptions): {
@@ -37,5 +38,5 @@ export interface ValidationContext {
37
38
  filePath: string;
38
39
  configDirectory?: string;
39
40
  }
40
- export declare const validateStopRules: (diffResult: FileDiffResult, stopRulesConfig?: Record<string, StopRule[]>, configDirectory?: string, logger?: import("../logger").Logger) => ValidationResult;
41
+ export declare const validateStopRules: (diffResult: FileDiffResult, stopRulesConfig?: Record<string, StopRule[]>, configDirectory?: string, logger?: Logger) => ValidationResult;
41
42
  export {};
@@ -89,16 +89,14 @@ const validateRule = (context) => {
89
89
  return validateRegex(rule, oldValue, updatedValue, filePath);
90
90
  if (rule.type === 'regexFile')
91
91
  return validateRegexFile(rule, oldValue, updatedValue, filePath, configDirectory);
92
- if (rule.type === 'regexFileKey')
93
- return validateRegexFileKey(rule, oldValue, updatedValue, filePath, configDirectory);
92
+ return validateRegexFileKey(rule, oldValue, updatedValue, filePath, configDirectory);
94
93
  }
95
94
  else {
96
95
  if (rule.type === 'regex')
97
96
  return validateRegexGlobal(rule, oldData, updatedData, filePath);
98
97
  if (rule.type === 'regexFile')
99
98
  return validateRegexFileGlobal(rule, oldData, updatedData, filePath, configDirectory);
100
- if (rule.type === 'regexFileKey')
101
- return validateRegexFileKeyGlobal(rule, oldData, updatedData, filePath, configDirectory);
99
+ return validateRegexFileKeyGlobal(rule, oldData, updatedData, filePath, configDirectory);
102
100
  }
103
101
  if (!rule.path)
104
102
  return undefined;
@@ -1,4 +1,4 @@
1
- import { OutputFormat } from '../config';
1
+ import { type OutputFormat } from '../config';
2
2
  export declare const YamlFormatterError: {
3
3
  new (message: string, options?: import("../utils/errors").ErrorOptions): {
4
4
  [key: string]: unknown;
@@ -33,31 +33,31 @@ const getFormattingRules = (filePath, outputFormat) => {
33
33
  const arraySort = [];
34
34
  const quoteValues = [];
35
35
  const allPatterns = new Set();
36
- if (outputFormat?.keyOrders)
36
+ if (outputFormat.keyOrders)
37
37
  for (const pattern of Object.keys(outputFormat.keyOrders))
38
38
  allPatterns.add(pattern);
39
- if (outputFormat?.keySort)
39
+ if (outputFormat.keySort)
40
40
  for (const pattern of Object.keys(outputFormat.keySort))
41
41
  allPatterns.add(pattern);
42
- if (outputFormat?.arraySort)
42
+ if (outputFormat.arraySort)
43
43
  for (const pattern of Object.keys(outputFormat.arraySort))
44
44
  allPatterns.add(pattern);
45
- if (outputFormat?.quoteValues)
45
+ if (outputFormat.quoteValues)
46
46
  for (const pattern of Object.keys(outputFormat.quoteValues))
47
47
  allPatterns.add(pattern);
48
48
  for (const pattern of allPatterns) {
49
49
  if (!patternMatcher_1.globalMatcher.match(filePath, pattern))
50
50
  continue;
51
- const keyOrder = outputFormat?.keyOrders?.[pattern];
51
+ const keyOrder = outputFormat.keyOrders?.[pattern];
52
52
  if (keyOrder)
53
53
  keyOrders.push(keyOrder);
54
- const keySortRule = outputFormat?.keySort?.[pattern];
54
+ const keySortRule = outputFormat.keySort?.[pattern];
55
55
  if (keySortRule)
56
56
  keySort.push(keySortRule);
57
- const arrayRule = outputFormat?.arraySort?.[pattern];
57
+ const arrayRule = outputFormat.arraySort?.[pattern];
58
58
  if (arrayRule)
59
59
  arraySort.push(arrayRule);
60
- const quoteValue = outputFormat?.quoteValues?.[pattern];
60
+ const quoteValue = outputFormat.quoteValues?.[pattern];
61
61
  if (quoteValue)
62
62
  quoteValues.push(quoteValue);
63
63
  }
@@ -317,7 +317,7 @@ const isPotentialMatch = (currentPath, targetPath) => {
317
317
  return true;
318
318
  };
319
319
  const sortYamlSeq = (seq, sortByField, order) => {
320
- if (!seq.items || seq.items.length === 0)
320
+ if (seq.items.length === 0)
321
321
  return;
322
322
  const firstItem = seq.items.find((item) => item != undefined);
323
323
  if (firstItem !== undefined) {
@@ -1,37 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
@@ -46,7 +13,7 @@ exports.BrowserLauncherError = BrowserLauncherError;
46
13
  exports.isBrowserLauncherError = (0, errors_1.createErrorTypeGuard)(BrowserLauncherError);
47
14
  const openInBrowser = async (filePath) => {
48
15
  try {
49
- const openModule = await Promise.resolve().then(() => __importStar(require('open')));
16
+ const openModule = await import('open');
50
17
  const open = openModule.default;
51
18
  const absolutePath = node_path_1.default.resolve(filePath);
52
19
  await open(absolutePath);
@@ -1,3 +1,3 @@
1
- import { Config } from '../config';
2
- import { FileDiffResult } from '../pipeline';
1
+ import { type Config } from '../config';
2
+ import { type FileDiffResult } from '../pipeline';
3
3
  export declare const showConsoleDiff: (diffResult: FileDiffResult, config: Config) => void;