1 |
3a515b92
|
cagy
|
'use strict';
|
2 |
|
|
|
3 |
|
|
const path = require('path');
|
4 |
|
|
const { parse } = require('url');
|
5 |
|
|
const querystring = require('querystring');
|
6 |
|
|
|
7 |
|
|
const parseRange = require('range-parser');
|
8 |
|
|
|
9 |
|
|
const HASH_REGEXP = /[0-9a-f]{10,}/;
|
10 |
|
|
|
11 |
|
|
// support for multi-compiler configuration
|
12 |
|
|
// see: https://github.com/webpack/webpack-dev-server/issues/641
|
13 |
|
|
function getPaths(publicPath, compiler, url) {
|
14 |
|
|
const compilers = compiler && compiler.compilers;
|
15 |
|
|
if (Array.isArray(compilers)) {
|
16 |
|
|
let compilerPublicPath;
|
17 |
|
|
|
18 |
|
|
// the path portion of compilerPublicPath
|
19 |
|
|
let compilerPublicPathBase;
|
20 |
|
|
|
21 |
|
|
for (let i = 0; i < compilers.length; i++) {
|
22 |
|
|
compilerPublicPath =
|
23 |
|
|
compilers[i].options &&
|
24 |
|
|
compilers[i].options.output &&
|
25 |
|
|
compilers[i].options.output.publicPath;
|
26 |
|
|
|
27 |
|
|
if (compilerPublicPath) {
|
28 |
|
|
compilerPublicPathBase =
|
29 |
|
|
compilerPublicPath.indexOf('/') === 0
|
30 |
|
|
? compilerPublicPath // eslint-disable-next-line
|
31 |
|
|
: // handle the case where compilerPublicPath is a URL with hostname
|
32 |
|
|
parse(compilerPublicPath).pathname;
|
33 |
|
|
|
34 |
|
|
// check the url vs the path part of the compilerPublicPath
|
35 |
|
|
if (url.indexOf(compilerPublicPathBase) === 0) {
|
36 |
|
|
return {
|
37 |
|
|
publicPath: compilerPublicPath,
|
38 |
|
|
outputPath: compilers[i].outputPath,
|
39 |
|
|
};
|
40 |
|
|
}
|
41 |
|
|
}
|
42 |
|
|
}
|
43 |
|
|
}
|
44 |
|
|
return {
|
45 |
|
|
publicPath,
|
46 |
|
|
outputPath: compiler.outputPath,
|
47 |
|
|
};
|
48 |
|
|
}
|
49 |
|
|
|
50 |
|
|
// eslint-disable-next-line consistent-return
|
51 |
|
|
function ready(context, fn, req) {
|
52 |
|
|
if (context.state) {
|
53 |
|
|
return fn(context.webpackStats);
|
54 |
|
|
}
|
55 |
|
|
|
56 |
|
|
context.log.info(`wait until bundle finished: ${req.url || fn.name}`);
|
57 |
|
|
context.callbacks.push(fn);
|
58 |
|
|
}
|
59 |
|
|
|
60 |
|
|
module.exports = {
|
61 |
|
|
getFilenameFromUrl(pubPath, compiler, url) {
|
62 |
|
|
const { outputPath, publicPath } = getPaths(pubPath, compiler, url);
|
63 |
|
|
// localPrefix is the folder our bundle should be in
|
64 |
|
|
const localPrefix = parse(publicPath || '/', false, true);
|
65 |
|
|
const urlObject = parse(url);
|
66 |
|
|
let filename;
|
67 |
|
|
|
68 |
|
|
const hostNameIsTheSame = localPrefix.hostname === urlObject.hostname;
|
69 |
|
|
|
70 |
|
|
// publicPath has the hostname that is not the same as request url's, should fail
|
71 |
|
|
if (
|
72 |
|
|
localPrefix.hostname !== null &&
|
73 |
|
|
urlObject.hostname !== null &&
|
74 |
|
|
!hostNameIsTheSame
|
75 |
|
|
) {
|
76 |
|
|
return false;
|
77 |
|
|
}
|
78 |
|
|
|
79 |
|
|
// publicPath is not in url, so it should fail
|
80 |
|
|
if (publicPath && hostNameIsTheSame && url.indexOf(publicPath) !== 0) {
|
81 |
|
|
return false;
|
82 |
|
|
}
|
83 |
|
|
|
84 |
|
|
// strip localPrefix from the start of url
|
85 |
|
|
if (urlObject.pathname.indexOf(localPrefix.pathname) === 0) {
|
86 |
|
|
filename = urlObject.pathname.substr(localPrefix.pathname.length);
|
87 |
|
|
}
|
88 |
|
|
|
89 |
|
|
if (
|
90 |
|
|
!urlObject.hostname &&
|
91 |
|
|
localPrefix.hostname &&
|
92 |
|
|
url.indexOf(localPrefix.path) !== 0
|
93 |
|
|
) {
|
94 |
|
|
return false;
|
95 |
|
|
}
|
96 |
|
|
|
97 |
|
|
let uri = outputPath;
|
98 |
|
|
|
99 |
|
|
/* istanbul ignore if */
|
100 |
|
|
if (process.platform === 'win32') {
|
101 |
|
|
// Path Handling for Microsoft Windows
|
102 |
|
|
if (filename) {
|
103 |
|
|
uri = path.posix.join(outputPath || '', querystring.unescape(filename));
|
104 |
|
|
|
105 |
|
|
if (!path.win32.isAbsolute(uri)) {
|
106 |
|
|
uri = `/${uri}`;
|
107 |
|
|
}
|
108 |
|
|
}
|
109 |
|
|
|
110 |
|
|
return uri;
|
111 |
|
|
}
|
112 |
|
|
|
113 |
|
|
// Path Handling for all other operating systems
|
114 |
|
|
if (filename) {
|
115 |
|
|
uri = path.posix.join(outputPath || '', filename);
|
116 |
|
|
|
117 |
|
|
if (!path.posix.isAbsolute(uri)) {
|
118 |
|
|
uri = `/${uri}`;
|
119 |
|
|
}
|
120 |
|
|
}
|
121 |
|
|
|
122 |
|
|
// if no matches, use outputPath as filename
|
123 |
|
|
return querystring.unescape(uri);
|
124 |
|
|
},
|
125 |
|
|
|
126 |
|
|
handleRangeHeaders(content, req, res) {
|
127 |
|
|
// assumes express API. For other servers, need to add logic to access
|
128 |
|
|
// alternative header APIs
|
129 |
|
|
res.setHeader('Accept-Ranges', 'bytes');
|
130 |
|
|
|
131 |
|
|
if (req.headers.range) {
|
132 |
|
|
const ranges = parseRange(content.length, req.headers.range);
|
133 |
|
|
|
134 |
|
|
// unsatisfiable
|
135 |
|
|
if (ranges === -1) {
|
136 |
|
|
res.setHeader('Content-Range', `bytes */${content.length}`);
|
137 |
|
|
// eslint-disable-next-line no-param-reassign
|
138 |
|
|
res.statusCode = 416;
|
139 |
|
|
}
|
140 |
|
|
|
141 |
|
|
// valid (syntactically invalid/multiple ranges are treated as a
|
142 |
|
|
// regular response)
|
143 |
|
|
if (ranges !== -2 && ranges.length === 1) {
|
144 |
|
|
const { length } = content;
|
145 |
|
|
|
146 |
|
|
// Content-Range
|
147 |
|
|
// eslint-disable-next-line no-param-reassign
|
148 |
|
|
res.statusCode = 206;
|
149 |
|
|
res.setHeader(
|
150 |
|
|
'Content-Range',
|
151 |
|
|
`bytes ${ranges[0].start}-${ranges[0].end}/${length}`
|
152 |
|
|
);
|
153 |
|
|
|
154 |
|
|
// eslint-disable-next-line no-param-reassign
|
155 |
|
|
content = content.slice(ranges[0].start, ranges[0].end + 1);
|
156 |
|
|
}
|
157 |
|
|
}
|
158 |
|
|
|
159 |
|
|
return content;
|
160 |
|
|
},
|
161 |
|
|
|
162 |
|
|
handleRequest(context, filename, processRequest, req) {
|
163 |
|
|
// in lazy mode, rebuild on bundle request
|
164 |
|
|
if (
|
165 |
|
|
context.options.lazy &&
|
166 |
|
|
(!context.options.filename || context.options.filename.test(filename))
|
167 |
|
|
) {
|
168 |
|
|
context.rebuild();
|
169 |
|
|
}
|
170 |
|
|
|
171 |
|
|
if (HASH_REGEXP.test(filename)) {
|
172 |
|
|
try {
|
173 |
|
|
if (context.fs.statSync(filename).isFile()) {
|
174 |
|
|
processRequest();
|
175 |
|
|
return;
|
176 |
|
|
}
|
177 |
|
|
} catch (e) {
|
178 |
|
|
// eslint-disable-line
|
179 |
|
|
}
|
180 |
|
|
}
|
181 |
|
|
|
182 |
|
|
ready(context, processRequest, req);
|
183 |
|
|
},
|
184 |
|
|
|
185 |
|
|
noop: () => {},
|
186 |
|
|
|
187 |
|
|
ready,
|
188 |
|
|
};
|