Skip to content

Commit 2b777e4

Browse files
clydinmgechev
authored andcommitted
fix(@angular-devkit/core): properly replace an added value in workspace API
Replacing a previous added value in the same session should cause the replace operation to become an add operation with the original add operation elided.
1 parent b336cc7 commit 2b777e4

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed

packages/angular_devkit/core/src/workspace/json/metadata.ts

+3
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ export class JsonWorkspaceMetadata {
6060
for (let i = this.changes.length - 1; i >= 0; --i) {
6161
const currentPath = this.changes[i].path;
6262
if (currentPath === path || currentPath.startsWith(path + '/')) {
63+
if (op === 'replace' && currentPath === path && this.changes[i].op === 'add') {
64+
op = 'add';
65+
}
6366
this.changes.splice(i, 1);
6467
}
6568
}

packages/angular_devkit/core/src/workspace/json/reader_spec.ts

+182
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,188 @@ describe('JSON WorkspaceDefinition Tracks Workspace Changes', () => {
183183
}
184184
});
185185

186+
it('tracks complex extension additions with Object.assign target', async () => {
187+
const host = createTestHost(basicFile);
188+
189+
const workspace = await readJsonWorkspace('', host);
190+
191+
const value = { a: 1, b: 2, c: { d: 'abc' } };
192+
workspace.extensions['x-baz'] = value;
193+
expect(workspace.extensions['x-baz']).toEqual({ a: 1, b: 2, c: { d: 'abc' } });
194+
195+
Object.assign(value, { x: 9, y: 8, z: 7 });
196+
expect(workspace.extensions['x-baz'])
197+
.toEqual({ a: 1, b: 2, c: { d: 'abc' }, x: 9, y: 8, z: 7 });
198+
199+
const metadata = getMetadata(workspace);
200+
201+
expect(metadata.hasChanges).toBeTruthy();
202+
expect(metadata.changeCount).toBe(1);
203+
204+
const change = metadata.findChangesForPath('/x-baz')[0];
205+
expect(change).not.toBeUndefined();
206+
if (change) {
207+
expect(change.op).toBe('add');
208+
expect(change.value).toEqual({ a: 1, b: 2, c: { d: 'abc' }, x: 9, y: 8, z: 7 });
209+
}
210+
});
211+
212+
it('tracks complex extension additions with Object.assign return', async () => {
213+
const host = createTestHost(basicFile);
214+
215+
const workspace = await readJsonWorkspace('', host);
216+
217+
const value = { a: 1, b: 2, c: { d: 'abc' } };
218+
workspace.extensions['x-baz'] = value;
219+
expect(workspace.extensions['x-baz']).toEqual({ a: 1, b: 2, c: { d: 'abc' } });
220+
221+
workspace.extensions['x-baz'] = Object.assign(value, { x: 9, y: 8, z: 7 });
222+
expect(workspace.extensions['x-baz'])
223+
.toEqual({ a: 1, b: 2, c: { d: 'abc' }, x: 9, y: 8, z: 7 });
224+
225+
const metadata = getMetadata(workspace);
226+
227+
expect(metadata.hasChanges).toBeTruthy();
228+
expect(metadata.changeCount).toBe(1);
229+
230+
const change = metadata.findChangesForPath('/x-baz')[0];
231+
expect(change).not.toBeUndefined();
232+
if (change) {
233+
expect(change.op).toBe('add');
234+
expect(change.value).toEqual({ a: 1, b: 2, c: { d: 'abc' }, x: 9, y: 8, z: 7 });
235+
}
236+
});
237+
238+
it('tracks complex extension additions with spread operator', async () => {
239+
const host = createTestHost(basicFile);
240+
241+
const workspace = await readJsonWorkspace('', host);
242+
243+
const value = { a: 1, b: 2, c: { d: 'abc' } };
244+
workspace.extensions['x-baz'] = value;
245+
expect(workspace.extensions['x-baz']).toEqual({ a: 1, b: 2, c: { d: 'abc' } });
246+
247+
workspace.extensions['x-baz'] = { ...value, ...{ x: 9, y: 8 }, z: 7 };
248+
expect(workspace.extensions['x-baz'])
249+
.toEqual({ a: 1, b: 2, c: { d: 'abc' }, x: 9, y: 8, z: 7 });
250+
251+
const metadata = getMetadata(workspace);
252+
253+
expect(metadata.hasChanges).toBeTruthy();
254+
expect(metadata.changeCount).toBe(1);
255+
256+
const change = metadata.findChangesForPath('/x-baz')[0];
257+
expect(change).not.toBeUndefined();
258+
if (change) {
259+
expect(change.op).toBe('add');
260+
expect(change.value).toEqual({ a: 1, b: 2, c: { d: 'abc' }, x: 9, y: 8, z: 7 });
261+
}
262+
});
263+
264+
it('tracks modifying an existing extension object with spread operator', async () => {
265+
const host = createTestHost(basicFile);
266+
267+
const workspace = await readJsonWorkspace('', host);
268+
269+
workspace.extensions['x-foo'] = {
270+
...workspace.extensions['x-foo'] as JsonObject,
271+
...{ x: 9, y: 8 }, z: 7 };
272+
expect(workspace.extensions['x-foo'])
273+
.toEqual({ is: ['good', 'great', 'awesome'], x: 9, y: 8, z: 7 });
274+
275+
const metadata = getMetadata(workspace);
276+
277+
expect(metadata.hasChanges).toBeTruthy();
278+
expect(metadata.changeCount).toBe(1);
279+
280+
const change = metadata.findChangesForPath('/x-foo')[0];
281+
expect(change).not.toBeUndefined();
282+
if (change) {
283+
expect(change.op).toBe('replace');
284+
expect(change.value).toEqual({ is: ['good', 'great', 'awesome'], x: 9, y: 8, z: 7 });
285+
}
286+
});
287+
288+
it('tracks modifying an existing extension object with Object.assign target', async () => {
289+
const host = createTestHost(basicFile);
290+
291+
const workspace = await readJsonWorkspace('', host);
292+
293+
Object.assign(
294+
workspace.extensions['x-foo'],
295+
{ x: 9, y: 8 },
296+
{ z: 7 },
297+
);
298+
expect(workspace.extensions['x-foo'])
299+
.toEqual({ is: ['good', 'great', 'awesome'], x: 9, y: 8, z: 7 });
300+
301+
const metadata = getMetadata(workspace);
302+
303+
expect(metadata.hasChanges).toBeTruthy();
304+
expect(metadata.changeCount).toBe(3);
305+
306+
let change = metadata.findChangesForPath('/x-foo/x')[0];
307+
expect(change).not.toBeUndefined();
308+
if (change) {
309+
expect(change.op).toBe('add');
310+
expect(change.value).toEqual(9);
311+
}
312+
313+
change = metadata.findChangesForPath('/x-foo/y')[0];
314+
expect(change).not.toBeUndefined();
315+
if (change) {
316+
expect(change.op).toBe('add');
317+
expect(change.value).toEqual(8);
318+
}
319+
320+
change = metadata.findChangesForPath('/x-foo/z')[0];
321+
expect(change).not.toBeUndefined();
322+
if (change) {
323+
expect(change.op).toBe('add');
324+
expect(change.value).toEqual(7);
325+
}
326+
});
327+
328+
it('tracks modifying an existing extension object with Object.assign return', async () => {
329+
const host = createTestHost(basicFile);
330+
331+
const workspace = await readJsonWorkspace('', host);
332+
333+
workspace.extensions['x-foo'] = Object.assign(
334+
workspace.extensions['x-foo'],
335+
{ x: 9, y: 8 },
336+
{ z: 7 },
337+
);
338+
expect(workspace.extensions['x-foo'])
339+
.toEqual({ is: ['good', 'great', 'awesome'], x: 9, y: 8, z: 7 });
340+
341+
const metadata = getMetadata(workspace);
342+
343+
expect(metadata.hasChanges).toBeTruthy();
344+
expect(metadata.changeCount).toBe(3);
345+
346+
let change = metadata.findChangesForPath('/x-foo/x')[0];
347+
expect(change).not.toBeUndefined();
348+
if (change) {
349+
expect(change.op).toBe('add');
350+
expect(change.value).toEqual(9);
351+
}
352+
353+
change = metadata.findChangesForPath('/x-foo/y')[0];
354+
expect(change).not.toBeUndefined();
355+
if (change) {
356+
expect(change.op).toBe('add');
357+
expect(change.value).toEqual(8);
358+
}
359+
360+
change = metadata.findChangesForPath('/x-foo/z')[0];
361+
expect(change).not.toBeUndefined();
362+
if (change) {
363+
expect(change.op).toBe('add');
364+
expect(change.value).toEqual(7);
365+
}
366+
});
367+
186368
it('tracks add and remove of an existing extension object', async () => {
187369
const host = createTestHost(basicFile);
188370

0 commit comments

Comments
 (0)