173 lines
4.4 KiB
JavaScript
173 lines
4.4 KiB
JavaScript
/* eslint-env browser */
|
|
|
|
import { createProxy, hasFatalError } from './proxy.js'
|
|
|
|
const logPrefix = '[HMR:Svelte]'
|
|
|
|
// eslint-disable-next-line no-console
|
|
const log = (...args) => console.log(logPrefix, ...args)
|
|
|
|
const domReload = () => {
|
|
// eslint-disable-next-line no-undef
|
|
const win = typeof window !== 'undefined' && window
|
|
if (win && win.location && win.location.reload) {
|
|
log('Reload')
|
|
win.location.reload()
|
|
} else {
|
|
log('Full reload required')
|
|
}
|
|
}
|
|
|
|
const replaceCss = (previousId, newId) => {
|
|
if (typeof document === 'undefined') return false
|
|
if (!previousId) return false
|
|
if (!newId) return false
|
|
// svelte-xxx-style => svelte-xxx
|
|
const previousClass = previousId.slice(0, -6)
|
|
const newClass = newId.slice(0, -6)
|
|
// eslint-disable-next-line no-undef
|
|
document.querySelectorAll('.' + previousClass).forEach(el => {
|
|
el.classList.remove(previousClass)
|
|
el.classList.add(newClass)
|
|
})
|
|
return true
|
|
}
|
|
|
|
const removeStylesheet = cssId => {
|
|
if (cssId == null) return
|
|
if (typeof document === 'undefined') return
|
|
// eslint-disable-next-line no-undef
|
|
const el = document.getElementById(cssId)
|
|
if (el) el.remove()
|
|
return
|
|
}
|
|
|
|
const defaultArgs = {
|
|
reload: domReload,
|
|
}
|
|
|
|
export const makeApplyHmr = transformArgs => args => {
|
|
const allArgs = transformArgs({ ...defaultArgs, ...args })
|
|
return applyHmr(allArgs)
|
|
}
|
|
|
|
let needsReload = false
|
|
|
|
function applyHmr(args) {
|
|
const {
|
|
id,
|
|
cssId,
|
|
nonCssHash,
|
|
reload = domReload,
|
|
// normalized hot API (must conform to rollup-plugin-hot)
|
|
hot,
|
|
hotOptions,
|
|
Component,
|
|
acceptable, // some types of components are impossible to HMR correctly
|
|
preserveLocalState,
|
|
ProxyAdapter,
|
|
emitCss,
|
|
} = args
|
|
|
|
const existing = hot.data && hot.data.record
|
|
|
|
const canAccept = acceptable && (!existing || existing.current.canAccept)
|
|
|
|
const r =
|
|
existing ||
|
|
createProxy({
|
|
Adapter: ProxyAdapter,
|
|
id,
|
|
Component,
|
|
hotOptions,
|
|
canAccept,
|
|
preserveLocalState,
|
|
})
|
|
|
|
const cssOnly =
|
|
hotOptions.injectCss &&
|
|
existing &&
|
|
nonCssHash &&
|
|
existing.current.nonCssHash === nonCssHash
|
|
|
|
r.update({
|
|
Component,
|
|
hotOptions,
|
|
canAccept,
|
|
nonCssHash,
|
|
cssId,
|
|
previousCssId: r.current.cssId,
|
|
cssOnly,
|
|
preserveLocalState,
|
|
})
|
|
|
|
hot.dispose(data => {
|
|
// handle previous fatal errors
|
|
if (needsReload || hasFatalError()) {
|
|
if (hotOptions && hotOptions.noReload) {
|
|
log('Full reload required')
|
|
} else {
|
|
reload()
|
|
}
|
|
}
|
|
|
|
// 2020-09-21 Snowpack master doesn't pass data as arg to dispose handler
|
|
data = data || hot.data
|
|
|
|
data.record = r
|
|
|
|
if (!emitCss && cssId && r.current.cssId !== cssId) {
|
|
if (hotOptions.cssEjectDelay) {
|
|
setTimeout(() => removeStylesheet(cssId), hotOptions.cssEjectDelay)
|
|
} else {
|
|
removeStylesheet(cssId)
|
|
}
|
|
}
|
|
})
|
|
|
|
if (canAccept) {
|
|
hot.accept(async arg => {
|
|
const { bubbled } = arg || {}
|
|
|
|
// NOTE Snowpack registers accept handlers only once, so we can NOT rely
|
|
// on the surrounding scope variables -- they're not the last version!
|
|
const { cssId: newCssId, previousCssId } = r.current
|
|
const cssChanged = newCssId !== previousCssId
|
|
// ensure old style sheet has been removed by now
|
|
if (!emitCss && cssChanged) removeStylesheet(previousCssId)
|
|
// guard: css only change
|
|
if (
|
|
// NOTE bubbled is provided only by rollup-plugin-hot, and we
|
|
// can't safely assume a CSS only change without it... this means we
|
|
// can't support CSS only injection with Nollup or Webpack currently
|
|
bubbled === false && // WARNING check false, not falsy!
|
|
r.current.cssOnly &&
|
|
(!cssChanged || replaceCss(previousCssId, newCssId))
|
|
) {
|
|
return
|
|
}
|
|
|
|
const success = await r.reload()
|
|
|
|
if (hasFatalError() || (!success && !hotOptions.optimistic)) {
|
|
needsReload = true
|
|
}
|
|
})
|
|
}
|
|
|
|
// well, endgame... we won't be able to render next updates, even successful,
|
|
// if we don't have proxies in svelte's tree
|
|
//
|
|
// since we won't return the proxy and the app will expect a svelte component,
|
|
// it's gonna crash... so it's best to report the real cause
|
|
//
|
|
// full reload required
|
|
//
|
|
const proxyOk = r && r.proxy
|
|
if (!proxyOk) {
|
|
throw new Error(`Failed to create HMR proxy for Svelte component ${id}`)
|
|
}
|
|
|
|
return r.proxy
|
|
}
|