options.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. /**
  7. * Defines the item type used by this module.
  8. * @template T
  9. * @typedef {Record<string, string | string[] | T>} Item
  10. */
  11. /**
  12. * Defines the container options format type used by this module.
  13. * @template T
  14. * @typedef {(string | Item<T>)[] | Item<T>} ContainerOptionsFormat
  15. */
  16. /**
  17. * Processes the provided t.
  18. * @template T
  19. * @template N
  20. * @param {ContainerOptionsFormat<T>} options options passed by the user
  21. * @param {(item: string | string[], itemOrKey: string) => N} normalizeSimple normalize a simple item
  22. * @param {(value: T, key: string) => N} normalizeOptions normalize a complex item
  23. * @param {(item: string, normalized: N) => void} fn processing function
  24. * @returns {void}
  25. */
  26. const process = (options, normalizeSimple, normalizeOptions, fn) => {
  27. /**
  28. * Processes the provided item.
  29. * @param {(string | Item<T>)[]} items items
  30. */
  31. const array = (items) => {
  32. for (const item of items) {
  33. if (typeof item === "string") {
  34. fn(item, normalizeSimple(item, item));
  35. } else if (item && typeof item === "object") {
  36. object(item);
  37. } else {
  38. throw new Error("Unexpected options format");
  39. }
  40. }
  41. };
  42. /**
  43. * Processes the provided obj.
  44. * @param {Item<T>} obj an object
  45. */
  46. const object = (obj) => {
  47. for (const [key, value] of Object.entries(obj)) {
  48. if (typeof value === "string" || Array.isArray(value)) {
  49. fn(key, normalizeSimple(value, key));
  50. } else {
  51. fn(key, normalizeOptions(value, key));
  52. }
  53. }
  54. };
  55. if (!options) {
  56. // Do nothing
  57. } else if (Array.isArray(options)) {
  58. array(options);
  59. } else if (typeof options === "object") {
  60. object(options);
  61. } else {
  62. throw new Error("Unexpected options format");
  63. }
  64. };
  65. /**
  66. * Returns parsed options.
  67. * @template T
  68. * @template R
  69. * @param {ContainerOptionsFormat<T>} options options passed by the user
  70. * @param {(item: string | string[], itemOrKey: string) => R} normalizeSimple normalize a simple item
  71. * @param {(value: T, key: string) => R} normalizeOptions normalize a complex item
  72. * @returns {[string, R][]} parsed options
  73. */
  74. const parseOptions = (options, normalizeSimple, normalizeOptions) => {
  75. /** @type {[string, R][]} */
  76. const items = [];
  77. process(options, normalizeSimple, normalizeOptions, (key, value) => {
  78. items.push([key, value]);
  79. });
  80. return items;
  81. };
  82. /**
  83. * Returns options to spread or pass.
  84. * @template T
  85. * @param {string} scope scope name
  86. * @param {ContainerOptionsFormat<T>} options options passed by the user
  87. * @returns {Record<string, string | string[] | T>} options to spread or pass
  88. */
  89. const scope = (scope, options) => {
  90. /** @type {Record<string, string | string[] | T>} */
  91. const obj = {};
  92. process(
  93. options,
  94. (item) => /** @type {string | string[] | T} */ (item),
  95. (item) => /** @type {string | string[] | T} */ (item),
  96. (key, value) => {
  97. obj[
  98. key.startsWith("./") ? `${scope}${key.slice(1)}` : `${scope}/${key}`
  99. ] = value;
  100. }
  101. );
  102. return obj;
  103. };
  104. module.exports.parseOptions = parseOptions;
  105. module.exports.scope = scope;