@expo/schemer
Version:
Centralized scheme validation library for Expo
266 lines (264 loc) • 9.59 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
ErrorCodes: null,
SchemerError: null,
ValidationError: null,
default: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: Object.getOwnPropertyDescriptor(all, name).get
});
}
_export(exports, {
get ErrorCodes () {
return _Error.ErrorCodes;
},
get SchemerError () {
return _Error.SchemerError;
},
get ValidationError () {
return _Error.ValidationError;
},
get default () {
return Schemer;
}
});
function _schemautils() {
const data = require("@expo/schema-utils");
_schemautils = function() {
return data;
};
return data;
}
function _fs() {
const data = /*#__PURE__*/ _interop_require_default(require("fs"));
_fs = function() {
return data;
};
return data;
}
function _path() {
const data = /*#__PURE__*/ _interop_require_default(require("path"));
_path = function() {
return data;
};
return data;
}
function _probeimagesize() {
const data = /*#__PURE__*/ _interop_require_default(require("probe-image-size"));
_probeimagesize = function() {
return data;
};
return data;
}
const _Error = require("./Error");
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
const ERROR_CODES = {
additionalProperties: 'SCHEMA_ADDITIONAL_PROPERTY',
required: 'SCHEMA_MISSING_REQUIRED_PROPERTY',
pattern: 'SCHEMA_INVALID_PATTERN',
not: 'SCHEMA_INVALID_NOT'
};
const toFieldPath = (path)=>path[0] === '.' ? path.slice(1) : path;
class Schemer {
options;
schema;
rootDir;
manualValidationErrors;
schemaValidationErrors;
constructor(schema, options = {}){
this.options = {
...options
};
this.schema = schema;
this.rootDir = this.options.rootDir || __dirname;
this.manualValidationErrors = [];
this.schemaValidationErrors = [];
}
_formatValidationError({ keyword, path, value, message }) {
return new _Error.ValidationError({
errorCode: ERROR_CODES[keyword] ?? 'SCHEMA_VALIDATION_ERROR',
fieldPath: toFieldPath(path),
message,
data: value,
meta: undefined
});
}
getErrors() {
return [
...this.schemaValidationErrors,
...this.manualValidationErrors
];
}
_throwOnErrors() {
// Clean error state after each validation
const errors = this.getErrors();
if (errors.length > 0) {
this.manualValidationErrors = [];
this.schemaValidationErrors = [];
throw new _Error.SchemerError(errors);
}
}
async validateAll(data) {
this._validateSchema(data);
await this._validateAssetsAsync(data);
this._throwOnErrors();
}
async validateAssetsAsync(data) {
await this._validateAssetsAsync(data);
this._throwOnErrors();
}
async validateSchemaAsync(data) {
this._validateSchema(data);
this._throwOnErrors();
}
_validateSchema(data) {
try {
(0, _schemautils().validate)(this.schema, data);
} catch (error) {
if (error instanceof _schemautils().ValidationError) {
for (const validationError of error.errors){
this.schemaValidationErrors.push(this._formatValidationError(validationError));
}
} else {
throw error;
}
}
}
async _validateAssetsAsync(data) {
const assets = [];
(0, _schemautils().visit)(this.schema, data, (subSchema, value, path)=>{
const meta = subSchema.meta;
if (path && meta?.asset) {
assets.push({
fieldPath: toFieldPath(path),
data: value,
meta
});
}
});
await Promise.all(assets.map(this._validateAssetAsync.bind(this)));
}
async _validateImageAsync({ fieldPath, data, meta }) {
if (meta && meta.asset && data) {
const { dimensions, square, contentTypePattern } = meta;
// filePath could be an URL
const filePath = _path().default.resolve(this.rootDir, data);
try {
// This cases on whether filePath is a remote URL or located on the machine
const isLocalFile = _fs().default.existsSync(filePath);
const probeResult = isLocalFile ? await (0, _probeimagesize().default)(require('fs').createReadStream(filePath)) : await (0, _probeimagesize().default)(data);
if (!probeResult) {
return;
}
const { width, height, type, mime } = probeResult;
const fileExtension = filePath.split('.').pop();
if (isLocalFile && mime !== `image/${fileExtension}`) {
this.manualValidationErrors.push(new _Error.ValidationError({
errorCode: 'FILE_EXTENSION_MISMATCH',
fieldPath,
message: `the file extension should match the content, but the file extension is .${fileExtension} while the file content at '${data}' is of type ${type}`,
data,
meta
}));
}
if (contentTypePattern && !mime.match(new RegExp(contentTypePattern))) {
this.manualValidationErrors.push(new _Error.ValidationError({
errorCode: 'INVALID_CONTENT_TYPE',
fieldPath,
message: `field '${fieldPath}' should point to ${meta.contentTypeHuman} but the file at '${data}' has type ${type}`,
data,
meta
}));
}
if (dimensions && (dimensions.height !== height || dimensions.width !== width)) {
this.manualValidationErrors.push(new _Error.ValidationError({
errorCode: 'INVALID_DIMENSIONS',
fieldPath,
message: `'${fieldPath}' should have dimensions ${dimensions.width}x${dimensions.height}, but the file at '${data}' has dimensions ${width}x${height}`,
data,
meta
}));
}
if (square && width !== height) {
this.manualValidationErrors.push(new _Error.ValidationError({
errorCode: 'NOT_SQUARE',
fieldPath,
message: `image should be square, but the file at '${data}' has dimensions ${width}x${height}`,
data,
meta
}));
}
} catch {
this.manualValidationErrors.push(new _Error.ValidationError({
errorCode: 'INVALID_ASSET_URI',
fieldPath,
message: `cannot access file at '${data}'`,
data,
meta
}));
}
}
}
async _validateDirectoryAsync({ fieldPath, data, meta }) {
if (meta && meta.asset && data) {
const filePath = _path().default.resolve(this.rootDir, data);
try {
if (!_fs().default.existsSync(filePath)) {
this.manualValidationErrors.push(new _Error.ValidationError({
errorCode: 'INVALID_ASSET_URI',
fieldPath,
message: `directory does not exist at '${data}'`,
data,
meta
}));
return;
}
if (!_fs().default.lstatSync(filePath).isDirectory()) {
this.manualValidationErrors.push(new _Error.ValidationError({
errorCode: 'INVALID_CONTENT_TYPE',
fieldPath,
message: `field '${fieldPath}' should point to ${meta.contentTypeHuman} but the path at '${data}' is not a directory`,
data,
meta
}));
}
} catch {
this.manualValidationErrors.push(new _Error.ValidationError({
errorCode: 'INVALID_ASSET_URI',
fieldPath,
message: `cannot access directory at '${data}'`,
data,
meta
}));
}
}
}
async _validateAssetAsync({ fieldPath, data, meta }) {
if (meta && meta.asset && data) {
if (meta.contentTypePattern && meta.contentTypePattern.startsWith('^image')) {
await this._validateImageAsync({
fieldPath,
data,
meta
});
} else if (meta.contentTypePattern === 'directory') {
await this._validateDirectoryAsync({
fieldPath,
data,
meta
});
}
}
}
}
//# sourceMappingURL=index.js.map