rspack-dev-middleware 0.4.0 → 0.4.2

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.
Files changed (75) hide show
  1. package/cjs/client/HotUpdate.cjs +1 -1
  2. package/cjs/client/client.cjs +2 -2
  3. package/cjs/client/events.cjs +5 -5
  4. package/cjs/client/events.d.cts +3 -7
  5. package/cjs/client/index.cjs +1 -1
  6. package/cjs/client/main.cjs +3 -3
  7. package/cjs/client/ui/Overlay.cjs +4 -4
  8. package/cjs/client/ui/Progress.cjs +3 -3
  9. package/cjs/client/ui/images/rspack-logo.svg.cjs +1 -1
  10. package/cjs/client/ui/utils.cjs +16 -22
  11. package/cjs/client/ui/utils.d.cts +3 -10
  12. package/cjs/server/compose.cjs +2 -2
  13. package/cjs/server/dev/ReadStream.cjs +5 -5
  14. package/cjs/server/dev/Service.cjs +10 -21
  15. package/cjs/server/dev/index.cjs +7 -3
  16. package/cjs/server/dev/middleware.cjs +4 -5
  17. package/cjs/server/dev/utils/fs.cjs +1 -1
  18. package/cjs/server/dev/utils/http.cjs +56 -28
  19. package/cjs/server/dev/utils/http.d.cts +26 -1
  20. package/cjs/server/dev/utils/path.cjs +1 -1
  21. package/cjs/server/dev/utils/paths.cjs +1 -1
  22. package/cjs/server/dev/utils/setupHooks.cjs +3 -9
  23. package/cjs/server/dev/utils/setupOutputFileSystem.cjs +1 -1
  24. package/cjs/server/dev/utils/setupWatching.cjs +11 -16
  25. package/cjs/server/dev/utils/setupWriteToDisk.cjs +1 -1
  26. package/cjs/server/hot/Message.d.cts +16 -32
  27. package/cjs/server/hot/Socket.cjs +19 -20
  28. package/cjs/server/hot/index.cjs +1 -1
  29. package/cjs/server/hot/utils.cjs +2 -8
  30. package/cjs/server/index.cjs +1 -1
  31. package/cjs/server/schema.cjs +27 -6
  32. package/cjs/server/utils.cjs +12 -1
  33. package/cjs/server/utils.d.cts +1 -0
  34. package/esm/client/HotUpdate.js +1 -1
  35. package/esm/client/client.js +2 -2
  36. package/esm/client/events.d.ts +3 -7
  37. package/esm/client/events.js +5 -5
  38. package/esm/client/index.js +1 -1
  39. package/esm/client/main.js +3 -3
  40. package/esm/client/ui/Overlay.js +5 -5
  41. package/esm/client/ui/Progress.js +4 -4
  42. package/esm/client/ui/images/rspack-logo.svg.js +1 -1
  43. package/esm/client/ui/utils.d.ts +3 -10
  44. package/esm/client/ui/utils.js +15 -21
  45. package/esm/server/compose.js +2 -2
  46. package/esm/server/dev/ReadStream.js +5 -5
  47. package/esm/server/dev/Service.js +10 -15
  48. package/esm/server/dev/index.js +7 -3
  49. package/esm/server/dev/middleware.js +4 -5
  50. package/esm/server/dev/utils/fs.js +1 -1
  51. package/esm/server/dev/utils/http.d.ts +26 -1
  52. package/esm/server/dev/utils/http.js +62 -29
  53. package/esm/server/dev/utils/path.js +1 -1
  54. package/esm/server/dev/utils/paths.js +1 -1
  55. package/esm/server/dev/utils/setupHooks.js +4 -10
  56. package/esm/server/dev/utils/setupOutputFileSystem.js +1 -1
  57. package/esm/server/dev/utils/setupWatching.js +11 -16
  58. package/esm/server/dev/utils/setupWriteToDisk.js +1 -1
  59. package/esm/server/hot/Message.d.ts +16 -32
  60. package/esm/server/hot/Socket.js +16 -17
  61. package/esm/server/hot/index.js +1 -1
  62. package/esm/server/hot/utils.js +3 -9
  63. package/esm/server/index.js +1 -1
  64. package/esm/server/schema.js +27 -6
  65. package/esm/server/utils.d.ts +1 -0
  66. package/esm/server/utils.js +12 -2
  67. package/package.json +35 -37
  68. package/cjs/server/dev/utils/hash.cjs +0 -47
  69. package/cjs/server/dev/utils/hash.d.cts +0 -9
  70. package/cjs/server/dev/utils/ready.cjs +0 -24
  71. package/cjs/server/dev/utils/ready.d.cts +0 -5
  72. package/esm/server/dev/utils/hash.d.ts +0 -9
  73. package/esm/server/dev/utils/hash.js +0 -45
  74. package/esm/server/dev/utils/ready.d.ts +0 -5
  75. package/esm/server/dev/utils/ready.js +0 -22
@@ -2,14 +2,14 @@
2
2
  * @module rspack-dev-middleware
3
3
  * @package rspack-dev-middleware
4
4
  * @license MIT
5
- * @version 0.4.0
5
+ * @version 0.4.2
6
6
  * @author nuintun <nuintun@qq.com>
7
7
  * @description A koa 2 middleware for rspack development and hot reloading.
8
8
  * @see https://github.com/nuintun/rspack-dev-middleware#readme
9
9
  */
10
10
 
11
11
  import logo from './images/rspack-logo.svg.js';
12
- import { getRootElement, insertCSSString, appendDOMString } from './utils.js';
12
+ import { getRootElement, appendStyleSheet, appendMarkup } from './utils.js';
13
13
 
14
14
  /**
15
15
  * @module Progress
@@ -65,8 +65,8 @@ class Progress {
65
65
  #track;
66
66
  constructor() {
67
67
  const root = getRootElement(PROGRESS);
68
- insertCSSString(CSS, root);
69
- [this.#svg] = appendDOMString('image/svg+xml', SVG, root);
68
+ appendStyleSheet(CSS, root);
69
+ this.#svg = appendMarkup('image/svg+xml', SVG, root);
70
70
  this.#track = this.#svg.querySelector(`.${PROGRESS}-track`);
71
71
  }
72
72
  update(percent) {
@@ -2,7 +2,7 @@
2
2
  * @module rspack-dev-middleware
3
3
  * @package rspack-dev-middleware
4
4
  * @license MIT
5
- * @version 0.4.0
5
+ * @version 0.4.2
6
6
  * @author nuintun <nuintun@qq.com>
7
7
  * @description A koa 2 middleware for rspack development and hot reloading.
8
8
  * @see https://github.com/nuintun/rspack-dev-middleware#readme
@@ -7,13 +7,6 @@ export declare function blockToHTML({ style, value, url }: AnsiBlock): string;
7
7
  export declare function ansiToHTML(text: string): string;
8
8
  export type RootElement = HTMLElement | ShadowRoot;
9
9
  export declare function getRootElement(tagName: string): ShadowRoot;
10
- export declare function insertCSSString(
11
- css: string,
12
- root?: HTMLElement | ShadowRoot,
13
- styleElement?: HTMLStyleElement
14
- ): HTMLStyleElement;
15
- export declare function appendDOMString<T extends DOMParserSupportedType>(
16
- type: T,
17
- string: string,
18
- root?: RootElement
19
- ): (T extends 'image/svg+xml' ? SVGElement : HTMLElement)[];
10
+ export declare function appendStyleSheet(css: string, root: RootElement): void;
11
+ export declare function appendMarkup(type: 'text/html', html: string, root: RootElement): HTMLElement;
12
+ export declare function appendMarkup(type: 'image/svg+xml', svg: string, root: RootElement): SVGElement;
@@ -2,7 +2,7 @@
2
2
  * @module rspack-dev-middleware
3
3
  * @package rspack-dev-middleware
4
4
  * @license MIT
5
- * @version 0.4.0
5
+ * @version 0.4.2
6
6
  * @author nuintun <nuintun@qq.com>
7
7
  * @description A koa 2 middleware for rspack development and hot reloading.
8
8
  * @see https://github.com/nuintun/rspack-dev-middleware#readme
@@ -63,10 +63,10 @@ function blockToHTML({ style, value, url }) {
63
63
  textDecorations.push('line-through');
64
64
  }
65
65
  const { color, background } = style;
66
- if (color) {
66
+ if (color != null) {
67
67
  styles.push(`color: rgb(${color})`);
68
68
  }
69
- if (background) {
69
+ if (background != null) {
70
70
  styles.push(`background-color: rgb(${background})`);
71
71
  }
72
72
  if (textDecorations.length > 0) {
@@ -102,24 +102,18 @@ function getRootElement(tagName) {
102
102
  document.body.appendChild(stage);
103
103
  return shadowRoot;
104
104
  }
105
- function insertCSSString(css, root = document.body, styleElement = document.createElement('style')) {
106
- styleElement.appendChild(document.createTextNode(css));
107
- if (!root.contains(styleElement)) {
108
- root.appendChild(styleElement);
109
- }
110
- return styleElement;
105
+ function appendStyleSheet(css, root) {
106
+ const styleElement = document.createElement('style');
107
+ styleElement.textContent = css;
108
+ root.appendChild(styleElement);
111
109
  }
112
- function appendDOMString(type, string, root = document.body) {
113
- const nodes = [];
114
- const parser = new DOMParser();
115
- const fragment = document.createDocumentFragment();
116
- const { body, childNodes } = parser.parseFromString(string, type);
117
- for (const node of body ? body.childNodes : childNodes) {
118
- nodes.push(node);
119
- fragment.appendChild(node);
120
- }
121
- root.appendChild(fragment);
122
- return nodes;
110
+ function appendMarkup(type, string, root) {
111
+ const { body, documentElement } = new DOMParser().parseFromString(string, type);
112
+ // XML: the root element is documentElement.
113
+ // HTML: the implicit <head> precedes <body>, the root element is body.firstElementChild.
114
+ const element = type === 'image/svg+xml' ? documentElement : body.firstElementChild;
115
+ root.appendChild(element);
116
+ return element;
123
117
  }
124
118
 
125
- export { ansiToHTML, appendDOMString, blockToHTML, escapeHTML, getRootElement, insertCSSString };
119
+ export { ansiToHTML, appendMarkup, appendStyleSheet, blockToHTML, escapeHTML, getRootElement };
@@ -2,7 +2,7 @@
2
2
  * @module rspack-dev-middleware
3
3
  * @package rspack-dev-middleware
4
4
  * @license MIT
5
- * @version 0.4.0
5
+ * @version 0.4.2
6
6
  * @author nuintun <nuintun@qq.com>
7
7
  * @description A koa 2 middleware for rspack development and hot reloading.
8
8
  * @see https://github.com/nuintun/rspack-dev-middleware#readme
@@ -31,7 +31,7 @@ async function dispatch(middlewares, index, stack, context, next) {
31
31
  await middleware(context, () => {
32
32
  return dispatch(middlewares, index + 1, stack, context, next);
33
33
  });
34
- } else if (next) {
34
+ } else if (next != null) {
35
35
  await next();
36
36
  }
37
37
  }
@@ -2,7 +2,7 @@
2
2
  * @module rspack-dev-middleware
3
3
  * @package rspack-dev-middleware
4
4
  * @license MIT
5
- * @version 0.4.0
5
+ * @version 0.4.2
6
6
  * @author nuintun <nuintun@qq.com>
7
7
  * @description A koa 2 middleware for rspack development and hot reloading.
8
8
  * @see https://github.com/nuintun/rspack-dev-middleware#readme
@@ -97,12 +97,12 @@ class ReadStream extends Readable {
97
97
  // Change read state.
98
98
  switch (this.#readState) {
99
99
  case 0 /* ReadState.PREFIX */:
100
- this.#readState = 1 /* ReadState.RANGE */;
100
+ this.#readState = 1; /* ReadState.RANGE */
101
101
  this.#readFileRange(fd, range, size - bytesRead);
102
102
  break;
103
103
  case 2 /* ReadState.SUFFIX */:
104
104
  this.#currentRangeIndex++;
105
- this.#readState = 0 /* ReadState.PREFIX */;
105
+ this.#readState = 0; /* ReadState.PREFIX */
106
106
  const nextRange = this.#getRange();
107
107
  if (nextRange == null) {
108
108
  this.push(null);
@@ -145,14 +145,14 @@ class ReadStream extends Readable {
145
145
  // file range read completed.
146
146
  if (bytesRead < size) {
147
147
  this.#bytesRead = 0;
148
- this.#readState = 2 /* ReadState.SUFFIX */;
148
+ this.#readState = 2; /* ReadState.SUFFIX */
149
149
  this.#readPadding(fd, range, size - bytesRead);
150
150
  }
151
151
  }
152
152
  });
153
153
  } else {
154
154
  this.#bytesRead = 0;
155
- this.#readState = 2 /* ReadState.SUFFIX */;
155
+ this.#readState = 2; /* ReadState.SUFFIX */
156
156
  this.#readPadding(fd, range, size);
157
157
  }
158
158
  }
@@ -2,19 +2,18 @@
2
2
  * @module rspack-dev-middleware
3
3
  * @package rspack-dev-middleware
4
4
  * @license MIT
5
- * @version 0.4.0
5
+ * @version 0.4.2
6
6
  * @author nuintun <nuintun@qq.com>
7
7
  * @description A koa 2 middleware for rspack development and hot reloading.
8
8
  * @see https://github.com/nuintun/rspack-dev-middleware#readme
9
9
  */
10
10
 
11
- import createETag from 'etag';
12
11
  import { ReadStream } from './ReadStream.js';
13
12
  import { isFunction } from '../utils.js';
14
13
  import { stat } from './utils/fs.js';
15
14
  import { resolve, extname, join } from 'node:path';
16
15
  import { unixify, isOutRoot, hasTrailingSlash } from './utils/path.js';
17
- import { isConditionalGET, isPreconditionFailed, parseRanges } from './utils/http.js';
16
+ import { createETag, isConditionalGET, isPreconditionFailed, parseRanges } from './utils/http.js';
18
17
 
19
18
  /**
20
19
  * @module Service
@@ -58,7 +57,7 @@ class Service {
58
57
  // Get headers.
59
58
  const headers = await options.headers(path, stats);
60
59
  // If headers not empty, set headers.
61
- if (headers) {
60
+ if (headers != null) {
62
61
  response.set(headers);
63
62
  }
64
63
  // Accept-Ranges.
@@ -152,10 +151,8 @@ class Service {
152
151
  }
153
152
  // Head request.
154
153
  if (request.method === 'HEAD') {
155
- // Set Content-Length.
154
+ // Set Content-Length, koa keeps it and sends no body for HEAD.
156
155
  response.length = stats.size;
157
- // Set body null
158
- response.body = null;
159
156
  // File found.
160
157
  return true;
161
158
  }
@@ -163,14 +160,12 @@ class Service {
163
160
  const ranges = parseRanges(context, stats);
164
161
  // 416
165
162
  if (ranges === -1) {
166
- // Set Content-Range.
167
- response.set('Content-Range', `bytes */${stats.size}`);
168
- // Unsatisfiable 416.
169
- return context.throw(416);
170
- }
171
- // 400.
172
- if (ranges === -2) {
173
- return context.throw(400);
163
+ // koa strips all response headers on error, pass Content-Range via err.headers.
164
+ return context.throw(416, {
165
+ headers: {
166
+ 'Content-Range': `bytes */${stats.size}`
167
+ }
168
+ });
174
169
  }
175
170
  // Set response body.
176
171
  response.body = new ReadStream(path, ranges, {
@@ -2,7 +2,7 @@
2
2
  * @module rspack-dev-middleware
3
3
  * @package rspack-dev-middleware
4
4
  * @license MIT
5
- * @version 0.4.0
5
+ * @version 0.4.2
6
6
  * @author nuintun <nuintun@qq.com>
7
7
  * @description A koa 2 middleware for rspack development and hot reloading.
8
8
  * @see https://github.com/nuintun/rspack-dev-middleware#readme
@@ -25,8 +25,12 @@ function setup(compiler, options) {
25
25
  compiler,
26
26
  stats: null,
27
27
  callbacks: [],
28
- logger: compiler.getInfrastructureLogger(PLUGIN_NAME),
29
- options: { fs: createMemfs(), highWaterMark: 65536, ...options }
28
+ options: {
29
+ ...options,
30
+ fs: options.fs ?? createMemfs(),
31
+ highWaterMark: options.highWaterMark ?? 65536
32
+ },
33
+ logger: compiler.getInfrastructureLogger(PLUGIN_NAME)
30
34
  };
31
35
  setupHooks(context);
32
36
  if (options.writeToDisk) {
@@ -2,14 +2,13 @@
2
2
  * @module rspack-dev-middleware
3
3
  * @package rspack-dev-middleware
4
4
  * @license MIT
5
- * @version 0.4.0
5
+ * @version 0.4.2
6
6
  * @author nuintun <nuintun@qq.com>
7
7
  * @description A koa 2 middleware for rspack development and hot reloading.
8
8
  * @see https://github.com/nuintun/rspack-dev-middleware#readme
9
9
  */
10
10
 
11
11
  import { Service } from './Service.js';
12
- import { ready } from './utils/ready.js';
13
12
  import { decodeURI } from './utils/http.js';
14
13
  import { getPaths } from './utils/paths.js';
15
14
 
@@ -29,13 +28,13 @@ function getFileServices(context, stats) {
29
28
  }
30
29
  function getFileServicesAsync(context) {
31
30
  return new Promise(resolve => {
32
- const { stats } = context;
33
- // If stats exists, resolve immediately.
31
+ const { stats, callbacks } = context;
32
+ // Resolve immediately if stats exists.
34
33
  if (stats) {
35
34
  resolve(getFileServices(context, stats));
36
35
  } else {
37
36
  // Otherwise, wait until bundle finished.
38
- ready(context, stats => {
37
+ callbacks.push(stats => {
39
38
  resolve(getFileServices(context, stats));
40
39
  });
41
40
  }
@@ -2,7 +2,7 @@
2
2
  * @module rspack-dev-middleware
3
3
  * @package rspack-dev-middleware
4
4
  * @license MIT
5
- * @version 0.4.0
5
+ * @version 0.4.2
6
6
  * @author nuintun <nuintun@qq.com>
7
7
  * @description A koa 2 middleware for rspack development and hot reloading.
8
8
  * @see https://github.com/nuintun/rspack-dev-middleware#readme
@@ -16,12 +16,37 @@ export interface Range {
16
16
  * @param URI The URI to decode.
17
17
  */
18
18
  export declare function decodeURI(URI: string): string | -1;
19
+ /**
20
+ * @function generateBoundary
21
+ * @description Generate a boundary string.
22
+ */
23
+ export declare function generateBoundary(): string;
19
24
  /**
20
25
  * @function isConditionalGET
21
26
  * @description Check if request is conditional GET.
22
27
  * @param context The koa context.
23
28
  */
24
29
  export declare function isConditionalGET({ request }: Context): boolean;
30
+ /**
31
+ * @function createETag
32
+ * @description Generate a weak entity tag from file stats.
33
+ * @param stats The file stats.
34
+ */
35
+ export declare function createETag(stats: Stats): string;
36
+ /**
37
+ * @function isETagMatch
38
+ * @see https://httpwg.org/specs/rfc9110.html
39
+ * @param match The match value.
40
+ * @param etag The etag value.
41
+ * @param isIfMatch The flag of if-match.
42
+ */
43
+ export declare function isETagMatch(match: string, etag: string, isIfMatch?: boolean): boolean;
44
+ /**
45
+ * @function isRangeFresh
46
+ * @description Check if request range fresh.
47
+ * @param context Koa context.
48
+ */
49
+ export declare function isRangeFresh({ request, response }: Context): boolean;
25
50
  /**
26
51
  * @function isPreconditionFailed
27
52
  * @description Check if request precondition failed.
@@ -34,4 +59,4 @@ export declare function isPreconditionFailed({ request, response }: Context): bo
34
59
  * @param context The koa context.
35
60
  * @param stats The file stats.
36
61
  */
37
- export declare function parseRanges(context: Context, stats: Stats): -1 | -2 | Range[];
62
+ export declare function parseRanges(context: Context, stats: Stats): -1 | Range[];
@@ -2,20 +2,21 @@
2
2
  * @module rspack-dev-middleware
3
3
  * @package rspack-dev-middleware
4
4
  * @license MIT
5
- * @version 0.4.0
5
+ * @version 0.4.2
6
6
  * @author nuintun <nuintun@qq.com>
7
7
  * @description A koa 2 middleware for rspack development and hot reloading.
8
8
  * @see https://github.com/nuintun/rspack-dev-middleware#readme
9
9
  */
10
10
 
11
- import { generate } from './hash.js';
12
11
  import { Buffer } from 'node:buffer';
13
12
  import parseRange from 'range-parser';
13
+ import { getRandomValues } from 'node:crypto';
14
14
 
15
15
  /**
16
16
  * @module http
17
17
  */
18
18
  const SPLIT_ETAG_RE = /\s*,\s*/;
19
+ const BOUNDARY_SCRATCH = Buffer.allocUnsafeSlow(24);
19
20
  const SINGLE_ETAG_RE = /^(?:W\/)?"[ !#-\x7E\x80-\xFF]+"$/;
20
21
  /**
21
22
  * @function decodeURI
@@ -29,6 +30,13 @@ function decodeURI(URI) {
29
30
  return -1;
30
31
  }
31
32
  }
33
+ /**
34
+ * @function generateBoundary
35
+ * @description Generate a boundary string.
36
+ */
37
+ function generateBoundary() {
38
+ return getRandomValues(BOUNDARY_SCRATCH).toString('base64url');
39
+ }
32
40
  /**
33
41
  * @function isConditionalGET
34
42
  * @description Check if request is conditional GET.
@@ -42,6 +50,14 @@ function isConditionalGET({ request }) {
42
50
  request.get('If-Unmodified-Since')
43
51
  );
44
52
  }
53
+ /**
54
+ * @function createETag
55
+ * @description Generate a weak entity tag from file stats.
56
+ * @param stats The file stats.
57
+ */
58
+ function createETag(stats) {
59
+ return `W/"${stats.size.toString(16)}-${stats.mtime.getTime().toString(16)}"`;
60
+ }
45
61
  /**
46
62
  * @function isETagMatch
47
63
  * @see https://httpwg.org/specs/rfc9110.html
@@ -50,25 +66,45 @@ function isConditionalGET({ request }) {
50
66
  * @param isIfMatch The flag of if-match.
51
67
  */
52
68
  function isETagMatch(match, etag, isIfMatch) {
69
+ // Trim match.
70
+ match = match.trim();
71
+ // Check If-Match.
72
+ if (isIfMatch && match === '*') {
73
+ // The condition is true if the selected representation exists.
74
+ return true;
75
+ }
53
76
  // Trim etag.
54
77
  etag = etag.trim();
55
78
  // Weak tags cannot be matched and return false.
56
79
  if (!etag || etag.startsWith('W/')) {
57
80
  return false;
58
81
  }
59
- // Trim match.
60
- match = match.trim();
61
82
  // Check If-Match.
62
83
  if (isIfMatch) {
63
- if (match === '*') {
64
- return true;
65
- }
66
84
  // If-Match maybe a list of etag.
67
85
  return match.split(SPLIT_ETAG_RE).includes(etag);
68
86
  }
69
87
  // Check If-Range.
70
88
  return match === etag;
71
89
  }
90
+ /**
91
+ * @function isRangeFresh
92
+ * @description Check if request range fresh.
93
+ * @param context Koa context.
94
+ */
95
+ function isRangeFresh({ request, response }) {
96
+ const ifRange = request.get('If-Range');
97
+ // No If-Range.
98
+ if (!ifRange) {
99
+ return true;
100
+ }
101
+ // If-Range as modified date failed.
102
+ if (SINGLE_ETAG_RE.test(ifRange)) {
103
+ return isETagMatch(ifRange, response.get('ETag'));
104
+ }
105
+ // Check if Last-Modified is valid and equal to If-Range date
106
+ return Date.parse(response.get('Last-Modified')) <= Date.parse(ifRange);
107
+ }
72
108
  /**
73
109
  * @function isPreconditionFailed
74
110
  * @description Check if request precondition failed.
@@ -91,24 +127,6 @@ function isPreconditionFailed({ request, response }) {
91
127
  // Check precondition passed.
92
128
  return false;
93
129
  }
94
- /**
95
- * @function isRangeFresh
96
- * @description Check if request range fresh.
97
- * @param context Koa context.
98
- */
99
- function isRangeFresh({ request, response }) {
100
- const ifRange = request.get('If-Range');
101
- // No If-Range.
102
- if (!ifRange) {
103
- return true;
104
- }
105
- // If-Range as modified date failed.
106
- if (SINGLE_ETAG_RE.test(ifRange)) {
107
- return isETagMatch(ifRange, response.get('ETag'));
108
- }
109
- // Check if Last-Modified is valid and equal to If-Range date
110
- return Date.parse(response.get('Last-Modified')) <= Date.parse(ifRange);
111
- }
112
130
  /**
113
131
  * @function parseRanges
114
132
  * @description Parse ranges.
@@ -126,10 +144,16 @@ function parseRanges(context, stats) {
126
144
  // Parse range -1 -2 or [].
127
145
  const parsed = parseRange(size, range, { combine: true });
128
146
  // -1 signals an unsatisfiable range.
129
- // -2 signals a malformed header string.
130
- if (parsed === -1 || parsed === -2) {
147
+ if (parsed === -1) {
131
148
  return parsed;
132
149
  }
150
+ // -2 signals a malformed or unknown-unit range, ignore it (RFC 9110 §14.2).
151
+ if (parsed === -2) {
152
+ // Set Content-Length.
153
+ response.length = size;
154
+ // Return ranges.
155
+ return [{ offset: 0, length: size }];
156
+ }
133
157
  // Ranges ok, support multiple ranges.
134
158
  if (parsed.type === 'bytes') {
135
159
  // Set 206 status.
@@ -144,7 +168,7 @@ function parseRanges(context, stats) {
144
168
  // Parsed entries.
145
169
  const entries = parsed.entries();
146
170
  // Range boundary.
147
- const boundary = `${generate(32)}`;
171
+ const boundary = generateBoundary();
148
172
  // Multipart Content-Type.
149
173
  const contentType = `Content-Type: ${response.type}`;
150
174
  // Range suffix.
@@ -190,4 +214,13 @@ function parseRanges(context, stats) {
190
214
  return [{ offset: 0, length: size }];
191
215
  }
192
216
 
193
- export { decodeURI, isConditionalGET, isPreconditionFailed, parseRanges };
217
+ export {
218
+ createETag,
219
+ decodeURI,
220
+ generateBoundary,
221
+ isConditionalGET,
222
+ isETagMatch,
223
+ isPreconditionFailed,
224
+ isRangeFresh,
225
+ parseRanges
226
+ };
@@ -2,7 +2,7 @@
2
2
  * @module rspack-dev-middleware
3
3
  * @package rspack-dev-middleware
4
4
  * @license MIT
5
- * @version 0.4.0
5
+ * @version 0.4.2
6
6
  * @author nuintun <nuintun@qq.com>
7
7
  * @description A koa 2 middleware for rspack development and hot reloading.
8
8
  * @see https://github.com/nuintun/rspack-dev-middleware#readme
@@ -2,7 +2,7 @@
2
2
  * @module rspack-dev-middleware
3
3
  * @package rspack-dev-middleware
4
4
  * @license MIT
5
- * @version 0.4.0
5
+ * @version 0.4.2
6
6
  * @author nuintun <nuintun@qq.com>
7
7
  * @description A koa 2 middleware for rspack development and hot reloading.
8
8
  * @see https://github.com/nuintun/rspack-dev-middleware#readme
@@ -2,26 +2,20 @@
2
2
  * @module rspack-dev-middleware
3
3
  * @package rspack-dev-middleware
4
4
  * @license MIT
5
- * @version 0.4.0
5
+ * @version 0.4.2
6
6
  * @author nuintun <nuintun@qq.com>
7
7
  * @description A koa 2 middleware for rspack development and hot reloading.
8
8
  * @see https://github.com/nuintun/rspack-dev-middleware#readme
9
9
  */
10
10
 
11
11
  import supportsColor from 'supports-color';
12
- import { PLUGIN_NAME, isMultiCompiler, isString, isBoolean } from '../../utils.js';
12
+ import { PLUGIN_NAME, isMultiCompiler, toStatsPreset } from '../../utils.js';
13
13
 
14
14
  /**
15
15
  * @module setupHooks
16
16
  */
17
17
  function normalizeStatsOptions(statsOptions) {
18
- if (statsOptions == null) {
19
- return { preset: 'normal' };
20
- } else if (isString(statsOptions)) {
21
- return { preset: statsOptions };
22
- } else if (isBoolean(statsOptions)) {
23
- return statsOptions ? { preset: 'normal' } : { preset: 'none' };
24
- }
18
+ statsOptions = toStatsPreset(statsOptions);
25
19
  if (statsOptions.colors == null) {
26
20
  const { stdout, stderr } = supportsColor;
27
21
  statsOptions.colors = stdout !== false && stderr !== false;
@@ -75,7 +69,7 @@ function setupHooks(context) {
75
69
  process.nextTick(() => {
76
70
  const { stats } = context;
77
71
  // Check if still in valid state.
78
- if (stats) {
72
+ if (stats != null) {
79
73
  // Call onCompilationDone.
80
74
  onCompilationDone(stats, statsOptions);
81
75
  // Callbacks.
@@ -2,7 +2,7 @@
2
2
  * @module rspack-dev-middleware
3
3
  * @package rspack-dev-middleware
4
4
  * @license MIT
5
- * @version 0.4.0
5
+ * @version 0.4.2
6
6
  * @author nuintun <nuintun@qq.com>
7
7
  * @description A koa 2 middleware for rspack development and hot reloading.
8
8
  * @see https://github.com/nuintun/rspack-dev-middleware#readme
@@ -2,7 +2,7 @@
2
2
  * @module rspack-dev-middleware
3
3
  * @package rspack-dev-middleware
4
4
  * @license MIT
5
- * @version 0.4.0
5
+ * @version 0.4.2
6
6
  * @author nuintun <nuintun@qq.com>
7
7
  * @description A koa 2 middleware for rspack development and hot reloading.
8
8
  * @see https://github.com/nuintun/rspack-dev-middleware#readme
@@ -13,11 +13,8 @@ import { isMultiCompiler } from '../../utils.js';
13
13
  /**
14
14
  * @module setupWatching
15
15
  */
16
- function getWatching({ compiler, logger }) {
17
- const isMulti = isMultiCompiler(compiler);
18
- if (!isMulti && compiler.watching) {
19
- return compiler.watching;
20
- }
16
+ function setupWatching(context) {
17
+ const { compiler, logger } = context;
21
18
  const errorHandler = error => {
22
19
  if (error != null) {
23
20
  // For example - `writeToDisk` can throw an error and right now it is ends watching.
@@ -26,17 +23,15 @@ function getWatching({ compiler, logger }) {
26
23
  logger.error(error);
27
24
  }
28
25
  };
29
- if (!isMulti) {
30
- const { watchOptions } = compiler.options;
31
- return compiler.watch(watchOptions, errorHandler);
26
+ if (isMultiCompiler(compiler)) {
27
+ const watchOptions = compiler.compilers.map(({ options }) => {
28
+ return options.watchOptions;
29
+ });
30
+ context.watching = compiler.watch(watchOptions, errorHandler);
31
+ } else {
32
+ // Reuse existing watching if present, otherwise start watching.
33
+ context.watching = compiler.watching ?? compiler.watch(compiler.options.watchOptions, errorHandler);
32
34
  }
33
- const watchOptions = compiler.compilers.map(({ options }) => {
34
- return options.watchOptions;
35
- });
36
- return compiler.watch(watchOptions, errorHandler);
37
- }
38
- function setupWatching(context) {
39
- context.watching = getWatching(context);
40
35
  }
41
36
 
42
37
  export { setupWatching };
@@ -2,7 +2,7 @@
2
2
  * @module rspack-dev-middleware
3
3
  * @package rspack-dev-middleware
4
4
  * @license MIT
5
- * @version 0.4.0
5
+ * @version 0.4.2
6
6
  * @author nuintun <nuintun@qq.com>
7
7
  * @description A koa 2 middleware for rspack development and hot reloading.
8
8
  * @see https://github.com/nuintun/rspack-dev-middleware#readme