Skip to content

Commit 7439fab

Browse files
clydinalexeagle
authored andcommitted
feat(@angular-devkit/core): implement workspace JSON reader/writer
1 parent c89dd30 commit 7439fab

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+3469
-19
lines changed

packages/angular_devkit/core/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ ts_library(
3333
"@npm//@types/node",
3434
"@npm//@types/source-map",
3535
"@npm//ajv",
36+
"@npm//magic-string",
3637
# @typings: es2015.core
3738
# @typings: es2015.symbol.wellknown
3839
# @typings: es2016.array.include

packages/angular_devkit/core/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"dependencies": {
1111
"ajv": "6.10.0",
1212
"fast-json-stable-stringify": "2.0.0",
13+
"magic-string": "0.25.1",
1314
"rxjs": "6.4.0",
1415
"source-map": "0.7.3"
1516
},

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

+7-12
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
import { basename, getSystemPath, join, normalize } from '../virtual-fs';
9-
import { ProjectDefinitionCollection, WorkspaceDefinition } from './definitions';
9+
import { WorkspaceDefinition } from './definitions';
1010
import { WorkspaceHost } from './host';
11+
import { readJsonWorkspace } from './json/reader';
12+
import { writeJsonWorkspace } from './json/writer';
1113

1214
const formatLookup = new WeakMap<WorkspaceDefinition, WorkspaceFormat>();
1315

@@ -45,8 +47,6 @@ export async function readWorkspace(
4547

4648
const potential = getSystemPath(join(directory, name));
4749
if (await host.isFile(potential)) {
48-
// TEMP - remove disable when actual reader is used
49-
// tslint:disable-next-line:no-dead-store
5050
path = potential;
5151
format = nameFormat;
5252
found = true;
@@ -70,12 +70,7 @@ export async function readWorkspace(
7070
let workspace;
7171
switch (format) {
7272
case WorkspaceFormat.JSON:
73-
// TEMP: remove the following two statements when JSON support is introduced
74-
await host.readFile(path);
75-
workspace = {
76-
extensions: {},
77-
projects: new ProjectDefinitionCollection(),
78-
};
73+
workspace = await readJsonWorkspace(path, host);
7974
break;
8075
default:
8176
throw new Error('Unsupported workspace format.');
@@ -88,8 +83,8 @@ export async function readWorkspace(
8883

8984
export async function writeWorkspace(
9085
workspace: WorkspaceDefinition,
91-
_host: WorkspaceHost,
92-
_path?: string,
86+
host: WorkspaceHost,
87+
path?: string,
9388
format?: WorkspaceFormat,
9489
): Promise<void> {
9590
if (format === undefined) {
@@ -101,7 +96,7 @@ export async function writeWorkspace(
10196

10297
switch (format) {
10398
case WorkspaceFormat.JSON:
104-
throw new Error('Not Implemented.');
99+
return writeJsonWorkspace(workspace, host, path);
105100
default:
106101
throw new Error('Unsupported workspace format.');
107102
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import { JsonAstArray, JsonAstKeyValue, JsonAstNode, JsonAstObject, JsonValue } from '../../json';
9+
import { ProjectDefinition, TargetDefinition, WorkspaceDefinition } from '../definitions';
10+
11+
export const JsonWorkspaceSymbol = Symbol.for('@angular/core:workspace-json');
12+
13+
export interface JsonWorkspaceDefinition extends WorkspaceDefinition {
14+
[JsonWorkspaceSymbol]: JsonWorkspaceMetadata;
15+
}
16+
17+
interface ChangeValues {
18+
json: JsonValue;
19+
project: ProjectDefinition;
20+
target: TargetDefinition;
21+
projectcollection: Iterable<[string, ProjectDefinition]>;
22+
targetcollection: Iterable<[string, TargetDefinition]>;
23+
}
24+
25+
export interface JsonChange<T extends keyof ChangeValues = keyof ChangeValues> {
26+
// core collections can only be added as they are managed directly by _Collection_ objects
27+
op: T extends 'json' | 'project' | 'target' ? 'add' | 'remove' | 'replace' : 'add';
28+
path: string;
29+
node: JsonAstNode | JsonAstKeyValue;
30+
value?: ChangeValues[T];
31+
type: T;
32+
}
33+
34+
export class JsonWorkspaceMetadata {
35+
readonly changes: JsonChange[] = [];
36+
37+
constructor(readonly filePath: string, readonly ast: JsonAstObject, readonly raw: string) { }
38+
39+
get hasChanges(): boolean {
40+
return this.changes.length > 0;
41+
}
42+
43+
get changeCount(): number {
44+
return this.changes.length;
45+
}
46+
47+
findChangesForPath(path: string): JsonChange[] {
48+
return this.changes.filter(c => c.path === path);
49+
}
50+
51+
addChange<T extends keyof ChangeValues = keyof ChangeValues>(
52+
op: 'add' | 'remove' | 'replace',
53+
path: string,
54+
node: JsonAstArray | JsonAstObject | JsonAstKeyValue,
55+
value?: ChangeValues[T],
56+
type?: T,
57+
): void {
58+
// Remove redundant operations
59+
if (op === 'remove' || op === 'replace') {
60+
for (let i = this.changes.length - 1; i >= 0; --i) {
61+
const currentPath = this.changes[i].path;
62+
if (currentPath === path || currentPath.startsWith(path + '/')) {
63+
this.changes.splice(i, 1);
64+
}
65+
}
66+
}
67+
68+
this.changes.push({ op, path, node, value, type: op === 'remove' || !type ? 'json' : type });
69+
}
70+
71+
reset(): void {
72+
this.changes.length = 0;
73+
}
74+
}

0 commit comments

Comments
 (0)