Skip to content

Commit

Permalink
feat: allow the headers option to accept function (#897)
Browse files Browse the repository at this point in the history
  • Loading branch information
anshumanv committed May 10, 2021
1 parent ee97029 commit 966afb3
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 37 deletions.
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,34 @@ This property allows a user to pass the list of HTTP request methods accepted by

### headers

Type: `Object`
Type: `Object|Function`
Default: `undefined`

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

or

```js
webpackDevMiddleware(compiler, {
headers: () => {
return {
'Last-Modified': new Date(),
};
},
});
```

or

```js
webpackDevMiddleware(compiler, {
headers: (req, res, context) => {
res.setHeader('Last-Modified', new Date());
},
});
```

### index

Type: `Boolean|String`
Expand Down
7 changes: 6 additions & 1 deletion src/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ export default function wrapper(context) {

async function processRequest() {
const filename = getFilenameFromUrl(context, req.url);
const { headers } = context.options;
let { headers } = context.options;

if (typeof headers === 'function') {
headers = headers(req, res, context);
}

let content;

if (!filename) {
Expand Down
9 changes: 8 additions & 1 deletion src/options.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,14 @@
}
},
"headers": {
"type": "object"
"anyOf": [
{
"type": "object"
},
{
"instanceof": "Function"
}
]
},
"publicPath": {
"description": "The `publicPath` specifies the public URL address of the output files when referenced in a browser.",
Expand Down
18 changes: 16 additions & 2 deletions test/__snapshots__/validation-options.test.js.snap.webpack4
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`validation should throw an error on the "headers" option with "1" value 1`] = `
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
- options.headers should be one of these:
object { … } | function
Details:
* options.headers should be an object:
object { … }
* options.headers should be an instance of function."
`;

exports[`validation should throw an error on the "headers" option with "true" value 1`] = `
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
- options.headers should be an object:
object { … }"
- options.headers should be one of these:
object { … } | function
Details:
* options.headers should be an object:
object { … }
* options.headers should be an instance of function."
`;

exports[`validation should throw an error on the "index" option with "{}" value 1`] = `
Expand Down
18 changes: 16 additions & 2 deletions test/__snapshots__/validation-options.test.js.snap.webpack5
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`validation should throw an error on the "headers" option with "1" value 1`] = `
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
- options.headers should be one of these:
object { … } | function
Details:
* options.headers should be an object:
object { … }
* options.headers should be an instance of function."
`;

exports[`validation should throw an error on the "headers" option with "true" value 1`] = `
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
- options.headers should be an object:
object { … }"
- options.headers should be one of these:
object { … } | function
Details:
* options.headers should be an object:
object { … }
* options.headers should be an instance of function."
`;

exports[`validation should throw an error on the "index" option with "{}" value 1`] = `
Expand Down
148 changes: 120 additions & 28 deletions test/middleware.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2392,45 +2392,137 @@ describe.each([
});

describe('headers option', () => {
beforeEach((done) => {
const compiler = getCompiler(webpackConfig);
describe('works with object', () => {
beforeEach((done) => {
const compiler = getCompiler(webpackConfig);

instance = middleware(compiler, {
headers: { 'X-nonsense-1': 'yes', 'X-nonsense-2': 'no' },
instance = middleware(compiler, {
headers: { 'X-nonsense-1': 'yes', 'X-nonsense-2': 'no' },
});

app = framework();
app.use(instance);

listen = listenShorthand(done);
});

app = framework();
app.use(instance);
afterEach(close);

listen = listenShorthand(done);
it('should return the "200" code for the "GET" request to the bundle file and return headers', (done) => {
request(app)
.get('/bundle.js')
.expect('X-nonsense-1', 'yes')
.expect('X-nonsense-2', 'no')
.expect(200, done);
});

it('should return the "200" code for the "GET" request to path not in outputFileSystem but not return headers', async () => {
app.use('/file.jpg', (req, res) => {
// Express API
if (res.send) {
res.send('welcome');
}
// Connect API
else {
res.end('welcome');
}
});

const res = await request(app).get('/file.jpg');
expect(res.statusCode).toEqual(200);
expect(res.headers['X-nonsense-1']).toBeUndefined();
expect(res.headers['X-nonsense-2']).toBeUndefined();
});
});
describe('works with function', () => {
beforeEach((done) => {
const compiler = getCompiler(webpackConfig);

afterEach(close);
instance = middleware(compiler, {
headers: () => {
return { 'X-nonsense-1': 'yes', 'X-nonsense-2': 'no' };
},
});

it('should return the "200" code for the "GET" request to the bundle file and return headers', (done) => {
request(app)
.get('/bundle.js')
.expect('X-nonsense-1', 'yes')
.expect('X-nonsense-2', 'no')
.expect(200, done);
app = framework();
app.use(instance);

listen = listenShorthand(done);
});

afterEach(close);

it('should return the "200" code for the "GET" request to the bundle file and return headers', (done) => {
request(app)
.get('/bundle.js')
.expect('X-nonsense-1', 'yes')
.expect('X-nonsense-2', 'no')
.expect(200, done);
});

it('should return the "200" code for the "GET" request to path not in outputFileSystem but not return headers', async () => {
app.use('/file.jpg', (req, res) => {
// Express API
if (res.send) {
res.send('welcome');
}
// Connect API
else {
res.end('welcome');
}
});

const res = await request(app).get('/file.jpg');
expect(res.statusCode).toEqual(200);
expect(res.headers['X-nonsense-1']).toBeUndefined();
expect(res.headers['X-nonsense-2']).toBeUndefined();
});
});
describe('works with headers function with params', () => {
beforeEach((done) => {
const compiler = getCompiler(webpackConfig);

it('should return the "200" code for the "GET" request to path not in outputFileSystem but not return headers', async () => {
app.use('/file.jpg', (req, res) => {
// Express API
if (res.send) {
res.send('welcome');
}
// Connect API
else {
res.end('welcome');
}
instance = middleware(compiler, {
// eslint-disable-next-line no-unused-vars
headers: (req, res, context) => {
res.setHeader('X-nonsense-1', 'yes');
res.setHeader('X-nonsense-2', 'no');
},
});

app = framework();
app.use(instance);

listen = listenShorthand(done);
});

const res = await request(app).get('/file.jpg');
expect(res.statusCode).toEqual(200);
expect(res.headers['X-nonsense-1']).toBeUndefined();
expect(res.headers['X-nonsense-2']).toBeUndefined();
afterEach(close);

it('should return the "200" code for the "GET" request to the bundle file and return headers', (done) => {
request(app)
.get('/bundle.js')
.expect('X-nonsense-1', 'yes')
.expect('X-nonsense-2', 'no')
.expect(200, done);
});

it('should return the "200" code for the "GET" request to path not in outputFileSystem but not return headers', async () => {
app.use('/file.jpg', (req, res) => {
// Express API
if (res.send) {
res.send('welcome');
}
// Connect API
else {
res.end('welcome');
}
});

const res = await request(app).get('/file.jpg');
expect(res.statusCode).toEqual(200);
expect(res.headers['X-nonsense-1']).toBeUndefined();
expect(res.headers['X-nonsense-2']).toBeUndefined();
});
});
});

Expand Down
4 changes: 2 additions & 2 deletions test/validation-options.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ describe('validation', () => {
failure: [{}, true],
},
headers: {
success: [{ 'X-Custom-Header': 'yes' }],
failure: [true],
success: [{ 'X-Custom-Header': 'yes' }, () => {}],
failure: [true, 1],
},
publicPath: {
success: ['/foo', '', 'auto', () => '/public/path'],
Expand Down

0 comments on commit 966afb3

Please sign in to comment.