UNPKG

@roots/bud-client

Version:

Client scripts for @roots/bud

132 lines (131 loc) 4.38 kB
/* eslint-disable no-console */ /* global __resourceQuery */ /* global __webpack_hash__ */ import * as components from './components/index.js'; import { injectEvents } from './events.js'; import { makeLogger } from './log.js'; import * as clientOptions from './options.js'; /** * Initializes bud.js HMR handling */ export const client = async (queryString, webpackHot) => { /* Guard: EventSource browser support */ if (typeof window?.EventSource === `undefined`) { console.error(`[bud] hot module reload requires EventSource to work. https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events#Tools`); return false; } /* Guard: webpackHot api availability */ if (!webpackHot) { console.error(`[bud] hot module reload requires the webpack hot api to be available`); return false; } /* Set client options from URL params */ const options = clientOptions.setFromParameters(queryString); /* Setup logger */ const logger = makeLogger(options); if (typeof window.bud === `undefined`) { window.bud = { controllers: [], current: {}, hmr: {}, listeners: {}, reload: () => { window.location.reload(); }, }; } if (!window.bud.current[options.name]) { window.bud.current[options.name] = null; } const isStale = (hash) => { if (hash) window.bud.current[options.name] = hash; return __webpack_hash__ === window.bud.current[options.name]; }; /** * Webpack HMR check handler */ const check = async () => { if (webpackHot.status() === `idle`) { await webpackHot.check(false); requestAnimationFrame(async function whenReady() { if (webpackHot.status() === `ready`) { await update(); } else { requestAnimationFrame(whenReady); } }); } }; /** * Webpack HMR unaccepted module handler */ const onUnacceptedOrDeclined = (info) => { console.warn(`[${options.name}] ${info.type}`, info); options.reload && window.location.reload(); }; const onAccepted = (info) => { window.bud.controllers.map(controller => controller?.update({ action: `sync`, errors: [] })); }; /** * Webpack HMR error handler */ const onErrored = (error) => { window.bud.controllers.map(controller => controller?.update({ errors: [error] })); }; /** * Webpack HMR update handler */ const update = async () => { try { await webpackHot.apply({ ignoreDeclined: true, ignoreErrored: true, ignoreUnaccepted: true, onAccepted, onDeclined: onUnacceptedOrDeclined, onErrored, onUnaccepted: onUnacceptedOrDeclined, }); if (!isStale()) await check(); } catch (error) { logger.error(error); } }; /* Instantiate indicator, overlay */ try { await components.make(options); } catch (error) { } /* Instantiate eventSource */ const events = injectEvents(EventSource).make(options); if (!window.bud.listeners?.[options.name]) { window.bud.listeners[options.name] = async (payload) => { if (!payload) return; if (options.reload && payload.action === `reload`) return window.bud.reload(); if (payload.name !== options.name) return; window.bud.controllers.map(controller => controller?.update(payload)); if (payload.errors?.length) return; if (payload.action === `built` || payload.action === `sync`) { if (isStale(payload.hash)) return; if (payload.action === `built`) { logger.log(`built in ${payload.time}ms`); } await check(); } }; /* * Instantiate HMR event source * and register client listeners */ events.addListener(window.bud.listeners[options.name]); } };