sf-decomposer 6.12.0 → 6.14.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 +17 -0
- package/README.md +180 -42
- package/lib/commands/decomposer/verify.d.ts +17 -0
- package/lib/commands/decomposer/verify.js +83 -0
- package/lib/commands/decomposer/verify.js.map +1 -0
- package/lib/core/decomposeMetadataTypes.js +6 -9
- package/lib/core/decomposeMetadataTypes.js.map +1 -1
- package/lib/core/verifyMetadataTypes.d.ts +12 -0
- package/lib/core/verifyMetadataTypes.js +143 -0
- package/lib/core/verifyMetadataTypes.js.map +1 -0
- package/lib/helpers/configOverrides.d.ts +48 -0
- package/lib/helpers/configOverrides.js +211 -36
- package/lib/helpers/configOverrides.js.map +1 -1
- package/lib/helpers/types.d.ts +46 -1
- package/lib/service/decompose/decomposeFileHandler.d.ts +3 -1
- package/lib/service/decompose/decomposeFileHandler.js +100 -32
- package/lib/service/decompose/decomposeFileHandler.js.map +1 -1
- package/lib/service/recompose/recomposeFileHandler.js +1 -1
- package/lib/service/recompose/recomposeFileHandler.js.map +1 -1
- package/lib/service/verify/diffDirectories.d.ts +34 -0
- package/lib/service/verify/diffDirectories.js +134 -0
- package/lib/service/verify/diffDirectories.js.map +1 -0
- package/messages/decomposer.decompose.md +1 -1
- package/messages/decomposer.verify.md +57 -0
- package/oclif.manifest.json +123 -2
- package/package.json +7 -4
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@
|
|
|
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.14.0](https://github.com/mcarvin8/sf-decomposer/compare/v6.13.0...v6.14.0) (2026-04-30)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add multiLevel override and round-trip verify command ([#418](https://github.com/mcarvin8/sf-decomposer/issues/418)) ([5f06265](https://github.com/mcarvin8/sf-decomposer/commit/5f06265333e9a0523154d0c745e83e0571938f98))
|
|
14
|
+
* **decompose:** expose splitTags as an override field ([#416](https://github.com/mcarvin8/sf-decomposer/issues/416)) ([eedf0f2](https://github.com/mcarvin8/sf-decomposer/commit/eedf0f23d248fdd028a097435aa9493449269954))
|
|
15
|
+
|
|
16
|
+
## [6.13.0](https://github.com/mcarvin8/sf-decomposer/compare/v6.12.0...v6.13.0) (2026-04-30)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
* **decompose:** add per-component overrides ([#415](https://github.com/mcarvin8/sf-decomposer/issues/415)) ([47d7c79](https://github.com/mcarvin8/sf-decomposer/commit/47d7c794db17ccc59e832309a0a4980d736ab89c))
|
|
22
|
+
* **deps:** bump dependabot/fetch-metadata from 2 to 3 ([#412](https://github.com/mcarvin8/sf-decomposer/issues/412)) ([6b0a24b](https://github.com/mcarvin8/sf-decomposer/commit/6b0a24b9c83dfb6fb9ef1d8dc8e2dfbec23200bf))
|
|
23
|
+
* **metadata:** bump @salesforce/source-deploy-retrieve ([#413](https://github.com/mcarvin8/sf-decomposer/issues/413)) ([e261049](https://github.com/mcarvin8/sf-decomposer/commit/e261049cc07a94642c1e80cf29dbd1e96a7183bd))
|
|
24
|
+
|
|
8
25
|
## [6.12.0](https://github.com/mcarvin8/sf-decomposer/compare/v6.11.0...v6.12.0) (2026-04-27)
|
|
9
26
|
|
|
10
27
|
|
package/README.md
CHANGED
|
@@ -17,7 +17,8 @@ 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
|
-
- [
|
|
20
|
+
- [sf decomposer verify](#sf-decomposer-verify)
|
|
21
|
+
- [Manifest-scoped runs](#manifest-scoped-runs)
|
|
21
22
|
- [Decompose Strategies](#decompose-strategies)
|
|
22
23
|
- [Custom Labels](#custom-labels-decomposition)
|
|
23
24
|
- [Permission Sets (grouped-by-tag)](#additional-permission-set-decomposition)
|
|
@@ -26,7 +27,9 @@ A Salesforce CLI plugin that **decomposes** large metadata XML files into smalle
|
|
|
26
27
|
- [Exceptions](#exceptions)
|
|
27
28
|
- [Troubleshooting](#troubleshooting)
|
|
28
29
|
- [Hooks](#hooks)
|
|
29
|
-
- [Per-Type Overrides](#per-type-overrides)
|
|
30
|
+
- [Per-Type & Per-Component Overrides](#per-type--per-component-overrides)
|
|
31
|
+
- [splitTags grammar](#splittags-grammar)
|
|
32
|
+
- [multiLevel grammar](#multilevel-grammar)
|
|
30
33
|
- [Ignore Files](#ignore-files)
|
|
31
34
|
- [.forceignore](#forceignore)
|
|
32
35
|
- [.sfdecomposerignore](#sfdecomposerignore)
|
|
@@ -79,7 +82,7 @@ A Salesforce CLI plugin that **decomposes** large metadata XML files into smalle
|
|
|
79
82
|
|
|
80
83
|
## Requirements
|
|
81
84
|
|
|
82
|
-
The [
|
|
85
|
+
The [config-disassembler-node](https://github.com/mcarvin8/config-disassembler-node) package, which depends on a Rust crate, ships with native binaries for these platforms:
|
|
83
86
|
|
|
84
87
|
| Platform | Architectures |
|
|
85
88
|
| ----------- | ---------------------------------- |
|
|
@@ -87,7 +90,7 @@ The [xml-disassembler-node](https://github.com/mcarvin8/xml-disassembler-node) p
|
|
|
87
90
|
| **Linux** | x64, arm64, ia32 |
|
|
88
91
|
| **Windows** | x64 |
|
|
89
92
|
|
|
90
|
-
If other platforms or architectures require support, please open an issue in [
|
|
93
|
+
If other platforms or architectures require support, please open an issue in [config-disassembler-node](https://github.com/mcarvin8/config-disassembler-node/issues).
|
|
91
94
|
|
|
92
95
|
---
|
|
93
96
|
|
|
@@ -113,10 +116,11 @@ Salesforce’s built-in decomposition is limited. sf-decomposer gives admins and
|
|
|
113
116
|
|
|
114
117
|
## Commands
|
|
115
118
|
|
|
116
|
-
| Command | Description
|
|
117
|
-
| ------------------------- |
|
|
118
|
-
| `sf decomposer decompose` | Decompose metadata in package directories into smaller files.
|
|
119
|
-
| `sf decomposer recompose` | Recompose decomposed files back into deployment-ready metadata.
|
|
119
|
+
| Command | Description |
|
|
120
|
+
| ------------------------- | ----------------------------------------------------------------------------------- |
|
|
121
|
+
| `sf decomposer decompose` | Decompose metadata in package directories into smaller files. |
|
|
122
|
+
| `sf decomposer recompose` | Recompose decomposed files back into deployment-ready metadata. |
|
|
123
|
+
| `sf decomposer verify` | Round-trip check: decompose + recompose in a temp directory and diff the originals. |
|
|
120
124
|
|
|
121
125
|
### sf decomposer decompose
|
|
122
126
|
|
|
@@ -135,7 +139,7 @@ FLAGS
|
|
|
135
139
|
--prepurge Remove existing decomposed files before decomposing [default: false]
|
|
136
140
|
--postpurge Remove original metadata files after decomposing [default: false]
|
|
137
141
|
-p, --decompose-nested-permissions With grouped-by-tag, further decompose permission set and muting permission set object/field permissions
|
|
138
|
-
-c, --config Load per-
|
|
142
|
+
-c, --config Load per-type and per-component overrides from .sfdecomposer.config.json in the repo root. Only the "overrides" array is consumed. See Per-Type & Per-Component Overrides. [default: false]
|
|
139
143
|
|
|
140
144
|
GLOBAL FLAGS
|
|
141
145
|
--json Output as JSON.
|
|
@@ -193,9 +197,49 @@ sf decomposer recompose -x "manifest/package.xml"
|
|
|
193
197
|
sf project deploy start -x "manifest/package.xml"
|
|
194
198
|
```
|
|
195
199
|
|
|
196
|
-
###
|
|
200
|
+
### sf decomposer verify
|
|
197
201
|
|
|
198
|
-
|
|
202
|
+
Non-destructive round-trip check: copies your package directories into a temp directory under your OS's `tmpdir()`, runs decompose then recompose there, and diffs the rebuilt parents against the originals using **structural XML equality** (sibling and attribute order are ignored). Exits non-zero on any drift; your working tree is never modified.
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
USAGE
|
|
206
|
+
$ sf decomposer verify [-m <value>] [-x <value>] [-f <value>] [-i <value>] [-s <value>] [-p -c --json]
|
|
207
|
+
|
|
208
|
+
FLAGS
|
|
209
|
+
-m, --metadata-type=<value> Metadata suffix to verify (e.g. flow, labels). Repeatable. Optional when --manifest is provided.
|
|
210
|
+
-x, --manifest=<value> Path to a package.xml manifest. When provided, only the components listed in the manifest are verified.
|
|
211
|
+
-f, --format=<value> Output format used for the round-trip decompose: xml | yaml | json | json5 [default: xml]
|
|
212
|
+
-i, --ignore-package-directory=<value> Package directory to skip. Repeatable.
|
|
213
|
+
-s, --strategy=<value> unique-id | grouped-by-tag [default: unique-id]
|
|
214
|
+
-p, --decompose-nested-permissions With grouped-by-tag, further decompose permission set and muting permission set object/field permissions.
|
|
215
|
+
-c, --config Load per-type and per-component overrides from .sfdecomposer.config.json in the repo root, the same as `decompose --config`. [default: false]
|
|
216
|
+
|
|
217
|
+
GLOBAL FLAGS
|
|
218
|
+
--json Output as JSON.
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
> At least one of `--metadata-type` or `--manifest` is required. When both are supplied, the run is scoped to the intersection of the two.
|
|
222
|
+
|
|
223
|
+
**Examples**
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# Verify two metadata types round-trip cleanly with defaults
|
|
227
|
+
sf decomposer verify -m "permissionset" -m "profile"
|
|
228
|
+
|
|
229
|
+
# Verify a different strategy + nested-perms split before committing the change
|
|
230
|
+
sf decomposer verify -m "permissionset" -s "grouped-by-tag" -p
|
|
231
|
+
|
|
232
|
+
# CI gate: verify just the components in a deploy manifest, using the repo-root config
|
|
233
|
+
sf decomposer verify -x "manifest/package.xml" --config
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Files where the **only** delta is sibling or attribute ordering are surfaced separately as informational notices ("Note: N file(s) round-tripped semantically but with sibling/attribute reordering") rather than as drift. This is safe — Salesforce treats metadata as order-agnostic and `config-disassembler` does not preserve original sibling order — but it tells you up front that committing the post-recompose output will produce a diff in git even though the metadata is functionally identical.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Manifest-scoped runs
|
|
241
|
+
|
|
242
|
+
The `-x` / `--manifest` flag is supported by every `sf decomposer` command (`decompose`, `recompose`, `verify`) and accepts any standard Salesforce `package.xml`, limiting 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
243
|
|
|
200
244
|
How it works:
|
|
201
245
|
|
|
@@ -232,7 +276,7 @@ sf project deploy start -x "manifest/package.xml"
|
|
|
232
276
|
|
|
233
277
|
## Decompose Strategies
|
|
234
278
|
|
|
235
|
-
> **
|
|
279
|
+
> **Tip:** A single decompose run can mix strategies and formats across metadata types — and even across components within the same type — through the `overrides` array (see [Per-Type & Per-Component Overrides](#per-type--per-component-overrides)). Recompose is deterministic from the on-disk sidecar, so any combination round-trips. When switching strategies for an existing component, pass `--prepurge` (or set `prePurge: true`) so leftover files from the previous strategy are removed before the new ones are written.
|
|
236
280
|
|
|
237
281
|
- **unique-id** (default): Each nested element goes to its own file, named by unique-id fields or content hash. Leaf elements stay in a file named like the original XML.
|
|
238
282
|
- **grouped-by-tag**: All elements with the same tag (e.g. `<fieldPermissions>`) go into one file named after the tag (e.g. `fieldPermissions.xml`). Leaf elements are still grouped in the original-named file.
|
|
@@ -334,14 +378,14 @@ For example, if you attempt to decompose Custom Labels but none of your package
|
|
|
334
378
|
|
|
335
379
|
### XML disassemble output (Rust crate)
|
|
336
380
|
|
|
337
|
-
The
|
|
381
|
+
The config-disassembler Node plugin uses a **Rust crate** for XML decomposing and recomposing. Disassemble errors and messages are shown in the terminal.
|
|
338
382
|
|
|
339
383
|
Control verbosity with the `RUST_LOG` environment variable (e.g. `RUST_LOG=debug` for detailed output).
|
|
340
384
|
|
|
341
385
|
Example output in the terminal (Rust log format):
|
|
342
386
|
|
|
343
387
|
```
|
|
344
|
-
[2026-
|
|
388
|
+
[2026-04-30T12:34:38Z ERROR config_disassembler::xml::builders::build_disassembled_files] The XML file C:\Users\matthew.carvin\Documents\sf-decomposer\fixtures\package-dir-1\permissionsets\only_leafs.permissionset-meta.xml only has leaf elements. This file will not be disassembled.
|
|
345
389
|
```
|
|
346
390
|
|
|
347
391
|
### Files with only leaf elements
|
|
@@ -359,27 +403,27 @@ Put **.sfdecomposer.config.json** in the project root to run:
|
|
|
359
403
|
- **After** `sf project retrieve start`: decompose.
|
|
360
404
|
- **Before** `sf project deploy start` / `sf project deploy validate`: recompose.
|
|
361
405
|
|
|
362
|
-
Copy and customize the [sample config](https://raw.githubusercontent.com/mcarvin8/sf-decomposer/main/examples/.sfdecomposer.config.json), or the [sample config with
|
|
406
|
+
Copy and customize the [sample config](https://raw.githubusercontent.com/mcarvin8/sf-decomposer/main/examples/.sfdecomposer.config.json), or the [sample config with overrides](https://raw.githubusercontent.com/mcarvin8/sf-decomposer/main/examples/.sfdecomposer.config.overrides.json) to vary format/strategy/etc. by metadata type or by individual component.
|
|
363
407
|
|
|
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-
|
|
408
|
+
| Option | Required | Description |
|
|
409
|
+
| ---------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
410
|
+
| `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. |
|
|
411
|
+
| `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. |
|
|
412
|
+
| `ignorePackageDirectories` | No | Comma-separated package directories to skip. |
|
|
413
|
+
| `prePurge` | No | Remove existing decomposed files before decomposing (default: false). |
|
|
414
|
+
| `postPurge` | No | After decompose: remove originals; after recompose: remove decomposed files (default: false). |
|
|
415
|
+
| `decomposedFormat` | No | xml, json, json5, or yaml (default: xml). |
|
|
416
|
+
| `strategy` | No | `unique-id` \| `grouped-by-tag` (default: unique-id). |
|
|
417
|
+
| `decomposeNestedPermissions` | No | With grouped-by-tag, set true to further decompose permission set and muting permission set object/field permissions. |
|
|
418
|
+
| `overrides` | No | Array of per-type and/or per-component overrides for `decomposedFormat`, `strategy`, `decomposeNestedPermissions`, `prePurge`, and `postPurge`. See [Per-Type & Per-Component Overrides](#per-type--per-component-overrides). |
|
|
375
419
|
|
|
376
420
|
---
|
|
377
421
|
|
|
378
|
-
## Per-Type Overrides
|
|
422
|
+
## Per-Type & Per-Component Overrides
|
|
379
423
|
|
|
380
|
-
|
|
424
|
+
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
425
|
|
|
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.
|
|
426
|
+
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 (**type-scope**) or per individual SDR component (**component-scope**) without splitting the run into multiple invocations.
|
|
383
427
|
|
|
384
428
|
```json
|
|
385
429
|
{
|
|
@@ -395,6 +439,11 @@ By default, a single decompose run uses one format and one strategy across every
|
|
|
395
439
|
"metadataTypes": ["permissionset", "mutingpermissionset"],
|
|
396
440
|
"strategy": "grouped-by-tag",
|
|
397
441
|
"decomposeNestedPermissions": true
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
"components": ["permissionset:HR_Admin", "permissionset:Big_PermSet"],
|
|
445
|
+
"strategy": "grouped-by-tag",
|
|
446
|
+
"decomposeNestedPermissions": true
|
|
398
447
|
}
|
|
399
448
|
]
|
|
400
449
|
}
|
|
@@ -402,24 +451,113 @@ By default, a single decompose run uses one format and one strategy across every
|
|
|
402
451
|
|
|
403
452
|
### What can be overridden
|
|
404
453
|
|
|
405
|
-
| Field | Notes
|
|
406
|
-
| ---------------------------- |
|
|
407
|
-
| `metadataTypes` |
|
|
408
|
-
| `
|
|
409
|
-
| `
|
|
410
|
-
| `
|
|
411
|
-
| `
|
|
412
|
-
| `
|
|
454
|
+
| Field | Notes |
|
|
455
|
+
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
456
|
+
| `metadataTypes` | Optional (required if `components` is omitted). Array of metadata suffixes (same vocabulary as `--metadata-type` / `metadataSuffixes`). Each suffix may appear in at most one override. |
|
|
457
|
+
| `components` | Optional (required if `metadataTypes` is omitted). Array of `<metadataSuffix>:<fullName>` keys (e.g. `permissionset:HR_Admin`, `report:MyFolder/MyReport`). Each component may appear in at most one override. |
|
|
458
|
+
| `decomposedFormat` | `xml` \| `json` \| `json5` \| `yaml`. |
|
|
459
|
+
| `strategy` | `unique-id` \| `grouped-by-tag`. Hard rules still win — `labels` and `loyaltyProgramSetup` are always treated as `unique-id`. |
|
|
460
|
+
| `decomposeNestedPermissions` | Only applies to `permissionset` / `mutingpermissionset` with `grouped-by-tag`. Sets a known-good `splitTags` default; ignored if `splitTags` is also set in the same scope. |
|
|
461
|
+
| `splitTags` | Custom `splitTags` spec for `grouped-by-tag` strategy. See [splitTags grammar](#splittags-grammar). Ignored when the resolved strategy is not `grouped-by-tag`. |
|
|
462
|
+
| `multiLevel` | Custom `multiLevel` spec for nested-array decomposition. See [multiLevel grammar](#multilevel-grammar). When set, replaces the hardcoded `loyaltyProgramSetup` default for the targeted scope. |
|
|
463
|
+
| `prePurge` | Per-scope prePurge (decompose). Component-scope `prePurge` only purges the named component's decomposed directory. |
|
|
464
|
+
| `postPurge` | Per-scope postPurge (decompose: remove originals after decomposing). |
|
|
413
465
|
|
|
414
466
|
Run-scope options (`metadataSuffixes`, `manifest`, `ignorePackageDirectories`) are **not** valid inside an override; the plugin will throw if they are present.
|
|
415
467
|
|
|
468
|
+
#### Component key conventions
|
|
469
|
+
|
|
470
|
+
The `<fullName>` part of a component key is the SDR fullName for the component, matching the basename of the decomposed directory:
|
|
471
|
+
|
|
472
|
+
- **Plain types** (e.g. `permissionset`, `flow`, `profile`, `workflow`): use the file stem, e.g. `permissionset:HR_Admin` for `permissionsets/HR_Admin.permissionset-meta.xml`.
|
|
473
|
+
- **Strict-directory types** (e.g. `bot`): use the bot directory name, e.g. `bot:My_Bot` for `bots/My_Bot/My_Bot.bot-meta.xml`.
|
|
474
|
+
- **Folder-typed metadata** (e.g. `report`, `dashboard`, `email`, `document`): the unit of decomposition is the folder; use the folder name, e.g. `report:MyFolder` to scope every report inside `reports/MyFolder/`.
|
|
475
|
+
- **`labels`**: there is exactly one labels file per labels directory, so component-scope keys are not meaningful — use the type-scope `metadataTypes: ["labels"]` instead.
|
|
476
|
+
|
|
477
|
+
Component overrides are not a filter. If `--metadata` / `metadataSuffixes` includes `permissionset`, every permission set is still decomposed; the override only changes how the named ones are decomposed. Use `--manifest` / the hook's `manifest` field if you want to scope the run itself to a subset of components.
|
|
478
|
+
|
|
416
479
|
### Precedence
|
|
417
480
|
|
|
418
|
-
For each
|
|
481
|
+
For each component, each option is resolved independently in this order (highest first):
|
|
482
|
+
|
|
483
|
+
1. The component-scope override value (matching `<suffix>:<fullName>` in `components`), if set.
|
|
484
|
+
2. The type-scope override value (matching `<suffix>` in `metadataTypes`), if set.
|
|
485
|
+
3. The run-wide value (CLI flag, hook config top-level field, or built-in default).
|
|
486
|
+
4. Hard plugin rules (e.g. `labels` and `loyaltyProgramSetup` forced to `unique-id`) override all of the above.
|
|
487
|
+
|
|
488
|
+
### splitTags grammar
|
|
489
|
+
|
|
490
|
+
`splitTags` lets you control how `grouped-by-tag` writes nested arrays for any metadata type. The plugin already applies a known-good default for permission sets when `decomposeNestedPermissions: true` is set; setting `splitTags` directly takes precedence and works for any metadata type.
|
|
491
|
+
|
|
492
|
+
**Spec:** Comma-separated rules. Each rule has 3 or 4 colon-separated parts:
|
|
493
|
+
|
|
494
|
+
- `<tag>:<mode>:<field>` — read array items from the top-level `<tag>`.
|
|
495
|
+
- `<tag>:<path>:<mode>:<field>` — read array items from the nested `<path>` (defaults to `<tag>`).
|
|
496
|
+
|
|
497
|
+
`<mode>` is one of:
|
|
498
|
+
|
|
499
|
+
- **`split`** — write one file per array item, named after the value of `<field>` on each item.
|
|
500
|
+
- **`group`** — group array items by the value of `<field>`, writing one file per group.
|
|
501
|
+
|
|
502
|
+
Each `<tag>` may appear at most once in a spec. The plugin validates the grammar at config-load time. Deeper checks (e.g. unknown tag names for the metadata type) are surfaced by the underlying disassembler crate at runtime.
|
|
503
|
+
|
|
504
|
+
#### splitTags cookbook
|
|
505
|
+
|
|
506
|
+
```json
|
|
507
|
+
"overrides": [
|
|
508
|
+
{
|
|
509
|
+
"metadataTypes": ["permissionset", "mutingpermissionset"],
|
|
510
|
+
"strategy": "grouped-by-tag",
|
|
511
|
+
"splitTags": "objectPermissions:split:object,fieldPermissions:group:field"
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
"metadataTypes": ["profile"],
|
|
515
|
+
"strategy": "grouped-by-tag",
|
|
516
|
+
"splitTags": "objectPermissions:split:object,fieldPermissions:group:field,layoutAssignments:group:layout"
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
"metadataTypes": ["flow"],
|
|
520
|
+
"strategy": "grouped-by-tag",
|
|
521
|
+
"splitTags": "actionCalls:split:name,decisions:split:name,assignments:split:name"
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
"metadataTypes": ["workflow"],
|
|
525
|
+
"strategy": "grouped-by-tag",
|
|
526
|
+
"splitTags": "rules:split:fullName,alerts:split:fullName,fieldUpdates:split:fullName,tasks:split:fullName"
|
|
527
|
+
}
|
|
528
|
+
]
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
> **Caveat:** When using `mode: split`, the chosen `<field>` must produce a unique value for every array item — otherwise two items would map to the same filename. If two items share a field value, prefer `mode: group` instead, which is designed for that case.
|
|
532
|
+
|
|
533
|
+
### multiLevel grammar
|
|
534
|
+
|
|
535
|
+
`multiLevel` enables a second decomposition pass on inner-level files for metadata types whose XML has deeply nested repeatable blocks (e.g. `loyaltyProgramSetup`'s `programProcesses → parameters → ...`). The plugin already applies a known-good default for `loyaltyProgramSetup` when running the `unique-id` strategy; setting `multiLevel` directly takes precedence and works for any metadata type.
|
|
536
|
+
|
|
537
|
+
**Spec:** A single rule with exactly 3 colon-separated parts (the third part is itself a comma-separated list):
|
|
538
|
+
|
|
539
|
+
```
|
|
540
|
+
<file_pattern>:<root_to_strip>:<unique_id_elements>
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
- **`<file_pattern>`** — basename pattern that selects which inner-level files get the second decomposition pass (e.g. `programProcesses`).
|
|
544
|
+
- **`<root_to_strip>`** — XML root tag to strip from each matched file before splitting.
|
|
545
|
+
- **`<unique_id_elements>`** — comma-separated list of element names used to derive a stable filename for each inner-level item (e.g. `parameterName,ruleName`). The first element that resolves to a non-empty value wins.
|
|
546
|
+
|
|
547
|
+
The plugin validates the grammar at config-load time; deeper checks (whether the file pattern matches anything, whether the unique-id elements actually appear on the inner XML) are surfaced by the underlying disassembler crate at runtime.
|
|
548
|
+
|
|
549
|
+
```json
|
|
550
|
+
"overrides": [
|
|
551
|
+
{
|
|
552
|
+
"metadataTypes": ["loyaltyProgramSetup"],
|
|
553
|
+
"multiLevel": "programProcesses:programProcesses:parameterName,ruleName"
|
|
554
|
+
}
|
|
555
|
+
]
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
> **Limit:** Only one rule per scope. The disassembler does not currently accept multiple comma-separated multiLevel specs, because the third part of the rule is itself a comma-separated list.
|
|
419
559
|
|
|
420
|
-
|
|
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.
|
|
560
|
+
> **Tip:** Use [`sf decomposer verify`](#sf-decomposer-verify) to non-destructively confirm a new override config still round-trips before committing it.
|
|
423
561
|
|
|
424
562
|
### Opting in from the CLI
|
|
425
563
|
|
|
@@ -463,7 +601,7 @@ Bugs and feature requests: [open an issue](https://github.com/mcarvin8/sf-decomp
|
|
|
463
601
|
|
|
464
602
|
## Built With
|
|
465
603
|
|
|
466
|
-
- [
|
|
604
|
+
- [config-disassembler-node](https://github.com/mcarvin8/config-disassembler-node) – Disassemble XML (and other config formats) into smaller, manageable files and reassemble when needed. Node.js + Rust (Neon). See [Requirements](#requirements).
|
|
467
605
|
- [@salesforce/source-deploy-retrieve](https://github.com/forcedotcom/source-deploy-retrieve) – JavaScript toolkit for working with Salesforce metadata.
|
|
468
606
|
|
|
469
607
|
---
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { SfCommand } from '@salesforce/sf-plugins-core';
|
|
2
|
+
import { VerifyResult } from '../../helpers/types.js';
|
|
3
|
+
export default class DecomposerVerify extends SfCommand<VerifyResult> {
|
|
4
|
+
static readonly summary: string;
|
|
5
|
+
static readonly description: string;
|
|
6
|
+
static readonly examples: string[];
|
|
7
|
+
static readonly flags: {
|
|
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>;
|
|
10
|
+
format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
'ignore-package-directory': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
strategy: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
'decompose-nested-permissions': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
config: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
};
|
|
16
|
+
run(): Promise<VerifyResult>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
|
|
3
|
+
import { Messages } from '@salesforce/core';
|
|
4
|
+
import { DECOMPOSED_FILE_TYPES, DECOMPOSED_STRATEGIES } from '../../helpers/constants.js';
|
|
5
|
+
import { verifyMetadataTypes } from '../../core/verifyMetadataTypes.js';
|
|
6
|
+
import { loadOverridesFromConfig, resolveDefaultConfigPath } from '../../helpers/configOverrides.js';
|
|
7
|
+
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
8
|
+
const messages = Messages.loadMessages('sf-decomposer', 'decomposer.verify');
|
|
9
|
+
export default class DecomposerVerify extends SfCommand {
|
|
10
|
+
static summary = messages.getMessage('summary');
|
|
11
|
+
static description = messages.getMessage('description');
|
|
12
|
+
static examples = messages.getMessages('examples');
|
|
13
|
+
static flags = {
|
|
14
|
+
'metadata-type': Flags.string({
|
|
15
|
+
summary: messages.getMessage('flags.metadata-type.summary'),
|
|
16
|
+
char: 'm',
|
|
17
|
+
multiple: true,
|
|
18
|
+
required: false,
|
|
19
|
+
}),
|
|
20
|
+
manifest: Flags.file({
|
|
21
|
+
summary: messages.getMessage('flags.manifest.summary'),
|
|
22
|
+
char: 'x',
|
|
23
|
+
required: false,
|
|
24
|
+
exists: true,
|
|
25
|
+
}),
|
|
26
|
+
format: Flags.string({
|
|
27
|
+
summary: messages.getMessage('flags.format.summary'),
|
|
28
|
+
char: 'f',
|
|
29
|
+
required: true,
|
|
30
|
+
multiple: false,
|
|
31
|
+
default: 'xml',
|
|
32
|
+
options: DECOMPOSED_FILE_TYPES,
|
|
33
|
+
}),
|
|
34
|
+
'ignore-package-directory': Flags.directory({
|
|
35
|
+
summary: messages.getMessage('flags.ignore-package-directory.summary'),
|
|
36
|
+
char: 'i',
|
|
37
|
+
required: false,
|
|
38
|
+
multiple: true,
|
|
39
|
+
}),
|
|
40
|
+
strategy: Flags.string({
|
|
41
|
+
summary: messages.getMessage('flags.strategy.summary'),
|
|
42
|
+
char: 's',
|
|
43
|
+
required: true,
|
|
44
|
+
multiple: false,
|
|
45
|
+
default: 'unique-id',
|
|
46
|
+
options: DECOMPOSED_STRATEGIES,
|
|
47
|
+
}),
|
|
48
|
+
'decompose-nested-permissions': Flags.boolean({
|
|
49
|
+
summary: messages.getMessage('flags.decompose-nested-permissions.summary'),
|
|
50
|
+
char: 'p',
|
|
51
|
+
required: false,
|
|
52
|
+
default: false,
|
|
53
|
+
}),
|
|
54
|
+
config: Flags.boolean({
|
|
55
|
+
summary: messages.getMessage('flags.config.summary'),
|
|
56
|
+
char: 'c',
|
|
57
|
+
required: false,
|
|
58
|
+
default: false,
|
|
59
|
+
}),
|
|
60
|
+
};
|
|
61
|
+
async run() {
|
|
62
|
+
const { flags } = await this.parse(DecomposerVerify);
|
|
63
|
+
if (!flags['metadata-type'] && !flags['manifest']) {
|
|
64
|
+
throw messages.createError('error.missingMetadataOrManifest');
|
|
65
|
+
}
|
|
66
|
+
const overrides = flags['config'] ? await loadOverridesFromConfig(await resolveDefaultConfigPath()) : undefined;
|
|
67
|
+
const result = await verifyMetadataTypes({
|
|
68
|
+
metadataTypes: flags['metadata-type'],
|
|
69
|
+
format: flags['format'],
|
|
70
|
+
ignoreDirs: flags['ignore-package-directory'],
|
|
71
|
+
strategy: flags['strategy'],
|
|
72
|
+
decomposeNestedPerms: flags['decompose-nested-permissions'],
|
|
73
|
+
manifest: flags['manifest'],
|
|
74
|
+
overrides,
|
|
75
|
+
log: this.log.bind(this),
|
|
76
|
+
});
|
|
77
|
+
if (result.drift.length > 0) {
|
|
78
|
+
throw messages.createError('error.driftDetected', [String(result.drift.length)]);
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../src/commands/decomposer/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -48,15 +48,12 @@ export async function decomposeMetadataTypes(options) {
|
|
|
48
48
|
log(`Skipping ${metadataType}: ${message}`);
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
effectiveStrategy = 'unique-id';
|
|
58
|
-
}
|
|
59
|
-
await decomposeFileHandler(metaAttributes, resolved.prepurge, resolved.postpurge, resolved.format, ignorePath, effectiveStrategy, resolved.decomposeNestedPerms, manifestXmlPaths);
|
|
51
|
+
// Type-scope resolved options serve as the base for component-scope resolution further
|
|
52
|
+
// down the call stack. Hard strategy rules (labels / loyaltyProgramSetup) are applied per
|
|
53
|
+
// file inside the disassembler so they remain in force even when a component-scope override
|
|
54
|
+
// tries to flip the strategy.
|
|
55
|
+
const typeResolved = resolveDecomposeOptionsForType(metadataType, { format, strategy, decomposeNestedPerms, prepurge, postpurge }, overrides);
|
|
56
|
+
await decomposeFileHandler(metaAttributes, typeResolved, ignorePath, overrides, manifestXmlPaths);
|
|
60
57
|
processed.push(metadataType);
|
|
61
58
|
log(`All metadata files have been decomposed for the metadata type: ${metadataType}`);
|
|
62
59
|
}));
|
|
@@ -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,aAAa,EAAkB,MAAM,8BAA8B,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,8CAA8C,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D,OAAO,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAE/E,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,OAAyB;IACpE,MAAM,EACJ,aAAa,EACb,QAAQ,EACR,SAAS,EACT,MAAM,EACN,UAAU,EACV,QAAQ,EACR,oBAAoB,EACpB,QAAQ,EACR,SAAS,EACT,GAAG,GACJ,GAAG,OAAO,CAAC;IAEZ,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,UAAkB,CAAC;QACvB,IAAI,CAAC;YACH,CAAC,EAAE,cAAc,EAAE,UAAU,EAAE,GAAG,MAAM,yBAAyB,CAAC,YAAY,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC;QAC5G,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,
|
|
1
|
+
{"version":3,"file":"decomposeMetadataTypes.js","sourceRoot":"","sources":["../../src/core/decomposeMetadataTypes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { VerifyOptions, VerifyResult } from '../helpers/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Run a non-destructive round-trip check: copy the user's package directories into a scratch
|
|
4
|
+
* directory under the OS temp folder, decompose + recompose them there, and diff the rebuilt
|
|
5
|
+
* parents against the originals.
|
|
6
|
+
*
|
|
7
|
+
* The temp directory is always removed before this function returns, and the user's working tree
|
|
8
|
+
* is never modified. The returned `drift` array is empty when every parent XML survived the round
|
|
9
|
+
* trip byte-identically; otherwise each entry names the offending file (relative to its package
|
|
10
|
+
* directory) and a short reason.
|
|
11
|
+
*/
|
|
12
|
+
export declare function verifyMetadataTypes(options: VerifyOptions): Promise<VerifyResult>;
|