You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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
}