UseEffectRulePlugin.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const util = require("util");
  7. /** @typedef {import("./RuleSetCompiler")} RuleSetCompiler */
  8. /** @typedef {import("./RuleSetCompiler").Effect} Effect */
  9. class UseEffectRulePlugin {
  10. /**
  11. * @param {RuleSetCompiler} ruleSetCompiler the rule set compiler
  12. * @returns {void}
  13. */
  14. apply(ruleSetCompiler) {
  15. ruleSetCompiler.hooks.rule.tap(
  16. "UseEffectRulePlugin",
  17. (path, rule, unhandledProperties, result, references) => {
  18. const conflictWith = (property, correctProperty) => {
  19. if (unhandledProperties.has(property)) {
  20. throw ruleSetCompiler.error(
  21. `${path}.${property}`,
  22. rule[property],
  23. `A Rule must not have a '${property}' property when it has a '${correctProperty}' property`
  24. );
  25. }
  26. };
  27. if (unhandledProperties.has("use")) {
  28. unhandledProperties.delete("use");
  29. unhandledProperties.delete("enforce");
  30. conflictWith("loader", "use");
  31. conflictWith("options", "use");
  32. const use = rule.use;
  33. const enforce = rule.enforce;
  34. const type = enforce ? `use-${enforce}` : "use";
  35. /**
  36. *
  37. * @param {string} path options path
  38. * @param {string} defaultIdent default ident when none is provided
  39. * @param {object} item user provided use value
  40. * @returns {Effect|function(any): Effect[]} effect
  41. */
  42. const useToEffect = (path, defaultIdent, item) => {
  43. if (typeof item === "function") {
  44. return data => useToEffectsWithoutIdent(path, item(data));
  45. } else {
  46. return useToEffectRaw(path, defaultIdent, item);
  47. }
  48. };
  49. /**
  50. *
  51. * @param {string} path options path
  52. * @param {string} defaultIdent default ident when none is provided
  53. * @param {object} item user provided use value
  54. * @returns {Effect} effect
  55. */
  56. const useToEffectRaw = (path, defaultIdent, item) => {
  57. if (typeof item === "string") {
  58. return {
  59. type,
  60. value: {
  61. loader: item,
  62. options: undefined,
  63. ident: undefined
  64. }
  65. };
  66. } else {
  67. const loader = item.loader;
  68. const options = item.options;
  69. let ident = item.ident;
  70. if (options && typeof options === "object") {
  71. if (!ident) ident = defaultIdent;
  72. references.set(ident, options);
  73. }
  74. if (typeof options === "string") {
  75. util.deprecate(
  76. () => {},
  77. `Using a string as loader options is deprecated (${path}.options)`,
  78. "DEP_WEBPACK_RULE_LOADER_OPTIONS_STRING"
  79. )();
  80. }
  81. return {
  82. type: enforce ? `use-${enforce}` : "use",
  83. value: {
  84. loader,
  85. options,
  86. ident
  87. }
  88. };
  89. }
  90. };
  91. /**
  92. * @param {string} path options path
  93. * @param {any} items user provided use value
  94. * @returns {Effect[]} effects
  95. */
  96. const useToEffectsWithoutIdent = (path, items) => {
  97. if (Array.isArray(items)) {
  98. return items.map((item, idx) =>
  99. useToEffectRaw(`${path}[${idx}]`, "[[missing ident]]", item)
  100. );
  101. }
  102. return [useToEffectRaw(path, "[[missing ident]]", items)];
  103. };
  104. /**
  105. * @param {string} path current path
  106. * @param {any} items user provided use value
  107. * @returns {(Effect|function(any): Effect[])[]} effects
  108. */
  109. const useToEffects = (path, items) => {
  110. if (Array.isArray(items)) {
  111. return items.map((item, idx) => {
  112. const subPath = `${path}[${idx}]`;
  113. return useToEffect(subPath, subPath, item);
  114. });
  115. }
  116. return [useToEffect(path, path, items)];
  117. };
  118. if (typeof use === "function") {
  119. result.effects.push(data =>
  120. useToEffectsWithoutIdent(`${path}.use`, use(data))
  121. );
  122. } else {
  123. for (const effect of useToEffects(`${path}.use`, use)) {
  124. result.effects.push(effect);
  125. }
  126. }
  127. }
  128. if (unhandledProperties.has("loader")) {
  129. unhandledProperties.delete("loader");
  130. unhandledProperties.delete("options");
  131. unhandledProperties.delete("enforce");
  132. const loader = rule.loader;
  133. const options = rule.options;
  134. const enforce = rule.enforce;
  135. if (loader.includes("!")) {
  136. throw ruleSetCompiler.error(
  137. `${path}.loader`,
  138. loader,
  139. "Exclamation mark separated loader lists has been removed in favor of the 'use' property with arrays"
  140. );
  141. }
  142. if (loader.includes("?")) {
  143. throw ruleSetCompiler.error(
  144. `${path}.loader`,
  145. loader,
  146. "Query arguments on 'loader' has been removed in favor of the 'options' property"
  147. );
  148. }
  149. if (typeof options === "string") {
  150. util.deprecate(
  151. () => {},
  152. `Using a string as loader options is deprecated (${path}.options)`,
  153. "DEP_WEBPACK_RULE_LOADER_OPTIONS_STRING"
  154. )();
  155. }
  156. const ident =
  157. options && typeof options === "object" ? path : undefined;
  158. references.set(ident, options);
  159. result.effects.push({
  160. type: enforce ? `use-${enforce}` : "use",
  161. value: {
  162. loader,
  163. options,
  164. ident
  165. }
  166. });
  167. }
  168. }
  169. );
  170. }
  171. useItemToEffects(path, item) {}
  172. }
  173. module.exports = UseEffectRulePlugin;