Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 73 additions & 21 deletions packages/webpack-cli/src/webpack-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from "node:fs";
import path from "node:path";
import { type Readable as ReadableType } from "node:stream";
import { pathToFileURL } from "node:url";
import util from "node:util";
import util, { stripVTControlCharacters } from "node:util";
import { type stringifyChunked as stringifyChunkedType } from "@discoveryjs/json-ext";
import {
type Argument,
Expand Down Expand Up @@ -1127,7 +1127,7 @@ class WebpackCLI {
value === "--version" ||
value === "-h" ||
value === "--help";
const { bold } = this.colors;
const { bold, dim, underline, yellow } = this.colors;
const outputIncorrectUsageOfHelp = () => {
this.logger.error("Incorrect use of help");
this.logger.error(
Expand Down Expand Up @@ -1156,9 +1156,7 @@ class WebpackCLI {
}

if (isGlobalHelp) {
return `${parentCmdNames}${command.usage()}\n${bold(
"Alternative usage to run commands:",
)} ${parentCmdNames}[command] [options]`;
return `${parentCmdNames}${command.usage()}`;
}

return `${parentCmdNames}${command.name()}|${command
Expand Down Expand Up @@ -1211,23 +1209,65 @@ class WebpackCLI {
);
},
formatHelp: (command, helper: Help) => {
const formatItem = (term: string, description: string) => {
const formatTabularItem = (term: string, description: string, padWidth: number) => {
const formattedTerm = bold(term);

if (description) {
return helper.formatItem(term, helper.padWidth(command, helper), description, helper);
return helper.formatItem(formattedTerm, padWidth, description, helper);
}

return term;
return formattedTerm;
};

const columns = process.stdout.columns || 80;
const centerBannerLine = (value: string) => {
const visibleWidth = stripVTControlCharacters(value).length;
const leftPadding = Math.max(0, Math.floor((columns - visibleWidth) / 2));

return `${" ".repeat(leftPadding)}${value}`;
};

const formatList = (textArray: string[]) => textArray.join("\n").replaceAll(/^/gm, "");
const formatSection = (title: string, textArray: string[]) => {
if (textArray.length === 0) {
return "";
}

return ["", underline(bold(title)), "", formatList(textArray), "", ""];
};

// Usage
let output = [`${bold("Usage:")} ${helper.commandUsage(command)}`, ""];
const padWidth = helper.padWidth(command, helper);
let output: string[] = [];

const bannerTitleText = `${dim("○")} ${underline(
dim("webpack"),
)} ${dim("○")}`;
const bannerTitle = centerBannerLine(bannerTitleText);
const bannerLinkText = underline(dim("https://webpack.js.org"));
const bannerLink = centerBannerLine(bannerLinkText);
const descriptionLine = centerBannerLine("The build tool for modern web applications");
const usageLine = `${bold("Usage:")} ${yellow(helper.commandUsage(command))}`;
const exampleLine = `${bold("Example:")} ${yellow("webpack help --flag | <command>")}`;

output = isGlobalHelp
? [
"",
bannerTitle,
"",
bannerLink,
"",
descriptionLine,
"",
centerBannerLine(usageLine),
"",
centerBannerLine(exampleLine),
"",
"",
]
: ["", bannerTitle, "", bannerLink, "", centerBannerLine(usageLine), "", ""];

// Description
const commandDescription = isGlobalHelp
? "The build tool for modern web applications."
: helper.commandDescription(command);
const commandDescription = isGlobalHelp ? "" : helper.commandDescription(command);

if (commandDescription.length > 0) {
output = [...output, commandDescription, ""];
Expand All @@ -1236,41 +1276,53 @@ class WebpackCLI {
// Arguments
const argumentList = helper
.visibleArguments(command)
.map((argument) => formatItem(argument.name(), argument.description));
.map((argument) => formatTabularItem(argument.name(), argument.description, padWidth));

if (argumentList.length > 0) {
output = [...output, bold("Arguments:"), formatList(argumentList), ""];
output = [...output, ...formatSection("Arguments:", argumentList)];
}

// Options
const optionList = helper
.visibleOptions(command)
.map((option) =>
formatItem(helper.optionTerm(option), helper.optionDescription(option)),
formatTabularItem(
helper.optionTerm(option),
helper.optionDescription(option),
padWidth,
),
);

if (optionList.length > 0) {
output = [...output, bold("Options:"), formatList(optionList), ""];
output = [...output, ...formatSection("Options:", optionList)];
}

// Global options
const globalOptionList = program.options.map((option) =>
formatItem(helper.optionTerm(option), helper.optionDescription(option)),
formatTabularItem(
helper.optionTerm(option),
helper.optionDescription(option),
padWidth,
),
);

if (globalOptionList.length > 0) {
output = [...output, bold("Global options:"), formatList(globalOptionList), ""];
output = [...output, ...formatSection("Global options:", globalOptionList)];
}

// Commands
const commandList = helper
.visibleCommands(isGlobalHelp ? program : command)
.map((command) =>
formatItem(helper.subcommandTerm(command), helper.subcommandDescription(command)),
formatTabularItem(
helper.subcommandTerm(command),
helper.subcommandDescription(command),
padWidth,
),
);

if (commandList.length > 0) {
output = [...output, bold("Commands:"), formatList(commandList), ""];
output = [...output, ...formatSection("Commands:", commandList)];
}

return output.join("\n");
Expand Down
Loading