From d8556a9b1e2e71f0efbce24b5a6ac42bb419268d Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Tue, 3 Dec 2019 11:52:08 +0900 Subject: [PATCH 1/9] Install ajv --- package-lock.json | 6 +++--- package.json | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4300fd2..8d625fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -581,9 +581,9 @@ "dev": true }, "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", "dev": true, "requires": { "fast-deep-equal": "^2.0.1", diff --git a/package.json b/package.json index 726e909..4f8fa07 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@types/sinon": "^7.0.13", "@typescript-eslint/eslint-plugin": "^2.0.0", "@typescript-eslint/parser": "^2.0.0", + "ajv": "^6.10.2", "chai": "^4.2.0", "css-loader": "^3.2.0", "eslint": "^6.2.2", From 3e2ebb7797f6e12777b6da943765ff172bd179a9 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Tue, 3 Dec 2019 14:29:36 +0900 Subject: [PATCH 2/9] Validate json settings with ajv --- src/shared/settings/Blacklist.ts | 66 ++++++++++++------------------ src/shared/settings/Keymaps.ts | 29 +++++++++---- src/shared/settings/Properties.ts | 37 +++++++++-------- src/shared/settings/Search.ts | 68 ++++++++++++------------------- src/shared/settings/Validator.ts | 20 +++++++++ 5 files changed, 112 insertions(+), 108 deletions(-) create mode 100644 src/shared/settings/Validator.ts diff --git a/src/shared/settings/Blacklist.ts b/src/shared/settings/Blacklist.ts index 0cfbd71..201e7fc 100644 --- a/src/shared/settings/Blacklist.ts +++ b/src/shared/settings/Blacklist.ts @@ -1,4 +1,23 @@ import Key from './Key'; +import Validator from './Validator'; + +const ItemSchema = { + anyOf: [ + { type: 'string' }, + { + type: 'object', + properties: { + url: { type: 'string' }, + keys: { + type: 'array', + items: { type: 'string', minLength: 1 }, + minItems: 1, + } + }, + required: ['url', 'keys'], + } + ], +}; export type BlacklistItemJSON = string | { url: string, @@ -12,18 +31,6 @@ const regexFromWildcard = (pattern: string): RegExp => { return new RegExp(regexStr); }; -const isArrayOfString = (raw: any): boolean => { - if (!Array.isArray(raw)) { - return false; - } - for (let x of Array.from(raw)) { - if (typeof x !== 'string') { - return false; - } - } - return true; -}; - export class BlacklistItem { public readonly pattern: string; @@ -47,30 +54,11 @@ export class BlacklistItem { this.keyEntities = this.keys.map(Key.fromMapKey); } - static fromJSON(raw: any): BlacklistItem { - if (typeof raw === 'string') { - return new BlacklistItem(raw, false, []); - } else if (typeof raw === 'object' && raw !== null) { - if (!('url' in raw)) { - throw new TypeError( - `missing field "url" of blacklist item: ${JSON.stringify(raw)}`); - } - if (typeof raw.url !== 'string') { - throw new TypeError( - `invalid field "url" of blacklist item: ${JSON.stringify(raw)}`); - } - if (!('keys' in raw)) { - throw new TypeError( - `missing field "keys" of blacklist item: ${JSON.stringify(raw)}`); - } - if (!isArrayOfString(raw.keys)) { - throw new TypeError( - `invalid field "keys" of blacklist item: ${JSON.stringify(raw)}`); - } - return new BlacklistItem(raw.url as string, true, raw.keys as string[]); - } - throw new TypeError( - `invalid format of blacklist item: ${JSON.stringify(raw)}`); + static fromJSON(json: unknown): BlacklistItem { + let obj = new Validator(ItemSchema).validate(json); + return typeof obj === 'string' + ? new BlacklistItem(obj, false, []) + : new BlacklistItem(obj.url, true, obj.keys); } toJSON(): BlacklistItemJSON { @@ -103,11 +91,11 @@ export default class Blacklist { ) { } - static fromJSON(json: any): Blacklist { + static fromJSON(json: unknown): Blacklist { if (!Array.isArray(json)) { - throw new TypeError('blacklist is not an array: ' + JSON.stringify(json)); + throw new TypeError('blacklist is not an array'); } - let items = Array.from(json).map(item => BlacklistItem.fromJSON(item)); + let items = json.map(o => BlacklistItem.fromJSON(o)); return new Blacklist(items); } diff --git a/src/shared/settings/Keymaps.ts b/src/shared/settings/Keymaps.ts index a5558b0..7e510d1 100644 --- a/src/shared/settings/Keymaps.ts +++ b/src/shared/settings/Keymaps.ts @@ -1,4 +1,18 @@ import * as operations from '../operations'; +import Validator from './Validator'; + +const Schema = { + type: 'object', + patternProperties: { + '.*': { + type: 'object', + properties: { + type: { type: 'string' }, + }, + required: ['type'], + }, + } +}; export type KeymapsJSON = { [key: string]: operations.Operation }; @@ -8,16 +22,13 @@ export default class Keymaps { ) { } - static fromJSON(json: any): Keymaps { - if (typeof json !== 'object' || json === null) { - throw new TypeError('invalid keymaps type: ' + JSON.stringify(json)); - } - - let data: KeymapsJSON = {}; - for (let key of Object.keys(json)) { - data[key] = operations.valueOf(json[key]); + static fromJSON(json: unknown): Keymaps { + let obj = new Validator(Schema).validate(json); + let entries: KeymapsJSON = {}; + for (let key of Object.keys(obj)) { + entries[key] = operations.valueOf(obj[key]); } - return new Keymaps(data); + return new Keymaps(entries); } combine(other: Keymaps): Keymaps { diff --git a/src/shared/settings/Properties.ts b/src/shared/settings/Properties.ts index 63ff991..9cdaffe 100644 --- a/src/shared/settings/Properties.ts +++ b/src/shared/settings/Properties.ts @@ -1,3 +1,20 @@ +import Validator from './Validator'; + +const Schema = { + type: 'object', + properties: { + hintchars: { + type: 'string', + }, + smoothscroll: { + type: 'boolean', + }, + complete: { + type: 'string', + }, + }, +}; + export type PropertiesJSON = { hintchars?: string; smoothscroll?: boolean; @@ -65,23 +82,9 @@ export default class Properties { this.complete = complete || defaultValues.complete; } - static fromJSON(json: any): Properties { - let defNames: Set = new Set(defs.map(def => def.name)); - let unknownName = Object.keys(json).find(name => !defNames.has(name)); - if (unknownName) { - throw new TypeError(`Unknown property name: "${unknownName}"`); - } - - for (let def of defs) { - if (!Object.prototype.hasOwnProperty.call(json, def.name)) { - continue; - } - if (typeof json[def.name] !== def.type) { - throw new TypeError( - `property "${def.name}" is not ${def.type}`); - } - } - return new Properties(json); + static fromJSON(json: unknown): Properties { + let obj = new Validator(Schema).validate(json); + return new Properties(obj); } static types(): PropertyTypes { diff --git a/src/shared/settings/Search.ts b/src/shared/settings/Search.ts index 4580236..bdbe4a8 100644 --- a/src/shared/settings/Search.ts +++ b/src/shared/settings/Search.ts @@ -1,3 +1,22 @@ +import Validator from './Validator'; + +const Schema = { + type: 'object', + properties: { + default: { type: 'string' }, + engines: { + type: 'object', + propertyNames: { + pattern: '^[A-Za-z_][A-Za-z0-9_]+$', + }, + patternProperties: { + '.*': { type: 'string' }, + }, + }, + }, + required: ['default'], +}; + type Entries = { [name: string]: string }; export type SearchJSON = { @@ -12,19 +31,10 @@ export default class Search { ) { } - static fromJSON(json: any): Search { - let defaultEngine = Search.getStringField(json, 'default'); - let engines = Search.getObjectField(json, 'engines'); + static fromJSON(json: unknown): Search { + let obj = new Validator(Schema).validate(json); - for (let [name, url] of Object.entries(engines)) { - if ((/\s/).test(name)) { - throw new TypeError( - `While space in the search engine not allowed: "${name}"`); - } - if (typeof url !== 'string') { - throw new TypeError( - `Invalid type of value in filed "engines": ${JSON.stringify(json)}`); - } + for (let [name, url] of Object.entries(obj.engines)) { let matches = url.match(/{}/g); if (matches === null) { throw new TypeError(`No {}-placeholders in URL of "${name}"`); @@ -32,15 +42,11 @@ export default class Search { throw new TypeError(`Multiple {}-placeholders in URL of "${name}"`); } } - - if (!Object.keys(engines).includes(defaultEngine)) { - throw new TypeError(`Default engine "${defaultEngine}" not found`); + if (!Object.keys(obj.engines).includes(obj.default)) { + throw new TypeError(`Default engine "${obj.default}" not found`); } - return new Search( - json.default as string, - json.engines, - ); + return new Search(obj.default, obj.engines); } toJSON(): SearchJSON { @@ -49,28 +55,4 @@ export default class Search { engines: this.engines, }; } - - private static getStringField(json: any, name: string): string { - if (!Object.prototype.hasOwnProperty.call(json, name)) { - throw new TypeError( - `missing field "${name}" on search: ${JSON.stringify(json)}`); - } - if (typeof json[name] !== 'string') { - throw new TypeError( - `invalid type of filed "${name}" on search: ${JSON.stringify(json)}`); - } - return json[name]; - } - - private static getObjectField(json: any, name: string): Object { - if (!Object.prototype.hasOwnProperty.call(json, name)) { - throw new TypeError( - `missing field "${name}" on search: ${JSON.stringify(json)}`); - } - if (typeof json[name] !== 'object' || json[name] === null) { - throw new TypeError( - `invalid type of filed "${name}" on search: ${JSON.stringify(json)}`); - } - return json[name]; - } } diff --git a/src/shared/settings/Validator.ts b/src/shared/settings/Validator.ts new file mode 100644 index 0000000..6aac07f --- /dev/null +++ b/src/shared/settings/Validator.ts @@ -0,0 +1,20 @@ +import Ajv from 'ajv'; + +export default class Validator { + constructor( + private schema: object | boolean, + ) { + } + + validate(data: any): T { + let ajv = new Ajv(); + let valid = ajv.validate(this.schema, data); + if (!valid) { + let message = ajv.errors!! + .map(err => `'${err.dataPath}' of ${err.keyword} ${err.message}`) + .join('; '); + throw new TypeError(message); + } + return data as T; + } +} From 470ace2f02fe8a632a3692b55a98be6f829a3308 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Tue, 3 Dec 2019 16:52:53 +0900 Subject: [PATCH 3/9] Install ajv-cli --- package-lock.json | 176 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 177 insertions(+) diff --git a/package-lock.json b/package-lock.json index 8d625fe..b844719 100644 --- a/package-lock.json +++ b/package-lock.json @@ -592,6 +592,28 @@ "uri-js": "^4.2.2" } }, + "ajv-cli": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ajv-cli/-/ajv-cli-3.0.0.tgz", + "integrity": "sha1-WCMjH2TigzBUEwaQsYCZYJ6YDyk=", + "dev": true, + "requires": { + "ajv": "^6.0.0", + "ajv-pack": "^0.3.0", + "fast-json-patch": "^0.5.6", + "glob": "^7.0.3", + "json-schema-migrate": "^0.2.0", + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, "ajv-errors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", @@ -604,6 +626,16 @@ "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", "dev": true }, + "ajv-pack": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/ajv-pack/-/ajv-pack-0.3.1.tgz", + "integrity": "sha1-tyxNQhnjko5ihC10Le2Tv1B5ZWA=", + "dev": true, + "requires": { + "js-beautify": "^1.6.4", + "require-from-string": "^1.2.0" + } + }, "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", @@ -1576,6 +1608,12 @@ } } }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -1670,6 +1708,16 @@ "typedarray": "^0.0.6" } }, + "config-chain": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "dev": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, "connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", @@ -2226,6 +2274,36 @@ "safe-buffer": "^5.0.1" } }, + "editorconfig": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", + "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==", + "dev": true, + "requires": { + "commander": "^2.19.0", + "lru-cache": "^4.1.5", + "semver": "^5.6.0", + "sigmund": "^1.0.1" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + } + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -3077,6 +3155,12 @@ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", "dev": true }, + "fast-json-patch": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-0.5.7.tgz", + "integrity": "sha1-taj0nSWWJFlu+YuHLz/aiVtNhmU=", + "dev": true + }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", @@ -4946,6 +5030,45 @@ "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==", "dev": true }, + "js-beautify": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.10.2.tgz", + "integrity": "sha512-ZtBYyNUYJIsBWERnQP0rPN9KjkrDfJcMjuVGcvXOUJrD1zmOGwhRwQ4msG+HJ+Ni/FA7+sRQEMYVzdTQDvnzvQ==", + "dev": true, + "requires": { + "config-chain": "^1.1.12", + "editorconfig": "^0.15.3", + "glob": "^7.1.3", + "mkdirp": "~0.5.1", + "nopt": "~4.0.1" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "dev": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + } + } + }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -4980,6 +5103,41 @@ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", "dev": true }, + "json-schema-migrate": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json-schema-migrate/-/json-schema-migrate-0.2.0.tgz", + "integrity": "sha1-ukelsAcvxyOWRg4b1gtE1SF4u8Y=", + "dev": true, + "requires": { + "ajv": "^5.0.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + } + } + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -7357,6 +7515,12 @@ } } }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "dev": true + }, "proxy-addr": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", @@ -7884,6 +8048,12 @@ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, + "require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=", + "dev": true + }, "require-main-filename": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", @@ -8339,6 +8509,12 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", diff --git a/package.json b/package.json index 4f8fa07..be027e1 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@typescript-eslint/eslint-plugin": "^2.0.0", "@typescript-eslint/parser": "^2.0.0", "ajv": "^6.10.2", + "ajv-cli": "^3.0.0", "chai": "^4.2.0", "css-loader": "^3.2.0", "eslint": "^6.2.2", From 81b44a6cc9f9ec4af1af074ae061f6206c3bcd03 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Tue, 3 Dec 2019 17:22:56 +0900 Subject: [PATCH 4/9] Add settings schema and compile script --- package.json | 1 + src/shared/settings/schema.json | 82 +++++ src/shared/settings/validate.js | 537 ++++++++++++++++++++++++++++++++ 3 files changed, 620 insertions(+) create mode 100644 src/shared/settings/schema.json create mode 100644 src/shared/settings/validate.js diff --git a/package.json b/package.json index be027e1..ff6ecdc 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "vim-vixen", "description": "Vim vixen", "scripts": { + "schema": "ajv compile -s src/shared/settings/schema.json -o src/shared/settings/validate.js", "start": "webpack --mode development -w --debug --devtool inline-source-map", "build": "NODE_ENV=production webpack --mode production --progress --display-error-details --devtool inline-source-map", "package": "npm run build && script/package", diff --git a/src/shared/settings/schema.json b/src/shared/settings/schema.json new file mode 100644 index 0000000..86add81 --- /dev/null +++ b/src/shared/settings/schema.json @@ -0,0 +1,82 @@ +{ + "type": "object", + "properties": { + "keymaps": { + "type": "object", + "patternProperties": { + ".*": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": [ + "type" + ] + } + } + }, + "search": { + "type": "object", + "properties": { + "default": { + "type": "string" + }, + "engines": { + "type": "object", + "patternProperties": { + ".*": { + "type": "string" + } + } + } + }, + "required": [ + "default" + ] + }, + "properties": { + "type": "object", + "properties": { + "hintchars": { + "type": "string" + }, + "smoothscroll": { + "type": "boolean" + }, + "complete": { + "type": "string" + } + } + }, + "blacklist": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "keys": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "url", + "keys" + ] + } + ] + } + } + } +} diff --git a/src/shared/settings/validate.js b/src/shared/settings/validate.js new file mode 100644 index 0000000..9de41d7 --- /dev/null +++ b/src/shared/settings/validate.js @@ -0,0 +1,537 @@ +'use strict'; +var validate = (function() { + var pattern0 = new RegExp('.*'); + var refVal = []; + return function validate(data, dataPath, parentData, parentDataProperty, rootData) { + 'use strict'; + var vErrors = null; + var errors = 0; + if ((data && typeof data === "object" && !Array.isArray(data))) { + var errs__0 = errors; + var valid1 = true; + var data1 = data.keymaps; + if (data1 === undefined) { + valid1 = true; + } else { + var errs_1 = errors; + if ((data1 && typeof data1 === "object" && !Array.isArray(data1))) { + var errs__1 = errors; + var valid2 = true; + for (var key1 in data1) { + if (pattern0.test(key1)) { + var data2 = data1[key1]; + var errs_2 = errors; + if ((data2 && typeof data2 === "object" && !Array.isArray(data2))) { + if (true) { + var errs__2 = errors; + var valid3 = true; + if (data2.type === undefined) { + valid3 = false; + validate.errors = [{ + keyword: 'required', + dataPath: (dataPath || '') + '.keymaps[\'' + key1 + '\']', + schemaPath: '#/properties/keymaps/patternProperties/.*/required', + params: { + missingProperty: 'type' + }, + message: 'should have required property \'type\'' + }]; + return false; + } else { + var errs_3 = errors; + if (typeof data2.type !== "string") { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.keymaps[\'' + key1 + '\'].type', + schemaPath: '#/properties/keymaps/patternProperties/.*/properties/type/type', + params: { + type: 'string' + }, + message: 'should be string' + }]; + return false; + } + var valid3 = errors === errs_3; + } + } + } else { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.keymaps[\'' + key1 + '\']', + schemaPath: '#/properties/keymaps/patternProperties/.*/type', + params: { + type: 'object' + }, + message: 'should be object' + }]; + return false; + } + var valid2 = errors === errs_2; + if (!valid2) break; + } else valid2 = true; + } + } else { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.keymaps', + schemaPath: '#/properties/keymaps/type', + params: { + type: 'object' + }, + message: 'should be object' + }]; + return false; + } + var valid1 = errors === errs_1; + } + if (valid1) { + var data1 = data.search; + if (data1 === undefined) { + valid1 = true; + } else { + var errs_1 = errors; + if ((data1 && typeof data1 === "object" && !Array.isArray(data1))) { + if (true) { + var errs__1 = errors; + var valid2 = true; + if (data1.default === undefined) { + valid2 = false; + validate.errors = [{ + keyword: 'required', + dataPath: (dataPath || '') + '.search', + schemaPath: '#/properties/search/required', + params: { + missingProperty: 'default' + }, + message: 'should have required property \'default\'' + }]; + return false; + } else { + var errs_2 = errors; + if (typeof data1.default !== "string") { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.search.default', + schemaPath: '#/properties/search/properties/default/type', + params: { + type: 'string' + }, + message: 'should be string' + }]; + return false; + } + var valid2 = errors === errs_2; + } + if (valid2) { + var data2 = data1.engines; + if (data2 === undefined) { + valid2 = true; + } else { + var errs_2 = errors; + if ((data2 && typeof data2 === "object" && !Array.isArray(data2))) { + var errs__2 = errors; + var valid3 = true; + for (var key2 in data2) { + if (pattern0.test(key2)) { + var errs_3 = errors; + if (typeof data2[key2] !== "string") { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.search.engines[\'' + key2 + '\']', + schemaPath: '#/properties/search/properties/engines/patternProperties/.*/type', + params: { + type: 'string' + }, + message: 'should be string' + }]; + return false; + } + var valid3 = errors === errs_3; + if (!valid3) break; + } else valid3 = true; + } + } else { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.search.engines', + schemaPath: '#/properties/search/properties/engines/type', + params: { + type: 'object' + }, + message: 'should be object' + }]; + return false; + } + var valid2 = errors === errs_2; + } + } + } + } else { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.search', + schemaPath: '#/properties/search/type', + params: { + type: 'object' + }, + message: 'should be object' + }]; + return false; + } + var valid1 = errors === errs_1; + } + if (valid1) { + var data1 = data.properties; + if (data1 === undefined) { + valid1 = true; + } else { + var errs_1 = errors; + if ((data1 && typeof data1 === "object" && !Array.isArray(data1))) { + var errs__1 = errors; + var valid2 = true; + if (data1.hintchars === undefined) { + valid2 = true; + } else { + var errs_2 = errors; + if (typeof data1.hintchars !== "string") { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.properties.hintchars', + schemaPath: '#/properties/properties/properties/hintchars/type', + params: { + type: 'string' + }, + message: 'should be string' + }]; + return false; + } + var valid2 = errors === errs_2; + } + if (valid2) { + if (data1.smoothscroll === undefined) { + valid2 = true; + } else { + var errs_2 = errors; + if (typeof data1.smoothscroll !== "boolean") { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.properties.smoothscroll', + schemaPath: '#/properties/properties/properties/smoothscroll/type', + params: { + type: 'boolean' + }, + message: 'should be boolean' + }]; + return false; + } + var valid2 = errors === errs_2; + } + if (valid2) { + if (data1.complete === undefined) { + valid2 = true; + } else { + var errs_2 = errors; + if (typeof data1.complete !== "string") { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.properties.complete', + schemaPath: '#/properties/properties/properties/complete/type', + params: { + type: 'string' + }, + message: 'should be string' + }]; + return false; + } + var valid2 = errors === errs_2; + } + } + } + } else { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.properties', + schemaPath: '#/properties/properties/type', + params: { + type: 'object' + }, + message: 'should be object' + }]; + return false; + } + var valid1 = errors === errs_1; + } + if (valid1) { + var data1 = data.blacklist; + if (data1 === undefined) { + valid1 = true; + } else { + var errs_1 = errors; + if (Array.isArray(data1)) { + var errs__1 = errors; + var valid1; + for (var i1 = 0; i1 < data1.length; i1++) { + var data2 = data1[i1]; + var errs_2 = errors; + var errs__2 = errors; + var valid2 = false; + var errs_3 = errors; + if (typeof data2 !== "string") { + var err = { + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', + schemaPath: '#/properties/blacklist/items/anyOf/0/type', + params: { + type: 'string' + }, + message: 'should be string' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } + var valid3 = errors === errs_3; + valid2 = valid2 || valid3; + if (!valid2) { + var errs_3 = errors; + if ((data2 && typeof data2 === "object" && !Array.isArray(data2))) { + if (true) { + var errs__3 = errors; + var valid4 = true; + if (data2.url === undefined) { + valid4 = false; + var err = { + keyword: 'required', + dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', + schemaPath: '#/properties/blacklist/items/anyOf/1/required', + params: { + missingProperty: 'url' + }, + message: 'should have required property \'url\'' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } else { + var errs_4 = errors; + if (typeof data2.url !== "string") { + var err = { + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist[' + i1 + '].url', + schemaPath: '#/properties/blacklist/items/anyOf/1/properties/url/type', + params: { + type: 'string' + }, + message: 'should be string' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } + var valid4 = errors === errs_4; + } + if (valid4) { + var data3 = data2.keys; + if (data3 === undefined) { + valid4 = false; + var err = { + keyword: 'required', + dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', + schemaPath: '#/properties/blacklist/items/anyOf/1/required', + params: { + missingProperty: 'keys' + }, + message: 'should have required property \'keys\'' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } else { + var errs_4 = errors; + if (Array.isArray(data3)) { + var errs__4 = errors; + var valid4; + for (var i4 = 0; i4 < data3.length; i4++) { + var errs_5 = errors; + if (typeof data3[i4] !== "string") { + var err = { + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist[' + i1 + '].keys[' + i4 + ']', + schemaPath: '#/properties/blacklist/items/anyOf/1/properties/keys/items/type', + params: { + type: 'string' + }, + message: 'should be string' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } + var valid5 = errors === errs_5; + if (!valid5) break; + } + } else { + var err = { + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist[' + i1 + '].keys', + schemaPath: '#/properties/blacklist/items/anyOf/1/properties/keys/type', + params: { + type: 'array' + }, + message: 'should be array' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } + var valid4 = errors === errs_4; + } + } + } + } else { + var err = { + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', + schemaPath: '#/properties/blacklist/items/anyOf/1/type', + params: { + type: 'object' + }, + message: 'should be object' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } + var valid3 = errors === errs_3; + valid2 = valid2 || valid3; + } + if (!valid2) { + var err = { + keyword: 'anyOf', + dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', + schemaPath: '#/properties/blacklist/items/anyOf', + params: {}, + message: 'should match some schema in anyOf' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + validate.errors = vErrors; + return false; + } else { + errors = errs__2; + if (vErrors !== null) { + if (errs__2) vErrors.length = errs__2; + else vErrors = null; + } + } + var valid2 = errors === errs_2; + if (!valid2) break; + } + } else { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist', + schemaPath: '#/properties/blacklist/type', + params: { + type: 'array' + }, + message: 'should be array' + }]; + return false; + } + var valid1 = errors === errs_1; + } + } + } + } + } else { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + "", + schemaPath: '#/type', + params: { + type: 'object' + }, + message: 'should be object' + }]; + return false; + } + validate.errors = vErrors; + return errors === 0; + }; +})(); +validate.schema = { + "type": "object", + "properties": { + "keymaps": { + "type": "object", + "patternProperties": { + ".*": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": ["type"] + } + } + }, + "search": { + "type": "object", + "properties": { + "default": { + "type": "string" + }, + "engines": { + "type": "object", + "patternProperties": { + ".*": { + "type": "string" + } + } + } + }, + "required": ["default"] + }, + "properties": { + "type": "object", + "properties": { + "hintchars": { + "type": "string" + }, + "smoothscroll": { + "type": "boolean" + }, + "complete": { + "type": "string" + } + } + }, + "blacklist": { + "type": "array", + "items": { + "anyOf": [{ + "type": "string" + }, { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "keys": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["url", "keys"] + }] + } + } + } +}; +validate.errors = null; +module.exports = validate; \ No newline at end of file From 2318e3a55592cf7888006bd1afe43deec396d394 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Tue, 3 Dec 2019 17:51:26 +0900 Subject: [PATCH 5/9] Disable type checking on JS --- tsconfig.json | 2 +- webpack.config.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 9f90223..f3a6a33 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "module": "commonjs", "lib": ["es6", "dom", "es2017"], "allowJs": true, - "checkJs": true, + "checkJs": false, "jsx": "react", "sourceMap": true, "outDir": "./build", diff --git a/webpack.config.js b/webpack.config.js index 1cd05df..ee252bf 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -4,7 +4,7 @@ const path = require('path'); const src = path.resolve(__dirname, 'src'); const dist = path.resolve(__dirname, 'build'); -config = { +const config = { entry: { content: path.join(src, 'content'), settings: path.join(src, 'settings'), From 776977e0dcaf47a81054dcc81d76722ada71f9eb Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Tue, 3 Dec 2019 18:11:32 +0900 Subject: [PATCH 6/9] Validate on top-level settings and use pre-compiled ajv --- src/shared/settings/Blacklist.ts | 33 +- src/shared/settings/Keymaps.ts | 31 +- src/shared/settings/Properties.ts | 21 +- src/shared/settings/Search.ts | 34 +- src/shared/settings/Settings.ts | 52 ++- src/shared/settings/Validator.ts | 20 - src/shared/settings/schema.json | 3 +- src/shared/settings/validate.js | 618 +++++++++++++------------ test/shared/settings/Blacklist.test.ts | 18 - test/shared/settings/Keymaps.test.ts | 1 - test/shared/settings/Search.test.ts | 13 - 11 files changed, 377 insertions(+), 467 deletions(-) delete mode 100644 src/shared/settings/Validator.ts diff --git a/src/shared/settings/Blacklist.ts b/src/shared/settings/Blacklist.ts index 201e7fc..1903a78 100644 --- a/src/shared/settings/Blacklist.ts +++ b/src/shared/settings/Blacklist.ts @@ -1,23 +1,4 @@ import Key from './Key'; -import Validator from './Validator'; - -const ItemSchema = { - anyOf: [ - { type: 'string' }, - { - type: 'object', - properties: { - url: { type: 'string' }, - keys: { - type: 'array', - items: { type: 'string', minLength: 1 }, - minItems: 1, - } - }, - required: ['url', 'keys'], - } - ], -}; export type BlacklistItemJSON = string | { url: string, @@ -54,11 +35,10 @@ export class BlacklistItem { this.keyEntities = this.keys.map(Key.fromMapKey); } - static fromJSON(json: unknown): BlacklistItem { - let obj = new Validator(ItemSchema).validate(json); - return typeof obj === 'string' - ? new BlacklistItem(obj, false, []) - : new BlacklistItem(obj.url, true, obj.keys); + static fromJSON(json: BlacklistItemJSON): BlacklistItem { + return typeof json === 'string' + ? new BlacklistItem(json, false, []) + : new BlacklistItem(json.url, true, json.keys); } toJSON(): BlacklistItemJSON { @@ -91,10 +71,7 @@ export default class Blacklist { ) { } - static fromJSON(json: unknown): Blacklist { - if (!Array.isArray(json)) { - throw new TypeError('blacklist is not an array'); - } + static fromJSON(json: BlacklistJSON): Blacklist { let items = json.map(o => BlacklistItem.fromJSON(o)); return new Blacklist(items); } diff --git a/src/shared/settings/Keymaps.ts b/src/shared/settings/Keymaps.ts index 7e510d1..5870313 100644 --- a/src/shared/settings/Keymaps.ts +++ b/src/shared/settings/Keymaps.ts @@ -1,32 +1,23 @@ import * as operations from '../operations'; -import Validator from './Validator'; -const Schema = { - type: 'object', - patternProperties: { - '.*': { - type: 'object', - properties: { - type: { type: 'string' }, - }, - required: ['type'], - }, - } +type OperationJson = { + type: string +} | { + type: string; + [prop: string]: string | number | boolean; }; - -export type KeymapsJSON = { [key: string]: operations.Operation }; +export type KeymapsJSON = { [key: string]: OperationJson }; export default class Keymaps { constructor( - private readonly data: KeymapsJSON, + private readonly data: { [key: string]: operations.Operation }, ) { } - static fromJSON(json: unknown): Keymaps { - let obj = new Validator(Schema).validate(json); - let entries: KeymapsJSON = {}; - for (let key of Object.keys(obj)) { - entries[key] = operations.valueOf(obj[key]); + static fromJSON(json: KeymapsJSON): Keymaps { + let entries: { [key: string]: operations.Operation } = {}; + for (let key of Object.keys(json)) { + entries[key] = operations.valueOf(json[key]); } return new Keymaps(entries); } diff --git a/src/shared/settings/Properties.ts b/src/shared/settings/Properties.ts index 9cdaffe..27fb62e 100644 --- a/src/shared/settings/Properties.ts +++ b/src/shared/settings/Properties.ts @@ -1,19 +1,3 @@ -import Validator from './Validator'; - -const Schema = { - type: 'object', - properties: { - hintchars: { - type: 'string', - }, - smoothscroll: { - type: 'boolean', - }, - complete: { - type: 'string', - }, - }, -}; export type PropertiesJSON = { hintchars?: string; @@ -82,9 +66,8 @@ export default class Properties { this.complete = complete || defaultValues.complete; } - static fromJSON(json: unknown): Properties { - let obj = new Validator(Schema).validate(json); - return new Properties(obj); + static fromJSON(json: PropertiesJSON): Properties { + return new Properties(json); } static types(): PropertyTypes { diff --git a/src/shared/settings/Search.ts b/src/shared/settings/Search.ts index bdbe4a8..7d7e555 100644 --- a/src/shared/settings/Search.ts +++ b/src/shared/settings/Search.ts @@ -1,22 +1,3 @@ -import Validator from './Validator'; - -const Schema = { - type: 'object', - properties: { - default: { type: 'string' }, - engines: { - type: 'object', - propertyNames: { - pattern: '^[A-Za-z_][A-Za-z0-9_]+$', - }, - patternProperties: { - '.*': { type: 'string' }, - }, - }, - }, - required: ['default'], -}; - type Entries = { [name: string]: string }; export type SearchJSON = { @@ -31,10 +12,11 @@ export default class Search { ) { } - static fromJSON(json: unknown): Search { - let obj = new Validator(Schema).validate(json); - - for (let [name, url] of Object.entries(obj.engines)) { + static fromJSON(json: SearchJSON): Search { + for (let [name, url] of Object.entries(json.engines)) { + if (!(/^[a-zA-Z0-9]+$/).test(name)) { + throw new TypeError('Search engine\'s name must be [a-zA-Z0-9]+'); + } let matches = url.match(/{}/g); if (matches === null) { throw new TypeError(`No {}-placeholders in URL of "${name}"`); @@ -42,11 +24,11 @@ export default class Search { throw new TypeError(`Multiple {}-placeholders in URL of "${name}"`); } } - if (!Object.keys(obj.engines).includes(obj.default)) { - throw new TypeError(`Default engine "${obj.default}" not found`); + if (!Object.keys(json.engines).includes(json.default)) { + throw new TypeError(`Default engine "${json.default}" not found`); } - return new Search(obj.default, obj.engines); + return new Search(json.default, json.engines); } toJSON(): SearchJSON { diff --git a/src/shared/settings/Settings.ts b/src/shared/settings/Settings.ts index 2c9e37f..f142717 100644 --- a/src/shared/settings/Settings.ts +++ b/src/shared/settings/Settings.ts @@ -1,13 +1,16 @@ +import Ajv from 'ajv'; + import Keymaps, { KeymapsJSON } from './Keymaps'; import Search, { SearchJSON } from './Search'; import Properties, { PropertiesJSON } from './Properties'; import Blacklist, { BlacklistJSON } from './Blacklist'; +import validate from './validate'; export type SettingsJSON = { - keymaps: KeymapsJSON, - search: SearchJSON, - properties: PropertiesJSON, - blacklist: BlacklistJSON, + keymaps?: KeymapsJSON, + search?: SearchJSON, + properties?: PropertiesJSON, + blacklist?: BlacklistJSON, }; export default class Settings { @@ -36,25 +39,30 @@ export default class Settings { this.blacklist = blacklist; } - static fromJSON(json: any): Settings { + static fromJSON(json: unknown): Settings { + let valid = validate(json); + if (!valid) { + let message = (validate as any).errors!! + .map((err: Ajv.ErrorObject) => { + return `'${err.dataPath}' of ${err.keyword} ${err.message}`; + }) + .join('; '); + throw new TypeError(message); + } + + let obj = json as SettingsJSON; let settings = { ...DefaultSetting }; - for (let key of Object.keys(json)) { - switch (key) { - case 'keymaps': - settings.keymaps = Keymaps.fromJSON(json.keymaps); - break; - case 'search': - settings.search = Search.fromJSON(json.search); - break; - case 'properties': - settings.properties = Properties.fromJSON(json.properties); - break; - case 'blacklist': - settings.blacklist = Blacklist.fromJSON(json.blacklist); - break; - default: - throw new TypeError('unknown setting: ' + key); - } + if (obj.keymaps) { + settings.keymaps = Keymaps.fromJSON(obj.keymaps); + } + if (obj.search) { + settings.search = Search.fromJSON(obj.search); + } + if (obj.properties) { + settings.properties = Properties.fromJSON(obj.properties); + } + if (obj.blacklist) { + settings.blacklist = Blacklist.fromJSON(obj.blacklist); } return new Settings(settings); } diff --git a/src/shared/settings/Validator.ts b/src/shared/settings/Validator.ts deleted file mode 100644 index 6aac07f..0000000 --- a/src/shared/settings/Validator.ts +++ /dev/null @@ -1,20 +0,0 @@ -import Ajv from 'ajv'; - -export default class Validator { - constructor( - private schema: object | boolean, - ) { - } - - validate(data: any): T { - let ajv = new Ajv(); - let valid = ajv.validate(this.schema, data); - if (!valid) { - let message = ajv.errors!! - .map(err => `'${err.dataPath}' of ${err.keyword} ${err.message}`) - .join('; '); - throw new TypeError(message); - } - return data as T; - } -} diff --git a/src/shared/settings/schema.json b/src/shared/settings/schema.json index 86add81..2e2f62a 100644 --- a/src/shared/settings/schema.json +++ b/src/shared/settings/schema.json @@ -78,5 +78,6 @@ ] } } - } + }, + "additionalProperties": false } diff --git a/src/shared/settings/validate.js b/src/shared/settings/validate.js index 9de41d7..c761cee 100644 --- a/src/shared/settings/validate.js +++ b/src/shared/settings/validate.js @@ -9,168 +9,90 @@ var validate = (function() { if ((data && typeof data === "object" && !Array.isArray(data))) { var errs__0 = errors; var valid1 = true; - var data1 = data.keymaps; - if (data1 === undefined) { - valid1 = true; - } else { - var errs_1 = errors; - if ((data1 && typeof data1 === "object" && !Array.isArray(data1))) { - var errs__1 = errors; - var valid2 = true; - for (var key1 in data1) { - if (pattern0.test(key1)) { - var data2 = data1[key1]; - var errs_2 = errors; - if ((data2 && typeof data2 === "object" && !Array.isArray(data2))) { - if (true) { - var errs__2 = errors; - var valid3 = true; - if (data2.type === undefined) { - valid3 = false; - validate.errors = [{ - keyword: 'required', - dataPath: (dataPath || '') + '.keymaps[\'' + key1 + '\']', - schemaPath: '#/properties/keymaps/patternProperties/.*/required', - params: { - missingProperty: 'type' - }, - message: 'should have required property \'type\'' - }]; - return false; - } else { - var errs_3 = errors; - if (typeof data2.type !== "string") { - validate.errors = [{ - keyword: 'type', - dataPath: (dataPath || '') + '.keymaps[\'' + key1 + '\'].type', - schemaPath: '#/properties/keymaps/patternProperties/.*/properties/type/type', - params: { - type: 'string' - }, - message: 'should be string' - }]; - return false; - } - var valid3 = errors === errs_3; - } - } - } else { - validate.errors = [{ - keyword: 'type', - dataPath: (dataPath || '') + '.keymaps[\'' + key1 + '\']', - schemaPath: '#/properties/keymaps/patternProperties/.*/type', - params: { - type: 'object' - }, - message: 'should be object' - }]; - return false; - } - var valid2 = errors === errs_2; - if (!valid2) break; - } else valid2 = true; - } - } else { + for (var key0 in data) { + var isAdditional0 = !(false || key0 == 'keymaps' || key0 == 'search' || key0 == 'properties' || key0 == 'blacklist'); + if (isAdditional0) { + valid1 = false; validate.errors = [{ - keyword: 'type', - dataPath: (dataPath || '') + '.keymaps', - schemaPath: '#/properties/keymaps/type', + keyword: 'additionalProperties', + dataPath: (dataPath || '') + "", + schemaPath: '#/additionalProperties', params: { - type: 'object' + additionalProperty: '' + key0 + '' }, - message: 'should be object' + message: 'should NOT have additional properties' }]; return false; + break; } - var valid1 = errors === errs_1; } if (valid1) { - var data1 = data.search; + var data1 = data.keymaps; if (data1 === undefined) { valid1 = true; } else { var errs_1 = errors; if ((data1 && typeof data1 === "object" && !Array.isArray(data1))) { - if (true) { - var errs__1 = errors; - var valid2 = true; - if (data1.default === undefined) { - valid2 = false; - validate.errors = [{ - keyword: 'required', - dataPath: (dataPath || '') + '.search', - schemaPath: '#/properties/search/required', - params: { - missingProperty: 'default' - }, - message: 'should have required property \'default\'' - }]; - return false; - } else { + var errs__1 = errors; + var valid2 = true; + for (var key1 in data1) { + if (pattern0.test(key1)) { + var data2 = data1[key1]; var errs_2 = errors; - if (typeof data1.default !== "string") { + if ((data2 && typeof data2 === "object" && !Array.isArray(data2))) { + if (true) { + var errs__2 = errors; + var valid3 = true; + if (data2.type === undefined) { + valid3 = false; + validate.errors = [{ + keyword: 'required', + dataPath: (dataPath || '') + '.keymaps[\'' + key1 + '\']', + schemaPath: '#/properties/keymaps/patternProperties/.*/required', + params: { + missingProperty: 'type' + }, + message: 'should have required property \'type\'' + }]; + return false; + } else { + var errs_3 = errors; + if (typeof data2.type !== "string") { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.keymaps[\'' + key1 + '\'].type', + schemaPath: '#/properties/keymaps/patternProperties/.*/properties/type/type', + params: { + type: 'string' + }, + message: 'should be string' + }]; + return false; + } + var valid3 = errors === errs_3; + } + } + } else { validate.errors = [{ keyword: 'type', - dataPath: (dataPath || '') + '.search.default', - schemaPath: '#/properties/search/properties/default/type', + dataPath: (dataPath || '') + '.keymaps[\'' + key1 + '\']', + schemaPath: '#/properties/keymaps/patternProperties/.*/type', params: { - type: 'string' + type: 'object' }, - message: 'should be string' + message: 'should be object' }]; return false; } var valid2 = errors === errs_2; - } - if (valid2) { - var data2 = data1.engines; - if (data2 === undefined) { - valid2 = true; - } else { - var errs_2 = errors; - if ((data2 && typeof data2 === "object" && !Array.isArray(data2))) { - var errs__2 = errors; - var valid3 = true; - for (var key2 in data2) { - if (pattern0.test(key2)) { - var errs_3 = errors; - if (typeof data2[key2] !== "string") { - validate.errors = [{ - keyword: 'type', - dataPath: (dataPath || '') + '.search.engines[\'' + key2 + '\']', - schemaPath: '#/properties/search/properties/engines/patternProperties/.*/type', - params: { - type: 'string' - }, - message: 'should be string' - }]; - return false; - } - var valid3 = errors === errs_3; - if (!valid3) break; - } else valid3 = true; - } - } else { - validate.errors = [{ - keyword: 'type', - dataPath: (dataPath || '') + '.search.engines', - schemaPath: '#/properties/search/properties/engines/type', - params: { - type: 'object' - }, - message: 'should be object' - }]; - return false; - } - var valid2 = errors === errs_2; - } - } + if (!valid2) break; + } else valid2 = true; } } else { validate.errors = [{ keyword: 'type', - dataPath: (dataPath || '') + '.search', - schemaPath: '#/properties/search/type', + dataPath: (dataPath || '') + '.keymaps', + schemaPath: '#/properties/keymaps/type', params: { type: 'object' }, @@ -181,65 +103,80 @@ var validate = (function() { var valid1 = errors === errs_1; } if (valid1) { - var data1 = data.properties; + var data1 = data.search; if (data1 === undefined) { valid1 = true; } else { var errs_1 = errors; if ((data1 && typeof data1 === "object" && !Array.isArray(data1))) { - var errs__1 = errors; - var valid2 = true; - if (data1.hintchars === undefined) { - valid2 = true; - } else { - var errs_2 = errors; - if (typeof data1.hintchars !== "string") { + if (true) { + var errs__1 = errors; + var valid2 = true; + if (data1.default === undefined) { + valid2 = false; validate.errors = [{ - keyword: 'type', - dataPath: (dataPath || '') + '.properties.hintchars', - schemaPath: '#/properties/properties/properties/hintchars/type', + keyword: 'required', + dataPath: (dataPath || '') + '.search', + schemaPath: '#/properties/search/required', params: { - type: 'string' + missingProperty: 'default' }, - message: 'should be string' + message: 'should have required property \'default\'' }]; return false; - } - var valid2 = errors === errs_2; - } - if (valid2) { - if (data1.smoothscroll === undefined) { - valid2 = true; } else { var errs_2 = errors; - if (typeof data1.smoothscroll !== "boolean") { + if (typeof data1.default !== "string") { validate.errors = [{ keyword: 'type', - dataPath: (dataPath || '') + '.properties.smoothscroll', - schemaPath: '#/properties/properties/properties/smoothscroll/type', + dataPath: (dataPath || '') + '.search.default', + schemaPath: '#/properties/search/properties/default/type', params: { - type: 'boolean' + type: 'string' }, - message: 'should be boolean' + message: 'should be string' }]; return false; } var valid2 = errors === errs_2; } if (valid2) { - if (data1.complete === undefined) { + var data2 = data1.engines; + if (data2 === undefined) { valid2 = true; } else { var errs_2 = errors; - if (typeof data1.complete !== "string") { + if ((data2 && typeof data2 === "object" && !Array.isArray(data2))) { + var errs__2 = errors; + var valid3 = true; + for (var key2 in data2) { + if (pattern0.test(key2)) { + var errs_3 = errors; + if (typeof data2[key2] !== "string") { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.search.engines[\'' + key2 + '\']', + schemaPath: '#/properties/search/properties/engines/patternProperties/.*/type', + params: { + type: 'string' + }, + message: 'should be string' + }]; + return false; + } + var valid3 = errors === errs_3; + if (!valid3) break; + } else valid3 = true; + } + } else { validate.errors = [{ keyword: 'type', - dataPath: (dataPath || '') + '.properties.complete', - schemaPath: '#/properties/properties/properties/complete/type', + dataPath: (dataPath || '') + '.search.engines', + schemaPath: '#/properties/search/properties/engines/type', params: { - type: 'string' + type: 'object' }, - message: 'should be string' + message: 'should be object' }]; return false; } @@ -250,8 +187,8 @@ var validate = (function() { } else { validate.errors = [{ keyword: 'type', - dataPath: (dataPath || '') + '.properties', - schemaPath: '#/properties/properties/type', + dataPath: (dataPath || '') + '.search', + schemaPath: '#/properties/search/type', params: { type: 'object' }, @@ -262,123 +199,148 @@ var validate = (function() { var valid1 = errors === errs_1; } if (valid1) { - var data1 = data.blacklist; + var data1 = data.properties; if (data1 === undefined) { valid1 = true; } else { var errs_1 = errors; - if (Array.isArray(data1)) { + if ((data1 && typeof data1 === "object" && !Array.isArray(data1))) { var errs__1 = errors; - var valid1; - for (var i1 = 0; i1 < data1.length; i1++) { - var data2 = data1[i1]; + var valid2 = true; + if (data1.hintchars === undefined) { + valid2 = true; + } else { var errs_2 = errors; - var errs__2 = errors; - var valid2 = false; - var errs_3 = errors; - if (typeof data2 !== "string") { - var err = { + if (typeof data1.hintchars !== "string") { + validate.errors = [{ keyword: 'type', - dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', - schemaPath: '#/properties/blacklist/items/anyOf/0/type', + dataPath: (dataPath || '') + '.properties.hintchars', + schemaPath: '#/properties/properties/properties/hintchars/type', params: { type: 'string' }, message: 'should be string' - }; - if (vErrors === null) vErrors = [err]; - else vErrors.push(err); - errors++; + }]; + return false; + } + var valid2 = errors === errs_2; + } + if (valid2) { + if (data1.smoothscroll === undefined) { + valid2 = true; + } else { + var errs_2 = errors; + if (typeof data1.smoothscroll !== "boolean") { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.properties.smoothscroll', + schemaPath: '#/properties/properties/properties/smoothscroll/type', + params: { + type: 'boolean' + }, + message: 'should be boolean' + }]; + return false; + } + var valid2 = errors === errs_2; + } + if (valid2) { + if (data1.complete === undefined) { + valid2 = true; + } else { + var errs_2 = errors; + if (typeof data1.complete !== "string") { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.properties.complete', + schemaPath: '#/properties/properties/properties/complete/type', + params: { + type: 'string' + }, + message: 'should be string' + }]; + return false; + } + var valid2 = errors === errs_2; + } } - var valid3 = errors === errs_3; - valid2 = valid2 || valid3; - if (!valid2) { + } + } else { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.properties', + schemaPath: '#/properties/properties/type', + params: { + type: 'object' + }, + message: 'should be object' + }]; + return false; + } + var valid1 = errors === errs_1; + } + if (valid1) { + var data1 = data.blacklist; + if (data1 === undefined) { + valid1 = true; + } else { + var errs_1 = errors; + if (Array.isArray(data1)) { + var errs__1 = errors; + var valid1; + for (var i1 = 0; i1 < data1.length; i1++) { + var data2 = data1[i1]; + var errs_2 = errors; + var errs__2 = errors; + var valid2 = false; var errs_3 = errors; - if ((data2 && typeof data2 === "object" && !Array.isArray(data2))) { - if (true) { - var errs__3 = errors; - var valid4 = true; - if (data2.url === undefined) { - valid4 = false; - var err = { - keyword: 'required', - dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', - schemaPath: '#/properties/blacklist/items/anyOf/1/required', - params: { - missingProperty: 'url' - }, - message: 'should have required property \'url\'' - }; - if (vErrors === null) vErrors = [err]; - else vErrors.push(err); - errors++; - } else { - var errs_4 = errors; - if (typeof data2.url !== "string") { - var err = { - keyword: 'type', - dataPath: (dataPath || '') + '.blacklist[' + i1 + '].url', - schemaPath: '#/properties/blacklist/items/anyOf/1/properties/url/type', - params: { - type: 'string' - }, - message: 'should be string' - }; - if (vErrors === null) vErrors = [err]; - else vErrors.push(err); - errors++; - } - var valid4 = errors === errs_4; - } - if (valid4) { - var data3 = data2.keys; - if (data3 === undefined) { + if (typeof data2 !== "string") { + var err = { + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', + schemaPath: '#/properties/blacklist/items/anyOf/0/type', + params: { + type: 'string' + }, + message: 'should be string' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } + var valid3 = errors === errs_3; + valid2 = valid2 || valid3; + if (!valid2) { + var errs_3 = errors; + if ((data2 && typeof data2 === "object" && !Array.isArray(data2))) { + if (true) { + var errs__3 = errors; + var valid4 = true; + if (data2.url === undefined) { valid4 = false; var err = { keyword: 'required', dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', schemaPath: '#/properties/blacklist/items/anyOf/1/required', params: { - missingProperty: 'keys' + missingProperty: 'url' }, - message: 'should have required property \'keys\'' + message: 'should have required property \'url\'' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } else { var errs_4 = errors; - if (Array.isArray(data3)) { - var errs__4 = errors; - var valid4; - for (var i4 = 0; i4 < data3.length; i4++) { - var errs_5 = errors; - if (typeof data3[i4] !== "string") { - var err = { - keyword: 'type', - dataPath: (dataPath || '') + '.blacklist[' + i1 + '].keys[' + i4 + ']', - schemaPath: '#/properties/blacklist/items/anyOf/1/properties/keys/items/type', - params: { - type: 'string' - }, - message: 'should be string' - }; - if (vErrors === null) vErrors = [err]; - else vErrors.push(err); - errors++; - } - var valid5 = errors === errs_5; - if (!valid5) break; - } - } else { + if (typeof data2.url !== "string") { var err = { keyword: 'type', - dataPath: (dataPath || '') + '.blacklist[' + i1 + '].keys', - schemaPath: '#/properties/blacklist/items/anyOf/1/properties/keys/type', + dataPath: (dataPath || '') + '.blacklist[' + i1 + '].url', + schemaPath: '#/properties/blacklist/items/anyOf/1/properties/url/type', params: { - type: 'array' + type: 'string' }, - message: 'should be array' + message: 'should be string' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); @@ -386,61 +348,118 @@ var validate = (function() { } var valid4 = errors === errs_4; } + if (valid4) { + var data3 = data2.keys; + if (data3 === undefined) { + valid4 = false; + var err = { + keyword: 'required', + dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', + schemaPath: '#/properties/blacklist/items/anyOf/1/required', + params: { + missingProperty: 'keys' + }, + message: 'should have required property \'keys\'' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } else { + var errs_4 = errors; + if (Array.isArray(data3)) { + var errs__4 = errors; + var valid4; + for (var i4 = 0; i4 < data3.length; i4++) { + var errs_5 = errors; + if (typeof data3[i4] !== "string") { + var err = { + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist[' + i1 + '].keys[' + i4 + ']', + schemaPath: '#/properties/blacklist/items/anyOf/1/properties/keys/items/type', + params: { + type: 'string' + }, + message: 'should be string' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } + var valid5 = errors === errs_5; + if (!valid5) break; + } + } else { + var err = { + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist[' + i1 + '].keys', + schemaPath: '#/properties/blacklist/items/anyOf/1/properties/keys/type', + params: { + type: 'array' + }, + message: 'should be array' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } + var valid4 = errors === errs_4; + } + } } + } else { + var err = { + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', + schemaPath: '#/properties/blacklist/items/anyOf/1/type', + params: { + type: 'object' + }, + message: 'should be object' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; } - } else { + var valid3 = errors === errs_3; + valid2 = valid2 || valid3; + } + if (!valid2) { var err = { - keyword: 'type', + keyword: 'anyOf', dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', - schemaPath: '#/properties/blacklist/items/anyOf/1/type', - params: { - type: 'object' - }, - message: 'should be object' + schemaPath: '#/properties/blacklist/items/anyOf', + params: {}, + message: 'should match some schema in anyOf' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; + validate.errors = vErrors; + return false; + } else { + errors = errs__2; + if (vErrors !== null) { + if (errs__2) vErrors.length = errs__2; + else vErrors = null; + } } - var valid3 = errors === errs_3; - valid2 = valid2 || valid3; - } - if (!valid2) { - var err = { - keyword: 'anyOf', - dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', - schemaPath: '#/properties/blacklist/items/anyOf', - params: {}, - message: 'should match some schema in anyOf' - }; - if (vErrors === null) vErrors = [err]; - else vErrors.push(err); - errors++; - validate.errors = vErrors; - return false; - } else { - errors = errs__2; - if (vErrors !== null) { - if (errs__2) vErrors.length = errs__2; - else vErrors = null; - } + var valid2 = errors === errs_2; + if (!valid2) break; } - var valid2 = errors === errs_2; - if (!valid2) break; + } else { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist', + schemaPath: '#/properties/blacklist/type', + params: { + type: 'array' + }, + message: 'should be array' + }]; + return false; } - } else { - validate.errors = [{ - keyword: 'type', - dataPath: (dataPath || '') + '.blacklist', - schemaPath: '#/properties/blacklist/type', - params: { - type: 'array' - }, - message: 'should be array' - }]; - return false; + var valid1 = errors === errs_1; } - var valid1 = errors === errs_1; } } } @@ -531,7 +550,8 @@ validate.schema = { }] } } - } + }, + "additionalProperties": false }; validate.errors = null; module.exports = validate; \ No newline at end of file diff --git a/test/shared/settings/Blacklist.test.ts b/test/shared/settings/Blacklist.test.ts index 133112c..0112757 100644 --- a/test/shared/settings/Blacklist.test.ts +++ b/test/shared/settings/Blacklist.test.ts @@ -16,16 +16,6 @@ describe('BlacklistItem', () => { expect(item.partial).to.be.true; expect(item.keys).to.deep.equal(['j', 'k']); }); - - it('throws a TypeError', () => { - expect(() => BlacklistItem.fromJSON(null)).to.throw(TypeError); - expect(() => BlacklistItem.fromJSON(100)).to.throw(TypeError); - expect(() => BlacklistItem.fromJSON({})).to.throw(TypeError); - expect(() => BlacklistItem.fromJSON({url: 'google.com'})).to.throw(TypeError); - expect(() => BlacklistItem.fromJSON({keys: ['a']})).to.throw(TypeError); - expect(() => BlacklistItem.fromJSON({url: 'google.com', keys: 10})).to.throw(TypeError); - expect(() => BlacklistItem.fromJSON({url: 'google.com', keys: ['a', 'b', 3]})).to.throw(TypeError); - }); }); describe('#matches', () => { @@ -118,14 +108,6 @@ describe('Blacklist', () => { let blacklist = Blacklist.fromJSON([]); expect(blacklist.toJSON()).to.deep.equals([]); }); - - it('throws a TypeError', () => { - expect(() => Blacklist.fromJSON(null)).to.throw(TypeError); - expect(() => Blacklist.fromJSON(100)).to.throw(TypeError); - expect(() => Blacklist.fromJSON({})).to.throw(TypeError); - expect(() => Blacklist.fromJSON([100])).to.throw(TypeError); - expect(() => Blacklist.fromJSON([{}])).to.throw(TypeError); - }) }); describe('#includesEntireBlacklist', () => { diff --git a/test/shared/settings/Keymaps.test.ts b/test/shared/settings/Keymaps.test.ts index 7896a63..9e4109f 100644 --- a/test/shared/settings/Keymaps.test.ts +++ b/test/shared/settings/Keymaps.test.ts @@ -19,7 +19,6 @@ describe('Keymaps', () => { }); it('throws a TypeError by invalid settings', () => { - expect(() => Keymaps.fromJSON(null)).to.throw(TypeError); expect(() => Keymaps.fromJSON({ k: { type: "invalid.operation" }, })).to.throw(TypeError); diff --git a/test/shared/settings/Search.test.ts b/test/shared/settings/Search.test.ts index 7c9134d..51cd3eb 100644 --- a/test/shared/settings/Search.test.ts +++ b/test/shared/settings/Search.test.ts @@ -26,19 +26,6 @@ describe('Search', () => { }); it('throws a TypeError by invalid settings', () => { - expect(() => Search.fromJSON(null)).to.throw(TypeError); - expect(() => Search.fromJSON({})).to.throw(TypeError); - expect(() => Search.fromJSON([])).to.throw(TypeError); - expect(() => Search.fromJSON({ - default: 123, - engines: {} - })).to.throw(TypeError); - expect(() => Search.fromJSON({ - default: 'google', - engines: { - 'google': 123456, - } - })).to.throw(TypeError); expect(() => Search.fromJSON({ default: 'wikipedia', engines: { From 00ccdc5ad71233795f3d6c35b98a5b33974511e1 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Tue, 3 Dec 2019 18:18:59 +0900 Subject: [PATCH 7/9] Exclude .js files from eslint targets --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ff6ecdc..381bb7d 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "start": "webpack --mode development -w --debug --devtool inline-source-map", "build": "NODE_ENV=production webpack --mode production --progress --display-error-details --devtool inline-source-map", "package": "npm run build && script/package", - "lint": "eslint --ext .js,.jsx,.ts,.tsx src", + "lint": "eslint --ext .ts,.tsx src", "type-checks": "tsc --noEmit", "test": "karma start", "test:e2e": "mocha --timeout 10000 --retries 10 --require ts-node/register --extension ts e2e" From 6c763aa14f967955343f597d4e14d7905ff5cafa Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Tue, 3 Dec 2019 18:21:08 +0900 Subject: [PATCH 8/9] Remove hack renaming --- .circleci/config.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fec66bd..8700c29 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -63,13 +63,6 @@ jobs: - checkout - setup_npm - run: npm run lint - - run: - # NOTE: Karma loads ts-node automatically and treats karma.conf.js as a TypeScript. - # Karma does not starts by karma.conf.js transpile failure, and this hack removes - # ts-node module from the local before test. - # See: https://github.com/karma-runner/karma/issues/3329 - name: Remove node-ts from node_modules - command: mv node_modules/ts-node node_modules/ts-node.orig - run: npm test - run: npm run package From 5205da572980f24d1632d0fa7df85124322ca960 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Tue, 3 Dec 2019 18:46:03 +0900 Subject: [PATCH 9/9] Make engines required --- src/shared/settings/Settings.ts | 2 +- src/shared/settings/schema.json | 3 ++- src/shared/settings/validate.js | 14 ++++++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/shared/settings/Settings.ts b/src/shared/settings/Settings.ts index f142717..97dda7f 100644 --- a/src/shared/settings/Settings.ts +++ b/src/shared/settings/Settings.ts @@ -44,7 +44,7 @@ export default class Settings { if (!valid) { let message = (validate as any).errors!! .map((err: Ajv.ErrorObject) => { - return `'${err.dataPath}' of ${err.keyword} ${err.message}`; + return `'${err.dataPath}' ${err.message}`; }) .join('; '); throw new TypeError(message); diff --git a/src/shared/settings/schema.json b/src/shared/settings/schema.json index 2e2f62a..31d47f1 100644 --- a/src/shared/settings/schema.json +++ b/src/shared/settings/schema.json @@ -33,7 +33,8 @@ } }, "required": [ - "default" + "default", + "engines" ] }, "properties": { diff --git a/src/shared/settings/validate.js b/src/shared/settings/validate.js index c761cee..236488d 100644 --- a/src/shared/settings/validate.js +++ b/src/shared/settings/validate.js @@ -143,7 +143,17 @@ var validate = (function() { if (valid2) { var data2 = data1.engines; if (data2 === undefined) { - valid2 = true; + valid2 = false; + validate.errors = [{ + keyword: 'required', + dataPath: (dataPath || '') + '.search', + schemaPath: '#/properties/search/required', + params: { + missingProperty: 'engines' + }, + message: 'should have required property \'engines\'' + }]; + return false; } else { var errs_2 = errors; if ((data2 && typeof data2 === "object" && !Array.isArray(data2))) { @@ -512,7 +522,7 @@ validate.schema = { } } }, - "required": ["default"] + "required": ["default", "engines"] }, "properties": { "type": "object",