1 |
|
2 | const _ = require('lodash');
|
3 | const fs = require('fs');
|
4 | const path = require('path');
|
5 | const chalk = require('chalk');
|
6 | const hash = require('hash-sum');
|
7 | const notifier = require('node-notifier');
|
8 | const webpack = require('webpack');
|
9 | const CleanWebpackPlugin = require('clean-webpack-plugin');
|
10 | const FriendlyErrors = require('friendly-errors-webpack-plugin');
|
11 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
12 | const CopyWebpackPlugin = require('copy-webpack-plugin');
|
13 | const {GhostProgressPlugin} = require('ghost-progress-webpack-plugin');
|
14 | const {VueLoaderPlugin} = require('vue-loader');
|
15 |
|
16 | const getDevServerUrls = require('../util/getDevServerUrls');
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | function getHtmlWebpackPlugin(config = {}) {
|
24 |
|
25 | const HtmlWebpackPlugin = require('html-webpack-plugin');
|
26 |
|
27 | let minify = false;
|
28 | if (config.minify) {
|
29 | minify = {
|
30 | removeComments: true,
|
31 | collapseWhitespace: true,
|
32 | collapseBooleanAttributes: true,
|
33 | removeScriptTypeAttributes: true,
|
34 | collapseInlineTagWhitespace: true,
|
35 | minifyCSS: true,
|
36 | minifyJS: true,
|
37 |
|
38 |
|
39 | };
|
40 | }
|
41 |
|
42 | return new HtmlWebpackPlugin({
|
43 | filename: `${config.entryHtml}`,
|
44 | template: path.join(config.sourcePath, config.entryHtml),
|
45 | inject: true,
|
46 | minify,
|
47 |
|
48 | chunksSortMode: 'dependency',
|
49 | });
|
50 | }
|
51 |
|
52 | function getCompressionPlugin() {
|
53 | const CompressionWebpackPlugin = require('compression-webpack-plugin');
|
54 |
|
55 | const ext = ['js', 'css', 'xml', 'json', 'ttf', 'svg'];
|
56 | const regex = new RegExp(`\\.(?:${ext.join('|')})$`);
|
57 | return new CompressionWebpackPlugin({
|
58 | asset: '[path].gz[query]',
|
59 | algorithm: 'gzip',
|
60 | test: regex,
|
61 | threshold: 4096,
|
62 | minRatio: 0.8,
|
63 | cache: true,
|
64 | });
|
65 | }
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 | function getBundleAnalyzerPlugin(config) {
|
72 | const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
|
73 |
|
74 | const options = {
|
75 | analyzerMode: 'static',
|
76 | reportFilename: 'webpack.report.html',
|
77 | generateStatsFile: false,
|
78 | statsFilename: 'webpack.stats.json',
|
79 | openAnalyzer: false,
|
80 | };
|
81 |
|
82 | if (_.isPlainObject(config.analyzeBundle)) {
|
83 | Object.assign(options, config.analyzeBundle);
|
84 | }
|
85 |
|
86 | return new BundleAnalyzerPlugin(options);
|
87 | }
|
88 |
|
89 | function getDevelopmentPlugins(config) {
|
90 | const plugins = [];
|
91 | if (!config.hasHotClient && !config.isSSR) {
|
92 | plugins.push(
|
93 |
|
94 | new webpack.HotModuleReplacementPlugin(),
|
95 | );
|
96 | }
|
97 |
|
98 | return plugins;
|
99 | }
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 | function getNamedChunksPlugin() {
|
106 | return new webpack.NamedChunksPlugin((chunk) => {
|
107 | if (chunk.name) {
|
108 | return chunk.name;
|
109 | }
|
110 |
|
111 | const joinedHash = hash(
|
112 | Array.from(chunk.modulesIterable, m => m.id).join('_')
|
113 | );
|
114 | return 'chunk-' + joinedHash;
|
115 | });
|
116 | }
|
117 |
|
118 | function getCSSFileName(config) {
|
119 | if (!config.appendHash) return 'css/[name].js';
|
120 |
|
121 | if (config.isProduction) return 'css/[name].[contenthash:7].css';
|
122 | return 'css/[name].js';
|
123 | }
|
124 |
|
125 | function getProductionPlugins(config) {
|
126 | const plugins = [
|
127 |
|
128 |
|
129 | new MiniCssExtractPlugin({
|
130 | filename: getCSSFileName(config),
|
131 | chunkFilename: getCSSFileName(config),
|
132 | }),
|
133 |
|
134 |
|
135 | new webpack.HashedModuleIdsPlugin(),
|
136 | ];
|
137 |
|
138 | if (!config.isSSR) {
|
139 | plugins.push(
|
140 |
|
141 | new CleanWebpackPlugin({
|
142 |
|
143 | cleanAfterEveryBuildPatterns: [
|
144 |
|
145 | '!vue-ssr-server-bundle.json',
|
146 | '!server-bundle.json',
|
147 | '!server-bundle.js',
|
148 | '!vue-ssr-client-manifest.json',
|
149 | '!client-manifest.json',
|
150 | ],
|
151 | verbose: false,
|
152 | }),
|
153 |
|
154 |
|
155 |
|
156 | new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /en-gb/),
|
157 | );
|
158 | }
|
159 |
|
160 | return plugins;
|
161 | }
|
162 |
|
163 | function getDevServerMessages(config) {
|
164 | if (!config.isDevServer) return undefined;
|
165 |
|
166 | const urls = getDevServerUrls(config);
|
167 | return {
|
168 | messages: [
|
169 | `${chalk.bold('Local')}: ${urls.local}`,
|
170 | `${chalk.bold('Network')}: ${urls.network}`,
|
171 | ],
|
172 | };
|
173 | }
|
174 |
|
175 | function getFriendlyErrorsPlugin(config) {
|
176 | if (config.isSSRDevServer) return null;
|
177 |
|
178 | let onErrors;
|
179 | if (config.isDevServer && config.devServer.notifyOnError) {
|
180 | onErrors = (severity, errors) => {
|
181 | if (severity !== 'error') {
|
182 | return;
|
183 | }
|
184 |
|
185 | const error = errors[0];
|
186 | notifier.notify({
|
187 | title: 'Webpack Error',
|
188 | message: `${error.name}\nin ${error.file || ''}`,
|
189 | subtitle: error.file || '',
|
190 | icon: null,
|
191 | });
|
192 | };
|
193 | }
|
194 |
|
195 |
|
196 |
|
197 | const {transformer, formatter} = require('../util/resolveLoaderError');
|
198 | return new FriendlyErrors({
|
199 | compilationSuccessInfo: getDevServerMessages(config),
|
200 | onErrors,
|
201 | additionalTransformers: [transformer],
|
202 | additionalFormatters: [formatter],
|
203 | });
|
204 | }
|
205 |
|
206 | function getCopyPlugin(config) {
|
207 | const from = path.join(config.sourcePath, 'public');
|
208 |
|
209 | if (!fs.existsSync(from)) {
|
210 | return null;
|
211 | }
|
212 |
|
213 | return new CopyWebpackPlugin([
|
214 | {
|
215 | from,
|
216 | to: path.join(config.destPath, 'public'),
|
217 | toType: 'dir',
|
218 | },
|
219 | ]);
|
220 | }
|
221 |
|
222 | function getProgressPlugin() {
|
223 | if (process.stdout.isTTY) {
|
224 | return new GhostProgressPlugin();
|
225 | }
|
226 |
|
227 |
|
228 | const SimpleProgressPlugin = require('../util/simpleProgress');
|
229 | return SimpleProgressPlugin();
|
230 | }
|
231 |
|
232 | function getSSRPlugins(config) {
|
233 | const VueSSRServerPlugin = require('vue-server-renderer/server-plugin');
|
234 | const env = config.isProduction ? 'production' : 'development';
|
235 | return [
|
236 | new webpack.DefinePlugin({
|
237 | 'process.env.NODE_ENV': JSON.stringify(env),
|
238 | 'process.env.VUE_ENV': JSON.stringify('server'),
|
239 | window: 'undefined',
|
240 | }),
|
241 |
|
242 |
|
243 |
|
244 | new VueSSRServerPlugin({
|
245 | filename: 'vue-ssr-server-bundle.json',
|
246 | }),
|
247 | ];
|
248 | }
|
249 |
|
250 | function getPlugins(config = {}) {
|
251 | const plugins = [];
|
252 |
|
253 | plugins.push(
|
254 |
|
255 | new VueLoaderPlugin(),
|
256 |
|
257 |
|
258 | getNamedChunksPlugin(config),
|
259 |
|
260 |
|
261 | getFriendlyErrorsPlugin(config),
|
262 |
|
263 |
|
264 | getProgressPlugin(config),
|
265 | );
|
266 |
|
267 |
|
268 | if (!config.isSSR) {
|
269 |
|
270 | if (!config.isDevServer) {
|
271 | plugins.push(getCopyPlugin(config));
|
272 | }
|
273 |
|
274 | if (!config.library && config.entryHtml) {
|
275 | plugins.push(getHtmlWebpackPlugin(config));
|
276 | }
|
277 |
|
278 | if (config.gzip) {
|
279 | plugins.push(getCompressionPlugin(config));
|
280 | }
|
281 |
|
282 | if (config.analyzeBundle) {
|
283 | plugins.push(getBundleAnalyzerPlugin(config));
|
284 | }
|
285 | }
|
286 |
|
287 | if (config.isSSR) {
|
288 | plugins.push(...getSSRPlugins(config));
|
289 | }
|
290 |
|
291 |
|
292 | if (config.hasSSR && !config.isSSR) {
|
293 |
|
294 |
|
295 |
|
296 |
|
297 |
|
298 |
|
299 |
|
300 | const VueSSRClientPlugin = require('vue-server-renderer/client-plugin');
|
301 | plugins.push(new VueSSRClientPlugin({
|
302 | filename: 'vue-ssr-client-manifest.json',
|
303 | }));
|
304 | }
|
305 |
|
306 | if (config.isProduction) {
|
307 | plugins.push(...getProductionPlugins(config));
|
308 | }
|
309 | else {
|
310 | plugins.push(...getDevelopmentPlugins(config));
|
311 | }
|
312 |
|
313 | return plugins.filter(Boolean);
|
314 | }
|
315 |
|
316 | module.exports = {
|
317 | getPlugins,
|
318 | };
|