Skip to content

Commit 7abf569

Browse files
committed
feat: Added ability to insert new configs
1 parent a0c319a commit 7abf569

File tree

2 files changed

+147
-24
lines changed

2 files changed

+147
-24
lines changed

src/utils/file-modification-calculator.test.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,115 @@ describe('File modification calculator tests', () => {
328328
console.log(result.diff)
329329
})
330330

331+
it('Can insert a new resource in an existing config', async () => {
332+
const existingFile =
333+
`[
334+
{
335+
"type": "project",
336+
"plugins": {
337+
"default": "latest"
338+
}
339+
}
340+
]`
341+
generateTestFile(existingFile);
342+
343+
const project = await CodifyParser.parse(defaultPath)
344+
project.resourceConfigs.forEach((r) => {
345+
r.attachResourceInfo(generateResourceInfo(r.type, ['param2']))
346+
});
347+
348+
const modifiedResource = new ResourceConfig({
349+
type: 'resource1',
350+
param2: ['a', 'b', 'c', 'd']
351+
})
352+
modifiedResource.attachResourceInfo(generateResourceInfo('resource1'))
353+
354+
const calculator = new FileModificationCalculator(project);
355+
const result = await calculator.calculate([{
356+
modification: ModificationType.INSERT_OR_UPDATE,
357+
resource: modifiedResource,
358+
}])
359+
360+
expect(result.newFile).to.eq('[\n' +
361+
' {\n' +
362+
' "type": "project",\n' +
363+
' "plugins": {\n' +
364+
' "default": "latest"\n' +
365+
' }\n' +
366+
' },\n' +
367+
' {\n' +
368+
' "type": "resource1",\n' +
369+
' "param2": [\n' +
370+
' "a",\n' +
371+
' "b",\n' +
372+
' "c",\n' +
373+
' "d"\n' +
374+
' ]\n' +
375+
' }\n' +
376+
']')
377+
console.log(result)
378+
console.log(result.diff)
379+
})
380+
381+
it('Can insert a new resource in an existing config 2 (multiple)', async () => {
382+
const existingFile =
383+
`[
384+
{
385+
"type": "project",
386+
"plugins": {
387+
"default": "latest"
388+
}
389+
}
390+
]`
391+
generateTestFile(existingFile);
392+
393+
const project = await CodifyParser.parse(defaultPath)
394+
project.resourceConfigs.forEach((r) => {
395+
r.attachResourceInfo(generateResourceInfo(r.type, ['param2']))
396+
});
397+
398+
const modifiedResource = new ResourceConfig({
399+
type: 'resource1',
400+
param2: ['a', 'b', 'c', 'd']
401+
})
402+
modifiedResource.attachResourceInfo(generateResourceInfo('resource1'))
403+
404+
const modifiedResource2 = new ResourceConfig({
405+
type: 'resource2',
406+
param2: ['a', 'b', 'c', 'd']
407+
})
408+
modifiedResource2.attachResourceInfo(generateResourceInfo('resource2'))
409+
410+
const calculator = new FileModificationCalculator(project);
411+
const result = await calculator.calculate([{
412+
modification: ModificationType.INSERT_OR_UPDATE,
413+
resource: modifiedResource,
414+
}, {
415+
modification: ModificationType.INSERT_OR_UPDATE,
416+
resource: modifiedResource2,
417+
}])
418+
419+
// expect(result.newFile).to.eq('[\n' +
420+
// ' {\n' +
421+
// ' "type": "project",\n' +
422+
// ' "plugins": {\n' +
423+
// ' "default": "latest"\n' +
424+
// ' }\n' +
425+
// ' },\n' +
426+
// ' {\n' +
427+
// ' "type": "resource1",\n' +
428+
// ' "param2": [\n' +
429+
// ' "a",\n' +
430+
// ' "b",\n' +
431+
// ' "c",\n' +
432+
// ' "d"\n' +
433+
// ' ]\n' +
434+
// ' }\n' +
435+
// ']')
436+
console.log(result)
437+
console.log(result.diff)
438+
})
439+
331440
afterEach(() => {
332441
vi.resetAllMocks();
333442
})

src/utils/file-modification-calculator.ts

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -43,24 +43,23 @@ export class FileModificationCalculator {
4343
}
4444

4545
async calculate(modifications: ModifiedResource[]): Promise<FileModificationResult> {
46-
const resultResources = [...this.existingConfigs]
47-
48-
if (this.existingConfigs.length === 0 || !this.existingFile) {
49-
const newFile = JSON.stringify(
50-
modifications
51-
.filter((r) => r.modification === ModificationType.INSERT_OR_UPDATE)
52-
.map((r) => r.resource.raw),
53-
null, 2)
54-
55-
return {
56-
newFile,
57-
diff: this.diff('', newFile),
58-
}
59-
}
46+
// if (this.existingConfigs.length === 0 || !this.existingFile) {
47+
// const newFile = JSON.stringify(
48+
// modifications
49+
// .filter((r) => r.modification === ModificationType.INSERT_OR_UPDATE)
50+
// .map((r) => r.resource.raw),
51+
// null, 2)
52+
//
53+
// return {
54+
// newFile,
55+
// diff: this.diff('', newFile),
56+
// }
57+
// }
6058

6159
this.validate(modifications);
6260

63-
let newFile = this.existingFile.contents.trimEnd();
61+
let newFile = this.existingFile!.contents.trimEnd();
62+
const updateCache = [...modifications];
6463

6564
// Reverse the traversal order so we edit from the back. This way the line numbers won't be messed up with new edits.
6665
for (const existing of this.existingConfigs.reverse()) {
@@ -70,6 +69,7 @@ export class FileModificationCalculator {
7069
if (duplicateIndex === -1) {
7170
continue;
7271
}
72+
updateCache.splice(duplicateIndex, 1)
7373

7474
const modified = modifications[duplicateIndex];
7575
const duplicateSourceKey = existing.sourceMapKey?.split('#').at(1)!;
@@ -82,10 +82,19 @@ export class FileModificationCalculator {
8282
continue;
8383
}
8484

85+
// Update an existing resource
8586
newFile = this.remove(newFile, this.sourceMap, sourceIndex);
8687
newFile = this.update(newFile, modified.resource, this.sourceMap, sourceIndex);
8788
}
8889

90+
// Insert new resources
91+
const newResourcesToInsert = updateCache
92+
.filter((r) => r.modification === ModificationType.INSERT_OR_UPDATE)
93+
.map((r) => r.resource)
94+
const insertionIndex = newFile.length - 2; // Last element is guarenteed to be the closing bracket. We insert 1 before that
95+
96+
newFile = this.insert(newFile, newResourcesToInsert, insertionIndex);
97+
8998
return {
9099
newFile: newFile,
91100
diff: this.diff(this.existingFile.contents, newFile),
@@ -137,15 +146,20 @@ export class FileModificationCalculator {
137146
}
138147

139148
// Insert always works at the end
140-
private insertConfig(
149+
private insert(
141150
file: string,
142-
config: string,
143-
indentString: string,
144-
) {
145-
const configWithIndents = config.split(/\n/).map((l) => `${indentString}l`).join('\n');
146-
const result = file.substring(0, configWithIndents.length - 1) + ',' + configWithIndents + file.at(-1);
151+
resources: ResourceConfig[],
152+
position: number,
153+
): string {
154+
let result = file;
155+
156+
for (const newResource of resources.reverse()) {
157+
let content = JSON.stringify(newResource.raw, null, 2);
158+
content = content.split(/\n/).map((l) => `${this.indentString}${l}`).join('\n')
159+
content = `,\n${content}`;
147160

148-
// Need to fix the position of the comma
161+
result = this.splice(result, position, 0, content)
162+
}
149163

150164
return result;
151165
}
@@ -165,7 +179,7 @@ export class FileModificationCalculator {
165179
// Start one later so we leave the previous trailing comma alone
166180
const start = isFirst || isLast ? value!.position : value!.position + 1;
167181

168-
let result = this.r(file, start, valueEnd!.position)
182+
let result = this.removeSlice(file, start, valueEnd!.position)
169183

170184
// If there's no gap between the remaining elements, we add a space.
171185
if (!isFirst && !/\s/.test(result[start])) {
@@ -224,7 +238,7 @@ export class FileModificationCalculator {
224238
return s.substring(0, start) + insert + s.substring(start + deleteCount);
225239
}
226240

227-
private r(s: string, start: number, end: number) {
241+
private removeSlice(s: string, start: number, end: number) {
228242
return s.substring(0, start) + s.substring(end);
229243
}
230244

0 commit comments

Comments
 (0)