helm-env-delta 1.9.1 β 1.9.3
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 +88 -36
- package/dist/configFile.d.ts +73 -0
- package/dist/configFile.js +44 -1
- package/dist/configLoader.d.ts +5 -2
- package/dist/configLoader.js +8 -4
- package/dist/fileDiff.js +3 -0
- package/dist/fileUpdater.js +3 -0
- package/dist/index.js +86 -56
- package/dist/utils/commentOnlyDetector.d.ts +1 -0
- package/dist/utils/commentOnlyDetector.js +18 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +3 -1
- package/dist/yamlFormatter.js +4 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,34 +1,40 @@
|
|
|
1
1
|
# π HelmEnvDelta
|
|
2
2
|
|
|
3
|
+
**GitOps-safe YAML sync for Helm values across environments (Dev β UAT β Prod)**
|
|
4
|
+
|
|
3
5
|
[](https://www.npmjs.com/package/helm-env-delta)
|
|
4
6
|
[](https://opensource.org/licenses/ISC)
|
|
5
7
|
[](https://nodejs.org/)
|
|
6
8
|
|
|
7
9
|
**Sync YAML configs across environments in seconds, not hours.**
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
**π‘οΈ Stop production incidents from configuration driftβbefore they happen.**
|
|
10
12
|
|
|
11
13
|
HelmEnvDelta (`hed`) automates environment synchronization for GitOps workflows while protecting your production-specific settings and preventing dangerous changes.
|
|
12
14
|
|
|
13
15
|
---
|
|
14
16
|
|
|
15
|
-
##
|
|
17
|
+
## π€ Who Is This For?
|
|
16
18
|
|
|
17
|
-
**
|
|
19
|
+
- **Platform/SRE teams** owning multiple environment repos
|
|
20
|
+
- **Teams** with repeated UAT β Prod copy-paste workflows
|
|
21
|
+
- **Helm/GitOps users** burned by config drift and sync errors
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- π Inconsistent YAML formatting across environments
|
|
23
|
-
- π Noisy git diffs make code review painful
|
|
23
|
+
> **Not a Helm plugin** Β· **Not a live cluster diff** Β· **Not a general YAML linter**
|
|
24
|
+
>
|
|
25
|
+
> HelmEnvDelta syncs _files_, not running clusters. Use alongside Helm, ArgoCD, or Fluxβnot instead of them.
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
---
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
## π‘ Why Teams Love HelmEnvDelta
|
|
30
|
+
|
|
31
|
+
| Problem | Impact | HelmEnvDelta Outcome |
|
|
32
|
+
| ---------------------------- | ----------------------------------------- | ------------------------------------------------ |
|
|
33
|
+
| Copying UAT β Prod by hand | 30+ minutes, human errors | **1 min** scripted sync for 50+ files |
|
|
34
|
+
| Hidden config drift | Incidents in Prod | Stop rules & fixed values prevent unsafe changes |
|
|
35
|
+
| Accidental overwrites | Production namespaces, replicas corrupted | `skipPath` preserves env-specific values |
|
|
36
|
+
| Major version slips | Breaking changes in Prod | `semverMajorUpgrade` rule blocks v1βv2 |
|
|
37
|
+
| Inconsistent YAML formatting | Noisy git diffs, painful reviews | Standardized formatting, clean diffs |
|
|
32
38
|
|
|
33
39
|
---
|
|
34
40
|
|
|
@@ -132,6 +138,8 @@ Analyzes differences and suggests transforms and stop rules automatically with c
|
|
|
132
138
|
|
|
133
139
|
**Challenge:** 20+ microservices across Dev β UAT β Prod. Each has environment-specific namespaces, resource limits, and URLs.
|
|
134
140
|
|
|
141
|
+
_A platform team managing 25 microservices across 3 environments used to spend 2 hours every release copying and tweaking values files. Now 2 engineers sync everything in under 5 minutes with zero manual edits._
|
|
142
|
+
|
|
135
143
|
**Solution:**
|
|
136
144
|
|
|
137
145
|
```yaml
|
|
@@ -159,6 +167,8 @@ transforms:
|
|
|
159
167
|
|
|
160
168
|
**Challenge:** Production incidents from accidental major version upgrades or scaling beyond cluster capacity.
|
|
161
169
|
|
|
170
|
+
_After a v1βv2 image upgrade slipped through and caused a 4-hour outage, the team added semverMajorUpgrade rules. They've blocked 12 accidental major bumps sinceβand zero incidents._
|
|
171
|
+
|
|
162
172
|
**Solution:**
|
|
163
173
|
|
|
164
174
|
```yaml
|
|
@@ -180,6 +190,8 @@ stopRules:
|
|
|
180
190
|
|
|
181
191
|
**Challenge:** Different editors, different formatting. Git diffs full of noise.
|
|
182
192
|
|
|
193
|
+
_Code reviews were painful: 80% of diff lines were formatting noise. After enabling outputFormat, PRs became clean structural diffs that reviewers actually read._
|
|
194
|
+
|
|
183
195
|
**Solution:**
|
|
184
196
|
|
|
185
197
|
```yaml
|
|
@@ -269,6 +281,28 @@ helm-env-delta --config example/6-fixed-values/config.yaml --dry-run --diff
|
|
|
269
281
|
- Array filter operators (`env[name=LOG_LEVEL].value`)
|
|
270
282
|
- Combining with skipPath and transforms
|
|
271
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
|
+
|
|
272
306
|
---
|
|
273
307
|
|
|
274
308
|
## π‘ Smart Configuration Suggestions (Heuristic)
|
|
@@ -362,8 +396,8 @@ helm-env-delta --config config.yaml
|
|
|
362
396
|
### π― Core Settings
|
|
363
397
|
|
|
364
398
|
```yaml
|
|
365
|
-
source: './uat' # Required: Source folder
|
|
366
|
-
destination: './prod' # Required: Destination folder
|
|
399
|
+
source: './uat' # Required: Source folder (optional with --format-only)
|
|
400
|
+
destination: './prod' # Required: Destination folder (must differ from source)
|
|
367
401
|
|
|
368
402
|
include: # Optional: File patterns (default: all)
|
|
369
403
|
- '**/*.yaml'
|
|
@@ -374,6 +408,8 @@ prune: false # Optional: Delete dest files not in source
|
|
|
374
408
|
confirmationDelay: 3000 # Optional: Delay in ms before sync (default: 3000, 0 to disable)
|
|
375
409
|
```
|
|
376
410
|
|
|
411
|
+
**Note:** Source and destination paths cannot resolve to the same folder.
|
|
412
|
+
|
|
377
413
|
---
|
|
378
414
|
|
|
379
415
|
### π Path Filtering (skipPath)
|
|
@@ -633,7 +669,7 @@ Standardize YAML across all environments.
|
|
|
633
669
|
```yaml
|
|
634
670
|
outputFormat:
|
|
635
671
|
indent: 2 # Indentation size
|
|
636
|
-
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)
|
|
637
673
|
|
|
638
674
|
keyOrders: # Custom key ordering
|
|
639
675
|
'apps/*.yaml':
|
|
@@ -717,24 +753,24 @@ hed --config <file> [options] # Short alias
|
|
|
717
753
|
|
|
718
754
|
### Options
|
|
719
755
|
|
|
720
|
-
| Flag | Description
|
|
721
|
-
| --------------------------- |
|
|
722
|
-
| `--config <path>` | **Required** - Configuration file
|
|
723
|
-
| `--validate` | Validate config and pattern usage (shows warnings)
|
|
724
|
-
| `--suggest` | Analyze differences and suggest config updates
|
|
725
|
-
| `--suggest-threshold <0-1>` | Minimum confidence for suggestions (default: 0.3)
|
|
726
|
-
| `--dry-run` | Preview changes without writing files
|
|
727
|
-
| `--force` | Override stop rules
|
|
728
|
-
| `--diff` | Show console diff
|
|
729
|
-
| `--diff-html` | Generate HTML report (opens in browser)
|
|
730
|
-
| `--diff-json` | Output JSON to stdout (pipe to jq)
|
|
731
|
-
| `--list-files` | List
|
|
732
|
-
| `--show-config` | Display resolved config after inheritance
|
|
733
|
-
| `--format-only` | Format destination files
|
|
734
|
-
| `--skip-format` | Skip YAML formatting during sync
|
|
735
|
-
| `--no-color` | Disable colored output (CI/accessibility)
|
|
736
|
-
| `--verbose` | Show detailed debug info
|
|
737
|
-
| `--quiet` | Suppress output except errors
|
|
756
|
+
| Flag | Description |
|
|
757
|
+
| --------------------------- | ------------------------------------------------------------------- |
|
|
758
|
+
| `--config <path>` | **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` | Preview changes without writing files |
|
|
763
|
+
| `--force` | Override stop rules |
|
|
764
|
+
| `--diff` | Show console diff |
|
|
765
|
+
| `--diff-html` | Generate HTML report (opens in browser) |
|
|
766
|
+
| `--diff-json` | Output JSON to stdout (pipe to jq) |
|
|
767
|
+
| `--list-files` | 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` | Skip YAML formatting during sync |
|
|
771
|
+
| `--no-color` | Disable colored output (CI/accessibility) |
|
|
772
|
+
| `--verbose` | Show detailed debug info |
|
|
773
|
+
| `--quiet` | Suppress output except errors |
|
|
738
774
|
|
|
739
775
|
### Examples
|
|
740
776
|
|
|
@@ -769,11 +805,19 @@ hed --config config.yaml
|
|
|
769
805
|
# Force override stop rules
|
|
770
806
|
hed --config config.yaml --force
|
|
771
807
|
|
|
772
|
-
# Format destination files only (no sync)
|
|
808
|
+
# Format destination files only (no sync, source not required in config)
|
|
773
809
|
hed --config config.yaml --format-only
|
|
774
810
|
|
|
775
811
|
# Preview format changes
|
|
776
812
|
hed --config config.yaml --format-only --dry-run
|
|
813
|
+
|
|
814
|
+
# List files that would be formatted (--list-files takes precedence)
|
|
815
|
+
hed --config config.yaml --format-only --list-files
|
|
816
|
+
|
|
817
|
+
# Format-only config example (no source needed):
|
|
818
|
+
# destination: './prod'
|
|
819
|
+
# outputFormat:
|
|
820
|
+
# indent: 2
|
|
777
821
|
```
|
|
778
822
|
|
|
779
823
|
---
|
|
@@ -1017,6 +1061,14 @@ spec:
|
|
|
1017
1061
|
|
|
1018
1062
|
## π Common Issues
|
|
1019
1063
|
|
|
1064
|
+
### β Source and destination folders cannot be the same
|
|
1065
|
+
|
|
1066
|
+
**Error:** `Source and destination folders cannot be the same`
|
|
1067
|
+
|
|
1068
|
+
**Fix:** Ensure source and destination paths resolve to different directories. Paths like `./envs/../envs/prod` and `./envs/prod` will be detected as identical.
|
|
1069
|
+
|
|
1070
|
+
---
|
|
1071
|
+
|
|
1020
1072
|
### β Stop rule violations blocking sync
|
|
1021
1073
|
|
|
1022
1074
|
**Error:** `π Stop Rule Violation (semverMajorUpgrade)`
|
package/dist/configFile.d.ts
CHANGED
|
@@ -240,8 +240,80 @@ declare const finalConfigSchema: z.ZodObject<{
|
|
|
240
240
|
}, z.core.$strip>>>>;
|
|
241
241
|
}, z.core.$strip>>>;
|
|
242
242
|
}, z.core.$strip>;
|
|
243
|
+
declare const formatOnlyConfigSchema: z.ZodObject<{
|
|
244
|
+
transforms: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
245
|
+
content: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
246
|
+
find: z.ZodString;
|
|
247
|
+
replace: z.ZodString;
|
|
248
|
+
}, z.core.$strip>>>;
|
|
249
|
+
filename: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
250
|
+
find: z.ZodString;
|
|
251
|
+
replace: z.ZodString;
|
|
252
|
+
}, z.core.$strip>>>;
|
|
253
|
+
contentFile: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
254
|
+
filenameFile: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
255
|
+
}, z.core.$strip>>>;
|
|
256
|
+
source: z.ZodOptional<z.ZodString>;
|
|
257
|
+
destination: z.ZodNonOptional<z.ZodOptional<z.ZodString>>;
|
|
258
|
+
skipPath: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
|
|
259
|
+
stopRules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
260
|
+
type: z.ZodLiteral<"semverMajorUpgrade">;
|
|
261
|
+
path: z.ZodString;
|
|
262
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
263
|
+
type: z.ZodLiteral<"semverDowngrade">;
|
|
264
|
+
path: z.ZodString;
|
|
265
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
266
|
+
type: z.ZodLiteral<"numeric">;
|
|
267
|
+
path: z.ZodString;
|
|
268
|
+
min: z.ZodOptional<z.ZodNumber>;
|
|
269
|
+
max: z.ZodOptional<z.ZodNumber>;
|
|
270
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
271
|
+
type: z.ZodLiteral<"regex">;
|
|
272
|
+
path: z.ZodOptional<z.ZodString>;
|
|
273
|
+
regex: z.ZodString;
|
|
274
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
275
|
+
type: z.ZodLiteral<"regexFile">;
|
|
276
|
+
path: z.ZodOptional<z.ZodString>;
|
|
277
|
+
file: z.ZodString;
|
|
278
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
279
|
+
type: z.ZodLiteral<"regexFileKey">;
|
|
280
|
+
path: z.ZodOptional<z.ZodString>;
|
|
281
|
+
file: z.ZodString;
|
|
282
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
283
|
+
type: z.ZodLiteral<"versionFormat">;
|
|
284
|
+
path: z.ZodString;
|
|
285
|
+
vPrefix: z.ZodDefault<z.ZodEnum<{
|
|
286
|
+
required: "required";
|
|
287
|
+
allowed: "allowed";
|
|
288
|
+
forbidden: "forbidden";
|
|
289
|
+
}>>;
|
|
290
|
+
}, z.core.$strict>], "type">>>>;
|
|
291
|
+
fixedValues: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodObject<{
|
|
292
|
+
path: z.ZodString;
|
|
293
|
+
value: z.ZodUnknown;
|
|
294
|
+
}, z.core.$strip>>>>;
|
|
295
|
+
include: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
296
|
+
exclude: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
297
|
+
prune: z.ZodDefault<z.ZodBoolean>;
|
|
298
|
+
confirmationDelay: z.ZodDefault<z.ZodNumber>;
|
|
299
|
+
outputFormat: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
300
|
+
indent: z.ZodDefault<z.ZodNumber>;
|
|
301
|
+
keySeparator: z.ZodDefault<z.ZodBoolean>;
|
|
302
|
+
quoteValues: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
|
|
303
|
+
keyOrders: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
|
|
304
|
+
arraySort: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodObject<{
|
|
305
|
+
path: z.ZodString;
|
|
306
|
+
sortBy: z.ZodString;
|
|
307
|
+
order: z.ZodDefault<z.ZodEnum<{
|
|
308
|
+
asc: "asc";
|
|
309
|
+
desc: "desc";
|
|
310
|
+
}>>;
|
|
311
|
+
}, z.core.$strip>>>>;
|
|
312
|
+
}, z.core.$strip>>>;
|
|
313
|
+
}, z.core.$strip>;
|
|
243
314
|
export type BaseConfig = z.infer<typeof baseConfigSchema>;
|
|
244
315
|
export type FinalConfig = z.infer<typeof finalConfigSchema>;
|
|
316
|
+
export type FormatOnlyConfig = z.infer<typeof formatOnlyConfigSchema>;
|
|
245
317
|
export type Config = FinalConfig;
|
|
246
318
|
export type StopRule = z.infer<typeof stopRuleSchema>;
|
|
247
319
|
export type SemverMajorUpgradeRule = z.infer<typeof semverMajorUpgradeRuleSchema>;
|
|
@@ -260,5 +332,6 @@ export type FixedValueRule = z.infer<typeof fixedValueRuleSchema>;
|
|
|
260
332
|
export type FixedValueConfig = Record<string, FixedValueRule[]>;
|
|
261
333
|
export declare const parseBaseConfig: (data: unknown, configPath?: string) => BaseConfig;
|
|
262
334
|
export declare const parseFinalConfig: (data: unknown, configPath?: string) => FinalConfig;
|
|
335
|
+
export declare const parseFormatOnlyConfig: (data: unknown, configPath?: string) => FormatOnlyConfig;
|
|
263
336
|
export declare const parseConfig: (data: unknown, configPath?: string) => FinalConfig;
|
|
264
337
|
export { ZodValidationError as ConfigValidationError, isZodValidationError as isConfigValidationError } from './ZodError';
|
package/dist/configFile.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isConfigValidationError = exports.ConfigValidationError = exports.parseConfig = exports.parseFinalConfig = exports.parseBaseConfig = void 0;
|
|
6
|
+
exports.isConfigValidationError = exports.ConfigValidationError = exports.parseConfig = exports.parseFormatOnlyConfig = exports.parseFinalConfig = exports.parseBaseConfig = void 0;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
4
8
|
const zod_1 = require("zod");
|
|
5
9
|
const ZodError_1 = require("./ZodError");
|
|
6
10
|
const semverMajorUpgradeRuleSchema = zod_1.z.object({
|
|
@@ -149,6 +153,33 @@ const finalConfigSchema = baseConfigSchema
|
|
|
149
153
|
})
|
|
150
154
|
.optional()
|
|
151
155
|
.default({ indent: 2, keySeparator: false })
|
|
156
|
+
})
|
|
157
|
+
.refine((data) => {
|
|
158
|
+
const normalizedSource = node_path_1.default.resolve(data.source);
|
|
159
|
+
const normalizedDestination = node_path_1.default.resolve(data.destination);
|
|
160
|
+
return normalizedSource !== normalizedDestination;
|
|
161
|
+
}, {
|
|
162
|
+
message: 'Source and destination folders cannot be the same',
|
|
163
|
+
path: ['destination']
|
|
164
|
+
});
|
|
165
|
+
const formatOnlyConfigSchema = baseConfigSchema
|
|
166
|
+
.omit({ extends: true })
|
|
167
|
+
.required({ destination: true })
|
|
168
|
+
.extend({
|
|
169
|
+
include: zod_1.z.array(zod_1.z.string().min(1)).default(['**/*']),
|
|
170
|
+
exclude: zod_1.z.array(zod_1.z.string().min(1)).default([]),
|
|
171
|
+
prune: zod_1.z.boolean().default(false),
|
|
172
|
+
confirmationDelay: zod_1.z.number().int().min(0).default(3000),
|
|
173
|
+
outputFormat: zod_1.z
|
|
174
|
+
.object({
|
|
175
|
+
indent: zod_1.z.number().int().min(1).max(10).default(2),
|
|
176
|
+
keySeparator: zod_1.z.boolean().default(false),
|
|
177
|
+
quoteValues: zod_1.z.record(zod_1.z.string(), zod_1.z.array(zod_1.z.string())).optional(),
|
|
178
|
+
keyOrders: zod_1.z.record(zod_1.z.string(), zod_1.z.array(zod_1.z.string())).optional(),
|
|
179
|
+
arraySort: zod_1.z.record(zod_1.z.string(), zod_1.z.array(arraySortRuleSchema)).optional()
|
|
180
|
+
})
|
|
181
|
+
.optional()
|
|
182
|
+
.default({ indent: 2, keySeparator: false })
|
|
152
183
|
});
|
|
153
184
|
const parseBaseConfig = (data, configPath) => {
|
|
154
185
|
const result = baseConfigSchema.safeParse(data);
|
|
@@ -169,6 +200,18 @@ const parseFinalConfig = (data, configPath) => {
|
|
|
169
200
|
return result.data;
|
|
170
201
|
};
|
|
171
202
|
exports.parseFinalConfig = parseFinalConfig;
|
|
203
|
+
const parseFormatOnlyConfig = (data, configPath) => {
|
|
204
|
+
const result = formatOnlyConfigSchema.safeParse(data);
|
|
205
|
+
if (!result.success) {
|
|
206
|
+
const error = new ZodError_1.ZodValidationError(result.error, configPath);
|
|
207
|
+
const destinationMissing = result.error.issues.some((issue) => issue.path[0] === 'destination' && issue.code === 'invalid_type');
|
|
208
|
+
if (destinationMissing)
|
|
209
|
+
error.message += '\n\n Hint: Format-only mode requires destination to be specified.';
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
return result.data;
|
|
213
|
+
};
|
|
214
|
+
exports.parseFormatOnlyConfig = parseFormatOnlyConfig;
|
|
172
215
|
exports.parseConfig = exports.parseFinalConfig;
|
|
173
216
|
var ZodError_2 = require("./ZodError");
|
|
174
217
|
Object.defineProperty(exports, "ConfigValidationError", { enumerable: true, get: function () { return ZodError_2.ZodValidationError; } });
|
package/dist/configLoader.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
import { type FinalConfig } from './configFile';
|
|
1
|
+
import { type FinalConfig, type FormatOnlyConfig } from './configFile';
|
|
2
2
|
export type Config = FinalConfig;
|
|
3
|
-
export
|
|
3
|
+
export type LoadConfigOptions = {
|
|
4
|
+
formatOnly?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare const loadConfigFile: (configPath: string, quiet?: boolean, logger?: import("./logger").Logger, options?: LoadConfigOptions) => FinalConfig | FormatOnlyConfig;
|
package/dist/configLoader.js
CHANGED
|
@@ -30,13 +30,17 @@ const expandTransformFiles = (config, configDirectory) => {
|
|
|
30
30
|
}
|
|
31
31
|
return { ...config, transforms: expandedTransforms };
|
|
32
32
|
};
|
|
33
|
-
const loadConfigFile = (configPath, quiet = false, logger) => {
|
|
33
|
+
const loadConfigFile = (configPath, quiet = false, logger, options = {}) => {
|
|
34
34
|
const configDirectory = node_path_1.default.dirname(node_path_1.default.resolve(configPath));
|
|
35
35
|
const mergedConfig = (0, configMerger_1.resolveConfigWithExtends)(configPath, new Set(), 0, logger);
|
|
36
36
|
const expandedConfig = expandTransformFiles(mergedConfig, configDirectory);
|
|
37
|
-
const config =
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
const config = options.formatOnly
|
|
38
|
+
? (0, configFile_1.parseFormatOnlyConfig)(expandedConfig, configPath)
|
|
39
|
+
: (0, configFile_1.parseFinalConfig)(expandedConfig, configPath);
|
|
40
|
+
if (!quiet) {
|
|
41
|
+
const sourceInfo = config.source ? `${config.source} -> ` : '';
|
|
42
|
+
console.log(`\nConfiguration loaded: ${sourceInfo}${config.destination}` + (config.prune ? ' [prune!]' : ''));
|
|
43
|
+
}
|
|
40
44
|
return config;
|
|
41
45
|
};
|
|
42
46
|
exports.loadConfigFile = loadConfigFile;
|
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");
|
|
@@ -143,6 +144,8 @@ const processYamlFile = (options) => {
|
|
|
143
144
|
parseError.message += destinationHints;
|
|
144
145
|
throw parseError;
|
|
145
146
|
}
|
|
147
|
+
if ((0, commentOnlyDetector_1.isCommentOnlyContent)(destinationContent))
|
|
148
|
+
return undefined;
|
|
146
149
|
const sourceTransformed = (0, transformer_1.applyTransforms)(sourceParsed, filePath, transforms);
|
|
147
150
|
const fixedValueRules = (0, fixedValues_1.getFixedValuesForFile)(filePath, fixedValues);
|
|
148
151
|
if (fixedValueRules.length > 0)
|
package/dist/fileUpdater.js
CHANGED
|
@@ -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");
|
|
@@ -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) {
|
package/dist/index.js
CHANGED
|
@@ -59,6 +59,7 @@ const patternUsageValidator_1 = require("./patternUsageValidator");
|
|
|
59
59
|
const stopRulesValidator_1 = require("./stopRulesValidator");
|
|
60
60
|
const suggestionEngine_1 = require("./suggestionEngine");
|
|
61
61
|
const collisionDetector_1 = require("./utils/collisionDetector");
|
|
62
|
+
const commentOnlyDetector_1 = require("./utils/commentOnlyDetector");
|
|
62
63
|
const filenameTransformer_1 = require("./utils/filenameTransformer");
|
|
63
64
|
const fileType_1 = require("./utils/fileType");
|
|
64
65
|
const versionChecker_1 = require("./utils/versionChecker");
|
|
@@ -84,15 +85,20 @@ const main = async () => {
|
|
|
84
85
|
(0, node_fs_1.mkdirSync)(configDirectory, { recursive: true });
|
|
85
86
|
(0, node_fs_1.writeFileSync)(firstRunMarker, new Date().toISOString());
|
|
86
87
|
}
|
|
87
|
-
const config = (0, configLoader_1.loadConfigFile)(command.config, command.quiet, logger);
|
|
88
|
+
const config = (0, configLoader_1.loadConfigFile)(command.config, command.quiet, logger, { formatOnly: command.formatOnly });
|
|
88
89
|
if (command.showConfig) {
|
|
89
90
|
console.log(chalk_1.default.cyan('\nβοΈ Resolved Configuration:\n'));
|
|
90
91
|
console.log(YAML.stringify(config, { indent: 2 }));
|
|
91
92
|
return;
|
|
92
93
|
}
|
|
93
94
|
if (command.validate) {
|
|
95
|
+
if (!config.source) {
|
|
96
|
+
logger.error('\nSource folder is required for validation mode.', 'critical');
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
const validationConfig = config;
|
|
94
100
|
logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Validating configuration...', 'info'));
|
|
95
|
-
const warningResult = (0, configWarnings_1.validateConfigWarnings)(
|
|
101
|
+
const warningResult = (0, configWarnings_1.validateConfigWarnings)(validationConfig);
|
|
96
102
|
let hasAnyWarnings = warningResult.hasWarnings;
|
|
97
103
|
if (warningResult.hasWarnings) {
|
|
98
104
|
console.warn(chalk_1.default.yellow('\nβ οΈ Configuration Warnings (non-fatal):\n'));
|
|
@@ -101,23 +107,23 @@ const main = async () => {
|
|
|
101
107
|
}
|
|
102
108
|
logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Loading files for validation...', 'loading'));
|
|
103
109
|
const sourceResult = await (0, fileLoader_1.loadFiles)({
|
|
104
|
-
baseDirectory:
|
|
105
|
-
include:
|
|
106
|
-
exclude:
|
|
107
|
-
transforms:
|
|
110
|
+
baseDirectory: validationConfig.source,
|
|
111
|
+
include: validationConfig.include,
|
|
112
|
+
exclude: validationConfig.exclude,
|
|
113
|
+
transforms: validationConfig.transforms,
|
|
108
114
|
skipExclude: true
|
|
109
115
|
}, logger);
|
|
110
116
|
const sourceFiles = sourceResult.fileMap;
|
|
111
117
|
const destinationResult = await (0, fileLoader_1.loadFiles)({
|
|
112
|
-
baseDirectory:
|
|
113
|
-
include:
|
|
114
|
-
exclude:
|
|
118
|
+
baseDirectory: validationConfig.destination,
|
|
119
|
+
include: validationConfig.include,
|
|
120
|
+
exclude: validationConfig.exclude,
|
|
115
121
|
skipExclude: true
|
|
116
122
|
}, logger);
|
|
117
123
|
const destinationFiles = destinationResult.fileMap;
|
|
118
124
|
logger.progress(`Loaded ${sourceFiles.size} source, ${destinationFiles.size} destination file(s)`, 'success');
|
|
119
125
|
logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Validating pattern usage...', 'info'));
|
|
120
|
-
const usageResult = (0, patternUsageValidator_1.validatePatternUsage)(
|
|
126
|
+
const usageResult = (0, patternUsageValidator_1.validatePatternUsage)(validationConfig, sourceFiles, destinationFiles);
|
|
121
127
|
hasAnyWarnings = hasAnyWarnings || usageResult.hasWarnings;
|
|
122
128
|
if (usageResult.hasWarnings) {
|
|
123
129
|
console.warn(chalk_1.default.yellow('\nβ οΈ Pattern Usage Warnings (non-fatal):\n'));
|
|
@@ -134,58 +140,43 @@ const main = async () => {
|
|
|
134
140
|
}
|
|
135
141
|
if (logger.shouldShow('debug')) {
|
|
136
142
|
logger.debug('\nConfig details:');
|
|
137
|
-
|
|
143
|
+
if (config.source)
|
|
144
|
+
logger.debug(` Source: ${config.source}`);
|
|
138
145
|
logger.debug(` Destination: ${config.destination}`);
|
|
139
146
|
logger.debug(` Include patterns: ${config.include.join(', ')}`);
|
|
140
147
|
logger.debug(` Exclude patterns: ${config.exclude.join(', ')}`);
|
|
141
148
|
logger.debug(` Transforms: ${Object.keys(config.transforms || {}).length} pattern(s)`);
|
|
142
149
|
logger.debug(` Prune enabled: ${config.prune}`);
|
|
143
150
|
}
|
|
144
|
-
logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Loading files...', 'loading'));
|
|
145
|
-
const sourceResult = await (0, fileLoader_1.loadFiles)({
|
|
146
|
-
baseDirectory: config.source,
|
|
147
|
-
include: config.include,
|
|
148
|
-
exclude: config.exclude,
|
|
149
|
-
transforms: config.transforms
|
|
150
|
-
}, logger);
|
|
151
|
-
const sourceFiles = sourceResult.fileMap;
|
|
152
|
-
const originalPaths = sourceResult.originalPaths;
|
|
153
|
-
logger.progress(`Loaded ${sourceFiles.size} source file(s)`, 'success');
|
|
154
|
-
const collisions = (0, collisionDetector_1.detectCollisions)(sourceFiles, config.transforms);
|
|
155
|
-
if (collisions.length > 0)
|
|
156
|
-
(0, collisionDetector_1.validateNoCollisions)(collisions);
|
|
157
|
-
if (logger.shouldShow('debug'))
|
|
158
|
-
logger.debug('Filename collision check: passed');
|
|
159
|
-
const destinationResult = await (0, fileLoader_1.loadFiles)({
|
|
160
|
-
baseDirectory: config.destination,
|
|
161
|
-
include: config.include,
|
|
162
|
-
exclude: config.exclude
|
|
163
|
-
}, logger);
|
|
164
|
-
const destinationFiles = destinationResult.fileMap;
|
|
165
|
-
logger.progress(`Loaded ${destinationFiles.size} destination file(s)`, 'success');
|
|
166
|
-
if (command.listFiles) {
|
|
167
|
-
const sourceFilesList = [...sourceFiles.keys()].toSorted();
|
|
168
|
-
const destinationFilesList = [...destinationFiles.keys()].toSorted();
|
|
169
|
-
console.log(chalk_1.default.cyan('\nπ Files to be synced:\n'));
|
|
170
|
-
console.log(chalk_1.default.green(`Source files: ${sourceFilesList.length}`));
|
|
171
|
-
for (const file of sourceFilesList)
|
|
172
|
-
console.log(` ${chalk_1.default.dim(file)}`);
|
|
173
|
-
console.log(chalk_1.default.yellow(`\nDestination files: ${destinationFilesList.length}`));
|
|
174
|
-
for (const file of destinationFilesList)
|
|
175
|
-
console.log(` ${chalk_1.default.dim(file)}`);
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
151
|
if (command.formatOnly) {
|
|
179
152
|
if (!config.outputFormat) {
|
|
180
153
|
logger.log(chalk_1.default.yellow('\nβ οΈ No outputFormat configured. Nothing to format.'));
|
|
181
154
|
return;
|
|
182
155
|
}
|
|
156
|
+
logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Loading destination files...', 'loading'));
|
|
157
|
+
const destinationResult = await (0, fileLoader_1.loadFiles)({
|
|
158
|
+
baseDirectory: config.destination,
|
|
159
|
+
include: config.include,
|
|
160
|
+
exclude: config.exclude
|
|
161
|
+
}, logger);
|
|
162
|
+
const destinationFiles = destinationResult.fileMap;
|
|
163
|
+
logger.progress(`Loaded ${destinationFiles.size} destination file(s)`, 'success');
|
|
164
|
+
if (command.listFiles) {
|
|
165
|
+
const filesList = [...destinationFiles.keys()].toSorted();
|
|
166
|
+
console.log(chalk_1.default.cyan('\nπ Files to be formatted:\n'));
|
|
167
|
+
console.log(chalk_1.default.yellow(`Destination files: ${filesList.length}`));
|
|
168
|
+
for (const file of filesList)
|
|
169
|
+
console.log(` ${chalk_1.default.dim(file)}`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
183
172
|
logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Formatting files...', 'info'));
|
|
184
173
|
let formattedCount = 0;
|
|
185
174
|
const errors = [];
|
|
186
175
|
for (const [relativePath, content] of destinationFiles) {
|
|
187
176
|
if (!(0, fileType_1.isYamlFile)(relativePath))
|
|
188
177
|
continue;
|
|
178
|
+
if ((0, commentOnlyDetector_1.isCommentOnlyContent)(content))
|
|
179
|
+
continue;
|
|
189
180
|
try {
|
|
190
181
|
const formatted = (0, yamlFormatter_1.formatYaml)(content, relativePath, config.outputFormat);
|
|
191
182
|
if (formatted !== content) {
|
|
@@ -215,12 +206,51 @@ const main = async () => {
|
|
|
215
206
|
}
|
|
216
207
|
return;
|
|
217
208
|
}
|
|
209
|
+
if (!config.source) {
|
|
210
|
+
logger.error('\nSource folder is required for sync operations.', 'critical');
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
const syncConfig = config;
|
|
214
|
+
logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Loading files...', 'loading'));
|
|
215
|
+
const sourceResult = await (0, fileLoader_1.loadFiles)({
|
|
216
|
+
baseDirectory: syncConfig.source,
|
|
217
|
+
include: syncConfig.include,
|
|
218
|
+
exclude: syncConfig.exclude,
|
|
219
|
+
transforms: syncConfig.transforms
|
|
220
|
+
}, logger);
|
|
221
|
+
const sourceFiles = sourceResult.fileMap;
|
|
222
|
+
const originalPaths = sourceResult.originalPaths;
|
|
223
|
+
logger.progress(`Loaded ${sourceFiles.size} source file(s)`, 'success');
|
|
224
|
+
const collisions = (0, collisionDetector_1.detectCollisions)(sourceFiles, syncConfig.transforms);
|
|
225
|
+
if (collisions.length > 0)
|
|
226
|
+
(0, collisionDetector_1.validateNoCollisions)(collisions);
|
|
227
|
+
if (logger.shouldShow('debug'))
|
|
228
|
+
logger.debug('Filename collision check: passed');
|
|
229
|
+
const destinationResult = await (0, fileLoader_1.loadFiles)({
|
|
230
|
+
baseDirectory: syncConfig.destination,
|
|
231
|
+
include: syncConfig.include,
|
|
232
|
+
exclude: syncConfig.exclude
|
|
233
|
+
}, logger);
|
|
234
|
+
const destinationFiles = destinationResult.fileMap;
|
|
235
|
+
logger.progress(`Loaded ${destinationFiles.size} destination file(s)`, 'success');
|
|
236
|
+
if (command.listFiles) {
|
|
237
|
+
const sourceFilesList = [...sourceFiles.keys()].toSorted();
|
|
238
|
+
const destinationFilesList = [...destinationFiles.keys()].toSorted();
|
|
239
|
+
console.log(chalk_1.default.cyan('\nπ Files to be synced:\n'));
|
|
240
|
+
console.log(chalk_1.default.green(`Source files: ${sourceFilesList.length}`));
|
|
241
|
+
for (const file of sourceFilesList)
|
|
242
|
+
console.log(` ${chalk_1.default.dim(file)}`);
|
|
243
|
+
console.log(chalk_1.default.yellow(`\nDestination files: ${destinationFilesList.length}`));
|
|
244
|
+
for (const file of destinationFilesList)
|
|
245
|
+
console.log(` ${chalk_1.default.dim(file)}`);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
218
248
|
logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Computing differences...', 'info'));
|
|
219
|
-
const diffResult = (0, fileDiff_1.computeFileDiff)(sourceFiles, destinationFiles,
|
|
249
|
+
const diffResult = (0, fileDiff_1.computeFileDiff)(sourceFiles, destinationFiles, syncConfig, logger, originalPaths);
|
|
220
250
|
if (logger.shouldShow('debug'))
|
|
221
251
|
logger.debug('Diff pipeline: parse β transforms β skipPath β normalize β compare');
|
|
222
252
|
if (command.diff && !command.quiet)
|
|
223
|
-
(0, consoleDiffReporter_1.showConsoleDiff)(diffResult,
|
|
253
|
+
(0, consoleDiffReporter_1.showConsoleDiff)(diffResult, syncConfig);
|
|
224
254
|
else {
|
|
225
255
|
logger.log(` New files: ${diffResult.addedFiles.length}`);
|
|
226
256
|
logger.log(` Deleted files: ${diffResult.deletedFiles.length}`);
|
|
@@ -230,7 +260,7 @@ const main = async () => {
|
|
|
230
260
|
if (command.suggest) {
|
|
231
261
|
logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Analyzing differences for suggestions...', 'info'));
|
|
232
262
|
try {
|
|
233
|
-
const suggestions = (0, suggestionEngine_1.analyzeDifferencesForSuggestions)(diffResult,
|
|
263
|
+
const suggestions = (0, suggestionEngine_1.analyzeDifferencesForSuggestions)(diffResult, syncConfig, command.suggestThreshold);
|
|
234
264
|
const yaml = (0, suggestionEngine_1.formatSuggestionsAsYaml)(suggestions);
|
|
235
265
|
console.log(chalk_1.default.cyan('\nπ‘ Suggested Configuration:\n'));
|
|
236
266
|
console.log(yaml);
|
|
@@ -251,7 +281,7 @@ const main = async () => {
|
|
|
251
281
|
}
|
|
252
282
|
}
|
|
253
283
|
const configFileDirectory = node_path_1.default.dirname(node_path_1.default.resolve(command.config));
|
|
254
|
-
const validationResult = (0, stopRulesValidator_1.validateStopRules)(diffResult,
|
|
284
|
+
const validationResult = (0, stopRulesValidator_1.validateStopRules)(diffResult, syncConfig.stopRules, configFileDirectory, logger);
|
|
255
285
|
if (validationResult.violations.length > 0)
|
|
256
286
|
if (command.force)
|
|
257
287
|
for (const violation of validationResult.violations)
|
|
@@ -270,20 +300,20 @@ const main = async () => {
|
|
|
270
300
|
console.log(chalk_1.default.dim('β'.repeat(60)));
|
|
271
301
|
console.log(` ${chalk_1.default.green('Added:')} ${diffResult.addedFiles.length} files`);
|
|
272
302
|
console.log(` ${chalk_1.default.yellow('Changed:')} ${diffResult.changedFiles.length} files`);
|
|
273
|
-
console.log(` ${chalk_1.default.red('Deleted:')} ${diffResult.deletedFiles.length} files (${
|
|
303
|
+
console.log(` ${chalk_1.default.red('Deleted:')} ${diffResult.deletedFiles.length} files (${syncConfig.prune ? 'prune enabled' : 'prune disabled'})`);
|
|
274
304
|
console.log(` ${chalk_1.default.blue('Unchanged:')} ${diffResult.unchangedFiles.length} files`);
|
|
275
305
|
console.log(chalk_1.default.dim('β'.repeat(60)));
|
|
276
|
-
if (diffResult.deletedFiles.length > 0 &&
|
|
306
|
+
if (diffResult.deletedFiles.length > 0 && syncConfig.prune)
|
|
277
307
|
console.warn(chalk_1.default.red('β οΈ Warning: Prune is enabled. Files will be permanently deleted!'));
|
|
278
308
|
console.log(chalk_1.default.dim('\nPress Ctrl+C to cancel, or use --dry-run to preview changes first.\n'));
|
|
279
|
-
if (
|
|
280
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
309
|
+
if (syncConfig.confirmationDelay > 0)
|
|
310
|
+
await new Promise((resolve) => setTimeout(resolve, syncConfig.confirmationDelay));
|
|
281
311
|
}
|
|
282
|
-
const formattedFiles = await (0, fileUpdater_1.updateFiles)(diffResult, sourceFiles, destinationFiles,
|
|
312
|
+
const formattedFiles = await (0, fileUpdater_1.updateFiles)(diffResult, sourceFiles, destinationFiles, syncConfig, command.dryRun, command.skipFormat, logger);
|
|
283
313
|
if (command.diffHtml && !command.quiet)
|
|
284
|
-
await (0, htmlReporter_1.generateHtmlReport)(diffResult, formattedFiles,
|
|
314
|
+
await (0, htmlReporter_1.generateHtmlReport)(diffResult, formattedFiles, syncConfig, command.dryRun, logger);
|
|
285
315
|
if (command.diffJson)
|
|
286
|
-
(0, jsonReporter_1.generateJsonReport)(diffResult, formattedFiles, validationResult,
|
|
316
|
+
(0, jsonReporter_1.generateJsonReport)(diffResult, formattedFiles, validationResult, syncConfig, command.dryRun, package_json_1.default.version);
|
|
287
317
|
};
|
|
288
318
|
(async () => {
|
|
289
319
|
try {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const isCommentOnlyContent: (content: string) => boolean;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isCommentOnlyContent = void 0;
|
|
4
|
+
const isCommentOnlyContent = (content) => {
|
|
5
|
+
if (!content)
|
|
6
|
+
return false;
|
|
7
|
+
const lines = content.split('\n');
|
|
8
|
+
for (const line of lines) {
|
|
9
|
+
const trimmed = line.trim();
|
|
10
|
+
if (trimmed === '')
|
|
11
|
+
continue;
|
|
12
|
+
if (trimmed.startsWith('#'))
|
|
13
|
+
continue;
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return true;
|
|
17
|
+
};
|
|
18
|
+
exports.isCommentOnlyContent = isCommentOnlyContent;
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -20,3 +20,4 @@ export { ANTONYM_PAIRS, ARRAY_KEY_FIELDS, CONFIDENCE_DEFAULTS, CONSTRAINT_FIELD_
|
|
|
20
20
|
export { applyFixedValues, getFixedValuesForFile, setValueAtPath } from './fixedValues';
|
|
21
21
|
export type { ApplicableFilter } from './arrayMerger';
|
|
22
22
|
export { findMatchingTargetItem, getApplicableArrayFilters, itemMatchesAnyFilter, shouldPreserveItem } from './arrayMerger';
|
|
23
|
+
export { isCommentOnlyContent } from './commentOnlyDetector';
|
package/dist/utils/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.NUMERIC_MIN_FLOOR = exports.MAX_EXAMPLES_PER_SUGGESTION = exports.ISO_TIMESTAMP_PATTERN = exports.FILTER_THRESHOLDS = exports.CONSTRAINT_FIELD_NAMES = exports.CONFIDENCE_DEFAULTS = exports.ARRAY_KEY_FIELDS = exports.ANTONYM_PAIRS = exports.isYamlSeq = exports.isYamlMap = exports.isYamlCollection = exports.isScalar = exports.extractScalarValue = exports.extractKeyValue = exports.validateVersionString = exports.applyRegexRulesSequentially = exports.validateTargetedRegex = exports.validatePathlessRegex = exports.getAllValuesRecursive = exports.RegexPatternFileLoaderError = exports.loadRegexPatternsFromKeys = exports.loadRegexPatternArray = exports.isRegexPatternFileLoaderError = exports.TransformFileLoaderError = exports.loadTransformFiles = exports.loadTransformFile = exports.isTransformFileLoaderError = exports.YamlFileLoaderError = exports.loadYamlFile = exports.isYamlFileLoaderError = exports.escapeRegex = exports.VersionCheckerError = exports.isVersionCheckerError = exports.checkForUpdates = exports.detectArrayChanges = exports.generateUnifiedDiff = exports.PatternMatcher = exports.globalMatcher = exports.isYamlFile = exports.parseJsonPath = exports.parseFilterSegment = exports.matchesFilter = exports.isFilterSegment = exports.getValueAtPath = exports.clearJsonPathCache = exports.serializeForDiff = exports.normalizeForComparison = exports.deepEqual = exports.createErrorTypeGuard = exports.createErrorClass = void 0;
|
|
4
|
-
exports.shouldPreserveItem = exports.itemMatchesAnyFilter = exports.getApplicableArrayFilters = exports.findMatchingTargetItem = exports.setValueAtPath = exports.getFixedValuesForFile = exports.applyFixedValues = exports.UUID_PATTERN = exports.SEMVER_PATTERN = exports.SEMANTIC_PATTERNS = exports.SEMANTIC_KEYWORDS = exports.PROBLEMATIC_REGEX_CHARS = exports.NUMERIC_MIN_MULTIPLIER = void 0;
|
|
4
|
+
exports.isCommentOnlyContent = exports.shouldPreserveItem = exports.itemMatchesAnyFilter = exports.getApplicableArrayFilters = exports.findMatchingTargetItem = exports.setValueAtPath = exports.getFixedValuesForFile = exports.applyFixedValues = exports.UUID_PATTERN = exports.SEMVER_PATTERN = exports.SEMANTIC_PATTERNS = exports.SEMANTIC_KEYWORDS = exports.PROBLEMATIC_REGEX_CHARS = exports.NUMERIC_MIN_MULTIPLIER = void 0;
|
|
5
5
|
var errors_1 = require("./errors");
|
|
6
6
|
Object.defineProperty(exports, "createErrorClass", { enumerable: true, get: function () { return errors_1.createErrorClass; } });
|
|
7
7
|
Object.defineProperty(exports, "createErrorTypeGuard", { enumerable: true, get: function () { return errors_1.createErrorTypeGuard; } });
|
|
@@ -84,3 +84,5 @@ Object.defineProperty(exports, "findMatchingTargetItem", { enumerable: true, get
|
|
|
84
84
|
Object.defineProperty(exports, "getApplicableArrayFilters", { enumerable: true, get: function () { return arrayMerger_1.getApplicableArrayFilters; } });
|
|
85
85
|
Object.defineProperty(exports, "itemMatchesAnyFilter", { enumerable: true, get: function () { return arrayMerger_1.itemMatchesAnyFilter; } });
|
|
86
86
|
Object.defineProperty(exports, "shouldPreserveItem", { enumerable: true, get: function () { return arrayMerger_1.shouldPreserveItem; } });
|
|
87
|
+
var commentOnlyDetector_1 = require("./commentOnlyDetector");
|
|
88
|
+
Object.defineProperty(exports, "isCommentOnlyContent", { enumerable: true, get: function () { return commentOnlyDetector_1.isCommentOnlyContent; } });
|
package/dist/yamlFormatter.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.formatYaml = exports.isYamlFormatterError = exports.YamlFormatterError = void 0;
|
|
7
7
|
const yaml_1 = __importDefault(require("yaml"));
|
|
8
8
|
const constants_1 = require("./constants");
|
|
9
|
+
const commentOnlyDetector_1 = require("./utils/commentOnlyDetector");
|
|
9
10
|
const errors_1 = require("./utils/errors");
|
|
10
11
|
const jsonPath_1 = require("./utils/jsonPath");
|
|
11
12
|
const patternMatcher_1 = require("./utils/patternMatcher");
|
|
@@ -73,6 +74,8 @@ const formatYaml = (content, filePath, outputFormat) => {
|
|
|
73
74
|
return content;
|
|
74
75
|
if (!content || content.trim() === '')
|
|
75
76
|
return content;
|
|
77
|
+
if ((0, commentOnlyDetector_1.isCommentOnlyContent)(content))
|
|
78
|
+
return content;
|
|
76
79
|
try {
|
|
77
80
|
const yamlDocument = yaml_1.default.parseDocument(content);
|
|
78
81
|
const rules = getFormattingRules(filePath, outputFormat);
|
|
@@ -310,7 +313,7 @@ const applyKeySeparator = (yamlString, indent) => {
|
|
|
310
313
|
const baseIndent = ' '.repeat(indent);
|
|
311
314
|
let hasSeenSecondLevelKey = false;
|
|
312
315
|
for (const line of lines) {
|
|
313
|
-
if (!line)
|
|
316
|
+
if (!line.trim())
|
|
314
317
|
continue;
|
|
315
318
|
if (topLevelKeys > 1) {
|
|
316
319
|
if (!line.startsWith(' ') && result.length > 0 && result.at(-1) !== '')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "helm-env-delta",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.3",
|
|
4
4
|
"description": "HelmEnvDelta β environment-aware YAML delta and sync for GitOps",
|
|
5
5
|
"author": "BCsabaEngine",
|
|
6
6
|
"license": "ISC",
|
|
@@ -70,8 +70,8 @@
|
|
|
70
70
|
"@types/hogan.js": "^3.0.5",
|
|
71
71
|
"@types/node": "^25.0.10",
|
|
72
72
|
"@types/picomatch": "^4.0.2",
|
|
73
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
74
|
-
"@typescript-eslint/parser": "^8.
|
|
73
|
+
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
|
74
|
+
"@typescript-eslint/parser": "^8.54.0",
|
|
75
75
|
"@vitest/coverage-v8": "^4.0.18",
|
|
76
76
|
"eslint": "^9.39.2",
|
|
77
77
|
"eslint-config-prettier": "^10.1.8",
|