Skip to content

Commit 449afdc

Browse files
author
Sascha Goldhofer
committed
Refator remaining functions and tests to ts
1 parent 58b6907 commit 449afdc

File tree

129 files changed

+6508
-688
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

129 files changed

+6508
-688
lines changed

distmodule/index.js

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import strings from "./lib/config/strings";
2+
import Interface from "./lib/cores/CoreInterface";
3+
import Draft04 from "./lib/cores/Draft04";
4+
import JsonEditor from "./lib/cores/JsonEditor";
5+
import addSchema from "./lib/addSchema";
6+
import addValidator from "./lib/addValidator";
7+
import compileSchema from "./lib/compileSchema";
8+
import createCustomError from "./lib/utils/createCustomError";
9+
import createSchemaOf from "./lib/createSchemaOf";
10+
import each from "./lib/each";
11+
import eachSchema from "./lib/eachSchema";
12+
import getChildSchemaSelection from "./lib/getChildSchemaSelection";
13+
import getSchema from "./lib/getSchema";
14+
import getTemplate from "./lib/getTemplate";
15+
import getTypeOf from "./lib/getTypeOf";
16+
import isValid from "./lib/isValid";
17+
import SchemaService from "./lib/SchemaService";
18+
import step from "./lib/step";
19+
import validate from "./lib/validate";
20+
import validateAsync from "./lib/validateAsync";
21+
export default {
22+
config: {
23+
strings
24+
},
25+
cores: {
26+
Interface,
27+
Draft04,
28+
JsonEditor // adjusted core of draft04 to better support the json-editor
29+
},
30+
addSchema,
31+
addValidator,
32+
compileSchema,
33+
createCustomError,
34+
createSchemaOf,
35+
each,
36+
eachSchema,
37+
getChildSchemaSelection,
38+
getSchema,
39+
getTemplate,
40+
getTypeOf,
41+
isValid,
42+
SchemaService,
43+
step,
44+
validate,
45+
validateAsync // async validation of data by a schema
46+
};
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,50 @@
1-
const getSchema = require("./getSchema");
2-
const Core = require("./cores/JsonEditor");
3-
const gp = require("gson-pointer");
4-
5-
6-
function copy(value) {
7-
return JSON.parse(JSON.stringify(value));
8-
}
9-
10-
11-
class SchemaService {
12-
1+
import getSchema from "./getSchema";
2+
import Core from "./cores/JsonEditor";
3+
import gp from "gson-pointer";
4+
import copy from "./utils/copy";
5+
export default class SchemaService {
136
constructor(schema, data) {
147
this.core = new Core(schema);
158
this.schema = schema;
169
this.data = data;
1710
this.cache = {};
1811
}
19-
2012
updateData(data) {
2113
this.data = data;
2214
this.cache = {};
2315
}
24-
2516
updateSchema(schema) {
2617
this.schema = schema;
2718
this.core.setSchema(schema);
2819
this.cache = {};
2920
}
30-
3121
get(pointer, data) {
3222
if (data) { // possibly separate entry point
33-
const schema = getSchema(this.core, data, this.schema, pointer);
23+
const schema = getSchema(this.core, pointer, data, this.schema);
3424
return copy(schema);
3525
}
36-
3726
if (pointer === "#") { // root
3827
return this.schema;
3928
}
40-
4129
if (this.cache[pointer]) { // return cached result
4230
return this.cache[pointer];
4331
}
44-
4532
const parentPointer = gp.join(pointer, "..");
4633
let parentSchema = this.cache[parentPointer];
4734
if (parentSchema == null) {
4835
// store parent (major performance improvement if its within oneof)
49-
parentSchema = getSchema(this.core, this.data, this.schema, parentPointer);
36+
parentSchema = getSchema(this.core, parentPointer, this.data, this.schema);
5037
if (parentSchema.variableSchema !== true) {
5138
this.cache[parentPointer] = copy(parentSchema);
5239
}
5340
}
54-
5541
// step from parent to child
5642
const key = gp.split(pointer).pop();
57-
let schema = getSchema(this.core, gp.get(this.data, parentPointer), this.cache[parentPointer], key);
43+
let schema = getSchema(this.core, key, gp.get(this.data, parentPointer), this.cache[parentPointer]);
5844
schema = copy(schema);
5945
if (schema.variableSchema !== true) {
6046
this.cache[pointer] = schema;
6147
}
6248
return schema;
6349
}
6450
}
65-
66-
67-
module.exports = SchemaService;

distmodule/lib/addSchema.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import remotes from "../remotes";
2+
import compileSchema from "./compileSchema";
3+
export default function addSchema(url, schema) {
4+
schema.id = schema.id || url;
5+
remotes[url] = compileSchema(schema);
6+
}

distmodule/lib/addValidator.js

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* @throws Error
3+
* Adds a custom error. May override existing errors
4+
*
5+
* @param core
6+
* @param errorId - id of error @see /lib/validation/errors
7+
* @param {Function} errorCreator - function returning an error-object @see /lib/utils/createCustomError
8+
*/
9+
function addError(core, errorId, errorCreator) {
10+
if (typeof errorCreator !== "function") {
11+
throw new Error(`Error callback 'errorCreator' must be of type function. Received ${typeof errorCreator}`);
12+
}
13+
core.errors[errorId] = errorCreator;
14+
}
15+
/**
16+
* Adds a custom format validator. Existing format may not be overriden (may still be modified manually)
17+
* @param core
18+
* @param formatType - format type (i.e. `format: "html"`)
19+
* @param validationFunction - called with (core, schema, value, pointer)
20+
*/
21+
function addFormat(core, formatType, validationFunction) {
22+
if (typeof validationFunction !== "function") {
23+
throw new Error(`Validation function expected. Received ${typeof validationFunction}`);
24+
}
25+
if (core.validateFormat[formatType] == null) {
26+
core.validateFormat[formatType] = validationFunction;
27+
return;
28+
}
29+
throw new Error(`A format '${formatType}' is already registered to validation`);
30+
}
31+
/**
32+
* Adds a custom keyword validation to a specific type. May not override existing keywords.
33+
*
34+
* @param core
35+
* @param datatype - valid datatype like "object", "array", "string", etc
36+
* @param keyword - The keyword to add, i.e. `minWidth: ...`
37+
* @param validationFunction - called with (core, schema, value, pointer)
38+
*/
39+
function addKeyword(core, datatype, keyword, validationFunction) {
40+
if (typeof validationFunction !== "function") {
41+
throw new Error(`Validation function expected. Received ${typeof validationFunction}`);
42+
}
43+
if (core.typeKeywords[datatype] == null) {
44+
throw new Error(`Unknown datatype ${datatype}. Failed adding custom keyword validation.`);
45+
}
46+
if (core.typeKeywords[datatype].includes(keyword) === false) {
47+
core.typeKeywords[datatype].push(keyword);
48+
}
49+
core.validateKeyword[keyword] = validationFunction;
50+
}
51+
export default {
52+
error: addError,
53+
format: addFormat,
54+
keyword: addKeyword
55+
};

distmodule/lib/compile/getRef.js

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { get } from "gson-pointer";
2+
import splitRef from "./splitRef";
3+
import getTypeOf from "../getTypeOf";
4+
const suffixes = /(#|\/)+$/g;
5+
// const emptyValues = ["", null, "#"];
6+
const isObject = val => (getTypeOf(val) === "object");
7+
// 1. combined is known
8+
// 2. base or pointer is known
9+
// 3. base + pointer is known
10+
export default function getRef(context, rootSchema, $ref) {
11+
if (isObject($ref)) {
12+
$ref = $ref.__ref || $ref.$ref;
13+
}
14+
if ($ref == null) {
15+
return rootSchema;
16+
}
17+
let schema;
18+
// is it a known $ref?
19+
const $remote = $ref.replace(suffixes, "");
20+
if (context.remotes[$remote]) {
21+
schema = context.remotes[$remote];
22+
if (schema && schema.$ref) {
23+
return getRef(context, rootSchema, schema.$ref);
24+
}
25+
return schema;
26+
}
27+
if (context.ids[$ref]) {
28+
schema = get(rootSchema, context.ids[$ref]);
29+
if (schema && schema.$ref) {
30+
return getRef(context, rootSchema, schema.$ref);
31+
}
32+
return schema;
33+
}
34+
// is it a ref with host/pointer?
35+
const fragments = splitRef($ref);
36+
if (fragments.length === 0) {
37+
return rootSchema;
38+
}
39+
if (fragments.length === 1) {
40+
$ref = fragments[0];
41+
if (context.remotes[$ref]) {
42+
schema = context.remotes[$ref];
43+
return getRef(context, rootSchema, schema.$ref);
44+
}
45+
if (context.ids[$ref]) {
46+
schema = get(rootSchema, context.ids[$ref]);
47+
if (schema && schema.$ref) {
48+
return getRef(context, rootSchema, schema.$ref);
49+
}
50+
return schema;
51+
}
52+
}
53+
if (fragments.length === 2) {
54+
const base = fragments[0];
55+
$ref = fragments[1];
56+
if (context.remotes[base]) {
57+
if (context.remotes[base].getRef) {
58+
return context.remotes[base].getRef($ref);
59+
}
60+
// console.log("warning: uncompiled remote - context may be wrong", base);
61+
return getRef(context, context.remotes[base], $ref);
62+
}
63+
if (context.ids[base]) {
64+
return getRef(context, get(rootSchema, context.ids[base]), $ref);
65+
}
66+
}
67+
schema = get(rootSchema, context.ids[$ref] || $ref);
68+
if (schema && schema.$ref) {
69+
return getRef(context, rootSchema, schema.$ref);
70+
}
71+
return schema;
72+
}

distmodule/lib/compile/index.js

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/* eslint max-statements-per-line: ["error", { "max": 2 }] */
2+
import eachSchema from "../eachSchema";
3+
import remotes from "../../remotes";
4+
import joinScope from "./joinScope";
5+
import getRef from "./getRef";
6+
const COMPILED = "__compiled";
7+
const COMPILED_REF = "__ref";
8+
const GET_REF = "getRef";
9+
const GET_ROOT = "getRoot";
10+
const suffixes = /(#|\/)+$/g;
11+
export default function compile(rootSchema, force = false) {
12+
if (rootSchema[COMPILED] !== undefined) {
13+
return rootSchema;
14+
} // eslint-disable-line
15+
const context = { ids: {}, remotes: Object.assign({}, remotes) };
16+
const rootSchemaAsString = JSON.stringify(rootSchema);
17+
rootSchema = JSON.parse(rootSchemaAsString);
18+
Object.defineProperty(rootSchema, COMPILED, { enumerable: false, value: true });
19+
Object.defineProperty(rootSchema, GET_REF, { enumerable: false, value: getRef.bind(null, context, rootSchema) });
20+
// Object.defineProperty(rootSchema, "debug", { enumerable: false, value: schema => {
21+
// console.log(JSON.stringify(context.ids, null, 2));
22+
// console.log("remotes", Object.keys(remotes));
23+
// console.log(JSON.stringify(rootSchema, null, 2));
24+
// } });
25+
if (force === false && rootSchemaAsString.includes("$ref") === false) {
26+
// bail early, when no $refs are defined
27+
return rootSchema;
28+
}
29+
const scopes = {};
30+
const getRoot = () => rootSchema;
31+
eachSchema(rootSchema, (schema, pointer) => {
32+
if (schema.id) {
33+
context.ids[schema.id.replace(suffixes, "")] = pointer;
34+
}
35+
// build up scopes and add them to $ref-resolution map
36+
pointer = `#${pointer}`.replace(/##+/, "#");
37+
const previousPointer = pointer.replace(/\/[^/]+$/, "");
38+
const parentPointer = pointer.replace(/\/[^/]+\/[^/]+$/, "");
39+
const previousScope = scopes[previousPointer] || scopes[parentPointer];
40+
const scope = joinScope(previousScope, schema.id);
41+
scopes[pointer] = scope;
42+
if (context.ids[scope] == null) {
43+
context.ids[scope] = pointer;
44+
}
45+
if (schema.$ref) {
46+
Object.defineProperty(schema, COMPILED_REF, { enumerable: false, value: joinScope(scope, schema.$ref) });
47+
// @todo currently not used:
48+
Object.defineProperty(schema, GET_ROOT, { enumerable: false, value: getRoot });
49+
// console.log("compiled ref", scope, schema.$ref, "=>", joinScope(scope, schema.$ref));
50+
}
51+
});
52+
// console.log(JSON.stringify(context.ids, null, 2));
53+
return rootSchema;
54+
}

distmodule/lib/compile/joinScope.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/* eslint max-statements-per-line: ["error", { "max": 2 }] */
2+
const suffixes = /(#|\/)+$/;
3+
const trailingHash = /#$/;
4+
const isDomain = /^[^:]+:\/\/[^/]+\//;
5+
const trailingFragments = /\/[^/]*$/;
6+
const idAndPointer = /#.*$/;
7+
export default function joinScope(previous, id) {
8+
if (previous == null && id == null) {
9+
return "#";
10+
}
11+
if (id == null) {
12+
return previous.replace(trailingHash, "");
13+
}
14+
if (previous == null) {
15+
return id.replace(trailingHash, "");
16+
}
17+
if (id[0] === "#") {
18+
return `${previous.replace(idAndPointer, "")}${id.replace(suffixes, "")}`;
19+
}
20+
if (isDomain.test(id)) {
21+
return id.replace(trailingHash, "");
22+
}
23+
return `${previous.replace(trailingFragments, "")}/${id.replace(trailingHash, "")}`;
24+
}

distmodule/lib/compile/splitRef.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const suffixes = /(#|\/)+$/g;
2+
const emptyValues = ["", null, "#"];
3+
export default function splitRef($ref) {
4+
if (emptyValues.includes($ref)) {
5+
return [];
6+
}
7+
$ref = $ref.replace(suffixes, "");
8+
if ($ref.indexOf("#") === -1) {
9+
return [$ref.replace(suffixes, "")];
10+
}
11+
if ($ref.indexOf("#") === 0) {
12+
return [$ref.replace(suffixes, "")];
13+
}
14+
const result = $ref.split("#");
15+
result[0] = result[0].replace(suffixes, "");
16+
result[1] = `#${result[1].replace(suffixes, "")}`;
17+
return result;
18+
}
19+
;

distmodule/lib/compileSchema.js

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import compile from "./compile";
2+
export default compile;

lib/config/settings.js renamed to distmodule/lib/config/settings.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module.exports = {
1+
export default {
22
DECLARATOR_ONEOF: "oneOfProperty",
33
GET_TEMPLATE_RECURSION_LIMIT: 1,
44
floatingPointPrecision: 10000,

lib/config/strings.js renamed to distmodule/lib/config/strings.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint max-len: 0 */
2-
module.exports = {
2+
export default {
33
// validation errors
44
AdditionalItemsError: "Array at `{{pointer}}` may not have an additional item `{{key}}`",
55
AdditionalPropertiesError: "Additional property `{{property}}` on `{{pointer}}` does not match schema `{{schema}}`",

0 commit comments

Comments
 (0)