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