helm-env-delta 1.7.1 → 1.7.2

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
@@ -642,7 +642,8 @@ hed --config <file> [options] # Short alias
642
642
  | `--diff-json` | Output JSON to stdout (pipe to jq) |
643
643
  | `--list-files` | List source/destination files without processing |
644
644
  | `--show-config` | Display resolved config after inheritance |
645
- | `--skip-format` | Skip YAML formatting |
645
+ | `--format-only` | Format destination files without syncing |
646
+ | `--skip-format` | Skip YAML formatting during sync |
646
647
  | `--no-color` | Disable colored output (CI/accessibility) |
647
648
  | `--verbose` | Show detailed debug info |
648
649
  | `--quiet` | Suppress output except errors |
@@ -679,6 +680,12 @@ hed --config config.yaml
679
680
 
680
681
  # Force override stop rules
681
682
  hed --config config.yaml --force
683
+
684
+ # Format destination files only (no sync)
685
+ hed --config config.yaml --format-only
686
+
687
+ # Preview format changes
688
+ hed --config config.yaml --format-only --dry-run
682
689
  ```
683
690
 
684
691
  ---
@@ -749,7 +756,7 @@ git push origin main
749
756
 
750
757
  ✅ **Flexibility** - Per-file patterns. Config inheritance. Regex transforms.
751
758
 
752
- ✅ **Reliability** - 917 tests, 84% coverage. Battle-tested.
759
+ ✅ **Reliability** - 920 tests, 84% coverage. Battle-tested.
753
760
 
754
761
  ---
755
762
 
@@ -6,6 +6,7 @@ export type SyncCommand = {
6
6
  diffHtml: boolean;
7
7
  diffJson: boolean;
8
8
  skipFormat: boolean;
9
+ formatOnly: boolean;
9
10
  validate: boolean;
10
11
  verbose: boolean;
11
12
  quiet: boolean;
@@ -19,6 +19,7 @@ const parseCommandLine = (argv) => {
19
19
  .option('--diff-html', 'Generate and open HTML diff report in browser', false)
20
20
  .option('--diff-json', 'Output diff as JSON to stdout', false)
21
21
  .option('--skip-format', 'Skip YAML formatting (outputFormat section)', false)
22
+ .option('--format-only', 'Format YAML files in destination without syncing', false)
22
23
  .option('--validate', 'Validate configuration file and exit', false)
23
24
  .option('--list-files', 'List files that would be synced without processing diffs', false)
24
25
  .option('--show-config', 'Display resolved configuration after inheritance and exit', false)
@@ -53,6 +54,10 @@ Documentation: https://github.com/balazscsaba2006/helm-env-delta
53
54
  console.error('Error: --verbose and --quiet flags are mutually exclusive');
54
55
  process.exit(1);
55
56
  }
57
+ if (options['formatOnly'] && options['skipFormat']) {
58
+ console.error('Error: --format-only and --skip-format flags are mutually exclusive');
59
+ process.exit(1);
60
+ }
56
61
  const threshold = Number.parseFloat(options['suggestThreshold']);
57
62
  if (Number.isNaN(threshold) || threshold < 0 || threshold > 1) {
58
63
  console.error('Error: --suggest-threshold must be a number between 0 and 1');
@@ -66,6 +71,7 @@ Documentation: https://github.com/balazscsaba2006/helm-env-delta
66
71
  diffHtml: options['diffHtml'],
67
72
  diffJson: options['diffJson'],
68
73
  skipFormat: options['skipFormat'],
74
+ formatOnly: options['formatOnly'],
69
75
  validate: options['validate'],
70
76
  listFiles: options['listFiles'],
71
77
  showConfig: options['showConfig'],
@@ -4,6 +4,7 @@ export interface FileLoaderOptions {
4
4
  include: string[];
5
5
  exclude: string[];
6
6
  transforms?: TransformConfig;
7
+ skipExclude?: boolean;
7
8
  }
8
9
  export type FileMap = Map<string, string>;
9
10
  export interface FileLoaderResult {
@@ -67,10 +67,12 @@ const validateAndResolveBaseDirectory = async (baseDirectory) => {
67
67
  throw accessError;
68
68
  }
69
69
  };
70
- const findMatchingFiles = async (baseDirectory, includePatterns, excludePatterns, transforms) => {
70
+ const findMatchingFiles = async (baseDirectory, includePatterns, excludePatterns, transforms, skipExclude) => {
71
71
  try {
72
72
  if (!transforms) {
73
- const allPatterns = [...includePatterns, ...excludePatterns.map((pattern) => `!${pattern}`)];
73
+ const allPatterns = skipExclude
74
+ ? [...includePatterns]
75
+ : [...includePatterns, ...excludePatterns.map((pattern) => `!${pattern}`)];
74
76
  const matchedFiles = await (0, tinyglobby_1.glob)(allPatterns, {
75
77
  cwd: baseDirectory,
76
78
  absolute: true,
@@ -94,9 +96,11 @@ const findMatchingFiles = async (baseDirectory, includePatterns, excludePatterns
94
96
  const included = includePatterns.some((pattern) => patternMatcher_1.globalMatcher.match(transformedPath, pattern));
95
97
  if (!included)
96
98
  continue;
97
- const excluded = excludePatterns.some((pattern) => patternMatcher_1.globalMatcher.match(transformedPath, pattern));
98
- if (excluded)
99
- continue;
99
+ if (!skipExclude) {
100
+ const excluded = excludePatterns.some((pattern) => patternMatcher_1.globalMatcher.match(transformedPath, pattern));
101
+ if (excluded)
102
+ continue;
103
+ }
100
104
  filtered.push(absolutePath);
101
105
  }
102
106
  return filtered;
@@ -153,7 +157,7 @@ const loadFiles = async (options, logger) => {
153
157
  const absoluteBaseDirectory = await validateAndResolveBaseDirectory(options.baseDirectory);
154
158
  const includePatterns = options.include ?? ['**/*'];
155
159
  const excludePatterns = options.exclude ?? [];
156
- const files = await findMatchingFiles(absoluteBaseDirectory, includePatterns, excludePatterns, options.transforms);
160
+ const files = await findMatchingFiles(absoluteBaseDirectory, includePatterns, excludePatterns, options.transforms, options.skipExclude);
157
161
  if (logger?.shouldShow('debug')) {
158
162
  logger.debug('Glob matching:');
159
163
  logger.debug(` Directory: ${absoluteBaseDirectory}`);
package/dist/index.js CHANGED
@@ -37,6 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  const node_fs_1 = require("node:fs");
40
+ const promises_1 = require("node:fs/promises");
40
41
  const node_os_1 = require("node:os");
41
42
  const node_path_1 = __importDefault(require("node:path"));
42
43
  const chalk_1 = __importDefault(require("chalk"));
@@ -60,7 +61,9 @@ const stopRulesValidator_1 = require("./stopRulesValidator");
60
61
  const suggestionEngine_1 = require("./suggestionEngine");
61
62
  const collisionDetector_1 = require("./utils/collisionDetector");
62
63
  const filenameTransformer_1 = require("./utils/filenameTransformer");
64
+ const fileType_1 = require("./utils/fileType");
63
65
  const versionChecker_1 = require("./utils/versionChecker");
66
+ const yamlFormatter_1 = require("./yamlFormatter");
64
67
  const ZodError_1 = require("./ZodError");
65
68
  const main = async () => {
66
69
  const command = (0, commandLine_1.parseCommandLine)();
@@ -102,13 +105,15 @@ const main = async () => {
102
105
  baseDirectory: config.source,
103
106
  include: config.include,
104
107
  exclude: config.exclude,
105
- transforms: config.transforms
108
+ transforms: config.transforms,
109
+ skipExclude: true
106
110
  }, logger);
107
111
  const sourceFiles = sourceResult.fileMap;
108
112
  const destinationResult = await (0, fileLoader_1.loadFiles)({
109
113
  baseDirectory: config.destination,
110
114
  include: config.include,
111
- exclude: config.exclude
115
+ exclude: config.exclude,
116
+ skipExclude: true
112
117
  }, logger);
113
118
  const destinationFiles = destinationResult.fileMap;
114
119
  logger.progress(`Loaded ${sourceFiles.size} source, ${destinationFiles.size} destination file(s)`, 'success');
@@ -171,6 +176,46 @@ const main = async () => {
171
176
  console.log(` ${chalk_1.default.dim(file)}`);
172
177
  return;
173
178
  }
179
+ if (command.formatOnly) {
180
+ if (!config.outputFormat) {
181
+ logger.log(chalk_1.default.yellow('\n⚠️ No outputFormat configured. Nothing to format.'));
182
+ return;
183
+ }
184
+ logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Formatting files...', 'info'));
185
+ let formattedCount = 0;
186
+ const errors = [];
187
+ for (const [relativePath, content] of destinationFiles) {
188
+ if (!(0, fileType_1.isYamlFile)(relativePath))
189
+ continue;
190
+ try {
191
+ const formatted = (0, yamlFormatter_1.formatYaml)(content, relativePath, config.outputFormat);
192
+ if (formatted !== content) {
193
+ const absolutePath = node_path_1.default.join(config.destination, relativePath);
194
+ if (command.dryRun)
195
+ logger.fileOp('format', relativePath, true);
196
+ else {
197
+ await (0, promises_1.writeFile)(absolutePath, formatted, 'utf8');
198
+ logger.fileOp('format', relativePath, false);
199
+ }
200
+ formattedCount++;
201
+ }
202
+ }
203
+ catch (error) {
204
+ errors.push({ path: relativePath, error: error });
205
+ }
206
+ }
207
+ if (command.dryRun)
208
+ logger.log(`\n[DRY RUN] Would format ${formattedCount} file(s)`);
209
+ else
210
+ logger.log(`\n✓ Formatted ${formattedCount} file(s)`);
211
+ if (errors.length > 0) {
212
+ logger.error(`\n❌ Encountered ${errors.length} error(s):`, 'critical');
213
+ for (const { path: errorPath, error } of errors)
214
+ logger.error(` ${errorPath}: ${error.message}`, 'critical');
215
+ process.exit(1);
216
+ }
217
+ return;
218
+ }
174
219
  logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Computing differences...', 'info'));
175
220
  const diffResult = (0, fileDiff_1.computeFileDiff)(sourceFiles, destinationFiles, config, logger, originalPaths);
176
221
  if (logger.shouldShow('debug'))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helm-env-delta",
3
- "version": "1.7.1",
3
+ "version": "1.7.2",
4
4
  "description": "HelmEnvDelta – environment-aware YAML delta and sync for GitOps",
5
5
  "author": "BCsabaEngine",
6
6
  "license": "ISC",
@@ -62,16 +62,16 @@
62
62
  },
63
63
  "devDependencies": {
64
64
  "@types/hogan.js": "^3.0.5",
65
- "@types/node": "^25.0.9",
65
+ "@types/node": "^25.0.10",
66
66
  "@types/picomatch": "^4.0.2",
67
- "@typescript-eslint/eslint-plugin": "^8.53.0",
68
- "@typescript-eslint/parser": "^8.53.0",
67
+ "@typescript-eslint/eslint-plugin": "^8.53.1",
68
+ "@typescript-eslint/parser": "^8.53.1",
69
69
  "@vitest/coverage-v8": "^4.0.17",
70
70
  "eslint": "^9.39.2",
71
71
  "eslint-config-prettier": "^10.1.8",
72
72
  "eslint-plugin-simple-import-sort": "^12.1.1",
73
73
  "eslint-plugin-unicorn": "^62.0.0",
74
- "prettier": "^3.8.0",
74
+ "prettier": "^3.8.1",
75
75
  "tsx": "^4.21.0",
76
76
  "typescript": "^5.9.3",
77
77
  "vitest": "^4.0.17"