Skip to content

Commit

Permalink
Add Elm compiler error in the browser
Browse files Browse the repository at this point in the history
  • Loading branch information
lucamug committed May 29, 2019
1 parent eb47eaa commit 36215fe
Show file tree
Hide file tree
Showing 9 changed files with 875 additions and 324 deletions.
17 changes: 17 additions & 0 deletions .eslintrc.js
@@ -0,0 +1,17 @@
module.exports = {
'env': {
'browser': true,
'es6': true
},
'extends': 'standard',
'globals': {
'Atomics': 'readonly',
'SharedArrayBuffer': 'readonly'
},
'parserOptions': {
'ecmaVersion': 2018,
'sourceType': 'module'
},
'rules': {
}
}
17 changes: 17 additions & 0 deletions .jsbeautifyrc
@@ -0,0 +1,17 @@
{
"js": {
"indent_size": 2,
"indent_char": " ",
"other": " ",
"indent_level": 0,
"indent_with_tabs": false,
"preserve_newlines": true,
"max_preserve_newlines": 2,
"jslint_happy": true,
"indent_handlebars": true
},
"json": {
"indent_size": 2,
"indent_char": " "
}
}
40 changes: 33 additions & 7 deletions lib/src/elm-live.js
Expand Up @@ -35,11 +35,15 @@ module.exports = (argv, options) => {

const path = require('path')
const spawnSync = require('cross-spawn').sync
const elmServe = require('elm-serve')
const elmServe = require('./elm-serve')
const chokidar = require('chokidar')
const debounce = require('./debounce')
const getSourceDirs = require('./get-source-dirs')

var webServer
var elmStdout
var elmStderr

const auxiliaryBuild = execPath => {
const process = spawnSync(execPath, [], {
stdio: [inputStream, outputStream, outputStream]
Expand Down Expand Up @@ -92,10 +96,17 @@ ${chalk.dim('elm-live:')}
return beforeBuild
}
}
const elmMake = spawnSync(args.pathToElm, ['make', ...args.elmMakeArgs], {
stdio: [inputStream, outputStream, outputStream]
})

const elmMake = spawnSync(args.pathToElm, ['make', ...args.elmMakeArgs])
elmStdout = elmMake.stdout.toString();
elmStderr = elmMake.stderr.toString();

outputStream.write(
`
${chalk.dim('elm-make:')}
${elmStdout}
${chalk.cyan(elmStderr)}
`)
if (elmMake.error && elmMake.error.code === 'ENOENT') {
outputStream.write(
`
Expand Down Expand Up @@ -159,7 +170,7 @@ ${chalk.dim('elm-live:')}
`
)
elmServe({
webServer = elmServe({
watchDir: args.dir,
port: args.port,
host: args.host,
Expand All @@ -172,6 +183,8 @@ ${chalk.dim('elm-live:')}
ssl: args.ssl
})

// Initial reload (in case there are other tabs open in the browser)
webServer.sendMessage("reload");
serverStarted = true
}

Expand Down Expand Up @@ -206,7 +219,8 @@ ${chalk.dim('elm-live:')}
outputStream.write(
`
${chalk.dim('elm-live:')}
Watching ${sourceDirs.join(', ')}.
Watching
${sourceDirs.join('\n ')}.
`
)
Expand All @@ -222,16 +236,28 @@ ${chalk.dim('elm-live:')}
debounce((event, filePath) => {
const relativePath = path.relative(process.cwd(), filePath)
const eventName = eventNameMap[event] || event
const message = `You’ve ${eventName} \`${relativePath}\`. Rebuilding!`

outputStream.write(
`
${chalk.dim('elm-live:')}
You’ve ${eventName} \`${relativePath}\`. Rebuilding!
${message}
`
)

if (webServer && webServer.sendMessage) {
webServer.sendMessage(`compiling::${message}`);
}
const buildResult = build()
if (webServer && webServer.sendMessage) {
if (buildResult.exitCode === SUCCESS) {
webServer.sendMessage("reload");
} else {
webServer.sendMessage(`error::${elmStderr}`);
}
}

if (!serverStarted && buildResult.exitCode === SUCCESS) {
startServer()
}
Expand Down
198 changes: 198 additions & 0 deletions lib/src/elm-reload-client.js
@@ -0,0 +1,198 @@
(function refresh () {
var verboseLogging = false
var socketUrl = window.location.origin

socketUrl = socketUrl.replace() // This is dynamically populated by the reload.js file before it is sent to the browser
var socket

if (verboseLogging) {
console.log('Reload Script Loaded')
}

if (!('WebSocket' in window)) {
throw new Error('Reload only works with browsers that support WebSockets')
}

// Explanation of the flags below:

// The first change flag is used to tell reload to wait until the socket closes at least once before we allow the page to open on a socket open event. Otherwise reload will go into a inifite loop, as the page will have a socket on open event once it loads for the first time
var firstChangeFlag = false

// The navigatedAwayFromPageFlag is set to true in the event handler onbeforeunload because we want to short-circuit reload to prevent it from causing the page to reload before the navigation occurs.
var navigatedAwayFromPageFlag

// Wait until the page loads for the first time and then call the webSocketWaiter function so that we can connect the socket for the first time
window.addEventListener('load', function () {
if (verboseLogging === true) {
console.log('Page Loaded - Calling webSocketWaiter')
}
websocketWaiter()
})

// If the user navigates away from the page, we want to short-circuit reload to prevent it from causing the page to reload before the navigation occurs.
window.addEventListener('beforeunload', function () {
if (verboseLogging === true) {
console.log('Navigated away from the current URL')
}

navigatedAwayFromPageFlag = true
})

function showError(error) {
console.error(error)
if (hideCompiling()) {
setTimeout(function () {
showError_(error)
}, 400);
} else {
showError_(error)
}
}

function showError_(error) {
var nodeContainer = document.getElementById("elm-live:elmErrorContainer");
if (!nodeContainer) {
nodeContainer = document.createElement('div');
nodeContainer.id = "elm-live:elmErrorContainer"
document.body.appendChild(nodeContainer);
}
nodeContainer.innerHTML =
"<div id='elm-live:elmErrorBackground' style='z-index: 100; perspective: 500px; transition: opacity 0.3s; opacity: 0; position: fixed; top: 0; left: 0; background-color: rgba(0,0,0,0.3); width: 100%; height: 100%; display: flex; justify-content: center; align-items: center;'>" +
"<xmp id='elm-live:elmError' style='transform: rotateX(-90deg); transition: transform 0.3s; transform-style: preserve-3d; font-size: 16px; overflow: scroll; background-color: rgba(0,0,0,0.9); color: #ddd; width: 70%; height: 60%; padding: 30px'>" +
error +
"</xmp>" +
"</div>"
setTimeout(function () {
document.getElementById("elm-live:elmErrorBackground").style.opacity = 1
document.getElementById("elm-live:elmError").style.transform = "rotateX(0deg)"
}, 0);
}

function hideError() {
var node = document.getElementById("elm-live:elmErrorContainer");
if (node) {
document.getElementById("elm-live:elmErrorBackground").style.opacity = 0
document.getElementById("elm-live:elmError").style.transform = "rotateX(-90deg)"
setTimeout(function () {
document.getElementById("elm-live:elmErrorContainer").remove();
}, 300);
}
}

function showCompiling(message) {
hideError();
var nodeContainer = document.getElementById("elm-live:elmCompilingContainer");
if (!nodeContainer) {
nodeContainer = document.createElement('div');
nodeContainer.id = "elm-live:elmCompilingContainer"
document.body.appendChild(nodeContainer);
}
nodeContainer.innerHTML =
"<style>" +
"#loading {" +
" display: inline-block;" +
" width: 50px;" +
" height: 50px;" +
" border: 3px solid rgba(255,255,255,.3);" +
" border-radius: 50%;" +
" border-top-color: #fff;" +
" animation: spin 1s linear infinite;" +
"}" +
"@keyframes spin {" +
"to { transform: rotate(360deg); }" +
"}" +
"</style>" +
"<div id='elm-live:elmCompilingBackground' style='z-index: 100; transition: opacity 0.3s; opacity: 0; position: fixed; top: 0; left: 0; background-color: rgba(0,0,0,0.3); width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; flex-direction: column'>" +
"<div id='loading'>" +
"</div>" +
"<div style='color: #fff; padding: 30px; font-size: 24px; font-weight: bold; font-family: sans-serif'>" +
message +
"</div>"+
"</div>"
setTimeout(function () {
document.getElementById("elm-live:elmCompilingBackground").style.opacity = 1
}, 0);
}

function hideCompiling() {
var node = document.getElementById("elm-live:elmCompilingContainer");
if (node) {
document.getElementById("elm-live:elmCompilingBackground").style.opacity = 0
setTimeout(function () {
document.getElementById("elm-live:elmCompilingContainer").remove();
}, 300);
return (true);
} else {
return (false);
}
}

// Check to see if the server sent us reload (meaning a manually reload event was fired) and then reloads the page
var socketOnMessage = function (msg) {
if (msg.data.match(/^error::/)) {
// Displaying the Elm compiler error in the console
// and in the browsers
showError(msg.data.replace(/^error::/, ""));
}
else if (msg.data === 'reload') {
hideCompiling()
window.location.reload()
}
else if (msg.data.match(/^compiling::/)) {
showCompiling(msg.data.replace(/^compiling::/, ""))
}
}

var socketOnOpen = function (msg) {
if (verboseLogging) {
console.log('Socket Opened')
}

// We only allow the reload on two conditions, one when the socket closed (firstChange === true) and two if we didn't navigate to a new page (navigatedAwayFromPageFlag === false)
if (firstChangeFlag === true && navigatedAwayFromPageFlag !== true) {
if (verboseLogging) {
// console.log('Reloaded')
}

// Reset the firstChangeFlag to false so that when the socket on open events are being fired it won't keep reloading the page
firstChangeFlag = false

// Now that everything is set up properly we reload the page
// window.location.reload()
}
}

// Socket on close event that sets flags and calls the webSocketWaiter function
var socketOnClose = function (msg) {
if (verboseLogging) {
console.log('Socket Closed - Calling webSocketWaiter')
}

// We encountered a change so we set firstChangeFlag to true so that as soon as the server comes back up and the socket opens we can allow the reload
firstChangeFlag = true

// Call the webSocketWaiter function so that we can open a new socket and set the event handlers
websocketWaiter()
}

var socketOnError = function (msg) {
if (verboseLogging) {
console.log(msg)
}
}

// Function that opens a new socket and sets the event handlers for the socket
function websocketWaiter () {
if (verboseLogging) {
console.log('Waiting for socket')
}
setTimeout(function () {
socket = new WebSocket(socketUrl); // eslint-disable-line

socket.onopen = socketOnOpen
socket.onclose = socketOnClose
socket.onmessage = socketOnMessage
socket.onerror = socketOnError
}, 250)
}
})()

0 comments on commit 36215fe

Please sign in to comment.