1. export function transformMiddleware(
    2. server: ViteDevServer
    3. ): Connect.NextHandleFunction {
    4. const {
    5. config: { root, logger, cacheDir },
    6. moduleGraph
    7. } = server
    8. // determine the url prefix of files inside cache directory
    9. let cacheDirPrefix: string | undefined
    10. if (cacheDir) {
    11. const cacheDirRelative = normalizePath(path.relative(root, cacheDir))
    12. if (cacheDirRelative.startsWith('../')) {
    13. // if the cache directory is outside root, the url prefix would be something
    14. // like '/@fs/absolute/path/to/node_modules/.vite'
    15. cacheDirPrefix = `/@fs/${normalizePath(cacheDir).replace(/^\//, '')}`
    16. } else {
    17. // if the cache directory is inside root, the url prefix would be something
    18. // like '/node_modules/.vite'
    19. cacheDirPrefix = `/${cacheDirRelative}`
    20. }
    21. }
    22. return async (req, res, next) => {
    23. // 如果不是 get 请求,获取资源的请求
    24. // 或者是在已知的可以选择忽略的资源('/','/favicon.ico'),则直接跳过转换
    25. if (req.method !== 'GET' || knownIgnoreList.has(req.url!)) {
    26. return next()
    27. }
    28. if (
    29. server._pendingReload &&
    30. // always allow vite client requests so that it can trigger page reload
    31. !req.url?.startsWith(CLIENT_PUBLIC_PATH) &&
    32. !req.url?.includes('vite/dist/client')
    33. ) {
    34. // missing dep pending reload, hold request until reload happens
    35. server._pendingReload.then(() =>
    36. // If the refresh has not happened after timeout, Vite considers
    37. // something unexpected has happened. In this case, Vite
    38. // returns an empty response that will error.
    39. setTimeout(() => {
    40. // status code request timeout
    41. res.statusCode = 408
    42. res.end(
    43. `<h1>[vite] Something unexpected happened while optimizing "${req.url}"<h1>` +
    44. `<p>The current page should have reloaded by now</p>`
    45. )
    46. }, NEW_DEPENDENCY_BUILD_TIMEOUT)
    47. )
    48. return
    49. }
    50. let url
    51. try {
    52. url = removeTimestampQuery(req.url!).replace(NULL_BYTE_PLACEHOLDER, '\0')
    53. } catch (err) {
    54. // if it starts with %PUBLIC%, someone's migrating from something
    55. // like create-react-app
    56. let errorMessage
    57. if (req.url?.startsWith('/%PUBLIC')) {
    58. errorMessage = `index.html shouldn't include environment variables like %PUBLIC_URL%, see https://vitejs.dev/guide/#index-html-and-project-root for more information`
    59. } else {
    60. errorMessage = `Vite encountered a suspiciously malformed request ${req.url}`
    61. }
    62. next(new Error(errorMessage))
    63. return
    64. }
    65. const withoutQuery = cleanUrl(url)
    66. try {
    67. const isSourceMap = withoutQuery.endsWith('.map')
    68. // since we generate source map references, handle those requests here
    69. if (isSourceMap) {
    70. const originalUrl = url.replace(/\.map($|\?)/, '$1')
    71. const map = (await moduleGraph.getModuleByUrl(originalUrl))
    72. ?.transformResult?.map
    73. if (map) {
    74. return send(req, res, JSON.stringify(map), 'json')
    75. } else {
    76. return next()
    77. }
    78. }
    79. // warn explicit /public/ paths
    80. if (url.startsWith('/public/')) {
    81. logger.warn(
    82. chalk.yellow(
    83. `files in the public directory are served at the root path.\n` +
    84. `Instead of ${chalk.cyan(url)}, use ${chalk.cyan(
    85. url.replace(/^\/public\//, '/')
    86. )}.`
    87. )
    88. )
    89. }
    90. if (
    91. isJSRequest(url) ||
    92. isImportRequest(url) ||
    93. isCSSRequest(url) ||
    94. isHTMLProxy(url)
    95. ) {
    96. // strip ?import
    97. url = removeImportQuery(url)
    98. // Strip valid id prefix. This is prepended to resolved Ids that are
    99. // not valid browser import specifiers by the importAnalysis plugin.
    100. url = unwrapId(url)
    101. // for CSS, we need to differentiate between normal CSS requests and
    102. // imports
    103. if (isCSSRequest(url) && req.headers.accept?.includes('text/css')) {
    104. url = injectQuery(url, 'direct')
    105. }
    106. // check if we can return 304 early
    107. const ifNoneMatch = req.headers['if-none-match']
    108. if (
    109. ifNoneMatch &&
    110. (await moduleGraph.getModuleByUrl(url))?.transformResult?.etag ===
    111. ifNoneMatch
    112. ) {
    113. isDebug && debugCache(`[304] ${prettifyUrl(url, root)}`)
    114. res.statusCode = 304
    115. return res.end()
    116. }
    117. // resolve, load and transform using the plugin container
    118. const result = await transformRequest(url, server, {
    119. html: req.headers.accept?.includes('text/html')
    120. })
    121. if (result) {
    122. const type = isDirectCSSRequest(url) ? 'css' : 'js'
    123. const isDep =
    124. DEP_VERSION_RE.test(url) ||
    125. (cacheDirPrefix && url.startsWith(cacheDirPrefix))
    126. return send(
    127. req,
    128. res,
    129. result.code,
    130. type,
    131. result.etag,
    132. // allow browser to cache npm deps!
    133. isDep ? 'max-age=31536000,immutable' : 'no-cache',
    134. result.map
    135. )
    136. }
    137. }
    138. } catch (e) {
    139. return next(e)
    140. }
    141. next()
    142. }
    143. }