You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

136 lines
3.6 KiB

4 years ago
  1. 'use strict';
  2. const path = require('path');
  3. const mime = require('mime');
  4. const DevMiddlewareError = require('./DevMiddlewareError');
  5. const {
  6. getFilenameFromUrl,
  7. handleRangeHeaders,
  8. handleRequest,
  9. ready,
  10. } = require('./util');
  11. // Do not add a charset to the Content-Type header of these file types
  12. // otherwise the client will fail to render them correctly.
  13. const NonCharsetFileTypes = /\.(wasm|usdz)$/;
  14. module.exports = function wrapper(context) {
  15. return function middleware(req, res, next) {
  16. // fixes #282. credit @cexoso. in certain edge situations res.locals is
  17. // undefined.
  18. // eslint-disable-next-line no-param-reassign
  19. res.locals = res.locals || {};
  20. function goNext() {
  21. if (!context.options.serverSideRender) {
  22. return next();
  23. }
  24. return new Promise((resolve) => {
  25. ready(
  26. context,
  27. () => {
  28. // eslint-disable-next-line no-param-reassign
  29. res.locals.webpackStats = context.webpackStats;
  30. // eslint-disable-next-line no-param-reassign
  31. res.locals.fs = context.fs;
  32. resolve(next());
  33. },
  34. req
  35. );
  36. });
  37. }
  38. const acceptedMethods = context.options.methods || ['GET', 'HEAD'];
  39. if (acceptedMethods.indexOf(req.method) === -1) {
  40. return goNext();
  41. }
  42. let filename = getFilenameFromUrl(
  43. context.options.publicPath,
  44. context.compiler,
  45. req.url
  46. );
  47. if (filename === false) {
  48. return goNext();
  49. }
  50. return new Promise((resolve) => {
  51. handleRequest(context, filename, processRequest, req);
  52. // eslint-disable-next-line consistent-return
  53. function processRequest() {
  54. try {
  55. let stat = context.fs.statSync(filename);
  56. if (!stat.isFile()) {
  57. if (stat.isDirectory()) {
  58. let { index } = context.options;
  59. // eslint-disable-next-line no-undefined
  60. if (index === undefined || index === true) {
  61. index = 'index.html';
  62. } else if (!index) {
  63. throw new DevMiddlewareError('next');
  64. }
  65. filename = path.posix.join(filename, index);
  66. stat = context.fs.statSync(filename);
  67. if (!stat.isFile()) {
  68. throw new DevMiddlewareError('next');
  69. }
  70. } else {
  71. throw new DevMiddlewareError('next');
  72. }
  73. }
  74. } catch (e) {
  75. return resolve(goNext());
  76. }
  77. // server content
  78. let content = context.fs.readFileSync(filename);
  79. content = handleRangeHeaders(content, req, res);
  80. let contentType = mime.getType(filename) || '';
  81. if (!NonCharsetFileTypes.test(filename)) {
  82. contentType += '; charset=UTF-8';
  83. }
  84. if (!res.getHeader || !res.getHeader('Content-Type')) {
  85. res.setHeader('Content-Type', contentType);
  86. }
  87. res.setHeader('Content-Length', content.length);
  88. const { headers } = context.options;
  89. if (headers) {
  90. for (const name in headers) {
  91. if ({}.hasOwnProperty.call(headers, name)) {
  92. res.setHeader(name, context.options.headers[name]);
  93. }
  94. }
  95. }
  96. // Express automatically sets the statusCode to 200, but not all servers do (Koa).
  97. // eslint-disable-next-line no-param-reassign
  98. res.statusCode = res.statusCode || 200;
  99. if (res.send) {
  100. res.send(content);
  101. } else {
  102. res.end(content);
  103. }
  104. resolve();
  105. }
  106. });
  107. };
  108. };