@fastify/static 8.1.1 → 8.2.0
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/.github/workflows/ci.yml +6 -0
- package/README.md +36 -3
- package/index.js +2 -1
- package/package.json +4 -4
- package/test/static-filtered/bar.private +1 -0
- package/test/static-filtered/deep/path/to/baz.html +3 -0
- package/test/static-filtered/deep/path/to/baz.private +1 -0
- package/test/static-filtered/index.html +3 -0
- package/test/static.test.js +74 -0
- package/types/index.d.ts +1 -0
- package/types/index.test-d.ts +1 -0
package/.github/workflows/ci.yml
CHANGED
package/README.md
CHANGED
|
@@ -15,10 +15,10 @@ npm i @fastify/static
|
|
|
15
15
|
|
|
16
16
|
| Plugin version | Fastify version |
|
|
17
17
|
| ---------------|-----------------|
|
|
18
|
-
|
|
|
18
|
+
| `>=8.x` | `^5.x` |
|
|
19
19
|
| `^7.x` | `^4.x` |
|
|
20
|
-
|
|
|
21
|
-
|
|
|
20
|
+
| `>=5.x <7.x` | `^3.x` |
|
|
21
|
+
| `>=2.x <5.x` | `^2.x` |
|
|
22
22
|
| `^1.x` | `^1.x` |
|
|
23
23
|
|
|
24
24
|
|
|
@@ -111,6 +111,31 @@ fastify.get('/path/without/cache/control', function (req, reply) {
|
|
|
111
111
|
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
+
### Managing cache-control headers
|
|
115
|
+
|
|
116
|
+
Production sites should use a reverse-proxy to manage caching headers.
|
|
117
|
+
However, here is an example of using fastify-static to host a Single Page Application (for example a [vite.js](https://vite.dev/) build) with sane caching.
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
fastify.register(require('@fastify/static'), {
|
|
121
|
+
root: path.join(import.meta.dirname, 'dist'), // import.meta.dirname node.js >= v20.11.0
|
|
122
|
+
// By default all assets are immutable and can be cached for a long period due to cache bursting techniques
|
|
123
|
+
maxAge: '30d',
|
|
124
|
+
immutable: true,
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
// Explicitly reduce caching of assets that don't use cache bursting techniques
|
|
128
|
+
fastify.get('/', function (req, reply) {
|
|
129
|
+
// index.html should never be cached
|
|
130
|
+
reply.sendFile('index.html', {maxAge: 0, immutable: false})
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
fastify.get('/favicon.ico', function (req, reply) {
|
|
134
|
+
// favicon can be cached for a short period
|
|
135
|
+
reply.sendFile('favicon.ico', {maxAge: '1d', immutable: false})
|
|
136
|
+
})
|
|
137
|
+
```
|
|
138
|
+
|
|
114
139
|
### Options
|
|
115
140
|
|
|
116
141
|
#### `root` (required)
|
|
@@ -208,6 +233,14 @@ are applied for getting the file list.
|
|
|
208
233
|
|
|
209
234
|
This option cannot be `false` if `redirect` is `true` and `ignoreTrailingSlash` is `true`.
|
|
210
235
|
|
|
236
|
+
#### `globIgnore`
|
|
237
|
+
|
|
238
|
+
Default: `undefined`
|
|
239
|
+
|
|
240
|
+
This is passed to [`glob`](https://www.npmjs.com/package/glob)
|
|
241
|
+
as the `ignore` option. It can be used to ignore files or directories
|
|
242
|
+
when using the `wildcard: false` option.
|
|
243
|
+
|
|
211
244
|
#### `allowedPath`
|
|
212
245
|
|
|
213
246
|
Default: `(pathName, root, request) => true`
|
package/index.js
CHANGED
|
@@ -135,7 +135,7 @@ async function fastifyStatic (fastify, opts) {
|
|
|
135
135
|
rootPath = rootPath.split(path.win32.sep).join(path.posix.sep)
|
|
136
136
|
!rootPath.endsWith('/') && (rootPath += '/')
|
|
137
137
|
const files = await glob('**/**', {
|
|
138
|
-
cwd: rootPath, absolute: false, follow: true, nodir: true, dot: opts.serveDotFiles
|
|
138
|
+
cwd: rootPath, absolute: false, follow: true, nodir: true, dot: opts.serveDotFiles, ignore: opts.globIgnore
|
|
139
139
|
})
|
|
140
140
|
|
|
141
141
|
for (let file of files) {
|
|
@@ -284,6 +284,7 @@ async function fastifyStatic (fastify, opts) {
|
|
|
284
284
|
prefix,
|
|
285
285
|
dotfiles: opts.dotfiles
|
|
286
286
|
}).catch((err) => reply.send(err))
|
|
287
|
+
return
|
|
287
288
|
}
|
|
288
289
|
}
|
|
289
290
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fastify/static",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.2.0",
|
|
4
4
|
"description": "Plugin for serving static files as fast as possible.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
],
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@fastify/accept-negotiator": "^2.0.0",
|
|
62
|
-
"@fastify/send": "^
|
|
62
|
+
"@fastify/send": "^4.0.0",
|
|
63
63
|
"content-disposition": "^0.5.4",
|
|
64
64
|
"fastify-plugin": "^5.0.0",
|
|
65
65
|
"fastq": "^1.17.1",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"@fastify/compress": "^8.0.0",
|
|
70
70
|
"@fastify/pre-commit": "^2.1.0",
|
|
71
71
|
"@types/node": "^22.0.0",
|
|
72
|
-
"borp": "^0.
|
|
72
|
+
"borp": "^0.20.0",
|
|
73
73
|
"c8": "^10.1.3",
|
|
74
74
|
"concat-stream": "^2.0.0",
|
|
75
75
|
"eslint": "^9.17.0",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"neostandard": "^0.12.0",
|
|
78
78
|
"pino": "^9.1.0",
|
|
79
79
|
"proxyquire": "^2.1.3",
|
|
80
|
-
"tsd": "^0.
|
|
80
|
+
"tsd": "^0.32.0"
|
|
81
81
|
},
|
|
82
82
|
"tsd": {
|
|
83
83
|
"directory": "test/types"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bar
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
baz
|
package/test/static.test.js
CHANGED
|
@@ -3669,3 +3669,77 @@ test('register /static/ with custom log level', async t => {
|
|
|
3669
3669
|
t.assert.deepStrictEqual(response2.status, 304)
|
|
3670
3670
|
})
|
|
3671
3671
|
})
|
|
3672
|
+
|
|
3673
|
+
test('register with wildcard false and globIgnore', async t => {
|
|
3674
|
+
t.plan(5)
|
|
3675
|
+
|
|
3676
|
+
const indexContent = fs
|
|
3677
|
+
.readFileSync('./test/static-filtered/index.html')
|
|
3678
|
+
.toString('utf8')
|
|
3679
|
+
|
|
3680
|
+
const deepContent = fs
|
|
3681
|
+
.readFileSync('./test/static-filtered/deep/path/to/baz.html')
|
|
3682
|
+
.toString('utf8')
|
|
3683
|
+
|
|
3684
|
+
const pluginOptions = {
|
|
3685
|
+
root: path.join(__dirname, '/static-filtered'),
|
|
3686
|
+
wildcard: false,
|
|
3687
|
+
globIgnore: ['**/*.private']
|
|
3688
|
+
}
|
|
3689
|
+
const fastify = Fastify()
|
|
3690
|
+
fastify.register(fastifyStatic, pluginOptions)
|
|
3691
|
+
|
|
3692
|
+
t.after(() => fastify.close())
|
|
3693
|
+
|
|
3694
|
+
await fastify.listen({ port: 0 })
|
|
3695
|
+
|
|
3696
|
+
fastify.server.unref()
|
|
3697
|
+
|
|
3698
|
+
await t.test('/index.html', async t => {
|
|
3699
|
+
t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
|
|
3700
|
+
|
|
3701
|
+
const response = await fetch('http://localhost:' + fastify.server.address().port + '/index.html')
|
|
3702
|
+
t.assert.ok(response.ok)
|
|
3703
|
+
t.assert.deepStrictEqual(response.status, 200)
|
|
3704
|
+
t.assert.deepStrictEqual(await response.text(), indexContent)
|
|
3705
|
+
genericResponseChecks(t, response)
|
|
3706
|
+
})
|
|
3707
|
+
|
|
3708
|
+
await t.test('/bar.private', async t => {
|
|
3709
|
+
t.plan(2)
|
|
3710
|
+
|
|
3711
|
+
const response = await fetch('http://localhost:' + fastify.server.address().port + '/bar.private')
|
|
3712
|
+
t.assert.ok(!response.ok)
|
|
3713
|
+
t.assert.deepStrictEqual(response.status, 404)
|
|
3714
|
+
await response.text()
|
|
3715
|
+
})
|
|
3716
|
+
|
|
3717
|
+
await t.test('/', async (t) => {
|
|
3718
|
+
t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
|
|
3719
|
+
|
|
3720
|
+
const response = await fetch('http://localhost:' + fastify.server.address().port)
|
|
3721
|
+
t.assert.ok(response.ok)
|
|
3722
|
+
t.assert.deepStrictEqual(response.status, 200)
|
|
3723
|
+
t.assert.deepStrictEqual(await response.text(), indexContent)
|
|
3724
|
+
genericResponseChecks(t, response)
|
|
3725
|
+
})
|
|
3726
|
+
|
|
3727
|
+
await t.test('/deep/path/to/baz.html', async (t) => {
|
|
3728
|
+
t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
|
|
3729
|
+
|
|
3730
|
+
const response = await fetch('http://localhost:' + fastify.server.address().port + '/deep/path/to/baz.html')
|
|
3731
|
+
t.assert.ok(response.ok)
|
|
3732
|
+
t.assert.deepStrictEqual(response.status, 200)
|
|
3733
|
+
t.assert.deepStrictEqual(await response.text(), deepContent)
|
|
3734
|
+
genericResponseChecks(t, response)
|
|
3735
|
+
})
|
|
3736
|
+
|
|
3737
|
+
await t.test('/deep/path/to/baz.private', async (t) => {
|
|
3738
|
+
t.plan(2)
|
|
3739
|
+
|
|
3740
|
+
const response = await fetch('http://localhost:' + fastify.server.address().port + '/deep/path/to/baz.private')
|
|
3741
|
+
t.assert.ok(!response.ok)
|
|
3742
|
+
t.assert.deepStrictEqual(response.status, 404)
|
|
3743
|
+
await response.text()
|
|
3744
|
+
})
|
|
3745
|
+
})
|
package/types/index.d.ts
CHANGED
|
@@ -94,6 +94,7 @@ declare namespace fastifyStatic {
|
|
|
94
94
|
setHeaders?: (res: SetHeadersResponse, path: string, stat: Stats) => void;
|
|
95
95
|
redirect?: boolean;
|
|
96
96
|
wildcard?: boolean;
|
|
97
|
+
globIgnore?: string[];
|
|
97
98
|
list?: boolean | ListOptionsJsonFormat | ListOptionsHtmlFormat;
|
|
98
99
|
allowedPath?: (pathName: string, root: string, request: FastifyRequest) => boolean;
|
|
99
100
|
/**
|