1
0
mirror of https://github.com/danog/gojekyll.git synced 2025-01-22 20:11:18 +01:00

Theme in-browser error reports

This commit is contained in:
Oliver Steele 2017-07-17 11:48:30 -04:00
parent d88b2bda2f
commit 0fc1fc6023
2 changed files with 74 additions and 6 deletions

View File

@ -90,7 +90,6 @@ Missing features:
- Liquid filter `sassify`
- Markdown features: [attribute lists](https://kramdown.gettalong.org/syntax.html#attribute-list-definitions), [`markdown=1`](https://kramdown.gettalong.org/syntax.html#html-blocks).
- `site.data` is not sorted.
- Undefined Liquid tags and filters are errors, not warnings.
- Windows compatibility
Also see the [detailed status](#feature-status) below.
@ -102,12 +101,12 @@ These will probably not change:
By design:
- Plugins must be listed in the config file, not a Gemfile.
- Liquid is run in strict mode.
- Liquid is run in strict mode; undefined filters are errors.a
- The wrong type in a `_config.yml` is an error.
- Server live reload is always on.
- `serve --watch` (the default) reloads the `_config.yml` and data files too.
- `serve` generates pages on the fly; it doesn't write to the file system.
- Files are cached to `/tmp/gojekyll-${USER}`, not `./.sass-cache`
- Files are cached in `/tmp/gojekyll-${USER}`, not `./.sass-cache`
- Jekyll provides an (undocumented) `jekyll.version` variable to templates. Copying this didn't seem right.
Muzukashii:
@ -144,7 +143,7 @@ Muzukashii:
- [x] `build`
- [x] `--source`, `--destination`, `--drafts`, `--future`, `--unpublished`, `--watch`
- [ ] `--baseurl`, `--config`, `--incremental`, `--lsi`
- [ ] `--force-polling`, `--limit-posts`, `JEKYLL_ENV=production` not planned
- [ ] `--force_polling`, `--limit-posts`, `JEKYLL_ENV=production` not planned
- [x] `clean`
- [x] `help`
- [x] `serve`
@ -187,6 +186,8 @@ They are used under the terms of the MIT License.
| [`jekyll-sitemap` plugin](https://github.com/jekyll/jekyll-redirect-from) | plugin emulation | sitemap template |
| [`jekyll-seo-tag` plugin](https://github.com/jekyll/jekyll-redirect-from) | plugin emulation | feed template |
The theme for in-browser error reporting was adapted from facebookincubator/create-react-app.
The gopher image in the `testdata` directory is from [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Gophercolor.jpg). It is used under the [Creative Commons Attribution-Share Alike 3.0 Unported license](https://creativecommons.org/licenses/by-sa/3.0/deed.en).
In addition to being totally and obviously inspired by Jekyll and its plugins, Jekyll's solid *documentation* was indispensible --- especially since I wanted to implement Jekyll as documented, not port its source code. The [Jekyll docs](https://jekyllrb.com/docs/home/) were always open in at least one tab during development.

View File

@ -1,15 +1,20 @@
package server
import (
"bytes"
"fmt"
"html"
"io"
"io/ioutil"
"mime"
"net/http"
"os"
"strings"
"sync"
"github.com/jaschaephraim/lrserver"
"github.com/osteele/gojekyll/site"
"github.com/osteele/liquid"
"github.com/pkg/browser"
)
@ -75,7 +80,69 @@ func (s *Server) handler(rw http.ResponseWriter, r *http.Request) {
}
err := site.WriteDocument(w, p)
if err != nil {
fmt.Fprintf(w, "<div>%s</div>", err)
fmt.Printf("Error rendering %s: %s\n", urlpath, err)
fmt.Fprintf(os.Stderr, "Error rendering %s: %s\n", urlpath, err)
eng := liquid.NewEngine()
excerpt, path := fileErrorContext(err)
out, e := eng.ParseAndRenderString(renderErrorTemplate, liquid.Bindings{
"error": err,
"excerpt": excerpt,
"path": path,
"watch": site.Config().Watch,
})
if e != nil {
panic(e)
}
if _, err := io.WriteString(w, out); err != nil {
fmt.Fprintf(os.Stderr, "Error writing HTTP response: %s", err)
}
}
}
func fileErrorContext(e error) (s, path string) {
cause, ok := e.(liquid.SourceError)
if !ok {
return
}
path, n := cause.Path(), cause.LineNumber()
b, err := ioutil.ReadFile(path)
if err != nil {
return
}
lines := strings.Split(strings.TrimRight(string(b), "\n"), "\n")
l0, l1 := n-4, n+4
w := new(bytes.Buffer)
for i := l0; i < l1; i++ {
if i < 0 || len(lines) <= i {
continue
}
var class string
if i+1 == n {
class = "error"
}
fmt.Fprintf(w, `<span class="line %s"><span class="gutter"></span><span class="lineno">%4d</span>%s<br /></span>`, class, i+1, html.EscapeString(lines[i]))
}
return w.String(), path
}
// CSS theme adapted from github.com/facebookincubator/create-react-app
const renderErrorTemplate = `<html><head>
<style type="text/css">
body { background-color: black; color: rgb(232, 232, 232); font-family: Menlo, Consolas, monospace; padding: 2rem; line-height: 1.2; }
h1 { color: #E36049 }
div { margin: 20px 0; }
code { font-size: xx-large; }
.line.error .gutter::before { content: "⚠️"; width: 0; float:left; }
.line.error, .line.error .lineno { color: red; }
.lineno { color: #6D7891; border-right: 1px solid #6D7891; padding-right: 10px; margin: 0 10px 0 55px; display: inline-block; text-align: right; }
footer { border-top: 1px solid #6D7891; margin-top: 5ex; padding-top: 5px; }
</style>
</head>
<body>
<h1>Failed to render.</h1>
<div>{{ error }}:</div>
<code>{{ excerpt }}</code>
{% if watch and path != "" %}
<footer>Edit and save {{ path }} to reload this page.</footer>
{% endif %}
</body>
</html>`