Compare commits

..

No commits in common. "main" and "v0.1.0" have entirely different histories.
main ... v0.1.0

12 changed files with 606 additions and 573 deletions

1012
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,42 +1,47 @@
[package]
description = "static site generator fit for a dog"
edition = "2024"
edition = "2021"
homepage = "https://webdog.zyl.gay"
license = "AGPL-3.0-or-later"
license = "AGPLv2"
name = "webdog"
readme = "README.md"
repository = "https://github.com/zyllian/webdog"
version = "0.1.3"
version = "0.1.0"
[dependencies]
clap = { version = "4", features = ["derive"] }
color-eyre = { version = "0.6", optional = true }
clap = {version = "4", features = ["derive"]}
color-eyre = {version = "0.6", optional = true}
extract-frontmatter = "4"
eyre = "0.6"
futures = { version = "0.3", optional = true }
grass = { version = "0.13", default-features = false }
hotwatch = { version = "0.5", optional = true }
html5ever = "0.31"
fs_extra = "1.2"
futures = {version = "0.3", optional = true}
grass = {version = "0.13", default-features = false}
hotwatch = {version = "0.5", optional = true}
html5ever = "0.29"
include_dir = "0.7"
kuchikiki = "0.8.8-speedreader"
itertools = "0.14"
kuchikiki = "0.8.6-speedreader"
lol_html = "2"
minifier = { version = "0.3", features = ["html"] }
percent-encoding = { version = "2", optional = true }
pulldown-cmark = { version = "0.13", default-features = false, features = [
minifier = {version = "0.3", features = ["html"]}
percent-encoding = {version = "2", optional = true}
pulldown-cmark = {version = "0.12", default-features = false, features = [
"simd",
"html",
] }
]}
rayon = "1"
rss = { version = "2", features = ["validation"] }
serde = { version = "1", features = ["derive"] }
serde_yaml_ng = "0.10"
rss = {version = "2", features = ["validation"]}
serde = {version = "1", features = ["derive"]}
serde_yml = "0.0.12"
syntect = "5"
tera = "1"
time = { version = "0.3", features = ["serde-human-readable"] }
tokio = { version = "1", features = ["rt-multi-thread"], optional = true }
url = { version = "2", features = ["serde"] }
time = {version = "0.3", features = ["serde-human-readable"]}
tokio = {version = "1", features = [
"macros",
"rt-multi-thread",
], optional = true}
url = {version = "2", features = ["serde"]}
walkdir = "2"
warp = { version = "0.3", optional = true }
warp = {version = "0.3", optional = true}
[features]
default = ["serve", "color-eyre"]

View file

@ -3,7 +3,7 @@
webdog, the static site generator fit for a dog :3
```sh
cargo install webdog --locked
cargo install webdog
```
after installing, you can create your first site:

View file

@ -37,10 +37,6 @@ list of sass/scss stylesheets in the site's `sass` directory to treat as root st
base url for the various cdn url transformation features of webdog.
## `webdog_path`
optional custom path for webdog's static resources.
## `code_theme`
the theme to use for code blocks. valid options: `base16-ocean.dark`, `base16-eighties.dark`, `base16-mocha.dark`, `base16-ocean.light`, `InspiredGitHub`, `Solarized (dark)`, and `Solarized (light)`

View file

@ -5,7 +5,7 @@ welcome to webdog, the static site generator fit for a dog :3
if you have [rust](https://rust-lang.org) installed, all you need to do to install webdog is run the following command:
```sh
cargo install webdog --locked
cargo install webdog --git https://github.com/zyllian/webdog
```
then you can make your first webdog site!

View file

@ -2,18 +2,18 @@
use std::{collections::HashMap, path::PathBuf};
use eyre::{Context, OptionExt, eyre};
use lol_html::{HtmlRewriter, Settings, element, html_content::ContentType};
use eyre::{eyre, Context, OptionExt};
use lol_html::{element, html_content::ContentType, HtmlRewriter, Settings};
use rayon::prelude::*;
use serde::Serialize;
use syntect::{highlighting::ThemeSet, parsing::SyntaxSet};
use tera::Tera;
use url::Url;
use crate::{PageMetadata, ROOT_PATH, SASS_PATH, Site, resource::ResourceBuilder, util};
use crate::{resource::ResourceBuilder, util, PageMetadata, Site, ROOT_PATH, SASS_PATH};
/// Default path for static webdog resources included with the site build.
const WEBDOG_DEFAULT_PATH: &str = "webdog";
/// Path for static webdog resources included with the site build.
const WEBDOG_PATH: &str = "webdog";
/// Struct containing data to be sent to templates when rendering them.
#[derive(Debug, Serialize)]
@ -25,7 +25,7 @@ struct TemplateData<'a, T> {
/// Custom template data.
pub data: T,
/// Userdata supplied from the page.
pub userdata: serde_yaml_ng::Value,
pub userdata: serde_yml::Value,
}
/// Struct used to build the site.
@ -96,13 +96,7 @@ impl SiteBuilder {
std::fs::create_dir(&self.build_path).wrap_err("Failed to create build directory")?;
}
let webdog_path = self.build_path.join(
self.site
.config
.webdog_path
.clone()
.unwrap_or_else(|| WEBDOG_DEFAULT_PATH.to_string()),
);
let webdog_path = self.build_path.join(WEBDOG_PATH);
std::fs::create_dir(&webdog_path)?;
std::fs::write(
webdog_path.join("webdog.js"),
@ -161,7 +155,6 @@ impl SiteBuilder {
}
/// Function to rewrite HTML wow.
#[allow(clippy::too_many_arguments)]
pub fn rewrite_html(
&self,
html: String,
@ -170,7 +163,6 @@ impl SiteBuilder {
scripts: &[String],
styles: &[String],
is_partial: bool,
webdog_path: &str,
) -> eyre::Result<String> {
use kuchikiki::traits::*;
@ -197,7 +189,7 @@ impl SiteBuilder {
let new_html = self.build_page_raw(
PageMetadata {
template: Some(template.to_string()),
userdata: serde_yaml_ng::to_value(attr_map)?,
userdata: serde_yml::to_value(attr_map)?,
is_partial: true,
..Default::default()
},
@ -259,9 +251,7 @@ impl SiteBuilder {
);
}
el.append(
&format!(
r#"<script type="text/javascript" src="/{webdog_path}/webdog.js" defer></script>"#
),
r#"<script type="text/javascript" src="/webdog/webdog.js" defer></script>"#,
ContentType::Html,
);
if self.serving {
@ -277,8 +267,7 @@ impl SiteBuilder {
#[allow(clippy::single_match)]
match command {
"cdn" => {
new_src =
self.site.config.cdn_url(&new_src)?.to_string();
new_src = self.site.config.cdn_url(&new_src)?.to_string();
}
_ => new_src = src,
}
@ -302,8 +291,7 @@ impl SiteBuilder {
)?;
}
"cdn" => {
new_href =
self.site.config.cdn_url(&new_href)?.to_string();
new_href = self.site.config.cdn_url(&new_href)?.to_string();
}
_ => {
new_href = href;
@ -387,12 +375,6 @@ impl SiteBuilder {
&page_metadata.scripts,
&page_metadata.styles,
page_metadata.is_partial,
&self
.site
.config
.webdog_path
.clone()
.unwrap_or_else(|| WEBDOG_DEFAULT_PATH.to_string()),
)?;
if let Some(data) = extra {

View file

@ -1,4 +1,5 @@
use lol_html::{RewriteStrSettings, element};
use itertools::Itertools;
use lol_html::{element, RewriteStrSettings};
use serde::{Deserialize, Serialize};
use crate::{builder::SiteBuilder, resource::ResourceTemplateData};
@ -29,7 +30,7 @@ impl Extra {
match self {
Self::Basic => {
let data: BasicData = serde_yaml_ng::from_value(data.inner.clone())?;
let data: BasicData = serde_yml::from_value(data.inner.clone())?;
let content = builder.tera.render(&data.template, &tera::Context::new())?;
append_to(&page, &content, "main.page")
}
@ -45,7 +46,7 @@ pub struct ExtraData {
pub name: String,
/// The inner data for the extra.
#[serde(flatten)]
pub inner: serde_yaml_ng::Value,
pub inner: serde_yml::Value,
}
/// Gets the extra for the given value.
@ -89,7 +90,7 @@ fn resource_list_outside(
resources: Vec<ResourceTemplateData<'r>>,
}
let data: ResourceListData = serde_yaml_ng::from_value(data.inner.clone())?;
let data: ResourceListData = serde_yml::from_value(data.inner.clone())?;
let res_builder = builder
.resource_builders
@ -110,12 +111,10 @@ fn resource_list_outside(
)
.map(|ts| (id, v, ts))
})
.map(|v| {
v.map(|(id, v, ts)| ResourceTemplateData {
resource: v,
id: id.clone(),
readable_timestamp: ts,
})
.map_ok(|(id, v, ts)| ResourceTemplateData {
resource: v,
id: id.clone(),
readable_timestamp: ts,
})
.collect::<eyre::Result<Vec<_>>>()?,
})?,

View file

@ -32,7 +32,7 @@ where
pub fn parse(input: String) -> eyre::Result<Self> {
if input.starts_with("---\n") {
if let Some((frontmatter, content)) = input[3..].split_once("\n---\n") {
let data = serde_yaml_ng::from_str(frontmatter)?;
let data = serde_yml::from_str(frontmatter)?;
return Ok(Self {
content: content.to_string(),
data,
@ -56,7 +56,7 @@ where
if let Some(data) = &self.data {
output.push_str("---\n");
output.push_str(&serde_yaml_ng::to_string(data)?);
output.push_str(&serde_yml::to_string(data)?);
output.push_str("---\n\n");
}

View file

@ -49,8 +49,6 @@ pub struct SiteConfig {
pub sass_styles: Vec<PathBuf>,
/// URL to the CDN used for the site's images.
pub cdn_url: Url,
/// The path to output webdog static resources to. Defaults to "webdog"
pub webdog_path: Option<String>,
/// The theme to use for the site's code blocks.
/// TODO: dark/light themes
/// TODO: export themes as CSS instead of styling HTML directly
@ -75,7 +73,6 @@ impl SiteConfig {
build: None,
sass_styles: vec!["index.scss".into()],
cdn_url,
webdog_path: None,
code_theme: "base16-ocean.dark".to_string(),
resources: Default::default(),
}
@ -103,9 +100,7 @@ impl SiteConfig {
if !config_path.exists() {
eyre::bail!("no site config found!");
}
Ok(serde_yaml_ng::from_str(&std::fs::read_to_string(
config_path,
)?)?)
Ok(serde_yml::from_str(&std::fs::read_to_string(config_path)?)?)
}
}
@ -134,7 +129,7 @@ pub struct PageMetadata {
pub extra: Option<ExtraData>,
/// Custom values passed to the base template.
#[serde(default)]
pub userdata: serde_yaml_ng::Value,
pub userdata: serde_yml::Value,
/// Whether this page being rendered is a partial. Set by the builder, not your page metadata.
#[serde(skip)]
pub is_partial: bool,

View file

@ -135,7 +135,7 @@ fn main() -> eyre::Result<()> {
let config = SiteConfig::new(base_url.clone(), cdn_url.unwrap_or(base_url), title);
std::fs::write(
cli.site.join(SiteConfig::FILENAME),
serde_yaml_ng::to_string(&config)?,
serde_yml::to_string(&config)?,
)?;
DEFAULT_PROJECT.extract(&cli.site)?;
std::fs::create_dir(cli.site.join(webdog::ROOT_PATH))?;
@ -227,7 +227,7 @@ fn main() -> eyre::Result<()> {
config.resources.insert(id.clone(), resource_config);
std::fs::write(config_path, serde_yaml_ng::to_string(&config)?)?;
std::fs::write(config_path, serde_yml::to_string(&config)?)?;
let resource_path = cli.site.join(webdog::RESOURCES_PATH).join(&id);
std::fs::create_dir_all(&resource_path)?;
@ -240,7 +240,7 @@ fn main() -> eyre::Result<()> {
tags: vec!["first".to_string()],
cdn_file: None,
desc: Some(format!("This is the first {name} :)")),
inner: serde_yaml_ng::Value::Null,
inner: serde_yml::Value::Null,
draft: true,
},
)?;
@ -256,11 +256,7 @@ fn main() -> eyre::Result<()> {
title,
template,
} => {
let page_path = cli
.site
.join(webdog::PAGES_PATH)
.join(&id)
.with_extension("md");
let page_path = cli.site.join(webdog::PAGES_PATH).join(&id).with_extension("md");
let dir = page_path.parent().expect("should never fail");
if page_path.exists() {
eprintln!("page already exists!");
@ -314,7 +310,7 @@ fn main() -> eyre::Result<()> {
tags,
cdn_file: None,
desc: description,
inner: serde_yaml_ng::Value::Null,
inner: serde_yml::Value::Null,
draft: !skip_draft,
},
)?;

View file

@ -4,16 +4,14 @@ use std::{
};
use eyre::Context;
use rss::{ChannelBuilder, ItemBuilder, validation::Validate};
use itertools::Itertools;
use rss::{validation::Validate, ChannelBuilder, ItemBuilder};
use serde::{Deserialize, Serialize};
use time::{OffsetDateTime, format_description::well_known::Rfc2822};
use time::{format_description::well_known::Rfc2822, OffsetDateTime};
use crate::{
PageMetadata,
builder::SiteBuilder,
frontmatter::FrontMatterRequired,
link_list::Link,
util::{self, format_timestamp},
builder::SiteBuilder, frontmatter::FrontMatterRequired, link_list::Link,
util::{self, format_timestamp}, PageMetadata,
};
/// Metadata for resources.
@ -32,7 +30,7 @@ pub struct ResourceMetadata {
pub desc: Option<String>,
/// Extra resource data not included.
#[serde(flatten)]
pub inner: serde_yaml_ng::Value,
pub inner: serde_yml::Value,
/// Whether the resource is a draft. Drafts can be committed without being published to the live site.
#[serde(default)]
pub draft: bool,
@ -318,7 +316,7 @@ impl ResourceBuilder {
let page_max = list.len() / items_per_page + (list.len() % items_per_page).min(1);
let mut previous = None;
let mut next;
for (page, iter) in list.chunks(items_per_page).enumerate() {
for (page, iter) in list.iter().chunks(items_per_page).into_iter().enumerate() {
next = (page + 1 != page_max).then_some(page + 2);
let out = builder.build_page_raw(
PageMetadata {
@ -328,7 +326,7 @@ impl ResourceBuilder {
},
"",
ResourceListTemplateData {
resources: iter.to_vec(),
resources: iter.copied().collect(),
tag,
rss_enabled: config.rss.is_some(),
page: page + 1,
@ -371,7 +369,7 @@ impl ResourceBuilder {
// Build list of tags
{
let mut links: Vec<_> = tags
let links = tags
.iter()
.map(|(tag, data)| {
let count = data.len();
@ -383,9 +381,9 @@ impl ResourceBuilder {
count,
)
})
.sorted_by(|(_, a), (_, b)| b.cmp(a))
.map(|(l, _)| l)
.collect();
links.sort_by(|(_, a), (_, b)| b.cmp(a));
let links = links.into_iter().map(|(l, _)| l).collect();
let out = crate::link_list::render_basic_link_list(
builder,
&self.config.tag_list_template,

View file

@ -95,7 +95,7 @@ fn create(
builder.build_all_resources()?;
}
} else if relative_path.display().to_string() == SiteConfig::FILENAME {
let new_config = serde_yaml_ng::from_str(&std::fs::read_to_string(path)?)?;
let new_config = serde_yml::from_str(&std::fs::read_to_string(path)?)?;
builder.site.config = new_config;
builder.reload()?;
builder.build_all()?;