Skip to content

Commit

Permalink
feat: allow array for headers option (#1042)
Browse files Browse the repository at this point in the history
  • Loading branch information
snitin315 committed Sep 21, 2021
1 parent 961e0ac commit 5a6a3f0
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 12 deletions.
38 changes: 37 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ This property allows a user to pass the list of HTTP request methods accepted by

### headers

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

This property allows a user to pass custom HTTP headers on each request.
Expand Down Expand Up @@ -98,6 +98,42 @@ webpackDevMiddleware(compiler, {
});
```

or

```js
webpackDevMiddleware(compiler, {
headers: [
{
key: "X-custom-header"
value: "foo"
},
{
key: "Y-custom-header",
value: "bar"
}
]
},
});
```

or

```js
webpackDevMiddleware(compiler, {
headers: () => [
{
key: "X-custom-header"
value: "foo"
},
{
key: "Y-custom-header",
value: "bar"
}
]
},
});
```

### index

Type: `Boolean|String`
Expand Down
14 changes: 10 additions & 4 deletions src/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,20 @@ export default function wrapper(context) {
headers = headers(req, res, context);
}

if (headers) {
const names = Object.keys(headers);
const allHeaders = [];

for (const name of names) {
setHeaderForResponse(res, name, headers[name]);
if (!Array.isArray(headers)) {
// eslint-disable-next-line guard-for-in
for (const name in headers) {
allHeaders.push({ key: name, value: headers[name] });
}
headers = allHeaders;
}

headers.forEach((header) => {
setHeaderForResponse(res, header.key, header.value);
});

if (!getHeaderFromResponse(res, "Content-Type")) {
// content-type name(like application/javascript; charset=utf-8) or false
const contentType = mime.contentType(path.extname(filename));
Expand Down
18 changes: 18 additions & 0 deletions src/options.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,24 @@
},
"headers": {
"anyOf": [
{
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"key": {
"description": "key of header.",
"type": "string"
},
"value": {
"description": "value of header.",
"type": "string"
}
}
},
"minItems": 1
},
{
"type": "object"
},
Expand Down
19 changes: 17 additions & 2 deletions test/__snapshots__/validation-options.test.js.snap.webpack4
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`validation should throw an error on the "headers" option with "[]" 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 a non-empty array."
`;

exports[`validation should throw an error on the "headers" option with "[{"foo":"bar"}]" value 1`] = `
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
- options.headers[0] has an unknown property 'foo'. These properties are valid:
object { key?, value? }"
`;

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
[object { key?, value? }, ...] (should not have fewer than 1 item) | object { … } | function
-> Allows to pass custom HTTP headers on each request
-> Read more at https://github.com/webpack/webpack-dev-middleware#headers
Details:
* options.headers should be an array:
[object { key?, value? }, ...] (should not have fewer than 1 item)
* options.headers should be an object:
object { … }
* options.headers should be an instance of function."
Expand All @@ -15,10 +28,12 @@ exports[`validation should throw an error on the "headers" option with "1" value
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 one of these:
object { … } | function
[object { key?, value? }, ...] (should not have fewer than 1 item) | object { … } | function
-> Allows to pass custom HTTP headers on each request
-> Read more at https://github.com/webpack/webpack-dev-middleware#headers
Details:
* options.headers should be an array:
[object { key?, value? }, ...] (should not have fewer than 1 item)
* options.headers should be an object:
object { … }
* options.headers should be an instance of function."
Expand Down
19 changes: 17 additions & 2 deletions test/__snapshots__/validation-options.test.js.snap.webpack5
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`validation should throw an error on the "headers" option with "[]" 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 a non-empty array."
`;

exports[`validation should throw an error on the "headers" option with "[{"foo":"bar"}]" value 1`] = `
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
- options.headers[0] has an unknown property 'foo'. These properties are valid:
object { key?, value? }"
`;

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
[object { key?, value? }, ...] (should not have fewer than 1 item) | object { … } | function
-> Allows to pass custom HTTP headers on each request
-> Read more at https://github.com/webpack/webpack-dev-middleware#headers
Details:
* options.headers should be an array:
[object { key?, value? }, ...] (should not have fewer than 1 item)
* options.headers should be an object:
object { … }
* options.headers should be an instance of function."
Expand All @@ -15,10 +28,12 @@ exports[`validation should throw an error on the "headers" option with "1" value
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 one of these:
object { … } | function
[object { key?, value? }, ...] (should not have fewer than 1 item) | object { … } | function
-> Allows to pass custom HTTP headers on each request
-> Read more at https://github.com/webpack/webpack-dev-middleware#headers
Details:
* options.headers should be an array:
[object { key?, value? }, ...] (should not have fewer than 1 item)
* options.headers should be an object:
object { … }
* options.headers should be an instance of function."
Expand Down
119 changes: 118 additions & 1 deletion test/middleware.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,7 @@ describe.each([
instance = middleware(compiler);

app = framework();
// eslint-disable-next-line no-shadow
app.use((req, res, next) => {
// Express API
if (res.set) {
Expand Down Expand Up @@ -2133,6 +2134,7 @@ describe.each([
app = framework();
app.use(instance);

// eslint-disable-next-line no-shadow
app.use("/file.jpg", (req, res) => {
// Express API
if (res.send) {
Expand Down Expand Up @@ -2762,6 +2764,7 @@ describe.each([
});

it('should return the "200" code for the "GET" request to path not in outputFileSystem but not return headers', async () => {
// eslint-disable-next-line no-shadow
app.use("/file.jpg", (req, res) => {
// Express API
if (res.send) {
Expand All @@ -2779,6 +2782,62 @@ describe.each([
expect(res.headers["X-nonsense-2"]).toBeUndefined();
});
});

describe("works with array of objects", () => {
beforeEach((done) => {
const compiler = getCompiler(webpackConfig);

instance = middleware(compiler, {
headers: [
{
key: "X-Foo",
value: "value1",
},
{
key: "X-Bar",
value: "value2",
},
],
});

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

listen = listenShorthand(done);

req = request(app);
});

afterEach(close);

it('should return the "200" code for the "GET" request to the bundle file and return headers', async () => {
const response = await req.get(`/bundle.js`);

expect(response.statusCode).toEqual(200);
expect(response.headers["x-foo"]).toEqual("value1");
expect(response.headers["x-bar"]).toEqual("value2");
});

it('should return the "200" code for the "GET" request to path not in outputFileSystem but not return headers', async () => {
// eslint-disable-next-line no-shadow
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-foo"]).toBeUndefined();
expect(res.headers["x-bar"]).toBeUndefined();
});
});

describe("works with function", () => {
beforeEach((done) => {
const compiler = getCompiler(webpackConfig);
Expand Down Expand Up @@ -2808,6 +2867,7 @@ describe.each([
});

it('should return the "200" code for the "GET" request to path not in outputFileSystem but not return headers', async () => {
// eslint-disable-next-line no-shadow
app.use("/file.jpg", (req, res) => {
// Express API
if (res.send) {
Expand All @@ -2826,12 +2886,67 @@ describe.each([
});
});

describe("works with function returning an array", () => {
beforeEach((done) => {
const compiler = getCompiler(webpackConfig);

instance = middleware(compiler, {
headers: () => [
{
key: "X-Foo",
value: "value1",
},
{
key: "X-Bar",
value: "value2",
},
],
});

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

listen = listenShorthand(done);

req = request(app);
});

afterEach(close);

it('should return the "200" code for the "GET" request to the bundle file and return headers', async () => {
const response = await req.get(`/bundle.js`);

expect(response.statusCode).toEqual(200);
expect(response.headers["x-foo"]).toEqual("value1");
expect(response.headers["x-bar"]).toEqual("value2");
});

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

const res = await req.get("/file.jpg");
expect(res.statusCode).toEqual(200);
expect(res.headers["x-foo"]).toBeUndefined();
expect(res.headers["x-bar"]).toBeUndefined();
});
});

describe("works with headers function with params", () => {
beforeEach((done) => {
const compiler = getCompiler(webpackConfig);

instance = middleware(compiler, {
// eslint-disable-next-line no-unused-vars
// eslint-disable-next-line no-unused-vars, no-shadow
headers: (req, res, context) => {
res.setHeader("X-nonsense-1", "yes");
res.setHeader("X-nonsense-2", "no");
Expand All @@ -2857,6 +2972,7 @@ describe.each([
});

it('should return the "200" code for the "GET" request to path not in outputFileSystem but not return headers', async () => {
// eslint-disable-next-line no-shadow
app.use("/file.jpg", (req, res) => {
// Express API
if (res.send) {
Expand Down Expand Up @@ -2934,6 +3050,7 @@ describe.each([

app = framework();
app.use(instance);
// eslint-disable-next-line no-shadow
app.use((req, res) => {
// eslint-disable-next-line prefer-destructuring
locals = res.locals;
Expand Down
8 changes: 6 additions & 2 deletions test/validation-options.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@ describe("validation", () => {
failure: [{}, true],
},
headers: {
success: [{ "X-Custom-Header": "yes" }, () => {}],
failure: [true, 1],
success: [
{ "X-Custom-Header": "yes" },
() => {},
[{ key: "foo", value: "bar" }],
],
failure: [true, 1, [], [{ foo: "bar" }]],
},
publicPath: {
success: ["/foo", "", "auto", () => "/public/path"],
Expand Down

0 comments on commit 5a6a3f0

Please sign in to comment.