diff --git a/antd-tools/getBabelCommonConfig.js b/antd-tools/getBabelCommonConfig.js index 1b8c6e8fd..6f392971a 100644 --- a/antd-tools/getBabelCommonConfig.js +++ b/antd-tools/getBabelCommonConfig.js @@ -3,6 +3,7 @@ module.exports = function(modules) { const plugins = [ require.resolve('babel-plugin-transform-vue-jsx'), + require.resolve('babel-plugin-inline-import-data-uri'), require.resolve('babel-plugin-transform-es3-member-expression-literals'), require.resolve('babel-plugin-transform-es3-property-literals'), require.resolve('babel-plugin-transform-object-assign'), diff --git a/antd-tools/getWebpackConfig.js b/antd-tools/getWebpackConfig.js index e8bb243b1..6b92bf4ee 100644 --- a/antd-tools/getWebpackConfig.js +++ b/antd-tools/getWebpackConfig.js @@ -1,28 +1,43 @@ const path = require('path'); const webpack = require('webpack'); -const ExtractTextPlugin = require('extract-text-webpack-plugin'); +const WebpackBar = require('webpackbar'); +const webpackMerge = require('webpack-merge'); +const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); -const deepAssign = require('deep-assign'); -const chalk = require('chalk'); const postcssConfig = require('./postcssConfig'); +const CleanUpStatsPlugin = require('./utils/CleanUpStatsPlugin'); + const distFileBaseName = 'antd'; -module.exports = function(modules) { + +const svgRegex = /\.svg(\?v=\d+\.\d+\.\d+)?$/; +const svgOptions = { + limit: 10000, + minetype: 'image/svg+xml', +}; + +const imageOptions = { + limit: 10000, +}; + +function getWebpackConfig(modules) { const pkg = require(path.join(process.cwd(), 'package.json')); const babelConfig = require('./getBabelCommonConfig')(modules || false); const pluginImportOptions = [ { style: true, - libraryName: 'antd', + libraryName: distFileBaseName, libraryDirectory: 'components', }, ]; - - // if (distFileBaseName !== 'antd') { pluginImportOptions.push({ style: - // 'css', libraryDirectory: 'components', libraryName: 'antd', }) } - babelConfig.plugins.push([require.resolve('babel-plugin-import'), pluginImportOptions]); + if (modules === false) { + babelConfig.plugins.push(require.resolve('./replaceLib')); + } + const config = { devtool: 'source-map', @@ -86,74 +101,75 @@ module.exports = function(modules) { }, { test: /\.css$/, - use: ExtractTextPlugin.extract({ - use: [ - { - loader: 'css-loader', - options: { - sourceMap: true, - }, + use: [ + MiniCssExtractPlugin.loader, + { + loader: 'css-loader', + options: { + sourceMap: true, }, - { - loader: 'postcss-loader', - options: Object.assign({}, postcssConfig, { sourceMap: true }), - }, - ], - }), + }, + { + loader: 'postcss-loader', + options: Object.assign({}, postcssConfig, { sourceMap: true }), + }, + ], }, { test: /\.less$/, - use: ExtractTextPlugin.extract({ - use: [ - { - loader: 'css-loader', - options: { - sourceMap: true, - }, + use: [ + MiniCssExtractPlugin.loader, + { + loader: 'css-loader', + options: { + sourceMap: true, }, - { - loader: 'postcss-loader', - options: Object.assign({}, postcssConfig, { sourceMap: true }), + }, + { + loader: 'postcss-loader', + options: Object.assign({}, postcssConfig, { sourceMap: true }), + }, + { + loader: 'less-loader', + options: { + sourceMap: true, + javascriptEnabled: true, }, - { - loader: 'less-loader', - options: { - sourceMap: true, - }, - }, - ], - }), + }, + ], + }, + // Images + { + test: svgRegex, + loader: 'url-loader', + options: svgOptions, + }, + { + test: /\.(png|jpg|jpeg|gif)(\?v=\d+\.\d+\.\d+)?$/i, + loader: 'url-loader', + options: imageOptions, }, ], }, plugins: [ - new ExtractTextPlugin({ filename: '[name].css', disable: false, allChunks: true }), new CaseSensitivePathsPlugin(), new webpack.BannerPlugin(` -${distFileBaseName} v${pkg.version} +${pkg.name} v${pkg.version} Copyright 2017-present, ant-design-vue. All rights reserved. `), - new webpack.ProgressPlugin((percentage, msg, addInfo) => { - const stream = process.stderr; - if (stream.isTTY && percentage < 0.71) { - stream.cursorTo(0); - stream.write(`📦 ${chalk.magenta(msg)} (${chalk.magenta(addInfo)})`); - stream.clearLine(1); - } else if (percentage === 1) { - console.log(chalk.green('\nwebpack: bundle build is now finished.')); - } + new WebpackBar({ + name: '🚚 Ant Design Vue Tools', + color: '#2f54eb', }), + new CleanUpStatsPlugin(), ], }; if (process.env.RUN_ENV === 'PRODUCTION') { const entry = ['./index']; - config.entry = { - [`${distFileBaseName}.min`]: entry, - }; config.externals = { vue: { root: 'Vue', @@ -164,38 +180,61 @@ All rights reserved. }; config.output.library = distFileBaseName; config.output.libraryTarget = 'umd'; - - const uncompressedConfig = deepAssign({}, config); - - config.plugins = config.plugins.concat([ - new webpack.optimize.UglifyJsPlugin({ - sourceMap: true, - output: { - ascii_only: true, - }, - compress: { - warnings: false, - }, - }), - new webpack.optimize.ModuleConcatenationPlugin(), - new webpack.LoaderOptionsPlugin({ minimize: true }), - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production'), - }), - ]); - - uncompressedConfig.entry = { - [distFileBaseName]: entry, + config.optimization = { + minimizer: [ + new UglifyJsPlugin({ + cache: true, + parallel: true, + sourceMap: true, + uglifyOptions: { + warnings: false, + }, + }), + ], }; - uncompressedConfig.plugins.push( - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify('development'), - }), - ); + // Development + const uncompressedConfig = webpackMerge({}, config, { + entry: { + [distFileBaseName]: entry, + }, + mode: 'development', + plugins: [ + new MiniCssExtractPlugin({ + filename: '[name].css', + }), + ], + }); - return [config, uncompressedConfig]; + // Production + const prodConfig = webpackMerge({}, config, { + entry: { + [`${distFileBaseName}.min`]: entry, + }, + mode: 'production', + plugins: [ + new webpack.optimize.ModuleConcatenationPlugin(), + new webpack.LoaderOptionsPlugin({ + minimize: true, + }), + new MiniCssExtractPlugin({ + filename: '[name].css', + }), + ], + optimization: { + minimizer: [new OptimizeCSSAssetsPlugin({})], + }, + }); + + return [prodConfig, uncompressedConfig]; } return config; -}; +} + +getWebpackConfig.webpack = webpack; +getWebpackConfig.svgRegex = svgRegex; +getWebpackConfig.svgOptions = svgOptions; +getWebpackConfig.imageOptions = imageOptions; + +module.exports = getWebpackConfig; diff --git a/antd-tools/transformLess.js b/antd-tools/transformLess.js index 2a03eefe2..269795e90 100644 --- a/antd-tools/transformLess.js +++ b/antd-tools/transformLess.js @@ -17,6 +17,7 @@ function transformLess(lessFile, config = {}) { paths: [path.dirname(resolvedLessFile)], filename: resolvedLessFile, plugins: [new NpmImportPlugin({ prefix: '~' })], + javascriptEnabled: true, }; return less .render(data, lessOpts) diff --git a/antd-tools/utils/CleanUpStatsPlugin.js b/antd-tools/utils/CleanUpStatsPlugin.js new file mode 100644 index 000000000..300168c4f --- /dev/null +++ b/antd-tools/utils/CleanUpStatsPlugin.js @@ -0,0 +1,38 @@ +// We should use `stats` props of webpack. But it not work in v4. +class CleanUpStatsPlugin { + constructor(option) { + this.option = { + MiniCSSExtractPlugin: true, + tsLoader: true, + ...option, + }; + } + + shouldPickStatChild(child) { + const { MiniCSSExtractPlugin } = this.option; + if (MiniCSSExtractPlugin && child.name.includes('mini-css-extract-plugin')) return false; + return true; + } + + shouldPickWarning(message) { + const { tsLoader } = this.option; + if (tsLoader && /export .* was not found in .*/.test(message)) { + return false; + } + return true; + } + + apply(compiler) { + compiler.hooks.done.tap('CleanUpStatsPlugin', stats => { + const { children, warnings } = stats.compilation; + if (Array.isArray(children)) { + stats.compilation.children = children.filter(child => this.shouldPickStatChild(child)); + } + if (Array.isArray(warnings)) { + stats.compilation.warnings = warnings.filter(message => this.shouldPickWarning(message)); + } + }); + } +} + +module.exports = CleanUpStatsPlugin; diff --git a/package.json b/package.json index 2e5c69ae7..591b8706b 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "scripts": { "dev": "cross-env NODE_ENV=development ENTRY_INDEX=dev ./node_modules/.bin/webpack-dev-server --open --hot --port 3001", "start": "cross-env NODE_ENV=development ./node_modules/.bin/webpack-dev-server --open --hot", + "site-test": "cross-env NODE_ENV=production webpack --config ./webpack.site.config.js", "test": "cross-env NODE_ENV=test jest --config .jest.js", "site": "node scripts/run.js site-dist", "copy": "node scripts/run.js copy-html", @@ -51,8 +52,8 @@ }, "homepage": "https://github.com/vueComponent/ant-design-vue", "pre-commit": [ - "lint:style", - "lint" + "lint", + "prettier" ], "peerDependencies": { "vue": ">=2.5.0", @@ -68,12 +69,13 @@ "axios": "^0.18.0", "babel-cli": "^6.26.0", "babel-core": "^6.26.0", - "babel-eslint": "^8.0.1", + "babel-eslint": "^10.0.1", "babel-helper-vue-jsx-merge-props": "^2.0.3", "babel-jest": "^22.4.3", "babel-loader": "^7.1.2", "babel-plugin-add-module-exports": "^1.0.0", "babel-plugin-import": "^1.1.1", + "babel-plugin-inline-import-data-uri": "^1.0.1", "babel-plugin-istanbul": "^4.1.1", "babel-plugin-syntax-dynamic-import": "^6.18.0", "babel-plugin-syntax-jsx": "^6.18.0", @@ -112,26 +114,28 @@ "gulp-babel": "^7.0.0", "gulp-strip-code": "^0.1.4", "highlight.js": "^9.12.0", - "html-webpack-plugin": "^2.30.1", + "html-webpack-plugin": "^3.2.0", "husky": "^0.14.3", "istanbul-instrumenter-loader": "^3.0.0", "jest": "^22.4.3", "jest-serializer-vue": "^1.0.0", "js-base64": "^2.4.8", "jsonp": "^0.2.1", - "less": "^2.7.2", - "less-loader": "^4.0.5", + "less": "^3.9.0", + "less-loader": "^4.1.0", "less-plugin-npm-import": "^2.1.0", "markdown-it": "^8.4.0", "markdown-it-anchor": "^4.0.0", "marked": "^0.3.7", "merge2": "^1.2.1", + "mini-css-extract-plugin": "^0.5.0", "minimist": "^1.2.0", "mkdirp": "^0.5.1", "mockdate": "^2.0.2", "nprogress": "^0.2.0", - "postcss": "^6.0.20", - "postcss-loader": "^2.1.2", + "optimize-css-assets-webpack-plugin": "^5.0.1", + "postcss": "^7.0.6", + "postcss-loader": "^3.0.0", "pre-commit": "^1.2.2", "prettier": "^1.15.3", "querystring": "^0.2.0", @@ -146,21 +150,24 @@ "stylelint-config-prettier": "^4.0.0", "stylelint-config-standard": "^18.2.0", "through2": "^2.0.3", + "uglifyjs-webpack-plugin": "^2.1.1", + "url-loader": "^1.1.2", "vue": "^2.5.16", - "vue-antd-md-loader": "^1.0.3", + "vue-antd-md-loader": "^1.1.0", "vue-clipboard2": "0.0.8", "vue-i18n": "^8.3.2", "vue-infinite-scroll": "^2.0.2", "vue-jest": "^2.5.0", - "vue-loader": "^13.0.5", + "vue-loader": "^15.5.1", "vue-router": "^3.0.1", "vue-server-renderer": "^2.5.16", "vue-template-compiler": "^2.5.16", "vue-virtual-scroller": "^0.12.0", - "webpack": "^3.11.0", - "webpack-chunk-hash": "^0.5.0", - "webpack-dev-server": "^2.8.2", - "webpack-merge": "^4.1.1" + "webpack": "^4.28.4", + "webpack-cli": "^3.2.1", + "webpack-dev-server": "^3.1.14", + "webpack-merge": "^4.1.1", + "webpackbar": "^3.1.5" }, "dependencies": { "@ant-design/icons": "^1.1.15", @@ -190,5 +197,11 @@ "shallowequal": "^1.0.2", "vue-ref": "^1.0.4", "warning": "^3.0.0" - } + }, + "sideEffects": [ + "dist/*", + "es/**/style/*", + "lib/**/style/*", + "*.less" + ] } diff --git a/types/breadcrumb.d.ts b/types/breadcrumb.d.ts index abdeafad2..f8f9c6bcb 100644 --- a/types/breadcrumb.d.ts +++ b/types/breadcrumb.d.ts @@ -34,7 +34,15 @@ export declare class Breadcrumb extends AntdComponent { * Custom item renderer, slot="itemRender" and slot-scope="{route, params, routes, paths}" * @type Function */ - itemRender: ( - { route, params, routes, paths }: { route: any; params: any; routes: any; paths: any }, - ) => VNode; + itemRender: ({ + route, + params, + routes, + paths, + }: { + route: any; + params: any; + routes: any; + paths: any; + }) => VNode; } diff --git a/types/cascader.d.ts b/types/cascader.d.ts index 800262c71..ec2fb742f 100644 --- a/types/cascader.d.ts +++ b/types/cascader.d.ts @@ -86,9 +86,13 @@ export declare class Cascader extends AntdComponent { * @default labels => labels.join(' / ') * @type Function */ - displayRender: ( - { labels, selectedOptions }: { labels: string[]; selectedOptions: CascaderOptionType[] }, - ) => VNode; + displayRender: ({ + labels, + selectedOptions, + }: { + labels: string[]; + selectedOptions: CascaderOptionType[]; + }) => VNode; /** * expand current item when click or hover, one of 'click' 'hover' diff --git a/types/layout/layout-content.d.ts b/types/layout/layout-content.d.ts index 54948f7b4..419c2193b 100644 --- a/types/layout/layout-content.d.ts +++ b/types/layout/layout-content.d.ts @@ -6,7 +6,6 @@ import { AntdComponent } from '../component'; export default class LayoutContent extends AntdComponent { - /** * container className * @default undefined diff --git a/types/layout/layout-footer.d.ts b/types/layout/layout-footer.d.ts index d8f155d05..c40a6e7f3 100644 --- a/types/layout/layout-footer.d.ts +++ b/types/layout/layout-footer.d.ts @@ -6,7 +6,6 @@ import { AntdComponent } from '../component'; export default class LayoutFooter extends AntdComponent { - /** * container className * @default undefined diff --git a/types/layout/layout-header.d.ts b/types/layout/layout-header.d.ts index c8a56cff6..3a9b73399 100644 --- a/types/layout/layout-header.d.ts +++ b/types/layout/layout-header.d.ts @@ -6,7 +6,6 @@ import { AntdComponent } from '../component'; export default class LayoutHeader extends AntdComponent { - /** * container className * @default undefined diff --git a/webpack.base.config.js b/webpack.base.config.js index 25342876a..0ef3eb21f 100644 --- a/webpack.base.config.js +++ b/webpack.base.config.js @@ -2,6 +2,8 @@ const path = require('path'); const hljs = require('highlight.js'); const Token = require('markdown-it/lib/token'); const cheerio = require('cheerio'); +const WebpackBar = require('webpackbar'); +const VueLoaderPlugin = require('vue-loader/lib/plugin'); const getBabelCommonConfig = require('./antd-tools/getBabelCommonConfig'); const babelConfig = getBabelCommonConfig(false); @@ -129,52 +131,45 @@ md.core.ruler.push('update_template', function replace({ tokens }) { ` : ''; - newContent += style - ? ` - - ` - : ''; - newContent += scopedStyle - ? ` - - ` - : ''; + newContent += style ? `` : ''; + newContent += scopedStyle ? `` : ''; const t = new Token('html_block', '', 0); t.content = newContent; tokens.push(t); } }); - +const vueLoaderOptions = { + loaders: { + js: [ + { + loader: 'babel-loader', + options: { + presets: ['env'], + plugins: ['transform-vue-jsx', 'transform-object-rest-spread'], + }, + }, + ], + }, +}; module.exports = { + mode: 'production', entry: { index: [`./site/${process.env.ENTRY_INDEX || 'index'}.js`], }, module: { rules: [ { - test: /\.md/, + test: /\.md$/, use: [ + { + loader: 'vue-loader', + options: vueLoaderOptions, + }, { loader: 'vue-antd-md-loader', options: Object.assign(md, { wrapper: 'div', - vueLoaderOptions: { - loaders: { - js: [ - { - loader: 'babel-loader', - options: { - presets: ['env'], - plugins: ['transform-vue-jsx', 'transform-object-rest-spread'], - }, - }, - ], - }, - }, + raw: true, }), }, ], @@ -182,23 +177,7 @@ module.exports = { { test: /\.vue$/, loader: 'vue-loader', - options: { - loaders: { - js: [ - { - loader: 'babel-loader', - options: { - presets: ['env'], - plugins: [ - 'transform-vue-jsx', - 'transform-object-rest-spread', - 'syntax-dynamic-import', - ], - }, - }, - ], - }, - }, + options: vueLoaderOptions, }, { test: /\.(js|jsx)$/, @@ -225,4 +204,5 @@ module.exports = { '@': path.join(__dirname, ''), }, }, + plugins: [new VueLoaderPlugin(), new WebpackBar()], }; diff --git a/webpack.config.js b/webpack.config.js index ece2fa47e..fa75971f9 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,6 +5,7 @@ const merge = require('webpack-merge'); const baseWebpackConfig = require('./webpack.base.config'); module.exports = merge(baseWebpackConfig, { + mode: 'development', output: { path: path.resolve(__dirname, './dist'), publicPath: '/ant-design-vue/', @@ -20,7 +21,7 @@ module.exports = merge(baseWebpackConfig, { loader: 'css-loader', options: { sourceMap: true }, }, - { loader: 'less-loader', options: { sourceMap: true } }, + { loader: 'less-loader', options: { sourceMap: true, javascriptEnabled: true } }, ], }, { diff --git a/webpack.site.config.js b/webpack.site.config.js index 0c80c9b0d..ca6d7f820 100644 --- a/webpack.site.config.js +++ b/webpack.site.config.js @@ -1,86 +1,67 @@ const path = require('path'); const webpack = require('webpack'); -const ExtractTextPlugin = require('extract-text-webpack-plugin'); -const WebpackChunkHash = require('webpack-chunk-hash'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const merge = require('webpack-merge'); const baseWebpackConfig = require('./webpack.base.config'); -const modulePlugin = new ExtractTextPlugin({ - filename: '[name].[chunkhash].css', - allChunks: true, -}); - module.exports = merge(baseWebpackConfig, { output: { path: path.resolve(__dirname, './site-dist'), publicPath: '/ant-design-vue/', - filename: '[name].[chunkhash].js', - chunkFilename: '[chunkhash].async.js', + filename: '[name].[contenthash:8].js', + chunkFilename: '[contenthash:8].async.js', }, module: { rules: [ { test: /\.less$/, - use: modulePlugin.extract({ - fallback: 'style-loader', - use: [ - { - loader: 'css-loader', - }, - { - loader: 'postcss-loader', - }, - { loader: 'less-loader' }, - ], - }), + use: [ + MiniCssExtractPlugin.loader, + 'css-loader', + 'postcss-loader', + { loader: 'less-loader', options: { javascriptEnabled: true } }, + ], }, { test: /\.css$/, - use: modulePlugin.extract({ - fallback: 'style-loader', - use: [ - { - loader: 'css-loader', - }, - { - loader: 'postcss-loader', - }, - ], - }), + use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'], }, ], }, + optimization: { + splitChunks: { + cacheGroups: { + vendors: { + name: `chunk-vendors`, + test: /[\\/]node_modules[\\/]/, + priority: -10, + chunks: 'initial', + }, + common: { + name: `chunk-common`, + minChunks: 2, + priority: -20, + chunks: 'initial', + reuseExistingChunk: true, + }, + }, + }, + }, plugins: [ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"', }, }), - new webpack.optimize.CommonsChunkPlugin({ - name: 'vender', - minChunks: function(module) { - return /node_modules/.test(module.context); - }, - }), - new webpack.optimize.CommonsChunkPlugin({ - name: 'manifest', - minChunks: Infinity, - }), - new webpack.optimize.UglifyJsPlugin({ - compress: { - warnings: false, - }, - }), new HtmlWebpackPlugin({ template: './site/index.html', inject: true, production: true, }), - new webpack.LoaderOptionsPlugin({ - minimize: true, + new MiniCssExtractPlugin({ + filename: '[name].[contenthash:8].css', + chunkFilename: '[id].[contenthash:8].css', }), - modulePlugin, - new WebpackChunkHash({ algorithm: 'md5' }), ], });