mcdev 4.0.1 → 4.1.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.
@@ -1,76 +1,78 @@
1
1
  name: 🐞 Bug
2
2
  description: File a bug/issue
3
- title: "[BUG] <title>"
3
+ title: '[BUG] <title>'
4
4
  labels: [bug, NEW]
5
5
  body:
6
- - type: checkboxes
7
- attributes:
8
- label: Is there an existing issue for this?
9
- description: Please [search here](https://github.com/Accenture/sfmc-devtools/issues) to see if an issue already exists for your problem.
10
- options:
11
- - label: I have searched the existing issues
12
- required: true
13
- - type: textarea
14
- attributes:
15
- label: Current Behavior
16
- description: A clear & concise description of what you're experiencing.
17
- validations:
18
- required: false
19
- - type: textarea
20
- attributes:
21
- label: Expected Behavior
22
- description: A clear & concise description of what you expected to happen.
23
- validations:
24
- required: false
25
- - type: textarea
26
- attributes:
27
- label: Steps To Reproduce
28
- description: Steps to reproduce the behavior.
29
- value: |
30
- 1. Go to '...'
31
- 2. Click on '....'
32
- 3. Run '...'
33
- 4. See error...
34
- validations:
35
- required: false
36
- - type: dropdown
37
- id: version
38
- attributes:
39
- label: Version
40
- description: What version of our software are you running? (mcdev --version)
41
- options:
42
- - 4.0.0
43
- - 3.1.3
44
- - 3.1.2
45
- - 3.1.1
46
- - 3.1.0
47
- - 3.0.3
48
- - 3.0.2
49
- - 3.0.1
50
- - develop-branch
51
- validations:
52
- required: true
53
- - type: textarea
54
- attributes:
55
- label: Environment
56
- description: |
57
- examples:
58
- - **Operating system (Windows/Mac/Linux/...)**: Windows 10
59
- - **Node (node --version)**: 13.14.0
60
- - **npm (npm --version)**: 7.6.3
61
- value: |
62
- - OS:
63
- - Node:
64
- - npm:
65
- validations:
66
- required: false
67
- - type: checkboxes
68
- attributes:
69
- label: Participation
70
- options:
71
- - label: I am willing to submit a pull request for this issue.
72
- required: false
73
- - type: textarea
74
- attributes:
75
- label: Additional comments
76
- description: Is there anything else that's important for the team to know?
6
+ - type: checkboxes
7
+ attributes:
8
+ label: Is there an existing issue for this?
9
+ description: Please [search here](https://github.com/Accenture/sfmc-devtools/issues) to see if an issue already exists for your problem.
10
+ options:
11
+ - label: I have searched the existing issues
12
+ required: true
13
+ - type: textarea
14
+ attributes:
15
+ label: Current Behavior
16
+ description: A clear & concise description of what you're experiencing.
17
+ validations:
18
+ required: false
19
+ - type: textarea
20
+ attributes:
21
+ label: Expected Behavior
22
+ description: A clear & concise description of what you expected to happen.
23
+ validations:
24
+ required: false
25
+ - type: textarea
26
+ attributes:
27
+ label: Steps To Reproduce
28
+ description: Steps to reproduce the behavior.
29
+ value: |
30
+ 1. Go to '...'
31
+ 2. Click on '....'
32
+ 3. Run '...'
33
+ 4. See error...
34
+ validations:
35
+ required: false
36
+ - type: dropdown
37
+ id: version
38
+ attributes:
39
+ label: Version
40
+ description: What version of our software are you running? (mcdev --version)
41
+ options:
42
+ - 4.0.2
43
+ - 4.0.1
44
+ - 4.0.0
45
+ - 3.1.3
46
+ - 3.1.2
47
+ - 3.1.1
48
+ - 3.1.0
49
+ - 3.0.3
50
+ - 3.0.2
51
+ - 3.0.1
52
+ - develop-branch
53
+ validations:
54
+ required: true
55
+ - type: textarea
56
+ attributes:
57
+ label: Environment
58
+ description: |
59
+ examples:
60
+ - **Operating system (Windows/Mac/Linux/...)**: Windows 10
61
+ - **Node (node --version)**: 13.14.0
62
+ - **npm (npm --version)**: 7.6.3
63
+ value: |
64
+ - OS:
65
+ - Node:
66
+ - npm:
67
+ validations:
68
+ required: false
69
+ - type: checkboxes
70
+ attributes:
71
+ label: Participation
72
+ options:
73
+ - label: I am willing to submit a pull request for this issue.
74
+ required: false
75
+ - type: textarea
76
+ attributes:
77
+ label: Additional comments
78
+ description: Is there anything else that's important for the team to know?
package/README.md CHANGED
@@ -123,7 +123,7 @@ If you experience issues installing Accenture SFMC DevTools, please check out th
123
123
  1. Install Accenture SFMC DevTools by running `npm install -g mcdev` (prefix with `sudo` on MacOS)
124
124
  - If you get an error, please see the below troubleshooting section.
125
125
 
126
- When completed you will see `+ mcdev@3.2.0` printed to your screen (or the current version of it respectively).
126
+ When completed run `mcdev --version` and it will show you which version you installed (e.g. `4.1.0`).
127
127
 
128
128
  > **_Side note for proud nerds_:**
129
129
  >
@@ -272,13 +272,15 @@ To work with our **developer-version** or to install a **specific older version*
272
272
  npm install -g accenture/sfmc-devtools#develop
273
273
  ```
274
274
 
275
+ _Note: Regardless of which tag or branch you install_
276
+
275
277
  **Install specific version (using a version tag on npm):**
276
278
 
277
279
  ```bash
278
- npm install -g mcdev@3.2.0
280
+ npm install -g mcdev@4.1.0
279
281
  ```
280
282
 
281
- **Warning**: When you used the above method to install Accenture SFMC DevTools for a specific version or tag, trying to [update Accenture SFMC DevTools](#updating-mcdev) might not download the most recently published official version but instead stay on the version or branch you previously selected (in the above examples: develop, 3.2.0)!
283
+ **Warning**: When you used the above method to install Accenture SFMC DevTools for a specific version or tag, trying to [update Accenture SFMC DevTools](#updating-mcdev) might not download the most recently published official version but instead stay on the version or branch you previously selected (in the above examples: develop, 4.1.0)!
282
284
 
283
285
  > **Note**: The version is currently _not_ updated on the developer branch until a new release is published. Hence, you will not see a change if you run `mcdev --version`.
284
286
 
@@ -1483,25 +1485,21 @@ If you want to enhance Accenture SFMC DevTools you are welcome to fork the repo
1483
1485
 
1484
1486
  Instead of installing Accenture SFMC DevTools as a npm dependency from our git repo, we recommend cloning our repo and then linking it locally:
1485
1487
 
1486
- Assuming you cloned Accenture SFMC DevTools into `C:\repos\sfmc-devtools\` (or `~/repos/sfmc-devtools/` on Mac) you can then go into your project directory, open a terminal there and run:
1487
-
1488
- _Local install:_
1489
-
1490
- ```bash
1491
- npm install --save-dev "C:\repos\sfmc-devtools"
1492
- ```
1488
+ Assuming you cloned Accenture SFMC DevTools into `C:\repos\sfmc-devtools\` (or `~/repos/sfmc-devtools/` on Mac):
1493
1489
 
1494
- or
1490
+ 1. Open a terminal in your repo folder (`repos/sfmc-devtools/`)
1491
+ 2. Execute `npm install` to download all the dependencies.
1492
+ 3. Execute `npx husky install` to enable our git hooks.
1493
+ 4. Execute `npm install -g "C:\repos\sfmc-devtools"` (this installs mcdev globally on your computer based on your cloned repo folder. Any changes you make in there will take immediate effect without the need for publishing or re-installing it).
1495
1494
 
1496
- _Global install **(recommended)**:_
1495
+ This should tell npm to create a symlink to your cloned local directoty, allowing you to see updates you make in your mcdev repo instantly.
1497
1496
 
1498
- ```bash
1499
- npm install -g "C:\repos\sfmc-devtools"
1500
- ```
1497
+ To test your new **global** developer setup, run `mcdev --version` in CLI which should return the current version (e.g. `4.1.0`). Then, go into your mcdev repo and update the version with the suffix `-dev`, e.g. to `4.1.0-dev` and then run `mcdev --version` again to verify that your change propagates instantly.
1501
1498
 
1502
- This should tell npm to create a symlink to your cloned local directoty, allowing you to see updates you make in your mcdev repo instantly.
1499
+ > **Not recommended:** Alternatively, you can install it locally only by opening a terminal in your project directory and executing `npm install --save-dev "C:\repos\sfmc-devtools"`
1500
+ > To run the local version you need to prepend "npx" before your commands, e.g. `npx mcdev --version`
1503
1501
 
1504
- > Note: on MacOS you might need to prepend `sudo` to elevate your command.
1502
+ **Note:** On MacOS you might need to prepend `sudo` to elevate your command.
1505
1503
 
1506
1504
  **Local vs. Global developer installation:**
1507
1505
 
@@ -1509,8 +1507,6 @@ If you use Accenture SFMC DevTools in your team it is recommended to install you
1509
1507
 
1510
1508
  If you do need to install it locally, make sure you don't commit your project's package.json with this change or you might break mcdev for other developers in your team that either didn't clone the Accenture SFMC DevTools repo or stored in a different directory.
1511
1509
 
1512
- To test your new **global** developer setup, run `mcdev --version` in CLI which should return the current version (e.g. `3.2.0`). Then, go into your mcdev repo and update the version with the suffix `-dev`, e.g. to `3.2.0-dev` and then run `mcdev --version` again to verify that your change propagates instantly.
1513
-
1514
1510
  <a name="local-install"></a>
1515
1511
 
1516
1512
  ### 9.2. Local install
@@ -1536,7 +1532,7 @@ The following explains how you _could_ install it locally for certain edge cases
1536
1532
  4. Afterwards, install Accenture SFMC DevTools by running `npm install --save-dev mcdev`
1537
1533
  - If you get an error, please see the below troubleshooting section.
1538
1534
 
1539
- When completed you will see `+ mcdev@3.2.0` printed to your screen (or the current version of it respectively).
1535
+ When completed run `mcdev --version` and it will show you which version you installed (e.g. `4.1.0`).
1540
1536
 
1541
1537
  ### 9.3. NPM Scripts
1542
1538
 
@@ -180,8 +180,9 @@ Provides default functionality that can be overwritten by child metadata type cl
180
180
  ## Typedefs
181
181
 
182
182
  <dl>
183
- <dt><a href="#SupportedMetadataTypes">SupportedMetadataTypes</a> : <code>Object.&lt;string, string&gt;</code></dt>
184
- <dd></dd>
183
+ <dt><a href="#TypeKeyCombo">TypeKeyCombo</a> : <code>Object.&lt;string, string&gt;</code></dt>
184
+ <dd><p>object-key=metadata type, value=array of external keys</p>
185
+ </dd>
185
186
  <dt><a href="#Cache">Cache</a> : <code>Object.&lt;string, any&gt;</code></dt>
186
187
  <dd><p>key=customer key</p>
187
188
  </dd>
@@ -346,7 +347,6 @@ Source and target business units are also compared before the deployment to appl
346
347
  * _instance_
347
348
  * [.metadata](#Deployer+metadata) : <code>TYPE.MultiMetadataTypeMap</code>
348
349
  * [._deploy([typeArr], [keyArr], [fromRetrieve])](#Deployer+_deploy) ⇒ <code>Promise</code>
349
- * [.deployCallback(result, metadataType)](#Deployer+deployCallback) ⇒ <code>void</code>
350
350
  * _static_
351
351
  * [.deploy(businessUnit, [selectedTypesArr], [keyArr], [fromRetrieve])](#Deployer.deploy) ⇒ <code>Promise.&lt;void&gt;</code>
352
352
  * [._deployBU(cred, bu, properties, [typeArr], [keyArr], [fromRetrieve])](#Deployer._deployBU) ⇒ <code>Promise</code>
@@ -378,22 +378,10 @@ Deploy all metadata that is located in the deployDir
378
378
 
379
379
  | Param | Type | Description |
380
380
  | --- | --- | --- |
381
- | [typeArr] | <code>Array.&lt;string&gt;</code> | limit deployment to given metadata type (can include subtype) |
381
+ | [typeArr] | <code>Array.&lt;TYPE.SupportedMetadataTypes&gt;</code> | limit deployment to given metadata type (can include subtype) |
382
382
  | [keyArr] | <code>Array.&lt;string&gt;</code> | limit deployment to given metadata keys |
383
383
  | [fromRetrieve] | <code>boolean</code> | if true, no folders will be updated/created |
384
384
 
385
- <a name="Deployer+deployCallback"></a>
386
-
387
- ### deployer.deployCallback(result, metadataType) ⇒ <code>void</code>
388
- Gets called for every deployed metadata entry
389
-
390
- **Kind**: instance method of [<code>Deployer</code>](#Deployer)
391
-
392
- | Param | Type | Description |
393
- | --- | --- | --- |
394
- | result | <code>object</code> | Deployment result |
395
- | metadataType | <code>string</code> | Name of metadata type |
396
-
397
385
  <a name="Deployer.deploy"></a>
398
386
 
399
387
  ### Deployer.deploy(businessUnit, [selectedTypesArr], [keyArr], [fromRetrieve]) ⇒ <code>Promise.&lt;void&gt;</code>
@@ -405,7 +393,7 @@ Deploys all metadata located in the 'deploy' directory to the specified business
405
393
  | Param | Type | Description |
406
394
  | --- | --- | --- |
407
395
  | businessUnit | <code>string</code> | references credentials from properties.json |
408
- | [selectedTypesArr] | <code>Array.&lt;string&gt;</code> | limit deployment to given metadata type |
396
+ | [selectedTypesArr] | <code>Array.&lt;TYPE.SupportedMetadataTypes&gt;</code> | limit deployment to given metadata type |
409
397
  | [keyArr] | <code>Array.&lt;string&gt;</code> | limit deployment to given metadata keys |
410
398
  | [fromRetrieve] | <code>boolean</code> | optionally deploy whats defined via selectedTypesArr + keyArr directly from retrieve folder instead of from deploy folder |
411
399
 
@@ -422,7 +410,7 @@ helper for deploy()
422
410
  | cred | <code>string</code> | name of Credential |
423
411
  | bu | <code>string</code> | name of BU |
424
412
  | properties | <code>TYPE.Mcdevrc</code> | General configuration to be used in retrieve |
425
- | [typeArr] | <code>Array.&lt;string&gt;</code> | limit deployment to given metadata type |
413
+ | [typeArr] | <code>Array.&lt;TYPE.SupportedMetadataTypes&gt;</code> | limit deployment to given metadata type |
426
414
  | [keyArr] | <code>Array.&lt;string&gt;</code> | limit deployment to given metadata keys |
427
415
  | [fromRetrieve] | <code>boolean</code> | optionally deploy whats defined via selectedTypesArr + keyArr directly from retrieve folder instead of from deploy folder |
428
416
 
@@ -518,6 +506,7 @@ handler for 'mcdev createDeltaPkg
518
506
  | argv | <code>object</code> | yargs parameters |
519
507
  | [argv.range] | <code>string</code> | git commit range into deploy directory |
520
508
  | [argv.filter] | <code>string</code> | filter file paths that start with any |
509
+ | [argv.diffArr] | <code>Array.&lt;TYPE.DeltaPkgItem&gt;</code> | list of files to include in delta package (skips git diff when provided) |
521
510
  | [argv.skipInteraction] | <code>TYPE.skipInteraction</code> | allows to skip interactive wizard |
522
511
 
523
512
  <a name="Mcdev.selectTypes"></a>
@@ -550,7 +539,7 @@ Retrieve all metadata from the specified business unit into the local file syste
550
539
  | Param | Type | Description |
551
540
  | --- | --- | --- |
552
541
  | businessUnit | <code>string</code> | references credentials from properties.json |
553
- | [selectedTypesArr] | <code>Array.&lt;string&gt;</code> | limit retrieval to given metadata type |
542
+ | [selectedTypesArr] | <code>Array.&lt;TYPE.SupportedMetadataTypes&gt;</code> \| <code>TYPE.TypeKeyCombo</code> | limit retrieval to given metadata type |
554
543
  | [keys] | <code>Array.&lt;string&gt;</code> | limit retrieval to given metadata key |
555
544
  | [changelogOnly] | <code>boolean</code> | skip saving, only create json in memory |
556
545
 
@@ -565,7 +554,7 @@ Deploys all metadata located in the 'deploy' directory to the specified business
565
554
  | Param | Type | Default | Description |
566
555
  | --- | --- | --- | --- |
567
556
  | businessUnit | <code>string</code> | | references credentials from properties.json |
568
- | [selectedTypesArr] | <code>Array.&lt;string&gt;</code> | | limit deployment to given metadata type |
557
+ | [selectedTypesArr] | <code>Array.&lt;TYPE.SupportedMetadataTypes&gt;</code> | | limit deployment to given metadata type |
569
558
  | [keyArr] | <code>Array.&lt;string&gt;</code> | | limit deployment to given metadata keys |
570
559
  | [fromRetrieve] | <code>boolean</code> | <code>false</code> | optionally deploy whats defined via selectedTypesArr + keyArr directly from retrieve folder instead of from deploy folder |
571
560
 
@@ -840,7 +829,7 @@ FileTransfer MetadataType
840
829
  * [.requestSubType(subType, subTypeArray, [retrieveDir], [templateName], [templateVariables], key)](#Asset.requestSubType) ⇒ <code>Promise</code>
841
830
  * [.requestAndSaveExtended(items, subType, retrieveDir, [templateVariables])](#Asset.requestAndSaveExtended) ⇒ <code>Promise</code>
842
831
  * [._retrieveExtendedFile(metadata, subType, retrieveDir)](#Asset._retrieveExtendedFile) ⇒ <code>Promise.&lt;void&gt;</code>
843
- * [._readExtendedFileFromFS(metadata, subType, deployDir)](#Asset._readExtendedFileFromFS) ⇒ <code>Promise.&lt;void&gt;</code>
832
+ * [._readExtendedFileFromFS(metadata, subType, deployDir, [pathOnly])](#Asset._readExtendedFileFromFS) ⇒ <code>Promise.&lt;string&gt;</code>
844
833
  * [.postRetrieveTasks(metadata)](#Asset.postRetrieveTasks) ⇒ <code>TYPE.CodeExtractItem</code>
845
834
  * [.preDeployTasks(metadata, deployDir, buObject)](#Asset.preDeployTasks) ⇒ <code>Promise.&lt;TYPE.AssetItem&gt;</code>
846
835
  * [._getMainSubtype(extendedSubType)](#Asset._getMainSubtype) ⇒ <code>string</code>
@@ -974,19 +963,20 @@ This method retrieves these and saves them alongside the metadata json
974
963
 
975
964
  <a name="Asset._readExtendedFileFromFS"></a>
976
965
 
977
- ### Asset.\_readExtendedFileFromFS(metadata, subType, deployDir) ⇒ <code>Promise.&lt;void&gt;</code>
966
+ ### Asset.\_readExtendedFileFromFS(metadata, subType, deployDir, [pathOnly]) ⇒ <code>Promise.&lt;string&gt;</code>
978
967
  helper for this.preDeployTasks()
979
968
  Some metadata types store their actual content as a separate file, e.g. images
980
969
  This method reads these from the local FS stores them in the metadata object allowing to deploy it
981
970
 
982
971
  **Kind**: static method of [<code>Asset</code>](#Asset)
983
- **Returns**: <code>Promise.&lt;void&gt;</code> - -
972
+ **Returns**: <code>Promise.&lt;string&gt;</code> - if found will return the path of the binary file
984
973
 
985
- | Param | Type | Description |
986
- | --- | --- | --- |
987
- | metadata | <code>TYPE.AssetItem</code> | a single asset |
988
- | subType | <code>TYPE.AssetSubType</code> | group of similar assets to put in a folder (ie. images) |
989
- | deployDir | <code>string</code> | directory of deploy files |
974
+ | Param | Type | Default | Description |
975
+ | --- | --- | --- | --- |
976
+ | metadata | <code>TYPE.AssetItem</code> | | a single asset |
977
+ | subType | <code>TYPE.AssetSubType</code> | | group of similar assets to put in a folder (ie. images) |
978
+ | deployDir | <code>string</code> | | directory of deploy files |
979
+ | [pathOnly] | <code>boolean</code> | <code>false</code> | used by getFilesToCommit which does not need the binary file to be actually read |
990
980
 
991
981
  <a name="Asset.postRetrieveTasks"></a>
992
982
 
@@ -4418,8 +4408,8 @@ Retrieve metadata of specified types into local file system and Retriever.metada
4418
4408
 
4419
4409
  | Param | Type | Description |
4420
4410
  | --- | --- | --- |
4421
- | metadataTypes | <code>Array.&lt;string&gt;</code> | String list of metadata types to retrieve |
4422
- | [namesOrKeys] | <code>Array.&lt;string&gt;</code> | name of Metadata to retrieveAsTemplate or list of keys for normal retrieval |
4411
+ | metadataTypes | <code>Array.&lt;TYPE.SupportedMetadataTypes&gt;</code> | list of metadata types to retrieve; can include subtypes! |
4412
+ | [namesOrKeys] | <code>Array.&lt;string&gt;</code> \| <code>TYPE.TypeKeyCombo</code> | name of Metadata to retrieveAsTemplate or list of keys for normal retrieval |
4423
4413
  | [templateVariables] | <code>TYPE.TemplateMap</code> | Object of values which can be replaced (in case of templating) |
4424
4414
  | [changelogOnly] | <code>boolean</code> | skip saving, only create json in memory |
4425
4415
 
@@ -4448,7 +4438,7 @@ CLI entry for SFMC DevTools
4448
4438
  * [.inverseGet(objs, val)](#Util.inverseGet) ⇒ <code>string</code>
4449
4439
  * [.getMetadataHierachy(metadataTypes)](#Util.getMetadataHierachy) ⇒ <code>Array.&lt;string&gt;</code>
4450
4440
  * [.resolveObjPath(path, obj)](#Util.resolveObjPath) ⇒ <code>any</code>
4451
- * [.execSync(cmd, [args])](#Util.execSync) ⇒ <code>undefined</code>
4441
+ * [.execSync(cmd, [args], [hideOutput])](#Util.execSync) ⇒ <code>undefined</code>
4452
4442
  * [.templateSearchResult(results, keyToSearch, searchValue)](#Util.templateSearchResult) ⇒ <code>TYPE.MetadataTypeItem</code>
4453
4443
  * [.setLoggingLevel(argv)](#Util.setLoggingLevel) ⇒ <code>void</code>
4454
4444
 
@@ -4645,7 +4635,7 @@ let's you dynamically walk down an object and get a value
4645
4635
 
4646
4636
  <a name="Util.execSync"></a>
4647
4637
 
4648
- ### Util.execSync(cmd, [args]) ⇒ <code>undefined</code>
4638
+ ### Util.execSync(cmd, [args], [hideOutput]) ⇒ <code>undefined</code>
4649
4639
  helper to run other commands as if run manually by user
4650
4640
 
4651
4641
  **Kind**: static method of [<code>Util</code>](#Util)
@@ -4654,6 +4644,7 @@ helper to run other commands as if run manually by user
4654
4644
  | --- | --- | --- |
4655
4645
  | cmd | <code>string</code> | to be executed command |
4656
4646
  | [args] | <code>Array.&lt;string&gt;</code> | list of arguments |
4647
+ | [hideOutput] | <code>boolean</code> | if true, output of command will be hidden from CLI |
4657
4648
 
4658
4649
  <a name="Util.templateSearchResult"></a>
4659
4650
 
@@ -4942,7 +4933,7 @@ DevOps helper class
4942
4933
  * [.getDeltaList(properties, [range], [saveToDeployDir], [filterPaths])](#DevOps.getDeltaList) ⇒ <code>Promise.&lt;Array.&lt;TYPE.DeltaPkgItem&gt;&gt;</code>
4943
4934
  * [~delta](#DevOps.getDeltaList..delta) : <code>Array.&lt;TYPE.DeltaPkgItem&gt;</code>
4944
4935
  * [~copied](#DevOps.getDeltaList..copied) : <code>TYPE.DeltaPkgItem</code>
4945
- * [.buildDeltaDefinitions(properties, range, [skipInteraction])](#DevOps.buildDeltaDefinitions) ⇒ <code>Promise.&lt;Array.&lt;TYPE.DeltaPkgItem&gt;&gt;</code>
4936
+ * [.buildDeltaDefinitions(properties, range, [diffArr], [skipInteraction])](#DevOps.buildDeltaDefinitions) ⇒ <code>Promise.&lt;Array.&lt;TYPE.DeltaPkgItem&gt;&gt;</code>
4946
4937
  * [~deltaDeployAll](#DevOps.buildDeltaDefinitions..deltaDeployAll) : <code>Array.&lt;TYPE.DeltaPkgItem&gt;</code>
4947
4938
  * [.document(directory, jsonReport)](#DevOps.document) ⇒ <code>void</code>
4948
4939
  * [.getFilesToCommit(properties, buObject, metadataType, keyArr)](#DevOps.getFilesToCommit) ⇒ <code>Promise.&lt;Array.&lt;string&gt;&gt;</code>
@@ -4978,7 +4969,7 @@ Interactive commit selection if no commits are passed.
4978
4969
  **Kind**: inner constant of [<code>getDeltaList</code>](#DevOps.getDeltaList)
4979
4970
  <a name="DevOps.buildDeltaDefinitions"></a>
4980
4971
 
4981
- ### DevOps.buildDeltaDefinitions(properties, range, [skipInteraction]) ⇒ <code>Promise.&lt;Array.&lt;TYPE.DeltaPkgItem&gt;&gt;</code>
4972
+ ### DevOps.buildDeltaDefinitions(properties, range, [diffArr], [skipInteraction]) ⇒ <code>Promise.&lt;Array.&lt;TYPE.DeltaPkgItem&gt;&gt;</code>
4982
4973
  wrapper around DevOps.getDeltaList, Builder.buildTemplate and M
4983
4974
 
4984
4975
  **Kind**: static method of [<code>DevOps</code>](#DevOps)
@@ -4988,6 +4979,7 @@ wrapper around DevOps.getDeltaList, Builder.buildTemplate and M
4988
4979
  | --- | --- | --- |
4989
4980
  | properties | <code>TYPE.Mcdevrc</code> | project config file |
4990
4981
  | range | <code>string</code> | git commit range |
4982
+ | [diffArr] | <code>Array.&lt;TYPE.DeltaPkgItem&gt;</code> | instead of running git diff the method can also get a list of files to process |
4991
4983
  | [skipInteraction] | <code>TYPE.SkipInteraction</code> | allows to skip interactive wizard |
4992
4984
 
4993
4985
  <a name="DevOps.buildDeltaDefinitions..deltaDeployAll"></a>
@@ -6206,7 +6198,7 @@ Util that contains logger and simple util methods
6206
6198
  * [.inverseGet(objs, val)](#Util.inverseGet) ⇒ <code>string</code>
6207
6199
  * [.getMetadataHierachy(metadataTypes)](#Util.getMetadataHierachy) ⇒ <code>Array.&lt;string&gt;</code>
6208
6200
  * [.resolveObjPath(path, obj)](#Util.resolveObjPath) ⇒ <code>any</code>
6209
- * [.execSync(cmd, [args])](#Util.execSync) ⇒ <code>undefined</code>
6201
+ * [.execSync(cmd, [args], [hideOutput])](#Util.execSync) ⇒ <code>undefined</code>
6210
6202
  * [.templateSearchResult(results, keyToSearch, searchValue)](#Util.templateSearchResult) ⇒ <code>TYPE.MetadataTypeItem</code>
6211
6203
  * [.setLoggingLevel(argv)](#Util.setLoggingLevel) ⇒ <code>void</code>
6212
6204
 
@@ -6403,7 +6395,7 @@ let's you dynamically walk down an object and get a value
6403
6395
 
6404
6396
  <a name="Util.execSync"></a>
6405
6397
 
6406
- ### Util.execSync(cmd, [args]) ⇒ <code>undefined</code>
6398
+ ### Util.execSync(cmd, [args], [hideOutput]) ⇒ <code>undefined</code>
6407
6399
  helper to run other commands as if run manually by user
6408
6400
 
6409
6401
  **Kind**: static method of [<code>Util</code>](#Util)
@@ -6412,6 +6404,7 @@ helper to run other commands as if run manually by user
6412
6404
  | --- | --- | --- |
6413
6405
  | cmd | <code>string</code> | to be executed command |
6414
6406
  | [args] | <code>Array.&lt;string&gt;</code> | list of arguments |
6407
+ | [hideOutput] | <code>boolean</code> | if true, output of command will be hidden from CLI |
6415
6408
 
6416
6409
  <a name="Util.templateSearchResult"></a>
6417
6410
 
@@ -6491,9 +6484,11 @@ wrapper around our standard winston logging to console and logfile
6491
6484
  initiate winston logger
6492
6485
 
6493
6486
  **Kind**: global function
6494
- <a name="SupportedMetadataTypes"></a>
6487
+ <a name="TypeKeyCombo"></a>
6488
+
6489
+ ## TypeKeyCombo : <code>Object.&lt;string, string&gt;</code>
6490
+ object-key=metadata type, value=array of external keys
6495
6491
 
6496
- ## SupportedMetadataTypes : <code>Object.&lt;string, string&gt;</code>
6497
6492
  **Kind**: global typedef
6498
6493
  <a name="Cache"></a>
6499
6494
 
@@ -6776,7 +6771,7 @@ SOAP format
6776
6771
  | binary | <code>boolean</code> | is a binary file |
6777
6772
  | moved | <code>boolean</code> | git thinks this file was moved |
6778
6773
  | [fromPath] | <code>string</code> | git thinks this relative path is where the file was before |
6779
- | type | [<code>SupportedMetadataTypes</code>](#SupportedMetadataTypes) | metadata type |
6774
+ | type | <code>SupportedMetadataTypes</code> | metadata type |
6780
6775
  | externalKey | <code>string</code> | key |
6781
6776
  | name | <code>string</code> | name |
6782
6777
  | gitAction | <code>&#x27;move&#x27;</code> \| <code>&#x27;add/update&#x27;</code> \| <code>&#x27;delete&#x27;</code> | what git recognized as an action |
package/lib/Deployer.js CHANGED
@@ -45,7 +45,7 @@ class Deployer {
45
45
  * Deploys all metadata located in the 'deploy' directory to the specified business unit
46
46
  *
47
47
  * @param {string} businessUnit references credentials from properties.json
48
- * @param {string[]} [selectedTypesArr] limit deployment to given metadata type
48
+ * @param {TYPE.SupportedMetadataTypes[]} [selectedTypesArr] limit deployment to given metadata type
49
49
  * @param {string[]} [keyArr] limit deployment to given metadata keys
50
50
  * @param {boolean} [fromRetrieve] optionally deploy whats defined via selectedTypesArr + keyArr directly from retrieve folder instead of from deploy folder
51
51
  * @returns {Promise.<void>} -
@@ -158,7 +158,7 @@ class Deployer {
158
158
  * @param {string} cred name of Credential
159
159
  * @param {string} bu name of BU
160
160
  * @param {TYPE.Mcdevrc} properties General configuration to be used in retrieve
161
- * @param {string[]} [typeArr] limit deployment to given metadata type
161
+ * @param {TYPE.SupportedMetadataTypes[]} [typeArr] limit deployment to given metadata type
162
162
  * @param {string[]} [keyArr] limit deployment to given metadata keys
163
163
  * @param {boolean} [fromRetrieve] optionally deploy whats defined via selectedTypesArr + keyArr directly from retrieve folder instead of from deploy folder
164
164
  * @returns {Promise} ensure that BUs are worked on sequentially
@@ -182,7 +182,7 @@ class Deployer {
182
182
  /**
183
183
  * Deploy all metadata that is located in the deployDir
184
184
  *
185
- * @param {string[]} [typeArr] limit deployment to given metadata type (can include subtype)
185
+ * @param {TYPE.SupportedMetadataTypes[]} [typeArr] limit deployment to given metadata type (can include subtype)
186
186
  * @param {string[]} [keyArr] limit deployment to given metadata keys
187
187
  * @param {boolean} [fromRetrieve] if true, no folders will be updated/created
188
188
  * @returns {Promise} Promise
@@ -247,28 +247,10 @@ class Deployer {
247
247
  this.buObject
248
248
  );
249
249
  cache.mergeMetadata(type, result);
250
- this.deployCallback(result, type);
251
250
  }
252
251
  }
253
252
  }
254
253
 
255
- /**
256
- * Gets called for every deployed metadata entry
257
- *
258
- * @param {object} result Deployment result
259
- * @param {string} metadataType Name of metadata type
260
- * @returns {void}
261
- */
262
- deployCallback(result, metadataType) {
263
- if (result) {
264
- File.writeJSONToFile('logs/', 'deployResult_' + metadataType, result);
265
- Util.logger.verbose(
266
- 'Deployer.deployCallback:: Results written to: ' +
267
- path.normalize(process.cwd() + '/logs/deployResult_' + metadataType + '.json')
268
- );
269
- }
270
- }
271
-
272
254
  /**
273
255
  * Returns metadata of a business unit that is saved locally
274
256
  *
package/lib/Retriever.js CHANGED
@@ -31,8 +31,8 @@ class Retriever {
31
31
  /**
32
32
  * Retrieve metadata of specified types into local file system and Retriever.metadata
33
33
  *
34
- * @param {string[]} metadataTypes String list of metadata types to retrieve
35
- * @param {string[]} [namesOrKeys] name of Metadata to retrieveAsTemplate or list of keys for normal retrieval
34
+ * @param {TYPE.SupportedMetadataTypes[]} metadataTypes list of metadata types to retrieve; can include subtypes!
35
+ * @param {string[]|TYPE.TypeKeyCombo} [namesOrKeys] name of Metadata to retrieveAsTemplate or list of keys for normal retrieval
36
36
  * @param {TYPE.TemplateMap} [templateVariables] Object of values which can be replaced (in case of templating)
37
37
  * @param {boolean} [changelogOnly] skip saving, only create json in memory
38
38
  * @returns {Promise.<TYPE.MultiMetadataTypeList>} Promise of a list of retrieved items grouped by type {automation:[...], query:[...]}
@@ -42,12 +42,31 @@ class Retriever {
42
42
  * @type {TYPE.MultiMetadataTypeList}
43
43
  */
44
44
  const retrieveChangelog = {};
45
- if (!namesOrKeys || !namesOrKeys.length) {
46
- // make iterating over namesOrKeys easier
45
+ if (!namesOrKeys || (Array.isArray(namesOrKeys) && !namesOrKeys.length)) {
46
+ // no keys were provided, ensure we retrieve all
47
47
  namesOrKeys = [null];
48
48
  }
49
+ /** @type {TYPE.TypeKeyCombo} */
50
+ let typeKeyMap = {};
51
+ if (Array.isArray(namesOrKeys)) {
52
+ // no keys or array of keys was provided (likely called via CLI or to retrieve all)
53
+ // transform into TypeKeyCombo to iterate over it
54
+ for (const type of metadataTypes) {
55
+ typeKeyMap[type] = namesOrKeys;
56
+ }
57
+ } else {
58
+ // assuming TypeKeyCombo was provided
59
+ typeKeyMap = namesOrKeys;
60
+ }
61
+ // defined colors for optionally printing the keys we filtered by
62
+ const color = {
63
+ reset: '\x1B[0m',
64
+ dim: '\x1B[2m',
65
+ };
49
66
  for (const metadataType of Util.getMetadataHierachy(metadataTypes)) {
50
67
  const [type, subType] = metadataType.split('-');
68
+ // if types were added by getMetadataHierachy() for caching, make sure the key-list is set to [null] for them which will retrieve all
69
+ typeKeyMap[metadataType] = typeKeyMap[metadataType] || [null];
51
70
  // add client to metadata process class instead of passing every time
52
71
  MetadataTypeInfo[type].client = auth.getSDK(this.buObject);
53
72
  MetadataTypeInfo[type].properties = this.properties;
@@ -65,7 +84,7 @@ class Retriever {
65
84
  Util.logger.info(`Retrieving as Template: ${metadataType}`);
66
85
 
67
86
  result = await Promise.all(
68
- namesOrKeys.map((name) =>
87
+ typeKeyMap[metadataType].map((name) =>
69
88
  MetadataTypeInfo[type].retrieveAsTemplate(
70
89
  this.templateDir,
71
90
  name,
@@ -75,11 +94,18 @@ class Retriever {
75
94
  )
76
95
  );
77
96
  } else {
78
- Util.logger.info('Retrieving: ' + metadataType);
97
+ Util.logger.info(
98
+ `Retrieving: ${metadataType}` +
99
+ (typeKeyMap[metadataType][0] !== null
100
+ ? ` ${color.dim}(Keys: ${typeKeyMap[metadataType].join(', ')})${
101
+ color.reset
102
+ }`
103
+ : '')
104
+ );
79
105
  result = await (changelogOnly
80
106
  ? MetadataTypeInfo[type].retrieveChangelog(this.buObject, null, subType)
81
107
  : Promise.all(
82
- namesOrKeys.map((key) =>
108
+ typeKeyMap[metadataType].map((key) =>
83
109
  MetadataTypeInfo[type].retrieve(
84
110
  this.savePath,
85
111
  null,
package/lib/index.js CHANGED
@@ -49,6 +49,7 @@ class Mcdev {
49
49
  * @param {string} [argv.range] git commit range
50
50
  into deploy directory
51
51
  * @param {string} [argv.filter] filter file paths that start with any
52
+ * @param {TYPE.DeltaPkgItem[]} [argv.diffArr] list of files to include in delta package (skips git diff when provided)
52
53
  * @param {TYPE.skipInteraction} [argv.skipInteraction] allows to skip interactive wizard
53
54
  * @returns {Promise.<TYPE.DeltaPkgItem[]>} list of changed items
54
55
  */
@@ -64,7 +65,12 @@ class Mcdev {
64
65
  ? // get source market and source BU from config
65
66
  DevOps.getDeltaList(properties, argv.range, true, argv.filter)
66
67
  : // If no custom filter was provided, use deployment marketLists & templating
67
- DevOps.buildDeltaDefinitions(properties, argv.range, argv.skipInteraction);
68
+ DevOps.buildDeltaDefinitions(
69
+ properties,
70
+ argv.range,
71
+ argv.diffArr,
72
+ argv.skipInteraction
73
+ );
68
74
  }
69
75
 
70
76
  /**
@@ -105,7 +111,7 @@ class Mcdev {
105
111
  * Retrieve all metadata from the specified business unit into the local file system.
106
112
  *
107
113
  * @param {string} businessUnit references credentials from properties.json
108
- * @param {string[]} [selectedTypesArr] limit retrieval to given metadata type
114
+ * @param {TYPE.SupportedMetadataTypes[]|TYPE.TypeKeyCombo} [selectedTypesArr] limit retrieval to given metadata type
109
115
  * @param {string[]} [keys] limit retrieval to given metadata key
110
116
  * @param {boolean} [changelogOnly] skip saving, only create json in memory
111
117
  * @returns {Promise.<object>} -
@@ -120,7 +126,9 @@ class Mcdev {
120
126
 
121
127
  // assume a list was passed in and check each entry's validity
122
128
  if (selectedTypesArr) {
123
- for (const selectedType of selectedTypesArr) {
129
+ for (const selectedType of Array.isArray(selectedTypesArr)
130
+ ? selectedTypesArr
131
+ : Object.keys(selectedTypesArr)) {
124
132
  if (!Util._isValidType(selectedType)) {
125
133
  return;
126
134
  }
@@ -195,7 +203,7 @@ class Mcdev {
195
203
  * @private
196
204
  * @param {string} cred name of Credential
197
205
  * @param {string} bu name of BU
198
- * @param {string[]} [selectedTypesArr] limit retrieval to given metadata type/subtype
206
+ * @param {TYPE.SupportedMetadataTypes[]|TYPE.TypeKeyCombo} [selectedTypesArr] limit retrieval to given metadata type/subtype
199
207
  * @param {string[]} [keys] limit retrieval to given metadata key
200
208
  * @param {boolean} [changelogOnly] skip saving, only create json in memory
201
209
  * @returns {Promise.<object>} ensure that BUs are worked on sequentially
@@ -218,7 +226,9 @@ class Mcdev {
218
226
  Util.logger.info(`\n :: Retrieving ${cred}/${bu}\n`);
219
227
  const retrieveTypesArr = [];
220
228
  if (selectedTypesArr) {
221
- for (const selectedType of selectedTypesArr) {
229
+ for (const selectedType of Array.isArray(selectedTypesArr)
230
+ ? selectedTypesArr
231
+ : Object.keys(selectedTypesArr)) {
222
232
  const [type, subType] = selectedType ? selectedType.split('-') : [];
223
233
  const removePathArr = [properties.directories.retrieve, cred, bu, type];
224
234
  if (
@@ -255,7 +265,7 @@ class Mcdev {
255
265
  // await is required or the calls end up conflicting
256
266
  const retrieveChangelog = await retriever.retrieve(
257
267
  retrieveTypesArr,
258
- keys,
268
+ Array.isArray(selectedTypesArr) ? keys : selectedTypesArr,
259
269
  null,
260
270
  changelogOnly
261
271
  );
@@ -272,7 +282,7 @@ class Mcdev {
272
282
  * Deploys all metadata located in the 'deploy' directory to the specified business unit
273
283
  *
274
284
  * @param {string} businessUnit references credentials from properties.json
275
- * @param {string[]} [selectedTypesArr] limit deployment to given metadata type
285
+ * @param {TYPE.SupportedMetadataTypes[]} [selectedTypesArr] limit deployment to given metadata type
276
286
  * @param {string[]} [keyArr] limit deployment to given metadata keys
277
287
  * @param {boolean} [fromRetrieve] optionally deploy whats defined via selectedTypesArr + keyArr directly from retrieve folder instead of from deploy folder
278
288
  * @returns {Promise.<void>} -
@@ -276,7 +276,11 @@ class Asset extends MetadataType {
276
276
  } else if (retrieveDir && !items.length) {
277
277
  Util.logger.info(` Downloaded asset-${subType}: ${items.length}`);
278
278
  }
279
- return items;
279
+
280
+ return items.map((item) => {
281
+ item._subType = subType;
282
+ return item;
283
+ });
280
284
  }
281
285
  /**
282
286
  * Retrieves extended metadata (files or extended content) of asset
@@ -446,20 +450,33 @@ class Asset extends MetadataType {
446
450
  * @param {TYPE.AssetItem} metadata a single asset
447
451
  * @param {TYPE.AssetSubType} subType group of similar assets to put in a folder (ie. images)
448
452
  * @param {string} deployDir directory of deploy files
449
- * @returns {Promise.<void>} -
453
+ * @param {boolean} [pathOnly=false] used by getFilesToCommit which does not need the binary file to be actually read
454
+ * @returns {Promise.<string>} if found will return the path of the binary file
450
455
  */
451
- static async _readExtendedFileFromFS(metadata, subType, deployDir) {
452
- if (metadata.fileProperties && metadata.fileProperties.extension) {
453
- // to handle uploaded files that bear the same name, SFMC engineers decided to add a number after the fileName
454
- // however, their solution was not following standards: fileName="header.png (4) " and then extension="png (4) "
455
- const fileExt = metadata.fileProperties.extension.split(' ')[0];
456
+ static async _readExtendedFileFromFS(metadata, subType, deployDir, pathOnly = false) {
457
+ // to handle uploaded files that bear the same name, SFMC engineers decided to add a number after the fileName
458
+ // however, their solution was not following standards: fileName="header.png (4) " and then extension="png (4) "
459
+ if (!metadata?.fileProperties?.extension) {
460
+ return;
461
+ }
462
+ const fileExt = metadata.fileProperties.extension.split(' ')[0];
456
463
 
457
- metadata.file = await File.readFilteredFilename(
458
- [deployDir, this.definition.type, subType],
459
- metadata.customerKey,
460
- fileExt,
461
- 'base64'
462
- );
464
+ const path = File.normalizePath([
465
+ deployDir,
466
+ this.definition.type,
467
+ subType,
468
+ `${metadata.customerKey}.${fileExt}`,
469
+ ]);
470
+ if (await File.pathExists(path)) {
471
+ if (!pathOnly) {
472
+ metadata.file = await File.readFilteredFilename(
473
+ [deployDir, this.definition.type, subType],
474
+ metadata.customerKey,
475
+ fileExt,
476
+ 'base64'
477
+ );
478
+ }
479
+ return path;
463
480
  }
464
481
  }
465
482
  /**
@@ -512,8 +529,8 @@ class Asset extends MetadataType {
512
529
  metadata.memberId !== buObject.mid &&
513
530
  !metadata[this.definition.keyField].startsWith(buObject.mid)
514
531
  ) {
515
- // #3 make sure customer key is unique by prefixing it with target MID (unless we are deploying to the same MID)
516
- // check if this prefixed with the source MID
532
+ // #3 make sure customer key is unique by suffixing it with target MID (unless we are deploying to the same MID)
533
+ // check if this suffixed with the source MID
517
534
  const suffix = '-' + buObject.mid;
518
535
  // for customer key max is 36 chars
519
536
  metadata[this.definition.keyField] =
@@ -1362,6 +1379,7 @@ class Asset extends MetadataType {
1362
1379
  const fileList = (
1363
1380
  await Promise.all(
1364
1381
  keyArr.map(async (key) => {
1382
+ // get subType, path an fileName by scanning the retrieve folder
1365
1383
  let subType;
1366
1384
  let filePath;
1367
1385
  let fileName;
@@ -1391,8 +1409,13 @@ class Asset extends MetadataType {
1391
1409
  break;
1392
1410
  }
1393
1411
  }
1394
- if (await File.pathExists(File.normalizePath([...filePath, fileName]))) {
1412
+ if (
1413
+ Array.isArray(filePath) &&
1414
+ (await File.pathExists(File.normalizePath([...filePath, fileName])))
1415
+ ) {
1416
+ // #1 load json to be able to find extracted text files & binary files
1395
1417
  const metadata = File.readJSONFile(filePath, fileName, true, false);
1418
+ // #2 find all extracted text files
1396
1419
  const fileListNested = (
1397
1420
  await this._mergeCode(metadata, basePath, subType, metadata.customerKey)
1398
1421
  ).map((item) =>
@@ -1402,8 +1425,22 @@ class Asset extends MetadataType {
1402
1425
  `${item.fileName}.${item.fileExt}`,
1403
1426
  ])
1404
1427
  );
1428
+ const response = [
1429
+ File.normalizePath([...filePath, fileName]),
1430
+ ...fileListNested,
1431
+ ];
1432
+ // #3 get binary file
1433
+ const binaryFilePath = await this._readExtendedFileFromFS(
1434
+ metadata,
1435
+ subType,
1436
+ basePath,
1437
+ false
1438
+ );
1439
+ if (binaryFilePath) {
1440
+ response.push(binaryFilePath);
1441
+ }
1405
1442
 
1406
- return [File.normalizePath([...filePath, fileName]), ...fileListNested];
1443
+ return response;
1407
1444
  } else {
1408
1445
  return [];
1409
1446
  }
@@ -792,9 +792,8 @@ class Automation extends MetadataType {
792
792
  output += `* Recurrance: every ${ical.INTERVAL > 1 ? ical.INTERVAL : ''} ${
793
793
  frequency === 'dai' ? 'day' : frequency
794
794
  }${ical.INTERVAL > 1 ? 's' : ''} ${ical.COUNT ? `for ${ical.COUNT} times` : ''}\n`;
795
- output += '\n';
796
795
  } else if (json.schedule) {
797
- output += `**Schedule:** Not defined\n\n`;
796
+ output += `**Schedule:** Not defined\n`;
798
797
  }
799
798
  } else if (json.type === 'triggered' && json.fileTrigger) {
800
799
  output += `**File Trigger:**\n\n`;
@@ -803,12 +802,19 @@ class Automation extends MetadataType {
803
802
  output += `* Pattern: ${json.fileTrigger.fileNamingPattern}\n`;
804
803
  output += `* Folder: ${json.fileTrigger.folderLocationText}\n`;
805
804
  }
805
+ // show table with automation steps
806
806
  if (tabled && tabled.length) {
807
+ // add empty line to ensure the following table is rendered properly
808
+ output += '\n';
807
809
  let tableSeparator = '';
808
810
  const row1 = [];
809
811
  for (const column of tabled[0]) {
810
812
  row1.push(
811
- `| ${column.title}${column.description ? `<br>_${column.description}_` : ''} `
813
+ `| ${column.title}${
814
+ column.description
815
+ ? `<br>_<small>${column.description.replace(/\n/g, '<br>')}</small>_`
816
+ : ''
817
+ } `
812
818
  );
813
819
  tableSeparator += '| --- ';
814
820
  }
@@ -284,7 +284,7 @@ class Script extends MetadataType {
284
284
  ssjs = metadata.script;
285
285
  fileExt = 'html';
286
286
  Util.logger.warn(
287
- ' - Could not find script tags, saving whole text in SSJS: ' + metadata.name
287
+ ` - Could not find script tags, saving code in ${metadata.name}.script-meta.html instead of as SSJS file.`
288
288
  );
289
289
  }
290
290
  delete metadata.script;
@@ -315,6 +315,7 @@ class Script extends MetadataType {
315
315
  const fileList = keyArr.flatMap((key) => [
316
316
  File.normalizePath([path, `${key}.${this.definition.type}-meta.json`]),
317
317
  File.normalizePath([path, `${key}.${this.definition.type}-meta.ssjs`]),
318
+ File.normalizePath([path, `${key}.${this.definition.type}-meta.html`]),
318
319
  ]);
319
320
  return fileList;
320
321
  }
@@ -248,10 +248,11 @@ const DevOps = {
248
248
  *
249
249
  * @param {TYPE.Mcdevrc} properties project config file
250
250
  * @param {string} range git commit range
251
+ * @param {TYPE.DeltaPkgItem[]} [diffArr] instead of running git diff the method can also get a list of files to process
251
252
  * @param {TYPE.SkipInteraction} [skipInteraction] allows to skip interactive wizard
252
253
  * @returns {Promise.<TYPE.DeltaPkgItem[]>} -
253
254
  */
254
- async buildDeltaDefinitions(properties, range, skipInteraction) {
255
+ async buildDeltaDefinitions(properties, range, diffArr, skipInteraction) {
255
256
  // check if sourceTargetMapping is valid
256
257
  if (
257
258
  !properties.options.deployment.sourceTargetMapping ||
@@ -304,7 +305,9 @@ const DevOps = {
304
305
  const sourceBU = Object.keys(properties.marketList[sourceMlName])[0];
305
306
  const sourceMarket = Object.values(properties.marketList[sourceMlName])[0];
306
307
 
307
- const delta = await DevOps.getDeltaList(properties, range, false, sourceBU);
308
+ const delta = Array.isArray(diffArr)
309
+ ? diffArr
310
+ : await DevOps.getDeltaList(properties, range, false, sourceBU);
308
311
  // If only chaing templating and buildDefinition if required
309
312
  if (!delta || delta.length === 0) {
310
313
  // info/error messages was printed by DevOps.createDeltaPkg() already
package/lib/util/init.js CHANGED
@@ -161,7 +161,7 @@ const Init = {
161
161
  if (initGit.status === 'init') {
162
162
  Util.logger.info(`Committing initial setup to Git:`);
163
163
  Util.execSync('git', ['add', '.']);
164
- Util.execSync('git', ['commit', '-m', '"Initial commit"', '--quiet']);
164
+ Util.execSync('git', ['commit', '-n', '-m', '"Initial commit"', '--quiet']);
165
165
  Util.logger.info(`✔️ Configuration committed`);
166
166
  }
167
167
 
@@ -204,7 +204,7 @@ const Init = {
204
204
  if (gitStatus === 'init') {
205
205
  Util.logger.info(`Committing first backup of your SFMC instance:`);
206
206
  Util.execSync('git', ['add', '.']);
207
- Util.execSync('git', ['commit', '-m', '"First instance backup"', '--quiet']);
207
+ Util.execSync('git', ['commit', '-n', '-m', '"First instance backup"', '--quiet']);
208
208
  Util.logger.info(`✔️ SFMC instance backed up`);
209
209
  } else if (gitStatus === 'update') {
210
210
  Util.logger.warn(
@@ -49,7 +49,7 @@ const Init = {
49
49
  this._getDefaultPackageJson(projectPackageJson);
50
50
  await File.writeToFile('./', 'package', 'json', JSON.stringify(projectPackageJson));
51
51
  // execute "no questions asked" npm init
52
- Util.execSync('npm', ['init', '--yes']);
52
+ Util.execSync('npm', ['init', '--yes'], true);
53
53
  try {
54
54
  fileContent = File.readFileSync('package.json', 'utf8');
55
55
  if (fileContent) {
package/lib/util/util.js CHANGED
@@ -353,15 +353,16 @@ const Util = {
353
353
  *
354
354
  * @param {string} cmd to be executed command
355
355
  * @param {string[]} [args] list of arguments
356
+ * @param {boolean} [hideOutput] if true, output of command will be hidden from CLI
356
357
  * @returns {undefined}
357
358
  */
358
- execSync(cmd, args) {
359
+ execSync(cmd, args, hideOutput) {
359
360
  args = args || [];
360
361
  Util.logger.info('⚡ ' + cmd + ' ' + args.join(' '));
361
362
 
362
363
  // the following options ensure the user sees the same output and
363
364
  // interaction options as if the command was manually run
364
- const options = { stdio: [0, 1, 2] };
365
+ const options = hideOutput ? {} : { stdio: [0, 1, 2] };
365
366
  return child_process.execSync(cmd + ' ' + args.join(' '), options);
366
367
  },
367
368
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcdev",
3
- "version": "4.0.1",
3
+ "version": "4.1.0",
4
4
  "description": "Accenture Salesforce Marketing Cloud DevTools",
5
5
  "author": "joern.berkefeld, douglas.midgley, robert.zimmermann, maciej.barnas",
6
6
  "license": "MIT",
@@ -36,7 +36,7 @@
36
36
  "lint-type": "eslint types/*.js",
37
37
  "lint-test": "eslint test/**/*.js",
38
38
  "upgrade": "npm-check --update",
39
- "prepare": "husky install",
39
+ "manual-prepare": "husky install",
40
40
  "test": "mocha"
41
41
  },
42
42
  "dependencies": {
@@ -67,14 +67,14 @@
67
67
  "eslint": "8.25.0",
68
68
  "eslint-config-prettier": "8.5.0",
69
69
  "eslint-config-ssjs": "1.1.11",
70
- "eslint-plugin-jsdoc": "39.3.6",
70
+ "eslint-plugin-jsdoc": "39.3.13",
71
71
  "eslint-plugin-mocha": "10.1.0",
72
72
  "eslint-plugin-prettier": "4.2.1",
73
73
  "eslint-plugin-unicorn": "44.0.2",
74
74
  "husky": "8.0.1",
75
75
  "jsdoc-to-markdown": "7.1.1",
76
76
  "lint-staged": "13.0.3",
77
- "mocha": "10.0.0",
77
+ "mocha": "10.1.0",
78
78
  "mock-fs": "5.1.4",
79
79
  "npm-check": "6.0.1",
80
80
  "npm-run-all": "4.1.5",
package/types/mcdev.d.js CHANGED
@@ -12,7 +12,8 @@ const SDK = require('sfmc-sdk');
12
12
  */
13
13
  /**
14
14
  * @typedef {Object.<string, string>} TemplateMap
15
- * @typedef {'accountUser'|'asset'|'attributeGroup'|'automation'|'campaign'|'contentArea'|'dataExtension'|'dataExtensionField'|'dataExtensionTemplate'|'dataExtract'|'dataExtractType'|'discovery'|'email'|'emailSendDefinition'|'eventDefinition'|'fileTransfer'|'filter'|'folder'|'ftpLocation'|'importFile'|'interaction'|'list'|'mobileCode'|'mobileKeyword'|'query'|'role'|'script'|'setDefinition'|'triggeredSendDefinition'} SupportedMetadataTypes
15
+ * @typedef {'accountUser'|'asset'|'asset-archive'|'asset-asset'|'asset-audio'|'asset-block'|'asset-code'|'asset-document'|'asset-image'|'asset-message'|'asset-other'|'asset-rawimage'|'asset-template'|'asset-textfile'|'asset-video'|'attributeGroup'|'automation'|'campaign'|'contentArea'|'dataExtension'|'dataExtensionField'|'dataExtensionTemplate'|'dataExtract'|'dataExtractType'|'discovery'|'email'|'emailSendDefinition'|'eventDefinition'|'fileTransfer'|'filter'|'folder'|'ftpLocation'|'importFile'|'interaction'|'list'|'mobileCode'|'mobileKeyword'|'query'|'role'|'script'|'setDefinition'|'triggeredSendDefinition'} SupportedMetadataTypes
16
+ * @typedef {Object.<SupportedMetadataTypes, string[]>} TypeKeyCombo object-key=metadata type, value=array of external keys
16
17
  */
17
18
 
18
19
  /**