@ds-sfdc/sfparty 0.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.
- package/LICENSE.md +28 -0
- package/README.md +18 -0
- package/index.js +535 -0
- package/lib/fileUtils.js +165 -0
- package/lib/label/combine.js +203 -0
- package/lib/label/definition.js +12 -0
- package/lib/label/split.js +213 -0
- package/lib/permset/combine.js +286 -0
- package/lib/permset/definition.js +74 -0
- package/lib/permset/split.js +287 -0
- package/lib/profile/combine.js +309 -0
- package/lib/profile/definition.js +55 -0
- package/lib/profile/split.js +983 -0
- package/lib/workflow/combine.js +330 -0
- package/lib/workflow/definition.js +55 -0
- package/lib/workflow/split.js +278 -0
- package/nodemon.json +4 -0
- package/package.json +51 -0
- package/sfdx-project.json +11 -0
- package/tests/root.spec.js +14 -0
|
@@ -0,0 +1,983 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import fs from 'fs'
|
|
5
|
+
import os from 'os'
|
|
6
|
+
import { readFile } from 'fs'
|
|
7
|
+
import { Parser } from 'xml2js'
|
|
8
|
+
import logUpdate from 'log-update';
|
|
9
|
+
import chalk from 'chalk'
|
|
10
|
+
import convertHrtime from 'convert-hrtime'
|
|
11
|
+
import cliSpinners from 'cli-spinners'
|
|
12
|
+
import * as yaml from 'js-yaml'
|
|
13
|
+
import * as fileUtils from '../fileUtils.js'
|
|
14
|
+
import { profileDefinition } from './definition.js'
|
|
15
|
+
|
|
16
|
+
const spinner = cliSpinners['dots']
|
|
17
|
+
let spinnerFrame = spinner.frames[0]
|
|
18
|
+
let index = 0
|
|
19
|
+
|
|
20
|
+
// TODO replace global logger with error message
|
|
21
|
+
export class Profile {
|
|
22
|
+
#fileName = ''
|
|
23
|
+
#spinnerMessage = ''
|
|
24
|
+
#profileInfo = {
|
|
25
|
+
name: undefined,
|
|
26
|
+
fullName: undefined,
|
|
27
|
+
userLicense: undefined,
|
|
28
|
+
custom: undefined,
|
|
29
|
+
description: undefined,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
constructor(config) {
|
|
33
|
+
this.sourceDir = config.sourceDir
|
|
34
|
+
this.targetDir = config.targetDir
|
|
35
|
+
this.metaFilePath = config.metaFilePath
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get metaFilePath() {
|
|
39
|
+
return this._metaFilePath
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
set metaFilePath(value) {
|
|
43
|
+
value = value.trim()
|
|
44
|
+
if (value === '') {
|
|
45
|
+
throw 'The file path cannot be empty'
|
|
46
|
+
}
|
|
47
|
+
this._metaFilePath = value
|
|
48
|
+
this.#fileName = fileUtils.fileInfo(value).filename
|
|
49
|
+
this.#spinnerMessage = `[%1] of ${global.processed.total} - Profile: ${chalk.yellowBright(this.#fileName)}[%2]`
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
split() {
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
if (!this.#fileName || !this.sourceDir || !this.targetDir || !this.metaFilePath) {
|
|
55
|
+
global.logger.error('Invalid information passed to split')
|
|
56
|
+
process.exit(1)
|
|
57
|
+
}
|
|
58
|
+
if (!fileUtils.fileExists(this.metaFilePath)) {
|
|
59
|
+
global.logger.error(`file not found: ${this.metaFilePath}`)
|
|
60
|
+
process.exit(1)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.profileInfo = fileUtils.fileInfo(this.metaFilePath)
|
|
64
|
+
this.#profileInfo.name = this.profileInfo.filename.replace('.profile-meta.xml', '')
|
|
65
|
+
this.targetDir = path.join(this.targetDir, this.#profileInfo.name)
|
|
66
|
+
let spinnerMessage = this.#spinnerMessage.toString()
|
|
67
|
+
logUpdate(spinnerMessage.toString().replace('[%2]', '').replace('[%1]', global.processed.current.toString().padStart(global.processed.total.toString().length, ' ')))
|
|
68
|
+
let parser = new Parser();
|
|
69
|
+
const getJSON = new Promise((resolve, reject) => {
|
|
70
|
+
readFile(this.metaFilePath, function (err, data) {
|
|
71
|
+
parser.parseString(data, function (err, result) {
|
|
72
|
+
if (result) {
|
|
73
|
+
resolve(result)
|
|
74
|
+
} else {
|
|
75
|
+
global.logger.error(`error converting xml to json: ${filePath}`)
|
|
76
|
+
process.exit(1)
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
getJSON.then((result) => {
|
|
82
|
+
resolve(processProfile(result, this.#profileInfo, this.targetDir, this.#spinnerMessage, process.hrtime.bigint()))
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
function processProfile(profileJSON, profileInfo, targetDir, spinnerMessage, startTime) {
|
|
87
|
+
fileUtils.deleteDirectory(targetDir, true) // recursive delete existing directory
|
|
88
|
+
fileUtils.createDirectory(targetDir) // create directory
|
|
89
|
+
let keys = Object.keys(profileJSON.Profile)
|
|
90
|
+
|
|
91
|
+
keys.forEach(key => {
|
|
92
|
+
logUpdate(spinnerMessage.replace('[%2]', `\n${chalk.magentaBright(nextFrame())} ${key}`).replace('[%1]', global.processed.current.toString().padStart(global.processed.total.toString().length, ' ')))
|
|
93
|
+
processKeys: if (profileDefinition.main.includes(key)) {
|
|
94
|
+
profileInfo[key] = profileJSON.Profile[key][0]
|
|
95
|
+
} else if (profileDefinition.directories.includes(key)) {
|
|
96
|
+
fileUtils.createDirectory(path.join(targetDir, key)) // create directory
|
|
97
|
+
switch (key) {
|
|
98
|
+
case 'fieldLevelSecurities':
|
|
99
|
+
fieldLevelSecurities(profileJSON.Profile.fieldLevelSecurities, targetDir)
|
|
100
|
+
break
|
|
101
|
+
case 'fieldPermissions':
|
|
102
|
+
fieldPermissions(profileJSON.Profile.fieldPermissions, targetDir)
|
|
103
|
+
break
|
|
104
|
+
case 'loginFlows':
|
|
105
|
+
loginFlows(profileJSON.Profile.loginFlows, targetDir)
|
|
106
|
+
break
|
|
107
|
+
case 'objectPermissions':
|
|
108
|
+
objectPermissions(profileJSON.Profile.objectPermissions, targetDir)
|
|
109
|
+
break
|
|
110
|
+
case 'recordTypeVisibilities':
|
|
111
|
+
recordTypeVisibilities(profileJSON.Profile.recordTypeVisibilities, targetDir)
|
|
112
|
+
break
|
|
113
|
+
default:
|
|
114
|
+
global.logger.warning('Not processed:', key)
|
|
115
|
+
}
|
|
116
|
+
} else if (profileDefinition.singleFiles.includes(key)) {
|
|
117
|
+
switch (key) {
|
|
118
|
+
case 'applicationVisibilities':
|
|
119
|
+
applicationVisibilities(profileJSON.Profile.applicationVisibilities, targetDir)
|
|
120
|
+
break
|
|
121
|
+
case 'categoryGroupVisibilities':
|
|
122
|
+
categoryGroupVisibilities(profileJSON.Profile.categoryGroupVisibilities, targetDir)
|
|
123
|
+
break
|
|
124
|
+
case 'classAccesses':
|
|
125
|
+
classAccesses(profileJSON.Profile.classAccesses, targetDir)
|
|
126
|
+
break
|
|
127
|
+
case 'customMetadataTypeAccesses':
|
|
128
|
+
customMetadataTypeAccesses(profileJSON.Profile.customMetadataTypeAccesses, targetDir)
|
|
129
|
+
break
|
|
130
|
+
case 'customPermissions':
|
|
131
|
+
customPermissions(profileJSON.Profile.customPermissions, targetDir)
|
|
132
|
+
break
|
|
133
|
+
case 'customSettingAccesses':
|
|
134
|
+
customSettingAccesses(profileJSON.Profile.customSettingAccesses, targetDir)
|
|
135
|
+
break
|
|
136
|
+
case 'externalDataSourceAccesses':
|
|
137
|
+
externalDataSourceAccesses(profileJSON.Profile.externalDataSourceAccesses, targetDir)
|
|
138
|
+
break
|
|
139
|
+
case 'flowAccesses':
|
|
140
|
+
flowAccesses(profileJSON.Profile.flowAccesses, targetDir)
|
|
141
|
+
break
|
|
142
|
+
case 'layoutAssignments':
|
|
143
|
+
layoutAssignments(profileJSON.Profile.layoutAssignments, targetDir)
|
|
144
|
+
break
|
|
145
|
+
case 'loginIpRanges':
|
|
146
|
+
loginIpRanges(profileJSON.Profile.loginIpRanges, targetDir)
|
|
147
|
+
break
|
|
148
|
+
case 'pageAccesses':
|
|
149
|
+
pageAccesses(profileJSON.Profile.pageAccesses, targetDir)
|
|
150
|
+
break
|
|
151
|
+
case 'tabVisibilities':
|
|
152
|
+
tabVisibilities(profileJSON.Profile.tabVisibilities, targetDir)
|
|
153
|
+
break
|
|
154
|
+
case 'userPermissions':
|
|
155
|
+
userPermissions(profileJSON.Profile.userPermissions, targetDir)
|
|
156
|
+
break
|
|
157
|
+
default:
|
|
158
|
+
global.logger.warning('Not processed:', key)
|
|
159
|
+
}
|
|
160
|
+
} else if (profileDefinition.ignore.includes(key)) {
|
|
161
|
+
break processKeys;
|
|
162
|
+
} else {
|
|
163
|
+
global.logger.warning('Not processed:', key)
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
// Main cannot be called until after all the keys are processed and profileJSON.Profile[key][0] is populated
|
|
168
|
+
Main(profileInfo, targetDir, spinnerMessage, startTime)
|
|
169
|
+
|
|
170
|
+
function Main(profileInfo, targetDir, spinnerMessage, startTime) {
|
|
171
|
+
let fileName = path.join(targetDir, `main.${global.format}`)
|
|
172
|
+
saveFile(profileInfo, fileName)
|
|
173
|
+
|
|
174
|
+
const diff = process.hrtime.bigint() - BigInt(startTime)
|
|
175
|
+
let executionTime = convertHrtime(diff);
|
|
176
|
+
executionTime.seconds = Math.round(executionTime.seconds)
|
|
177
|
+
executionTime.milliseconds = Math.round(executionTime.milliseconds / 1000)
|
|
178
|
+
if (executionTime.milliseconds == 0 && executionTime.nanoseconds > 0) executionTime.milliseconds = 1
|
|
179
|
+
let durationMessage = `${executionTime.seconds}.${executionTime.milliseconds}s`
|
|
180
|
+
logUpdate(spinnerMessage.replace('[%2]', `. ${chalk.greenBright('✔')} Processed in ${durationMessage}.`).replace('[%1]', global.processed.current.toString().padStart(global.processed.total.toString().length, ' ')))
|
|
181
|
+
logUpdate.done()
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function applicationVisibilities(jsonResult, targetDir) {
|
|
185
|
+
let json = {
|
|
186
|
+
applicationVisibilities: undefined
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// sort json to order by field
|
|
190
|
+
jsonResult.sort((a, b) => {
|
|
191
|
+
if (a.application < b.application) {
|
|
192
|
+
return -1;
|
|
193
|
+
}
|
|
194
|
+
if (a.application > b.application) {
|
|
195
|
+
return 1;
|
|
196
|
+
}
|
|
197
|
+
return 0;
|
|
198
|
+
})
|
|
199
|
+
json.applicationVisibilities = jsonResult
|
|
200
|
+
let fileName = path.join(targetDir, 'applicationVisibilities' + `.${global.format}`)
|
|
201
|
+
saveFile(json, fileName)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function categoryGroupVisibilities(jsonResult) {
|
|
205
|
+
let json = {
|
|
206
|
+
categoryGroupVisibilities: undefined
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// sort json to order by field
|
|
210
|
+
jsonResult.sort((a, b) => {
|
|
211
|
+
if (a.dataCategoryGroup < b.dataCategoryGroup) {
|
|
212
|
+
return -1;
|
|
213
|
+
}
|
|
214
|
+
if (a.dataCategoryGroup > b.dataCategoryGroup) {
|
|
215
|
+
return 1;
|
|
216
|
+
}
|
|
217
|
+
return 0;
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
// sort json keys to put field first
|
|
221
|
+
jsonResult.forEach(function (part, index) {
|
|
222
|
+
this[index].dataCategoryGroup = this[index].dataCategoryGroup[0]
|
|
223
|
+
this[index].visibility = this[index].visibility[0]
|
|
224
|
+
this[index] = Object.keys(this[index])
|
|
225
|
+
.sort((a, b) => {
|
|
226
|
+
if (a == 'dataCategoryGroup') return -1
|
|
227
|
+
if (b == 'dataCategoryGroup') return 1
|
|
228
|
+
if (a == 'visibility') return -1
|
|
229
|
+
if (b == 'visibility') return 1
|
|
230
|
+
if (a == 'dataCategories') return -1
|
|
231
|
+
if (b == 'dataCategories') return 1
|
|
232
|
+
|
|
233
|
+
return 0;
|
|
234
|
+
})
|
|
235
|
+
.reduce((accumulator, key) => {
|
|
236
|
+
accumulator[key] = this[index][key];
|
|
237
|
+
|
|
238
|
+
return accumulator;
|
|
239
|
+
}, {});
|
|
240
|
+
}, jsonResult);
|
|
241
|
+
|
|
242
|
+
json.categoryGroupVisibilities = jsonResult
|
|
243
|
+
let fileName = path.join(targetDir, 'categoryGroupVisibilities' + `.${global.format}`)
|
|
244
|
+
saveFile(json, fileName)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function classAccesses(jsonResult, targetDir) {
|
|
248
|
+
let json = {
|
|
249
|
+
classAccesses: undefined
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// sort json to order by field
|
|
253
|
+
jsonResult.sort((a, b) => {
|
|
254
|
+
if (a.apexClass < b.apexClass) {
|
|
255
|
+
return -1;
|
|
256
|
+
}
|
|
257
|
+
if (a.apexClass > b.apexClass) {
|
|
258
|
+
return 1;
|
|
259
|
+
}
|
|
260
|
+
return 0;
|
|
261
|
+
})
|
|
262
|
+
json.classAccesses = jsonResult
|
|
263
|
+
let fileName = path.join(targetDir, 'classAccesses' + `.${global.format}`)
|
|
264
|
+
saveFile(json, fileName)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function customMetadataTypeAccesses(jsonResult, targetDir) {
|
|
268
|
+
let json = {
|
|
269
|
+
customMetadataTypeAccesses: undefined
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// sort json keys to put field first
|
|
273
|
+
jsonResult.forEach(function (part, index) {
|
|
274
|
+
this[index] = Object.keys(this[index])
|
|
275
|
+
.sort((a, b) => {
|
|
276
|
+
if (a == 'name') return -1
|
|
277
|
+
if (b == 'name') return 1
|
|
278
|
+
if (a == 'enabled') return -1
|
|
279
|
+
if (b == 'enabled') return 1
|
|
280
|
+
|
|
281
|
+
return 0;
|
|
282
|
+
})
|
|
283
|
+
.reduce((accumulator, key) => {
|
|
284
|
+
accumulator[key] = this[index][key];
|
|
285
|
+
|
|
286
|
+
return accumulator;
|
|
287
|
+
}, {});
|
|
288
|
+
}, jsonResult);
|
|
289
|
+
|
|
290
|
+
// sort json to order by name
|
|
291
|
+
jsonResult.sort((a, b) => {
|
|
292
|
+
if (a.name < b.name) {
|
|
293
|
+
return -1;
|
|
294
|
+
}
|
|
295
|
+
if (a.name > b.name) {
|
|
296
|
+
return 1;
|
|
297
|
+
}
|
|
298
|
+
return 0;
|
|
299
|
+
})
|
|
300
|
+
json.customMetadataTypeAccesses = jsonResult
|
|
301
|
+
let fileName = path.join(targetDir, 'customMetadataTypeAccesses' + `.${global.format}`)
|
|
302
|
+
saveFile(json, fileName)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function customPermissions(jsonResult, targetDir) {
|
|
306
|
+
let json = {
|
|
307
|
+
customPermissions: undefined
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// sort json keys to put field first
|
|
311
|
+
jsonResult.forEach(function (part, index) {
|
|
312
|
+
this[index] = Object.keys(this[index])
|
|
313
|
+
.sort((a, b) => {
|
|
314
|
+
if (a == 'name') return -1
|
|
315
|
+
if (b == 'name') return 1
|
|
316
|
+
if (a == 'enabled') return -1
|
|
317
|
+
if (b == 'enabled') return 1
|
|
318
|
+
|
|
319
|
+
return 0;
|
|
320
|
+
})
|
|
321
|
+
.reduce((accumulator, key) => {
|
|
322
|
+
accumulator[key] = this[index][key];
|
|
323
|
+
|
|
324
|
+
return accumulator;
|
|
325
|
+
}, {});
|
|
326
|
+
}, jsonResult);
|
|
327
|
+
|
|
328
|
+
// sort json to order by name
|
|
329
|
+
jsonResult.sort((a, b) => {
|
|
330
|
+
if (a.name < b.name) {
|
|
331
|
+
return -1;
|
|
332
|
+
}
|
|
333
|
+
if (a.name > b.name) {
|
|
334
|
+
return 1;
|
|
335
|
+
}
|
|
336
|
+
return 0;
|
|
337
|
+
})
|
|
338
|
+
json.customPermissions = jsonResult
|
|
339
|
+
let fileName = path.join(targetDir, 'customPermissions' + `.${global.format}`)
|
|
340
|
+
saveFile(json, fileName)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function customSettingAccesses(jsonResult, targetDir) {
|
|
344
|
+
let json = {
|
|
345
|
+
customSettingAccesses: undefined
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// sort json keys to put field first
|
|
349
|
+
jsonResult.forEach(function (part, index) {
|
|
350
|
+
this[index] = Object.keys(this[index])
|
|
351
|
+
.sort((a, b) => {
|
|
352
|
+
if (a == 'name') return -1
|
|
353
|
+
if (b == 'name') return 1
|
|
354
|
+
if (a == 'enabled') return -1
|
|
355
|
+
if (b == 'enabled') return 1
|
|
356
|
+
|
|
357
|
+
return 0;
|
|
358
|
+
})
|
|
359
|
+
.reduce((accumulator, key) => {
|
|
360
|
+
accumulator[key] = this[index][key];
|
|
361
|
+
|
|
362
|
+
return accumulator;
|
|
363
|
+
}, {});
|
|
364
|
+
}, jsonResult);
|
|
365
|
+
|
|
366
|
+
// sort json to order by name
|
|
367
|
+
jsonResult.sort((a, b) => {
|
|
368
|
+
if (a.name < b.name) {
|
|
369
|
+
return -1;
|
|
370
|
+
}
|
|
371
|
+
if (a.name > b.name) {
|
|
372
|
+
return 1;
|
|
373
|
+
}
|
|
374
|
+
return 0;
|
|
375
|
+
})
|
|
376
|
+
json.customSettingAccesses = jsonResult
|
|
377
|
+
let fileName = path.join(targetDir, 'customSettingAccesses' + `.${global.format}`)
|
|
378
|
+
saveFile(json, fileName)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function externalDataSourceAccesses(jsonResult, targetDir) {
|
|
382
|
+
let json = {
|
|
383
|
+
externalDataSourceAccesses: undefined
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// sort json keys to put field first
|
|
387
|
+
jsonResult.forEach(function (part, index) {
|
|
388
|
+
this[index] = Object.keys(this[index])
|
|
389
|
+
.sort((a, b) => {
|
|
390
|
+
if (a == 'externalDataSource') return -1
|
|
391
|
+
if (b == 'externalDataSource') return 1
|
|
392
|
+
if (a == 'enabled') return -1
|
|
393
|
+
if (b == 'enabled') return 1
|
|
394
|
+
|
|
395
|
+
return 0;
|
|
396
|
+
})
|
|
397
|
+
.reduce((accumulator, key) => {
|
|
398
|
+
accumulator[key] = this[index][key];
|
|
399
|
+
|
|
400
|
+
return accumulator;
|
|
401
|
+
}, {});
|
|
402
|
+
}, jsonResult);
|
|
403
|
+
|
|
404
|
+
// sort json to order by externalDataSource
|
|
405
|
+
jsonResult.sort((a, b) => {
|
|
406
|
+
if (a.externalDataSource < b.externalDataSource) {
|
|
407
|
+
return -1;
|
|
408
|
+
}
|
|
409
|
+
if (a.externalDataSource > b.externalDataSource) {
|
|
410
|
+
return 1;
|
|
411
|
+
}
|
|
412
|
+
return 0;
|
|
413
|
+
})
|
|
414
|
+
json.externalDataSourceAccesses = jsonResult
|
|
415
|
+
let fileName = path.join(targetDir, 'externalDataSourceAccesses' + `.${global.format}`)
|
|
416
|
+
saveFile(json, fileName)
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function fieldLevelSecurities(jsonResult, targetDir) {
|
|
420
|
+
const objects = {}
|
|
421
|
+
|
|
422
|
+
// populate objects with fieldLevelSecurities data per object
|
|
423
|
+
jsonResult.forEach(element => {
|
|
424
|
+
let [object] = element.field[0].toString().replaceAll('\n', '').trim().split('.')
|
|
425
|
+
if (objects[object] === undefined) {
|
|
426
|
+
objects[object] = []
|
|
427
|
+
}
|
|
428
|
+
objects[object].push(element)
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
// iterate objects keys (per object)
|
|
432
|
+
let keys = Object.keys(objects)
|
|
433
|
+
keys.forEach(key => {
|
|
434
|
+
let tag = 'fieldLevelSecurities'
|
|
435
|
+
let fileName = path.join(targetDir, tag, key + `.${global.format}`)
|
|
436
|
+
|
|
437
|
+
// sort json keys to put field first
|
|
438
|
+
let jsonResult = objects[key]
|
|
439
|
+
jsonResult.forEach(function (part, index) {
|
|
440
|
+
this[index] = Object.keys(this[index])
|
|
441
|
+
.sort((a, b) => {
|
|
442
|
+
if (a == 'field') return -1
|
|
443
|
+
if (b == 'field') return 1
|
|
444
|
+
if (a == 'editable') return -1
|
|
445
|
+
if (b == 'editable') return 1
|
|
446
|
+
if (a == 'readable') return -1
|
|
447
|
+
if (b == 'readable') return 1
|
|
448
|
+
if (a == 'hidden') return -1
|
|
449
|
+
if (b == 'hidden') return 1
|
|
450
|
+
|
|
451
|
+
return 0;
|
|
452
|
+
})
|
|
453
|
+
.reduce((accumulator, key) => {
|
|
454
|
+
accumulator[key] = this[index][key];
|
|
455
|
+
|
|
456
|
+
return accumulator;
|
|
457
|
+
}, {});
|
|
458
|
+
}, jsonResult);
|
|
459
|
+
|
|
460
|
+
// sort json to order by field
|
|
461
|
+
jsonResult.sort((a, b) => {
|
|
462
|
+
if (a.field < b.field) {
|
|
463
|
+
return -1;
|
|
464
|
+
}
|
|
465
|
+
if (a.field > b.field) {
|
|
466
|
+
return 1;
|
|
467
|
+
}
|
|
468
|
+
return 0;
|
|
469
|
+
})
|
|
470
|
+
|
|
471
|
+
let json = {
|
|
472
|
+
object: undefined,
|
|
473
|
+
fieldLevelSecurities: undefined
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
json.object = key
|
|
477
|
+
json.fieldLevelSecurities = jsonResult
|
|
478
|
+
saveFile(json, fileName)
|
|
479
|
+
})
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function fieldPermissions(jsonResult, targetDir) {
|
|
483
|
+
const objects = {}
|
|
484
|
+
|
|
485
|
+
// populate objects with fieldPermission data per object
|
|
486
|
+
jsonResult.forEach(element => {
|
|
487
|
+
let [object] = element.field[0].toString().replaceAll('\n', '').trim().split('.')
|
|
488
|
+
if (objects[object] === undefined) {
|
|
489
|
+
objects[object] = []
|
|
490
|
+
}
|
|
491
|
+
objects[object].push(element)
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
// iterate objects keys (per object)
|
|
495
|
+
let keys = Object.keys(objects)
|
|
496
|
+
keys.forEach(key => {
|
|
497
|
+
let tag = 'fieldPermissions'
|
|
498
|
+
let fileName = path.join(targetDir, tag, key + `.${global.format}`)
|
|
499
|
+
|
|
500
|
+
// sort json keys to put field first
|
|
501
|
+
let jsonResult = objects[key]
|
|
502
|
+
jsonResult.forEach(function (part, index) {
|
|
503
|
+
this[index] = Object.keys(this[index])
|
|
504
|
+
.sort((a, b) => {
|
|
505
|
+
if (a == 'field') return -1
|
|
506
|
+
if (b == 'field') return 1
|
|
507
|
+
if (a == 'editable') return -1
|
|
508
|
+
if (b == 'editable') return 1
|
|
509
|
+
if (a == 'readable') return -1
|
|
510
|
+
if (b == 'readable') return 1
|
|
511
|
+
if (a == 'hidden') return -1
|
|
512
|
+
if (b == 'hidden') return 1
|
|
513
|
+
|
|
514
|
+
return 0;
|
|
515
|
+
})
|
|
516
|
+
.reduce((accumulator, key) => {
|
|
517
|
+
accumulator[key] = this[index][key];
|
|
518
|
+
|
|
519
|
+
return accumulator;
|
|
520
|
+
}, {});
|
|
521
|
+
}, jsonResult);
|
|
522
|
+
|
|
523
|
+
// sort json to order by field
|
|
524
|
+
jsonResult.sort((a, b) => {
|
|
525
|
+
if (a.field < b.field) {
|
|
526
|
+
return -1;
|
|
527
|
+
}
|
|
528
|
+
if (a.field > b.field) {
|
|
529
|
+
return 1;
|
|
530
|
+
}
|
|
531
|
+
return 0;
|
|
532
|
+
})
|
|
533
|
+
|
|
534
|
+
let json = {
|
|
535
|
+
object: undefined,
|
|
536
|
+
fieldPermissions: undefined
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
json.object = key
|
|
540
|
+
json.fieldPermissions = jsonResult
|
|
541
|
+
saveFile(json, fileName)
|
|
542
|
+
})
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function flowAccesses(jsonResult, targetDir) {
|
|
546
|
+
let json = {
|
|
547
|
+
flowAccesses: undefined
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// sort json keys to put field first
|
|
551
|
+
jsonResult.forEach(function (part, index) {
|
|
552
|
+
this[index] = Object.keys(this[index])
|
|
553
|
+
.sort((a, b) => {
|
|
554
|
+
if (a == 'flow') return -1
|
|
555
|
+
if (b == 'flow') return 1
|
|
556
|
+
if (a == 'enabled') return -1
|
|
557
|
+
if (b == 'enabled') return 1
|
|
558
|
+
|
|
559
|
+
return 0;
|
|
560
|
+
})
|
|
561
|
+
.reduce((accumulator, key) => {
|
|
562
|
+
accumulator[key] = this[index][key];
|
|
563
|
+
|
|
564
|
+
return accumulator;
|
|
565
|
+
}, {});
|
|
566
|
+
}, jsonResult);
|
|
567
|
+
|
|
568
|
+
// sort json to order by flow
|
|
569
|
+
jsonResult.sort((a, b) => {
|
|
570
|
+
if (a.flow < b.flow) {
|
|
571
|
+
return -1;
|
|
572
|
+
}
|
|
573
|
+
if (a.flow > b.flow) {
|
|
574
|
+
return 1;
|
|
575
|
+
}
|
|
576
|
+
return 0;
|
|
577
|
+
})
|
|
578
|
+
json.flowAccesses = jsonResult
|
|
579
|
+
let fileName = path.join(targetDir, 'flowAccesses' + `.${global.format}`)
|
|
580
|
+
saveFile(json, fileName)
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function layoutAssignments(jsonResult, targetDir) {
|
|
584
|
+
let json = {
|
|
585
|
+
layoutAssignments: undefined
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// sort json keys to put field first
|
|
589
|
+
jsonResult.forEach(function (part, index) {
|
|
590
|
+
this[index] = Object.keys(this[index])
|
|
591
|
+
.sort((a, b) => {
|
|
592
|
+
if (a == 'layout') return -1
|
|
593
|
+
if (b == 'layout') return 1
|
|
594
|
+
if (a == 'recordType') return -1
|
|
595
|
+
if (b == 'recordType') return 1
|
|
596
|
+
|
|
597
|
+
return 0;
|
|
598
|
+
})
|
|
599
|
+
.reduce((accumulator, key) => {
|
|
600
|
+
accumulator[key] = this[index][key];
|
|
601
|
+
|
|
602
|
+
return accumulator;
|
|
603
|
+
}, {});
|
|
604
|
+
}, jsonResult);
|
|
605
|
+
|
|
606
|
+
// sort json to order by layout
|
|
607
|
+
jsonResult.sort((a, b) => {
|
|
608
|
+
if (a.layout < b.layout) {
|
|
609
|
+
return -1;
|
|
610
|
+
}
|
|
611
|
+
if (a.layout > b.layout) {
|
|
612
|
+
return 1;
|
|
613
|
+
}
|
|
614
|
+
if (a.recordType < b.recordType || a.recordType === undefined) {
|
|
615
|
+
return -1;
|
|
616
|
+
}
|
|
617
|
+
if (a.recordType > b.recordType || b.recordType === undefined) {
|
|
618
|
+
return 1;
|
|
619
|
+
}
|
|
620
|
+
return 0;
|
|
621
|
+
})
|
|
622
|
+
json.layoutAssignments = jsonResult
|
|
623
|
+
let fileName = path.join(targetDir, 'layoutAssignments' + `.${global.format}`)
|
|
624
|
+
saveFile(json, fileName)
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function loginFlows(jsonResult, targetDir) {
|
|
628
|
+
const flows = {}
|
|
629
|
+
|
|
630
|
+
// populate objects with fieldPermission data per object
|
|
631
|
+
jsonResult.forEach(element => {
|
|
632
|
+
let [flow] = element.friendlyName
|
|
633
|
+
flows[flow] = []
|
|
634
|
+
flows[flow].push(element)
|
|
635
|
+
})
|
|
636
|
+
|
|
637
|
+
// iterate flows keys (per object)
|
|
638
|
+
let keys = Object.keys(flows)
|
|
639
|
+
keys.forEach(key => {
|
|
640
|
+
let tag = 'loginFlows'
|
|
641
|
+
let fileName = path.join(targetDir, tag, key + `.${global.format}`)
|
|
642
|
+
|
|
643
|
+
// sort json keys to put field first
|
|
644
|
+
let jsonResult = flows[key]
|
|
645
|
+
jsonResult.forEach(function (part, index) {
|
|
646
|
+
this[index] = Object.keys(this[index])
|
|
647
|
+
.sort((a, b) => {
|
|
648
|
+
if (a == 'friendlyName') return -1
|
|
649
|
+
if (b == 'friendlyName') return 1
|
|
650
|
+
if (a == 'flow') return -1
|
|
651
|
+
if (b == 'flow') return 1
|
|
652
|
+
if (a == 'flowType') return -1
|
|
653
|
+
if (b == 'flowType') return 1
|
|
654
|
+
if (a == 'uiLoginFlowType') return -1
|
|
655
|
+
if (b == 'uiLoginFlowType') return 1
|
|
656
|
+
if (a == 'useLightningRuntime') return -1
|
|
657
|
+
if (b == 'useLightningRuntime') return 1
|
|
658
|
+
if (a == 'vfFlowPage') return -1
|
|
659
|
+
if (b == 'vfFlowPage') return 1
|
|
660
|
+
if (a == 'vfFlowPageTitle') return -1
|
|
661
|
+
if (b == 'vfFlowPageTitle') return 1
|
|
662
|
+
|
|
663
|
+
return 0;
|
|
664
|
+
})
|
|
665
|
+
.reduce((accumulator, key) => {
|
|
666
|
+
accumulator[key] = this[index][key];
|
|
667
|
+
|
|
668
|
+
return accumulator;
|
|
669
|
+
}, {});
|
|
670
|
+
}, jsonResult);
|
|
671
|
+
|
|
672
|
+
let json = {
|
|
673
|
+
flow: undefined,
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
json.loginFlows = jsonResult
|
|
677
|
+
saveFile(json, fileName)
|
|
678
|
+
})
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// TODO loginHours
|
|
682
|
+
|
|
683
|
+
function loginIpRanges(jsonResult, targetDir) {
|
|
684
|
+
let json = {
|
|
685
|
+
loginIpRanges: undefined
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// sort json keys to put field first
|
|
689
|
+
jsonResult.forEach(function (part, index) {
|
|
690
|
+
this[index] = Object.keys(this[index])
|
|
691
|
+
.sort((a, b) => {
|
|
692
|
+
if (a == 'startAddress') return -1
|
|
693
|
+
if (b == 'startAddress') return 1
|
|
694
|
+
if (a == 'endAddress') return -1
|
|
695
|
+
if (b == 'endAddress') return 1
|
|
696
|
+
if (a == 'description') return -1
|
|
697
|
+
if (b == 'description') return 1
|
|
698
|
+
|
|
699
|
+
return 0;
|
|
700
|
+
})
|
|
701
|
+
.reduce((accumulator, key) => {
|
|
702
|
+
accumulator[key] = this[index][key];
|
|
703
|
+
|
|
704
|
+
return accumulator;
|
|
705
|
+
}, {});
|
|
706
|
+
}, jsonResult);
|
|
707
|
+
|
|
708
|
+
// sort json to order by startAddress
|
|
709
|
+
jsonResult.sort((a, b) => {
|
|
710
|
+
if (a.startAddress < b.startAddress) {
|
|
711
|
+
return -1;
|
|
712
|
+
}
|
|
713
|
+
if (a.startAddress > b.startAddress) {
|
|
714
|
+
return 1;
|
|
715
|
+
}
|
|
716
|
+
return 0;
|
|
717
|
+
})
|
|
718
|
+
json.loginIpRanges = jsonResult
|
|
719
|
+
let fileName = path.join(targetDir, 'loginIpRanges' + `.${global.format}`)
|
|
720
|
+
saveFile(json, fileName)
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function objectPermissions(jsonResult, targetDir) {
|
|
724
|
+
const objects = {}
|
|
725
|
+
|
|
726
|
+
// populate objects with fieldPermission data per object
|
|
727
|
+
jsonResult.forEach(element => {
|
|
728
|
+
let [object] = element.object
|
|
729
|
+
if (objects[object] === undefined) {
|
|
730
|
+
objects[object] = []
|
|
731
|
+
}
|
|
732
|
+
objects[object].push(element)
|
|
733
|
+
})
|
|
734
|
+
|
|
735
|
+
// iterate objects keys (per object)
|
|
736
|
+
let keys = Object.keys(objects)
|
|
737
|
+
keys.forEach(key => {
|
|
738
|
+
let tag = 'objectPermissions'
|
|
739
|
+
let fileName = path.join(targetDir, tag, key + `.${global.format}`)
|
|
740
|
+
|
|
741
|
+
// sort json keys
|
|
742
|
+
let jsonResult = objects[key]
|
|
743
|
+
delete jsonResult[0].object
|
|
744
|
+
|
|
745
|
+
jsonResult.forEach(function (part, index) {
|
|
746
|
+
this[index] = Object.keys(this[index])
|
|
747
|
+
.sort((a, b) => {
|
|
748
|
+
if (a == 'allowCreate') return -1
|
|
749
|
+
if (b == 'allowCreate') return 1
|
|
750
|
+
if (a == 'allowRead') return -1
|
|
751
|
+
if (b == 'allowRead') return 1
|
|
752
|
+
if (a == 'allowEdit') return -1
|
|
753
|
+
if (b == 'allowEdit') return 1
|
|
754
|
+
if (a == 'allowDelete') return -1
|
|
755
|
+
if (b == 'allowDelete') return 1
|
|
756
|
+
if (a == 'viewAllRecords') return -1
|
|
757
|
+
if (b == 'viewAllRecords') return 1
|
|
758
|
+
if (a == 'modifyAllRecords') return -1
|
|
759
|
+
if (b == 'modifyAllRecords') return 1
|
|
760
|
+
|
|
761
|
+
return 0;
|
|
762
|
+
})
|
|
763
|
+
.reduce((accumulator, key) => {
|
|
764
|
+
accumulator[key] = this[index][key];
|
|
765
|
+
|
|
766
|
+
return accumulator;
|
|
767
|
+
}, {});
|
|
768
|
+
}, jsonResult);
|
|
769
|
+
let innerKeys = Object.keys(jsonResult[0])
|
|
770
|
+
innerKeys.forEach(innerKey => {
|
|
771
|
+
jsonResult[0][innerKey] = jsonResult[0][innerKey][0]
|
|
772
|
+
})
|
|
773
|
+
|
|
774
|
+
objects[key] = jsonResult
|
|
775
|
+
let json = {
|
|
776
|
+
object: undefined,
|
|
777
|
+
fieldPermissions: undefined
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
json.object = key
|
|
781
|
+
json.objectPermissions = jsonResult[0]
|
|
782
|
+
saveFile(json, fileName)
|
|
783
|
+
})
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
function pageAccesses(jsonResult, targetDir) {
|
|
787
|
+
let json = {
|
|
788
|
+
pageAccesses: undefined
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// sort json keys to put field first
|
|
792
|
+
jsonResult.forEach(function (part, index) {
|
|
793
|
+
this[index] = Object.keys(this[index])
|
|
794
|
+
.sort((a, b) => {
|
|
795
|
+
if (a == 'apexPage') return -1
|
|
796
|
+
if (b == 'apexPage') return 1
|
|
797
|
+
if (a == 'enabled') return -1
|
|
798
|
+
if (b == 'enabled') return 1
|
|
799
|
+
|
|
800
|
+
return 0;
|
|
801
|
+
})
|
|
802
|
+
.reduce((accumulator, key) => {
|
|
803
|
+
accumulator[key] = this[index][key];
|
|
804
|
+
|
|
805
|
+
return accumulator;
|
|
806
|
+
}, {});
|
|
807
|
+
}, jsonResult);
|
|
808
|
+
|
|
809
|
+
// sort json to order by apexPage
|
|
810
|
+
jsonResult.sort((a, b) => {
|
|
811
|
+
if (a.apexPage < b.apexPage) {
|
|
812
|
+
return -1;
|
|
813
|
+
}
|
|
814
|
+
if (a.apexPage > b.apexPage) {
|
|
815
|
+
return 1;
|
|
816
|
+
}
|
|
817
|
+
return 0;
|
|
818
|
+
})
|
|
819
|
+
json.pageAccesses = jsonResult
|
|
820
|
+
let fileName = path.join(targetDir, 'pageAccesses' + `.${global.format}`)
|
|
821
|
+
saveFile(json, fileName)
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// TODO profileActionOverrides
|
|
825
|
+
|
|
826
|
+
function recordTypeVisibilities(jsonResult, targetDir) {
|
|
827
|
+
const objects = {}
|
|
828
|
+
|
|
829
|
+
// populate objects with fieldPermission data per object
|
|
830
|
+
jsonResult.forEach(element => {
|
|
831
|
+
let [object] = element.recordType[0].toString().replaceAll('\n', '').trim().split('.')
|
|
832
|
+
if (objects[object] === undefined) {
|
|
833
|
+
objects[object] = []
|
|
834
|
+
}
|
|
835
|
+
objects[object].push(element)
|
|
836
|
+
})
|
|
837
|
+
|
|
838
|
+
// iterate objects keys (per object)
|
|
839
|
+
let keys = Object.keys(objects)
|
|
840
|
+
keys.forEach(key => {
|
|
841
|
+
let tag = 'recordTypeVisibilities'
|
|
842
|
+
let fileName = path.join(targetDir, tag, key + `.${global.format}`)
|
|
843
|
+
|
|
844
|
+
// sort json keys to put field first
|
|
845
|
+
let jsonResult = objects[key]
|
|
846
|
+
jsonResult.forEach(function (part, index) {
|
|
847
|
+
this[index] = Object.keys(this[index])
|
|
848
|
+
.sort((a, b) => {
|
|
849
|
+
if (a == 'recordType') return -1
|
|
850
|
+
if (b == 'recordType') return 1
|
|
851
|
+
if (a == 'default') return -1
|
|
852
|
+
if (b == 'default') return 1
|
|
853
|
+
if (a == 'visible') return -1
|
|
854
|
+
if (b == 'visible') return 1
|
|
855
|
+
if (a == 'personAccountDefault') return -1
|
|
856
|
+
if (b == 'personAccountDefault') return 1
|
|
857
|
+
|
|
858
|
+
return 0;
|
|
859
|
+
})
|
|
860
|
+
.reduce((accumulator, key) => {
|
|
861
|
+
accumulator[key] = this[index][key];
|
|
862
|
+
|
|
863
|
+
return accumulator;
|
|
864
|
+
}, {});
|
|
865
|
+
}, jsonResult);
|
|
866
|
+
|
|
867
|
+
// sort json to order by field
|
|
868
|
+
jsonResult.sort((a, b) => {
|
|
869
|
+
if (a.recordType < b.recordType) {
|
|
870
|
+
return -1;
|
|
871
|
+
}
|
|
872
|
+
if (a.recordType > b.recordType) {
|
|
873
|
+
return 1;
|
|
874
|
+
}
|
|
875
|
+
return 0;
|
|
876
|
+
})
|
|
877
|
+
|
|
878
|
+
let json = {
|
|
879
|
+
object: undefined,
|
|
880
|
+
recordTypeVisibilities: undefined
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
json.object = key
|
|
884
|
+
json.recordTypeVisibilities = jsonResult
|
|
885
|
+
saveFile(json, fileName)
|
|
886
|
+
})
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
function tabVisibilities(jsonResult, targetDir) {
|
|
890
|
+
let json = {
|
|
891
|
+
tabVisibilities: undefined
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// sort json keys to put field first
|
|
895
|
+
jsonResult.forEach(function (part, index) {
|
|
896
|
+
this[index] = Object.keys(this[index])
|
|
897
|
+
.sort((a, b) => {
|
|
898
|
+
if (a == 'name') return -1
|
|
899
|
+
if (b == 'name') return 1
|
|
900
|
+
if (a == 'enabled') return -1
|
|
901
|
+
if (b == 'enabled') return 1
|
|
902
|
+
|
|
903
|
+
return 0;
|
|
904
|
+
})
|
|
905
|
+
.reduce((accumulator, key) => {
|
|
906
|
+
accumulator[key] = this[index][key];
|
|
907
|
+
|
|
908
|
+
return accumulator;
|
|
909
|
+
}, {});
|
|
910
|
+
}, jsonResult);
|
|
911
|
+
|
|
912
|
+
// sort json to order by name
|
|
913
|
+
jsonResult.sort((a, b) => {
|
|
914
|
+
if (a.name < b.name) {
|
|
915
|
+
return -1;
|
|
916
|
+
}
|
|
917
|
+
if (a.name > b.name) {
|
|
918
|
+
return 1;
|
|
919
|
+
}
|
|
920
|
+
return 0;
|
|
921
|
+
})
|
|
922
|
+
json.tabVisibilities = jsonResult
|
|
923
|
+
let fileName = path.join(targetDir, 'tabVisibilities' + `.${global.format}`)
|
|
924
|
+
saveFile(json, fileName)
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
function userPermissions(jsonResult, targetDir) {
|
|
928
|
+
let json = {
|
|
929
|
+
userPermissions: undefined
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// sort json keys to put field first
|
|
933
|
+
jsonResult.forEach(function (part, index) {
|
|
934
|
+
this[index] = Object.keys(this[index])
|
|
935
|
+
.sort((a, b) => {
|
|
936
|
+
if (a == 'name') return -1
|
|
937
|
+
if (b == 'name') return 1
|
|
938
|
+
if (a == 'enabled') return -1
|
|
939
|
+
if (b == 'enabled') return 1
|
|
940
|
+
|
|
941
|
+
return 0;
|
|
942
|
+
})
|
|
943
|
+
.reduce((accumulator, key) => {
|
|
944
|
+
accumulator[key] = this[index][key];
|
|
945
|
+
|
|
946
|
+
return accumulator;
|
|
947
|
+
}, {});
|
|
948
|
+
}, jsonResult);
|
|
949
|
+
|
|
950
|
+
// sort json to order by name
|
|
951
|
+
jsonResult.sort((a, b) => {
|
|
952
|
+
if (a.name < b.name) {
|
|
953
|
+
return -1;
|
|
954
|
+
}
|
|
955
|
+
if (a.name > b.name) {
|
|
956
|
+
return 1;
|
|
957
|
+
}
|
|
958
|
+
return 0;
|
|
959
|
+
})
|
|
960
|
+
json.userPermissions = jsonResult
|
|
961
|
+
let fileName = path.join(targetDir, 'userPermissions' + `.${global.format}`)
|
|
962
|
+
saveFile(json, fileName)
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function nextFrame() {
|
|
969
|
+
spinnerFrame = spinner.frames[index = ++index % spinner.frames.length]
|
|
970
|
+
return spinnerFrame
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
function saveFile(json, fileName) {
|
|
974
|
+
switch (global.format) {
|
|
975
|
+
case 'json':
|
|
976
|
+
let jsonString = JSON.stringify(json, null, '\t')
|
|
977
|
+
fs.writeFileSync(fileName, jsonString)
|
|
978
|
+
break
|
|
979
|
+
case 'yaml':
|
|
980
|
+
let doc = yaml.dump(json)
|
|
981
|
+
fs.writeFileSync(fileName, doc)
|
|
982
|
+
}
|
|
983
|
+
}
|