helm-env-delta 1.9.2 → 1.10.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.
package/README.md CHANGED
@@ -56,7 +56,7 @@ HelmEnvDelta (`hed`) automates environment synchronization for GitOps workflows
56
56
 
57
57
  📊 **Multiple Reports** - Console, HTML (visual), and JSON (CI/CD) output formats.
58
58
 
59
- 🔍 **Discovery Tools** - Preview files (`--list-files`), inspect config (`--show-config`), validate with comprehensive warnings including unused pattern detection.
59
+ 🔍 **Discovery Tools** - Preview files (`-l`), inspect config (`--show-config`), filter by filename/content (`-f`), filter by change type (`-m`), validate with comprehensive warnings including unused pattern detection.
60
60
 
61
61
  💡 **Smart Suggestions** - Heuristic analysis (`--suggest`) detects patterns and recommends transforms and stop rules automatically. Control sensitivity with `--suggest-threshold`.
62
62
 
@@ -102,28 +102,28 @@ transforms:
102
102
  ### 2️⃣ Preview Changes
103
103
 
104
104
  ```bash
105
- helm-env-delta --config config.yaml --dry-run --diff
105
+ hed -c config.yaml -D -d
106
106
  ```
107
107
 
108
108
  ### 3️⃣ Execute Sync
109
109
 
110
110
  ```bash
111
- helm-env-delta --config config.yaml
111
+ hed -c config.yaml
112
112
  ```
113
113
 
114
114
  ### 4️⃣ Review in Browser
115
115
 
116
116
  ```bash
117
- helm-env-delta --config config.yaml --diff-html
117
+ hed -c config.yaml -H
118
118
  ```
119
119
 
120
120
  ### 5️⃣ Get Smart Suggestions (Optional)
121
121
 
122
122
  ```bash
123
- helm-env-delta --config config.yaml --suggest
123
+ hed -c config.yaml --suggest
124
124
 
125
125
  # Control suggestion sensitivity (0-1, default: 0.3)
126
- helm-env-delta --config config.yaml --suggest --suggest-threshold 0.7
126
+ hed -c config.yaml --suggest --suggest-threshold 0.7
127
127
  ```
128
128
 
129
129
  Analyzes differences and suggests transforms and stop rules automatically with configurable confidence filtering.
@@ -281,6 +281,28 @@ helm-env-delta --config example/6-fixed-values/config.yaml --dry-run --diff
281
281
  - Array filter operators (`env[name=LOG_LEVEL].value`)
282
282
  - Combining with skipPath and transforms
283
283
 
284
+ ### 🎨 Example 7: Format-Only Mode
285
+
286
+ Format YAML files without syncing. No source directory required - perfect for standardizing existing files.
287
+
288
+ ```bash
289
+ # Preview which files would be formatted
290
+ helm-env-delta --config example/7-format-only/config.yaml --format-only --list-files
291
+
292
+ # Preview formatting changes
293
+ helm-env-delta --config example/7-format-only/config.yaml --format-only --dry-run
294
+
295
+ # Apply formatting
296
+ helm-env-delta --config example/7-format-only/config.yaml --format-only
297
+ ```
298
+
299
+ **Features shown:**
300
+
301
+ - Format-only mode (no source required)
302
+ - Combining `--format-only` with `--list-files` to preview files
303
+ - Key ordering, array sorting, indentation standardization
304
+ - Minimal config for formatting existing files
305
+
284
306
  ---
285
307
 
286
308
  ## 💡 Smart Configuration Suggestions (Heuristic)
@@ -647,7 +669,7 @@ Standardize YAML across all environments.
647
669
  ```yaml
648
670
  outputFormat:
649
671
  indent: 2 # Indentation size
650
- keySeparator: true # Blank line between top-level keys
672
+ keySeparator: true # Blank line between top-level keys (or second-level keys when single top-level key)
651
673
 
652
674
  keyOrders: # Custom key ordering
653
675
  'apps/*.yaml':
@@ -731,63 +753,80 @@ hed --config <file> [options] # Short alias
731
753
 
732
754
  ### Options
733
755
 
734
- | Flag | Description |
735
- | --------------------------- | --------------------------------------------------- |
736
- | `--config <path>` | **Required** - Configuration file |
737
- | `--validate` | Validate config and pattern usage (shows warnings) |
738
- | `--suggest` | Analyze differences and suggest config updates |
739
- | `--suggest-threshold <0-1>` | Minimum confidence for suggestions (default: 0.3) |
740
- | `--dry-run` | Preview changes without writing files |
741
- | `--force` | Override stop rules |
742
- | `--diff` | Show console diff |
743
- | `--diff-html` | Generate HTML report (opens in browser) |
744
- | `--diff-json` | Output JSON to stdout (pipe to jq) |
745
- | `--list-files` | List source/destination files without processing |
746
- | `--show-config` | Display resolved config after inheritance |
747
- | `--format-only` | Format destination files only (source not required) |
748
- | `--skip-format` | Skip YAML formatting during sync |
749
- | `--no-color` | Disable colored output (CI/accessibility) |
750
- | `--verbose` | Show detailed debug info |
751
- | `--quiet` | Suppress output except errors |
756
+ | Flag | Short | Description |
757
+ | --------------------------- | ----- | ------------------------------------------------------------------- |
758
+ | `--config <path>` | `-c` | **Required** - Configuration file |
759
+ | `--validate` | | Validate config and pattern usage (shows warnings) |
760
+ | `--suggest` | | Analyze differences and suggest config updates |
761
+ | `--suggest-threshold <0-1>` | | Minimum confidence for suggestions (default: 0.3) |
762
+ | `--dry-run` | `-D` | Preview changes without writing files |
763
+ | `--force` | | Override stop rules |
764
+ | `--diff` | `-d` | Show console diff |
765
+ | `--diff-html` | `-H` | Generate HTML report (opens in browser) |
766
+ | `--diff-json` | `-J` | Output JSON to stdout (pipe to jq) |
767
+ | `--list-files` | `-l` | List files without processing (takes precedence over --format-only) |
768
+ | `--show-config` | | Display resolved config after inheritance |
769
+ | `--format-only` | | Format destination files only (source not required) |
770
+ | `--skip-format` | `-S` | Skip YAML formatting during sync |
771
+ | `--filter <string>` | `-f` | Filter files by filename or content (case-insensitive) |
772
+ | `--mode <type>` | `-m` | Filter by change type: new, modified, deleted, all (default: all) |
773
+ | `--no-color` | | Disable colored output (CI/accessibility) |
774
+ | `--verbose` | | Show detailed debug info |
775
+ | `--quiet` | | Suppress output except errors |
752
776
 
753
777
  ### Examples
754
778
 
755
779
  ```bash
756
780
  # Validate configuration (shows warnings)
757
- hed --config config.yaml --validate
781
+ hed -c config.yaml --validate
758
782
 
759
783
  # Get smart configuration suggestions
760
- hed --config config.yaml --suggest
784
+ hed -c config.yaml --suggest
761
785
 
762
786
  # Get only high-confidence suggestions
763
- hed --config config.yaml --suggest --suggest-threshold 0.7
787
+ hed -c config.yaml --suggest --suggest-threshold 0.7
764
788
 
765
789
  # Preview files that will be synced
766
- hed --config config.yaml --list-files
790
+ hed -c config.yaml -l
767
791
 
768
792
  # Display resolved config (after inheritance)
769
- hed --config config.yaml --show-config
793
+ hed -c config.yaml --show-config
770
794
 
771
795
  # Preview with diff
772
- hed --config config.yaml --dry-run --diff
796
+ hed -c config.yaml -D -d
773
797
 
774
798
  # Visual HTML report
775
- hed --config config.yaml --diff-html
799
+ hed -c config.yaml -H
776
800
 
777
801
  # CI/CD integration (no colors)
778
- hed --config config.yaml --diff-json --no-color | jq '.summary'
802
+ hed -c config.yaml -J --no-color | jq '.summary'
779
803
 
780
804
  # Execute sync
781
- hed --config config.yaml
805
+ hed -c config.yaml
782
806
 
783
807
  # Force override stop rules
784
- hed --config config.yaml --force
808
+ hed -c config.yaml --force
809
+
810
+ # Filter to only process files matching 'prod'
811
+ hed -c config.yaml -f prod -d
812
+
813
+ # Sync only new files
814
+ hed -c config.yaml -m new
815
+
816
+ # Preview modified files only
817
+ hed -c config.yaml -m modified -D -d
818
+
819
+ # Combine filter and mode
820
+ hed -c config.yaml -f deployment -m modified -D -d
785
821
 
786
822
  # Format destination files only (no sync, source not required in config)
787
- hed --config config.yaml --format-only
823
+ hed -c config.yaml --format-only
788
824
 
789
825
  # Preview format changes
790
- hed --config config.yaml --format-only --dry-run
826
+ hed -c config.yaml --format-only -D
827
+
828
+ # List files that would be formatted (--list-files takes precedence)
829
+ hed -c config.yaml --format-only -l
791
830
 
792
831
  # Format-only config example (no source needed):
793
832
  # destination: './prod'
@@ -816,13 +855,13 @@ flowchart LR
816
855
 
817
856
  ```bash
818
857
  # 1. Preview changes
819
- hed --config config.yaml --dry-run --diff
858
+ hed -c config.yaml -D -d
820
859
 
821
860
  # 2. Review in browser
822
- hed --config config.yaml --diff-html
861
+ hed -c config.yaml -H
823
862
 
824
863
  # 3. Execute sync
825
- hed --config config.yaml
864
+ hed -c config.yaml
826
865
 
827
866
  # 4. Git workflow
828
867
  git add prod/
@@ -1,3 +1,4 @@
1
+ export type ChangeMode = 'new' | 'modified' | 'deleted' | 'all';
1
2
  export type SyncCommand = {
2
3
  config: string;
3
4
  dryRun: boolean;
@@ -15,5 +16,7 @@ export type SyncCommand = {
15
16
  noColor: boolean;
16
17
  suggest: boolean;
17
18
  suggestThreshold: number;
19
+ filter?: string;
20
+ mode: ChangeMode;
18
21
  };
19
22
  export declare const parseCommandLine: (argv?: string[]) => SyncCommand;
@@ -13,19 +13,21 @@ const parseCommandLine = (argv) => {
13
13
  .description('Environment-aware YAML delta and sync for GitOps workflows')
14
14
  .version(package_json_1.default.version)
15
15
  .requiredOption('-c, --config <file>', 'Path to YAML configuration file')
16
- .option('--dry-run', 'Preview changes without writing files', false)
16
+ .option('-D, --dry-run', 'Preview changes without writing files', false)
17
17
  .option('--force', 'Override stop rules and proceed with changes', false)
18
- .option('--diff', 'Display console diff for changed files', false)
19
- .option('--diff-html', 'Generate and open HTML diff report in browser', false)
20
- .option('--diff-json', 'Output diff as JSON to stdout', false)
21
- .option('--skip-format', 'Skip YAML formatting (outputFormat section)', false)
18
+ .option('-d, --diff', 'Display console diff for changed files', false)
19
+ .option('-H, --diff-html', 'Generate and open HTML diff report in browser', false)
20
+ .option('-J, --diff-json', 'Output diff as JSON to stdout', false)
21
+ .option('-S, --skip-format', 'Skip YAML formatting (outputFormat section)', false)
22
22
  .option('--format-only', 'Format YAML files in destination without syncing', false)
23
23
  .option('--validate', 'Validate configuration file and exit', false)
24
- .option('--list-files', 'List files that would be synced without processing diffs', false)
24
+ .option('-l, --list-files', 'List files that would be synced without processing diffs', false)
25
25
  .option('--show-config', 'Display resolved configuration after inheritance and exit', false)
26
26
  .option('--suggest', 'Analyze differences and suggest transforms and stop rules', false)
27
27
  .option('--suggest-threshold <number>', 'Minimum confidence for suggestions (0-1, default: 0.3)', '0.3')
28
28
  .option('--no-color', 'Disable colored output')
29
+ .option('-f, --filter <string>', 'Filter files by filename or content (case-insensitive)')
30
+ .option('-m, --mode <type>', 'Filter by change type: new, modified, deleted, all', 'all')
29
31
  .option('--verbose', 'Show detailed debug information', false)
30
32
  .option('--quiet', 'Suppress all output except critical errors', false)
31
33
  .addHelpText('after', `
@@ -45,6 +47,15 @@ Examples:
45
47
  # CI/CD usage with JSON output
46
48
  $ helm-env-delta --config config.yaml --diff-json | jq '.summary'
47
49
 
50
+ # Filter to only process files matching 'prod'
51
+ $ helm-env-delta --config config.yaml -f prod --diff
52
+
53
+ # Sync only new files
54
+ $ helm-env-delta --config config.yaml --mode new
55
+
56
+ # Preview modified files only
57
+ $ helm-env-delta --config config.yaml --mode modified --dry-run --diff
58
+
48
59
  Documentation: https://github.com/balazscsaba2006/helm-env-delta
49
60
  `);
50
61
  program.showSuggestionAfterError(true);
@@ -63,6 +74,11 @@ Documentation: https://github.com/balazscsaba2006/helm-env-delta
63
74
  console.error('Error: --suggest-threshold must be a number between 0 and 1');
64
75
  process.exit(1);
65
76
  }
77
+ const validModes = ['new', 'modified', 'deleted', 'all'];
78
+ if (!validModes.includes(options['mode'])) {
79
+ console.error('Error: --mode must be one of: ' + validModes.join(', '));
80
+ process.exit(1);
81
+ }
66
82
  return {
67
83
  config: options['config'],
68
84
  dryRun: options['dryRun'],
@@ -79,7 +95,9 @@ Documentation: https://github.com/balazscsaba2006/helm-env-delta
79
95
  verbose: options['verbose'],
80
96
  quiet: options['quiet'],
81
97
  suggest: options['suggest'],
82
- suggestThreshold: threshold
98
+ suggestThreshold: threshold,
99
+ filter: options['filter'],
100
+ mode: options['mode']
83
101
  };
84
102
  };
85
103
  exports.parseCommandLine = parseCommandLine;
@@ -5,9 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.showConsoleDiff = void 0;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
- const yaml_1 = __importDefault(require("yaml"));
9
8
  const fileDiff_1 = require("./fileDiff");
10
- const arrayDiffProcessor_1 = require("./utils/arrayDiffProcessor");
11
9
  const diffGenerator_1 = require("./utils/diffGenerator");
12
10
  const fileType_1 = require("./utils/fileType");
13
11
  const serialization_1 = require("./utils/serialization");
@@ -29,7 +27,7 @@ const formatAddedFiles = (files) => {
29
27
  if (files.length === 0)
30
28
  return '';
31
29
  const header = chalk_1.default.green.bold(`\nAdded Files (${files.length}):`);
32
- const fileList = files.map((file) => chalk_1.default.green(` + ${file}`)).join('\n');
30
+ const fileList = files.map((file) => chalk_1.default.green(` + ${file.path}`)).join('\n');
33
31
  return `${header}\n${fileList}\n`;
34
32
  };
35
33
  const formatDeletedFiles = (files) => {
@@ -39,30 +37,6 @@ const formatDeletedFiles = (files) => {
39
37
  const fileList = files.map((file) => chalk_1.default.red(` - ${file}`)).join('\n');
40
38
  return `${header}\n${fileList}\n`;
41
39
  };
42
- const formatArrayDiff = (change) => {
43
- let output = '';
44
- if (change.removed.length > 0) {
45
- output += chalk_1.default.red.bold(`\n Removed (${change.removed.length}):\n`);
46
- for (const item of change.removed) {
47
- const yaml = yaml_1.default.stringify(item, { indent: 4 });
48
- const lines = yaml.split('\n').filter((l) => l.trim());
49
- output += lines.map((l) => chalk_1.default.red(` - ${l}`)).join('\n');
50
- output += '\n';
51
- }
52
- }
53
- if (change.added.length > 0) {
54
- output += chalk_1.default.green.bold(`\n Added (${change.added.length}):\n`);
55
- for (const item of change.added) {
56
- const yaml = yaml_1.default.stringify(item, { indent: 4 });
57
- const lines = yaml.split('\n').filter((l) => l.trim());
58
- output += lines.map((l) => chalk_1.default.green(` + ${l}`)).join('\n');
59
- output += '\n';
60
- }
61
- }
62
- if (change.unchanged.length > 0)
63
- output += chalk_1.default.gray(`\n Unchanged: ${change.unchanged.length} items\n`);
64
- return output;
65
- };
66
40
  const formatChangedFile = (file, config) => {
67
41
  const isYaml = (0, fileType_1.isYamlFile)(file.path);
68
42
  const separator = chalk_1.default.yellow('━'.repeat(60));
@@ -70,48 +44,21 @@ const formatChangedFile = (file, config) => {
70
44
  const skipPathInfo = skipPaths.length > 0
71
45
  ? chalk_1.default.dim(`SkipPath patterns applied: ${skipPaths.join(', ')}`)
72
46
  : chalk_1.default.dim('No skipPath patterns applied');
73
- if (!isYaml) {
74
- const destinationContent = String(file.processedDestContent);
75
- const sourceContent = String(file.processedSourceContent);
76
- const unifiedDiff = (0, diffGenerator_1.generateUnifiedDiff)(file.path, destinationContent, sourceContent);
77
- const colorizedDiff = colorizeUnifiedDiff(unifiedDiff);
78
- return `
79
- ${separator}
80
- ${chalk_1.default.yellow.bold(`File: ${file.path}`)}
81
- ${skipPathInfo}
82
-
83
- ${colorizedDiff}
84
- `;
85
- }
86
- const arrayInfo = (0, arrayDiffProcessor_1.detectArrayChanges)(file);
87
- if (!arrayInfo.hasArrays) {
88
- const destinationContent = (0, serialization_1.serializeForDiff)(file.processedDestContent, true);
89
- const sourceContent = (0, serialization_1.serializeForDiff)(file.processedSourceContent, true);
90
- const unifiedDiff = (0, diffGenerator_1.generateUnifiedDiff)(file.path, destinationContent, sourceContent);
91
- const colorizedDiff = colorizeUnifiedDiff(unifiedDiff);
92
- return `
47
+ const destinationContent = isYaml
48
+ ? (0, serialization_1.serializeForDiff)(file.processedDestContent, true)
49
+ : String(file.processedDestContent);
50
+ const sourceContent = isYaml
51
+ ? (0, serialization_1.serializeForDiff)(file.processedSourceContent, true)
52
+ : String(file.processedSourceContent);
53
+ const unifiedDiff = (0, diffGenerator_1.generateUnifiedDiff)(file.path, destinationContent, sourceContent);
54
+ const colorizedDiff = colorizeUnifiedDiff(unifiedDiff);
55
+ return `
93
56
  ${separator}
94
57
  ${chalk_1.default.yellow.bold(`File: ${file.path}`)}
95
58
  ${skipPathInfo}
96
59
 
97
60
  ${colorizedDiff}
98
61
  `;
99
- }
100
- let output = `\n${separator}\n${chalk_1.default.yellow.bold(`File: ${file.path}`)}\n${skipPathInfo}\n`;
101
- const destinationContent = (0, serialization_1.serializeForDiff)(file.processedDestContent, true);
102
- const sourceContent = (0, serialization_1.serializeForDiff)(file.processedSourceContent, true);
103
- const unifiedDiff = (0, diffGenerator_1.generateUnifiedDiff)(file.path, destinationContent, sourceContent);
104
- const colorizedDiff = colorizeUnifiedDiff(unifiedDiff);
105
- output += `\n${colorizedDiff}\n`;
106
- if (arrayInfo.hasChanges) {
107
- output += chalk_1.default.cyan.bold('\nArray-specific details:\n');
108
- for (const change of arrayInfo.changes) {
109
- const pathString = change.path.join('.');
110
- output += chalk_1.default.cyan(`\n ${pathString}:\n`);
111
- output += formatArrayDiff(change);
112
- }
113
- }
114
- return output;
115
62
  };
116
63
  const formatChangedFiles = (files, config) => {
117
64
  if (files.length === 0)
@@ -1,7 +1,7 @@
1
1
  import { Config, FixedValueConfig, TransformConfig } from './configFile';
2
2
  import { FileMap } from './fileLoader';
3
3
  export interface FileDiffResult {
4
- addedFiles: string[];
4
+ addedFiles: AddedFile[];
5
5
  deletedFiles: string[];
6
6
  changedFiles: ChangedFile[];
7
7
  unchangedFiles: string[];
@@ -21,6 +21,12 @@ export interface ChangedFile {
21
21
  parsedSource?: unknown;
22
22
  parsedDest?: unknown;
23
23
  }
24
+ export interface AddedFile {
25
+ path: string;
26
+ originalPath?: string;
27
+ content: string;
28
+ processedContent: string;
29
+ }
24
30
  export interface ProcessYamlOptions {
25
31
  filePath: string;
26
32
  sourceContent: string;
package/dist/fileDiff.js CHANGED
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.computeFileDiff = exports.getSkipPathsForFile = exports.isFileDiffError = exports.FileDiffError = void 0;
7
7
  const yaml_1 = __importDefault(require("yaml"));
8
+ const commentOnlyDetector_1 = require("./utils/commentOnlyDetector");
8
9
  const deepEqual_1 = require("./utils/deepEqual");
9
10
  const errors_1 = require("./utils/errors");
10
11
  const fileType_1 = require("./utils/fileType");
@@ -13,6 +14,7 @@ const jsonPath_1 = require("./utils/jsonPath");
13
14
  const patternMatcher_1 = require("./utils/patternMatcher");
14
15
  const serialization_1 = require("./utils/serialization");
15
16
  const transformer_1 = require("./utils/transformer");
17
+ const yamlFormatter_1 = require("./yamlFormatter");
16
18
  const FileDiffErrorClass = (0, errors_1.createErrorClass)('File Diff Error', {
17
19
  YAML_PARSE_ERROR: 'YAML file could not be parsed'
18
20
  });
@@ -20,11 +22,36 @@ class FileDiffError extends FileDiffErrorClass {
20
22
  }
21
23
  exports.FileDiffError = FileDiffError;
22
24
  exports.isFileDiffError = (0, errors_1.createErrorTypeGuard)(FileDiffError);
23
- const detectAddedFiles = (sourceFiles, destinationFiles) => {
25
+ const processAddedFileContent = (filePath, content, transforms, fixedValues, outputFormat) => {
26
+ if (!(0, fileType_1.isYamlFile)(filePath))
27
+ return content;
28
+ try {
29
+ const parsed = yaml_1.default.parse(content);
30
+ const transformed = (0, transformer_1.applyTransforms)(parsed, filePath, transforms);
31
+ const fixedValueRules = (0, fixedValues_1.getFixedValuesForFile)(filePath, fixedValues);
32
+ if (fixedValueRules.length > 0)
33
+ (0, fixedValues_1.applyFixedValues)(transformed, fixedValueRules);
34
+ let processed = yaml_1.default.stringify(transformed);
35
+ processed = (0, yamlFormatter_1.formatYaml)(processed, filePath, outputFormat);
36
+ return processed;
37
+ }
38
+ catch {
39
+ return content;
40
+ }
41
+ };
42
+ const detectAddedFiles = (sourceFiles, destinationFiles, config, originalPaths) => {
24
43
  const addedFiles = [];
25
- for (const path of sourceFiles.keys())
26
- if (!destinationFiles.has(path))
27
- addedFiles.push(path);
44
+ for (const [path, content] of sourceFiles.entries())
45
+ if (!destinationFiles.has(path)) {
46
+ const originalPath = originalPaths?.get(path);
47
+ const processedContent = processAddedFileContent(path, content, config.transforms, config.fixedValues, config.outputFormat);
48
+ addedFiles.push({
49
+ path,
50
+ originalPath,
51
+ content,
52
+ processedContent
53
+ });
54
+ }
28
55
  return addedFiles;
29
56
  };
30
57
  const detectDeletedFiles = (sourceFiles, destinationFiles) => {
@@ -143,6 +170,8 @@ const processYamlFile = (options) => {
143
170
  parseError.message += destinationHints;
144
171
  throw parseError;
145
172
  }
173
+ if ((0, commentOnlyDetector_1.isCommentOnlyContent)(destinationContent))
174
+ return undefined;
146
175
  const sourceTransformed = (0, transformer_1.applyTransforms)(sourceParsed, filePath, transforms);
147
176
  const fixedValueRules = (0, fixedValues_1.getFixedValuesForFile)(filePath, fixedValues);
148
177
  if (fixedValueRules.length > 0)
@@ -225,7 +254,7 @@ const computeFileDiff = (sourceFiles, destinationFiles, config, logger, original
225
254
  if (skipPathCount > 0)
226
255
  logger.debug(` SkipPath patterns: ${skipPathCount}`);
227
256
  }
228
- const addedFiles = detectAddedFiles(sourceFiles, destinationFiles);
257
+ const addedFiles = detectAddedFiles(sourceFiles, destinationFiles, config, originalPaths);
229
258
  const deletedFiles = config.prune ? detectDeletedFiles(sourceFiles, destinationFiles) : [];
230
259
  const { changedFiles, unchangedFiles } = processChangedFiles(sourceFiles, destinationFiles, config.skipPath, config.transforms, config.fixedValues, originalPaths);
231
260
  return { addedFiles, deletedFiles, changedFiles, unchangedFiles };
@@ -9,6 +9,7 @@ const node_path_1 = __importDefault(require("node:path"));
9
9
  const yaml_1 = __importDefault(require("yaml"));
10
10
  const consoleFormatter_1 = require("./consoleFormatter");
11
11
  const arrayMerger_1 = require("./utils/arrayMerger");
12
+ const commentOnlyDetector_1 = require("./utils/commentOnlyDetector");
12
13
  const errors_1 = require("./utils/errors");
13
14
  const fileType_1 = require("./utils/fileType");
14
15
  const fixedValues_1 = require("./utils/fixedValues");
@@ -248,11 +249,11 @@ const deleteFile = async (relativePath, absoluteDestinationDirectory, dryRun, lo
248
249
  const addNewFiles = async (files, sourceFiles, context) => {
249
250
  if (context.logger.shouldShow('debug'))
250
251
  context.logger.debug(`Processing ${files.length} new files`);
251
- for (const relativePath of files)
252
+ for (const addedFile of files)
252
253
  try {
253
- const content = sourceFiles.get(relativePath);
254
+ const content = sourceFiles.get(addedFile.path);
254
255
  await addFile({
255
- relativePath,
256
+ relativePath: addedFile.path,
256
257
  content,
257
258
  absoluteDestinationDirectory: context.absoluteDestinationDirectory,
258
259
  config: context.config,
@@ -262,7 +263,7 @@ const addNewFiles = async (files, sourceFiles, context) => {
262
263
  });
263
264
  }
264
265
  catch (error) {
265
- context.errors.push({ operation: 'add', path: relativePath, error: error });
266
+ context.errors.push({ operation: 'add', path: addedFile.path, error: error });
266
267
  }
267
268
  };
268
269
  const updateChangedFiles = async (files, context) => {
@@ -289,6 +290,8 @@ const formatUnchangedFiles = async (files, destinationFiles, context) => {
289
290
  if ((0, fileType_1.isYamlFile)(relativePath))
290
291
  try {
291
292
  const content = destinationFiles.get(relativePath);
293
+ if ((0, commentOnlyDetector_1.isCommentOnlyContent)(content))
294
+ continue;
292
295
  const effectiveOutputFormat = context.skipFormat ? undefined : context.config.outputFormat;
293
296
  const formatted = (0, yamlFormatter_1.formatYaml)(content, relativePath, effectiveOutputFormat);
294
297
  if (formatted !== content) {
@@ -11,6 +11,7 @@ const node_path_1 = __importDefault(require("node:path"));
11
11
  const diff2html_1 = require("diff2html");
12
12
  const browserLauncher_1 = require("./reporters/browserLauncher");
13
13
  const htmlTemplate_1 = require("./reporters/htmlTemplate");
14
+ const treeRenderer_1 = require("./reporters/treeRenderer");
14
15
  const diffGenerator_1 = require("./utils/diffGenerator");
15
16
  const errors_1 = require("./utils/errors");
16
17
  const fileType_1 = require("./utils/fileType");
@@ -42,6 +43,28 @@ const generateFileSummary = (file) => {
42
43
  return file.path;
43
44
  return `<span class="filename-transform">${file.originalPath} → ${file.path}</span>`;
44
45
  };
46
+ const generateAddedFileSummary = (file) => {
47
+ if (!file.originalPath)
48
+ return file.path;
49
+ return `<span class="filename-transform">${file.originalPath} → ${file.path}</span>`;
50
+ };
51
+ const generateAddedFileSection = (file, fileId) => {
52
+ const summary = generateAddedFileSummary(file);
53
+ const escapedContent = (0, treeRenderer_1.escapeHtml)(file.processedContent);
54
+ const filename = file.path.split('/').pop() || file.path;
55
+ return `
56
+ <details class="file-section" id="${fileId}" data-file-id="${fileId}" open>
57
+ <summary>${summary}</summary>
58
+ <div class="content-container">
59
+ <div class="content-actions">
60
+ <button class="copy-btn" data-file-id="${fileId}" title="Copy to clipboard">📋 Copy</button>
61
+ <button class="download-btn" data-file-id="${fileId}" data-filename="${(0, treeRenderer_1.escapeHtml)(filename)}" title="Download file">⬇ Download</button>
62
+ </div>
63
+ <pre class="file-content"><code>${escapedContent}</code></pre>
64
+ </div>
65
+ </details>
66
+ `;
67
+ };
45
68
  const generateChangedFileSection = (file, fileId) => {
46
69
  const isYaml = (0, fileType_1.isYamlFile)(file.path);
47
70
  const summary = generateFileSummary(file);
@@ -91,7 +114,11 @@ const generateHtmlReport = async (diffResult, formattedFiles, config, dryRun, lo
91
114
  for (const [index, file] of diffResult.changedFiles.entries())
92
115
  changedFileIds.set(file.path, `file-${index}`);
93
116
  const changedSections = diffResult.changedFiles.map((file, index) => generateChangedFileSection(file, `file-${index}`));
94
- const htmlContent = (0, htmlTemplate_1.generateHtmlTemplate)(diffResult, formattedFiles, trulyUnchangedFiles, metadata, changedSections, changedFileIds);
117
+ const addedFileIds = new Map();
118
+ for (const [index, file] of diffResult.addedFiles.entries())
119
+ addedFileIds.set(file.path, `added-file-${index}`);
120
+ const addedSections = diffResult.addedFiles.map((file, index) => generateAddedFileSection(file, `added-file-${index}`));
121
+ const htmlContent = (0, htmlTemplate_1.generateHtmlTemplate)(diffResult, formattedFiles, trulyUnchangedFiles, metadata, changedSections, changedFileIds, addedSections, addedFileIds);
95
122
  await writeHtmlFile(htmlContent, reportPath);
96
123
  logger?.log(`✓ HTML report generated: ${reportPath}, opening in browser...`);
97
124
  try {