mcdev 3.1.3 → 4.0.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.
Files changed (134) hide show
  1. package/.eslintrc.json +67 -7
  2. package/.github/ISSUE_TEMPLATE/bug.yml +2 -1
  3. package/.github/PULL_REQUEST_TEMPLATE.md +5 -3
  4. package/.github/dependabot.yml +14 -0
  5. package/.github/workflows/code-analysis.yml +57 -0
  6. package/.husky/commit-msg +10 -0
  7. package/.husky/post-checkout +5 -0
  8. package/.husky/pre-commit +2 -1
  9. package/.prettierrc +8 -0
  10. package/.vscode/settings.json +1 -1
  11. package/LICENSE +2 -2
  12. package/README.md +134 -45
  13. package/boilerplate/config.json +5 -11
  14. package/boilerplate/files/.prettierrc +8 -0
  15. package/boilerplate/files/.vscode/extensions.json +0 -1
  16. package/boilerplate/files/.vscode/settings.json +28 -2
  17. package/boilerplate/files/README.md +2 -2
  18. package/boilerplate/forcedUpdates.json +10 -0
  19. package/boilerplate/npm-dependencies.json +5 -5
  20. package/docs/dist/documentation.md +2795 -1724
  21. package/jsconfig.json +1 -1
  22. package/lib/Builder.js +166 -75
  23. package/lib/Deployer.js +244 -96
  24. package/lib/MetadataTypeDefinitions.js +2 -0
  25. package/lib/MetadataTypeInfo.js +2 -0
  26. package/lib/Retriever.js +61 -84
  27. package/lib/cli.js +116 -11
  28. package/lib/index.js +241 -561
  29. package/lib/metadataTypes/AccountUser.js +101 -95
  30. package/lib/metadataTypes/Asset.js +677 -248
  31. package/lib/metadataTypes/AttributeGroup.js +23 -12
  32. package/lib/metadataTypes/Automation.js +451 -354
  33. package/lib/metadataTypes/Campaign.js +33 -93
  34. package/lib/metadataTypes/ContentArea.js +31 -11
  35. package/lib/metadataTypes/DataExtension.js +387 -372
  36. package/lib/metadataTypes/DataExtensionField.js +131 -54
  37. package/lib/metadataTypes/DataExtensionTemplate.js +22 -4
  38. package/lib/metadataTypes/DataExtract.js +61 -48
  39. package/lib/metadataTypes/DataExtractType.js +14 -8
  40. package/lib/metadataTypes/Discovery.js +21 -16
  41. package/lib/metadataTypes/Email.js +32 -12
  42. package/lib/metadataTypes/EmailSendDefinition.js +85 -80
  43. package/lib/metadataTypes/EventDefinition.js +61 -43
  44. package/lib/metadataTypes/FileTransfer.js +72 -52
  45. package/lib/metadataTypes/Filter.js +11 -4
  46. package/lib/metadataTypes/Folder.js +149 -117
  47. package/lib/metadataTypes/FtpLocation.js +14 -8
  48. package/lib/metadataTypes/ImportFile.js +61 -64
  49. package/lib/metadataTypes/Interaction.js +19 -4
  50. package/lib/metadataTypes/List.js +54 -13
  51. package/lib/metadataTypes/MetadataType.js +668 -454
  52. package/lib/metadataTypes/MobileCode.js +46 -0
  53. package/lib/metadataTypes/MobileKeyword.js +114 -0
  54. package/lib/metadataTypes/Query.js +204 -103
  55. package/lib/metadataTypes/Role.js +76 -61
  56. package/lib/metadataTypes/Script.js +145 -81
  57. package/lib/metadataTypes/SetDefinition.js +20 -8
  58. package/lib/metadataTypes/TriggeredSendDefinition.js +78 -58
  59. package/lib/metadataTypes/definitions/Asset.definition.js +21 -10
  60. package/lib/metadataTypes/definitions/AttributeGroup.definition.js +12 -0
  61. package/lib/metadataTypes/definitions/Automation.definition.js +10 -5
  62. package/lib/metadataTypes/definitions/Campaign.definition.js +44 -1
  63. package/lib/metadataTypes/definitions/DataExtension.definition.js +4 -0
  64. package/lib/metadataTypes/definitions/DataExtensionTemplate.definition.js +6 -0
  65. package/lib/metadataTypes/definitions/DataExtract.definition.js +18 -14
  66. package/lib/metadataTypes/definitions/Discovery.definition.js +12 -0
  67. package/lib/metadataTypes/definitions/EmailSendDefinition.definition.js +4 -0
  68. package/lib/metadataTypes/definitions/EventDefinition.definition.js +22 -0
  69. package/lib/metadataTypes/definitions/FileTransfer.definition.js +4 -0
  70. package/lib/metadataTypes/definitions/Filter.definition.js +4 -0
  71. package/lib/metadataTypes/definitions/Folder.definition.js +6 -0
  72. package/lib/metadataTypes/definitions/FtpLocation.definition.js +4 -0
  73. package/lib/metadataTypes/definitions/ImportFile.definition.js +10 -5
  74. package/lib/metadataTypes/definitions/Interaction.definition.js +4 -0
  75. package/lib/metadataTypes/definitions/MobileCode.definition.js +163 -0
  76. package/lib/metadataTypes/definitions/MobileKeyword.definition.js +253 -0
  77. package/lib/metadataTypes/definitions/Query.definition.js +4 -0
  78. package/lib/metadataTypes/definitions/Role.definition.js +5 -0
  79. package/lib/metadataTypes/definitions/Script.definition.js +4 -0
  80. package/lib/metadataTypes/definitions/SetDefinition.definition.js +28 -0
  81. package/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js +4 -0
  82. package/lib/retrieveChangelog.js +7 -6
  83. package/lib/util/auth.js +117 -0
  84. package/lib/util/businessUnit.js +55 -66
  85. package/lib/util/cache.js +194 -0
  86. package/lib/util/cli.js +90 -116
  87. package/lib/util/config.js +302 -0
  88. package/lib/util/devops.js +240 -50
  89. package/lib/util/file.js +120 -191
  90. package/lib/util/init.config.js +195 -69
  91. package/lib/util/init.git.js +45 -50
  92. package/lib/util/init.js +72 -59
  93. package/lib/util/init.npm.js +48 -39
  94. package/lib/util/util.js +280 -564
  95. package/package.json +44 -33
  96. package/test/dataExtension.test.js +152 -0
  97. package/test/mockRoot/.mcdev-auth.json +8 -0
  98. package/test/mockRoot/.mcdevrc.json +67 -0
  99. package/test/mockRoot/deploy/testInstance/testBU/dataExtension/childBU_dataextension_test.dataExtension-meta.json +39 -0
  100. package/test/mockRoot/deploy/testInstance/testBU/dataExtension/testDataExtension.dataExtension-meta.json +23 -0
  101. package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.json +11 -0
  102. package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.sql +4 -0
  103. package/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.json +11 -0
  104. package/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.sql +4 -0
  105. package/test/query.test.js +149 -0
  106. package/test/resourceFactory.js +142 -0
  107. package/test/resources/1111111/dataFolder/retrieve-response.xml +43 -0
  108. package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json +18 -0
  109. package/test/resources/9999999/automation/v1/queries/get-response.json +24 -0
  110. package/test/resources/9999999/automation/v1/queries/post-response.json +18 -0
  111. package/test/resources/9999999/dataExtension/build-expected.json +51 -0
  112. package/test/resources/9999999/dataExtension/create-expected.json +23 -0
  113. package/test/resources/9999999/dataExtension/create-response.xml +54 -0
  114. package/test/resources/9999999/dataExtension/retrieve-expected.json +51 -0
  115. package/test/resources/9999999/dataExtension/retrieve-response.xml +47 -0
  116. package/test/resources/9999999/dataExtension/template-expected.json +51 -0
  117. package/test/resources/9999999/dataExtension/update-expected.json +55 -0
  118. package/test/resources/9999999/dataExtension/update-response.xml +52 -0
  119. package/test/resources/9999999/dataExtensionField/retrieve-response.xml +93 -0
  120. package/test/resources/9999999/dataExtensionTemplate/retrieve-response.xml +303 -0
  121. package/test/resources/9999999/dataFolder/retrieve-response.xml +65 -0
  122. package/test/resources/9999999/query/build-expected.json +8 -0
  123. package/test/resources/9999999/query/get-expected.json +11 -0
  124. package/test/resources/9999999/query/patch-expected.json +11 -0
  125. package/test/resources/9999999/query/post-expected.json +11 -0
  126. package/test/resources/9999999/query/template-expected.json +8 -0
  127. package/test/resources/auth.json +32 -0
  128. package/test/resources/rest404-response.json +5 -0
  129. package/test/resources/retrieve-response.xml +21 -0
  130. package/test/utils.js +107 -0
  131. package/types/mcdev.d.js +301 -0
  132. package/CHANGELOG.md +0 -126
  133. package/PULL_REQUEST_TEMPLATE.md +0 -19
  134. package/test/util/file.js +0 -51
package/lib/util/file.js CHANGED
@@ -1,19 +1,18 @@
1
1
  /* eslint-disable no-control-regex */
2
2
  'use strict';
3
3
 
4
+ const TYPE = require('../../types/mcdev.d');
4
5
  const fs = require('fs-extra');
5
6
  const packageJson = require('../../package.json');
6
- const path = require('path');
7
+ const path = require('node:path');
7
8
  const prettier = require('prettier');
8
9
  const Util = require('./util');
9
- const sql = require('sql-formatter-plus');
10
- const updateNotifier = require('update-notifier-git');
10
+ const updateNotifier = require('update-notifier');
11
11
 
12
12
  // inform user when there is an update
13
13
  const notifier = updateNotifier({
14
14
  pkg: packageJson,
15
15
  updateCheckInterval: 1000 * 3600 * 24, // once per day
16
- remoteUrl: packageJson.repository.url.split('git+')[1],
17
16
  });
18
17
  // Notify using the built-in convenience method
19
18
  notifier.notify();
@@ -24,9 +23,10 @@ notifier.notify();
24
23
  const File = {
25
24
  /**
26
25
  * copies a file from one path to another
27
- * @param {String} from - full filepath including name of existing file
28
- * @param {String} to - full filepath including name where file should go
29
- * @returns {Object} - results object
26
+ *
27
+ * @param {string} from - full filepath including name of existing file
28
+ * @param {string} to - full filepath including name where file should go
29
+ * @returns {object} - results object
30
30
  */
31
31
  async copyFile(from, to) {
32
32
  try {
@@ -34,21 +34,20 @@ const File = {
34
34
  return { status: 'ok', file: from };
35
35
  } catch (ex) {
36
36
  // This can happen in some cases where referencing files deleted in Commit
37
- if (ex.message.startsWith('ENOENT: no such file or directory')) {
38
- return {
39
- status: 'skipped',
40
- statusMessage: 'deleted from repository',
41
- file: from,
42
- };
43
- } else {
44
- return { status: 'failed', statusMessage: ex.message, file: from };
45
- }
37
+ return ex.message.startsWith('ENOENT: no such file or directory')
38
+ ? {
39
+ status: 'skipped',
40
+ statusMessage: 'deleted from repository',
41
+ file: from,
42
+ }
43
+ : { status: 'failed', statusMessage: ex.message, file: from };
46
44
  }
47
45
  },
48
46
  /**
49
47
  * makes sure Windows accepts path names
50
- * @param {String} path - filename or path
51
- * @returns {String} - corrected string
48
+ *
49
+ * @param {string} path - filename or path
50
+ * @returns {string} - corrected string
52
51
  */
53
52
  filterIllegalPathChars(path) {
54
53
  return (
@@ -69,13 +68,20 @@ const File = {
69
68
  // convert closing-curly brackets back for templating
70
69
  .split('%7D')
71
70
  .join('}')
71
+ // convert brackets back for asset blocks
72
+ .split('%5B')
73
+ .join('[')
74
+ // convert brackets back for asset blocks
75
+ .split('%5D')
76
+ .join(']')
72
77
  );
73
78
  },
74
79
 
75
80
  /**
76
81
  * makes sure Windows accepts file names
77
- * @param {String} filename - filename or path
78
- * @returns {String} - corrected string
82
+ *
83
+ * @param {string} filename - filename or path
84
+ * @returns {string} - corrected string
79
85
  */
80
86
  filterIllegalFilenames(filename) {
81
87
  return (
@@ -90,13 +96,20 @@ const File = {
90
96
  // convert closing-curly brackets back for templating
91
97
  .split('%7D')
92
98
  .join('}')
99
+ // convert brackets back for asset blocks
100
+ .split('%5B')
101
+ .join('[')
102
+ // convert brackets back for asset blocks
103
+ .split('%5D')
104
+ .join(']')
93
105
  );
94
106
  },
95
107
 
96
108
  /**
97
109
  * makes sure Windows accepts file names
98
- * @param {String} filename - filename or path
99
- * @returns {String} - corrected string
110
+ *
111
+ * @param {string} filename - filename or path
112
+ * @returns {string} - corrected string
100
113
  */
101
114
  reverseFilterIllegalFilenames(filename) {
102
115
  return decodeURIComponent(filename).split('_STAR_').join('*');
@@ -104,10 +117,12 @@ const File = {
104
117
 
105
118
  /**
106
119
  * Takes various types of path strings and formats into a platform specific path
120
+ *
107
121
  * @param {string|string[]} denormalizedPath directory the file will be written to
108
- * @returns {String} Path strings
122
+ * @returns {string} Path strings
109
123
  */
110
124
  normalizePath: function (denormalizedPath) {
125
+ /* eslint-disable unicorn/prefer-ternary */
111
126
  if (Array.isArray(denormalizedPath)) {
112
127
  // if the value is undefined set to empty string to allow parsing
113
128
  return path.join(...denormalizedPath.map((val) => val || ''));
@@ -115,22 +130,22 @@ const File = {
115
130
  // if directory is empty put . as otherwill will write to c://
116
131
  return path.join(denormalizedPath || '.');
117
132
  }
133
+ /* eslint-enable unicorn/prefer-ternary */
118
134
  },
119
135
  /**
120
136
  * Saves json content to a file in the local file system. Will create the parent directory if it does not exist
137
+ *
121
138
  * @param {string|string[]} directory directory the file will be written to
122
- * @param {String} filename name of the file without '.json' ending
123
- * @param {Object} content filecontent
139
+ * @param {string} filename name of the file without '.json' ending
140
+ * @param {object} content filecontent
124
141
  * @returns {Promise} Promise
125
142
  */
126
143
  writeJSONToFile: async function (directory, filename, content) {
127
144
  directory = this.filterIllegalPathChars(this.normalizePath(directory));
128
145
  filename = this.filterIllegalFilenames(filename);
129
- if (!fs.existsSync(directory)) {
130
- fs.mkdirpSync(directory);
131
- }
146
+ await fs.ensureDir(directory);
132
147
  try {
133
- await fs.writeJSON(path.join(directory, filename + '.json'), content, { spaces: 4 });
148
+ return fs.writeJSON(path.join(directory, filename + '.json'), content, { spaces: 4 });
134
149
  } catch (ex) {
135
150
  Util.logger.error('File.writeJSONToFile:: error | ' + ex.message);
136
151
  }
@@ -138,57 +153,30 @@ const File = {
138
153
  /**
139
154
  * Saves beautified files in the local file system. Will create the parent directory if it does not exist
140
155
  * ! Important: run 'await File.initPrettier()' in your MetadataType.retrieve() once before hitting this
156
+ *
141
157
  * @param {string|string[]} directory directory the file will be written to
142
- * @param {String} filename name of the file without suffix
143
- * @param {String} filetype filetype ie. JSON or SSJS
144
- * @param {String} content filecontent
145
- * @param {Object} [templateVariables] templating variables to be replaced in the metadata
146
- * @returns {Promise<Boolean>} Promise
158
+ * @param {string} filename name of the file without suffix
159
+ * @param {string} filetype filetype ie. JSON or SSJS
160
+ * @param {string} content filecontent
161
+ * @param {TYPE.TemplateMap} [templateVariables] templating variables to be replaced in the metadata
162
+ * @returns {Promise.<boolean>} Promise
147
163
  */
148
164
  writePrettyToFile: async function (directory, filename, filetype, content, templateVariables) {
149
- let formatted;
150
- if (filetype === 'sql') {
151
- formatted = this._beautify_sql(content);
152
- } else {
153
- // we need to ensure formatted is a String, not a Promise
154
- formatted = await this._beautify_prettier(directory, filename, filetype, content);
155
- }
165
+ let formatted = await this._beautify_prettier(directory, filename, filetype, content);
156
166
  if (templateVariables) {
157
167
  formatted = Util.replaceByObject(formatted, templateVariables);
158
168
  }
159
-
160
169
  return this.writeToFile(directory, filename, filetype, formatted);
161
170
  },
162
- /**
163
- * helper for writePrettyToFile, applying sql formatting onto given stringified content
164
- * @param {String} content filecontent
165
- * @returns {String} original string on error; formatted string on success
166
- */
167
- _beautify_sql: function (content) {
168
- let formatted;
169
- try {
170
- formatted = sql.format(content, {
171
- language: 'sql', // Defaults to "sql"
172
- indent: ' ', // Defaults to two spaces,W
173
- uppercase: true, // Defaults to false
174
- linesBetweenQueries: 1, // Defaults to 1
175
- });
176
- // if templating variables were in the code, those now have extra spaces
177
- formatted = formatted.split('{ { { ').join('{{{').split(' } } }').join('}}}');
178
- } catch (ex) {
179
- Util.logger.debug('SQL Formatter Exception: ' + ex.message);
180
- formatted = content;
181
- }
182
- return formatted;
183
- },
184
171
  /**
185
172
  * helper for writePrettyToFile, applying prettier onto given stringified content
186
173
  * ! Important: run 'await File.initPrettier()' in your MetadataType.retrieve() once before hitting this
174
+ *
187
175
  * @param {string|string[]} directory directory the file will be written to
188
- * @param {String} filename name of the file without suffix
189
- * @param {String} filetype filetype ie. JSON or SSJS
190
- * @param {String} content filecontent
191
- * @returns {String} original string on error; formatted string on success
176
+ * @param {string} filename name of the file without suffix
177
+ * @param {string} filetype filetype ie. JSON or SSJS
178
+ * @param {string} content filecontent
179
+ * @returns {string} original string on error; formatted string on success
192
180
  */
193
181
  _beautify_prettier: function (directory, filename, filetype, content) {
194
182
  if (!FileFs.prettierConfig) {
@@ -234,6 +222,9 @@ const File = {
234
222
  case 'md':
235
223
  FileFs.prettierConfig.parser = 'markdown';
236
224
  break;
225
+ case 'sql':
226
+ FileFs.prettierConfig.parser = 'sql';
227
+ break;
237
228
  default:
238
229
  FileFs.prettierConfig.parser = 'babel';
239
230
  }
@@ -252,7 +243,7 @@ const File = {
252
243
  filename + '.error',
253
244
  'log',
254
245
  `Error Log\nParser: ${FileFs.prettierConfig.parser}\n${ex.message.replace(
255
- /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
246
+ /[\u001B\u009B][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
256
247
 
257
248
  ''
258
249
  )}`
@@ -265,23 +256,25 @@ const File = {
265
256
  },
266
257
  /**
267
258
  * Saves text content to a file in the local file system. Will create the parent directory if it does not exist
259
+ *
268
260
  * @param {string|string[]} directory directory the file will be written to
269
- * @param {String} filename name of the file without '.json' ending
270
- * @param {String} filetype filetype suffix
271
- * @param {String} content filecontent
272
- * @param {Object} [encoding] added for certain file types (like images)
273
- * @returns {Promise<Boolean>} Promise
261
+ * @param {string} filename name of the file without '.json' ending
262
+ * @param {string} filetype filetype suffix
263
+ * @param {string} content filecontent
264
+ * @param {object} [encoding] added for certain file types (like images)
265
+ * @returns {Promise.<boolean>} Promise
274
266
  */
275
267
  writeToFile: async function (directory, filename, filetype, content, encoding) {
276
268
  directory = this.filterIllegalPathChars(this.normalizePath(directory));
277
- if (!fs.existsSync(directory)) {
278
- fs.mkdirpSync(directory);
279
- }
269
+ await fs.ensureDir(directory);
280
270
  // filter characters that are illegal for file names in Windows
281
271
  filename = this.filterIllegalFilenames(filename);
282
-
272
+ const filePath = path.join(directory, filename + '.' + filetype);
283
273
  try {
284
- await fs.writeFile(path.join(directory, filename + '.' + filetype), content, encoding);
274
+ if (await fs.pathExists(filePath)) {
275
+ Util.logger.debug(`Overwriting: ${filePath}`);
276
+ }
277
+ await fs.writeFile(filePath, content, encoding);
285
278
  return true;
286
279
  } catch (ex) {
287
280
  Util.logger.error('File.writeToFile:: error | ' + ex.message);
@@ -291,11 +284,12 @@ const File = {
291
284
 
292
285
  /**
293
286
  * Saves json content to a file in the local file system. Will create the parent directory if it does not exist
294
- * @param {String|String[]} directory directory where the file is stored
295
- * @param {String} filename name of the file without '.json' ending
296
- * @param {Boolean} sync should execute sync (default is async)
297
- * @param {Boolean} cleanPath should execute sync (default is true)
298
- * @returns {Promise|Object} Promise or JSON object depending on if async or not
287
+ *
288
+ * @param {string | string[]} directory directory where the file is stored
289
+ * @param {string} filename name of the file without '.json' ending
290
+ * @param {boolean} sync should execute sync (default is async)
291
+ * @param {boolean} cleanPath should execute sync (default is true)
292
+ * @returns {Promise | object} Promise or JSON object depending on if async or not
299
293
  */
300
294
  readJSONFile: function (directory, filename, sync, cleanPath) {
301
295
  try {
@@ -326,30 +320,32 @@ const File = {
326
320
  },
327
321
  /**
328
322
  * reads file from local file system.
329
- * @param {String|String[]} directory directory where the file is stored
330
- * @param {String} filename name of the file without '.json' ending
331
- * @param {String} filetype filetype suffix
332
- * @param {String} [encoding='utf8'] read file with encoding (defaults to utf-8)
333
- * @returns {Promise<String>} file contents
323
+ *
324
+ * @param {string | string[]} directory directory where the file is stored
325
+ * @param {string} filename name of the file without '.json' ending
326
+ * @param {string} filetype filetype suffix
327
+ * @param {string} [encoding='utf8'] read file with encoding (defaults to utf-8)
328
+ * @returns {Promise.<string>} file contents
334
329
  */
335
- readFile: function (directory, filename, filetype, encoding) {
330
+ readFilteredFilename: function (directory, filename, filetype, encoding) {
336
331
  try {
337
332
  directory = this.filterIllegalPathChars(this.normalizePath(directory));
338
333
  filename = this.filterIllegalFilenames(filename);
339
334
  return fs.readFile(path.join(directory, filename + '.' + filetype), encoding || 'utf8');
340
335
  } catch (ex) {
341
- Util.logger.error('File.readFile:: error | ' + ex.message);
336
+ Util.logger.error('File.readFilteredFilename:: error | ' + ex.message);
342
337
  }
343
338
  },
344
339
  /**
345
340
  * reads directories to a specific depth returning an array
346
341
  * of file paths to be iterated over
342
+ *
347
343
  * @example ['deploy/mcdev/bu1']
348
- * @param {String} directory directory to checkin
349
- * @param {Number} depth how many levels to check (1 base)
350
- * @param {Boolean} [includeStem] include the parent directory in the response
351
- * @param {Number} [_stemLength] set recursively for subfolders. do not set manually!
352
- * @returns {Promise<String[]>} array of fully defined file paths
344
+ * @param {string} directory directory to checkin
345
+ * @param {number} depth how many levels to check (1 base)
346
+ * @param {boolean} [includeStem] include the parent directory in the response
347
+ * @param {number} [_stemLength] set recursively for subfolders. do not set manually!
348
+ * @returns {Promise.<string[]>} array of fully defined file paths
353
349
  */
354
350
  readDirectories: async function (directory, depth, includeStem, _stemLength) {
355
351
  try {
@@ -362,8 +358,8 @@ const File = {
362
358
  for (const dirent of raw) {
363
359
  const direntPath = path.join(directory, dirent.name);
364
360
  if (
365
- fs.existsSync(direntPath) &&
366
- fs.lstatSync(direntPath).isDirectory() &&
361
+ (await fs.pathExists(direntPath)) &&
362
+ (await fs.lstat(direntPath)).isDirectory() &&
367
363
  depth > 0
368
364
  ) {
369
365
  const nestedChildren = await this.readDirectories(
@@ -376,14 +372,15 @@ const File = {
376
372
  }
377
373
  }
378
374
  if (children.length === 0) {
379
- if (includeStem) {
380
- return [directory];
381
- } else {
382
- // remove base directory and leading slahes and backslashes
383
- return [
384
- directory.substring(_stemLength).replace(/^\\+/, '').replace(/^\/+/, ''),
385
- ];
386
- }
375
+ // if not includeStem then remove base directory and leading slahes and backslashes
376
+ return includeStem
377
+ ? [directory]
378
+ : [
379
+ directory
380
+ .slice(Math.max(0, _stemLength))
381
+ .replace(/^\\+/, '')
382
+ .replace(/^\/+/, ''),
383
+ ];
387
384
  } else {
388
385
  return children;
389
386
  }
@@ -396,12 +393,14 @@ const File = {
396
393
  /**
397
394
  * reads directories to a specific depth returning an array
398
395
  * of file paths to be iterated over using sync api (required in constructors)
396
+ * TODO - merge with readDirectories. so far the logic is really different
397
+ *
399
398
  * @example ['deploy/mcdev/bu1']
400
- * @param {String} directory directory to checkin
401
- * @param {Number} [depth] how many levels to check (1 base)
402
- * @param {Boolean} [includeStem] include the parent directory in the response
403
- * @param {Number} [_stemLength] set recursively for subfolders. do not set manually!
404
- * @returns {String[]} array of fully defined file paths
399
+ * @param {string} directory directory to checkin
400
+ * @param {number} [depth] how many levels to check (1 base)
401
+ * @param {boolean} [includeStem] include the parent directory in the response
402
+ * @param {number} [_stemLength] set recursively for subfolders. do not set manually!
403
+ * @returns {string[]} array of fully defined file paths
405
404
  */
406
405
  readDirectoriesSync: function (directory, depth, includeStem, _stemLength) {
407
406
  try {
@@ -417,8 +416,8 @@ const File = {
417
416
  children.push(directory);
418
417
  } else {
419
418
  // remove base directory and leading slahes and backslashes
420
- const currentPath = directory.substring(_stemLength).replace(path.sep, '');
421
- children.push(currentPath ? currentPath : '.');
419
+ const currentPath = directory.slice(Math.max(0, _stemLength)).replace(path.sep, '');
420
+ children.push(currentPath || '.');
422
421
  }
423
422
  // read all directories
424
423
  const raw = fs.readdirSync(directory, { withFileTypes: true });
@@ -442,94 +441,24 @@ const File = {
442
441
  Util.logger.debug(ex.stack);
443
442
  }
444
443
  },
445
- /**
446
- * loads central properties from config file
447
- * @param {Boolean} [silent] omit throwing errors and print messages; assuming not silent if not set
448
- * @returns {Object} central properties object
449
- */
450
- loadConfigFile(silent) {
451
- let properties;
452
- if (fs.existsSync(Util.configFileName)) {
453
- // properties = JSON.parse(fs.readFileSync(Util.configFileName, 'utf8'));
454
- try {
455
- properties = fs.readJsonSync(Util.configFileName);
456
- } catch (ex) {
457
- Util.logger.error(`${ex.code}: ${ex.message}`);
458
- return;
459
- }
460
- if (fs.existsSync(Util.authFileName)) {
461
- let auth;
462
- try {
463
- auth = fs.readJsonSync(Util.authFileName);
464
- } catch (ex) {
465
- Util.logger.error(`${ex.code}: ${ex.message}`);
466
- return;
467
- }
468
-
469
- if (!auth.credentials) {
470
- const err = `${Util.authFileName} is not set up correctly.`;
471
- Util.logger.error(err);
472
- throw new Error(err);
473
- }
474
- for (const cred in properties.credentials) {
475
- const configset = properties.credentials[cred];
476
- const authset = auth.credentials[cred];
477
- if (authset) {
478
- if (configset.eid === authset.eid) {
479
- for (const key in authset) {
480
- configset[key] = authset[key];
481
- }
482
- } else if (!silent) {
483
- const err = `'${cred}' found in ${Util.configFileName} and ${Util.authFileName} have a Enterprise ID mismatch. Please check.`;
484
- Util.logger.error(err);
485
- throw new Error(err);
486
- }
487
- } else if (!silent) {
488
- Util.logger.warn(
489
- `'${cred}' found in ${Util.configFileName} but not in ${Util.authFileName}. Please run 'mcdev init' to provide the missing credential details.`
490
- );
491
- }
492
- }
493
- } else if (!silent) {
494
- Util.logger.warn(
495
- `${Util.authFileName} not found. Please run 'mcdev init' to provide the missing credential details.`
496
- );
497
- }
498
- }
499
- return properties;
500
- },
501
444
  /**
502
445
  * helper that splits the config back into auth & config parts to save them separately
503
- * @param {Object} properties central properties object
504
- * @returns {Promise<void>} -
446
+ *
447
+ * @param {TYPE.Mcdevrc} properties central properties object
448
+ * @returns {Promise.<void>} -
505
449
  */
506
450
  async saveConfigFile(properties) {
507
- const auth = { credentials: {} };
508
- const config = properties;
509
- for (const cred in config.credentials) {
510
- auth.credentials[cred] = {};
511
- // copy id+secret+tenant to auth file
512
- auth.credentials[cred].clientId = config.credentials[cred].clientId;
513
- auth.credentials[cred].clientSecret = config.credentials[cred].clientSecret;
514
- auth.credentials[cred].tenant = config.credentials[cred].tenant;
515
- // copy eid as well to make sure we can test for equality when merging the files during runtime
516
- auth.credentials[cred].eid = config.credentials[cred].eid;
517
- // delete id+secret from config file
518
- delete config.credentials[cred].clientId;
519
- delete config.credentials[cred].clientSecret;
520
- delete config.credentials[cred].tenant;
521
- }
522
451
  // we want to save to save the full version here to allow us to upgrade configs properly in the future
523
- config.version = packageJson.version;
452
+ properties.version = packageJson.version;
524
453
 
525
- await this.writeJSONToFile('', Util.configFileName.split('.json')[0], config);
526
- await this.writeJSONToFile('', Util.authFileName.split('.json')[0], auth);
454
+ await this.writeJSONToFile('', Util.configFileName.split('.json')[0], properties);
527
455
  Util.logger.info(`✔️ ${Util.configFileName} and ${Util.authFileName} saved successfully`);
528
456
  },
529
457
  /**
530
458
  * Initalises Prettier formatting lib async.
531
- * @param {String} [filetype='html'] filetype ie. JSON or SSJS
532
- * @returns {Promise<Boolean>} success of config load
459
+ *
460
+ * @param {string} [filetype='html'] filetype ie. JSON or SSJS
461
+ * @returns {Promise.<boolean>} success of config load
533
462
  */
534
463
  async initPrettier(filetype) {
535
464
  if (FileFs.prettierConfig === null) {