Skip to content

Commit 5a6a3f0

Browse files
authored
feat: allow array for headers option (#1042)
1 parent 961e0ac commit 5a6a3f0

File tree

7 files changed

+223
-12
lines changed

7 files changed

+223
-12
lines changed

README.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ This property allows a user to pass the list of HTTP request methods accepted by
7070

7171
### headers
7272

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

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

101+
or
102+
103+
```js
104+
webpackDevMiddleware(compiler, {
105+
headers: [
106+
{
107+
key: "X-custom-header"
108+
value: "foo"
109+
},
110+
{
111+
key: "Y-custom-header",
112+
value: "bar"
113+
}
114+
]
115+
},
116+
});
117+
```
118+
119+
or
120+
121+
```js
122+
webpackDevMiddleware(compiler, {
123+
headers: () => [
124+
{
125+
key: "X-custom-header"
126+
value: "foo"
127+
},
128+
{
129+
key: "Y-custom-header",
130+
value: "bar"
131+
}
132+
]
133+
},
134+
});
135+
```
136+
101137
### index
102138

103139
Type: `Boolean|String`

src/middleware.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,20 @@ export default function wrapper(context) {
8787
headers = headers(req, res, context);
8888
}
8989

90-
if (headers) {
91-
const names = Object.keys(headers);
90+
const allHeaders = [];
9291

93-
for (const name of names) {
94-
setHeaderForResponse(res, name, headers[name]);
92+
if (!Array.isArray(headers)) {
93+
// eslint-disable-next-line guard-for-in
94+
for (const name in headers) {
95+
allHeaders.push({ key: name, value: headers[name] });
9596
}
97+
headers = allHeaders;
9698
}
9799

100+
headers.forEach((header) => {
101+
setHeaderForResponse(res, header.key, header.value);
102+
});
103+
98104
if (!getHeaderFromResponse(res, "Content-Type")) {
99105
// content-type name(like application/javascript; charset=utf-8) or false
100106
const contentType = mime.contentType(path.extname(filename));

src/options.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,24 @@
2929
},
3030
"headers": {
3131
"anyOf": [
32+
{
33+
"type": "array",
34+
"items": {
35+
"type": "object",
36+
"additionalProperties": false,
37+
"properties": {
38+
"key": {
39+
"description": "key of header.",
40+
"type": "string"
41+
},
42+
"value": {
43+
"description": "value of header.",
44+
"type": "string"
45+
}
46+
}
47+
},
48+
"minItems": 1
49+
},
3250
{
3351
"type": "object"
3452
},

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

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`validation should throw an error on the "headers" option with "[]" 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 a non-empty array."
6+
`;
7+
8+
exports[`validation should throw an error on the "headers" option with "[{"foo":"bar"}]" value 1`] = `
9+
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
10+
- options.headers[0] has an unknown property 'foo'. These properties are valid:
11+
object { key?, value? }"
12+
`;
13+
314
exports[`validation should throw an error on the "headers" option with "1" value 1`] = `
415
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
516
- options.headers should be one of these:
6-
object { … } | function
17+
[object { key?, value? }, ...] (should not have fewer than 1 item) | object { … } | function
718
-> Allows to pass custom HTTP headers on each request
819
-> Read more at https://github.com/webpack/webpack-dev-middleware#headers
920
Details:
21+
* options.headers should be an array:
22+
[object { key?, value? }, ...] (should not have fewer than 1 item)
1023
* options.headers should be an object:
1124
object { … }
1225
* options.headers should be an instance of function."
@@ -15,10 +28,12 @@ exports[`validation should throw an error on the "headers" option with "1" value
1528
exports[`validation should throw an error on the "headers" option with "true" value 1`] = `
1629
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
1730
- options.headers should be one of these:
18-
object { … } | function
31+
[object { key?, value? }, ...] (should not have fewer than 1 item) | object { … } | function
1932
-> Allows to pass custom HTTP headers on each request
2033
-> Read more at https://github.com/webpack/webpack-dev-middleware#headers
2134
Details:
35+
* options.headers should be an array:
36+
[object { key?, value? }, ...] (should not have fewer than 1 item)
2237
* options.headers should be an object:
2338
object { … }
2439
* options.headers should be an instance of function."

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

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`validation should throw an error on the "headers" option with "[]" 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 a non-empty array."
6+
`;
7+
8+
exports[`validation should throw an error on the "headers" option with "[{"foo":"bar"}]" value 1`] = `
9+
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
10+
- options.headers[0] has an unknown property 'foo'. These properties are valid:
11+
object { key?, value? }"
12+
`;
13+
314
exports[`validation should throw an error on the "headers" option with "1" value 1`] = `
415
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
516
- options.headers should be one of these:
6-
object { … } | function
17+
[object { key?, value? }, ...] (should not have fewer than 1 item) | object { … } | function
718
-> Allows to pass custom HTTP headers on each request
819
-> Read more at https://github.com/webpack/webpack-dev-middleware#headers
920
Details:
21+
* options.headers should be an array:
22+
[object { key?, value? }, ...] (should not have fewer than 1 item)
1023
* options.headers should be an object:
1124
object { … }
1225
* options.headers should be an instance of function."
@@ -15,10 +28,12 @@ exports[`validation should throw an error on the "headers" option with "1" value
1528
exports[`validation should throw an error on the "headers" option with "true" value 1`] = `
1629
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
1730
- options.headers should be one of these:
18-
object { … } | function
31+
[object { key?, value? }, ...] (should not have fewer than 1 item) | object { … } | function
1932
-> Allows to pass custom HTTP headers on each request
2033
-> Read more at https://github.com/webpack/webpack-dev-middleware#headers
2134
Details:
35+
* options.headers should be an array:
36+
[object { key?, value? }, ...] (should not have fewer than 1 item)
2237
* options.headers should be an object:
2338
object { … }
2439
* options.headers should be an instance of function."

test/middleware.test.js

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,7 @@ describe.each([
10121012
instance = middleware(compiler);
10131013

10141014
app = framework();
1015+
// eslint-disable-next-line no-shadow
10151016
app.use((req, res, next) => {
10161017
// Express API
10171018
if (res.set) {
@@ -2133,6 +2134,7 @@ describe.each([
21332134
app = framework();
21342135
app.use(instance);
21352136

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

27642766
it('should return the "200" code for the "GET" request to path not in outputFileSystem but not return headers', async () => {
2767+
// eslint-disable-next-line no-shadow
27652768
app.use("/file.jpg", (req, res) => {
27662769
// Express API
27672770
if (res.send) {
@@ -2779,6 +2782,62 @@ describe.each([
27792782
expect(res.headers["X-nonsense-2"]).toBeUndefined();
27802783
});
27812784
});
2785+
2786+
describe("works with array of objects", () => {
2787+
beforeEach((done) => {
2788+
const compiler = getCompiler(webpackConfig);
2789+
2790+
instance = middleware(compiler, {
2791+
headers: [
2792+
{
2793+
key: "X-Foo",
2794+
value: "value1",
2795+
},
2796+
{
2797+
key: "X-Bar",
2798+
value: "value2",
2799+
},
2800+
],
2801+
});
2802+
2803+
app = framework();
2804+
app.use(instance);
2805+
2806+
listen = listenShorthand(done);
2807+
2808+
req = request(app);
2809+
});
2810+
2811+
afterEach(close);
2812+
2813+
it('should return the "200" code for the "GET" request to the bundle file and return headers', async () => {
2814+
const response = await req.get(`/bundle.js`);
2815+
2816+
expect(response.statusCode).toEqual(200);
2817+
expect(response.headers["x-foo"]).toEqual("value1");
2818+
expect(response.headers["x-bar"]).toEqual("value2");
2819+
});
2820+
2821+
it('should return the "200" code for the "GET" request to path not in outputFileSystem but not return headers', async () => {
2822+
// eslint-disable-next-line no-shadow
2823+
app.use("/file.jpg", (req, res) => {
2824+
// Express API
2825+
if (res.send) {
2826+
res.send("welcome");
2827+
}
2828+
// Connect API
2829+
else {
2830+
res.end("welcome");
2831+
}
2832+
});
2833+
2834+
const res = await request(app).get("/file.jpg");
2835+
expect(res.statusCode).toEqual(200);
2836+
expect(res.headers["x-foo"]).toBeUndefined();
2837+
expect(res.headers["x-bar"]).toBeUndefined();
2838+
});
2839+
});
2840+
27822841
describe("works with function", () => {
27832842
beforeEach((done) => {
27842843
const compiler = getCompiler(webpackConfig);
@@ -2808,6 +2867,7 @@ describe.each([
28082867
});
28092868

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

2889+
describe("works with function returning an array", () => {
2890+
beforeEach((done) => {
2891+
const compiler = getCompiler(webpackConfig);
2892+
2893+
instance = middleware(compiler, {
2894+
headers: () => [
2895+
{
2896+
key: "X-Foo",
2897+
value: "value1",
2898+
},
2899+
{
2900+
key: "X-Bar",
2901+
value: "value2",
2902+
},
2903+
],
2904+
});
2905+
2906+
app = framework();
2907+
app.use(instance);
2908+
2909+
listen = listenShorthand(done);
2910+
2911+
req = request(app);
2912+
});
2913+
2914+
afterEach(close);
2915+
2916+
it('should return the "200" code for the "GET" request to the bundle file and return headers', async () => {
2917+
const response = await req.get(`/bundle.js`);
2918+
2919+
expect(response.statusCode).toEqual(200);
2920+
expect(response.headers["x-foo"]).toEqual("value1");
2921+
expect(response.headers["x-bar"]).toEqual("value2");
2922+
});
2923+
2924+
it('should return the "200" code for the "GET" request to path not in outputFileSystem but not return headers', async () => {
2925+
// eslint-disable-next-line no-shadow
2926+
app.use("/file.jpg", (req, res) => {
2927+
// Express API
2928+
if (res.send) {
2929+
res.send("welcome");
2930+
}
2931+
// Connect API
2932+
else {
2933+
res.end("welcome");
2934+
}
2935+
});
2936+
2937+
const res = await req.get("/file.jpg");
2938+
expect(res.statusCode).toEqual(200);
2939+
expect(res.headers["x-foo"]).toBeUndefined();
2940+
expect(res.headers["x-bar"]).toBeUndefined();
2941+
});
2942+
});
2943+
28292944
describe("works with headers function with params", () => {
28302945
beforeEach((done) => {
28312946
const compiler = getCompiler(webpackConfig);
28322947

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

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

29353051
app = framework();
29363052
app.use(instance);
3053+
// eslint-disable-next-line no-shadow
29373054
app.use((req, res) => {
29383055
// eslint-disable-next-line prefer-destructuring
29393056
locals = res.locals;

test/validation-options.test.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,12 @@ describe("validation", () => {
2828
failure: [{}, true],
2929
},
3030
headers: {
31-
success: [{ "X-Custom-Header": "yes" }, () => {}],
32-
failure: [true, 1],
31+
success: [
32+
{ "X-Custom-Header": "yes" },
33+
() => {},
34+
[{ key: "foo", value: "bar" }],
35+
],
36+
failure: [true, 1, [], [{ foo: "bar" }]],
3337
},
3438
publicPath: {
3539
success: ["/foo", "", "auto", () => "/public/path"],

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy