From 08753138d336fb870a66face70f5624ba64b4c69 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Sun, 11 Apr 2021 17:33:08 +0200 Subject: [PATCH] feat(@angular-devkit/core): update schema validator With this change we update ajv to version 8 BREAKING CHANGE: support for JSON Schema draft-04 and draft-06 is removed. If you have schemas using the `id` keyword replace them with `$id`. For an interim period we will auto rename any top level `id` keyword to `$id`. **NB**: This change only effects schematics and builders authors. --- .../angular_devkit/core/src/_golden-api.d.ts | 53 +--- package.json | 3 +- packages/angular_devkit/core/BUILD.bazel | 1 + packages/angular_devkit/core/package.json | 3 +- .../core/src/json/schema/interface.ts | 50 +--- .../core/src/json/schema/registry.ts | 246 +++++++----------- .../core/src/json/schema/registry_spec.ts | 11 +- .../schematics/src/formats/html-selector.ts | 2 +- .../e2e/tests/generate/help-output.ts | 2 +- yarn.lock | 25 +- 10 files changed, 140 insertions(+), 256 deletions(-) diff --git a/etc/api/angular_devkit/core/src/_golden-api.d.ts b/etc/api/angular_devkit/core/src/_golden-api.d.ts index 1f0a0250dd0d..10390f0ff530 100644 --- a/etc/api/angular_devkit/core/src/_golden-api.d.ts +++ b/etc/api/angular_devkit/core/src/_golden-api.d.ts @@ -1,10 +1,3 @@ -export interface AdditionalPropertiesValidatorError extends SchemaValidatorErrorBase { - keyword: 'additionalProperties'; - params: { - additionalProperty: string; - }; -} - export declare function addUndefinedDefaults(value: JsonValue, _pointer: JsonPointer, schema?: JsonSchema): JsonValue; export declare class AliasHost extends ResolverHost { @@ -157,8 +150,8 @@ export interface CordHostRename { export declare class CoreSchemaRegistry implements SchemaRegistry { constructor(formats?: SchemaFormat[]); - protected _resolver(ref: string, validate?: ajv.ValidateFunction): { - context?: ajv.ValidateFunction; + protected _resolver(ref: string, validate?: ValidateFunction): { + context?: ValidateFunction; schema?: JsonObject; }; addFormat(format: SchemaFormat): void; @@ -228,13 +221,6 @@ export declare class FileDoesNotExistException extends BaseException { constructor(path: string); } -export interface FormatValidatorError extends SchemaValidatorErrorBase { - keyword: 'format'; - params: { - format: string; - }; -} - export declare class ForwardingAnalytics implements Analytics { protected _fn: AnalyticsForwarderFn; constructor(_fn: AnalyticsForwarderFn); @@ -451,13 +437,6 @@ export declare class LevelTransformLogger extends Logger { export declare function levenshtein(a: string, b: string): number; -export interface LimitValidatorError extends SchemaValidatorErrorBase { - keyword: 'maxItems' | 'minItems' | 'maxLength' | 'minLength' | 'maxProperties' | 'minProperties'; - params: { - limit: number; - }; -} - export interface LogEntry extends LoggerMetadata { level: LogLevel; message: string; @@ -746,24 +725,10 @@ export interface ReferenceResolver { }; } -export interface RefValidatorError extends SchemaValidatorErrorBase { - keyword: '$ref'; - params: { - ref: string; - }; -} - export declare function relative(from: Path, to: Path): Path; export declare type ReplacementFunction = (path: Path) => Path; -export interface RequiredValidatorError extends SchemaValidatorErrorBase { - keyword: 'required'; - params: { - missingProperty: string; - }; -} - export declare function resetNormalizeCache(): void; export declare function resolve(p1: Path, p2: Path): Path; @@ -801,10 +766,7 @@ export interface SchemaFormat { name: string; } -export interface SchemaFormatter { - readonly async: boolean; - validate(data: any): boolean | Observable; -} +export declare type SchemaFormatter = Format; export interface SchemaKeywordValidator { (data: JsonValue, schema: JsonValue, parent: JsonObject | JsonArray | undefined, parentProperty: string | number | undefined, pointer: JsonPointer, rootData: JsonValue): boolean | Observable; @@ -831,14 +793,7 @@ export interface SchemaValidator { (data: JsonValue, options?: SchemaValidatorOptions): Observable; } -export declare type SchemaValidatorError = RefValidatorError | LimitValidatorError | AdditionalPropertiesValidatorError | FormatValidatorError | RequiredValidatorError; - -export interface SchemaValidatorErrorBase { - data?: JsonValue; - dataPath: string; - keyword: string; - message?: string; -} +export declare type SchemaValidatorError = ErrorObject; export interface SchemaValidatorOptions { applyPostTransforms?: boolean; diff --git a/package.json b/package.json index b644849dfef6..647012fa8ba8 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,8 @@ "@types/webpack-dev-server": "^3.1.7", "@types/webpack-sources": "^2.0.0", "@yarnpkg/lockfile": "1.1.0", - "ajv": "6.12.6", + "ajv": "8.1.0", + "ajv-formats": "2.0.2", "ansi-colors": "4.1.1", "babel-loader": "8.2.2", "bootstrap": "^4.0.0", diff --git a/packages/angular_devkit/core/BUILD.bazel b/packages/angular_devkit/core/BUILD.bazel index 7e26ecdb4fc2..d9f09172b1ca 100644 --- a/packages/angular_devkit/core/BUILD.bazel +++ b/packages/angular_devkit/core/BUILD.bazel @@ -37,6 +37,7 @@ ts_library( deps = [ "@npm//@types/node", "@npm//ajv", + "@npm//ajv-formats", "@npm//fast-json-stable-stringify", "@npm//magic-string", "@npm//rxjs", diff --git a/packages/angular_devkit/core/package.json b/packages/angular_devkit/core/package.json index d03c7f3a0366..c24f1da0bf45 100644 --- a/packages/angular_devkit/core/package.json +++ b/packages/angular_devkit/core/package.json @@ -8,7 +8,8 @@ "core" ], "dependencies": { - "ajv": "6.12.6", + "ajv": "8.1.0", + "ajv-formats": "2.0.2", "fast-json-stable-stringify": "2.1.0", "magic-string": "0.25.7", "rxjs": "6.6.7", diff --git a/packages/angular_devkit/core/src/json/schema/interface.ts b/packages/angular_devkit/core/src/json/schema/interface.ts index 88c644c5ce46..42c581e8d026 100644 --- a/packages/angular_devkit/core/src/json/schema/interface.ts +++ b/packages/angular_devkit/core/src/json/schema/interface.ts @@ -5,58 +5,21 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import { ErrorObject, Format } from 'ajv'; import { Observable, SubscribableOrPromise } from 'rxjs'; import { JsonArray, JsonObject, JsonValue } from '../interface'; export type JsonPointer = string & { __PRIVATE_DEVKIT_JSON_POINTER: void; }; - -export type SchemaValidatorError = - RefValidatorError | - LimitValidatorError | - AdditionalPropertiesValidatorError | - FormatValidatorError | - RequiredValidatorError; - -export interface SchemaValidatorErrorBase { - keyword: string; - dataPath: string; - message?: string; - data?: JsonValue; -} - -export interface RefValidatorError extends SchemaValidatorErrorBase { - keyword: '$ref'; - params: { ref: string }; -} - -export interface LimitValidatorError extends SchemaValidatorErrorBase { - keyword: 'maxItems' | 'minItems' | 'maxLength' | 'minLength' | 'maxProperties' | 'minProperties'; - params: { limit: number }; -} - -export interface AdditionalPropertiesValidatorError extends SchemaValidatorErrorBase { - keyword: 'additionalProperties'; - params: { additionalProperty: string }; -} - -export interface FormatValidatorError extends SchemaValidatorErrorBase { - keyword: 'format'; - params: { format: string }; -} - -export interface RequiredValidatorError extends SchemaValidatorErrorBase { - keyword: 'required'; - params: { missingProperty: string }; -} - export interface SchemaValidatorResult { data: JsonValue; success: boolean; errors?: SchemaValidatorError[]; } +export type SchemaValidatorError = ErrorObject; + export interface SchemaValidatorOptions { applyPreTransforms?: boolean; applyPostTransforms?: boolean; @@ -67,12 +30,7 @@ export interface SchemaValidator { (data: JsonValue, options?: SchemaValidatorOptions): Observable; } -export interface SchemaFormatter { - readonly async: boolean; - // TODO should be unknown remove before next major release - // tslint:disable-next-line:no-any - validate(data: any): boolean | Observable; -} +export type SchemaFormatter = Format; export interface SchemaFormat { name: string; diff --git a/packages/angular_devkit/core/src/json/schema/registry.ts b/packages/angular_devkit/core/src/json/schema/registry.ts index b3cf4db0a1cd..1b4bd474373a 100644 --- a/packages/angular_devkit/core/src/json/schema/registry.ts +++ b/packages/angular_devkit/core/src/json/schema/registry.ts @@ -5,7 +5,8 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import * as ajv from 'ajv'; +import Ajv, { ValidateFunction } from 'ajv'; +import ajvAddFormats from 'ajv-formats'; import * as http from 'http'; import * as https from 'https'; import { Observable, from, isObservable } from 'rxjs'; @@ -20,7 +21,6 @@ import { PromptDefinition, PromptProvider, SchemaFormat, - SchemaFormatter, SchemaRegistry, SchemaValidator, SchemaValidatorError, @@ -32,20 +32,6 @@ import { JsonSchema } from './schema'; import { getTypesOfSchema } from './utility'; import { visitJson, visitJsonSchema } from './visitor'; -// This interface should be exported from ajv, but they only export the class and not the type. -interface AjvValidationError { - message: string; - errors: Array; - ajv: true; - validation: true; -} - -interface AjvRefMap { - refs: string[]; - refVal: any; // tslint:disable-line:no-any - schema: JsonObject; -} - export type UriHandler = (uri: string) => Observable | Promise | null | undefined; @@ -74,7 +60,7 @@ export class SchemaValidationException extends BaseException { } const messages = errors.map((err) => { - let message = `Data path ${JSON.stringify(err.dataPath)} ${err.message}`; + let message = `Data path ${JSON.stringify(err.instancePath)} ${err.message}`; if (err.keyword === 'additionalProperties') { message += `(${err.params.additionalProperty})`; } @@ -92,7 +78,7 @@ interface SchemaInfo { } export class CoreSchemaRegistry implements SchemaRegistry { - private _ajv: ajv.Ajv; + private _ajv: Ajv; private _uriCache = new Map(); private _uriHandlers = new Set(); private _pre = new PartiallyOrderedSet(); @@ -105,25 +91,17 @@ export class CoreSchemaRegistry implements SchemaRegistry { private _sourceMap = new Map>(); constructor(formats: SchemaFormat[] = []) { - /** - * Build an AJV instance that will be used to validate schemas. - */ - - const formatsObj: { [name: string]: SchemaFormatter } = {}; - - for (const format of formats) { - formatsObj[format.name] = format.formatter; - } - - this._ajv = ajv({ - formats: formatsObj, + this._ajv = new Ajv({ + strict: false, loadSchema: (uri: string) => this._fetch(uri), - schemaId: 'auto', passContext: true, }); - this._ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); - this._ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json')); + ajvAddFormats(this._ajv); + + for (const format of formats) { + this.addFormat(format); + } } private async _fetch(uri: string): Promise { @@ -201,42 +179,33 @@ export class CoreSchemaRegistry implements SchemaRegistry { protected _resolver( ref: string, - validate?: ajv.ValidateFunction, - ): { context?: ajv.ValidateFunction, schema?: JsonObject } { - if (!validate || !validate.refs || !validate.refVal || !ref) { + validate?: ValidateFunction, + ): { context?: ValidateFunction, schema?: JsonObject } { + if (!validate || !ref) { return {}; } - let refMap = validate as AjvRefMap; - const rootRefMap = validate.root as AjvRefMap; - - // Resolve from the root if it's different. - if (validate.root && validate.schema !== rootRefMap.schema) { - refMap = rootRefMap; - } + const schema = validate.schemaEnv.root.schema; + const id = typeof schema === 'object' ? schema.$id : null; - const schema = refMap.schema ? typeof refMap.schema == 'object' && refMap.schema : null; - const maybeId = schema ? (schema as JsonObject).id || (schema as JsonObject).$id : null; + let fullReference = ref; + if (typeof id === 'string') { + fullReference = Url.resolve(id, ref); - if (typeof maybeId == 'string') { - ref = Url.resolve(maybeId, ref); + if (ref.startsWith('#')) { + fullReference = id + fullReference; + } } - let fullReference = (ref[0] === '#' && maybeId) ? maybeId + ref : ref; - if (fullReference.endsWith('#')) { + if (fullReference.startsWith('#')) { fullReference = fullReference.slice(0, -1); } + const resolvedSchema = this._ajv.getSchema(fullReference); - // tslint:disable-next-line:no-any - const context = validate.refVal[(validate.refs as any)[fullReference]]; - - if (typeof context == 'function') { - // Context will be a function if the schema isn't loaded yet, and an actual schema if it's - // synchronously available. - return { context, schema: context && context.schema as JsonObject }; - } else { - return { context: validate, schema: context as JsonObject }; - } + return { + context: resolvedSchema?.schemaEnv.validate, + schema: resolvedSchema?.schema as JsonObject, + }; } /** @@ -254,6 +223,7 @@ export class CoreSchemaRegistry implements SchemaRegistry { } private async _flatten(schema: JsonObject): Promise { + this._replaceDeprecatedSchemaIdKeyword(schema); this._ajv.removeSchema(schema); this._currentCompilationSchemaInfo = undefined; @@ -301,7 +271,14 @@ export class CoreSchemaRegistry implements SchemaRegistry { ); } - private async _compile(schema: JsonSchema) { + private async _compile(schema: JsonSchema): + Promise<(data: JsonValue, options?: SchemaValidatorOptions) => Promise> { + if (typeof schema === 'boolean') { + return async data => ({ success: schema, data }); + } + + this._replaceDeprecatedSchemaIdKeyword(schema); + const schemaInfo: SchemaInfo = { smartDefaultRecord: new Map(), promptDefinitions: [], @@ -309,7 +286,7 @@ export class CoreSchemaRegistry implements SchemaRegistry { this._ajv.removeSchema(schema); - let validator: ajv.ValidateFunction; + let validator: ValidateFunction; try { this._currentCompilationSchemaInfo = schemaInfo; validator = await this._ajv.compileAsync(schema); @@ -317,7 +294,7 @@ export class CoreSchemaRegistry implements SchemaRegistry { this._currentCompilationSchemaInfo = undefined; } - const validate = async (data: JsonValue, options?: SchemaValidatorOptions) => { + return async (data: JsonValue, options?: SchemaValidatorOptions) => { const validationOptions: SchemaValidatorOptions = { withPrompts: true, applyPostTransforms: true, @@ -331,7 +308,7 @@ export class CoreSchemaRegistry implements SchemaRegistry { // Apply pre-validation transforms if (validationOptions.applyPreTransforms) { for (const visitor of this._pre.values()) { - data = await visitJson(data, visitor, schema, this._resolver, validator).toPromise(); + data = await visitJson(data, visitor, schema, this._resolver.bind(this), validator).toPromise(); } } @@ -348,7 +325,7 @@ export class CoreSchemaRegistry implements SchemaRegistry { return value; }; if (typeof schema === 'object') { - await visitJson(data, visitor, schema, this._resolver, validator).toPromise(); + await visitJson(data, visitor, schema, this._resolver.bind(this), validator).toPromise(); } const definitions = schemaInfo.promptDefinitions @@ -360,58 +337,24 @@ export class CoreSchemaRegistry implements SchemaRegistry { } // Validate using ajv - const result = validator.call(validationContext, data); - let errors; - if (typeof result === 'boolean') { - // Synchronous result - if (!result) { - errors = validator.errors || []; - } - } else { - // Asynchronous result - try { - await result; - } catch (e) { - if ((e as AjvValidationError).ajv) { - errors = (e as AjvValidationError).errors || []; - } else { - throw e; - } - } - } - - if (errors) { - return { data, success: false, errors } as SchemaValidatorResult; + const success = await validator.call(validationContext, data); + if (!success) { + return { data, success, errors: validator.errors ?? [] }; } // Apply post-validation transforms if (validationOptions.applyPostTransforms) { for (const visitor of this._post.values()) { - data = await visitJson(data, visitor, schema, this._resolver, validator).toPromise(); + data = await visitJson(data, visitor, schema, this._resolver.bind(this), validator).toPromise(); } } - return { data, success: true } as SchemaValidatorResult; + return { data, success: true }; }; - - return validate; } addFormat(format: SchemaFormat): void { - const validate = (data: unknown) => { - const result = format.formatter.validate(data); - - if (typeof result == 'boolean') { - return result; - } else { - return result.toPromise(); - } - }; - - this._ajv.addFormat(format.name, { - async: format.formatter.async, - validate, - }); + this._ajv.addFormat(format.name, format.formatter); } addSmartDefaultProvider(source: string, provider: SmartDefaultProvider) { @@ -424,7 +367,8 @@ export class CoreSchemaRegistry implements SchemaRegistry { if (!this._smartDefaultKeyword) { this._smartDefaultKeyword = true; - this._ajv.addKeyword('$default', { + this._ajv.addKeyword({ + keyword: '$default', errors: false, valid: true, compile: (schema, _parentSchema, it) => { @@ -434,9 +378,12 @@ export class CoreSchemaRegistry implements SchemaRegistry { } // We cheat, heavily. + const pathArray = it.dataPathArr + .slice(1, it.dataLevel + 1) + .map(p => typeof p === 'number' ? p : p.str.slice(1, -1)); + compilationSchemInfo.smartDefaultRecord.set( - // tslint:disable-next-line:no-any - JSON.stringify((it as any).dataPathArr.slice(1, (it as any).dataLevel + 1) as string[]), + JSON.stringify(pathArray), schema, ); @@ -448,7 +395,7 @@ export class CoreSchemaRegistry implements SchemaRegistry { '$source': { type: 'string' }, }, additionalProperties: true, - required: [ '$source' ], + required: ['$source'], }, }); } @@ -467,7 +414,8 @@ export class CoreSchemaRegistry implements SchemaRegistry { return; } - this._ajv.addKeyword('x-prompt', { + this._ajv.addKeyword({ + keyword: 'x-prompt', errors: false, valid: true, compile: (schema, parentSchema, it) => { @@ -476,9 +424,9 @@ export class CoreSchemaRegistry implements SchemaRegistry { return () => true; } - // tslint:disable-next-line:no-any - const pathArray = ((it as any).dataPathArr as string[]).slice(1, it.dataLevel + 1); - const path = '/' + pathArray.map(p => p.replace(/^\'/, '').replace(/\'$/, '')).join('/'); + const path = '/' + it.dataPathArr + .slice(1, it.dataLevel + 1) + .map(p => typeof p === 'number' ? p : p.str.slice(1, -1)).join('/'); let type: string | undefined; let items: Array | undefined; @@ -543,8 +491,8 @@ export class CoreSchemaRegistry implements SchemaRegistry { propertyTypes, default: typeof (parentSchema as JsonObject).default == 'object' && - (parentSchema as JsonObject).default !== null && - !Array.isArray((parentSchema as JsonObject).default) + (parentSchema as JsonObject).default !== null && + !Array.isArray((parentSchema as JsonObject).default) ? undefined : (parentSchema as JsonObject).default as string[], async validator(data: JsonValue) { @@ -570,7 +518,7 @@ export class CoreSchemaRegistry implements SchemaRegistry { compilationSchemInfo.promptDefinitions.push(definition); - return function(this: { promptFieldsWithValue: Set }) { + return function (this: { promptFieldsWithValue: Set }) { // If 'this' is undefined in the call, then it defaults to the global // 'this'. if (this && this.promptFieldsWithValue) { @@ -590,7 +538,7 @@ export class CoreSchemaRegistry implements SchemaRegistry { 'message': { type: 'string' }, }, additionalProperties: true, - required: [ 'message' ], + required: ['message'], }, ], }, @@ -605,17 +553,11 @@ export class CoreSchemaRegistry implements SchemaRegistry { const answers = await from(provider(prompts)).toPromise(); for (const path in answers) { - const pathFragments = path.split('/').map(pf => { - if (/^\d+$/.test(pf)) { - return pf; - } else { - return '\'' + pf + '\''; - } - }); + const pathFragments = path.split('/').slice(1); CoreSchemaRegistry._set( data, - pathFragments.slice(1), + pathFragments, answers[path] as {}, null, undefined, @@ -647,36 +589,29 @@ export class CoreSchemaRegistry implements SchemaRegistry { } return; - } else if (f.startsWith('key')) { + } + + if (f.startsWith('key')) { if (typeof data !== 'object') { return; } - Object.getOwnPropertyNames(data).forEach(property => { + for (const property in data) { CoreSchemaRegistry._set(data[property], fragments.slice(i + 1), value, data, property); - }); - - return; - } else if (f.startsWith('\'') && f[f.length - 1] == '\'') { - const property = f - .slice(1, -1) - .replace(/\\'/g, '\'') - .replace(/\\n/g, '\n') - .replace(/\\r/g, '\r') - .replace(/\\f/g, '\f') - .replace(/\\t/g, '\t'); - - // We know we need an object because the fragment is a property key. - if (!data && parent !== null && parentProperty) { - data = parent[parentProperty] = {}; } - parent = data; - parentProperty = property; - data = data[property]; - } else { return; } + + + // We know we need an object because the fragment is a property key. + if (!data && parent !== null && parentProperty) { + data = parent[parentProperty] = {}; + } + parent = data; + parentProperty = f; + + data = data[f]; } if (parent && parentProperty && (force || parent[parentProperty] === undefined)) { @@ -705,10 +640,11 @@ export class CoreSchemaRegistry implements SchemaRegistry { } useXDeprecatedProvider(onUsage: (message: string) => void): void { - this._ajv.addKeyword('x-deprecated', { - validate: (schema, _data, _parentSchema, _dataPath, _parentDataObject, propertyName) => { + this._ajv.addKeyword({ + keyword: 'x-deprecated', + validate: (schema, _data, _parentSchema, dataCxt) => { if (schema) { - onUsage(`Option "${propertyName}" is deprecated${typeof schema == 'string' ? ': ' + schema : '.'}`); + onUsage(`Option "${dataCxt?.parentDataProperty}" is deprecated${typeof schema == 'string' ? ': ' + schema : '.'}`); } return true; @@ -716,4 +652,18 @@ export class CoreSchemaRegistry implements SchemaRegistry { errors: false, }); } + + /** + * Workaround to avoid a breaking change in downstream schematics. + * @deprecated will be removed in version 13. + */ + private _replaceDeprecatedSchemaIdKeyword(schema: JsonObject): void { + if (typeof schema.id === 'string') { + schema.$id = schema.id; + delete schema.id; + + // tslint:disable-next-line:no-console + console.warn(`"${schema.$id}" schema is using the keyword "id" which its support is deprecated. Use "$id" for schema ID.`); + } + } } diff --git a/packages/angular_devkit/core/src/json/schema/registry_spec.ts b/packages/angular_devkit/core/src/json/schema/registry_spec.ts index 213e1d6a59cd..5790bdce095a 100644 --- a/packages/angular_devkit/core/src/json/schema/registry_spec.ts +++ b/packages/angular_devkit/core/src/json/schema/registry_spec.ts @@ -8,6 +8,7 @@ // tslint:disable:no-any non-null-operator no-big-function import { of as observableOf } from 'rxjs'; import { map, mergeMap } from 'rxjs/operators'; +import { SchemaFormat } from './interface'; import { CoreSchemaRegistry } from './registry'; import { addUndefinedDefaults } from './transforms'; @@ -162,7 +163,6 @@ describe('CoreSchemaRegistry', () => { const format = { name: 'is-hotdog', formatter: { - async: false, validate: (str: string) => str === 'hotdog', }, }; @@ -187,11 +187,12 @@ describe('CoreSchemaRegistry', () => { it('supports async format', done => { const registry = new CoreSchemaRegistry(); const data = { str: 'hotdog' }; - const format = { + + const format: SchemaFormat = { name: 'is-hotdog', formatter: { async: true, - validate: (str: string) => observableOf(str === 'hotdog'), + validate: async (str: string) => str === 'hotdog', }, }; @@ -216,7 +217,7 @@ describe('CoreSchemaRegistry', () => { it('shows dataPath and message on error', done => { const registry = new CoreSchemaRegistry(); const data = { hotdot: 'hotdog', banana: 'banana' }; - const format = { + const format: SchemaFormat = { name: 'is-hotdog', formatter: { async: false, @@ -239,7 +240,7 @@ describe('CoreSchemaRegistry', () => { expect(result.success).toBe(false); expect(result.errors && result.errors[0]).toBeTruthy(); expect(result.errors && result.errors[0].keyword).toBe('format'); - expect(result.errors && result.errors[0].dataPath).toBe('.banana'); + expect(result.errors && result.errors[0].instancePath).toBe('.banana'); expect(result.errors && (result.errors[0].params as any).format).toBe('is-hotdog'); }), ) diff --git a/packages/angular_devkit/schematics/src/formats/html-selector.ts b/packages/angular_devkit/schematics/src/formats/html-selector.ts index b048482d2eb7..dbea9afbb627 100644 --- a/packages/angular_devkit/schematics/src/formats/html-selector.ts +++ b/packages/angular_devkit/schematics/src/formats/html-selector.ts @@ -46,6 +46,6 @@ export const htmlSelectorFormat: schema.SchemaFormat = { name: 'html-selector', formatter: { async: false, - validate: name => typeof name === 'string' && isValidElementName(name), + validate: (name: unknown) => typeof name === 'string' && isValidElementName(name), }, }; diff --git a/tests/legacy-cli/e2e/tests/generate/help-output.ts b/tests/legacy-cli/e2e/tests/generate/help-output.ts index 7fc079729562..22b0e8a397e0 100644 --- a/tests/legacy-cli/e2e/tests/generate/help-output.ts +++ b/tests/legacy-cli/e2e/tests/generate/help-output.ts @@ -27,7 +27,7 @@ export default function() { }`, [join(genRoot, 'fake-schema.json')]: ` { - "id": "FakeSchema", + "$id": "FakeSchema", "title": "Fake Schema", "type": "object", "properties": { diff --git a/yarn.lock b/yarn.lock index 3f87a2ca4d1d..e44b64e64ad5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2455,12 +2455,29 @@ ajv-errors@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== +ajv-formats@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.0.2.tgz#69875cb99d76c74be46e9c7a4444bc232354eba0" + integrity sha512-Brah4Uo5/U8v76c6euTwtjVFFaVishwnJrQBYpev1JRh4vjA1F4HY3UzQez41YUCszUCXKagG8v6eVRBHV1gkw== + dependencies: + ajv "^8.0.0" + ajv-keywords@^3.1.0, ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@6.12.6, ajv@^6.1.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: +ajv@8.1.0, ajv@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.1.0.tgz#45d5d3d36c7cdd808930cc3e603cf6200dbeb736" + integrity sha512-B/Sk2Ix7A36fs/ZkuGLIR86EdjbgR6fsAcbx9lOP/QBSXujDNbVmIS/U4Itz5k8fPFDeVZl/zQ/gJW4Jrq6XjQ== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ajv@^6.1.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2471,9 +2488,9 @@ ajv@6.12.6, ajv@^6.1.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: uri-js "^4.2.2" ajv@^7.0.3: - version "7.1.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.1.0.tgz#f982ea7933dc7f1012eae9eec5a86687d805421b" - integrity sha512-svS9uILze/cXbH0z2myCK2Brqprx/+JJYK5pHicT/GQiBfzzhUVAIT6MwqJg8y4xV/zoGsUeuPuwtoiKSGE15g== + version "7.2.4" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.2.4.tgz#8e239d4d56cf884bccca8cca362f508446dc160f" + integrity sha512-nBeQgg/ZZA3u3SYxyaDvpvDtgZ/EZPF547ARgZBrG9Bhu1vKDwAIjtIf+sDtJUKa2zOcEbmRLBRSyMraS/Oy1A== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0"