Skip to content

Commit 24b0a49

Browse files
committed
refactor(@angular-devkit/architect): abstract workspace access from architect
This change allows architect runtime implementations to have more control over how builder information and options for targets are found. A workspace file (and accompanying definition class) is not necessarily needed now. This also has benefits for unit testing by reducing the amount of potential setup needed per test.
1 parent 244ced0 commit 24b0a49

File tree

1 file changed

+85
-46
lines changed

1 file changed

+85
-46
lines changed

‎packages/angular_devkit/architect/node/node-modules-architect-host.ts

+85-46
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,86 @@ function clone(obj: unknown): unknown {
2828
}
2929
}
3030

31-
// TODO: create a base class for all workspace related hosts.
32-
export class WorkspaceNodeModulesArchitectHost implements ArchitectHost<NodeModulesBuilderInfo> {
33-
constructor(protected _workspace: workspaces.WorkspaceDefinition, protected _root: string) {}
31+
export interface WorkspaceHost {
32+
getBuilderName(project: string, target: string): Promise<string>;
33+
getMetadata(project: string): Promise<json.JsonObject>;
34+
getOptions(project: string, target: string, configuration?: string): Promise<json.JsonObject>;
35+
hasTarget(project: string, target: string): Promise<boolean>;
36+
}
3437

35-
async getBuilderNameForTarget(target: Target) {
36-
const targetDefinition = this.findProjectTarget(target);
37-
if (!targetDefinition) {
38-
throw new Error('Project target does not exist.');
38+
function findProjectTarget(
39+
workspace: workspaces.WorkspaceDefinition,
40+
project: string,
41+
target: string,
42+
): workspaces.TargetDefinition {
43+
const projectDefinition = workspace.projects.get(project);
44+
if (!projectDefinition) {
45+
throw new Error(`Project "${project}" does not exist.`);
46+
}
47+
48+
const targetDefinition = projectDefinition.targets.get(target);
49+
if (!targetDefinition) {
50+
throw new Error('Project target does not exist.');
51+
}
52+
53+
return targetDefinition;
54+
}
55+
56+
export class WorkspaceNodeModulesArchitectHost implements ArchitectHost<NodeModulesBuilderInfo> {
57+
private workspaceHost: WorkspaceHost;
58+
59+
constructor(workspaceHost: WorkspaceHost, _root: string);
60+
61+
constructor(workspace: workspaces.WorkspaceDefinition, _root: string);
62+
63+
constructor(
64+
workspaceOrHost: workspaces.WorkspaceDefinition | WorkspaceHost,
65+
protected _root: string,
66+
) {
67+
if ('getBuilderName' in workspaceOrHost) {
68+
this.workspaceHost = workspaceOrHost;
69+
} else {
70+
this.workspaceHost = {
71+
async getBuilderName(project, target) {
72+
const targetDefinition = findProjectTarget(workspaceOrHost, project, target);
73+
74+
return targetDefinition.builder;
75+
},
76+
async getOptions(project, target, configuration) {
77+
const targetDefinition = findProjectTarget(workspaceOrHost, project, target);
78+
79+
if (configuration === undefined) {
80+
return (targetDefinition.options ?? {}) as json.JsonObject;
81+
}
82+
83+
if (!targetDefinition.configurations?.[configuration]) {
84+
throw new Error(`Configuration '${configuration}' is not set in the workspace.`);
85+
}
86+
87+
return (targetDefinition.configurations?.[configuration] ?? {}) as json.JsonObject;
88+
},
89+
async getMetadata(project) {
90+
const projectDefinition = workspaceOrHost.projects.get(project);
91+
if (!projectDefinition) {
92+
throw new Error(`Project "${project}" does not exist.`);
93+
}
94+
95+
return ({
96+
root: projectDefinition.root,
97+
sourceRoot: projectDefinition.sourceRoot,
98+
prefix: projectDefinition.prefix,
99+
...(clone(projectDefinition.extensions) as {}),
100+
} as unknown) as json.JsonObject;
101+
},
102+
async hasTarget(project, target) {
103+
return !!workspaceOrHost.projects.get(project)?.targets.has(target);
104+
},
105+
};
39106
}
107+
}
40108

41-
return targetDefinition.builder;
109+
async getBuilderNameForTarget(target: Target) {
110+
return this.workspaceHost.getBuilderName(target.project, target.target);
42111
}
43112

44113
/**
@@ -95,49 +164,28 @@ export class WorkspaceNodeModulesArchitectHost implements ArchitectHost<NodeModu
95164
}
96165

97166
async getOptionsForTarget(target: Target): Promise<json.JsonObject | null> {
98-
const targetSpec = this.findProjectTarget(target);
99-
if (targetSpec === undefined) {
167+
if (!(await this.workspaceHost.hasTarget(target.project, target.target))) {
100168
return null;
101169
}
102170

103-
let additionalOptions = {};
171+
let options = await this.workspaceHost.getOptions(target.project, target.target);
104172

105173
if (target.configuration) {
106-
const configurations = target.configuration.split(',').map(c => c.trim());
174+
const configurations = target.configuration.split(',').map((c) => c.trim());
107175
for (const configuration of configurations) {
108-
if (!(targetSpec['configurations'] && targetSpec['configurations'][configuration])) {
109-
throw new Error(`Configuration '${configuration}' is not set in the workspace.`);
110-
} else {
111-
additionalOptions = {
112-
...additionalOptions,
113-
...targetSpec['configurations'][configuration],
114-
};
115-
}
176+
options = {
177+
...options,
178+
...await this.workspaceHost.getOptions(target.project, target.target, configuration),
179+
};
116180
}
117181
}
118182

119-
const options = {
120-
...targetSpec['options'],
121-
...additionalOptions,
122-
};
123-
124183
return clone(options) as json.JsonObject;
125184
}
126185

127186
async getProjectMetadata(target: Target | string): Promise<json.JsonObject | null> {
128187
const projectName = typeof target === 'string' ? target : target.project;
129-
130-
const projectDefinition = this._workspace.projects.get(projectName);
131-
if (!projectDefinition) {
132-
throw new Error(`Project "${projectName}" does not exist.`);
133-
}
134-
135-
const metadata = ({
136-
root: projectDefinition.root,
137-
sourceRoot: projectDefinition.sourceRoot,
138-
prefix: projectDefinition.prefix,
139-
...clone(projectDefinition.extensions) as {},
140-
} as unknown) as json.JsonObject;
188+
const metadata = this.workspaceHost.getMetadata(projectName);
141189

142190
return metadata;
143191
}
@@ -150,13 +198,4 @@ export class WorkspaceNodeModulesArchitectHost implements ArchitectHost<NodeModu
150198

151199
throw new Error('Builder is not a builder');
152200
}
153-
154-
private findProjectTarget(target: Target) {
155-
const projectDefinition = this._workspace.projects.get(target.project);
156-
if (!projectDefinition) {
157-
throw new Error(`Project "${target.project}" does not exist.`);
158-
}
159-
160-
return projectDefinition.targets.get(target.target);
161-
}
162201
}

0 commit comments

Comments
 (0)