Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 85 additions & 20 deletions build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import cats.effect.*
import cats.syntax.all.*
import com.monovore.decline.Opts
import com.monovore.decline.effect.CommandIOApp
import scala.collection.immutable.SortedMap

// Welcome to the typelevel.org build script!
// This script builds the site and can serve it locally for previewing.
Expand Down Expand Up @@ -52,13 +53,15 @@ object Main extends CommandIOApp("build", "builds the site") {
LaikaBuild.build(FilePath.fromFS2Path(destination)).as(ExitCode.Success)

case Subcommand.Serve(port) =>
val serverConfig = ServerConfig.defaults
.withPort(port)
.withBinaryRenderers(LaikaBuild.binaryRenderers)
val server = ServerBuilder[IO](LaikaBuild.parser, LaikaBuild.input)
.withConfig(serverConfig)
.build
server.evalTap(logServer(_)).useForever
LaikaBuild.inputWithTagPages.flatMap { input =>
val serverConfig = ServerConfig.defaults
.withPort(port)
.withBinaryRenderers(LaikaBuild.binaryRenderers)
val server = ServerBuilder[IO](LaikaBuild.parser, input)
.withConfig(serverConfig)
.build
server.evalTap(logServer(_)).useForever
}
}

def logServer(server: Server) =
Expand All @@ -82,7 +85,7 @@ object LaikaBuild {

val indexConfig = IndexConfig.default

def input = {
private def input = {
val securityPolicy = new URI(
"https://raw.githubusercontent.com/typelevel/.github/refs/heads/main/SECURITY.md"
).toURL()
Expand All @@ -102,6 +105,55 @@ object LaikaBuild {
.merge(Redirects.inputTree)
}

def inputWithTagPages: IO[InputTreeBuilder[IO]] = parser.use { parser =>
val sourceInput = input
parser.fromInput(sourceInput).parse.map { tree =>
val posts = tree.root.allDocuments.filter(
_.config.get[java.time.OffsetDateTime]("date").isRight
)
val tagCounts = posts
.flatMap(
_.config.get[Seq[String]]("tags").toOption.toSeq.flatten.distinct
)
.foldMap(str => SortedMap(str -> 1))

val tagProperties = tagCounts
.map { case (tag, count) =>
s"""{ slug: "$tag", title: "${tag.capitalize}", count: $count }"""
}
.mkString(",\n ")

val indexPage =
s"""{%
| laika.title: Blog Tags
| laika.html.template: /blog/tags/index.template.html
| laika.targetFormats: [html]
| blogTags: [
| $tagProperties
| ]
|%}
|""".stripMargin

tagCounts.keys.toSeq
.foldLeft(sourceInput) { (inputTree, tag) =>
val page =
s"""{%
| laika.html.template: /blog/tags/tag.template.html
| laika.targetFormats: [html]
| blogTag: "$tag"
|%}
|
|# ${tag.capitalize}
|""".stripMargin
inputTree.addString(page, Path.Root / "blog" / "tags" / s"$tag.md")
}
.addString(
indexPage,
Path.Root / "blog" / "tags" / "README.md"
)
}
}

def theme = {
val provider = new ThemeProvider {
def build[F[_]: Async] =
Expand Down Expand Up @@ -163,13 +215,15 @@ object LaikaBuild {
.build

(html, rss, index).tupled.use { (html, rss, index) =>
parser.fromInput(input).parse.flatMap { tree =>
html.from(tree).toDirectory(destination).render *>
rss.from(tree).toFile(destination / "blog" / "feed.rss").render *>
index
.from(tree)
.toFile(destination / "search" / "searchIndex.idx")
.render
inputWithTagPages.flatMap { input =>
parser.fromInput(input).parse.flatMap { tree =>
html.from(tree).toDirectory(destination).render *>
rss.from(tree).toFile(destination / "blog" / "feed.rss").render *>
index
.from(tree)
.toFile(destination / "search" / "searchIndex.idx")
.render
}
}
}
}
Expand Down Expand Up @@ -264,11 +318,22 @@ object LaikaCustomizations {
def contentScope(value: ConfigValue) =
TemplateScope(TemplateSpanSequence(b), value, s)

val posts = c.parent.allDocuments.flatMap { d =>
d.config.get[OffsetDateTime]("date").toList.tupleLeft(d)
}

posts
val tag = c.config.get[String]("blogTag").toOption

c.root.allDocuments
.filter(_.path.toString.startsWith("/blog/"))
.flatMap { d =>
val dated =
d.config.get[OffsetDateTime]("date").toList.tupleLeft(d)
tag.fold(dated) { value =>
dated.filter { case (post, _) =>
post.config
.get[Seq[String]]("tags")
.toOption
.exists(_.contains(value))
}
}
}
.sortBy(_._2)(using summon[Ordering[OffsetDateTime]].reverse)
.take(count.getOrElse(Int.MaxValue))
.traverse { (d, _) =>
Expand Down
18 changes: 10 additions & 8 deletions src/blog/blog.template.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,18 @@
<p class="bulma-subtitle bulma-has-text-light">
Follow our blog for announcements, events, and community-contributed posts.
</p>
<p>
<a class="bulma-is-underlined bulma-has-text-light" href="@:target(/blog/tags/README.md)">Browse all tags</a>
</p>
</div>
</section>

<div class="bulma-section bulma-container bulma-is-max-desktop bulma-columns bulma-is-multiline">
<!-- custom Laika template directive for listing blog posts -->
@:forBlogPosts
<div class="bulma-column bulma-is-6">
<div class="bulma-card">
<a href="@:target(_.cursor.currentDocument.sourcePath)">
<div class="bulma-card-content">
<div class="bulma-card-content">
<a href="@:target(_.cursor.currentDocument.sourcePath)">
<p class="bulma-is-6 bulma-has-text-right bulma-has-text-grey">@:date(_.date, "MMMM d, yyyy")</p>
<h4 class="bulma-title bulma-is-5">${_.cursor.currentDocument.title}</h4>
<p class="blog-post-byline bulma-subtitle bulma-is-6 bulma-has-text-grey">
Expand All @@ -33,11 +35,11 @@ <h4 class="bulma-title bulma-is-5">${_.cursor.currentDocument.title}</h4>
${_.event-date} at ${_.event-location}
@:@
</p>
@:for(_.tags)
<span class="bulma-tag">${_}</span>
@:@
</div>
</a>
</a>
@:for(_.tags)
<a class="bulma-tag" href="/blog/tags/${_}.html">${_}</a>
@:@
</div>
</div>
</div>
@:@
Expand Down
18 changes: 18 additions & 0 deletions src/blog/tags/index.template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@:embed(/templates/main.template.html)

<section class="bulma-hero bulma-is-primary bulma-is-bold">
<div class="bulma-hero-body bulma-container bulma-is-max-desktop">
<p class="bulma-title bulma-has-text-light">${cursor.currentDocument.title}</p>
<p class="bulma-subtitle bulma-has-text-light">Browse blog posts by tag.</p>
</div>
</section>

<div class="bulma-section bulma-container bulma-is-max-desktop bulma-content">
<div class="bulma-tags">
@:for(blogTags)
<a class="bulma-tag bulma-is-medium" href="/blog/tags/${_.slug}.html">${_.title} (${_.count})</a>
@:@
</div>
</div>

@:@
37 changes: 37 additions & 0 deletions src/blog/tags/tag.template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@:embed(/templates/main.template.html)

<section class="bulma-hero bulma-is-primary bulma-is-bold">
<div class="bulma-hero-body bulma-container bulma-is-max-desktop">
<p class="bulma-title bulma-has-text-light">${cursor.currentDocument.title}</p>
<p class="bulma-subtitle bulma-has-text-light">Blog posts tagged with <code>${blogTag}</code></p>
<p><a class="bulma-is-underlined bulma-has-text-light" href="@:target(/blog/tags/README.md)">Browse all tags</a></p>
</div>
</section>

<div class="bulma-section bulma-container bulma-is-max-desktop bulma-columns bulma-is-multiline">
@:forBlogPosts
<div class="bulma-column bulma-is-6">
<div class="bulma-card">
<div class="bulma-card-content">
<a href="@:target(_.cursor.currentDocument.sourcePath)">
<p class="bulma-is-6 bulma-has-text-right bulma-has-text-grey">@:date(_.date, "MMMM d, yyyy")</p>
<h4 class="bulma-title bulma-is-5">${_.cursor.currentDocument.title}</h4>
<p class="blog-post-byline bulma-subtitle bulma-is-6 bulma-has-text-grey">
@:for(_.author)
${_.name}<span class="delimiter">,</span><span class="last-delimiter"> &</span>
@:@
@:for(_.event-date)
${_.event-date} at ${_.event-location}
@:@
</p>
</a>
@:for(_.tags)
<a class="bulma-tag" href="/blog/tags/${_}.html">${_}</a>
@:@
</div>
</div>
</div>
@:@
</div>

@:@
Loading