fix: dont force exit when it was explicitly disabled (#4973)

* fix: remove slash from warning text

* fix: dont force-exit when explicitly disabled

chore: add tests for force-exit behaviour

* feat: default option value can be fn
This commit is contained in:
Pim 2019-02-08 11:06:47 +01:00 committed by Clark Du
parent 3d2deacd3a
commit 4b82aa9d84
10 changed files with 173 additions and 34 deletions

View File

@ -13,9 +13,6 @@ export default class NuxtCommand {
} }
this.cmd = cmd this.cmd = cmd
// If the cmd is a server then dont forcibly exit when the cmd has finished
this.isServer = cmd.isServer !== undefined ? cmd.isServer : Boolean(this.cmd.options.hostname)
this._argv = Array.from(argv) this._argv = Array.from(argv)
this._parsedArgv = null // Lazy evaluate this._parsedArgv = null // Lazy evaluate
} }
@ -48,9 +45,9 @@ export default class NuxtCommand {
const runResolve = Promise.resolve(this.cmd.run(this)) const runResolve = Promise.resolve(this.cmd.run(this))
// TODO: For v3 set timeout to 0 when force-exit === true if (this.argv['force-exit']) {
if (!this.isServer || this.argv['force-exit']) { const forceExitByUser = this.isUserSuppliedArg('force-exit')
runResolve.then(() => forceExit(this.cmd.name, forceExitTimeout)) runResolve.then(() => forceExit(this.cmd.name, forceExitByUser ? false : forceExitTimeout))
} }
return runResolve return runResolve
@ -102,6 +99,14 @@ export default class NuxtCommand {
return new Generator(nuxt, builder) return new Generator(nuxt, builder)
} }
isUserSuppliedArg(option) {
return this._argv.includes(`--${option}`) || this._argv.includes(`--no-${option}`)
}
_getDefaultOptionValue(option) {
return typeof option.default === 'function' ? option.default(this.cmd) : option.default
}
_getMinimistOptions() { _getMinimistOptions() {
const minimistOptions = { const minimistOptions = {
alias: {}, alias: {},
@ -120,7 +125,7 @@ export default class NuxtCommand {
minimistOptions[option.type].push(option.alias || name) minimistOptions[option.type].push(option.alias || name)
} }
if (option.default) { if (option.default) {
minimistOptions.default[option.alias || name] = option.default minimistOptions.default[option.alias || name] = this._getDefaultOptionValue(option)
} }
} }
@ -135,7 +140,7 @@ export default class NuxtCommand {
const option = this.cmd.options[name] const option = this.cmd.options[name]
let optionHelp = '--' let optionHelp = '--'
optionHelp += option.type === 'boolean' && option.default ? 'no-' : '' optionHelp += option.type === 'boolean' && this._getDefaultOptionValue(option) ? 'no-' : ''
optionHelp += name optionHelp += name
if (option.alias) { if (option.alias) {
optionHelp += `, -${option.alias}` optionHelp += `, -${option.alias}`

View File

@ -28,11 +28,12 @@ export default {
} }
} }
}, },
// TODO: Change this to default: false in Nuxt v3 (see related todo's)
'force-exit': { 'force-exit': {
type: 'boolean', type: 'boolean',
default: true, default(cmd) {
description: 'Do not force Nuxt.js to exit after the command has finished (this option has no effect on commands which start a server)' return ['build', 'generate'].includes(cmd.name)
},
description: 'Whether Nuxt.js should force exit after the command has finished'
}, },
version: { version: {
alias: 'v', alias: 'v',

View File

@ -140,10 +140,10 @@ export function normalizeArg(arg, defaultValue) {
} }
export function forceExit(cmdName, timeout) { export function forceExit(cmdName, timeout) {
if (timeout) { if (timeout !== false) {
const exitTimeout = setTimeout(() => { const exitTimeout = setTimeout(() => {
const msg = `The command 'nuxt ${cmdName}' finished but did not exit after ${timeout}s const msg = `The command 'nuxt ${cmdName}' finished but did not exit after ${timeout}s
This is most likely not caused by a bug in Nuxt.js\ This is most likely not caused by a bug in Nuxt.js
Make sure to cleanup all timers and listeners you or your plugins/modules start. Make sure to cleanup all timers and listeners you or your plugins/modules start.
Nuxt.js will now force exit Nuxt.js will now force exit

View File

@ -18,10 +18,9 @@ exports[`cli/command builds help text 1`] = `
--modern, -m Build/Start app for --modern, -m Build/Start app for
modern browsers, e.g. server, client and modern browsers, e.g. server, client and
false false
--no-force-exit Do not force Nuxt.js --force-exit Whether Nuxt.js
to exit after the command has finished should force exit after the command has
(this option has no effect on commands finished
which start a server)
--version, -v Display the Nuxt --version, -v Display the Nuxt
version version
--help, -h Display this message --help, -h Display this message

View File

@ -74,4 +74,36 @@ describe('build', () => {
expect(options.modern).toBe(true) expect(options.modern).toBe(true)
}) })
test('build force-exits by default', async () => {
mockGetNuxt()
mockGetBuilder(Promise.resolve())
const cmd = NuxtCommand.from(build, ['build', '.'])
await cmd.run()
expect(utils.forceExit).toHaveBeenCalledTimes(1)
expect(utils.forceExit).toHaveBeenCalledWith('build', 5)
})
test('build can set force exit explicitly', async () => {
mockGetNuxt()
mockGetBuilder(Promise.resolve())
const cmd = NuxtCommand.from(build, ['build', '.', '--force-exit'])
await cmd.run()
expect(utils.forceExit).toHaveBeenCalledTimes(1)
expect(utils.forceExit).toHaveBeenCalledWith('build', false)
})
test('build can disable force exit explicitly', async () => {
mockGetNuxt()
mockGetBuilder(Promise.resolve())
const cmd = NuxtCommand.from(build, ['build', '.', '--no-force-exit'])
await cmd.run()
expect(utils.forceExit).not.toHaveBeenCalled()
})
}) })

View File

@ -6,7 +6,6 @@ jest.mock('../../src/commands')
describe('cli', () => { describe('cli', () => {
beforeAll(() => { beforeAll(() => {
// TODO: Below spyOn can be removed in v3 when force-exit is default false
jest.spyOn(utils, 'forceExit').mockImplementation(() => {}) jest.spyOn(utils, 'forceExit').mockImplementation(() => {})
}) })
@ -20,6 +19,7 @@ describe('cli', () => {
await run() await run()
expect(mockedCommand.run).toHaveBeenCalled() expect(mockedCommand.run).toHaveBeenCalled()
expect(utils.forceExit).not.toHaveBeenCalled()
}) })
test('sets NODE_ENV=development for dev', async () => { test('sets NODE_ENV=development for dev', async () => {

View File

@ -6,7 +6,6 @@ describe('dev', () => {
beforeAll(async () => { beforeAll(async () => {
dev = await import('../../src/commands/dev').then(m => m.default) dev = await import('../../src/commands/dev').then(m => m.default)
// TODO: Below spyOn can be removed in v3 when force-exit is default false
jest.spyOn(utils, 'forceExit').mockImplementation(() => {}) jest.spyOn(utils, 'forceExit').mockImplementation(() => {})
}) })
@ -107,4 +106,35 @@ describe('dev', () => {
expect(consola.error).toHaveBeenCalledWith(new Error('Listen Error')) expect(consola.error).toHaveBeenCalledWith(new Error('Listen Error'))
}) })
test('dev doesnt force-exit by default', async () => {
mockNuxt()
mockBuilder()
const cmd = NuxtCommand.from(dev, ['dev', '.'])
await cmd.run()
expect(utils.forceExit).not.toHaveBeenCalled()
})
test('dev can set force exit explicitly', async () => {
mockNuxt()
mockBuilder()
const cmd = NuxtCommand.from(dev, ['dev', '.', '--force-exit'])
await cmd.run()
expect(utils.forceExit).toHaveBeenCalledTimes(1)
expect(utils.forceExit).toHaveBeenCalledWith('dev', false)
})
test('dev can disable force exit explicitly', async () => {
mockNuxt()
mockBuilder()
const cmd = NuxtCommand.from(dev, ['dev', '.', '--no-force-exit'])
await cmd.run()
expect(utils.forceExit).not.toHaveBeenCalled()
})
}) })

View File

@ -63,4 +63,49 @@ describe('generate', () => {
expect(options.modern).toBe('client') expect(options.modern).toBe('client')
}) })
test('generate with modern mode', async () => {
mockGetNuxt()
mockGetGenerator(Promise.resolve())
const cmd = NuxtCommand.from(generate, ['generate', '.', '--m'])
const options = await cmd.getNuxtConfig()
await cmd.run()
expect(options.modern).toBe('client')
})
test('generate force-exits by default', async () => {
mockGetNuxt()
mockGetGenerator(Promise.resolve())
const cmd = NuxtCommand.from(generate, ['generate', '.'])
await cmd.run()
expect(utils.forceExit).toHaveBeenCalledTimes(1)
expect(utils.forceExit).toHaveBeenCalledWith('generate', 5)
})
test('generate can set force exit explicitly', async () => {
mockGetNuxt()
mockGetGenerator(Promise.resolve())
const cmd = NuxtCommand.from(generate, ['generate', '.', '--force-exit'])
await cmd.run()
expect(utils.forceExit).toHaveBeenCalledTimes(1)
expect(utils.forceExit).toHaveBeenCalledWith('generate', false)
})
test('generate can disable force exit explicitly', async () => {
mockGetNuxt()
mockGetGenerator(Promise.resolve())
const cmd = NuxtCommand.from(generate, ['generate', '.', '--no-force-exit'])
await cmd.run()
expect(utils.forceExit).not.toHaveBeenCalled()
})
}) })

View File

@ -7,7 +7,6 @@ describe('start', () => {
beforeAll(async () => { beforeAll(async () => {
start = await import('../../src/commands/start').then(m => m.default) start = await import('../../src/commands/start').then(m => m.default)
// TODO: Below spyOn can be removed in v3 when force-exit is default false
jest.spyOn(utils, 'forceExit').mockImplementation(() => {}) jest.spyOn(utils, 'forceExit').mockImplementation(() => {})
}) })
@ -35,4 +34,32 @@ describe('start', () => {
await NuxtCommand.from(start).run() await NuxtCommand.from(start).run()
expect(consola.fatal).not.toHaveBeenCalled() expect(consola.fatal).not.toHaveBeenCalled()
}) })
test('start doesnt force-exit by default', async () => {
mockGetNuxtStart()
const cmd = NuxtCommand.from(start, ['start', '.'])
await cmd.run()
expect(utils.forceExit).not.toHaveBeenCalled()
})
test('start can set force exit explicitly', async () => {
mockGetNuxtStart()
const cmd = NuxtCommand.from(start, ['start', '.', '--force-exit'])
await cmd.run()
expect(utils.forceExit).toHaveBeenCalledTimes(1)
expect(utils.forceExit).toHaveBeenCalledWith('start', false)
})
test('start can disable force exit explicitly', async () => {
mockGetNuxtStart()
const cmd = NuxtCommand.from(start, ['start', '.', '--no-force-exit'])
await cmd.run()
expect(utils.forceExit).not.toHaveBeenCalled()
})
}) })

View File

@ -18,12 +18,12 @@ jest.mock('../../src/imports', () => {
} }
}) })
export const mockGetNuxt = (options, implementation) => { export const mockGetNuxt = (options = {}, implementation) => {
Command.prototype.getNuxt = jest.fn().mockImplementationOnce(() => { Command.prototype.getNuxt = jest.fn().mockImplementationOnce(() => {
return Object.assign({ return Object.assign({
hook: jest.fn(), hook: jest.fn(),
options options
}, implementation || {}) }, implementation)
}) })
} }