normalize.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const defaults_1 = require("./defaults");
  4. const utils_1 = require("./utils");
  5. exports.normalize = (options, schemas, opts) => new Normalizer(schemas, opts).normalize(options);
  6. class Normalizer {
  7. constructor(schemas, opts) {
  8. // istanbul ignore next
  9. const { logger = console, descriptor = defaults_1.defaultDescriptor, unknown = defaults_1.defaultUnknownHandler, invalid = defaults_1.defaultInvalidHandler, deprecated = defaults_1.defaultDeprecatedHandler, } = opts || {};
  10. this._utils = {
  11. descriptor,
  12. logger: /* istanbul ignore next */ logger || { warn: () => { } },
  13. schemas: utils_1.recordFromArray(schemas, 'name'),
  14. normalizeDefaultResult: utils_1.normalizeDefaultResult,
  15. normalizeDeprecatedResult: utils_1.normalizeDeprecatedResult,
  16. normalizeForwardResult: utils_1.normalizeForwardResult,
  17. normalizeRedirectResult: utils_1.normalizeRedirectResult,
  18. normalizeValidateResult: utils_1.normalizeValidateResult,
  19. };
  20. this._unknownHandler = unknown;
  21. this._invalidHandler = invalid;
  22. this._deprecatedHandler = deprecated;
  23. this.cleanHistory();
  24. }
  25. cleanHistory() {
  26. this._hasDeprecationWarned = utils_1.createAutoChecklist();
  27. }
  28. normalize(options) {
  29. const normalized = {};
  30. const restOptionsArray = [options];
  31. const applyNormalization = () => {
  32. while (restOptionsArray.length !== 0) {
  33. const currentOptions = restOptionsArray.shift();
  34. const transferredOptionsArray = this._applyNormalization(currentOptions, normalized);
  35. restOptionsArray.push(...transferredOptionsArray);
  36. }
  37. };
  38. applyNormalization();
  39. for (const key of Object.keys(this._utils.schemas)) {
  40. const schema = this._utils.schemas[key];
  41. if (!(key in normalized)) {
  42. const defaultResult = utils_1.normalizeDefaultResult(schema.default(this._utils));
  43. if ('value' in defaultResult) {
  44. restOptionsArray.push({ [key]: defaultResult.value });
  45. }
  46. }
  47. }
  48. applyNormalization();
  49. for (const key of Object.keys(this._utils.schemas)) {
  50. const schema = this._utils.schemas[key];
  51. if (key in normalized) {
  52. normalized[key] = schema.postprocess(normalized[key], this._utils);
  53. }
  54. }
  55. return normalized;
  56. }
  57. _applyNormalization(options, normalized) {
  58. const transferredOptionsArray = [];
  59. const [knownOptionNames, unknownOptionNames] = utils_1.partition(Object.keys(options), key => key in this._utils.schemas);
  60. for (const key of knownOptionNames) {
  61. const schema = this._utils.schemas[key];
  62. const value = schema.preprocess(options[key], this._utils);
  63. const validateResult = utils_1.normalizeValidateResult(schema.validate(value, this._utils), value);
  64. if (validateResult !== true) {
  65. const { value: invalidValue } = validateResult;
  66. const errorMessageOrError = this._invalidHandler(key, invalidValue, this._utils);
  67. throw typeof errorMessageOrError === 'string'
  68. ? new Error(errorMessageOrError)
  69. : /* istanbul ignore next*/ errorMessageOrError;
  70. }
  71. const appendTransferredOptions = ({ from, to }) => {
  72. transferredOptionsArray.push(typeof to === 'string' ? { [to]: from } : { [to.key]: to.value });
  73. };
  74. const warnDeprecated = ({ value: currentValue, redirectTo, }) => {
  75. const deprecatedResult = utils_1.normalizeDeprecatedResult(schema.deprecated(currentValue, this._utils), value,
  76. /* doNotNormalizeTrue */ true);
  77. if (deprecatedResult === false) {
  78. return;
  79. }
  80. if (deprecatedResult === true) {
  81. if (!this._hasDeprecationWarned(key)) {
  82. this._utils.logger.warn(this._deprecatedHandler(key, redirectTo, this._utils));
  83. }
  84. }
  85. else {
  86. for (const { value: deprecatedValue } of deprecatedResult) {
  87. const pair = { key, value: deprecatedValue };
  88. if (!this._hasDeprecationWarned(pair)) {
  89. const redirectToPair = typeof redirectTo === 'string'
  90. ? { key: redirectTo, value: deprecatedValue }
  91. : redirectTo;
  92. this._utils.logger.warn(this._deprecatedHandler(pair, redirectToPair, this._utils));
  93. }
  94. }
  95. }
  96. };
  97. const forwardResult = utils_1.normalizeForwardResult(schema.forward(value, this._utils), value);
  98. forwardResult.forEach(appendTransferredOptions);
  99. const redirectResult = utils_1.normalizeRedirectResult(schema.redirect(value, this._utils), value);
  100. redirectResult.redirect.forEach(appendTransferredOptions);
  101. if ('remain' in redirectResult) {
  102. const remainingValue = redirectResult.remain;
  103. normalized[key] =
  104. key in normalized
  105. ? schema.overlap(normalized[key], remainingValue, this._utils)
  106. : remainingValue;
  107. warnDeprecated({ value: remainingValue });
  108. }
  109. for (const { from, to } of redirectResult.redirect) {
  110. warnDeprecated({ value: from, redirectTo: to });
  111. }
  112. }
  113. for (const key of unknownOptionNames) {
  114. const value = options[key];
  115. const unknownResult = this._unknownHandler(key, value, this._utils);
  116. if (unknownResult) {
  117. for (const unknownKey of Object.keys(unknownResult)) {
  118. const unknownOption = { [unknownKey]: unknownResult[unknownKey] };
  119. if (unknownKey in this._utils.schemas) {
  120. transferredOptionsArray.push(unknownOption);
  121. }
  122. else {
  123. Object.assign(normalized, unknownOption);
  124. }
  125. }
  126. }
  127. }
  128. return transferredOptionsArray;
  129. }
  130. }
  131. exports.Normalizer = Normalizer;