Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added modifyResponseData option #1529

Merged
merged 2 commits into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 43 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,19 @@ See [below](#other-servers) for an example of use with fastify.

## Options

| Name | Type | Default | Description |
| :-----------------------------------------: | :--------: | :-------------------------------------------: | :--------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| **[`methods`](#methods)** | `Array` | `[ 'GET', 'HEAD' ]` | Allows to pass the list of HTTP request methods accepted by the middleware |
| **[`headers`](#headers)** | `Array\ | Object\| Function` | `undefined` | Allows to pass custom HTTP headers on each request. |
| **[`index`](#index)** | `Boolean\ | String` | `index.html` | If `false` (but not `undefined`), the server will not respond to requests to the root URL. |
| **[`mimeTypes`](#mimetypes)** | `Object` | `undefined` | Allows to register custom mime types or extension mappings. |
| **[`mimeTypeDefault`](#mimetypedefault)** | `String` | `undefined` | Allows to register a default mime type when we can't determine the content type. |
| **[`publicPath`](#publicpath)** | `String` | `output.publicPath` (from a configuration) | The public path that the middleware is bound to. |
| **[`stats`](#stats)** | `Boolean\ | String\| Object` | `stats` (from a configuration) | Stats options object or preset name. |
| **[`serverSideRender`](#serversiderender)** | `Boolean` | `undefined` | Instructs the module to enable or disable the server-side rendering mode. |
| **[`writeToDisk`](#writetodisk)** | `Boolean\ | Function` | `false` | Instructs the module to write files to the configured location on disk as specified in your `webpack` configuration. |
| **[`outputFileSystem`](#outputfilesystem)** | `Object` | [`memfs`](https://github.com/streamich/memfs) | Set the default file system which will be used by webpack as primary destination of generated files. |
| Name | Type | Default | Description |
| :---------------------------------------------: | :-----------------------: | :-------------------------------------------: | :------------------------------------------------------------------------------------------------------------------- |
| **[`methods`](#methods)** | `Array` | `[ 'GET', 'HEAD' ]` | Allows to pass the list of HTTP request methods accepted by the middleware |
| **[`headers`](#headers)** | `Array\|Object\|Function` | `undefined` | Allows to pass custom HTTP headers on each request. |
| **[`index`](#index)** | `Boolean\|String` | `index.html` | If `false` (but not `undefined`), the server will not respond to requests to the root URL. |
| **[`mimeTypes`](#mimetypes)** | `Object` | `undefined` | Allows to register custom mime types or extension mappings. |
| **[`mimeTypeDefault`](#mimetypedefault)** | `String` | `undefined` | Allows to register a default mime type when we can't determine the content type. |
| **[`publicPath`](#publicpath)** | `String` | `output.publicPath` (from a configuration) | The public path that the middleware is bound to. |
| **[`stats`](#stats)** | `Boolean\|String\|Object` | `stats` (from a configuration) | Stats options object or preset name. |
| **[`serverSideRender`](#serversiderender)** | `Boolean` | `undefined` | Instructs the module to enable or disable the server-side rendering mode. |
| **[`writeToDisk`](#writetodisk)** | `Boolean\|Function` | `false` | Instructs the module to write files to the configured location on disk as specified in your `webpack` configuration. |
| **[`outputFileSystem`](#outputfilesystem)** | `Object` | [`memfs`](https://github.com/streamich/memfs) | Set the default file system which will be used by webpack as primary destination of generated files. |
| **[`modifyResponseData`](#modifyresponsedata)** | `Function` | `undefined` | Allows to set up a callback to change the response data. |

The middleware accepts an `options` Object. The following is a property reference for the Object.

Expand Down Expand Up @@ -119,14 +120,13 @@ webpackDevMiddleware(compiler, {
headers: [
{
key: "X-custom-header",
value: "foo"
value: "foo",
},
{
key: "Y-custom-header",
value: "bar"
}
]
},
value: "bar",
},
],
});
```

Expand All @@ -137,14 +137,13 @@ webpackDevMiddleware(compiler, {
headers: () => [
{
key: "X-custom-header",
value: "foo"
value: "foo",
},
{
key: "Y-custom-header",
value: "bar"
}
]
},
value: "bar",
},
],
});
```

Expand Down Expand Up @@ -250,6 +249,28 @@ const compiler = webpack({
middleware(compiler, { outputFileSystem: myOutputFileSystem });
```

### modifyResponseData

Allows to set up a callback to change the response data.

```js
const webpack = require("webpack");
const configuration = {
/* Webpack configuration */
};
const compiler = webpack(configuration);

middleware(compiler, {
// Note - if you send the `Range` header you will have `ReadStream`
// Also `data` can be `string` or `Buffer`
modifyResponseData: (req, res, data, byteLength) => {
// Your logic
// Don't use `res.end()` or `res.send()` here
return { data, byteLength };
},
});
```

## API

`webpack-dev-middleware` also provides convenience methods that can be use to
Expand Down
19 changes: 19 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const noop = () => {};
/** @typedef {import("webpack").Configuration} Configuration */
/** @typedef {import("webpack").Stats} Stats */
/** @typedef {import("webpack").MultiStats} MultiStats */
/** @typedef {import("fs").ReadStream} ReadStream */

/**
* @typedef {Object} ExtendedServerResponse
Expand Down Expand Up @@ -55,6 +56,23 @@ const noop = () => {};
* @param {Stats | MultiStats} [stats]
*/

/**
* @typedef {Object} ResponseData
* @property {string | Buffer | ReadStream} data
* @property {number} byteLength
*/

/**
* @template {IncomingMessage} RequestInternal
* @template {ServerResponse} ResponseInternal
* @callback ModifyResponseData
* @param {RequestInternal} req
* @param {ResponseInternal} res
* @param {string | Buffer | ReadStream} data
* @param {number} byteLength
* @return {ResponseData}
*/

/**
* @template {IncomingMessage} RequestInternal
* @template {ServerResponse} ResponseInternal
Expand Down Expand Up @@ -89,6 +107,7 @@ const noop = () => {};
* @property {boolean} [serverSideRender]
* @property {OutputFileSystem} [outputFileSystem]
* @property {boolean | string} [index]
* @property {ModifyResponseData<RequestInternal, ResponseInternal>} [modifyResponseData]
*/

/**
Expand Down
35 changes: 28 additions & 7 deletions src/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,26 @@ function wrapper(context) {
);
setHeaderForResponse(res, "Content-Type", "text/html; charset=utf-8");

const document = createHtmlDocument(416, `Error: ${message}`);
const byteLength = Buffer.byteLength(document);
/** @type {string | Buffer | import("fs").ReadStream} */
let document = createHtmlDocument(416, `Error: ${message}`);
let byteLength = Buffer.byteLength(document);

setHeaderForResponse(
res,
"Content-Length",
Buffer.byteLength(document)
);

if (context.options.modifyResponseData) {
({ data: document, byteLength } =
context.options.modifyResponseData(
req,
res,
document,
byteLength
));
}

send(req, res, document, byteLength);

return;
Expand Down Expand Up @@ -244,7 +255,7 @@ function wrapper(context) {
const isFsSupportsStream =
typeof context.outputFileSystem.createReadStream === "function";

let bufferOtStream;
let bufferOrStream;
let byteLength;

try {
Expand All @@ -253,26 +264,36 @@ function wrapper(context) {
typeof end !== "undefined" &&
isFsSupportsStream
) {
bufferOtStream =
bufferOrStream =
/** @type {import("fs").createReadStream} */
(context.outputFileSystem.createReadStream)(filename, {
start,
end,
});
byteLength = end - start + 1;
} else {
bufferOtStream = /** @type {import("fs").readFileSync} */ (
bufferOrStream = /** @type {import("fs").readFileSync} */ (
context.outputFileSystem.readFileSync
)(filename);
({ byteLength } = bufferOtStream);
({ byteLength } = bufferOrStream);
}
} catch (_ignoreError) {
await goNext();

return;
}

send(req, res, bufferOtStream, byteLength);
if (context.options.modifyResponseData) {
({ data: bufferOrStream, byteLength } =
context.options.modifyResponseData(
req,
res,
bufferOrStream,
byteLength
));
}

send(req, res, bufferOrStream, byteLength);
}
};
}
Expand Down
5 changes: 5 additions & 0 deletions src/options.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@
"minLength": 1
}
]
},
"modifyResponseData": {
"description": "Allows to set up a callback to change the response data.",
"link": "https://github.com/webpack/webpack-dev-middleware#modifyresponsedata",
"instanceof": "Function"
}
},
"additionalProperties": false
Expand Down
14 changes: 14 additions & 0 deletions test/__snapshots__/validation-options.test.js.snap.webpack5
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ exports[`validation should throw an error on the "methods" option with "true" va
-> Read more at https://github.com/webpack/webpack-dev-middleware#methods"
`;

exports[`validation should throw an error on the "mimeTypeDefault" option with "0" value 1`] = `
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
- options.mimeTypeDefault should be a string.
-> Allows a user to register a default mime type when we can't determine the content type.
-> Read more at https://github.com/webpack/webpack-dev-middleware#mimetypedefault"
`;

exports[`validation should throw an error on the "mimeTypes" option with "foo" value 1`] = `
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
- options.mimeTypes should be an object:
Expand All @@ -85,6 +92,13 @@ exports[`validation should throw an error on the "mimeTypes" option with "foo" v
-> Read more at https://github.com/webpack/webpack-dev-middleware#mimetypes"
`;

exports[`validation should throw an error on the "modifyResponseData" 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.modifyResponseData should be an instance of function.
-> Allows to set up a callback to change the response data.
-> Read more at https://github.com/webpack/webpack-dev-middleware#modifyresponsedata"
`;

exports[`validation should throw an error on the "outputFileSystem" option with "false" value 1`] = `
"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema.
- options.outputFileSystem should be an object:
Expand Down
3 changes: 0 additions & 3 deletions test/helpers/isWebpack5.js

This file was deleted.

Loading