@simplysf/simply-document 0.2.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/README.md +253 -0
- package/lib/commands/simply/document/diff.d.ts +25 -0
- package/lib/commands/simply/document/diff.js +232 -0
- package/lib/commands/simply/document/diff.js.map +1 -0
- package/lib/commands/simply/document/generate.d.ts +25 -0
- package/lib/commands/simply/document/generate.js +466 -0
- package/lib/commands/simply/document/generate.js.map +1 -0
- package/lib/common/changeReportTemplate.d.ts +22 -0
- package/lib/common/changeReportTemplate.js +269 -0
- package/lib/common/changeReportTemplate.js.map +1 -0
- package/lib/common/technicalDesignDocumentTemplate.d.ts +211 -0
- package/lib/common/technicalDesignDocumentTemplate.js +1254 -0
- package/lib/common/technicalDesignDocumentTemplate.js.map +1 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +17 -0
- package/lib/index.js.map +1 -0
- package/messages/simply.document.diff.md +63 -0
- package/messages/simply.document.generate.md +61 -0
- package/package.json +210 -0
package/README.md
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# @simplysf/simply-document
|
|
2
|
+
|
|
3
|
+
[](https://npmjs.com/@simplysf/simply-document) [](https://npmjs.com/@simplysf/simply-document) [](https://raw.githubusercontent.com/SimplySF/simply/main/LICENSE.txt)
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
sf plugins install @simplysf/simply-document
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Issues
|
|
12
|
+
|
|
13
|
+
Please report any issues at https://github.com/SimplySF/simply-node/issues
|
|
14
|
+
|
|
15
|
+
## Contributing
|
|
16
|
+
|
|
17
|
+
This package is part of the [`@simplysf/simply`](https://github.com/SimplySF/simply-node) monorepo. See the repo's [CONTRIBUTING.md](https://github.com/SimplySF/simply-node/blob/main/CONTRIBUTING.md) for the repo structure, how to set up and build the project, our commit conventions, and how to submit a pull request. Please also read our [Code of Conduct](https://github.com/SimplySF/simply-node/blob/main/CODE_OF_CONDUCT.md).
|
|
18
|
+
|
|
19
|
+
## Commands
|
|
20
|
+
|
|
21
|
+
<!-- commands -->
|
|
22
|
+
|
|
23
|
+
- [`sf simply document diff`](#sf-simply-document-diff)
|
|
24
|
+
- [`sf simply document generate`](#sf-simply-document-generate)
|
|
25
|
+
|
|
26
|
+
## `sf simply document diff`
|
|
27
|
+
|
|
28
|
+
Generate a change report between two git refs.
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
USAGE
|
|
32
|
+
$ sf simply document diff --from-tag <value> --to-tag <value> [--json] [--flags-dir <value>] [--output-file <value>]
|
|
33
|
+
[--template-file <value>] [--output-format html]
|
|
34
|
+
|
|
35
|
+
FLAGS
|
|
36
|
+
--from-tag=<value> (required) The starting git ref for the diff report.
|
|
37
|
+
--output-file=<value> Path to write the generated report to.
|
|
38
|
+
--output-format=<option> [default: html] Output format to render the report in.
|
|
39
|
+
<options: html>
|
|
40
|
+
--template-file=<value> Path to a custom Handlebars template to render instead of the built-in one.
|
|
41
|
+
--to-tag=<value> (required) The ending git ref for the diff report.
|
|
42
|
+
|
|
43
|
+
GLOBAL FLAGS
|
|
44
|
+
--flags-dir=<value> Import flag values from a directory.
|
|
45
|
+
--json Format output as json.
|
|
46
|
+
|
|
47
|
+
DESCRIPTION
|
|
48
|
+
Generate a change report between two git refs.
|
|
49
|
+
|
|
50
|
+
Runs `git diff --name-status` between two git refs (tags, branches, or commits) in the current working directory,
|
|
51
|
+
groups the changed files by Salesforce metadata component type, and renders a Confluence-storage-format change report
|
|
52
|
+
suitable for pasting into a release or change-management page.
|
|
53
|
+
|
|
54
|
+
EXAMPLES
|
|
55
|
+
$ sf simply document diff --from-tag v1.0.0 --to-tag v1.1.0
|
|
56
|
+
|
|
57
|
+
$ sf simply document diff --from-tag v1.0.0 --to-tag v1.1.0 --output-file change-report.html
|
|
58
|
+
|
|
59
|
+
$ sf simply document diff --from-tag v1.0.0 --to-tag v1.1.0 --template-file my-change-report.hbs
|
|
60
|
+
|
|
61
|
+
FLAG DESCRIPTIONS
|
|
62
|
+
--output-file=<value> Path to write the generated report to.
|
|
63
|
+
|
|
64
|
+
When specified, the generated report is written to this path instead of being printed to the terminal.
|
|
65
|
+
|
|
66
|
+
--output-format=html Output format to render the report in.
|
|
67
|
+
|
|
68
|
+
Currently only `html` is supported. Reserved for future formats (e.g. Markdown).
|
|
69
|
+
|
|
70
|
+
--template-file=<value> Path to a custom Handlebars template to render instead of the built-in one.
|
|
71
|
+
|
|
72
|
+
When specified, this template is rendered with the same data the built-in report template receives, and can reuse
|
|
73
|
+
the built-in `changeTable` partial. See this package's README "Custom Templates" section for the data shape and
|
|
74
|
+
available partials.
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
_See code: [lib/commands/simply/document/diff.js](https://github.com/SimplySF/simply-node/blob/@simplysf/simply-document@0.3.1/packages/simply-document/lib/commands/simply/document/diff.js)_
|
|
78
|
+
|
|
79
|
+
## `sf simply document generate`
|
|
80
|
+
|
|
81
|
+
Generate a technical design document for a Salesforce project.
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
USAGE
|
|
85
|
+
$ sf simply document generate -d <value> --output-file <value> [--json] [--flags-dir <value>] [--template-file <value>]
|
|
86
|
+
[--output-format html]
|
|
87
|
+
|
|
88
|
+
FLAGS
|
|
89
|
+
-d, --directory=<value> (required) Salesforce project source directory to scan.
|
|
90
|
+
--output-file=<value> (required) Path to write the generated document to.
|
|
91
|
+
--output-format=<option> [default: html] Output format to render the document in.
|
|
92
|
+
<options: html>
|
|
93
|
+
--template-file=<value> Path to a custom Handlebars template to render instead of the built-in one.
|
|
94
|
+
|
|
95
|
+
GLOBAL FLAGS
|
|
96
|
+
--flags-dir=<value> Import flag values from a directory.
|
|
97
|
+
--json Format output as json.
|
|
98
|
+
|
|
99
|
+
DESCRIPTION
|
|
100
|
+
Generate a technical design document for a Salesforce project.
|
|
101
|
+
|
|
102
|
+
Scans a Salesforce DX project's source directory for metadata (objects, fields, Apex, Lightning components, flows,
|
|
103
|
+
permissions, and more) and renders a Confluence-storage-format technical design document covering the data model,
|
|
104
|
+
security model, groups/queues/permissions, solution inventory, and custom code inventory.
|
|
105
|
+
|
|
106
|
+
EXAMPLES
|
|
107
|
+
$ sf simply document generate --directory force-app --output-file technical-design-document.html
|
|
108
|
+
|
|
109
|
+
$ sf simply document generate --directory force-app --output-file technical-design-document.html --template-file my-tdd-template.hbs
|
|
110
|
+
|
|
111
|
+
FLAG DESCRIPTIONS
|
|
112
|
+
--output-format=html Output format to render the document in.
|
|
113
|
+
|
|
114
|
+
Currently only `html` is supported. Reserved for future formats (e.g. Markdown).
|
|
115
|
+
|
|
116
|
+
--template-file=<value> Path to a custom Handlebars template to render instead of the built-in one.
|
|
117
|
+
|
|
118
|
+
When specified, this template is rendered with the same scanned project data the built-in technical design document
|
|
119
|
+
template receives, and can reuse the built-in `loud` helper. See this package's README "Custom Templates" section
|
|
120
|
+
for the data shape.
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
_See code: [lib/commands/simply/document/generate.js](https://github.com/SimplySF/simply-node/blob/@simplysf/simply-document@0.3.1/packages/simply-document/lib/commands/simply/document/generate.js)_
|
|
124
|
+
<!-- commandsstop -->
|
|
125
|
+
|
|
126
|
+
## Custom Templates
|
|
127
|
+
|
|
128
|
+
Both commands render a built-in [Handlebars](https://handlebarsjs.com/) template by default. Pass `--template-file` to render your own template instead — it's compiled against the exact same Handlebars instance as the built-in template, so it can use any partial or helper the built-in template uses, and receives the exact same data.
|
|
129
|
+
|
|
130
|
+
A template file is just Handlebars source (typically saved with an `.hbs` extension, though the extension itself doesn't matter — the file is read as plain text). Nothing is auto-escaped as Confluence storage format or any other target; you control the entire output, so a custom template can produce plain HTML, Markdown, or anything else Handlebars can produce as text.
|
|
131
|
+
|
|
132
|
+
### `sf simply document diff --template-file`
|
|
133
|
+
|
|
134
|
+
The data passed to the template is a plain object keyed by **component type**, where each value is an array of changed-component records. A key is present only if at least one changed file mapped to that component type — check for a key's existence with `{{#if someKey}}` before iterating it, the same way the built-in template does.
|
|
135
|
+
|
|
136
|
+
Each entry in a component type's array has this shape:
|
|
137
|
+
|
|
138
|
+
| Field | Type | Description |
|
|
139
|
+
| ------------------- | ------ | ---------------------------------------------------------------------------------- |
|
|
140
|
+
| `componentName` | string | The component's name (e.g. `MyClass`, `MyObject__c`). |
|
|
141
|
+
| `componentType` | string | The component type key this entry is grouped under (same as its parent key). |
|
|
142
|
+
| `changeType` | string | One of `Added`, `Modified`, `Renamed`, `Deleted`, `Copied`. |
|
|
143
|
+
| `changeDescription` | string | `Renamed from <oldPath>` / `Copied from <oldPath>` for renames/copies, else empty. |
|
|
144
|
+
| `path` | string | The changed file's path, as reported by `git diff`. |
|
|
145
|
+
|
|
146
|
+
The possible top-level keys are:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
standardObjects, customObjects, customSettings, customMetadataTypes, customMetadata, platformEvents,
|
|
150
|
+
apexClasses, apexTriggers, visualforcePages, lightningComponents, auraComponents, flows, flexipages,
|
|
151
|
+
approvalProcesses, customApplications, customLabels, staticResources, dashboards, reports, emailTemplates,
|
|
152
|
+
digitalExperienceBundles, experienceBundles, groups, queues, permissionSets, permissionSetGroups, sharingRules
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
A `changeTable` partial is pre-registered and available to your template — pass it any one of the arrays above to render the same table the built-in template uses:
|
|
156
|
+
|
|
157
|
+
```handlebars
|
|
158
|
+
<h1>My Custom Change Report</h1>
|
|
159
|
+
|
|
160
|
+
<h2>Apex Classes</h2>
|
|
161
|
+
{{#if apexClasses}}
|
|
162
|
+
{{> changeTable apexClasses}}
|
|
163
|
+
{{else}}
|
|
164
|
+
<p>No Apex class changes.</p>
|
|
165
|
+
{{/if}}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### `sf simply document generate --template-file`
|
|
169
|
+
|
|
170
|
+
The data passed to the template is a single object with one array-valued field per metadata category (every field is always present, though it may be empty). Every array item at minimum has a `name` field (or, for a few types noted below, an equivalent identifying field); most other fields are optional and only populated when the underlying metadata has a value.
|
|
171
|
+
|
|
172
|
+
| Field | Item type |
|
|
173
|
+
| -------------------------- | -------------------------------------------------------------------- |
|
|
174
|
+
| `standardObjects` | [Object](#object-item) |
|
|
175
|
+
| `customObjects` | [Object](#object-item) |
|
|
176
|
+
| `customSettings` | [Object](#object-item) |
|
|
177
|
+
| `customMetadataTypes` | [Object](#object-item) |
|
|
178
|
+
| `platformEvents` | [Object](#object-item) |
|
|
179
|
+
| `customMetadata` | `{ name, label? }` |
|
|
180
|
+
| `apexClasses` | `{ name, status?, apiVersion? }` |
|
|
181
|
+
| `apexTriggers` | `{ name, status?, apiVersion? }` |
|
|
182
|
+
| `visualforcePages` | `{ name, label?, description?, apiVersion? }` |
|
|
183
|
+
| `lightningComponents` | `{ name, description?, apiVersion? }` |
|
|
184
|
+
| `auraComponents` | `{ name, description?, apiVersion? }` |
|
|
185
|
+
| `flows` | `{ name, label?, processType?, description? }` |
|
|
186
|
+
| `flexipages` | `{ name, masterLabel?, type? }` |
|
|
187
|
+
| `approvalProcesses` | `{ name, label?, active?, description? }` |
|
|
188
|
+
| `customApplications` | `{ name, label?, description? }` |
|
|
189
|
+
| `customLabels` | `{ fullName?, shortDescription? }` |
|
|
190
|
+
| `staticResources` | `{ name, contentType?, cacheControl?, description? }` |
|
|
191
|
+
| `dashboards` | `{ name, folderName?, description? }` |
|
|
192
|
+
| `reports` | `{ name, folderName?, description? }` |
|
|
193
|
+
| `emailTemplates` | `{ apiName, label?, type?, description? }` |
|
|
194
|
+
| `digitalExperienceBundles` | `{ name }` |
|
|
195
|
+
| `experienceBundles` | `{ name }` |
|
|
196
|
+
| `groups` | `{ apiName?, label?, doesIncludeBosses? }` |
|
|
197
|
+
| `queues` | `{ name, label?, doesSendEmailToMembers?, queueObjects?: string[] }` |
|
|
198
|
+
| `permissionSets` | `{ name, label?, description? }` |
|
|
199
|
+
| `permissionSetGroups` | `{ name, label?, description? }` |
|
|
200
|
+
| `sharingRules` | `{ label?, fullName?, object?, type?, accessLevel?, description? }` |
|
|
201
|
+
|
|
202
|
+
#### Object item
|
|
203
|
+
|
|
204
|
+
`standardObjects`, `customObjects`, `customSettings`, `customMetadataTypes`, and `platformEvents` all share this shape (they're all backed by `CustomObject`-family metadata):
|
|
205
|
+
|
|
206
|
+
| Field | Type | Description |
|
|
207
|
+
| ---------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
208
|
+
| `name` | string | API name. |
|
|
209
|
+
| `label` | string? | Label. |
|
|
210
|
+
| `miniDescription` | string? | The object's description, with the org's `description: ... usage notes: ...` convention stripped down to just the description portion (or the raw description, if that convention wasn't used). |
|
|
211
|
+
| `sharingModel` | string? | Internal sharing model (custom objects only). |
|
|
212
|
+
| `externalSharingModel` | string? | External sharing model (custom objects only). |
|
|
213
|
+
| `customSettingsType` | string? | Set only for custom settings (`List` or `Hierarchy`). |
|
|
214
|
+
| `eventType` | string? | Set only for platform events. |
|
|
215
|
+
| `publishBehavior` | string? | Set only for platform events. |
|
|
216
|
+
| `recordTypes` | `RecordTypeItem[]?` | `{ fullName, label?, active?, description? }` |
|
|
217
|
+
| `layouts` | `LayoutItem[]?` | `{ nameOnly? }` |
|
|
218
|
+
| `customFields` | `CustomFieldItem[]?` | See below. |
|
|
219
|
+
| `fieldSets` | `FieldSetItem[]?` | `{ fullName, label?, description?, displayedFields?: Array<{ field? }> }` |
|
|
220
|
+
| `validationRules` | `ValidationRuleItem[]?` | `{ fullName, active?, description? }` |
|
|
221
|
+
|
|
222
|
+
A field in `customFields` has this shape:
|
|
223
|
+
|
|
224
|
+
| Field | Type | Description |
|
|
225
|
+
| ----------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
226
|
+
| `fullName` | string | API name. |
|
|
227
|
+
| `label` | string? | Label. |
|
|
228
|
+
| `type` | string? | Display type — for `LongTextArea`/`Lookup`/`MasterDetail`/`Number`/`Text` fields this is already formatted as e.g. `Lookup(Account)`, `Number(18,0)`, ready to render as-is. |
|
|
229
|
+
| `required` | string? | `"true"`/`"false"` (a string, not a boolean, matching the underlying metadata). |
|
|
230
|
+
| `externalId` | string? | `"true"`/`"false"`. |
|
|
231
|
+
| `unique` | string? | `"true"`/`"false"`. |
|
|
232
|
+
| `miniDescription` | string? | Same convention as the object's `miniDescription`. |
|
|
233
|
+
|
|
234
|
+
A `loud` helper is pre-registered and available to your template — it upper-cases a string, handy for rendering the `"true"`/`"false"` string fields above the way the built-in template does (`{{#if this.required}}{{loud this.required}}{{else}}FALSE{{/if}}`):
|
|
235
|
+
|
|
236
|
+
```handlebars
|
|
237
|
+
<h1>My Custom Technical Design Document</h1>
|
|
238
|
+
|
|
239
|
+
<h2>Apex Classes</h2>
|
|
240
|
+
<ul>
|
|
241
|
+
{{#each apexClasses}}
|
|
242
|
+
<li>{{this.name}} ({{loud this.status}})</li>
|
|
243
|
+
{{/each}}
|
|
244
|
+
</ul>
|
|
245
|
+
|
|
246
|
+
<h2>Custom Objects</h2>
|
|
247
|
+
{{#each customObjects}}
|
|
248
|
+
<h3>{{this.label}}</h3>
|
|
249
|
+
{{#each this.customFields}}
|
|
250
|
+
<p>{{this.label}} — {{this.type}}</p>
|
|
251
|
+
{{/each}}
|
|
252
|
+
{{/each}}
|
|
253
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { SfCommand } from '@salesforce/sf-plugins-core';
|
|
2
|
+
/** The rendered report, and where it was written (if `--output-file` was specified). */
|
|
3
|
+
export type DocumentDiffResult = {
|
|
4
|
+
html: string;
|
|
5
|
+
outputFile?: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Generates a Confluence-storage-format change report between two git refs, grouping changed
|
|
9
|
+
* files by Salesforce metadata component type.
|
|
10
|
+
*/
|
|
11
|
+
export default class DocumentDiff extends SfCommand<DocumentDiffResult> {
|
|
12
|
+
static readonly summary: string;
|
|
13
|
+
static readonly description: string;
|
|
14
|
+
static readonly examples: string[];
|
|
15
|
+
static readonly flags: {
|
|
16
|
+
'from-tag': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
'to-tag': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
'output-file': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
'template-file': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
|
+
'output-format': import("@oclif/core/interfaces").OptionFlag<"html", import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
'flags-dir': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
22
|
+
};
|
|
23
|
+
/** @returns The rendered change report, and where it was written (if `--output-file` was specified). */
|
|
24
|
+
run(): Promise<DocumentDiffResult>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026, Clay Chipps; Copyright (c) 2026 Salesforce, Inc.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { exec as execCallback } from 'node:child_process';
|
|
17
|
+
import fs from 'node:fs/promises';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import { promisify } from 'node:util';
|
|
20
|
+
import { Messages } from '@salesforce/core';
|
|
21
|
+
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
|
|
22
|
+
import { buildChangeReportHtml, } from '../../../common/changeReportTemplate.js';
|
|
23
|
+
const exec = promisify(execCallback);
|
|
24
|
+
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
25
|
+
const messages = Messages.loadMessages('@simplysf/simply-document', 'simply.document.diff');
|
|
26
|
+
/** Maps a source-format directory name to the component type it's grouped under in the report. */
|
|
27
|
+
const COMPONENT_TYPE_MAPPING = {
|
|
28
|
+
classes: 'apexClasses',
|
|
29
|
+
triggers: 'apexTriggers',
|
|
30
|
+
approvalProcesses: 'approvalProcesses',
|
|
31
|
+
aura: 'auraComponents',
|
|
32
|
+
applications: 'customApplications',
|
|
33
|
+
labels: 'customLabels',
|
|
34
|
+
customMetadata: 'customMetadata',
|
|
35
|
+
dashboards: 'dashboards',
|
|
36
|
+
digitalExperienceBundles: 'digitalExperienceBundles',
|
|
37
|
+
email: 'emailTemplates',
|
|
38
|
+
experienceBundles: 'experienceBundles',
|
|
39
|
+
flexipages: 'flexipages',
|
|
40
|
+
flows: 'flows',
|
|
41
|
+
groups: 'groups',
|
|
42
|
+
lwc: 'lightningComponents',
|
|
43
|
+
permissionsets: 'permissionSets',
|
|
44
|
+
permissionsetgroups: 'permissionSetGroups',
|
|
45
|
+
queues: 'queues',
|
|
46
|
+
reports: 'reports',
|
|
47
|
+
sharingRules: 'sharingRules',
|
|
48
|
+
staticresources: 'staticResources',
|
|
49
|
+
pages: 'visualforcePages',
|
|
50
|
+
};
|
|
51
|
+
/** Maps a `git diff --name-status` status letter to the label rendered in the report. */
|
|
52
|
+
const CHANGE_TYPE_LABELS = {
|
|
53
|
+
A: 'Added',
|
|
54
|
+
M: 'Modified',
|
|
55
|
+
R: 'Renamed',
|
|
56
|
+
D: 'Deleted',
|
|
57
|
+
C: 'Copied',
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Determine the report component type/name for a changed source-format file path, by walking its
|
|
61
|
+
* path segments looking for a recognized metadata directory name.
|
|
62
|
+
*
|
|
63
|
+
* @param filePath - The changed file's path, as reported by `git diff --name-status`.
|
|
64
|
+
* @returns The component type this file is grouped under, and its component name.
|
|
65
|
+
*/
|
|
66
|
+
async function getComponentTypeFromPath(filePath) {
|
|
67
|
+
const parts = filePath.split('/');
|
|
68
|
+
for (let i = 0; i < parts.length; i++) {
|
|
69
|
+
const part = parts[i];
|
|
70
|
+
if (part === 'objects') {
|
|
71
|
+
const objectName = parts[i + 1] || 'Unknown';
|
|
72
|
+
let componentType = 'standardObjects';
|
|
73
|
+
if (objectName.endsWith('__mdt')) {
|
|
74
|
+
componentType = 'customMetadataTypes';
|
|
75
|
+
}
|
|
76
|
+
else if (objectName.endsWith('__e')) {
|
|
77
|
+
componentType = 'platformEvents';
|
|
78
|
+
}
|
|
79
|
+
else if (objectName.endsWith('__c')) {
|
|
80
|
+
componentType = 'customObjects';
|
|
81
|
+
// Try to detect if it's a Custom Setting by looking for the .object-meta.xml file on disk
|
|
82
|
+
const objectMetaPath = filePath.split(`/objects/${objectName}/`)[0] + `/objects/${objectName}/${objectName}.object-meta.xml`;
|
|
83
|
+
try {
|
|
84
|
+
// eslint-disable-next-line no-await-in-loop
|
|
85
|
+
const content = await fs.readFile(objectMetaPath, 'utf-8');
|
|
86
|
+
if (content.includes('<customSettingsType>')) {
|
|
87
|
+
componentType = 'customSettings';
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// File might not exist (e.g. deleted, or we're in a different directory)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
componentType,
|
|
96
|
+
componentName: objectName.replace(/\.[^/.]+$/, ''),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (COMPONENT_TYPE_MAPPING[part]) {
|
|
100
|
+
const componentName = parts[i + 1] || 'Unknown';
|
|
101
|
+
return {
|
|
102
|
+
componentType: COMPONENT_TYPE_MAPPING[part],
|
|
103
|
+
componentName: componentName.replace(/\.[^/.]+$/, ''),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const fileName = path.basename(filePath);
|
|
108
|
+
return {
|
|
109
|
+
componentType: 'Unknown',
|
|
110
|
+
componentName: fileName.replace(/\.[^/.]+$/, ''),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Generates a Confluence-storage-format change report between two git refs, grouping changed
|
|
115
|
+
* files by Salesforce metadata component type.
|
|
116
|
+
*/
|
|
117
|
+
export default class DocumentDiff extends SfCommand {
|
|
118
|
+
static summary = messages.getMessage('summary');
|
|
119
|
+
static description = messages.getMessage('description');
|
|
120
|
+
static examples = messages.getMessages('examples');
|
|
121
|
+
static flags = {
|
|
122
|
+
...SfCommand.baseFlags,
|
|
123
|
+
'from-tag': Flags.string({
|
|
124
|
+
summary: messages.getMessage('flags.from-tag.summary'),
|
|
125
|
+
required: true,
|
|
126
|
+
}),
|
|
127
|
+
'to-tag': Flags.string({
|
|
128
|
+
summary: messages.getMessage('flags.to-tag.summary'),
|
|
129
|
+
required: true,
|
|
130
|
+
}),
|
|
131
|
+
'output-file': Flags.string({
|
|
132
|
+
summary: messages.getMessage('flags.output-file.summary'),
|
|
133
|
+
description: messages.getMessage('flags.output-file.description'),
|
|
134
|
+
}),
|
|
135
|
+
'template-file': Flags.file({
|
|
136
|
+
summary: messages.getMessage('flags.template-file.summary'),
|
|
137
|
+
description: messages.getMessage('flags.template-file.description'),
|
|
138
|
+
exists: true,
|
|
139
|
+
}),
|
|
140
|
+
'output-format': Flags.custom({ options: ['html'] })({
|
|
141
|
+
summary: messages.getMessage('flags.output-format.summary'),
|
|
142
|
+
description: messages.getMessage('flags.output-format.description'),
|
|
143
|
+
default: 'html',
|
|
144
|
+
}),
|
|
145
|
+
};
|
|
146
|
+
/** @returns The rendered change report, and where it was written (if `--output-file` was specified). */
|
|
147
|
+
async run() {
|
|
148
|
+
const { flags } = await this.parse(DocumentDiff);
|
|
149
|
+
const fromTag = flags['from-tag'];
|
|
150
|
+
const toTag = flags['to-tag'];
|
|
151
|
+
const customTemplateSource = flags['template-file']
|
|
152
|
+
? await fs.readFile(flags['template-file'], 'utf-8')
|
|
153
|
+
: undefined;
|
|
154
|
+
this.spinner.start(messages.getMessage('info.generatingDiff', [fromTag, toTag]));
|
|
155
|
+
let stdout;
|
|
156
|
+
let stderr;
|
|
157
|
+
try {
|
|
158
|
+
({ stdout, stderr } = await exec(`git diff --name-status ${fromTag}..${toTag}`));
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
this.spinner.stop();
|
|
162
|
+
throw messages.createError('error.gitDiffFailed', [error.message]);
|
|
163
|
+
}
|
|
164
|
+
if (stderr) {
|
|
165
|
+
// git diff can write informational messages to stderr, so this isn't fatal
|
|
166
|
+
this.warn(stderr);
|
|
167
|
+
}
|
|
168
|
+
const changedFiles = stdout.trim().length > 0 ? stdout.trim().split(/\r?\n/) : [];
|
|
169
|
+
const changes = {};
|
|
170
|
+
for (const file of changedFiles) {
|
|
171
|
+
const parts = file.split('\t');
|
|
172
|
+
if (parts.length < 2) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
let changeType = parts[0];
|
|
176
|
+
let filePath = parts[1];
|
|
177
|
+
let changeDescription = '';
|
|
178
|
+
// For renames (R...) and copies (C...), git diff returns 3 columns: [status, oldPath, newPath]
|
|
179
|
+
if (parts.length === 3) {
|
|
180
|
+
const oldPath = parts[1];
|
|
181
|
+
filePath = parts[2];
|
|
182
|
+
if (changeType.startsWith('R')) {
|
|
183
|
+
changeType = 'R';
|
|
184
|
+
changeDescription = `Renamed from ${oldPath}`;
|
|
185
|
+
}
|
|
186
|
+
else if (changeType.startsWith('C')) {
|
|
187
|
+
changeType = 'C';
|
|
188
|
+
changeDescription = `Copied from ${oldPath}`;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// eslint-disable-next-line no-await-in-loop
|
|
192
|
+
const { componentType, componentName } = await getComponentTypeFromPath(filePath);
|
|
193
|
+
changes[componentType] ??= [];
|
|
194
|
+
const shortChangeType = changeType.charAt(0);
|
|
195
|
+
const transformedChangeType = CHANGE_TYPE_LABELS[shortChangeType] ?? changeType;
|
|
196
|
+
changes[componentType].push({
|
|
197
|
+
componentName,
|
|
198
|
+
componentType,
|
|
199
|
+
changeType: transformedChangeType,
|
|
200
|
+
changeDescription,
|
|
201
|
+
path: filePath,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
for (const key of Object.keys(changes)) {
|
|
205
|
+
changes[key].sort((a, b) => {
|
|
206
|
+
const typeCompare = a.changeType.localeCompare(b.changeType);
|
|
207
|
+
if (typeCompare !== 0) {
|
|
208
|
+
return typeCompare;
|
|
209
|
+
}
|
|
210
|
+
return a.componentName.localeCompare(b.componentName);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
this.spinner.stop();
|
|
214
|
+
let html;
|
|
215
|
+
if (flags['output-format'] === 'html') {
|
|
216
|
+
html = buildChangeReportHtml(changes, customTemplateSource);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
throw messages.createError('error.unsupportedOutputFormat', [flags['output-format']]);
|
|
220
|
+
}
|
|
221
|
+
const outputFile = flags['output-file'];
|
|
222
|
+
if (outputFile) {
|
|
223
|
+
await fs.writeFile(outputFile, html, 'utf-8');
|
|
224
|
+
this.info(messages.getMessage('info.complete', [outputFile]));
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
this.log(html);
|
|
228
|
+
}
|
|
229
|
+
return { html, outputFile };
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../../../src/commands/simply/document/diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { SfCommand } from '@salesforce/sf-plugins-core';
|
|
2
|
+
/** Where the generated document was written, and how many top-level metadata items it covers. */
|
|
3
|
+
export type DocumentGenerateResult = {
|
|
4
|
+
outputFile: string;
|
|
5
|
+
componentCount: number;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Scans a Salesforce project directory for metadata and generates a Confluence-storage-format
|
|
9
|
+
* technical design document covering the data model, security model, groups/queues/permissions,
|
|
10
|
+
* solution inventory, and custom code inventory.
|
|
11
|
+
*/
|
|
12
|
+
export default class DocumentGenerate extends SfCommand<DocumentGenerateResult> {
|
|
13
|
+
static readonly summary: string;
|
|
14
|
+
static readonly description: string;
|
|
15
|
+
static readonly examples: string[];
|
|
16
|
+
static readonly flags: {
|
|
17
|
+
directory: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
'output-file': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
'template-file': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
|
+
'output-format': import("@oclif/core/interfaces").OptionFlag<"html", import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
'flags-dir': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
22
|
+
};
|
|
23
|
+
/** @returns Where the generated document was written, and how many top-level metadata items it covers. */
|
|
24
|
+
run(): Promise<DocumentGenerateResult>;
|
|
25
|
+
}
|