"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const defaults_1 = require("./defaults"); const utils_1 = require("./utils"); exports.normalize = (options, schemas, opts) => new Normalizer(schemas, opts).normalize(options); class Normalizer { constructor(schemas, opts) { // istanbul ignore next const { logger = console, descriptor = defaults_1.defaultDescriptor, unknown = defaults_1.defaultUnknownHandler, invalid = defaults_1.defaultInvalidHandler, deprecated = defaults_1.defaultDeprecatedHandler, } = opts || {}; this._utils = { descriptor, logger: /* istanbul ignore next */ logger || { warn: () => { } }, schemas: utils_1.recordFromArray(schemas, 'name'), normalizeDefaultResult: utils_1.normalizeDefaultResult, normalizeDeprecatedResult: utils_1.normalizeDeprecatedResult, normalizeForwardResult: utils_1.normalizeForwardResult, normalizeRedirectResult: utils_1.normalizeRedirectResult, normalizeValidateResult: utils_1.normalizeValidateResult, }; this._unknownHandler = unknown; this._invalidHandler = invalid; this._deprecatedHandler = deprecated; this.cleanHistory(); } cleanHistory() { this._hasDeprecationWarned = utils_1.createAutoChecklist(); } normalize(options) { const normalized = {}; const restOptionsArray = [options]; const applyNormalization = () => { while (restOptionsArray.length !== 0) { const currentOptions = restOptionsArray.shift(); const transferredOptionsArray = this._applyNormalization(currentOptions, normalized); restOptionsArray.push(...transferredOptionsArray); } }; applyNormalization(); for (const key of Object.keys(this._utils.schemas)) { const schema = this._utils.schemas[key]; if (!(key in normalized)) { const defaultResult = utils_1.normalizeDefaultResult(schema.default(this._utils)); if ('value' in defaultResult) { restOptionsArray.push({ [key]: defaultResult.value }); } } } applyNormalization(); for (const key of Object.keys(this._utils.schemas)) { const schema = this._utils.schemas[key]; if (key in normalized) { normalized[key] = schema.postprocess(normalized[key], this._utils); } } return normalized; } _applyNormalization(options, normalized) { const transferredOptionsArray = []; const [knownOptionNames, unknownOptionNames] = utils_1.partition(Object.keys(options), key => key in this._utils.schemas); for (const key of knownOptionNames) { const schema = this._utils.schemas[key]; const value = schema.preprocess(options[key], this._utils); const validateResult = utils_1.normalizeValidateResult(schema.validate(value, this._utils), value); if (validateResult !== true) { const { value: invalidValue } = validateResult; const errorMessageOrError = this._invalidHandler(key, invalidValue, this._utils); throw typeof errorMessageOrError === 'string' ? new Error(errorMessageOrError) : /* istanbul ignore next*/ errorMessageOrError; } const appendTransferredOptions = ({ from, to }) => { transferredOptionsArray.push(typeof to === 'string' ? { [to]: from } : { [to.key]: to.value }); }; const warnDeprecated = ({ value: currentValue, redirectTo, }) => { const deprecatedResult = utils_1.normalizeDeprecatedResult(schema.deprecated(currentValue, this._utils), value, /* doNotNormalizeTrue */ true); if (deprecatedResult === false) { return; } if (deprecatedResult === true) { if (!this._hasDeprecationWarned(key)) { this._utils.logger.warn(this._deprecatedHandler(key, redirectTo, this._utils)); } } else { for (const { value: deprecatedValue } of deprecatedResult) { const pair = { key, value: deprecatedValue }; if (!this._hasDeprecationWarned(pair)) { const redirectToPair = typeof redirectTo === 'string' ? { key: redirectTo, value: deprecatedValue } : redirectTo; this._utils.logger.warn(this._deprecatedHandler(pair, redirectToPair, this._utils)); } } } }; const forwardResult = utils_1.normalizeForwardResult(schema.forward(value, this._utils), value); forwardResult.forEach(appendTransferredOptions); const redirectResult = utils_1.normalizeRedirectResult(schema.redirect(value, this._utils), value); redirectResult.redirect.forEach(appendTransferredOptions); if ('remain' in redirectResult) { const remainingValue = redirectResult.remain; normalized[key] = key in normalized ? schema.overlap(normalized[key], remainingValue, this._utils) : remainingValue; warnDeprecated({ value: remainingValue }); } for (const { from, to } of redirectResult.redirect) { warnDeprecated({ value: from, redirectTo: to }); } } for (const key of unknownOptionNames) { const value = options[key]; const unknownResult = this._unknownHandler(key, value, this._utils); if (unknownResult) { for (const unknownKey of Object.keys(unknownResult)) { const unknownOption = { [unknownKey]: unknownResult[unknownKey] }; if (unknownKey in this._utils.schemas) { transferredOptionsArray.push(unknownOption); } else { Object.assign(normalized, unknownOption); } } } } return transferredOptionsArray; } } exports.Normalizer = Normalizer;