@ds-sfdc/sfparty 1.2.6 → 1.3.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 (89) hide show
  1. package/.github/workflows/cicd.yaml +38 -0
  2. package/babel.config.cjs +3 -0
  3. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/applicationVisibilities.yaml +2 -2
  4. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/fieldPermissions/Case.yaml +1 -1
  5. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/main.yaml +2 -2
  6. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AIRecordInsight.yaml +3 -3
  7. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Account.yaml +3 -3
  8. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AppointmentInvitation.yaml +3 -3
  9. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AppointmentTopicTimeSlot.yaml +3 -3
  10. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Asset.yaml +1 -1
  11. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AuthorizationForm.yaml +4 -4
  12. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AuthorizationFormConsent.yaml +4 -4
  13. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AuthorizationFormDataUse.yaml +4 -4
  14. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AuthorizationFormText.yaml +4 -4
  15. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/BusinessBrand.yaml +4 -4
  16. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Campaign.yaml +1 -1
  17. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/CommSubscription.yaml +3 -3
  18. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/CommSubscriptionChannelType.yaml +3 -3
  19. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/CommSubscriptionConsent.yaml +4 -4
  20. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/CommSubscriptionTiming.yaml +4 -4
  21. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Contact.yaml +3 -3
  22. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointAddress.yaml +4 -4
  23. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointConsent.yaml +4 -4
  24. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointEmail.yaml +4 -4
  25. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointPhone.yaml +4 -4
  26. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointTypeConsent.yaml +4 -4
  27. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactRequest.yaml +3 -3
  28. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContractLineItem.yaml +1 -1
  29. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Customer.yaml +4 -4
  30. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/DandBCompany.yaml +1 -1
  31. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/DataUseLegalBasis.yaml +4 -4
  32. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/DataUsePurpose.yaml +3 -3
  33. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Document.yaml +3 -3
  34. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/EngagementChannelType.yaml +3 -3
  35. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Entitlement.yaml +2 -2
  36. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/EntitlementContact.yaml +3 -3
  37. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Individual.yaml +3 -3
  38. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Lead.yaml +4 -4
  39. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Macro.yaml +1 -1
  40. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/OperatingHours.yaml +3 -3
  41. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Opportunity.yaml +4 -4
  42. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/PartyConsent.yaml +4 -4
  43. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Promotion.yaml +4 -4
  44. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/QuickText.yaml +1 -1
  45. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Seller.yaml +4 -4
  46. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ServiceContract.yaml +2 -2
  47. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ServiceTerritory.yaml +2 -2
  48. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Shift.yaml +3 -3
  49. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/SocialPersona.yaml +3 -3
  50. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Solution.yaml +1 -1
  51. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/StreamingChannel.yaml +4 -4
  52. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WebCart.yaml +1 -1
  53. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WebStore.yaml +1 -1
  54. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WorkPlan.yaml +3 -3
  55. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WorkPlanTemplate.yaml +4 -4
  56. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WorkStepTemplate.yaml +4 -4
  57. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/tabVisibilities.yaml +29 -29
  58. package/force-app-party/main/default/profiles/External Identity User/applicationVisibilities.yaml +1 -1
  59. package/force-app-party/main/default/profiles/External Identity User/main.yaml +2 -2
  60. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Account.yaml +2 -2
  61. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Asset.yaml +3 -3
  62. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/BusinessBrand.yaml +2 -2
  63. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Contact.yaml +3 -3
  64. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/ContactPointEmail.yaml +3 -3
  65. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/ContactPointPhone.yaml +3 -3
  66. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Customer.yaml +2 -2
  67. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Document.yaml +1 -1
  68. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Individual.yaml +3 -3
  69. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/PushTopic.yaml +4 -4
  70. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Seller.yaml +2 -2
  71. package/force-app-party/main/default/profiles/External Identity User/tabVisibilities.yaml +30 -30
  72. package/package.json +7 -5
  73. package/src/index.js +7 -45
  74. package/src/lib/checkVersion.js +42 -0
  75. package/src/lib/fileUtils.js +38 -38
  76. package/src/lib/gitUtils.js +87 -140
  77. package/test/lib/git/diff.spec.js +54 -0
  78. package/test/lib/git/lastCommit.spec.js +69 -0
  79. package/test/lib/git/log.spec.js +33 -0
  80. package/test/lib/git/updateLastCommit.spec.js +65 -0
  81. package/test/root.spec.js +7 -0
  82. package/test_mocha/lib/fileUtils.js +374 -0
  83. package/test_mocha/lib/git.js +82 -0
  84. package/test_mocha/lib/gitUtils.js +54 -0
  85. package/{tests/lib/versionCheck.spec.js → test_mocha/lib/versionCheck.js} +0 -0
  86. package/{tests/root.spec.js → test_mocha/root.js} +0 -0
  87. package/force-app-party/main/default/labels/CustomLabels/labels/Description.yaml +0 -7
  88. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/SocialPersona.yaml +0 -8
  89. package/tests/lib/gitUtils.spec.js +0 -137
package/src/index.js CHANGED
@@ -13,7 +13,6 @@ import { marked } from 'marked'
13
13
  import markedTerminal from 'marked-terminal'
14
14
 
15
15
  import * as fileUtils from './lib/fileUtils.js'
16
- import * as pkgObj from '../package.json' assert { type: "json" }
17
16
  import * as yargOptions from './meta/yargs.js'
18
17
  import * as metadataSplit from './party/split.js'
19
18
  import * as metadataCombine from './party/combine.js'
@@ -21,8 +20,10 @@ import * as labelDefinition from './meta/CustomLabels.js'
21
20
  import * as profileDefinition from './meta/Profiles.js'
22
21
  import * as permsetDefinition from './meta/PermissionSets.js'
23
22
  import * as workflowDefinition from './meta/Workflows.js'
23
+ import { checkVersion } from './lib/checkVersion.js'
24
24
  import * as git from './lib/gitUtils.js'
25
25
 
26
+ const pkgObj = fileUtils.readFile('package.json')
26
27
  const processStartTime = process.hrtime.bigint()
27
28
 
28
29
  marked.setOptions({
@@ -139,7 +140,7 @@ yargs(hideBin(process.argv))
139
140
  .check(yargCheck)
140
141
  },
141
142
  handler: (argv) => {
142
- checkVersion(pkgObj.default.version, true)
143
+ checkVersion(axios, exec, pkgObj.version, true)
143
144
  }
144
145
  })
145
146
  .command({
@@ -154,7 +155,7 @@ yargs(hideBin(process.argv))
154
155
  .check(yargCheck)
155
156
  },
156
157
  handler: (argv) => {
157
- checkVersion(pkgObj.default.version)
158
+ checkVersion(axios, exec, pkgObj.version)
158
159
  global.format = argv.format
159
160
  splitHandler(argv, processStartTime)
160
161
  }
@@ -171,7 +172,7 @@ yargs(hideBin(process.argv))
171
172
  .check(yargCheck)
172
173
  },
173
174
  handler: (argv) => {
174
- checkVersion(pkgObj.default.version)
175
+ checkVersion(axios, exec, pkgObj.version)
175
176
  global.format = argv.format
176
177
  const startProm = new Promise((resolve, reject) => {
177
178
  if (argv.git !== undefined) {
@@ -551,7 +552,7 @@ function gitFiles(data) {
551
552
  const pathArray = item.path.split(path.sep)
552
553
  if (pathArray.length > 3) {
553
554
  if (getDirectories().includes(pathArray[3])) {
554
- switch (git.action[item.type]) {
555
+ switch (item.action) {
555
556
  case 'add':
556
557
  global.metaTypes[getKey(pathArray[3])].add.files.push(path.join(global.__basedir, item.path))
557
558
  if (!global.metaTypes[getKey(pathArray[3])].add.directories.includes(pathArray[4])) {
@@ -602,7 +603,7 @@ function displayHeader() {
602
603
  horizontal: '─',
603
604
  vertical: '│',
604
605
  }
605
- let versionString = `sfparty v${pkgObj.default.version}${(process.stdout.columns > pkgObj.default.description.length + 15) ? ' - ' + pkgObj.default.description : ''}`
606
+ let versionString = `sfparty v${pkgObj.version}${(process.stdout.columns > pkgObj.description.length + 15) ? ' - ' + pkgObj.description : ''}`
606
607
  let titleMessage = `${global.icons.party} ${chalk.yellowBright(versionString)} ${global.icons.party}`
607
608
  titleMessage = titleMessage.padEnd((process.stdout.columns / 2) + versionString.length / 1.65)
608
609
  titleMessage = titleMessage.padStart(process.stdout.columns)
@@ -640,42 +641,3 @@ function getRootPath(packageDir) {
640
641
 
641
642
  return defaultDir
642
643
  }
643
-
644
- export async function checkVersion(currentVersion, update = false, test = false) {
645
- try {
646
- const { data } = await axios.get('https://registry.npmjs.org/@ds-sfdc/sfparty')
647
- const command = 'npm i -g @ds-sfdc/sfparty@latest'
648
- if (currentVersion !== data['dist-tags'].latest) {
649
- if (!test) console.log(`${(update) ? global.icons.working : global.icons.fail} A newer version ${chalk.bgCyanBright(data['dist-tags'].latest)} is available.`)
650
- if (!update) {
651
- if (test) return 'A newer version'
652
- console.log(`Please upgrade by running ${chalk.cyanBright('sfparty update')}`)
653
- } else {
654
- if (!test) console.log(`Updating the application using ${chalk.cyanBright(command)}`)
655
- exec('npm -v', (error, stdout, stderr) => {
656
- if (error) {
657
- if (test) 'npm is not installed'
658
- global.logger.error("npm is not installed on this system. Please install npm and run the command again.")
659
- return
660
- } else {
661
- exec(command, (error, stdout, stderr) => {
662
- if (error) {
663
- global.logger.error(error)
664
- return
665
- }
666
- console.log(stdout)
667
- console.log(stderr)
668
- })
669
- }
670
- })
671
- }
672
- } else {
673
- if (update) {
674
- if (test) return 'You are on the latest version'
675
- console.log(`${global.icons.success} You are on the latest version.`)
676
- }
677
- }
678
- } catch (error) {
679
- global.logger.error(error)
680
- }
681
- }
@@ -0,0 +1,42 @@
1
+ export async function checkVersion(axios, exec, currentVersion, update = false) {
2
+ try {
3
+ const { data } = await axios.get('https://registry.npmjs.org/@ds-sfdc/sfparty')
4
+ const command = 'npm i -g @ds-sfdc/sfparty@latest'
5
+ if (currentVersion !== data['dist-tags'].latest) {
6
+ console.log(`${(update) ? global.icons.working : global.icons.fail} A newer version ${chalk.bgCyanBright(data['dist-tags'].latest)} is available.`)
7
+ if (!update) {
8
+ console.log(`Please upgrade by running ${chalk.cyanBright('sfparty update')}`)
9
+ return 'A newer version'
10
+ } else {
11
+ console.log(`Updating the application using ${chalk.cyanBright(command)}`)
12
+ exec('npm -v', (error, stdout, stderr) => {
13
+ if (error) {
14
+ global.logger.error("npm is not installed on this system. Please install npm and run the command again.")
15
+ return 'npm is not installed'
16
+ } else {
17
+ exec(command, (error, stdout, stderr) => {
18
+ if (error) {
19
+ global.logger.error(error)
20
+ reject(error)
21
+ } else {
22
+ console.log(stdout)
23
+ console.log(stderr)
24
+ resolve(true)
25
+ }
26
+ })
27
+ }
28
+ })
29
+ }
30
+ } else {
31
+ if (update) {
32
+ console.log(`${global.icons.success} You are on the latest version.`)
33
+ return 'You are on the latest version'
34
+ }
35
+ }
36
+ } catch (error) {
37
+ global.logger.error(error)
38
+ throw error
39
+ }
40
+ }
41
+
42
+
@@ -4,42 +4,42 @@ import * as path from 'path'
4
4
  import * as yaml from 'js-yaml'
5
5
  import { Parser } from 'xml2js'
6
6
 
7
- export function directoryExists(dirPath) {
8
- return fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()
7
+ export function directoryExists(dirPath, fsTmp = fs) {
8
+ return fsTmp.existsSync(dirPath) && fsTmp.statSync(dirPath).isDirectory()
9
9
  }
10
10
 
11
- export function fileExists(filePath) {
12
- return fs.existsSync(filePath) && fs.statSync(filePath).isFile()
11
+ export function fileExists(filePath, fsTmp = fs) {
12
+ return fsTmp.existsSync(filePath) && fsTmp.statSync(filePath).isFile()
13
13
  }
14
14
 
15
- export function createDirectory(dirPath) {
16
- if (!fs.existsSync(dirPath)) {
17
- fs.mkdirSync(dirPath, { recursive: true })
15
+ export function createDirectory(dirPath, fsTmp = fs) {
16
+ if (!fsTmp.existsSync(dirPath)) {
17
+ fsTmp.mkdirSync(dirPath, { recursive: true })
18
18
  }
19
19
  }
20
20
 
21
- export function deleteDirectory(dirPath, recursive = false) {
22
- if (!directoryExists(dirPath)) {
21
+ export function deleteDirectory(dirPath, recursive = false, fsTmp = fs) {
22
+ if (!directoryExists(dirPath, fsTmp)) {
23
23
  return false
24
24
  } else {
25
- if (fs.existsSync(dirPath)) {
26
- fs.readdirSync(dirPath).forEach(function (file) {
25
+ if (fsTmp.existsSync(dirPath)) {
26
+ fsTmp.readdirSync(dirPath).forEach(function (file) {
27
27
  var curPath = path.join(dirPath, file)
28
- if (fs.lstatSync(curPath).isDirectory() && recursive) { // recurse
29
- deleteDirectory(curPath, recursive);
28
+ if (fsTmp.lstatSync(curPath).isDirectory() && recursive) { // recurse
29
+ deleteDirectory(curPath, recursive, fsTmp);
30
30
  } else { // delete file
31
- fs.unlinkSync(curPath);
31
+ fsTmp.unlinkSync(curPath);
32
32
  }
33
33
  })
34
- return fs.rmdirSync(dirPath);
34
+ return fsTmp.rmdirSync(dirPath);
35
35
  }
36
36
  }
37
37
  }
38
38
 
39
- export function getFiles(dirPath, filter = undefined) {
39
+ export function getFiles(dirPath, filter = undefined, fsTmp = fs) {
40
40
  const filesList = []
41
- if (directoryExists(dirPath)) {
42
- fs.readdirSync(dirPath).forEach(file => {
41
+ if (directoryExists(dirPath, fsTmp)) {
42
+ fsTmp.readdirSync(dirPath).forEach(file => {
43
43
  if (!filter) {
44
44
  filesList.push(file)
45
45
  } else {
@@ -55,9 +55,9 @@ export function getFiles(dirPath, filter = undefined) {
55
55
  }
56
56
  }
57
57
 
58
- export function getDirectories(dirPath) {
59
- if (directoryExists(dirPath)) {
60
- return fs.readdirSync(dirPath, { withFileTypes: true })
58
+ export function getDirectories(dirPath, fsTmp = fs) {
59
+ if (directoryExists(dirPath, fsTmp)) {
60
+ return fsTmp.readdirSync(dirPath, { withFileTypes: true })
61
61
  .filter(dirent => dirent.isDirectory())
62
62
  .map(dirent => dirent.name)
63
63
  } else {
@@ -65,35 +65,35 @@ export function getDirectories(dirPath) {
65
65
  }
66
66
  }
67
67
 
68
- export function deleteFile(filePath) {
69
- if (!fileExists(filePath)) {
68
+ export function deleteFile(filePath, fsTmp = fs) {
69
+ if (!fileExists(filePath, fsTmp)) {
70
70
  return false
71
71
  } else {
72
- return fs.unlinkSync(filePath, { recursive: false, force: true });
72
+ return fsTmp.unlinkSync(filePath, { recursive: false, force: true });
73
73
  }
74
74
  }
75
75
 
76
- export function fileInfo(filePath) {
76
+ export function fileInfo(filePath, fsTmp = fs) {
77
77
  return {
78
78
  "dirname": path.join(path.dirname(filePath)), //something/folder/example
79
79
  "basename": path.basename(filePath, path.extname(filePath)), //example
80
80
  "filename": path.basename(filePath), //example.txt
81
81
  "extname": path.extname(filePath), //txt
82
- "exists": fs.existsSync(filePath), //true if exists or false if not exists
83
- "stats": fs.existsSync(filePath) ? fs.statSync(filePath) : undefined //stats object if exists or undefined if not exists
82
+ "exists": fsTmp.existsSync(filePath), //true if exists or false if not exists
83
+ "stats": fsTmp.existsSync(filePath) ? fsTmp.statSync(filePath) : undefined //stats object if exists or undefined if not exists
84
84
  }
85
85
  }
86
86
 
87
- export function saveFile(json, fileName, format = path.extname(fileName).replace('.', '')) {
87
+ export function saveFile(json, fileName, format = path.extname(fileName).replace('.', ''), fsTmp = fs) {
88
88
  try {
89
89
  switch (format) {
90
90
  case 'json':
91
91
  let jsonString = JSON.stringify(json, null, '\t')
92
- fs.writeFileSync(fileName, jsonString)
92
+ fsTmp.writeFileSync(fileName, jsonString)
93
93
  break
94
94
  case 'yaml':
95
95
  let doc = yaml.dump(json)
96
- fs.writeFileSync(fileName, doc)
96
+ fsTmp.writeFileSync(fileName, doc)
97
97
  break
98
98
  }
99
99
  return true
@@ -103,11 +103,11 @@ export function saveFile(json, fileName, format = path.extname(fileName).replace
103
103
  }
104
104
  }
105
105
 
106
- export function readFile(fileName, convert = true) {
106
+ export function readFile(fileName, convert = true, fsTmp = fs) {
107
107
  try {
108
108
  let result = undefined
109
- if (fileExists(fileName)) {
110
- const data = fs.readFileSync(fileName, { encoding: 'utf8', flag: 'r' })
109
+ if (fileExists(fileName, fsTmp)) {
110
+ const data = fsTmp.readFileSync(fileName, { encoding: 'utf8', flag: 'r' })
111
111
  if (convert && fileName.indexOf('.yaml') != -1) {
112
112
  result = yaml.load(data)
113
113
  } else if (convert && fileName.indexOf('.json') != -1) {
@@ -141,17 +141,17 @@ async function convertXML(data) {
141
141
  })
142
142
  }
143
143
 
144
- export function writeFile(fileName, data, atime = new Date(), mtime = new Date()) {
144
+ export function writeFile(fileName, data, atime = new Date(), mtime = new Date(), fsTmp = fs) {
145
145
  try {
146
146
  // write data to the file
147
- fs.writeFileSync(fileName, data)
147
+ fsTmp.writeFileSync(fileName, data)
148
148
 
149
149
  // if atime or mtime are undefined, use current date/time
150
150
  if (atime === undefined) atime = new Date()
151
151
  if (mtime === undefined) mtime = new Date()
152
152
 
153
153
  // update XML file to match the latest atime and mtime of the files processed
154
- fs.utimesSync(fileName, atime, mtime)
154
+ fsTmp.utimesSync(fileName, atime, mtime)
155
155
 
156
156
  } catch (error) {
157
157
  global.logger.error(error)
@@ -159,7 +159,7 @@ export function writeFile(fileName, data, atime = new Date(), mtime = new Date()
159
159
  }
160
160
  }
161
161
 
162
- export function find(filename, root) {
162
+ export function find(filename, root, fsTmp = fs) {
163
163
  // code Copyright (c) 2014, Ben Gourley
164
164
  // https://github.com/bengourley/find-nearest-file
165
165
  root = root || process.cwd();
@@ -176,7 +176,7 @@ export function find(filename, root) {
176
176
  var file = path.join(directory, filename)
177
177
 
178
178
  try {
179
- if (fs.statSync(file).isFile()) return file
179
+ if (fsTmp.statSync(file).isFile()) return file
180
180
  // stat existed, but isFile() returned false
181
181
  return nextLevelUp()
182
182
  } catch (e) {
@@ -1,5 +1,5 @@
1
1
  import path from 'path'
2
- import { spawn } from 'node:child_process'
2
+ import { execSync } from 'node:child_process'
3
3
  import * as os from 'node:os'
4
4
  import { existsSync } from 'fs'
5
5
  import * as fileUtils from './fileUtils.js'
@@ -15,169 +15,116 @@ const defaultDefinition = {
15
15
  }
16
16
 
17
17
  const status = {
18
- A: 'add',
19
- C: 'copy',
20
- D: 'delete',
21
- M: 'modify',
22
- R: 'rename',
23
- T: 'type change',
24
- U: 'unmerged',
25
- X: 'unknown',
26
- }
27
-
28
- export const action = {
29
- add: 'add',
30
- copy: 'add',
31
- delete: 'delete',
32
- modify: 'add',
33
- rename: 'ignore',
34
- change: 'ignore',
35
- unmerged: 'ignore',
36
- unknown: 'ignore',
18
+ A: {
19
+ type: 'add',
20
+ action: 'add'
21
+ },
22
+ C: {
23
+ type: 'copy',
24
+ action: 'add',
25
+ },
26
+ D: {
27
+ type: 'delete',
28
+ action: 'delete'
29
+ },
30
+ M: {
31
+ type: 'modify',
32
+ action: 'add'
33
+ },
34
+ R: {
35
+ type: 'rename',
36
+ action: 'add'
37
+ },
38
+ T: {
39
+ type: 'type change',
40
+ action: 'add'
41
+ },
42
+ U: {
43
+ type: 'unmerged',
44
+ action: 'ignore'
45
+ },
46
+ X: {
47
+ type: 'unknown',
48
+ action: 'ignore'
49
+ },
37
50
  }
38
51
 
39
- export function diff(dir, gitRef) {
52
+ export function diff(dir, gitRef = 'HEAD', existsSyncStub = existsSync, execSyncStub = execSync) {
40
53
  return new Promise((resolve, reject) => {
41
- if (!existsSync(dir)) {
42
- reject(new Error(`The directory "${dir}" does not exist`))
43
- }
44
- if (!existsSync(`${dir}/.git`)) {
54
+ if (!existsSyncStub(dir) || !existsSyncStub(path.join(dir, '.git'))) {
45
55
  reject(new Error(`The directory "${dir}" is not a git repository`))
46
56
  }
47
57
 
48
58
  let data = ''
49
- const files = []
50
- const gitDiff = spawn('git', ['diff', '--name-status', '--oneline', '--relative', `${gitRef}`], { cwd: dir })
51
- gitDiff.stdout.on("data", result => {
52
- const gitString = result.toString()
53
- const lastIndex = gitString.lastIndexOf(os.EOL)
54
- let count = (gitString.match(/\n/g) || []).length;
55
- data += gitString
56
- const gitData = gitString.split(os.EOL)
57
- let leftOver = ''
58
- gitData.forEach((gitRow, index) => {
59
- if (gitRow.indexOf('\t') !== -1 && (index < count || lastIndex + 1 == gitString)) {
60
- const file = gitRow.split('\t')
61
- if (file.slice(-1) !== '') {
62
- files.push({
63
- type: status[(file[0] === file.slice(-1)) ? 'A' : Array.from(file[0])[0]],
64
- path: file.slice(-1)[0],
65
- })
66
- }
67
- } else {
68
- leftOver = gitRow
69
- }
70
- if (leftOver !== '') {
71
- data = leftOver
72
- } else {
73
- data = ''
74
- }
75
- })
76
- })
77
- gitDiff.stderr.on("data", data => {
78
- const errorMessage = 'git diff: ' + data.toString().split(os.EOL)[0]
79
-
80
- reject(new Error(errorMessage))
81
- })
82
- gitDiff.on('error', (error) => {
83
- if (error.message.indexOf('ENOENT')) {
84
- error.message = 'git not installed or no entry found in path'
85
- }
59
+ try {
60
+ data = execSyncStub(`git diff --name-status --oneline --relative ${gitRef}`, { cwd: dir }).toString()
61
+ } catch (error) {
86
62
  reject(error)
87
- })
88
- gitDiff.on("close", code => {
89
- if (data !== '') {
90
- const gitData = data.toString().split(os.EOL)
91
- gitData.forEach(gitRow => {
92
- const file = gitRow.split('\t')
93
- if (file.slice(-1) !== '') {
94
- files.push({
95
- type: status[(file[0] === file.slice(-1)) ? 'A' : Array.from(file[0])[0]],
96
- path: file.slice(-1)[0],
97
- })
98
- }
99
- })
100
- }
101
-
102
- resolve(files)
103
- })
104
- })
105
- }
63
+ }
106
64
 
107
- export function log(dir, gitRef) {
108
- return new Promise((resolve, reject) => {
109
- const commits = []
110
- const gitLog = spawn('git', ['log', '--format=format:%H', `${gitRef}`], { cwd: dir })
111
- gitLog.stdout.on("data", data => {
112
- commits.push(data.toString().split(os.EOL)[0])
113
- })
114
- gitLog.stderr.on("data", data => {
115
- const errorMessage = data.toString().split(os.EOL)[0]
116
- global.logger.error(`git log: ${errorMessage}`)
117
- reject(errorMessage)
118
- })
119
- gitLog.on('error', (error) => {
120
- if (error.message.indexOf('ENOENT')) {
121
- error.message = 'git not installed or no entry found in path'
65
+ const gitData = data.toString().split(os.EOL)
66
+ const files = gitData.reduce((acc, gitRow) => {
67
+ if (gitRow.indexOf('\t') > 0) {
68
+ const file = gitRow.split('\t')
69
+ if (file.slice(-1) !== '') {
70
+ const statusType = status[(file[0] === file.slice(-1)) ? 'A' : Array.from(file[0])[0]];
71
+ acc.push({
72
+ type: statusType.type,
73
+ path: file.slice(-1)[0],
74
+ action: statusType.action
75
+ });
76
+ }
122
77
  }
123
- reject(error)
124
- })
125
- gitLog.on("close", code => {
126
- resolve(commits)
127
- })
78
+ return acc;
79
+ }, []);
80
+ resolve(files);
128
81
  })
129
82
  }
130
83
 
131
- export function latestCommit(dir) {
132
- return new Promise((resolve, reject) => {
133
- const commit = log(dir, '-1')
134
- commit
135
- .then((data, error) => {
136
- resolve(data[0])
137
- })
138
- .catch((error) => {
139
- reject(error)
140
- })
141
- })
84
+ export function log(dir, gitRef, execSyncStub = execSync) {
85
+ try {
86
+ const gitLog = execSyncStub(`git log --format=format:%H ${gitRef}`, { cwd: dir, encoding: 'utf-8' });
87
+ const commits = gitLog.split(os.EOL).filter(commit => commit)
88
+ return commits
89
+ } catch (error) {
90
+ if (error.message.indexOf('ENOENT') > -1) {
91
+ error.message = 'git not installed or no entry found in path'
92
+ }
93
+ throw error
94
+ }
142
95
  }
143
96
 
144
- export function lastCommit(dir, fileName = 'index.yaml') {
145
- return new Promise((resolve, reject) => {
146
- const folder = path.join(dir, '.sfdx', 'sfparty')
147
- const filePath = path.join(folder, fileName)
148
- let last = undefined
149
- let latest = undefined
150
- fileUtils.createDirectory(folder)
151
- if (fileUtils.fileExists(filePath)) {
152
- const data = fileUtils.readFile(filePath)
97
+ export function lastCommit(dir, fileName = 'index.yaml', existsSyncStub = existsSync, execSyncStub = execSync, fileUtilsStub = fileUtils) {
98
+ try {
99
+ const folder = path.resolve(dir, '.sfdx', 'sfparty')
100
+ const filePath = path.resolve(folder, fileName)
101
+ let lastCommit = undefined
102
+
103
+ fileUtilsStub.createDirectory(folder)
104
+ if (existsSyncStub(filePath)) {
105
+ const data = fileUtilsStub.readFile(filePath)
153
106
  if (data.git.lastCommit !== undefined) {
154
- last = data.git.lastCommit
107
+ lastCommit = data.git.lastCommit
155
108
  }
156
109
  }
157
- const commit = latestCommit(dir)
158
- commit
159
- .then((data, error) => {
160
- latest = data
161
- resolve({
162
- lastCommit: last,
163
- latestCommit: latest,
164
- })
165
- })
166
- .catch((error) => {
167
- reject(error)
168
- })
169
-
170
- })
110
+ const latestCommit = execSyncStub(`git log --format=format:%H -1`, { cwd: dir, encoding: 'utf-8' })
111
+ return {
112
+ lastCommit: lastCommit,
113
+ latestCommit: latestCommit,
114
+ }
115
+ } catch (error) {
116
+ throw new Error(error)
117
+ }
171
118
  }
172
119
 
173
- export function updateLastCommit(dir, latest) {
120
+ export function updateLastCommit(dir, latest, fileUtilsStub = fileUtils) {
174
121
  if (typeof latest !== 'string' && typeof latest !== 'undefined') throw new Error(`updateLastCommit received a ${typeof latest} instead of string`)
175
122
  if (latest !== undefined) {
176
123
  const folder = path.join(dir, '.sfdx', 'sfparty')
177
124
  const fileName = path.join(folder, 'index.yaml')
178
125
  let data = undefined
179
- if (fileUtils.fileExists(fileName)) {
180
- data = fileUtils.readFile(fileName)
126
+ if (fileUtilsStub.fileExists(fileName)) {
127
+ data = fileUtilsStub.readFile(fileName)
181
128
  }
182
129
 
183
130
  if (data === undefined) {
@@ -185,6 +132,6 @@ export function updateLastCommit(dir, latest) {
185
132
  }
186
133
 
187
134
  data.git.lastCommit = latest
188
- fileUtils.saveFile(data, fileName)
135
+ fileUtilsStub.saveFile(data, fileName)
189
136
  }
190
137
  }
@@ -0,0 +1,54 @@
1
+ import { execSync } from 'child_process'
2
+ import { existsSync } from 'fs'
3
+ import { diff } from '../../../src/lib/gitUtils'
4
+
5
+ jest.mock('fs', () => ({
6
+ existsSync: jest.fn()
7
+ }))
8
+ jest.mock('child_process', () => ({
9
+ execSync: jest.fn()
10
+ }))
11
+ const gitRef = "HEAD~1..HEAD"
12
+
13
+ describe('diff', () => {
14
+ beforeEach(() => {
15
+ jest.clearAllMocks()
16
+ })
17
+
18
+ test('rejects if directory is not a git repository', async () => {
19
+ existsSync.mockReturnValueOnce(false)
20
+ try {
21
+ await diff('/path/to/dir')
22
+ fail('Expected function to throw an error', gitRef, existsSync)
23
+ } catch (error) {
24
+ expect(error.message).toEqual('The directory "/path/to/dir" is not a git repository')
25
+ }
26
+ })
27
+
28
+ test('resolves with files when git diff command is successful', async () => {
29
+ existsSync.mockReturnValueOnce(true).mockReturnValueOnce(true)
30
+ execSync.mockReturnValueOnce(
31
+ `A\tfile1.txt
32
+ M\tfile2.txt
33
+ D\tfile3.txt`)
34
+
35
+ const files = await diff('/path/to/dir', gitRef, existsSync, execSync)
36
+ expect(files).toEqual([
37
+ { type: 'add', path: 'file1.txt', action: 'add' },
38
+ { type: 'modify', path: 'file2.txt', action: 'add' },
39
+ { type: 'delete', path: 'file3.txt', action: 'delete' }
40
+ ])
41
+ })
42
+
43
+ test('rejects when git diff command fails', async () => {
44
+ existsSync.mockReturnValueOnce(true).mockReturnValueOnce(true)
45
+ execSync.mockImplementation(() => { throw new Error('Command failed') })
46
+
47
+ try {
48
+ await diff('/path/to/dir', gitRef, existsSync, execSync)
49
+ fail('Expected function to throw an error')
50
+ } catch (error) {
51
+ expect(error.message).toEqual('Command failed')
52
+ }
53
+ })
54
+ })