diff --git a/index.d.ts b/index.d.ts index 043044e..df3f3ca 100644 --- a/index.d.ts +++ b/index.d.ts @@ -45,11 +45,13 @@ interface ListRender { interface ListOptions { names?: string[]; extendedFolderInfo?: boolean; + jsonFormat?: 'names' | 'extended'; } interface ListOptionsJsonFormat extends ListOptions { format: 'json'; - jsonFormat?: 'names' | 'extended'; + // Required when the URL parameter `format=html` exists + render?: ListRender; } interface ListOptionsHtmlFormat extends ListOptions { diff --git a/index.js b/index.js index 9e3fe0b..35ab8d4 100644 --- a/index.js +++ b/index.js @@ -195,7 +195,7 @@ async function fastifyStatic (fastify, opts) { stream.on('error', function (err) { if (err.code === 'ENOENT') { - // when preCompress is enabled and the path is a directoy without a trailing shash + // when preCompress is enabled and the path is a directory without a trailing slash if (opts.preCompressed && encoding) { const indexPathname = findIndexFile(pathname, options.root, options.index) if (indexPathname) { @@ -494,7 +494,7 @@ function getEncodingExtension (encoding) { function getRedirectUrl (url) { let i = 0 - // we detech how many slash before a valid path + // we detect how many slash before a valid path for (i; i < url.length; i++) { if (url[i] !== '/' && url[i] !== '\\') break } diff --git a/lib/dirList.js b/lib/dirList.js index 3b5f0cf..7aec88b 100644 --- a/lib/dirList.js +++ b/lib/dirList.js @@ -104,6 +104,10 @@ const dirList = { * @param {string} dotfiles */ send: async function ({ reply, dir, options, route, prefix, dotfiles }) { + if (reply.request.query.format === 'html' && typeof options.render !== 'function') { + throw new Error('The `list.render` option must be a function and is required with the URL parameter `format=html`') + } + let entries try { entries = await dirList.list(dir, options, dotfiles) @@ -200,9 +204,6 @@ const dirList = { if (options.list.format === 'html' && typeof options.list.render !== 'function') { return new TypeError('The `list.render` option must be a function and is required with html format') } - if (options.list.format === 'html' && options.list.jsonFormat != null) { - return new TypeError('The `list.jsonFormat` option must be with json format') - } } } diff --git a/test/dir-list.test.js b/test/dir-list.test.js index 7572fc4..1017d48 100644 --- a/test/dir-list.test.js +++ b/test/dir-list.test.js @@ -72,14 +72,6 @@ t.test('throws when `list.format` is html and `list render` is not a function', t.equal(err.message, 'The `list.render` option must be a function and is required with html format') }) -t.test('throws when `list.format` is html and `list.jsonFormat` is given', t => { - t.plan(2) - - const err = dirList.validateOptions({ list: { format: 'html', render: () => '', jsonFormat: 'extended' } }) - t.type(err, TypeError) - t.equal(err.message, 'The `list.jsonFormat` option must be with json format') -}) - t.test('dir list wrong options', t => { t.plan(3) @@ -485,7 +477,103 @@ t.test('dir list json format - extended info', t => { }) }) -t.test('dir list - url parameter format', t => { +t.test('json format with url parameter format', t => { + t.plan(13) + + const options = { + root: path.join(__dirname, '/static'), + prefix: '/public', + index: false, + list: { + format: 'json', + render (dirs, files) { + return 'html' + } + } + } + const route = '/public/' + const jsonContent = { dirs: ['deep', 'shallow'], files: ['.example', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] } + + helper.arrange(t, options, (url) => { + simple.concat({ + method: 'GET', + url: url + route + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 200) + t.equal(body.toString(), JSON.stringify(jsonContent)) + t.ok(response.headers['content-type'].includes('application/json')) + }) + + simple.concat({ + method: 'GET', + url: url + route + '?format=html' + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 200) + t.equal(body.toString(), 'html') + t.ok(response.headers['content-type'].includes('text/html')) + }) + + simple.concat({ + method: 'GET', + url: url + route + '?format=json' + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 200) + t.equal(body.toString(), JSON.stringify(jsonContent)) + t.ok(response.headers['content-type'].includes('application/json')) + }) + }) +}) + +t.test('json format with url parameter format and without render option', t => { + t.plan(12) + + const options = { + root: path.join(__dirname, '/static'), + prefix: '/public', + index: false, + list: { + format: 'json' + } + } + const route = '/public/' + const jsonContent = { dirs: ['deep', 'shallow'], files: ['.example', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] } + + helper.arrange(t, options, (url) => { + simple.concat({ + method: 'GET', + url: url + route + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 200) + t.equal(body.toString(), JSON.stringify(jsonContent)) + t.ok(response.headers['content-type'].includes('application/json')) + }) + + simple.concat({ + method: 'GET', + url: url + route + '?format=html' + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 500) + t.equal(JSON.parse(body.toString()).message, 'The `list.render` option must be a function and is required with the URL parameter `format=html`') + }) + + simple.concat({ + method: 'GET', + url: url + route + '?format=json' + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 200) + t.equal(body.toString(), JSON.stringify(jsonContent)) + t.ok(response.headers['content-type'].includes('application/json')) + }) + }) +}) + +t.test('html format with url parameter format', t => { t.plan(13) const options = { @@ -500,6 +588,7 @@ t.test('dir list - url parameter format', t => { } } const route = '/public/' + const jsonContent = { dirs: ['deep', 'shallow'], files: ['.example', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] } helper.arrange(t, options, (url) => { simple.concat({ @@ -528,7 +617,7 @@ t.test('dir list - url parameter format', t => { }, (err, response, body) => { t.error(err) t.equal(response.statusCode, 200) - t.ok(body.toString()) + t.equal(body.toString(), JSON.stringify(jsonContent)) t.ok(response.headers['content-type'].includes('application/json')) }) }) diff --git a/test/static.test.js b/test/static.test.js index bda3848..f3531cd 100644 --- a/test/static.test.js +++ b/test/static.test.js @@ -2840,7 +2840,7 @@ t.test('routes should fallback to default errorHandler', t => { }) }) -t.test('precent encoded URLs in glob mode', (t) => { +t.test('percent encoded URLs in glob mode', (t) => { t.plan(4) const fastify = Fastify({}) diff --git a/test/types/index.ts b/test/types/index.ts index c26dace..ab955b9 100644 --- a/test/types/index.ts +++ b/test/types/index.ts @@ -39,7 +39,7 @@ expectAssignable({ } }) -expectError({ +expectAssignable({ root: '', list: { format: 'json',