serverPluginModuleRewrite.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. exports.resolveImport = exports.rewriteImports = exports.moduleRewritePlugin = void 0;
  7. const path_1 = __importDefault(require("path"));
  8. const lru_cache_1 = __importDefault(require("lru-cache"));
  9. const magic_string_1 = __importDefault(require("magic-string"));
  10. const es_module_lexer_1 = require("es-module-lexer");
  11. const resolver_1 = require("../resolver");
  12. const serverPluginHmr_1 = require("./serverPluginHmr");
  13. const serverPluginClient_1 = require("./serverPluginClient");
  14. const utils_1 = require("../utils");
  15. const chalk_1 = __importDefault(require("chalk"));
  16. const cssUtils_1 = require("../utils/cssUtils");
  17. const serverPluginEnv_1 = require("./serverPluginEnv");
  18. const fs_extra_1 = __importDefault(require("fs-extra"));
  19. const debug = require('debug')('vite:rewrite');
  20. const rewriteCache = new lru_cache_1.default({ max: 1024 });
  21. // Plugin for rewriting served js.
  22. // - Rewrites named module imports to `/@modules/:id` requests, e.g.
  23. // "vue" => "/@modules/vue"
  24. // - Rewrites files containing HMR code (reference to `import.meta.hot`) to
  25. // inject `import.meta.hot` and track HMR boundary accept whitelists.
  26. // - Also tracks importer/importee relationship graph during the rewrite.
  27. // The graph is used by the HMR plugin to perform analysis on file change.
  28. exports.moduleRewritePlugin = ({ root, app, watcher, resolver }) => {
  29. app.use(async (ctx, next) => {
  30. await next();
  31. if (ctx.status === 304) {
  32. return;
  33. }
  34. // we are doing the js rewrite after all other middlewares have finished;
  35. // this allows us to post-process javascript produced by user middlewares
  36. // regardless of the extension of the original files.
  37. const publicPath = ctx.path;
  38. if (ctx.body &&
  39. ctx.response.is('js') &&
  40. !cssUtils_1.isCSSRequest(ctx.path) &&
  41. !ctx.url.endsWith('.map') &&
  42. !resolver.isPublicRequest(ctx.path) &&
  43. // skip internal client
  44. publicPath !== serverPluginClient_1.clientPublicPath &&
  45. // need to rewrite for <script>\<template> part in vue files
  46. !((ctx.path.endsWith('.vue') || ctx.vue) && ctx.query.type === 'style')) {
  47. const content = await utils_1.readBody(ctx.body);
  48. const cacheKey = publicPath + content;
  49. const isHmrRequest = !!ctx.query.t;
  50. if (!isHmrRequest && rewriteCache.has(cacheKey)) {
  51. debug(`(cached) ${ctx.url}`);
  52. ctx.body = rewriteCache.get(cacheKey);
  53. }
  54. else {
  55. await es_module_lexer_1.init;
  56. // dynamic import may contain extension-less path,
  57. // (.e.g import(runtimePathString))
  58. // so we need to normalize importer to ensure it contains extension
  59. // before we perform hmr analysis.
  60. // on the other hand, static import is guaranteed to have extension
  61. // because they must all have gone through module rewrite.
  62. const importer = utils_1.removeUnRelatedHmrQuery(resolver.normalizePublicPath(ctx.url));
  63. ctx.body = rewriteImports(root, content, importer, resolver, ctx.query.t);
  64. if (!isHmrRequest) {
  65. rewriteCache.set(cacheKey, ctx.body);
  66. }
  67. }
  68. }
  69. else {
  70. debug(`(skipped) ${ctx.url}`);
  71. }
  72. });
  73. // bust module rewrite cache on file change
  74. watcher.on('change', async (filePath) => {
  75. const publicPath = resolver.fileToRequest(filePath);
  76. // #662 use fs.read instead of cacheRead, avoid cache hit when request file
  77. // and caused pass `notModified` into transform is always true
  78. const cacheKey = publicPath + (await fs_extra_1.default.readFile(filePath)).toString();
  79. debug(`${publicPath}: cache busted`);
  80. rewriteCache.del(cacheKey);
  81. });
  82. };
  83. function rewriteImports(root, source, importer, resolver, timestamp) {
  84. // #806 strip UTF-8 BOM
  85. if (source.charCodeAt(0) === 0xfeff) {
  86. source = source.slice(1);
  87. }
  88. try {
  89. let imports = [];
  90. try {
  91. imports = es_module_lexer_1.parse(source)[0];
  92. }
  93. catch (e) {
  94. console.error(chalk_1.default.yellow(`[vite] failed to parse ${chalk_1.default.cyan(importer)} for import rewrite.\nIf you are using ` +
  95. `JSX, make sure to named the file with the .jsx extension.`));
  96. }
  97. const hasHMR = source.includes('import.meta.hot');
  98. const hasEnv = source.includes('import.meta.env');
  99. if (imports.length || hasHMR || hasEnv) {
  100. debug(`${importer}: rewriting`);
  101. const s = new magic_string_1.default(source);
  102. let hasReplaced = false;
  103. const prevImportees = serverPluginHmr_1.importeeMap.get(importer);
  104. const currentImportees = new Set();
  105. serverPluginHmr_1.importeeMap.set(importer, currentImportees);
  106. for (let i = 0; i < imports.length; i++) {
  107. const { s: start, e: end, d: dynamicIndex } = imports[i];
  108. let id = source.substring(start, end);
  109. let hasLiteralDynamicId = false;
  110. if (dynamicIndex >= 0) {
  111. // #998 remove comment
  112. id = id.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '');
  113. const literalIdMatch = id.match(/^\s*(?:'([^']+)'|"([^"]+)")\s*$/);
  114. if (literalIdMatch) {
  115. hasLiteralDynamicId = true;
  116. id = literalIdMatch[1] || literalIdMatch[2];
  117. }
  118. }
  119. if (dynamicIndex === -1 || hasLiteralDynamicId) {
  120. // do not rewrite external imports
  121. if (utils_1.isExternalUrl(id)) {
  122. continue;
  123. }
  124. const resolved = exports.resolveImport(root, importer, id, resolver, timestamp);
  125. if (resolved !== id) {
  126. debug(` "${id}" --> "${resolved}"`);
  127. s.overwrite(start, end, hasLiteralDynamicId ? `'${resolved}'` : resolved);
  128. hasReplaced = true;
  129. }
  130. // save the import chain for hmr analysis
  131. const importee = utils_1.cleanUrl(resolved);
  132. if (importee !== importer &&
  133. // no need to track hmr client or module dependencies
  134. importee !== serverPluginClient_1.clientPublicPath) {
  135. currentImportees.add(importee);
  136. serverPluginHmr_1.debugHmr(` ${importer} imports ${importee}`);
  137. serverPluginHmr_1.ensureMapEntry(serverPluginHmr_1.importerMap, importee).add(importer);
  138. }
  139. }
  140. else if (id !== 'import.meta' &&
  141. !/\/\*\s*@vite-ignore\s*\*\//.test(id)) {
  142. console.warn(chalk_1.default.yellow(`[vite] ignored dynamic import(${id}) in ${importer}.`));
  143. }
  144. }
  145. if (hasHMR) {
  146. serverPluginHmr_1.debugHmr(`rewriting ${importer} for HMR.`);
  147. serverPluginHmr_1.rewriteFileWithHMR(root, source, importer, resolver, s);
  148. hasReplaced = true;
  149. }
  150. if (hasEnv) {
  151. debug(` injecting import.meta.env for ${importer}`);
  152. s.prepend(`import __VITE_ENV__ from "${serverPluginEnv_1.envPublicPath}"; ` +
  153. `import.meta.env = __VITE_ENV__; `);
  154. hasReplaced = true;
  155. }
  156. // since the importees may have changed due to edits,
  157. // check if we need to remove this importer from certain importees
  158. if (prevImportees) {
  159. prevImportees.forEach((importee) => {
  160. if (!currentImportees.has(importee)) {
  161. const importers = serverPluginHmr_1.importerMap.get(importee);
  162. if (importers) {
  163. importers.delete(importer);
  164. }
  165. }
  166. });
  167. }
  168. if (!hasReplaced) {
  169. debug(` nothing needs rewriting.`);
  170. }
  171. return hasReplaced ? s.toString() : source;
  172. }
  173. else {
  174. debug(`${importer}: no imports found.`);
  175. }
  176. return source;
  177. }
  178. catch (e) {
  179. console.error(`[vite] Error: module imports rewrite failed for ${importer}.\n`, e);
  180. debug(source);
  181. return source;
  182. }
  183. }
  184. exports.rewriteImports = rewriteImports;
  185. exports.resolveImport = (root, importer, id, resolver, timestamp) => {
  186. id = resolver.alias(id) || id;
  187. if (utils_1.bareImportRE.test(id)) {
  188. // directly resolve bare module names to its entry path so that relative
  189. // imports from it (including source map urls) can work correctly
  190. id = `/@modules/${resolver_1.resolveBareModuleRequest(root, id, importer, resolver)}`;
  191. }
  192. else {
  193. // 1. relative to absolute
  194. // ./foo -> /some/path/foo
  195. let { pathname, query } = resolver.resolveRelativeRequest(importer, id);
  196. // 2. resolve dir index and extensions.
  197. pathname = resolver.normalizePublicPath(pathname);
  198. // 3. mark non-src imports
  199. if (!query && path_1.default.extname(pathname) && !resolver_1.jsSrcRE.test(pathname)) {
  200. query += `?import`;
  201. }
  202. id = pathname + query;
  203. }
  204. // 4. force re-fetch dirty imports by appending timestamp
  205. if (timestamp) {
  206. const dirtyFiles = serverPluginHmr_1.hmrDirtyFilesMap.get(timestamp);
  207. const cleanId = utils_1.cleanUrl(id);
  208. // only rewrite if:
  209. if (dirtyFiles && dirtyFiles.has(cleanId)) {
  210. // 1. this is a marked dirty file (in the import chain of the changed file)
  211. id += `${id.includes(`?`) ? `&` : `?`}t=${timestamp}`;
  212. }
  213. else if (serverPluginHmr_1.latestVersionsMap.has(cleanId)) {
  214. // 2. this file was previously hot-updated and has an updated version
  215. id += `${id.includes(`?`) ? `&` : `?`}t=${serverPluginHmr_1.latestVersionsMap.get(cleanId)}`;
  216. }
  217. }
  218. return id;
  219. };
  220. //# sourceMappingURL=serverPluginModuleRewrite.js.map