options.js 2.7 KB

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