diff --git a/.gitignore b/.gitignore index f4e73280e..9108a84e4 100644 --- a/.gitignore +++ b/.gitignore @@ -70,3 +70,5 @@ package-lock.json # 备份文件 /components/test/* list.txt + +site/dev.js diff --git a/antd-tools/getWebpackConfig.js b/antd-tools/getWebpackConfig.js index 6b92bf4ee..edcafe366 100644 --- a/antd-tools/getWebpackConfig.js +++ b/antd-tools/getWebpackConfig.js @@ -2,7 +2,7 @@ const path = require('path'); const webpack = require('webpack'); const WebpackBar = require('webpackbar'); const webpackMerge = require('webpack-merge'); -const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); +const TerserPlugin = require('terser-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'); @@ -182,13 +182,8 @@ All rights reserved. config.output.libraryTarget = 'umd'; config.optimization = { minimizer: [ - new UglifyJsPlugin({ - cache: true, - parallel: true, + new TerserPlugin({ sourceMap: true, - uglifyOptions: { - warnings: false, - }, }), ], }; diff --git a/antd-tools/gulpfile.js b/antd-tools/gulpfile.js index a0a8ecc36..d91348953 100644 --- a/antd-tools/gulpfile.js +++ b/antd-tools/gulpfile.js @@ -34,7 +34,7 @@ const esDir = path.join(cwd, 'es'); function dist(done) { rimraf.sync(path.join(cwd, 'dist')); process.env.RUN_ENV = 'PRODUCTION'; - const webpackConfig = require(path.join(cwd, 'webpack.build.config.js')); + const webpackConfig = require(path.join(cwd, 'build/webpack.build.conf.js')); webpack(webpackConfig, (err, stats) => { if (err) { console.error(err.stack || err); diff --git a/build/config.js b/build/config.js new file mode 100644 index 000000000..fd0be0d9c --- /dev/null +++ b/build/config.js @@ -0,0 +1,3 @@ +module.exports = { + devComponent: 'select', +}; diff --git a/build/dev.js b/build/dev.js new file mode 100644 index 000000000..2d0112475 --- /dev/null +++ b/build/dev.js @@ -0,0 +1,172 @@ +process.env.ENTRY_INDEX = 'dev'; + +const fs = require('fs'); +const path = require('path'); +const chokidar = require('chokidar'); +const importFresh = require('import-fresh'); +const replace = require('json-templater/string'); +const webpack = require('webpack'); +const WebpackDevServer = require('webpack-dev-server'); +const devWebpack = require('./webpack.dev.conf'); + +const configPath = path.join(__dirname, './config.js'); + +let { devComponent } = require('./config'); + +const componentsInPrototype = ['Modal', 'message', 'notification']; + +const MAIN_TEMPLATE = `import 'babel-polyfill'; +import './index.less'; +import 'highlight.js/styles/solarized-light.css'; +import Vue from 'vue'; +import Vuex from 'vuex'; +import VueI18n from 'vue-i18n'; +import VueRouter from 'vue-router'; +import VueClipboard from 'vue-clipboard2'; +import Md from './components/md'; +import Api from './components/api'; +import demoBox from './components/demoBox'; +import demoContainer from './components/demoContainer'; +import Modal from '../components/modal'; +import message from '../components/message'; +import notification from '../components/notification'; +{{importComponents}} +{{importStyles}} +import '../components/modal/style'; +import '../components/message/style'; +import '../components/notification/style'; +import Test from '../components/{{name}}/demo/index.vue'; +import zhCN from './theme/zh-CN'; +import enUS from './theme/en-US'; + +Vue.use(Vuex); +Vue.use(VueClipboard); +Vue.use(VueRouter); +Vue.use(VueI18n); +Vue.component(Md.name, Md); +Vue.component(Api.name, Api); +Vue.component('demo-box', demoBox); +Vue.component('demo-container', demoContainer); + +Vue.prototype.$message = message; +Vue.prototype.$notification = notification; +Vue.prototype.$info = Modal.info; +Vue.prototype.$success = Modal.success; +Vue.prototype.$error = Modal.error; +Vue.prototype.$warning = Modal.warning; +Vue.prototype.$confirm = Modal.confirm; +Vue.prototype.$destroyAll = Modal.destroyAll; + +Vue.use(Modal); +{{install}} + +const i18n = new VueI18n({ + locale: enUS.locale, + messages: { + [enUS.locale]: { message: enUS.messages }, + [zhCN.locale]: { message: zhCN.messages }, + }, +}); + +const router = new VueRouter({ + mode: 'history', + routes: [{ path: '/*', component: Test }], +}); + +const store = new Vuex.Store({ + state: { + username: 'zeka', + }, + mutations: { + update(state, payload) { + state.username = payload.username; + }, + }, +}); +new Vue({ + el: '#app', + i18n, + router, + store, +}); +`; + +const OUTPUT_PATH = path.join(__dirname, '../site/dev.js'); + +const generateEntry = components => + Object.keys(components) + .map(component => `import ${component} from '../components/${components[component]}';`) + .join('\n'); + +const generateStyles = components => + Object.keys(components) + .map(component => `import '../components/${components[component]}/style';`) + .join('\n'); + +const generateInstall = components => + Object.keys(components) + .map(component => `Vue.use(${component});`) + .join('\n'); + +const renderTemplate = name => { + const components = { + Tooltip: 'tooltip', // for DemoBox + }; + + const demoPaths = fs.readdirSync(path.join(__dirname, `../components/${name}/demo`)); + + demoPaths.forEach(demoPath => { + const demo = fs + .readFileSync(path.join(__dirname, `../components/${name}/demo/${demoPath}`)) + .toString(); + + const componentsInDemo = demo.match(/a-(\w+(-\w+)*)/g) || []; + componentsInDemo.forEach(name => { + const componentName = name.replace(/-(\w)/g, ($, $1) => $1.toUpperCase()).replace(/^a/, ''); + + if (componentsInPrototype.includes(componentName)) { + return; + } + + const componentPath = path.join(__dirname, `../components/${name.replace(/^a-/, '')}`); + if (fs.existsSync(componentPath)) { + components[componentName] = name.replace(/^a-/, ''); + } + }); + }); + + const importComponents = generateEntry(components); + const importStyles = generateStyles(components); + const install = generateInstall(components); + const template = replace(MAIN_TEMPLATE, { + importComponents, + importStyles, + install, + name, + }); + fs.writeFileSync(OUTPUT_PATH, template); +}; + +let demoWatcher; + +chokidar.watch(configPath, { ignoreInitial: true }).on('change', async () => { + devComponent = importFresh(configPath).devComponent; + + demoWatcher && (await demoWatcher.close()); + + demoWatcher = chokidar.watch(path.join(__dirname, `../components/${devComponent}/demo`)); + demoWatcher.on('change', () => { + renderTemplate(devComponent); + }); + + renderTemplate(devComponent); +}); + +renderTemplate(devComponent); + +const compiler = webpack(devWebpack); + +const configuration = devWebpack.devServer; + +const server = new WebpackDevServer(compiler, configuration); +server.listen(configuration.port); diff --git a/webpack.base.config.js b/build/webpack.base.conf.js similarity index 93% rename from webpack.base.config.js rename to build/webpack.base.conf.js index fbbe605cb..2b2673363 100644 --- a/webpack.base.config.js +++ b/build/webpack.base.conf.js @@ -4,7 +4,7 @@ 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 getBabelCommonConfig = require('../antd-tools/getBabelCommonConfig'); const babelConfig = getBabelCommonConfig(false); babelConfig.plugins.push(require.resolve('babel-plugin-syntax-dynamic-import')); @@ -199,11 +199,11 @@ module.exports = { extensions: ['.js', '.jsx', '.vue', '.md'], alias: { vue$: 'vue/dist/vue.esm.js', - antd: path.join(__dirname, 'components'), - 'ant-design-vue': path.join(__dirname, 'components'), - 'ant-design-vue/es': path.join(__dirname, 'components'), - 'ant-design-vue/lib': path.join(__dirname, 'components'), - '@': path.join(__dirname, ''), + antd: path.join(__dirname, '../components'), + 'ant-design-vue': path.join(__dirname, '../components'), + 'ant-design-vue/es': path.join(__dirname, '../components'), + 'ant-design-vue/lib': path.join(__dirname, '../components'), + '@': path.join(__dirname, '../'), }, }, plugins: [new VueLoaderPlugin(), new WebpackBar()], diff --git a/webpack.build.config.js b/build/webpack.build.conf.js similarity index 93% rename from webpack.build.config.js rename to build/webpack.build.conf.js index 27c2357dd..e7e58eada 100644 --- a/webpack.build.config.js +++ b/build/webpack.build.conf.js @@ -1,6 +1,6 @@ // This config is for building dist files const webpack = require('webpack'); -const getWebpackConfig = require('./antd-tools/getWebpackConfig'); +const getWebpackConfig = require('../antd-tools/getWebpackConfig'); // noParse still leave `require('./locale' + name)` in dist files // ignore is better diff --git a/webpack.config.js b/build/webpack.dev.conf.js similarity index 89% rename from webpack.config.js rename to build/webpack.dev.conf.js index 2e13e185f..7d7aea5c2 100644 --- a/webpack.config.js +++ b/build/webpack.dev.conf.js @@ -1,12 +1,12 @@ const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const merge = require('webpack-merge'); -const baseWebpackConfig = require('./webpack.base.config'); +const baseWebpackConfig = require('./webpack.base.conf'); module.exports = merge(baseWebpackConfig, { mode: 'development', output: { - path: path.resolve(__dirname, './dist'), + path: path.resolve(__dirname, '../dist'), publicPath: '/', filename: 'build.js', }, @@ -36,6 +36,8 @@ module.exports = merge(baseWebpackConfig, { rewrites: [{ from: /./, to: '/index.html' }], }, disableHostCheck: true, + hot: true, + open: true, headers: { 'Access-Control-Allow-Origin': '*' }, }, performance: { diff --git a/webpack.site.config.js b/build/webpack.site.conf.js similarity index 93% rename from webpack.site.config.js rename to build/webpack.site.conf.js index ce69d81b2..b95cfa651 100644 --- a/webpack.site.config.js +++ b/build/webpack.site.conf.js @@ -3,11 +3,11 @@ const webpack = require('webpack'); 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 baseWebpackConfig = require('./webpack.base.conf'); module.exports = merge(baseWebpackConfig, { output: { - path: path.resolve(__dirname, './_site'), + path: path.resolve(__dirname, '../_site'), publicPath: '/', filename: '[name].[contenthash:8].js', chunkFilename: '[contenthash:8].async.js', diff --git a/package.json b/package.json index 29f788ac3..8dd6b5e56 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,8 @@ "scripts" ], "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", + "dev": "node build/dev.js", + "start": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.dev.conf.js", "test": "cross-env NODE_ENV=test jest --config .jest.js", "site": "node scripts/run.js _site", "copy": "node scripts/run.js copy-html", @@ -109,7 +109,7 @@ "fs-extra": "^8.0.0", "glob": "^7.1.2", "gulp": "^4.0.1", - "gulp-babel": "^8.0.0", + "gulp-babel": "^7.0.0", "gulp-strip-code": "^0.1.4", "highlight.js": "^9.12.0", "html-webpack-plugin": "^3.2.0", @@ -119,6 +119,7 @@ "jest-serializer-vue": "^2.0.0", "jest-transform-stub": "^2.0.0", "js-base64": "^2.4.8", + "json-templater": "^1.2.0", "jsonp": "^0.2.1", "less": "^3.9.0", "less-loader": "^5.0.0", @@ -149,8 +150,8 @@ "stylelint": "^12.0.0", "stylelint-config-prettier": "^8.0.0", "stylelint-config-standard": "^19.0.0", + "terser-webpack-plugin": "^2.3.1", "through2": "^3.0.0", - "uglifyjs-webpack-plugin": "^2.1.1", "url-loader": "^3.0.0", "vue": "^2.6.11", "vue-antd-md-loader": "^1.1.0", diff --git a/scripts/gulpfile.js b/scripts/gulpfile.js index 3306f72e6..bcae3e28e 100644 --- a/scripts/gulpfile.js +++ b/scripts/gulpfile.js @@ -14,7 +14,7 @@ const cwd = process.cwd(); function dist(done) { rimraf.sync(path.join(cwd, '_site')); process.env.RUN_ENV = 'PRODUCTION'; - const webpackConfig = require(path.join(cwd, 'webpack.site.config.js')); + const webpackConfig = require(path.join(cwd, 'build/webpack.site.conf.js')); webpack(webpackConfig, (err, stats) => { if (err) { console.error(err.stack || err); diff --git a/site/dev.js b/site/dev.js deleted file mode 100644 index 808744b02..000000000 --- a/site/dev.js +++ /dev/null @@ -1,55 +0,0 @@ -import 'babel-polyfill'; -import '../components/style.js'; -import './index.less'; -import 'highlight.js/styles/solarized-light.css'; -import Vue from 'vue'; -import Vuex from 'vuex'; -import VueI18n from 'vue-i18n'; -import VueRouter from 'vue-router'; -import VueClipboard from 'vue-clipboard2'; -import Md from './components/md'; -import Api from './components/api'; -import './components'; -import demoBox from './components/demoBox'; -import demoContainer from './components/demoContainer'; -import Test from '../components/table/demo/index.vue'; -import zhCN from './theme/zh-CN'; -import enUS from './theme/en-US'; -Vue.use(Vuex); -Vue.use(VueClipboard); -Vue.use(VueRouter); -Vue.use(VueI18n); -Vue.component(Md.name, Md); -Vue.component(Api.name, Api); -Vue.component('demo-box', demoBox); -Vue.component('demo-container', demoContainer); - -const i18n = new VueI18n({ - locale: enUS.locale, - messages: { - [enUS.locale]: { message: enUS.messages }, - [zhCN.locale]: { message: zhCN.messages }, - }, -}); - -const router = new VueRouter({ - mode: 'history', - routes: [{ path: '/*', component: Test }], -}); - -const store = new Vuex.Store({ - state: { - username: 'zeka', - }, - mutations: { - update(state, payload) { - state.username = payload.username; - }, - }, -}); -new Vue({ - el: '#app', - i18n, - router, - store, -});