選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

util.js 4.7 KiB

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