'use strict'; const path = require('path'); const { parse } = require('url'); const querystring = require('querystring'); const parseRange = require('range-parser'); const HASH_REGEXP = /[0-9a-f]{10,}/; // support for multi-compiler configuration // see: https://github.com/webpack/webpack-dev-server/issues/641 function getPaths(publicPath, compiler, url) { const compilers = compiler && compiler.compilers; if (Array.isArray(compilers)) { let compilerPublicPath; // the path portion of compilerPublicPath let compilerPublicPathBase; for (let i = 0; i < compilers.length; i++) { compilerPublicPath = compilers[i].options && compilers[i].options.output && compilers[i].options.output.publicPath; if (compilerPublicPath) { compilerPublicPathBase = compilerPublicPath.indexOf('/') === 0 ? compilerPublicPath // eslint-disable-next-line : // handle the case where compilerPublicPath is a URL with hostname parse(compilerPublicPath).pathname; // check the url vs the path part of the compilerPublicPath if (url.indexOf(compilerPublicPathBase) === 0) { return { publicPath: compilerPublicPath, outputPath: compilers[i].outputPath, }; } } } } return { publicPath, outputPath: compiler.outputPath, }; } // eslint-disable-next-line consistent-return function ready(context, fn, req) { if (context.state) { return fn(context.webpackStats); } context.log.info(`wait until bundle finished: ${req.url || fn.name}`); context.callbacks.push(fn); } module.exports = { getFilenameFromUrl(pubPath, compiler, url) { const { outputPath, publicPath } = getPaths(pubPath, compiler, url); // localPrefix is the folder our bundle should be in const localPrefix = parse(publicPath || '/', false, true); const urlObject = parse(url); let filename; const hostNameIsTheSame = localPrefix.hostname === urlObject.hostname; // publicPath has the hostname that is not the same as request url's, should fail if ( localPrefix.hostname !== null && urlObject.hostname !== null && !hostNameIsTheSame ) { return false; } // publicPath is not in url, so it should fail if (publicPath && hostNameIsTheSame && url.indexOf(publicPath) !== 0) { return false; } // strip localPrefix from the start of url if (urlObject.pathname.indexOf(localPrefix.pathname) === 0) { filename = urlObject.pathname.substr(localPrefix.pathname.length); } if ( !urlObject.hostname && localPrefix.hostname && url.indexOf(localPrefix.path) !== 0 ) { return false; } let uri = outputPath; /* istanbul ignore if */ if (process.platform === 'win32') { // Path Handling for Microsoft Windows if (filename) { uri = path.posix.join(outputPath || '', querystring.unescape(filename)); if (!path.win32.isAbsolute(uri)) { uri = `/${uri}`; } } return uri; } // Path Handling for all other operating systems if (filename) { uri = path.posix.join(outputPath || '', filename); if (!path.posix.isAbsolute(uri)) { uri = `/${uri}`; } } // if no matches, use outputPath as filename return querystring.unescape(uri); }, handleRangeHeaders(content, req, res) { // assumes express API. For other servers, need to add logic to access // alternative header APIs res.setHeader('Accept-Ranges', 'bytes'); if (req.headers.range) { const ranges = parseRange(content.length, req.headers.range); // unsatisfiable if (ranges === -1) { res.setHeader('Content-Range', `bytes */${content.length}`); // eslint-disable-next-line no-param-reassign res.statusCode = 416; } // valid (syntactically invalid/multiple ranges are treated as a // regular response) if (ranges !== -2 && ranges.length === 1) { const { length } = content; // Content-Range // eslint-disable-next-line no-param-reassign res.statusCode = 206; res.setHeader( 'Content-Range', `bytes ${ranges[0].start}-${ranges[0].end}/${length}` ); // eslint-disable-next-line no-param-reassign content = content.slice(ranges[0].start, ranges[0].end + 1); } } return content; }, handleRequest(context, filename, processRequest, req) { // in lazy mode, rebuild on bundle request if ( context.options.lazy && (!context.options.filename || context.options.filename.test(filename)) ) { context.rebuild(); } if (HASH_REGEXP.test(filename)) { try { if (context.fs.statSync(filename).isFile()) { processRequest(); return; } } catch (e) { // eslint-disable-line } } ready(context, processRequest, req); }, noop: () => {}, ready, };