UNPKG

20.5 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6
7var _keys = require('babel-runtime/core-js/object/keys');
8
9var _keys2 = _interopRequireDefault(_keys);
10
11var _assign = require('babel-runtime/core-js/object/assign');
12
13var _assign2 = _interopRequireDefault(_assign);
14
15var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
16
17var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
18
19var _webpack = require('webpack');
20
21var _webpack2 = _interopRequireDefault(_webpack);
22
23var _webpackChain = require('webpack-chain');
24
25var _webpackChain2 = _interopRequireDefault(_webpackChain);
26
27var _path = require('path');
28
29var _chalk = require('chalk');
30
31var _chalk2 = _interopRequireDefault(_chalk);
32
33var _webpackNodeExternals = require('webpack-node-externals');
34
35var _webpackNodeExternals2 = _interopRequireDefault(_webpackNodeExternals);
36
37var _extractTextWebpackPlugin = require('extract-text-webpack-plugin');
38
39var _extractTextWebpackPlugin2 = _interopRequireDefault(_extractTextWebpackPlugin);
40
41var _friendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
42
43var _friendlyErrorsWebpackPlugin2 = _interopRequireDefault(_friendlyErrorsWebpackPlugin);
44
45var _optimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
46
47var _optimizeCssAssetsWebpackPlugin2 = _interopRequireDefault(_optimizeCssAssetsWebpackPlugin);
48
49var _copyWebpackPlugin = require('copy-webpack-plugin');
50
51var _copyWebpackPlugin2 = _interopRequireDefault(_copyWebpackPlugin);
52
53var _serverPlugin = require('vue-server-renderer/server-plugin');
54
55var _serverPlugin2 = _interopRequireDefault(_serverPlugin);
56
57var _webpackBundleAnalyzer = require('webpack-bundle-analyzer');
58
59var _swRegisterWebpackPlugin = require('sw-register-webpack-plugin');
60
61var _swRegisterWebpackPlugin2 = _interopRequireDefault(_swRegisterWebpackPlugin);
62
63var _progressBarWebpackPlugin = require('progress-bar-webpack-plugin');
64
65var _progressBarWebpackPlugin2 = _interopRequireDefault(_progressBarWebpackPlugin);
66
67var _timefixWebpackPlugin = require('./plugins/timefix-webpack-plugin');
68
69var _timefixWebpackPlugin2 = _interopRequireDefault(_timefixWebpackPlugin);
70
71var _uglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin');
72
73var _uglifyjsWebpackPlugin2 = _interopRequireDefault(_uglifyjsWebpackPlugin);
74
75var _loader = require('./utils/loader');
76
77var _path2 = require('./utils/path');
78
79var _workbox = require('./utils/workbox');
80
81var _constants = require('./constants');
82
83var _fs = require('fs');
84
85var _fs2 = _interopRequireDefault(_fs);
86
87var _gracefulFs = require('graceful-fs');
88
89var _gracefulFs2 = _interopRequireDefault(_gracefulFs);
90
91function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
92
93// solve 'too many open files' problem on Windows
94// see https://github.com/webpack-contrib/copy-webpack-plugin/issues/59
95_gracefulFs2.default.gracefulify(_fs2.default); /**
96 * @file webpack base config
97 * @author *__ author __*{% if: *__ email __* %}(*__ email __*){% /if %}
98 */
99
100class WebpackConfig {
101 constructor(config = {}, env) {
102 this.config = config;
103 this.env = env;
104 this.isProd = this.env === 'production';
105 this.isDev = this.env === 'development';
106 }
107
108 addStyleRules(config, options) {
109 (0, _loader.styleLoaders)(options).forEach(({ name, use, test }) => {
110 let currentStyleRule = config.module.rule(`style-${name}`).test(test);
111
112 use.forEach(loader => {
113 if (typeof loader === 'string') {
114 currentStyleRule.use(loader).loader(loader);
115 } else {
116 currentStyleRule.use(loader.loader).loader(loader.loader).options(loader.options);
117 }
118 });
119 });
120 }
121
122 generateBabelOptions(options, isClient) {
123 if (!options.babelrc && !options.presets) {
124 options.presets = [['vue-app', {
125 targets: isClient ? { ie: 9, uglify: true } : { node: 'current' }
126 }]];
127 }
128 return options;
129 }
130
131 /**
132 * generate webpack base config based on lavas config
133 *
134 * @param {Object} buildConfig build config
135 * @return {Object} webpackChain config
136 */
137 base(buildConfig = {}) {
138 var _this = this;
139
140 return (0, _asyncToGenerator3.default)(function* () {
141 let { globals, build } = _this.config;
142 /* eslint-disable fecs-one-var-per-line */
143 let { path, publicPath, filenames, cssSourceMap, cssMinimize,
144 cssExtract, jsSourceMap, babelOptions,
145 alias: { base: baseAlias = {} },
146 defines: { base: baseDefines = {} }
147 } = (0, _assign2.default)({}, build, buildConfig);
148 /* eslint-enable fecs-one-var-per-line */
149
150 let baseConfig = new _webpackChain2.default();
151
152 // set output
153 baseConfig.output.path(path).publicPath(publicPath);
154
155 // set extensions & alias
156 baseConfig.resolve.extensions.add('.js').add('.vue').add('.json');
157 let aliasObject = (0, _assign2.default)({
158 '@': globals.rootDir,
159 '$': (0, _path.join)(globals.rootDir, '.lavas')
160 }, baseAlias);
161 (0, _keys2.default)(aliasObject).forEach(function (aliasKey) {
162 baseConfig.resolve.alias.set(aliasKey, aliasObject[aliasKey]);
163 });
164
165 /**
166 * set module rules
167 */
168 baseConfig.module.noParse(/es6-promise\.js$/);
169
170 baseConfig.module.rule('vue').test(/\.vue$/).use('vue').loader('vue-loader').options((0, _loader.vueLoaders)({
171 cssSourceMap,
172 cssMinimize,
173 cssExtract,
174 babelOptions
175 }));
176
177 baseConfig.module.rule('js').test(/\.js$/).use('babel').loader('babel-loader').options(babelOptions).end().exclude.add(/node_modules/);
178
179 baseConfig.module.rule('img').test(/\.(png|jpe?g|gif|svg)(\?.*)?$/).use('url').loader('url-loader').options({
180 limit: 10000,
181 name: (0, _path2.assetsPath)(filenames.img)
182 });
183
184 baseConfig.module.rule('font').test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/).use('url').loader('url-loader').options({
185 limit: 10000,
186 name: (0, _path2.assetsPath)(filenames.fonts)
187 });
188
189 /**
190 * set plugins
191 */
192 baseConfig.plugin('define').use(_webpack2.default.DefinePlugin, [baseDefines]);
193
194 if (cssExtract) {
195 baseConfig.plugin('extract-css').use(_extractTextWebpackPlugin2.default, [{
196 filename: (0, _path2.assetsPath)(filenames.css)
197 }]);
198 }
199
200 // https://github.com/clessg/progress-bar-webpack-plugin#options
201 baseConfig.plugin('progress-bar').use(_progressBarWebpackPlugin2.default, [{
202 format: ' [:bar] ' + _chalk2.default.green.bold(':percent') + ' (:elapsed seconds)',
203 clear: false
204 }]);
205
206 if (_this.isProd) {
207 // enable scope hoisting
208 baseConfig.plugin('module-concatenation').use(_webpack2.default.optimize.ModuleConcatenationPlugin);
209
210 // https://webpack.js.org/plugins/hashed-module-ids-plugin
211 baseConfig.plugin('hashed-module-ids').use(_webpack2.default.HashedModuleIdsPlugin);
212
213 // https://github.com/lavas-project/lavas/issues/77
214 baseConfig.plugin('uglify-js').use(_uglifyjsWebpackPlugin2.default, [{
215 parallel: true, // enable `parallel` option
216 sourceMap: jsSourceMap
217 }]);
218
219 baseConfig.plugin('optimize-css').use(_optimizeCssAssetsWebpackPlugin2.default, [{
220 cssProcessorOptions: {
221 safe: true
222 }
223 }]);
224 } else {
225 // fix watchpack time problem
226 baseConfig.plugin('time-fix').use(_timefixWebpackPlugin2.default);
227
228 // https://webpack.js.org/plugins/named-modules-plugin
229 baseConfig.plugin('named-modules').use(_webpack2.default.NamedModulesPlugin);
230
231 baseConfig.plugin('friendly-error').use(_friendlyErrorsWebpackPlugin2.default);
232 }
233
234 // return a webpackChain object
235 return baseConfig;
236 })();
237 }
238
239 /**
240 * generate client base config based on lavas config
241 *
242 * @param {Object} internalBuildConfig build config
243 * @return {Object} client base config
244 */
245 client(internalBuildConfig = {}) {
246 var _this2 = this;
247
248 return (0, _asyncToGenerator3.default)(function* () {
249 let { globals, build, serviceWorker } = _this2.config;
250
251 /* eslint-disable fecs-one-var-per-line */
252 let { publicPath, filenames, cssSourceMap, cssMinimize, cssExtract,
253 jsSourceMap, bundleAnalyzerReport, extend, extendWithWebpackChain,
254 babel,
255 defines: { client: clientDefines = {} },
256 alias: { client: clientAlias = {} },
257 plugins: { base: basePlugins = [], client: clientPlugins = [] } } = (0, _assign2.default)({}, build, internalBuildConfig);
258 /* eslint-enable fecs-one-var-per-line */
259
260 let clientConfig = yield _this2.base((0, _assign2.default)(internalBuildConfig, {
261 babelOptions: _this2.generateBabelOptions(babel, true)
262 }));
263
264 // set output format
265 clientConfig.output.filename((0, _path2.assetsPath)(filenames.entry)).chunkFilename((0, _path2.assetsPath)(filenames.chunk));
266
267 // add alias for client
268 (0, _keys2.default)(clientAlias).forEach(function (aliasKey) {
269 clientConfig.resolve.alias.set(aliasKey, clientAlias[aliasKey]);
270 });
271
272 // add module.rules for style
273 _this2.addStyleRules(clientConfig, {
274 cssSourceMap,
275 cssMinimize,
276 cssExtract
277 });
278
279 // set sourcemap in dev & prod mode
280 clientConfig.devtool(jsSourceMap ? _this2.isDev ? 'cheap-module-eval-source-map' : 'nosources-source-map' : false);
281
282 // modify vars in DefinePlugin
283 clientConfig.plugin('define').init(function (Plugin, args) {
284 return new Plugin((0, _assign2.default)(args[0], {
285 'process.env.VUE_ENV': '"client"',
286 'process.env.NODE_ENV': `"${_this2.env}"`
287 }, clientDefines));
288 });
289
290 // split vendor js into its own file
291 clientConfig.plugin('chunk-vendor').use(_webpack2.default.optimize.CommonsChunkPlugin, [{
292 name: 'vendor',
293 filename: (0, _path2.assetsPath)(filenames.vendor),
294 minChunks(module, count) {
295 // any required modules inside node_modules are extracted to vendor
296 return module.resource && /\.js$/.test(module.resource) && module.resource.indexOf('node_modules') >= 0;
297 }
298 }]);
299
300 // split vue, vue-router, vue-meta and vuex into vue chunk
301 clientConfig.plugin('chunk-vue').use(_webpack2.default.optimize.CommonsChunkPlugin, [{
302 name: 'vue',
303 filename: (0, _path2.assetsPath)(filenames.vue),
304 minChunks(module, count) {
305 // On Windows, resource path will be seperated by '\',
306 // then paths like '\node_modules\vue\' cannot be matched because of '\v'.
307 // Transforming into '::node_modules::vue::' can solve this.
308 const PATH_SEP = '::';
309 let resource = module.resource;
310 let replacedResource = resource ? resource.split(_path.sep).join(PATH_SEP) : '';
311 let targets = ['vue', 'vue-router', 'vuex', 'vue-meta'];
312 // /^(::vue::|::vue-router::)$/i
313 let npmRegExp = new RegExp(PATH_SEP + targets.join('::|::') + PATH_SEP, 'i');
314 // /^(_vue@2.4.2@vue|_vue-router@1.2.3@vue-router)$/i
315 let cnpmRegExp = new RegExp(targets.map(t => `_${t}@\\d+\\.\\d+\\.\\d+@${t}`).join('|'), 'i');
316
317 return resource && replacedResource.indexOf('node_modules') !== -1 && (npmRegExp.test(replacedResource) || cnpmRegExp.test(replacedResource));
318 }
319 }]);
320
321 // extract webpack runtime and module manifest to its own file in order to
322 // prevent vendor hash from being updated whenever app bundle is updated
323 clientConfig.plugin('chunk-manifest').use(_webpack2.default.optimize.CommonsChunkPlugin, [{
324 name: 'manifest',
325 chunks: ['vue']
326 }]);
327
328 // Copy static files to /dist.
329 let copyList = [{
330 from: (0, _path.join)(globals.rootDir, _constants.ASSETS_DIRNAME_IN_DIST),
331 to: _constants.ASSETS_DIRNAME_IN_DIST,
332 ignore: ['*.md']
333 }];
334
335 if (_this2.isProd && serviceWorker) {
336 // Copy workbox.dev|prod.js from node_modules manually.
337 copyList = copyList.concat((0, _workbox.getWorkboxFiles)(_this2.isProd).map(function (f) {
338 return {
339 from: (0, _path.join)(_workbox.WORKBOX_PATH, `../${f}`),
340 to: (0, _path2.assetsPath)(`js/${f}`)
341 };
342 }));
343
344 // Use workbox@2.x in prod mode.
345 (0, _workbox.useWorkbox)(clientConfig, _this2.config);
346
347 // inject register code for service worker into HTML
348 clientConfig.plugin('sw-register').use(_swRegisterWebpackPlugin2.default, [{
349 filePath: (0, _path.resolve)(__dirname, 'templates/sw-register.js'),
350 prefix: serviceWorker && serviceWorker.swPath || publicPath
351 }]).after('html');
352 }
353 clientConfig.plugin('copy').use(_copyWebpackPlugin2.default, [copyList]);
354
355 // Bundle analyzer.
356 if (bundleAnalyzerReport) {
357 clientConfig.plugin('bundle-analyzer').use(_webpackBundleAnalyzer.BundleAnalyzerPlugin, [bundleAnalyzerReport]);
358 }
359
360 // call extendWithWebpackChain function if provided
361 let extendWithWebpackChainArray = [extendWithWebpackChain, _this2.config.build.extendWithWebpackChain];
362 for (let i = 0; i < extendWithWebpackChainArray.length; i++) {
363 let extendFunc = extendWithWebpackChainArray[i];
364 if (typeof extendFunc === 'function') {
365 yield extendFunc(clientConfig, {
366 type: 'client',
367 env: _this2.env
368 });
369 }
370 }
371
372 // convert webpackChain to plain webpack config object
373 let webpackConfigObject = clientConfig.toConfig();
374
375 if (basePlugins && basePlugins.length) {
376 webpackConfigObject.plugins = [...webpackConfigObject.plugins, ...basePlugins];
377 }
378 if (clientPlugins && clientPlugins.length) {
379 webpackConfigObject.plugins = [...webpackConfigObject.plugins, ...clientPlugins];
380 }
381
382 // call extend function if provided
383 if (typeof extend === 'function') {
384 extend.call(_this2, webpackConfigObject, {
385 type: 'base',
386 env: _this2.env
387 });
388 extend.call(_this2, webpackConfigObject, {
389 type: 'client',
390 env: _this2.env
391 });
392 }
393
394 return webpackConfigObject;
395 })();
396 }
397
398 /**
399 * generate webpack server config based on lavas config
400 *
401 * @param {Object} internalBuildConfig build config
402 * @return {Object} webpack server config
403 */
404 server(internalBuildConfig = {}) {
405 var _this3 = this;
406
407 return (0, _asyncToGenerator3.default)(function* () {
408 /* eslint-disable fecs-one-var-per-line */
409 let { extend, extendWithWebpackChain, nodeExternalsWhitelist = [],
410 babel,
411 defines: { server: serverDefines = {} },
412 alias: { server: serverAlias = {} },
413 plugins: { base: basePlugins = [], server: serverPlugins = [] }
414 } = (0, _assign2.default)({}, _this3.config.build, internalBuildConfig);
415 /* eslint-enable fecs-one-var-per-line */
416
417 let serverConfig = yield _this3.base((0, _assign2.default)(internalBuildConfig, {
418 babelOptions: _this3.generateBabelOptions(babel, false)
419 }));
420
421 // set target & output
422 serverConfig.target('node').output.filename('server-bundle.js').libraryTarget('commonjs2');
423
424 // add alias for server
425 (0, _keys2.default)(serverAlias).forEach(function (aliasKey) {
426 serverConfig.resolve.alias.set(aliasKey, serverAlias[aliasKey]);
427 });
428
429 /**
430 * Generally in ssr, we don't need any loader to handle style files,
431 * but some UI library such as vuetify will require style files directly in JS file.
432 * So we still add some relative loaders here.
433 */
434 _this3.addStyleRules(serverConfig, {
435 cssSourceMap: false,
436 cssMinimize: false,
437 cssExtract: false
438 });
439
440 // https://webpack.js.org/configuration/externals/#externals
441 // https://github.com/liady/webpack-node-externals
442 serverConfig.externals((0, _webpackNodeExternals2.default)({
443 // do not externalize CSS files in case we need to import it from a dep
444 whitelist: [...nodeExternalsWhitelist, /\.(css|vue)$/]
445 }));
446
447 // modify vars in DefinePlugin
448 serverConfig.plugin('define').init(function (Plugin, args) {
449 return new Plugin((0, _assign2.default)(args[0], {
450 'process.env.VUE_ENV': '"server"',
451 'process.env.NODE_ENV': `"${_this3.env}"`
452 }, serverDefines));
453 });
454
455 // add vue-ssr-server-plugin
456 serverConfig.plugin('ssr-server').use(_serverPlugin2.default, [{
457 filename: (0, _path.join)(_constants.LAVAS_DIRNAME_IN_DIST, _constants.SERVER_BUNDLE)
458 }]);
459
460 // call extendWithWebpackChain function if provided
461 let extendWithWebpackChainArray = [extendWithWebpackChain, _this3.config.build.extendWithWebpackChain];
462 for (let i = 0; i < extendWithWebpackChainArray.length; i++) {
463 let extendFunc = extendWithWebpackChainArray[i];
464 if (typeof extendFunc === 'function') {
465 yield extendFunc(serverConfig, {
466 type: 'server',
467 env: _this3.env
468 });
469 }
470 }
471
472 // convert webpackChain to plain webpack config object
473 let webpackConfigObject = serverConfig.toConfig();
474
475 // add plugins from `plugins` option
476 if (basePlugins && basePlugins.length) {
477 webpackConfigObject.plugins = [...webpackConfigObject.plugins, ...basePlugins];
478 }
479 if (serverPlugins && serverPlugins.length) {
480 webpackConfigObject.plugins = [...webpackConfigObject.plugins, ...serverPlugins];
481 }
482
483 // call extend function if provided
484 if (typeof extend === 'function') {
485 extend.call(_this3, webpackConfigObject, {
486 type: 'base',
487 env: _this3.env
488 });
489 extend.call(_this3, webpackConfigObject, {
490 type: 'server',
491 env: _this3.env
492 });
493 }
494
495 return webpackConfigObject;
496 })();
497 }
498}
499exports.default = WebpackConfig;
500module.exports = exports['default'];
\No newline at end of file