Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support webpackLoaders and webpackPlugins #3938

Merged
merged 9 commits into from
Dec 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions docs/guide/basic/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,78 @@ dll // dll 构建产物文件夹
└── dll-pkg.json // build.json 中所配置的 dllEntry 信息
````

### webpackPlugins

- 类型:`object`
- 默认值:无

通过 `webpackPlugins` 可以方便地新增或者修改工程上的 webpack 插件配置。

配置方式:

```json
{
"webpackPlugins": {
"webpack.ProvidePlugin": {
"options": {
"identifier": "module1"
}
},
"HtmlWebpackPlugin": {
"before": "webpack.ProvidePlugin"
}
}
}
```

配置规则如下:
- 对于 webpack 内置的 plugins,可以通过 webpack.PluginName 的形式作为 key 值进行配置
- 对于其他 webpack 插件,需要将插件的 npm 包名作为 key 值进行配置,package.json 中需要添加并安装该插件依赖
- 每一项插件配置支持 before/after 用来调整 webpack 插件执行顺序
- 如果配置设置的插件已被添加,则修改插件配置

### webpackLoaders

- 类型:`object`
- 默认值:无

通过 `webpackLoaders` 可以方便地新增或者修改工程上的 webpack loader 配置。

配置方式:

```json
{
"webpackLoaders": {
"css": {
"test": ".css$",
"loaders": {
"style-loader": {
"options": {
"loaderoption": true
},
"before": "less-loader"
}
}
}
}
}
```

配置规则如下:
- webpackLoaders 配置下每一项为具体的 webpack loader 规则,支持参数
- test:配置类型 `string|string[]`,同 [Rule.test](https://webpack.js.org/configuration/module/#ruletest)
- oneOf:配置类型 `[oneOfName: string]: { resourceQuery: string; loaders: Loaders }`,同[Rule.oneOf](https://webpack.js.org/configuration/module/#ruleoneof)
- includeClear:清除默认 include 配置
- include:配置类型 `string|string[]`,同 [Rule.include](https://webpack.js.org/configuration/module/#ruleinclude)
- excludeClear:清除默认 exclude 配置
- exclude:配置类型 `string|string[]`,同 [Rule.exclude](https://webpack.js.org/configuration/module/#ruleexclude)
- pre:配置类型 `boolean`,配置 rule 的 enforce 值为 pre
- post:配置类型 `boolean`,配置 rule 的 enforce 值为 post
- before:配置类型 `string`,用于配置定义顺序,前置指定
- after:配置类型 `string`,用于配置定义顺序,后置指定
- loaders:配置具体的 webpack loader
- loaders 参数用来指定具体 webpack loader 的参数;每一项 loader 参数支持 before/after 用来调整 webpack loader 的执行顺序;如果 loader 名已被添加,则修改插件配置

## 根据环境区分工程配置

参考 [区分不同环境](/docs/guide/basic/config.md)。
Expand Down
10 changes: 9 additions & 1 deletion packages/build-user-config/src/config/user.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,16 @@ module.exports = [
name: 'targets',
validation: 'array',
},
{
name: 'webpackLoaders',
validation: 'object',
},
{
name: 'webpackPlugins',
validation: 'object',
},
{
name: 'postcssOptions',
validation: 'object',
}
},
];
105 changes: 105 additions & 0 deletions packages/build-user-config/src/userConfig/webpackLoaders.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
const genRegExpRule = (value) => new RegExp(Array.isArray(value) ? value.join('|') : value);
const ensureArray = (value) => Array.isArray(value) ? value : [value];

const optionAPIs = {
test: (rule, value) => {
rule.test(genRegExpRule(value));
},
oneOf: (rule, value) => {
/**
* config.module
* .rule('css')
* .oneOf('inline')
* .resourceQuery(/inline/)
* .use('url')
* .loader('url-loader')
* .end()
* .end()
* .oneOf('external')
* .resourceQuery(/external/)
* .use('file')
* .loader('file-loader')
*/
Object.keys(value).forEach((oneOfName) => {
const { resourceQuery, loaders } = value[oneOfName];
const loaderRule = rule.oneOf(oneOfName);
if (resourceQuery) {
loaderRule.resourceQuery(genRegExpRule(resourceQuery));
}
configRuleLoaders(loaderRule, loaders || {});
});
},
// clear include rules
includeClear: (rule) => {
rule.include.clear();
},
include: (rule, value) => {
ensureArray(value).forEach((includeValue) => {
rule.include.add(includeValue);
});
},
// clear exclude rules
excludeClear: (rule) => {
rule.exclude.clear();
},
exclude: (rule, value) => {
ensureArray(value).forEach((excludeValue) => {
rule.exclude.add(excludeValue);
});
},
pre: (rule) => {
rule.pre();
},
post: (rule) => {
rule.post();
},
enforce: (rule) => {
rule.enforce();
},
before: (rule, value) => {
rule.before(value);
},
after: (rule, value) => {
rule.after(value);
},
};
const validRuleOption = Object.keys(optionAPIs);

function configModuleRule(rule, options) {
// loop validRuleOption to make sure optionAPIs excute in order
validRuleOption.forEach((optionsKey) => {
const optionValue = options[optionsKey];
if (optionValue) {
optionAPIs[optionsKey](rule, optionValue);
}
});
}

function configRuleLoaders(rule, loaders) {
const loaderNames = Object.keys(loaders);
loaderNames.forEach((loaderName) => {
const { options, before, after } = loaders[loaderName];
// check if loader is exsits
let loaderRule = null;
if (rule.uses.has(loaderName)) {
loaderRule = rule.use(loaderName).tap(opts => ({ ...opts, ...options}));
} else {
loaderRule = rule.use(loaderName).loader(loaderName).options(options);
}
if (before) loaderRule.before(before);
if (after) loaderRule.after(after);
});
}

module.exports = (config, webpackLoaders) => {
if (webpackLoaders) {
const ruleNames = Object.keys(webpackLoaders);
ruleNames.forEach((ruleName) => {
// create new rule if module rule is not exists
const rule = config.module.rule(ruleName);
const ruleOptions = webpackLoaders[ruleName];
configModuleRule(rule, ruleOptions);
configRuleLoaders(rule, ruleOptions.loaders || {});
});
}
};
28 changes: 28 additions & 0 deletions packages/build-user-config/src/userConfig/webpackPlugins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module.exports = (config, webpackPlugins, context) => {
if (webpackPlugins) {
const pluginNames = Object.keys(webpackPlugins);
pluginNames.forEach((pluginName) => {
const { options, after, before } = webpackPlugins[pluginName];
let pluginRule = null;
// check if plugin has been already registed
if (config.plugins.has(pluginName)) {
// modify plugin options
pluginRule = config.plugin(pluginName).tap(([args]) => [{...args, ...options}]);
} else {
// add new plugin
let plugin = null;
if (pluginName.match(/^webpack\./)) {
// webpack builtin plugins
const { webpack } = context;
plugin = webpack[pluginName];
} else {
// eslint-disable-next-line
plugin = require(pluginName);
}
pluginRule = config.plugin(pluginName).use(plugin, [options]);
}
if (before) pluginRule.before(before);
if (after) pluginRule.after(after);
});
}
};