I got the static site generation working while working on the plane! It feels pretty hacker to be able to have a vision of what you want to work on and then have it come to life so quickly. The static site generation is especially exciting because I feel like I am that much closer to being able to host blogs for people with little to no cost to me. It would be great to be able to offer a super cheap hosting option for a blog.
- I was surprised that I could drag and drop an image into this editor and have it just work (blocknotejs is dope) 2) I am realizing that images will not work properly on the blog, I will need to make sure their routes get rewritten.
some jank code
When you are writing a static site generator, you will eventually need to make sure the generated site knows what domain/URL/base path it is hosted from. For example, this blog is hosted on https://breadchris.com
. Locally, my generated site is hosted from http://localhost:8080/data/sites/generated/breadchris.com/latest/
. For local development, I need to make sure all links have the prefix /data/sites/generated/breadchris.com/latest/
since that is where the content is hosted on my computer. To get this to work within godom
, I could have prop drilled the URL throughout the DOM components, but I thought the dev experience would have been annoying since most hrefs or script srcs would depend on this. Instead, I felt it was better to have all links reference the root of the site (ex. /some/path) and then when the page is being rendered, modify the path so that it references the correct base URL. I called these "DynamicAttrs", for a lack of a better term, and I really cannot figure out if this is stupid or not. It required no changes to the existing godom code I wrote for my blog's views, but it did require updating the html library.
An Href
attribute will build itself just before rendering:
func Href(s string) *Node {
return &Node{
transform: func(p *Node) {
p.DynamicAttrs["href"] = func(ctx context.Context) string {
if strings.HasPrefix(s, "/") {
if baseURL, ok := ctx.Value("baseURL").(string); ok {
return baseURL + s
} else {
return "baseURL not found in ctx"
}
}
return s
}
},
}
}
ctx := context.WithValue(context.Background(), "baseURL", "/data/sites/generated/breadchris.com/latest/")
// ...
attrs := map[string]string{}
if s.locator != "" {
attrs["data-godom"] = s.locator
}
for k, v := range s.DynamicAttrs {
attrs[k] = v(ctx)
}
for k, v := range s.Attrs {
attrs[k] = v
}
I totally almost just got sad, I hit back in my browser and thought I lost all of this writing...but past chris is a G, he totally has all of this state saved to local storage
async function saveToStorage(jsonBlocks: Block[]) {
// Save contents to local storage. You might want to debounce this or replace
// with a call to your API / database.
localStorage.setItem("editorContent", JSON.stringify(jsonBlocks));
}
async function loadFromStorage() {
// Gets the previously stored editor contents.
const storageString = localStorage.getItem("editorContent");
return storageString
? (JSON.parse(storageString) as PartialBlock[])
: undefined;
}
There was a distinct moment where I thought that adding the attr stuff in the way I did was stupid. And it might be! I had to remind myself that code isn't stupid if it gets the job done, and it did. I might have written some lame code, but at least I have a working feature now that brings me closer to providing someone value!