Skip to content

Commit

Permalink
refactor: code
Browse files Browse the repository at this point in the history
  • Loading branch information
evilebottnawi committed Nov 11, 2020
1 parent b4289c0 commit f6f4585
Show file tree
Hide file tree
Showing 18 changed files with 371 additions and 351 deletions.
4 changes: 2 additions & 2 deletions packages/webpack-cli/__tests__/arg-parser.test.js
Expand Up @@ -13,7 +13,7 @@ const processExitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {});
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});

const argParser = require('../lib/utils/arg-parser');
const { core } = require('../lib/utils/cli-flags');
const { flags } = require('../lib/utils/cli-flags');

const basicOptions = [
{
Expand Down Expand Up @@ -362,7 +362,7 @@ describe('arg-parser', () => {
});

it('parses webpack args', () => {
const res = argParser(core, ['--entry', 'test.js', '--hot', '-o', './dist/', '--stats'], true);
const res = argParser(flags, ['--entry', 'test.js', '--hot', '-o', './dist/', '--stats'], true);
expect(res.unknownArgs.length).toEqual(0);
expect(res.opts.entry).toEqual(['test.js']);
expect(res.opts.hot).toBeTruthy();
Expand Down
25 changes: 14 additions & 11 deletions packages/webpack-cli/lib/bootstrap.js
@@ -1,17 +1,23 @@
const WebpackCLI = require('./webpack-cli');
const { core } = require('./utils/cli-flags');
const { flags } = require('./utils/cli-flags');
const logger = require('./utils/logger');
const { isCommandUsed } = require('./utils/arg-utils');
const { isCommandUsed } = require('./utils/cli-flags');
const argParser = require('./utils/arg-parser');
const leven = require('leven');
const { options: coloretteOptions } = require('colorette');

process.title = 'webpack-cli';

const runCLI = async (cliArgs) => {
const parsedArgs = argParser(core, cliArgs, true, process.title);
const parsedArgs = argParser(flags, cliArgs, true, process.title);

// Enable/Disable colors
if (typeof parsedArgs.opts.color !== 'undefined') {
coloretteOptions.enabled = Boolean(parsedArgs.opts.color);
}

const commandIsUsed = isCommandUsed(cliArgs);

if (commandIsUsed) {
return;
}
Expand All @@ -24,11 +30,6 @@ const runCLI = async (cliArgs) => {
// If the unknown arg starts with a '-', it will be considered an unknown flag rather than an entry
let entry;

// enable/disable colors
if (typeof parsedArgs.opts.color !== 'undefined') {
coloretteOptions.enabled = Boolean(parsedArgs.opts.color);
}

if (parsedArgs.unknownArgs.length > 0) {
entry = [];

Expand All @@ -44,10 +45,12 @@ const runCLI = async (cliArgs) => {
}

if (parsedArgs.unknownArgs.length > 0) {
parsedArgs.unknownArgs.forEach(async (unknown) => {
parsedArgs.unknownArgs.forEach((unknown) => {
logger.error(`Unknown argument: ${unknown}`);

const strippedFlag = unknown.substr(2);
const { name: suggestion } = core.find((flag) => leven(strippedFlag, flag.name) < 3);
const { name: suggestion } = flags.find((flag) => leven(strippedFlag, flag.name) < 3);

if (suggestion) {
logger.raw(`Did you mean --${suggestion}?`);
}
Expand All @@ -62,7 +65,7 @@ const runCLI = async (cliArgs) => {
parsedArgsOpts.entry = entry;
}

await cli.run(parsedArgsOpts, core);
await cli.run(parsedArgsOpts, flags);
} catch (error) {
logger.error(error);
process.exit(2);
Expand Down
208 changes: 115 additions & 93 deletions packages/webpack-cli/lib/groups/runHelp.js
@@ -1,116 +1,138 @@
const { yellow, bold, underline, options } = require('colorette');
const { options, green, bold, underline } = require('colorette');
const commandLineUsage = require('command-line-usage');

const { core, commands } = require('../utils/cli-flags');
const { hasUnknownArgs, allNames, commands: commandNames } = require('../utils/unknown-args');
const { commands, flags } = require('../utils/cli-flags');
const logger = require('../utils/logger');

// This function prints a warning about invalid flag
const printInvalidArgWarning = (args) => {
const invalidArgs = hasUnknownArgs(args, allNames);
const outputHelp = (args) => {
if (args.includes('--color')) {
options.enabled = true;
} else if (args.includes('--no-color')) {
options.enabled = false;
}

const hasUnknownVersionArgs = (args, commands, flags) => {
return args.filter((arg) => {
if (arg === 'version' || arg === 'help' || arg === '--help' || arg === '-h' || arg === '--no-color') {
return false;
}

const foundCommand = commands.find((command) => {
return command.name === arg || command.alias === arg;
});
const foundFlag = flags.find((command) => {
return `--${command.name}` === arg || `-${command.alias}` === arg;
});

return !foundCommand && !foundFlag;
});
};

const invalidArgs = hasUnknownVersionArgs(args, commands, flags);

if (invalidArgs.length > 0) {
const argType = invalidArgs[0].startsWith('-') ? 'option' : 'command';
logger.warn(`You provided an invalid ${argType} '${invalidArgs[0]}'.`);
logger.error(`Invalid ${argType} '${invalidArgs[0]}'.`);
logger.error('Run webpack --help to see available commands and arguments.');
process.exit(2);
}
};

// This function is responsible for printing command/flag scoped help
const printSubHelp = (subject, isCommand) => {
const info = isCommand ? commands : core;
// Contains object with details about given subject
const options = info.find((commandOrFlag) => {
if (isCommand) {
return commandOrFlag.name == subject || commandOrFlag.alias == subject;
const usedCommand = commands.filter((command) => {
return args.includes(command.name) || args.includes(command.alias);
});
const usedFlag = flags.filter((flag) => {
if (flag.name === 'help' || flag.name === 'color') {
return false;
}
return commandOrFlag.name === subject.slice(2) || commandOrFlag.alias === subject.slice(1);

return args.includes(`--${flag.name}`) || args.includes(`-${flag.alias}`);
});

const header = (head) => bold(underline(head));
const flagAlias = options.alias ? (isCommand ? ` ${options.alias} |` : ` -${options.alias},`) : '';
const usage = yellow(`webpack${flagAlias} ${options.usage}`);
const description = options.description;
const link = options.link;
const usedCommandOrFlag = [].concat(usedCommand).concat(usedFlag);

logger.raw(`${header('Usage')}: ${usage}`);
logger.raw(`${header('Description')}: ${description}`);
if (usedCommandOrFlag.length > 1) {
logger.error(
`You provided multiple commands or arguments - ${usedCommandOrFlag
.map((usedFlagOrCommand) => {
const isCommand = usedFlagOrCommand.packageName;
if (link) {
logger.raw(`${header('Documentation')}: ${link}`);
return `${isCommand ? 'command ' : 'argument '}'${isCommand ? usedFlagOrCommand.name : `--${usedFlagOrCommand.name}`}'${
usedFlagOrCommand.alias ? ` (alias '${isCommand ? usedFlagOrCommand.alias : `-${usedFlagOrCommand.alias}`}')` : ''
}`;
})
.join(', ')}. Please use only one command at a time.`,
);
process.exit(2);
}

if (options.flags) {
const flags = commandLineUsage({
header: 'Options',
optionList: options.flags,
});
logger.raw(flags);
}
};
// Print full help when no flag or command is supplied with help
if (usedCommandOrFlag.length === 1) {
const [item] = usedCommandOrFlag;
const isCommand = item.packageName;
const header = (head) => bold(underline(head));
const flagAlias = item.alias ? (isCommand ? ` ${item.alias} |` : ` -${item.alias},`) : '';
const usage = green(`webpack${flagAlias} ${item.usage}`);
const description = item.description;
const link = item.link;

const printHelp = () => {
const o = (s) => yellow(s);
const options = require('../utils/cli-flags');
const negatedFlags = options.core
.filter((flag) => flag.negative)
.reduce((allFlags, flag) => {
return [...allFlags, { name: `no-${flag.name}`, description: `Negates ${flag.name}`, type: Boolean }];
}, []);
const title = bold('⬡ ') + underline('webpack') + bold(' ⬡');
const desc = 'The build tool for modern web applications';
const websitelink = ' ' + underline('https://webpack.js.org');

const usage = bold('Usage') + ': ' + '`' + o('webpack [...options] | <command>') + '`';
const examples = bold('Example') + ': ' + '`' + o('webpack help --flag | <command>') + '`';

const hh = ` ${title}\n
${websitelink}\n
${desc}\n
${usage}\n
${examples}\n
`;
return commandLineUsage([
{
content: hh,
raw: true,
},
{
header: 'Available Commands',
content: options.commands.map((cmd) => {
return { name: `${cmd.name} | ${cmd.alias}`, summary: cmd.description };
}),
},
{
header: 'Options',
optionList: options.core
.map((e) => {
if (e.type.length > 1) e.type = e.type[0];
// Here we replace special characters with chalk's escape
// syntax (`\$&`) to avoid chalk trying to re-process our input.
// This is needed because chalk supports a form of `{var}`
// interpolation.
e.description = e.description.replace(/[{}\\]/g, '\\$&');
return e;
})
.concat(negatedFlags),
},
]);
};
logger.raw(`${header('Usage')}: ${usage}`);
logger.raw(`${header('Description')}: ${description}`);

const outputHelp = (cliArgs) => {
options.enabled = !cliArgs.includes('--no-color');
printInvalidArgWarning(cliArgs);
const flagOrCommandUsed = allNames.filter((name) => {
return cliArgs.includes(name);
})[0];
const isCommand = commandNames.includes(flagOrCommandUsed);
if (link) {
logger.raw(`${header('Documentation')}: ${link}`);
}

// Print full help when no flag or command is supplied with help
if (flagOrCommandUsed) {
printSubHelp(flagOrCommandUsed, isCommand);
if (item.flags) {
const flags = commandLineUsage({
header: 'Options',
optionList: item.flags,
});
logger.raw(flags);
}
} else {
logger.raw(printHelp());
const negatedFlags = flags
.filter((flag) => flag.negative)
.reduce((allFlags, flag) => {
return [...allFlags, { name: `no-${flag.name}`, description: `Negates ${flag.name}`, type: Boolean }];
}, []);
const title = bold('⬡ ') + underline('webpack') + bold(' ⬡');
const desc = 'The build tool for modern web applications';
const websitelink = ' ' + underline('https://webpack.js.org');
const usage = bold('Usage') + ': ' + '`' + green('webpack [...options] | <command>') + '`';
const examples = bold('Example') + ': ' + '`' + green('webpack help --flag | <command>') + '`';
const hh = ` ${title}\n\n${websitelink}\n\n${desc}\n\n${usage}\n${examples}`;
const output = commandLineUsage([
{ content: hh, raw: true },
{
header: 'Available Commands',
content: commands.map((cmd) => {
return { name: `${cmd.name} | ${cmd.alias}`, summary: cmd.description };
}),
},
{
header: 'Options',
optionList: flags
.map((e) => {
if (e.type.length > 1) {
e.type = e.type[0];
}

// Here we replace special characters with chalk's escape
// syntax (`\$&`) to avoid chalk trying to re-process our input.
// This is needed because chalk supports a form of `{var}`
// interpolation.
e.description = e.description.replace(/[{}\\]/g, '\\$&');

return e;
})
.concat(negatedFlags),
},
]);

logger.raw(output);
}
logger.raw('\n Made with ♥️ by the webpack team');

logger.raw(' Made with ♥️ by the webpack team');
};

module.exports = outputHelp;

0 comments on commit f6f4585

Please sign in to comment.