This commit is contained in:
+145
@@ -0,0 +1,145 @@
|
||||
import { log, logCompilerWarnings } from './utils/log.js';
|
||||
import { toRollupError } from './utils/error.js';
|
||||
|
||||
/**
|
||||
* Vite-specific HMR handling
|
||||
*
|
||||
* @param {Function} compileSvelte
|
||||
* @param {import('vite').HmrContext} ctx
|
||||
* @param {import('./types/id.d.ts').SvelteRequest} svelteRequest
|
||||
* @param {import('./utils/vite-plugin-svelte-cache.js').VitePluginSvelteCache} cache
|
||||
* @param {import('./types/options.d.ts').ResolvedOptions} options
|
||||
* @returns {Promise<import('vite').ModuleNode[] | void>}
|
||||
*/
|
||||
export async function handleHotUpdate(compileSvelte, ctx, svelteRequest, cache, options) {
|
||||
if (!cache.has(svelteRequest)) {
|
||||
// file hasn't been requested yet (e.g. async component)
|
||||
log.debug(
|
||||
`handleHotUpdate called before initial transform for ${svelteRequest.id}`,
|
||||
undefined,
|
||||
'hmr'
|
||||
);
|
||||
return;
|
||||
}
|
||||
const { read, server, modules } = ctx;
|
||||
|
||||
const cachedJS = cache.getJS(svelteRequest);
|
||||
const cachedCss = cache.getCSS(svelteRequest);
|
||||
|
||||
const content = await read();
|
||||
/** @type {import('./types/compile.d.ts').CompileData} */
|
||||
let compileData;
|
||||
try {
|
||||
compileData = await compileSvelte(svelteRequest, content, options);
|
||||
cache.update(compileData);
|
||||
} catch (e) {
|
||||
cache.setError(svelteRequest, e);
|
||||
throw toRollupError(e, options);
|
||||
}
|
||||
|
||||
const affectedModules = [...modules];
|
||||
|
||||
const cssIdx = modules.findIndex((m) => m.id === svelteRequest.cssId);
|
||||
if (cssIdx > -1) {
|
||||
const cssUpdated = cssChanged(cachedCss, compileData.compiled.css);
|
||||
if (!cssUpdated) {
|
||||
log.debug(`skipping unchanged css for ${svelteRequest.cssId}`, undefined, 'hmr');
|
||||
affectedModules.splice(cssIdx, 1);
|
||||
}
|
||||
}
|
||||
const jsIdx = modules.findIndex((m) => m.id === svelteRequest.id);
|
||||
if (jsIdx > -1) {
|
||||
const jsUpdated = jsChanged(cachedJS, compileData.compiled.js, svelteRequest.filename);
|
||||
if (!jsUpdated) {
|
||||
log.debug(`skipping unchanged js for ${svelteRequest.id}`, undefined, 'hmr');
|
||||
affectedModules.splice(jsIdx, 1);
|
||||
// transform won't be called, log warnings here
|
||||
logCompilerWarnings(svelteRequest, compileData.compiled.warnings, options);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO is this enough? see also: https://github.com/vitejs/vite/issues/2274
|
||||
const ssrModulesToInvalidate = affectedModules.filter((m) => !!m.ssrTransformResult);
|
||||
if (ssrModulesToInvalidate.length > 0) {
|
||||
log.debug(
|
||||
`invalidating modules ${ssrModulesToInvalidate.map((m) => m.id).join(', ')}`,
|
||||
undefined,
|
||||
'hmr'
|
||||
);
|
||||
ssrModulesToInvalidate.forEach((moduleNode) => server.moduleGraph.invalidateModule(moduleNode));
|
||||
}
|
||||
if (affectedModules.length > 0) {
|
||||
log.debug(
|
||||
`handleHotUpdate for ${svelteRequest.id} result: ${affectedModules
|
||||
.map((m) => m.id)
|
||||
.join(', ')}`,
|
||||
undefined,
|
||||
'hmr'
|
||||
);
|
||||
}
|
||||
return affectedModules;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('./types/compile.d.ts').Code | null} [prev]
|
||||
* @param {import('./types/compile.d.ts').Code | null} [next]
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function cssChanged(prev, next) {
|
||||
return !isCodeEqual(prev?.code, next?.code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('./types/compile.d.ts').Code | null} [prev]
|
||||
* @param {import('./types/compile.d.ts').Code | null} [next]
|
||||
* @param {string} [filename]
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function jsChanged(prev, next, filename) {
|
||||
const prevJs = prev?.code;
|
||||
const nextJs = next?.code;
|
||||
const isStrictEqual = isCodeEqual(prevJs, nextJs);
|
||||
if (isStrictEqual) {
|
||||
return false;
|
||||
}
|
||||
const isLooseEqual = isCodeEqual(normalizeJsCode(prevJs), normalizeJsCode(nextJs));
|
||||
if (!isStrictEqual && isLooseEqual) {
|
||||
log.debug(
|
||||
`ignoring compiler output js change for ${filename} as it is equal to previous output after normalization`,
|
||||
undefined,
|
||||
'hmr'
|
||||
);
|
||||
}
|
||||
return !isLooseEqual;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [prev]
|
||||
* @param {string} [next]
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isCodeEqual(prev, next) {
|
||||
if (!prev && !next) {
|
||||
return true;
|
||||
}
|
||||
if ((!prev && next) || (prev && !next)) {
|
||||
return false;
|
||||
}
|
||||
return prev === next;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove code that only changes metadata and does not require a js update for the component to keep working
|
||||
*
|
||||
* 1) add_location() calls. These add location metadata to elements, only used by some dev tools
|
||||
* 2) ... maybe more (or less) in the future
|
||||
*
|
||||
* @param {string} [code]
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
function normalizeJsCode(code) {
|
||||
if (!code) {
|
||||
return code;
|
||||
}
|
||||
return code.replace(/\s*\badd_location\s*\([^)]*\)\s*;?/g, '');
|
||||
}
|
||||
Reference in New Issue
Block a user