mcdev 4.0.0 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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?
@@ -11,4 +11,4 @@ updates:
11
11
  interval: 'daily'
12
12
  target-branch: 'develop'
13
13
  labels:
14
- - 'chore'
14
+ - 'dependencies'
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.0.2`).
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.0.2
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.0.2)!
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
 
@@ -1509,7 +1511,7 @@ If you use Accenture SFMC DevTools in your team it is recommended to install you
1509
1511
 
1510
1512
  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
1513
 
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.
1514
+ To test your new **global** developer setup, run `mcdev --version` in CLI which should return the current version (e.g. `4.0.2`). Then, go into your mcdev repo and update the version with the suffix `-dev`, e.g. to `4.0.2-dev` and then run `mcdev --version` again to verify that your change propagates instantly.
1513
1515
 
1514
1516
  <a name="local-install"></a>
1515
1517
 
@@ -1536,7 +1538,7 @@ The following explains how you _could_ install it locally for certain edge cases
1536
1538
  4. Afterwards, install Accenture SFMC DevTools by running `npm install --save-dev mcdev`
1537
1539
  - If you get an error, please see the below troubleshooting section.
1538
1540
 
1539
- When completed you will see `+ mcdev@3.2.0` printed to your screen (or the current version of it respectively).
1541
+ When completed run `mcdev --version` and it will show you which version you installed (e.g. `4.0.2`).
1540
1542
 
1541
1543
  ### 9.3. NPM Scripts
1542
1544
 
@@ -17,8 +17,10 @@
17
17
  "*.ics": "${capture}.asset-document-meta.json",
18
18
  "*.pdf": "${capture}.asset-document-meta.json",
19
19
  "*.jpg": "${capture}.asset-image-meta.json",
20
+ "*.jpeg": "${capture}.asset-image-meta.json",
20
21
  "*.png": "${capture}.asset-image-meta.json",
21
22
  "*.gif": "${capture}.asset-image-meta.json",
23
+ "*.eps": "${capture}.asset-image-meta.json",
22
24
  "*-meta.sql": "${capture}-meta.json",
23
25
  "*-meta.ssjs": "${capture}-meta.json",
24
26
  "*-meta.amp": "${capture}-meta.json",
@@ -244,7 +244,7 @@ Builds metadata from a template using market specific customisation
244
244
  * _static_
245
245
  * [.buildTemplate(businessUnit, selectedType, keyArr, market)](#Builder.buildTemplate) ⇒ <code>Promise.&lt;TYPE.MultiMetadataTypeList&gt;</code>
246
246
  * [.buildDefinition(businessUnit, selectedType, name, market)](#Builder.buildDefinition) ⇒ <code>Promise.&lt;TYPE.MultiMetadataTypeList&gt;</code>
247
- * [.buildDefinitionBulk(listName, type, name)](#Builder.buildDefinitionBulk) ⇒ <code>Promise.&lt;void&gt;</code>
247
+ * [.buildDefinitionBulk(listName, type, name)](#Builder.buildDefinitionBulk) ⇒ <code>Promise.&lt;Array.&lt;TYPE.MultiMetadataTypeList&gt;&gt;</code>
248
248
 
249
249
  <a name="new_Builder_new"></a>
250
250
 
@@ -321,11 +321,11 @@ Build a specific metadata file based on a template.
321
321
 
322
322
  <a name="Builder.buildDefinitionBulk"></a>
323
323
 
324
- ### Builder.buildDefinitionBulk(listName, type, name) ⇒ <code>Promise.&lt;void&gt;</code>
324
+ ### Builder.buildDefinitionBulk(listName, type, name) ⇒ <code>Promise.&lt;Array.&lt;TYPE.MultiMetadataTypeList&gt;&gt;</code>
325
325
  Build a specific metadata file based on a template using a list of bu-market combos
326
326
 
327
327
  **Kind**: static method of [<code>Builder</code>](#Builder)
328
- **Returns**: <code>Promise.&lt;void&gt;</code> - -
328
+ **Returns**: <code>Promise.&lt;Array.&lt;TYPE.MultiMetadataTypeList&gt;&gt;</code> - -
329
329
 
330
330
  | Param | Type | Description |
331
331
  | --- | --- | --- |
@@ -4448,7 +4448,7 @@ CLI entry for SFMC DevTools
4448
4448
  * [.inverseGet(objs, val)](#Util.inverseGet) ⇒ <code>string</code>
4449
4449
  * [.getMetadataHierachy(metadataTypes)](#Util.getMetadataHierachy) ⇒ <code>Array.&lt;string&gt;</code>
4450
4450
  * [.resolveObjPath(path, obj)](#Util.resolveObjPath) ⇒ <code>any</code>
4451
- * [.execSync(cmd, [args])](#Util.execSync) ⇒ <code>undefined</code>
4451
+ * [.execSync(cmd, [args], [hideOutput])](#Util.execSync) ⇒ <code>undefined</code>
4452
4452
  * [.templateSearchResult(results, keyToSearch, searchValue)](#Util.templateSearchResult) ⇒ <code>TYPE.MetadataTypeItem</code>
4453
4453
  * [.setLoggingLevel(argv)](#Util.setLoggingLevel) ⇒ <code>void</code>
4454
4454
 
@@ -4645,7 +4645,7 @@ let's you dynamically walk down an object and get a value
4645
4645
 
4646
4646
  <a name="Util.execSync"></a>
4647
4647
 
4648
- ### Util.execSync(cmd, [args]) ⇒ <code>undefined</code>
4648
+ ### Util.execSync(cmd, [args], [hideOutput]) ⇒ <code>undefined</code>
4649
4649
  helper to run other commands as if run manually by user
4650
4650
 
4651
4651
  **Kind**: static method of [<code>Util</code>](#Util)
@@ -4654,6 +4654,7 @@ helper to run other commands as if run manually by user
4654
4654
  | --- | --- | --- |
4655
4655
  | cmd | <code>string</code> | to be executed command |
4656
4656
  | [args] | <code>Array.&lt;string&gt;</code> | list of arguments |
4657
+ | [hideOutput] | <code>boolean</code> | if true, output of command will be hidden from CLI |
4657
4658
 
4658
4659
  <a name="Util.templateSearchResult"></a>
4659
4660
 
@@ -4942,7 +4943,8 @@ DevOps helper class
4942
4943
  * [.getDeltaList(properties, [range], [saveToDeployDir], [filterPaths])](#DevOps.getDeltaList) ⇒ <code>Promise.&lt;Array.&lt;TYPE.DeltaPkgItem&gt;&gt;</code>
4943
4944
  * [~delta](#DevOps.getDeltaList..delta) : <code>Array.&lt;TYPE.DeltaPkgItem&gt;</code>
4944
4945
  * [~copied](#DevOps.getDeltaList..copied) : <code>TYPE.DeltaPkgItem</code>
4945
- * [.buildDeltaDefinitions(properties, range, [skipInteraction])](#DevOps.buildDeltaDefinitions)
4946
+ * [.buildDeltaDefinitions(properties, range, [skipInteraction])](#DevOps.buildDeltaDefinitions) ⇒ <code>Promise.&lt;Array.&lt;TYPE.DeltaPkgItem&gt;&gt;</code>
4947
+ * [~deltaDeployAll](#DevOps.buildDeltaDefinitions..deltaDeployAll) : <code>Array.&lt;TYPE.DeltaPkgItem&gt;</code>
4946
4948
  * [.document(directory, jsonReport)](#DevOps.document) ⇒ <code>void</code>
4947
4949
  * [.getFilesToCommit(properties, buObject, metadataType, keyArr)](#DevOps.getFilesToCommit) ⇒ <code>Promise.&lt;Array.&lt;string&gt;&gt;</code>
4948
4950
 
@@ -4977,10 +4979,11 @@ Interactive commit selection if no commits are passed.
4977
4979
  **Kind**: inner constant of [<code>getDeltaList</code>](#DevOps.getDeltaList)
4978
4980
  <a name="DevOps.buildDeltaDefinitions"></a>
4979
4981
 
4980
- ### DevOps.buildDeltaDefinitions(properties, range, [skipInteraction])
4982
+ ### DevOps.buildDeltaDefinitions(properties, range, [skipInteraction]) ⇒ <code>Promise.&lt;Array.&lt;TYPE.DeltaPkgItem&gt;&gt;</code>
4981
4983
  wrapper around DevOps.getDeltaList, Builder.buildTemplate and M
4982
4984
 
4983
4985
  **Kind**: static method of [<code>DevOps</code>](#DevOps)
4986
+ **Returns**: <code>Promise.&lt;Array.&lt;TYPE.DeltaPkgItem&gt;&gt;</code> - -
4984
4987
 
4985
4988
  | Param | Type | Description |
4986
4989
  | --- | --- | --- |
@@ -4988,6 +4991,10 @@ wrapper around DevOps.getDeltaList, Builder.buildTemplate and M
4988
4991
  | range | <code>string</code> | git commit range |
4989
4992
  | [skipInteraction] | <code>TYPE.SkipInteraction</code> | allows to skip interactive wizard |
4990
4993
 
4994
+ <a name="DevOps.buildDeltaDefinitions..deltaDeployAll"></a>
4995
+
4996
+ #### buildDeltaDefinitions~deltaDeployAll : <code>Array.&lt;TYPE.DeltaPkgItem&gt;</code>
4997
+ **Kind**: inner constant of [<code>buildDeltaDefinitions</code>](#DevOps.buildDeltaDefinitions)
4991
4998
  <a name="DevOps.document"></a>
4992
4999
 
4993
5000
  ### DevOps.document(directory, jsonReport) ⇒ <code>void</code>
@@ -6200,7 +6207,7 @@ Util that contains logger and simple util methods
6200
6207
  * [.inverseGet(objs, val)](#Util.inverseGet) ⇒ <code>string</code>
6201
6208
  * [.getMetadataHierachy(metadataTypes)](#Util.getMetadataHierachy) ⇒ <code>Array.&lt;string&gt;</code>
6202
6209
  * [.resolveObjPath(path, obj)](#Util.resolveObjPath) ⇒ <code>any</code>
6203
- * [.execSync(cmd, [args])](#Util.execSync) ⇒ <code>undefined</code>
6210
+ * [.execSync(cmd, [args], [hideOutput])](#Util.execSync) ⇒ <code>undefined</code>
6204
6211
  * [.templateSearchResult(results, keyToSearch, searchValue)](#Util.templateSearchResult) ⇒ <code>TYPE.MetadataTypeItem</code>
6205
6212
  * [.setLoggingLevel(argv)](#Util.setLoggingLevel) ⇒ <code>void</code>
6206
6213
 
@@ -6397,7 +6404,7 @@ let's you dynamically walk down an object and get a value
6397
6404
 
6398
6405
  <a name="Util.execSync"></a>
6399
6406
 
6400
- ### Util.execSync(cmd, [args]) ⇒ <code>undefined</code>
6407
+ ### Util.execSync(cmd, [args], [hideOutput]) ⇒ <code>undefined</code>
6401
6408
  helper to run other commands as if run manually by user
6402
6409
 
6403
6410
  **Kind**: static method of [<code>Util</code>](#Util)
@@ -6406,6 +6413,7 @@ helper to run other commands as if run manually by user
6406
6413
  | --- | --- | --- |
6407
6414
  | cmd | <code>string</code> | to be executed command |
6408
6415
  | [args] | <code>Array.&lt;string&gt;</code> | list of arguments |
6416
+ | [hideOutput] | <code>boolean</code> | if true, output of command will be hidden from CLI |
6409
6417
 
6410
6418
  <a name="Util.templateSearchResult"></a>
6411
6419
 
package/lib/Builder.js CHANGED
@@ -199,7 +199,7 @@ saved
199
199
  * @param {string} listName name of list of BU-market combos
200
200
  * @param {string} type supported metadata type
201
201
  * @param {string} name name of the metadata
202
- * @returns {Promise.<void>} -
202
+ * @returns {Promise.<TYPE.MultiMetadataTypeList[]>} -
203
203
  */
204
204
  static async buildDefinitionBulk(listName, type, name) {
205
205
  const properties = await config.getProperties();
@@ -212,6 +212,8 @@ saved
212
212
  return;
213
213
  }
214
214
  let i = 0;
215
+ const bdPromises = [];
216
+
215
217
  for (const businessUnit in properties.marketList[listName]) {
216
218
  if (businessUnit === 'description') {
217
219
  // skip, it's just a metadata on this list and not a BU
@@ -228,13 +230,17 @@ saved
228
230
  for (const market of marketList) {
229
231
  if (Util.checkMarket(market, properties)) {
230
232
  Util.logger.info(`Executing for '${businessUnit}': '${market}'`);
231
- this.buildDefinition(businessUnit, type, name, market);
233
+ // omitting "await" to speed up creation
234
+ bdPromises.push(this.buildDefinition(businessUnit, type, name, market));
232
235
  }
233
236
  }
234
237
  }
238
+ const response = await Promise.all(bdPromises);
239
+
235
240
  if (!i) {
236
241
  Util.logger.error('Please define properties.marketList in your config');
237
242
  }
243
+ return response;
238
244
  }
239
245
  }
240
246
 
@@ -135,32 +135,40 @@ class AccountUser extends MetadataType {
135
135
  }
136
136
  let result;
137
137
  switch (interval) {
138
- case 'years':
138
+ case 'years': {
139
139
  result = now.getFullYear() - date.getFullYear();
140
140
  break;
141
- case 'months':
141
+ }
142
+ case 'months': {
142
143
  result =
143
144
  now.getFullYear() * 12 +
144
145
  now.getMonth() -
145
146
  (date.getFullYear() * 12 + date.getMonth());
146
147
  break;
147
- case 'weeks':
148
+ }
149
+ case 'weeks': {
148
150
  result = Math.floor(timediff / week);
149
151
  break;
150
- case 'days':
152
+ }
153
+ case 'days': {
151
154
  result = Math.floor(timediff / day);
152
155
  break;
153
- case 'hours':
156
+ }
157
+ case 'hours': {
154
158
  result = Math.floor(timediff / hour);
155
159
  break;
156
- case 'minutes':
160
+ }
161
+ case 'minutes': {
157
162
  result = Math.floor(timediff / minute);
158
163
  break;
159
- case 'seconds':
164
+ }
165
+ case 'seconds': {
160
166
  result = Math.floor(timediff / second);
161
167
  break;
162
- default:
168
+ }
169
+ default: {
163
170
  return;
171
+ }
164
172
  }
165
173
  return result + ' ' + interval;
166
174
  }
@@ -375,17 +375,21 @@ class Asset extends MetadataType {
375
375
  // reset logging level
376
376
  let obj;
377
377
  switch (loggerLevelBak) {
378
- case 'info':
378
+ case 'info': {
379
379
  obj = {};
380
380
  break;
381
- case 'verbose':
381
+ }
382
+ case 'verbose': {
382
383
  obj = { verbose: true };
383
384
  break;
384
- case 'debug':
385
+ }
386
+ case 'debug': {
385
387
  obj = { debug: true };
386
388
  break;
387
- case 'error':
389
+ }
390
+ case 'error': {
388
391
  obj = { silent: true };
392
+ }
389
393
  }
390
394
  Util.setLoggingLevel(obj);
391
395
  }
@@ -708,7 +712,7 @@ class Asset extends MetadataType {
708
712
  );
709
713
  fileList.push({
710
714
  subFolder: [this.definition.type, subType],
711
- fileName: metadata.customerKey,
715
+ fileName: templateName,
712
716
  fileExt: fileExt,
713
717
  content: filecontent,
714
718
  encoding: 'base64',
@@ -774,7 +778,8 @@ class Asset extends MetadataType {
774
778
  let readDirArr;
775
779
  switch (metadata.assetType.name) {
776
780
  case 'templatebasedemail': // message
777
- case 'htmlemail': // message
781
+ case 'htmlemail': {
782
+ // message
778
783
  // this complex type always creates its own subdir per asset
779
784
  subDirArr = [this.definition.type, subType];
780
785
  readDirArr = [deployDir, ...subDirArr, templateName || metadata.customerKey];
@@ -821,7 +826,9 @@ class Asset extends MetadataType {
821
826
  );
822
827
  }
823
828
  break;
824
- case 'textonlyemail': // message
829
+ }
830
+ case 'textonlyemail': {
831
+ // message
825
832
  // metadata.views.text.content
826
833
  subDirArr = [this.definition.type, subType];
827
834
  readDirArr = [deployDir, ...subDirArr];
@@ -854,7 +861,9 @@ class Asset extends MetadataType {
854
861
  }
855
862
  }
856
863
  break;
857
- case 'webpage': // asset
864
+ }
865
+ case 'webpage': {
866
+ // asset
858
867
  // this complex type always creates its own subdir per asset
859
868
  subDirArr = [this.definition.type, subType];
860
869
  readDirArr = [deployDir, ...subDirArr, templateName || metadata.customerKey];
@@ -930,6 +939,7 @@ class Asset extends MetadataType {
930
939
  }
931
940
 
932
941
  break;
942
+ }
933
943
  case 'buttonblock': // block - Button Block
934
944
  case 'freeformblock': // block
935
945
  case 'htmlblock': // block
@@ -937,7 +947,8 @@ class Asset extends MetadataType {
937
947
  case 'imageblock': // block - Image Block
938
948
  case 'textblock': // block
939
949
  case 'smartcaptureblock': // other
940
- case 'codesnippetblock': // other
950
+ case 'codesnippetblock': {
951
+ // other
941
952
  // metadata.content
942
953
  subDirArr = [this.definition.type, subType];
943
954
  readDirArr = [deployDir, ...subDirArr];
@@ -968,7 +979,7 @@ class Asset extends MetadataType {
968
979
  // to use this method in templating, store a copy of the info in fileList
969
980
  fileList.push({
970
981
  subFolder: subDirArr,
971
- fileName: metadata.customerKey + subtypeExtension,
982
+ fileName: (templateName || metadata.customerKey) + subtypeExtension,
972
983
  fileExt: ext,
973
984
  content: metadata.content,
974
985
  });
@@ -978,6 +989,7 @@ class Asset extends MetadataType {
978
989
  }
979
990
  }
980
991
  break;
992
+ }
981
993
  }
982
994
  return fileList;
983
995
  }
@@ -1073,7 +1085,8 @@ class Asset extends MetadataType {
1073
1085
  let subType;
1074
1086
  switch (metadata.assetType.name) {
1075
1087
  case 'templatebasedemail': // message
1076
- case 'htmlemail': // message
1088
+ case 'htmlemail': {
1089
+ // message
1077
1090
  // metadata.views.html.content (mandatory)
1078
1091
  if (metadata.views?.html?.content?.length) {
1079
1092
  codeArr.push({
@@ -1091,7 +1104,9 @@ class Asset extends MetadataType {
1091
1104
  }
1092
1105
 
1093
1106
  return { json: metadata, codeArr: codeArr, subFolder: [metadata.customerKey] };
1094
- case 'textonlyemail': // message
1107
+ }
1108
+ case 'textonlyemail': {
1109
+ // message
1095
1110
  // metadata.views.text.content
1096
1111
  if (metadata.views?.text?.content?.length) {
1097
1112
  codeArr.push({
@@ -1103,7 +1118,9 @@ class Asset extends MetadataType {
1103
1118
  delete metadata.views.text.content;
1104
1119
  }
1105
1120
  return { json: metadata, codeArr: codeArr, subFolder: null };
1106
- case 'webpage': // asset
1121
+ }
1122
+ case 'webpage': {
1123
+ // asset
1107
1124
  // metadata.views.html.content (pre & post 20222)
1108
1125
  if (metadata.views?.html?.content?.length) {
1109
1126
  codeArr.push({
@@ -1133,6 +1150,7 @@ class Asset extends MetadataType {
1133
1150
  delete metadata.content;
1134
1151
  }
1135
1152
  return { json: metadata, codeArr: codeArr, subFolder: [metadata.customerKey] };
1153
+ }
1136
1154
  case 'buttonblock': // block - Button Block
1137
1155
  case 'freeformblock': // block
1138
1156
  case 'htmlblock': // block
@@ -1140,7 +1158,8 @@ class Asset extends MetadataType {
1140
1158
  case 'imageblock': // block - Image Block
1141
1159
  case 'textblock': // block
1142
1160
  case 'smartcaptureblock': // other
1143
- case 'codesnippetblock': // other
1161
+ case 'codesnippetblock': {
1162
+ // other
1144
1163
  // metadata.content
1145
1164
  let fileExt = 'html'; // eslint-disable-line no-case-declarations
1146
1165
  if (
@@ -1160,7 +1179,8 @@ class Asset extends MetadataType {
1160
1179
  delete metadata.content;
1161
1180
  }
1162
1181
  return { json: metadata, codeArr: codeArr, subFolder: null };
1163
- default:
1182
+ }
1183
+ default: {
1164
1184
  subType = this._getSubtype(metadata);
1165
1185
  if (!this.definition.binarySubtypes.includes(subType)) {
1166
1186
  Util.logger.debug(
@@ -1168,6 +1188,7 @@ class Asset extends MetadataType {
1168
1188
  );
1169
1189
  }
1170
1190
  return { json: metadata, codeArr: codeArr, subFolder: null };
1191
+ }
1171
1192
  }
1172
1193
  }
1173
1194
  /**
@@ -112,12 +112,14 @@ class Automation extends MetadataType {
112
112
  'Name',
113
113
  ]);
114
114
  const resultsConverted = {};
115
- for (const m of results.Results) {
116
- resultsConverted[m.CustomerKey] = {
117
- id: m.ObjectID,
118
- key: m.CustomerKey,
119
- name: m.Name,
120
- };
115
+ if (Array.isArray(results?.Results)) {
116
+ for (const m of results.Results) {
117
+ resultsConverted[m.CustomerKey] = {
118
+ id: m.ObjectID,
119
+ key: m.CustomerKey,
120
+ name: m.Name,
121
+ };
122
+ }
121
123
  }
122
124
  return { metadata: resultsConverted, type: this.definition.type };
123
125
  }
@@ -520,6 +522,10 @@ class Automation extends MetadataType {
520
522
  }
521
523
  if (metadata.steps) {
522
524
  for (const step of metadata.steps) {
525
+ const stepNumber = step.stepNumber || step.step;
526
+ delete step.stepNumber;
527
+ delete step.step;
528
+
523
529
  for (const activity of step.activities) {
524
530
  try {
525
531
  // get metadata type of activity
@@ -528,58 +534,52 @@ class Automation extends MetadataType {
528
534
  activity.objectTypeId
529
535
  );
530
536
  delete activity.objectTypeId;
531
- // if no activityObjectId then either serialized activity
532
- // (config in Automation ) or unconfigured so no further action to be taken
533
- if (
534
- activity.activityObjectId ===
535
- '00000000-0000-0000-0000-000000000000' ||
536
- activity.activityObjectId == null ||
537
- !this.definition.dependencies.includes(activity.r__type)
538
- ) {
539
- // empty if block
540
- }
541
- // / if managed by cache we can update references to support deployment
542
- else if (
543
- Definitions[activity.r__type]?.['idField'] &&
544
- cache.getCache(this.buObject.mid)[activity.r__type]
545
- ) {
546
- try {
547
- activity.activityObjectId = cache.searchForField(
548
- activity.r__type,
549
- activity.activityObjectId,
550
- Definitions[activity.r__type].idField,
551
- Definitions[activity.r__type].nameField
552
- );
553
- } catch (ex) {
554
- // getFromCache throws error where the dependent metadata is not found
555
- Util.logger.warn(
556
- ` - Missing ${activity.r__type} activity '${activity.name}'` +
557
- ` in step ${step.stepNumber || step.step}.${
558
- activity.displayOrder
559
- }` +
560
- ` of Automation '${metadata.name}' (${ex.message})`
561
- );
562
- return null;
563
- }
564
- } else {
537
+ } catch {
538
+ Util.logger.warn(
539
+ ` - Unknown activity type '${activity.objectTypeId}'` +
540
+ ` in step ${stepNumber}.${activity.displayOrder}` +
541
+ ` of Automation '${metadata.name}'`
542
+ );
543
+ continue;
544
+ }
545
+
546
+ // if no activityObjectId then either serialized activity
547
+ // (config in Automation ) or unconfigured so no further action to be taken
548
+ if (
549
+ activity.activityObjectId === '00000000-0000-0000-0000-000000000000' ||
550
+ activity.activityObjectId == null ||
551
+ !this.definition.dependencies.includes(activity.r__type)
552
+ ) {
553
+ // empty if block
554
+ }
555
+ // / if managed by cache we can update references to support deployment
556
+ else if (
557
+ Definitions[activity.r__type]?.['idField'] &&
558
+ cache.getCache(this.buObject.mid)[activity.r__type]
559
+ ) {
560
+ try {
561
+ activity.activityObjectId = cache.searchForField(
562
+ activity.r__type,
563
+ activity.activityObjectId,
564
+ Definitions[activity.r__type].idField,
565
+ Definitions[activity.r__type].nameField
566
+ );
567
+ } catch (ex) {
568
+ // getFromCache throws error where the dependent metadata is not found
565
569
  Util.logger.warn(
566
570
  ` - Missing ${activity.r__type} activity '${activity.name}'` +
567
- ` in step ${step.stepNumber || step.step}.${
568
- activity.displayOrder
569
- }` +
570
- ` of Automation '${metadata.name}' (Not Found in Cache)`
571
+ ` in step ${stepNumber}.${activity.displayOrder}` +
572
+ ` of Automation '${metadata.name}' (${ex.message})`
571
573
  );
572
- return null;
573
574
  }
574
- } catch {
575
+ } else {
575
576
  Util.logger.warn(
576
- ` - Excluding automation '${metadata.name}' from retrieve (ObjectType ${activity.objectTypeId} is unknown)`
577
+ ` - Missing ${activity.r__type} activity '${activity.name}'` +
578
+ ` in step ${stepNumber}.${activity.displayOrder}` +
579
+ ` of Automation '${metadata.name}' (Not Found in Cache)`
577
580
  );
578
- return null;
579
581
  }
580
582
  }
581
- delete step.stepNumber;
582
- delete step.step;
583
583
  }
584
584
  }
585
585
  return JSON.parse(JSON.stringify(metadata));
@@ -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
  }
@@ -259,11 +259,7 @@ class MetadataType {
259
259
 
260
260
  try {
261
261
  // write to file
262
- await File.writeJSONToFile(
263
- [templateDir, ...typeDirArr],
264
- key + '.' + this.definition.type + suffix,
265
- metadata
266
- );
262
+ await File.writeJSONToFile([templateDir, ...typeDirArr], fileName, metadata);
267
263
  Util.logger.info(
268
264
  `- templated ${this.definition.type}: ${key} (${
269
265
  metadata[this.definition.nameField]
@@ -251,7 +251,7 @@ class Query extends MetadataType {
251
251
  } catch {
252
252
  throw new Error(
253
253
  `${this.definition.type}:: Error applying template variables on ${
254
- metadata[this.definition.keyField] + '.' + this.definition.type
254
+ templateName + '.' + this.definition.type
255
255
  }-meta.sql.`
256
256
  );
257
257
  }
@@ -263,14 +263,14 @@ class Query extends MetadataType {
263
263
  for (const targetDir of targetDirArr) {
264
264
  File.writeToFile(
265
265
  [targetDir, this.definition.type],
266
- metadata[this.definition.keyField] + '.' + this.definition.type + '-meta',
266
+ templateName + '.' + this.definition.type + '-meta',
267
267
  'sql',
268
268
  code
269
269
  );
270
270
  nestedFilePaths.push([
271
271
  targetDir,
272
272
  this.definition.type,
273
- metadata[this.definition.keyField] + '.' + this.definition.type + '-meta.sql',
273
+ templateName + '.' + this.definition.type + '-meta.sql',
274
274
  ]);
275
275
  }
276
276
  return nestedFilePaths;
@@ -225,7 +225,7 @@ class Script extends MetadataType {
225
225
  } catch {
226
226
  throw new Error(
227
227
  `${this.definition.type}:: Error applying template variables on ${
228
- metadata[this.definition.keyField] + '.' + this.definition.type
228
+ templateName + '.' + this.definition.type
229
229
  }-meta.ssjs.`
230
230
  );
231
231
  }
@@ -237,14 +237,14 @@ class Script extends MetadataType {
237
237
  for (const targetDir of targetDirArr) {
238
238
  File.writeToFile(
239
239
  [targetDir, this.definition.type],
240
- metadata[this.definition.keyField] + '.' + this.definition.type + '-meta',
240
+ templateName + '.' + this.definition.type + '-meta',
241
241
  'ssjs',
242
242
  code
243
243
  );
244
244
  nestedFilePaths.push([
245
245
  targetDir,
246
246
  this.definition.type,
247
- metadata[this.definition.keyField] + '.' + this.definition.type + '-meta.ssjs',
247
+ templateName + '.' + this.definition.type + '-meta.ssjs',
248
248
  ]);
249
249
  }
250
250
  return nestedFilePaths;
@@ -249,6 +249,7 @@ const DevOps = {
249
249
  * @param {TYPE.Mcdevrc} properties project config file
250
250
  * @param {string} range git commit range
251
251
  * @param {TYPE.SkipInteraction} [skipInteraction] allows to skip interactive wizard
252
+ * @returns {Promise.<TYPE.DeltaPkgItem[]>} -
252
253
  */
253
254
  async buildDeltaDefinitions(properties, range, skipInteraction) {
254
255
  // check if sourceTargetMapping is valid
@@ -260,7 +261,8 @@ const DevOps = {
260
261
  return;
261
262
  }
262
263
  const sourceMarketListArr = Object.keys(properties.options.deployment.sourceTargetMapping);
263
-
264
+ /** @type {TYPE.DeltaPkgItem[]} */
265
+ const deltaDeployAll = [];
264
266
  for (const sourceML of sourceMarketListArr) {
265
267
  // check if sourceTargetMapping has valid values
266
268
  // #1 check source marketlist
@@ -315,10 +317,12 @@ const DevOps = {
315
317
  const buTypeDelta = {}; // for bt, with BU info
316
318
  const typeDelta = {}; // for bdb, without BU info - thats taken from the marketList
317
319
  let deltaCounter = 0;
318
- for (const file of delta
320
+ const deltaDeploy = delta
319
321
  // Only template/build files that were added/updated/moved. no deletions
320
322
  // ! doesn't work for folder, because their name parsing doesnt work at the moment
321
- .filter((file) => file.gitAction !== 'delete' && file.name)) {
323
+ .filter((file) => file.gitAction !== 'delete' && file.name);
324
+ deltaDeployAll.push(...deltaDeploy);
325
+ for (const file of deltaDeploy) {
322
326
  const buPath = `${file._credential}/${file._businessUnit}`;
323
327
  if (!buTypeDelta[buPath]) {
324
328
  // init object
@@ -356,10 +360,10 @@ const DevOps = {
356
360
  if (deltaCounter) {
357
361
  Util.logger.info(`- ✔️ Templates created: ${deltaCounter}`);
358
362
  } else {
359
- Util.logger.error(
360
- '- ❌ No Templates or Deploy Definitions created. Check if you expected no changes.'
363
+ Util.logger.warn(
364
+ `- No Templates or Deploy Definitions created for ${sourceMlName}`
361
365
  );
362
- return;
366
+ continue;
363
367
  }
364
368
 
365
369
  // Run build definitions bulk for each type
@@ -413,6 +417,12 @@ const DevOps = {
413
417
  );
414
418
  }
415
419
  }
420
+ if (!deltaDeployAll.length) {
421
+ Util.logger.error(
422
+ '- ❌ No Templates or Deploy Definitions created. Check if you expected no changes.'
423
+ );
424
+ }
425
+ return deltaDeployAll;
416
426
  },
417
427
 
418
428
  /**
package/lib/util/file.js CHANGED
@@ -192,41 +192,52 @@ const File = {
192
192
  // load the right prettier config relative to our file
193
193
  switch (filetype) {
194
194
  case 'htm':
195
- case 'html':
195
+ case 'html': {
196
196
  FileFs.prettierConfig.parser = 'html';
197
197
  break;
198
+ }
198
199
  case 'ssjs':
199
- case 'js':
200
+ case 'js': {
200
201
  FileFs.prettierConfig.parser = 'babel';
201
202
  break;
202
- case 'json':
203
+ }
204
+ case 'json': {
203
205
  FileFs.prettierConfig.parser = 'json';
204
206
  break;
207
+ }
205
208
  case 'yaml':
206
- case 'yml':
209
+ case 'yml': {
207
210
  FileFs.prettierConfig.parser = 'yaml';
208
211
  break;
209
- case 'ts':
212
+ }
213
+ case 'ts': {
210
214
  FileFs.prettierConfig.parser = 'babel-ts';
211
215
  break;
212
- case 'css':
216
+ }
217
+ case 'css': {
213
218
  FileFs.prettierConfig.parser = 'css';
214
219
  break;
215
- case 'less':
220
+ }
221
+ case 'less': {
216
222
  FileFs.prettierConfig.parser = 'less';
217
223
  break;
224
+ }
218
225
  case 'sass':
219
- case 'scss':
226
+ case 'scss': {
220
227
  FileFs.prettierConfig.parser = 'scss';
221
228
  break;
222
- case 'md':
229
+ }
230
+ case 'md': {
223
231
  FileFs.prettierConfig.parser = 'markdown';
224
232
  break;
225
- case 'sql':
233
+ }
234
+ case 'sql': {
226
235
  FileFs.prettierConfig.parser = 'sql';
227
236
  break;
228
- default:
237
+ }
238
+ default: {
229
239
  FileFs.prettierConfig.parser = 'babel';
240
+ }
230
241
  }
231
242
  formatted = prettier.format(content, FileFs.prettierConfig);
232
243
  } catch (ex) {
@@ -35,7 +35,7 @@ const Init = {
35
35
  if (missingFields.length) {
36
36
  for (const fieldName of missingFields) {
37
37
  switch (fieldName) {
38
- case 'marketList':
38
+ case 'marketList': {
39
39
  if (properties.marketBulk) {
40
40
  upgradeMsgs.push(`- ✔️ converted 'marketBulk' to '${fieldName}'`);
41
41
  properties[fieldName] = properties.marketBulk;
@@ -45,7 +45,8 @@ const Init = {
45
45
  this._updateLeaf(properties, defaultProps, fieldName);
46
46
  }
47
47
  break;
48
- case 'directories.docs':
48
+ }
49
+ case 'directories.docs': {
49
50
  if (properties.directories.badKeys) {
50
51
  delete properties.directories.badKeys;
51
52
  upgradeMsgs.push(`- ✋ removed 'directories.badKeys'`);
@@ -75,7 +76,8 @@ const Init = {
75
76
  this._updateLeaf(properties, defaultProps, fieldName);
76
77
  upgradeMsgs.push(`- ✔️ added '${fieldName}'`);
77
78
  break;
78
- case 'metaDataTypes.documentOnRetrieve':
79
+ }
80
+ case 'metaDataTypes.documentOnRetrieve': {
79
81
  if (!properties.options.documentOnRetrieve) {
80
82
  properties.metaDataTypes.documentOnRetrieve = [];
81
83
  } else {
@@ -86,7 +88,8 @@ const Init = {
86
88
  `- ✔️ converted 'options.documentOnRetrieve' to '${fieldName}'`
87
89
  );
88
90
  break;
89
- case 'options.deployment.commitHistory':
91
+ }
92
+ case 'options.deployment.commitHistory': {
90
93
  if (properties.options.commitHistory) {
91
94
  upgradeMsgs.push(
92
95
  `- ✔️ converted 'options.commitHistory' to '${fieldName}'`
@@ -99,7 +102,8 @@ const Init = {
99
102
  this._updateLeaf(properties, defaultProps, fieldName);
100
103
  }
101
104
  break;
102
- case 'options.exclude':
105
+ }
106
+ case 'options.exclude': {
103
107
  if (properties.options.filter) {
104
108
  upgradeMsgs.push(`- ✔️ converted 'options.filter' to '${fieldName}'`);
105
109
  properties.options.exclude = properties.options.filter;
@@ -109,13 +113,16 @@ const Init = {
109
113
  this._updateLeaf(properties, defaultProps, fieldName);
110
114
  }
111
115
  break;
112
- case 'version':
116
+ }
117
+ case 'version': {
113
118
  // do nothing other than ensure we re-save the config (with the new version)
114
119
  upgradeMsgs.push(`- ✔️ version updated`);
115
120
  break;
116
- default:
121
+ }
122
+ default: {
117
123
  this._updateLeaf(properties, defaultProps, fieldName);
118
124
  upgradeMsgs.push(`- ✔️ added '${fieldName}'`);
125
+ }
119
126
  }
120
127
  }
121
128
  updateConfigNeeded = true;
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.0",
3
+ "version": "4.0.2",
4
4
  "description": "Accenture Salesforce Marketing Cloud DevTools",
5
5
  "author": "joern.berkefeld, douglas.midgley, robert.zimmermann, maciej.barnas",
6
6
  "license": "MIT",
@@ -42,7 +42,7 @@
42
42
  "dependencies": {
43
43
  "cli-progress": "3.11.2",
44
44
  "command-exists": "1.2.9",
45
- "conf": "10.1.2",
45
+ "conf": "10.2.0",
46
46
  "console.table": "0.10.0",
47
47
  "deep-equal": "2.0.5",
48
48
  "fs-extra": "10.1.0",
@@ -51,32 +51,32 @@
51
51
  "mustache": "4.2.0",
52
52
  "p-limit": "3.1.0",
53
53
  "prettier": "2.7.1",
54
- "prettier-plugin-sql": "0.8.3",
55
- "semver": "7.3.7",
54
+ "prettier-plugin-sql": "0.12.1",
55
+ "semver": "7.3.8",
56
56
  "sfmc-sdk": "0.6.1",
57
- "simple-git": "3.10.0",
57
+ "simple-git": "3.14.1",
58
58
  "toposort": "2.0.2",
59
59
  "update-notifier": "5.1.0",
60
- "winston": "3.8.1",
61
- "yargs": "17.5.1"
60
+ "winston": "3.8.2",
61
+ "yargs": "17.6.0"
62
62
  },
63
63
  "devDependencies": {
64
64
  "assert": "2.0.0",
65
- "axios-mock-adapter": "1.21.1",
65
+ "axios-mock-adapter": "1.21.2",
66
66
  "chai": "4.3.6",
67
- "eslint": "8.19.0",
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.3",
71
- "eslint-plugin-mocha": "10.0.5",
70
+ "eslint-plugin-jsdoc": "39.3.6",
71
+ "eslint-plugin-mocha": "10.1.0",
72
72
  "eslint-plugin-prettier": "4.2.1",
73
- "eslint-plugin-unicorn": "43.0.0",
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
77
  "mocha": "10.0.0",
78
- "mock-fs": "5.1.2",
79
- "npm-check": "5.9.2",
78
+ "mock-fs": "5.1.4",
79
+ "npm-check": "6.0.1",
80
80
  "npm-run-all": "4.1.5",
81
81
  "prettier-eslint": "15.0.1"
82
82
  },