Skip to content

Commit 929b289

Browse files
committed
feat(@angular-devkit/core): add compileAsync to SchemaRegistry
Introduce promise based method to reduce RXJS usage and boiler-platting. DEPRECATED: The Observable based `SchemaRegistry.compile` has been deprecated in favor of the Promise based `SchemaRegistry.compileAsync`.
1 parent 2c48e84 commit 929b289

File tree

15 files changed

+341
-431
lines changed

15 files changed

+341
-431
lines changed

goldens/public-api/angular_devkit/core/index.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,9 @@ class CoreSchemaRegistry implements SchemaRegistry {
172172
addPreTransform(visitor: JsonVisitor, deps?: JsonVisitor[]): void;
173173
// (undocumented)
174174
addSmartDefaultProvider<T>(source: string, provider: SmartDefaultProvider<T>): void;
175-
compile(schema: JsonSchema): Observable<SchemaValidator>;
175+
// @deprecated
176+
compile(schema: JsonSchema): Observable<SchemaValidatorObservable>;
177+
compileAsync(schema: JsonSchema): Promise<SchemaValidator>;
176178
// @deprecated
177179
flatten(schema: JsonObject): Observable<JsonObject>;
178180
// (undocumented)
@@ -186,6 +188,8 @@ class CoreSchemaRegistry implements SchemaRegistry {
186188
usePromptProvider(provider: PromptProvider): void;
187189
// (undocumented)
188190
useXDeprecatedProvider(onUsage: (message: string) => void): void;
191+
// (undocumented)
192+
ɵflatten(schema: JsonObject): Promise<JsonObject>;
189193
}
190194

191195
// @public (undocumented)
@@ -821,6 +825,7 @@ declare namespace schema {
821825
SchemaValidatorError,
822826
SchemaValidatorOptions,
823827
SchemaValidator,
828+
SchemaValidatorObservable,
824829
SchemaFormatter,
825830
SchemaFormat,
826831
SmartDefaultProvider,
@@ -872,14 +877,18 @@ interface SchemaRegistry {
872877
addPreTransform(visitor: JsonVisitor, deps?: JsonVisitor[]): void;
873878
// (undocumented)
874879
addSmartDefaultProvider<T>(source: string, provider: SmartDefaultProvider<T>): void;
880+
// @deprecated (undocumented)
881+
compile(schema: Object): Observable<SchemaValidatorObservable>;
875882
// (undocumented)
876-
compile(schema: Object): Observable<SchemaValidator>;
883+
compileAsync(schema: Object): Promise<SchemaValidator>;
877884
// @deprecated (undocumented)
878885
flatten(schema: JsonObject | string): Observable<JsonObject>;
879886
// (undocumented)
880887
usePromptProvider(provider: PromptProvider): void;
881888
// (undocumented)
882889
useXDeprecatedProvider(onUsage: (message: string) => void): void;
890+
// (undocumented)
891+
ɵflatten(schema: JsonObject | string): Promise<JsonObject>;
883892
}
884893

885894
// @public (undocumented)
@@ -894,12 +903,18 @@ class SchemaValidationException extends BaseException {
894903
// @public (undocumented)
895904
interface SchemaValidator {
896905
// (undocumented)
897-
(data: JsonValue, options?: SchemaValidatorOptions): Observable<SchemaValidatorResult>;
906+
(data: JsonValue, options?: SchemaValidatorOptions): Promise<SchemaValidatorResult>;
898907
}
899908

900909
// @public (undocumented)
901910
type SchemaValidatorError = Partial<ErrorObject>;
902911

912+
// @public @deprecated (undocumented)
913+
interface SchemaValidatorObservable {
914+
// (undocumented)
915+
(data: JsonValue, options?: SchemaValidatorOptions): Observable<SchemaValidatorResult>;
916+
}
917+
903918
// @public (undocumented)
904919
interface SchemaValidatorOptions {
905920
// (undocumented)

packages/angular/cli/src/command-builder/utilities/json-schema.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
*/
88

99
import { json } from '@angular-devkit/core';
10-
import assert from 'node:assert';
1110
import yargs from 'yargs';
1211

1312
/**
@@ -198,9 +197,7 @@ export async function parseJsonSchemaToOptions(
198197
options.push(option);
199198
}
200199

201-
const flattenedSchema = await registry.flatten(schema).toPromise();
202-
// TODO(RXJS): temporary assert to avoid adding rxjs dependency.
203-
assert(flattenedSchema);
200+
const flattenedSchema = await registry.ɵflatten(schema);
204201
json.schema.visitJsonSchema(flattenedSchema, visitor);
205202

206203
// Sort by positional and name.

packages/angular/cli/src/utilities/config.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
import { json, workspaces } from '@angular-devkit/core';
1010
import { existsSync, promises as fs } from 'fs';
11-
import assert from 'node:assert';
1211
import * as os from 'os';
1312
import * as path from 'path';
1413
import { PackageManager } from '../../lib/config/workspace-schema';
@@ -254,12 +253,8 @@ export async function validateWorkspace(data: json.JsonObject, isGlobal: boolean
254253

255254
const { formats } = await import('@angular-devkit/schematics');
256255
const registry = new json.schema.CoreSchemaRegistry(formats.standardFormats);
257-
const validator = await registry.compile(schemaToValidate).toPromise();
258-
// TODO(RXJS): temporary assert to avoid adding rxjs dependency.
259-
assert(validator);
260-
const result = await validator(data).toPromise();
261-
// TODO(RXJS): temporary assert to avoid adding rxjs dependency.
262-
assert(result);
256+
const validator = await registry.compileAsync(schemaToValidate);
257+
const result = await validator(data);
263258
const { success, errors } = result;
264259
if (!success) {
265260
throw new json.schema.SchemaValidationException(errors);

packages/angular_devkit/architect/src/architect.ts

Lines changed: 19 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,14 @@
77
*/
88

99
import { json, logging } from '@angular-devkit/core';
10-
import {
11-
Observable,
12-
firstValueFrom,
13-
from,
14-
lastValueFrom,
15-
merge,
16-
of,
17-
onErrorResumeNext,
18-
} from 'rxjs';
10+
import { Observable, from, merge, of, onErrorResumeNext } from 'rxjs';
1911
import {
2012
concatMap,
2113
first,
2214
ignoreElements,
2315
last,
2416
map,
2517
shareReplay,
26-
switchMap,
2718
takeUntil,
2819
} from 'rxjs/operators';
2920
import {
@@ -73,7 +64,7 @@ function _createJobHandlerFromBuilderInfo(
7364
function handler(argument: json.JsonObject, context: JobHandlerContext) {
7465
// Add input validation to the inbound bus.
7566
const inboundBusWithInputValidation = context.inboundBus.pipe(
76-
concatMap((message) => {
67+
concatMap(async (message) => {
7768
if (message.kind === JobInboundMessageKind.Input) {
7869
const v = message.value as BuilderInput;
7970
const options = {
@@ -82,20 +73,17 @@ function _createJobHandlerFromBuilderInfo(
8273
};
8374

8475
// Validate v against the options schema.
85-
return registry.compile(info.optionSchema).pipe(
86-
concatMap((validation) => validation(options)),
87-
map((validationResult: json.schema.SchemaValidatorResult) => {
88-
const { data, success, errors } = validationResult;
89-
if (success) {
90-
return { ...v, options: data } as BuilderInput;
91-
}
92-
93-
throw new json.schema.SchemaValidationException(errors);
94-
}),
95-
map((value) => ({ ...message, value })),
96-
);
76+
const validation = await registry.compileAsync(info.optionSchema);
77+
const validationResult = await validation(options);
78+
const { data, success, errors } = validationResult;
79+
80+
if (!success) {
81+
throw new json.schema.SchemaValidationException(errors);
82+
}
83+
84+
return { ...message, value: { ...v, options: data } } as JobInboundMessage<BuilderInput>;
9785
} else {
98-
return of(message as JobInboundMessage<BuilderInput>);
86+
return message as JobInboundMessage<BuilderInput>;
9987
}
10088
}),
10189
// Using a share replay because the job might be synchronously sending input, but
@@ -343,18 +331,14 @@ function _validateOptionsFactory(host: ArchitectHost, registry: json.schema.Sche
343331
throw new Error(`No builder info were found for builder ${JSON.stringify(builderName)}.`);
344332
}
345333

346-
return firstValueFrom(
347-
registry.compile(builderInfo.optionSchema).pipe(
348-
concatMap((validation) => validation(options)),
349-
switchMap(({ data, success, errors }) => {
350-
if (success) {
351-
return of(data as json.JsonObject);
352-
}
334+
const validation = await registry.compileAsync(builderInfo.optionSchema);
335+
const { data, success, errors } = await validation(options);
353336

354-
throw new json.schema.SchemaValidationException(errors);
355-
}),
356-
),
357-
);
337+
if (!success) {
338+
throw new json.schema.SchemaValidationException(errors);
339+
}
340+
341+
return data as json.JsonObject;
358342
},
359343
{
360344
name: '..validateOptions',

packages/angular_devkit/architect/src/jobs/simple-scheduler.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ export class JobOutputSchemaValidationError extends schema.SchemaValidationExcep
6565
interface JobHandlerWithExtra extends JobHandler<JsonValue, JsonValue, JsonValue> {
6666
jobDescription: JobDescription;
6767

68-
argumentV: Observable<schema.SchemaValidator>;
69-
outputV: Observable<schema.SchemaValidator>;
70-
inputV: Observable<schema.SchemaValidator>;
68+
argumentV: Observable<schema.SchemaValidatorObservable>;
69+
outputV: Observable<schema.SchemaValidatorObservable>;
70+
inputV: Observable<schema.SchemaValidatorObservable>;
7171
}
7272

7373
function _jobShare<T>(): MonoTypeOperatorFunction<T> {

packages/angular_devkit/build_angular/src/testing/builder-harness.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ import {
2626
Observable,
2727
Subject,
2828
firstValueFrom,
29-
lastValueFrom,
29+
from,
3030
from as observableFrom,
3131
of as observableOf,
3232
} from 'rxjs';
33-
import { catchError, finalize, first, map, mergeMap, shareReplay } from 'rxjs/operators';
33+
import { catchError, finalize, map, mergeMap, shareReplay } from 'rxjs/operators';
3434
import { BuilderWatcherFactory, WatcherNotifier } from './file-watching';
3535

3636
export interface BuilderHarnessExecutionResult<T extends BuilderOutput = BuilderOutput> {
@@ -214,8 +214,8 @@ export class BuilderHarness<T> {
214214
}
215215
}
216216

217-
const validator = await lastValueFrom(this.schemaRegistry.compile(schema ?? true));
218-
const { data } = await lastValueFrom(validator(options));
217+
const validator = await this.schemaRegistry.compileAsync(schema ?? true);
218+
const { data } = await validator(options);
219219

220220
return data as json.JsonObject;
221221
},
@@ -237,7 +237,7 @@ export class BuilderHarness<T> {
237237
const logs: logging.LogEntry[] = [];
238238
context.logger.subscribe((e) => logs.push(e));
239239

240-
return this.schemaRegistry.compile(this.builderInfo.optionSchema).pipe(
240+
return from(this.schemaRegistry.compileAsync(this.builderInfo.optionSchema)).pipe(
241241
mergeMap((validator) => validator(targetOptions)),
242242
map((validationResult) => validationResult.data),
243243
mergeMap((data) =>

packages/angular_devkit/core/src/json/schema/interface.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ export interface SchemaValidatorOptions {
2828
}
2929

3030
export interface SchemaValidator {
31+
(data: JsonValue, options?: SchemaValidatorOptions): Promise<SchemaValidatorResult>;
32+
}
33+
34+
/**
35+
* @deprecated since 15.2 use the SchemaValidator instead.
36+
*/
37+
export interface SchemaValidatorObservable {
3138
(data: JsonValue, options?: SchemaValidatorOptions): Observable<SchemaValidatorResult>;
3239
}
3340

@@ -72,7 +79,15 @@ export type PromptProvider = (
7279
) => ObservableInput<{ [id: string]: JsonValue }>;
7380

7481
export interface SchemaRegistry {
75-
compile(schema: Object): Observable<SchemaValidator>;
82+
/**
83+
* @deprecated since 15.2 use `compileAsync` instead.
84+
*/
85+
compile(schema: Object): Observable<SchemaValidatorObservable>;
86+
87+
compileAsync(schema: Object): Promise<SchemaValidator>;
88+
89+
/** @private */
90+
ɵflatten(schema: JsonObject | string): Promise<JsonObject>;
7691
/**
7792
* @deprecated since 11.2 without replacement.
7893
* Producing a flatten schema document does not in all cases produce a schema with identical behavior to the original.

packages/angular_devkit/core/src/json/schema/registry.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
SchemaRegistry,
2626
SchemaValidator,
2727
SchemaValidatorError,
28+
SchemaValidatorObservable,
2829
SchemaValidatorOptions,
2930
SchemaValidatorResult,
3031
SmartDefaultProvider,
@@ -231,6 +232,11 @@ export class CoreSchemaRegistry implements SchemaRegistry {
231232
return from(this._flatten(schema));
232233
}
233234

235+
/** @private */
236+
ɵflatten(schema: JsonObject): Promise<JsonObject> {
237+
return this._flatten(schema);
238+
}
239+
234240
private async _flatten(schema: JsonObject): Promise<JsonObject> {
235241
this._ajv.removeSchema(schema);
236242

@@ -273,10 +279,23 @@ export class CoreSchemaRegistry implements SchemaRegistry {
273279
*
274280
* @param schema The schema to validate. If a string, will fetch the schema before compiling it
275281
* (using schema as a URI).
276-
* @returns An Observable of the Validation function.
277282
*/
278-
compile(schema: JsonSchema): Observable<SchemaValidator> {
279-
return from(this._compile(schema)).pipe(
283+
async compileAsync(schema: JsonSchema): Promise<SchemaValidator> {
284+
const validate = await this._compile(schema);
285+
286+
return (value, options) => validate(value, options);
287+
}
288+
289+
/**
290+
* Compile and return a validation function for the Schema.
291+
*
292+
* @param schema The schema to validate. If a string, will fetch the schema before compiling it
293+
* (using schema as a URI).
294+
*
295+
* @deprecated since 15.2 use `compileAsync` instead.
296+
*/
297+
compile(schema: JsonSchema): Observable<SchemaValidatorObservable> {
298+
return from(this.compileAsync(schema)).pipe(
280299
map((validate) => (value, options) => from(validate(value, options))),
281300
);
282301
}

0 commit comments

Comments
 (0)