Skip to content

Commit aa95e44

Browse files
filipesilvahansl
authored andcommitted
refactor: update architect packages to use new workspace
1 parent 54c21f5 commit aa95e44

58 files changed

Lines changed: 647 additions & 1099 deletions

Some content is hidden

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

packages/angular_devkit/architect/src/architect.ts

Lines changed: 109 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,21 @@ import {
1212
JsonParseMode,
1313
Path,
1414
dirname,
15+
experimental,
1516
getSystemPath,
1617
join,
1718
logging,
1819
normalize,
1920
parseJson,
2021
resolve,
21-
schema,
2222
virtualFs,
2323
} from '@angular-devkit/core';
2424
import { resolve as nodeResolve } from '@angular-devkit/core/node';
2525
import { Observable } from 'rxjs/Observable';
2626
import { forkJoin } from 'rxjs/observable/forkJoin';
2727
import { of } from 'rxjs/observable/of';
2828
import { _throw } from 'rxjs/observable/throw';
29-
import { concatMap, map } from 'rxjs/operators';
29+
import { concatMap, map, tap } from 'rxjs/operators';
3030
import {
3131
BuildEvent,
3232
Builder,
@@ -36,15 +36,6 @@ import {
3636
BuilderPaths,
3737
BuilderPathsMap,
3838
} from './builder';
39-
import { Workspace } from './workspace';
40-
41-
42-
export class ProjectNotFoundException extends BaseException {
43-
constructor(name?: string) {
44-
const nameOrDefault = name ? `Project '${name}'` : `Default project`;
45-
super(`${nameOrDefault} could not be found in workspace.`);
46-
}
47-
}
4839

4940
export class TargetNotFoundException extends BaseException {
5041
constructor(name?: string) {
@@ -59,21 +50,15 @@ export class ConfigurationNotFoundException extends BaseException {
5950
}
6051
}
6152

62-
export class SchemaValidationException extends BaseException {
63-
constructor(errors: string[]) {
64-
super(`Schema validation failed with the following errors:\n ${errors.join('\n ')}`);
65-
}
66-
}
67-
6853
// TODO: break this exception apart into more granular ones.
6954
export class BuilderCannotBeResolvedException extends BaseException {
7055
constructor(builder: string) {
7156
super(`Builder '${builder}' cannot be resolved.`);
7257
}
7358
}
7459

75-
export class WorkspaceNotYetLoadedException extends BaseException {
76-
constructor() { super(`Workspace needs to be loaded before Architect is used.`); }
60+
export class ArchitectNotYetLoadedException extends BaseException {
61+
constructor() { super(`Architect needs to be loaded before Architect is used.`); }
7762
}
7863

7964
export class BuilderNotFoundException extends BaseException {
@@ -82,155 +67,151 @@ export class BuilderNotFoundException extends BaseException {
8267
}
8368
}
8469

85-
export interface Target<OptionsT = {}> {
70+
export interface BuilderConfiguration<OptionsT = {}> {
8671
root: Path;
8772
projectType: string;
8873
builder: string;
8974
options: OptionsT;
9075
}
9176

92-
export interface TargetOptions<OptionsT = {}> {
93-
project?: string;
94-
target?: string;
77+
export interface TargetSpecifier<OptionsT = {}> {
78+
project: string;
79+
target: string;
9580
configuration?: string;
9681
overrides?: Partial<OptionsT>;
9782
}
83+
84+
export interface TargetsMap {
85+
[k: string]: Target;
86+
}
87+
88+
export declare type TargetOptions<T = JsonObject> = T;
89+
export declare type TargetConfiguration<T = JsonObject> = Partial<T>;
90+
91+
export interface Target<T = JsonObject> {
92+
builder: string;
93+
options: TargetOptions<T>;
94+
configurations?: { [k: string]: TargetConfiguration<T> };
95+
}
96+
9897
export class Architect {
99-
private readonly _workspaceSchemaPath = join(normalize(__dirname), 'workspace-schema.json');
98+
private readonly _targetsSchemaPath = join(normalize(__dirname), 'targets-schema.json');
10099
private readonly _buildersSchemaPath = join(normalize(__dirname), 'builders-schema.json');
101-
private _workspaceSchema: JsonObject;
100+
private _targetsSchema: JsonObject;
102101
private _buildersSchema: JsonObject;
103102
private _architectSchemasLoaded = false;
104103
private _builderPathsMap = new Map<string, BuilderPaths>();
105104
private _builderDescriptionMap = new Map<string, BuilderDescription>();
106105
private _builderConstructorMap = new Map<string, BuilderConstructor<{}>>();
107-
private _workspace: Workspace;
108-
109-
constructor(private _root: Path, private _host: virtualFs.Host<{}>) { }
110-
111-
loadWorkspaceFromHost(workspacePath: Path) {
112-
return this._loadArchitectSchemas().pipe(
113-
concatMap(() => this._loadJsonFile(join(this._root, workspacePath))),
114-
concatMap(json => this.loadWorkspaceFromJson(json as {} as Workspace)),
115-
);
116-
}
117-
118-
loadWorkspaceFromJson(json: Workspace) {
119-
return this._loadArchitectSchemas().pipe(
120-
concatMap(() => this._validateAgainstSchema(json, this._workspaceSchema)),
121-
concatMap((validatedWorkspace: Workspace) => {
122-
this._workspace = validatedWorkspace;
123106

124-
return of(this);
125-
}),
126-
);
127-
}
107+
constructor(private _workspace: experimental.workspace.Workspace) { }
128108

129-
private _loadArchitectSchemas() {
109+
loadArchitect() {
130110
if (this._architectSchemasLoaded) {
131-
return of(null);
111+
return of(this);
132112
} else {
133113
return forkJoin(
134-
this._loadJsonFile(this._workspaceSchemaPath),
114+
this._loadJsonFile(this._targetsSchemaPath),
135115
this._loadJsonFile(this._buildersSchemaPath),
136116
).pipe(
137117
concatMap(([workspaceSchema, buildersSchema]) => {
138-
this._workspaceSchema = workspaceSchema;
118+
this._targetsSchema = workspaceSchema;
139119
this._buildersSchema = buildersSchema;
120+
this._architectSchemasLoaded = true;
140121

141-
return of(null);
122+
return of(this);
142123
}),
143124
);
144125
}
145126
}
146127

147-
getTarget<OptionsT>(options: TargetOptions = {}): Target<OptionsT> {
148-
let { project, target: targetName } = options;
149-
const { configuration, overrides } = options;
150-
151-
if (!this._workspace) {
152-
throw new WorkspaceNotYetLoadedException();
153-
}
154-
155-
project = project || this._workspace.defaultProject as string;
156-
const workspaceProject = this._workspace.projects[project];
157-
158-
if (!workspaceProject) {
159-
throw new ProjectNotFoundException(project);
160-
}
161-
162-
targetName = targetName || workspaceProject.defaultTarget as string;
163-
const workspaceTarget = workspaceProject.targets[targetName];
164-
165-
if (!workspaceTarget) {
166-
throw new TargetNotFoundException(targetName);
167-
}
128+
getBuilderConfiguration<OptionsT>(
129+
targetSpec: TargetSpecifier,
130+
): Observable<BuilderConfiguration<OptionsT>> {
131+
const {
132+
project: projectName,
133+
target: targetName,
134+
configuration: configurationName,
135+
overrides,
136+
} = targetSpec;
137+
138+
const project = this._workspace.getProject(projectName);
139+
const targets = this._workspace.getProjectArchitect(projectName);
140+
let configuration: TargetConfiguration = {};
141+
let target: Target, options: TargetOptions;
142+
143+
return this._workspace.validateAgainstSchema<TargetsMap>(targets, this._targetsSchema).pipe(
144+
concatMap((validatedWorkspaceTargets) => {
145+
target = validatedWorkspaceTargets[targetName];
146+
147+
if (!target) {
148+
return _throw(new TargetNotFoundException(targetName));
149+
}
168150

169-
const workspaceTargetOptions = workspaceTarget.options;
170-
let workspaceConfiguration;
151+
options = target.options;
171152

172-
if (configuration) {
173-
workspaceConfiguration = workspaceTarget.configurations
174-
&& workspaceTarget.configurations[configuration];
153+
if (configurationName) {
154+
if (!target.configurations) {
155+
return _throw(new ConfigurationNotFoundException(configurationName));
156+
}
175157

176-
if (!workspaceConfiguration) {
177-
throw new ConfigurationNotFoundException(configuration);
178-
}
179-
}
158+
configuration = target.configurations[configurationName];
180159

181-
// Resolve root for the target.
182-
// TODO: add Path format to JSON schemas
183-
const target: Target<OptionsT> = {
184-
root: resolve(this._root, normalize(workspaceProject.root)),
185-
projectType: workspaceProject.projectType,
186-
builder: workspaceTarget.builder,
187-
options: {
188-
...workspaceTargetOptions,
189-
...workspaceConfiguration,
190-
...overrides as {},
191-
} as OptionsT,
192-
};
160+
if (!configuration) {
161+
return _throw(new ConfigurationNotFoundException(configurationName));
162+
}
163+
}
193164

194-
// Return a copy of the target object, JSON validation changes objects and we don't
195-
// want the original properties to be modified.
196-
return JSON.parse(JSON.stringify(target));
165+
const builderConfiguration: BuilderConfiguration<OptionsT> = {
166+
root: resolve(this._workspace.root, normalize(project.root)),
167+
projectType: project.projectType,
168+
builder: target.builder,
169+
options: {
170+
...options,
171+
...configuration,
172+
...overrides as {},
173+
} as OptionsT,
174+
};
175+
176+
return of(builderConfiguration);
177+
}),
178+
);
197179
}
198180

199-
// Will run the target using the target.
200181
run<OptionsT>(
201-
target: Target<OptionsT>,
182+
builderConfig: BuilderConfiguration<OptionsT>,
202183
partialContext: Partial<BuilderContext> = {},
203184
): Observable<BuildEvent> {
204185
const context: BuilderContext = {
205186
logger: new logging.NullLogger(),
206187
architect: this,
207-
host: this._host,
188+
host: this._workspace.host,
208189
...partialContext,
209190
};
210191

211192
let builderDescription: BuilderDescription;
212193

213-
return this.getBuilderDescription(target).pipe(
214-
concatMap(description => {
215-
builderDescription = description;
216-
217-
return this.validateBuilderOptions(target, builderDescription);
218-
}),
194+
return this.getBuilderDescription(builderConfig).pipe(
195+
tap(description => builderDescription = description),
196+
concatMap(() => this.validateBuilderOptions(builderConfig, builderDescription)),
197+
tap(validatedBuilderConfig => builderConfig = validatedBuilderConfig),
219198
map(() => this.getBuilder(builderDescription, context)),
220-
concatMap(builder => builder.run(target)),
199+
concatMap(builder => builder.run(builderConfig)),
221200
);
222201
}
223202

224-
getBuilderDescription<OptionsT>(target: Target<OptionsT>): Observable<BuilderDescription> {
203+
getBuilderDescription<OptionsT>(
204+
builderConfig: BuilderConfiguration<OptionsT>,
205+
): Observable<BuilderDescription> {
225206
// Check cache for this builder description.
226-
if (this._builderDescriptionMap.has(target.builder)) {
227-
return of(this._builderDescriptionMap.get(target.builder) as BuilderDescription);
207+
if (this._builderDescriptionMap.has(builderConfig.builder)) {
208+
return of(this._builderDescriptionMap.get(builderConfig.builder) as BuilderDescription);
228209
}
229210

230211
return new Observable((obs) => {
231212
// TODO: this probably needs to be more like NodeModulesEngineHost.
232-
const basedir = getSystemPath(this._root);
233-
const [pkg, builderName] = target.builder.split(':');
213+
const basedir = getSystemPath(this._workspace.root);
214+
const [pkg, builderName] = builderConfig.builder.split(':');
234215
const pkgJsonPath = nodeResolve(pkg, { basedir, resolvePackageJson: true });
235216
let buildersJsonPath: Path;
236217
let builderPaths: BuilderPaths;
@@ -240,21 +221,21 @@ export class Architect {
240221
concatMap((pkgJson: JsonObject) => {
241222
const pkgJsonBuildersentry = pkgJson['builders'] as string;
242223
if (!pkgJsonBuildersentry) {
243-
throw new BuilderCannotBeResolvedException(target.builder);
224+
return _throw(new BuilderCannotBeResolvedException(builderConfig.builder));
244225
}
245226

246227
buildersJsonPath = join(dirname(normalize(pkgJsonPath)), pkgJsonBuildersentry);
247228

248229
return this._loadJsonFile(buildersJsonPath);
249230
}),
250231
// Validate builders json.
251-
concatMap((builderPathsMap) =>
252-
this._validateAgainstSchema<BuilderPathsMap>(builderPathsMap, this._buildersSchema)),
232+
concatMap((builderPathsMap) => this._workspace.validateAgainstSchema<BuilderPathsMap>(
233+
builderPathsMap, this._buildersSchema)),
253234
concatMap((builderPathsMap) => {
254235
builderPaths = builderPathsMap.builders[builderName];
255236

256237
if (!builderPaths) {
257-
throw new BuilderCannotBeResolvedException(target.builder);
238+
return _throw(new BuilderCannotBeResolvedException(builderConfig.builder));
258239
}
259240

260241
// Resolve paths in the builder paths.
@@ -263,14 +244,14 @@ export class Architect {
263244
builderPaths.class = join(builderJsonDir, builderPaths.class);
264245

265246
// Save the builder paths so that we can lazily load the builder.
266-
this._builderPathsMap.set(target.builder, builderPaths);
247+
this._builderPathsMap.set(builderConfig.builder, builderPaths);
267248

268249
// Load the schema.
269250
return this._loadJsonFile(builderPaths.schema);
270251
}),
271252
map(builderSchema => {
272253
const builderDescription = {
273-
name: target.builder,
254+
name: builderConfig.builder,
274255
schema: builderSchema,
275256
description: builderPaths.description,
276257
};
@@ -285,9 +266,17 @@ export class Architect {
285266
}
286267

287268
validateBuilderOptions<OptionsT>(
288-
target: Target<OptionsT>, builderDescription: BuilderDescription,
289-
): Observable<OptionsT> {
290-
return this._validateAgainstSchema<OptionsT>(target.options, builderDescription.schema);
269+
builderConfig: BuilderConfiguration<OptionsT>, builderDescription: BuilderDescription,
270+
): Observable<BuilderConfiguration<OptionsT>> {
271+
return this._workspace.validateAgainstSchema<OptionsT>(
272+
builderConfig.options, builderDescription.schema,
273+
).pipe(
274+
map(validatedOptions => {
275+
builderConfig.options = validatedOptions;
276+
277+
return builderConfig;
278+
}),
279+
);
291280
}
292281

293282
getBuilder<OptionsT>(
@@ -319,25 +308,8 @@ export class Architect {
319308
return builder;
320309
}
321310

322-
// Warning: this method changes contentJson in place.
323-
// TODO: add transforms to resolve paths.
324-
private _validateAgainstSchema<T = {}>(contentJson: {}, schemaJson: JsonObject): Observable<T> {
325-
const registry = new schema.CoreSchemaRegistry();
326-
327-
return registry.compile(schemaJson).pipe(
328-
concatMap(validator => validator(contentJson)),
329-
concatMap(validatorResult => {
330-
if (validatorResult.success) {
331-
return of(contentJson as T);
332-
} else {
333-
return _throw(new SchemaValidationException(validatorResult.errors as string[]));
334-
}
335-
}),
336-
);
337-
}
338-
339311
private _loadJsonFile(path: Path): Observable<JsonObject> {
340-
return this._host.read(normalize(path)).pipe(
312+
return this._workspace.host.read(normalize(path)).pipe(
341313
map(buffer => virtualFs.fileBufferToString(buffer)),
342314
map(str => parseJson(str, JsonParseMode.Loose) as {} as JsonObject),
343315
);

0 commit comments

Comments
 (0)