From 4503d42d5423ef544dae8272ca6d177b10524f5e Mon Sep 17 00:00:00 2001 From: Jonas Galvez Date: Mon, 29 Oct 2018 19:16:16 -0300 Subject: [PATCH] refactor(cli): cleanups and improvements (#4222) --- packages/cli/src/command.js | 78 ++++++------ packages/cli/src/commands/build.js | 113 +++++++++++------- packages/cli/src/commands/dev.js | 107 +++++++++-------- packages/cli/src/commands/generate.js | 45 ++++--- packages/cli/src/commands/start.js | 77 ++++++------ packages/cli/src/index.js | 2 +- packages/cli/src/options.js | 101 ---------------- packages/cli/src/options/common.js | 27 +++++ packages/cli/src/options/index.js | 2 + packages/cli/src/options/server.js | 29 +++++ packages/cli/src/run.js | 5 +- packages/cli/src/utils.js | 4 +- .../unit/__snapshots__/command.test.js.snap | 23 ++++ packages/cli/test/unit/build.test.js | 25 ++-- packages/cli/test/unit/cli.test.js | 10 +- packages/cli/test/unit/command.test.js | 66 ++++------ packages/cli/test/unit/dev.test.js | 23 ++-- packages/cli/test/unit/generate.test.js | 25 ++-- packages/cli/test/unit/start.test.js | 20 ++-- packages/cli/test/unit/utils.test.js | 4 +- packages/cli/test/utils/index.js | 1 + 21 files changed, 391 insertions(+), 396 deletions(-) delete mode 100644 packages/cli/src/options.js create mode 100644 packages/cli/src/options/common.js create mode 100644 packages/cli/src/options/index.js create mode 100644 packages/cli/src/options/server.js create mode 100644 packages/cli/test/unit/__snapshots__/command.test.js.snap diff --git a/packages/cli/src/command.js b/packages/cli/src/command.js index fbc9552871..d1908f46ac 100644 --- a/packages/cli/src/command.js +++ b/packages/cli/src/command.js @@ -1,19 +1,26 @@ import parseArgs from 'minimist' -import wrapAnsi from 'wrap-ansi' import { name, version } from '../package.json' -import { loadNuxtConfig, indent, indentLines, foldLines } from './utils' -import { options as Options, defaultOptions as DefaultOptions } from './options' +import { loadNuxtConfig, indent, foldLines } from './utils' import * as imports from './imports' -const startSpaces = 6 +const startSpaces = 2 const optionSpaces = 2 const maxCharsPerLine = 80 export default class NuxtCommand { - constructor({ description, usage, options } = {}) { + constructor({ name, description, usage, options, run } = {}) { + this.name = name || '' this.description = description || '' this.usage = usage || '' - this.options = Array.from(new Set((options || []).concat(DefaultOptions))) + this.options = Object.assign({}, options) + this._run = run + } + + static from(options) { + if (options instanceof NuxtCommand) { + return options + } + return new NuxtCommand(options) } _getMinimistOptions() { @@ -24,8 +31,8 @@ export default class NuxtCommand { default: {} } - for (const name of this.options) { - const option = Options[name] + for (const name of Object.keys(this.options)) { + const option = this.options[name] if (option.alias) { minimistOptions.alias[option.alias] = name @@ -54,13 +61,17 @@ export default class NuxtCommand { return argv } + run() { + return this._run(this) + } + async getNuxtConfig(argv, extraOptions) { const config = await loadNuxtConfig(argv) const options = Object.assign(config, extraOptions || {}) - for (const name of this.options) { - if (Options[name].handle) { - Options[name].handle(options, argv) + for (const name of Object.keys(this.options)) { + if (this.options[name].prepare) { + this.options[name].prepare(this, options, argv) } } @@ -86,38 +97,37 @@ export default class NuxtCommand { _getHelp() { const options = [] - let maxOptionLength = 0 - // For consistency Options determines order - for (const name in Options) { - const option = Options[name] - if (this.options.includes(name)) { - let optionHelp = '--' - optionHelp += option.type === 'boolean' && option.default ? 'no-' : '' - optionHelp += name - if (option.alias) { - optionHelp += `, -${option.alias}` - } - maxOptionLength = Math.max(maxOptionLength, optionHelp.length) - options.push([ optionHelp, option.description ]) + for (const name in this.options) { + const option = this.options[name] + + let optionHelp = '--' + optionHelp += option.type === 'boolean' && option.default ? 'no-' : '' + optionHelp += name + if (option.alias) { + optionHelp += `, -${option.alias}` } + + maxOptionLength = Math.max(maxOptionLength, optionHelp.length) + options.push([ optionHelp, option.description ]) } - const optionStr = options.map(([option, description]) => { - const line = option + - indent(maxOptionLength + optionSpaces - option.length) + - wrapAnsi(description, maxCharsPerLine - startSpaces - maxOptionLength - optionSpaces) - return indentLines(line, startSpaces + maxOptionLength + optionSpaces, startSpaces) + const _opts = options.map(([option, description]) => { + 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 - return ` - Description\n${description} - Usage - $ nuxt ${this.usage} - Options\n${optionStr}\n\n` + return `${usage}\n\n${description}\n\n${opts}\n\n` } showVersion() { diff --git a/packages/cli/src/commands/build.js b/packages/cli/src/commands/build.js index 2868b81ab8..c55e45b0a0 100644 --- a/packages/cli/src/commands/build.js +++ b/packages/cli/src/commands/build.js @@ -1,46 +1,77 @@ import consola from 'consola' -import NuxtCommand from '../command' +import { common } from '../options' -export default async function build() { - const nuxtCmd = new NuxtCommand({ - description: 'Compiles the application for production deployment', - usage: 'build ', - options: [ 'analyze', 'quiet' ] - }) - - const argv = nuxtCmd.getArgv() - - // Create production build when calling `nuxt build` (dev: false) - const nuxt = await nuxtCmd.getNuxt( - await nuxtCmd.getNuxtConfig(argv, { dev: false }) - ) - - // Setup hooks - nuxt.hook('error', err => consola.fatal(err)) - - let builderOrGenerator - if (nuxt.options.mode !== 'spa' || argv.generate === false) { - // Build only - builderOrGenerator = (await nuxtCmd.getBuilder(nuxt)).build() - } else { - // Build + Generate for static deployment - builderOrGenerator = (await nuxtCmd.getGenerator(nuxt)).generate({ - build: true - }) - } - - return builderOrGenerator - .then(() => { - // In analyze mode wait for plugin - // emitting assets and opening browser - if ( - nuxt.options.build.analyze === true || - typeof nuxt.options.build.analyze === 'object' - ) { - return +export default { + name: 'build', + description: 'Compiles the application for production deployment', + usage: 'build ', + options: { + ...common, + analyze: { + alias: 'a', + type: 'boolean', + description: 'Launch webpack-bundle-analyzer to optimize your bundles', + prepare(cmd, options, argv) { + // Analyze option + options.build = options.build || {} + if (argv.analyze && typeof options.build.analyze !== 'object') { + options.build.analyze = true + } } + }, + generate: { + type: 'boolean', + default: true, + description: 'Don\'t generate static version for SPA mode (useful for nuxt start)' + }, + quiet: { + alias: 'q', + type: 'boolean', + description: 'Disable output except for errors', + handle(options, argv) { + // Silence output when using --quiet + options.build = options.build || {} + if (argv.quiet) { + options.build.quiet = !!argv.quiet + } + } + } + }, + async run(nuxtCmd) { + const argv = nuxtCmd.getArgv() - process.exit(0) - }) - .catch(err => consola.fatal(err)) + // Create production build when calling `nuxt build` (dev: false) + const nuxt = await nuxtCmd.getNuxt( + await nuxtCmd.getNuxtConfig(argv, { dev: false }) + ) + + // Setup hooks + nuxt.hook('error', err => consola.fatal(err)) + + let builderOrGenerator + if (nuxt.options.mode !== 'spa' || argv.generate === false) { + // Build only + builderOrGenerator = (await nuxtCmd.getBuilder(nuxt)).build() + } else { + // Build + Generate for static deployment + builderOrGenerator = (await nuxtCmd.getGenerator(nuxt)).generate({ + build: true + }) + } + + return builderOrGenerator + .then(() => { + // In analyze mode wait for plugin + // emitting assets and opening browser + if ( + nuxt.options.build.analyze === true || + typeof nuxt.options.build.analyze === 'object' + ) { + return + } + + process.exit(0) + }) + .catch(err => consola.fatal(err)) + } } diff --git a/packages/cli/src/commands/dev.js b/packages/cli/src/commands/dev.js index d6e0f23772..c6c81594d9 100644 --- a/packages/cli/src/commands/dev.js +++ b/packages/cli/src/commands/dev.js @@ -1,59 +1,62 @@ import consola from 'consola' -import NuxtCommand from '../command' +import { common, server } from '../options' -export default async function dev() { - const nuxtCmd = new NuxtCommand({ - description: 'Start the application in development mode (e.g. hot-code reloading, error reporting)', - usage: 'dev -p -H ', - options: [ 'hostname', 'port' ] - }) +export default { + name: 'dev', + description: 'Start the application in development mode (e.g. hot-code reloading, error reporting)', + usage: 'dev ', + options: { + ...common, + ...server + }, + async run(cmd) { + const argv = cmd.getArgv() - const argv = nuxtCmd.getArgv() - - const errorHandler = (err, instance) => { - instance && instance.builder.watchServer() - consola.error(err) - } - - // Start dev - async function startDev(oldInstance) { - let nuxt, builder - - try { - nuxt = await nuxtCmd.getNuxt( - await nuxtCmd.getNuxtConfig(argv, { dev: true }) - ) - builder = await nuxtCmd.getBuilder(nuxt) - nuxt.hook('watch:fileChanged', async (builder, fname) => { - consola.debug(`[${fname}] changed, Rebuilding the app...`) - await startDev({ nuxt: builder.nuxt, builder }) - }) - } catch (err) { - return errorHandler(err, oldInstance) + const errorHandler = (err, instance) => { + instance && instance.builder.watchServer() + consola.error(err) } - return ( - Promise.resolve() - .then(() => oldInstance && oldInstance.nuxt.clearHook('watch:fileChanged')) - .then(() => oldInstance && oldInstance.builder.unwatch()) - // Start build - .then(() => builder.build()) - // Close old nuxt no matter if build successfully - .catch((err) => { - oldInstance && oldInstance.nuxt.close() - // Jump to errorHandler - throw err - }) - .then(() => oldInstance && oldInstance.nuxt.close()) - // Start listening - .then(() => nuxt.listen()) - // Show ready message first time, others will be shown through WebpackBar - .then(() => !oldInstance && nuxt.showReady(false)) - .then(() => builder.watchServer()) - // Handle errors - .catch(err => errorHandler(err, { builder, nuxt })) - ) - } + // Start dev + async function startDev(oldInstance) { + let nuxt, builder - await startDev() + try { + nuxt = await cmd.getNuxt( + await cmd.getNuxtConfig(argv, { dev: true }) + ) + builder = await cmd.getBuilder(nuxt) + nuxt.hook('watch:fileChanged', async (builder, fname) => { + consola.debug(`[${fname}] changed, Rebuilding the app...`) + await startDev({ nuxt: builder.nuxt, builder }) + }) + } catch (err) { + return errorHandler(err, oldInstance) + } + + return ( + Promise.resolve() + .then(() => oldInstance && oldInstance.nuxt.clearHook('watch:fileChanged')) + .then(() => oldInstance && oldInstance.builder.unwatch()) + // Start build + .then(() => builder.build()) + // Close old nuxt no matter if build successfully + .catch((err) => { + oldInstance && oldInstance.nuxt.close() + // Jump to errorHandler + throw err + }) + .then(() => oldInstance && oldInstance.nuxt.close()) + // Start listening + .then(() => nuxt.listen()) + // Show ready message first time, others will be shown through WebpackBar + .then(() => !oldInstance && nuxt.showReady(false)) + .then(() => builder.watchServer()) + // Handle errors + .catch(err => errorHandler(err, { builder, nuxt })) + ) + } + + await startDev() + } } diff --git a/packages/cli/src/commands/generate.js b/packages/cli/src/commands/generate.js index a2d79697ed..c2e406f96c 100644 --- a/packages/cli/src/commands/generate.js +++ b/packages/cli/src/commands/generate.js @@ -1,25 +1,32 @@ import consola from 'consola' -import NuxtCommand from '../command' +import { common } from '../options' -export default async function generate() { - const nuxtCmd = new NuxtCommand({ - description: 'Generate a static web application (server-rendered)', - usage: 'generate ', - options: [ 'build' ] - }) +export default { + name: 'generate', + description: 'Generate a static web application (server-rendered)', + usage: 'generate ', + options: { + ...common, + build: { + type: 'boolean', + default: true, + description: 'Only generate pages for dynamic routes. Nuxt has to be built once before using this option' + } + }, + async run(cmd) { + const argv = cmd.getArgv() - const argv = nuxtCmd.getArgv() - - const generator = await nuxtCmd.getGenerator( - await nuxtCmd.getNuxt( - await nuxtCmd.getNuxtConfig(argv, { dev: false }) + const generator = await cmd.getGenerator( + await cmd.getNuxt( + await cmd.getNuxtConfig(argv, { dev: false }) + ) ) - ) - return generator.generate({ - init: true, - build: argv.build - }).then(() => { - process.exit(0) - }).catch(err => consola.fatal(err)) + return generator.generate({ + init: true, + build: argv.build + }).then(() => { + process.exit(0) + }).catch(err => consola.fatal(err)) + } } diff --git a/packages/cli/src/commands/start.js b/packages/cli/src/commands/start.js index 2498a06a87..e08e08ffcc 100644 --- a/packages/cli/src/commands/start.js +++ b/packages/cli/src/commands/start.js @@ -1,49 +1,52 @@ import fs from 'fs' import path from 'path' import consola from 'consola' -import NuxtCommand from '../command' +import { common, server } from '../options' -export default async function start() { - const nuxtCmd = new NuxtCommand({ - description: 'Start the application in production mode (the application should be compiled with `nuxt build` first)', - usage: 'start -p -H ', - options: [ 'hostname', 'port', 'unix-socket' ] - }) +export default { + name: 'start', + description: 'Start the application in production mode (the application should be compiled with `nuxt build` first)', + usage: 'start ', + options: { + ...common, + ...server + }, + async run(cmd) { + const argv = cmd.getArgv() - const argv = nuxtCmd.getArgv() - - // Create production build when calling `nuxt build` - const nuxt = await nuxtCmd.getNuxt( - await nuxtCmd.getNuxtConfig(argv, { dev: false }) - ) - - // Setup hooks - nuxt.hook('error', err => consola.fatal(err)) - - // Check if project is built for production - const distDir = path.resolve( - nuxt.options.rootDir, - nuxt.options.buildDir || '.nuxt', - 'dist', - 'server' - ) - if (!fs.existsSync(distDir)) { - consola.fatal( - 'No build files found, please run `nuxt build` before launching `nuxt start`' + // Create production build when calling `nuxt build` + const nuxt = await cmd.getNuxt( + await cmd.getNuxtConfig(argv, { dev: false }) ) - } - // Check if SSR Bundle is required - if (nuxt.options.render.ssr === true) { - const ssrBundlePath = path.resolve(distDir, 'server-bundle.json') - if (!fs.existsSync(ssrBundlePath)) { + // Setup hooks + nuxt.hook('error', err => consola.fatal(err)) + + // Check if project is built for production + const distDir = path.resolve( + nuxt.options.rootDir, + nuxt.options.buildDir || '.nuxt', + 'dist', + 'server' + ) + if (!fs.existsSync(distDir)) { consola.fatal( - 'No SSR build! Please start with `nuxt start --spa` or build using `nuxt build --universal`' + 'No build files found, please run `nuxt build` before launching `nuxt start`' ) } - } - return nuxt.listen().then(() => { - nuxt.showReady(false) - }) + // Check if SSR Bundle is required + if (nuxt.options.render.ssr === true) { + const ssrBundlePath = path.resolve(distDir, 'server-bundle.json') + if (!fs.existsSync(ssrBundlePath)) { + consola.fatal( + 'No SSR build! Please start with `nuxt start --spa` or build using `nuxt build --universal`' + ) + } + } + + return nuxt.listen().then(() => { + nuxt.showReady(false) + }) + } } diff --git a/packages/cli/src/index.js b/packages/cli/src/index.js index 5c39b23a97..3ee655ce7c 100644 --- a/packages/cli/src/index.js +++ b/packages/cli/src/index.js @@ -4,7 +4,7 @@ import * as _imports from './imports' export const commands = _commands export const imports = _imports +export { default as NuxtCommand } from './command' export { default as setup } from './setup' export { default as run } from './run' - export { loadNuxtConfig } from './utils' diff --git a/packages/cli/src/options.js b/packages/cli/src/options.js deleted file mode 100644 index 2afd911650..0000000000 --- a/packages/cli/src/options.js +++ /dev/null @@ -1,101 +0,0 @@ -import consola from 'consola' - -export const defaultOptions = [ - 'spa', - 'universal', - 'config-file', - 'version', - 'help' -] - -export const options = { - port: { - alias: 'p', - type: 'string', - description: 'Port number on which to start the application', - handle(options, argv) { - if (argv.port) { - options.server.port = +argv.port - } - } - }, - hostname: { - alias: 'H', - type: 'string', - description: 'Hostname on which to start the application', - handle(options, argv) { - if (argv.hostname === '') { - consola.fatal('Provided hostname argument has no value') - } - } - }, - 'unix-socket': { - alias: 'n', - type: 'string', - description: 'Path to a UNIX socket' - }, - analyze: { - alias: 'a', - type: 'boolean', - description: 'Launch webpack-bundle-analyzer to optimize your bundles', - handle(options, argv) { - // Analyze option - options.build = options.build || {} - if (argv.analyze && typeof options.build.analyze !== 'object') { - options.build.analyze = true - } - } - }, - build: { - type: 'boolean', - default: true, - description: 'Only generate pages for dynamic routes. Nuxt has to be built once before using this option' - }, - generate: { - type: 'boolean', - default: true, - description: 'Don\'t generate static version for SPA mode (useful for nuxt start)' - }, - spa: { - alias: 's', - type: 'boolean', - description: 'Launch in SPA mode' - }, - universal: { - alias: 'u', - type: 'boolean', - description: 'Launch in Universal mode (default)' - }, - 'config-file': { - alias: 'c', - type: 'string', - default: 'nuxt.config.js', - description: 'Path to Nuxt.js config file (default: nuxt.config.js)' - }, - quiet: { - alias: 'q', - type: 'boolean', - description: 'Disable output except for errors', - handle(options, argv) { - // Silence output when using --quiet - options.build = options.build || {} - if (argv.quiet) { - options.build.quiet = !!argv.quiet - } - } - }, - verbose: { - alias: 'v', - type: 'boolean', - description: 'Show debug information' - }, - version: { - type: 'boolean', - description: 'Display the Nuxt version' - }, - help: { - alias: 'h', - type: 'boolean', - description: 'Display this message' - } -} diff --git a/packages/cli/src/options/common.js b/packages/cli/src/options/common.js new file mode 100644 index 0000000000..a1565a0950 --- /dev/null +++ b/packages/cli/src/options/common.js @@ -0,0 +1,27 @@ +export default { + spa: { + alias: 's', + type: 'boolean', + description: 'Launch in SPA mode' + }, + universal: { + alias: 'u', + type: 'boolean', + description: 'Launch in Universal mode (default)' + }, + 'config-file': { + alias: 'c', + type: 'string', + default: 'nuxt.config.js', + description: 'Path to Nuxt.js config file (default: nuxt.config.js)' + }, + version: { + type: 'boolean', + description: 'Display the Nuxt version' + }, + help: { + alias: 'h', + type: 'boolean', + description: 'Display this message' + } +} diff --git a/packages/cli/src/options/index.js b/packages/cli/src/options/index.js new file mode 100644 index 0000000000..347fa2c72a --- /dev/null +++ b/packages/cli/src/options/index.js @@ -0,0 +1,2 @@ +export { default as common } from './common' +export { default as server } from './server' diff --git a/packages/cli/src/options/server.js b/packages/cli/src/options/server.js new file mode 100644 index 0000000000..91aad77c68 --- /dev/null +++ b/packages/cli/src/options/server.js @@ -0,0 +1,29 @@ +import consola from 'consola' + +export default { + port: { + alias: 'p', + type: 'string', + description: 'Port number on which to start the application', + prepare(cmd, options, argv) { + if (argv.port) { + options.server.port = +argv.port + } + } + }, + hostname: { + alias: 'H', + type: 'string', + description: 'Hostname on which to start the application', + prepare(cmd, options, argv) { + if (argv.hostname === '') { + consola.fatal('Provided hostname argument has no value') + } + } + }, + 'unix-socket': { + alias: 'n', + type: 'string', + description: 'Path to a UNIX socket' + } +} diff --git a/packages/cli/src/run.js b/packages/cli/src/run.js index 97c322b53b..97976d71b6 100644 --- a/packages/cli/src/run.js +++ b/packages/cli/src/run.js @@ -1,4 +1,5 @@ import consola from 'consola' +import NuxtCommand from './command' import * as commands from './commands' import setup from './setup' @@ -26,7 +27,9 @@ export default function run() { }) return commands[cmd]() // eslint-disable-line import/namespace - .then(m => m.default()) + .then(m => m.default) + .then(options => NuxtCommand.from(options)) + .then(command => command.run()) .catch((error) => { consola.fatal(error) }) diff --git a/packages/cli/src/utils.js b/packages/cli/src/utils.js index 5810e4d792..6b609005bf 100644 --- a/packages/cli/src/utils.js +++ b/packages/cli/src/utils.js @@ -76,11 +76,11 @@ export function indentLines(string, spaces, firstLineSpaces) { } if (lines.length) { const i = indent(spaces) - s += '\n' + lines.map(l => i + l.trim()).join('\n') + s += '\n' + lines.map(l => i + l).join('\n') } return s } export function foldLines(string, maxCharsPerLine, spaces, firstLineSpaces) { - return indentLines(wrapAnsi(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 new file mode 100644 index 0000000000..20ef9648a3 --- /dev/null +++ b/packages/cli/test/unit/__snapshots__/command.test.js.snap @@ -0,0 +1,23 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +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 + + Options: + + --spa, -s Launch in SPA mode + --universal, -u Launch in Universal mode (default) + --config-file, -c Path to Nuxt.js config file (default: nuxt.config.js) + --version Display the Nuxt version + --help, -h Display this message + --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 + +" +`; diff --git a/packages/cli/test/unit/build.test.js b/packages/cli/test/unit/build.test.js index 5c501804ec..1ebc3ea7b4 100644 --- a/packages/cli/test/unit/build.test.js +++ b/packages/cli/test/unit/build.test.js @@ -1,25 +1,18 @@ -import { consola, mockGetNuxt, mockGetBuilder, mockGetGenerator } from '../utils' +import { consola, mockGetNuxt, mockGetBuilder, mockGetGenerator, NuxtCommand } from '../utils' describe('build', () => { let build beforeAll(async () => { - build = await import('../../src/commands/build') - build = build.default - + build = await import('../../src/commands/build').then(m => m.default) jest.spyOn(process, 'exit').mockImplementation(code => code) }) - afterAll(() => { - process.exit.mockRestore() - }) + afterAll(() => process.exit.mockRestore()) + afterEach(() => jest.resetAllMocks()) - afterEach(() => { - jest.resetAllMocks() - }) - - test('is function', () => { - expect(typeof build).toBe('function') + test('has run function', () => { + expect(typeof build.run).toBe('function') }) test('builds on universal mode', async () => { @@ -31,7 +24,7 @@ describe('build', () => { }) const builder = mockGetBuilder(Promise.resolve()) - await build() + await NuxtCommand.from(build).run() expect(builder).toHaveBeenCalled() }) @@ -45,7 +38,7 @@ describe('build', () => { }) const generate = mockGetGenerator(Promise.resolve()) - await build() + await NuxtCommand.from(build).run() expect(generate).toHaveBeenCalled() expect(process.exit).toHaveBeenCalled() @@ -55,7 +48,7 @@ describe('build', () => { mockGetNuxt({ mode: 'universal' }) mockGetBuilder(Promise.reject(new Error('Builder Error'))) - await build() + await NuxtCommand.from(build).run() expect(consola.fatal).toHaveBeenCalledWith(new Error('Builder Error')) }) diff --git a/packages/cli/test/unit/cli.test.js b/packages/cli/test/unit/cli.test.js index fa283776bd..8f597e7ba4 100644 --- a/packages/cli/test/unit/cli.test.js +++ b/packages/cli/test/unit/cli.test.js @@ -10,9 +10,7 @@ const readDir = promisify(readdir) jest.mock('../../src/commands') describe('cli', () => { - afterEach(() => { - jest.resetAllMocks() - }) + afterEach(() => jest.resetAllMocks()) test('exports for all commands defined', async () => { const cmds = await readDir(resolve(__dirname, '..', '..', 'src', 'commands')) @@ -32,12 +30,14 @@ describe('cli', () => { test('calls expected method', async () => { const argv = process.argv process.argv = ['', '', 'dev'] - const defaultExport = jest.fn().mockImplementation(() => Promise.resolve()) + const defaultExport = { + run: jest.fn().mockImplementation(() => Promise.resolve()) + } commands.dev.mockImplementationOnce(() => Promise.resolve({ default: defaultExport })) await run() - expect(defaultExport).toHaveBeenCalled() + expect(defaultExport.run).toHaveBeenCalled() process.argv = argv }) diff --git a/packages/cli/test/unit/command.test.js b/packages/cli/test/unit/command.test.js index cb0c05af7e..07cbec1058 100644 --- a/packages/cli/test/unit/command.test.js +++ b/packages/cli/test/unit/command.test.js @@ -1,39 +1,31 @@ import Command from '../../src/command' -import { options as Options } from '../../src/options' +import { common, server } from '../../src/options' import { consola } from '../utils' jest.mock('@nuxt/core') jest.mock('@nuxt/builder') jest.mock('@nuxt/generator') +const allOptions = { + ...common, + ...server +} + describe('cli/command', () => { - beforeEach(() => { - jest.restoreAllMocks() - }) - - test('adds default options', () => { - const cmd = new Command() - - expect(cmd.options.length).not.toBe(0) - }) + beforeEach(() => jest.restoreAllMocks()) test('builds minimist options', () => { - const cmd = new Command({ - options: Object.keys(Options) - }) - + const cmd = new Command({ options: allOptions }) const minimistOptions = cmd._getMinimistOptions() expect(minimistOptions.string.length).toBe(4) - expect(minimistOptions.boolean.length).toBe(9) + expect(minimistOptions.boolean.length).toBe(4) expect(minimistOptions.alias.c).toBe('config-file') - expect(minimistOptions.default.c).toBe(Options['config-file'].default) + expect(minimistOptions.default.c).toBe(common['config-file'].default) }) test('parses args', () => { - const cmd = new Command({ - options: Object.keys(Options) - }) + const cmd = new Command({ options: { ...common, ...server } }) let args = ['-c', 'test-file', '-s', '-p', '3001'] let argv = cmd.getArgv(args) @@ -41,7 +33,6 @@ describe('cli/command', () => { expect(argv['config-file']).toBe(args[1]) expect(argv.spa).toBe(true) expect(argv.universal).toBe(false) - expect(argv.build).toBe(true) expect(argv.port).toBe('3001') args = ['--no-build'] @@ -61,7 +52,7 @@ describe('cli/command', () => { }) test('prints help automatically', () => { - const cmd = new Command() + const cmd = new Command({ options: allOptions }) cmd.showHelp = jest.fn() const args = ['-h'] @@ -71,9 +62,7 @@ describe('cli/command', () => { }) test('returns nuxt config', async () => { - const cmd = new Command({ - options: Object.keys(Options) - }) + const cmd = new Command({ options: allOptions }) const args = ['-c', 'test-file', '-a', '-p', '3001', '-q', '-H'] const argv = cmd.getArgv(args) @@ -83,8 +72,6 @@ describe('cli/command', () => { expect(options.testOption).toBe(true) expect(options.server.port).toBe(3001) - expect(options.build.quiet).toBe(true) - expect(options.build.analyze).toBe(true) expect(consola.fatal).toHaveBeenCalledWith('Provided hostname argument has no value') // hostname check }) @@ -117,26 +104,17 @@ describe('cli/command', () => { description: 'a very long description that is longer than 80 chars and ' + 'should wrap to the next line while keeping indentation', usage: 'this is how you do it', - options: ['build'] + 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' + } + } }) - const expectedText = ` - Description - a very long description that is longer than 80 chars and should wrap to the next - line while keeping indentation - Usage - $ nuxt this is how you do it - Options - --no-build Only generate pages for dynamic routes. Nuxt has to be - built once before using this option - --spa, -s Launch in SPA mode - --universal, -u Launch in Universal mode (default) - --config-file, -c Path to Nuxt.js config file (default: nuxt.config.js) - --version Display the Nuxt version - --help, -h Display this message - -` - expect(cmd._getHelp()).toBe(expectedText) + expect(cmd._getHelp()).toMatchSnapshot() }) test('show version prints to stdout and exits', () => { diff --git a/packages/cli/test/unit/dev.test.js b/packages/cli/test/unit/dev.test.js index 30a623b350..dad9e2011b 100644 --- a/packages/cli/test/unit/dev.test.js +++ b/packages/cli/test/unit/dev.test.js @@ -1,26 +1,23 @@ -import { consola, mockNuxt, mockBuilder, mockGetNuxtConfig } from '../utils' +import { consola, mockNuxt, mockBuilder, mockGetNuxtConfig, NuxtCommand } from '../utils' describe('dev', () => { let dev beforeAll(async () => { - dev = await import('../../src/commands/dev') - dev = dev.default + dev = await import('../../src/commands/dev').then(m => m.default) }) - afterEach(() => { - jest.clearAllMocks() - }) + afterEach(() => jest.clearAllMocks()) - test('is function', () => { - expect(typeof dev).toBe('function') + test('has run function', () => { + expect(typeof dev.run).toBe('function') }) test('reloads on fileChanged hook', async () => { const Nuxt = mockNuxt() const Builder = mockBuilder() - await dev() + await NuxtCommand.from(dev).run() expect(consola.error).not.toHaveBeenCalled() @@ -51,7 +48,7 @@ describe('dev', () => { const Nuxt = mockNuxt() const Builder = mockBuilder() - await dev() + await NuxtCommand.from(dev).run() jest.clearAllMocks() // Test error on second build so we cover oldInstance stuff @@ -68,7 +65,7 @@ describe('dev', () => { const Nuxt = mockNuxt() const Builder = mockBuilder() - await dev() + await NuxtCommand.from(dev).run() jest.clearAllMocks() const builder = new Builder() @@ -84,7 +81,7 @@ describe('dev', () => { const Nuxt = mockNuxt() const Builder = mockBuilder() - await dev() + await NuxtCommand.from(dev).run() jest.clearAllMocks() mockGetNuxtConfig().mockImplementationOnce(() => { @@ -106,7 +103,7 @@ describe('dev', () => { }) mockBuilder() - await dev() + await NuxtCommand.from(dev).run() expect(consola.error).toHaveBeenCalledWith(new Error('Listen Error')) }) diff --git a/packages/cli/test/unit/generate.test.js b/packages/cli/test/unit/generate.test.js index 42b8f0d141..4e4cd9bfee 100644 --- a/packages/cli/test/unit/generate.test.js +++ b/packages/cli/test/unit/generate.test.js @@ -1,33 +1,26 @@ -import { consola, mockGetNuxt, mockGetGenerator } from '../utils' +import { consola, mockGetNuxt, mockGetGenerator, NuxtCommand } from '../utils' import Command from '../../src/command' describe('generate', () => { let generate beforeAll(async () => { - generate = await import('../../src/commands/generate') - generate = generate.default - + generate = await import('../../src/commands/generate').then(m => m.default) jest.spyOn(process, 'exit').mockImplementation(code => code) }) - afterAll(() => { - process.exit.mockRestore() - }) + afterAll(() => process.exit.mockRestore()) + afterEach(() => jest.resetAllMocks()) - afterEach(() => { - jest.resetAllMocks() - }) - - test('is function', () => { - expect(typeof generate).toBe('function') + test('has run function', () => { + expect(typeof generate.run).toBe('function') }) test('builds by default', async () => { mockGetNuxt() const generator = mockGetGenerator(Promise.resolve()) - await generate() + await NuxtCommand.from(generate).run() expect(generator).toHaveBeenCalled() expect(generator.mock.calls[0][0].build).toBe(true) @@ -46,7 +39,7 @@ describe('generate', () => { }) const generator = mockGetGenerator(Promise.resolve()) - await generate() + await NuxtCommand.from(generate).run() expect(generator).toHaveBeenCalled() expect(generator.mock.calls[0][0].build).toBe(false) @@ -57,7 +50,7 @@ describe('generate', () => { mockGetNuxt() mockGetGenerator(Promise.reject(new Error('Generator Error'))) - await generate() + await NuxtCommand.from(generate).run() expect(consola.fatal).toHaveBeenCalledWith(new Error('Generator Error')) }) diff --git a/packages/cli/test/unit/start.test.js b/packages/cli/test/unit/start.test.js index 876b50eb6b..4568ac0eca 100644 --- a/packages/cli/test/unit/start.test.js +++ b/packages/cli/test/unit/start.test.js @@ -1,29 +1,27 @@ import fs from 'fs' -import { consola, mockGetNuxtStart, mockGetNuxtConfig } from '../utils' +import { consola, mockGetNuxtStart, mockGetNuxtConfig, NuxtCommand } from '../utils' describe('start', () => { let start beforeAll(async () => { - start = await import('../../src/commands/start') - start = start.default + start = await import('../../src/commands/start').then(m => m.default) }) afterEach(() => { if (fs.existsSync.mockRestore) { fs.existsSync.mockRestore() } - jest.resetAllMocks() }) - test('is function', () => { - expect(typeof start).toBe('function') + test('has run function', () => { + expect(typeof start.run).toBe('function') }) test('starts listening and calls showReady', async () => { const { listen, showReady } = mockGetNuxtStart() - await start() + await NuxtCommand.from(start).run() expect(listen).toHaveBeenCalled() expect(showReady).toHaveBeenCalled() @@ -34,7 +32,7 @@ describe('start', () => { mockGetNuxtConfig() jest.spyOn(fs, 'existsSync').mockImplementationOnce(() => true) - await start() + await NuxtCommand.from(start).run() expect(consola.fatal).not.toHaveBeenCalled() }) @@ -43,7 +41,7 @@ describe('start', () => { mockGetNuxtStart() jest.spyOn(fs, 'existsSync').mockImplementationOnce(() => false) - await start() + await NuxtCommand.from(start).run() expect(consola.fatal).toHaveBeenCalledWith('No build files found, please run `nuxt build` before launching `nuxt start`') }) @@ -53,7 +51,7 @@ describe('start', () => { mockGetNuxtConfig() jest.spyOn(fs, 'existsSync').mockImplementation(() => true) - await start() + await NuxtCommand.from(start).run() expect(consola.fatal).not.toHaveBeenCalled() }) @@ -62,7 +60,7 @@ describe('start', () => { mockGetNuxtStart(true) jest.spyOn(fs, 'existsSync').mockImplementation(() => false) - await start() + await NuxtCommand.from(start).run() expect(consola.fatal).toHaveBeenCalledWith('No SSR build! Please start with `nuxt start --spa` or build using `nuxt build --universal`') }) diff --git a/packages/cli/test/unit/utils.test.js b/packages/cli/test/unit/utils.test.js index e7a5cd50e1..b1a1bf00c5 100644 --- a/packages/cli/test/unit/utils.test.js +++ b/packages/cli/test/unit/utils.test.js @@ -3,9 +3,7 @@ import { consola } from '../utils' import * as utils from '../../src/utils' describe('cli/utils', () => { - afterEach(() => { - jest.resetAllMocks() - }) + afterEach(() => jest.resetAllMocks()) test('loadNuxtConfig: defaults', async () => { const argv = { diff --git a/packages/cli/test/utils/index.js b/packages/cli/test/utils/index.js index e31e09d6c0..ba9acd8c08 100644 --- a/packages/cli/test/utils/index.js +++ b/packages/cli/test/utils/index.js @@ -1,5 +1,6 @@ import consola from 'consola' export * from './mocking' +export { NuxtCommand } from '../../src' jest.mock('consola')