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