Skip to content

Commit 74f7cdd

Browse files
authored
feat(@ngtools/json-schema): Introduce a separate package for JSON schema. (angular#3927)
This is the same code we used before, but: 1. its in a separate package, 2. it also support new serialization to .d.ts, 3. OneOf is now supported entirely (instead of the previous hack). Also, removed the schema.d.ts file and generate it before building the package. It is not git ignored and changing it will not work (it will be overwritten).
1 parent c6d1c99 commit 74f7cdd

29 files changed

+844
-262
lines changed

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
"test:deps": "node scripts/publish/validate_dependencies.js",
2222
"test:inspect": "node --inspect --debug-brk tests/runner",
2323
"test:packages": "node scripts/run-packages-spec.js",
24-
"build-config-interface": "dtsgen packages/angular-cli/lib/config/schema.json --out packages/angular-cli/lib/config/schema.d.ts",
2524
"eslint": "eslint .",
2625
"tslint": "tslint \"**/*.ts\" -c tslint.json -e \"**/blueprints/*/files/**/*.ts\" -e \"node_modules/**\" -e \"tmp/**\" -e \"dist/**\"",
2726
"lint": "npm-run-all -c eslint tslint"
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "@ngtools/json-schema",
3+
"version": "1.2.1",
4+
"description": "Schema validating and reading for configurations, similar to Angular CLI config.",
5+
"main": "./src/index.js",
6+
"typings": "src/index.d.ts",
7+
"license": "MIT",
8+
"keywords": [
9+
"angular",
10+
"json",
11+
"json-schema",
12+
"schema",
13+
"config"
14+
],
15+
"repository": {
16+
"type": "git",
17+
"url": "https://github.com/angular/angular-cli.git"
18+
},
19+
"author": "angular",
20+
"bugs": {
21+
"url": "https://github.com/angular/angular-cli/issues"
22+
},
23+
"homepage": "https://github.com/angular/angular-cli/tree/master/packages/@ngtools/json-schema",
24+
"engines": {
25+
"node": ">= 4.1.0",
26+
"npm": ">= 3.0.0"
27+
},
28+
"dependencies": {
29+
},
30+
"peerDependencies": {
31+
}
32+
}
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
export class JsonSchemaErrorBase extends Error {
3+
constructor(message?: string) {
4+
super();
5+
6+
if (message) {
7+
this.message = message;
8+
} else {
9+
this.message = (<any>this.constructor).name;
10+
}
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {SchemaClass, SchemaClassFactory} from './schema-class-factory';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {JsonSchemaErrorBase} from './error';
2+
import {Serializer, WriterFn} from './serializer';
3+
import {JsonSerializer} from './serializers/json';
4+
import {DTsSerializer} from './serializers/dts';
5+
6+
7+
export class UnknownMimetype extends JsonSchemaErrorBase {}
8+
9+
10+
export function createSerializerFromMimetype(mimetype: string,
11+
writer: WriterFn,
12+
...opts: any[]): Serializer {
13+
let Klass: { new (writer: WriterFn, ...args: any[]): Serializer } = null;
14+
switch (mimetype) {
15+
case 'application/json': Klass = JsonSerializer; break;
16+
case 'text/json': Klass = JsonSerializer; break;
17+
case 'text/x.typescript': Klass = DTsSerializer; break;
18+
case 'text/x.dts': Klass = DTsSerializer; break;
19+
20+
default: throw new UnknownMimetype();
21+
}
22+
23+
return new Klass(writer, ...opts);
24+
25+
}
26+
27+
28+
declare module './serializer' {
29+
namespace Serializer {
30+
export let fromMimetype: typeof createSerializerFromMimetype;
31+
}
32+
}
33+
34+
Serializer.fromMimetype = createSerializerFromMimetype;
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import {Serializer} from './serializer';
2+
3+
4+
// A TypeScript Type. This can be used to do `new tsType(value)`.
5+
// `null` implies any type; be careful.
6+
export type TypeScriptType = typeof Number
7+
| typeof Boolean
8+
| typeof String
9+
| typeof Object
10+
| typeof Array
11+
| null;
12+
13+
14+
// The most generic interface for a schema node. This is used by the serializers.
15+
export interface SchemaNode {
16+
readonly name: string;
17+
readonly type: string;
18+
readonly tsType: TypeScriptType;
19+
readonly defined: boolean;
20+
readonly dirty: boolean;
21+
readonly frozen: boolean;
22+
readonly readOnly: boolean;
23+
readonly defaultValue: any | null;
24+
readonly required: boolean;
25+
readonly parent: SchemaNode | null;
26+
27+
// Schema related properties.
28+
readonly description: string | null;
29+
30+
// Object-only properties. `null` for everything else.
31+
readonly children: { [key: string]: SchemaNode } | null;
32+
33+
// Array-only properties. `null` for everything else.
34+
readonly items: SchemaNode[] | null;
35+
readonly itemPrototype: SchemaNode | null;
36+
37+
// Mutable properties.
38+
value: any;
39+
40+
// Serialization.
41+
serialize(serializer: Serializer): void;
42+
}

packages/angular-cli/models/json-schema/schema-class-factory.ts renamed to packages/@ngtools/json-schema/src/schema-class-factory.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import {NgToolkitError} from '../error';
21
import {Serializer} from './serializer';
32
import {RootSchemaTreeNode, SchemaTreeNode} from './schema-tree';
3+
import {JsonSchemaErrorBase} from './error';
44

5-
export class InvalidJsonPath extends NgToolkitError {}
5+
import './mimetypes';
6+
7+
8+
export class InvalidJsonPath extends JsonSchemaErrorBase {}
69

710

811
// The schema tree node property of the SchemaClass.
@@ -66,6 +69,9 @@ export interface SchemaClass<JsonType> extends Object {
6669
$$defined(path: string): boolean;
6770
$$delete(path: string): void;
6871

72+
// Direct access to the schema.
73+
$$schema(): RootSchemaTreeNode;
74+
6975
$$serialize(mimetype?: string): string;
7076
}
7177

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {readFileSync} from 'fs';
2+
import {join} from 'path';
3+
4+
import {RootSchemaTreeNode} from './schema-tree';
5+
6+
7+
describe('SchemaTreeNode', () => {
8+
9+
});
10+
11+
12+
describe('OneOfSchemaTreeNode', () => {
13+
const schemaJsonFilePath = join(__dirname, '../tests/schema1.json');
14+
const schemaJson = JSON.parse(readFileSync(schemaJsonFilePath, 'utf-8'));
15+
const valueJsonFilePath = join(__dirname, '../tests/value1-1.json');
16+
const valueJson = JSON.parse(readFileSync(valueJsonFilePath, 'utf-8'));
17+
18+
19+
it('works', () => {
20+
const proto: any = Object.create(null);
21+
new RootSchemaTreeNode(proto, {
22+
value: valueJson,
23+
schema: schemaJson
24+
});
25+
26+
expect(proto.oneOfKey2 instanceof Array).toBe(true);
27+
expect(proto.oneOfKey2.length).toBe(2);
28+
29+
// Set it to a string, which is valid.
30+
proto.oneOfKey2 = 'hello';
31+
expect(proto.oneOfKey2 instanceof Array).toBe(false);
32+
});
33+
});
34+

0 commit comments

Comments
 (0)