Skip to content

Commit 966afb3

Browse files
authored
feat: allow the headers option to accept function (#897)
1 parent ee97029 commit 966afb3

7 files changed

+191
-37
lines changed

README.md

+23-1
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,34 @@ This property allows a user to pass the list of HTTP request methods accepted by
7070

7171
### headers
7272

73-
Type: `Object`
73+
Type: `Object|Function`
7474
Default: `undefined`
7575

7676
This property allows a user to pass custom HTTP headers on each request.
7777
eg. `{ "X-Custom-Header": "yes" }`
7878

79+
or
80+
81+
```js
82+
webpackDevMiddleware(compiler, {
83+
headers: () => {
84+
return {
85+
'Last-Modified': new Date(),
86+
};
87+
},
88+
});
89+
```
90+
91+
or
92+
93+
```js
94+
webpackDevMiddleware(compiler, {
95+
headers: (req, res, context) => {
96+
res.setHeader('Last-Modified', new Date());
97+
},
98+
});
99+
```
100+
79101
### index
80102

81103
Type: `Boolean|String`

src/middleware.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,12 @@ export default function wrapper(context) {
4242

4343
async function processRequest() {
4444
const filename = getFilenameFromUrl(context, req.url);
45-
const { headers } = context.options;
45+
let { headers } = context.options;
46+
47+
if (typeof headers === 'function') {
48+
headers = headers(req, res, context);
49+
}
50+
4651
let content;
4752

4853
if (!filename) {

src/options.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,14 @@
2525
}
2626
},
2727
"headers": {
28-
"type": "object"
28+
"anyOf": [
29+
{
30+
"type": "object"
31+
},
32+
{
33+
"instanceof": "Function"
34+
}
35+
]
2936
},
3037
"publicPath": {
3138
"description": "The `publicPath` specifies the public URL address of the output files when referenced in a browser.",

test/__snapshots__/validation-options.test.js.snap.webpack4

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`validation should throw an error on the "headers" option with "1" value 1`] = `
4+
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
5+
- options.headers should be one of these:
6+
object { … } | function
7+
Details:
8+
* options.headers should be an object:
9+
object { … }
10+
* options.headers should be an instance of function."
11+
`;
12+
313
exports[`validation should throw an error on the "headers" option with "true" value 1`] = `
414
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
5-
- options.headers should be an object:
6-
object { … }"
15+
- options.headers should be one of these:
16+
object { … } | function
17+
Details:
18+
* options.headers should be an object:
19+
object { … }
20+
* options.headers should be an instance of function."
721
`;
822

923
exports[`validation should throw an error on the "index" option with "{}" value 1`] = `

test/__snapshots__/validation-options.test.js.snap.webpack5

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`validation should throw an error on the "headers" option with "1" value 1`] = `
4+
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
5+
- options.headers should be one of these:
6+
object { … } | function
7+
Details:
8+
* options.headers should be an object:
9+
object { … }
10+
* options.headers should be an instance of function."
11+
`;
12+
313
exports[`validation should throw an error on the "headers" option with "true" value 1`] = `
414
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
5-
- options.headers should be an object:
6-
object { … }"
15+
- options.headers should be one of these:
16+
object { … } | function
17+
Details:
18+
* options.headers should be an object:
19+
object { … }
20+
* options.headers should be an instance of function."
721
`;
822

923
exports[`validation should throw an error on the "index" option with "{}" value 1`] = `

test/middleware.test.js

+120-28
Original file line numberDiff line numberDiff line change
@@ -2392,45 +2392,137 @@ describe.each([
23922392
});
23932393

23942394
describe('headers option', () => {
2395-
beforeEach((done) => {
2396-
const compiler = getCompiler(webpackConfig);
2395+
describe('works with object', () => {
2396+
beforeEach((done) => {
2397+
const compiler = getCompiler(webpackConfig);
23972398

2398-
instance = middleware(compiler, {
2399-
headers: { 'X-nonsense-1': 'yes', 'X-nonsense-2': 'no' },
2399+
instance = middleware(compiler, {
2400+
headers: { 'X-nonsense-1': 'yes', 'X-nonsense-2': 'no' },
2401+
});
2402+
2403+
app = framework();
2404+
app.use(instance);
2405+
2406+
listen = listenShorthand(done);
24002407
});
24012408

2402-
app = framework();
2403-
app.use(instance);
2409+
afterEach(close);
24042410

2405-
listen = listenShorthand(done);
2411+
it('should return the "200" code for the "GET" request to the bundle file and return headers', (done) => {
2412+
request(app)
2413+
.get('/bundle.js')
2414+
.expect('X-nonsense-1', 'yes')
2415+
.expect('X-nonsense-2', 'no')
2416+
.expect(200, done);
2417+
});
2418+
2419+
it('should return the "200" code for the "GET" request to path not in outputFileSystem but not return headers', async () => {
2420+
app.use('/file.jpg', (req, res) => {
2421+
// Express API
2422+
if (res.send) {
2423+
res.send('welcome');
2424+
}
2425+
// Connect API
2426+
else {
2427+
res.end('welcome');
2428+
}
2429+
});
2430+
2431+
const res = await request(app).get('/file.jpg');
2432+
expect(res.statusCode).toEqual(200);
2433+
expect(res.headers['X-nonsense-1']).toBeUndefined();
2434+
expect(res.headers['X-nonsense-2']).toBeUndefined();
2435+
});
24062436
});
2437+
describe('works with function', () => {
2438+
beforeEach((done) => {
2439+
const compiler = getCompiler(webpackConfig);
24072440

2408-
afterEach(close);
2441+
instance = middleware(compiler, {
2442+
headers: () => {
2443+
return { 'X-nonsense-1': 'yes', 'X-nonsense-2': 'no' };
2444+
},
2445+
});
24092446

2410-
it('should return the "200" code for the "GET" request to the bundle file and return headers', (done) => {
2411-
request(app)
2412-
.get('/bundle.js')
2413-
.expect('X-nonsense-1', 'yes')
2414-
.expect('X-nonsense-2', 'no')
2415-
.expect(200, done);
2447+
app = framework();
2448+
app.use(instance);
2449+
2450+
listen = listenShorthand(done);
2451+
});
2452+
2453+
afterEach(close);
2454+
2455+
it('should return the "200" code for the "GET" request to the bundle file and return headers', (done) => {
2456+
request(app)
2457+
.get('/bundle.js')
2458+
.expect('X-nonsense-1', 'yes')
2459+
.expect('X-nonsense-2', 'no')
2460+
.expect(200, done);
2461+
});
2462+
2463+
it('should return the "200" code for the "GET" request to path not in outputFileSystem but not return headers', async () => {
2464+
app.use('/file.jpg', (req, res) => {
2465+
// Express API
2466+
if (res.send) {
2467+
res.send('welcome');
2468+
}
2469+
// Connect API
2470+
else {
2471+
res.end('welcome');
2472+
}
2473+
});
2474+
2475+
const res = await request(app).get('/file.jpg');
2476+
expect(res.statusCode).toEqual(200);
2477+
expect(res.headers['X-nonsense-1']).toBeUndefined();
2478+
expect(res.headers['X-nonsense-2']).toBeUndefined();
2479+
});
24162480
});
2481+
describe('works with headers function with params', () => {
2482+
beforeEach((done) => {
2483+
const compiler = getCompiler(webpackConfig);
24172484

2418-
it('should return the "200" code for the "GET" request to path not in outputFileSystem but not return headers', async () => {
2419-
app.use('/file.jpg', (req, res) => {
2420-
// Express API
2421-
if (res.send) {
2422-
res.send('welcome');
2423-
}
2424-
// Connect API
2425-
else {
2426-
res.end('welcome');
2427-
}
2485+
instance = middleware(compiler, {
2486+
// eslint-disable-next-line no-unused-vars
2487+
headers: (req, res, context) => {
2488+
res.setHeader('X-nonsense-1', 'yes');
2489+
res.setHeader('X-nonsense-2', 'no');
2490+
},
2491+
});
2492+
2493+
app = framework();
2494+
app.use(instance);
2495+
2496+
listen = listenShorthand(done);
24282497
});
24292498

2430-
const res = await request(app).get('/file.jpg');
2431-
expect(res.statusCode).toEqual(200);
2432-
expect(res.headers['X-nonsense-1']).toBeUndefined();
2433-
expect(res.headers['X-nonsense-2']).toBeUndefined();
2499+
afterEach(close);
2500+
2501+
it('should return the "200" code for the "GET" request to the bundle file and return headers', (done) => {
2502+
request(app)
2503+
.get('/bundle.js')
2504+
.expect('X-nonsense-1', 'yes')
2505+
.expect('X-nonsense-2', 'no')
2506+
.expect(200, done);
2507+
});
2508+
2509+
it('should return the "200" code for the "GET" request to path not in outputFileSystem but not return headers', async () => {
2510+
app.use('/file.jpg', (req, res) => {
2511+
// Express API
2512+
if (res.send) {
2513+
res.send('welcome');
2514+
}
2515+
// Connect API
2516+
else {
2517+
res.end('welcome');
2518+
}
2519+
});
2520+
2521+
const res = await request(app).get('/file.jpg');
2522+
expect(res.statusCode).toEqual(200);
2523+
expect(res.headers['X-nonsense-1']).toBeUndefined();
2524+
expect(res.headers['X-nonsense-2']).toBeUndefined();
2525+
});
24342526
});
24352527
});
24362528

test/validation-options.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ describe('validation', () => {
2828
failure: [{}, true],
2929
},
3030
headers: {
31-
success: [{ 'X-Custom-Header': 'yes' }],
32-
failure: [true],
31+
success: [{ 'X-Custom-Header': 'yes' }, () => {}],
32+
failure: [true, 1],
3333
},
3434
publicPath: {
3535
success: ['/foo', '', 'auto', () => '/public/path'],

0 commit comments

Comments
 (0)