fuse-box
Version:
Fuse-Box a bundler that does it right
220 lines (203 loc) • 7.41 kB
text/typescript
/**
* @module listens to `source-changed` socket events and actions hot reload
*/
import { SocketClient } from '../fusebox-websocket';
const Client: typeof SocketClient = require('fusebox-websocket').SocketClient,
bundleErrors: { [bundleName: string]: string[] } = {},
outputElement: HTMLDivElement = document.createElement('div'),
styleElement = document.createElement('style'),
minimizeToggleId = 'fuse-box-toggle-minimized',
hideButtonId = 'fuse-box-hide',
expandedOutputClass = 'fuse-box-expanded-output',
localStoragePrefix = '__fuse-box_'
function storeSetting (key: string, value: boolean) {
localStorage[localStoragePrefix + key] = value
}
function getSetting (key: string) {
return localStorage[localStoragePrefix + key] === 'true' ? true : false
}
let outputInBody = false,
outputMinimized = getSetting(minimizeToggleId),
outputHidden = false
outputElement.id = 'fuse-box-output'
styleElement.innerHTML = `
#${outputElement.id}, #${outputElement.id} * {
box-sizing: border-box;
}
#${outputElement.id} {
z-index: 999999999999;
position: fixed;
top: 10px;
right: 10px;
width: 400px;
overflow: auto;
background: #fdf3f1;
border: 1px solid #eca494;
border-radius: 5px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
box-shadow: 0px 3px 6px 1px rgba(0,0,0,.15);
}
#${outputElement.id}.${expandedOutputClass} {
height: auto;
width: auto;
left: 10px;
max-height: calc(100vh - 50px);
}
#${outputElement.id} .fuse-box-errors {
display: none;
}
#${outputElement.id}.${expandedOutputClass} .fuse-box-errors {
display: block;
border-top: 1px solid #eca494;
padding: 0 10px;
}
#${outputElement.id} button {
border: 1px solid #eca494;
padding: 5px 10px;
border-radius: 4px;
margin-left: 5px;
background-color: white;
color: black;
box-shadow: 0px 2px 2px 0px rgba(0,0,0,.05);
}
#${outputElement.id} .fuse-box-header {
padding: 10px;
}
#${outputElement.id} .fuse-box-header h4 {
display: inline-block;
margin: 4px;
}`
styleElement.type = 'text/css'
document.getElementsByTagName('head')[0].appendChild(styleElement)
function displayBundleErrors() {
const errorMessages = Object.keys(bundleErrors).reduce((allMessages, bundleName) => {
const bundleMessages = bundleErrors[bundleName]
return allMessages.concat(bundleMessages.map(message => {
const messageOutput = message
.replace(/\n/g, '<br>')
.replace(/\t/g, ' &nbps;&npbs;&nbps;')
.replace(/ /g, ' ')
return `<pre>${messageOutput}</pre>`
}))
}, []),
errorOutput = errorMessages.join('')
if (errorOutput && !outputHidden) {
outputElement.innerHTML = `
<div class="fuse-box-header" style="">
<h4 style="">Fuse Box Bundle Errors (${errorMessages.length}):</h4>
<div style="float: right;">
<button id="${minimizeToggleId}">${outputMinimized ? 'Expand' : 'Minimize'}</button>
<button id="${hideButtonId}">Hide</button>
</div>
</div>
<div class="fuse-box-errors">
${errorOutput}
</div>
`
document.body.appendChild(outputElement)
outputElement.className = outputMinimized ? '' : expandedOutputClass
outputInBody = true
document.getElementById(minimizeToggleId).onclick = () => {
outputMinimized = !outputMinimized
storeSetting(minimizeToggleId, outputMinimized)
displayBundleErrors()
}
document.getElementById(hideButtonId).onclick = () => {
outputHidden = true
displayBundleErrors()
}
} else if (outputInBody) {
document.body.removeChild(outputElement)
outputInBody = false
}
}
export const connect = (port: string, uri: string, reloadFullPage : boolean) => {
if (FuseBox.isServer) {
return;
}
port = port || window.location.port;
let client = new Client({
port,
uri,
});
client.connect();
client.on('page-reload', (data) => {
return window.location.reload();
});
client.on('page-hmr', (data) => {
FuseBox.flush();
FuseBox.dynamic(data.path, data.content);
if (FuseBox.mainFile) {
try {
FuseBox.import(FuseBox.mainFile);
} catch (e) {
if (typeof e === 'string') {
if (/not found/.test(e)) {
return window.location.reload();
}
}
console.error(e);
}
}
});
client.on('source-changed', (data) => {
console.info(`%cupdate "${data.path}"`, 'color: #237abe');
if(reloadFullPage){
return window.location.reload();
}
/**
* If a plugin handles this request then we don't have to do anything
**/
for (var index = 0; index < FuseBox.plugins.length; index++) {
var plugin = FuseBox.plugins[index];
if (plugin.hmrUpdate && plugin.hmrUpdate(data)) {
return;
}
}
if (data.type === "hosted-css") {
var fileId = data.path.replace(/^\//, '').replace(/[\.\/]+/g, '-');
var existing = document.getElementById(fileId);
if (existing) {
existing.setAttribute("href", data.path + "?" + new Date().getTime());
} else {
var node = document.createElement('link');
node.id = fileId;
node.type = 'text/css';
node.rel = 'stylesheet';
node.href = data.path;
document.getElementsByTagName('head')[0].appendChild(node);
}
}
if (data.type === 'js' || data.type === "css") {
FuseBox.flush();
FuseBox.dynamic(data.path, data.content);
if (FuseBox.mainFile) {
try {
FuseBox.import(FuseBox.mainFile);
} catch (e) {
if (typeof e === 'string') {
if (/not found/.test(e)) {
return window.location.reload();
}
}
console.error(e);
}
}
}
});
client.on('error', (error) => {
console.log(error);
});
client.on('bundle-error', ({ bundleName, message }: { bundleName: string, message: string }) => {
console.error(`Bundle error in ${bundleName}: ${message}`)
const errorsForBundle = bundleErrors[bundleName] || []
errorsForBundle.push(message)
bundleErrors[bundleName] = errorsForBundle
displayBundleErrors()
})
client.on('update-bundle-errors', ({ bundleName, messages }: { bundleName: string, messages: string[] }) => {
messages.forEach(message => console.error(`Bundle error in ${bundleName}: ${message}`))
bundleErrors[bundleName] = messages
displayBundleErrors()
})
};