From 6d8d75bb66e8edea351511a1a01cca194c1fce12 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sat, 6 Aug 2022 00:54:08 +0800 Subject: [PATCH] fix(vite): improve hmr by using hash instead of timestamp, close #176 --- packages/vite/src/modes/global/dev.ts | 36 ++++++++++++++++----------- playground/src/auto-imports.d.ts | 4 +++ 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/packages/vite/src/modes/global/dev.ts b/packages/vite/src/modes/global/dev.ts index 33be9aca60..d8883f2035 100644 --- a/packages/vite/src/modes/global/dev.ts +++ b/packages/vite/src/modes/global/dev.ts @@ -1,10 +1,11 @@ import type { Plugin, Update, ViteDevServer, ResolvedConfig as ViteResolvedConfig } from 'vite' import type { UnocssPluginContext } from '@unocss/core' import { notNull } from '@unocss/core' -import { LAYER_MARK_ALL, getPath, resolveId, resolveLayer } from '../../integration' +import { LAYER_MARK_ALL, getHash, getPath, resolveId, resolveLayer } from '../../integration' const WARN_TIMEOUT = 20000 const WS_EVENT_PREFIX = 'unocss:hmr' +const HASH_LENGTH = 6 export function GlobalModeDevPlugin({ uno, tokens, affectedModules, onInvalidate, extract, filter }: UnocssPluginContext): Plugin[] { const servers: ViteDevServer[] = [] @@ -14,8 +15,7 @@ export function GlobalModeDevPlugin({ uno, tokens, affectedModules, onInvalidate const entries = new Set() let invalidateTimer: any - let lastUpdate = Date.now() - let lastServed = 0 + const lastServedHash = new Map() let resolved = false let resolvedWarnTimer: any @@ -37,11 +37,13 @@ export function GlobalModeDevPlugin({ uno, tokens, affectedModules, onInvalidate } } clearTimeout(invalidateTimer) - invalidateTimer = setTimeout(() => sendUpdate(ids), timer) + invalidateTimer = setTimeout(() => { + lastServedHash.clear() + sendUpdate(ids) + }, timer) } function sendUpdate(ids: Set) { - lastUpdate = Date.now() for (const server of servers) { server.ws.send({ type: 'update', @@ -53,7 +55,7 @@ export function GlobalModeDevPlugin({ uno, tokens, affectedModules, onInvalidate return { acceptedPath: mod.url, path: mod.url, - timestamp: lastUpdate, + timestamp: Date.now(), type: 'js-update', } }) @@ -80,7 +82,7 @@ export function GlobalModeDevPlugin({ uno, tokens, affectedModules, onInvalidate } onInvalidate(() => { - invalidate(0, new Set([...entries, ...affectedModules])) + invalidate(10, new Set([...entries, ...affectedModules])) }) return [ @@ -92,9 +94,9 @@ export function GlobalModeDevPlugin({ uno, tokens, affectedModules, onInvalidate async configureServer(_server) { servers.push(_server) - _server.ws.on(WS_EVENT_PREFIX, (servedTime: number) => { - if (servedTime < lastUpdate) - invalidate(0) + _server.ws.on(WS_EVENT_PREFIX, ([layer, hash]: string[]) => { + if (lastServedHash.get(layer) !== hash) + invalidate(10) }) }, buildStart() { @@ -128,11 +130,15 @@ export function GlobalModeDevPlugin({ uno, tokens, affectedModules, onInvalidate await Promise.all(tasks) const result = await uno.generate(tokens) - lastServed = Date.now() - return layer === LAYER_MARK_ALL + + const css = layer === LAYER_MARK_ALL ? result.getLayers(undefined, Array.from(entries) .map(i => resolveLayer(i)).filter((i): i is string => !!i)) : result.getLayer(layer) + const hash = getHash(css || '', HASH_LENGTH) + lastServedHash.set(layer, hash) + // add hash to the chunk of CSS that it will send back to client to check if there is new CSS generated + return `/*${hash}*/${css}` }, }, { @@ -143,11 +149,13 @@ export function GlobalModeDevPlugin({ uno, tokens, affectedModules, onInvalidate }, enforce: 'post', transform(code, id) { + const layer = resolveLayer(getPath(id)) + // inject css modules to send callback on css load - if (entries.has(getPath(id)) && code.includes('import.meta.hot')) { + if (layer && code.includes('import.meta.hot')) { return `${code} if (import.meta.hot) { - try { await import.meta.hot.send('${WS_EVENT_PREFIX}', ${lastServed}); } + try { await import.meta.hot.send('${WS_EVENT_PREFIX}', ['${layer}', __vite__css.slice(2,${2 + HASH_LENGTH})]); } catch (e) { console.warn('[unocss-hmr]', e) } if (!import.meta.url.includes('?')) await new Promise(resolve => setTimeout(resolve, 100)) diff --git a/playground/src/auto-imports.d.ts b/playground/src/auto-imports.d.ts index ac098283a6..5daf732b9c 100644 --- a/playground/src/auto-imports.d.ts +++ b/playground/src/auto-imports.d.ts @@ -199,9 +199,11 @@ declare global { const usePermission: typeof import('@vueuse/core')['usePermission'] const usePointer: typeof import('@vueuse/core')['usePointer'] const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe'] + const usePrecision: typeof import('@vueuse/math')['usePrecision'] const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme'] const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark'] const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages'] + const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion'] const useProjection: typeof import('@vueuse/math')['useProjection'] const useRafFn: typeof import('@vueuse/core')['useRafFn'] const useRefHistory: typeof import('@vueuse/core')['useRefHistory'] @@ -238,6 +240,8 @@ declare global { const useTimestamp: typeof import('@vueuse/core')['useTimestamp'] const useTitle: typeof import('@vueuse/core')['useTitle'] const useToFixed: typeof import('@vueuse/math')['useToFixed'] + const useToNumber: typeof import('@vueuse/core')['useToNumber'] + const useToString: typeof import('@vueuse/core')['useToString'] const useToggle: typeof import('@vueuse/core')['useToggle'] const useTransition: typeof import('@vueuse/core')['useTransition'] const useTrunc: typeof import('@vueuse/math')['useTrunc']