Skip to content

Commit 19c6101

Browse files
Switch from lustre SSG and Djot to simplifile and Markdown
1 parent b2f2d8d commit 19c6101

File tree

8 files changed

+193
-110
lines changed

8 files changed

+193
-110
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Gears' Website
22

33
Code for [my website](https://gearsco.de), written in [Gleam](https://gleam.run),
4-
using [Lustre](https://lustre.build) and [Lustre SSG](https://hexdocs.pm/lustre_ssg).
4+
using [Lustre](https://lustre.build) and [Mork](https://hexdocs.mp/mork).

blog/gleam-test-timeouts.djot renamed to blog/gleam-test-timeouts.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description = "By default, Gleam tests come with a timeout of 5 seconds. It's no
55
---
66

77
If you don't care about the backstory and just want to know the answer, you can
8-
[skip there](#How-do-I-configure-my-test-timeout).
8+
[skip there](#how-do-i-configure-my-test-timeout).
99

1010
## How Gleam tests work
1111

@@ -33,7 +33,7 @@ so if any test runs for longer than that it is immediately terminated.
3333

3434
EUnit has a somewhat unconventional API. As the default Gleam project explains,
3535
it runs all public functions whose names end in `_test`. But there's a second
36-
more obscure API, in the form of *test generators*.
36+
more obscure API, in the form of **test generators**.
3737

3838
Test generators are another kind of EUnit test. Test generator functions end
3939
in `_test_` (note the trailing underscore), and they have the ability to configure

gleam.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
name = "website"
22
version = "1.0.0"
3-
target = "javascript"
43

54
# Fill out these fields if you intend to generate HTML documentation or publish
65
# your project to the Hex package manager.
@@ -17,11 +16,13 @@ target = "javascript"
1716
gleam_stdlib = ">= 0.44.0 and < 2.0.0"
1817
lustre = ">= 5.0.2 and < 6.0.0"
1918
tom = ">= 1.1.0 and < 2.0.0"
20-
simplifile = ">= 2.2.0 and < 3.0.0"
21-
lustre_ssg = ">= 0.11.0 and < 1.0.0"
22-
jot = ">= 4.0.0 and < 5.0.0"
19+
simplifile = ">= 2.3.0 and < 3.0.0"
2320
pearl = ">= 2.2.0 and < 3.0.0"
2421
contour = ">= 1.2.1 and < 2.0.0"
22+
mork = ">= 1.8.0 and < 2.0.0"
23+
filepath = ">= 1.1.2 and < 2.0.0"
24+
frontmatter = ">= 1.0.1 and < 2.0.0"
25+
gleam_regexp = ">= 1.1.1 and < 2.0.0"
2526

2627
[dev-dependencies]
2728
gleeunit = ">= 1.0.0 and < 2.0.0"

manifest.toml

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22
# You typically do not need to edit this file
33

44
packages = [
5+
{ name = "casefold", version = "1.5.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "casefold", source = "hex", outer_checksum = "7DF0295BA71C525FADFBE6C3700C534EDE01ED512138F3EBF00AC158DE3AFF3C" },
56
{ name = "contour", version = "1.2.1", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_stdlib", "glexer", "houdini"], otp_app = "contour", source = "hex", outer_checksum = "C7895472BBAAC5D15DBDBAF6DD1847F6981F2F6132F726BAD30434A6E3BAAB95" },
6-
{ name = "envoy", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "95FD059345AA982E89A0B6E2A3BF1CF43E17A7048DCD85B5B65D3B9E4E39D359" },
7-
{ name = "exception", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "329D269D5C2A314F7364BD2711372B6F2C58FA6F39981572E5CA68624D291F8C" },
87
{ name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" },
8+
{ name = "frontmatter", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "frontmatter", source = "hex", outer_checksum = "D7FF83A8D22A52867809681EE254A3F989169F92539299958879FE47737DAB7A" },
99
{ name = "gleam_community_ansi", version = "1.4.3", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_regexp", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "8A62AE9CC6EA65BEA630D95016D6C07E4F9973565FA3D0DE68DC4200D8E0DD27" },
1010
{ name = "gleam_community_colour", version = "2.0.2", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "E34DD2C896AC3792151EDA939DA435FF3B69922F33415ED3C4406C932FBE9634" },
11-
{ name = "gleam_crypto", version = "1.5.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "50774BAFFF1144E7872814C566C5D653D83A3EBF23ACC3156B757A1B6819086E" },
1211
{ name = "gleam_erlang", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "1124AD3AA21143E5AF0FC5CF3D9529F6DB8CA03E43A55711B60B6B7B3874375C" },
1312
{ name = "gleam_json", version = "3.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "874FA3C3BB6E22DD2BB111966BD40B3759E9094E05257899A7C08F5DE77EC049" },
1413
{ name = "gleam_otp", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "BA6A294E295E428EC1562DC1C11EA7530DCB981E8359134BEABC8493B7B2258E" },
@@ -17,23 +16,23 @@ packages = [
1716
{ name = "gleeunit", version = "1.6.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "FDC68A8C492B1E9B429249062CD9BAC9B5538C6FBF584817205D0998C42E1DAC" },
1817
{ name = "glexer", version = "2.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "splitter"], otp_app = "glexer", source = "hex", outer_checksum = "40A1FB0919FA080AD6C5809B4C7DBA545841CAAC8168FACDFA0B0667C22475CC" },
1918
{ name = "houdini", version = "1.2.0", build_tools = ["gleam"], requirements = [], otp_app = "houdini", source = "hex", outer_checksum = "5DB1053F1AF828049C2B206D4403C18970ABEF5C18671CA3C2D2ED0DD64F6385" },
20-
{ name = "jot", version = "4.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "houdini", "splitter"], otp_app = "jot", source = "hex", outer_checksum = "E9E266D2768EA1238283D2CF125AA68095F17BAA4DDF3598360FD19F38593C59" },
2119
{ name = "lustre", version = "5.3.5", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib", "houdini"], otp_app = "lustre", source = "hex", outer_checksum = "5CBB5DD2849D8316A2101792FC35AEB58CE4B151451044A9C2A2A70A2F7FCEB8" },
22-
{ name = "lustre_ssg", version = "0.11.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_regexp", "gleam_stdlib", "jot", "lustre", "simplifile", "temporary", "tom"], otp_app = "lustre_ssg", source = "hex", outer_checksum = "D1F2B47EBE27C2B1DE6552A0883BC676D8EB076359D152FFA323FADDC74FFC41" },
20+
{ name = "mork", version = "1.8.0", build_tools = ["gleam"], requirements = ["casefold", "gleam_regexp", "gleam_stdlib", "splitter"], otp_app = "mork", source = "hex", outer_checksum = "6C48D71DC9DD20F23E63E9422B17F9D12689FB1D47A9DBEF97E982B638C8D1E9" },
2321
{ name = "pearl", version = "2.2.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_stdlib", "houdini", "splitter"], otp_app = "pearl", source = "hex", outer_checksum = "E5E14CE725542BA2516570D04140F0D1471761CCE27C6579C6396369FFF1DA9F" },
2422
{ name = "simplifile", version = "2.3.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0A868DAC6063D9E983477981839810DC2E553285AB4588B87E3E9C96A7FB4CB4" },
2523
{ name = "splitter", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "splitter", source = "hex", outer_checksum = "05564A381580395DCDEFF4F88A64B021E8DAFA6540AE99B4623962F52976AA9D" },
26-
{ name = "temporary", version = "1.0.0", build_tools = ["gleam"], requirements = ["envoy", "exception", "filepath", "gleam_crypto", "gleam_stdlib", "simplifile"], otp_app = "temporary", source = "hex", outer_checksum = "51C0FEF4D72CE7CA507BD188B21C1F00695B3D5B09D7DFE38240BFD3A8E1E9B3" },
2724
{ name = "tom", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "tom", source = "hex", outer_checksum = "0910EE688A713994515ACAF1F486A4F05752E585B9E3209D8F35A85B234C2719" },
2825
]
2926

3027
[requirements]
3128
contour = { version = ">= 1.2.1 and < 2.0.0" }
29+
filepath = { version = ">= 1.1.2 and < 2.0.0" }
30+
frontmatter = { version = ">= 1.0.1 and < 2.0.0" }
31+
gleam_regexp = { version = ">= 1.1.1 and < 2.0.0" }
3232
gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" }
3333
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
34-
jot = { version = ">= 4.0.0 and < 5.0.0" }
3534
lustre = { version = ">= 5.0.2 and < 6.0.0" }
36-
lustre_ssg = { version = ">= 0.11.0 and < 1.0.0" }
35+
mork = { version = ">= 1.8.0 and < 2.0.0" }
3736
pearl = { version = ">= 2.2.0 and < 3.0.0" }
38-
simplifile = { version = ">= 2.2.0 and < 3.0.0" }
37+
simplifile = { version = ">= 2.3.0 and < 3.0.0" }
3938
tom = { version = ">= 1.1.0 and < 2.0.0" }

src/build.gleam

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,38 @@
1+
import filepath as path
12
import gleam/io
23
import gleam/list
34
import lustre/element
4-
import lustre/ssg
5+
import simplifile as file
56
import website/data/blog
67
import website/page/blog_post
78
import website/page/index
89

10+
const out_dir = "./priv"
11+
912
pub fn main() {
1013
let posts = blog.posts()
1114

12-
let build =
13-
ssg.new("./priv")
14-
|> ssg.add_static_route("/", index.view(posts))
15-
|> ssg.add_static_dir("./static")
16-
|> add_dynamic_routes(
17-
"/blog",
18-
posts,
19-
fn(post) { post.slug },
20-
blog_post.view,
21-
)
22-
|> ssg.build
15+
let assert Ok(Nil) = file.delete(out_dir)
16+
let assert Ok(Nil) = file.copy_directory("./static", out_dir)
17+
create_page("/", index.view(posts))
18+
list.each(posts, fn(post) {
19+
create_page("blog/" <> post.slug, blog_post.view(post))
20+
})
2321

24-
case build {
25-
Ok(_) -> io.println("Build succeeded!")
26-
Error(e) -> {
27-
echo e
28-
io.println("Build failed!")
29-
}
22+
io.println("Build succeeded!")
23+
}
24+
25+
fn create_page(path: String, element: element.Element(a)) -> Nil {
26+
let path = case path {
27+
"/" <> path -> path
28+
_ -> path
3029
}
30+
let path = out_dir |> path.join(path) |> path.join("index.html")
31+
write_file(path, element.to_document_string(element))
3132
}
3233

33-
fn add_dynamic_routes(
34-
config: ssg.Config(ssg.HasStaticRoutes, a, b),
35-
path: String,
36-
list: List(data),
37-
slug: fn(data) -> String,
38-
view: fn(data) -> element.Element(msg),
39-
) -> ssg.Config(ssg.HasStaticRoutes, a, b) {
40-
list.fold(list, config, fn(config, data) {
41-
ssg.add_static_route(
42-
config,
43-
path <> "/" <> slug(data) <> "/index",
44-
view(data),
45-
)
46-
})
34+
fn write_file(to path: String, write contents: String) -> Nil {
35+
let assert Ok(Nil) = file.create_directory_all(path.directory_name(path))
36+
let assert Ok(Nil) = file.write(contents, to: path)
37+
Nil
4738
}

src/website/component.gleam

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,14 @@ pub fn text_page(header: String, content: List(Element(a))) -> Section(a) {
6767
])
6868
}
6969

70-
pub fn dangerous_html(html: String) -> Element(_) {
70+
pub fn raw_html(html: String) -> Element(_) {
7171
element.unsafe_raw_html("", "span", [], html)
7272
}
7373

7474
pub fn code_block(language: Option(String), code_text: String) -> Element(_) {
7575
let code = case language {
76-
Some("erlang") | Some("erl") ->
77-
dangerous_html(pearl.highlight_html(code_text))
78-
Some("gleam") -> dangerous_html(contour.to_html(code_text))
76+
Some("erlang") | Some("erl") -> raw_html(pearl.highlight_html(code_text))
77+
Some("gleam") -> raw_html(contour.to_html(code_text))
7978
_ -> html.text(code_text)
8079
}
8180

src/website/data/blog.gleam

Lines changed: 10 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,37 @@
1+
import frontmatter
12
import gleam/bool
23
import gleam/dict.{type Dict}
34
import gleam/list
5+
import gleam/option
46
import gleam/string
5-
import jot
6-
import lustre/attribute
77
import lustre/element
8-
import lustre/element/html
9-
import lustre/ssg/djot
108
import simplifile
119
import tom.{type Toml}
12-
import website/component
10+
import website/markdown
1311

1412
pub fn posts() -> List(Post(_)) {
1513
let assert Ok(files) = simplifile.read_directory("blog/")
1614

1715
list.filter_map(files, fn(file) {
18-
use <- bool.guard(!string.ends_with(file, ".djot"), Error(Nil))
16+
use <- bool.guard(!string.ends_with(file, ".md"), Error(Nil))
1917
let file_path = "blog/" <> file
2018
let assert Ok(contents) = simplifile.read(file_path)
2119

22-
let assert Ok(metadata) = djot.metadata(contents)
23-
let contents = djot.render(contents, renderer())
20+
let extracted = frontmatter.extract(contents)
21+
let assert Ok(metadata) =
22+
tom.parse(option.unwrap(extracted.frontmatter, ""))
23+
let contents = markdown.render(extracted.content)
2424

2525
let title = get_string_key(metadata, "title")
2626
let description = get_string_key(metadata, "description")
2727
let date = get_string_key(metadata, "date")
28-
// Remove .djot suffix
29-
let slug = string.drop_end(file, 5)
28+
// Remove .md suffix
29+
let slug = string.drop_end(file, 3)
3030

3131
Ok(Post(title:, slug:, date:, contents:, description:))
3232
})
3333
}
3434

35-
fn renderer() -> djot.Renderer(element.Element(_)) {
36-
let default = djot.default_renderer()
37-
djot.Renderer(
38-
..default,
39-
link: fn(destination, references, content) {
40-
case destination {
41-
jot.Reference(ref) ->
42-
case dict.get(references, ref) {
43-
Ok(url) ->
44-
html.a(
45-
[
46-
attribute.href(url),
47-
attribute.target(target(url)),
48-
attribute.class("underline"),
49-
],
50-
content,
51-
)
52-
Error(_) ->
53-
html.a(
54-
[
55-
attribute.href("#" <> ref),
56-
attribute.id("back-to-" <> ref),
57-
],
58-
content,
59-
)
60-
}
61-
jot.Url(url) ->
62-
html.a(
63-
[
64-
attribute.href(url),
65-
attribute.target(target(url)),
66-
attribute.class("underline"),
67-
],
68-
content,
69-
)
70-
}
71-
},
72-
codeblock: fn(_attrs, lang, code) { component.code_block(lang, code) },
73-
)
74-
}
75-
76-
fn target(url: String) -> String {
77-
case url {
78-
"https://" <> _ | "http://" <> _ -> "_blank"
79-
_ -> ""
80-
}
81-
}
82-
8335
fn get_string_key(toml: Dict(String, Toml), key: String) -> String {
8436
let assert Ok(value) = dict.get(toml, key)
8537
let assert tom.String(value) = value

0 commit comments

Comments
 (0)