Skip to content

Commit

Permalink
Optimize performance a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
Marsup committed Oct 21, 2018
1 parent 24c8432 commit a1a79b3
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 127 deletions.
12 changes: 6 additions & 6 deletions lib/types/alternatives/index.js
Expand Up @@ -30,7 +30,7 @@ internals.Alternatives = class extends Any {

_base(value, state, options) {

let errors = [];
const errors = [];
const il = this._inner.matches.length;
const baseType = this._baseType;

Expand Down Expand Up @@ -62,7 +62,7 @@ internals.Alternatives = class extends Any {
return result;
}

errors = errors.concat(result.errors);
errors.push(...result.errors);
}

if (errors.length) {
Expand All @@ -82,7 +82,7 @@ internals.Alternatives = class extends Any {
for (let i = 0; i < schemas.length; ++i) {
const cast = Cast.schema(this._currentJoi, schemas[i]);
if (cast._refs.length) {
obj._refs = obj._refs.concat(cast._refs);
obj._refs.push(...cast._refs);
}

obj._inner.matches.push({ schema: cast });
Expand Down Expand Up @@ -134,15 +134,15 @@ internals.Alternatives = class extends Any {

if (!schemaCondition) {
Ref.push(obj._refs, item.ref);
obj._refs = obj._refs.concat(item.is._refs);
obj._refs.push(...item.is._refs);
}

if (item.then && item.then._refs) {
obj._refs = obj._refs.concat(item.then._refs);
obj._refs.push(...item.then._refs);
}

if (item.otherwise && item.otherwise._refs) {
obj._refs = obj._refs.concat(item.otherwise._refs);
obj._refs.push(...item.otherwise._refs);
}

obj._inner.matches.push(item);
Expand Down
173 changes: 94 additions & 79 deletions lib/types/any/index.js
Expand Up @@ -6,9 +6,13 @@ const Hoek = require('hoek');
const Settings = require('./settings');
const Ref = require('../../ref');
const Errors = require('../../errors');
const State = require('../state');
const Symbols = require('../symbols');

let Alternatives = null; // Delay-loaded to prevent circular dependencies
// Delay-loaded to prevent circular dependencies
let Alternatives = null;
let Cast = null;
let Schemas = null;


// Declare internals
Expand Down Expand Up @@ -97,7 +101,7 @@ module.exports = internals.Any = class {

checkOptions(options) {

const Schemas = require('../../schemas');
Schemas = Schemas || require('../../schemas');

const result = Schemas.options.validate(options);

Expand Down Expand Up @@ -162,16 +166,16 @@ module.exports = internals.Any = class {
obj._settings = obj._settings ? Settings.concat(obj._settings, schema._settings) : schema._settings;
obj._valids.merge(schema._valids, schema._invalids);
obj._invalids.merge(schema._invalids, schema._valids);
obj._tests = obj._tests.concat(schema._tests);
obj._refs = obj._refs.concat(schema._refs);
obj._tests.push(...schema._tests);
obj._refs.push(...schema._refs);
Hoek.merge(obj._flags, schema._flags);

obj._description = schema._description || obj._description;
obj._unit = schema._unit || obj._unit;
obj._notes = obj._notes.concat(schema._notes);
obj._tags = obj._tags.concat(schema._tags);
obj._examples = obj._examples.concat(schema._examples);
obj._meta = obj._meta.concat(schema._meta);
obj._notes.push(...schema._notes);
obj._tags.push(...schema._tags);
obj._examples.push(...schema._examples);
obj._meta.push(...schema._meta);

const inners = Object.keys(schema._inner);
const isObject = obj._type === 'object';
Expand Down Expand Up @@ -484,7 +488,8 @@ module.exports = internals.Any = class {
options = {};
}

const result = this._validate(value, { key: '', path: [], parent: options.parent || null }, Settings.concat(internals.defaults, options.context ? { context: options.context } : null));
const localState = new State('', [], options.parent || null);
const result = this._validate(value, localState, Settings.concat(internals.defaults, options.context ? { context: options.context } : null));
Hoek.assert(!result.errors, `Bad example at index ${i}:`, result.errors && Errors.process(result.errors, value));

const ex = { value };
Expand Down Expand Up @@ -524,77 +529,29 @@ module.exports = internals.Any = class {

// Setup state and settings

state = state || { key: '', path: [], parent: null, reference };
state = state || new State('', [], null, reference);

if (this._settings) {
options = Settings.concat(options, this._settings);
}

let errors = [];
const finish = () => {

let finalValue;

if (value !== undefined) {
finalValue = this._flags.raw ? originalValue : value;
}
else if (options.noDefaults) {
finalValue = value;
}
else if (Ref.isRef(this._flags.default)) {
finalValue = this._flags.default(state.parent, options);
}
else if (typeof this._flags.default === 'function' &&
!(this._flags.func && !this._flags.default.description)) {

let args;

if (state.parent !== null &&
this._flags.default.length > 0) {

args = [Hoek.clone(state.parent), options];
}

const defaultValue = internals._try(this._flags.default, args);
finalValue = defaultValue.value;
if (defaultValue.error) {
errors.push(this.createError('any.default', { error: defaultValue.error }, state, options));
}
const isDefaultOptions = options === internals.defaults;
if (isDefaultOptions && this._settings[Symbols.settingsCache]) {
options = this._settings[Symbols.settingsCache];
}
else {
finalValue = Hoek.clone(this._flags.default);
}

if (errors.length && typeof this._flags.error === 'function') {
const change = this._flags.error.call(this, errors);

if (typeof change === 'string') {
errors = [this.createOverrideError('override', { reason: errors }, state, options, change)];
}
else {
errors = [].concat(change)
.map((err) => {

return err instanceof Error ?
err :
this.createOverrideError(err.type || 'override', err.context, state, options, err.message, err.template);
});
options = Settings.concat(options, this._settings);
if (isDefaultOptions) {
this._settings[Symbols.settingsCache] = options;
}
}
}

return {
value: this._flags.strip ? undefined : finalValue,
finalValue,
errors: errors.length ? errors : null
};
};
let errors = [];

if (this._coerce) {
const coerced = this._coerce(value, state, options);
if (coerced.errors) {
value = coerced.value;
errors = errors.concat(coerced.errors);
return finish(); // Coerced error always aborts early
return this._finalizeValue(value, originalValue, errors, state, options); // Coerced error always aborts early
}

value = coerced.value;
Expand All @@ -614,23 +571,23 @@ module.exports = internals.Any = class {
value = {};
}
else {
return finish();
return this._finalizeValue(value, originalValue, errors, state, options);
}
}
}
else if (presence === 'required' &&
value === undefined) {

errors.push(this.createError('any.required', null, state, options));
return finish();
return this._finalizeValue(value, originalValue, errors, state, options);
}
else if (presence === 'forbidden') {
if (value === undefined) {
return finish();
return this._finalizeValue(value, originalValue, errors, state, options);
}

errors.push(this.createError('any.unknown', null, state, options));
return finish();
return this._finalizeValue(value, originalValue, errors, state, options);
}

// Check allowed and denied values using the original value
Expand All @@ -641,15 +598,15 @@ module.exports = internals.Any = class {
value = match.value;
}

return finish();
return this._finalizeValue(value, originalValue, errors, state, options);
}

if (this._invalids.has(value, state, options, this._flags.insensitive)) {
errors.push(this.createError(value === '' ? 'any.empty' : 'any.invalid', { value, invalids: this._invalids.values({ stripUndefined: true }) }, state, options));
if (options.abortEarly ||
value === undefined) { // No reason to keep validating missing value

return finish();
return this._finalizeValue(value, originalValue, errors, state, options);
}
}

Expand All @@ -660,7 +617,7 @@ module.exports = internals.Any = class {
if (base.errors) {
value = base.value;
errors = errors.concat(base.errors);
return finish(); // Base error always aborts early
return this._finalizeValue(value, originalValue, errors, state, options); // Base error always aborts early
}

if (base.value !== value) {
Expand All @@ -671,13 +628,13 @@ module.exports = internals.Any = class {
match = this._valids.get(value, state, options, this._flags.insensitive);
if (match) {
value = match.value;
return finish();
return this._finalizeValue(value, originalValue, errors, state, options);
}

if (this._invalids.has(value, state, options, this._flags.insensitive)) {
errors.push(this.createError(value === '' ? 'any.empty' : 'any.invalid', { value, invalids: this._invalids.values({ stripUndefined: true }) }, state, options));
if (options.abortEarly) {
return finish();
return this._finalizeValue(value, originalValue, errors, state, options);
}
}
}
Expand All @@ -688,7 +645,7 @@ module.exports = internals.Any = class {
if (this._flags.allowOnly) {
errors.push(this.createError('any.allowOnly', { value, valids: this._valids.values({ stripUndefined: true }) }, state, options));
if (options.abortEarly) {
return finish();
return this._finalizeValue(value, originalValue, errors, state, options);
}
}

Expand All @@ -700,15 +657,73 @@ module.exports = internals.Any = class {
if (ret instanceof Errors.Err) {
errors.push(ret);
if (options.abortEarly) {
return finish();
return this._finalizeValue(value, originalValue, errors, state, options);
}
}
else {
value = ret;
}
}

return finish();
return this._finalizeValue(value, originalValue, errors, state, options);
}

_finalizeValue(value, originalValue, errors, state, options) {

let finalValue;

if (value !== undefined) {
finalValue = this._flags.raw ? originalValue : value;
}
else if (options.noDefaults) {
finalValue = value;
}
else if (Ref.isRef(this._flags.default)) {
finalValue = this._flags.default(state.parent, options);
}
else if (typeof this._flags.default === 'function' &&
!(this._flags.func && !this._flags.default.description)) {

let args;

if (state.parent !== null &&
this._flags.default.length > 0) {

args = [Hoek.clone(state.parent), options];
}

const defaultValue = internals._try(this._flags.default, args);
finalValue = defaultValue.value;
if (defaultValue.error) {
errors.push(this.createError('any.default', { error: defaultValue.error }, state, options));
}
}
else {
finalValue = Hoek.clone(this._flags.default);
}

if (errors.length && typeof this._flags.error === 'function') {
const change = this._flags.error.call(this, errors);

if (typeof change === 'string') {
errors = [this.createOverrideError('override', { reason: errors }, state, options, change)];
}
else {
errors = [].concat(change)
.map((err) => {

return err instanceof Error ?
err :
this.createOverrideError(err.type || 'override', err.context, state, options, err.message, err.template);
});
}
}

return {
value: this._flags.strip ? undefined : finalValue,
finalValue,
errors: errors.length ? errors : null
};
}

_validateWithOptions(value, options, callback) {
Expand Down
23 changes: 12 additions & 11 deletions lib/types/any/settings.js
Expand Up @@ -4,6 +4,8 @@

const Hoek = require('hoek');

const Symbols = require('../symbols');


// Declare internals

Expand All @@ -18,17 +20,16 @@ exports.concat = function (target, source) {

const obj = Object.assign({}, target);

const sKeys = Object.keys(source);
for (let i = 0; i < sKeys.length; ++i) {
const key = sKeys[i];
if (key !== 'language' ||
!obj.hasOwnProperty(key)) {

obj[key] = source[key];
}
else {
obj[key] = Hoek.applyToDefaults(obj[key], source[key]);
}
const language = source.language;

Object.assign(obj, source);

if (language) {
obj.language = Hoek.applyToDefaults(obj.language, language);
}

if (obj[Symbols.settingsCache]) {
delete obj[Symbols.settingsCache];
}

return obj;
Expand Down

0 comments on commit a1a79b3

Please sign in to comment.