From b514195b56279aa09eddcfff6ced98f01f161e15 Mon Sep 17 00:00:00 2001 From: Jason Cassidy <47318351+jcassidyav@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:46:08 +0000 Subject: [PATCH] fix: ensure that PbxGroups are not duplicated --- .gitignore | 2 +- lib/guidMapper.js | 52 ++++++++++++++++++++++++++++++++++ lib/pbxProject.js | 27 +++++++++++++++++- test/guidMapper.js | 69 ++++++++++++++++++++++++++++++++++++++++++++++ test/pbxProject.js | 17 ++++++++++++ 5 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 lib/guidMapper.js create mode 100644 test/guidMapper.js diff --git a/.gitignore b/.gitignore index 0a5bb72..5ef95a1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,5 @@ node_modules/* .DS_Store npm-debug.log package-lock.json - +.ns-build-pbxgroup-data.json *.tgz \ No newline at end of file diff --git a/lib/guidMapper.js b/lib/guidMapper.js new file mode 100644 index 0000000..98300ff --- /dev/null +++ b/lib/guidMapper.js @@ -0,0 +1,52 @@ +const fs = require('fs'); +const path = require('path'); + +function guidMapper(filePath) { + this.filePath = filePath; + this.data = this.loadFromFile(); +} + +guidMapper.prototype.loadFromFile = function () { + try { + const rawData = fs.readFileSync(this.filePath, 'utf8'); + return JSON.parse(rawData); + } catch (error) { + // If file doesn't exist or there's an error parsing it, initialize with an empty object. + return {}; + } +}; + +guidMapper.prototype.writeFileSync = function () { + const jsonData = JSON.stringify(this.data, null, 2); + fs.writeFileSync(this.filePath, jsonData, 'utf8'); +}; + +guidMapper.prototype.addEntry = function (guid, path, name) { + if(!!guid && !! path && !!name){ + this.data[guid] = { path: path, name: name }; + } +}; + +guidMapper.prototype.removeEntry = function (guid) { + if (this.data[guid]) { + delete this.data[guid]; + } +}; + +guidMapper.prototype.getEntries = function () { + return this.data; +}; + +guidMapper.prototype.findEntryGuid = function (name, path) { + for (const guid in this.data) { + if (this.data.hasOwnProperty(guid)) { + const entry = this.data[guid]; + if (entry.path === path && entry.name === name) { + return guid; + } + } + } + return null; +}; + +module.exports = guidMapper; \ No newline at end of file diff --git a/lib/pbxProject.js b/lib/pbxProject.js index 7852427..6364ee3 100644 --- a/lib/pbxProject.js +++ b/lib/pbxProject.js @@ -35,7 +35,8 @@ var util = require('util'), isEntitlementFileType = constants.isEntitlementFileType, isAssetFileType = constants.isAssetFileType, isPlistFileType = constants.isPlistFileType, - isModuleMapFileType = constants.isModuleMapFileType; + isModuleMapFileType = constants.isModuleMapFileType, + guidMapper = require('./guidMapper'); function pbxProject(filename) { if (!(this instanceof pbxProject)) @@ -74,6 +75,10 @@ pbxProject.prototype.parseSync = function() { } pbxProject.prototype.writeSync = function(options) { + if(this.pbxGroupTracker){ + this.pbxGroupTracker.writeFileSync(); + } + this.writer = new pbxWriter(this.hash, options); return this.writer.writeSync(); } @@ -538,10 +543,27 @@ pbxProject.prototype.findMainPbxGroup = function () { return null; } +pbxProject.prototype.getPbxGroupTracker = function (path) { + + if(!this.pbxGroupTracker){ + this.pbxGroupTracker = new guidMapper($path.join(path, '.ns-build-pbxgroup-data.json')); + } + + return this.pbxGroupTracker; +} pbxProject.prototype.addPbxGroup = function (filePathsArray, name, path, sourceTree, opt) { opt = opt || {}; var srcRootPath = $path.dirname($path.dirname(this.filepath)); + + var existingGroupId = this.getPbxGroupTracker(srcRootPath).findEntryGuid(name, path); + if(existingGroupId){ + if(this.getPBXGroupByKey(existingGroupId)){ + this.removePbxGroupByKey(existingGroupId, path); + } + this.pbxGroupTracker.removeEntry(existingGroupId); + } + var groups = this.hash.project.objects['PBXGroup'], pbxGroupUuid = opt.uuid || this.generateUuid(), commentKey = f("%s_comment", pbxGroupUuid), @@ -560,6 +582,9 @@ pbxProject.prototype.addPbxGroup = function (filePathsArray, name, path, sourceT pbxGroup.path = path; } + // save to group to the tracker + this.pbxGroupTracker.addEntry(pbxGroupUuid, path, name); + for (var key in fileReferenceSection) { // only look for comments if (!COMMENT_KEY.test(key)) continue; diff --git a/test/guidMapper.js b/test/guidMapper.js new file mode 100644 index 0000000..8c9cb72 --- /dev/null +++ b/test/guidMapper.js @@ -0,0 +1,69 @@ +var guidMapper = require('../lib/guidMapper'); +const fs = require('fs'); +const $uuid = require('uuid'); +const TEST_FILE_NAME = 'test/.ns-build-pbxgroup-data.json'; +const goodGUID = $uuid.v4(); +const goodName = "goodName"; +const badName = 'badName'; +const goodPath = "goodPath"; +const badPath = "badPath"; +exports.setUp = function(callback) { + if(fs.existsSync(TEST_FILE_NAME)){ + fs.rmSync(TEST_FILE_NAME); + } + callback(); +} +exports.tearDown = function(callback) { + if(fs.existsSync(TEST_FILE_NAME)){ + fs.rmSync(TEST_FILE_NAME); + } + callback(); +} +function addTestData(){ + var mapper = new guidMapper(TEST_FILE_NAME); + mapper.addEntry(goodGUID, goodPath, goodName); + mapper.writeFileSync(); +} +exports.operations = { + 'should be able to add to map': function(test) { + var mapper = new guidMapper(TEST_FILE_NAME); + mapper.addEntry(goodGUID, goodPath, goodName); + mapper.writeFileSync(); + mapper = new guidMapper(TEST_FILE_NAME); + const result = mapper.findEntryGuid(goodName, goodPath); + + test.ok(result === goodGUID) + test.done(); + }, + 'should not match only on name': function(test) { + addTestData(); + var mapper = new guidMapper(TEST_FILE_NAME); + + const result = mapper.findEntryGuid(goodName, badPath); + + test.ok(result === null) + test.done(); + }, + 'should not match only on path': function(test) { + addTestData(); + var mapper = new guidMapper(TEST_FILE_NAME); + + const result = mapper.findEntryGuid(badName, goodPath); + + test.ok(result === null) + test.done(); + }, + 'can remove': function(test) { + addTestData(); + var mapper = new guidMapper(TEST_FILE_NAME); + mapper.removeEntry(goodGUID); + var result = mapper.findEntryGuid(goodName, goodPath); + + test.ok(result === null); + mapper.writeFileSync(); + result = mapper.findEntryGuid(goodName, goodPath); + test.ok(result === null) + + test.done(); + } +} \ No newline at end of file diff --git a/test/pbxProject.js b/test/pbxProject.js index e846294..860c4f4 100644 --- a/test/pbxProject.js +++ b/test/pbxProject.js @@ -433,3 +433,20 @@ exports['addToPbxFileReferenceSection'] = { } } + +exports['addPbxGroup'] = { + 'should not add the same group twice': function (test) { + var newProj = new pbx('test/parser/projects/group.pbxproj'); + newProj.parse(function (err, hash) { + this.hash.project.objects['PBXVariantGroup']={}; + var group1 = newProj.addPbxGroup(['test/somefile'], "TestGroup", "test/somepath", null, null); + var group2 = newProj.addPbxGroup(['test/somefile'], "TestGroup", "test/somepath", null, null); + test.equal(newProj.getPBXGroupByKey(group1.uuid), null); + test.equal(newProj.getPBXGroupByKey(group2.uuid).name, "TestGroup"); + test.equal(newProj.getPbxGroupTracker().getEntries().hasOwnProperty(group1.uuid), false); + test.equal(newProj.getPbxGroupTracker().getEntries().hasOwnProperty(group2.uuid), true); + + test.done(); + }); + } +} \ No newline at end of file