helm-env-delta 1.6.0 → 1.7.1
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 +92 -20
- package/dist/configMerger.d.ts +1 -0
- package/dist/configMerger.js +4 -4
- package/dist/configWarnings.d.ts +5 -1
- package/dist/configWarnings.js +4 -1
- package/dist/consoleDiffReporter.js +16 -37
- package/dist/constants.d.ts +5 -0
- package/dist/constants.js +8 -0
- package/dist/fileDiff.d.ts +11 -2
- package/dist/fileDiff.js +42 -6
- package/dist/fileLoader.d.ts +6 -1
- package/dist/fileLoader.js +11 -11
- package/dist/fileUpdater.d.ts +19 -1
- package/dist/fileUpdater.js +64 -26
- package/dist/htmlReporter.d.ts +2 -7
- package/dist/htmlReporter.js +52 -484
- package/dist/index.js +44 -9
- package/dist/jsonReporter.d.ts +1 -0
- package/dist/jsonReporter.js +2 -6
- package/dist/patternUsageValidator.d.ts +13 -0
- package/dist/patternUsageValidator.js +156 -0
- package/dist/reporters/browserLauncher.d.ts +20 -0
- package/dist/reporters/browserLauncher.js +64 -0
- package/dist/reporters/htmlStyles.d.ts +2 -0
- package/dist/reporters/htmlStyles.js +279 -0
- package/dist/reporters/htmlTemplate.d.ts +8 -0
- package/dist/reporters/htmlTemplate.js +105 -0
- package/dist/stopRulesValidator.d.ts +8 -0
- package/dist/stopRulesValidator.js +72 -182
- package/dist/suggestionEngine.d.ts +1 -0
- package/dist/utils/arrayDiffProcessor.d.ts +14 -0
- package/dist/utils/arrayDiffProcessor.js +43 -0
- package/dist/utils/collisionDetector.d.ts +1 -0
- package/dist/utils/diffGenerator.js +1 -3
- package/dist/utils/errors.d.ts +2 -0
- package/dist/utils/errors.js +8 -1
- package/dist/utils/fileType.js +1 -3
- package/dist/utils/filenameTransformer.d.ts +17 -6
- package/dist/utils/filenameTransformer.js +27 -31
- package/dist/utils/index.d.ts +6 -1
- package/dist/utils/index.js +22 -1
- package/dist/utils/jsonPath.d.ts +5 -0
- package/dist/utils/jsonPath.js +75 -5
- package/dist/utils/regexPatternFileLoader.d.ts +2 -0
- package/dist/utils/regexTransform.d.ts +2 -0
- package/dist/utils/regexTransform.js +17 -0
- package/dist/utils/regexValidator.d.ts +22 -0
- package/dist/utils/regexValidator.js +71 -0
- package/dist/utils/transformFileLoader.d.ts +2 -0
- package/dist/utils/transformer.d.ts +1 -0
- package/dist/utils/transformer.js +3 -8
- package/dist/utils/versionChecker.d.ts +1 -0
- package/dist/utils/versionValidator.d.ts +6 -0
- package/dist/utils/versionValidator.js +88 -0
- package/dist/utils/yamlFileLoader.d.ts +2 -0
- package/dist/utils/yamlTypeGuards.d.ts +7 -0
- package/dist/utils/yamlTypeGuards.js +35 -0
- package/dist/yamlFormatter.d.ts +12 -5
- package/dist/yamlFormatter.js +66 -98
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ HelmEnvDelta (`hed`) automates environment synchronization for GitOps workflows
|
|
|
48
48
|
|
|
49
49
|
📊 **Multiple Reports** - Console, HTML (visual), and JSON (CI/CD) output formats.
|
|
50
50
|
|
|
51
|
-
🔍 **Discovery Tools** - Preview files (`--list-files`), inspect config (`--show-config`), validate with warnings.
|
|
51
|
+
🔍 **Discovery Tools** - Preview files (`--list-files`), inspect config (`--show-config`), validate with comprehensive warnings including unused pattern detection.
|
|
52
52
|
|
|
53
53
|
💡 **Smart Suggestions** - Heuristic analysis (`--suggest`) detects patterns and recommends transforms and stop rules automatically. Control sensitivity with `--suggest-threshold`.
|
|
54
54
|
|
|
@@ -374,7 +374,26 @@ skipPath:
|
|
|
374
374
|
- 'resources.limits'
|
|
375
375
|
```
|
|
376
376
|
|
|
377
|
-
|
|
377
|
+
#### Filter Expressions (Skip by Name)
|
|
378
|
+
|
|
379
|
+
Skip specific array items by property value using filter syntax:
|
|
380
|
+
|
|
381
|
+
```yaml
|
|
382
|
+
skipPath:
|
|
383
|
+
'**/*.yaml':
|
|
384
|
+
- 'env[name=SECRET_KEY]' # Skip item where name=SECRET_KEY
|
|
385
|
+
- 'env[name=INTERNAL_TOKEN].value' # Skip nested field in matching item
|
|
386
|
+
- 'containers[name=sidecar]' # Skip entire sidecar container
|
|
387
|
+
- 'spec.containers[name=app].env[name=DEBUG]' # Nested filters
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**Syntax:**
|
|
391
|
+
|
|
392
|
+
- `array[prop=value]` - Match items where property equals value
|
|
393
|
+
- `array[prop="value with spaces"]` - Quoted values for special characters
|
|
394
|
+
- Combine with wildcards: `containers[name=app].env[*].value`
|
|
395
|
+
|
|
396
|
+
**Use cases:** Namespaces, replicas, resource limits, secrets, URLs, environment-specific array items.
|
|
378
397
|
|
|
379
398
|
---
|
|
380
399
|
|
|
@@ -610,23 +629,23 @@ hed --config <file> [options] # Short alias
|
|
|
610
629
|
|
|
611
630
|
### Options
|
|
612
631
|
|
|
613
|
-
| Flag | Description
|
|
614
|
-
| --------------------------- |
|
|
615
|
-
| `--config <path>` | **Required** - Configuration file
|
|
616
|
-
| `--validate` | Validate config and
|
|
617
|
-
| `--suggest` | Analyze differences and suggest config updates
|
|
618
|
-
| `--suggest-threshold <0-1>` | Minimum confidence for suggestions (default: 0.3)
|
|
619
|
-
| `--dry-run` | Preview changes without writing files
|
|
620
|
-
| `--force` | Override stop rules
|
|
621
|
-
| `--diff` | Show console diff
|
|
622
|
-
| `--diff-html` | Generate HTML report (opens in browser)
|
|
623
|
-
| `--diff-json` | Output JSON to stdout (pipe to jq)
|
|
624
|
-
| `--list-files` | List source/destination files without processing
|
|
625
|
-
| `--show-config` | Display resolved config after inheritance
|
|
626
|
-
| `--skip-format` | Skip YAML formatting
|
|
627
|
-
| `--no-color` | Disable colored output (CI/accessibility)
|
|
628
|
-
| `--verbose` | Show detailed debug info
|
|
629
|
-
| `--quiet` | Suppress output except errors
|
|
632
|
+
| Flag | Description |
|
|
633
|
+
| --------------------------- | -------------------------------------------------- |
|
|
634
|
+
| `--config <path>` | **Required** - Configuration file |
|
|
635
|
+
| `--validate` | Validate config and pattern usage (shows warnings) |
|
|
636
|
+
| `--suggest` | Analyze differences and suggest config updates |
|
|
637
|
+
| `--suggest-threshold <0-1>` | Minimum confidence for suggestions (default: 0.3) |
|
|
638
|
+
| `--dry-run` | Preview changes without writing files |
|
|
639
|
+
| `--force` | Override stop rules |
|
|
640
|
+
| `--diff` | Show console diff |
|
|
641
|
+
| `--diff-html` | Generate HTML report (opens in browser) |
|
|
642
|
+
| `--diff-json` | Output JSON to stdout (pipe to jq) |
|
|
643
|
+
| `--list-files` | List source/destination files without processing |
|
|
644
|
+
| `--show-config` | Display resolved config after inheritance |
|
|
645
|
+
| `--skip-format` | Skip YAML formatting |
|
|
646
|
+
| `--no-color` | Disable colored output (CI/accessibility) |
|
|
647
|
+
| `--verbose` | Show detailed debug info |
|
|
648
|
+
| `--quiet` | Suppress output except errors |
|
|
630
649
|
|
|
631
650
|
### Examples
|
|
632
651
|
|
|
@@ -730,7 +749,7 @@ git push origin main
|
|
|
730
749
|
|
|
731
750
|
✅ **Flexibility** - Per-file patterns. Config inheritance. Regex transforms.
|
|
732
751
|
|
|
733
|
-
✅ **Reliability** -
|
|
752
|
+
✅ **Reliability** - 917 tests, 84% coverage. Battle-tested.
|
|
734
753
|
|
|
735
754
|
---
|
|
736
755
|
|
|
@@ -824,6 +843,57 @@ env:
|
|
|
824
843
|
|
|
825
844
|
---
|
|
826
845
|
|
|
846
|
+
### 🔍 Pattern Usage Validation
|
|
847
|
+
|
|
848
|
+
HelmEnvDelta validates that your configuration patterns actually match files and exist in your YAML structure. This helps catch typos, outdated patterns, and misconfigured rules early.
|
|
849
|
+
|
|
850
|
+
**Run validation:**
|
|
851
|
+
|
|
852
|
+
```bash
|
|
853
|
+
helm-env-delta --config config.yaml --validate
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
**What gets validated:**
|
|
857
|
+
|
|
858
|
+
1. **exclude patterns** - Warns if pattern matches no files
|
|
859
|
+
2. **skipPath patterns** - Two-level validation:
|
|
860
|
+
- Glob pattern must match at least one file
|
|
861
|
+
- JSONPath must exist in at least one matched file
|
|
862
|
+
- Filter expressions `[prop=value]` validated against actual array items
|
|
863
|
+
3. **stopRules patterns** - Two-level validation:
|
|
864
|
+
- Glob pattern must match at least one file
|
|
865
|
+
- JSONPath (if specified) must exist in at least one matched file
|
|
866
|
+
|
|
867
|
+
**Example output:**
|
|
868
|
+
|
|
869
|
+
```
|
|
870
|
+
✓ Configuration is valid
|
|
871
|
+
|
|
872
|
+
⚠️ Pattern Usage Warnings (non-fatal):
|
|
873
|
+
|
|
874
|
+
• Exclude pattern 'test/**/*.yaml' matches no files
|
|
875
|
+
• skipPath pattern 'legacy/*.yaml' matches no files
|
|
876
|
+
• skipPath JSONPath 'microservice.replicaCountX' not found in any matched files (Pattern: svc/**/values.yaml, matches 50 file(s))
|
|
877
|
+
• stopRules glob pattern 'helm-charts/**/*.yaml' matches no files (3 rule(s) defined)
|
|
878
|
+
• stopRules JSONPath 'spec.replicas' not found in any matched files (Rule type: numeric, matches 5 file(s))
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
**Validation phases:**
|
|
882
|
+
|
|
883
|
+
- **Phase 1 (Static)**: Validates config syntax, checks for inefficient patterns, duplicates, conflicts
|
|
884
|
+
- **Phase 2 (File-Based)**: Loads files and validates all patterns actually match and paths exist
|
|
885
|
+
|
|
886
|
+
**When to use:**
|
|
887
|
+
|
|
888
|
+
- After updating configuration
|
|
889
|
+
- When patterns stop working as expected
|
|
890
|
+
- To verify config file patterns after file reorganization
|
|
891
|
+
- As part of CI/CD pre-flight checks
|
|
892
|
+
|
|
893
|
+
**Important:** Warnings are non-fatal and don't block execution. They help you catch potential issues but won't stop your workflow.
|
|
894
|
+
|
|
895
|
+
---
|
|
896
|
+
|
|
827
897
|
### 🔀 Deep Merge
|
|
828
898
|
|
|
829
899
|
Preserves destination values for skipped paths.
|
|
@@ -880,6 +950,8 @@ spec:
|
|
|
880
950
|
# ✅ Correct
|
|
881
951
|
- 'spec.replicas' # No prefix
|
|
882
952
|
- 'env[*].name' # Array wildcard
|
|
953
|
+
- 'env[name=DEBUG]' # Filter by property value
|
|
954
|
+
- 'containers[name=app].env[name=SECRET]' # Nested filters
|
|
883
955
|
```
|
|
884
956
|
|
|
885
957
|
---
|
package/dist/configMerger.d.ts
CHANGED
package/dist/configMerger.js
CHANGED
|
@@ -41,6 +41,7 @@ const node_fs_1 = require("node:fs");
|
|
|
41
41
|
const node_path_1 = __importDefault(require("node:path"));
|
|
42
42
|
const YAML = __importStar(require("yaml"));
|
|
43
43
|
const configFile_1 = require("./configFile");
|
|
44
|
+
const constants_1 = require("./constants");
|
|
44
45
|
const errors_1 = require("./utils/errors");
|
|
45
46
|
const ConfigMergerErrorClass = (0, errors_1.createErrorClass)('Config Merger Error', {
|
|
46
47
|
CIRCULAR_DEPENDENCY: 'Circular dependency detected in extends chain',
|
|
@@ -69,7 +70,6 @@ class ConfigMergerError extends ConfigMergerErrorClass {
|
|
|
69
70
|
}
|
|
70
71
|
exports.ConfigMergerError = ConfigMergerError;
|
|
71
72
|
exports.isConfigMergerError = (0, errors_1.createErrorTypeGuard)(ConfigMergerError);
|
|
72
|
-
const MAX_EXTENDS_DEPTH = 5;
|
|
73
73
|
const mergeConfigs = (parent, child) => {
|
|
74
74
|
const merged = {};
|
|
75
75
|
if (child.source !== undefined)
|
|
@@ -143,14 +143,14 @@ const mergePerFileRecords = (parent, child) => {
|
|
|
143
143
|
return merged;
|
|
144
144
|
};
|
|
145
145
|
const resolveConfigWithExtends = (configPath, visited = new Set(), depth = 0, logger) => {
|
|
146
|
-
if (depth >
|
|
147
|
-
const depthError = new ConfigMergerError(
|
|
146
|
+
if (depth > constants_1.MAX_CONFIG_EXTENDS_DEPTH) {
|
|
147
|
+
const depthError = new ConfigMergerError(`Extends chain exceeds maximum depth of ${constants_1.MAX_CONFIG_EXTENDS_DEPTH}`, {
|
|
148
148
|
code: 'MAX_DEPTH_EXCEEDED',
|
|
149
149
|
path: configPath,
|
|
150
150
|
depth
|
|
151
151
|
});
|
|
152
152
|
depthError.message += '\n\n Hint: Simplify your config inheritance:';
|
|
153
|
-
depthError.message +=
|
|
153
|
+
depthError.message += `\n - Maximum depth is ${constants_1.MAX_CONFIG_EXTENDS_DEPTH} levels`;
|
|
154
154
|
depthError.message += `\n - Current depth: ${depth}`;
|
|
155
155
|
depthError.message += '\n - Consider consolidating base configs';
|
|
156
156
|
throw depthError;
|
package/dist/configWarnings.d.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import type { FinalConfig } from './configFile';
|
|
2
|
-
export
|
|
2
|
+
export interface WarningResult {
|
|
3
|
+
warnings: string[];
|
|
4
|
+
hasWarnings: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare const validateConfigWarnings: (config: FinalConfig) => WarningResult;
|
package/dist/configWarnings.js
CHANGED
|
@@ -24,6 +24,9 @@ const validateConfigWarnings = (config) => {
|
|
|
24
24
|
for (const [pattern, rules] of Object.entries(config.transforms))
|
|
25
25
|
if ((rules.content?.length ?? 0) === 0 && (rules.filename?.length ?? 0) === 0)
|
|
26
26
|
warnings.push(`Transform pattern '${pattern}' has empty content and filename arrays (will have no effect)`);
|
|
27
|
-
return
|
|
27
|
+
return {
|
|
28
|
+
warnings,
|
|
29
|
+
hasWarnings: warnings.length > 0
|
|
30
|
+
};
|
|
28
31
|
};
|
|
29
32
|
exports.validateConfigWarnings = validateConfigWarnings;
|
|
@@ -6,12 +6,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.showConsoleDiff = void 0;
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
8
|
const yaml_1 = __importDefault(require("yaml"));
|
|
9
|
-
const arrayDiffer_1 = require("./arrayDiffer");
|
|
10
9
|
const fileDiff_1 = require("./fileDiff");
|
|
11
|
-
const
|
|
10
|
+
const arrayDiffProcessor_1 = require("./utils/arrayDiffProcessor");
|
|
12
11
|
const diffGenerator_1 = require("./utils/diffGenerator");
|
|
13
12
|
const fileType_1 = require("./utils/fileType");
|
|
14
|
-
const jsonPath_1 = require("./utils/jsonPath");
|
|
15
13
|
const serialization_1 = require("./utils/serialization");
|
|
16
14
|
const colorizeUnifiedDiff = (diff) => {
|
|
17
15
|
return diff
|
|
@@ -41,29 +39,28 @@ const formatDeletedFiles = (files) => {
|
|
|
41
39
|
const fileList = files.map((file) => chalk_1.default.red(` - ${file}`)).join('\n');
|
|
42
40
|
return `${header}\n${fileList}\n`;
|
|
43
41
|
};
|
|
44
|
-
const formatArrayDiff = (
|
|
45
|
-
const diff = (0, arrayDiffer_1.diffArrays)(sourceArray, destinationArray);
|
|
42
|
+
const formatArrayDiff = (change) => {
|
|
46
43
|
let output = '';
|
|
47
|
-
if (
|
|
48
|
-
output += chalk_1.default.red.bold(`\n Removed (${
|
|
49
|
-
for (const item of
|
|
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) {
|
|
50
47
|
const yaml = yaml_1.default.stringify(item, { indent: 4 });
|
|
51
48
|
const lines = yaml.split('\n').filter((l) => l.trim());
|
|
52
49
|
output += lines.map((l) => chalk_1.default.red(` - ${l}`)).join('\n');
|
|
53
50
|
output += '\n';
|
|
54
51
|
}
|
|
55
52
|
}
|
|
56
|
-
if (
|
|
57
|
-
output += chalk_1.default.green.bold(`\n Added (${
|
|
58
|
-
for (const item of
|
|
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) {
|
|
59
56
|
const yaml = yaml_1.default.stringify(item, { indent: 4 });
|
|
60
57
|
const lines = yaml.split('\n').filter((l) => l.trim());
|
|
61
58
|
output += lines.map((l) => chalk_1.default.green(` + ${l}`)).join('\n');
|
|
62
59
|
output += '\n';
|
|
63
60
|
}
|
|
64
61
|
}
|
|
65
|
-
if (
|
|
66
|
-
output += chalk_1.default.gray(`\n Unchanged: ${
|
|
62
|
+
if (change.unchanged.length > 0)
|
|
63
|
+
output += chalk_1.default.gray(`\n Unchanged: ${change.unchanged.length} items\n`);
|
|
67
64
|
return output;
|
|
68
65
|
};
|
|
69
66
|
const formatChangedFile = (file, config) => {
|
|
@@ -86,8 +83,8 @@ ${skipPathInfo}
|
|
|
86
83
|
${colorizedDiff}
|
|
87
84
|
`;
|
|
88
85
|
}
|
|
89
|
-
const
|
|
90
|
-
if (!
|
|
86
|
+
const arrayInfo = (0, arrayDiffProcessor_1.detectArrayChanges)(file);
|
|
87
|
+
if (!arrayInfo.hasArrays) {
|
|
91
88
|
const destinationContent = (0, serialization_1.serializeForDiff)(file.processedDestContent, true);
|
|
92
89
|
const sourceContent = (0, serialization_1.serializeForDiff)(file.processedSourceContent, true);
|
|
93
90
|
const unifiedDiff = (0, diffGenerator_1.generateUnifiedDiff)(file.path, destinationContent, sourceContent);
|
|
@@ -106,30 +103,12 @@ ${colorizedDiff}
|
|
|
106
103
|
const unifiedDiff = (0, diffGenerator_1.generateUnifiedDiff)(file.path, destinationContent, sourceContent);
|
|
107
104
|
const colorizedDiff = colorizeUnifiedDiff(unifiedDiff);
|
|
108
105
|
output += `\n${colorizedDiff}\n`;
|
|
109
|
-
|
|
110
|
-
const hasArrayChanges = arrayPaths.some((path) => {
|
|
111
|
-
const sourceArray = (0, jsonPath_1.getValueAtPath)(file.rawParsedSource, path);
|
|
112
|
-
const destinationArray = (0, jsonPath_1.getValueAtPath)(file.rawParsedDest, path);
|
|
113
|
-
if (!Array.isArray(sourceArray) || !Array.isArray(destinationArray))
|
|
114
|
-
return false;
|
|
115
|
-
return !(0, deepEqual_1.deepEqual)((0, serialization_1.normalizeForComparison)(sourceArray), (0, serialization_1.normalizeForComparison)(destinationArray));
|
|
116
|
-
});
|
|
117
|
-
if (hasArrayChanges) {
|
|
106
|
+
if (arrayInfo.hasChanges) {
|
|
118
107
|
output += chalk_1.default.cyan.bold('\nArray-specific details:\n');
|
|
119
|
-
for (const
|
|
120
|
-
const pathString = path.join('.');
|
|
121
|
-
const sourceArray = (0, jsonPath_1.getValueAtPath)(file.rawParsedSource, path);
|
|
122
|
-
const destinationArray = (0, jsonPath_1.getValueAtPath)(file.rawParsedDest, path);
|
|
123
|
-
if (!Array.isArray(sourceArray))
|
|
124
|
-
continue;
|
|
125
|
-
if (!Array.isArray(destinationArray))
|
|
126
|
-
continue;
|
|
127
|
-
const normalizedSource = (0, serialization_1.normalizeForComparison)(sourceArray);
|
|
128
|
-
const normalizedDestination = (0, serialization_1.normalizeForComparison)(destinationArray);
|
|
129
|
-
if ((0, deepEqual_1.deepEqual)(normalizedSource, normalizedDestination))
|
|
130
|
-
continue;
|
|
108
|
+
for (const change of arrayInfo.changes) {
|
|
109
|
+
const pathString = change.path.join('.');
|
|
131
110
|
output += chalk_1.default.cyan(`\n ${pathString}:\n`);
|
|
132
|
-
output += formatArrayDiff(
|
|
111
|
+
output += formatArrayDiff(change);
|
|
133
112
|
}
|
|
134
113
|
}
|
|
135
114
|
return output;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SEMVER_STRICT_PATTERN = exports.MAX_CONFIG_EXTENDS_DEPTH = exports.YAML_DEFAULT_INDENT = exports.YAML_LINE_WIDTH_UNLIMITED = exports.SYNC_CONFIRMATION_DELAY_MS = void 0;
|
|
4
|
+
exports.SYNC_CONFIRMATION_DELAY_MS = 2000;
|
|
5
|
+
exports.YAML_LINE_WIDTH_UNLIMITED = 0;
|
|
6
|
+
exports.YAML_DEFAULT_INDENT = 2;
|
|
7
|
+
exports.MAX_CONFIG_EXTENDS_DEPTH = 5;
|
|
8
|
+
exports.SEMVER_STRICT_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
package/dist/fileDiff.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Config } from './configFile';
|
|
1
|
+
import { Config, TransformConfig } from './configFile';
|
|
2
2
|
import { FileMap } from './fileLoader';
|
|
3
3
|
export interface FileDiffResult {
|
|
4
4
|
addedFiles: string[];
|
|
@@ -8,6 +8,7 @@ export interface FileDiffResult {
|
|
|
8
8
|
}
|
|
9
9
|
export interface ChangedFile {
|
|
10
10
|
path: string;
|
|
11
|
+
originalPath?: string;
|
|
11
12
|
sourceContent: string;
|
|
12
13
|
destinationContent: string;
|
|
13
14
|
processedSourceContent: unknown;
|
|
@@ -20,12 +21,20 @@ export interface ChangedFile {
|
|
|
20
21
|
parsedSource?: unknown;
|
|
21
22
|
parsedDest?: unknown;
|
|
22
23
|
}
|
|
24
|
+
export interface ProcessYamlOptions {
|
|
25
|
+
filePath: string;
|
|
26
|
+
sourceContent: string;
|
|
27
|
+
destinationContent: string;
|
|
28
|
+
skipPath?: Record<string, string[]>;
|
|
29
|
+
transforms?: TransformConfig;
|
|
30
|
+
}
|
|
23
31
|
declare const FileDiffErrorClass: {
|
|
24
32
|
new (message: string, options?: import("./utils/errors").ErrorOptions): {
|
|
25
33
|
[key: string]: unknown;
|
|
26
34
|
readonly code?: string;
|
|
27
35
|
readonly path?: string;
|
|
28
36
|
readonly cause?: Error;
|
|
37
|
+
readonly hints?: string[];
|
|
29
38
|
name: string;
|
|
30
39
|
message: string;
|
|
31
40
|
stack?: string;
|
|
@@ -38,5 +47,5 @@ export declare class FileDiffError extends FileDiffErrorClass {
|
|
|
38
47
|
}
|
|
39
48
|
export declare const isFileDiffError: (error: unknown) => error is FileDiffError;
|
|
40
49
|
export declare const getSkipPathsForFile: (filePath: string, skipPath?: Record<string, string[]>) => string[];
|
|
41
|
-
export declare const computeFileDiff: (sourceFiles: FileMap, destinationFiles: FileMap, config: Config, logger?: import("./logger").Logger) => FileDiffResult;
|
|
50
|
+
export declare const computeFileDiff: (sourceFiles: FileMap, destinationFiles: FileMap, config: Config, logger?: import("./logger").Logger, originalPaths?: Map<string, string>) => FileDiffResult;
|
|
42
51
|
export {};
|
package/dist/fileDiff.js
CHANGED
|
@@ -41,6 +41,30 @@ const deleteJsonPathRecursive = (object, parts, index) => {
|
|
|
41
41
|
const currentPart = parts[index];
|
|
42
42
|
if (!currentPart)
|
|
43
43
|
return;
|
|
44
|
+
if ((0, jsonPath_1.isFilterSegment)(currentPart)) {
|
|
45
|
+
if (!Array.isArray(object))
|
|
46
|
+
return;
|
|
47
|
+
const filter = (0, jsonPath_1.parseFilterSegment)(currentPart);
|
|
48
|
+
if (!filter)
|
|
49
|
+
return;
|
|
50
|
+
if (index === parts.length - 1)
|
|
51
|
+
for (let index_ = object.length - 1; index_ >= 0; index_--) {
|
|
52
|
+
const item = object[index_];
|
|
53
|
+
if (item && typeof item === 'object') {
|
|
54
|
+
const itemValue = item[filter.property];
|
|
55
|
+
if (String(itemValue) === filter.value)
|
|
56
|
+
object.splice(index_, 1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else
|
|
60
|
+
for (const item of object)
|
|
61
|
+
if (item && typeof item === 'object') {
|
|
62
|
+
const itemValue = item[filter.property];
|
|
63
|
+
if (String(itemValue) === filter.value)
|
|
64
|
+
deleteJsonPathRecursive(item, parts, index + 1);
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
44
68
|
if (index === parts.length - 1) {
|
|
45
69
|
if (currentPart === '*' && Array.isArray(object))
|
|
46
70
|
object.length = 0;
|
|
@@ -80,7 +104,8 @@ const getSkipPathsForFile = (filePath, skipPath) => {
|
|
|
80
104
|
return pathsToSkip;
|
|
81
105
|
};
|
|
82
106
|
exports.getSkipPathsForFile = getSkipPathsForFile;
|
|
83
|
-
const processYamlFile = (
|
|
107
|
+
const processYamlFile = (options) => {
|
|
108
|
+
const { filePath, sourceContent, destinationContent, skipPath, transforms } = options;
|
|
84
109
|
let sourceParsed;
|
|
85
110
|
let destinationParsed;
|
|
86
111
|
try {
|
|
@@ -141,18 +166,28 @@ const processYamlFile = (filePath, sourceContent, destinationContent, skipPath,
|
|
|
141
166
|
parsedDest: destinationParsed
|
|
142
167
|
};
|
|
143
168
|
};
|
|
144
|
-
const processChangedFiles = (sourceFiles, destinationFiles, skipPath, transforms) => {
|
|
169
|
+
const processChangedFiles = (sourceFiles, destinationFiles, skipPath, transforms, originalPaths) => {
|
|
145
170
|
const changedFiles = [];
|
|
146
171
|
const unchangedFiles = [];
|
|
147
172
|
for (const [path, sourceContent] of sourceFiles.entries()) {
|
|
148
173
|
if (!destinationFiles.has(path))
|
|
149
174
|
continue;
|
|
150
175
|
const destinationContent = destinationFiles.get(path);
|
|
176
|
+
const originalPath = originalPaths?.get(path);
|
|
151
177
|
const isYaml = (0, fileType_1.isYamlFile)(path);
|
|
152
178
|
if (isYaml) {
|
|
153
|
-
const changed = processYamlFile(
|
|
154
|
-
|
|
179
|
+
const changed = processYamlFile({
|
|
180
|
+
filePath: path,
|
|
181
|
+
sourceContent,
|
|
182
|
+
destinationContent,
|
|
183
|
+
skipPath,
|
|
184
|
+
transforms
|
|
185
|
+
});
|
|
186
|
+
if (changed) {
|
|
187
|
+
if (originalPath)
|
|
188
|
+
changed.originalPath = originalPath;
|
|
155
189
|
changedFiles.push(changed);
|
|
190
|
+
}
|
|
156
191
|
else
|
|
157
192
|
unchangedFiles.push(path);
|
|
158
193
|
}
|
|
@@ -161,6 +196,7 @@ const processChangedFiles = (sourceFiles, destinationFiles, skipPath, transforms
|
|
|
161
196
|
else
|
|
162
197
|
changedFiles.push({
|
|
163
198
|
path,
|
|
199
|
+
originalPath,
|
|
164
200
|
sourceContent,
|
|
165
201
|
destinationContent: destinationContent,
|
|
166
202
|
processedSourceContent: sourceContent,
|
|
@@ -172,7 +208,7 @@ const processChangedFiles = (sourceFiles, destinationFiles, skipPath, transforms
|
|
|
172
208
|
}
|
|
173
209
|
return { changedFiles, unchangedFiles };
|
|
174
210
|
};
|
|
175
|
-
const computeFileDiff = (sourceFiles, destinationFiles, config, logger) => {
|
|
211
|
+
const computeFileDiff = (sourceFiles, destinationFiles, config, logger, originalPaths) => {
|
|
176
212
|
if (logger?.shouldShow('debug')) {
|
|
177
213
|
logger.debug('Computing file differences:');
|
|
178
214
|
logger.debug(` Source files: ${sourceFiles.size}`);
|
|
@@ -186,7 +222,7 @@ const computeFileDiff = (sourceFiles, destinationFiles, config, logger) => {
|
|
|
186
222
|
}
|
|
187
223
|
const addedFiles = detectAddedFiles(sourceFiles, destinationFiles);
|
|
188
224
|
const deletedFiles = config.prune ? detectDeletedFiles(sourceFiles, destinationFiles) : [];
|
|
189
|
-
const { changedFiles, unchangedFiles } = processChangedFiles(sourceFiles, destinationFiles, config.skipPath, config.transforms);
|
|
225
|
+
const { changedFiles, unchangedFiles } = processChangedFiles(sourceFiles, destinationFiles, config.skipPath, config.transforms, originalPaths);
|
|
190
226
|
return { addedFiles, deletedFiles, changedFiles, unchangedFiles };
|
|
191
227
|
};
|
|
192
228
|
exports.computeFileDiff = computeFileDiff;
|
package/dist/fileLoader.d.ts
CHANGED
|
@@ -6,12 +6,17 @@ export interface FileLoaderOptions {
|
|
|
6
6
|
transforms?: TransformConfig;
|
|
7
7
|
}
|
|
8
8
|
export type FileMap = Map<string, string>;
|
|
9
|
+
export interface FileLoaderResult {
|
|
10
|
+
fileMap: FileMap;
|
|
11
|
+
originalPaths: Map<string, string>;
|
|
12
|
+
}
|
|
9
13
|
declare const FileLoaderErrorClass: {
|
|
10
14
|
new (message: string, options?: import("./utils/errors").ErrorOptions): {
|
|
11
15
|
[key: string]: unknown;
|
|
12
16
|
readonly code?: string;
|
|
13
17
|
readonly path?: string;
|
|
14
18
|
readonly cause?: Error;
|
|
19
|
+
readonly hints?: string[];
|
|
15
20
|
name: string;
|
|
16
21
|
message: string;
|
|
17
22
|
stack?: string;
|
|
@@ -23,5 +28,5 @@ declare const FileLoaderErrorClass: {
|
|
|
23
28
|
export declare class FileLoaderError extends FileLoaderErrorClass {
|
|
24
29
|
}
|
|
25
30
|
export declare const isFileLoaderError: (error: unknown) => error is FileLoaderError;
|
|
26
|
-
export declare const loadFiles: (options: FileLoaderOptions, logger?: import("./logger").Logger) => Promise<
|
|
31
|
+
export declare const loadFiles: (options: FileLoaderOptions, logger?: import("./logger").Logger) => Promise<FileLoaderResult>;
|
|
27
32
|
export {};
|
package/dist/fileLoader.js
CHANGED
|
@@ -162,20 +162,20 @@ const loadFiles = async (options, logger) => {
|
|
|
162
162
|
logger.debug(` Matched: ${files.length} file(s)`);
|
|
163
163
|
}
|
|
164
164
|
const fileMap = await readFilesIntoMap(absoluteBaseDirectory, files);
|
|
165
|
-
const
|
|
165
|
+
const transformResult = (0, filenameTransformer_1.transformFilenameMap)(fileMap, options.transforms);
|
|
166
166
|
if (options.transforms && logger?.shouldShow('debug')) {
|
|
167
|
-
logger.debug(`Filename transforms applied: ${fileMap.size} → ${
|
|
167
|
+
logger.debug(`Filename transforms applied: ${fileMap.size} → ${transformResult.fileMap.size} files`);
|
|
168
168
|
let exampleCount = 0;
|
|
169
|
-
for (const [
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (exampleCount >= 3)
|
|
175
|
-
break;
|
|
176
|
-
}
|
|
169
|
+
for (const [transformedPath, originalPath] of transformResult.originalPaths.entries()) {
|
|
170
|
+
logger.debug(` ${originalPath} → ${transformedPath}`);
|
|
171
|
+
exampleCount++;
|
|
172
|
+
if (exampleCount >= 3)
|
|
173
|
+
break;
|
|
177
174
|
}
|
|
178
175
|
}
|
|
179
|
-
return
|
|
176
|
+
return {
|
|
177
|
+
fileMap: sortMapByKeys(transformResult.fileMap),
|
|
178
|
+
originalPaths: transformResult.originalPaths
|
|
179
|
+
};
|
|
180
180
|
};
|
|
181
181
|
exports.loadFiles = loadFiles;
|
package/dist/fileUpdater.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Config } from './configFile';
|
|
2
|
-
import { FileDiffResult } from './fileDiff';
|
|
2
|
+
import { ChangedFile, FileDiffResult } from './fileDiff';
|
|
3
3
|
import { FileMap } from './fileLoader';
|
|
4
4
|
import { Logger } from './logger';
|
|
5
5
|
export interface FileUpdateError {
|
|
@@ -7,12 +7,30 @@ export interface FileUpdateError {
|
|
|
7
7
|
path: string;
|
|
8
8
|
error: Error;
|
|
9
9
|
}
|
|
10
|
+
export interface FileOperationOptions {
|
|
11
|
+
relativePath: string;
|
|
12
|
+
content: string;
|
|
13
|
+
absoluteDestinationDirectory: string;
|
|
14
|
+
config: Config;
|
|
15
|
+
dryRun: boolean;
|
|
16
|
+
skipFormat: boolean;
|
|
17
|
+
logger: Logger;
|
|
18
|
+
}
|
|
19
|
+
export interface UpdateFileOptions {
|
|
20
|
+
changedFile: ChangedFile;
|
|
21
|
+
absoluteDestinationDirectory: string;
|
|
22
|
+
config: Config;
|
|
23
|
+
dryRun: boolean;
|
|
24
|
+
skipFormat: boolean;
|
|
25
|
+
logger: Logger;
|
|
26
|
+
}
|
|
10
27
|
declare const FileUpdaterErrorClass: {
|
|
11
28
|
new (message: string, options?: import("./utils/errors").ErrorOptions): {
|
|
12
29
|
[key: string]: unknown;
|
|
13
30
|
readonly code?: string;
|
|
14
31
|
readonly path?: string;
|
|
15
32
|
readonly cause?: Error;
|
|
33
|
+
readonly hints?: string[];
|
|
16
34
|
name: string;
|
|
17
35
|
message: string;
|
|
18
36
|
stack?: string;
|