Skip to content
Open
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
15 changes: 15 additions & 0 deletions .c8rc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"all": false,
"check-coverage": true,
"statements": 100,
"branches": 100,
"functions": 100,
"lines": 100,
"exclude": [
"test/**",
"templates/**",
"examples/**",
"suite-runner.js",
"coverage/**"
]
}
2 changes: 1 addition & 1 deletion args.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ module.exports = function parseCliArgs (args) {

const configFileOptions = commandLineArguments.config ? requireModule(commandLineArguments.config) : undefined

const additionalArgs = commandLineArguments['--'] || []
const additionalArgs = commandLineArguments['--']
const pluginParsed = parseArgs(additionalArgs, { options: {}, strict: false })
const { _, ...pluginOptions } = pluginParsed
const ignoreWatchArg = commandLineArguments.ignoreWatch || configFileOptions?.ignoreWatch || ''
Expand Down
2 changes: 1 addition & 1 deletion eject.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,5 @@ module.exports = {
}

if (require.main === module) {
cli()
cli(process.argv.slice(2))
}
4 changes: 2 additions & 2 deletions generate-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ async function generate (dir, template) {
pkg.types = template.types
pkg.description = ''
pkg.license = 'MIT'
pkg.scripts = Object.assign(pkg.scripts || {}, template.scripts)
pkg.scripts = { ...pkg.scripts, ...template.scripts }
pkg.dependencies = Object.assign(pkg.dependencies || {}, template.dependencies)
pkg.devDependencies = Object.assign(pkg.devDependencies || {}, template.devDependencies)
if (template.tstyche) {
pkg.tstyche = Object.assign(pkg.tstyche || {}, template.tstyche)
pkg.tstyche = { ...pkg.tstyche, ...template.tstyche }
}

log('debug', 'edited package.json, saving')
Expand Down
10 changes: 1 addition & 9 deletions generate-readme.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,11 @@ function generate (dir, { pluginMeta, encapsulated, pluginFileName }) {
})
}

function stop (error) {
if (error) {
console.log(error)
process.exit(1)
}
process.exit()
}

function showHelp () {
console.log(
readFileSync(path.join(__dirname, 'help', 'readme.txt'), 'utf8')
)
return stop()
process.exit()
}

function cli (args) {
Expand Down
8 changes: 6 additions & 2 deletions generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,11 @@ function generate (dir, template) {
}

process.chdir(dir)
execSync('npm init -y')
try {
execSync('npm init -y')
} catch (err) {
return reject(err)
}

log('info', `reading package.json in ${dir}`)
readFile('package.json', (err, data) => {
Expand All @@ -115,7 +119,7 @@ function generate (dir, template) {

pkg.type = template.type

pkg.scripts = Object.assign(pkg.scripts || {}, template.scripts)
pkg.scripts = { ...pkg.scripts, ...template.scripts }

pkg.dependencies = Object.assign(pkg.dependencies || {}, template.dependencies)

Expand Down
2 changes: 1 addition & 1 deletion lib/parse-args.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ function parseArgsStandard (args, config) {
}
if (token.kind === 'positional') {
if (inRest) {
rest.push(token.original !== undefined ? token.original : token.value)
rest.push(token.value)
} else {
mainPositionals.push(token.value)
}
Expand Down
10 changes: 3 additions & 7 deletions lib/watch/fork.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict'

const chalk = require('chalk').default
const { stop, runFastify } = require('../../start')
const { runFastify } = require('../../start')

const {
GRACEFUL_SHUT,
Expand Down Expand Up @@ -42,12 +42,8 @@ const main = async () => {

process.send({ type, err: null })

try {
await fastify.ready()
process.send({ type: READY })
} catch (err) {
stop(err)
}
await fastify.ready()
process.send({ type: READY })
}

main().catch((err) => {
Expand Down
5 changes: 1 addition & 4 deletions lib/watch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,7 @@ const watch = function (args, ignoreWatch, verboseWatch, followWatch) {
const child = childs.shift()
child.send(GRACEFUL_SHUT)
} catch (err) {
if (childs.length !== 0) {
console.log(chalk.red(err))
stop(watcher, err)
}
// the previous child already exited: start a new one
childs.push(run('restart'))
}
})
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@
"is-docker": "^4.0.0",
"pino-pretty": "^13.0.0",
"pkg-up": "^5.0.0",
"resolve-from": "^5.0.0",
"semver": "^7.3.5"
"resolve-from": "^5.0.0"
},
"devDependencies": {
"@fastify/autoload": "^6.0.0",
Expand All @@ -75,6 +74,7 @@
"neostandard": "^0.13.0",
"proxyquire": "^2.1.3",
"rimraf": "^6.1.0",
"semver": "^7.3.5",
"sinon": "^22.0.0",
"strip-ansi": "^6.0.1",
"ts-node": "^10.4.0",
Expand Down
12 changes: 4 additions & 8 deletions start.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,14 +150,10 @@ async function runFastify (args, additionalOptions, serverOptions, serverModule)
}

if (opts.debug) {
if (process.version.match(/v[0-6]\..*/g)) {
stop('Fastify debug mode not compatible with Node.js version < 6')
} else {
const debugHost = opts.debugHost ?? (
isDocker() || isKubernetes() ? listenAddressDocker : undefined
)
require('node:inspector').open(opts.debugPort, debugHost)
}
const debugHost = opts.debugHost ?? (
isDocker() || isKubernetes() ? listenAddressDocker : undefined
)
require('node:inspector').open(opts.debugPort, debugHost)
}

if (serverOptions) {
Expand Down
18 changes: 18 additions & 0 deletions test/args.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -558,3 +558,21 @@ test('should collect repeated options into an array', t => {
t.assert.deepStrictEqual(parsedArgs.require, ['./a.js', './b.js'])
t.assert.strictEqual(parsedArgs.import, './c.mjs')
})

test('should drop dist from the ignore list when watching typescript sources', t => {
t.plan(1)

const parsedArgs = parseArgs(['--ignore-watch', '.ts$', 'app.ts'])

t.assert.strictEqual(parsedArgs.ignoreWatch, 'node_modules build .git bower_components logs .swp .nyc_output .ts$')
})

test('should read the ignore and follow watch options from the config file', t => {
t.plan(3)

const parsedArgs = parseArgs(['--config', './test/data/watch-config.js', 'app.js'])

t.assert.strictEqual(parsedArgs.ignoreWatch, 'node_modules build dist .git bower_components logs .swp .nyc_output ignoreme.js')
t.assert.strictEqual(parsedArgs.followWatch, 'followme.js')
t.assert.strictEqual(parsedArgs.trustProxy, true)
})
7 changes: 7 additions & 0 deletions test/cli.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,10 @@ test('generate --help', async t => {
readFileSync(path.join(__dirname, '../help/generate.txt'), 'utf-8')
)
})

test('version', async t => {
t.assert.equal(
execSync('node cli.js version').toString().trim(),
require('../package.json').version
)
})
10 changes: 10 additions & 0 deletions test/data/crashing-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict'

// Throws asynchronously once the server is up, to simulate an app crash
module.exports = function (fastify, opts, next) {
fastify.addHook('onReady', function (done) {
setTimeout(() => { throw new Error('async crash') }, 50)
done()
})
next()
}
7 changes: 7 additions & 0 deletions test/data/hanging-close-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict'

// Never completes the onClose hook, so close() hangs forever
module.exports = function (fastify, opts, next) {
fastify.addHook('onClose', function (instance, done) {})
next()
}
8 changes: 8 additions & 0 deletions test/data/ip-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict'

module.exports = function (fastify, opts, next) {
fastify.get('/', function (req, reply) {
reply.send({ ip: req.ip })
})
next()
}
6 changes: 6 additions & 0 deletions test/data/slow-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use strict'

// Takes a while to become ready, so the server is not up when the plugin loads
module.exports = function (fastify, opts, next) {
setTimeout(next, 1000)
}
7 changes: 7 additions & 0 deletions test/data/watch-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict'

module.exports = {
ignoreWatch: 'ignoreme.js',
followWatch: 'followme.js',
trustProxyEnabled: 'true'
}
48 changes: 48 additions & 0 deletions test/eject-errors.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict'

const { test } = require('node:test')
const fs = require('node:fs')
const os = require('node:os')
const path = require('node:path')
const { execFile } = require('node:child_process')
const { promisify } = require('node:util')
const proxyquire = require('proxyquire')
const rimraf = require('rimraf')

const pExecFile = promisify(execFile)
const ejectCli = path.join(__dirname, '..', 'eject.js')

function makeTmpDir (t) {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'fastify-cli-eject-'))
t.after(() => rimraf.sync(dir))
return dir
}

test('eject should reject when the template cannot be copied', async (t) => {
const { eject } = proxyquire('../eject', {
generify: (from, to, data, onFile, cb) => cb(new Error('copy failed'))
})

await t.assert.rejects(eject(makeTmpDir(t), 'eject'), /copy failed/)
})

test('cli should log the error and exit when eject fails', async (t) => {
const { cli } = proxyquire('../eject', {
generify: (from, to, data, onFile, cb) => cb(new Error('copy failed'))
})
t.mock.method(process, 'exit', () => {})
const log = t.mock.method(console, 'log', () => {})

await cli([])

t.assert.strictEqual(process.exit.mock.calls[0].arguments[0], 1)
t.assert.match(log.mock.calls.map(c => c.arguments.join(' ')).join('\n'), /copy failed/)
})

test('should eject the esm template when run directly', async (t) => {
const dir = makeTmpDir(t)
await pExecFile(process.execPath, [ejectCli, '--esm'], { cwd: dir })

t.assert.ok(fs.existsSync(path.join(dir, 'server.js')))
t.assert.match(fs.readFileSync(path.join(dir, 'server.js'), 'utf8'), /^import /m)
})
97 changes: 97 additions & 0 deletions test/generate-errors.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
'use strict'

const { test } = require('node:test')
const fs = require('node:fs')
const os = require('node:os')
const path = require('node:path')
const { execFile } = require('node:child_process')
const { promisify } = require('node:util')
const proxyquire = require('proxyquire')
const rimraf = require('rimraf')
const { javascriptTemplate } = require('../generate')

const pExecFile = promisify(execFile)
const generateCli = path.join(__dirname, '..', 'generate.js')

function makeTmpDir (t) {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'fastify-cli-generate-'))
t.after(() => rimraf.sync(dir))
return dir
}

function silence (t) {
t.mock.method(console, 'log', () => {})
}

test('generate should reject when the template cannot be copied', async (t) => {
silence(t)
const { generate } = proxyquire('../generate', {
generify: (from, to, data, onFile, cb) => cb(new Error('copy failed'))
})

await t.assert.rejects(generate(makeTmpDir(t), javascriptTemplate), /copy failed/)
})

test('generate should reject when package.json cannot be read', async (t) => {
silence(t)
const { generate } = proxyquire('../generate', {
'node:fs': {
...fs,
readFile: (file, cb) => cb(new Error('read failed'))
}
})

await t.assert.rejects(generate(makeTmpDir(t), javascriptTemplate), /read failed/)
})

test('generate should reject when package.json is not valid JSON', async (t) => {
silence(t)
const { generate } = proxyquire('../generate', {
'node:fs': {
...fs,
readFile: (file, cb) => cb(null, '{ not json')
}
})

await t.assert.rejects(generate(makeTmpDir(t), javascriptTemplate), SyntaxError)
})

test('generate should reject when package.json cannot be written', async (t) => {
silence(t)
const { generate } = proxyquire('../generate', {
'node:fs': {
...fs,
writeFile: (file, data, cb) => cb(new Error('write failed'))
}
})

await t.assert.rejects(generate(makeTmpDir(t), javascriptTemplate), /write failed/)
})

test('cli should exit with an error when generate fails', async (t) => {
const dir = makeTmpDir(t)
// an invalid package.json makes `npm init -y` fail inside generate()
fs.writeFileSync(path.join(dir, 'package.json'), '{ not json')

await t.assert.rejects(
pExecFile(process.execPath, [generateCli, '.', '--integrate'], { cwd: dir }),
err => {
t.assert.strictEqual(err.code, 1)
t.assert.match(err.stdout, /Command failed: npm init -y/)
return true
}
)
})

test('cli should generate a typescript esm project', async (t) => {
const dir = makeTmpDir(t)
const target = path.join(dir, 'app')
const { stdout } = await pExecFile(process.execPath, [generateCli, target, '--lang=ts', '--esm'], { cwd: dir })

t.assert.match(stdout, /generated successfully/)
const pkg = JSON.parse(fs.readFileSync(path.join(target, 'package.json'), 'utf8'))
t.assert.strictEqual(pkg.type, 'module')
t.assert.strictEqual(pkg.scripts.dev, 'fastify start -w -l info src/app.ts')
t.assert.strictEqual(pkg.scripts['dev:start'], undefined)
t.assert.ok(pkg.devDependencies.c8)
})
Loading