From 064e290ec6c2cb04897fcc232ba26cf9b289564c Mon Sep 17 00:00:00 2001 From: ClarkXia Date: Wed, 13 Jul 2022 19:34:48 +0800 Subject: [PATCH] feat: optimize log (#344) * fix: optimize webpack log * fix: merge error * chore: remove log * chore: optimize log * chore: optimize code --- packages/bundles/package.json | 1 + packages/bundles/scripts/tasks.ts | 2 +- packages/ice/src/commands/build.ts | 10 ++++- packages/ice/src/commands/start.ts | 14 ++++-- packages/ice/src/createService.ts | 30 +++++++++++-- packages/ice/src/service/webpackCompiler.ts | 44 ++++++++++-------- packages/ice/src/tasks/web/index.ts | 2 + packages/ice/src/utils/createSpinner.ts | 16 +++++++ packages/ice/src/utils/generateHTML.ts | 23 +--------- packages/ice/src/utils/getRoutePaths.ts | 23 ++++++++++ packages/types/src/config.ts | 2 + packages/webpack-config/src/index.ts | 50 ++++++++++++++++++--- pnpm-lock.yaml | 50 +++++++++++++++++++-- 13 files changed, 207 insertions(+), 60 deletions(-) create mode 100644 packages/ice/src/utils/createSpinner.ts create mode 100644 packages/ice/src/utils/getRoutePaths.ts diff --git a/packages/bundles/package.json b/packages/bundles/package.json index 51fe8559d..82e731d6c 100644 --- a/packages/bundles/package.json +++ b/packages/bundles/package.json @@ -42,6 +42,7 @@ "less-loader": "10.2.0", "lodash": "4.17.21", "mini-css-extract-plugin": "2.6.0", + "ora": "5.4.1", "postcss-loader": "6.2.1", "postcss-modules": "4.3.1", "postcss-nested": "5.0.6", diff --git a/packages/bundles/scripts/tasks.ts b/packages/bundles/scripts/tasks.ts index 6b7dff679..b0de7a0f8 100644 --- a/packages/bundles/scripts/tasks.ts +++ b/packages/bundles/scripts/tasks.ts @@ -45,7 +45,7 @@ const tasks = [ 'less-loader', 'postcss-loader', 'sass-loader', 'css-loader', 'postcss-preset-env', 'postcss-nested', 'postcss-modules', 'postcss-plugin-rpx2vw', 'webpack-bundle-analyzer', 'es-module-lexer', 'terser', - 'eslint-webpack-plugin', 'copy-webpack-plugin', 'cacache', + 'eslint-webpack-plugin', 'copy-webpack-plugin', 'cacache', 'ora', ].map((pkgName) => ({ pkgName })), { // pack main package diff --git a/packages/ice/src/commands/build.ts b/packages/ice/src/commands/build.ts index 585d0f085..040d5311c 100644 --- a/packages/ice/src/commands/build.ts +++ b/packages/ice/src/commands/build.ts @@ -6,6 +6,7 @@ import type { StatsError } from 'webpack'; import type { Config } from '@ice/types'; import type { ServerCompiler } from '@ice/types/esm/plugin.js'; import webpack from '@ice/bundles/compiled/webpack/index.js'; +import type ora from '@ice/bundles/compiled/ora/index.js'; import webpackCompiler from '../service/webpackCompiler.js'; import formatWebpackMessages from '../utils/formatWebpackMessages.js'; import { RUNTIME_TMP_DIR, SERVER_ENTRY, SERVER_OUTPUT_DIR } from '../constant.js'; @@ -14,9 +15,13 @@ import emptyDir from '../utils/emptyDir.js'; const build = async ( context: Context, - taskConfigs: TaskConfig[], - serverCompiler: ServerCompiler, + options: { + taskConfigs: TaskConfig[]; + serverCompiler: ServerCompiler; + spinner: ora.Ora; + }, ) => { + const { taskConfigs, serverCompiler, spinner } = options; const { applyHook, commandArgs, command, rootDir, userConfig } = context; const webpackConfigs = taskConfigs.map(({ config }) => getWebpackConfig({ config, @@ -37,6 +42,7 @@ const build = async ( command, applyHook, serverCompiler, + spinner, }); const { ssg, ssr, server: { format } } = userConfig; // compile server bundle diff --git a/packages/ice/src/commands/start.ts b/packages/ice/src/commands/start.ts index 19b975619..663f5014c 100644 --- a/packages/ice/src/commands/start.ts +++ b/packages/ice/src/commands/start.ts @@ -8,6 +8,7 @@ import type { ExtendsPluginAPI, ServerCompiler } from '@ice/types/esm/plugin.js' import type { AppConfig, RenderMode } from '@ice/runtime'; import { getWebpackConfig } from '@ice/webpack-config'; import webpack from '@ice/bundles/compiled/webpack/index.js'; +import type ora from '@ice/bundles/compiled/ora/index.js'; import webpackCompiler from '../service/webpackCompiler.js'; import prepareURLs from '../utils/prepareURLs.js'; import createRenderMiddleware from '../middlewares/ssr/renderMiddleware.js'; @@ -20,10 +21,15 @@ const { merge } = lodash; const start = async ( context: Context, - taskConfigs: TaskConfig[], - serverCompiler: ServerCompiler, - appConfig: AppConfig, + options: { + taskConfigs: TaskConfig[]; + serverCompiler: ServerCompiler; + appConfig: AppConfig; + devPath: string; + spinner: ora.Ora; + }, ) => { + const { taskConfigs, serverCompiler, appConfig, devPath, spinner } = options; const { applyHook, commandArgs, command, rootDir, userConfig, extendsPluginAPI: { serverCompileTask } } = context; const { port, host, https = false } = commandArgs; @@ -117,6 +123,8 @@ const start = async ( command, applyHook, serverCompiler, + spinner, + devPath, }); const devServer = new WebpackDevServer(devServerConfig, compiler); devServer.startCallback(() => { diff --git a/packages/ice/src/createService.ts b/packages/ice/src/createService.ts index d9e5c515e..4dbac59ed 100644 --- a/packages/ice/src/createService.ts +++ b/packages/ice/src/createService.ts @@ -21,6 +21,8 @@ import { generateRoutesInfo } from './routes.js'; import getWebTask from './tasks/web/index.js'; import getDataLoaderTask from './tasks/web/data-loader.js'; import * as config from './config.js'; +import createSpinner from './utils/createSpinner.js'; +import getRoutePaths from './utils/getRoutePaths.js'; import { RUNTIME_TMP_DIR } from './constant.js'; import ServerCompileTask from './utils/ServerCompileTask.js'; @@ -33,6 +35,7 @@ interface CreateServiceOptions { } async function createService({ rootDir, command, commandArgs }: CreateServiceOptions) { + const buildSpinner = createSpinner('loading config...'); const templateDir = path.join(__dirname, '../templates/'); const configFile = 'ice.config.(mts|mjs|ts|js|cjs|json)'; const dataCache = new Map(); @@ -164,10 +167,29 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt return { run: async () => { - if (command === 'start') { - return await start(ctx, taskConfigs, serverCompiler, appConfig); - } else if (command === 'build') { - return await build(ctx, taskConfigs, serverCompiler); + try { + if (command === 'start') { + const routePaths = getRoutePaths(routesInfo.routes) + .sort((a, b) => + // Sort by length, shortest path first. + a.split('/').filter(Boolean).length - b.split('/').filter(Boolean).length); + return await start(ctx, { + taskConfigs, + serverCompiler, + appConfig, + devPath: (routePaths[0] || '').replace(/^\//, ''), + spinner: buildSpinner, + }); + } else if (command === 'build') { + return await build(ctx, { + taskConfigs, + serverCompiler, + spinner: buildSpinner, + }); + } + } catch (err) { + buildSpinner.stop(); + throw err; } }, }; diff --git a/packages/ice/src/service/webpackCompiler.ts b/packages/ice/src/service/webpackCompiler.ts index bbd076619..c295d9df2 100644 --- a/packages/ice/src/service/webpackCompiler.ts +++ b/packages/ice/src/service/webpackCompiler.ts @@ -1,4 +1,5 @@ import webpack from '@ice/bundles/compiled/webpack/index.js'; +import type ora from '@ice/bundles/compiled/ora/index.js'; import consola from 'consola'; import chalk from 'chalk'; import type { CommandArgs, TaskConfig } from 'build-scripts'; @@ -19,8 +20,20 @@ async function webpackCompiler(options: { rootDir: string; urls?: Urls; serverCompiler: ServerCompiler; + spinner: ora.Ora; + devPath?: string; }) { - const { taskConfigs, urls, applyHook, command, commandArgs, serverCompiler, webpackConfigs } = options; + const { + taskConfigs, + urls, + applyHook, + command, + commandArgs, + serverCompiler, + webpackConfigs, + spinner, + devPath, + } = options; await applyHook(`before.${command}.run`, { urls, commandArgs, @@ -28,6 +41,15 @@ async function webpackCompiler(options: { webpackConfigs, serverCompiler, }); + // Add default plugins for spinner + webpackConfigs[0].plugins.push((compiler: Compiler) => { + compiler.hooks.beforeCompile.tap('spinner', () => { + spinner.text = 'compiling...'; + }); + compiler.hooks.afterEmit.tap('spinner', () => { + spinner.stop(); + }); + }); let compiler: Compiler; try { // @ts-expect-error ignore error with different webpack referer @@ -50,19 +72,6 @@ async function webpackCompiler(options: { }); const messages = formatWebpackMessages(statsData); const isSuccessful = !messages.errors.length; - if (isSuccessful && !process.env.DISABLE_STATS) { - const assetsStatsOptions = { - errors: false, - warnings: false, - colors: true, - assets: true, - chunks: false, - entrypoints: false, - modules: false, - timings: false, - }; - consola.log(stats.toString(assetsStatsOptions)); - } if (messages.errors.length) { // Only keep the first error. Others are often indicative // of the same problem, but confuse the reader with noise. @@ -82,11 +91,11 @@ async function webpackCompiler(options: { let logoutMessage = '\n'; logoutMessage += chalk.green(' Starting the development server at:'); if (process.env.CLOUDIDE_ENV) { - logoutMessage += `\n - IDE server: https://${process.env.WORKSPACE_UUID}-${commandArgs.port}.${process.env.WORKSPACE_HOST}`; + logoutMessage += `\n - IDE server: https://${process.env.WORKSPACE_UUID}-${commandArgs.port}.${process.env.WORKSPACE_HOST}${devPath}`; } else { logoutMessage += `\n - - Local : ${chalk.underline.white(urls.localUrlForBrowser)} - - Network: ${chalk.underline.white(urls.lanUrlForTerminal)}`; + - Local : ${chalk.underline.white(urls.localUrlForBrowser)}${devPath} + - Network: ${chalk.underline.white(urls.lanUrlForTerminal)}${devPath}`; } consola.log(`${logoutMessage}\n`); @@ -107,7 +116,6 @@ async function webpackCompiler(options: { } if (isSuccessful) { - consola.success(`Compiled successfully in ${(statsData.children ? statsData.children[0] : statsData).time} ms`); // if compiled successfully reset first compile flag after been posted to lifecycle hooks isFirstCompile = false; } diff --git a/packages/ice/src/tasks/web/index.ts b/packages/ice/src/tasks/web/index.ts index ae7326c31..1b523cc5a 100644 --- a/packages/ice/src/tasks/web/index.ts +++ b/packages/ice/src/tasks/web/index.ts @@ -4,6 +4,7 @@ import { CACHE_DIR, RUNTIME_TMP_DIR } from '../../constant.js'; const getWebTask = ({ rootDir, command }): Config => { // basic task config of web task + const defaultLogging = command === 'start' ? 'summary' : 'summary assets'; return { mode: command === 'start' ? 'development' : 'production', sourceMap: command === 'start' ? 'cheap-module-source-map' : false, @@ -21,6 +22,7 @@ const getWebTask = ({ rootDir, command }): Config => { }, assetsManifest: true, fastRefresh: command === 'start', + logging: process.env.WEBPACK_LOGGING || defaultLogging, }; }; diff --git a/packages/ice/src/utils/createSpinner.ts b/packages/ice/src/utils/createSpinner.ts new file mode 100644 index 000000000..9972e69aa --- /dev/null +++ b/packages/ice/src/utils/createSpinner.ts @@ -0,0 +1,16 @@ +import ora from '@ice/bundles/compiled/ora/index.js'; + +export default function createSpinner( + text: string, + options: ora.Options = {}, +) { + const spinner = ora({ + text, + stream: process.stdout, + isEnabled: process.stdout.isTTY, + interval: 200, + ...options, + }); + spinner.start(); + return spinner; +} \ No newline at end of file diff --git a/packages/ice/src/utils/generateHTML.ts b/packages/ice/src/utils/generateHTML.ts index a06771c8b..067b34bb6 100644 --- a/packages/ice/src/utils/generateHTML.ts +++ b/packages/ice/src/utils/generateHTML.ts @@ -3,8 +3,8 @@ import type { Request } from 'webpack-dev-server'; import fse from 'fs-extra'; import consola from 'consola'; import type { ServerContext, RenderMode } from '@ice/runtime'; -import type { RouteObject } from 'react-router'; import { ROUTER_MANIFEST } from '../constant.js'; +import getRoutePaths from './getRoutePaths.js'; interface Options { rootDir: string; @@ -34,7 +34,7 @@ export default async function generateHTML(options: Options) { // Read the latest routes info. const routeManifest = path.join(rootDir, ROUTER_MANIFEST); const routes = JSON.parse(fse.readFileSync(routeManifest, 'utf8')); - const paths = getPaths(routes); + const paths = getRoutePaths(routes); for (let i = 0, n = paths.length; i < n; i++) { const routePath = paths[i]; @@ -61,22 +61,3 @@ export default async function generateHTML(options: Options) { await fse.writeFile(contentPath, html); } } - -/** - * get all route path - * @param routes - * @returns - */ -function getPaths(routes: RouteObject[], parentPath = ''): string[] { - let pathList = []; - - routes.forEach(route => { - if (route.children) { - pathList = pathList.concat(getPaths(route.children, route.path)); - } else { - pathList.push(path.join('/', parentPath, route.path || '')); - } - }); - - return pathList; -} diff --git a/packages/ice/src/utils/getRoutePaths.ts b/packages/ice/src/utils/getRoutePaths.ts new file mode 100644 index 000000000..fa403a536 --- /dev/null +++ b/packages/ice/src/utils/getRoutePaths.ts @@ -0,0 +1,23 @@ +import * as path from 'path'; +import type { RouteObject } from 'react-router'; + +/** + * get all route path + * @param routes + * @returns + */ +function getRoutePaths(routes: RouteObject[], parentPath = ''): string[] { + let pathList = []; + + routes.forEach(route => { + if (route.children) { + pathList = pathList.concat(getRoutePaths(route.children, route.path)); + } else { + pathList.push(path.join('/', parentPath, route.path || '')); + } + }); + + return pathList; +} + +export default getRoutePaths; diff --git a/packages/types/src/config.ts b/packages/types/src/config.ts index dff297aa2..91eddba50 100644 --- a/packages/types/src/config.ts +++ b/packages/types/src/config.ts @@ -109,4 +109,6 @@ export interface Config { fastRefresh?: boolean; basename?: string; + + logging?: string; } diff --git a/packages/webpack-config/src/index.ts b/packages/webpack-config/src/index.ts index f7f7d46f6..e52eec352 100644 --- a/packages/webpack-config/src/index.ts +++ b/packages/webpack-config/src/index.ts @@ -11,7 +11,7 @@ import TerserPlugin from '@ice/bundles/compiled/terser-webpack-plugin/index.js'; import ForkTsCheckerPlugin from '@ice/bundles/compiled/fork-ts-checker-webpack-plugin/index.js'; import ESlintPlugin from '@ice/bundles/compiled/eslint-webpack-plugin/index.js'; import CopyPlugin from '@ice/bundles/compiled/copy-webpack-plugin/index.js'; -import type { Configuration, WebpackPluginInstance } from 'webpack'; +import type { Configuration, WebpackPluginInstance, Compiler } from 'webpack'; import type webpack from 'webpack'; import type { Configuration as DevServerConfiguration } from 'webpack-dev-server'; import type { Config } from '@ice/types'; @@ -84,6 +84,7 @@ const getWebpackConfig: GetWebpackConfig = ({ rootDir, config, webpack, runtimeT concatenateModules, devServer, fastRefresh, + logging, } = config; const absoluteOutputDir = path.isAbsolute(outputDir) ? outputDir : path.join(rootDir, outputDir); const dev = mode !== 'production'; @@ -298,12 +299,47 @@ const getWebpackConfig: GetWebpackConfig = ({ rootDir, config, webpack, runtimeT webpackConfig.optimization.usedExports = false; } - if (process.env.WEBPACK_LOGGING) { - webpackConfig.infrastructureLogging = { - level: 'verbose', - debug: /FileSystemInfo/, - }; - webpackConfig.stats = 'verbose'; + if (logging) { + const infra = logging.includes('infrastructure'); + const profile = logging.includes('profile'); + const summary = logging.includes('summary'); + const assets = logging.includes('assets'); + + if (infra) { + webpackConfig.infrastructureLogging = { + level: 'verbose', + debug: /FileSystemInfo/, + }; + webpackConfig.stats = 'verbose'; + } + + if (profile || summary) { + webpackConfig.plugins!.push((compiler: Compiler) => { + compiler.hooks.done.tap('webpack-logging', (stats) => { + console.log( + stats.toString(profile ? { + colors: true, + logging: 'verbose', + } : { + preset: 'summary', + assets, + colors: true, + timings: true, + }), + ); + }); + }); + } + + if (profile) { + const ProgressPlugin = webpack.ProgressPlugin as typeof webpack.ProgressPlugin; + webpackConfig.plugins!.push( + new ProgressPlugin({ + profile: true, + }), + ); + webpackConfig.profile = true; + } } // pipe webpack by built-in functions and custom functions diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a73c95d3c..1b3f94271 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -236,6 +236,7 @@ importers: less-loader: 10.2.0 lodash: 4.17.21 mini-css-extract-plugin: 2.6.0 + ora: 5.4.1 postcss: 8.4.12 postcss-loader: 6.2.1 postcss-modules: 4.3.1 @@ -278,8 +279,9 @@ importers: fs-extra: 10.1.0 less-loader: 10.2.0_less@4.1.2+webpack@5.72.0 lodash: 4.17.21 - mini-css-extract-plugin: 2.6.0_webpack@5.72.0 - postcss-loader: 6.2.1_ophkbroklgd6jdvupnp5h6xq4i + mini-css-extract-plugin: 2.6.0_webpack@5.73.0 + ora: 5.4.1 + postcss-loader: 6.2.1_x2aj2q6umelkzhlylaf35s6ot4 postcss-modules: 4.3.1_postcss@8.4.12 postcss-nested: 5.0.6_postcss@8.4.12 postcss-plugin-rpx2vw: 1.0.0_postcss@8.4.12 @@ -7196,7 +7198,11 @@ packages: engines: {node: '>=8'} dependencies: restore-cursor: 3.1.0 - dev: false + + /cli-spinners/2.6.1: + resolution: {integrity: sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==} + engines: {node: '>=6'} + dev: true /cli-table3/0.6.2: resolution: {integrity: sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==} @@ -7239,6 +7245,11 @@ packages: dependencies: mimic-response: 1.0.1 + /clone/1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + dev: true + /clsx/1.1.1: resolution: {integrity: sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==} engines: {node: '>=6'} @@ -8195,6 +8206,12 @@ packages: dependencies: execa: 5.1.1 + /defaults/1.0.3: + resolution: {integrity: sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==} + dependencies: + clone: 1.0.4 + dev: true + /defer-to-connect/1.1.3: resolution: {integrity: sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==} @@ -11017,6 +11034,11 @@ packages: global-dirs: 3.0.0 is-path-inside: 3.0.3 + /is-interactive/1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + dev: true + /is-ip/3.1.0: resolution: {integrity: sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==} engines: {node: '>=8'} @@ -12373,6 +12395,21 @@ packages: word-wrap: 1.2.3 dev: true + /ora/5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.6.1 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + dev: true + /os-tmpdir/1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -15014,7 +15051,6 @@ packages: dependencies: onetime: 5.1.2 signal-exit: 3.0.7 - dev: false /retry/0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} @@ -17135,6 +17171,12 @@ packages: dependencies: minimalistic-assert: 1.0.1 + /wcwidth/1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + dependencies: + defaults: 1.0.3 + dev: true + /web-namespaces/1.1.4: resolution: {integrity: sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==}