feat: optimize log (#344)

* fix: optimize webpack log

* fix: merge error

* chore: remove log

* chore: optimize log

* chore: optimize code
This commit is contained in:
ClarkXia 2022-07-13 19:34:48 +08:00
parent 64676f49f3
commit 064e290ec6
13 changed files with 207 additions and 60 deletions

View File

@ -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",

View File

@ -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

View File

@ -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<Config>,
taskConfigs: TaskConfig<Config>[],
serverCompiler: ServerCompiler,
options: {
taskConfigs: TaskConfig<Config>[];
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

View File

@ -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<Config, ExtendsPluginAPI>,
taskConfigs: TaskConfig<Config>[],
serverCompiler: ServerCompiler,
appConfig: AppConfig,
options: {
taskConfigs: TaskConfig<Config>[];
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(() => {

View File

@ -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<string, string>();
@ -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;
}
},
};

View File

@ -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;
}

View File

@ -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,
};
};

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -109,4 +109,6 @@ export interface Config {
fastRefresh?: boolean;
basename?: string;
logging?: string;
}

View File

@ -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

View File

@ -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==}