diff --git a/packages/cli/src/command.js b/packages/cli/src/command.js index d1908f46ac..5c85a87964 100644 --- a/packages/cli/src/command.js +++ b/packages/cli/src/command.js @@ -1,12 +1,9 @@ import parseArgs from 'minimist' import { name, version } from '../package.json' -import { loadNuxtConfig, indent, foldLines } from './utils' +import { loadNuxtConfig } from './utils' +import { indent, foldLines, startSpaces, optionSpaces } from './formatting' import * as imports from './imports' -const startSpaces = 2 -const optionSpaces = 2 -const maxCharsPerLine = 80 - export default class NuxtCommand { constructor({ name, description, usage, options, run } = {}) { this.name = name || '' @@ -117,15 +114,14 @@ export default class NuxtCommand { const i = indent(maxOptionLength + optionSpaces - option.length) return foldLines( option + i + description, - maxCharsPerLine, startSpaces + maxOptionLength + optionSpaces * 2, startSpaces + optionSpaces ) }).join('\n') - const usage = foldLines(`Usage: nuxt ${this.usage} [options]`, maxCharsPerLine, startSpaces) - const description = foldLines(this.description, maxCharsPerLine, startSpaces) - const opts = foldLines(`Options:`, maxCharsPerLine, startSpaces) + '\n\n' + _opts + const usage = foldLines(`Usage: nuxt ${this.usage} [options]`, startSpaces) + const description = foldLines(this.description, startSpaces) + const opts = foldLines(`Options:`, startSpaces) + '\n\n' + _opts return `${usage}\n\n${description}\n\n${opts}\n\n` } diff --git a/packages/cli/src/formatting.js b/packages/cli/src/formatting.js new file mode 100644 index 0000000000..c0085ffc54 --- /dev/null +++ b/packages/cli/src/formatting.js @@ -0,0 +1,29 @@ +import wrapAnsi from 'wrap-ansi' + +export const startSpaces = 2 +export const optionSpaces = 2 + +// 80% of terminal column width +export const maxCharsPerLine = (process.stdout.columns || 100) * 80 / 100 + +export function indent(count, chr = ' ') { + return chr.repeat(count) +} + +export function indentLines(string, spaces, firstLineSpaces) { + const lines = Array.isArray(string) ? string : string.split('\n') + let s = '' + if (lines.length) { + const i0 = indent(firstLineSpaces === undefined ? spaces : firstLineSpaces) + s = i0 + lines.shift() + } + if (lines.length) { + const i = indent(spaces) + s += '\n' + lines.map(l => i + l).join('\n') + } + return s +} + +export function foldLines(string, spaces, firstLineSpaces, maxCharsPerLine) { + return indentLines(wrapAnsi(string, maxCharsPerLine, { trim: false }), spaces, firstLineSpaces) +} diff --git a/packages/cli/src/run.js b/packages/cli/src/run.js index 97976d71b6..54ae0bcb32 100644 --- a/packages/cli/src/run.js +++ b/packages/cli/src/run.js @@ -2,6 +2,34 @@ import consola from 'consola' import NuxtCommand from './command' import * as commands from './commands' import setup from './setup' +import { indent, foldLines, startSpaces, optionSpaces } from './formatting' + +async function listCommands(_commands) { + _commands = await Promise.all( + Object.keys(_commands).map((cmd) => { + return _commands[cmd]().then(m => m.default) + }) + ) + let maxLength = 0 + const commandsHelp = [] + for (const name in _commands) { + commandsHelp.push([_commands[name].usage, _commands[name].description]) + maxLength = Math.max(maxLength, _commands[name].usage.length) + } + + const _cmmds = commandsHelp.map(([cmd, description]) => { + const i = indent(maxLength + optionSpaces - cmd.length) + return foldLines( + cmd + i + description, + startSpaces + maxLength + optionSpaces * 2, + startSpaces + optionSpaces + ) + }).join('\n') + + const usage = foldLines(`Usage: nuxt [--help|-h]`, startSpaces) + const cmmds = foldLines(`Commands:`, startSpaces) + '\n\n' + _cmmds + process.stdout.write(`${usage}\n\n${cmmds}\n\n`) +} export default function run() { const defaultCommand = 'dev' @@ -18,6 +46,10 @@ export default function run() { if (cmds.has(cmd)) { process.argv.splice(2, 1) } else { + if (process.argv.includes('--help') || process.argv.includes('-h')) { + listCommands({ ...commands }).then(() => process.exit(0)) + return + } cmd = defaultCommand } diff --git a/packages/cli/src/utils.js b/packages/cli/src/utils.js index fd9e138230..ac15beb8b7 100644 --- a/packages/cli/src/utils.js +++ b/packages/cli/src/utils.js @@ -2,7 +2,6 @@ import path from 'path' import { existsSync } from 'fs' import consola from 'consola' import esm from 'esm' -import wrapAnsi from 'wrap-ansi' import defaultsDeep from 'lodash/defaultsDeep' import { getDefaultNuxtConfig } from '@nuxt/config' @@ -62,25 +61,3 @@ export async function loadNuxtConfig(argv) { return options } - -export function indent(count, chr = ' ') { - return chr.repeat(count) -} - -export function indentLines(string, spaces, firstLineSpaces) { - const lines = Array.isArray(string) ? string : string.split('\n') - let s = '' - if (lines.length) { - const i0 = indent(firstLineSpaces === undefined ? spaces : firstLineSpaces) - s = i0 + lines.shift().trim() - } - if (lines.length) { - const i = indent(spaces) - s += '\n' + lines.map(l => i + l.trim()).join('\n') - } - return s -} - -export function foldLines(string, maxCharsPerLine, spaces, firstLineSpaces) { - return indentLines(wrapAnsi(string, maxCharsPerLine, { trim: false }), spaces, firstLineSpaces) -} diff --git a/packages/cli/test/unit/__snapshots__/command.test.js.snap b/packages/cli/test/unit/__snapshots__/command.test.js.snap index be77274aa6..017fc5e908 100644 --- a/packages/cli/test/unit/__snapshots__/command.test.js.snap +++ b/packages/cli/test/unit/__snapshots__/command.test.js.snap @@ -3,8 +3,7 @@ exports[`cli/command builds help text 1`] = ` " Usage: nuxt this is how you do it [options] - a very long description that is longer than 80 chars and should wrap to the next - line while keeping indentation + a very long description that should not wrap to the next line because is not longer than the terminal width Options: @@ -16,8 +15,7 @@ exports[`cli/command builds help text 1`] = ` --port, -p Port number on which to start the application --hostname, -H Hostname on which to start the application --unix-socket, -n Path to a UNIX socket - --foo very long option that is longer than 80 chars and should wrap - to the next line while keeping indentation + --foo very long option that is not longer than the terminal width and should not wrap to the next line " `; diff --git a/packages/cli/test/unit/command.test.js b/packages/cli/test/unit/command.test.js index 07cbec1058..d3501fdf3b 100644 --- a/packages/cli/test/unit/command.test.js +++ b/packages/cli/test/unit/command.test.js @@ -101,15 +101,15 @@ describe('cli/command', () => { test('builds help text', () => { const cmd = new Command({ - description: 'a very long description that is longer than 80 chars and ' + - 'should wrap to the next line while keeping indentation', + description: 'a very long description that should not wrap to the next line because is not longer ' + + 'than the terminal width', usage: 'this is how you do it', options: { ...allOptions, foo: { type: 'boolean', - description: 'very long option that is longer than 80 chars and ' + - 'should wrap to the next line while keeping indentation' + description: 'very long option that is not longer than the terminal width and ' + + 'should not wrap to the next line' } } }) diff --git a/packages/cli/test/unit/utils.test.js b/packages/cli/test/unit/utils.test.js index 5eb385101b..5c84900818 100644 --- a/packages/cli/test/unit/utils.test.js +++ b/packages/cli/test/unit/utils.test.js @@ -1,6 +1,7 @@ import { getDefaultNuxtConfig } from '@nuxt/config' import { consola } from '../utils' import * as utils from '../../src/utils' +import * as fmt from '../../src/formatting' describe('cli/utils', () => { afterEach(() => jest.resetAllMocks()) @@ -96,10 +97,10 @@ describe('cli/utils', () => { }) test('indent', () => { - expect(utils.indent(4)).toBe(' ') + expect(fmt.indent(4)).toBe(' ') }) test('indent custom char', () => { - expect(utils.indent(4, '-')).toBe('----') + expect(fmt.indent(4, '-')).toBe('----') }) })