diff --git a/packages/angular_devkit/core/src/experimental/jobs/simple-scheduler_spec.ts b/packages/angular_devkit/core/src/experimental/jobs/simple-scheduler_spec.ts index a3ddbe628c2c..4973f96531eb 100644 --- a/packages/angular_devkit/core/src/experimental/jobs/simple-scheduler_spec.ts +++ b/packages/angular_devkit/core/src/experimental/jobs/simple-scheduler_spec.ts @@ -102,7 +102,7 @@ describe('SimpleScheduler', () => { // is released, otherwise this breaks because bazel downgrade to ES5 which does not support // extending Error. // expect(e instanceof JobInboundMessageSchemaValidationError).toBe(true); - expect(e.message).toMatch(/"\[0\]".*number/); + expect(e.message).toMatch(/"\/0" must be number/); } }); diff --git a/packages/angular_devkit/core/src/json/schema/registry.ts b/packages/angular_devkit/core/src/json/schema/registry.ts index 2939d91d4802..69333feb6dfb 100644 --- a/packages/angular_devkit/core/src/json/schema/registry.ts +++ b/packages/angular_devkit/core/src/json/schema/registry.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import Ajv, { ValidateFunction } from 'ajv'; +import Ajv, { SchemaObjCxt, ValidateFunction } from 'ajv'; import ajvAddFormats from 'ajv-formats'; import * as http from 'http'; import * as https from 'https'; @@ -305,6 +305,8 @@ export class CoreSchemaRegistry implements SchemaRegistry { try { this._currentCompilationSchemaInfo = schemaInfo; validator = this._ajv.compile(schema); + } catch { + validator = await this._ajv.compileAsync(schema); } finally { this._currentCompilationSchemaInfo = undefined; } @@ -406,10 +408,7 @@ 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))); - + const pathArray = this.normalizeDataPathArr(it); compilationSchemInfo.smartDefaultRecord.set(JSON.stringify(pathArray), schema); return () => true; @@ -449,12 +448,7 @@ export class CoreSchemaRegistry implements SchemaRegistry { return () => true; } - const path = - '/' + - it.dataPathArr - .slice(1, it.dataLevel + 1) - .map((p) => (typeof p === 'number' ? p : p.str.slice(1, -1))) - .join('/'); + const path = '/' + this.normalizeDataPathArr(it).join('/'); let type: string | undefined; let items: Array | undefined; @@ -593,11 +587,31 @@ export class CoreSchemaRegistry implements SchemaRegistry { data: any, fragments: string[], value: unknown, - parent: Record | null = null, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + parent: any = null, parentProperty?: string, force?: boolean, ): void { - for (const fragment of fragments) { + for (let index = 0; index < fragments.length; index++) { + const fragment = fragments[index]; + if (/^i\d+$/.test(fragment)) { + if (!Array.isArray(data)) { + return; + } + + for (let dataIndex = 0; dataIndex < data.length; dataIndex++) { + CoreSchemaRegistry._set( + data[dataIndex], + fragments.slice(index + 1), + value, + data, + `${dataIndex}`, + ); + } + + return; + } + if (!data && parent !== null && parentProperty) { data = parent[parentProperty] = {}; } @@ -665,4 +679,10 @@ export class CoreSchemaRegistry implements SchemaRegistry { ); } } + + private normalizeDataPathArr(it: SchemaObjCxt): (number | string)[] { + return it.dataPathArr + .slice(1, it.dataLevel + 1) + .map((p) => (typeof p === 'number' ? p : p.str.replace(/\"/g, ''))); + } } 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 2ce888f74f9e..d64d96238d09 100644 --- a/packages/angular_devkit/core/src/json/schema/registry_spec.ts +++ b/packages/angular_devkit/core/src/json/schema/registry_spec.ts @@ -30,7 +30,7 @@ describe('CoreSchemaRegistry', () => { }, }, tslint: { - $ref: 'https://json.schemastore.org/tslint#', + $ref: 'https://json.schemastore.org/npm-link-up#', }, }, }) @@ -130,7 +130,7 @@ describe('CoreSchemaRegistry', () => { map((result) => { expect(result.success).toBe(false); expect(result.errors && result.errors[0].message).toContain( - 'should NOT have additional properties', + 'must NOT have additional properties', ); }), ) @@ -180,10 +180,10 @@ describe('CoreSchemaRegistry', () => { mergeMap((validator) => validator(data)), map((result) => { expect(result.success).toBe(false); - expect(result.errors && result.errors[0].message).toContain( - 'should NOT have additional properties', + expect(result.errors?.[0].message).toContain( + 'must NOT have additional properties', ); - expect(result.errors && result.errors[0].keyword).toBe('additionalProperties'); + expect(result.errors?.[0].keyword).toBe('additionalProperties'); }), ) .toPromise() @@ -249,7 +249,7 @@ describe('CoreSchemaRegistry', () => { .then(done, done.fail); }); - it('shows dataPath and message on error', (done) => { + it('shows dataPath and message on error', async () => { const registry = new CoreSchemaRegistry(); const data = { hotdot: 'hotdog', banana: 'banana' }; const format: SchemaFormat = { @@ -262,7 +262,7 @@ describe('CoreSchemaRegistry', () => { registry.addFormat(format); - registry + await registry .compile({ properties: { hotdot: { type: 'string', format: 'is-hotdog' }, @@ -275,12 +275,11 @@ 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].instancePath).toBe('.banana'); + expect(result.errors && result.errors[0].instancePath).toBe('/banana'); expect(result.errors && (result.errors[0].params as any).format).toBe('is-hotdog'); }), ) .toPromise() - .then(done, done.fail); }); it('supports smart defaults', (done) => {