@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
package/lib/fileUtils.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
import * as fs from 'fs'
|
|
3
|
+
import * as path from 'path'
|
|
4
|
+
import * as yaml from 'js-yaml'
|
|
5
|
+
|
|
6
|
+
export function directoryExists(dirPath) {
|
|
7
|
+
return fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function fileExists(filePath) {
|
|
11
|
+
return fs.existsSync(filePath) && fs.statSync(filePath).isFile()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function createDirectory(dirPath) {
|
|
15
|
+
if (!fs.existsSync(dirPath)) {
|
|
16
|
+
fs.mkdirSync(dirPath, { recursive: true })
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function deleteDirectory(dirPath, recursive = false) {
|
|
21
|
+
if (!directoryExists(dirPath)) {
|
|
22
|
+
return false
|
|
23
|
+
} else {
|
|
24
|
+
if (fs.existsSync(dirPath)) {
|
|
25
|
+
fs.readdirSync(dirPath).forEach(function (file) {
|
|
26
|
+
var curPath = path.join(dirPath, file)
|
|
27
|
+
if (fs.lstatSync(curPath).isDirectory() && recursive) { // recurse
|
|
28
|
+
deleteDirectory(curPath, recursive);
|
|
29
|
+
} else { // delete file
|
|
30
|
+
fs.unlinkSync(curPath);
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
return fs.rmdirSync(dirPath);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getFiles(dirPath, filter = undefined) {
|
|
39
|
+
const filesList = []
|
|
40
|
+
fs.readdirSync(dirPath).forEach(file => {
|
|
41
|
+
if (!filter) {
|
|
42
|
+
filesList.push(file)
|
|
43
|
+
} else {
|
|
44
|
+
if (file.endsWith(filter)) {
|
|
45
|
+
filesList.push(file)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
filesList.sort()
|
|
50
|
+
return filesList
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function getDirectories(dirPath) {
|
|
54
|
+
return fs.readdirSync(dirPath, { withFileTypes: true })
|
|
55
|
+
.filter(dirent => dirent.isDirectory())
|
|
56
|
+
.map(dirent => dirent.name)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function deleteFile(filePath) {
|
|
60
|
+
if (!fileExists(filePath)) {
|
|
61
|
+
return false
|
|
62
|
+
} else {
|
|
63
|
+
return unlinkSync(filePath, { recursive: false, force: true });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function fileInfo(filePath) {
|
|
68
|
+
return {
|
|
69
|
+
"dirname": path.join(path.dirname(filePath)), //something/folder/example
|
|
70
|
+
"basename": path.basename(filePath, path.extname(filePath)), //example
|
|
71
|
+
"filename": path.basename(filePath), //example.txt
|
|
72
|
+
"extname": path.extname(filePath), //txt
|
|
73
|
+
"exists": fs.existsSync(filePath), //true if exists or false if not exists
|
|
74
|
+
"stats": fs.existsSync(filePath) ? fs.statSync(filePath) : undefined //stats object if exists or undefined if not exists
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function savePartFile(json, fileName, format) {
|
|
79
|
+
try {
|
|
80
|
+
switch (format) {
|
|
81
|
+
case 'json':
|
|
82
|
+
let jsonString = JSON.stringify(json, null, '\t')
|
|
83
|
+
fs.writeFileSync(fileName, jsonString)
|
|
84
|
+
break
|
|
85
|
+
case 'yaml':
|
|
86
|
+
let doc = yaml.dump(json)
|
|
87
|
+
fs.writeFileSync(fileName, doc)
|
|
88
|
+
break
|
|
89
|
+
}
|
|
90
|
+
return true
|
|
91
|
+
} catch (error) {
|
|
92
|
+
global.logger.error(error)
|
|
93
|
+
throw error
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function readPartFile(fileName) {
|
|
98
|
+
try {
|
|
99
|
+
let result = undefined
|
|
100
|
+
const data = fs.readFileSync(fileName, { encoding: 'utf8', flag: 'r' })
|
|
101
|
+
if (fileName.indexOf('.yaml') != -1) {
|
|
102
|
+
result = yaml.load(data)
|
|
103
|
+
} else {
|
|
104
|
+
result = JSON.parse(data)
|
|
105
|
+
}
|
|
106
|
+
return result
|
|
107
|
+
} catch (error) {
|
|
108
|
+
global.logger.error(error)
|
|
109
|
+
throw error
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function writeFile(fileName, data, atime = new Date(), mtime = new Date()) {
|
|
114
|
+
try {
|
|
115
|
+
// write data to the file
|
|
116
|
+
fs.writeFileSync(fileName, data)
|
|
117
|
+
|
|
118
|
+
// if atime or mtime are undefined, use current date/time
|
|
119
|
+
if (atime === undefined) atime = new Date()
|
|
120
|
+
if (mtime === undefined) mtime = new Date()
|
|
121
|
+
|
|
122
|
+
// update XML file to match the latest atime and mtime of the files processed
|
|
123
|
+
fs.utimesSync(fileName, atime, mtime)
|
|
124
|
+
|
|
125
|
+
} catch (error) {
|
|
126
|
+
global.logger.error(error)
|
|
127
|
+
throw error
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function find(filename, root) {
|
|
132
|
+
// code Copyright (c) 2014, Ben Gourley
|
|
133
|
+
// https://github.com/bengourley/find-nearest-file
|
|
134
|
+
root = root || process.cwd();
|
|
135
|
+
|
|
136
|
+
if (!filename) throw new Error('filename is required')
|
|
137
|
+
|
|
138
|
+
if (filename.indexOf('/') !== -1 || filename === '..') {
|
|
139
|
+
throw new Error('filename must be just a filename and not a path')
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
function findFile(directory, filename) {
|
|
144
|
+
|
|
145
|
+
var file = path.join(directory, filename)
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
if (fs.statSync(file).isFile()) return file
|
|
149
|
+
// stat existed, but isFile() returned false
|
|
150
|
+
return nextLevelUp()
|
|
151
|
+
} catch (e) {
|
|
152
|
+
// stat did not exist
|
|
153
|
+
return nextLevelUp()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function nextLevelUp() {
|
|
157
|
+
// Don't proceed to the next directory when already at the fs root
|
|
158
|
+
if (directory === path.resolve('/')) return null
|
|
159
|
+
return findFile(path.dirname(directory), filename)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return findFile(root, filename)
|
|
165
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import os from 'os'
|
|
3
|
+
import { readFileSync, writeFileSync, utimesSync } from 'fs'
|
|
4
|
+
import logUpdate from 'log-update'
|
|
5
|
+
import chalk from 'chalk'
|
|
6
|
+
import convertHrtime from 'convert-hrtime'
|
|
7
|
+
import cliSpinners from 'cli-spinners'
|
|
8
|
+
import * as fileUtils from '../fileUtils.js'
|
|
9
|
+
import { labelDefinition } from './definition.js'
|
|
10
|
+
import * as xml2js from 'xml2js'
|
|
11
|
+
import * as yaml from 'js-yaml'
|
|
12
|
+
|
|
13
|
+
const spinner = cliSpinners['dots']
|
|
14
|
+
|
|
15
|
+
export class CustomLabel {
|
|
16
|
+
#xml = ''
|
|
17
|
+
#types = []
|
|
18
|
+
#spinnerMessage = ''
|
|
19
|
+
#index = 0
|
|
20
|
+
#startTime = 0
|
|
21
|
+
#fileName = ''
|
|
22
|
+
#errorMessage = ''
|
|
23
|
+
#fileStats
|
|
24
|
+
|
|
25
|
+
constructor(config) {
|
|
26
|
+
this.sourceDir = config.sourceDir
|
|
27
|
+
this.targetDir = config.targetDir
|
|
28
|
+
this.metaDir = config.metaDir
|
|
29
|
+
this.processList = config.processList
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
combine() {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const that = this
|
|
35
|
+
if (!fileUtils.directoryExists(that.sourceDir)) reject(`Path does not exist: ${that.sourceDir}`)
|
|
36
|
+
|
|
37
|
+
that.#xml = `<?xml version="1.0" encoding="UTF-8"?>${os.EOL}`
|
|
38
|
+
that.#xml += `<CustomLabels xmlns="https://soap.sforce.com/2006/04/metadata">${os.EOL}`
|
|
39
|
+
|
|
40
|
+
labelDefinition.directories.forEach(key => { that.#types.push(key) })
|
|
41
|
+
that.#types.sort()
|
|
42
|
+
|
|
43
|
+
setFileName(that)
|
|
44
|
+
processLabel(that)
|
|
45
|
+
|
|
46
|
+
saveXML(that)
|
|
47
|
+
resolve(true)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
function setFileName(that) {
|
|
51
|
+
that.#fileName = path.join(that.targetDir, 'CustomLabels.labels-meta.xml')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function processLabel(that) {
|
|
55
|
+
that.#startTime = process.hrtime.bigint()
|
|
56
|
+
that.#spinnerMessage = `[%1] of ${global.processed.total} - Custom Label: [%4]${chalk.yellowBright('[%5]')}[%2][%3]`
|
|
57
|
+
that.processList.sort()
|
|
58
|
+
that.#types.forEach(key => {
|
|
59
|
+
processDirectory(that, key)
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function processFile(that, key) {
|
|
64
|
+
const fileName = path.join(that.sourceDir, that.metaDir, `${key}.${global.format}`)
|
|
65
|
+
if (fileUtils.fileExists(fileName)) {
|
|
66
|
+
if (labelDefinition.singleFiles.includes(key)) {
|
|
67
|
+
genericXML(that, key)
|
|
68
|
+
} else {
|
|
69
|
+
that.#errorMessage += `\n${global.statusLevel.warn} Not processed: ${key}`
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function processDirectory(that, key) {
|
|
75
|
+
that.sequence = 0
|
|
76
|
+
let startTime
|
|
77
|
+
that.#errorMessage = ''
|
|
78
|
+
that.processList.forEach(fileName => {
|
|
79
|
+
startTime = process.hrtime.bigint()
|
|
80
|
+
that.sequence++
|
|
81
|
+
logUpdate(that.#spinnerMessage
|
|
82
|
+
.replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
|
|
83
|
+
.replace('[%2]', `\n${chalk.magentaBright(nextFrame(that))} ${key}`)
|
|
84
|
+
.replace('[%3]', `${that.#errorMessage}`)
|
|
85
|
+
.replace('[%4]', `${global.statusLevel.working} `)
|
|
86
|
+
.replace('[%5]', `${fileName} `)
|
|
87
|
+
)
|
|
88
|
+
try {
|
|
89
|
+
genericXML(that, key, fileName)
|
|
90
|
+
} catch (error) {
|
|
91
|
+
that.#errorMessage = error.message
|
|
92
|
+
}
|
|
93
|
+
let executionTime = getTimeDiff(BigInt(startTime))
|
|
94
|
+
let durationMessage = `${executionTime.seconds}.${executionTime.milliseconds}s`
|
|
95
|
+
let stateIcon = (that.#errorMessage == '') ? global.statusLevel.success : global.statusLevel.fail
|
|
96
|
+
logUpdate(that.#spinnerMessage
|
|
97
|
+
.replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
|
|
98
|
+
.replace('[%2]', `. Processed in ${durationMessage}.`)
|
|
99
|
+
.replace('[%3]', `${that.#errorMessage}`)
|
|
100
|
+
.replace('[%4]', `${stateIcon} `)
|
|
101
|
+
.replace('[%5', fileName)
|
|
102
|
+
)
|
|
103
|
+
logUpdate.done()
|
|
104
|
+
})
|
|
105
|
+
// TODO that.#fileStats = fileUtils.fileInfo(fileName).stats
|
|
106
|
+
|
|
107
|
+
// genericDirectoryXML(that, key)
|
|
108
|
+
// that.#errorMessage += `\n${global.statusLevel.warn} Not processed: ${key}`
|
|
109
|
+
return true
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function getTimeDiff(startTime, endTime = process.hrtime.bigint()) {
|
|
113
|
+
const diff = BigInt(endTime) - BigInt(startTime)
|
|
114
|
+
let executionTime = convertHrtime(diff)
|
|
115
|
+
executionTime.seconds = Math.round(executionTime.seconds)
|
|
116
|
+
executionTime.milliseconds = Math.round(executionTime.milliseconds / 1000)
|
|
117
|
+
if (executionTime.milliseconds == 0 && executionTime.nanoseconds > 0) executionTime.milliseconds = 1
|
|
118
|
+
return executionTime
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function saveXML(that) {
|
|
122
|
+
fileUtils.createDirectory(that.targetDir)
|
|
123
|
+
that.#xml += '</CustomLabels>\n'
|
|
124
|
+
writeFileSync(that.#fileName, that.#xml)
|
|
125
|
+
// utimesSync(that.#fileName, that.#fileStats.atime, that.#fileStats.mtime)
|
|
126
|
+
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function nextFrame(that) {
|
|
130
|
+
return spinner.frames[that.#index = ++that.#index % spinner.frames.length]
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function genericXML(that, key, fileName) {
|
|
134
|
+
fileName = path.join(that.sourceDir, fileName)
|
|
135
|
+
const builder = new xml2js.Builder({ cdata: false, headless: true, rootName: key })
|
|
136
|
+
if (fileUtils.fileExists(fileName)) {
|
|
137
|
+
const data = readFileSync(fileName, { encoding: 'utf8', flag: 'r' })
|
|
138
|
+
const result = (global.format == 'yaml') ? yaml.load(data) : JSON.parse(data)
|
|
139
|
+
result[key.slice(0, -1)][labelDefinition.sortKeys[key]] = result[labelDefinition.sortKeys[key]]
|
|
140
|
+
result[key.slice(0, -1)] = sortJSONKeys(sortJSON(result[key.slice(0, -1)], labelDefinition.sortKeys[key]))
|
|
141
|
+
that.#xml += `\t<${key}>${os.EOL}`
|
|
142
|
+
Object.keys(result[key.slice(0, -1)]).forEach(tag => {
|
|
143
|
+
let xml
|
|
144
|
+
try {
|
|
145
|
+
xml = builder.buildObject(result[key.slice(0, -1)][tag]).replace(`<${key}>`, '').replace(`</${key}>`, '')
|
|
146
|
+
that.#xml += `\t\t<${tag}>${xml}</${tag}>${os.EOL}`
|
|
147
|
+
} catch (error) {
|
|
148
|
+
global.logger.error(error)
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
that.#xml += `\t</${key}>${os.EOL}`
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// end of functions
|
|
155
|
+
// end of combine
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// end of class
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function sortJSON(json, key) {
|
|
162
|
+
if (Array.isArray(json)) {
|
|
163
|
+
json.sort((a, b) => {
|
|
164
|
+
if (a[key] < b[key]) return -1
|
|
165
|
+
if (a[key] > b[key]) return 1
|
|
166
|
+
return 0
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
return json
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function sortJSONKeys(json) {
|
|
173
|
+
// sort json keys alphabetically
|
|
174
|
+
if (Array.isArray(json)) {
|
|
175
|
+
json.forEach(function (part, index) {
|
|
176
|
+
this[index] = Object.keys(this[index])
|
|
177
|
+
.sort((a, b) => {
|
|
178
|
+
if (a < b) return -1
|
|
179
|
+
if (a > b) return 1
|
|
180
|
+
return 0
|
|
181
|
+
})
|
|
182
|
+
.reduce((accumulator, key) => {
|
|
183
|
+
accumulator[key] = this[index][key]
|
|
184
|
+
|
|
185
|
+
return accumulator
|
|
186
|
+
}, {})
|
|
187
|
+
}, json)
|
|
188
|
+
|
|
189
|
+
} else {
|
|
190
|
+
json = Object.keys(json)
|
|
191
|
+
.sort((a, b) => {
|
|
192
|
+
if (a < b) return -1
|
|
193
|
+
if (a > b) return 1
|
|
194
|
+
return 0
|
|
195
|
+
})
|
|
196
|
+
.reduce((accumulator, key) => {
|
|
197
|
+
accumulator[key] = json[key]
|
|
198
|
+
|
|
199
|
+
return accumulator
|
|
200
|
+
}, {})
|
|
201
|
+
}
|
|
202
|
+
return json
|
|
203
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const labelDefinition = {
|
|
2
|
+
metaUrl: 'https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_customlabels.htm',
|
|
3
|
+
directories: [
|
|
4
|
+
'labels',
|
|
5
|
+
],
|
|
6
|
+
sortKeys: {
|
|
7
|
+
'labels': 'fullName',
|
|
8
|
+
},
|
|
9
|
+
keyOrder: {
|
|
10
|
+
labels: ['fullName', 'shortDescription', 'categories', 'protected', 'language', 'value'],
|
|
11
|
+
},
|
|
12
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
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 { labelDefinition } from './definition.js'
|
|
15
|
+
|
|
16
|
+
const spinner = cliSpinners['dots']
|
|
17
|
+
|
|
18
|
+
export class CustomLabel {
|
|
19
|
+
#fileName = {
|
|
20
|
+
'fullName': undefined,
|
|
21
|
+
'shortName': undefined,
|
|
22
|
+
}
|
|
23
|
+
#json
|
|
24
|
+
#errorMessage = ''
|
|
25
|
+
#index = 0
|
|
26
|
+
#startTime = 0
|
|
27
|
+
#spinnerMessage = ''
|
|
28
|
+
|
|
29
|
+
constructor(config) {
|
|
30
|
+
this.sourceDir = config.sourceDir
|
|
31
|
+
this.targetDir = config.targetDir
|
|
32
|
+
this.metaFilePath = config.metaFilePath
|
|
33
|
+
this.sequence = config.sequence
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get metaFilePath() {
|
|
37
|
+
return this._metaFilePath
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
set metaFilePath(value) {
|
|
41
|
+
value = value.trim()
|
|
42
|
+
if (value === '') {
|
|
43
|
+
throw 'The file path cannot be empty'
|
|
44
|
+
}
|
|
45
|
+
this._metaFilePath = value
|
|
46
|
+
this.#fileName.fullName = fileUtils.fileInfo(value).filename
|
|
47
|
+
this.#fileName.shortName = fileUtils.fileInfo(value).filename.replace('.labels-meta.xml', '')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
split() {
|
|
51
|
+
const that = this
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
|
|
54
|
+
if (!that.#fileName || !that.sourceDir || !that.targetDir || !that.metaFilePath) {
|
|
55
|
+
global.logger.error('Invalid information passed to split')
|
|
56
|
+
process.exit(1)
|
|
57
|
+
}
|
|
58
|
+
if (!fileUtils.fileExists(that.metaFilePath)) {
|
|
59
|
+
global.logger.error(`file not found: ${that.metaFilePath}`)
|
|
60
|
+
process.exit(1)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// that.targetDir = path.join(that.targetDir, that.#fileName.shortName)
|
|
64
|
+
let parser = new Parser()
|
|
65
|
+
const getJSON = new Promise((resolve, reject) => {
|
|
66
|
+
readFile(that.metaFilePath, function (err, data) {
|
|
67
|
+
parser.parseString(data, function (err, result) {
|
|
68
|
+
if (result) {
|
|
69
|
+
resolve(result)
|
|
70
|
+
} else {
|
|
71
|
+
global.logger.error(`error converting xml to json: ${that.metaFilePath}`)
|
|
72
|
+
process.exit(1)
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
getJSON.then((result) => {
|
|
78
|
+
// modify the json to remove unwanted arrays
|
|
79
|
+
delete result.CustomLabels['$']
|
|
80
|
+
let jsonString = JSON.stringify(result, (name, value) => {
|
|
81
|
+
if (name == '' || !isNaN(name) || labelDefinition.directories.includes(name)) {
|
|
82
|
+
return value
|
|
83
|
+
} else {
|
|
84
|
+
return xml2json(value)
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
that.#json = JSON.parse(jsonString)
|
|
88
|
+
|
|
89
|
+
Object.keys(that.#json.CustomLabels).forEach(key => {
|
|
90
|
+
const keyOrder = labelDefinition.keyOrder[key]
|
|
91
|
+
const sortKey = labelDefinition.sortKeys[key]
|
|
92
|
+
|
|
93
|
+
if (Array.isArray(that.#json.CustomLabels[key])) {
|
|
94
|
+
// sort json to order by sortKey
|
|
95
|
+
that.#json.CustomLabels[key].sort((a, b) => {
|
|
96
|
+
if (a[sortKey] < b[sortKey]) {
|
|
97
|
+
return -1;
|
|
98
|
+
}
|
|
99
|
+
if (a[sortKey] > b[sortKey]) {
|
|
100
|
+
return 1;
|
|
101
|
+
}
|
|
102
|
+
return 0;
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
// sort json keys in specified order
|
|
106
|
+
that.#json.CustomLabels[key].forEach(function (part, index) {
|
|
107
|
+
this[index] = Object.keys(this[index])
|
|
108
|
+
.sort((a, b) => {
|
|
109
|
+
if (keyOrder.indexOf(a) < keyOrder.indexOf(b)) return -1
|
|
110
|
+
if (keyOrder.indexOf(a) > keyOrder.indexOf(b)) return 1
|
|
111
|
+
return 0
|
|
112
|
+
})
|
|
113
|
+
.reduce((accumulator, key) => {
|
|
114
|
+
accumulator[key] = this[index][key]
|
|
115
|
+
return accumulator
|
|
116
|
+
}, {})
|
|
117
|
+
}, that.#json.CustomLabels[key])
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
processFile(that)
|
|
122
|
+
completeFile(that)
|
|
123
|
+
resolve(true)
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
function nextFrame(that) {
|
|
128
|
+
return spinner.frames[that.#index = ++that.#index % spinner.frames.length]
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function completeFile(that) {
|
|
132
|
+
let executionTime = getTimeDiff(BigInt(that.#startTime))
|
|
133
|
+
let durationMessage = `${executionTime.seconds}.${executionTime.milliseconds}s`
|
|
134
|
+
let stateIcon = (that.#errorMessage == '') ? global.statusLevel.success : global.statusLevel.fail
|
|
135
|
+
logUpdate(that.#spinnerMessage
|
|
136
|
+
.replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
|
|
137
|
+
.replace('[%2]', `. Processed in ${durationMessage}.`)
|
|
138
|
+
.replace('[%3]', `${that.#errorMessage}`)
|
|
139
|
+
.replace('[%4]', `${stateIcon} `)
|
|
140
|
+
)
|
|
141
|
+
logUpdate.done()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function processFile(that) {
|
|
145
|
+
that.#startTime = process.hrtime.bigint()
|
|
146
|
+
that.#spinnerMessage = `[%1] of ${global.processed.total} - Custom Labels: [%4]${chalk.yellowBright(that.#fileName.shortName)}[%2][%3]`
|
|
147
|
+
|
|
148
|
+
fileUtils.deleteDirectory(that.targetDir, true) // recursive delete existing directory
|
|
149
|
+
fileUtils.createDirectory(that.targetDir) // create directory
|
|
150
|
+
|
|
151
|
+
Object.keys(that.#json.CustomLabels).forEach(key => {
|
|
152
|
+
that.sequence = global.processed.current
|
|
153
|
+
logUpdate(that.#spinnerMessage
|
|
154
|
+
.replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
|
|
155
|
+
.replace('[%2]', `\n${chalk.magentaBright(nextFrame(that))} ${key}`)
|
|
156
|
+
.replace('[%3]', `${that.#errorMessage}`)
|
|
157
|
+
.replace('[%4]', `${global.statusLevel.working} `)
|
|
158
|
+
)
|
|
159
|
+
if (labelDefinition.directories.includes(key)) {
|
|
160
|
+
processDirectory(that, key)
|
|
161
|
+
} else {
|
|
162
|
+
that.#errorMessage += `\n${global.statusLevel.warn} Not processed: ${key}`
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
return true
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function processDirectory(that, key) {
|
|
170
|
+
const myKey = labelDefinition.sortKeys[key]
|
|
171
|
+
|
|
172
|
+
// populate objects with data per object
|
|
173
|
+
that.#json.CustomLabels[key].forEach(element => {
|
|
174
|
+
let fileName = path.join(that.targetDir, `${element[myKey]}.${global.format}`)
|
|
175
|
+
const labelJSON = {}
|
|
176
|
+
labelJSON[myKey] = element[myKey]
|
|
177
|
+
delete element[myKey]
|
|
178
|
+
labelJSON[key.slice(0, -1)] = element //use slice to remove the s
|
|
179
|
+
|
|
180
|
+
let jsonString = JSON.stringify(labelJSON, null, '\t')
|
|
181
|
+
switch (global.format) {
|
|
182
|
+
case 'json':
|
|
183
|
+
fs.writeFileSync(fileName, jsonString)
|
|
184
|
+
break
|
|
185
|
+
case 'yaml':
|
|
186
|
+
let doc = yaml.dump(JSON.parse(jsonString))
|
|
187
|
+
fs.writeFileSync(fileName, doc)
|
|
188
|
+
break
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function getTimeDiff(startTime, endTime = process.hrtime.bigint()) {
|
|
196
|
+
const diff = BigInt(endTime) - BigInt(startTime)
|
|
197
|
+
let executionTime = convertHrtime(diff)
|
|
198
|
+
executionTime.seconds = Math.round(executionTime.seconds)
|
|
199
|
+
executionTime.milliseconds = Math.round(executionTime.milliseconds / 1000)
|
|
200
|
+
if (executionTime.milliseconds == 0 && executionTime.nanoseconds > 0) executionTime.milliseconds = 1
|
|
201
|
+
return executionTime
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function xml2json(currentValue) {
|
|
205
|
+
if (Array.isArray(currentValue)) {
|
|
206
|
+
if (currentValue.length == 1) {
|
|
207
|
+
currentValue = currentValue[0].toString().trim()
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (currentValue == 'true') currentValue = true
|
|
211
|
+
if (currentValue == 'false') currentValue = false
|
|
212
|
+
return currentValue
|
|
213
|
+
}
|