client.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. // This file runs in the browser.
  2. window.process = window.process || {};
  3. window.process.env = window.process.env || {};
  4. window.process.env.NODE_ENV = __MODE__;
  5. const defines = __DEFINES__;
  6. Object.keys(defines).forEach((key) => {
  7. const segs = key.split('.');
  8. let target = window;
  9. for (let i = 0; i < segs.length; i++) {
  10. const seg = segs[i];
  11. if (i === segs.length - 1) {
  12. target[seg] = defines[key];
  13. }
  14. else {
  15. target = target[seg] || (target[seg] = {});
  16. }
  17. }
  18. });
  19. console.log('[vite] connecting...');
  20. // use server configuration, then fallback to inference
  21. const socketProtocol = __HMR_PROTOCOL__ || (location.protocol === 'https:' ? 'wss' : 'ws');
  22. const socketHost = `${__HMR_HOSTNAME__ || location.hostname}:${__HMR_PORT__}`;
  23. const socket = new WebSocket(`${socketProtocol}://${socketHost}`, 'vite-hmr');
  24. function warnFailedFetch(err, path) {
  25. if (!err.message.match('fetch')) {
  26. console.error(err);
  27. }
  28. console.error(`[hmr] Failed to reload ${path}. ` +
  29. `This could be due to syntax errors or importing non-existent ` +
  30. `modules. (see errors above)`);
  31. }
  32. // Listen for messages
  33. socket.addEventListener('message', async ({ data }) => {
  34. const payload = JSON.parse(data);
  35. if (payload.type === 'multi') {
  36. payload.updates.forEach(handleMessage);
  37. }
  38. else {
  39. handleMessage(payload);
  40. }
  41. });
  42. async function handleMessage(payload) {
  43. const { path, changeSrcPath, timestamp } = payload;
  44. switch (payload.type) {
  45. case 'connected':
  46. console.log(`[vite] connected.`);
  47. break;
  48. case 'vue-reload':
  49. queueUpdate(import(`${path}?t=${timestamp}`)
  50. .catch((err) => warnFailedFetch(err, path))
  51. .then((m) => () => {
  52. __VUE_HMR_RUNTIME__.reload(path, m.default);
  53. console.log(`[vite] ${path} reloaded.`);
  54. }));
  55. break;
  56. case 'vue-rerender':
  57. const templatePath = `${path}?type=template`;
  58. import(`${templatePath}&t=${timestamp}`).then((m) => {
  59. __VUE_HMR_RUNTIME__.rerender(path, m.render);
  60. console.log(`[vite] ${path} template updated.`);
  61. });
  62. break;
  63. case 'style-update':
  64. // check if this is referenced in html via <link>
  65. const el = document.querySelector(`link[href*='${path}']`);
  66. if (el) {
  67. el.setAttribute('href', `${path}${path.includes('?') ? '&' : '?'}t=${timestamp}`);
  68. break;
  69. }
  70. // imported CSS
  71. const importQuery = path.includes('?') ? '&import' : '?import';
  72. await import(`${path}${importQuery}&t=${timestamp}`);
  73. console.log(`[vite] ${path} updated.`);
  74. break;
  75. case 'style-remove':
  76. removeStyle(payload.id);
  77. break;
  78. case 'js-update':
  79. queueUpdate(updateModule(path, changeSrcPath, timestamp));
  80. break;
  81. case 'custom':
  82. const cbs = customUpdateMap.get(payload.id);
  83. if (cbs) {
  84. cbs.forEach((cb) => cb(payload.customData));
  85. }
  86. break;
  87. case 'full-reload':
  88. if (path.endsWith('.html')) {
  89. // if html file is edited, only reload the page if the browser is
  90. // currently on that page.
  91. const pagePath = location.pathname;
  92. if (pagePath === path ||
  93. (pagePath.endsWith('/') && pagePath + 'index.html' === path)) {
  94. location.reload();
  95. }
  96. return;
  97. }
  98. else {
  99. location.reload();
  100. }
  101. }
  102. }
  103. let pending = false;
  104. let queued = [];
  105. /**
  106. * buffer multiple hot updates triggered by the same src change
  107. * so that they are invoked in the same order they were sent.
  108. * (otherwise the order may be inconsistent because of the http request round trip)
  109. */
  110. async function queueUpdate(p) {
  111. queued.push(p);
  112. if (!pending) {
  113. pending = true;
  114. await Promise.resolve();
  115. pending = false;
  116. const loading = [...queued];
  117. queued = [];
  118. (await Promise.all(loading)).forEach((fn) => fn && fn());
  119. }
  120. }
  121. // ping server
  122. socket.addEventListener('close', () => {
  123. console.log(`[vite] server connection lost. polling for restart...`);
  124. setInterval(() => {
  125. fetch('/')
  126. .then(() => {
  127. location.reload();
  128. })
  129. .catch((e) => {
  130. /* ignore */
  131. });
  132. }, 1000);
  133. });
  134. // https://wicg.github.io/construct-stylesheets
  135. const supportsConstructedSheet = (() => {
  136. try {
  137. new CSSStyleSheet();
  138. return true;
  139. }
  140. catch (e) { }
  141. return false;
  142. })();
  143. const sheetsMap = new Map();
  144. export function updateStyle(id, content) {
  145. let style = sheetsMap.get(id);
  146. if (supportsConstructedSheet && !content.includes('@import')) {
  147. if (style && !(style instanceof CSSStyleSheet)) {
  148. removeStyle(id);
  149. style = undefined;
  150. }
  151. if (!style) {
  152. style = new CSSStyleSheet();
  153. style.replaceSync(content);
  154. // @ts-ignore
  155. document.adoptedStyleSheets = [...document.adoptedStyleSheets, style];
  156. }
  157. else {
  158. style.replaceSync(content);
  159. }
  160. }
  161. else {
  162. if (style && !(style instanceof HTMLStyleElement)) {
  163. removeStyle(id);
  164. style = undefined;
  165. }
  166. if (!style) {
  167. style = document.createElement('style');
  168. style.setAttribute('type', 'text/css');
  169. style.innerHTML = content;
  170. document.head.appendChild(style);
  171. }
  172. else {
  173. style.innerHTML = content;
  174. }
  175. }
  176. sheetsMap.set(id, style);
  177. }
  178. function removeStyle(id) {
  179. let style = sheetsMap.get(id);
  180. if (style) {
  181. if (style instanceof CSSStyleSheet) {
  182. // @ts-ignore
  183. const index = document.adoptedStyleSheets.indexOf(style);
  184. // @ts-ignore
  185. document.adoptedStyleSheets = document.adoptedStyleSheets.filter((s) => s !== style);
  186. }
  187. else {
  188. document.head.removeChild(style);
  189. }
  190. sheetsMap.delete(id);
  191. }
  192. }
  193. async function updateModule(id, changedPath, timestamp) {
  194. const mod = hotModulesMap.get(id);
  195. if (!mod) {
  196. // In a code-spliting project,
  197. // it is common that the hot-updating module is not loaded yet.
  198. // https://github.com/vitejs/vite/issues/721
  199. return;
  200. }
  201. const moduleMap = new Map();
  202. const isSelfUpdate = id === changedPath;
  203. // make sure we only import each dep once
  204. const modulesToUpdate = new Set();
  205. if (isSelfUpdate) {
  206. // self update - only update self
  207. modulesToUpdate.add(id);
  208. }
  209. else {
  210. // dep update
  211. for (const { deps } of mod.callbacks) {
  212. if (Array.isArray(deps)) {
  213. deps.forEach((dep) => modulesToUpdate.add(dep));
  214. }
  215. else {
  216. modulesToUpdate.add(deps);
  217. }
  218. }
  219. }
  220. // determine the qualified callbacks before we re-import the modules
  221. const callbacks = mod.callbacks.filter(({ deps }) => {
  222. return Array.isArray(deps)
  223. ? deps.some((dep) => modulesToUpdate.has(dep))
  224. : modulesToUpdate.has(deps);
  225. });
  226. await Promise.all(Array.from(modulesToUpdate).map(async (dep) => {
  227. const disposer = disposeMap.get(dep);
  228. if (disposer)
  229. await disposer(dataMap.get(dep));
  230. try {
  231. const newMod = await import(dep + (dep.includes('?') ? '&' : '?') + `t=${timestamp}`);
  232. moduleMap.set(dep, newMod);
  233. }
  234. catch (e) {
  235. warnFailedFetch(e, dep);
  236. }
  237. }));
  238. return () => {
  239. for (const { deps, fn } of callbacks) {
  240. if (Array.isArray(deps)) {
  241. fn(deps.map((dep) => moduleMap.get(dep)));
  242. }
  243. else {
  244. fn(moduleMap.get(deps));
  245. }
  246. }
  247. console.log(`[vite]: js module hot updated: `, id);
  248. };
  249. }
  250. const hotModulesMap = new Map();
  251. const disposeMap = new Map();
  252. const dataMap = new Map();
  253. const customUpdateMap = new Map();
  254. export const createHotContext = (id) => {
  255. if (!dataMap.has(id)) {
  256. dataMap.set(id, {});
  257. }
  258. // when a file is hot updated, a new context is created
  259. // clear its stale callbacks
  260. const mod = hotModulesMap.get(id);
  261. if (mod) {
  262. mod.callbacks = [];
  263. }
  264. const hot = {
  265. get data() {
  266. return dataMap.get(id);
  267. },
  268. accept(callback = () => { }) {
  269. hot.acceptDeps(id, callback);
  270. },
  271. acceptDeps(deps, callback = () => { }) {
  272. const mod = hotModulesMap.get(id) || {
  273. id,
  274. callbacks: []
  275. };
  276. mod.callbacks.push({
  277. deps: deps,
  278. fn: callback
  279. });
  280. hotModulesMap.set(id, mod);
  281. },
  282. dispose(cb) {
  283. disposeMap.set(id, cb);
  284. },
  285. // noop, used for static analysis only
  286. decline() { },
  287. invalidate() {
  288. location.reload();
  289. },
  290. // custom events
  291. on(event, cb) {
  292. const existing = customUpdateMap.get(event) || [];
  293. existing.push(cb);
  294. customUpdateMap.set(event, existing);
  295. }
  296. };
  297. return hot;
  298. };