Skip to content

Commit

Permalink
wip stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanabx committed Dec 13, 2024
1 parent 1053b18 commit e1dbbd1
Show file tree
Hide file tree
Showing 15 changed files with 306 additions and 3 deletions.
42 changes: 42 additions & 0 deletions docs/command_reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Djot command reference

[Back to main page](./index.md)

## Installation

```shell
cargo install simple-ssg
```

## Building Locally

```shell
git clone https://github.com/ryanabx/simple-ssg
cd simple-ssg
cargo build --release
```

## Usage

```shell
Plain and simple static site generator for Djot and Markdown light markup languages

Usage: simple-ssg [OPTIONS] [DIRECTORY]

Arguments:
[DIRECTORY] Path to the directory to use to generate the site (not required if -f is specified)

Options:
-f <FILE> Process a single file instead of a directory
-o <OUTPUT_PATH> Optional output path override. Defaults to ./output for directories
--clean Clean the output directory before generating the site. Useful for multiple runs
--web-prefix <WEB_PREFIX> Specify the website prefix (defaults to local paths i.e. `./`)
-t, --template <TEMPLATE> Specify a built in template to use (will override a template.html in any directory!). defaults to whatever templates are found in template.html in the directories [possible values: github-markdown, force-none]
-h, --help Print help
-V, --version Print version
```
## Debugging
> *TIP:* Use the `RUST_LOG` environment variable to change the log level of the application: \
> i.e. `RUST_LOG=trace simple-ssg ...`
File renamed without changes.
6 changes: 6 additions & 0 deletions docs/notes/contact-ryanabx.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[Back to home page](../index.md)

# Contact Ryanabx

- [Mastodon](https://mastodon.social/@ryanabx)
- [GitHub](https://github.com/ryanabx)
9 changes: 9 additions & 0 deletions docs/notes/djot-example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Djot example

---

This is a simple djot example to showcase this static site generator! It gets put into the template called `template.html` in the home directory of this documentation, and as a result it has some nice formatting and a simple table of contents! The template is of course not required though :)

---

For more, see the [Markdown example](./markdown-example.md) and go to the command reference at [../command_reference.md](../command_reference.md)
4 changes: 2 additions & 2 deletions docs/notes/markdown-example.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This static site generator also supports Markdown, using [pulldown-cmark](https://github.com/pulldown-cmark/pulldown-cmark)!

This provides an example of using markdown alongside djot documents in the same static site! See: [../index.dj](../index.dj) for the home page, and [../command_reference](../command_reference.dj) for the command reference!
This provides an example of using markdown alongside djot documents in the same static site! See: [../index.md](../index.md) for the home page, and [../command_reference](../command_reference.md) for the command reference!


Contact me [here](contact-ryanabx.dj)
Contact me [here](contact-ryanabx.md)
File renamed without changes.
33 changes: 33 additions & 0 deletions out/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# SMPL Static Site Generator

<https://github.com/ryanabx/simple-ssg>

[Command Reference](./command_reference)

## A simple static site generator for Djot/Markdown!

### What is Djot?

[Djot](https://github.com/jgm/djot) is a light markup language created by [John MacFarlane](https://github.com/jgm), who was consequential in developing [CommonMark](https://commonmark.org/), which is a standardized variant of Markdown.

### Why Djot and not Markdown?

I decided to make this simple generator because the tooling for Djot is actually quite nice. The reference parser is written in TypeScript, but there is a parser written in rust which I'm using for this project called [Jotdown](https://github.com/hellux/jotdown). One of Djot's goals is to have single-pass parsing, and the rules are much simpler than Markdown, leaving less up to interpretation. This makes writing a parser for Djot very simple compared to having to interpret edge cases in Markdown.

The syntax of Djot is very similar to Markdown, but it removes some of the inconsistencies that couldn't be removed in CommonMark. John MacFarlane has a great blog post with some of the ideas that made it into Djot, see <https://johnmacfarlane.net/beyond-markdown.html>.

### Why make a static site generator?

Honestly, I was just wanting a bit more tooling for Djot. I created a [Visual studio code plugin](https://github.com/ryanabx/djot-vscode), and decided I wanted to have more fun with Djot. Since I build projects in rust, I'm thankful that Jotdown exists to take care of the parsing part for me, all I had to do was handle the conversions.

The scope of this generator is pretty simple, no table of contents, no sidebar, just raw Djot parsing. There are some nice warnings that the generator will let you know about though!

- A warning if `index.dj || index.djot` doesn't exist. Most sites will use `index.html` as the entry point to a site
- A warning if a local link is broken
- A warning if a path in the directory walk is not relative to the target directory (this should pretty much never happen, but it's there just in case)

You can also use the `--no-warn` argument to turn warnings into errors, if you value having an absolutely "correct" website generated!

See more from me at <https://github.com/ryanabx>

![ryanabx profile photo](https://avatars.githubusercontent.com/u/56272643?v=4)
File renamed without changes.
File renamed without changes.
4 changes: 4 additions & 0 deletions out/notes/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<p><a href="./">..</a>
<a href="./contact-ryanabx.dj">contact-ryanabx.dj</a></p>
<p><a href="./djot-example.dj">djot-example.dj</a></p>
<p><a href="./markdown-example.html">markdown-example.md</a></p>
4 changes: 4 additions & 0 deletions out/notes/markdown-example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<h1>Markdown example</h1>
<p>This static site generator also supports Markdown, using <a href="https://github.com/pulldown-cmark/pulldown-cmark">pulldown-cmark</a>!</p>
<p>This provides an example of using markdown alongside djot documents in the same static site! See: <a href="../index.dj">../index.dj</a> for the home page, and <a href="../command_reference.dj">../command_reference</a> for the command reference!</p>
<p>Contact me <a href="contact-ryanabx.dj">here</a></p>
24 changes: 24 additions & 0 deletions out/templating.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<h1>Templating</h1>
<p>Using templates in <code>simple-ssg</code> is simple! All you need to do is provide a <code>template.html</code> file in any directory of your documentation.</p>
<blockquote>
<p><strong>NOTE:</strong> A nested <code>template.html</code> will take priority over a parent folder's <code>template.html</code></p>
</blockquote>
<p>An example template could be this:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;style&gt;
&lt;!-- insert a style here --&gt;
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Contents:&lt;/h1&gt;
&lt;!-- {TABLE_OF_CONTENTS} --&gt;
&lt;hr&gt;
&lt;!-- {CONTENT} --&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>This template contains two macros, the <code>&lt;!-- {TABLE_OF_CONTENTS} --&gt;</code> macro which provides a simple bulleted list of all the page links for your static site, and <code>&lt;!-- {CONTENT} --&gt;</code>, which is where the output of the Markdown and Djot converters goes.</p>
<h2>Built-in Templates</h2>
<p>There is currently one built-in template: <code>github-markdown</code>. Use the <code>-t</code> option to specify this template. You may also use <code>-t force-none</code> to force a blank page without a template!</p>
110 changes: 109 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use jotdown::{Container, Event};
use pulldown_cmark::{CowStr, Options};
use std::{
env,
fs::{self, read_dir},
path::{Path, PathBuf},
};
use templates::BuiltInTemplate;
Expand All @@ -17,6 +18,8 @@ mod templates;
mod tests;
mod utils;

mod markdown;

/// Djot static site generator
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
Expand All @@ -40,13 +43,37 @@ struct ConsoleArgs {
/// directories.
#[arg(short, long)]
template: Option<BuiltInTemplate>,
/// Try the legacy method of generating a static site
#[arg(long)]
legacy: bool,
}

fn main() -> anyhow::Result<()> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init();
log::trace!("Begin simple-ssg::main()");
let args = ConsoleArgs::parse();
run_program(args)
if args.legacy {
run_program(args)
} else {
run_program_2(args)
}
}

fn run_program_2(args: ConsoleArgs) -> anyhow::Result<()> {
if args.directory.is_none()
|| args.file.is_some()
|| args.template.is_some()
|| args.output_path.is_none()
{
log::error!("Args incorrect");
anyhow::bail!("Args incorrect");
}
generate_site_2(
&args.directory.unwrap(),
&args.output_path.unwrap(),
args.web_prefix.as_deref(),
)?;
Ok(())
}

fn run_program(args: ConsoleArgs) -> anyhow::Result<()> {
Expand Down Expand Up @@ -112,6 +139,87 @@ pub enum FirstPassResult {
},
}

fn generate_site_2(
root_dir: &Path,
output_dir: &Path,
web_prefix: Option<&str>,
) -> anyhow::Result<()> {
let root_dir = root_dir.canonicalize()?;
let _ = fs::create_dir_all(&output_dir);
let output_dir = output_dir.canonicalize()?;

for x in WalkDir::new(&root_dir) {
let x = x?;
let x_path = x.path();
log::info!("{:?}", x_path);
if x_path.is_dir() {
if !x_path.join("index.md").exists() {
let mut directory_index = String::new();
if x.depth() > 0 {
let parent_dir = x_path.parent().unwrap();
directory_index.push_str(&directory_string(
parent_dir,
x_path,
"..",
));
}
directory_index.push_str(&create_directory_index(x_path)?);
log::info!("{}", &directory_index);
let html = markdown::md_to_html(&directory_index, x_path, web_prefix)?;
let result_path =
output_dir.join(x_path.join("index.html").strip_prefix(&root_dir)?);
log::info!("Result path: {:?}", &result_path);
let _ = std::fs::create_dir_all(result_path.parent().unwrap());
std::fs::write(&result_path, html.as_bytes())?;
}
} else if x_path.is_file() {
let result_path =
output_dir.join(x_path.with_extension("html").strip_prefix(&root_dir)?);
if x_path.extension().is_some_and(|ext| ext == "md") {
let md = fs::read_to_string(x_path)?;
let html = markdown::md_to_html(&md, x_path.parent().unwrap(), web_prefix)?;
std::fs::write(&result_path, html.as_bytes())?;
} else {
std::fs::copy(x_path, &result_path)?;
}
}
}

Ok(())
}

fn get_relative_url(root_path: &Path, target_path: &Path) -> String {
target_path
.strip_prefix(root_path)
.unwrap()
.to_string_lossy()
.to_string()
}

fn create_directory_index(folder: &Path) -> anyhow::Result<String> {
let mut result = String::new();

for x in read_dir(folder)? {
let x_path = &x?.path();
result.push_str(&directory_string(
&x_path,
folder,
&x_path.file_name().unwrap().to_string_lossy(),
));
result.push_str("\n");
}

Ok(result)
}

fn directory_string(target_path: &Path, root_path: &Path, name: &str) -> String {
format!(
"[{}](./{})\n",
name,
get_relative_url(root_path, target_path)
)
}

fn generate_site(
target_path: &Path,
output_path: &Path,
Expand Down
71 changes: 71 additions & 0 deletions src/markdown.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use std::path::Path;

use pulldown_cmark::{CowStr, Options};


/// Take markdown input, and change links if necessary
pub fn md_to_html(
markdown: &str,
file_parent_dir: &Path,
web_prefix: Option<&str>,
) -> anyhow::Result<String> {
log::info!("Markdown to html: {:?}", file_parent_dir);
let mut options = Options::empty();
options.insert(Options::ENABLE_GFM);
let events = pulldown_cmark::Parser::new_ext(markdown, options)
.map(|event| -> anyhow::Result<pulldown_cmark::Event> {
match event {
pulldown_cmark::Event::Start(pulldown_cmark::Tag::Link {
link_type,
dest_url,
title,
id,
}) => {
let inner = dest_url.to_string();
let referenced_path = file_parent_dir.join(&inner);
if referenced_path
.extension()
.is_some_and(|ext| ext == "md")
{
let new_path = Path::new(&inner).with_extension("html");
log::info!("Path: {:?} -> {:?}", &inner, &new_path);
if !referenced_path.exists() {
log::warn!("Path {:?} doesn't exist!", referenced_path);
Ok(pulldown_cmark::Event::Start(pulldown_cmark::Tag::Link {
link_type,
dest_url,
title,
id,
}))
}
else {
// Create destination url
let dest_url = CowStr::Boxed(
format!("{}{}", web_prefix.unwrap_or(""), new_path.to_string_lossy())
.into_boxed_str(),
);
Ok(pulldown_cmark::Event::Start(pulldown_cmark::Tag::Link {
link_type,
dest_url,
title,
id,
}))
}
} else {
Ok(pulldown_cmark::Event::Start(pulldown_cmark::Tag::Link {
link_type,
dest_url,
title,
id,
}))
}
}
_ => Ok(event),
}
})
.collect::<Result<Vec<pulldown_cmark::Event>, _>>()?;
log::info!("Done");
let mut html = String::new();
pulldown_cmark::html::push_html(&mut html, events.iter().cloned());
Ok(html)
}
2 changes: 2 additions & 0 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ fn site_with_links() -> anyhow::Result<()> {
clean: false,
web_prefix: None,
template: Some(crate::templates::BuiltInTemplate::ForceNone),
legacy: true
};
log::trace!("Running program");
crate::run_program(args)?;
Expand Down Expand Up @@ -103,6 +104,7 @@ fn site_warn_without_index() -> anyhow::Result<()> {
web_prefix: None,
template: Some(crate::templates::BuiltInTemplate::ForceNone),
file: None,
legacy: true,
};
crate::run_program(args)?;
Ok(())
Expand Down

0 comments on commit e1dbbd1

Please sign in to comment.