UNPKG

tff-tailor

Version:

tailor for toursforfun frontend

534 lines (471 loc) 12.8 kB
/** * @description plugins * @author Leon.Cai */ const Webpack = require("webpack"), Path = require("path"), Log = require("../../lib/util/log"), HtmlWebpackPlugin = require("html-webpack-plugin"), StringReplaceWebpackPlugin = require("string-replace-webpack-plugin"), CopyWebpackPlugin = require("copy-webpack-plugin"), ExtractTextPlugin = require("extract-text-webpack-plugin"), StyleExtHtmlWebpackPlugin = require("style-ext-html-webpack-plugin"), WriteFileWebpackPlugin = require("write-file-webpack-plugin"), FriendlyErrorsWebpackPlugin = require("friendly-errors-webpack-plugin"), BundleAnalyzerPlugin = require("webpack-bundle-analyzer") .BundleAnalyzerPlugin, StyleLintPlugin = require("stylelint-webpack-plugin"), AddAssetHtmlWebpackPlugin = require("add-asset-html-webpack-plugin"), AssetsPlugin = require("assets-webpack-plugin"), Moment = require("moment"), MiniMatch = require("minimatch"), HappyPack = require("happypack"), OS = require("os"), ENV = require("../../constant/env.js"), Loaders = require("./loaders"), DllPlugin = Webpack.DllPlugin, DllReferencePlugin = Webpack.DllReferencePlugin, UglifyJsPlugin = Webpack.optimize.UglifyJsPlugin, ModuleConcatenationPlugin = Webpack.optimize.ModuleConcatenationPlugin, CommonsChunkPlugin = Webpack.optimize.CommonsChunkPlugin, HashedModuleIdsPlugin = Webpack.HashedModuleIdsPlugin, SourceMapDevToolPlugin = Webpack.SourceMapDevToolPlugin, ProvidePlugin = Webpack.ProvidePlugin, DefinePlugin = Webpack.DefinePlugin, COMMON_CHUNKS_NAME = "common-chunks", COMMON_MANIFEST_NAME = "common-manifest", COMMON_DLL_NAME = "common-dll"; /** * plugins for html * @param {Object} config [description] * @param {Object} entry [description] * @return {Array} [description] */ function htmlPlugin(config, entry) { let plugins = [], inputConfig = config.input, outputConfig = config.output, htmlInputConfig = inputConfig.html, htmlOutputConfig = outputConfig.html, commonEntries = inputConfig.entry.common || {}, groupEntries = inputConfig.entry.group || {}, groups = findGroups(entry, groupEntries); let commons = []; Object.keys(commonEntries).forEach(commonName => { commons.push(commonName); }); Object.keys(entry).forEach(page => { let pageItem = entry[page], pagePath = htmlOutputConfig.path, pageName = page, pageInputExt = htmlInputConfig.ext[0], pageOutputExt = htmlOutputConfig.ext, chunks = [COMMON_MANIFEST_NAME, ...commons], groupName = getGroup(pageName, groups); groupName && chunks.push(`${groupName}-${COMMON_CHUNKS_NAME}`); //push group name, common chunk name chunks.push(pageName); let template = `${pageName}.${pageInputExt}`; let options = { filename: `${pagePath}/${pageName}.${pageOutputExt}`, chunks: chunks, //TODO template: template, chunksSortMode: "manual", inject: !false, //TODO: auto inject minify: outputConfig.html.optm ? { html5: true, collapseWhitespace: true, collapseBooleanAttributes: true, collapseWhitespace: true, ignoreCustomComments: [/<!--[\w\W]+-->/g] // minifyCSS: true, // minifyJS: true, } : false }; // FSE.pathExistsSync(`${config.root}/${inputConfig.path}/${template}`) && new RegExp(inputConfig.entry.prefix + "$").test(pageName) && plugins.push(new HtmlWebpackPlugin(options)); }); function getGroup(entryName, groups) { for (let groupName in groups) { let items = groups[groupName]; if (items.indexOf(entryName) > -1) { return groupName; } } } return plugins; } /** * plugins for style * @param {Object} config [description] * @param {Object} entry [description] * @return {Array} [description] */ function stylePlugin(config, entry) { let plugins = [], outputConfig = config.output, styleConfig = outputConfig.style; plugins.push( new ExtractTextPlugin({ //extract css filename: `${styleConfig.path || "."}/[name]` + (outputConfig.useHash ? `.[contenthash:${outputConfig.hashLen}]` : "") + `.css`, allChunks: true, disable: !styleConfig.extract }) ); return plugins; } /** * plugins for lint * @param {[type]} config [description] * @param {[type]} entry [description] * @return {[type]} [description] */ function lintPlugin(config, entry) { let plugins = [], inputConfig = config.input, outputConfig = config.output; plugins.push( new StyleLintPlugin({ configFile: Path.join(config.root, ".stylelintrc"), failOnWarning: false, // warning occured then stop failOnError: false, // error occured then stop emitError: true, emitOnWarning: false, quiet: false, cache: true }) ); return plugins; } /** * plugins for dev * @param {Object} config [description] * @param {Object} entry [description] * @return {Array} [description] */ function devPlugin(config, entry) { let plugins = [], outputConfig = config.output, inputConfig = config.input, htmlOutputConfig = outputConfig.html, // imageInputConfig = inputConfig.image, fileInputConfig = inputConfig.file; plugins.push( //views write to hard disk new WriteFileWebpackPlugin({ test: new RegExp(`(${[htmlOutputConfig.ext].join("|")})$`, "i") }), new Webpack.optimize.OccurrenceOrderPlugin(), new Webpack.HotModuleReplacementPlugin(), new Webpack.NoEmitOnErrorsPlugin() // new HardSourceWebpackPlugin() ); return plugins; } /** * plugins for optm * @param {Object} config [description] * @param {Object} entry [description] * @return {Array} [description] */ function optmPlugin(config, entry) { let plugins = [], outputConfig = config.output, fileConfig = outputConfig.file; plugins.push( new UglifyJsPlugin({ //this will be very slow drop_debugger: true, dead_code: true, join_vars: true, reduce_vars: true, drop_console: true, comments: /[^\s\S]/g, sourceMap: true, parallel: true, exclude: /\.min\.js$/ }), new ModuleConcatenationPlugin() ); [ENV.test, ENV.prod].indexOf(config.env) > -1 && plugins.push( new AssetsPlugin({ path: Path.join(outputConfig.path, fileConfig.path), filename: "map.json", metadata: { date: Moment().format(), tailor: config.tailor.package.version } }) ); return plugins; } function happyPlugin(config, entry) { // HappyPack.SERIALIZABLE_OPTIONS = HappyPack.SERIALIZABLE_OPTIONS.concat([ // "postcss" // ]); let happyThreadPool = HappyPack.ThreadPool({ size: OS.cpus().length }), plugins = [], loaders = Loaders(config); plugins.push( new HappyPack({ id: loaders.happyJsLoaderName, loaders: [loaders.babelLoader, loaders.eslintLoader], threadPool: happyThreadPool, verbose: true }), new HappyPack({ id: loaders.happyStyleLoaderName, loaders: [loaders.lessLoader], threadPool: happyThreadPool, verbose: true }) ); return plugins; } /** * plugins for common * @param {Object} config [description] * @return {Object} [description] */ function commonPlugin(config, entry) { let plugins = [], inputConfig = config.input, groupEntries = inputConfig.entry.group || {}, outputConfig = config.output, jsConfig = outputConfig.js, entryKeys = Object.keys(entry), fileConfig = outputConfig.file, projConfig = config._projConfig, dllConfig = projConfig.dll; /** * TODO: * SourceMapDevToolPlugin * BundleAnalyzerPlugin */ plugins.push( new HashedModuleIdsPlugin(), new StringReplaceWebpackPlugin(), new FriendlyErrorsWebpackPlugin(), new DefinePlugin(varsHandler(config.vars)), new ProvidePlugin({ ...(config.providers || {}) }) ); if (config.env !== ENV.dll) { plugins.push( /** * @see https://doc.webpack-china.org/plugins/commons-chunk-plugin * @see https://github.com/creeperyang/blog/issues/37 * @type {Array} */ new CommonsChunkPlugin({ name: COMMON_MANIFEST_NAME, minChunks: Infinity }), new CopyWebpackPlugin([ { context: Path.join(config.root, inputConfig.path), from: { glob: "**/vendor/**", dot: true }, to: Path.join( config.root, outputConfig.path, jsConfig.path ), toType: "dir" }, { context: Path.join(config.root, inputConfig.path), from: { glob: `${dllConfig.output.path.replace( inputConfig.path, "" )}/**`.replace(/^\/+/, ""), dot: true }, to: Path.join( config.root, outputConfig.path, jsConfig.path ), toType: "dir", force: true } ]) ); let commonEntries = inputConfig.entry.common || {}; for (let commonName in commonEntries) { let chunks = commonEntries[commonName]; chunks.length && plugins.push( new CommonsChunkPlugin({ name: commonName, chunks: chunks, filename: `${jsConfig.path}/[name]` + (outputConfig.useHash ? `.[chunkhash]` : "") + `.js`, minChunks: Infinity }) ); } let groups = findGroups(entry, groupEntries); for (let groupName in groups) { let chunks = groups[groupName] || []; chunks.length && plugins.push( new CommonsChunkPlugin({ name: `${groupName}-${COMMON_CHUNKS_NAME}`, chunks: chunks, minChunks: (mod, count) => { return count >= 3; }, filename: `${jsConfig.path}/[name]` + (outputConfig.useHash ? `.[chunkhash]` : "") + `.js` }) ); } } //for happy pack if (config.parallel) { plugins.push(...happyPlugin(config, entry)); } return plugins; } function findGroups(entries, group) { let groups = {}; for (let groupName in group) { let items = [].concat(group[groupName]), arr = (groups[groupName] = []); for (let entryName in entries) { for (let i = 0, len = items.length; i < len; i++) { let item = items[i]; if (MiniMatch(entryName, item)) { arr.push(entryName); } } } } return groups; } /** * @see https://github.com/superpig/blog/issues/6 */ function dllPlugin(config, entry) { let plugins = [], inputConfig = config.input, outputConfig = config.output, dlls = inputConfig.entry.dll || {}, projConfig = config._projConfig, dllConfig = projConfig.dll; if (Object.keys(dlls).length) { let context = Path.join(config.root, inputConfig.path); if (config.env === ENV.dll) { plugins.push( new DllPlugin({ context: context, library: `${outputConfig.library}`, name: `${outputConfig.library}`, path: Path.join( outputConfig.path, `[name]-${COMMON_DLL_NAME}.json` ) }) ); } else { let dllAssets = [], dllPath = Path.resolve(config.root, dllConfig.output.path), styleInputConfig = inputConfig.style, styleReg = new RegExp( `\\.(${styleInputConfig.ext.join("|")})$`, "i" ), outputPath = `/${outputConfig.js.path}/` + dllConfig.output.path.replace(inputConfig.path, ""), publicPath = `${outputConfig.publicPath}/${outputPath}`; outputPath = outputPath.replace(/([\w_-]+)\/+/g, "$1/"); publicPath = publicPath.replace(/([\w_-]+)\/+/g, "$1/"); for (let key in dlls) { let manifest = require(`${config.root}/${ dllConfig.output.path }/${key}-${COMMON_DLL_NAME}.json`); plugins.push( new DllReferencePlugin({ context: context, manifest: manifest }) ); let existStyle = Object.keys(manifest.content).some(key => { return styleReg.test(key); }); if (existStyle) { dllAssets.push({ filepath: Path.resolve(dllPath, `${key}.css`), typeOfAsset: "css", hash: true, outputPath: outputPath, publicPath: publicPath }); } dllAssets.push({ filepath: Path.resolve(dllPath, `${key}.js`), typeOfAsset: "js", hash: true, outputPath: outputPath, publicPath: publicPath }); } if (dllAssets.length) { plugins.push(new AddAssetHtmlWebpackPlugin(dllAssets)); } } } return plugins; } /** * vars handler * @param {Object} json [description] * @return {Object} [description] */ function varsHandler(json) { json = json || {}; let result = {}; for (let key in json) { let val = json[key]; if (typeof val === "string") { result[key] = JSON.stringify(val); } else if (Object.prototype.toString.call(val) === "[object Object]") { varsHandler(val); } else { result[key] = val; } } return result; } module.exports = (config, entry) => { return { common: commonPlugin(config, entry), html: htmlPlugin(config, entry), style: stylePlugin(config, entry), dev: devPlugin(config, entry), optm: optmPlugin(config, entry), lint: lintPlugin(config, entry), dll: dllPlugin(config, entry) }; };