sf-decomposer 6.33.1 → 6.33.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -2
- package/HANDBOOK.md +10 -9
- package/README.md +99 -285
- package/lib/commands/decomposer/decompose.js +2 -2
- package/lib/commands/decomposer/decompose.js.map +1 -1
- package/lib/commands/decomposer/recompose.js +1 -1
- package/lib/commands/decomposer/recompose.js.map +1 -1
- package/lib/commands/decomposer/verify.js +2 -2
- package/lib/commands/decomposer/verify.js.map +1 -1
- package/lib/core/decomposeMetadataTypes.d.ts +1 -1
- package/lib/core/decomposeMetadataTypes.js +5 -5
- package/lib/core/decomposeMetadataTypes.js.map +1 -1
- package/lib/core/recomposeMetadataTypes.js +2 -2
- package/lib/core/recomposeMetadataTypes.js.map +1 -1
- package/lib/core/verifyMetadataTypes.js +2 -2
- package/lib/core/verifyMetadataTypes.js.map +1 -1
- package/lib/helpers/configOverrides.js +3 -3
- package/lib/helpers/configOverrides.js.map +1 -1
- package/lib/helpers/types.d.ts +0 -5
- package/lib/hooks/prerun.js +2 -2
- package/lib/hooks/prerun.js.map +1 -1
- package/lib/hooks/scopedPostRetrieve.js +2 -2
- package/lib/hooks/scopedPostRetrieve.js.map +1 -1
- package/lib/metadata/getPackageDirectories.js +4 -4
- package/lib/metadata/getPackageDirectories.js.map +1 -1
- package/lib/metadata/getRegistryValuesBySuffix.js +2 -1
- package/lib/metadata/getRegistryValuesBySuffix.js.map +1 -1
- package/lib/metadata/parseManifest.js +3 -3
- package/lib/metadata/parseManifest.js.map +1 -1
- package/lib/service/core/getRepoRoot.js +3 -3
- package/lib/service/core/getRepoRoot.js.map +1 -1
- package/lib/service/core/moveFiles.js +1 -1
- package/lib/service/core/moveFiles.js.map +1 -1
- package/lib/service/core/updateForceignore.js +1 -1
- package/lib/service/core/updateForceignore.js.map +1 -1
- package/lib/service/decompose/customLabels.js +2 -2
- package/lib/service/decompose/customLabels.js.map +1 -1
- package/lib/service/decompose/decomposeFileHandler.d.ts +1 -1
- package/lib/service/decompose/decomposeFileHandler.js +4 -4
- package/lib/service/decompose/decomposeFileHandler.js.map +1 -1
- package/lib/service/decompose/renameWorkflows.js +2 -2
- package/lib/service/decompose/renameWorkflows.js.map +1 -1
- package/lib/service/recompose/deleteFilesinDirectory.js +1 -1
- package/lib/service/recompose/deleteFilesinDirectory.js.map +1 -1
- package/lib/service/recompose/recomposeFileHandler.js +1 -1
- package/lib/service/recompose/recomposeFileHandler.js.map +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +97 -30
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@
|
|
|
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.33.2](https://github.com/mcarvin8/sf-decomposer/compare/v6.33.1...v6.33.2) (2026-06-29)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **deps:** bump all deps, drop Node 20 support ([#505](https://github.com/mcarvin8/sf-decomposer/issues/505)) ([7425c87](https://github.com/mcarvin8/sf-decomposer/commit/7425c87c24721ab8e41938ed6fb85792ef56dff4))
|
|
14
|
+
|
|
8
15
|
## [6.33.1](https://github.com/mcarvin8/sf-decomposer/compare/v6.33.0...v6.33.1) (2026-06-25)
|
|
9
16
|
|
|
10
17
|
|
|
@@ -335,8 +342,8 @@ All notable changes to this project will be documented in this file. See [standa
|
|
|
335
342
|
|
|
336
343
|
### ⚠ BREAKING CHANGES
|
|
337
344
|
|
|
338
|
-
* Remove --debug flag and disassemble.log file
|
|
339
|
-
* Remove INI and TOML formats from decompose
|
|
345
|
+
* Remove --debug flag and disassemble.log file
|
|
346
|
+
* Remove INI and TOML formats from decompose
|
|
340
347
|
* Re-decompose files to include updated key config JSONs from rust crate
|
|
341
348
|
|
|
342
349
|
### Features
|
package/HANDBOOK.md
CHANGED
|
@@ -23,7 +23,7 @@ If you want the underlying option grammar instead of recipes, see the [main READ
|
|
|
23
23
|
Three knobs cover almost every case:
|
|
24
24
|
|
|
25
25
|
| Symptom of the source XML | Reach for |
|
|
26
|
-
|
|
26
|
+
|-----------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------|
|
|
27
27
|
| One repeating section at the top level (e.g. `<labels>` of a custom-labels file). | `strategy: unique-id` (default). |
|
|
28
28
|
| Lots of small repeatable tags, you want one file per tag-name, not per-instance. | `strategy: grouped-by-tag`. |
|
|
29
29
|
| `grouped-by-tag`, but a few specific tags (e.g. `objectPermissions`) need finer-grained diff. | Add `splitTags`. |
|
|
@@ -36,7 +36,7 @@ Hard rules the plugin always enforces (so you don't have to):
|
|
|
36
36
|
Built-in `multiLevel` defaults — applied automatically when `strategy` is `unique-id` and you do not supply your own `multiLevel` for that type. You can replace any of them by setting an explicit `multiLevel` on the override.
|
|
37
37
|
|
|
38
38
|
| Metadata type | Built-in `multiLevel` |
|
|
39
|
-
|
|
39
|
+
|-----------------------|---------------------------------------------------------------------|
|
|
40
40
|
| `bot` | `["botDialogs:botDialogs:developerName", "botSteps:botSteps:type"]` |
|
|
41
41
|
| `loyaltyProgramSetup` | `"programProcesses:programProcesses:parameterName,ruleName"` |
|
|
42
42
|
|
|
@@ -264,13 +264,14 @@ layouts/
|
|
|
264
264
|
|
|
265
265
|
These follow the same pattern; pick the rules that match your repo's data. None of these are decomposed natively by Salesforce.
|
|
266
266
|
|
|
267
|
-
| Metadata type | Suggested override
|
|
268
|
-
|
|
269
|
-
| `flow` | `multiLevel: ["actionCalls:actionCalls:name", "decisions:decisions:name", "rules:rules:name"]`
|
|
270
|
-
| `globalValueSet` | `multiLevel: ["customValue:customValue:fullName"]` — handy when value sets have hundreds of picks.
|
|
271
|
-
| `
|
|
272
|
-
| `
|
|
273
|
-
| `
|
|
267
|
+
| Metadata type | Suggested override |
|
|
268
|
+
|--------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
269
|
+
| `flow` | `multiLevel: ["actionCalls:actionCalls:name", "decisions:decisions:name", "rules:rules:name"]` |
|
|
270
|
+
| `globalValueSet` | `multiLevel: ["customValue:customValue:fullName"]` — handy when value sets have hundreds of picks. |
|
|
271
|
+
| `field` (CustomField) | `multiLevel: "valueSet:valueSetDefinition:fullName"` — splits each picklist value into its own file. Only effective on fields that define picklist values inline; fields referencing a global value set or non-picklist fields are leaf-only and skipped. |
|
|
272
|
+
| `marketingappextension` | `multiLevel: ["activityDefinitions:activityDefinitions:apiName"]` |
|
|
273
|
+
| `cmsDeliveryChannel` (and other CMS types) | `strategy: grouped-by-tag` plus `splitTags` for any wide repeatable tag. |
|
|
274
|
+
| `dashboard` | `multiLevel: ["components:components:title"]` — one file per dashboard widget. |
|
|
274
275
|
|
|
275
276
|
If a metadata type has a single deeply-nested repeatable block, a one-rule `multiLevel` is enough. Reach for the array form only when you have **two or more** distinct nested sections you want addressable on disk.
|
|
276
277
|
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ A Salesforce CLI plugin that **decomposes** large metadata XML files into smalle
|
|
|
25
25
|
- [Decompose Strategies](#decompose-strategies)
|
|
26
26
|
- [Supported Metadata](#supported-metadata)
|
|
27
27
|
- [Manifest-scoped Runs](#manifest-scoped-runs)
|
|
28
|
-
- [Per-Type & Per-Component Overrides](
|
|
28
|
+
- [Per-Type & Per-Component Overrides](CONFIGURATION.md)
|
|
29
29
|
- [Ignore Files](#ignore-files)
|
|
30
30
|
- [Troubleshooting](#troubleshooting)
|
|
31
31
|
- [Built With](#built-with)
|
|
@@ -44,7 +44,7 @@ Complete these steps once per project. After setup, see [Daily Workflow](#daily-
|
|
|
44
44
|
### 1. Requirements
|
|
45
45
|
|
|
46
46
|
- [Salesforce CLI](https://developer.salesforce.com/tools/sfdxcli) (`sf`) installed
|
|
47
|
-
- Node.js
|
|
47
|
+
- Node.js 22.x or later
|
|
48
48
|
- A Salesforce DX project with `sfdx-project.json` and package directories
|
|
49
49
|
|
|
50
50
|
**Supported Platforms**
|
|
@@ -52,7 +52,7 @@ Complete these steps once per project. After setup, see [Daily Workflow](#daily-
|
|
|
52
52
|
sf-decomposer depends on [config-disassembler-node](https://github.com/mcarvin8/config-disassembler-node), which ships prebuilt native binaries as platform-specific optional npm packages — your package manager installs only the one matching your `os` / `cpu` / `libc`:
|
|
53
53
|
|
|
54
54
|
| Platform | Architectures |
|
|
55
|
-
|
|
55
|
+
|-------------|--------------------------------------|
|
|
56
56
|
| **macOS** | x64 (Intel), arm64 (Apple Silicon) |
|
|
57
57
|
| **Linux** | x64 (gnu + musl), arm64 (gnu + musl) |
|
|
58
58
|
| **Windows** | x64, arm64, ia32 |
|
|
@@ -87,7 +87,7 @@ Add `.sfdecomposer.config.json` to your project root. Copy and customize one of
|
|
|
87
87
|
- [Sample with overrides](https://raw.githubusercontent.com/mcarvin8/sf-decomposer/main/examples/.sfdecomposer.config.overrides.json) — vary format/strategy per metadata type or component
|
|
88
88
|
|
|
89
89
|
| Option | Required | Description |
|
|
90
|
-
|
|
90
|
+
|------------------------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
91
91
|
| `metadataSuffixes` | Conditional | Comma-separated metadata suffixes to decompose/recompose. Required unless `manifest` is set; when both are set, run is scoped to the intersection. |
|
|
92
92
|
| `manifest` | Conditional | Path (relative to project root) to a `package.xml` manifest. When set, only listed components are decomposed/recomposed. |
|
|
93
93
|
| `ignorePackageDirectories` | No | Comma-separated package directories to skip. |
|
|
@@ -97,7 +97,7 @@ Add `.sfdecomposer.config.json` to your project root. Copy and customize one of
|
|
|
97
97
|
| `strategy` | No | `unique-id` \| `grouped-by-tag` (default: unique-id). |
|
|
98
98
|
| `decomposeNestedPermissions` | No | With `grouped-by-tag`, set `true` to further decompose permission set and muting permission set object/field permissions. |
|
|
99
99
|
| `updateForceignore` | No | Set `true` to automatically add decomposed file paths to `.forceignore` after each hook-triggered decomposition (default: false). |
|
|
100
|
-
| `overrides` | No | Array of per-type and/or per-component overrides. See [
|
|
100
|
+
| `overrides` | No | Array of per-type and/or per-component overrides. See [CONFIGURATION.md](CONFIGURATION.md). |
|
|
101
101
|
|
|
102
102
|
---
|
|
103
103
|
|
|
@@ -135,7 +135,7 @@ Pass `-x manifest/package.xml` to both `decompose` and `recompose` (and `deploy`
|
|
|
135
135
|
### Commands
|
|
136
136
|
|
|
137
137
|
| Command | Description |
|
|
138
|
-
|
|
138
|
+
|---------------------------|-------------------------------------------------------------------------------------|
|
|
139
139
|
| `sf decomposer decompose` | Decompose metadata in package directories into smaller files. |
|
|
140
140
|
| `sf decomposer recompose` | Recompose decomposed files back into deployment-ready metadata. |
|
|
141
141
|
| `sf decomposer verify` | Round-trip check: decompose + recompose in a temp directory and diff the originals. |
|
|
@@ -258,12 +258,11 @@ Files whose **only** delta is sibling or attribute ordering are reported as info
|
|
|
258
258
|
|
|
259
259
|
### Decompose Strategies
|
|
260
260
|
|
|
261
|
-
|
|
261
|
+
Two primary strategies control how nested XML elements are split on disk. Both round-trip deterministically and can be mixed across types — or even across components of the same type — via the `overrides` array (see [CONFIGURATION.md](CONFIGURATION.md)). When switching strategies for an existing component, pass `--prepurge` (or `prePurge: true`) to remove leftover files from the prior strategy before writing new ones.
|
|
262
262
|
|
|
263
|
-
|
|
264
|
-
- **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.
|
|
263
|
+
#### unique-id (default)
|
|
265
264
|
|
|
266
|
-
|
|
265
|
+
Each nested element gets its own file, named by one or more unique-id fields (or a content hash when no UID is found). Leaf elements stay in a file named like the original XML.
|
|
267
266
|
|
|
268
267
|
```
|
|
269
268
|
permissionsets/
|
|
@@ -289,7 +288,76 @@ permissionsets/
|
|
|
289
288
|
└── APIEnabled.userPermissions-meta.xml
|
|
290
289
|
```
|
|
291
290
|
|
|
292
|
-
**
|
|
291
|
+
**Filename safety.** Two safety nets apply automatically. Neither requires configuration:
|
|
292
|
+
|
|
293
|
+
- **Path-segment sanitization (silent).** Characters illegal or reserved on at least one supported filesystem — path separators (`/`, `\`), Windows-reserved chars (`:`, `*`, `?`, `"`, `<`, `>`, `|`), and ASCII control bytes — are replaced with `_`; trailing `.` and spaces are stripped. Sanitized filenames are byte-stable across platforms.
|
|
294
|
+
- **Sibling-collision fallback (emits `WARN`).** When two or more siblings of the same parent tag would resolve to the same filename (the configured unique-id elements are too narrow, or sanitization folded two distinct values together), every sibling in the colliding group is written to its own per-element SHA-256 shard instead. No row is silently overwritten.
|
|
295
|
+
|
|
296
|
+
If you see a hash-named shard and want to know whether it came from a collision (vs. a missing UID), set `RUST_LOG=warn` and rerun — see [Rust crate logging](#xml-disassemble-output-rust-crate).
|
|
297
|
+
|
|
298
|
+
**Extending with multiLevel.** When a metadata type has deeply-nested repeatable blocks (a block inside a block), add a `multiLevel` override to decompose those inner arrays into their own subdirectories. `bot` and `loyaltyProgramSetup` ship with built-in `multiLevel` defaults applied automatically. See the [admin handbook](https://github.com/mcarvin8/sf-decomposer/blob/main/HANDBOOK.md) for ready-to-paste recipes for Bots, Flexipages, Layouts, Flows, and more.
|
|
299
|
+
|
|
300
|
+
**Type-specific notes (unique-id):**
|
|
301
|
+
|
|
302
|
+
- **Custom Labels (`labels`)** — always forced to `unique-id` (grouped-by-tag would be a no-op since every element shares the same tag). Each label becomes its own file:
|
|
303
|
+
|
|
304
|
+
```
|
|
305
|
+
labels/
|
|
306
|
+
├── CustomLabels.labels-meta.xml ← original file (safe to delete after decompose)
|
|
307
|
+
├── quoteAuto.label-meta.xml ← one file per <labels> entry, named by fullName
|
|
308
|
+
└── quoteManual.label-meta.xml
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
- **Bot (`bot`)** — built-in `multiLevel` default applies two rules automatically: `botDialogs` (outer, keyed by `developerName`) and `botSteps` (inner, keyed by `type`). No config needed to get the canonical layout; override only to change it. See the [admin handbook](https://github.com/mcarvin8/sf-decomposer/blob/main/HANDBOOK.md) for the full layout, step-shape notes, and single-rule variant.
|
|
312
|
+
|
|
313
|
+
```
|
|
314
|
+
bots/
|
|
315
|
+
└── Sample_Chat_Bot/
|
|
316
|
+
├── Sample_Chat_Bot.bot-meta.xml ← bot header (untouched)
|
|
317
|
+
├── v1/
|
|
318
|
+
│ ├── nlpProviders/
|
|
319
|
+
│ │ └── EinsteinAi.nlpProviders-meta.xml
|
|
320
|
+
│ ├── botDialogs/ ← outer rule: one directory per dialog
|
|
321
|
+
│ │ ├── Welcome/
|
|
322
|
+
│ │ │ ├── Welcome.xml ← dialog leaf properties
|
|
323
|
+
│ │ │ └── botSteps/ ← inner rule: one entry per step
|
|
324
|
+
│ │ │ ├── 853b6432/ ← step with nested content → subdir
|
|
325
|
+
│ │ │ │ └── ...
|
|
326
|
+
│ │ │ └── 9d031e75.botSteps-meta.xml ← step with no nested content → leaf file
|
|
327
|
+
│ │ └── ...
|
|
328
|
+
│ ├── .multi_level.json ← required for recompose; do not hand-edit
|
|
329
|
+
│ └── v1.botVersion-meta.xml
|
|
330
|
+
└── ...
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
- **Loyalty Program Setup (`loyaltyProgramSetup`)** — always forced to `unique-id` with a built-in `multiLevel` default that splits `<programProcesses>` into per-process folders containing per-`<parameters>` / per-`<rules>` files. Recompose always removes the decomposed tree (with or without `--postpurge`); rely on version control to inspect it after a deploy.
|
|
334
|
+
|
|
335
|
+
```
|
|
336
|
+
loyaltyProgramSetups/
|
|
337
|
+
└── Cloud_Kicks_Inner_Circle/
|
|
338
|
+
├── Cloud_Kicks_Inner_Circle.loyaltyProgramSetup-meta.xml ← leaf properties (e.g. label)
|
|
339
|
+
├── .key_order.json
|
|
340
|
+
├── .multi_level.json ← required for recompose; do not hand-edit
|
|
341
|
+
└── programProcesses/ ← one folder per process, named by processName
|
|
342
|
+
├── Manual Points Adjustments/
|
|
343
|
+
│ ├── Manual Points Adjustments.xml ← process leaf properties
|
|
344
|
+
│ ├── .key_order.json
|
|
345
|
+
│ ├── parameters/ ← one file per parameter, named by parameterName
|
|
346
|
+
│ │ ├── EA_PerAdjustmentRewardTracking.parameters-meta.xml
|
|
347
|
+
│ │ ├── EventType.parameters-meta.xml
|
|
348
|
+
│ │ └── ...
|
|
349
|
+
│ └── rules/ ← one file per rule, named by ruleName
|
|
350
|
+
│ ├── Bulk Voucher Upload.rules-meta.xml
|
|
351
|
+
│ ├── Finalize.rules-meta.xml
|
|
352
|
+
│ └── Set Up Step.rules-meta.xml
|
|
353
|
+
├── Member Enrollment Process/
|
|
354
|
+
│ └── ... ← same shape per process
|
|
355
|
+
└── ...
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
#### grouped-by-tag
|
|
359
|
+
|
|
360
|
+
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. Best for types with many small repeatable tags where one-file-per-element diffs would be noisy.
|
|
293
361
|
|
|
294
362
|
```
|
|
295
363
|
permissionsets/
|
|
@@ -306,34 +374,9 @@ permissionsets/
|
|
|
306
374
|
└── userPermissions.xml
|
|
307
375
|
```
|
|
308
376
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
Two safety nets apply automatically to every shard filename emitted by the **unique-id** strategy. Neither requires configuration:
|
|
312
|
-
|
|
313
|
-
- **Path-segment sanitization (silent).** Characters illegal or reserved on at least one supported filesystem — path separators (`/`, `\`), Windows-reserved chars (`:`, `*`, `?`, `"`, `<`, `>`, `|`), and ASCII control bytes — are replaced with `_`; trailing `.` and spaces are stripped. Sanitized filenames are byte-stable across platforms.
|
|
314
|
-
- **Sibling-collision fallback (emits `WARN`).** When two or more siblings of the same parent tag would resolve to the same filename (the configured unique-id elements are too narrow, or sanitization folded two distinct values together), every sibling in the colliding group is written to its own per-element SHA-256 shard instead. No row is silently overwritten.
|
|
315
|
-
|
|
316
|
-
If you see a hash-named shard and want to know whether it came from a collision (vs. a missing UID), set `RUST_LOG=warn` and rerun — see [Rust crate logging](#xml-disassemble-output-rust-crate).
|
|
317
|
-
|
|
318
|
-
#### Custom Labels Decomposition
|
|
319
|
-
|
|
320
|
-
Custom labels are always decomposed with `unique-id` (grouped-by-tag would be a no-op since every element shares the same tag). Each label is written to its own file:
|
|
321
|
-
|
|
322
|
-
```
|
|
323
|
-
labels/
|
|
324
|
-
├── CustomLabels.labels-meta.xml ← original file (safe to delete after decompose)
|
|
325
|
-
├── quoteAuto.label-meta.xml ← one file per <labels> entry, named by fullName
|
|
326
|
-
└── quoteManual.label-meta.xml
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
#### Additional Permission Set Decomposition
|
|
330
|
-
|
|
331
|
-
With **grouped-by-tag**, use `--decompose-nested-permissions` (`-p`) to further decompose permission sets and muting permission sets:
|
|
332
|
-
|
|
333
|
-
- Write each `<objectPermissions>` to its own file under `objectPermissions/`.
|
|
334
|
-
- Group `<fieldPermissions>` by object under `fieldPermissions/`.
|
|
377
|
+
**Extending with splitTags.** A `splitTags` override lets specific tags within a `grouped-by-tag` run break out into per-element files (split mode) or sub-grouped files (group mode), while all other tags stay grouped. See [splitTags grammar](CONFIGURATION.md#splittags-grammar).
|
|
335
378
|
|
|
336
|
-
|
|
379
|
+
**Extending with decomposeNestedPermissions.** For `permissionset` and `mutingpermissionset`, add `--decompose-nested-permissions` (`-p`) to further decompose `<objectPermissions>` into per-object files and group `<fieldPermissions>` by object — similar to Salesforce's `decomposePermissionSetBeta2` but with more format and strategy options.
|
|
337
380
|
|
|
338
381
|
```bash
|
|
339
382
|
sf decomposer decompose -m "permissionset" -s "grouped-by-tag" -p
|
|
@@ -357,68 +400,37 @@ permissionsets/
|
|
|
357
400
|
└── Job_Request__c.objectPermissions-meta.xml
|
|
358
401
|
```
|
|
359
402
|
|
|
360
|
-
#### Loyalty Program Setup Decomposition
|
|
361
|
-
|
|
362
|
-
`loyaltyProgramSetup` is always decomposed with `unique-id`, with a built-in `multiLevel` default that splits `<programProcesses>` into per-process folders containing per-`<parameters>` / per-`<rules>` files.
|
|
363
|
-
|
|
364
|
-
> Recompose for `loyaltyProgramSetup` always removes the decomposed tree, with or without `--postpurge`. Rely on version control if you need to inspect it after a deploy.
|
|
365
|
-
|
|
366
|
-
```
|
|
367
|
-
loyaltyProgramSetups/
|
|
368
|
-
└── Cloud_Kicks_Inner_Circle/
|
|
369
|
-
├── Cloud_Kicks_Inner_Circle.loyaltyProgramSetup-meta.xml ← leaf properties (e.g. label)
|
|
370
|
-
├── .key_order.json
|
|
371
|
-
├── .multi_level.json ← required for recompose; do not hand-edit
|
|
372
|
-
└── programProcesses/ ← one folder per process, named by processName
|
|
373
|
-
├── Manual Points Adjustments/
|
|
374
|
-
│ ├── Manual Points Adjustments.xml ← process leaf properties
|
|
375
|
-
│ ├── .key_order.json
|
|
376
|
-
│ ├── parameters/ ← one file per parameter, named by parameterName
|
|
377
|
-
│ │ ├── EA_PerAdjustmentRewardTracking.parameters-meta.xml
|
|
378
|
-
│ │ ├── EventType.parameters-meta.xml
|
|
379
|
-
│ │ └── ...
|
|
380
|
-
│ └── rules/ ← one file per rule, named by ruleName
|
|
381
|
-
│ ├── Bulk Voucher Upload.rules-meta.xml
|
|
382
|
-
│ ├── Finalize.rules-meta.xml
|
|
383
|
-
│ └── Set Up Step.rules-meta.xml
|
|
384
|
-
├── Member Enrollment Process/
|
|
385
|
-
│ └── ... ← same shape per process
|
|
386
|
-
└── ...
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
> **Tip:** This three-level layout (`programProcesses` → `parameters`/`rules`) is the multi-level decomposition pattern. The same pattern powers Bots, Flexipages, and Layouts via opt-in `multiLevel` overrides — see the [admin handbook](https://github.com/mcarvin8/sf-decomposer/blob/main/HANDBOOK.md) for those recipes.
|
|
390
|
-
|
|
391
403
|
---
|
|
392
404
|
|
|
393
405
|
### Supported Metadata
|
|
394
406
|
|
|
395
|
-
All parent metadata types from this plugin's version of **@salesforce/source-deploy-retrieve** (SDR) are supported, except where noted below.
|
|
407
|
+
All parent and child metadata types from this plugin's version of **@salesforce/source-deploy-retrieve** (SDR) are supported, except where noted below. Child types (e.g. `field`, `listView`, `validationRule`) resolve via their parent in the SDR registry and behave like any other type — most are leaf-only and will be skipped non-fatally if their files contain no nested repeatable elements (the Rust disassembler logs a skip at `RUST_LOG=error`, but the CLI does not fail).
|
|
396
408
|
|
|
397
409
|
Use the metadata **suffix** for `-m` / `--metadata-type`, as in [SDR's metadataRegistry.json](https://github.com/forcedotcom/source-deploy-retrieve/blob/main/src/registry/metadataRegistry.json), or infer from the file name: `*.{suffix}-meta.xml`.
|
|
398
410
|
|
|
399
|
-
| Metadata Type | CLI value | Notes
|
|
400
|
-
|
|
401
|
-
| Custom Labels | `labels` | Strategy overridden to `unique-id` if `grouped-by-tag` is provided (grouping labels by tag would be no different from the original file).
|
|
402
|
-
| Workflows | `workflow` |
|
|
403
|
-
| Profiles | `profile` |
|
|
404
|
-
| Permission Sets | `permissionset` | Supports `--decompose-nested-permissions` with grouped-by-tag.
|
|
405
|
-
| Muting Permission Sets | `mutingpermissionset` | Extends permission set metadata type. Supports `--decompose-nested-permissions` with grouped-by-tag.
|
|
406
|
-
| AI Scoring Model Definition | `aiScoringModelDefinition` |
|
|
407
|
-
| Decision Matrix Definition | `decisionMatrixDefinition` |
|
|
408
|
-
| Bot | `bot` |
|
|
409
|
-
| Marketing App Extension | `marketingappextension` |
|
|
410
|
-
| Loyalty Program Setup | `loyaltyProgramSetup` |
|
|
411
|
+
| Metadata Type | CLI value | Notes |
|
|
412
|
+
|-----------------------------|----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
413
|
+
| Custom Labels | `labels` | Strategy overridden to `unique-id` if `grouped-by-tag` is provided (grouping labels by tag would be no different from the original file). |
|
|
414
|
+
| Workflows | `workflow` | |
|
|
415
|
+
| Profiles | `profile` | |
|
|
416
|
+
| Permission Sets | `permissionset` | Supports `--decompose-nested-permissions` with grouped-by-tag. |
|
|
417
|
+
| Muting Permission Sets | `mutingpermissionset` | Extends permission set metadata type. Supports `--decompose-nested-permissions` with grouped-by-tag. |
|
|
418
|
+
| AI Scoring Model Definition | `aiScoringModelDefinition` | |
|
|
419
|
+
| Decision Matrix Definition | `decisionMatrixDefinition` | |
|
|
420
|
+
| Bot | `bot` | Built-in `multiLevel` default splits `botDialogs` (by `developerName`) and `botSteps` (by `type`) automatically. No config required; override only to change the layout. See [Decompose Strategies](#decompose-strategies) and the [admin handbook](https://github.com/mcarvin8/sf-decomposer/blob/main/HANDBOOK.md). |
|
|
421
|
+
| Marketing App Extension | `marketingappextension` | |
|
|
422
|
+
| Loyalty Program Setup | `loyaltyProgramSetup` | Always forced to `unique-id`; `grouped-by-tag` is overridden. Built-in `multiLevel` splits `<programProcesses>` into per-process folders automatically. Recompose always removes the decomposed tree. See [Decompose Strategies](#decompose-strategies). |
|
|
411
423
|
|
|
412
424
|
For a comprehensive breakdown of supported, leaf-only, and unsupported metadata types — including multi-level decomposition patterns, Salesforce native decomposition conflicts, and adapter strategy limitations — see [**METADATA_SUPPORT.md**](./METADATA_SUPPORT.md).
|
|
413
425
|
|
|
414
426
|
#### Exceptions
|
|
415
427
|
|
|
416
428
|
| Situation | Message |
|
|
417
|
-
|
|
429
|
+
|------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
|
|
418
430
|
| `botVersion` used directly | Automatically redirected to `bot` with a warning. No error is thrown. |
|
|
419
431
|
| Custom Objects | `Custom Objects are not supported by this plugin.` |
|
|
420
432
|
| Unsupported SDR strategies (e.g. matchingContentFile, digitalExperience, mixedContent, bundle) | `Metadata types with [matchingContentFile, digitalExperience, mixedContent, bundle] strategies are not supported by this plugin.` |
|
|
421
|
-
|
|
|
433
|
+
| Invalid or unknown suffix | `Metadata type not found for the given suffix: [suffix].` |
|
|
422
434
|
|
|
423
435
|
---
|
|
424
436
|
|
|
@@ -457,207 +469,9 @@ sf project deploy start -x "manifest/package.xml"
|
|
|
457
469
|
|
|
458
470
|
### Per-Type & Per-Component Overrides
|
|
459
471
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
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.
|
|
463
|
-
|
|
464
|
-
```json
|
|
465
|
-
{
|
|
466
|
-
"metadataSuffixes": "labels,workflow,profile,flow,permissionset",
|
|
467
|
-
"ignorePackageDirectories": "force-app,examples",
|
|
468
|
-
"prePurge": true,
|
|
469
|
-
"postPurge": true,
|
|
470
|
-
"decomposedFormat": "xml",
|
|
471
|
-
"strategy": "unique-id",
|
|
472
|
-
"overrides": [
|
|
473
|
-
{ "metadataTypes": ["flow"], "decomposedFormat": "yaml" },
|
|
474
|
-
{
|
|
475
|
-
"metadataTypes": ["permissionset", "mutingpermissionset"],
|
|
476
|
-
"strategy": "grouped-by-tag",
|
|
477
|
-
"decomposeNestedPermissions": true
|
|
478
|
-
},
|
|
479
|
-
{
|
|
480
|
-
"components": ["permissionset:HR_Admin", "permissionset:Big_PermSet"],
|
|
481
|
-
"strategy": "grouped-by-tag",
|
|
482
|
-
"decomposeNestedPermissions": true
|
|
483
|
-
}
|
|
484
|
-
]
|
|
485
|
-
}
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
#### What can be overridden
|
|
489
|
-
|
|
490
|
-
| Field | Notes |
|
|
491
|
-
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
492
|
-
| `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. |
|
|
493
|
-
| `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. |
|
|
494
|
-
| `decomposedFormat` | `xml` \| `json` \| `json5` \| `yaml`. |
|
|
495
|
-
| `strategy` | `unique-id` \| `grouped-by-tag`. Hard rules still win — `labels` and `loyaltyProgramSetup` are always treated as `unique-id`. |
|
|
496
|
-
| `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. |
|
|
497
|
-
| `splitTags` | Custom `splitTags` spec for `grouped-by-tag` strategy. See [splitTags grammar](#splittags-grammar). Ignored when the resolved strategy is not `grouped-by-tag`. |
|
|
498
|
-
| `multiLevel` | One or more `multiLevel` specs for nested-array decomposition. Pass a string, a `string[]`, or a `;`-separated string. See [multiLevel grammar](#multilevel-grammar). When set, replaces the hardcoded `loyaltyProgramSetup` default for the targeted scope. |
|
|
499
|
-
| `uniqueIdElements` | Comma-separated list of XML element names (or compound `+`-joined keys) used to derive stable filenames for `unique-id` decomposition. When set, replaces the built-in per-type registry entry for the targeted scope. See [uniqueIdElements grammar](#uniqueidelements-grammar). |
|
|
500
|
-
| `prePurge` | Per-scope prePurge (decompose). Component-scope `prePurge` only purges the named component's decomposed directory. |
|
|
501
|
-
| `postPurge` | Per-scope postPurge (decompose: remove originals after decomposing). |
|
|
502
|
-
|
|
503
|
-
Run-scope options (`metadataSuffixes`, `manifest`, `ignorePackageDirectories`) are **not** valid inside an override; the plugin will throw if they are present.
|
|
504
|
-
|
|
505
|
-
#### Component key conventions
|
|
506
|
-
|
|
507
|
-
The `<fullName>` part of a component key is the SDR fullName for the component, matching the basename of the decomposed directory:
|
|
508
|
-
|
|
509
|
-
- **Plain types** (e.g. `permissionset`, `flow`, `profile`, `workflow`): use the file stem, e.g. `permissionset:HR_Admin` for `permissionsets/HR_Admin.permissionset-meta.xml`.
|
|
510
|
-
- **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`.
|
|
511
|
-
- **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/`.
|
|
512
|
-
- **`labels`**: there is exactly one labels file per labels directory, so component-scope keys are not meaningful — use the type-scope `metadataTypes: ["labels"]` instead.
|
|
513
|
-
|
|
514
|
-
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.
|
|
515
|
-
|
|
516
|
-
#### Precedence
|
|
517
|
-
|
|
518
|
-
For each component, each option is resolved independently in this order (highest first):
|
|
519
|
-
|
|
520
|
-
1. The component-scope override value (matching `<suffix>:<fullName>` in `components`), if set.
|
|
521
|
-
2. The type-scope override value (matching `<suffix>` in `metadataTypes`), if set.
|
|
522
|
-
3. The run-wide value (CLI flag, hook config top-level field, or built-in default).
|
|
523
|
-
4. Hard plugin rules (e.g. `labels` and `loyaltyProgramSetup` forced to `unique-id`) override all of the above.
|
|
524
|
-
|
|
525
|
-
#### splitTags grammar
|
|
526
|
-
|
|
527
|
-
`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.
|
|
528
|
-
|
|
529
|
-
**Spec:** Comma-separated rules. Each rule has 3 or 4 colon-separated parts:
|
|
530
|
-
|
|
531
|
-
- `<tag>:<mode>:<field>` — read array items from the top-level `<tag>`.
|
|
532
|
-
- `<tag>:<path>:<mode>:<field>` — read array items from the nested `<path>` (defaults to `<tag>`).
|
|
533
|
-
|
|
534
|
-
`<mode>` is one of:
|
|
535
|
-
|
|
536
|
-
- **`split`** — write one file per array item, named after the value of `<field>` on each item.
|
|
537
|
-
- **`group`** — group array items by the value of `<field>`, writing one file per group.
|
|
538
|
-
|
|
539
|
-
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.
|
|
540
|
-
|
|
541
|
-
**Examples:**
|
|
542
|
-
|
|
543
|
-
```json
|
|
544
|
-
"overrides": [
|
|
545
|
-
{
|
|
546
|
-
"metadataTypes": ["permissionset", "mutingpermissionset"],
|
|
547
|
-
"strategy": "grouped-by-tag",
|
|
548
|
-
"splitTags": "objectPermissions:split:object,fieldPermissions:group:field"
|
|
549
|
-
},
|
|
550
|
-
{
|
|
551
|
-
"metadataTypes": ["profile"],
|
|
552
|
-
"strategy": "grouped-by-tag",
|
|
553
|
-
"splitTags": "objectPermissions:split:object,fieldPermissions:group:field,layoutAssignments:group:layout"
|
|
554
|
-
}
|
|
555
|
-
]
|
|
556
|
-
```
|
|
557
|
-
|
|
558
|
-
> **Caveat:** With `mode: split`, the chosen `<field>` must produce a unique value across every array item — otherwise two items map to the same filename. If items can share a field value, use `mode: group` instead.
|
|
559
|
-
|
|
560
|
-
See the [admin handbook](https://github.com/mcarvin8/sf-decomposer/blob/main/HANDBOOK.md) for additional `splitTags` and `multiLevel` recipes (flows, workflows, layouts, flexipages, bots).
|
|
561
|
-
|
|
562
|
-
#### multiLevel grammar
|
|
563
|
-
|
|
564
|
-
`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 → ...`, or a Bot's `botVersion → botDialogs → botSteps`). 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.
|
|
565
|
-
|
|
566
|
-
**Spec:** Each rule has exactly 3 colon-separated parts (the third part is itself a comma-separated list):
|
|
567
|
-
|
|
568
|
-
```
|
|
569
|
-
<file_pattern>:<root_to_strip>:<unique_id_elements>
|
|
570
|
-
```
|
|
571
|
-
|
|
572
|
-
- **`<file_pattern>`** — basename pattern that selects which inner-level files get the second decomposition pass (e.g. `programProcesses`).
|
|
573
|
-
- **`<root_to_strip>`** — XML root tag to strip from each matched file before splitting.
|
|
574
|
-
- **`<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.
|
|
575
|
-
|
|
576
|
-
A scope may target several nested sections by passing **multiple rules**. Three input shapes are supported:
|
|
577
|
-
|
|
578
|
-
- a single rule string (legacy, unchanged behaviour);
|
|
579
|
-
- a JSON `string[]` of rules (preferred — clearest intent, easiest to diff);
|
|
580
|
-
- a single `;`-separated string of rules (compact form, also accepted).
|
|
581
|
-
|
|
582
|
-
Within one scope, the `(file_pattern, root_to_strip)` pair must be unique across rules. The plugin validates the grammar at config-load time; deeper checks (whether a file pattern matches anything, whether the unique-id elements actually appear on the inner XML) are surfaced by the underlying disassembler crate at runtime.
|
|
583
|
-
|
|
584
|
-
```json
|
|
585
|
-
"overrides": [
|
|
586
|
-
{
|
|
587
|
-
"metadataTypes": ["dashboard"],
|
|
588
|
-
"multiLevel": "components:components:title"
|
|
589
|
-
},
|
|
590
|
-
{
|
|
591
|
-
"metadataTypes": ["layout"],
|
|
592
|
-
"multiLevel": [
|
|
593
|
-
"layoutSections:layoutSections:label",
|
|
594
|
-
"layoutItems:layoutItems:field,customLink,emptySpace"
|
|
595
|
-
]
|
|
596
|
-
}
|
|
597
|
-
]
|
|
598
|
-
```
|
|
599
|
-
|
|
600
|
-
> **Built-in defaults.** `bot` and `loyaltyProgramSetup` ship with built-in `multiLevel` rules, so you do not need an override to get the canonical layout — supply your own only to replace the default. Full registry: [`src/metadata/multiLevelDefaults.ts`](https://github.com/mcarvin8/sf-decomposer/blob/main/src/metadata/multiLevelDefaults.ts).
|
|
601
|
-
>
|
|
602
|
-
> **Pass all rules at once.** Sequential single-rule decomposes rewrite `.multi_level.json` and only the last rule survives — bundle every rule for a given component into one override. Use [`sf decomposer verify`](#sf-decomposer-verify) to confirm a new config round-trips before committing it.
|
|
603
|
-
|
|
604
|
-
#### uniqueIdElements grammar
|
|
605
|
-
|
|
606
|
-
`uniqueIdElements` lets you specify which XML element names the disassembler crate uses to derive stable, human-readable filenames during `unique-id` decomposition. The plugin ships with a built-in registry covering the most common metadata types ([`src/metadata/uniqueIdElements.ts`](https://github.com/mcarvin8/sf-decomposer/blob/main/src/metadata/uniqueIdElements.ts)); use this override when a type is missing from the registry or when the built-in selection produces collisions for your org's data.
|
|
607
|
-
|
|
608
|
-
**When to use:**
|
|
609
|
-
|
|
610
|
-
- A metadata type released after the last plugin update is not in the built-in registry and produces SHA-256 hash filenames (`abc1234.mytype-meta.xml`) instead of readable ones.
|
|
611
|
-
- You see `RUST_LOG=warn` collision warnings for an existing type and want to add a tiebreaker compound key without waiting for a plugin release.
|
|
612
|
-
- You want to replace the built-in element list for a specific type or component with a narrower or wider set.
|
|
613
|
-
|
|
614
|
-
**Spec:** Comma-separated list of element names. Each entry is either a simple name or a compound key whose fields are joined by `+`. The disassembler tries each entry in order; the first one that resolves to a non-empty, unique value within the parent element wins. The global defaults `fullName` and `name` are always prepended regardless — you do not need to include them.
|
|
615
|
-
|
|
616
|
-
```
|
|
617
|
-
<element>[+<element>...][,<element>[+<element>...]...]
|
|
618
|
-
```
|
|
619
|
-
|
|
620
|
-
**Error behaviour:**
|
|
621
|
-
|
|
622
|
-
- An empty string or an entry with empty comma slots is rejected at **config-load time** — the command fails immediately before any decomposition starts.
|
|
623
|
-
- Element names that pass format validation but do not exist in the XML are silently ignored by the disassembler crate; it falls back to SHA-256 hash filenames for the affected elements (the same behaviour as today when no registry entry matches). The plugin does not throw an error and continues decomposing all remaining files.
|
|
624
|
-
|
|
625
|
-
**Examples:**
|
|
626
|
-
|
|
627
|
-
```json
|
|
628
|
-
"overrides": [
|
|
629
|
-
{
|
|
630
|
-
"metadataTypes": ["myNewSalesforceType"],
|
|
631
|
-
"uniqueIdElements": "developerName"
|
|
632
|
-
},
|
|
633
|
-
{
|
|
634
|
-
"metadataTypes": ["serviceChannel"],
|
|
635
|
-
"uniqueIdElements": "type+value,value"
|
|
636
|
-
},
|
|
637
|
-
{
|
|
638
|
-
"components": ["app:My_App"],
|
|
639
|
-
"uniqueIdElements": "actionName+pageOrSobjectType+formFactor+profile+recordType,actionName+pageOrSobjectType+formFactor+profile,actionName+pageOrSobjectType+formFactor+recordType,actionName+pageOrSobjectType+formFactor"
|
|
640
|
-
}
|
|
641
|
-
]
|
|
642
|
-
```
|
|
643
|
-
|
|
644
|
-
> **Tip:** If you resolve a collision by adding a compound key and it works, consider opening an issue or PR to add it to the built-in registry so other orgs benefit automatically.
|
|
645
|
-
|
|
646
|
-
#### Opting in from the CLI
|
|
647
|
-
|
|
648
|
-
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`):
|
|
649
|
-
|
|
650
|
-
```bash
|
|
651
|
-
sf decomposer decompose -m "flow" -m "permissionset" -c
|
|
652
|
-
```
|
|
653
|
-
|
|
654
|
-
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.
|
|
655
|
-
|
|
656
|
-
If `--config` is set but `.sfdecomposer.config.json` is missing from the repo root, the command fails with a clear error.
|
|
657
|
-
|
|
658
|
-
`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.
|
|
472
|
+
The optional `overrides` array in `.sfdecomposer.config.json` lets you vary format, strategy, and decomposition options per metadata suffix or per individual component without splitting the run into multiple invocations.
|
|
659
473
|
|
|
660
|
-
|
|
474
|
+
See [CONFIGURATION.md](CONFIGURATION.md) for the full reference: override fields, component key conventions, precedence rules, and grammar for `splitTags`, `multiLevel`, and `uniqueIdElements`.
|
|
661
475
|
|
|
662
476
|
---
|
|
663
477
|
|
|
@@ -706,7 +520,7 @@ For example, if you attempt to decompose Custom Labels but none of your package
|
|
|
706
520
|
The underlying Rust crate logs through [env_logger](https://docs.rs/env_logger). Set `RUST_LOG` to opt into more verbosity:
|
|
707
521
|
|
|
708
522
|
| Level | What it covers |
|
|
709
|
-
|
|
523
|
+
|------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
710
524
|
| `RUST_LOG=error` | Default. Parse errors and skipped files (leaf-only XML — primitives only, nothing to decompose). |
|
|
711
525
|
| `RUST_LOG=warn` | Adds [sibling-collision fallback](#filename-safety-unique-id) signals — one line per colliding group (parent tag, collided id, sibling count). **Recommended in CI** when shipping overrides. |
|
|
712
526
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
|
|
3
2
|
import { Messages } from '@salesforce/core';
|
|
4
|
-
import {
|
|
3
|
+
import { Flags, SfCommand } from '@salesforce/sf-plugins-core';
|
|
5
4
|
import { decomposeMetadataTypes } from '../../core/decomposeMetadataTypes.js';
|
|
6
5
|
import { loadOverridesFromConfig, resolveDefaultConfigPath } from '../../helpers/configOverrides.js';
|
|
6
|
+
import { DECOMPOSED_FILE_TYPES, DECOMPOSED_STRATEGIES } from '../../helpers/constants.js';
|
|
7
7
|
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
8
8
|
const messages = Messages.loadMessages('sf-decomposer', 'decomposer.decompose');
|
|
9
9
|
export default class DecomposerDecompose extends SfCommand {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decompose.js","sourceRoot":"","sources":["../../../src/commands/decomposer/decompose.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"decompose.js","sourceRoot":"","sources":["../../../src/commands/decomposer/decompose.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
|
|
3
2
|
import { Messages } from '@salesforce/core';
|
|
3
|
+
import { Flags, SfCommand } from '@salesforce/sf-plugins-core';
|
|
4
4
|
import { recomposeMetadataTypes } from '../../core/recomposeMetadataTypes.js';
|
|
5
5
|
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
6
6
|
const messages = Messages.loadMessages('sf-decomposer', 'decomposer.recompose');
|