@fastify/static 6.1.0 → 6.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/index.d.ts +90 -73
- package/index.js +7 -5
- package/lib/dirList.js +19 -7
- package/package.json +6 -6
- package/test/dir-list.test.js +192 -3
- package/test/static-dotfiles/.aaa +0 -0
- package/test/static-dotfiles/dir/index.html +0 -0
- package/test/static-dotfiles/test.txt +0 -0
- package/test/static.test.js +6 -6
- package/test/types/index.ts +61 -3
package/README.md
CHANGED
|
@@ -190,7 +190,9 @@ If set, it provide the directory list calling the directory path.
|
|
|
190
190
|
|
|
191
191
|
Default response is json.
|
|
192
192
|
|
|
193
|
-
Note:
|
|
193
|
+
Note:
|
|
194
|
+
- Multi-root is not supported within the `list` option.
|
|
195
|
+
- If `dotfiles` option value is `deny` or `ignore`, dotfiles will be excluded.
|
|
194
196
|
|
|
195
197
|
**Example:**
|
|
196
198
|
|
package/index.d.ts
CHANGED
|
@@ -8,91 +8,108 @@ import { Stats } from 'fs';
|
|
|
8
8
|
declare module "fastify" {
|
|
9
9
|
interface FastifyReply {
|
|
10
10
|
sendFile(filename: string, rootPath?: string): FastifyReply;
|
|
11
|
-
sendFile(filename: string, options?: SendOptions): FastifyReply;
|
|
12
|
-
sendFile(filename: string, rootPath?: string, options?: SendOptions): FastifyReply;
|
|
13
|
-
download(filepath: string, options?: SendOptions): FastifyReply;
|
|
11
|
+
sendFile(filename: string, options?: fastifyStatic.SendOptions): FastifyReply;
|
|
12
|
+
sendFile(filename: string, rootPath?: string, options?: fastifyStatic.SendOptions): FastifyReply;
|
|
13
|
+
download(filepath: string, options?: fastifyStatic.SendOptions): FastifyReply;
|
|
14
14
|
download(filepath: string, filename?: string): FastifyReply;
|
|
15
|
-
download(filepath: string, filename?: string, options?: SendOptions): FastifyReply;
|
|
15
|
+
download(filepath: string, filename?: string, options?: fastifyStatic.SendOptions): FastifyReply;
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
fileCount: number;
|
|
21
|
-
totalFileCount: number;
|
|
22
|
-
folderCount: number;
|
|
23
|
-
totalFolderCount: number;
|
|
24
|
-
totalSize: number;
|
|
25
|
-
lastModified: number;
|
|
26
|
-
}
|
|
19
|
+
type FastifyStaticPlugin = FastifyPluginCallback<NonNullable<fastifyStatic.FastifyStaticOptions>>;
|
|
27
20
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
21
|
+
declare namespace fastifyStatic {
|
|
22
|
+
export interface ExtendedInformation {
|
|
23
|
+
fileCount: number;
|
|
24
|
+
totalFileCount: number;
|
|
25
|
+
folderCount: number;
|
|
26
|
+
totalFolderCount: number;
|
|
27
|
+
totalSize: number;
|
|
28
|
+
lastModified: number;
|
|
29
|
+
}
|
|
34
30
|
|
|
35
|
-
interface
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
31
|
+
export interface ListDir {
|
|
32
|
+
href: string;
|
|
33
|
+
name: string;
|
|
34
|
+
stats: Stats;
|
|
35
|
+
extendedInfo?: ExtendedInformation;
|
|
36
|
+
}
|
|
40
37
|
|
|
41
|
-
interface
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
export interface ListFile {
|
|
39
|
+
href: string;
|
|
40
|
+
name: string;
|
|
41
|
+
stats: Stats;
|
|
42
|
+
}
|
|
44
43
|
|
|
45
|
-
interface
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
render: ListRender;
|
|
49
|
-
extendedFolderInfo?: boolean;
|
|
50
|
-
jsonFormat?: 'names' | 'extended';
|
|
51
|
-
}
|
|
44
|
+
export interface ListRender {
|
|
45
|
+
(dirs: ListDir[], files: ListFile[]): string;
|
|
46
|
+
}
|
|
52
47
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
etag?: boolean;
|
|
59
|
-
extensions?: string[];
|
|
60
|
-
immutable?: boolean;
|
|
61
|
-
index?: string[] | string | false;
|
|
62
|
-
lastModified?: boolean;
|
|
63
|
-
maxAge?: string | number;
|
|
64
|
-
}
|
|
48
|
+
export interface ListOptions {
|
|
49
|
+
names?: string[];
|
|
50
|
+
extendedFolderInfo?: boolean;
|
|
51
|
+
jsonFormat?: 'names' | 'extended';
|
|
52
|
+
}
|
|
65
53
|
|
|
66
|
-
export interface
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
list?: boolean | ListOptions;
|
|
77
|
-
allowedPath?: (pathName: string, root?: string) => boolean;
|
|
78
|
-
/**
|
|
79
|
-
* @description
|
|
80
|
-
* Opt-in to looking for pre-compressed files
|
|
81
|
-
*/
|
|
82
|
-
preCompressed?: boolean;
|
|
54
|
+
export interface ListOptionsJsonFormat extends ListOptions {
|
|
55
|
+
format: 'json';
|
|
56
|
+
// Required when the URL parameter `format=html` exists
|
|
57
|
+
render?: ListRender;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface ListOptionsHtmlFormat extends ListOptions {
|
|
61
|
+
format: 'html';
|
|
62
|
+
render: ListRender;
|
|
63
|
+
}
|
|
83
64
|
|
|
84
65
|
// Passed on to `send`
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
66
|
+
export interface SendOptions {
|
|
67
|
+
acceptRanges?: boolean;
|
|
68
|
+
cacheControl?: boolean;
|
|
69
|
+
dotfiles?: 'allow' | 'deny' | 'ignore';
|
|
70
|
+
etag?: boolean;
|
|
71
|
+
extensions?: string[];
|
|
72
|
+
immutable?: boolean;
|
|
73
|
+
index?: string[] | string | false;
|
|
74
|
+
lastModified?: boolean;
|
|
75
|
+
maxAge?: string | number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface FastifyStaticOptions extends SendOptions {
|
|
79
|
+
root: string | string[];
|
|
80
|
+
prefix?: string;
|
|
81
|
+
prefixAvoidTrailingSlash?: boolean;
|
|
82
|
+
serve?: boolean;
|
|
83
|
+
decorateReply?: boolean;
|
|
84
|
+
schemaHide?: boolean;
|
|
85
|
+
setHeaders?: (...args: any[]) => void;
|
|
86
|
+
redirect?: boolean;
|
|
87
|
+
wildcard?: boolean;
|
|
88
|
+
list?: boolean | ListOptionsJsonFormat | ListOptionsHtmlFormat;
|
|
89
|
+
allowedPath?: (pathName: string, root?: string) => boolean;
|
|
90
|
+
/**
|
|
91
|
+
* @description
|
|
92
|
+
* Opt-in to looking for pre-compressed files
|
|
93
|
+
*/
|
|
94
|
+
preCompressed?: boolean;
|
|
95
|
+
|
|
96
|
+
// Passed on to `send`
|
|
97
|
+
acceptRanges?: boolean;
|
|
98
|
+
cacheControl?: boolean;
|
|
99
|
+
dotfiles?: 'allow' | 'deny' | 'ignore';
|
|
100
|
+
etag?: boolean;
|
|
101
|
+
extensions?: string[];
|
|
102
|
+
immutable?: boolean;
|
|
103
|
+
index?: string[] | string | false;
|
|
104
|
+
lastModified?: boolean;
|
|
105
|
+
maxAge?: string | number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const fastifyStatic: FastifyStaticPlugin;
|
|
109
|
+
|
|
110
|
+
export { fastifyStatic as default };
|
|
94
111
|
}
|
|
95
112
|
|
|
96
|
-
|
|
113
|
+
declare function fastifyStatic(...params: Parameters<FastifyStaticPlugin>): ReturnType<FastifyStaticPlugin>;
|
|
97
114
|
|
|
98
|
-
export
|
|
115
|
+
export = fastifyStatic;
|
package/index.js
CHANGED
|
@@ -9,7 +9,7 @@ const contentDisposition = require('content-disposition')
|
|
|
9
9
|
const fp = require('fastify-plugin')
|
|
10
10
|
const util = require('util')
|
|
11
11
|
const globPromise = util.promisify(glob)
|
|
12
|
-
const encodingNegotiator = require('
|
|
12
|
+
const encodingNegotiator = require('@fastify/accept-negotiator')
|
|
13
13
|
|
|
14
14
|
const dirList = require('./lib/dirList')
|
|
15
15
|
|
|
@@ -161,7 +161,8 @@ async function fastifyStatic (fastify, opts) {
|
|
|
161
161
|
dir: path,
|
|
162
162
|
options: opts.list,
|
|
163
163
|
route: pathname,
|
|
164
|
-
prefix
|
|
164
|
+
prefix,
|
|
165
|
+
dotfiles: opts.dotfiles
|
|
165
166
|
}).catch((err) => reply.send(err))
|
|
166
167
|
return
|
|
167
168
|
}
|
|
@@ -194,7 +195,7 @@ async function fastifyStatic (fastify, opts) {
|
|
|
194
195
|
|
|
195
196
|
stream.on('error', function (err) {
|
|
196
197
|
if (err.code === 'ENOENT') {
|
|
197
|
-
// when preCompress is enabled and the path is a
|
|
198
|
+
// when preCompress is enabled and the path is a directory without a trailing slash
|
|
198
199
|
if (opts.preCompressed && encoding) {
|
|
199
200
|
const indexPathname = findIndexFile(pathname, options.root, options.index)
|
|
200
201
|
if (indexPathname) {
|
|
@@ -217,7 +218,8 @@ async function fastifyStatic (fastify, opts) {
|
|
|
217
218
|
dir: dirList.path(opts.root, pathname),
|
|
218
219
|
options: opts.list,
|
|
219
220
|
route: pathname,
|
|
220
|
-
prefix
|
|
221
|
+
prefix,
|
|
222
|
+
dotfiles: opts.dotfiles
|
|
221
223
|
}).catch((err) => reply.send(err))
|
|
222
224
|
return
|
|
223
225
|
}
|
|
@@ -492,7 +494,7 @@ function getEncodingExtension (encoding) {
|
|
|
492
494
|
|
|
493
495
|
function getRedirectUrl (url) {
|
|
494
496
|
let i = 0
|
|
495
|
-
// we
|
|
497
|
+
// we detect how many slash before a valid path
|
|
496
498
|
for (i; i < url.length; i++) {
|
|
497
499
|
if (url[i] !== '/' && url[i] !== '\\') break
|
|
498
500
|
}
|
package/lib/dirList.js
CHANGED
|
@@ -8,12 +8,16 @@ const dirList = {
|
|
|
8
8
|
/**
|
|
9
9
|
* get files and dirs from dir, or error
|
|
10
10
|
* @param {string} dir full path fs dir
|
|
11
|
-
* @param {
|
|
11
|
+
* @param {(boolean | ListOptionsJsonFormat | ListOptionsHtmlFormat)} options
|
|
12
|
+
* @param {string} dotfiles
|
|
12
13
|
* note: can't use glob because don't get error on non existing dir
|
|
13
14
|
*/
|
|
14
|
-
list: async function (dir, options) {
|
|
15
|
+
list: async function (dir, options, dotfiles) {
|
|
15
16
|
const entries = { dirs: [], files: [] }
|
|
16
|
-
|
|
17
|
+
let files = await fs.readdir(dir)
|
|
18
|
+
if (dotfiles === 'deny' || dotfiles === 'ignore') {
|
|
19
|
+
files = files.filter(f => f.charAt(0) !== '.')
|
|
20
|
+
}
|
|
17
21
|
if (files.length < 1) {
|
|
18
22
|
return entries
|
|
19
23
|
}
|
|
@@ -95,13 +99,18 @@ const dirList = {
|
|
|
95
99
|
* send dir list content, or 404 on error
|
|
96
100
|
* @param {Fastify.Reply} reply
|
|
97
101
|
* @param {string} dir full path fs dir
|
|
98
|
-
* @param {
|
|
102
|
+
* @param {(boolean | ListOptionsJsonFormat | ListOptionsHtmlFormat)} options
|
|
99
103
|
* @param {string} route request route
|
|
104
|
+
* @param {string} dotfiles
|
|
100
105
|
*/
|
|
101
|
-
send: async function ({ reply, dir, options, route, prefix }) {
|
|
106
|
+
send: async function ({ reply, dir, options, route, prefix, dotfiles }) {
|
|
107
|
+
if (reply.request.query.format === 'html' && typeof options.render !== 'function') {
|
|
108
|
+
throw new Error('The `list.render` option must be a function and is required with the URL parameter `format=html`')
|
|
109
|
+
}
|
|
110
|
+
|
|
102
111
|
let entries
|
|
103
112
|
try {
|
|
104
|
-
entries = await dirList.list(dir, options)
|
|
113
|
+
entries = await dirList.list(dir, options, dotfiles)
|
|
105
114
|
} catch (error) {
|
|
106
115
|
return reply.callNotFound()
|
|
107
116
|
}
|
|
@@ -146,7 +155,7 @@ const dirList = {
|
|
|
146
155
|
/**
|
|
147
156
|
* say if the route can be handled by dir list or not
|
|
148
157
|
* @param {string} route request route
|
|
149
|
-
* @param {
|
|
158
|
+
* @param {(boolean | ListOptionsJsonFormat | ListOptionsHtmlFormat)} options
|
|
150
159
|
* @return {boolean}
|
|
151
160
|
*/
|
|
152
161
|
handle: function (route, options) {
|
|
@@ -189,6 +198,9 @@ const dirList = {
|
|
|
189
198
|
if (options.list.names && !Array.isArray(options.list.names)) {
|
|
190
199
|
return new TypeError('The `list.names` option must be an array')
|
|
191
200
|
}
|
|
201
|
+
if (options.list.jsonFormat != null && options.list.jsonFormat !== 'names' && options.list.jsonFormat !== 'extended') {
|
|
202
|
+
return new TypeError('The `list.jsonFormat` option must be name or extended')
|
|
203
|
+
}
|
|
192
204
|
if (options.list.format === 'html' && typeof options.list.render !== 'function') {
|
|
193
205
|
return new TypeError('The `list.render` option must be a function and is required with html format')
|
|
194
206
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fastify/static",
|
|
3
|
-
"version": "6.1
|
|
3
|
+
"version": "6.3.1",
|
|
4
4
|
"description": "Plugin for serving static files as fast as possible.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -30,16 +30,16 @@
|
|
|
30
30
|
"homepage": "https://github.com/fastify/fastify-static",
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"content-disposition": "^0.5.3",
|
|
33
|
-
"
|
|
34
|
-
"fastify-plugin": "^
|
|
33
|
+
"@fastify/accept-negotiator": "^1.0.0",
|
|
34
|
+
"fastify-plugin": "^4.0.0",
|
|
35
35
|
"glob": "^8.0.1",
|
|
36
36
|
"p-limit": "^3.1.0",
|
|
37
|
-
"readable-stream": "^
|
|
37
|
+
"readable-stream": "^4.0.0",
|
|
38
38
|
"send": "^0.18.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@fastify/compress": "^6.0.0",
|
|
42
|
-
"@types/node": "^
|
|
42
|
+
"@types/node": "^18.0.0",
|
|
43
43
|
"@typescript-eslint/eslint-plugin": "^2.29.0",
|
|
44
44
|
"@typescript-eslint/parser": "^2.29.0",
|
|
45
45
|
"concat-stream": "^2.0.0",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"snazzy": "^9.0.0",
|
|
54
54
|
"standard": "^17.0.0",
|
|
55
55
|
"tap": "^16.0.0",
|
|
56
|
-
"tsd": "^0.
|
|
56
|
+
"tsd": "^0.22.0",
|
|
57
57
|
"typescript": "^4.0.2"
|
|
58
58
|
},
|
|
59
59
|
"tsd": {
|
package/test/dir-list.test.js
CHANGED
|
@@ -40,7 +40,7 @@ t.test('throws when `root` is an array', t => {
|
|
|
40
40
|
t.equal(err.message, 'multi-root with list option is not supported')
|
|
41
41
|
})
|
|
42
42
|
|
|
43
|
-
t.test('throws when `list.format option
|
|
43
|
+
t.test('throws when `list.format` option is invalid', t => {
|
|
44
44
|
t.plan(2)
|
|
45
45
|
|
|
46
46
|
const err = dirList.validateOptions({ list: { format: 'hello' } })
|
|
@@ -56,6 +56,14 @@ t.test('throws when `list.names option` is not an array', t => {
|
|
|
56
56
|
t.equal(err.message, 'The `list.names` option must be an array')
|
|
57
57
|
})
|
|
58
58
|
|
|
59
|
+
t.test('throws when `list.jsonFormat` option is invalid', t => {
|
|
60
|
+
t.plan(2)
|
|
61
|
+
|
|
62
|
+
const err = dirList.validateOptions({ list: { jsonFormat: 'hello' } })
|
|
63
|
+
t.type(err, TypeError)
|
|
64
|
+
t.equal(err.message, 'The `list.jsonFormat` option must be name or extended')
|
|
65
|
+
})
|
|
66
|
+
|
|
59
67
|
t.test('throws when `list.format` is html and `list render` is not a function', t => {
|
|
60
68
|
t.plan(2)
|
|
61
69
|
|
|
@@ -469,7 +477,103 @@ t.test('dir list json format - extended info', t => {
|
|
|
469
477
|
})
|
|
470
478
|
})
|
|
471
479
|
|
|
472
|
-
t.test('
|
|
480
|
+
t.test('json format with url parameter format', t => {
|
|
481
|
+
t.plan(13)
|
|
482
|
+
|
|
483
|
+
const options = {
|
|
484
|
+
root: path.join(__dirname, '/static'),
|
|
485
|
+
prefix: '/public',
|
|
486
|
+
index: false,
|
|
487
|
+
list: {
|
|
488
|
+
format: 'json',
|
|
489
|
+
render (dirs, files) {
|
|
490
|
+
return 'html'
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
const route = '/public/'
|
|
495
|
+
const jsonContent = { dirs: ['deep', 'shallow'], files: ['.example', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] }
|
|
496
|
+
|
|
497
|
+
helper.arrange(t, options, (url) => {
|
|
498
|
+
simple.concat({
|
|
499
|
+
method: 'GET',
|
|
500
|
+
url: url + route
|
|
501
|
+
}, (err, response, body) => {
|
|
502
|
+
t.error(err)
|
|
503
|
+
t.equal(response.statusCode, 200)
|
|
504
|
+
t.equal(body.toString(), JSON.stringify(jsonContent))
|
|
505
|
+
t.ok(response.headers['content-type'].includes('application/json'))
|
|
506
|
+
})
|
|
507
|
+
|
|
508
|
+
simple.concat({
|
|
509
|
+
method: 'GET',
|
|
510
|
+
url: url + route + '?format=html'
|
|
511
|
+
}, (err, response, body) => {
|
|
512
|
+
t.error(err)
|
|
513
|
+
t.equal(response.statusCode, 200)
|
|
514
|
+
t.equal(body.toString(), 'html')
|
|
515
|
+
t.ok(response.headers['content-type'].includes('text/html'))
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
simple.concat({
|
|
519
|
+
method: 'GET',
|
|
520
|
+
url: url + route + '?format=json'
|
|
521
|
+
}, (err, response, body) => {
|
|
522
|
+
t.error(err)
|
|
523
|
+
t.equal(response.statusCode, 200)
|
|
524
|
+
t.equal(body.toString(), JSON.stringify(jsonContent))
|
|
525
|
+
t.ok(response.headers['content-type'].includes('application/json'))
|
|
526
|
+
})
|
|
527
|
+
})
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
t.test('json format with url parameter format and without render option', t => {
|
|
531
|
+
t.plan(12)
|
|
532
|
+
|
|
533
|
+
const options = {
|
|
534
|
+
root: path.join(__dirname, '/static'),
|
|
535
|
+
prefix: '/public',
|
|
536
|
+
index: false,
|
|
537
|
+
list: {
|
|
538
|
+
format: 'json'
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
const route = '/public/'
|
|
542
|
+
const jsonContent = { dirs: ['deep', 'shallow'], files: ['.example', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] }
|
|
543
|
+
|
|
544
|
+
helper.arrange(t, options, (url) => {
|
|
545
|
+
simple.concat({
|
|
546
|
+
method: 'GET',
|
|
547
|
+
url: url + route
|
|
548
|
+
}, (err, response, body) => {
|
|
549
|
+
t.error(err)
|
|
550
|
+
t.equal(response.statusCode, 200)
|
|
551
|
+
t.equal(body.toString(), JSON.stringify(jsonContent))
|
|
552
|
+
t.ok(response.headers['content-type'].includes('application/json'))
|
|
553
|
+
})
|
|
554
|
+
|
|
555
|
+
simple.concat({
|
|
556
|
+
method: 'GET',
|
|
557
|
+
url: url + route + '?format=html'
|
|
558
|
+
}, (err, response, body) => {
|
|
559
|
+
t.error(err)
|
|
560
|
+
t.equal(response.statusCode, 500)
|
|
561
|
+
t.equal(JSON.parse(body.toString()).message, 'The `list.render` option must be a function and is required with the URL parameter `format=html`')
|
|
562
|
+
})
|
|
563
|
+
|
|
564
|
+
simple.concat({
|
|
565
|
+
method: 'GET',
|
|
566
|
+
url: url + route + '?format=json'
|
|
567
|
+
}, (err, response, body) => {
|
|
568
|
+
t.error(err)
|
|
569
|
+
t.equal(response.statusCode, 200)
|
|
570
|
+
t.equal(body.toString(), JSON.stringify(jsonContent))
|
|
571
|
+
t.ok(response.headers['content-type'].includes('application/json'))
|
|
572
|
+
})
|
|
573
|
+
})
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
t.test('html format with url parameter format', t => {
|
|
473
577
|
t.plan(13)
|
|
474
578
|
|
|
475
579
|
const options = {
|
|
@@ -484,6 +588,7 @@ t.test('dir list - url parameter format', t => {
|
|
|
484
588
|
}
|
|
485
589
|
}
|
|
486
590
|
const route = '/public/'
|
|
591
|
+
const jsonContent = { dirs: ['deep', 'shallow'], files: ['.example', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] }
|
|
487
592
|
|
|
488
593
|
helper.arrange(t, options, (url) => {
|
|
489
594
|
simple.concat({
|
|
@@ -512,7 +617,7 @@ t.test('dir list - url parameter format', t => {
|
|
|
512
617
|
}, (err, response, body) => {
|
|
513
618
|
t.error(err)
|
|
514
619
|
t.equal(response.statusCode, 200)
|
|
515
|
-
t.
|
|
620
|
+
t.equal(body.toString(), JSON.stringify(jsonContent))
|
|
516
621
|
t.ok(response.headers['content-type'].includes('application/json'))
|
|
517
622
|
})
|
|
518
623
|
})
|
|
@@ -636,6 +741,90 @@ t.test('serve a non existent dir and get error', t => {
|
|
|
636
741
|
})
|
|
637
742
|
})
|
|
638
743
|
|
|
744
|
+
t.test('dir list with dotfiles allow option', t => {
|
|
745
|
+
t.plan(2)
|
|
746
|
+
|
|
747
|
+
const options = {
|
|
748
|
+
root: path.join(__dirname, '/static-dotfiles'),
|
|
749
|
+
prefix: '/public',
|
|
750
|
+
dotfiles: 'allow',
|
|
751
|
+
index: false,
|
|
752
|
+
list: true
|
|
753
|
+
}
|
|
754
|
+
const route = '/public/'
|
|
755
|
+
const content = { dirs: ['dir'], files: ['.aaa', 'test.txt'] }
|
|
756
|
+
|
|
757
|
+
helper.arrange(t, options, (url) => {
|
|
758
|
+
t.test(route, t => {
|
|
759
|
+
t.plan(3)
|
|
760
|
+
simple.concat({
|
|
761
|
+
method: 'GET',
|
|
762
|
+
url: url + route
|
|
763
|
+
}, (err, response, body) => {
|
|
764
|
+
t.error(err)
|
|
765
|
+
t.equal(response.statusCode, 200)
|
|
766
|
+
t.equal(body.toString(), JSON.stringify(content))
|
|
767
|
+
})
|
|
768
|
+
})
|
|
769
|
+
})
|
|
770
|
+
})
|
|
771
|
+
|
|
772
|
+
t.test('dir list with dotfiles deny option', t => {
|
|
773
|
+
t.plan(2)
|
|
774
|
+
|
|
775
|
+
const options = {
|
|
776
|
+
root: path.join(__dirname, '/static-dotfiles'),
|
|
777
|
+
prefix: '/public',
|
|
778
|
+
dotfiles: 'deny',
|
|
779
|
+
index: false,
|
|
780
|
+
list: true
|
|
781
|
+
}
|
|
782
|
+
const route = '/public/'
|
|
783
|
+
const content = { dirs: ['dir'], files: ['test.txt'] }
|
|
784
|
+
|
|
785
|
+
helper.arrange(t, options, (url) => {
|
|
786
|
+
t.test(route, t => {
|
|
787
|
+
t.plan(3)
|
|
788
|
+
simple.concat({
|
|
789
|
+
method: 'GET',
|
|
790
|
+
url: url + route
|
|
791
|
+
}, (err, response, body) => {
|
|
792
|
+
t.error(err)
|
|
793
|
+
t.equal(response.statusCode, 200)
|
|
794
|
+
t.equal(body.toString(), JSON.stringify(content))
|
|
795
|
+
})
|
|
796
|
+
})
|
|
797
|
+
})
|
|
798
|
+
})
|
|
799
|
+
|
|
800
|
+
t.test('dir list with dotfiles ignore option', t => {
|
|
801
|
+
t.plan(2)
|
|
802
|
+
|
|
803
|
+
const options = {
|
|
804
|
+
root: path.join(__dirname, '/static-dotfiles'),
|
|
805
|
+
prefix: '/public',
|
|
806
|
+
dotfiles: 'ignore',
|
|
807
|
+
index: false,
|
|
808
|
+
list: true
|
|
809
|
+
}
|
|
810
|
+
const route = '/public/'
|
|
811
|
+
const content = { dirs: ['dir'], files: ['test.txt'] }
|
|
812
|
+
|
|
813
|
+
helper.arrange(t, options, (url) => {
|
|
814
|
+
t.test(route, t => {
|
|
815
|
+
t.plan(3)
|
|
816
|
+
simple.concat({
|
|
817
|
+
method: 'GET',
|
|
818
|
+
url: url + route
|
|
819
|
+
}, (err, response, body) => {
|
|
820
|
+
t.error(err)
|
|
821
|
+
t.equal(response.statusCode, 200)
|
|
822
|
+
t.equal(body.toString(), JSON.stringify(content))
|
|
823
|
+
})
|
|
824
|
+
})
|
|
825
|
+
})
|
|
826
|
+
})
|
|
827
|
+
|
|
639
828
|
t.test('dir list error', t => {
|
|
640
829
|
t.plan(7)
|
|
641
830
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/test/static.test.js
CHANGED
|
@@ -1642,7 +1642,7 @@ t.test('with fastify-compress', t => {
|
|
|
1642
1642
|
})
|
|
1643
1643
|
})
|
|
1644
1644
|
t.test('register /static/ with schemaHide true', t => {
|
|
1645
|
-
t.plan(
|
|
1645
|
+
t.plan(4)
|
|
1646
1646
|
|
|
1647
1647
|
const pluginOptions = {
|
|
1648
1648
|
root: path.join(__dirname, '/static'),
|
|
@@ -1682,7 +1682,7 @@ t.test('register /static/ with schemaHide true', t => {
|
|
|
1682
1682
|
})
|
|
1683
1683
|
|
|
1684
1684
|
t.test('register /static/ with schemaHide false', t => {
|
|
1685
|
-
t.plan(
|
|
1685
|
+
t.plan(4)
|
|
1686
1686
|
|
|
1687
1687
|
const pluginOptions = {
|
|
1688
1688
|
root: path.join(__dirname, '/static'),
|
|
@@ -1722,7 +1722,7 @@ t.test('register /static/ with schemaHide false', t => {
|
|
|
1722
1722
|
})
|
|
1723
1723
|
|
|
1724
1724
|
t.test('register /static/ without schemaHide', t => {
|
|
1725
|
-
t.plan(
|
|
1725
|
+
t.plan(4)
|
|
1726
1726
|
|
|
1727
1727
|
const pluginOptions = {
|
|
1728
1728
|
root: path.join(__dirname, '/static'),
|
|
@@ -2769,7 +2769,7 @@ t.test('inject support', async (t) => {
|
|
|
2769
2769
|
})
|
|
2770
2770
|
|
|
2771
2771
|
t.test('routes should use custom errorHandler premature stream close', t => {
|
|
2772
|
-
t.plan(
|
|
2772
|
+
t.plan(4)
|
|
2773
2773
|
|
|
2774
2774
|
const pluginOptions = {
|
|
2775
2775
|
root: path.join(__dirname, '/static'),
|
|
@@ -2804,7 +2804,7 @@ t.test('routes should use custom errorHandler premature stream close', t => {
|
|
|
2804
2804
|
})
|
|
2805
2805
|
|
|
2806
2806
|
t.test('routes should fallback to default errorHandler', t => {
|
|
2807
|
-
t.plan(
|
|
2807
|
+
t.plan(4)
|
|
2808
2808
|
|
|
2809
2809
|
const pluginOptions = {
|
|
2810
2810
|
root: path.join(__dirname, '/static'),
|
|
@@ -2840,7 +2840,7 @@ t.test('routes should fallback to default errorHandler', t => {
|
|
|
2840
2840
|
})
|
|
2841
2841
|
})
|
|
2842
2842
|
|
|
2843
|
-
t.test('
|
|
2843
|
+
t.test('percent encoded URLs in glob mode', (t) => {
|
|
2844
2844
|
t.plan(4)
|
|
2845
2845
|
|
|
2846
2846
|
const fastify = Fastify({})
|
package/test/types/index.ts
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
|
-
import fastify from 'fastify'
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import fastify, { FastifyInstance, FastifyPluginCallback } from 'fastify'
|
|
2
|
+
import { Server } from 'http';
|
|
3
|
+
import { expectAssignable, expectError, expectType } from 'tsd'
|
|
4
|
+
import * as fastifyStaticStar from '../..';
|
|
5
|
+
import fastifyStatic, {
|
|
6
|
+
FastifyStaticOptions,
|
|
7
|
+
fastifyStatic as fastifyStaticNamed,
|
|
8
|
+
} from '../..'
|
|
9
|
+
|
|
10
|
+
import fastifyStaticCjsImport = require('../..');
|
|
11
|
+
const fastifyStaticCjs = require('../..');
|
|
12
|
+
|
|
13
|
+
const app: FastifyInstance = fastify();
|
|
14
|
+
|
|
15
|
+
app.register(fastifyStatic);
|
|
16
|
+
app.register(fastifyStaticNamed);
|
|
17
|
+
app.register(fastifyStaticCjs);
|
|
18
|
+
app.register(fastifyStaticCjsImport.default);
|
|
19
|
+
app.register(fastifyStaticCjsImport.fastifyStatic);
|
|
20
|
+
app.register(fastifyStaticStar.default);
|
|
21
|
+
app.register(fastifyStaticStar.fastifyStatic);
|
|
22
|
+
|
|
23
|
+
expectType<FastifyPluginCallback<FastifyStaticOptions, Server>>(fastifyStatic);
|
|
24
|
+
expectType<FastifyPluginCallback<FastifyStaticOptions, Server>>(fastifyStaticNamed);
|
|
25
|
+
expectType<FastifyPluginCallback<FastifyStaticOptions, Server>>(fastifyStaticCjsImport.default);
|
|
26
|
+
expectType<FastifyPluginCallback<FastifyStaticOptions, Server>>(fastifyStaticCjsImport.fastifyStatic);
|
|
27
|
+
expectType<FastifyPluginCallback<FastifyStaticOptions, Server>>(fastifyStaticStar.default);
|
|
28
|
+
expectType<FastifyPluginCallback<FastifyStaticOptions, Server>>(
|
|
29
|
+
fastifyStaticStar.fastifyStatic
|
|
30
|
+
);
|
|
31
|
+
expectType<any>(fastifyStaticCjs);
|
|
4
32
|
|
|
5
33
|
const appWithImplicitHttp = fastify()
|
|
6
34
|
const options: FastifyStaticOptions = {
|
|
@@ -32,6 +60,36 @@ expectError<FastifyStaticOptions>({
|
|
|
32
60
|
wildcard: '**/**'
|
|
33
61
|
})
|
|
34
62
|
|
|
63
|
+
expectAssignable<FastifyStaticOptions>({
|
|
64
|
+
root: '',
|
|
65
|
+
list: {
|
|
66
|
+
format: 'json',
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
expectAssignable<FastifyStaticOptions>({
|
|
71
|
+
root: '',
|
|
72
|
+
list: {
|
|
73
|
+
format: 'json',
|
|
74
|
+
render: () => ''
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
expectAssignable<FastifyStaticOptions>({
|
|
79
|
+
root: '',
|
|
80
|
+
list: {
|
|
81
|
+
format: 'html',
|
|
82
|
+
render: () => ''
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
expectError<FastifyStaticOptions>({
|
|
87
|
+
root: '',
|
|
88
|
+
list: {
|
|
89
|
+
format: 'html',
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
|
|
35
93
|
appWithImplicitHttp
|
|
36
94
|
.register(fastifyStatic, options)
|
|
37
95
|
.after(() => {
|