UNPKG

15 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const error_1 = require("./error");
4class InvalidSchema extends error_1.JsonSchemaErrorBase {
5}
6exports.InvalidSchema = InvalidSchema;
7class InvalidValueError extends error_1.JsonSchemaErrorBase {
8}
9exports.InvalidValueError = InvalidValueError;
10class MissingImplementationError extends error_1.JsonSchemaErrorBase {
11}
12exports.MissingImplementationError = MissingImplementationError;
13class SettingReadOnlyPropertyError extends error_1.JsonSchemaErrorBase {
14}
15exports.SettingReadOnlyPropertyError = SettingReadOnlyPropertyError;
16class InvalidUpdateValue extends error_1.JsonSchemaErrorBase {
17}
18exports.InvalidUpdateValue = InvalidUpdateValue;
19/**
20 * Holds all the information, including the value, of a node in the schema tree.
21 */
22class SchemaTreeNode {
23 constructor(nodeMetaData) {
24 this._defined = false;
25 this._dirty = false;
26 this._schema = nodeMetaData.schema;
27 this._name = nodeMetaData.name;
28 this._value = nodeMetaData.value;
29 this._forward = nodeMetaData.forward;
30 this._parent = nodeMetaData.parent;
31 }
32 dispose() {
33 this._parent = null;
34 this._schema = null;
35 this._value = null;
36 if (this._forward) {
37 this._forward.dispose();
38 }
39 this._forward = null;
40 }
41 get defined() { return this._defined; }
42 get dirty() { return this._dirty; }
43 set dirty(v) {
44 if (v) {
45 this._defined = true;
46 this._dirty = true;
47 if (this._parent) {
48 this._parent.dirty = true;
49 }
50 }
51 }
52 get value() { return this.get(); }
53 get name() { return this._name; }
54 get readOnly() { return this._schema['readOnly']; }
55 get frozen() { return true; }
56 get description() {
57 return 'description' in this._schema ? this._schema['description'] : null;
58 }
59 get required() {
60 if (!this._parent) {
61 return false;
62 }
63 return this._parent.isChildRequired(this.name);
64 }
65 isChildRequired(_name) { return false; }
66 get parent() { return this._parent; }
67 get children() { return null; }
68 get items() { return null; }
69 get itemPrototype() { return null; }
70 set(_v, _init = false, _force = false) {
71 if (!this.readOnly) {
72 throw new MissingImplementationError();
73 }
74 throw new SettingReadOnlyPropertyError();
75 }
76 isCompatible(_v) { return false; }
77 static _defineProperty(proto, treeNode) {
78 if (treeNode.readOnly) {
79 Object.defineProperty(proto, treeNode.name, {
80 enumerable: true,
81 get: () => treeNode.get()
82 });
83 }
84 else {
85 Object.defineProperty(proto, treeNode.name, {
86 enumerable: true,
87 get: () => treeNode.get(),
88 set: (v) => treeNode.set(v)
89 });
90 }
91 }
92}
93exports.SchemaTreeNode = SchemaTreeNode;
94/** Base Class used for Non-Leaves TreeNode. Meaning they can have children. */
95class NonLeafSchemaTreeNode extends SchemaTreeNode {
96 dispose() {
97 for (const key of Object.keys(this.children || {})) {
98 this.children[key].dispose();
99 }
100 for (let item of this.items || []) {
101 item.dispose();
102 }
103 super.dispose();
104 }
105 get() {
106 if (this.defined) {
107 return this._value;
108 }
109 else {
110 return undefined;
111 }
112 }
113 destroy() {
114 this._defined = false;
115 this._value = null;
116 }
117 // Helper function to create a child based on its schema.
118 _createChildProperty(name, value, forward, schema, define = true) {
119 const type = ('oneOf' in schema) ? 'oneOf' :
120 ('enum' in schema) ? 'enum' : schema['type'];
121 let Klass = null;
122 switch (type) {
123 case 'object':
124 Klass = ObjectSchemaTreeNode;
125 break;
126 case 'array':
127 Klass = ArraySchemaTreeNode;
128 break;
129 case 'string':
130 Klass = StringSchemaTreeNode;
131 break;
132 case 'boolean':
133 Klass = BooleanSchemaTreeNode;
134 break;
135 case 'number':
136 Klass = NumberSchemaTreeNode;
137 break;
138 case 'integer':
139 Klass = IntegerSchemaTreeNode;
140 break;
141 case 'enum':
142 Klass = EnumSchemaTreeNode;
143 break;
144 case 'oneOf':
145 Klass = OneOfSchemaTreeNode;
146 break;
147 default:
148 throw new InvalidSchema('Type ' + type + ' not understood by SchemaClassFactory.');
149 }
150 const metaData = new Klass({ parent: this, forward, value, schema, name });
151 if (define) {
152 SchemaTreeNode._defineProperty(this._value, metaData);
153 }
154 return metaData;
155 }
156}
157exports.NonLeafSchemaTreeNode = NonLeafSchemaTreeNode;
158class OneOfSchemaTreeNode extends NonLeafSchemaTreeNode {
159 constructor(metaData) {
160 super(metaData);
161 let { value, forward, schema } = metaData;
162 this._typesPrototype = schema['oneOf'].map((schema) => {
163 return this._createChildProperty('', '', forward, schema, false);
164 });
165 this._currentTypeHolder = null;
166 this._set(value, true, false);
167 }
168 _set(v, init, force) {
169 if (!init && this.readOnly && !force) {
170 throw new SettingReadOnlyPropertyError();
171 }
172 // Find the first type prototype that is compatible with the
173 let proto = null;
174 for (let i = 0; i < this._typesPrototype.length; i++) {
175 const p = this._typesPrototype[i];
176 if (p.isCompatible(v)) {
177 proto = p;
178 break;
179 }
180 }
181 if (proto == null) {
182 return;
183 }
184 if (!init) {
185 this.dirty = true;
186 }
187 this._currentTypeHolder = proto;
188 this._currentTypeHolder.set(v, false, true);
189 }
190 set(v, _init = false, force = false) {
191 return this._set(v, false, force);
192 }
193 get() {
194 return this._currentTypeHolder ? this._currentTypeHolder.get() : null;
195 }
196 get defaultValue() {
197 return null;
198 }
199 get defined() { return this._currentTypeHolder ? this._currentTypeHolder.defined : false; }
200 get items() { return this._typesPrototype; }
201 get type() { return 'oneOf'; }
202 get tsType() { return null; }
203 serialize(serializer) { serializer.outputOneOf(this); }
204}
205exports.OneOfSchemaTreeNode = OneOfSchemaTreeNode;
206/** A Schema Tree Node that represents an object. */
207class ObjectSchemaTreeNode extends NonLeafSchemaTreeNode {
208 constructor(metaData) {
209 super(metaData);
210 this._frozen = false;
211 this._set(metaData.value, true, false);
212 }
213 _set(value, init, force) {
214 if (!init && this.readOnly && !force) {
215 throw new SettingReadOnlyPropertyError();
216 }
217 const schema = this._schema;
218 const forward = this._forward;
219 this._defined = !!value;
220 this._children = Object.create(null);
221 this._value = Object.create(null);
222 this._dirty = this._dirty || !init;
223 if (schema['properties']) {
224 for (const name of Object.keys(schema['properties'])) {
225 const propertySchema = schema['properties'][name];
226 this._children[name] = this._createChildProperty(name, value ? value[name] : undefined, forward ? forward.children[name] : null, propertySchema);
227 }
228 }
229 else if (!schema['additionalProperties']) {
230 throw new InvalidSchema('Schema does not have a properties, but doesnt allow for '
231 + 'additional properties.');
232 }
233 if (!schema['additionalProperties']) {
234 this._frozen = true;
235 Object.freeze(this._value);
236 Object.freeze(this._children);
237 }
238 else if (value) {
239 // Set other properties which don't have a schema.
240 for (const key of Object.keys(value)) {
241 if (!this._children[key]) {
242 this._value[key] = value[key];
243 }
244 }
245 }
246 }
247 set(v, force = false) {
248 return this._set(v, false, force);
249 }
250 get frozen() { return this._frozen; }
251 get children() { return this._children; }
252 get type() { return 'object'; }
253 get tsType() { return Object; }
254 get defaultValue() { return null; }
255 isCompatible(v) { return typeof v == 'object' && v !== null; }
256 isChildRequired(name) {
257 if (this._schema['required']) {
258 return this._schema['required'].indexOf(name) != -1;
259 }
260 return false;
261 }
262 serialize(serializer) { serializer.object(this); }
263}
264exports.ObjectSchemaTreeNode = ObjectSchemaTreeNode;
265/** A Schema Tree Node that represents an array. */
266class ArraySchemaTreeNode extends NonLeafSchemaTreeNode {
267 constructor(metaData) {
268 super(metaData);
269 this._set(metaData.value, true, false);
270 // Keep the item's schema as a schema node. This is important to keep type information.
271 this._itemPrototype = this._createChildProperty('', undefined, null, metaData.schema['items'], false);
272 }
273 _set(value, init, _force) {
274 const schema = this._schema;
275 const forward = this._forward;
276 this._value = Object.create(null);
277 this._dirty = this._dirty || !init;
278 if (value) {
279 this._defined = true;
280 }
281 else {
282 this._defined = false;
283 value = [];
284 }
285 this._items = [];
286 this._value = [];
287 for (let index = 0; index < value.length; index++) {
288 this._items[index] = this._createChildProperty('' + index, value && value[index], forward && forward.items[index], schema['items']);
289 }
290 }
291 set(v, init = false, force = false) {
292 return this._set(v, init, force);
293 }
294 isCompatible(v) { return Array.isArray(v); }
295 get type() { return 'array'; }
296 get tsType() { return Array; }
297 get items() { return this._items; }
298 get itemPrototype() { return this._itemPrototype; }
299 get defaultValue() { return null; }
300 serialize(serializer) { serializer.array(this); }
301}
302exports.ArraySchemaTreeNode = ArraySchemaTreeNode;
303/**
304 * The root class of the tree node. Receives a prototype that will be filled with the
305 * properties of the Schema root.
306 */
307class RootSchemaTreeNode extends ObjectSchemaTreeNode {
308 constructor(proto, metaData) {
309 super(metaData);
310 for (const key of Object.keys(this._children)) {
311 if (this._children[key]) {
312 SchemaTreeNode._defineProperty(proto, this._children[key]);
313 }
314 }
315 }
316}
317exports.RootSchemaTreeNode = RootSchemaTreeNode;
318/** A leaf in the schema tree. Must contain a single primitive value. */
319class LeafSchemaTreeNode extends SchemaTreeNode {
320 constructor(metaData) {
321 super(metaData);
322 this._defined = metaData.value !== undefined;
323 if ('default' in metaData.schema) {
324 this._default = this.convert(metaData.schema['default']);
325 }
326 }
327 get() {
328 if (!this.defined && this._forward) {
329 return this._forward.get();
330 }
331 if (!this.defined) {
332 return 'default' in this._schema ? this._default : undefined;
333 }
334 return this._value === undefined
335 ? undefined
336 : (this._value === null ? null : this.convert(this._value));
337 }
338 set(v, init = false, force = false) {
339 if (this.readOnly && !force) {
340 throw new SettingReadOnlyPropertyError();
341 }
342 let convertedValue = this.convert(v);
343 if (convertedValue === null || convertedValue === undefined) {
344 if (this.required) {
345 throw new InvalidValueError(`Invalid value "${v}" on a required field.`);
346 }
347 }
348 this.dirty = !init;
349 this._value = convertedValue;
350 }
351 destroy() {
352 this._defined = false;
353 this._value = null;
354 }
355 get defaultValue() {
356 return this.hasDefault ? this._default : null;
357 }
358 get hasDefault() {
359 return 'default' in this._schema;
360 }
361 serialize(serializer) {
362 serializer.outputValue(this);
363 }
364}
365exports.LeafSchemaTreeNode = LeafSchemaTreeNode;
366/** Basic primitives for JSON Schema. */
367class StringSchemaTreeNode extends LeafSchemaTreeNode {
368 serialize(serializer) { serializer.outputString(this); }
369 isCompatible(v) { return typeof v == 'string' || v instanceof String; }
370 convert(v) { return v === undefined ? undefined : '' + v; }
371 get type() { return 'string'; }
372 get tsType() { return String; }
373}
374class EnumSchemaTreeNode extends LeafSchemaTreeNode {
375 constructor(metaData) {
376 super(metaData);
377 if (!Array.isArray(metaData.schema['enum'])) {
378 throw new InvalidSchema();
379 }
380 if (this.hasDefault && !this._isInEnum(this._default)) {
381 throw new InvalidSchema();
382 }
383 this.set(metaData.value, true, true);
384 }
385 _isInEnum(value) {
386 return this._schema['enum'].some((v) => v === value);
387 }
388 get items() { return this._schema['enum']; }
389 set(value, init = false, force = false) {
390 if (!(value === undefined || this._isInEnum(value))) {
391 throw new InvalidUpdateValue('Invalid value can only be one of these: ' + this.items);
392 }
393 super.set(value, init, force);
394 }
395 isCompatible(v) {
396 return this._isInEnum(v);
397 }
398 convert(v) {
399 if (v === undefined) {
400 return undefined;
401 }
402 if (!this._isInEnum(v)) {
403 return undefined;
404 }
405 return v;
406 }
407 get type() {
408 return this._schema['type'] || 'any';
409 }
410 get tsType() { return null; }
411 serialize(serializer) { serializer.outputEnum(this); }
412}
413class BooleanSchemaTreeNode extends LeafSchemaTreeNode {
414 serialize(serializer) { serializer.outputBoolean(this); }
415 isCompatible(v) { return typeof v == 'boolean' || v instanceof Boolean; }
416 convert(v) { return v === undefined ? undefined : !!v; }
417 get type() { return 'boolean'; }
418 get tsType() { return Boolean; }
419}
420class NumberSchemaTreeNode extends LeafSchemaTreeNode {
421 serialize(serializer) { serializer.outputNumber(this); }
422 isCompatible(v) { return typeof v == 'number' || v instanceof Number; }
423 convert(v) { return v === undefined ? undefined : +v; }
424 get type() { return 'number'; }
425 get tsType() { return Number; }
426}
427class IntegerSchemaTreeNode extends NumberSchemaTreeNode {
428 convert(v) { return v === undefined ? undefined : Math.floor(+v); }
429}
430//# sourceMappingURL=/users/hansl/sources/angular-cli/src/schema-tree.js.map
\No newline at end of file