Grunt errors in browser

I have been using Grunt for quite a while, but I had never found anything to handle compile errors, to show them in the web browser, for example.

After looking around, I found some dead posts on StackOverflow, or some closed issues on Github, but nothing that really helps.

So I decided to try to create a plugin to display compile errors in the browser.

Screencast

Grunt plugin

There were two main issues while implementing the plugin:

  • Extract the errors in a way that would work for (almost) everything
  • Displaying the error in the browser without stopping the workflow

Error handling

To extract the error message, as Grunt does not, as far as I know provides any easy way to do this, the simplest thing to do was to hook grunt.log methods to append the output to some buffer.

appendBuffer = (msg) -> buffer += msg
appendBufferLn = (msg) -> appendBuffer(msg + "\n")
grunt.util.hooker.hook grunt.log, 'write', appendBuffer
grunt.util.hooker.hook grunt.log, 'writeln', appendBufferLn
grunt.util.hooker.hook grunt.log, 'error', appendBufferLn

and to check if an error has occured or not, switch the force flag to true before starting the task, hook grunt.fail.warn to register the error, and restore back the flag.

grunt.util.hooker.hook grunt.fail, 'warn', ( (e) -> error = e )

with this, after running the task, just need to check if error is null or not, and if it is not, handle the error.

Browser display

Background

A designer of my team told me he would rather have the errors display in his browser, to avoid needing to have to check his terminal all day long for nothing. Of course, I wanted to avoid stopping the workflow, by forcing him to close a tab every time an error occured.

One solution would be to insert some script in all the pages, this is what is often done for LiveReload, and to use it to display errors on the page when needed, but I prefered having a different page for displaying the error rather than to use the page we are working on, so I decided to have a different page, and to close it using websockets.

Implementation

The problem while implementing this is that everything runs in a different process, and the process stops after the compilation, so no easy way to keep track of the opened error pages.

To take care of all this, I created a task to run a websocket server taking care of all this and to send the success or error to the server during the compilation process. The server then shows the error page if there is any error, or closes the error page otherwise.

showError = ->
  errorHtml = template({ errors: errors, port: options.port })
  fs.writeFileSync ERROR_FILE_PATH, errorHtml
  opn ERROR_FILE_PATH, { keepFocus: true }
closeWindow = ->
  client.send('close') for i, client of clients
handlers =
  error: (error) ->
    closeWindow()
    errors[error.task] = error
    showError()
  success: (task) ->
    closeWindow()
    delete errors[task]
    showError() unless _.isEmpty(errors)
wss.on 'connection', (ws) ->
  id = currentId
  clients[id] = ws
  currentId++
  ws.on 'message', (message) ->
    message = JSON.parse message
    handlers[message.code](message.data)
  ws.on 'close', ->
    delete clients[id]

The full source is available on Github.

comments powered by Disqus
© 2023 Daniel Perez   Creative Commons License