introduce webdog cli tool

This commit is contained in:
zyl 2024-11-08 18:16:23 -08:00
parent 8b7620b0bd
commit 399a0c1b87
Signed by: zyl
SSH key fingerprint: SHA256:uxxbSXbdroP/OnKBGnEDk5q7EKB2razvstC/KmzdXXs
26 changed files with 528 additions and 108 deletions

View file

@ -46,7 +46,7 @@ pub struct SiteBuilder {
/// The path to the build directory.
pub build_path: PathBuf,
/// Whether the site is going to be served locally with the dev server.
serving: bool,
pub serving: bool,
/// The resource builders available to the builder.
pub resource_builders: HashMap<String, ResourceBuilder>,
@ -105,7 +105,7 @@ impl SiteBuilder {
std::fs::create_dir(&webdog_path)?;
std::fs::write(
webdog_path.join("webdog.js"),
include_str!("./js/webdog.js"),
include_str!("./embedded/js/webdog.js"),
)?;
let root_path = self.site.site_path.join(ROOT_PATH);

1
src/embedded/default_site/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,7 @@
---
title: Not Found
---
# 404 Not Found
The page or resource requested does not exist. [Back to home](/)

View file

@ -0,0 +1 @@
hello from webdog! awruff!!

View file

@ -0,0 +1,4 @@
.page {
background-color: #bbb;
padding: 8px;
}

View file

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="referrer" content="no-referrer">
<link rel="stylesheet" href="/styles/index.css">
<title>{{ title }}</title>
{# header information from webdog #}
{{ head | safe }}
{# include scripts defined in the page frontmatter #}
{% for script in scripts %}
<script type="text/javascript" src="{{script}}" defer></script>
{% endfor %}
{# include styles defined in the page frontmatter #}
{% for style in styles %}
<link rel="stylesheet" href="/styles/{{style}}">
{% endfor %}
</head>
<body>
<h1><a href="/">webdog site</a></h1>
<main class="page">
{% block content %}{{ page | safe }}{% endblock content %}
</main>
</body>
</html>

View file

@ -0,0 +1,9 @@
{% extends "base.tera" %}
{% block content %}
<h1>{{ title }}</h1>
<ul>
{% for link in links %}
<li><a href="{{link.link}}">{{ link.title }}</a></li>
{% endfor %}
</ul>
{% endblock content %}

View file

@ -0,0 +1,23 @@
{% extends "base.tera" %}
{% block content %}
{% if tag %}
<h1>!!RESOURCE_NAME_PLURAL!! tagged {{ tag }}</h1>
<p><a href="/!!RESOURCE_TYPE!!/">View all !!RESOURCE_NAME_PLURAL_LOWERCASE!!</a></p>
{% else %}
<h1>!!RESOURCE_NAME_PLURAL!!</h1>
<p><a href="tags">view !!RESOURCE_NAME!! tags</a></p>
<p><a href="rss.xml">rss feed</a></p>
{% endif %}
<h1>Page {{ page }}/{{ page_max }}</h1>
{% if previous %}
<a href="./{{previous}}">previous page</a>
{% endif %}
{% if next %}
<a href="./{{next}}">next page</a>
{% endif %}
<div>
{% for resource in resources %}
<p><a href="/!!RESOURCE_TYPE!!/{{resource.id}}">{{ resource.title }}</a></p>
{% endfor %}
</div>
{% endblock content %}

View file

@ -0,0 +1,21 @@
{% extends "base.tera" %}
{% block content %}
<div>
<h1>{{ title }}</h1>
<span>published {{ timestamp }}</span>
{% if draft %}
<h2>DRAFT</h2>
{% endif %}
<p>{{ desc }}</p>
<div>
{{ content | safe }}
</div>
<hr />
<h3>tags</h3>
<div>
{% for tag in tags %}
<a href="/!!RESOURCE_TYPE!!/tag/{{tag}}">{{ tag }}</a>{% if not loop.last %},{% endif %}
{% endfor %}
</div>
</div>
{% endblock content %}

View file

@ -0,0 +1 @@
<div>{{ desc | safe }}</div>

View file

@ -104,7 +104,7 @@ fn resource_list_outside(
.map(|(id, v)| ResourceTemplateData {
resource: v,
id: id.clone(),
timestamp: v.timestamp,
timestamp: v.data().timestamp,
})
.collect(),
})?,

View file

@ -1,11 +1,14 @@
use serde::{de::DeserializeOwned, Serialize};
use std::ops::Deref;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
/// Very basic YAML front matter parser.
#[derive(Debug)]
#[derive(Debug, Serialize, Deserialize)]
pub struct FrontMatter<T> {
/// The content past the front matter.
pub content: String,
/// The front matter found, if any.
#[serde(flatten)]
pub data: Option<T>,
}
@ -54,3 +57,46 @@ where
Ok(output)
}
}
/// Wrapper around `FrontMatter` to only function when the data is present.
#[derive(Debug, Serialize, Deserialize)]
pub struct FrontMatterRequired<T>(FrontMatter<T>);
impl<T> FrontMatterRequired<T> {
/// Gets a reference to the front matter's data.
pub fn data(&self) -> &T {
self.0.data.as_ref().expect("missing front matter data")
}
/// Gets a mutable reference to the front matter's data.
pub fn data_mut(&mut self) -> &mut T {
self.0.data.as_mut().expect("missing front matter data")
}
/// Gets a mutable reference to the front matter's content.
pub fn content_mut(&mut self) -> &mut String {
&mut self.0.content
}
}
impl<T> FrontMatterRequired<T>
where
T: DeserializeOwned,
{
/// Parses the given input for front matter, failing if missing.
pub fn parse(input: String) -> eyre::Result<Self> {
let fm = FrontMatter::parse(input)?;
if fm.data.is_none() {
eyre::bail!("missing frontmatter!");
}
Ok(Self(fm))
}
}
impl<T> Deref for FrontMatterRequired<T> {
type Target = FrontMatter<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}

View file

@ -1,8 +1,8 @@
mod builder;
mod extras;
mod frontmatter;
pub mod frontmatter;
mod link_list;
mod resource;
pub mod resource;
#[cfg(feature = "serve")]
pub mod serving;
mod util;
@ -16,19 +16,25 @@ use extras::ExtraData;
use eyre::Context;
use rayon::prelude::*;
use resource::{EmbedMetadata, ResourceBuilderConfig};
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use url::Url;
use walkdir::WalkDir;
use builder::SiteBuilder;
const PAGES_PATH: &str = "pages";
const TEMPLATES_PATH: &str = "templates";
const SASS_PATH: &str = "sass";
const ROOT_PATH: &str = "root";
/// Source base path for normal site pages.
pub const PAGES_PATH: &str = "pages";
/// Source base path for site templates.
pub const TEMPLATES_PATH: &str = "templates";
/// Source base path for SASS stylesheets.
pub const SASS_PATH: &str = "sass";
/// Source base path for files which will be copied to the site root.
pub const ROOT_PATH: &str = "root";
/// Source base path for resources.
pub const RESOURCES_PATH: &str = "resources";
/// Struct for the site's configuration.
#[derive(Debug, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct SiteConfig {
/// The location the site is at.
pub base_url: Url,
@ -53,16 +59,18 @@ pub struct SiteConfig {
}
impl SiteConfig {
/// The filename for site config files.
pub const FILENAME: &str = "config.yaml";
/// Creates a new site config from the given title.
pub fn new(title: String) -> Self {
let url: Url = "/".parse().expect("should never fail");
pub fn new(base_url: Url, cdn_url: Url, title: String) -> Self {
Self {
base_url: url.clone(),
base_url,
title,
description: Default::default(),
build: None,
sass_styles: vec!["index.scss".into()],
cdn_url: url,
cdn_url,
code_theme: "base16-ocean.dark".to_string(),
resources: Default::default(),
}
@ -83,6 +91,15 @@ impl SiteConfig {
.ok_or_else(|| eyre::eyre!("missing code theme: {}", self.code_theme))?;
Ok(())
}
/// Helper to read the site config from the given path.
pub fn read(site_path: &Path) -> eyre::Result<Self> {
let config_path = site_path.join(SiteConfig::FILENAME);
if !config_path.exists() {
eyre::bail!("no site config found!");
}
Ok(serde_yml::from_str(&std::fs::read_to_string(config_path)?)?)
}
}
/// Struct for the front matter in templates. (nothing here yet)
@ -124,11 +141,7 @@ pub struct Site {
impl Site {
/// Creates a new site from the given path.
pub fn new(site_path: &Path) -> eyre::Result<Self> {
let config: SiteConfig = serde_yml::from_str(
&std::fs::read_to_string(site_path.join("config.yaml"))
.wrap_err("Failed to read site config")?,
)
.wrap_err("Failed to parse site config")?;
let config = SiteConfig::read(site_path)?;
let mut page_index = HashMap::new();
let pages_path = site_path.join(PAGES_PATH);

View file

@ -1,64 +1,281 @@
use std::path::Path;
use std::path::{Path, PathBuf};
use webdog::Site;
use clap::{Parser, Subcommand};
use include_dir::{include_dir, Dir};
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
use url::Url;
use webdog::{
frontmatter::FrontMatter,
resource::{ResourceBuilderConfig, ResourceMetadata},
Site, SiteConfig,
};
#[cfg(feature = "serve")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum Mode {
Build,
Serve,
Now,
/// The default project to use when creating a new one, embedded into the binary.
static DEFAULT_PROJECT: Dir = include_dir!("$CARGO_MANIFEST_DIR/src/embedded/default_site");
/// The default resource template, embedded into the binary.
static DEFAULT_RESOURCE_TEMPLATES: Dir =
include_dir!("$CARGO_MANIFEST_DIR/src/embedded/resource-template");
#[derive(Debug, Parser)]
#[command(version, about, long_about = None)]
struct Cli {
#[command(subcommand)]
command: Commands,
/// The path to the site.
#[arg(global = true, long, default_value = ".")]
site_path: PathBuf,
}
#[derive(Debug, Subcommand)]
enum Commands {
/// Create a new webdog site.
Create {
/// The site's base URL.
base_url: Url,
/// The site's title.
title: String,
/// The site's CDN URL. (defaults to the base URL)
#[arg(long)]
cdn_url: Option<Url>,
},
/// Builds the site.
Build {},
/// Serves the site for locally viewing edits made before publishing.
#[cfg(feature = "serve")]
Serve {
/// The IP address to bind to.
#[arg(long, default_value = "127.0.0.1")]
ip: String,
/// The port to bind to.
#[arg(short, long, default_value = "8080")]
port: u16,
},
/// Helper to get the current timestamp.
Now,
/// For dealing with site resources.
Resource {
#[clap(subcommand)]
command: ResourceCommands,
},
/// Creates a new resource of the given type.
New {
/// The type of resource to create.
resource_type: String,
/// The resource's ID.
id: String,
/// The resource's title.
title: String,
/// The resource's tags.
#[arg(short, long = "tag")]
tags: Vec<String>,
/// The resource's description.
#[arg(short, long)]
description: Option<String>,
/// Whether to skip setting the resource as a draft or not.
#[arg(long, default_value = "false")]
skip_draft: bool,
},
}
#[derive(Debug, Subcommand)]
enum ResourceCommands {
/// Creates a new resource type.
Create {
/// The resource type's ID.
id: String,
/// The name of the resource type to create.
name: String,
/// The name of the resource type, but plural.
plural: String,
},
}
#[cfg(feature = "serve")]
#[tokio::main]
async fn main() -> eyre::Result<()> {
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
#[cfg(feature = "color-eyre")]
color_eyre::install()?;
let site = Site::new(&Path::new("site").canonicalize()?)?;
let cli = Cli::parse();
let mut mode = Mode::Build;
for arg in std::env::args() {
if arg == "serve" {
mode = Mode::Serve;
break;
} else if arg == "now" {
mode = Mode::Now;
break;
}
}
let site = || -> eyre::Result<Site> { Site::new(&Path::new(&cli.site_path).canonicalize()?) };
match cli.command {
Commands::Create {
base_url,
cdn_url,
title,
} => {
if cli.site_path.exists() {
eprintln!("content exists in the given path! canceling!");
return Ok(());
}
std::fs::create_dir_all(&cli.site_path)?;
let config = SiteConfig::new(base_url.clone(), cdn_url.unwrap_or(base_url), title);
std::fs::write(
cli.site_path.join(SiteConfig::FILENAME),
serde_yml::to_string(&config)?,
)?;
DEFAULT_PROJECT.extract(&cli.site_path)?;
std::fs::create_dir(cli.site_path.join(webdog::ROOT_PATH))?;
match mode {
Mode::Build => {
build(site)?;
}
Mode::Serve => site.serve("127.0.0.1:8080").await?,
Mode::Now => {
let time = OffsetDateTime::now_utc();
println!(
"{}",
time.format(&Rfc3339)
.expect("failed to format the current time")
"Base site created at {:?}! Ready for editing, woof!",
cli.site_path
);
Ok(())
}
Commands::Build {} => {
println!("Building site...");
let now = std::time::Instant::now();
site()?.build_once()?;
println!("Build completed in {:?}", now.elapsed());
Ok(())
}
#[cfg(feature = "serve")]
Commands::Serve { ip, port } => {
let site = site()?;
site.serve(&format!("{}:{}", ip, port)).await
}
Commands::Now => {
let time = OffsetDateTime::now_utc();
println!("{}", time.format(&Rfc3339)?);
Ok(())
}
Commands::Resource { command } => match command {
ResourceCommands::Create { id, name, plural } => {
let config_path = cli.site_path.join(SiteConfig::FILENAME);
let mut config = SiteConfig::read(&cli.site_path)?;
if config.resources.contains_key(&id) {
eprintln!("resource type {id} already exists, canceling");
return Ok(());
}
let resource_template_path = cli.site_path.join(webdog::TEMPLATES_PATH).join(&id);
if resource_template_path.exists() {
eprintln!(
"path for resource already exists at {resource_template_path:?}, canceling"
);
return Ok(());
}
std::fs::create_dir_all(&resource_template_path)?;
for file in DEFAULT_RESOURCE_TEMPLATES.files() {
let resource_path = resource_template_path.join(file.path());
if let Some(contents) = file.contents_utf8() {
let mut contents = contents.to_owned();
contents = contents.replace("!!RESOURCE_TYPE!!", &id);
contents = contents.replace("!!RESOURCE_NAME!!", &name);
contents =
contents.replace("!!RESOURCE_NAME_LOWERCASE!!", &name.to_lowercase());
contents = contents.replace("!!RESOURCE_NAME_PLURAL!!", &plural);
contents = contents
.replace("!!RESOURCE_NAME_PLURAL_LOWERCASE!!", &plural.to_lowercase());
std::fs::write(resource_path, contents)?;
} else {
std::fs::write(resource_path, file.contents())?;
}
}
let resource_config = ResourceBuilderConfig {
source_path: id.clone(),
output_path_short: id.clone(),
output_path_long: id.clone(),
resource_template: format!("{id}/resource.tera"),
resource_list_template: format!("{id}/list.tera"),
tag_list_template: "basic-link-list.tera".to_string(),
rss_template: format!("{id}/rss.tera"),
rss_title: id.clone(),
rss_description: Default::default(),
list_title: name.clone(),
tag_list_title: format!("{name} tags"),
resource_name_plural: plural,
resources_per_page: 3,
};
config.resources.insert(id.clone(), resource_config);
std::fs::write(config_path, serde_yml::to_string(&config)?)?;
let resource_path = cli.site_path.join(webdog::RESOURCES_PATH).join(&id);
std::fs::create_dir_all(&resource_path)?;
create_resource(
&resource_path.join("first.md"),
&ResourceMetadata {
title: format!("First {name}"),
timestamp: OffsetDateTime::now_utc(),
tags: vec!["first".to_string()],
cdn_file: None,
desc: Some(format!("This is the first {name} :)")),
inner: serde_yml::Value::Null,
draft: true,
},
)?;
println!("Created the new resource type {id}! The first resource of this time is available at {:?}.", resource_path);
Ok(())
}
},
Commands::New {
resource_type,
id,
title,
tags,
description,
skip_draft,
} => {
let config = SiteConfig::read(&cli.site_path)?;
if let Some(resource) = config.resources.get(&resource_type) {
let resource_path = cli
.site_path
.join(webdog::RESOURCES_PATH)
.join(&resource.source_path)
.join(&id)
.with_extension("md");
if resource_path.exists() {
eprintln!(
"A {resource_type} resource of the ID {id} already exists, canceling!"
);
return Ok(());
}
create_resource(
&resource_path,
&ResourceMetadata {
title,
timestamp: OffsetDateTime::now_utc(),
tags,
cdn_file: None,
desc: description,
inner: serde_yml::Value::Null,
draft: !skip_draft,
},
)?;
println!(
"Created the new {resource_type} resource {id}! Available at {:?}",
resource_path
);
} else {
eprintln!("no resource of type {resource_type}, canceling");
}
Ok(())
}
}
}
Ok(())
}
#[cfg(not(feature = "serve"))]
fn main() -> eyre::Result<()> {
let site = Site::new(&Path::new("site").canonicalize()?)?;
build(site)
}
fn build(site: Site) -> eyre::Result<()> {
println!("Building site...");
let now = std::time::Instant::now();
site.build_once()?;
println!("Build completed in {:?}", now.elapsed());
/// Creates a new resource from the given metadata.
fn create_resource(resource_path: &Path, metadata: &ResourceMetadata) -> eyre::Result<()> {
std::fs::write(
resource_path,
FrontMatter {
content: "hello world :)".to_string(),
data: Some(metadata),
}
.format()?,
)?;
Ok(())
}

View file

@ -10,10 +10,9 @@ use rss::{validation::Validate, ChannelBuilder, ItemBuilder};
use serde::{Deserialize, Serialize, Serializer};
use time::{format_description::well_known::Rfc2822, OffsetDateTime};
use crate::{builder::SiteBuilder, frontmatter::FrontMatter, link_list::Link, PageMetadata};
/// Source base path for resources.
pub const RESOURCES_PATH: &str = "resources";
use crate::{
builder::SiteBuilder, frontmatter::FrontMatterRequired, link_list::Link, PageMetadata,
};
/// Metadata for resources.
#[derive(Debug, Deserialize, Serialize)]
@ -35,16 +34,13 @@ pub struct ResourceMetadata {
/// Whether the resource is a draft. Drafts can be committed without being published to the live site.
#[serde(default)]
pub draft: bool,
/// The resource's content. Defaults to nothing until loaded in another step.
#[serde(default)]
pub content: String,
}
#[derive(Debug, Serialize)]
pub struct ResourceTemplateData<'r> {
/// The resource's metadata.
#[serde(flatten)]
pub resource: &'r ResourceMetadata,
pub resource: &'r FrontMatterRequired<ResourceMetadata>,
/// The resource's ID.
pub id: String,
/// The resource's timestamp. Duplicated to change serialization method.
@ -129,7 +125,7 @@ struct ResourceListTemplateData<'r> {
}
/// Config for the resource builder.
#[derive(Debug, Clone, Default, Deserialize)]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ResourceBuilderConfig {
/// Path to where the resources should be loaded from.
pub source_path: String,
@ -165,7 +161,7 @@ pub struct ResourceBuilder {
/// The builder's config.
pub config: ResourceBuilderConfig,
/// The currently loaded resource metadata.
pub loaded_metadata: Vec<(String, ResourceMetadata)>,
pub loaded_metadata: Vec<(String, FrontMatterRequired<ResourceMetadata>)>,
}
impl ResourceBuilder {
@ -187,26 +183,27 @@ impl ResourceBuilder {
}
/// Loads resource metadata from the given path.
fn load(builder: &SiteBuilder, path: &Path) -> eyre::Result<(String, ResourceMetadata)> {
fn load(
builder: &SiteBuilder,
path: &Path,
) -> eyre::Result<(String, FrontMatterRequired<ResourceMetadata>)> {
let id = Self::get_id(path);
let input = std::fs::read_to_string(path)?;
let page = FrontMatter::<ResourceMetadata>::parse(input)
let mut page = FrontMatterRequired::<ResourceMetadata>::parse(input)
.wrap_err_with(|| eyre::eyre!("Failed to parse resource front matter"))?;
let parser = Parser::new_ext(&page.content, Options::all());
let mut html = String::new();
pulldown_cmark::html::push_html(&mut html, parser);
*page.content_mut() = html;
let mut data = page
.data
.ok_or_else(|| eyre::eyre!("missing front matter for file at {path:?}"))?;
data.content = html;
if let Some(cdn_file) = data.cdn_file {
data.cdn_file = Some(builder.site.config.cdn_url(&cdn_file)?.to_string());
let data = page.data_mut();
if let Some(cdn_file) = &data.cdn_file {
data.cdn_file = Some(builder.site.config.cdn_url(cdn_file)?.to_string());
}
Ok((id, data))
Ok((id, page))
}
/// Loads all resource metadata from the given config.
@ -216,20 +213,26 @@ impl ResourceBuilder {
for e in builder
.site
.site_path
.join(RESOURCES_PATH)
.join(crate::RESOURCES_PATH)
.join(&self.config.source_path)
.read_dir()?
{
let p = e?.path();
if let Some("md") = p.extension().and_then(|e| e.to_str()) {
let (id, metadata) = Self::load(builder, &p)?;
if cfg!(not(debug_assertions)) && metadata.draft {
if !builder.serving && metadata.data.as_ref().expect("should never fail").draft {
continue;
}
lmd.push((id, metadata));
}
}
lmd.sort_by(|a, b| b.1.timestamp.cmp(&a.1.timestamp));
lmd.sort_by(|a, b| {
b.1.data
.as_ref()
.expect("should never fail")
.timestamp
.cmp(&a.1.data.as_ref().expect("should never fail").timestamp)
});
Ok(())
}
@ -246,19 +249,20 @@ impl ResourceBuilder {
&self,
builder: &SiteBuilder,
id: String,
resource: &ResourceMetadata,
resource: &FrontMatterRequired<ResourceMetadata>,
) -> eyre::Result<()> {
let out_path = self.build_path(&builder.build_path, &id);
let data = resource.data();
let out = builder.build_page_raw(
PageMetadata {
template: Some(self.config.resource_template.clone()),
title: Some(resource.title.clone()),
title: Some(data.title.clone()),
embed: Some(EmbedMetadata {
title: resource.title.clone(),
title: data.title.clone(),
site_name: builder.site.config.title.clone(),
description: resource.desc.clone(),
image: if let Some(cdn_file) = &resource.cdn_file {
description: data.desc.clone(),
image: if let Some(cdn_file) = &data.cdn_file {
Some(builder.site.config.cdn_url(cdn_file)?.to_string())
} else {
None
@ -273,7 +277,7 @@ impl ResourceBuilder {
ResourceTemplateData {
resource,
id,
timestamp: resource.timestamp,
timestamp: resource.data.as_ref().expect("should never fail").timestamp,
},
)?;
std::fs::write(out_path, out)?;
@ -303,7 +307,7 @@ impl ResourceBuilder {
data.push(ResourceTemplateData {
resource,
id: id.clone(),
timestamp: resource.timestamp,
timestamp: resource.data().timestamp,
});
}
@ -368,7 +372,7 @@ impl ResourceBuilder {
// Build resource lists by tag
let mut tags: BTreeMap<String, Vec<&ResourceTemplateData>> = BTreeMap::new();
for resource in &data {
for tag in resource.resource.tags.iter().cloned() {
for tag in resource.resource.data().tags.iter().cloned() {
tags.entry(tag).or_default().push(resource);
}
}
@ -416,7 +420,7 @@ impl ResourceBuilder {
for resource in data {
items.push(
ItemBuilder::default()
.title(Some(resource.resource.title.to_owned()))
.title(Some(resource.resource.data().title.to_owned()))
.link(Some(
builder
.site
@ -428,7 +432,7 @@ impl ResourceBuilder {
))?
.to_string(),
))
.description(resource.resource.desc.clone())
.description(resource.resource.data().desc.clone())
.pub_date(Some(resource.timestamp.format(&Rfc2822)?))
.content(Some(builder.tera.render(
&self.config.rss_template,

View file

@ -19,7 +19,7 @@ use warp::{
};
use crate::{
resource::RESOURCES_PATH, Site, SiteBuilder, PAGES_PATH, ROOT_PATH, SASS_PATH, TEMPLATES_PATH,
Site, SiteBuilder, SiteConfig, PAGES_PATH, RESOURCES_PATH, ROOT_PATH, SASS_PATH, TEMPLATES_PATH,
};
/// Helper to get the "name" of a path.
@ -94,7 +94,7 @@ fn create(
builder.site.build_all_pages(builder)?;
builder.build_all_resources()?;
}
} else if relative_path.display().to_string() == "config.yaml" {
} else if relative_path.display().to_string() == SiteConfig::FILENAME {
let new_config = serde_yml::from_str(&std::fs::read_to_string(path)?)?;
builder.site.config = new_config;
builder.reload()?;
@ -277,8 +277,9 @@ impl Site {
.expect("Failed to decode URL");
if p == "_dev.js" {
let res =
Response::new(include_str!("./js/refresh_websocket.js").into());
let res = Response::new(
include_str!("./embedded/js/refresh_websocket.js").into(),
);
return Ok(res);
}