sf-decomposer 6.10.0 → 6.12.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/CHANGELOG.md +14 -0
- package/README.md +145 -18
- package/lib/commands/decomposer/decompose.d.ts +3 -1
- package/lib/commands/decomposer/decompose.js +20 -1
- package/lib/commands/decomposer/decompose.js.map +1 -1
- package/lib/commands/decomposer/recompose.d.ts +2 -1
- package/lib/commands/decomposer/recompose.js +11 -1
- package/lib/commands/decomposer/recompose.js.map +1 -1
- package/lib/core/decomposeMetadataTypes.js +49 -8
- package/lib/core/decomposeMetadataTypes.js.map +1 -1
- package/lib/core/recomposeMetadataTypes.js +43 -5
- package/lib/core/recomposeMetadataTypes.js.map +1 -1
- package/lib/helpers/configOverrides.d.ts +33 -0
- package/lib/helpers/configOverrides.js +132 -0
- package/lib/helpers/configOverrides.js.map +1 -0
- package/lib/helpers/types.d.ts +15 -2
- package/lib/hooks/prerun.js +34 -26
- package/lib/hooks/prerun.js.map +1 -1
- package/lib/hooks/scopedPostRetrieve.js +39 -40
- package/lib/hooks/scopedPostRetrieve.js.map +1 -1
- package/lib/metadata/parseManifest.d.ts +5 -0
- package/lib/metadata/parseManifest.js +164 -0
- package/lib/metadata/parseManifest.js.map +1 -0
- package/lib/service/core/moveFiles.js +20 -8
- package/lib/service/core/moveFiles.js.map +1 -1
- package/lib/service/decompose/decomposeFileHandler.d.ts +1 -1
- package/lib/service/decompose/decomposeFileHandler.js +41 -2
- package/lib/service/decompose/decomposeFileHandler.js.map +1 -1
- package/lib/service/recompose/recomposeFileHandler.d.ts +1 -1
- package/lib/service/recompose/recomposeFileHandler.js +57 -2
- package/lib/service/recompose/recomposeFileHandler.js.map +1 -1
- package/messages/decomposer.decompose.md +15 -1
- package/messages/decomposer.recompose.md +11 -1
- package/oclif.manifest.json +37 -7
- package/package.json +1 -4
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@
|
|
|
5
5
|
|
|
6
6
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
7
7
|
|
|
8
|
+
## [6.12.0](https://github.com/mcarvin8/sf-decomposer/compare/v6.11.0...v6.12.0) (2026-04-27)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add per-metadata-type overrides for decompose ([#407](https://github.com/mcarvin8/sf-decomposer/issues/407)) ([43be31d](https://github.com/mcarvin8/sf-decomposer/commit/43be31d6190e095a0dbc5b763e2439582c5b4214))
|
|
14
|
+
|
|
15
|
+
## [6.11.0](https://github.com/mcarvin8/sf-decomposer/compare/v6.10.0...v6.11.0) (2026-04-24)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* add --manifest/-x flag for scoped decompose and recompose ([#405](https://github.com/mcarvin8/sf-decomposer/issues/405)) ([2a1f39f](https://github.com/mcarvin8/sf-decomposer/commit/2a1f39f572362899265c6d6092adb8284b9c4427))
|
|
21
|
+
|
|
8
22
|
## [6.10.0](https://github.com/mcarvin8/sf-decomposer/compare/v6.9.0...v6.10.0) (2026-04-23)
|
|
9
23
|
|
|
10
24
|
|
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ A Salesforce CLI plugin that **decomposes** large metadata XML files into smalle
|
|
|
17
17
|
- [Commands](#commands)
|
|
18
18
|
- [sf decomposer decompose](#sf-decomposer-decompose)
|
|
19
19
|
- [sf decomposer recompose](#sf-decomposer-recompose)
|
|
20
|
+
- [Manifest-scoped runs](#manifest-scoped-runs)
|
|
20
21
|
- [Decompose Strategies](#decompose-strategies)
|
|
21
22
|
- [Custom Labels](#custom-labels-decomposition)
|
|
22
23
|
- [Permission Sets (grouped-by-tag)](#additional-permission-set-decomposition)
|
|
@@ -25,6 +26,7 @@ A Salesforce CLI plugin that **decomposes** large metadata XML files into smalle
|
|
|
25
26
|
- [Exceptions](#exceptions)
|
|
26
27
|
- [Troubleshooting](#troubleshooting)
|
|
27
28
|
- [Hooks](#hooks)
|
|
29
|
+
- [Per-Type Overrides](#per-type-overrides)
|
|
28
30
|
- [Ignore Files](#ignore-files)
|
|
29
31
|
- [.forceignore](#forceignore)
|
|
30
32
|
- [.sfdecomposerignore](#sfdecomposerignore)
|
|
@@ -66,11 +68,18 @@ A Salesforce CLI plugin that **decomposes** large metadata XML files into smalle
|
|
|
66
68
|
sf project deploy start
|
|
67
69
|
```
|
|
68
70
|
|
|
71
|
+
Or scope the recompose to just the components in your deploy manifest:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
sf decomposer recompose -x "manifest/package.xml"
|
|
75
|
+
sf project deploy start -x "manifest/package.xml"
|
|
76
|
+
```
|
|
77
|
+
|
|
69
78
|
---
|
|
70
79
|
|
|
71
80
|
## Requirements
|
|
72
81
|
|
|
73
|
-
The [xml-disassembler](https://github.com/mcarvin8/xml-disassembler)
|
|
82
|
+
The [xml-disassembler-node](https://github.com/mcarvin8/xml-disassembler-node) package, which depends on a Rust crate, ships with native binaries for these platforms:
|
|
74
83
|
|
|
75
84
|
| Platform | Architectures |
|
|
76
85
|
| ----------- | ---------------------------------- |
|
|
@@ -78,7 +87,7 @@ The [xml-disassembler](https://github.com/mcarvin8/xml-disassembler) NodeJS depe
|
|
|
78
87
|
| **Linux** | x64, arm64, ia32 |
|
|
79
88
|
| **Windows** | x64 |
|
|
80
89
|
|
|
81
|
-
If other platforms or architectures require support, please open an issue in
|
|
90
|
+
If other platforms or architectures require support, please open an issue in [xml-disassembler-node](https://github.com/mcarvin8/xml-disassembler-node/issues).
|
|
82
91
|
|
|
83
92
|
---
|
|
84
93
|
|
|
@@ -90,6 +99,7 @@ Salesforce’s built-in decomposition is limited. sf-decomposer gives admins and
|
|
|
90
99
|
|
|
91
100
|
- **Broader metadata support** – Works with most Metadata API types, not just the subset Salesforce decomposes.
|
|
92
101
|
- **Selective decomposition** – Decompose only what you need; use [.sfdecomposerignore](#sfdecomposerignore) to skip specific files.
|
|
102
|
+
- **Manifest-scoped runs** – Pass `-x package.xml` to decompose or recompose only the components listed in a Salesforce manifest, mirroring `sf project deploy start -x`. Ideal for CI/CD pipelines that only ship a subset of metadata per deployment.
|
|
93
103
|
- **Two [strategies](#decompose-strategies)**:
|
|
94
104
|
- **unique-id** (default): one file per nested element, named by content or hash.
|
|
95
105
|
- **grouped-by-tag**: one file per tag (e.g. all `fieldPermissions` in a permission set in `fieldPermissions.xml`). Use `--decompose-nested-permissions` for deeper permission set and muting permission set decomposition.
|
|
@@ -114,21 +124,25 @@ Decomposes metadata in all local package directories (from `sfdx-project.json`)
|
|
|
114
124
|
|
|
115
125
|
```
|
|
116
126
|
USAGE
|
|
117
|
-
$ sf decomposer decompose -m <value> -f <value> -i <value> -s <value> [--prepurge --postpurge -p --json]
|
|
127
|
+
$ sf decomposer decompose [-m <value>] [-x <value>] [-f <value>] [-i <value>] [-s <value>] [--prepurge --postpurge -p -c --json]
|
|
118
128
|
|
|
119
129
|
FLAGS
|
|
120
|
-
-m, --metadata-type=<value> Metadata suffix to process (e.g. flow, labels). Repeatable.
|
|
130
|
+
-m, --metadata-type=<value> Metadata suffix to process (e.g. flow, labels). Repeatable. Optional when --manifest is provided.
|
|
131
|
+
-x, --manifest=<value> Path to a package.xml manifest. When provided, only the components listed in the manifest are decomposed.
|
|
121
132
|
-f, --format=<value> Output format: xml | yaml | json | json5 [default: xml]
|
|
122
133
|
-i, --ignore-package-directory=<value> Package directory to skip (as in sfdx-project.json). Repeatable.
|
|
123
134
|
-s, --strategy=<value> unique-id | grouped-by-tag [default: unique-id]
|
|
124
135
|
--prepurge Remove existing decomposed files before decomposing [default: false]
|
|
125
136
|
--postpurge Remove original metadata files after decomposing [default: false]
|
|
126
137
|
-p, --decompose-nested-permissions With grouped-by-tag, further decompose permission set and muting permission set object/field permissions
|
|
138
|
+
-c, --config Load per-metadata-type overrides from .sfdecomposer.config.json in the repo root. Only the "overrides" array is consumed. See Per-Type Overrides. [default: false]
|
|
127
139
|
|
|
128
140
|
GLOBAL FLAGS
|
|
129
141
|
--json Output as JSON.
|
|
130
142
|
```
|
|
131
143
|
|
|
144
|
+
> At least one of `--metadata-type` or `--manifest` is required. When both are supplied, the run is scoped to the intersection of the two.
|
|
145
|
+
|
|
132
146
|
**Examples**
|
|
133
147
|
|
|
134
148
|
```bash
|
|
@@ -140,6 +154,12 @@ sf decomposer decompose -m "flow" -m "labels" -f "yaml" --prepurge --postpurge
|
|
|
140
154
|
|
|
141
155
|
# Decompose flows, excluding the force-app package
|
|
142
156
|
sf decomposer decompose -m "flow" -i "force-app"
|
|
157
|
+
|
|
158
|
+
# Decompose only the components listed in a manifest
|
|
159
|
+
sf decomposer decompose -x "manifest/package.xml" --prepurge
|
|
160
|
+
|
|
161
|
+
# Restrict a manifest run to a single metadata type
|
|
162
|
+
sf decomposer decompose -x "manifest/package.xml" -m "permissionset"
|
|
143
163
|
```
|
|
144
164
|
|
|
145
165
|
### sf decomposer recompose
|
|
@@ -148,10 +168,11 @@ Recomposes decomposed files into deployment-compatible metadata.
|
|
|
148
168
|
|
|
149
169
|
```
|
|
150
170
|
USAGE
|
|
151
|
-
$ sf decomposer recompose -m <value> -i <value> [--postpurge --json]
|
|
171
|
+
$ sf decomposer recompose [-m <value>] [-x <value>] [-i <value>] [--postpurge --json]
|
|
152
172
|
|
|
153
173
|
FLAGS
|
|
154
|
-
-m, --metadata-type=<value> Metadata suffix to process (e.g. flow, labels). Repeatable.
|
|
174
|
+
-m, --metadata-type=<value> Metadata suffix to process (e.g. flow, labels). Repeatable. Optional when --manifest is provided.
|
|
175
|
+
-x, --manifest=<value> Path to a package.xml manifest. When provided, only the components listed in the manifest are recomposed.
|
|
155
176
|
-i, --ignore-package-directory=<value> Package directory to skip. Repeatable.
|
|
156
177
|
--postpurge Remove decomposed files after recomposing [default: false]
|
|
157
178
|
|
|
@@ -159,11 +180,52 @@ GLOBAL FLAGS
|
|
|
159
180
|
--json Output as JSON.
|
|
160
181
|
```
|
|
161
182
|
|
|
183
|
+
> At least one of `--metadata-type` or `--manifest` is required. When both are supplied, the run is scoped to the intersection of the two.
|
|
184
|
+
|
|
162
185
|
**Examples**
|
|
163
186
|
|
|
164
187
|
```bash
|
|
165
188
|
sf decomposer recompose -m "flow" --postpurge
|
|
166
189
|
sf decomposer recompose -m "flow" -i "force-app"
|
|
190
|
+
|
|
191
|
+
# Recompose only the components listed in a deploy manifest before deploying
|
|
192
|
+
sf decomposer recompose -x "manifest/package.xml"
|
|
193
|
+
sf project deploy start -x "manifest/package.xml"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Manifest-scoped runs
|
|
197
|
+
|
|
198
|
+
The `-x` / `--manifest` flag accepts any standard Salesforce `package.xml` and limits the work to just the components it lists. This is especially useful for CI/CD pipelines that deploy a subset of metadata per change.
|
|
199
|
+
|
|
200
|
+
How it works:
|
|
201
|
+
|
|
202
|
+
- The manifest is parsed with `@salesforce/source-deploy-retrieve`'s `ManifestResolver`, so the same XML you pass to `sf project deploy start -x` is honored here.
|
|
203
|
+
- For each entry, the plugin resolves the matching parent metadata files in your local package directories (using each metadata type's `directoryName`, `suffix`, `strictDirectoryName`, and `folderType` from the SDR registry).
|
|
204
|
+
- Only those files are decomposed/recomposed; everything else on disk is left untouched.
|
|
205
|
+
- Wildcards (`<members>*</members>`) expand against your local source. Folder-typed members (e.g. `MyFolder/MyReport`) are resolved by walking the folder.
|
|
206
|
+
- Types in the manifest that the plugin does not support (e.g. `CustomObject`, `ApexClass`) are skipped with a warning instead of failing the run, so a single manifest can drive both deploys and decomposer runs.
|
|
207
|
+
- If both `--metadata-type` and `--manifest` are provided, the run is scoped to the intersection (only types present in both).
|
|
208
|
+
|
|
209
|
+
Example manifest:
|
|
210
|
+
|
|
211
|
+
```xml
|
|
212
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
213
|
+
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
214
|
+
<types>
|
|
215
|
+
<members>HR_Admin</members>
|
|
216
|
+
<name>PermissionSet</name>
|
|
217
|
+
</types>
|
|
218
|
+
<types>
|
|
219
|
+
<members>Case</members>
|
|
220
|
+
<name>Workflow</name>
|
|
221
|
+
</types>
|
|
222
|
+
<version>58.0</version>
|
|
223
|
+
</Package>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
sf decomposer recompose -x "manifest/package.xml"
|
|
228
|
+
sf project deploy start -x "manifest/package.xml"
|
|
167
229
|
```
|
|
168
230
|
|
|
169
231
|
---
|
|
@@ -297,17 +359,83 @@ Put **.sfdecomposer.config.json** in the project root to run:
|
|
|
297
359
|
- **After** `sf project retrieve start`: decompose.
|
|
298
360
|
- **Before** `sf project deploy start` / `sf project deploy validate`: recompose.
|
|
299
361
|
|
|
300
|
-
Copy and customize the [sample config](https://raw.githubusercontent.com/mcarvin8/sf-decomposer/main/examples/.sfdecomposer.config.json).
|
|
362
|
+
Copy and customize the [sample config](https://raw.githubusercontent.com/mcarvin8/sf-decomposer/main/examples/.sfdecomposer.config.json), or the [sample config with per-type overrides](https://raw.githubusercontent.com/mcarvin8/sf-decomposer/main/examples/.sfdecomposer.config.overrides.json) to vary format/strategy/etc. by metadata type.
|
|
363
|
+
|
|
364
|
+
| Option | Required | Description |
|
|
365
|
+
| ---------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
366
|
+
| `metadataSuffixes` | Conditional | Comma-separated metadata suffixes to decompose/recompose. Required unless `manifest` is set; when both are set, the run is scoped to the intersection. |
|
|
367
|
+
| `manifest` | Conditional | Path (relative to the project root) to a `package.xml` manifest. When set, only the components listed in the manifest are decomposed/recomposed. See `-x` above. |
|
|
368
|
+
| `ignorePackageDirectories` | No | Comma-separated package directories to skip. |
|
|
369
|
+
| `prePurge` | No | Remove existing decomposed files before decomposing (default: false). |
|
|
370
|
+
| `postPurge` | No | After decompose: remove originals; after recompose: remove decomposed files (default: false). |
|
|
371
|
+
| `decomposedFormat` | No | xml, json, json5, or yaml (default: xml). |
|
|
372
|
+
| `strategy` | No | `unique-id` \| `grouped-by-tag` (default: unique-id). |
|
|
373
|
+
| `decomposeNestedPermissions` | No | With grouped-by-tag, set true to further decompose permission set and muting permission set object/field permissions. |
|
|
374
|
+
| `overrides` | No | Array of per-metadata-type overrides for `decomposedFormat`, `strategy`, `decomposeNestedPermissions`, `prePurge`, and `postPurge`. See [Per-Type Overrides](#per-type-overrides). |
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Per-Type Overrides
|
|
379
|
+
|
|
380
|
+
Per-type overrides apply to **decompose only**. Recompose is a deterministic round-trip — it auto-detects format from the on-disk files and does not depend on strategy — so it ignores the `overrides` array.
|
|
381
|
+
|
|
382
|
+
By default, a single decompose run uses one format and one strategy across every metadata type. The optional `overrides` array in `.sfdecomposer.config.json` lets you vary a small set of options per metadata suffix without splitting the run into multiple invocations.
|
|
383
|
+
|
|
384
|
+
```json
|
|
385
|
+
{
|
|
386
|
+
"metadataSuffixes": "labels,workflow,profile,flow,permissionset",
|
|
387
|
+
"ignorePackageDirectories": "force-app,examples",
|
|
388
|
+
"prePurge": true,
|
|
389
|
+
"postPurge": true,
|
|
390
|
+
"decomposedFormat": "xml",
|
|
391
|
+
"strategy": "unique-id",
|
|
392
|
+
"overrides": [
|
|
393
|
+
{ "metadataTypes": ["flow"], "decomposedFormat": "yaml" },
|
|
394
|
+
{
|
|
395
|
+
"metadataTypes": ["permissionset", "mutingpermissionset"],
|
|
396
|
+
"strategy": "grouped-by-tag",
|
|
397
|
+
"decomposeNestedPermissions": true
|
|
398
|
+
}
|
|
399
|
+
]
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### What can be overridden
|
|
404
|
+
|
|
405
|
+
| Field | Notes |
|
|
406
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
407
|
+
| `metadataTypes` | Required. Array of metadata suffixes (same vocabulary as `--metadata-type` / `metadataSuffixes`). Each suffix may appear in at most one override. |
|
|
408
|
+
| `decomposedFormat` | `xml` \| `json` \| `json5` \| `yaml`. |
|
|
409
|
+
| `strategy` | `unique-id` \| `grouped-by-tag`. Hard rules still win — `labels` and `loyaltyProgramSetup` are always treated as `unique-id`. |
|
|
410
|
+
| `decomposeNestedPermissions` | Only applies to `permissionset` / `mutingpermissionset` with `grouped-by-tag`. |
|
|
411
|
+
| `prePurge` | Per-type prePurge (decompose). |
|
|
412
|
+
| `postPurge` | Per-type postPurge (decompose: remove originals after decomposing). |
|
|
413
|
+
|
|
414
|
+
Run-scope options (`metadataSuffixes`, `manifest`, `ignorePackageDirectories`) are **not** valid inside an override; the plugin will throw if they are present.
|
|
415
|
+
|
|
416
|
+
### Precedence
|
|
417
|
+
|
|
418
|
+
For each metadata type, the effective value is resolved as:
|
|
419
|
+
|
|
420
|
+
1. The per-type override value, if set.
|
|
421
|
+
2. Otherwise, the run-wide value (CLI flag, hook config top-level field, or built-in default).
|
|
422
|
+
3. Hard plugin rules (e.g. labels → `unique-id`) still override both.
|
|
423
|
+
|
|
424
|
+
### Opting in from the CLI
|
|
425
|
+
|
|
426
|
+
CLI users can opt into overrides on `decompose` with the boolean `--config` (`-c`) flag. When set, the plugin reads `.sfdecomposer.config.json` from the repo root (the nearest ancestor directory that contains `sfdx-project.json`):
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
sf decomposer decompose -m "flow" -m "permissionset" -c
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
When `--config` is set, **only** the `overrides` array is consumed from the file. Top-level fields like `decomposedFormat`, `strategy`, `metadataSuffixes`, etc. are ignored — the CLI flags remain the source of truth for run-wide values. This keeps direct CLI behavior predictable and lets you reuse the same config file as the post-retrieve hook without any surprises.
|
|
433
|
+
|
|
434
|
+
If `--config` is set but `.sfdecomposer.config.json` is missing from the repo root, the command fails with a clear error.
|
|
435
|
+
|
|
436
|
+
`recompose` does not accept `--config` because it does not need the override information — format is auto-detected from the decomposed files on disk and recompose does not depend on strategy.
|
|
301
437
|
|
|
302
|
-
|
|
303
|
-
| ---------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------- |
|
|
304
|
-
| `metadataSuffixes` | Yes | Comma-separated metadata suffixes to decompose/recompose. |
|
|
305
|
-
| `ignorePackageDirectories` | No | Comma-separated package directories to skip. |
|
|
306
|
-
| `prePurge` | No | Remove existing decomposed files before decomposing (default: false). |
|
|
307
|
-
| `postPurge` | No | After decompose: remove originals; after recompose: remove decomposed files (default: false). |
|
|
308
|
-
| `decomposedFormat` | No | xml, json, json5, or yaml (default: xml). |
|
|
309
|
-
| `strategy` | No | `unique-id` \| `grouped-by-tag` (default: unique-id). |
|
|
310
|
-
| `decomposeNestedPermissions` | No | With grouped-by-tag, set true to further decompose permission set and muting permission set object/field permissions. |
|
|
438
|
+
The post-retrieve hook automatically picks up `overrides` from `.sfdecomposer.config.json` — no extra setup required. Existing config files without an `overrides` field continue to behave exactly as before.
|
|
311
439
|
|
|
312
440
|
---
|
|
313
441
|
|
|
@@ -336,7 +464,6 @@ Bugs and feature requests: [open an issue](https://github.com/mcarvin8/sf-decomp
|
|
|
336
464
|
## Built With
|
|
337
465
|
|
|
338
466
|
- [xml-disassembler](https://github.com/mcarvin8/xml-disassembler) – Disassemble XML into smaller, manageable files and reassemble when needed. Node.js + Rust (Neon). Includes prebuilt binaries for macOS (x64, arm64), Linux (x64, arm64, ia32), and Windows (x64).
|
|
339
|
-
- [fs-extra](https://github.com/jprichardson/node-fs-extra) – Node.js: extra methods for the `fs` object like copy(), remove(), mkdirs().
|
|
340
467
|
- [@salesforce/source-deploy-retrieve](https://github.com/forcedotcom/source-deploy-retrieve) – JavaScript toolkit for working with Salesforce metadata.
|
|
341
468
|
|
|
342
469
|
---
|
|
@@ -347,4 +474,4 @@ Contributions are welcome. See [CONTRIBUTING.md](https://github.com/mcarvin8/sf-
|
|
|
347
474
|
|
|
348
475
|
## License
|
|
349
476
|
|
|
350
|
-
MIT
|
|
477
|
+
[MIT](https://raw.githubusercontent.com/mcarvin8/sf-decomposer/main/LICENSE.md)
|
|
@@ -5,13 +5,15 @@ export default class DecomposerDecompose extends SfCommand<DecomposerResult> {
|
|
|
5
5
|
static readonly description: string;
|
|
6
6
|
static readonly examples: string[];
|
|
7
7
|
static readonly flags: {
|
|
8
|
-
'metadata-type': import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
'metadata-type': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
manifest: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
prepurge: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
postpurge: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
12
|
format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
'ignore-package-directory': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
14
|
strategy: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
15
|
'decompose-nested-permissions': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
config: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
17
|
};
|
|
16
18
|
run(): Promise<DecomposerResult>;
|
|
17
19
|
}
|
|
@@ -3,6 +3,7 @@ import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
|
|
|
3
3
|
import { Messages } from '@salesforce/core';
|
|
4
4
|
import { DECOMPOSED_FILE_TYPES, DECOMPOSED_STRATEGIES } from '../../helpers/constants.js';
|
|
5
5
|
import { decomposeMetadataTypes } from '../../core/decomposeMetadataTypes.js';
|
|
6
|
+
import { loadOverridesFromConfig, resolveDefaultConfigPath } from '../../helpers/configOverrides.js';
|
|
6
7
|
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
7
8
|
const messages = Messages.loadMessages('sf-decomposer', 'decomposer.decompose');
|
|
8
9
|
export default class DecomposerDecompose extends SfCommand {
|
|
@@ -14,7 +15,13 @@ export default class DecomposerDecompose extends SfCommand {
|
|
|
14
15
|
summary: messages.getMessage('flags.metadata-type.summary'),
|
|
15
16
|
char: 'm',
|
|
16
17
|
multiple: true,
|
|
17
|
-
required:
|
|
18
|
+
required: false,
|
|
19
|
+
}),
|
|
20
|
+
manifest: Flags.file({
|
|
21
|
+
summary: messages.getMessage('flags.manifest.summary'),
|
|
22
|
+
char: 'x',
|
|
23
|
+
required: false,
|
|
24
|
+
exists: true,
|
|
18
25
|
}),
|
|
19
26
|
prepurge: Flags.boolean({
|
|
20
27
|
summary: messages.getMessage('flags.prepurge.summary'),
|
|
@@ -54,9 +61,19 @@ export default class DecomposerDecompose extends SfCommand {
|
|
|
54
61
|
required: false,
|
|
55
62
|
default: false,
|
|
56
63
|
}),
|
|
64
|
+
config: Flags.boolean({
|
|
65
|
+
summary: messages.getMessage('flags.config.summary'),
|
|
66
|
+
char: 'c',
|
|
67
|
+
required: false,
|
|
68
|
+
default: false,
|
|
69
|
+
}),
|
|
57
70
|
};
|
|
58
71
|
async run() {
|
|
59
72
|
const { flags } = await this.parse(DecomposerDecompose);
|
|
73
|
+
if (!flags['metadata-type'] && !flags['manifest']) {
|
|
74
|
+
throw messages.createError('error.missingMetadataOrManifest');
|
|
75
|
+
}
|
|
76
|
+
const overrides = flags['config'] ? await loadOverridesFromConfig(await resolveDefaultConfigPath()) : undefined;
|
|
60
77
|
return decomposeMetadataTypes({
|
|
61
78
|
metadataTypes: flags['metadata-type'],
|
|
62
79
|
prepurge: flags['prepurge'],
|
|
@@ -65,6 +82,8 @@ export default class DecomposerDecompose extends SfCommand {
|
|
|
65
82
|
ignoreDirs: flags['ignore-package-directory'],
|
|
66
83
|
strategy: flags['strategy'],
|
|
67
84
|
decomposeNestedPerms: flags['decompose-nested-permissions'],
|
|
85
|
+
manifest: flags['manifest'],
|
|
86
|
+
overrides,
|
|
68
87
|
log: this.log.bind(this),
|
|
69
88
|
});
|
|
70
89
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decompose.js","sourceRoot":"","sources":["../../../src/commands/decomposer/decompose.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAC1F,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"decompose.js","sourceRoot":"","sources":["../../../src/commands/decomposer/decompose.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAC1F,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAGrG,QAAQ,CAAC,kCAAkC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7D,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,CAAC,eAAe,EAAE,sBAAsB,CAAC,CAAC;AAEhF,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,SAA2B;IACnE,MAAM,CAAmB,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAClE,MAAM,CAAmB,WAAW,GAAG,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IAC1E,MAAM,CAAmB,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAErE,MAAM,CAAmB,KAAK,GAAG;QACtC,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC;YAC5B,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,6BAA6B,CAAC;YAC3D,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,KAAK;SAChB,CAAC;QACF,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC;YACnB,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,wBAAwB,CAAC;YACtD,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,IAAI;SACb,CAAC;QACF,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC;YACtB,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,wBAAwB,CAAC;YACtD,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,KAAK;SACf,CAAC;QACF,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC;YACvB,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,yBAAyB,CAAC;YACvD,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,KAAK;SACf,CAAC;QACF,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC;YACnB,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,sBAAsB,CAAC;YACpD,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,qBAAqB;SAC/B,CAAC;QACF,0BAA0B,EAAE,KAAK,CAAC,SAAS,CAAC;YAC1C,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,wCAAwC,CAAC;YACtE,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,IAAI;SACf,CAAC;QACF,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC;YACrB,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,wBAAwB,CAAC;YACtD,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,WAAW;YACpB,OAAO,EAAE,qBAAqB;SAC/B,CAAC;QACF,8BAA8B,EAAE,KAAK,CAAC,OAAO,CAAC;YAC5C,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,4CAA4C,CAAC;YAC1E,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,KAAK;SACf,CAAC;QACF,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC;YACpB,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,sBAAsB,CAAC;YACpD,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,KAAK;SACf,CAAC;KACH,CAAC;IAEK,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAExD,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;YAClD,MAAM,QAAQ,CAAC,WAAW,CAAC,iCAAiC,CAAC,CAAC;QAChE,CAAC;QAED,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,uBAAuB,CAAC,MAAM,wBAAwB,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAEhH,OAAO,sBAAsB,CAAC;YAC5B,aAAa,EAAE,KAAK,CAAC,eAAe,CAAC;YACrC,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC;YAC3B,SAAS,EAAE,KAAK,CAAC,WAAW,CAAC;YAC7B,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC;YACvB,UAAU,EAAE,KAAK,CAAC,0BAA0B,CAAC;YAC7C,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC;YAC3B,oBAAoB,EAAE,KAAK,CAAC,8BAA8B,CAAC;YAC3D,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC;YAC3B,SAAS;YACT,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;SACzB,CAAC,CAAC;IACL,CAAC"}
|
|
@@ -5,7 +5,8 @@ export default class DecomposerRecompose extends SfCommand<DecomposerResult> {
|
|
|
5
5
|
static readonly description: string;
|
|
6
6
|
static readonly examples: string[];
|
|
7
7
|
static readonly flags: {
|
|
8
|
-
'metadata-type': import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
'metadata-type': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
manifest: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
postpurge: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
'ignore-package-directory': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
};
|
|
@@ -13,7 +13,13 @@ export default class DecomposerRecompose extends SfCommand {
|
|
|
13
13
|
summary: messages.getMessage('flags.metadata-type.summary'),
|
|
14
14
|
char: 'm',
|
|
15
15
|
multiple: true,
|
|
16
|
-
required:
|
|
16
|
+
required: false,
|
|
17
|
+
}),
|
|
18
|
+
manifest: Flags.file({
|
|
19
|
+
summary: messages.getMessage('flags.manifest.summary'),
|
|
20
|
+
char: 'x',
|
|
21
|
+
required: false,
|
|
22
|
+
exists: true,
|
|
17
23
|
}),
|
|
18
24
|
postpurge: Flags.boolean({
|
|
19
25
|
summary: messages.getMessage('flags.postpurge.summary'),
|
|
@@ -29,10 +35,14 @@ export default class DecomposerRecompose extends SfCommand {
|
|
|
29
35
|
};
|
|
30
36
|
async run() {
|
|
31
37
|
const { flags } = await this.parse(DecomposerRecompose);
|
|
38
|
+
if (!flags['metadata-type'] && !flags['manifest']) {
|
|
39
|
+
throw messages.createError('error.missingMetadataOrManifest');
|
|
40
|
+
}
|
|
32
41
|
return recomposeMetadataTypes({
|
|
33
42
|
metadataTypes: flags['metadata-type'],
|
|
34
43
|
postpurge: flags['postpurge'],
|
|
35
44
|
ignoreDirs: flags['ignore-package-directory'],
|
|
45
|
+
manifest: flags['manifest'],
|
|
36
46
|
log: this.log.bind(this),
|
|
37
47
|
});
|
|
38
48
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recompose.js","sourceRoot":"","sources":["../../../src/commands/decomposer/recompose.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAG9E,QAAQ,CAAC,kCAAkC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7D,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,CAAC,eAAe,EAAE,sBAAsB,CAAC,CAAC;AAEhF,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,SAA2B;IACnE,MAAM,CAAmB,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAClE,MAAM,CAAmB,WAAW,GAAG,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IAC1E,MAAM,CAAmB,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAErE,MAAM,CAAmB,KAAK,GAAG;QACtC,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC;YAC5B,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,6BAA6B,CAAC;YAC3D,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,IAAI;
|
|
1
|
+
{"version":3,"file":"recompose.js","sourceRoot":"","sources":["../../../src/commands/decomposer/recompose.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAG9E,QAAQ,CAAC,kCAAkC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7D,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,CAAC,eAAe,EAAE,sBAAsB,CAAC,CAAC;AAEhF,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,SAA2B;IACnE,MAAM,CAAmB,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAClE,MAAM,CAAmB,WAAW,GAAG,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IAC1E,MAAM,CAAmB,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAErE,MAAM,CAAmB,KAAK,GAAG;QACtC,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC;YAC5B,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,6BAA6B,CAAC;YAC3D,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,KAAK;SAChB,CAAC;QACF,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC;YACnB,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,wBAAwB,CAAC;YACtD,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,IAAI;SACb,CAAC;QACF,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC;YACvB,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,yBAAyB,CAAC;YACvD,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,KAAK;SACf,CAAC;QACF,0BAA0B,EAAE,KAAK,CAAC,SAAS,CAAC;YAC1C,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,wCAAwC,CAAC;YACtE,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,IAAI;SACf,CAAC;KACH,CAAC;IAEK,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAExD,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;YAClD,MAAM,QAAQ,CAAC,WAAW,CAAC,iCAAiC,CAAC,CAAC;QAChE,CAAC;QAED,OAAO,sBAAsB,CAAC;YAC5B,aAAa,EAAE,KAAK,CAAC,eAAe,CAAC;YACrC,SAAS,EAAE,KAAK,CAAC,WAAW,CAAC;YAC7B,UAAU,EAAE,KAAK,CAAC,0BAA0B,CAAC;YAC7C,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC;YAC3B,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;SACzB,CAAC,CAAC;IACL,CAAC"}
|
|
@@ -1,25 +1,66 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
import pLimit from 'p-limit';
|
|
3
3
|
import { getRegistryValuesBySuffix } from '../metadata/getRegistryValuesBySuffix.js';
|
|
4
|
+
import { parseManifest } from '../metadata/parseManifest.js';
|
|
4
5
|
import { decomposeFileHandler } from '../service/decompose/decomposeFileHandler.js';
|
|
5
6
|
import { CONCURRENCY_LIMITS } from '../helpers/constants.js';
|
|
7
|
+
import { resolveDecomposeOptionsForType } from '../helpers/configOverrides.js';
|
|
6
8
|
export async function decomposeMetadataTypes(options) {
|
|
7
|
-
const { metadataTypes, prepurge, postpurge, format, ignoreDirs, strategy, decomposeNestedPerms, log } = options;
|
|
9
|
+
const { metadataTypes, prepurge, postpurge, format, ignoreDirs, strategy, decomposeNestedPerms, manifest, overrides, log, } = options;
|
|
10
|
+
let manifestFilter;
|
|
11
|
+
let effectiveTypes;
|
|
12
|
+
if (manifest) {
|
|
13
|
+
manifestFilter = await parseManifest(manifest, ignoreDirs);
|
|
14
|
+
if (metadataTypes && metadataTypes.length > 0) {
|
|
15
|
+
const manifestTypes = new Set(manifestFilter.suffixes);
|
|
16
|
+
effectiveTypes = metadataTypes.filter((type) => manifestTypes.has(type));
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
effectiveTypes = manifestFilter.suffixes;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
if (!metadataTypes || metadataTypes.length === 0) {
|
|
24
|
+
throw Error('Either --metadata-type or --manifest must be provided.');
|
|
25
|
+
}
|
|
26
|
+
effectiveTypes = metadataTypes;
|
|
27
|
+
}
|
|
28
|
+
if (effectiveTypes.length === 0) {
|
|
29
|
+
log('No metadata types to decompose after applying the manifest filter.');
|
|
30
|
+
return { metadata: [] };
|
|
31
|
+
}
|
|
8
32
|
// Limit concurrent metadata type processing to prevent file system overload
|
|
9
33
|
const limit = pLimit(CONCURRENCY_LIMITS.METADATA_TYPES);
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
34
|
+
const processed = [];
|
|
35
|
+
const tasks = effectiveTypes.map((metadataType) => limit(async () => {
|
|
36
|
+
const manifestXmlPaths = manifestFilter?.parentXmlsBySuffix.get(metadataType);
|
|
37
|
+
let metaAttributes;
|
|
38
|
+
let ignorePath;
|
|
39
|
+
try {
|
|
40
|
+
({ metaAttributes, ignorePath } = await getRegistryValuesBySuffix(metadataType, 'decompose', ignoreDirs));
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
/* istanbul ignore if -- @preserve: preserves non-manifest behavior; unreachable via known CLI types */
|
|
44
|
+
if (!manifestFilter)
|
|
45
|
+
throw err;
|
|
46
|
+
/* istanbul ignore next -- @preserve: getRegistryValuesBySuffix always throws Error instances */
|
|
47
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
48
|
+
log(`Skipping ${metadataType}: ${message}`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const resolved = resolveDecomposeOptionsForType(metadataType, { format, strategy, decomposeNestedPerms, prepurge, postpurge }, overrides);
|
|
52
|
+
let effectiveStrategy = resolved.strategy;
|
|
53
|
+
if (metadataType === 'labels' && effectiveStrategy === 'grouped-by-tag') {
|
|
14
54
|
effectiveStrategy = 'unique-id';
|
|
15
55
|
}
|
|
16
|
-
if (metadataType === 'loyaltyProgramSetup' &&
|
|
56
|
+
if (metadataType === 'loyaltyProgramSetup' && effectiveStrategy === 'grouped-by-tag') {
|
|
17
57
|
effectiveStrategy = 'unique-id';
|
|
18
58
|
}
|
|
19
|
-
await decomposeFileHandler(metaAttributes, prepurge, postpurge, format, ignorePath, effectiveStrategy, decomposeNestedPerms);
|
|
59
|
+
await decomposeFileHandler(metaAttributes, resolved.prepurge, resolved.postpurge, resolved.format, ignorePath, effectiveStrategy, resolved.decomposeNestedPerms, manifestXmlPaths);
|
|
60
|
+
processed.push(metadataType);
|
|
20
61
|
log(`All metadata files have been decomposed for the metadata type: ${metadataType}`);
|
|
21
62
|
}));
|
|
22
63
|
await Promise.all(tasks);
|
|
23
|
-
return { metadata:
|
|
64
|
+
return { metadata: processed };
|
|
24
65
|
}
|
|
25
66
|
//# sourceMappingURL=decomposeMetadataTypes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decomposeMetadataTypes.js","sourceRoot":"","sources":["../../src/core/decomposeMetadataTypes.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,MAAM,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,0CAA0C,CAAC;AACrF,OAAO,EAAE,oBAAoB,EAAE,MAAM,8CAA8C,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"decomposeMetadataTypes.js","sourceRoot":"","sources":["../../src/core/decomposeMetadataTypes.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,20 +1,58 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
import pLimit from 'p-limit';
|
|
3
3
|
import { getRegistryValuesBySuffix } from '../metadata/getRegistryValuesBySuffix.js';
|
|
4
|
+
import { parseManifest } from '../metadata/parseManifest.js';
|
|
4
5
|
import { recomposeFileHandler } from '../service/recompose/recomposeFileHandler.js';
|
|
5
6
|
import { CONCURRENCY_LIMITS } from '../helpers/constants.js';
|
|
6
7
|
export async function recomposeMetadataTypes(options) {
|
|
7
|
-
const { metadataTypes, postpurge, ignoreDirs, log } = options;
|
|
8
|
+
const { metadataTypes, postpurge, ignoreDirs, manifest, log } = options;
|
|
9
|
+
let manifestFilter;
|
|
10
|
+
let effectiveTypes;
|
|
11
|
+
if (manifest) {
|
|
12
|
+
manifestFilter = await parseManifest(manifest, ignoreDirs);
|
|
13
|
+
if (metadataTypes && metadataTypes.length > 0) {
|
|
14
|
+
const manifestTypes = new Set(manifestFilter.suffixes);
|
|
15
|
+
effectiveTypes = metadataTypes.filter((type) => manifestTypes.has(type));
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
effectiveTypes = manifestFilter.suffixes;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
if (!metadataTypes || metadataTypes.length === 0) {
|
|
23
|
+
throw Error('Either --metadata-type or --manifest must be provided.');
|
|
24
|
+
}
|
|
25
|
+
effectiveTypes = metadataTypes;
|
|
26
|
+
}
|
|
27
|
+
if (effectiveTypes.length === 0) {
|
|
28
|
+
log('No metadata types to recompose after applying the manifest filter.');
|
|
29
|
+
return { metadata: [] };
|
|
30
|
+
}
|
|
8
31
|
// Limit concurrent metadata type processing to prevent file system overload
|
|
9
32
|
const limit = pLimit(CONCURRENCY_LIMITS.METADATA_TYPES);
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
33
|
+
const processed = [];
|
|
34
|
+
const tasks = effectiveTypes.map((metadataType) => limit(async () => {
|
|
35
|
+
const manifestXmlPaths = manifestFilter?.parentXmlsBySuffix.get(metadataType);
|
|
36
|
+
let metaAttributes;
|
|
37
|
+
try {
|
|
38
|
+
({ metaAttributes } = await getRegistryValuesBySuffix(metadataType, 'recompose', ignoreDirs));
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
/* istanbul ignore if -- @preserve: preserves non-manifest behavior; unreachable via known CLI types */
|
|
42
|
+
if (!manifestFilter)
|
|
43
|
+
throw err;
|
|
44
|
+
/* istanbul ignore next -- @preserve: getRegistryValuesBySuffix always throws Error instances */
|
|
45
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
46
|
+
log(`Skipping ${metadataType}: ${message}`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
await recomposeFileHandler(metaAttributes, postpurge, manifestXmlPaths);
|
|
50
|
+
processed.push(metadataType);
|
|
13
51
|
log(`All metadata files have been recomposed for the metadata type: ${metadataType}`);
|
|
14
52
|
}));
|
|
15
53
|
await Promise.all(tasks);
|
|
16
54
|
return {
|
|
17
|
-
metadata:
|
|
55
|
+
metadata: processed,
|
|
18
56
|
};
|
|
19
57
|
}
|
|
20
58
|
//# sourceMappingURL=recomposeMetadataTypes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recomposeMetadataTypes.js","sourceRoot":"","sources":["../../src/core/recomposeMetadataTypes.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,MAAM,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,0CAA0C,CAAC;AACrF,OAAO,EAAE,oBAAoB,EAAE,MAAM,8CAA8C,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAG7D,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,OAAyB;IACpE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"recomposeMetadataTypes.js","sourceRoot":"","sources":["../../src/core/recomposeMetadataTypes.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,MAAM,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,0CAA0C,CAAC;AACrF,OAAO,EAAE,aAAa,EAAkB,MAAM,8BAA8B,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,8CAA8C,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAG7D,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,OAAyB;IACpE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IAExE,IAAI,cAA0C,CAAC;IAC/C,IAAI,cAAwB,CAAC;IAE7B,IAAI,QAAQ,EAAE,CAAC;QACb,cAAc,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAC3D,IAAI,aAAa,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9C,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;YACvD,cAAc,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3E,CAAC;aAAM,CAAC;YACN,cAAc,GAAG,cAAc,CAAC,QAAQ,CAAC;QAC3C,CAAC;IACH,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjD,MAAM,KAAK,CAAC,wDAAwD,CAAC,CAAC;QACxE,CAAC;QACD,cAAc,GAAG,aAAa,CAAC;IACjC,CAAC;IAED,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,GAAG,CAAC,oEAAoE,CAAC,CAAC;QAC1E,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC1B,CAAC;IAED,4EAA4E;IAC5E,MAAM,KAAK,GAAG,MAAM,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAAC;IAExD,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAChD,KAAK,CAAC,KAAK,IAAI,EAAE;QACf,MAAM,gBAAgB,GAAG,cAAc,EAAE,kBAAkB,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAE9E,IAAI,cAAc,CAAC;QACnB,IAAI,CAAC;YACH,CAAC,EAAE,cAAc,EAAE,GAAG,MAAM,yBAAyB,CAAC,YAAY,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC;QAChG,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,uGAAuG;YACvG,IAAI,CAAC,cAAc;gBAAE,MAAM,GAAG,CAAC;YAC/B,gGAAgG;YAChG,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,GAAG,CAAC,YAAY,YAAY,KAAK,OAAO,EAAE,CAAC,CAAC;YAC5C,OAAO;QACT,CAAC;QAED,MAAM,oBAAoB,CAAC,cAAc,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAC;QACxE,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC7B,GAAG,CAAC,kEAAkE,YAAY,EAAE,CAAC,CAAC;IACxF,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAEzB,OAAO;QACL,QAAQ,EAAE,SAAS;KACpB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { DecomposerOverride } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the absolute path of the default `.sfdecomposer.config.json`, located in the
|
|
4
|
+
* repo root (the nearest ancestor directory that contains `sfdx-project.json`). Throws
|
|
5
|
+
* a clear error when the repo root cannot be located or the config file does not exist.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveDefaultConfigPath(): Promise<string>;
|
|
8
|
+
export type ResolvedDecomposeTypeOptions = {
|
|
9
|
+
format: string;
|
|
10
|
+
strategy: string;
|
|
11
|
+
decomposeNestedPerms: boolean;
|
|
12
|
+
prepurge: boolean;
|
|
13
|
+
postpurge: boolean;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Load and validate the `overrides` array from a `.sfdecomposer.config.json` file.
|
|
17
|
+
* Returns an empty array if the file is missing, unreadable, or contains no overrides.
|
|
18
|
+
*/
|
|
19
|
+
export declare function loadOverridesFromConfig(configPath: string): Promise<DecomposerOverride[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Validate that the overrides array is well-formed. Throws on any structural problem.
|
|
22
|
+
* Unknown override keys are tolerated (ignored), but forbidden run-scope keys throw.
|
|
23
|
+
*/
|
|
24
|
+
export declare function validateOverrides(overrides: DecomposerOverride[]): void;
|
|
25
|
+
/**
|
|
26
|
+
* Find the override (if any) that targets a specific metadata suffix.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getOverrideForType(metadataType: string, overrides?: DecomposerOverride[]): DecomposerOverride | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the effective decompose options for a single metadata type. The base values are
|
|
31
|
+
* the run-wide options (CLI flags or defaults); per-type override values, when present, win.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveDecomposeOptionsForType(metadataType: string, base: ResolvedDecomposeTypeOptions, overrides?: DecomposerOverride[]): ResolvedDecomposeTypeOptions;
|