UNPKG

flow-build

Version:
352 lines (302 loc) 12.1 kB
"use strict"; let path = require("path"); let _ = require("lodash"); let assert = require("assert"); let chalk = require("chalk"); let merge = require("webpack-merge"); let MiniCssExtractPlugin = require("mini-css-extract-plugin"); let Config = require("./Config"); /** * webpack 基础配置项 */ class BaseConfig extends Config { /** * 构造器 * @param {Object} config */ constructor(config) { super(config); } /** * 初始化webpack基础配置 * @param {Object} config */ initBase(config) { this.setImageName(config.image.dirname, config.image.hash); this.setCssName(config.css.dirname, config.css.hash); this.setFontName(config.font.dirname, config.font.hash); this.setOutputPath(config.build.outputPath); this.setOutputFileName(config.js.dirname, config.js.hash); this.set("resolve.alias", config.alias || {}); this.update("resolve.extensions", old => { return _.union(old, config.extensions || []); }); this.builder.emit("base-config", this); //hook: mergeRules this.setRules(config.rules); // hook: mergePlugin this.setPlugins(config.plugins); } /** * 合并rule配置项 * @param {Object | Array} rules * @param {Object} target */ mergeRule(rules = {}, target) { target = target || this.rules; const cloneRules = _.cloneDeep(rules); const sourceRules = Array.isArray(cloneRules) ? {} : cloneRules; if (Array.isArray(cloneRules)) { cloneRules.forEach(rule => { if (this.utils.isWebpackRule(rule)) { let label = this.utils.getLoaderLabel(rule); sourceRules[label] = Object.assign({}, rule, { enable: true }); } else if (_.isPlainObject(rule) && Object.keys(rule).length === 1) { let label = Object.keys(rule)[0]; sourceRules[label] = rule[label]; } }); } Object.keys(sourceRules).forEach(key => { let sourceRule = sourceRules[key]; if (sourceRule.loader) { sourceRule.use = [ { loader: sourceRule.loader, options: sourceRule.options || {} } ]; } const rule = target[key]; if (rule) { if (_.isPlainObject(sourceRule)) { if (sourceRule.enable === undefined) { target[key].enable = true; } target[key] = Object.assign(target[key], sourceRule); if (sourceRule.use) { target[key].use = sourceRule.use; } } else if (_.isBoolean(sourceRule)) { target[key].enable = sourceRule; } } else if (this.utils.isWebpackRule(sourceRule)) { target[key] = sourceRule; } if (sourceRule.options && target[key]) { let index = target[key].use.length - 1; if (target[key].use[index]) { target[key].use[index].options = sourceRule.options; let label = this.utils.getLoaderLabel(target[key].use[index]); this.config.loaderOptions[label] = sourceRule.options; } } }); return target; } /** * 设置webpack的loader * @param {Object} rules */ setRules(rules) { this.builder.emit("merge-rule", this); this.mergeRule(rules); let target = _.cloneDeep(this.rules); let result = []; let postcssLoader = this.createPostCssLoader(); let cssExtension = this.config.cssExtension; Object.keys(target).forEach(name => { let itemRule = target[name]; if (this.isUse(itemRule)) { let useloaders = itemRule.use; if (_.isFunction(itemRule.use)) { itemRule.use = itemRule.use.apply(this); } if (itemRule.postcss) { useloaders.splice(1, 0, postcssLoader); } useloaders.forEach((k, i) => { if (_.isString(k)) { useloaders[i] = { loader: k }; } let label = this.utils.getLoaderLabel(k); if (cssExtension.includes(name)) { useloaders[i].options = Object.assign( { sourceMap: this.config.cssSourceMap }, useloaders[i].options ); } else { if (label == "babel") { useloaders[i].options = Object.assign( {}, useloaders[i].options, this.config.loaderOptions[label] ); } else { useloaders[i].options = Object.assign({}, useloaders[i].options); } } }); if (cssExtension.includes(name)) { const fallback = this.config.extract ? MiniCssExtractPlugin.loader : this.config.fallback; itemRule.use = [fallback].concat(useloaders); } else { itemRule.use = useloaders; } ["type", "enable", "postcss", "loader", "options"].forEach(propery => { delete itemRule[propery]; }); result.push(itemRule); } }); this.webpackConfig.module.rules = result; } /** * 创建postcss-loader * @param {Object} loaderOptions */ createPostCssLoader(loaderOptions = {}) { let options = Object.assign({}, this.config.loaderOptions.postcss, loaderOptions); return { loader: "postcss-loader", options: options }; } /** * 合并plugin * @param {Object | Array} plugins * @param {*} target */ mergePlugin(plugins = {}, target) { target = target || this.plugins; const clonePlugins = _.cloneDeep(plugins); const sourcePlugins = Array.isArray(clonePlugins) ? {} : clonePlugins; if (Array.isArray(clonePlugins)) { clonePlugins.forEach(plugin => { if (this.utils.isWebpackPlugin(plugin)) { sourcePlugins[plugin.constructor.name] = plugin; } else if (_.isPlainObject(plugin) && plugin.name) { if (_.isString(plugin.name) && _.isString(plugin.label)) { sourcePlugins[plugin.label] = plugin; } else if (this.utils.isWebpackPlugin(plugin.name)) { sourcePlugins[plugin.label || plugin.name.constructor.name] = plugin; } else if (_.isFunction(plugin.name) && plugin.name.name) { sourcePlugins[plugin.name.name] = plugin; } } }); } Object.keys(sourcePlugins).forEach(name => { let configPlugin = sourcePlugins[name]; if (target[name]) { if (this.utils.isWebpackPlugin(configPlugin)) { target[name].name = configPlugin; } else if (_.isPlainObject(configPlugin)) { if (typeof configPlugin.enable === "undefined") { target[name].enable = true; } else if (_.isFunction(configPlugin.enable)) { target[name].enable = configPlugin.enable.apply(this); } Object.keys(configPlugin).forEach(k => { if (target[name].hasOwnProperty(k)) target[name][k] = configPlugin[k]; }); } else if (_.isBoolean(configPlugin)) { target[name].enable = configPlugin; } else if (_.isFunction(configPlugin)) { target[name] = Object.assign({}, target[name], { args: configPlugin }); } } else if ( this.utils.isWebpackPlugin(configPlugin) || this.utils.isConfigPlugin(configPlugin) ) { target[name] = configPlugin; } }); return target; } /** * 设置webpack的plugins * @param {Object} plugins */ setPlugins(plugins) { let self = this; this.builder.emit("merge-plugin", this); this.mergePlugin(plugins); let target = _.cloneDeep(this.plugins); let webpackPlugins = []; const modules = this.webpackConfig.resolveLoader && this.webpackConfig.resolveLoader.modules; Object.keys(target).forEach(name => { let itemPlugin = target[name]; if (this.isUse(itemPlugin)) { let plugin, pluginName; if (this.utils.isWebpackPlugin(itemPlugin)) { plugin = itemPlugin; pluginName = itemPlugin.constructor.name; } else if (_.isPlainObject(itemPlugin.name)) { plugin = itemPlugin.name; pluginName = itemPlugin.constructor && itemPlugin.constructor.name; } else if (_.isString(itemPlugin.name) || _.isFunction(itemPlugin.name)) { let Clazz = itemPlugin.name; if (_.isString(itemPlugin.name)) { pluginName = itemPlugin.name; Clazz = require(itemPlugin.name); } else if (_.isFunction(itemPlugin.name)) { pluginName = itemPlugin.name.name; } assert( Clazz, chalk.red( `dynamic create plugin[${name}] error, please check the npm module [${pluginName}] whether installed. if not installed, please execute the command [npm install ${pluginName} --save-dev] in command line` ) ); if (itemPlugin.entry) { Clazz = Clazz[itemPlugin.entry]; } if (itemPlugin.args) { let args; if (_.isFunction(itemPlugin.args)) { args = itemPlugin.args.apply(this); } else { args = itemPlugin.args; } plugin = new (Function.prototype.bind.apply(Clazz, [null].concat(args)))(); } else { plugin = new Clazz(); } } if (plugin) { plugin.__plugin__ = pluginName; plugin.__lable__ = name; webpackPlugins.push(plugin); } } }); this.webpackConfig.plugins = webpackPlugins; } /** * 合并optimization */ mergeOptimization(opt = {}) { this.update("optimization", (old = {}) => { return merge(old, opt); }); } /** * 设置optimization * @param {*} opt */ setOptimization(opt = {}) { this.set("optimization", opt); this.builder.emit("merge-optimization", this); } } module.exports = BaseConfig;