Skip to content

Commit e37d181

Browse files
committed
feat: support module.rules config
1 parent a3649ba commit e37d181

4 files changed

Lines changed: 199 additions & 28 deletions

File tree

lib/core/config.js

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,7 @@ class Config {
7474
if (this._typescript) {
7575
return this._typescript;
7676
}
77-
const typescript = this.getLoaderByName('ts');
78-
if (this.utils.isObject(typescript) && this.utils.isTrue(typescript.enable)) {
79-
this._typescript = typescript;
80-
}
77+
this._typescript = this.isUseLoader('ts');
8178
return this._typescript;
8279
}
8380

@@ -340,12 +337,31 @@ class Config {
340337
return this.utils.isTrue(enable);
341338
}
342339

340+
isUseLoader(name) {
341+
const configInfo = this.getMergeLoaderByName(name)[name];
342+
return this.isUseConfig(configInfo);
343+
}
344+
345+
isUsePlugin(name) {
346+
const configInfo = this.getPluginByName(name);
347+
return this.isUseConfig(configInfo);
348+
}
349+
350+
isUseConfig(configInfo) {
351+
return configInfo && this.isType(configInfo.type) && this.isEnv(configInfo.env) && this.isEnable(configInfo.enable);
352+
}
353+
343354
isUse(name, range = 'plugin') {
344355
if (this.utils.isBoolean(name)) {
345356
return name;
346357
}
347-
const configInfo = this.utils.isObject(name) ? name : (range === 'plugin' ? this.getPluginByName(name) : this.getLoaderByName(name));
348-
return configInfo && this.isType(configInfo.type) && this.isEnv(configInfo.env) && this.isEnable(configInfo.enable);
358+
if (this.utils.isObject(name)) {
359+
return this.isUseConfig(name);
360+
}
361+
if (range === 'plugin') {
362+
return this.isUsePlugin(name);
363+
}
364+
return this.isUseLoader(name);
349365
}
350366

351367
isWebpackLoader(loader) {

lib/core/loader.js

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ const { STYLE_LOADER } = require('../core/constant');
66

77
module.exports = {
88

9+
getLoaderLabel(loader) {
10+
return this.utils.getLoaderLabel(loader, this);
11+
},
12+
913
getConfigLoader(config) {
1014
const { loaders = {} } = config;
1115
const { rules = [] } = config.module || {};
@@ -26,18 +30,29 @@ module.exports = {
2630
}
2731

2832
return rules.find(loader => {
29-
const label = this.utils.getLoaderLabel(loader);
33+
const label = this.getLoaderLabel(loader);
3034
if (name === label || mappingName === label) {
3135
return true;
3236
}
3337
return false;
3438
});
3539
},
3640

37-
getLoaderByName(name) {
41+
getMergeLoaderByName(name) {
3842
const defaultLoader = this.utils.cloneDeep(this.loaders[name]);
3943
const configLoader = this.getConfigLoaderByName(name);
40-
return this.mergeLoader({ [name]: configLoader }, { [name]: defaultLoader })[name];
44+
return this.mergeLoader({ [name]: configLoader }, { [name]: defaultLoader });
45+
},
46+
47+
getLoaderByName(name) {
48+
const loader = this.getMergeLoaderByName(name);
49+
const options = this.merge(this.config.loaderOptions, this.loaders.options);
50+
const loaders = this.mergeLoaderOption(loader, options);
51+
const webpackLoaders = this.createLoader(loaders);
52+
if (webpackLoaders.length) {
53+
return webpackLoaders[0];
54+
}
55+
return {};
4156
},
4257

4358
initLoader() {
@@ -143,6 +158,17 @@ module.exports = {
143158
return null;
144159
},
145160

161+
getTsLoader() {
162+
const itemLoader = this.getLoaderByName('ts');
163+
if (itemLoader && itemLoader.use) {
164+
const tsLoader = itemLoader.use.find(loader => {
165+
return this.utils.isObject(loader) && loader.loader === 'ts-loader';
166+
});
167+
return tsLoader;
168+
}
169+
return null;
170+
},
171+
146172
addLoader(loader) {
147173
if (loader.test && (loader.use || loader.loader)) {
148174
const loaderInfo = {};
@@ -167,7 +193,7 @@ module.exports = {
167193
const sourceLoaders = Array.isArray(cloneLoaders) ? {} : cloneLoaders;
168194
if (Array.isArray(cloneLoaders)) {
169195
cloneLoaders.forEach(loader => {
170-
const label = this.utils.getLoaderLabel(loader);
196+
const label = this.getLoaderLabel(loader);
171197
if (this.isWebpackLoader(loader)) {
172198
sourceLoaders[label] = this.merge(loader, { enable: true });
173199
} else {
@@ -213,7 +239,7 @@ module.exports = {
213239
}
214240
if (Array.isArray(itemLoader.use)) {
215241
itemLoader.use.forEach((loader, index) => {
216-
const label = this.utils.getLoaderLabel(loader);
242+
const label = this.getLoaderLabel(loader);
217243
const mLabel = this.loaderKeyLabelMapping[name];
218244
const configOptions = itemLoader.options && (label === mLabel || label === name) ? itemLoader.options : {};
219245
const options = this.merge(loaderOptions[label], { options: configOptions });
@@ -275,7 +301,7 @@ module.exports = {
275301
});
276302
loaderNames.forEach(name => {
277303
const itemLoader = loaders[name];
278-
['type', 'enable', 'postcss', 'framework', 'loader', 'options'].forEach(propery => {
304+
['type', 'enable', 'postcss', 'framework', 'loader', 'options', 'name'].forEach(propery => {
279305
delete itemLoader[propery];
280306
});
281307
});
@@ -320,13 +346,8 @@ module.exports = {
320346
// https://github.com/TypeStrong/ts-loader/pull/782
321347
// vue-loader 14 版本中,需在 options 重复配置 ts-loader,升级 15 以后可移除
322348
if (this.typescript) {
323-
const use = this.utils.isFunction(this.typescript.use) ? this.typescript.use.apply(this) : this.typescript.use;
324-
const tsLoader = use.find(item => {
325-
return item.loader === 'ts-loader';
326-
});
327-
if (tsLoader) {
328-
loaders.ts = tsLoader;
329-
}
349+
const tsLoader = this.getTsLoader();
350+
loaders.ts = tsLoader;
330351
}
331352
return { preLoaders, loaders };
332353
},

test/vue.test.js

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,5 +75,136 @@ describe('vue.test.js', () => {
7575
const webpackConfig = builder.create();
7676
expect(webpackConfig.output.path).to.equal(path.join(__dirname, '../app/view'));
7777
});
78+
79+
it('should vue ts framework loaders test', () => {
80+
const builder = new WebpackServerBuilder({
81+
loaders: {
82+
ts: true,
83+
vue: {
84+
test: /\.vue$/,
85+
exclude: /node_modules/,
86+
use() {
87+
const options = this.createFrameworkLoader('vue-style-loader');
88+
options.transformToRequire = { img: ['url', 'src'] };
89+
return [
90+
{
91+
loader: 'vue-loader',
92+
options
93+
}
94+
];
95+
}
96+
}
97+
}
98+
});
99+
const webpackConfig = builder.create();
100+
const vueLoader = helper.getLoaderByName('vue', webpackConfig.module.rules);
101+
expect(vueLoader.use[0].options.loaders).to.include.keys(['ts', 'js', 'css']);
102+
});
103+
it('should vue typescript loaders framework test', () => {
104+
const builder = new WebpackServerBuilder({
105+
loaders: {
106+
typescript: true,
107+
vue: {
108+
test: /\.vue$/,
109+
exclude: /node_modules/,
110+
use() {
111+
const options = this.createFrameworkLoader('vue-style-loader');
112+
options.transformToRequire = { img: ['url', 'src'] };
113+
return [
114+
{
115+
loader: 'vue-loader',
116+
options
117+
}
118+
];
119+
}
120+
}
121+
}
122+
});
123+
const webpackConfig = builder.create();
124+
const vueLoader = helper.getLoaderByName('vue', webpackConfig.module.rules);
125+
expect(vueLoader.use[0].options.loaders).to.include.keys(['ts', 'js', 'css']);
126+
});
127+
it('should vue typescript module rules test', () => {
128+
const builder = new WebpackServerBuilder({
129+
module: {
130+
rules: [
131+
{ typescript: true },
132+
{
133+
vue: {
134+
test: /\.vue$/,
135+
exclude: /node_modules/,
136+
use() {
137+
const options = this.createFrameworkLoader('vue-style-loader');
138+
options.transformToRequire = { img: ['url', 'src'] };
139+
return [
140+
{
141+
loader: 'vue-loader',
142+
options
143+
}
144+
];
145+
}
146+
}
147+
}
148+
]
149+
}
150+
});
151+
const webpackConfig = builder.create();
152+
const vueLoader = helper.getLoaderByName('vue', webpackConfig.module.rules);
153+
expect(vueLoader.use[0].options.loaders).to.include.keys(['ts', 'js', 'css']);
154+
});
155+
it('should vue ts module rules test', () => {
156+
const builder = new WebpackServerBuilder({
157+
module: {
158+
rules: [
159+
{ ts: true },
160+
{
161+
vue: {
162+
test: /\.vue$/,
163+
exclude: /node_modules/,
164+
use() {
165+
const options = this.createFrameworkLoader('vue-style-loader');
166+
options.transformToRequire = { img: ['url', 'src'] };
167+
return [
168+
{
169+
loader: 'vue-loader',
170+
options
171+
}
172+
];
173+
}
174+
}
175+
}
176+
]
177+
}
178+
});
179+
const webpackConfig = builder.create();
180+
const vueLoader = helper.getLoaderByName('vue', webpackConfig.module.rules);
181+
expect(vueLoader.use[0].options.loaders).to.include.keys(['ts', 'js', 'css']);
182+
});
183+
it('should vue ts module rules native config test', () => {
184+
const builder = new WebpackServerBuilder({
185+
module: {
186+
rules: [
187+
{ ts: true },
188+
{
189+
test: /\.vue$/,
190+
exclude: /node_modules/,
191+
use() {
192+
const options = this.createFrameworkLoader('vue-style-loader');
193+
options.transformToRequire = { img: ['url', 'src'] };
194+
return [
195+
{
196+
loader: 'vue-loader',
197+
options
198+
}
199+
];
200+
}
201+
}
202+
]
203+
}
204+
});
205+
const webpackConfig = builder.create();
206+
const vueLoader = helper.getLoaderByName('vue', webpackConfig.module.rules);
207+
expect(vueLoader.use[0].options.loaders).to.include.keys(['ts', 'js', 'css']);
208+
});
78209
});
79210
});

utils/utils.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -271,20 +271,23 @@ utils.getLoaderOptionString = (name, options) => {
271271
return optionStr;
272272
};
273273

274-
utils.getLoaderLabel = loader => {
274+
utils.getLoaderLabel = (loader, ctx) => {
275275
let loaderName = loader;
276276
if (utils.isObject(loader)) {
277-
if (loader.loader) {
277+
if (loader.name) {
278+
loaderName = loader.name;
279+
} else if (loader.loader) {
278280
loaderName = loader.loader;
279-
} else if (Array.isArray(loader.use)) {
280-
loaderName = loader.use.reduce((name, item) => {
281+
} else if (Array.isArray(loader.use) || utils.isFunction(loader.use)) {
282+
const loaders = utils.isFunction(loader.use) ? loader.use.apply(ctx) : loader.use;
283+
loaderName = loaders.reduce((names, item) => {
281284
if (utils.isString(item)) {
282-
name += item;
283-
} else {
284-
name += item.loader;
285+
names.push(item.replace(/-loader$/, ''));
286+
} else if (item.loader) {
287+
names.push(item.loader.replace(/-loader$/, ''));
285288
}
286-
return name;
287-
}, '');
289+
return names;
290+
}, []).join('-');
288291
} else if (Object.keys(loader).length === 1) {
289292
loaderName = Object.keys(loader)[0];
290293
}

0 commit comments

Comments
 (0)