diff options
Diffstat (limited to 'src/data/validators.js')
-rw-r--r-- | src/data/validators.js | 126 |
1 files changed, 89 insertions, 37 deletions
diff --git a/src/data/validators.js b/src/data/validators.js index 4fc2ac6..5d68131 100644 --- a/src/data/validators.js +++ b/src/data/validators.js @@ -311,8 +311,11 @@ export function isCommentary(commentaryText) { const ownInput = commentaryText.slice(position, position + length); const restOfInput = commentaryText.slice(position + length); - const nextLineBreak = restOfInput.indexOf('\n'); - const upToNextLineBreak = restOfInput.slice(0, nextLineBreak); + + const upToNextLineBreak = + (restOfInput.includes('\n') + ? restOfInput.slice(0, restOfInput.indexOf('\n')) + : restOfInput); if (/\S/.test(upToNextLineBreak)) { throw new TypeError( @@ -420,6 +423,14 @@ const illegalContentSpec = [ {illegal: '\u2005', annotation: `four-per-em space`, ...illegalVisibleSpace}, {illegal: '\u205f', annotation: `medium mathematical space`, ...illegalVisibleSpace}, {illegal: '\xa0', annotation: `non-breaking space`, ...illegalVisibleSpace}, + + { + action: 'replace', + illegal: '<a href', + annotation: `HTML-style link`, + with: '[...](...)', + withAnnotation: `markdown`, + }, ]; for (const entry of illegalContentSpec) { @@ -432,24 +443,23 @@ for (const entry of illegalContentSpec) { } } -const illegalContentRegexp = - new RegExp( - illegalContentSpec - .map(entry => entry.illegal) - .map(illegal => `${illegal}+`) - .join('|'), - 'g'); - -const illegalCharactersInContent = +const illegalSequencesInContent = illegalContentSpec .map(entry => entry.illegal) - .join(''); + .map(illegal => + (illegal.length === 1 + ? `${illegal}+` + : `(?:${illegal})+`)) + .join('|'); + +const illegalContentRegexp = + new RegExp(illegalSequencesInContent, 'g'); const legalContentNearEndRegexp = - new RegExp(`[^\n${illegalCharactersInContent}]+$`); + new RegExp(`(?<=^|${illegalSequencesInContent})(?:(?!${illegalSequencesInContent}).)+$`); const legalContentNearStartRegexp = - new RegExp(`^[^\n${illegalCharactersInContent}]+`); + new RegExp(`^(?:(?!${illegalSequencesInContent}).)+`); const trimWhitespaceNearBothSidesRegexp = /^ +| +$/gm; @@ -595,16 +605,37 @@ export function isContentString(content) { export function isThingClass(thingClass) { isFunction(thingClass); - if (!Object.hasOwn(thingClass, Symbol.for('Thing.referenceType'))) { - throw new TypeError(`Expected a Thing constructor, missing Thing.referenceType`); + // This is *expressly* no faster than an instanceof check, because it's + // deliberately still walking the prototype chain for the provided object. + // (This is necessary because the symbol we're checking is defined only on + // the Thing constructor, and not directly on each subclass.) However, it's + // preferred over an instanceof check anyway, because instanceof would + // require that the #validators module has access to #thing, which it + // currently doesn't! + if (!(Symbol.for('Thing.isThingConstructor') in thingClass)) { + throw new TypeError(`Expected a Thing constructor, missing Thing.isThingConstructor`); + } + + return true; +} + +export function isThing(thing) { + isObject(thing); + + // This *is* faster than an instanceof check, because it doesn't walk the + // prototype chain. It works because this property is set as part of every + // Thing subclass's inherited "public class fields" - it's set directly on + // every constructed Thing. + if (!Object.hasOwn(thing, Symbol.for('Thing.isThing'))) { + throw new TypeError(`Expected a Thing, missing Thing.isThing`); } return true; } export const isContribution = validateProperties({ - who: isArtistRef, - what: optional(isStringNonEmpty), + artist: isArtistRef, + annotation: optional(isStringNonEmpty), }); export const isContributionList = validateArrayItems(isContribution); @@ -612,7 +643,7 @@ export const isContributionList = validateArrayItems(isContribution); export const isAdditionalFile = validateProperties({ title: isName, description: optional(isContentString), - files: validateArrayItems(isString), + files: optional(validateArrayItems(isString)), }); export const isAdditionalFileList = validateArrayItems(isAdditionalFile); @@ -632,10 +663,15 @@ export function isDimensions(dimensions) { if (dimensions.length !== 2) throw new TypeError(`Expected 2 item array`); - isPositive(dimensions[0]); - isInteger(dimensions[0]); - isPositive(dimensions[1]); - isInteger(dimensions[1]); + if (dimensions[0] !== null) { + isPositive(dimensions[0]); + isInteger(dimensions[0]); + } + + if (dimensions[1] !== null) { + isPositive(dimensions[1]); + isInteger(dimensions[1]); + } return true; } @@ -718,12 +754,31 @@ export function validateReferenceList(type = '') { return validateArrayItems(validateReference(type)); } +export function validateThing({ + referenceType: expectedReferenceType = '', +} = {}) { + return (thing) => { + isThing(thing); + + if (expectedReferenceType) { + const {[Symbol.for('Thing.referenceType')]: referenceType} = + thing.constructor; + + if (referenceType !== expectedReferenceType) { + throw new TypeError(`Expected only ${expectedReferenceType}, got other type: ${referenceType}`); + } + } + + return true; + }; +} + const validateWikiData_cache = {}; export function validateWikiData({ referenceType = '', allowMixedTypes = false, -}) { +} = {}) { if (referenceType && allowMixedTypes) { throw new TypeError(`Don't specify both referenceType and allowMixedTypes`); } @@ -752,25 +807,22 @@ export function validateWikiData({ let foundOtherObject = false; for (const object of array) { - const {[Symbol.for('Thing.referenceType')]: referenceType} = object.constructor; - - if (referenceType === undefined) { - foundOtherObject = true; - - // Early-exit if a Thing has been found - nothing more can be learned. - if (foundThing) { - throw new TypeError(`Expected array of wiki data objects, got mixed items`); - } - } else { - foundThing = true; - + if (Object.hasOwn(object, Symbol.for('Thing.isThing'))) { // Early-exit if a non-Thing object has been found - nothing more can // be learned. if (foundOtherObject) { throw new TypeError(`Expected array of wiki data objects, got mixed items`); } - allRefTypes.add(referenceType); + foundThing = true; + allRefTypes.add(object.constructor[Symbol.for('Thing.referenceType')]); + } else { + // Early-exit if a Thing has been found - nothing more can be learned. + if (foundThing) { + throw new TypeError(`Expected array of wiki data objects, got mixed items`); + } + + foundOtherObject = true; } } |