diff options
30 files changed, 842 insertions, 235 deletions
| diff --git a/dev/data/manifest-variants.json b/dev/data/manifest-variants.json index ce82d0e3..119b47c2 100644 --- a/dev/data/manifest-variants.json +++ b/dev/data/manifest-variants.json @@ -311,7 +311,6 @@          },          {              "name": "safari", -            "fileName": null,              "modifications": [                  {                      "action": "remove", diff --git a/dev/jsconfig.json b/dev/jsconfig.json index a52153a8..d613e88d 100644 --- a/dev/jsconfig.json +++ b/dev/jsconfig.json @@ -29,7 +29,7 @@              "error": ["../types/ext/error"],              "event-listener-collection": ["../types/ext/event-listener-collection"],              "japanese-util": ["../types/ext/japanese-util"], -            "json-schema": ["../types/ext/json-schema"], +            "ext/json-schema": ["../types/ext/json-schema"],              "log": ["../types/ext/log"],              "settings": ["../types/ext/settings"],              "structured-content": ["../types/ext/structured-content"], diff --git a/dev/manifest-util.js b/dev/manifest-util.js index 6a53c8d6..e4487035 100644 --- a/dev/manifest-util.js +++ b/dev/manifest-util.js @@ -121,7 +121,7 @@ export class ManifestUtil {      /**       * @param {import('dev/manifest').Manifest} manifest -     * @param {import('dev/manifest').Modification[]} modifications +     * @param {import('dev/manifest').Modification[]|undefined} modifications       * @returns {import('dev/manifest').Manifest}       */      _applyModifications(manifest, modifications) { diff --git a/dev/schema-validate.js b/dev/schema-validate.js index 57faf96c..d4e977e1 100644 --- a/dev/schema-validate.js +++ b/dev/schema-validate.js @@ -62,6 +62,6 @@ class JsonSchemaAjv {  export function createJsonSchema(mode, schema) {      switch (mode) {          case 'ajv': return new JsonSchemaAjv(schema); -        default: return new JsonSchema(/** @type {import('json-schema').Schema} */ (schema)); +        default: return new JsonSchema(/** @type {import('ext/json-schema').Schema} */ (schema));      }  } diff --git a/dev/util.js b/dev/util.js index 6a7fa8f5..542ad6a2 100644 --- a/dev/util.js +++ b/dev/util.js @@ -72,7 +72,7 @@ export function getArgs(args, argMap) {  /**   * @param {string} baseDirectory - * @param {?(fileName: string) => boolean} predicate + * @param {?(fileName: string, isDirectory: boolean) => boolean} predicate   * @returns {string[]}   */  export function getAllFiles(baseDirectory, predicate = null) { @@ -86,11 +86,13 @@ export function getAllFiles(baseDirectory, predicate = null) {              const relativeFileName = path.relative(baseDirectory, fullFileName);              const stats = fs.lstatSync(fullFileName);              if (stats.isFile()) { -                if (typeof predicate !== 'function' || predicate(relativeFileName)) { +                if (typeof predicate !== 'function' || predicate(relativeFileName, false)) {                      results.push(relativeFileName);                  }              } else if (stats.isDirectory()) { -                directories.push(fullFileName); +                if (typeof predicate !== 'function' || predicate(relativeFileName, true)) { +                    directories.push(fullFileName); +                }              }          }      } diff --git a/ext/js/background/profile-conditions-util.js b/ext/js/background/profile-conditions-util.js index b1bbe1d5..8d477094 100644 --- a/ext/js/background/profile-conditions-util.js +++ b/ext/js/background/profile-conditions-util.js @@ -158,7 +158,7 @@ export class ProfileConditionsUtil {      /**       * @param {string} value -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaPopupLevelEqual(value) {          const number = this._stringToNumber(value); @@ -172,7 +172,7 @@ export class ProfileConditionsUtil {      /**       * @param {string} value -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaPopupLevelNotEqual(value) {          return { @@ -184,7 +184,7 @@ export class ProfileConditionsUtil {      /**       * @param {string} value -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaPopupLevelLessThan(value) {          const number = this._stringToNumber(value); @@ -198,7 +198,7 @@ export class ProfileConditionsUtil {      /**       * @param {string} value -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaPopupLevelGreaterThan(value) {          const number = this._stringToNumber(value); @@ -212,7 +212,7 @@ export class ProfileConditionsUtil {      /**       * @param {string} value -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaPopupLevelLessThanOrEqual(value) {          const number = this._stringToNumber(value); @@ -226,7 +226,7 @@ export class ProfileConditionsUtil {      /**       * @param {string} value -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaPopupLevelGreaterThanOrEqual(value) {          const number = this._stringToNumber(value); @@ -242,7 +242,7 @@ export class ProfileConditionsUtil {      /**       * @param {string} value -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaUrlMatchDomain(value) {          const oneOf = []; @@ -261,7 +261,7 @@ export class ProfileConditionsUtil {      /**       * @param {string} value -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaUrlMatchRegExp(value) {          return { @@ -276,7 +276,7 @@ export class ProfileConditionsUtil {      /**       * @param {string} value -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaModifierKeysAre(value) {          return this._createSchemaArrayCheck('modifierKeys', value, true, false); @@ -284,7 +284,7 @@ export class ProfileConditionsUtil {      /**       * @param {string} value -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaModifierKeysAreNot(value) {          return { @@ -296,7 +296,7 @@ export class ProfileConditionsUtil {      /**       * @param {string} value -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaModifierKeysInclude(value) {          return this._createSchemaArrayCheck('modifierKeys', value, false, false); @@ -304,7 +304,7 @@ export class ProfileConditionsUtil {      /**       * @param {string} value -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaModifierKeysNotInclude(value) {          return this._createSchemaArrayCheck('modifierKeys', value, false, true); @@ -314,7 +314,7 @@ export class ProfileConditionsUtil {      /**       * @param {string} value -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaFlagsAre(value) {          return this._createSchemaArrayCheck('flags', value, true, false); @@ -322,7 +322,7 @@ export class ProfileConditionsUtil {      /**       * @param {string} value -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaFlagsAreNot(value) {          return { @@ -334,7 +334,7 @@ export class ProfileConditionsUtil {      /**       * @param {string} value -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaFlagsInclude(value) {          return this._createSchemaArrayCheck('flags', value, false, false); @@ -342,7 +342,7 @@ export class ProfileConditionsUtil {      /**       * @param {string} value -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaFlagsNotInclude(value) {          return this._createSchemaArrayCheck('flags', value, false, true); @@ -355,10 +355,10 @@ export class ProfileConditionsUtil {       * @param {string} value       * @param {boolean} exact       * @param {boolean} none -     * @returns {import('json-schema').Schema} +     * @returns {import('ext/json-schema').Schema}       */      _createSchemaArrayCheck(key, value, exact, none) { -        /** @type {import('json-schema').Schema[]} */ +        /** @type {import('ext/json-schema').Schema[]} */          const containsList = [];          for (const item of this._split(value)) {              if (item.length === 0) { continue; } @@ -369,7 +369,7 @@ export class ProfileConditionsUtil {              });          }          const containsListCount = containsList.length; -        /** @type {import('json-schema').Schema} */ +        /** @type {import('ext/json-schema').Schema} */          const schema = {              type: 'array'          }; diff --git a/ext/js/data/json-schema.js b/ext/js/data/json-schema.js index 52a55d85..08414164 100644 --- a/ext/js/data/json-schema.js +++ b/ext/js/data/json-schema.js @@ -22,47 +22,47 @@ import {CacheMap} from '../general/cache-map.js';  export class JsonSchemaError extends Error {      /**       * @param {string} message -     * @param {import('json-schema').ValueStackItem[]} valueStack -     * @param {import('json-schema').SchemaStackItem[]} schemaStack +     * @param {import('ext/json-schema').ValueStackItem[]} valueStack +     * @param {import('ext/json-schema').SchemaStackItem[]} schemaStack       */      constructor(message, valueStack, schemaStack) {          super(message); -        /** @type {import('json-schema').ValueStackItem[]} */ +        /** @type {import('ext/json-schema').ValueStackItem[]} */          this._valueStack = valueStack; -        /** @type {import('json-schema').SchemaStackItem[]} */ +        /** @type {import('ext/json-schema').SchemaStackItem[]} */          this._schemaStack = schemaStack;      }      /** @type {unknown|undefined} */      get value() { return this._valueStack.length > 0 ? this._valueStack[this._valueStack.length - 1].value : void 0; } -    /** @type {import('json-schema').Schema|import('json-schema').Schema[]|undefined} */ +    /** @type {import('ext/json-schema').Schema|import('ext/json-schema').Schema[]|undefined} */      get schema() { return this._schemaStack.length > 0 ? this._schemaStack[this._schemaStack.length - 1].schema : void 0; } -    /** @type {import('json-schema').ValueStackItem[]} */ +    /** @type {import('ext/json-schema').ValueStackItem[]} */      get valueStack() { return this._valueStack; } -    /** @type {import('json-schema').SchemaStackItem[]} */ +    /** @type {import('ext/json-schema').SchemaStackItem[]} */      get schemaStack() { return this._schemaStack; }  }  export class JsonSchema {      /** -     * @param {import('json-schema').Schema} schema -     * @param {import('json-schema').Schema} [rootSchema] +     * @param {import('ext/json-schema').Schema} schema +     * @param {import('ext/json-schema').Schema} [rootSchema]       */      constructor(schema, rootSchema) { -        /** @type {import('json-schema').Schema} */ +        /** @type {import('ext/json-schema').Schema} */          this._startSchema = schema; -        /** @type {import('json-schema').Schema} */ +        /** @type {import('ext/json-schema').Schema} */          this._rootSchema = typeof rootSchema !== 'undefined' ? rootSchema : schema;          /** @type {?CacheMap<string, RegExp>} */          this._regexCache = null; -        /** @type {?Map<string, {schema: import('json-schema').Schema, stack: import('json-schema').SchemaStackItem[]}>} */ +        /** @type {?Map<string, {schema: import('ext/json-schema').Schema, stack: import('ext/json-schema').SchemaStackItem[]}>} */          this._refCache = null; -        /** @type {import('json-schema').ValueStackItem[]} */ +        /** @type {import('ext/json-schema').ValueStackItem[]} */          this._valueStack = []; -        /** @type {import('json-schema').SchemaStackItem[]} */ +        /** @type {import('ext/json-schema').SchemaStackItem[]} */          this._schemaStack = [];          /** @type {?(jsonSchema: JsonSchema) => void} */          this._progress = null; @@ -72,12 +72,12 @@ export class JsonSchema {          this._progressInterval = 1;      } -    /** @type {import('json-schema').Schema} */ +    /** @type {import('ext/json-schema').Schema} */      get schema() {          return this._startSchema;      } -    /** @type {import('json-schema').Schema} */ +    /** @type {import('ext/json-schema').Schema} */      get rootSchema() {          return this._rootSchema;      } @@ -101,8 +101,8 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').Value} value -     * @returns {import('json-schema').Value} +     * @param {import('ext/json-schema').Value} value +     * @returns {import('ext/json-schema').Value}       */      createProxy(value) {          return ( @@ -142,7 +142,7 @@ export class JsonSchema {      /**       * @param {unknown} [value] -     * @returns {import('json-schema').Value} +     * @returns {import('ext/json-schema').Value}       */      getValidValueOrDefault(value) {          const schema = this._startSchema; @@ -195,7 +195,7 @@ export class JsonSchema {      // Internal state functions for error construction and progress callback      /** -     * @returns {import('json-schema').ValueStackItem[]} +     * @returns {import('ext/json-schema').ValueStackItem[]}       */      getValueStack() {          const result = []; @@ -206,7 +206,7 @@ export class JsonSchema {      }      /** -     * @returns {import('json-schema').SchemaStackItem[]} +     * @returns {import('ext/json-schema').SchemaStackItem[]}       */      getSchemaStack() {          const result = []; @@ -225,7 +225,7 @@ export class JsonSchema {      /**       * @param {number} index -     * @returns {import('json-schema').ValueStackItem} +     * @returns {import('ext/json-schema').ValueStackItem}       */      getValueStackItem(index) {          const {value, path} = this._valueStack[index + 1]; @@ -241,7 +241,7 @@ export class JsonSchema {      /**       * @param {number} index -     * @returns {import('json-schema').SchemaStackItem} +     * @returns {import('ext/json-schema').SchemaStackItem}       */      getSchemaStackItem(index) {          const {schema, path} = this._schemaStack[index + 1]; @@ -275,7 +275,7 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').Schema|import('json-schema').Schema[]} schema +     * @param {import('ext/json-schema').Schema|import('ext/json-schema').Schema[]} schema       * @param {string|number|null} path       */      _schemaPush(schema, path) { @@ -283,7 +283,7 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').SchemaStackItem[]} items +     * @param {import('ext/json-schema').SchemaStackItem[]} items       */      _schemaPushMultiple(items) {          this._schemaStack.push(...items); @@ -337,9 +337,9 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').Schema} schema +     * @param {import('ext/json-schema').Schema} schema       * @param {string} property -     * @returns {{schema: import('json-schema').Schema, stack: import('json-schema').SchemaStackItem[]}} +     * @returns {{schema: import('ext/json-schema').Schema, stack: import('ext/json-schema').SchemaStackItem[]}}       */      _getObjectPropertySchemaInfo(schema, property) {          if (typeof schema === 'boolean') { @@ -362,9 +362,9 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').Schema} schema +     * @param {import('ext/json-schema').Schema} schema       * @param {number} index -     * @returns {{schema: import('json-schema').Schema, stack: import('json-schema').SchemaStackItem[]}} +     * @returns {{schema: import('ext/json-schema').Schema, stack: import('ext/json-schema').SchemaStackItem[]}}       */      _getArrayItemSchemaInfo(schema, index) {          if (typeof schema === 'boolean') { @@ -411,9 +411,9 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').Schema|undefined} schema +     * @param {import('ext/json-schema').Schema|undefined} schema       * @param {string|number|null} path -     * @returns {{schema: import('json-schema').Schema, stack: import('json-schema').SchemaStackItem[]}} +     * @returns {{schema: import('ext/json-schema').Schema, stack: import('ext/json-schema').SchemaStackItem[]}}       */      _getOptionalSchemaInfo(schema, path) {          switch (typeof schema) { @@ -430,7 +430,7 @@ export class JsonSchema {      /**       * @param {unknown} value -     * @returns {?import('json-schema').Type} +     * @returns {?import('ext/json-schema').Type}       * @throws {Error}       */      _getValueType(value) { @@ -451,8 +451,8 @@ export class JsonSchema {      /**       * @param {unknown} value -     * @param {?import('json-schema').Type} type -     * @param {import('json-schema').Type|import('json-schema').Type[]|undefined} schemaTypes +     * @param {?import('ext/json-schema').Type} type +     * @param {import('ext/json-schema').Type|import('ext/json-schema').Type[]|undefined} schemaTypes       * @returns {boolean}       */      _isValueTypeAny(value, type, schemaTypes) { @@ -471,8 +471,8 @@ export class JsonSchema {      /**       * @param {unknown} value -     * @param {?import('json-schema').Type} type -     * @param {import('json-schema').Type} schemaType +     * @param {?import('ext/json-schema').Type} type +     * @param {import('ext/json-schema').Type} schemaType       * @returns {boolean}       */      _isValueType(value, type, schemaType) { @@ -484,7 +484,7 @@ export class JsonSchema {      /**       * @param {unknown} value1 -     * @param {import('json-schema').Value[]} valueList +     * @param {import('ext/json-schema').Value[]} valueList       * @returns {boolean}       */      _valuesAreEqualAny(value1, valueList) { @@ -498,7 +498,7 @@ export class JsonSchema {      /**       * @param {unknown} value1 -     * @param {import('json-schema').Value} value2 +     * @param {import('ext/json-schema').Value} value2       * @returns {boolean}       */      _valuesAreEqual(value1, value2) { @@ -506,9 +506,9 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').Schema} schema -     * @param {import('json-schema').SchemaStackItem[]} stack -     * @returns {{schema: import('json-schema').Schema, stack: import('json-schema').SchemaStackItem[]}} +     * @param {import('ext/json-schema').Schema} schema +     * @param {import('ext/json-schema').SchemaStackItem[]} stack +     * @returns {{schema: import('ext/json-schema').Schema, stack: import('ext/json-schema').SchemaStackItem[]}}       */      _getResolvedSchemaInfo(schema, stack) {          if (typeof schema !== 'boolean') { @@ -526,7 +526,7 @@ export class JsonSchema {      /**       * @param {string} ref -     * @returns {{schema: import('json-schema').Schema, stack: import('json-schema').SchemaStackItem[]}} +     * @returns {{schema: import('ext/json-schema').Schema, stack: import('ext/json-schema').SchemaStackItem[]}}       * @throws {Error}       */      _getReference(ref) { @@ -534,7 +534,7 @@ export class JsonSchema {              throw this._createError(`Unsupported reference path: ${ref}`);          } -        /** @type {{schema: import('json-schema').Schema, stack: import('json-schema').SchemaStackItem[]}|undefined} */ +        /** @type {{schema: import('ext/json-schema').Schema, stack: import('ext/json-schema').SchemaStackItem[]}|undefined} */          let info;          if (this._refCache !== null) {              info = this._refCache.get(ref); @@ -553,13 +553,13 @@ export class JsonSchema {      /**       * @param {string} ref -     * @returns {{schema: import('json-schema').Schema, stack: import('json-schema').SchemaStackItem[]}} +     * @returns {{schema: import('ext/json-schema').Schema, stack: import('ext/json-schema').SchemaStackItem[]}}       * @throws {Error}       */      _getReferenceUncached(ref) {          /** @type {Set<string>} */          const visited = new Set(); -        /** @type {import('json-schema').SchemaStackItem[]} */ +        /** @type {import('ext/json-schema').SchemaStackItem[]} */          const stack = [];          while (true) {              if (visited.has(ref)) { @@ -594,11 +594,11 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').SchemaStackItem[]} schemaStack -     * @returns {import('json-schema').SchemaStackItem[]} +     * @param {import('ext/json-schema').SchemaStackItem[]} schemaStack +     * @returns {import('ext/json-schema').SchemaStackItem[]}       */      _copySchemaStack(schemaStack) { -        /** @type {import('json-schema').SchemaStackItem[]} */ +        /** @type {import('ext/json-schema').SchemaStackItem[]} */          const results = [];          for (const {schema, path} of schemaStack) {              results.push({schema, path}); @@ -609,7 +609,7 @@ export class JsonSchema {      // Validation      /** -     * @param {import('json-schema').SchemaObject} schema +     * @param {import('ext/json-schema').SchemaObject} schema       * @param {unknown} value       * @returns {boolean}       */ @@ -623,7 +623,7 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').Schema} schema +     * @param {import('ext/json-schema').Schema} schema       * @param {unknown} value       */      _validate(schema, value) { @@ -643,7 +643,7 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').Schema} schema +     * @param {import('ext/json-schema').Schema} schema       * @param {unknown} value       * @throws {Error}       */ @@ -659,7 +659,7 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').SchemaObject} schema +     * @param {import('ext/json-schema').SchemaObject} schema       * @param {unknown} value       */      _validateConditional(schema, value) { @@ -688,7 +688,7 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').SchemaObject} schema +     * @param {import('ext/json-schema').SchemaObject} schema       * @param {unknown} value       */      _validateAllOf(schema, value) { @@ -712,7 +712,7 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').SchemaObject} schema +     * @param {import('ext/json-schema').SchemaObject} schema       * @param {unknown} value       */      _validateAnyOf(schema, value) { @@ -741,7 +741,7 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').SchemaObject} schema +     * @param {import('ext/json-schema').SchemaObject} schema       * @param {unknown} value       */      _validateOneOf(schema, value) { @@ -773,7 +773,7 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').SchemaObject} schema +     * @param {import('ext/json-schema').SchemaObject} schema       * @param {unknown} value       * @throws {Error}       */ @@ -797,7 +797,7 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').SchemaObject} schema +     * @param {import('ext/json-schema').SchemaObject} schema       * @param {unknown} value       * @throws {Error}       */ @@ -824,16 +824,16 @@ export class JsonSchema {                  this._validateString(schema, /** @type {string} */ (value));                  break;              case 'array': -                this._validateArray(schema, /** @type {import('json-schema').Value[]} */ (value)); +                this._validateArray(schema, /** @type {import('ext/json-schema').Value[]} */ (value));                  break;              case 'object': -                this._validateObject(schema, /** @type {import('json-schema').ValueObject} */ (value)); +                this._validateObject(schema, /** @type {import('ext/json-schema').ValueObject} */ (value));                  break;          }      }      /** -     * @param {import('json-schema').SchemaObject} schema +     * @param {import('ext/json-schema').SchemaObject} schema       * @param {number} value       * @throws {Error}       */ @@ -861,7 +861,7 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').SchemaObject} schema +     * @param {import('ext/json-schema').SchemaObject} schema       * @param {string} value       * @throws {Error}       */ @@ -893,7 +893,7 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').SchemaObject} schema +     * @param {import('ext/json-schema').SchemaObject} schema       * @param {unknown[]} value       * @throws {Error}       */ @@ -931,7 +931,7 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').SchemaObject} schema +     * @param {import('ext/json-schema').SchemaObject} schema       * @param {unknown[]} value       * @throws {Error}       */ @@ -960,8 +960,8 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').SchemaObject} schema -     * @param {import('json-schema').ValueObject} value +     * @param {import('ext/json-schema').SchemaObject} schema +     * @param {import('ext/json-schema').ValueObject} value       * @throws {Error}       */      _validateObject(schema, value) { @@ -1008,8 +1008,8 @@ export class JsonSchema {      // Creation      /** -     * @param {import('json-schema').Type|import('json-schema').Type[]|undefined} type -     * @returns {import('json-schema').Value} +     * @param {import('ext/json-schema').Type|import('ext/json-schema').Type[]|undefined} type +     * @returns {import('ext/json-schema').Value}       */      _getDefaultTypeValue(type) {          if (Array.isArray(type)) { type = type[0]; } @@ -1034,8 +1034,8 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').SchemaObject} schema -     * @returns {import('json-schema').Value} +     * @param {import('ext/json-schema').SchemaObject} schema +     * @returns {import('ext/json-schema').Value}       */      _getDefaultSchemaValue(schema) {          const {type: schemaType, default: schemaDefault} = schema; @@ -1048,11 +1048,11 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').Schema} schema +     * @param {import('ext/json-schema').Schema} schema       * @param {string|number|null} path       * @param {unknown} value -     * @param {import('json-schema').SchemaStackItem[]} stack -     * @returns {import('json-schema').Value} +     * @param {import('ext/json-schema').SchemaStackItem[]} stack +     * @returns {import('ext/json-schema').Value}       */      _getValidValueOrDefault(schema, path, value, stack) {          ({schema, stack} = this._getResolvedSchemaInfo(schema, stack)); @@ -1067,14 +1067,14 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').Schema} schema +     * @param {import('ext/json-schema').Schema} schema       * @param {unknown} value -     * @returns {import('json-schema').Value} +     * @returns {import('ext/json-schema').Value}       */      _getValidValueOrDefaultInner(schema, value) {          let type = this._getValueType(value);          if (typeof schema === 'boolean') { -            return type !== null ? /** @type {import('json-schema').ValueObject} */ (value) : null; +            return type !== null ? /** @type {import('ext/json-schema').ValueObject} */ (value) : null;          }          if (typeof value === 'undefined' || !this._isValueTypeAny(value, type, schema.type)) {              value = this._getDefaultSchemaValue(schema); @@ -1083,9 +1083,9 @@ export class JsonSchema {          switch (type) {              case 'object': -                return this._populateObjectDefaults(schema, /** @type {import('json-schema').ValueObject} */ (value)); +                return this._populateObjectDefaults(schema, /** @type {import('ext/json-schema').ValueObject} */ (value));              case 'array': -                return this._populateArrayDefaults(schema, /** @type {import('json-schema').Value[]} */ (value)); +                return this._populateArrayDefaults(schema, /** @type {import('ext/json-schema').Value[]} */ (value));              default:                  if (!this._isValidCurrent(schema, value)) {                      const schemaDefault = this._getDefaultSchemaValue(schema); @@ -1096,13 +1096,13 @@ export class JsonSchema {                  break;          } -        return /** @type {import('json-schema').ValueObject} */ (value); +        return /** @type {import('ext/json-schema').ValueObject} */ (value);      }      /** -     * @param {import('json-schema').SchemaObject} schema -     * @param {import('json-schema').ValueObject} value -     * @returns {import('json-schema').ValueObject} +     * @param {import('ext/json-schema').SchemaObject} schema +     * @param {import('ext/json-schema').ValueObject} value +     * @returns {import('ext/json-schema').ValueObject}       */      _populateObjectDefaults(schema, value) {          const properties = new Set(Object.getOwnPropertyNames(value)); @@ -1131,9 +1131,9 @@ export class JsonSchema {      }      /** -     * @param {import('json-schema').SchemaObject} schema -     * @param {import('json-schema').Value[]} value -     * @returns {import('json-schema').Value[]} +     * @param {import('ext/json-schema').SchemaObject} schema +     * @param {import('ext/json-schema').Value[]} value +     * @returns {import('ext/json-schema').Value[]}       */      _populateArrayDefaults(schema, value) {          for (let i = 0, ii = value.length; i < ii; ++i) { @@ -1162,7 +1162,7 @@ export class JsonSchema {  }  /** - * @implements {ProxyHandler<import('json-schema').ValueObjectOrArray>} + * @implements {ProxyHandler<import('ext/json-schema').ValueObjectOrArray>}   */  class JsonSchemaProxyHandler {      /** @@ -1176,7 +1176,7 @@ class JsonSchemaProxyHandler {      }      /** -     * @param {import('json-schema').ValueObjectOrArray} target +     * @param {import('ext/json-schema').ValueObjectOrArray} target       * @returns {?import('core').UnknownObject}       */      getPrototypeOf(target) { @@ -1184,14 +1184,14 @@ class JsonSchemaProxyHandler {      }      /** -     * @type {(target: import('json-schema').ValueObjectOrArray, newPrototype: ?unknown) => boolean} +     * @type {(target: import('ext/json-schema').ValueObjectOrArray, newPrototype: ?unknown) => boolean}       */      setPrototypeOf() {          throw new Error('setPrototypeOf not supported');      }      /** -     * @param {import('json-schema').ValueObjectOrArray} target +     * @param {import('ext/json-schema').ValueObjectOrArray} target       * @returns {boolean}       */      isExtensible(target) { @@ -1199,7 +1199,7 @@ class JsonSchemaProxyHandler {      }      /** -     * @param {import('json-schema').ValueObjectOrArray} target +     * @param {import('ext/json-schema').ValueObjectOrArray} target       * @returns {boolean}       */      preventExtensions(target) { @@ -1208,7 +1208,7 @@ class JsonSchemaProxyHandler {      }      /** -     * @param {import('json-schema').ValueObjectOrArray} target +     * @param {import('ext/json-schema').ValueObjectOrArray} target       * @param {string|symbol} property       * @returns {PropertyDescriptor|undefined}       */ @@ -1217,14 +1217,14 @@ class JsonSchemaProxyHandler {      }      /** -     * @type {(target: import('json-schema').ValueObjectOrArray, property: string|symbol, attributes: PropertyDescriptor) => boolean} +     * @type {(target: import('ext/json-schema').ValueObjectOrArray, property: string|symbol, attributes: PropertyDescriptor) => boolean}       */      defineProperty() {          throw new Error('defineProperty not supported');      }      /** -     * @param {import('json-schema').ValueObjectOrArray} target +     * @param {import('ext/json-schema').ValueObjectOrArray} target       * @param {string|symbol} property       * @returns {boolean}       */ @@ -1233,7 +1233,7 @@ class JsonSchemaProxyHandler {      }      /** -     * @param {import('json-schema').ValueObjectOrArray} target +     * @param {import('ext/json-schema').ValueObjectOrArray} target       * @param {string|symbol} property       * @param {import('core').SafeAny} _receiver       * @returns {import('core').SafeAny} @@ -1257,11 +1257,11 @@ class JsonSchemaProxyHandler {          if (propertySchema === null) { return void 0; }          const value = /** @type {import('core').UnknownObject} */ (target)[property]; -        return value !== null && typeof value === 'object' ? propertySchema.createProxy(/** @type {import('json-schema').Value} */ (value)) : value; +        return value !== null && typeof value === 'object' ? propertySchema.createProxy(/** @type {import('ext/json-schema').Value} */ (value)) : value;      }      /** -     * @param {import('json-schema').ValueObjectOrArray} target +     * @param {import('ext/json-schema').ValueObjectOrArray} target       * @param {string|number|symbol} property       * @param {import('core').SafeAny} value       * @returns {boolean} @@ -1297,7 +1297,7 @@ class JsonSchemaProxyHandler {      }      /** -     * @param {import('json-schema').ValueObjectOrArray} target +     * @param {import('ext/json-schema').ValueObjectOrArray} target       * @param {string|symbol} property       * @returns {boolean}       * @throws {Error} @@ -1315,7 +1315,7 @@ class JsonSchemaProxyHandler {      }      /** -     * @param {import('json-schema').ValueObjectOrArray} target +     * @param {import('ext/json-schema').ValueObjectOrArray} target       * @returns {ArrayLike<string|symbol>}       */      ownKeys(target) { @@ -1323,14 +1323,14 @@ class JsonSchemaProxyHandler {      }      /** -     * @type {(target: import('json-schema').ValueObjectOrArray, thisArg: import('core').SafeAny, argArray: import('core').SafeAny[]) => import('core').SafeAny} +     * @type {(target: import('ext/json-schema').ValueObjectOrArray, thisArg: import('core').SafeAny, argArray: import('core').SafeAny[]) => import('core').SafeAny}       */      apply() {          throw new Error('apply not supported');      }      /** -     * @type {(target: import('json-schema').ValueObjectOrArray, argArray: import('core').SafeAny[], newTarget: import('core').SafeFunction) => import('json-schema').ValueObjectOrArray} +     * @type {(target: import('ext/json-schema').ValueObjectOrArray, argArray: import('core').SafeAny[], newTarget: import('core').SafeFunction) => import('ext/json-schema').ValueObjectOrArray}       */      construct() {          throw new Error('construct not supported'); diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js index a17763e9..c219bed3 100644 --- a/ext/js/data/options-util.js +++ b/ext/js/data/options-util.js @@ -31,7 +31,7 @@ export class OptionsUtil {      /** */      async prepare() { -        /** @type {import('json-schema').Schema} */ +        /** @type {import('ext/json-schema').Schema} */          const schema = await this._fetchJson('/data/schemas/options-schema.json');          this._optionsSchema = new JsonSchema(schema);      } diff --git a/ext/js/display/sandbox/structured-content-generator.js b/ext/js/display/sandbox/structured-content-generator.js index 5a91b01c..a28464ae 100644 --- a/ext/js/display/sandbox/structured-content-generator.js +++ b/ext/js/display/sandbox/structured-content-generator.js @@ -53,7 +53,7 @@ export class StructuredContentGenerator {      }      /** -     * @param {import('structured-content').ImageElementBase} data +     * @param {import('structured-content').ImageElement|import('dictionary-data').TermGlossaryImage} data       * @param {string} dictionary       * @returns {HTMLAnchorElement}       */ diff --git a/ext/js/media/audio-downloader.js b/ext/js/media/audio-downloader.js index a9b2133b..4e602f8c 100644 --- a/ext/js/media/audio-downloader.js +++ b/ext/js/media/audio-downloader.js @@ -278,7 +278,7 @@ export class AudioDownloader {          if (this._customAudioListSchema === null) {              const schema = await this._getCustomAudioListSchema(); -            this._customAudioListSchema = new JsonSchema(/** @type {import('json-schema').Schema} */ (schema)); +            this._customAudioListSchema = new JsonSchema(/** @type {import('ext/json-schema').Schema} */ (schema));          }          this._customAudioListSchema.validate(responseJson); diff --git a/jsconfig.json b/jsconfig.json index 82048af1..4ab13106 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -12,7 +12,8 @@          "skipLibCheck": false,          "baseUrl": ".",          "paths": { -            "*": ["./types/ext/*"] +            "*": ["./types/ext/*"], +            "ext/json-schema": ["./types/ext/json-schema"]          },          "types": [              "chrome", diff --git a/package-lock.json b/package-lock.json index 1e291cf2..75f696d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,6 +52,7 @@                  "stylelint": "^15.11.0",                  "stylelint-config-recommended": "^13.0.0",                  "stylelint-stylistic": "^0.4.3", +                "ts-json-schema-generator": "^1.5.0",                  "typescript": "5.3.3",                  "vitest": "^0.34.6"              } @@ -2112,6 +2113,15 @@                  "node": ">= 0.8"              }          }, +        "node_modules/commander": { +            "version": "11.1.0", +            "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", +            "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", +            "dev": true, +            "engines": { +                "node": ">=16" +            } +        },          "node_modules/comment-parser": {              "version": "1.4.1",              "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", @@ -3812,6 +3822,18 @@              "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",              "dev": true          }, +        "node_modules/json5": { +            "version": "2.2.3", +            "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", +            "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", +            "dev": true, +            "bin": { +                "json5": "lib/cli.js" +            }, +            "engines": { +                "node": ">=6" +            } +        },          "node_modules/jsonc-eslint-parser": {              "version": "2.4.0",              "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.0.tgz", @@ -5011,6 +5033,15 @@              "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",              "dev": true          }, +        "node_modules/safe-stable-stringify": { +            "version": "2.4.3", +            "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", +            "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", +            "dev": true, +            "engines": { +                "node": ">=10" +            } +        },          "node_modules/safer-buffer": {              "version": "2.1.2",              "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -5719,6 +5750,67 @@                  "typescript": ">=4.2.0"              }          }, +        "node_modules/ts-json-schema-generator": { +            "version": "1.5.0", +            "resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-1.5.0.tgz", +            "integrity": "sha512-RkiaJ6YxGc5EWVPfyHxszTmpGxX8HC2XBvcFlAl1zcvpOG4tjjh+eXioStXJQYTvr9MoK8zCOWzAUlko3K0DiA==", +            "dev": true, +            "dependencies": { +                "@types/json-schema": "^7.0.12", +                "commander": "^11.0.0", +                "glob": "^8.0.3", +                "json5": "^2.2.3", +                "normalize-path": "^3.0.0", +                "safe-stable-stringify": "^2.4.3", +                "typescript": "~5.3.2" +            }, +            "bin": { +                "ts-json-schema-generator": "bin/ts-json-schema-generator" +            }, +            "engines": { +                "node": ">=10.0.0" +            } +        }, +        "node_modules/ts-json-schema-generator/node_modules/brace-expansion": { +            "version": "2.0.1", +            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", +            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", +            "dev": true, +            "dependencies": { +                "balanced-match": "^1.0.0" +            } +        }, +        "node_modules/ts-json-schema-generator/node_modules/glob": { +            "version": "8.1.0", +            "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", +            "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", +            "dev": true, +            "dependencies": { +                "fs.realpath": "^1.0.0", +                "inflight": "^1.0.4", +                "inherits": "2", +                "minimatch": "^5.0.1", +                "once": "^1.3.0" +            }, +            "engines": { +                "node": ">=12" +            }, +            "funding": { +                "url": "https://github.com/sponsors/isaacs" +            } +        }, +        "node_modules/ts-json-schema-generator/node_modules/minimatch": { +            "version": "5.1.6", +            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", +            "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", +            "dev": true, +            "dependencies": { +                "brace-expansion": "^2.0.1" +            }, +            "engines": { +                "node": ">=10" +            } +        },          "node_modules/tslib": {              "version": "2.6.2",              "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -7662,6 +7754,12 @@                  "delayed-stream": "~1.0.0"              }          }, +        "commander": { +            "version": "11.1.0", +            "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", +            "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", +            "dev": true +        },          "comment-parser": {              "version": "1.4.1",              "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", @@ -8882,6 +8980,12 @@              "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",              "dev": true          }, +        "json5": { +            "version": "2.2.3", +            "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", +            "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", +            "dev": true +        },          "jsonc-eslint-parser": {              "version": "2.4.0",              "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.0.tgz", @@ -9739,6 +9843,12 @@              "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",              "dev": true          }, +        "safe-stable-stringify": { +            "version": "2.4.3", +            "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", +            "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", +            "dev": true +        },          "safer-buffer": {              "version": "2.1.2",              "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -10295,6 +10405,54 @@              "dev": true,              "requires": {}          }, +        "ts-json-schema-generator": { +            "version": "1.5.0", +            "resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-1.5.0.tgz", +            "integrity": "sha512-RkiaJ6YxGc5EWVPfyHxszTmpGxX8HC2XBvcFlAl1zcvpOG4tjjh+eXioStXJQYTvr9MoK8zCOWzAUlko3K0DiA==", +            "dev": true, +            "requires": { +                "@types/json-schema": "^7.0.12", +                "commander": "^11.0.0", +                "glob": "^8.0.3", +                "json5": "^2.2.3", +                "normalize-path": "^3.0.0", +                "safe-stable-stringify": "^2.4.3", +                "typescript": "~5.3.2" +            }, +            "dependencies": { +                "brace-expansion": { +                    "version": "2.0.1", +                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", +                    "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", +                    "dev": true, +                    "requires": { +                        "balanced-match": "^1.0.0" +                    } +                }, +                "glob": { +                    "version": "8.1.0", +                    "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", +                    "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", +                    "dev": true, +                    "requires": { +                        "fs.realpath": "^1.0.0", +                        "inflight": "^1.0.4", +                        "inherits": "2", +                        "minimatch": "^5.0.1", +                        "once": "^1.3.0" +                    } +                }, +                "minimatch": { +                    "version": "5.1.6", +                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", +                    "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", +                    "dev": true, +                    "requires": { +                        "brace-expansion": "^2.0.1" +                    } +                } +            } +        },          "tslib": {              "version": "2.6.2",              "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", diff --git a/package.json b/package.json index adffa549..6be76caf 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@          "stylelint": "^15.11.0",          "stylelint-config-recommended": "^13.0.0",          "stylelint-stylistic": "^0.4.3", +        "ts-json-schema-generator": "^1.5.0",          "typescript": "5.3.3",          "vitest": "^0.34.6"      }, diff --git a/test/data/json.json b/test/data/json.json new file mode 100644 index 00000000..6d806263 --- /dev/null +++ b/test/data/json.json @@ -0,0 +1,174 @@ +{ +    "files": [ +        {"path": "package.json", "ignore": true}, +        {"path": "package-lock.json", "ignore": true}, +        {"path": "jsconfig.json", "ignore": true}, +        {"path": ".stylelintrc.json", "ignore": true}, +        {"path": ".htmlvalidate.json", "ignore": true}, +        {"path": ".eslintrc.json", "ignore": true}, +        {"path": ".vscode/settings.json", "ignore": true}, +        {"path": ".vscode/extensions.json", "ignore": true}, +        {"path": "dev/jsconfig.json", "ignore": true}, +        {"path": "ext/manifest.json", "ignore": true}, +        {"path": "test/data/dictionaries/invalid-dictionary1/index.json", "ignore": true}, +        {"path": "test/data/dictionaries/invalid-dictionary2/kanji_bank_1.json", "ignore": true}, +        {"path": "test/data/dictionaries/invalid-dictionary2/index.json", "ignore": true}, +        {"path": "test/data/dictionaries/invalid-dictionary3/kanji_meta_bank_1.json", "ignore": true}, +        {"path": "test/data/dictionaries/invalid-dictionary3/index.json", "ignore": true}, +        {"path": "test/data/dictionaries/invalid-dictionary4/tag_bank_1.json", "ignore": true}, +        {"path": "test/data/dictionaries/invalid-dictionary4/index.json", "ignore": true}, +        {"path": "test/data/dictionaries/invalid-dictionary5/term_bank_1.json", "ignore": true}, +        {"path": "test/data/dictionaries/invalid-dictionary5/index.json", "ignore": true}, +        {"path": "test/data/dictionaries/invalid-dictionary6/term_meta_bank_1.json", "ignore": true}, +        {"path": "test/data/dictionaries/invalid-dictionary6/index.json", "ignore": true}, +        {"path": "test/jsconfig.json", "ignore": true}, + +        { +            "path": "dev/data/manifest-variants.json", +            "typeFile": "types/dev/manifest.d.ts", +            "type": "ManifestConfig" +        }, +        { +            "path": "ext/data/deinflect.json", +            "typeFile": "types/ext/deinflector.d.ts", +            "type": "ReasonsRaw" +        }, +        { +            "path": "ext/data/pronunciation-style.json", +            "typeFile": "types/ext/css-style-applier.d.ts", +            "type": "RawStyleData" +        }, +        { +            "path": "ext/data/structured-content-style.json", +            "typeFile": "types/ext/css-style-applier.d.ts", +            "type": "RawStyleData" +        }, +        { +            "path": "ext/data/schemas/dictionary-index-schema.json", +            "typeFile": "types/test/json.d.ts", +            "type": "AjvSchema" +        }, +        { +            "path": "ext/data/schemas/dictionary-kanji-bank-v1-schema.json", +            "typeFile": "types/test/json.d.ts", +            "type": "AjvSchema" +        }, +        { +            "path": "ext/data/schemas/dictionary-kanji-bank-v3-schema.json", +            "typeFile": "types/test/json.d.ts", +            "type": "AjvSchema" +        }, +        { +            "path": "ext/data/schemas/dictionary-kanji-meta-bank-v3-schema.json", +            "typeFile": "types/test/json.d.ts", +            "type": "AjvSchema" +        }, +        { +            "path": "ext/data/schemas/dictionary-tag-bank-v3-schema.json", +            "typeFile": "types/test/json.d.ts", +            "type": "AjvSchema" +        }, +        { +            "path": "ext/data/schemas/dictionary-term-bank-v1-schema.json", +            "typeFile": "types/test/json.d.ts", +            "type": "AjvSchema" +        }, +        { +            "path": "ext/data/schemas/dictionary-term-bank-v3-schema.json", +            "typeFile": "types/test/json.d.ts", +            "type": "AjvSchema" +        }, +        { +            "path": "ext/data/schemas/dictionary-term-meta-bank-v3-schema.json", +            "typeFile": "types/test/json.d.ts", +            "type": "AjvSchema" +        }, +        { +            "path": "ext/data/schemas/custom-audio-list-schema.json", +            "typeFile": "types/test/json.d.ts", +            "type": "AjvSchema" +        }, +        { +            "path": "ext/data/schemas/options-schema.json", +            "typeFile": "types/test/json.d.ts", +            "type": "AjvSchema" +        }, +        { +            "path": "test/data/translator-test-inputs.json", +            "typeFile": "types/test/translator.d.ts", +            "type": "TranslatorTestInputs", +            "jsconfig": "test" +        }, +        { +            "path": "test/data/translator-test-results.json", +            "typeFile": "types/test/translator.d.ts", +            "type": "TranslatorTestResults", +            "jsconfig": "test" +        }, +        { +            "path": "test/data/translator-test-results-note-data1.json", +            "typeFile": "types/test/translator.d.ts", +            "type": "TranslatorTestNoteDataResults", +            "jsconfig": "test" +        }, +        { +            "path": "test/data/anki-note-builder-test-results.json", +            "typeFile": "types/test/translator.d.ts", +            "type": "AnkiNoteBuilderTestResults", +            "jsconfig": "test" +        }, +        { +            "path": "test/data/json.json", +            "typeFile": "types/test/json.d.ts", +            "type": "JsonInfo" +        }, +        { +            "path": "test/data/dictionaries/valid-dictionary1/index.json", +            "typeFile": "types/ext/dictionary-data.d.ts", +            "type": "Index", +            "schema": "ext/data/schemas/dictionary-index-schema.json" +        }, +        { +            "path": "test/data/dictionaries/valid-dictionary1/kanji_bank_1.json", +            "typeFile": "types/ext/dictionary-data.d.ts", +            "type": "KanjiV3Array", +            "schema": "ext/data/schemas/dictionary-kanji-bank-v3-schema.json" +        }, +        { +            "path": "test/data/dictionaries/valid-dictionary1/kanji_meta_bank_1.json", +            "typeFile": "types/ext/dictionary-data.d.ts", +            "type": "KanjiMetaArray", +            "schema": "ext/data/schemas/dictionary-kanji-meta-bank-v3-schema.json" +        }, +        { +            "path": "test/data/dictionaries/valid-dictionary1/tag_bank_1.json", +            "typeFile": "types/ext/dictionary-data.d.ts", +            "type": "TagArray", +            "schema": "ext/data/schemas/dictionary-tag-bank-v3-schema.json" +        }, +        { +            "path": "test/data/dictionaries/valid-dictionary1/tag_bank_2.json", +            "typeFile": "types/ext/dictionary-data.d.ts", +            "type": "TagArray", +            "schema": "ext/data/schemas/dictionary-tag-bank-v3-schema.json" +        }, +        { +            "path": "test/data/dictionaries/valid-dictionary1/tag_bank_3.json", +            "typeFile": "types/ext/dictionary-data.d.ts", +            "type": "TagArray", +            "schema": "ext/data/schemas/dictionary-tag-bank-v3-schema.json" +        }, +        { +            "path": "test/data/dictionaries/valid-dictionary1/term_bank_1.json", +            "typeFile": "types/ext/dictionary-data.d.ts", +            "type": "TermV3Array", +            "schema": "ext/data/schemas/dictionary-term-bank-v3-schema.json" +        }, +        { +            "path": "test/data/dictionaries/valid-dictionary1/term_meta_bank_1.json", +            "typeFile": "types/ext/dictionary-data.d.ts", +            "type": "TermMetaArray", +            "schema": "ext/data/schemas/dictionary-term-meta-bank-v3-schema.json" +        } +    ] +}
\ No newline at end of file diff --git a/test/data/translator-test-inputs.json b/test/data/translator-test-inputs.json index cf4b8f6a..5afb6a60 100644 --- a/test/data/translator-test-inputs.json +++ b/test/data/translator-test-inputs.json @@ -19,12 +19,12 @@              "sortFrequencyDictionary": null,              "sortFrequencyDictionaryOrder": "descending",              "removeNonJapaneseCharacters": true, -            "convertHalfWidthCharacters": false, -            "convertNumericCharacters": false, -            "convertAlphabeticCharacters": false, -            "convertHiraganaToKatakana": false, -            "convertKatakanaToHiragana": false, -            "collapseEmphaticSequences": false, +            "convertHalfWidthCharacters": "false", +            "convertNumericCharacters": "false", +            "convertAlphabeticCharacters": "false", +            "convertHiraganaToKatakana": "false", +            "convertKatakanaToHiragana": "false", +            "collapseEmphaticSequences": "false",              "textReplacements": [                  null              ], diff --git a/test/fixtures/translator-test.js b/test/fixtures/translator-test.js index cb1a3ef5..3304c587 100644 --- a/test/fixtures/translator-test.js +++ b/test/fixtures/translator-test.js @@ -18,8 +18,8 @@  import {IDBKeyRange, indexedDB} from 'fake-indexeddb';  import {readFileSync} from 'fs'; -import {fileURLToPath, pathToFileURL} from 'node:url'; -import {dirname, join, resolve} from 'path'; +import {fileURLToPath} from 'node:url'; +import {dirname, join} from 'path';  import {expect, vi} from 'vitest';  import {parseJson} from '../../dev/json.js';  import {createDictionaryArchive} from '../../dev/util.js'; @@ -28,43 +28,13 @@ import {DictionaryDatabase} from '../../ext/js/language/dictionary-database.js';  import {DictionaryImporter} from '../../ext/js/language/dictionary-importer.js';  import {JapaneseUtil} from '../../ext/js/language/sandbox/japanese-util.js';  import {Translator} from '../../ext/js/language/translator.js'; +import {chrome, fetch} from '../mocks/common.js';  import {DictionaryImporterMediaLoader} from '../mocks/dictionary-importer-media-loader.js';  import {createDomTest} from './dom-test.js';  const extDir = join(dirname(fileURLToPath(import.meta.url)), '../../ext');  const deinflectionReasonsPath = join(extDir, 'data/deinflect.json'); -/** @type {import('dev/vm').PseudoChrome} */ -const chrome = { -    runtime: { -        getURL: (path) => { -            return pathToFileURL(join(extDir, path.replace(/^\//, ''))).href; -        } -    } -}; - -/** - * @param {string} url - * @returns {Promise<import('dev/vm').PseudoFetchResponse>} - */ -async function fetch(url) { -    let filePath; -    try { -        filePath = fileURLToPath(url); -    } catch (e) { -        filePath = resolve(extDir, url.replace(/^[/\\]/, '')); -    } -    await Promise.resolve(); -    const content = readFileSync(filePath, {encoding: null}); -    return { -        ok: true, -        status: 200, -        statusText: 'OK', -        text: async () => content.toString('utf8'), -        json: async () => parseJson(content.toString('utf8')) -    }; -} -  vi.stubGlobal('indexedDB', indexedDB);  vi.stubGlobal('IDBKeyRange', IDBKeyRange);  vi.stubGlobal('fetch', fetch); diff --git a/test/jsconfig.json b/test/jsconfig.json index 9ab0c332..a9845861 100644 --- a/test/jsconfig.json +++ b/test/jsconfig.json @@ -15,7 +15,9 @@              "*": ["../types/ext/*"],              "dev/*": ["../types/dev/*"],              "test/*": ["../types/test/*"], -            "rollup/parseAst": ["../types/other/rollup-parse-ast"] +            "rollup/parseAst": ["../types/other/rollup-parse-ast"], +            "ext/json-schema": ["../types/ext/json-schema"], +            "json-schema": ["json-schema"]          },          "types": [              "chrome", diff --git a/test/json-schema.test.js b/test/json-schema.test.js index fb7644de..0c1a483b 100644 --- a/test/json-schema.test.js +++ b/test/json-schema.test.js @@ -23,7 +23,7 @@ import {parseJson} from '../dev/json.js';  import {JsonSchema} from '../ext/js/data/json-schema.js';  /** - * @param {import('json-schema').Schema} schema + * @param {import('ext/json-schema').Schema} schema   * @param {unknown} value   * @returns {boolean}   */ @@ -32,18 +32,18 @@ function schemaValidate(schema, value) {  }  /** - * @param {import('json-schema').Schema} schema + * @param {import('ext/json-schema').Schema} schema   * @param {unknown} value - * @returns {import('json-schema').Value} + * @returns {import('ext/json-schema').Value}   */  function getValidValueOrDefault(schema, value) {      return new JsonSchema(schema).getValidValueOrDefault(value);  }  /** - * @param {import('json-schema').Schema} schema - * @param {import('json-schema').Value} value - * @returns {import('json-schema').Value} + * @param {import('ext/json-schema').Schema} schema + * @param {import('ext/json-schema').Value} value + * @returns {import('ext/json-schema').Value}   */  function createProxy(schema, value) {      return new JsonSchema(schema).createProxy(value); @@ -62,7 +62,7 @@ function clone(value) {  /** */  function testValidate1() {      test('Validate1', () => { -        /** @type {import('json-schema').Schema} */ +        /** @type {import('ext/json-schema').Schema} */          const schema = {              allOf: [                  { @@ -123,7 +123,7 @@ function testValidate1() {  /** */  function testValidate2() {      test('Validate2', () => { -        /** @type {{schema: import('json-schema').Schema, inputs: {expected: boolean, value: unknown}[]}[]} */ +        /** @type {{schema: import('ext/json-schema').Schema, inputs: {expected: boolean, value: unknown}[]}[]} */          const data = [              // String tests              { @@ -530,7 +530,7 @@ function testValidate2() {  /** */  function testGetValidValueOrDefault1() {      test('GetValidValueOrDefault1', () => { -        /** @type {{schema: import('json-schema').Schema, inputs: [value: unknown, expected: unknown][]}[]} */ +        /** @type {{schema: import('ext/json-schema').Schema, inputs: [value: unknown, expected: unknown][]}[]} */          const data = [              // Test value defaulting on objects with additionalProperties=false              { @@ -702,7 +702,7 @@ function testGetValidValueOrDefault1() {                      type: 'object',                      required: ['toString'],                      properties: { -                        toString: /** @type {import('json-schema').SchemaObject} */ ({ +                        toString: /** @type {import('ext/json-schema').SchemaObject} */ ({                              type: 'string',                              default: 'default'                          }) @@ -888,7 +888,7 @@ function testGetValidValueOrDefault1() {  /** */  function testProxy1() {      test('Proxy1', () => { -        /** @type {{schema: import('json-schema').Schema, tests: {error: boolean, value?: import('json-schema').Value, action: (value: import('core').SafeAny) => void}[]}[]} */ +        /** @type {{schema: import('ext/json-schema').Schema, tests: {error: boolean, value?: import('ext/json-schema').Value, action: (value: import('core').SafeAny) => void}[]}[]} */          const data = [              // Object tests              { diff --git a/test/json.test.js b/test/json.test.js new file mode 100644 index 00000000..8cf01491 --- /dev/null +++ b/test/json.test.js @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <https://www.gnu.org/licenses/>. + */ + +import Ajv from 'ajv'; +import {readFileSync} from 'fs'; +import {join, dirname as pathDirname} from 'path'; +import {createGenerator} from 'ts-json-schema-generator'; +import {fileURLToPath} from 'url'; +import {describe, expect, test} from 'vitest'; +import {parseJson} from '../dev/json.js'; +import {getAllFiles} from '../dev/util.js'; + +const dirname = pathDirname(fileURLToPath(import.meta.url)); +const rootDir = join(dirname, '..'); + +/** + * @param {import('test/json').JsconfigType|undefined} jsconfigType + * @returns {string} + */ +function getJsconfigPath(jsconfigType) { +    let path; +    switch (jsconfigType) { +        case 'dev': path = '../dev/jsconfig.json'; break; +        case 'test': path = '../test/jsconfig.json'; break; +        default: path = '../jsconfig.json'; break; +    } +    return join(dirname, path); +} + +/** + * @returns {Ajv} + */ +function createAjv() { +    return new Ajv({ +        meta: true, +        strictTuples: false, +        allowUnionTypes: true +    }); +} + +/** + * @param {string} path + * @param {string} type + * @param {import('test/json').JsconfigType|undefined} jsconfigType + * @returns {import('ajv').ValidateFunction<unknown>} + */ +function createValidatorFunctionFromTypeScript(path, type, jsconfigType) { +    /** @type {import('ts-json-schema-generator/dist/src/Config').Config} */ +    const config = { +        path, +        tsconfig: getJsconfigPath(jsconfigType), +        type, +        jsDoc: 'none', +        additionalProperties: false, +        minify: false, +        expose: 'none', +        strictTuples: true +    }; +    const schema = createGenerator(config).createSchema(config.type); +    const ajv = createAjv(); +    return ajv.compile(schema); +} + +/** + * @param {string} path + * @returns {import('ajv').ValidateFunction<unknown>} + */ +function createValidatorFunctionFromSchemaJson(path) { +    /** @type {import('ajv').Schema} */ +    const schema = parseJson(readFileSync(path, {encoding: 'utf8'})); +    const ajv = createAjv(); +    return ajv.compile(schema); +} + +/** + * @param {string} value + * @returns {string} + */ +function normalizePathDirectorySeparators(value) { +    return value.replace(/\\/g, '/'); +} + + +describe.concurrent('JSON validation', () => { +    const ignoreDirectories = new Set([ +        'builds', +        'dictionaries', +        'node_modules', +        'playwright-report', +        'playwright', +        'test-results', +        'dev/lib', +        'test/playwright' +    ]); + +    const existingJsonFiles = getAllFiles(rootDir, (path, isDirectory) => { +        const fileNameNormalized = normalizePathDirectorySeparators(path); +        if (isDirectory) { +            return !ignoreDirectories.has(fileNameNormalized); +        } else { +            return /\.json$/i.test(fileNameNormalized); +        } +    }); +    /** @type {Set<string>} */ +    const existingJsonFileSet = new Set(); +    for (const path of existingJsonFiles) { +        existingJsonFileSet.add(normalizePathDirectorySeparators(path)); +    } + +    const jsonFileName = 'json.json'; + +    /** @type {import('test/json').JsonInfo} */ +    const jsonFileData = parseJson(readFileSync(join(dirname, `data/${jsonFileName}`), {encoding: 'utf8'})); + +    test(`Each item in ${jsonFileName} must have a unique path`, () => { +        /** @type {Set<string>} */ +        const set = new Set(); +        for (const {path} of jsonFileData.files) { +            set.add(path); +        } +        expect(set.size).toBe(jsonFileData.files.length); +    }); + +    /** @type {Map<string, import('test/json').JsonFileInfo>} */ +    const jsonFileMap = new Map(); +    for (const item of jsonFileData.files) { +        jsonFileMap.set(item.path, item); +    } + +    // Validate file existance +    const requiredFiles = jsonFileData.files.filter((v) => !v.ignore); +    test.each(requiredFiles)('File must exist in project: $path', ({path}) => { +        expect(existingJsonFileSet.has(path)).toBe(true); +    }); + +    // Validate new files +    const existingJsonFiles2 = existingJsonFiles.map((path) => ({path: normalizePathDirectorySeparators(path)})); +    test.each(existingJsonFiles2)(`File must exist in ${jsonFileName}: $path`, ({path}) => { +        expect(jsonFileMap.has(path)).toBe(true); +    }); + +    // Validate schemas 1 +    /** @type {import('test/json').JsonFileParseInfo[]} */ +    const schemaValidationTargets1 = []; +    for (const info of jsonFileData.files) { +        if (info.ignore || !existingJsonFileSet.has(info.path)) { continue; } +        schemaValidationTargets1.push(info); +    } +    test.each(schemaValidationTargets1)('Validating file against TypeScript: $path', ({path, typeFile, type, jsconfig}) => { +        const validate = createValidatorFunctionFromTypeScript(join(rootDir, typeFile), type, jsconfig); +        const data = parseJson(readFileSync(join(rootDir, path), {encoding: 'utf8'})); +        const valid = validate(data); +        const {errors} = validate; +        expect(errors).toBe(null); +        expect(valid).toBe(true); +    }); + +    // Validate schemas 2 +    /** @type {{path: string, schema: string}[]} */ +    const schemaValidationTargets2 = []; +    for (const info of jsonFileData.files) { +        if (info.ignore || !existingJsonFileSet.has(info.path)) { continue; } +        const {schema, path} = info; +        if (typeof schema !== 'string') { continue; } +        schemaValidationTargets2.push({schema, path}); +    } +    test.each(schemaValidationTargets2)('Validating file against schema: $path', ({path, schema}) => { +        const validate = createValidatorFunctionFromSchemaJson(join(rootDir, schema)); +        const data = parseJson(readFileSync(join(rootDir, path), {encoding: 'utf8'})); +        const valid = validate(data); +        const {errors} = validate; +        expect(errors).toBe(null); +        expect(valid).toBe(true); +    }); +}); diff --git a/test/mocks/common.js b/test/mocks/common.js new file mode 100644 index 00000000..7fe30a3e --- /dev/null +++ b/test/mocks/common.js @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2020-2022  Yomichan Authors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <https://www.gnu.org/licenses/>. + */ + +import {readFileSync} from 'fs'; +import {fileURLToPath, pathToFileURL} from 'node:url'; +import {dirname, join, resolve} from 'path'; +import {parseJson} from '../../dev/json.js'; + +const extDir = join(dirname(fileURLToPath(import.meta.url)), '../../ext'); + +/** @type {import('test/mocks').ChromeMock} */ +export const chrome = { +    runtime: { +        getURL: (path) => { +            return pathToFileURL(join(extDir, path.replace(/^\//, ''))).href; +        } +    } +}; + +/** @type {import('test/mocks').FetchMock} */ +export async function fetch(url) { +    let filePath; +    try { +        filePath = fileURLToPath(url); +    } catch (e) { +        filePath = resolve(extDir, url.replace(/^[/\\]/, '')); +    } +    await Promise.resolve(); +    const content = readFileSync(filePath, {encoding: null}); +    return { +        ok: true, +        status: 200, +        statusText: 'OK', +        text: async () => content.toString('utf8'), +        json: async () => parseJson(content.toString('utf8')) +    }; +} diff --git a/test/options-util.test.js b/test/options-util.test.js index 41185756..7bb9767a 100644 --- a/test/options-util.test.js +++ b/test/options-util.test.js @@ -19,40 +19,15 @@  /* eslint-disable no-multi-spaces */  import fs from 'fs'; -import url, {fileURLToPath} from 'node:url'; +import {fileURLToPath} from 'node:url';  import path from 'path';  import {expect, test, vi} from 'vitest'; -import {parseJson} from '../dev/json.js';  import {OptionsUtil} from '../ext/js/data/options-util.js';  import {TemplatePatcher} from '../ext/js/templates/template-patcher.js'; +import {chrome, fetch} from './mocks/common.js';  const dirname = path.dirname(fileURLToPath(import.meta.url)); -/** - * @param {string} url2 - * @returns {Promise<import('dev/vm').PseudoFetchResponse>} - */ -async function fetch(url2) { -    const filePath = url.fileURLToPath(url2); -    await Promise.resolve(); -    const content = fs.readFileSync(filePath, {encoding: null}); -    return { -        ok: true, -        status: 200, -        statusText: 'OK', -        text: async () => Promise.resolve(content.toString('utf8')), -        json: async () => Promise.resolve(parseJson(content.toString('utf8'))) -    }; -} -/** @type {import('dev/vm').PseudoChrome} */ -const chrome = { -    runtime: { -        getURL(path2) { -            return url.pathToFileURL(path.join(dirname, '..', 'ext', path2.replace(/^\//, ''))).href; -        } -    } -}; -  vi.stubGlobal('fetch', fetch);  vi.stubGlobal('chrome', chrome); diff --git a/test/profile-conditions-util.test.js b/test/profile-conditions-util.test.js index f64ce79c..d5c8f8d2 100644 --- a/test/profile-conditions-util.test.js +++ b/test/profile-conditions-util.test.js @@ -62,7 +62,7 @@ function testNormalizeContext() {  /** */  function testSchemas() {      test('Schemas', () => { -        /** @type {{conditionGroups: import('settings').ProfileConditionGroup[], expectedSchema?: import('json-schema').Schema, inputs?: {expected: boolean, context: import('settings').OptionsContext}[]}[]} */ +        /** @type {{conditionGroups: import('settings').ProfileConditionGroup[], expectedSchema?: import('ext/json-schema').Schema, inputs?: {expected: boolean, context: import('settings').OptionsContext}[]}[]} */          const data = [              // Empty              { diff --git a/test/utilities/translator.js b/test/utilities/translator.js index 9073b206..81081af6 100644 --- a/test/utilities/translator.js +++ b/test/utilities/translator.js @@ -18,10 +18,11 @@  /** + * TODO : This function is not very type safe at the moment, could be improved.   * @template {import('translation').FindTermsOptions|import('translation').FindKanjiOptions} T   * @param {string} dictionaryName - * @param {import('dev/vm').OptionsPresetObject} optionsPresets - * @param {string|import('dev/vm').OptionsPresetObject|(string|import('dev/vm').OptionsPresetObject)[]} optionsArray + * @param {import('test/translator').OptionsPresetObject} optionsPresets + * @param {import('test/translator').OptionsList} optionsArray   * @returns {T}   * @throws {Error}   */ diff --git a/types/dev/manifest.d.ts b/types/dev/manifest.d.ts index e455208f..af475ee9 100644 --- a/types/dev/manifest.d.ts +++ b/types/dev/manifest.d.ts @@ -30,7 +30,7 @@ export type ManifestVariant = {      fileName?: string;      fileCopies?: string[];      excludeFiles?: string[]; -    modifications: Modification[]; +    modifications?: Modification[];  };  export type Modification = ( diff --git a/types/ext/dictionary-data.d.ts b/types/ext/dictionary-data.d.ts index f594f913..b194c190 100644 --- a/types/ext/dictionary-data.d.ts +++ b/types/ext/dictionary-data.d.ts @@ -44,6 +44,8 @@ export type IndexTag = {      score: number;  }; +export type TermV1Array = TermV1[]; +  export type TermV1 = [      expression: string,      reading: string, @@ -53,6 +55,8 @@ export type TermV1 = [      ...glossary: string[],  ]; +export type TermV3Array = TermV3[]; +  export type TermV3 = [      expression: string,      reading: string, @@ -64,6 +68,8 @@ export type TermV3 = [      termTags: string,  ]; +export type KanjiV1Array = KanjiV1[]; +  export type KanjiV1 = [      character: string,      onyomi: string, @@ -72,6 +78,8 @@ export type KanjiV1 = [      ...meanings: string[],  ]; +export type KanjiV3Array = KanjiV3[]; +  export type KanjiV3 = [      character: string,      onyomi: string, @@ -93,7 +101,13 @@ export type TermGlossaryText = {type: 'text', text: string};  export type TermGlossaryImage = {type: 'image'} & TermImage;  export type TermGlossaryStructuredContent = {type: 'structured-content', content: StructuredContent.Content}; -export type TermImage = StructuredContent.ImageElementBase; +export type TermImage = StructuredContent.ImageElementBase & { +    // Compatibility properties +    verticalAlign?: undefined; +    sizeUnits?: undefined; +}; + +export type TagArray = Tag[];  export type Tag = [      name: string, @@ -109,6 +123,8 @@ export type GenericFrequencyData = string | number | {      reading?: undefined; // Used for type disambiguation, field does not actually exist  }; +export type TermMetaArray = TermMeta[]; +  export type TermMeta = TermMetaFrequency | TermMetaPitch;  export type TermMetaFrequencyDataWithReading = { @@ -138,6 +154,8 @@ export type TermMetaPitch = [      data: TermMetaPitchData,  ]; +export type KanjiMetaArray = KanjiMeta[]; +  export type KanjiMeta = KanjiMetaFrequency;  export type KanjiMetaFrequency = [ diff --git a/types/ext/structured-content.d.ts b/types/ext/structured-content.d.ts index 09755c88..572b827a 100644 --- a/types/ext/structured-content.d.ts +++ b/types/ext/structured-content.d.ts @@ -161,14 +161,6 @@ export type ImageElementBase = {       * Whether or not the image can be collapsed.       */      collapsible?: boolean; -    /** -     * This property is not defined on the base class. -     */ -    verticalAlign?: undefined; -    /** -     * This property is not defined on the base class. -     */ -    sizeUnits?: undefined;  };  export type ImageElement = ImageElementBase & { diff --git a/types/ext/translation.d.ts b/types/ext/translation.d.ts index 3adfc673..595a5a35 100644 --- a/types/ext/translation.d.ts +++ b/types/ext/translation.d.ts @@ -28,7 +28,7 @@ export type FindKanjiOptions = {       * The mapping of dictionaries to search for kanji in.       * The key is the dictionary name.       */ -    enabledDictionaryMap: Map<string, FindKanjiDictionary>; +    enabledDictionaryMap: KanjiEnabledDictionaryMap;      /**       * Whether or not non-Japanese characters should be searched.       */ diff --git a/types/test/json.d.ts b/types/test/json.d.ts new file mode 100644 index 00000000..9ae21b0f --- /dev/null +++ b/types/test/json.d.ts @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <https://www.gnu.org/licenses/>. + */ + +import type {Schema} from 'ajv'; + +export type JsonInfo = { +    files: JsonFileInfo[]; +}; + +export type JsonFileInfo = JsonFileIgnoreInfo | JsonFileParseInfo; + +export type JsonFileIgnoreInfo = { +    path: string; +    ignore: true; +}; + +export type JsonFileParseInfo = { +    path: string; +    ignore?: undefined; +    typeFile: string; +    type: string; +    schema?: string; +    jsconfig?: JsconfigType; +}; + +export type AjvSchema = Schema; + +export type JsconfigType = 'main' | 'dev' | 'test'; diff --git a/types/dev/vm.d.ts b/types/test/mocks.d.ts index 3eb0949f..13b56ac6 100644 --- a/types/dev/vm.d.ts +++ b/types/test/mocks.d.ts @@ -15,24 +15,18 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -import type * as Translation from '../ext/translation'; - -export type PseudoChrome = { +export type ChromeMock = {      runtime: {          getURL(path: string): string;      };  }; -export type PseudoFetchResponse = { +export type FetchMock = (url: string) => Promise<FetchResponseMock>; + +export type FetchResponseMock = {      ok: boolean;      status: number;      statusText: string;      text(): Promise<string>;      json(): Promise<unknown>;  }; - -export type OptionsPresetObject = { -    [key: string]: OptionsPreset; -}; - -export type OptionsPreset = Partial<Translation.FindTermsOptions>; diff --git a/types/test/translator.d.ts b/types/test/translator.d.ts index 3e4c8b9d..7dff116e 100644 --- a/types/test/translator.d.ts +++ b/types/test/translator.d.ts @@ -15,12 +15,49 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -import type {OptionsPresetObject} from 'dev/vm'; +import type {FindTermsMatchType, FindTermsSortOrder, FindTermsVariantMode, FindTermsEmphaticSequencesMode, FindKanjiDictionary, FindTermDictionary} from '../ext/translation';  import type {FindTermsMode} from 'translator';  import type {DictionaryEntry} from 'dictionary';  import type {NoteData} from 'anki-templates';  import type {NoteFields} from 'anki'; +export type OptionsPresetObject = { +    [key: string]: OptionsPreset; +}; + +export type OptionsList = string | (string | OptionsPreset)[]; + +export type OptionsPreset = FindKanjiOptionsPreset | FindTermsOptionsPreset; + +export type FindKanjiOptionsPreset = { +    enabledDictionaryMap?: [key: string, value: FindKanjiDictionary][]; +    removeNonJapaneseCharacters?: boolean; +}; + +export type FindTermsOptionsPreset = { +    matchType?: FindTermsMatchType; +    deinflect?: boolean; +    mainDictionary?: string; +    sortFrequencyDictionary?: string | null; +    sortFrequencyDictionaryOrder?: FindTermsSortOrder; +    removeNonJapaneseCharacters?: boolean; +    convertHalfWidthCharacters?: FindTermsVariantMode; +    convertNumericCharacters?: FindTermsVariantMode; +    convertAlphabeticCharacters?: FindTermsVariantMode; +    convertHiraganaToKatakana?: FindTermsVariantMode; +    convertKatakanaToHiragana?: FindTermsVariantMode; +    collapseEmphaticSequences?: FindTermsEmphaticSequencesMode; +    textReplacements?: (FindTermsTextReplacement[] | null)[]; +    enabledDictionaryMap?: [key: string, value: FindTermDictionary][]; +    excludeDictionaryDefinitions?: string[] | null; +}; + +export type FindTermsTextReplacement = { +    pattern: string; +    flags: string; +    replacement: string; +}; +  export type TranslatorTestInputs = {      optionsPresets: OptionsPresetObject;      tests: TestInput[]; @@ -32,7 +69,7 @@ export type TestInputFindKanji = {      func: 'findKanji';      name: string;      text: string; -    options: string; +    options: OptionsList;  };  export type TestInputFindTerm = { @@ -40,7 +77,7 @@ export type TestInputFindTerm = {      name: string;      mode: FindTermsMode;      text: string; -    options: string; +    options: OptionsList;  };  export type TranslatorTestResults = TranslatorTestResult[]; @@ -55,7 +92,7 @@ export type TranslatorTestNoteDataResults = TranslatorTestNoteDataResult[];  export type TranslatorTestNoteDataResult = {      name: string; -    noteDataList: NoteData[]; +    noteDataList: Omit<NoteData, 'dictionaryEntry'>[] | null;  };  export type AnkiNoteBuilderTestResults = AnkiNoteBuilderTestResult[]; |