Table of Contents
Break down large Salesforce metadata files (XML) into smaller, more manageable files (XML/JSON/YAML/TOML/JSON5/INI) for version control and then recreate deployment-compatible files.
- Install plugin using
sf
sf plugins install sf-decomposer@x.y.z
-
Retrieve metadata into your Salesforce DX project
-
Decompose the metadata type(s)
sf decomposer decompose -m "flow" -m "labels" --postpurge
- Add decomposed files to
.forceignore
This is REQUIRED to avoid errors when running
sf
commands
-
Stage decomposed files in version control
-
Recompose the metadata type(s) before deployment
sf decomposer recompose -m "flow" -m "labels"
- Deploy recomposed metadata
Salesforce's built-in decomposition has limitations. sf-decomposer
offers more control, flexibility, and versioning benefits for Admins and Developers.
- Supports More Metadata – Works with most Metadata API types, unlike Salesforce’s limited decomposition.
- Selective Decomposition – Decompose only what you need, avoiding Salesforce’s all-or-nothing approach.
- Multiple Decomposition Strategies – Choose between:
unique-id
(default): disassembles each nested element into its own uniquely named file based on XML content or hash.grouped-by-tag
: groups all nested elements by tag into a single file per tag (e.g., all<fieldPermissions>
in a permission set intofieldPermissions.xml
).- Additionally opt into further decomposition on permisison sets by using the
grouped-by-tag
strategy with the--decompose-nested-permissions
flag.
- Additionally opt into further decomposition on permisison sets by using the
- Both strategies decompose leaf elements into the same file named after the original XML.
- Fully Decomposes Metadata – Allow complete decomposition for types that Salesforce only partially decomposes (e.g.,
decomposePermissionSetBeta2
). - Consistent Sorting – Keeps elements in a predictable order to reduce unnecessary version control noise.
DISCLAIMER: If you use "toml" or "ini" format for decomposed files, the element sorting will vary compared to the other formats
- Multiple Output Formats – Supports XML, JSON, JSON5, TOML, INI, and YAML for greater flexibility.
- CI/CD Integration – Hooks enable seamless decomposition and recomposition in automated workflows.
- Improved Version Control – Smaller, structured files make pull requests easier to review and reduce merge conflicts.
- Better Peer Reviews – More readable diffs for large metadata in GitHub and other CI/CD platforms.
- Safer Deployments – Ensures only intended changes are deployed, improving release quality.
The sf-decomposer
supports 2 commands:
sf decomposer decompose
sf decomposer recompose
Decomposes the original metadata files in all local package directories into smaller files for version control.
USAGE
$ sf decomposer decompose -m <value> -f <value> -i <value> -s <value> [--prepurge --postpurge --debug -p --json]
FLAGS
-m, --metadata-type=<value> The metadata suffix to process, such as 'flow', 'labels', etc.
Can be declared multiple times.
-f, --format=<value> The file type for the decomposed files.
Options: ['xml', 'yaml', 'json', 'toml', 'ini', 'json5']
[default: 'xml']
-i, --ignore-package-directory=<value> Package directory to ignore.
Should be as they appear in the "sfdx-project.json".
Can be declared multiple times.
-s, --strategy=<value> The decompose strategy to use.
Options: ['unique-id', 'grouped-by-tag']
[default: 'unique-id']
--prepurge Purgd directories of pre-existing decomposed files.
[default: false]
--postpurge Purge the original files after decomposing them.
[default: false]
--debug Log debugging results to a text file (disassemble.log).
[default: false]
-p, --decompose-nested-permissions If strategy is "grouped-by-tag", opt into further decomposition
on object and field permissions on permission sets.
GLOBAL FLAGS
--json Format output as json.
EXAMPLES
Decompose all flows in XML format:
$ sf decomposer decompose -m "flow" -f "xml" --prepurge --postpurge --debug
Decompose all flows and custom labels in YAML format
$ sf decomposer decompose -m "flow" -m "labels" -f "yaml" --prepurge --postpurge --debug
Decompose flows except for those in the "force-app" package directory.
$ sf decomposer decompose -m "flow" -i "force-app"
Recompose decomposed files into deployment-compatible files.
USAGE
$ sf decomposer recompose -m <value> -i <value> [--postpurge --debug --json]
FLAGS
-m, --metadata-type=<value> The metadata suffix to process, such as 'flow', 'labels', etc.
Can be declared multiple times.
-i, --ignore-package-directory=<value> Package directory to ignore.
Should be as they appear in the "sfdx-project.json".
Can be declared multiple times.
--postpurge Purge the decomposed files after recomposing them.
[default: false]
--debug Log debugging results to a text file (disassemble.log).
[default: false]
GLOBAL FLAGS
--json Format output as json.
EXAMPLES
Recompose all flows:
$ sf decomposer recompose -m "flow" --postpurge --debug
Recompose flows except for those in the "force-app" package directory.
$ sf decomposer recompose -m "flow" -i "force-app"
Ensure you do not MIX strategies on the same metadata type. If you have previously decomposed metadata with a past verson of
sf-decomposer
, which uses the unique-id strategy, and would like to switch over to the grouped-by-tag strategy, you will need to supply the decompose command with the--prepurge
flag and-s "grouped-by-tag"
flag to re-create decomposed files with the new strategy.
You can decompose all metadata, except for custom labels, via 1 of 2 strategies:
- unique-id (default): Each nested element is written to its own file. File names are derived from specified unique ID elements or hashed content.
- grouped-by-tag: All nested elements with the same tag, e.g.
<fieldPermissions>
, are grouped into a single file, named after the tag (e.g.,fieldPermissions.xml
).
Leaf elements (like <userLicense>Salesforce</userLicense>
) are always grouped in a file named after the original source XML in both strategies.
Decomposed Permission Set Example - Unique ID Strategy
Format | Example |
---|---|
XML | ![]() |
YAML | ![]() |
JSON | ![]() |
JSON5 | ![]() |
TOML | ![]() |
INI | ![]() |
Decomposed Permission Set Example - Grouped by Tag Strategy
Format | Example |
---|---|
XML | ![]() |
YAML | ![]() |
JSON | ![]() |
JSON5 | ![]() |
TOML | ![]() |
INI | ![]() |
Custom labels can only be decomposed via the unique-id
strategy. If you attempt to decompose custom labels with the grouped-by-tag
strategy, it will warn and skip decomposing custom labels.
Custom labels decomposed under the unique-id
strategy will look like such, each label will have its own file:
When using the grouped-by-tag
strategy, you can opt into additional decomposition on <objectPermissions>
and <fieldPermissions>
on permission sets by supplying the --decompose-nested-permissions
/-p
flag.
When you run sf decomposer decompose -m "permissionset" -s "grouped-by-tag" -p
, it will decompose all <objectPermissions>
into their own files in a sub-directory, i.e. permissionsets\HR_Admin\objectPermissions\Account.objectPermissions-meta.xml
and decompose <fieldPermisisons>
into separate files in a sub-directory for each Object, i.e. permissionsets\HR_Admin\fieldPermissions\Account.fieldPermissions.xml
. This is similar to the decomposePermissionSetBeta2
behavior provided natively by Salesforce.
All parent metadata types imported from this plugin's version of @salesforce/source-deploy-retrieve
(SDR) toolkit are supported except for certain types.
The --metadata-type
/-m
flag should be the metadata's suffix
value as listed in the metadataRegistry.json. You can also infer the suffix by looking at the original XML file-name, i.e. *.{suffix}-meta.xml
.
Here are some examples:
Metadata Type | CLI Option |
---|---|
Custom Labels | --metadata-type "labels" |
Workflows | --metadata-type "workflow" |
Profiles | --metadata-type "profile" |
Permission Sets | --metadata-type "permissionset" |
AI Scoring Model Definition | --metadata-type "aiScoringModelDefinition" |
Decision Matrix Definition | --metadata-type "decisionMatrixDefinition" |
Bot | --metadata-type "bot" |
Marketing App Extension | --metadata-type "marketingappextension" |
Scenario | Message |
---|---|
botVersion is blocked from being run directly |
Error (1): botVersion suffix should not be used. Please use bot to decompose/recompose bot and bot version files. |
Custom Objects not supported | Error (1): Custom Objects are not supported by this plugin. |
Unsupported SDR adapter strategies (e.g., matchingContentFile , digitalExperience , mixedContent , bundle ) |
Error (1): Metadata types with [matchingContentFile, digitalExperience, mixedContent, bundle] strategies are not supported by this plugin. |
Children metadata types (e.g., custom fields) and invalid suffixes | Error (1): Metadata type not found for the given suffix: field. |
sf-decomposer
searches the current working directory for the sfdx-project.json
, and if it's not found in the current working directory, it will search upwards for it until it hits your root drive. If the sfdx-project.json
file isn't found, the plugin will fail with:
Error (1): sfdx-project.json not found in any parent directory.
The xml-disassembler
package will create a log file, disassemble.log
, at all times. By default, the log will only contain XML decomposing/recomposing errors. XML decomposing/recomposing errors do not cause the Salesforce CLI to fail. The CLI will proceed to decompose/recompose all remaining metadata.
The Salesforce CLI will print XML errors as warnings in the terminal:
Warning: C:\Users\matth\Documents\sf-decomposer\test\baselines\flows\Get_Info\actionCalls\Get_Info.actionCalls-meta.xml was unabled to be parsed and will not be processed. Confirm formatting and try again.
To add debugging to the log, provide the --debug
flag to the decompose or recompose command.
[2024-03-30T14:28:37.959] [DEBUG] default - Created disassembled file: mock\no-nested-elements\HR_Admin\HR_Admin.permissionset-meta.xml
If a file only contains leaf elements, the decomposer has nothing to decompose so it will print this warning and skip processing the file:
Warning: The XML file force-app\main\default\permissionsets\view_of_projects_tab_on_opportunity.permissionset-meta.xml only has leaf elements. This file will not be disassembled.
Custom labels can only be decomposed via the unique-id
strategy. If the other one is provided, it will print this warning and skip to the next metadata entry.
Warning: You cannot decompose custom labels using the grouped-by-tag strategy. Please switch strategies and try again for labels.
NOTE: In order to avoid errors when running
sf
commands, you must configure your.forceignore
file to have the Salesforce CLI ignore the decomposed files. See Ignore Files.
sf-decomposer
supports automatic decomposition and recomposition by defining a .sfdecomposer.config.json
file in your project root.
You can copy and update the sample .sfdecomposer.config.json.
Configuration Option | Required | Description |
---|---|---|
metadataSuffixes |
Yes | Comma-separated string of metadata suffixes to decompose and recompose based on the CLI command. |
ignorePackageDirectories |
No | Comma-separated string of package directories to ignore. |
prePurge |
No | true or false . If true , deletes existing decomposed files before decomposing. Defaults to false . |
postPurge |
No | true or false . If true , deletes the retrieval file after decomposing or deletes decomposed files after recomposing. Defaults to false . |
decomposedFormat |
No | Format of decomposed files: xml , json , json5 , toml , ini , or yaml . Defaults to xml . |
strategy |
No | Strategy for decomposing the files: unique-id or grouped-by-tag . Defaults to unique-id . |
decomposeNestedPermissions |
No | If strategy is grouped-by-tag and this is set to true , decompose permission sets further by object and field permissions. |
If .sfdecomposer.config.json
is found, the hooks will run:
- the decompose command after a
sf project retrieve start
command completes successfully - the recompose command before a
sf project deploy [start/validate]
command starts
The Salesforce CLI must ignore the decomposed files and allow the recomposed files.
You can use the sample .forceignore. Update the decomposed file extensions based on what format you're using (.xml
, .json
, .json5
, .toml
, .ini
, or .yaml
).
Optionally, you can create a .sfdecomposerignore
file in the root of your Salesforce DX project to ignore specific XMLs when decomposing. The .sfdecomposerignore
file should follow .gitignore spec 2.22.1.
When you run sf decomposer decompose --debug
and it processes a file that matches an entry in .sfdecomposerignore
, a warning will be printed to the disassemble.log
:
[2024-05-22T09:32:12.078] [WARN] default - File ignored by .sfdecomposerignore: C:\Users\matth\Documents\sf-decomposer\test\baselines\bots\Assessment_Bot\v1.botVersion-meta.xml
.sfdecomposerignore
is not read when recomposing metadata.
Optionally, git can ignore the recomposed files so you don't stage those in your repositories. You can also have git ignore the disassemble.log
created by the xml-disassembler
package.
You can use the sample .gitignore.
If you encounter any bugs or would like to request features, please create an issue.
xml-disassembler
- Disassembles XML files into smaller files and reassembles the XMLfs-extra
- Node.js: extra methods for the fs object like copy(), remove(), mkdirs()@salesforce/source-deploy-retrieve
- JavaScript toolkit for working with Salesforce metadata
Contributions are welcome! See Contributing.
This project is licensed under the MIT license. Please see the LICENSE file for details.