diff --git a/.eslintrc.js b/.eslintrc.js index 5850016e..be2a2b04 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,6 +15,7 @@ module.exports = { 'vue/comment-directive': 'off', 'no-param-reassign': 'off', 'func-names': 'off', + 'global-require': 'off', 'class-methods-use-this': 'off' } }; diff --git a/package.json b/package.json index 437a7ed2..d7920393 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,6 @@ "lerna": "^3.18.4" }, "devDependencies": { - "@webank/eslint-config-webank": "^0.1.6" + "@webank/eslint-config-webank": "^0.1.7" } } diff --git a/packages/fes-cli/bin/index.js b/packages/fes-cli/bin/index.js index 55fc4a0e..ca6caa64 100755 --- a/packages/fes-cli/bin/index.js +++ b/packages/fes-cli/bin/index.js @@ -7,7 +7,7 @@ const generateConfig = require('../build/helpers/config'); const log = require('../build/helpers/log'); commander.usage(' [options]') - .version(pkg.version) + .version(pkg.version, '-v, --vers') .option('-e, --env ', '配置环境 local(本地) | sit(测试) | prod(生产)') .description(pkg.description); diff --git a/packages/fes-cli/build/configs/webpack.config.js b/packages/fes-cli/build/configs/webpack.config.js index 407e99d1..a1df81b6 100644 --- a/packages/fes-cli/build/configs/webpack.config.js +++ b/packages/fes-cli/build/configs/webpack.config.js @@ -4,14 +4,26 @@ const merge = require('webpack-merge'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); -const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin'); +const FriendlyErrorsPlugin = require('@soda/friendly-errors-webpack-plugin'); const CopyPlugin = require('copy-webpack-plugin'); const OptimizeCssnanoPlugin = require('@intervolga/optimize-cssnano-plugin'); const TerserPlugin = require('terser-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const HtmlPlugin = require('html-webpack-plugin'); +const CompressionWebpackPlugin = require('compression-webpack-plugin'); +const autoprefixer = require('autoprefixer'); const browsers = require('../helpers/browser'); + +function handleGzipCompress(compress) { + if (!compress) return null; + if (typeof compress === 'boolean') { + return {}; + } + return compress; +} + + module.exports = function webpackConfig(configs, webpack, mode) { let template = path.resolve( configs.folders.PROJECT_DIR, @@ -24,36 +36,26 @@ module.exports = function webpackConfig(configs, webpack, mode) { const isDev = mode === 'dev'; const isBuild = mode === 'build'; - const projectNodeModulesDir = path.resolve( - configs.folders.PROJECT_DIR, - './node_modules' - ); - const cliNodeModulesDir = path.resolve( - configs.folders.CLI_DIR, - './node_modules' - ); - const nodeModulesDir = ['node_modules', projectNodeModulesDir, cliNodeModulesDir]; + const gzipCompress = handleGzipCompress(configs.compress); + const presets = [ [ - path.resolve(cliNodeModulesDir, '@babel/preset-env'), - { - modules: false, - useBuiltIns: 'entry', - corejs: 3, - targets: { - browsers - } - } + require.resolve('@babel/preset-env') ] ]; const plugins = [ - path.resolve(cliNodeModulesDir, '@babel/plugin-proposal-object-rest-spread'), - path.resolve(cliNodeModulesDir, '@babel/plugin-syntax-dynamic-import') + [ + require.resolve('@babel/plugin-transform-runtime'), { + corejs: 3 + } + ], + require.resolve('@babel/plugin-proposal-object-rest-spread'), + require.resolve('@babel/plugin-syntax-dynamic-import') ]; const cssloaders = [ isDev ? { - loader: 'vue-style-loader', + loader: require.resolve('vue-style-loader'), options: { sourceMap: false, shadowMode: false @@ -66,17 +68,19 @@ module.exports = function webpackConfig(configs, webpack, mode) { } }, { - loader: 'css-loader', + loader: require.resolve('css-loader'), options: { sourceMap: false, importLoaders: 2 } }, { - loader: 'postcss-loader', + loader: require.resolve('postcss-loader'), options: { - config: { - path: path.resolve(configs.folders.CLI_DIR, 'build/configs/postcss.config.js') + postcssOptions: { + plugins: [ + autoprefixer({ browsers }) + ] }, sourceMap: false } @@ -85,22 +89,18 @@ module.exports = function webpackConfig(configs, webpack, mode) { const baseConfig = { - mode: isDev ? 'development' : 'production', context: path.resolve(configs.folders.PROJECT_DIR), entry: { app: [ - path.resolve(configs.folders.CLI_DIR, './node_modules/babel-polyfill'), - // path.resolve(configs.folders.CLI_DIR, './build/utils/create-nonce'), path.resolve(configs.folders.FES_DIR, './src/app.js') ] }, resolve: { extensions: ['.js', '.fes', '.vue', '.json'], - modules: nodeModulesDir, alias: { projectRoot: configs.folders.PROJECT_DIR, '@': path.resolve(configs.folders.PROJECT_DIR, 'src'), @@ -113,10 +113,6 @@ module.exports = function webpackConfig(configs, webpack, mode) { } }, - resolveLoader: { - modules: nodeModulesDir - }, - output: { globalObject: 'this', filename: isDev ? 'js/[name].js' : 'js/[name].[contenthash:8].js', @@ -134,13 +130,13 @@ module.exports = function webpackConfig(configs, webpack, mode) { test: /\.vue|fes$/, use: [ { - loader: 'cache-loader', + loader: require.resolve('cache-loader'), options: { cacheDirectory: path.resolve(configs.folders.PROJECT_DIR, 'node_modules/.cache/vue-loader') } }, { - loader: 'vue-loader', + loader: require.resolve('vue-loader'), options: { compilerOptions: { preserveWhitespace: false @@ -156,11 +152,11 @@ module.exports = function webpackConfig(configs, webpack, mode) { test: /\.(png|jpe?g|gif|webp)(\?.*)?$/, use: [ { - loader: 'url-loader', + loader: require.resolve('url-loader'), options: { limit: 4096, fallback: { - loader: 'file-loader', + loader: require.resolve('file-loader'), options: { name: isDev ? 'img/[name].[ext]' : 'img/[name].[hash:8].[ext]' } @@ -175,7 +171,7 @@ module.exports = function webpackConfig(configs, webpack, mode) { test: /\.(svg)(\?.*)?$/, use: [ { - loader: 'file-loader', + loader: require.resolve('file-loader'), options: { name: isDev ? 'img/[name].[ext]' : 'img/[name].[hash:8].[ext]' } @@ -188,11 +184,11 @@ module.exports = function webpackConfig(configs, webpack, mode) { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, use: [ { - loader: 'url-loader', + loader: require.resolve('url-loader'), options: { limit: 4096, fallback: { - loader: 'file-loader', + loader: require.resolve('file-loader'), options: { name: isDev ? 'media/[name].[ext]' : 'media/[name].[hash:8].[ext]' } @@ -207,11 +203,11 @@ module.exports = function webpackConfig(configs, webpack, mode) { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, use: [ { - loader: 'url-loader', + loader: require.resolve('url-loader'), options: { limit: 4096, fallback: { - loader: 'file-loader', + loader: require.resolve('file-loader'), options: { name: isDev ? 'fonts/[name].[ext]' : 'fonts/[name].[hash:8].[ext]' } @@ -238,7 +234,7 @@ module.exports = function webpackConfig(configs, webpack, mode) { test: /\.scss$/, use: cssloaders.concat([ { - loader: 'sass-loader', + loader: require.resolve('sass-loader'), options: { sourceMap: false } @@ -251,7 +247,7 @@ module.exports = function webpackConfig(configs, webpack, mode) { test: /\.sass$/, use: cssloaders.concat([ { - loader: 'sass-loader', + loader: require.resolve('sass-loader'), options: { sourceMap: false, indentedSyntax: true @@ -265,7 +261,7 @@ module.exports = function webpackConfig(configs, webpack, mode) { test: /\.less$/, use: cssloaders.concat([ { - loader: 'less-loader', + loader: require.resolve('less-loader'), options: { sourceMap: false, javascriptEnabled: true @@ -279,7 +275,7 @@ module.exports = function webpackConfig(configs, webpack, mode) { test: /\.styl(us)?$/, use: cssloaders.concat([ { - loader: 'stylus-loader', + loader: require.resolve('stylus-loader'), options: { sourceMap: false, preferPathResolver: 'webpack' @@ -291,18 +287,19 @@ module.exports = function webpackConfig(configs, webpack, mode) { /* config.module.rule('js') */ { test: /\.m?jsx?$/, + exclude: /(node_modules|bower_components)/, use: [ { - loader: 'cache-loader', + loader: require.resolve('cache-loader'), options: { cacheDirectory: path.resolve(configs.folders.PROJECT_DIR, 'node_modules/.cache/babel-loader') } }, { - loader: 'thread-loader' + loader: require.resolve('thread-loader') }, { - loader: 'babel-loader', + loader: require.resolve('babel-loader'), options: { presets, plugins @@ -402,6 +399,14 @@ module.exports = function webpackConfig(configs, webpack, mode) { /* config.plugin('friendly-errors') */ new FriendlyErrorsPlugin(), + isBuild && gzipCompress && new CompressionWebpackPlugin({ // gzip 压缩 + filename: '[path][base].gz', + test: /\.js$|\.html$|\.css/, + threshold: 10240, + minRatio: 0.8, + ...gzipCompress + }), + /* config.plugin('index.html') */ new HtmlPlugin({ template, diff --git a/packages/fes-cli/build/helpers/browser.js b/packages/fes-cli/build/helpers/browser.js index 5504a544..6f13c0b9 100644 --- a/packages/fes-cli/build/helpers/browser.js +++ b/packages/fes-cli/build/helpers/browser.js @@ -1 +1,8 @@ -module.exports = ['>1%', 'last 2 versions', 'safari >= 7', 'ie >= 9']; +module.exports = [ + 'Chrome >= 46', + 'Firefox >= 45', + 'Safari >= 10', + 'Edge >= 13', + 'iOS >= 10', + 'Electron >= 0.36' +]; diff --git a/packages/fes-cli/build/helpers/config.js b/packages/fes-cli/build/helpers/config.js index 9eb00788..26d75a91 100644 --- a/packages/fes-cli/build/helpers/config.js +++ b/packages/fes-cli/build/helpers/config.js @@ -44,6 +44,8 @@ function generateConfig(command, env) { const fesCofig = require(path.join(config.folders.PROJECT_DIR, 'fes.config.js')); config.CDN = fesCofig.env[config.env].cdn; config.needCDN = !!config.CDN; + config.compress = fesCofig.compress; + config.lazyRouter = fesCofig.lazyRouter; } catch (e) { config.needCDN = false; } diff --git a/packages/fes-cli/build/helpers/createDevServer.js b/packages/fes-cli/build/helpers/createDevServer.js index 4bf01f8f..e193ee4a 100644 --- a/packages/fes-cli/build/helpers/createDevServer.js +++ b/packages/fes-cli/build/helpers/createDevServer.js @@ -3,13 +3,15 @@ const const webpack = require('webpack'); const express = require('express'); const opn = require('opn'); +const path = require('path'); const webpackHotMiddleware = require('webpack-hot-middleware'); const webpackDevMiddleware = require('webpack-dev-middleware'); const initMock = require('../mock/init.js'); module.exports = function createDevServer(port, defaultConfig) { - defaultConfig.entry.app.unshift('webpack-hot-middleware/client?reload=true'); + const hotMiddlewarePath = require.resolve('webpack-hot-middleware'); + defaultConfig.entry.app.unshift(`${hotMiddlewarePath.replace(path.basename(hotMiddlewarePath), '')}client?reload=true`); defaultConfig.plugins.push(new webpack.HotModuleReplacementPlugin()); defaultConfig.plugins.push(new webpack.NamedModulesPlugin()); @@ -19,6 +21,7 @@ module.exports = function createDevServer(port, defaultConfig) { // devServer 自带支持,添加自定义插件。 app.use(webpackDevMiddleware(compiler, { lazy: false, + logLevel: 'silent', watchOptions: { aggregateTimeout: 300, poll: 1000 @@ -31,7 +34,9 @@ module.exports = function createDevServer(port, defaultConfig) { publicPath: defaultConfig.output.publicPath })); - app.use(webpackHotMiddleware(compiler)); + app.use(webpackHotMiddleware(compiler, { + log: false + })); app.use('/static', express.static('src/static')); diff --git a/packages/fes-cli/build/mock/init.js b/packages/fes-cli/build/mock/init.js index 1b9b3747..664ddbb4 100644 --- a/packages/fes-cli/build/mock/init.js +++ b/packages/fes-cli/build/mock/init.js @@ -27,7 +27,6 @@ const main = { app.use(cookieParser()); this.customRoute(); - this.defaultRoute(); }, customRoute() { @@ -92,20 +91,6 @@ const main = { log.message('[INFO] mock.js 发生变化'); loadRouteConfig(); }); - }, - - defaultRoute() { - const app = this.app; - - setTimeout(() => { - app.use((err, req, res) => { - res.json({ - status: err.status || 500, - message: err.message, - err - }); - }); - }); } }; diff --git a/packages/fes-cli/build/tasks/route.js b/packages/fes-cli/build/tasks/route.js index bb77c71c..9543b7d9 100644 --- a/packages/fes-cli/build/tasks/route.js +++ b/packages/fes-cli/build/tasks/route.js @@ -17,17 +17,30 @@ export default {{routes}}; const routes = getRoute(config.folders.PROJECT_PAGE_DIR, config.folders.PROJECT_PAGE_DIR); const componentsTemplate = []; - routes.components.forEach((item) => { - componentsTemplate.push(render(IMPORT_TEMPLATE, { - name: item.name, - path: item.path - })); - }); + let template = ''; + if (config.lazyRouter) { + const componentsObj = {}; + routes.components.forEach((item) => { + componentsObj[item.name] = item.path; + }); + // component: () => import( /* webpackChunkName: "home" */ '../views/Home.vue') + template = render(MAIN_TEMPLATE, { + include: '', + routes: JSON.stringify(routes.newRoutes).replace(/"component":"(.+?)"/g, ($0, $1) => `"component": () => import( /* webpackChunkName: "${$1}" */ '${componentsObj[$1]}')`) + }); + } else { + routes.components.forEach((item) => { + componentsTemplate.push(render(IMPORT_TEMPLATE, { + name: item.name, + path: item.path + })); + }); - const template = render(MAIN_TEMPLATE, { - include: componentsTemplate.join(endOfLine), - routes: JSON.stringify(routes.newRoutes).replace(/"component":"(.+?)"/g, '"component": $1') - }); + template = render(MAIN_TEMPLATE, { + include: componentsTemplate.join(endOfLine), + routes: JSON.stringify(routes.newRoutes).replace(/"component":"(.+?)"/g, '"component": $1') + }); + } fs.outputFileSync(OUTPUT_PATH, template); } diff --git a/packages/fes-cli/package.json b/packages/fes-cli/package.json index 52a5e07f..7dc9228f 100644 --- a/packages/fes-cli/package.json +++ b/packages/fes-cli/package.json @@ -32,17 +32,19 @@ "@babel/plugin-transform-runtime": "^7.6.2", "@babel/preset-env": "^7.6.3", "@babel/runtime": "^7.7.2", + "@babel/runtime-corejs3": "^7.11.2", "@intervolga/optimize-cssnano-plugin": "^1.0.6", + "@soda/friendly-errors-webpack-plugin": "^1.7.1", "autoprefixer": "^8.1.0", "babel-loader": "^8.0.6", - "babel-polyfill": "^6.26.0", "body-parser": "^1.5.2", "cache-loader": "^4.1.0", "case-sensitive-paths-webpack-plugin": "^2.2.0", - "chalk": "^1.1.1", + "chalk": "^4.1.0", "chokidar": "^1.7.0", "clean-webpack-plugin": "^3.0.0", "commander": "^4.1.0", + "compression-webpack-plugin": "^6.0.0", "cookie-parser": "^1.4.3", "copy-webpack-plugin": "^5.0.4", "cross-spawn": "^2.1.0", @@ -59,27 +61,31 @@ "hash-sum": "^2.0.0", "html-webpack-plugin": "^3.2.0", "http-proxy": "^1.12.0", - "is-ci": "^1.0.10", "json-templater": "^1.2.0", + "less": "^3.12.2", + "less-loader": "^7.0.1", "lodash": "^4.17.4", "mini-css-extract-plugin": "^0.8.0", "mockjs": "^1.1.0", "morgan": "^1.2.2", "node-plus-string": "^1.0.1", - "node-sass": "^4.13.0", + "node-sass": "^4.14.1", "normalize-path": "^1.0.0", "on-finished": "^2.3.0", "opn": "^4.0.2", "path": "^0.12.7", - "postcss-loader": "^3.0.0", + "postcss": "^7.0.32", + "postcss-loader": "^4.0.1", "prompts": "^2.3.0", "request": "^2.81.0", "require-dir": "^0.3.0", - "sass-loader": "^8.0.0", + "sass-loader": "^10.0.2", "shelljs": "^0.5.3", "string-replace-loader": "^2.2.0", "strip-indent": "^2.0.0", "style-loader": "^1.0.0", + "stylus": "^0.54.8", + "stylus-loader": "^3.0.2", "tar": "^6.0.5", "tar-fs": "^1.16.0", "terser-webpack-plugin": "^2.2.1", @@ -91,7 +97,6 @@ "webpack": "^4.41.2", "webpack-cli": "^3.3.9", "webpack-dev-middleware": "^3.7.2", - "webpack-dev-server": "^3.9.0", "webpack-hot-middleware": "^2.25.0", "webpack-merge": "^4.2.2", "yargs": "^3.31.0"