@rspack/dev-middleware 0.2.12 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +17 -19
- package/README.md +735 -10
- package/dist/index.js +9291 -15
- package/dist/index.js.LICENSE.txt +19 -0
- package/dist/rslib-runtime.js +29 -0
- package/package.json +70 -19
- package/types/index.d.ts +487 -0
- package/types/middleware.d.ts +43 -0
- package/types/utils/compatibleAPI.d.ts +261 -0
- package/types/utils/escapeHtml.d.ts +6 -0
- package/types/utils/etag.d.ts +12 -0
- package/types/utils/getFilenameFromUrl.d.ts +46 -0
- package/types/utils/getPaths.d.ts +31 -0
- package/types/utils/memorize.d.ts +33 -0
- package/types/utils/parseTokenList.d.ts +7 -0
- package/types/utils/ready.d.ts +23 -0
- package/types/utils/setupHooks.d.ts +40 -0
- package/types/utils/setupOutputFileSystem.d.ts +23 -0
- package/types/utils/setupWriteToDisk.d.ts +21 -0
- package/dist/index.d.ts +0 -8
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/index.cjs +0 -2
package/README.md
CHANGED
|
@@ -1,18 +1,743 @@
|
|
|
1
|
-
|
|
2
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://lf3-static.bytednsdoc.com/obj/eden-cn/rjhwzy/ljhwZthlaukjlkulzlp/rspack-banner-1610-dark.png">
|
|
3
|
-
<img alt="Rspack Banner" src="https://lf3-static.bytednsdoc.com/obj/eden-cn/rjhwzy/ljhwZthlaukjlkulzlp/rspack-banner-1610.png">
|
|
4
|
-
</picture>
|
|
1
|
+
# @rspack/dev-middleware
|
|
5
2
|
|
|
6
|
-
|
|
3
|
+
<p>
|
|
4
|
+
<a href="https://npmjs.com/package/@rspack/dev-middleware?activeTab=readme"><img src="https://img.shields.io/npm/v/@rspack/dev-middleware?style=flat-square&colorA=564341&colorB=EDED91" alt="npm version" /></a>
|
|
5
|
+
<a href="https://npmcharts.com/compare/@rspack/dev-middleware?minimal=true"><img src="https://img.shields.io/npm/dm/@rspack/dev-middleware.svg?style=flat-square&colorA=564341&colorB=EDED91" alt="downloads" /></a>
|
|
6
|
+
<a href="https://nodejs.org/en/about/previous-releases"><img src="https://img.shields.io/node/v/@rspack/dev-middleware.svg?style=flat-square&colorA=564341&colorB=EDED91" alt="node version"></a>
|
|
7
|
+
<a href="https://github.com/rstackjs/rspack-dev-middleware/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square&colorA=564341&colorB=EDED91" alt="license" /></a>
|
|
8
|
+
</p>
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
An express-style development middleware for use with [Rspack](https://rspack.rs). It serves the files emitted by the compiler from memory.
|
|
11
|
+
|
|
12
|
+
This should be used for **development only**.
|
|
13
|
+
|
|
14
|
+
Some of the benefits of using this middleware include:
|
|
15
|
+
|
|
16
|
+
- No files are written to disk, rather it handles files in memory
|
|
17
|
+
- If files changed in watch mode, the middleware delays requests until compiling
|
|
18
|
+
has completed.
|
|
19
|
+
- Supports hot module reload (HMR).
|
|
20
|
+
|
|
21
|
+
## Getting Started
|
|
22
|
+
|
|
23
|
+
Install the module:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# npm
|
|
27
|
+
npm install @rspack/dev-middleware -D
|
|
28
|
+
|
|
29
|
+
# pnpm
|
|
30
|
+
pnpm add -D @rspack/dev-middleware
|
|
31
|
+
|
|
32
|
+
# yarn
|
|
33
|
+
yarn add -D @rspack/dev-middleware
|
|
34
|
+
|
|
35
|
+
# bun
|
|
36
|
+
bun add -D @rspack/dev-middleware
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
const { devMiddleware } = require("@rspack/dev-middleware");
|
|
43
|
+
const express = require("express");
|
|
44
|
+
const { rspack } = require("@rspack/core");
|
|
45
|
+
|
|
46
|
+
const compiler = rspack({
|
|
47
|
+
// Rspack options
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const app = express();
|
|
51
|
+
|
|
52
|
+
app.use(
|
|
53
|
+
devMiddleware(compiler, {
|
|
54
|
+
// options
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
app.listen(3000, () => console.log("Example app listening on port 3000!"));
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
See [below](#other-servers) for an example of use with fastify.
|
|
62
|
+
|
|
63
|
+
## Options
|
|
64
|
+
|
|
65
|
+
| Name | Type | Default | Description |
|
|
66
|
+
| :---------------------------------------------: | :-------------------------------: | :-------------------------------------------: | :------------------------------------------------------------------------------------------------------------------- |
|
|
67
|
+
| **[`methods`](#methods)** | `Array` | `[ 'GET', 'HEAD' ]` | Allows to pass the list of HTTP request methods accepted by the middleware |
|
|
68
|
+
| **[`headers`](#headers)** | `Array\|Object\|Function` | `undefined` | Allows to pass custom HTTP headers on each request. |
|
|
69
|
+
| **[`index`](#index)** | `boolean\|string` | `index.html` | If `false` (but not `undefined`), the server will not respond to requests to the root URL. |
|
|
70
|
+
| **[`mimeTypes`](#mimetypes)** | `Object` | `undefined` | Allows to register custom mime types or extension mappings. |
|
|
71
|
+
| **[`mimeTypeDefault`](#mimetypedefault)** | `string` | `undefined` | Allows to register a default mime type when we can't determine the content type. |
|
|
72
|
+
| **[`etag`](#tag)** | `boolean\| "weak"\| "strong"` | `undefined` | Enable or disable etag generation. |
|
|
73
|
+
| **[`lastModified`](#lastmodified)** | `boolean` | `undefined` | Enable or disable `Last-Modified` header. Uses the file system's last modified value. |
|
|
74
|
+
| **[`cacheControl`](#cachecontrol)** | `boolean\|number\|string\|Object` | `undefined` | Enable or disable setting `Cache-Control` response header. |
|
|
75
|
+
| **[`cacheImmutable`](#cacheimmutable)** | `boolean\` | `undefined` | Enable or disable setting `Cache-Control: public, max-age=31536000, immutable` response header for immutable assets. |
|
|
76
|
+
| **[`publicPath`](#publicpath)** | `string` | `undefined` | The public path that the middleware is bound to. |
|
|
77
|
+
| **[`stats`](#stats)** | `boolean\|string\|Object` | `stats` (from a configuration) | Stats options object or preset name. |
|
|
78
|
+
| **[`serverSideRender`](#serversiderender)** | `boolean` | `undefined` | Instructs the module to enable or disable the server-side rendering mode. |
|
|
79
|
+
| **[`writeToDisk`](#writetodisk)** | `boolean\|Function` | `false` | Instructs the module to write files to the configured location on disk as specified in your Rspack configuration. |
|
|
80
|
+
| **[`outputFileSystem`](#outputfilesystem)** | `Object` | [`memfs`](https://github.com/streamich/memfs) | Set the default file system which will be used by Rspack as primary destination of generated files. |
|
|
81
|
+
| **[`modifyResponseData`](#modifyresponsedata)** | `Function` | `undefined` | Allows to set up a callback to change the response data. |
|
|
82
|
+
|
|
83
|
+
The middleware accepts an `options` Object. The following is a property reference for the Object.
|
|
84
|
+
|
|
85
|
+
### methods
|
|
86
|
+
|
|
87
|
+
Type: `Array`
|
|
88
|
+
Default: `[ 'GET', 'HEAD' ]`
|
|
89
|
+
|
|
90
|
+
This property allows a user to pass the list of HTTP request methods accepted by the middleware\*\*.
|
|
91
|
+
|
|
92
|
+
### headers
|
|
93
|
+
|
|
94
|
+
Type: `Array|Object|Function`
|
|
95
|
+
Default: `undefined`
|
|
96
|
+
|
|
97
|
+
This property allows a user to pass custom HTTP headers on each request.
|
|
98
|
+
eg. `{ "X-Custom-Header": "yes" }`
|
|
99
|
+
|
|
100
|
+
or
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
devMiddleware(compiler, {
|
|
104
|
+
headers: () => ({
|
|
105
|
+
"Last-Modified": new Date(),
|
|
106
|
+
}),
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
or
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
devMiddleware(compiler, {
|
|
114
|
+
headers: (req, res, context) => {
|
|
115
|
+
res.setHeader("Last-Modified", new Date());
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
or
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
devMiddleware(compiler, {
|
|
124
|
+
headers: [
|
|
125
|
+
{
|
|
126
|
+
key: "X-custom-header",
|
|
127
|
+
value: "foo",
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
key: "Y-custom-header",
|
|
131
|
+
value: "bar",
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
or
|
|
138
|
+
|
|
139
|
+
```js
|
|
140
|
+
devMiddleware(compiler, {
|
|
141
|
+
headers: () => [
|
|
142
|
+
{
|
|
143
|
+
key: "X-custom-header",
|
|
144
|
+
value: "foo",
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
key: "Y-custom-header",
|
|
148
|
+
value: "bar",
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### index
|
|
155
|
+
|
|
156
|
+
Type: `Boolean|String`
|
|
157
|
+
Default: `index.html`
|
|
158
|
+
|
|
159
|
+
If `false` (but not `undefined`), the server will not respond to requests to the root URL.
|
|
160
|
+
|
|
161
|
+
### mimeTypes
|
|
162
|
+
|
|
163
|
+
Type: `Object`
|
|
164
|
+
Default: `undefined`
|
|
165
|
+
|
|
166
|
+
This property allows a user to register custom mime types or extension mappings.
|
|
167
|
+
eg. `mimeTypes: { phtml: 'text/html' }`.
|
|
168
|
+
|
|
169
|
+
Please see the documentation for [`mrmime`](https://github.com/lukeed/mrmime) for more information.
|
|
170
|
+
|
|
171
|
+
### mimeTypeDefault
|
|
172
|
+
|
|
173
|
+
Type: `String`
|
|
174
|
+
Default: `undefined`
|
|
175
|
+
|
|
176
|
+
This property allows a user to register a default mime type when we can't determine the content type.
|
|
177
|
+
|
|
178
|
+
### etag
|
|
179
|
+
|
|
180
|
+
Type: `"weak" | "strong"`
|
|
181
|
+
Default: `undefined`
|
|
182
|
+
|
|
183
|
+
Enable or disable etag generation. Boolean value use
|
|
184
|
+
|
|
185
|
+
### lastModified
|
|
186
|
+
|
|
187
|
+
Type: `Boolean`
|
|
188
|
+
Default: `undefined`
|
|
189
|
+
|
|
190
|
+
Enable or disable `Last-Modified` header. Uses the file system's last modified value.
|
|
191
|
+
|
|
192
|
+
### cacheControl
|
|
193
|
+
|
|
194
|
+
Type: `Boolean | Number | String | { maxAge?: number, immutable?: boolean }`
|
|
195
|
+
Default: `undefined`
|
|
196
|
+
|
|
197
|
+
Depending on the setting, the following headers will be generated:
|
|
198
|
+
|
|
199
|
+
- `Boolean` - `Cache-Control: public, max-age=31536000000`
|
|
200
|
+
- `Number` - `Cache-Control: public, max-age=YOUR_NUMBER`
|
|
201
|
+
- `String` - `Cache-Control: YOUR_STRING`
|
|
202
|
+
- `{ maxAge?: number, immutable?: boolean }` - `Cache-Control: public, max-age=YOUR_MAX_AGE_or_31536000000`, also `, immutable` can be added if you set the `immutable` option to `true`
|
|
203
|
+
|
|
204
|
+
Enable or disable setting `Cache-Control` response header.
|
|
205
|
+
|
|
206
|
+
### cacheImmutable
|
|
207
|
+
|
|
208
|
+
Type: `Boolean`
|
|
209
|
+
Default: `undefined`
|
|
210
|
+
|
|
211
|
+
Enable or disable setting `Cache-Control: public, max-age=31536000, immutable` response header for immutable assets (i.e. asset with a hash like `image.a4c12bde.jpg`).
|
|
212
|
+
Immutable assets are assets that have their hash in the file name therefore they can be cached, because if you change their contents the file name will be changed.
|
|
213
|
+
Take preference over the `cacheControl` option if the asset was defined as immutable.
|
|
214
|
+
|
|
215
|
+
### publicPath
|
|
216
|
+
|
|
217
|
+
Type: `String`
|
|
218
|
+
Default: `output.publicPath` (from a configuration)
|
|
219
|
+
|
|
220
|
+
The public path that the middleware is bound to.
|
|
221
|
+
|
|
222
|
+
> Best Practice: use the same `publicPath` defined in your Rspack config.
|
|
223
|
+
|
|
224
|
+
### stats
|
|
225
|
+
|
|
226
|
+
Type: `Boolean|String|Object`
|
|
227
|
+
Default: `stats` (from a configuration)
|
|
228
|
+
|
|
229
|
+
Stats options object or preset name.
|
|
230
|
+
|
|
231
|
+
### serverSideRender
|
|
232
|
+
|
|
233
|
+
Type: `Boolean`
|
|
234
|
+
Default: `undefined`
|
|
235
|
+
|
|
236
|
+
Instructs the module to enable or disable the server-side rendering mode.
|
|
237
|
+
Please see [Server-Side Rendering](#server-side-rendering) for more information.
|
|
238
|
+
|
|
239
|
+
### writeToDisk
|
|
240
|
+
|
|
241
|
+
Type: `Boolean|Function`
|
|
242
|
+
Default: `false`
|
|
243
|
+
|
|
244
|
+
If `true`, the option will instruct the module to write files to the configured location on disk as specified in your Rspack config file.
|
|
245
|
+
|
|
246
|
+
_Setting `writeToDisk: true` won't change the behavior of `@rspack/dev-middleware`, and bundle files accessed through the browser will still be served from memory._
|
|
247
|
+
|
|
248
|
+
This option also accepts a `Function` value, which can be used to filter which files are written to disk.
|
|
249
|
+
The function follows the same premise as [`Array#filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) in which a return value of `false` _will not_ write the file, and a return value of `true` _will_ write the file to disk. eg.
|
|
250
|
+
|
|
251
|
+
```js
|
|
252
|
+
const { rspack } = require("@rspack/core");
|
|
253
|
+
|
|
254
|
+
const configuration = {
|
|
255
|
+
/* Rspack configuration */
|
|
256
|
+
};
|
|
257
|
+
const compiler = rspack(configuration);
|
|
258
|
+
|
|
259
|
+
devMiddleware(compiler, {
|
|
260
|
+
writeToDisk: (filePath) => /superman\.css$/.test(filePath),
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### outputFileSystem
|
|
265
|
+
|
|
266
|
+
Type: `Object`
|
|
267
|
+
Default: [memfs](https://github.com/streamich/memfs)
|
|
268
|
+
|
|
269
|
+
Set the default file system which will be used by Rspack as primary destination of generated files.
|
|
270
|
+
This option isn't affected by the [writeToDisk](#writeToDisk) option.
|
|
271
|
+
|
|
272
|
+
This can be done simply by using `path.join`:
|
|
273
|
+
|
|
274
|
+
```js
|
|
275
|
+
const path = require("node:path");
|
|
276
|
+
const mkdirp = require("mkdirp");
|
|
277
|
+
const myOutputFileSystem = require("my-fs");
|
|
278
|
+
const { rspack } = require("@rspack/core");
|
|
279
|
+
|
|
280
|
+
myOutputFileSystem.join = path.join.bind(path); // no need to bind
|
|
281
|
+
myOutputFileSystem.mkdirp = mkdirp.bind(mkdirp); // no need to bind
|
|
282
|
+
|
|
283
|
+
const compiler = rspack({
|
|
284
|
+
/* Rspack configuration */
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
devMiddleware(compiler, { outputFileSystem: myOutputFileSystem });
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### modifyResponseData
|
|
291
|
+
|
|
292
|
+
Allows to set up a callback to change the response data.
|
|
293
|
+
|
|
294
|
+
```js
|
|
295
|
+
const { rspack } = require("@rspack/core");
|
|
296
|
+
|
|
297
|
+
const configuration = {
|
|
298
|
+
/* Rspack configuration */
|
|
299
|
+
};
|
|
300
|
+
const compiler = rspack(configuration);
|
|
301
|
+
|
|
302
|
+
devMiddleware(compiler, {
|
|
303
|
+
// Note - if you send the `Range` header you will have `ReadStream`
|
|
304
|
+
// Also `data` can be `string` or `Buffer`
|
|
305
|
+
modifyResponseData: (req, res, data, byteLength) =>
|
|
306
|
+
// Your logic
|
|
307
|
+
// Don't use `res.end()` or `res.send()` here
|
|
308
|
+
({ data, byteLength }),
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## API
|
|
313
|
+
|
|
314
|
+
`@rspack/dev-middleware` also provides convenience methods that can be use to
|
|
315
|
+
interact with the middleware at runtime:
|
|
316
|
+
|
|
317
|
+
### `close(callback)`
|
|
318
|
+
|
|
319
|
+
Instructs the `@rspack/dev-middleware` instance to stop watching for file changes.
|
|
320
|
+
|
|
321
|
+
#### Parameters
|
|
322
|
+
|
|
323
|
+
##### `callback`
|
|
324
|
+
|
|
325
|
+
Type: `Function`
|
|
326
|
+
Required: `No`
|
|
327
|
+
|
|
328
|
+
A function executed once the middleware has stopped watching.
|
|
329
|
+
|
|
330
|
+
```js
|
|
331
|
+
const { devMiddleware } = require("@rspack/dev-middleware");
|
|
332
|
+
const express = require("express");
|
|
333
|
+
const { rspack } = require("@rspack/core");
|
|
334
|
+
|
|
335
|
+
const compiler = rspack({
|
|
336
|
+
/* Rspack configuration */
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const instance = devMiddleware(compiler);
|
|
340
|
+
|
|
341
|
+
// eslint-disable-next-line new-cap
|
|
342
|
+
const app = new express();
|
|
343
|
+
|
|
344
|
+
app.use(instance);
|
|
345
|
+
|
|
346
|
+
setTimeout(() => {
|
|
347
|
+
// Says Rspack to stop watch changes
|
|
348
|
+
instance.close();
|
|
349
|
+
}, 1000);
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### `invalidate(callback)`
|
|
353
|
+
|
|
354
|
+
Instructs the `@rspack/dev-middleware` instance to recompile the bundle, e.g. after a change to the configuration.
|
|
355
|
+
|
|
356
|
+
#### Parameters
|
|
357
|
+
|
|
358
|
+
##### `callback`
|
|
359
|
+
|
|
360
|
+
Type: `Function`
|
|
361
|
+
Required: `No`
|
|
362
|
+
|
|
363
|
+
A function executed once the middleware has invalidated.
|
|
364
|
+
|
|
365
|
+
```js
|
|
366
|
+
const { devMiddleware } = require("@rspack/dev-middleware");
|
|
367
|
+
const express = require("express");
|
|
368
|
+
const { rspack } = require("@rspack/core");
|
|
369
|
+
|
|
370
|
+
const compiler = rspack({
|
|
371
|
+
/* Rspack configuration */
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
const instance = devMiddleware(compiler);
|
|
375
|
+
|
|
376
|
+
// eslint-disable-next-line new-cap
|
|
377
|
+
const app = new express();
|
|
378
|
+
|
|
379
|
+
app.use(instance);
|
|
380
|
+
|
|
381
|
+
setTimeout(() => {
|
|
382
|
+
// After a short delay the configuration is changed and a banner plugin is added to the config
|
|
383
|
+
new rspack.BannerPlugin("A new banner").apply(compiler);
|
|
384
|
+
|
|
385
|
+
// Recompile the bundle with the banner plugin:
|
|
386
|
+
instance.invalidate();
|
|
387
|
+
}, 1000);
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### `waitUntilValid(callback)`
|
|
391
|
+
|
|
392
|
+
Executes a callback function when the compiler bundle is valid, typically after
|
|
393
|
+
compilation.
|
|
394
|
+
|
|
395
|
+
#### Parameters
|
|
396
|
+
|
|
397
|
+
##### `callback`
|
|
398
|
+
|
|
399
|
+
Type: `Function`
|
|
400
|
+
Required: `No`
|
|
401
|
+
|
|
402
|
+
A function executed when the bundle becomes valid.
|
|
403
|
+
If the bundle is valid at the time of calling, the callback is executed immediately.
|
|
404
|
+
|
|
405
|
+
```js
|
|
406
|
+
const { devMiddleware } = require("@rspack/dev-middleware");
|
|
407
|
+
const express = require("express");
|
|
408
|
+
const { rspack } = require("@rspack/core");
|
|
409
|
+
|
|
410
|
+
const compiler = rspack({
|
|
411
|
+
/* Rspack configuration */
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
const instance = devMiddleware(compiler);
|
|
415
|
+
|
|
416
|
+
// eslint-disable-next-line new-cap
|
|
417
|
+
const app = new express();
|
|
418
|
+
|
|
419
|
+
app.use(instance);
|
|
420
|
+
|
|
421
|
+
instance.waitUntilValid(() => {
|
|
422
|
+
console.log("Package is in a valid state");
|
|
423
|
+
});
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### `getFilenameFromUrl(url)`
|
|
427
|
+
|
|
428
|
+
Get filename from URL.
|
|
429
|
+
|
|
430
|
+
#### Parameters
|
|
431
|
+
|
|
432
|
+
##### `url`
|
|
433
|
+
|
|
434
|
+
Type: `String`
|
|
435
|
+
Required: `Yes`
|
|
436
|
+
|
|
437
|
+
URL for the requested file.
|
|
438
|
+
|
|
439
|
+
```js
|
|
440
|
+
const { devMiddleware } = require("@rspack/dev-middleware");
|
|
441
|
+
const express = require("express");
|
|
442
|
+
const { rspack } = require("@rspack/core");
|
|
443
|
+
|
|
444
|
+
const compiler = rspack({
|
|
445
|
+
/* Rspack configuration */
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
const instance = devMiddleware(compiler);
|
|
449
|
+
|
|
450
|
+
// eslint-disable-next-line new-cap
|
|
451
|
+
const app = new express();
|
|
452
|
+
|
|
453
|
+
app.use(instance);
|
|
454
|
+
|
|
455
|
+
instance.waitUntilValid(() => {
|
|
456
|
+
const filename = instance.getFilenameFromUrl("/bundle.js");
|
|
457
|
+
|
|
458
|
+
console.log(`Filename is ${filename}`);
|
|
459
|
+
});
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
## FAQ
|
|
463
|
+
|
|
464
|
+
### Avoid blocking requests to non-Rspack resources.
|
|
465
|
+
|
|
466
|
+
Since `output.publicPath` and `output.filename`/`output.chunkFilename` can be dynamic, it's not possible to know which files are Rspack bundles (and they public paths) and which are not, so we can't avoid blocking requests.
|
|
467
|
+
|
|
468
|
+
But there is a solution to avoid it - mount the middleware to a non-root route, for example:
|
|
469
|
+
|
|
470
|
+
```js
|
|
471
|
+
const { devMiddleware } = require("@rspack/dev-middleware");
|
|
472
|
+
const express = require("express");
|
|
473
|
+
const { rspack } = require("@rspack/core");
|
|
474
|
+
|
|
475
|
+
const compiler = rspack({
|
|
476
|
+
// Rspack options
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
const app = express();
|
|
480
|
+
|
|
481
|
+
// Mounting the middleware to the non-root route allows avoids this.
|
|
482
|
+
// Note - check your public path, if you want to handle `/dist/`, you need to setup `output.publicPath` to `/` value.
|
|
483
|
+
app.use(
|
|
484
|
+
"/dist/",
|
|
485
|
+
devMiddleware(compiler, {
|
|
486
|
+
// @rspack/dev-middleware options
|
|
487
|
+
}),
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
app.listen(3000, () => console.log("Example app listening on port 3000!"));
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
## Server-Side Rendering
|
|
494
|
+
|
|
495
|
+
_Note: this feature is experimental and may be removed or changed completely in the future._
|
|
496
|
+
|
|
497
|
+
In order to develop an app using server-side rendering, we need access to the
|
|
498
|
+
[`stats`](https://rspack.rs/api/javascript-api/stats), which is
|
|
499
|
+
generated with each build.
|
|
500
|
+
|
|
501
|
+
With server-side rendering enabled, `@rspack/dev-middleware` sets the `stats` to `res.locals.rspack.devMiddleware.stats`
|
|
502
|
+
and the filesystem to `res.locals.rspack.devMiddleware.outputFileSystem` before invoking the next middleware,
|
|
503
|
+
allowing a developer to render the page body and manage the response to clients.
|
|
504
|
+
|
|
505
|
+
_Note: Requests for bundle files will still be handled by
|
|
506
|
+
`@rspack/dev-middleware` and all requests will be pending until the build
|
|
507
|
+
process is finished with server-side rendering enabled._
|
|
508
|
+
|
|
509
|
+
Example Implementation:
|
|
510
|
+
|
|
511
|
+
```js
|
|
512
|
+
const { devMiddleware } = require("@rspack/dev-middleware");
|
|
513
|
+
const express = require("express");
|
|
514
|
+
const isObject = require("is-object");
|
|
515
|
+
const { rspack } = require("@rspack/core");
|
|
516
|
+
|
|
517
|
+
const compiler = rspack({
|
|
518
|
+
/* Rspack configuration */
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
// eslint-disable-next-line new-cap
|
|
522
|
+
const app = new express();
|
|
523
|
+
|
|
524
|
+
// This function makes server rendering of asset references consistent with different Rspack chunk/entry configurations
|
|
525
|
+
function normalizeAssets(assets) {
|
|
526
|
+
if (isObject(assets)) {
|
|
527
|
+
return Object.values(assets);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
return Array.isArray(assets) ? assets : [assets];
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
app.use(devMiddleware(compiler, { serverSideRender: true }));
|
|
534
|
+
|
|
535
|
+
// The following middleware would not be invoked until the latest build is finished.
|
|
536
|
+
app.use((req, res) => {
|
|
537
|
+
const { devMiddleware } = res.locals.rspack;
|
|
538
|
+
const { outputFileSystem } = devMiddleware;
|
|
539
|
+
const jsonStats = devMiddleware.stats.toJson();
|
|
540
|
+
const { assetsByChunkName, outputPath } = jsonStats;
|
|
541
|
+
|
|
542
|
+
// Then use `assetsByChunkName` for server-side rendering
|
|
543
|
+
// For example, if you have only one main chunk:
|
|
544
|
+
res.send(`
|
|
545
|
+
<html>
|
|
546
|
+
<head>
|
|
547
|
+
<title>My App</title>
|
|
548
|
+
<style>
|
|
549
|
+
${normalizeAssets(assetsByChunkName.main)
|
|
550
|
+
.filter((path) => path.endsWith(".css"))
|
|
551
|
+
.map((path) => outputFileSystem.readFileSync(path.join(outputPath, path)))
|
|
552
|
+
.join("\n")}
|
|
553
|
+
</style>
|
|
554
|
+
</head>
|
|
555
|
+
<body>
|
|
556
|
+
<div id="root"></div>
|
|
557
|
+
${normalizeAssets(assetsByChunkName.main)
|
|
558
|
+
.filter((path) => path.endsWith(".js"))
|
|
559
|
+
.map((path) => `<script src="${path}"></script>`)
|
|
560
|
+
.join("\n")}
|
|
561
|
+
</body>
|
|
562
|
+
</html>
|
|
563
|
+
`);
|
|
564
|
+
});
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
## Other servers
|
|
568
|
+
|
|
569
|
+
Examples of use with other servers will follow here.
|
|
570
|
+
|
|
571
|
+
### Connect
|
|
572
|
+
|
|
573
|
+
```js
|
|
574
|
+
const http = require("node:http");
|
|
575
|
+
const { devMiddleware } = require("@rspack/dev-middleware");
|
|
576
|
+
const connect = require("connect");
|
|
577
|
+
const { rspack } = require("@rspack/core");
|
|
578
|
+
const rspackConfig = require("./rspack.config.js");
|
|
579
|
+
|
|
580
|
+
const compiler = rspack(rspackConfig);
|
|
581
|
+
const devMiddlewareOptions = {
|
|
582
|
+
// options
|
|
583
|
+
};
|
|
584
|
+
const app = connect();
|
|
585
|
+
|
|
586
|
+
app.use(devMiddleware(compiler, devMiddlewareOptions));
|
|
587
|
+
|
|
588
|
+
http.createServer(app).listen(3000);
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### Router
|
|
592
|
+
|
|
593
|
+
```js
|
|
594
|
+
const http = require("node:http");
|
|
595
|
+
const { devMiddleware } = require("@rspack/dev-middleware");
|
|
596
|
+
const finalhandler = require("finalhandler");
|
|
597
|
+
const Router = require("router");
|
|
598
|
+
const { rspack } = require("@rspack/core");
|
|
599
|
+
const rspackConfig = require("./rspack.config.js");
|
|
600
|
+
|
|
601
|
+
const compiler = rspack(rspackConfig);
|
|
602
|
+
const devMiddlewareOptions = {
|
|
603
|
+
// options
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
// eslint-disable-next-line new-cap
|
|
607
|
+
const router = Router();
|
|
608
|
+
|
|
609
|
+
router.use(devMiddleware(compiler, devMiddlewareOptions));
|
|
610
|
+
|
|
611
|
+
const server = http.createServer((req, res) => {
|
|
612
|
+
router(req, res, finalhandler(req, res));
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
server.listen(3000);
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
### Express
|
|
619
|
+
|
|
620
|
+
```js
|
|
621
|
+
const { devMiddleware } = require("@rspack/dev-middleware");
|
|
622
|
+
const express = require("express");
|
|
623
|
+
const { rspack } = require("@rspack/core");
|
|
624
|
+
const rspackConfig = require("./rspack.config.js");
|
|
625
|
+
|
|
626
|
+
const compiler = rspack(rspackConfig);
|
|
627
|
+
const devMiddlewareOptions = {
|
|
628
|
+
// options
|
|
629
|
+
};
|
|
630
|
+
const app = express();
|
|
631
|
+
|
|
632
|
+
app.use(devMiddleware(compiler, devMiddlewareOptions));
|
|
633
|
+
|
|
634
|
+
app.listen(3000, () => console.log("Example app listening on port 3000!"));
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
### Koa
|
|
638
|
+
|
|
639
|
+
```js
|
|
640
|
+
const { devMiddleware } = require("@rspack/dev-middleware");
|
|
641
|
+
const Koa = require("koa");
|
|
642
|
+
const { rspack } = require("@rspack/core");
|
|
643
|
+
const rspackConfig = require("./rspack.simple.config");
|
|
644
|
+
|
|
645
|
+
const compiler = rspack(rspackConfig);
|
|
646
|
+
const devMiddlewareOptions = {
|
|
647
|
+
// options
|
|
648
|
+
};
|
|
649
|
+
const app = new Koa();
|
|
650
|
+
|
|
651
|
+
app.use(middleware.koaWrapper(compiler, devMiddlewareOptions));
|
|
652
|
+
|
|
653
|
+
app.listen(3000);
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
### Hapi
|
|
657
|
+
|
|
658
|
+
```js
|
|
659
|
+
const Hapi = require("@hapi/hapi");
|
|
660
|
+
const { devMiddleware } = require("@rspack/dev-middleware");
|
|
661
|
+
const { rspack } = require("@rspack/core");
|
|
662
|
+
const rspackConfig = require("./rspack.config.js");
|
|
663
|
+
|
|
664
|
+
const compiler = rspack(rspackConfig);
|
|
665
|
+
const devMiddlewareOptions = {};
|
|
666
|
+
|
|
667
|
+
const server = Hapi.server({ port: 3000, host: "localhost" });
|
|
668
|
+
|
|
669
|
+
await server.register({
|
|
670
|
+
plugin: devMiddleware.hapiWrapper(),
|
|
671
|
+
options: {
|
|
672
|
+
// The `compiler` option is required
|
|
673
|
+
compiler,
|
|
674
|
+
...devMiddlewareOptions,
|
|
675
|
+
},
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
await server.start();
|
|
679
|
+
|
|
680
|
+
console.log("Server running on %s", server.info.uri);
|
|
681
|
+
|
|
682
|
+
process.on("unhandledRejection", (err) => {
|
|
683
|
+
console.log(err);
|
|
684
|
+
process.exit(1);
|
|
685
|
+
});
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
### Fastify
|
|
689
|
+
|
|
690
|
+
Fastify interop will require the use of `fastify-express` instead of `middie` for providing middleware support. As the authors of `fastify-express` recommend, this should only be used as a stopgap while full Fastify support is worked on.
|
|
691
|
+
|
|
692
|
+
```js
|
|
693
|
+
const { devMiddleware } = require("@rspack/dev-middleware");
|
|
694
|
+
const fastify = require("fastify")();
|
|
695
|
+
const { rspack } = require("@rspack/core");
|
|
696
|
+
const rspackConfig = require("./rspack.config.js");
|
|
697
|
+
|
|
698
|
+
const compiler = rspack(rspackConfig);
|
|
699
|
+
const devMiddlewareOptions = {
|
|
700
|
+
// options
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
await fastify.register(require("@fastify/express"));
|
|
704
|
+
await fastify.use(devMiddleware(compiler, devMiddlewareOptions));
|
|
705
|
+
await fastify.listen(3000);
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
### Hono
|
|
709
|
+
|
|
710
|
+
```js
|
|
711
|
+
import { serve } from "@hono/node-server";
|
|
712
|
+
import devMiddleware from "@rspack/dev-middleware";
|
|
713
|
+
import { Hono } from "hono";
|
|
714
|
+
import { rspack } from "@rspack/core";
|
|
715
|
+
import rspackConfig from "./rspack.config.js";
|
|
716
|
+
|
|
717
|
+
const compiler = rspack(rspackConfig);
|
|
718
|
+
const devMiddlewareOptions = {
|
|
719
|
+
// options
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
const app = new Hono();
|
|
723
|
+
|
|
724
|
+
app.use(devMiddleware.honoWrapper(compiler, devMiddlewareOptions));
|
|
725
|
+
|
|
726
|
+
serve(app);
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
## Credits
|
|
730
|
+
|
|
731
|
+
This repository is forked from [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware). It adapts the original implementation for the Rspack ecosystem, bridging behavioral differences with webpack while adding Rspack-specific capabilities.
|
|
732
|
+
|
|
733
|
+
> Thanks to the webpack-dev-middleware maintainers and its original creator, [@sokra](https://github.com/sokra).
|
|
9
734
|
|
|
10
|
-
|
|
735
|
+
## Contributing
|
|
11
736
|
|
|
12
|
-
|
|
737
|
+
Please take a moment to read our contributing guidelines if you haven't yet done so.
|
|
13
738
|
|
|
14
|
-
|
|
739
|
+
[CONTRIBUTING](./CONTRIBUTING.md)
|
|
15
740
|
|
|
16
741
|
## License
|
|
17
742
|
|
|
18
|
-
|
|
743
|
+
[MIT](./LICENSE)
|