mcdev 3.1.3 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +67 -7
- package/.github/ISSUE_TEMPLATE/bug.yml +2 -1
- package/.github/PULL_REQUEST_TEMPLATE.md +5 -3
- package/.github/dependabot.yml +14 -0
- package/.github/workflows/code-analysis.yml +57 -0
- package/.husky/commit-msg +10 -0
- package/.husky/post-checkout +5 -0
- package/.husky/pre-commit +2 -1
- package/.prettierrc +8 -0
- package/.vscode/settings.json +1 -1
- package/LICENSE +2 -2
- package/README.md +134 -45
- package/boilerplate/config.json +5 -11
- package/boilerplate/files/.prettierrc +8 -0
- package/boilerplate/files/.vscode/extensions.json +0 -1
- package/boilerplate/files/.vscode/settings.json +30 -2
- package/boilerplate/files/README.md +2 -2
- package/boilerplate/forcedUpdates.json +10 -0
- package/boilerplate/npm-dependencies.json +5 -5
- package/docs/dist/documentation.md +2807 -1730
- package/jsconfig.json +1 -1
- package/lib/Builder.js +171 -74
- package/lib/Deployer.js +244 -96
- package/lib/MetadataTypeDefinitions.js +2 -0
- package/lib/MetadataTypeInfo.js +2 -0
- package/lib/Retriever.js +61 -84
- package/lib/cli.js +116 -11
- package/lib/index.js +241 -561
- package/lib/metadataTypes/AccountUser.js +117 -103
- package/lib/metadataTypes/Asset.js +705 -255
- package/lib/metadataTypes/AttributeGroup.js +23 -12
- package/lib/metadataTypes/Automation.js +489 -392
- package/lib/metadataTypes/Campaign.js +33 -93
- package/lib/metadataTypes/ContentArea.js +31 -11
- package/lib/metadataTypes/DataExtension.js +387 -372
- package/lib/metadataTypes/DataExtensionField.js +131 -54
- package/lib/metadataTypes/DataExtensionTemplate.js +22 -4
- package/lib/metadataTypes/DataExtract.js +61 -48
- package/lib/metadataTypes/DataExtractType.js +14 -8
- package/lib/metadataTypes/Discovery.js +21 -16
- package/lib/metadataTypes/Email.js +32 -12
- package/lib/metadataTypes/EmailSendDefinition.js +85 -80
- package/lib/metadataTypes/EventDefinition.js +61 -43
- package/lib/metadataTypes/FileTransfer.js +72 -52
- package/lib/metadataTypes/Filter.js +11 -4
- package/lib/metadataTypes/Folder.js +149 -117
- package/lib/metadataTypes/FtpLocation.js +14 -8
- package/lib/metadataTypes/ImportFile.js +61 -64
- package/lib/metadataTypes/Interaction.js +19 -4
- package/lib/metadataTypes/List.js +54 -13
- package/lib/metadataTypes/MetadataType.js +664 -454
- package/lib/metadataTypes/MobileCode.js +46 -0
- package/lib/metadataTypes/MobileKeyword.js +114 -0
- package/lib/metadataTypes/Query.js +206 -105
- package/lib/metadataTypes/Role.js +76 -61
- package/lib/metadataTypes/Script.js +147 -83
- package/lib/metadataTypes/SetDefinition.js +20 -8
- package/lib/metadataTypes/TriggeredSendDefinition.js +78 -58
- package/lib/metadataTypes/definitions/Asset.definition.js +21 -10
- package/lib/metadataTypes/definitions/AttributeGroup.definition.js +12 -0
- package/lib/metadataTypes/definitions/Automation.definition.js +10 -5
- package/lib/metadataTypes/definitions/Campaign.definition.js +44 -1
- package/lib/metadataTypes/definitions/DataExtension.definition.js +4 -0
- package/lib/metadataTypes/definitions/DataExtensionTemplate.definition.js +6 -0
- package/lib/metadataTypes/definitions/DataExtract.definition.js +18 -14
- package/lib/metadataTypes/definitions/Discovery.definition.js +12 -0
- package/lib/metadataTypes/definitions/EmailSendDefinition.definition.js +4 -0
- package/lib/metadataTypes/definitions/EventDefinition.definition.js +22 -0
- package/lib/metadataTypes/definitions/FileTransfer.definition.js +4 -0
- package/lib/metadataTypes/definitions/Filter.definition.js +4 -0
- package/lib/metadataTypes/definitions/Folder.definition.js +6 -0
- package/lib/metadataTypes/definitions/FtpLocation.definition.js +4 -0
- package/lib/metadataTypes/definitions/ImportFile.definition.js +10 -5
- package/lib/metadataTypes/definitions/Interaction.definition.js +4 -0
- package/lib/metadataTypes/definitions/MobileCode.definition.js +163 -0
- package/lib/metadataTypes/definitions/MobileKeyword.definition.js +253 -0
- package/lib/metadataTypes/definitions/Query.definition.js +4 -0
- package/lib/metadataTypes/definitions/Role.definition.js +5 -0
- package/lib/metadataTypes/definitions/Script.definition.js +4 -0
- package/lib/metadataTypes/definitions/SetDefinition.definition.js +28 -0
- package/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js +4 -0
- package/lib/retrieveChangelog.js +7 -6
- package/lib/util/auth.js +117 -0
- package/lib/util/businessUnit.js +55 -66
- package/lib/util/cache.js +194 -0
- package/lib/util/cli.js +90 -116
- package/lib/util/config.js +302 -0
- package/lib/util/devops.js +250 -50
- package/lib/util/file.js +141 -201
- package/lib/util/init.config.js +208 -75
- package/lib/util/init.git.js +45 -50
- package/lib/util/init.js +72 -59
- package/lib/util/init.npm.js +48 -39
- package/lib/util/util.js +280 -564
- package/package.json +45 -34
- package/test/dataExtension.test.js +152 -0
- package/test/mockRoot/.mcdev-auth.json +8 -0
- package/test/mockRoot/.mcdevrc.json +67 -0
- package/test/mockRoot/deploy/testInstance/testBU/dataExtension/childBU_dataextension_test.dataExtension-meta.json +39 -0
- package/test/mockRoot/deploy/testInstance/testBU/dataExtension/testDataExtension.dataExtension-meta.json +23 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.json +11 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.sql +4 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.json +11 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.sql +4 -0
- package/test/query.test.js +149 -0
- package/test/resourceFactory.js +142 -0
- package/test/resources/1111111/dataFolder/retrieve-response.xml +43 -0
- package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json +18 -0
- package/test/resources/9999999/automation/v1/queries/get-response.json +24 -0
- package/test/resources/9999999/automation/v1/queries/post-response.json +18 -0
- package/test/resources/9999999/dataExtension/build-expected.json +51 -0
- package/test/resources/9999999/dataExtension/create-expected.json +23 -0
- package/test/resources/9999999/dataExtension/create-response.xml +54 -0
- package/test/resources/9999999/dataExtension/retrieve-expected.json +51 -0
- package/test/resources/9999999/dataExtension/retrieve-response.xml +47 -0
- package/test/resources/9999999/dataExtension/template-expected.json +51 -0
- package/test/resources/9999999/dataExtension/update-expected.json +55 -0
- package/test/resources/9999999/dataExtension/update-response.xml +52 -0
- package/test/resources/9999999/dataExtensionField/retrieve-response.xml +93 -0
- package/test/resources/9999999/dataExtensionTemplate/retrieve-response.xml +303 -0
- package/test/resources/9999999/dataFolder/retrieve-response.xml +65 -0
- package/test/resources/9999999/query/build-expected.json +8 -0
- package/test/resources/9999999/query/get-expected.json +11 -0
- package/test/resources/9999999/query/patch-expected.json +11 -0
- package/test/resources/9999999/query/post-expected.json +11 -0
- package/test/resources/9999999/query/template-expected.json +8 -0
- package/test/resources/auth.json +32 -0
- package/test/resources/rest404-response.json +5 -0
- package/test/resources/retrieve-response.xml +21 -0
- package/test/utils.js +107 -0
- package/types/mcdev.d.js +301 -0
- package/CHANGELOG.md +0 -126
- package/PULL_REQUEST_TEMPLATE.md +0 -19
- 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
|
|
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
|
-
*
|
|
28
|
-
* @param {
|
|
29
|
-
* @
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
*
|
|
51
|
-
* @
|
|
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
|
-
*
|
|
78
|
-
* @
|
|
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
|
-
*
|
|
99
|
-
* @
|
|
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 {
|
|
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 {
|
|
123
|
-
* @param {
|
|
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
|
-
|
|
130
|
-
fs.mkdirpSync(directory);
|
|
131
|
-
}
|
|
146
|
+
await fs.ensureDir(directory);
|
|
132
147
|
try {
|
|
133
|
-
|
|
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 {
|
|
143
|
-
* @param {
|
|
144
|
-
* @param {
|
|
145
|
-
* @param {
|
|
146
|
-
* @returns {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 {
|
|
189
|
-
* @param {
|
|
190
|
-
* @param {
|
|
191
|
-
* @returns {
|
|
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) {
|
|
@@ -204,38 +192,52 @@ const File = {
|
|
|
204
192
|
// load the right prettier config relative to our file
|
|
205
193
|
switch (filetype) {
|
|
206
194
|
case 'htm':
|
|
207
|
-
case 'html':
|
|
195
|
+
case 'html': {
|
|
208
196
|
FileFs.prettierConfig.parser = 'html';
|
|
209
197
|
break;
|
|
198
|
+
}
|
|
210
199
|
case 'ssjs':
|
|
211
|
-
case 'js':
|
|
200
|
+
case 'js': {
|
|
212
201
|
FileFs.prettierConfig.parser = 'babel';
|
|
213
202
|
break;
|
|
214
|
-
|
|
203
|
+
}
|
|
204
|
+
case 'json': {
|
|
215
205
|
FileFs.prettierConfig.parser = 'json';
|
|
216
206
|
break;
|
|
207
|
+
}
|
|
217
208
|
case 'yaml':
|
|
218
|
-
case 'yml':
|
|
209
|
+
case 'yml': {
|
|
219
210
|
FileFs.prettierConfig.parser = 'yaml';
|
|
220
211
|
break;
|
|
221
|
-
|
|
212
|
+
}
|
|
213
|
+
case 'ts': {
|
|
222
214
|
FileFs.prettierConfig.parser = 'babel-ts';
|
|
223
215
|
break;
|
|
224
|
-
|
|
216
|
+
}
|
|
217
|
+
case 'css': {
|
|
225
218
|
FileFs.prettierConfig.parser = 'css';
|
|
226
219
|
break;
|
|
227
|
-
|
|
220
|
+
}
|
|
221
|
+
case 'less': {
|
|
228
222
|
FileFs.prettierConfig.parser = 'less';
|
|
229
223
|
break;
|
|
224
|
+
}
|
|
230
225
|
case 'sass':
|
|
231
|
-
case 'scss':
|
|
226
|
+
case 'scss': {
|
|
232
227
|
FileFs.prettierConfig.parser = 'scss';
|
|
233
228
|
break;
|
|
234
|
-
|
|
229
|
+
}
|
|
230
|
+
case 'md': {
|
|
235
231
|
FileFs.prettierConfig.parser = 'markdown';
|
|
236
232
|
break;
|
|
237
|
-
|
|
233
|
+
}
|
|
234
|
+
case 'sql': {
|
|
235
|
+
FileFs.prettierConfig.parser = 'sql';
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
default: {
|
|
238
239
|
FileFs.prettierConfig.parser = 'babel';
|
|
240
|
+
}
|
|
239
241
|
}
|
|
240
242
|
formatted = prettier.format(content, FileFs.prettierConfig);
|
|
241
243
|
} catch (ex) {
|
|
@@ -252,7 +254,7 @@ const File = {
|
|
|
252
254
|
filename + '.error',
|
|
253
255
|
'log',
|
|
254
256
|
`Error Log\nParser: ${FileFs.prettierConfig.parser}\n${ex.message.replace(
|
|
255
|
-
/[\
|
|
257
|
+
/[\u001B\u009B][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
|
|
256
258
|
|
|
257
259
|
''
|
|
258
260
|
)}`
|
|
@@ -265,23 +267,25 @@ const File = {
|
|
|
265
267
|
},
|
|
266
268
|
/**
|
|
267
269
|
* Saves text content to a file in the local file system. Will create the parent directory if it does not exist
|
|
270
|
+
*
|
|
268
271
|
* @param {string|string[]} directory directory the file will be written to
|
|
269
|
-
* @param {
|
|
270
|
-
* @param {
|
|
271
|
-
* @param {
|
|
272
|
-
* @param {
|
|
273
|
-
* @returns {Promise
|
|
272
|
+
* @param {string} filename name of the file without '.json' ending
|
|
273
|
+
* @param {string} filetype filetype suffix
|
|
274
|
+
* @param {string} content filecontent
|
|
275
|
+
* @param {object} [encoding] added for certain file types (like images)
|
|
276
|
+
* @returns {Promise.<boolean>} Promise
|
|
274
277
|
*/
|
|
275
278
|
writeToFile: async function (directory, filename, filetype, content, encoding) {
|
|
276
279
|
directory = this.filterIllegalPathChars(this.normalizePath(directory));
|
|
277
|
-
|
|
278
|
-
fs.mkdirpSync(directory);
|
|
279
|
-
}
|
|
280
|
+
await fs.ensureDir(directory);
|
|
280
281
|
// filter characters that are illegal for file names in Windows
|
|
281
282
|
filename = this.filterIllegalFilenames(filename);
|
|
282
|
-
|
|
283
|
+
const filePath = path.join(directory, filename + '.' + filetype);
|
|
283
284
|
try {
|
|
284
|
-
await fs.
|
|
285
|
+
if (await fs.pathExists(filePath)) {
|
|
286
|
+
Util.logger.debug(`Overwriting: ${filePath}`);
|
|
287
|
+
}
|
|
288
|
+
await fs.writeFile(filePath, content, encoding);
|
|
285
289
|
return true;
|
|
286
290
|
} catch (ex) {
|
|
287
291
|
Util.logger.error('File.writeToFile:: error | ' + ex.message);
|
|
@@ -291,11 +295,12 @@ const File = {
|
|
|
291
295
|
|
|
292
296
|
/**
|
|
293
297
|
* Saves json content to a file in the local file system. Will create the parent directory if it does not exist
|
|
294
|
-
*
|
|
295
|
-
* @param {
|
|
296
|
-
* @param {
|
|
297
|
-
* @param {
|
|
298
|
-
* @
|
|
298
|
+
*
|
|
299
|
+
* @param {string | string[]} directory directory where the file is stored
|
|
300
|
+
* @param {string} filename name of the file without '.json' ending
|
|
301
|
+
* @param {boolean} sync should execute sync (default is async)
|
|
302
|
+
* @param {boolean} cleanPath should execute sync (default is true)
|
|
303
|
+
* @returns {Promise | object} Promise or JSON object depending on if async or not
|
|
299
304
|
*/
|
|
300
305
|
readJSONFile: function (directory, filename, sync, cleanPath) {
|
|
301
306
|
try {
|
|
@@ -326,30 +331,32 @@ const File = {
|
|
|
326
331
|
},
|
|
327
332
|
/**
|
|
328
333
|
* reads file from local file system.
|
|
329
|
-
*
|
|
330
|
-
* @param {
|
|
331
|
-
* @param {
|
|
332
|
-
* @param {
|
|
333
|
-
* @
|
|
334
|
+
*
|
|
335
|
+
* @param {string | string[]} directory directory where the file is stored
|
|
336
|
+
* @param {string} filename name of the file without '.json' ending
|
|
337
|
+
* @param {string} filetype filetype suffix
|
|
338
|
+
* @param {string} [encoding='utf8'] read file with encoding (defaults to utf-8)
|
|
339
|
+
* @returns {Promise.<string>} file contents
|
|
334
340
|
*/
|
|
335
|
-
|
|
341
|
+
readFilteredFilename: function (directory, filename, filetype, encoding) {
|
|
336
342
|
try {
|
|
337
343
|
directory = this.filterIllegalPathChars(this.normalizePath(directory));
|
|
338
344
|
filename = this.filterIllegalFilenames(filename);
|
|
339
345
|
return fs.readFile(path.join(directory, filename + '.' + filetype), encoding || 'utf8');
|
|
340
346
|
} catch (ex) {
|
|
341
|
-
Util.logger.error('File.
|
|
347
|
+
Util.logger.error('File.readFilteredFilename:: error | ' + ex.message);
|
|
342
348
|
}
|
|
343
349
|
},
|
|
344
350
|
/**
|
|
345
351
|
* reads directories to a specific depth returning an array
|
|
346
352
|
* of file paths to be iterated over
|
|
353
|
+
*
|
|
347
354
|
* @example ['deploy/mcdev/bu1']
|
|
348
|
-
* @param {
|
|
349
|
-
* @param {
|
|
350
|
-
* @param {
|
|
351
|
-
* @param {
|
|
352
|
-
* @returns {Promise
|
|
355
|
+
* @param {string} directory directory to checkin
|
|
356
|
+
* @param {number} depth how many levels to check (1 base)
|
|
357
|
+
* @param {boolean} [includeStem] include the parent directory in the response
|
|
358
|
+
* @param {number} [_stemLength] set recursively for subfolders. do not set manually!
|
|
359
|
+
* @returns {Promise.<string[]>} array of fully defined file paths
|
|
353
360
|
*/
|
|
354
361
|
readDirectories: async function (directory, depth, includeStem, _stemLength) {
|
|
355
362
|
try {
|
|
@@ -362,8 +369,8 @@ const File = {
|
|
|
362
369
|
for (const dirent of raw) {
|
|
363
370
|
const direntPath = path.join(directory, dirent.name);
|
|
364
371
|
if (
|
|
365
|
-
fs.
|
|
366
|
-
fs.
|
|
372
|
+
(await fs.pathExists(direntPath)) &&
|
|
373
|
+
(await fs.lstat(direntPath)).isDirectory() &&
|
|
367
374
|
depth > 0
|
|
368
375
|
) {
|
|
369
376
|
const nestedChildren = await this.readDirectories(
|
|
@@ -376,14 +383,15 @@ const File = {
|
|
|
376
383
|
}
|
|
377
384
|
}
|
|
378
385
|
if (children.length === 0) {
|
|
379
|
-
if
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
386
|
+
// if not includeStem then remove base directory and leading slahes and backslashes
|
|
387
|
+
return includeStem
|
|
388
|
+
? [directory]
|
|
389
|
+
: [
|
|
390
|
+
directory
|
|
391
|
+
.slice(Math.max(0, _stemLength))
|
|
392
|
+
.replace(/^\\+/, '')
|
|
393
|
+
.replace(/^\/+/, ''),
|
|
394
|
+
];
|
|
387
395
|
} else {
|
|
388
396
|
return children;
|
|
389
397
|
}
|
|
@@ -396,12 +404,14 @@ const File = {
|
|
|
396
404
|
/**
|
|
397
405
|
* reads directories to a specific depth returning an array
|
|
398
406
|
* of file paths to be iterated over using sync api (required in constructors)
|
|
407
|
+
* TODO - merge with readDirectories. so far the logic is really different
|
|
408
|
+
*
|
|
399
409
|
* @example ['deploy/mcdev/bu1']
|
|
400
|
-
* @param {
|
|
401
|
-
* @param {
|
|
402
|
-
* @param {
|
|
403
|
-
* @param {
|
|
404
|
-
* @returns {
|
|
410
|
+
* @param {string} directory directory to checkin
|
|
411
|
+
* @param {number} [depth] how many levels to check (1 base)
|
|
412
|
+
* @param {boolean} [includeStem] include the parent directory in the response
|
|
413
|
+
* @param {number} [_stemLength] set recursively for subfolders. do not set manually!
|
|
414
|
+
* @returns {string[]} array of fully defined file paths
|
|
405
415
|
*/
|
|
406
416
|
readDirectoriesSync: function (directory, depth, includeStem, _stemLength) {
|
|
407
417
|
try {
|
|
@@ -417,8 +427,8 @@ const File = {
|
|
|
417
427
|
children.push(directory);
|
|
418
428
|
} else {
|
|
419
429
|
// remove base directory and leading slahes and backslashes
|
|
420
|
-
const currentPath = directory.
|
|
421
|
-
children.push(currentPath
|
|
430
|
+
const currentPath = directory.slice(Math.max(0, _stemLength)).replace(path.sep, '');
|
|
431
|
+
children.push(currentPath || '.');
|
|
422
432
|
}
|
|
423
433
|
// read all directories
|
|
424
434
|
const raw = fs.readdirSync(directory, { withFileTypes: true });
|
|
@@ -442,94 +452,24 @@ const File = {
|
|
|
442
452
|
Util.logger.debug(ex.stack);
|
|
443
453
|
}
|
|
444
454
|
},
|
|
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
455
|
/**
|
|
502
456
|
* helper that splits the config back into auth & config parts to save them separately
|
|
503
|
-
*
|
|
504
|
-
* @
|
|
457
|
+
*
|
|
458
|
+
* @param {TYPE.Mcdevrc} properties central properties object
|
|
459
|
+
* @returns {Promise.<void>} -
|
|
505
460
|
*/
|
|
506
461
|
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
462
|
// we want to save to save the full version here to allow us to upgrade configs properly in the future
|
|
523
|
-
|
|
463
|
+
properties.version = packageJson.version;
|
|
524
464
|
|
|
525
|
-
await this.writeJSONToFile('', Util.configFileName.split('.json')[0],
|
|
526
|
-
await this.writeJSONToFile('', Util.authFileName.split('.json')[0], auth);
|
|
465
|
+
await this.writeJSONToFile('', Util.configFileName.split('.json')[0], properties);
|
|
527
466
|
Util.logger.info(`✔️ ${Util.configFileName} and ${Util.authFileName} saved successfully`);
|
|
528
467
|
},
|
|
529
468
|
/**
|
|
530
469
|
* Initalises Prettier formatting lib async.
|
|
531
|
-
*
|
|
532
|
-
* @
|
|
470
|
+
*
|
|
471
|
+
* @param {string} [filetype='html'] filetype ie. JSON or SSJS
|
|
472
|
+
* @returns {Promise.<boolean>} success of config load
|
|
533
473
|
*/
|
|
534
474
|
async initPrettier(filetype) {
|
|
535
475
|
if (FileFs.prettierConfig === null) {
|