webpack-dev-service 0.7.2 → 0.8.0

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 (71) hide show
  1. package/README.md +7 -10
  2. package/client/cjs/client.cjs +1 -1
  3. package/client/cjs/events.cjs +1 -1
  4. package/client/cjs/hot.cjs +1 -1
  5. package/client/cjs/index.cjs +1 -1
  6. package/client/cjs/main.cjs +1 -1
  7. package/client/cjs/ui/overlay.cjs +1 -1
  8. package/client/cjs/ui/progress.cjs +1 -1
  9. package/client/cjs/ui/utils.cjs +1 -1
  10. package/client/esm/client.js +1 -1
  11. package/client/esm/events.js +1 -1
  12. package/client/esm/hot.js +1 -1
  13. package/client/esm/index.js +1 -1
  14. package/client/esm/main.js +1 -1
  15. package/client/esm/ui/overlay.js +1 -1
  16. package/client/esm/ui/progress.js +1 -1
  17. package/client/esm/ui/utils.js +1 -1
  18. package/global.d.ts +2 -0
  19. package/package.json +13 -6
  20. package/server/cjs/dev/Files.cjs +394 -0
  21. package/server/cjs/dev/index.cjs +62 -0
  22. package/server/cjs/dev/middleware.cjs +70 -0
  23. package/server/cjs/dev/utils/boundary.cjs +42 -0
  24. package/server/cjs/dev/utils/common.cjs +98 -0
  25. package/server/cjs/dev/utils/compose.cjs +58 -0
  26. package/server/cjs/dev/utils/getPaths.cjs +65 -0
  27. package/server/cjs/dev/utils/http.cjs +69 -0
  28. package/server/cjs/dev/utils/ready.cjs +26 -0
  29. package/server/cjs/dev/utils/setupHooks.cjs +95 -0
  30. package/server/cjs/dev/utils/setupOutputFileSystem.cjs +64 -0
  31. package/server/cjs/dev/utils/setupWatching.cjs +43 -0
  32. package/server/cjs/dev/utils/setupWriteToDisk.cjs +62 -0
  33. package/server/cjs/{hot.cjs → hot/Socket.cjs} +6 -20
  34. package/server/cjs/hot/index.cjs +36 -0
  35. package/server/cjs/index.cjs +11 -15
  36. package/server/esm/dev/Files.js +384 -0
  37. package/server/esm/dev/index.js +60 -0
  38. package/server/esm/dev/middleware.js +68 -0
  39. package/server/esm/dev/utils/boundary.js +40 -0
  40. package/server/esm/dev/utils/common.js +98 -0
  41. package/server/esm/dev/utils/compose.js +56 -0
  42. package/server/esm/dev/utils/getPaths.js +63 -0
  43. package/server/esm/dev/utils/http.js +65 -0
  44. package/server/esm/dev/utils/ready.js +24 -0
  45. package/server/esm/dev/utils/setupHooks.js +87 -0
  46. package/server/esm/dev/utils/setupOutputFileSystem.js +62 -0
  47. package/server/esm/dev/utils/setupWatching.js +41 -0
  48. package/server/esm/dev/utils/setupWriteToDisk.js +60 -0
  49. package/server/esm/{hot.js → hot/Socket.js} +6 -20
  50. package/server/esm/hot/index.js +34 -0
  51. package/server/esm/index.js +11 -9
  52. package/types/server/dev/Files.d.ts +83 -0
  53. package/types/server/dev/index.d.ts +8 -0
  54. package/types/server/dev/interface.d.ts +46 -0
  55. package/types/server/dev/middleware.d.ts +6 -0
  56. package/types/server/dev/utils/boundary.d.ts +8 -0
  57. package/types/server/dev/utils/common.d.ts +44 -0
  58. package/types/server/dev/utils/compose.d.ts +18 -0
  59. package/types/server/dev/utils/getPaths.d.ts +10 -0
  60. package/types/server/dev/utils/http.d.ts +22 -0
  61. package/types/server/dev/utils/ready.d.ts +5 -0
  62. package/types/server/dev/utils/setupHooks.d.ts +5 -0
  63. package/types/server/dev/utils/setupOutputFileSystem.d.ts +5 -0
  64. package/types/server/dev/utils/setupWatching.d.ts +5 -0
  65. package/types/server/dev/utils/setupWriteToDisk.d.ts +5 -0
  66. package/types/server/hot/Socket.d.ts +27 -0
  67. package/types/server/{hot.d.ts → hot/index.d.ts} +7 -10
  68. package/types/server/index.d.ts +4 -4
  69. package/server/cjs/dev.cjs +0 -50
  70. package/server/esm/dev.js +0 -42
  71. package/types/server/dev.d.ts +0 -17
package/README.md CHANGED
@@ -168,20 +168,17 @@ const compiler = webpack({
168
168
  const port = 8000;
169
169
  const app = new Koa();
170
170
  const fs = createMemfs();
171
- const server = dev(compiler, { index: false, outputFileSystem: fs });
172
- const logger = compiler.getInfrastructureLogger('webpack-dev-middleware');
173
-
174
- app.use(async (ctx, next) => {
175
- ctx.set({
171
+ const server = dev(compiler, {
172
+ index: false,
173
+ outputFileSystem: fs,
174
+ headers: {
176
175
  'Cache-Control': 'no-store',
177
176
  'Access-Control-Allow-Origin': '*',
178
177
  'Access-Control-Allow-Methods': '*',
179
178
  'Access-Control-Allow-Headers': '*',
180
179
  'X-Content-Type-Options': 'nosniff',
181
180
  'Access-Control-Allow-Credentials': 'true'
182
- });
183
-
184
- await next();
181
+ }
185
182
  });
186
183
 
187
184
  app.use(server);
@@ -196,8 +193,8 @@ app.on('error', error => {
196
193
  });
197
194
 
198
195
  app.listen(port, () => {
199
- server.waitUntilValid(() => {
200
- logger.info(`server run at: \u001B[36mhttp://127.0.0.1:${port}\u001B[0m`);
196
+ server.ready(() => {
197
+ server.logger.info(`server run at: \u001B[36mhttp://127.0.0.1:${port}\u001B[0m`);
201
198
  });
202
199
  });
203
200
  ```
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.7.2
4
+ * @version 0.8.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.7.2
4
+ * @version 0.8.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.7.2
4
+ * @version 0.8.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.7.2
4
+ * @version 0.8.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.7.2
4
+ * @version 0.8.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.7.2
4
+ * @version 0.8.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.7.2
4
+ * @version 0.8.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.7.2
4
+ * @version 0.8.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.7.2
4
+ * @version 0.8.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.7.2
4
+ * @version 0.8.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
package/client/esm/hot.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.7.2
4
+ * @version 0.8.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.7.2
4
+ * @version 0.8.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.7.2
4
+ * @version 0.8.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.7.2
4
+ * @version 0.8.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.7.2
4
+ * @version 0.8.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.7.2
4
+ * @version 0.8.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
package/global.d.ts CHANGED
@@ -12,3 +12,5 @@ declare const __WDS_HOT_OPTIONS__: {
12
12
  };
13
13
 
14
14
  declare type HotUpdateStatus = `${webpack.HotUpdateStatus}`;
15
+
16
+ declare type Optional<T extends object, K extends keyof T = keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webpack-dev-service",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "A koa 2 middleware for webpack development and hot reloading.",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -89,22 +89,29 @@
89
89
  "@nuintun/ansi": "^0.0.3",
90
90
  "@types/koa": "^2.15.0",
91
91
  "@types/ws": "^8.5.10",
92
- "koa-compose": "^4.1.0",
92
+ "destroy": "^1.2.0",
93
+ "etag": "^1.8.1",
94
+ "memfs": "^4.8.0",
95
+ "range-parser": "^1.2.1",
96
+ "supports-color": "^9.4.0",
93
97
  "tslib": "^2.6.2",
94
- "webpack-dev-middleware": "^7.1.0",
95
98
  "ws": "^8.16.0"
96
99
  },
97
100
  "devDependencies": {
98
101
  "@rollup/plugin-typescript": "^11.1.6",
99
102
  "@swc/core": "^1.4.8",
100
103
  "@swc/helpers": "^0.5.7",
104
+ "@types/destroy": "^1.0.3",
105
+ "@types/etag": "^1.8.3",
101
106
  "@types/koa-compose": "^3.2.8",
102
107
  "@types/node": "^20.11.30",
108
+ "@types/range-parser": "^1.2.7",
103
109
  "@types/react": "^18.2.67",
104
110
  "@types/react-dom": "^18.2.22",
105
111
  "css-loader": "^6.10.0",
106
112
  "html-webpack-plugin": "^5.6.0",
107
- "koa": "^2.15.1",
113
+ "koa": "^2.15.2",
114
+ "koa-compress": "^5.1.1",
108
115
  "magic-string": "^0.30.8",
109
116
  "memfs": "^4.8.0",
110
117
  "mini-css-extract-plugin": "^2.8.1",
@@ -115,8 +122,8 @@
115
122
  "rollup": "^4.13.0",
116
123
  "swc-loader": "^0.2.6",
117
124
  "tsc-alias": "^1.8.8",
118
- "typescript": "^5.4.2",
119
- "webpack": "^5.90.3"
125
+ "typescript": "^5.4.3",
126
+ "webpack": "^5.91.0"
120
127
  },
121
128
  "peerDependencies": {
122
129
  "webpack": "^5.0.0"
@@ -0,0 +1,394 @@
1
+ /**
2
+ * @package webpack-dev-service
3
+ * @license MIT
4
+ * @version 0.8.0
5
+ * @author nuintun <nuintun@qq.com>
6
+ * @description A koa 2 middleware for webpack development and hot reloading.
7
+ * @see https://github.com/nuintun/webpack-dev-service#readme
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const etag = require('etag');
13
+ const destroy = require('destroy');
14
+ const stream = require('stream');
15
+ const parseRange = require('range-parser');
16
+ const boundary = require('./utils/boundary.cjs');
17
+ const path = require('path');
18
+ const http = require('./utils/http.cjs');
19
+ const common = require('./utils/common.cjs');
20
+
21
+ function _interopDefault(e) {
22
+ return e && e.__esModule ? e : { default: e };
23
+ }
24
+
25
+ const etag__default = /*#__PURE__*/ _interopDefault(etag);
26
+ const destroy__default = /*#__PURE__*/ _interopDefault(destroy);
27
+ const parseRange__default = /*#__PURE__*/ _interopDefault(parseRange);
28
+
29
+ /**
30
+ * @module Files
31
+ */
32
+ /**
33
+ * @class Files
34
+ */
35
+ class Files {
36
+ /**
37
+ * @constructor
38
+ * @description Create files service.
39
+ * @param root Files service root.
40
+ * @param options Files service options.
41
+ */
42
+ constructor(root, options) {
43
+ this.options = options;
44
+ this.root = common.unixify(path.resolve(root));
45
+ }
46
+ /**
47
+ * @private
48
+ * @method isConditionalGET
49
+ * @description Check if request is conditional GET.
50
+ * @param context Koa context.
51
+ */
52
+ isConditionalGET(context) {
53
+ const { request } = context;
54
+ return !!(
55
+ request.get('If-Match') ||
56
+ request.get('If-None-Match') ||
57
+ request.get('If-Modified-Since') ||
58
+ request.get('if-Unmodified-Since')
59
+ );
60
+ }
61
+ /**
62
+ * @private
63
+ * @method isPreconditionFailure
64
+ * @description Check if request precondition failure.
65
+ * @param context Koa context.
66
+ */
67
+ isPreconditionFailure(context) {
68
+ const { request, response } = context;
69
+ // If-Match.
70
+ const match = request.get('If-Match');
71
+ if (match) {
72
+ const etag = response.get('ETag');
73
+ return !etag || (match !== '*' && !http.isETagFresh(match, etag));
74
+ }
75
+ // If-Unmodified-Since.
76
+ const unmodifiedSince = Date.parse(request.get('If-Unmodified-Since'));
77
+ if (!isNaN(unmodifiedSince)) {
78
+ const lastModified = Date.parse(response.get('Last-Modified'));
79
+ return isNaN(lastModified) || lastModified > unmodifiedSince;
80
+ }
81
+ return false;
82
+ }
83
+ /**
84
+ * @private
85
+ * @method isRangeFresh
86
+ * @description Check if request range fresh.
87
+ * @param context Koa context.
88
+ */
89
+ isRangeFresh(context) {
90
+ const { request, response } = context;
91
+ const ifRange = request.get('If-Range');
92
+ // No If-Range.
93
+ if (!ifRange) {
94
+ return true;
95
+ }
96
+ // If-Range as etag.
97
+ if (http.isETag(ifRange)) {
98
+ const etag = response.get('ETag');
99
+ return !!(etag && http.isETagFresh(ifRange, etag));
100
+ }
101
+ // If-Range as modified date.
102
+ const lastModified = response.get('Last-Modified');
103
+ return Date.parse(lastModified) <= Date.parse(ifRange);
104
+ }
105
+ /**
106
+ * @private
107
+ * @method parseRange
108
+ * @description Parse range.
109
+ * @param context Koa context.
110
+ * @param stats File stats.
111
+ */
112
+ parseRange(context, stats) {
113
+ const { size } = stats;
114
+ // Range support.
115
+ if (this.options.acceptRanges !== false) {
116
+ const range = context.request.get('Range');
117
+ // Range fresh.
118
+ if (range && this.isRangeFresh(context)) {
119
+ // Parse range -1 -2 or [].
120
+ const parsed = parseRange__default.default(size, range, { combine: true });
121
+ // -1 signals an unsatisfiable range.
122
+ // -2 signals a malformed header string.
123
+ if (parsed === -1 || parsed === -2) {
124
+ return parsed;
125
+ }
126
+ // Ranges ok, support multiple ranges.
127
+ if (parsed.type === 'bytes') {
128
+ // Set 206 status.
129
+ context.status = 206;
130
+ const { length } = parsed;
131
+ // Multiple ranges.
132
+ if (length > 1) {
133
+ // Content-Length.
134
+ let contentLength = 0;
135
+ // Ranges.
136
+ const ranges = [];
137
+ // Range boundary.
138
+ const boundary$1 = `<${boundary.generate()}>`;
139
+ // Range suffix.
140
+ const suffix = `\r\n--${boundary$1}--\r\n`;
141
+ // Multipart Content-Type.
142
+ const contentType = `Content-Type: ${context.type}`;
143
+ // Override Content-Type.
144
+ context.type = `multipart/byteranges; boundary=${boundary$1}`;
145
+ // Map ranges.
146
+ for (let index = 0; index < length; index++) {
147
+ const { start, end } = parsed[index];
148
+ // The first prefix boundary no \r\n.
149
+ const prefixHead = index > 0 ? '\r\n' : '';
150
+ const contentRange = `Content-Range: bytes ${start}-${end}/${size}`;
151
+ const prefix = `${prefixHead}--${boundary$1}\r\n${contentType}\r\n${contentRange}\r\n\r\n`;
152
+ // Compute Content-Length
153
+ contentLength += end - start + 1 + Buffer.byteLength(prefix);
154
+ // Cache range.
155
+ ranges.push({ start, end, prefix });
156
+ }
157
+ // The last add suffix boundary.
158
+ ranges[length - 1].suffix = suffix;
159
+ // Compute Content-Length.
160
+ contentLength += Buffer.byteLength(suffix);
161
+ // Set Content-Length.
162
+ context.length = contentLength;
163
+ // Return ranges.
164
+ return ranges;
165
+ } else {
166
+ const [{ start, end }] = parsed;
167
+ // Set Content-Length.
168
+ context.length = end - start + 1;
169
+ // Set Content-Range.
170
+ context.set('Content-Range', `bytes ${start}-${end}/${size}`);
171
+ // Return ranges.
172
+ return parsed;
173
+ }
174
+ }
175
+ }
176
+ }
177
+ // Set Content-Length.
178
+ context.length = size;
179
+ // Return ranges.
180
+ return [{ start: 0, end: Math.max(size - 1) }];
181
+ }
182
+ /**
183
+ * @private
184
+ * @method setupHeaders
185
+ * @description Setup headers
186
+ * @param context Koa context
187
+ * @param path File path
188
+ * @param stats File stats
189
+ */
190
+ setupHeaders(context, path$1, stats) {
191
+ const { options } = this;
192
+ const { headers, cacheControl } = options;
193
+ // Set headers.
194
+ if (headers) {
195
+ if (common.isFunction(headers)) {
196
+ context.set(headers(path$1, stats));
197
+ } else {
198
+ context.set(headers);
199
+ }
200
+ }
201
+ // Set status.
202
+ context.status = 200;
203
+ // Set Content-Type.
204
+ context.type = path.extname(path$1);
205
+ // ETag.
206
+ if (options.etag === false) {
207
+ context.remove('ETag');
208
+ } else {
209
+ // Set ETag.
210
+ context.set('ETag', etag__default.default(stats));
211
+ }
212
+ // Accept-Ranges.
213
+ if (options.acceptRanges === false) {
214
+ context.remove('Accept-Ranges');
215
+ } else {
216
+ // Set Accept-Ranges.
217
+ context.set('Accept-Ranges', 'bytes');
218
+ }
219
+ // Cache-Control.
220
+ if (cacheControl && common.isString(cacheControl)) {
221
+ // Set Cache-Control.
222
+ context.set('Cache-Control', cacheControl);
223
+ }
224
+ // Last-Modified.
225
+ if (options.lastModified === false) {
226
+ context.remove('Last-Modified');
227
+ } else {
228
+ // Set mtime utc string.
229
+ context.set('Last-Modified', stats.mtime.toUTCString());
230
+ }
231
+ }
232
+ /**
233
+ * @private
234
+ * @method readTo
235
+ * @description Read file.
236
+ * @param stream Destination stream.
237
+ * @param path File path.
238
+ * @param range Read range.
239
+ * @param end Is destory destination stream after read.
240
+ */
241
+ readTo(stream, path, range, end) {
242
+ const { fs } = this.options;
243
+ return new Promise((resolve, reject) => {
244
+ // Create file stream.
245
+ const file = fs.createReadStream(path, range);
246
+ // File read stream open.
247
+ if (range.prefix) {
248
+ file.once('open', () => {
249
+ // Write prefix boundary.
250
+ stream.write(range.prefix);
251
+ });
252
+ }
253
+ // File read stream error.
254
+ file.once('error', error => {
255
+ // Unpipe.
256
+ file.unpipe(stream);
257
+ // Reject.
258
+ reject(error);
259
+ // Destroy file stream.
260
+ destroy__default.default(file);
261
+ });
262
+ // File read stream end.
263
+ if (range.suffix) {
264
+ file.once('end', () => {
265
+ // Push suffix boundary.
266
+ stream.write(range.suffix);
267
+ });
268
+ }
269
+ // File read stream close.
270
+ file.once('close', () => {
271
+ // Unpipe.
272
+ file.unpipe(stream);
273
+ // Resolve.
274
+ resolve(true);
275
+ // Destroy file stream.
276
+ destroy__default.default(file);
277
+ });
278
+ // Write data to buffer.
279
+ file.pipe(stream, { end });
280
+ });
281
+ }
282
+ /**
283
+ * @private
284
+ * @method send
285
+ * @description Send file.
286
+ * @param context Koa context.
287
+ * @param path File path.
288
+ * @param ranges Read ranges.
289
+ */
290
+ async send(context, path, ranges) {
291
+ // Set stream body, highWaterMark 64kb.
292
+ const stream$1 = new stream.PassThrough({
293
+ highWaterMark: 65536
294
+ });
295
+ // Set response body.
296
+ context.body = stream$1;
297
+ // Ranges length.
298
+ let { length } = ranges;
299
+ // Read file ranges.
300
+ try {
301
+ for (const range of ranges) {
302
+ await this.readTo(stream$1, path, range, --length === 0);
303
+ }
304
+ } catch (_a) {
305
+ // End stream when read exception.
306
+ stream$1.end();
307
+ }
308
+ }
309
+ /**
310
+ * @public
311
+ * @method response
312
+ * @description Response to koa context.
313
+ * @param context Koa context.
314
+ */
315
+ async response(context) {
316
+ const { root } = this;
317
+ // Only support GET and HEAD (405).
318
+ if (context.method !== 'GET' && context.method !== 'HEAD') {
319
+ return false;
320
+ }
321
+ // Get path of file.
322
+ const path$1 = common.unixify(path.join(root, context.path));
323
+ // Malicious path (403).
324
+ if (common.isOutRoot(path$1, root)) {
325
+ return false;
326
+ }
327
+ // Get file system.
328
+ const { fs } = this.options;
329
+ // File stats.
330
+ let stats;
331
+ // Get file stats.
332
+ try {
333
+ stats = await common.fstat(fs, path$1);
334
+ } catch (_a) {
335
+ // 404 | 500.
336
+ return false;
337
+ }
338
+ // File not exist (404 | 500).
339
+ if (!stats) {
340
+ return false;
341
+ }
342
+ // Is directory (403).
343
+ if (stats.isDirectory()) {
344
+ return false;
345
+ }
346
+ // Not a directory but has trailing slash (404).
347
+ if (common.hasTrailingSlash(path$1)) {
348
+ return false;
349
+ }
350
+ // Setup headers.
351
+ this.setupHeaders(context, path$1, stats);
352
+ // Conditional get support.
353
+ if (this.isConditionalGET(context)) {
354
+ // Request precondition failure.
355
+ if (this.isPreconditionFailure(context)) {
356
+ return context.throw(412);
357
+ }
358
+ // Request fresh (304).
359
+ if (context.fresh) {
360
+ // Set status.
361
+ context.status = 304;
362
+ // Set body null.
363
+ context.body = null;
364
+ return true;
365
+ }
366
+ }
367
+ // Head request.
368
+ if (context.method === 'HEAD') {
369
+ // Set Content-Length.
370
+ context.length = stats.size;
371
+ // Set body null
372
+ context.body = null;
373
+ return true;
374
+ }
375
+ // Parsed ranges.
376
+ const ranges = this.parseRange(context, stats);
377
+ // 416
378
+ if (ranges === -1) {
379
+ // Set Content-Range.
380
+ context.set('Content-Range', `bytes */${stats.size}`);
381
+ // Unsatisfiable 416.
382
+ return context.throw(416);
383
+ }
384
+ // 400.
385
+ if (ranges === -2) {
386
+ return context.throw(400);
387
+ }
388
+ // Send file.
389
+ this.send(context, path$1, ranges);
390
+ return true;
391
+ }
392
+ }
393
+
394
+ module.exports = Files;