UNPKG

5.31 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const util = require("util");
9
10/** @typedef {import("./RuleSetCompiler")} RuleSetCompiler */
11/** @typedef {import("./RuleSetCompiler").Effect} Effect */
12
13class 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
194module.exports = UseEffectRulePlugin;