-
Notifications
You must be signed in to change notification settings - Fork 1
/
workspaces.ts
164 lines (137 loc) · 5.88 KB
/
workspaces.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import { json, workspaces } from '@angular-devkit/core';
import { Rule, SchematicsException, Tree, noop } from '@angular-devkit/schematics';
import { ProjectType } from './workspace-models';
const DEFAULT_WORKSPACE_PATH = '/angular.json';
// re-export the workspace definition types for convenience
export type WorkspaceDefinition = workspaces.WorkspaceDefinition;
export type ProjectDefinition = workspaces.ProjectDefinition;
export type TargetDefinition = workspaces.TargetDefinition;
/**
* A {@link workspaces.WorkspaceHost} backed by a Schematics {@link Tree} instance.
*/
export class TreeWorkspaceHost implements workspaces.WorkspaceHost {
constructor(private readonly tree: Tree) {}
async readFile(path: string): Promise<string> {
return this.tree.readText(path);
}
async writeFile(path: string, data: string): Promise<void> {
if (this.tree.exists(path)) {
this.tree.overwrite(path, data);
} else {
this.tree.create(path, data);
}
}
async isDirectory(path: string): Promise<boolean> {
// approximate a directory check
return !this.tree.exists(path) && this.tree.getDir(path).subfiles.length > 0;
}
async isFile(path: string): Promise<boolean> {
return this.tree.exists(path);
}
}
/**
* Updates the workspace file (`angular.json`) found within the root of the schematic's tree.
* The workspace object model can be directly modified within the provided updater function
* with changes being written to the workspace file after the updater function returns.
* The spacing and overall layout of the file (including comments) will be maintained where
* possible when updating the file.
*
* @param updater An update function that can be used to modify the object model for the
* workspace. A {@link WorkspaceDefinition} is provided as the first argument to the function.
*/
export function updateWorkspace(
updater: (workspace: WorkspaceDefinition) => void | Rule | PromiseLike<void | Rule>,
): Rule {
return async (host: Tree) => {
const workspace = await getWorkspace(host);
const result = await updater(workspace);
await workspaces.writeWorkspace(workspace, new TreeWorkspaceHost(host));
return result || noop;
};
}
// TODO: This should be renamed `readWorkspace` once deep imports are restricted (already exported from `utility` with that name)
/**
* Reads a workspace file (`angular.json`) from the provided {@link Tree} instance.
*
* @param tree A schematics {@link Tree} instance used to access the workspace file.
* @param path The path where a workspace file should be found. If a file is specified, the file
* path will be used. If a directory is specified, the file `angular.json` will be used from
* within the specified directory. Defaults to `/angular.json`.
* @returns A {@link WorkspaceDefinition} representing the workspace found at the specified path.
*/
export async function getWorkspace(tree: Tree, path = DEFAULT_WORKSPACE_PATH): Promise<WorkspaceDefinition> {
const host = new TreeWorkspaceHost(tree);
const { workspace } = await workspaces.readWorkspace(path, host);
return workspace;
}
/**
* Writes a workspace file (`angular.json`) to the provided {@link Tree} instance.
* The spacing and overall layout of an exisitng file (including comments) will be maintained where
* possible when writing the file.
*
* @param tree A schematics {@link Tree} instance used to access the workspace file.
* @param workspace The {@link WorkspaceDefinition} to write.
* @param path The path where a workspace file should be written. If a file is specified, the file
* path will be used. If not provided, the definition's underlying file path stored during reading
* will be used.
*/
export async function writeWorkspace(tree: Tree, workspace: WorkspaceDefinition, path?: string): Promise<void> {
const host = new TreeWorkspaceHost(tree);
return workspaces.writeWorkspace(workspace, host, path);
}
/**
* Build a default project path for generating.
* @param project The project which will have its default path generated.
*/
export function buildDefaultPath(project: workspaces.ProjectDefinition): string {
const root = project.sourceRoot ? `/${project.sourceRoot}/` : `/${project.root}/src/`;
const projectDirName = project.extensions['projectType'] === ProjectType.Application ? 'app' : 'lib';
return `${root}${projectDirName}`;
}
export async function createDefaultPath(tree: Tree, projectName: string): Promise<string> {
const workspace = await getWorkspace(tree);
const project = workspace.projects.get(projectName);
if (!project) {
throw new Error(`Project "${projectName}" does not exist.`);
}
return buildDefaultPath(project);
}
export function* allWorkspaceTargets(
workspace: workspaces.WorkspaceDefinition,
): Iterable<[string, workspaces.TargetDefinition, string, workspaces.ProjectDefinition]> {
for (const [projectName, project] of workspace.projects) {
for (const [targetName, target] of project.targets) {
yield [targetName, target, projectName, project];
}
}
}
export function* allTargetOptions(
target: workspaces.TargetDefinition,
skipBaseOptions = false,
): Iterable<[string | undefined, Record<string, json.JsonValue | undefined>]> {
if (!skipBaseOptions && target.options) {
yield [undefined, target.options];
}
if (!target.configurations) {
return;
}
for (const [name, options] of Object.entries(target.configurations)) {
if (options !== undefined) {
yield [name, options];
}
}
}
export function getDefaultProjectName(workspace: WorkspaceDefinition): string {
const projectName = workspace.projects.keys().next().value;
if (!projectName) {
throw new SchematicsException(`You don't have any project in your workspace`);
}
return projectName;
}