Skip to content

Commit 68220f1

Browse files
hanslKeen Yee Liau
authored and
Keen Yee Liau
committed
feat(@angular-devkit/architect): New Architect API first draft
The new API has been described in this design doc: https://docs.google.com/document/d/1SpN_2XEooI9_CPjqspAcNEBjVY874OWPZqOenjuF0qo/view This first drafts add support for the API (given some deep imports). It is still in draft mode but is committed to make it available to people to start testing and moving their own builders. This API rebuilds (not backward compatible) the Architect API package. To use it people will need to import "@angular-devkit/architect/src/index2" to start using it. A reference builder will be added in the next commit. There are 2 pieces missing from this commit that will be added in the same PR; 1) the architect-host and CLI to test, and 2) a reference builder moved from the old API to the new one. These will be part of the same PR. Finally, there are missing tests in this package, but everything tested manually and automatically works so far. Test coverage will be added before the package is considered finished. Due to a desire to keep architect, our tests and the scope of this PR limited and keep the two APIs separated, every clashing files will have a "2" suffix added to it. Once all builders have been moved and we are sure everything works, all those files will be moved to their final destination and the old API will be removed, in one PR.
1 parent 7023255 commit 68220f1

17 files changed

+1422
-17
lines changed

packages/angular_devkit/architect/BUILD

+21
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,27 @@ licenses(["notice"]) # MIT
77

88
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
99
load("@build_bazel_rules_nodejs//:defs.bzl", "npm_package")
10+
load("//tools:ts_json_schema.bzl", "ts_json_schema")
1011

1112
package(default_visibility = ["//visibility:public"])
1213

14+
ts_json_schema(
15+
name = "builder_input_schema",
16+
src = "src/input-schema.json",
17+
)
18+
ts_json_schema(
19+
name = "builder_output_schema",
20+
src = "src/output-schema.json",
21+
)
22+
ts_json_schema(
23+
name = "builder_builders_schema",
24+
src = "src/builders-schema.json",
25+
)
26+
ts_json_schema(
27+
name = "progress_schema",
28+
src = "src/progress-schema.json",
29+
)
30+
1331
ts_library(
1432
name = "architect",
1533
srcs = glob(
@@ -29,6 +47,9 @@ ts_library(
2947
"@rxjs",
3048
"@rxjs//operators",
3149
"@npm//@types/node",
50+
":builder_input_schema",
51+
":builder_output_schema",
52+
":progress_schema",
3253
],
3354
)
3455

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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+
export * from './architect-legacy';
9+
// export * from './api';
10+
// export { Architect, ScheduleOptions } from './architect';
11+
// export { createBuilder } from './create-builder';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
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 { experimental, json, logging } from '@angular-devkit/core';
9+
import { Observable } from 'rxjs';
10+
import { Schema as RealBuilderInput, Target as RealTarget } from './input-schema';
11+
import { Schema as RealBuilderOutput } from './output-schema';
12+
import { Schema as RealBuilderProgress, State as BuilderProgressState } from './progress-schema';
13+
14+
export type Target = json.JsonObject & RealTarget;
15+
export {
16+
BuilderProgressState,
17+
};
18+
19+
// Type short hands.
20+
export type BuilderRegistry =
21+
experimental.jobs.Registry<json.JsonObject, BuilderInput, BuilderOutput>;
22+
23+
24+
/**
25+
* An API typed BuilderProgress. The interface generated from the schema is too permissive,
26+
* so this API is the one we show in our API. Please note that not all fields are in there; this
27+
* is in addition to fields in the schema.
28+
*/
29+
export type TypedBuilderProgress = (
30+
{ state: BuilderProgressState.Stopped; }
31+
| { state: BuilderProgressState.Error; error: json.JsonValue; }
32+
| { state: BuilderProgressState.Waiting; status?: string; }
33+
| { state: BuilderProgressState.Running; status?: string; current: number; total?: number; }
34+
);
35+
36+
/**
37+
* Declaration of those types as JsonObject compatible. JsonObject is not compatible with
38+
* optional members, so those wouldn't be directly assignable to our internal Json typings.
39+
* Forcing the type to be both a JsonObject and the type from the Schema tells Typescript they
40+
* are compatible (which they are).
41+
* These types should be used everywhere.
42+
*/
43+
export type BuilderInput = json.JsonObject & RealBuilderInput;
44+
export type BuilderOutput = json.JsonObject & RealBuilderOutput;
45+
export type BuilderProgress = json.JsonObject & RealBuilderProgress & TypedBuilderProgress;
46+
47+
/**
48+
* A progress report is what the tooling will receive. It contains the builder info and the target.
49+
* Although these are serializable, they are only exposed through the tooling interface, not the
50+
* builder interface. The watch dog sends BuilderProgress and the Builder has a set of functions
51+
* to manage the state.
52+
*/
53+
export type BuilderProgressReport = BuilderProgress & ({
54+
target?: Target;
55+
builder: BuilderInfo;
56+
});
57+
58+
/**
59+
* A Run, which is what is returned by scheduleBuilder or scheduleTarget functions. This should
60+
* be reconstructed across memory boundaries (it's not serializable but all internal information
61+
* are).
62+
*/
63+
export interface BuilderRun {
64+
/**
65+
* Unique amongst runs. This is the same ID as the context generated for the run. It can be
66+
* used to identify multiple unique runs. There is no guarantee that a run is a single output;
67+
* a builder can rebuild on its own and will generate multiple outputs.
68+
*/
69+
id: number;
70+
71+
/**
72+
* The builder information.
73+
*/
74+
info: BuilderInfo;
75+
76+
/**
77+
* The next output from a builder. This is recommended when scheduling a builder and only being
78+
* interested in the result of that single run, not of a watch-mode builder.
79+
*/
80+
result: Promise<BuilderOutput>;
81+
82+
/**
83+
* The output(s) from the builder. A builder can have multiple outputs.
84+
* This always replay the last output when subscribed.
85+
*/
86+
output: Observable<BuilderOutput>;
87+
88+
/**
89+
* The progress report. A progress also contains an ID, which can be different than this run's
90+
* ID (if the builder calls scheduleBuilder or scheduleTarget).
91+
* This will always replay the last progress on new subscriptions.
92+
*/
93+
progress: Observable<BuilderProgressReport>;
94+
95+
/**
96+
* Stop the builder from running. Returns a promise that resolves when the builder is stopped.
97+
* Some builders might not handle stopping properly and should have a timeout here.
98+
*/
99+
stop(): Promise<void>;
100+
}
101+
102+
/**
103+
* The context received as a second argument in your builder.
104+
*/
105+
export interface BuilderContext {
106+
/**
107+
* Unique amongst contexts. Contexts instances are not guaranteed to be the same (but it could
108+
* be the same context), and all the fields in a context could be the same, yet the builder's
109+
* context could be different. This is the same ID as the corresponding run.
110+
*/
111+
id: number;
112+
113+
/**
114+
* The builder info that called your function. Since the builder info is from the builder.json
115+
* (or the host), it could contain information that is different than expected.
116+
*/
117+
builder: BuilderInfo;
118+
119+
/**
120+
* A logger that appends messages to a log. This could be a separate interface or completely
121+
* ignored. `console.log` could also be completely ignored.
122+
*/
123+
logger: logging.LoggerApi;
124+
125+
/**
126+
* The absolute workspace root of this run. This is a system path and will not be normalized;
127+
* ie. on Windows it will starts with `C:\\` (or whatever drive).
128+
*/
129+
workspaceRoot: string;
130+
131+
/**
132+
* The current directory the user is in. This could be outside the workspace root. This is a
133+
* system path and will not be normalized; ie. on Windows it will starts with `C:\\` (or
134+
* whatever drive).
135+
*/
136+
currentDirectory: string;
137+
138+
/**
139+
* The target that was used to run this builder.
140+
* Target is optional if a builder was ran using `scheduleBuilder()`.
141+
*/
142+
target?: Target;
143+
144+
/**
145+
* Schedule a target in the same workspace. This can be the same target that is being executed
146+
* right now, but targets of the same name are serialized.
147+
* Running the same target and waiting for it to end will result in a deadlocking scenario.
148+
* Targets are considered the same if the project, the target AND the configuration are the same.
149+
* @param target The target to schedule.
150+
* @param overrides A set of options to override the workspace set of options.
151+
* @return A promise of a run. It will resolve when all the members of the run are available.
152+
*/
153+
scheduleTarget(
154+
target: Target,
155+
overrides?: json.JsonObject,
156+
): Promise<BuilderRun>;
157+
158+
/**
159+
* Schedule a builder by its name. This can be the same builder that is being executed.
160+
* @param builderName The name of the builder, ie. its `packageName:builderName` tuple.
161+
* @param options All options to use for the builder (by default empty object). There is no
162+
* additional options added, e.g. from the workspace.
163+
* @return A promise of a run. It will resolve when all the members of the run are available.
164+
*/
165+
scheduleBuilder(
166+
builderName: string,
167+
options?: json.JsonObject,
168+
): Promise<BuilderRun>;
169+
170+
/**
171+
* Set the builder to running. This should be used if an external event triggered a re-run,
172+
* e.g. a file watched was changed.
173+
*/
174+
reportRunning(): void;
175+
176+
/**
177+
* Update the status string shown on the interface.
178+
* @param status The status to set it to. An empty string can be used to remove the status.
179+
*/
180+
reportStatus(status: string): void;
181+
182+
/**
183+
* Update the progress for this builder run.
184+
* @param current The current progress. This will be between 0 and total.
185+
* @param total A new total to set. By default at the start of a run this is 1. If omitted it
186+
* will use the same value as the last total.
187+
* @param status Update the status string. If omitted the status string is not modified.
188+
*/
189+
reportProgress(current: number, total?: number, status?: string): void;
190+
}
191+
192+
193+
/**
194+
* An accepted return value from a builder. Can be either an Observable, a Promise or a vector.
195+
*/
196+
export type BuilderOutputLike = Observable<BuilderOutput> | Promise<BuilderOutput> | BuilderOutput;
197+
198+
199+
/**
200+
* A builder handler function. The function signature passed to `createBuilder()`.
201+
*/
202+
export interface BuilderHandlerFn<A extends json.JsonObject> {
203+
/**
204+
* Builders are defined by users to perform any kind of task, like building, testing or linting,
205+
* and should use this interface.
206+
* @param input The options (a JsonObject), validated by the schema and received by the
207+
* builder. This can include resolved options from the CLI or the workspace.
208+
* @param context A context that can be used to interact with the Architect framework.
209+
* @return One or many builder output.
210+
*/
211+
(input: A, context: BuilderContext): BuilderOutputLike;
212+
}
213+
214+
/**
215+
* A Builder general information. This is generated by the host and is expanded by the host, but
216+
* the public API contains those fields.
217+
*/
218+
export type BuilderInfo = json.JsonObject & {
219+
builderName: string;
220+
description: string;
221+
optionSchema: json.schema.JsonSchema;
222+
};
223+
224+
225+
/**
226+
* Returns a string of "project:target[:configuration]" for the target object.
227+
*/
228+
export function targetStringFromTarget({project, target, configuration}: Target) {
229+
return `${project}:${target}${configuration !== undefined ? ':' + configuration : ''}`;
230+
}

0 commit comments

Comments
 (0)