Skip to content

Commit 779bef7

Browse files
authored
feat: make it easier to use Maudit as a library (#37)
* feat: make it easier to build routes individually * feat: library example * fix: do content sources serially * refactor: always use routes
1 parent 4496b9b commit 779bef7

36 files changed

Lines changed: 862 additions & 320 deletions

File tree

Cargo.lock

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

benchmarks/md-benchmark/src/page.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ struct Params {
99
}
1010

1111
impl Page<Params> for Article {
12-
fn routes(&self, context: &mut DynamicRouteContext) -> Vec<Route<Params>> {
12+
fn routes(&self, context: &DynamicRouteContext) -> Routes<Params> {
1313
context
1414
.content
1515
.get_source::<UntypedMarkdownContent>("articles")

crates/maudit-macros/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub fn route(attrs: TokenStream, item: TokenStream) -> TokenStream {
3636
self.render(ctx).into()
3737
}
3838

39-
fn routes_internal(&self, ctx: &mut maudit::page::DynamicRouteContext) -> Vec<(maudit::page::RouteParams, Box<dyn std::any::Any + Send + Sync>, Box<dyn std::any::Any + Send + Sync>)> {
39+
fn routes_internal(&self, ctx: &maudit::page::DynamicRouteContext) -> Vec<(maudit::page::RouteParams, Box<dyn std::any::Any + Send + Sync>, Box<dyn std::any::Any + Send + Sync>)> {
4040
self.routes(ctx)
4141
.into_iter()
4242
.map(|route| {

crates/maudit/CHANGELOG.md

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,22 @@
66

77
- [9cd5fdd](https://github.com/bruits/maudit/commit/9cd5fdd8abe3044bd09d48b96217e3a0d2878b13) Updates default quality for webp to 80 to match sharp — Thanks @Princesseuh!
88

9-
109
## 0.5.0
1110

1211
### Minor changes
1312

1413
- [d5a7fad](https://github.com/bruits/maudit/commit/d5a7fad563e9642be46b24d8db500e753c1175f5) The data URI and average RGBA for thumbnails is now calculated lazily, as such the `average_rgba` and `data_uri` fields have been replaced by methods. — Thanks @Princesseuh!
1514
- [0403ac9](https://github.com/bruits/maudit/commit/0403ac9996f9d4e79945758fe06e7510729e383e) Add `is_dev()` function to allow one to toggle off things whenever running in dev — Thanks @Princesseuh!
1615
- [39db004](https://github.com/bruits/maudit/commit/39db004b63ab7aa582a92593082e1261bae55b92) Added support for shortcodes in Markdown. Shortcodes allows you to substitute custom content in your Markdown files. This feature is useful for embedding dynamic content or reusable components within your Markdown documents.
17-
16+
1817
For instance, you might define a shortcode for embedding YouTube videos using only the video ID, or for inserting custom alerts or notes.
19-
18+
2019
```markdown
2120
{{ youtube id="FbJ63spk48s" }}
2221
```
23-
22+
2423
Would render to:
25-
24+
2625
```html
2726
<iframe
2827
width="560"
@@ -35,35 +34,34 @@
3534
allowfullscreen
3635
></iframe>
3736
```
38-
37+
3938
To define and register shortcodes, pass a MarkdownShortcodes instance to the MarkdownOptions when rendering Markdown content.
40-
39+
4140
```rust
4241
let mut shortcodes = MarkdownShortcodes::new();
43-
42+
4443
shortcodes.register("youtube", |args, _ctx| {
4544
let id: String = args.get_required("id");
4645
format!(
4746
r#"<iframe width="560" height="315" src="https://www.youtube.com/embed/{}" frameborder="0" allowfullscreen></iframe>"#,
4847
id
4948
)
5049
});
51-
50+
5251
MarkdownOptions {
5352
shortcodes,
5453
..Default::default()
5554
}
56-
55+
5756
// Then pass options to, i.e. glob_markdown in a content source
5857
```
59-
58+
6059
Note that shortcodes are expanded before Markdown is rendered, so you can use shortcodes anywhere in your Markdown content, for instance in your frontmatter. Additionally, shortcodes may expand to Markdown content, which will then be rendered as part of the overall Markdown rendering process. — Thanks @Princesseuh!
6160

6261
### Patch changes
6362

6463
- [d5a7fad](https://github.com/bruits/maudit/commit/d5a7fad563e9642be46b24d8db500e753c1175f5) Added caching mechanism to placeholder and image transformation — Thanks @Princesseuh!
6564

66-
6765
## 0.4.0
6866

6967
### Minor changes
@@ -96,6 +94,7 @@ impl Page for ImagePage {
9694
```
9795

9896
See the [Assets documentation](https://maudit.org/docs/assets/) for more details. — Thanks @Princesseuh!
97+
9998
- [52eda9e](https://github.com/bruits/maudit/commit/52eda9ea4eac8efd3efd945d00f39a1b99f284ab) Adds support for dynamic routes with properties. In addition to its parameters, a dynamic route can now provide additional properties that can be used during rendering.
10099

101100
```rs
@@ -126,7 +125,7 @@ impl Page<Params, Props> for Post {
126125
).into()
127126
}
128127

129-
fn routes(&self, ctx: &mut DynamicRouteContext) -> Routes<Params, Props> {
128+
fn routes(&self, ctx: &DynamicRouteContext) -> Routes<Params, Props> {
130129
vec![Route::from_params_and_props(
131130
Params {
132131
slug: "hello-world".to_string(),
@@ -145,4 +144,3 @@ For more information on dynamic routes, see the [Routing documentation](https://
145144
### Patch changes
146145

147146
- Updated dependencies: maudit-macros@0.4.0
148-

crates/maudit/src/assets.rs

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use dyn_eq::DynEq;
33
use log::debug;
44
use rustc_hash::FxHashSet;
55
use std::hash::Hash;
6-
use std::sync::OnceLock;
76
use std::time::Instant;
87
use std::{fs, path::PathBuf};
98
use xxhash_rust::xxh3::xxh3_64;
@@ -18,17 +17,39 @@ pub use style::{Style, StyleOptions};
1817

1918
#[derive(Default)]
2019
pub struct PageAssets {
21-
pub(crate) images: FxHashSet<Image>,
22-
pub(crate) scripts: FxHashSet<Script>,
23-
pub(crate) styles: FxHashSet<Style>,
24-
25-
pub(crate) included_styles: Vec<Style>,
26-
pub(crate) included_scripts: Vec<Script>,
20+
pub images: FxHashSet<Image>,
21+
pub scripts: FxHashSet<Script>,
22+
pub styles: FxHashSet<Style>,
2723

2824
pub(crate) assets_dir: PathBuf,
2925
}
3026

3127
impl PageAssets {
28+
pub fn new(assets_dir: PathBuf) -> Self {
29+
Self {
30+
assets_dir,
31+
..Default::default()
32+
}
33+
}
34+
35+
pub fn assets(&self) -> impl Iterator<Item = &dyn Asset> {
36+
self.images
37+
.iter()
38+
.map(|asset| asset as &dyn Asset)
39+
.chain(self.scripts.iter().map(|asset| asset as &dyn Asset))
40+
.chain(self.styles.iter().map(|asset| asset as &dyn Asset))
41+
}
42+
43+
/// Get all styles that are marked as included
44+
pub fn included_styles(&self) -> impl Iterator<Item = &Style> {
45+
self.styles.iter().filter(|s| s.included)
46+
}
47+
48+
/// Get all scripts that are marked as included
49+
pub fn included_scripts(&self) -> impl Iterator<Item = &Script> {
50+
self.scripts.iter().filter(|s| s.included)
51+
}
52+
3253
/// Add an image to the page assets, causing the file to be created in the output directory. The image is resolved relative to the current working directory.
3354
///
3455
/// The image will not automatically be included in the page, but can be included through the `.url()` method on the returned `Image` object.
@@ -64,7 +85,6 @@ impl PageAssets {
6485
} else {
6586
Some(options)
6687
},
67-
__cache_placeholder: OnceLock::new(),
6888
};
6989

7090
self.images.insert(image.clone());
@@ -94,6 +114,7 @@ impl PageAssets {
94114
path: path.clone(),
95115
assets_dir: self.assets_dir.clone(),
96116
hash: calculate_hash(&path, None),
117+
included: false,
97118
};
98119

99120
self.scripts.insert(script.clone());
@@ -115,10 +136,10 @@ impl PageAssets {
115136
path: path.clone(),
116137
assets_dir: self.assets_dir.clone(),
117138
hash: calculate_hash(&path, None),
139+
included: true,
118140
};
119141

120-
self.scripts.insert(script.clone());
121-
self.included_scripts.push(script);
142+
self.scripts.insert(script);
122143
}
123144

124145
/// Add a style to the page assets, causing the file to be created in the output directory. The style is resolved relative to the current working directory.
@@ -149,6 +170,7 @@ impl PageAssets {
149170
assets_dir: self.assets_dir.clone(),
150171
hash: calculate_hash(&path, Some(HashConfig::Style(&options))),
151172
tailwind: options.tailwind,
173+
included: false,
152174
};
153175

154176
self.styles.insert(style.clone());
@@ -184,10 +206,10 @@ impl PageAssets {
184206
assets_dir: self.assets_dir.clone(),
185207
hash,
186208
tailwind: options.tailwind,
209+
included: true,
187210
};
188211

189-
self.styles.insert(style.clone());
190-
self.included_styles.push(style);
212+
self.styles.insert(style);
191213
}
192214
}
193215

@@ -333,7 +355,7 @@ mod tests {
333355
page_assets.include_style(temp_dir.join("style.css"));
334356

335357
assert!(page_assets.styles.len() == 1);
336-
assert!(page_assets.included_styles.len() == 1);
358+
assert!(page_assets.included_styles().count() == 1);
337359
}
338360

339361
#[test]
@@ -359,7 +381,7 @@ mod tests {
359381
page_assets.include_script(temp_dir.join("script.js"));
360382

361383
assert!(page_assets.scripts.len() == 1);
362-
assert!(page_assets.included_scripts.len() == 1);
384+
assert!(page_assets.included_scripts().count() == 1);
363385
}
364386

365387
#[test]
@@ -459,7 +481,7 @@ mod tests {
459481
let image_webp = page_assets.add_image_with_options(
460482
&image_path,
461483
ImageOptions {
462-
format: Some(ImageFormat::Webp),
484+
format: Some(ImageFormat::WebP),
463485
..Default::default()
464486
},
465487
);
@@ -476,7 +498,7 @@ mod tests {
476498
ImageOptions {
477499
width: Some(100),
478500
height: Some(100),
479-
format: Some(ImageFormat::Webp),
501+
format: Some(ImageFormat::WebP),
480502
},
481503
);
482504

@@ -617,6 +639,7 @@ mod tests {
617639
Some(HashConfig::Style(&StyleOptions::default())),
618640
),
619641
tailwind: false,
642+
included: false,
620643
};
621644

622645
assert_ne!(

crates/maudit/src/assets/image.rs

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ use super::image_cache::ImageCache;
1010
use crate::assets::{Asset, InternalAsset};
1111
use crate::is_dev;
1212

13-
#[derive(Clone, PartialEq, Eq, Hash)]
13+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1414
pub enum ImageFormat {
1515
Png,
1616
Jpeg,
17-
Webp,
17+
WebP,
1818
Avif,
1919
Gif,
2020
}
@@ -24,7 +24,7 @@ impl ImageFormat {
2424
match self {
2525
ImageFormat::Png => "png",
2626
ImageFormat::Jpeg => "jpg",
27-
ImageFormat::Webp => "webp",
27+
ImageFormat::WebP => "webp",
2828
ImageFormat::Avif => "avif",
2929
ImageFormat::Gif => "gif",
3030
}
@@ -34,7 +34,7 @@ impl ImageFormat {
3434
match self {
3535
ImageFormat::Png => 1,
3636
ImageFormat::Jpeg => 2,
37-
ImageFormat::Webp => 3,
37+
ImageFormat::WebP => 3,
3838
ImageFormat::Gif => 4,
3939
ImageFormat::Avif => 5,
4040
}
@@ -46,59 +46,36 @@ impl From<ImageFormat> for image::ImageFormat {
4646
match val {
4747
ImageFormat::Png => image::ImageFormat::Png,
4848
ImageFormat::Jpeg => image::ImageFormat::Jpeg,
49-
ImageFormat::Webp => image::ImageFormat::WebP,
49+
ImageFormat::WebP => image::ImageFormat::WebP,
5050
ImageFormat::Avif => image::ImageFormat::Avif,
5151
ImageFormat::Gif => image::ImageFormat::Gif,
5252
}
5353
}
5454
}
5555

56-
#[derive(Default, Clone, PartialEq, Eq, Hash)]
56+
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
5757
pub struct ImageOptions {
5858
pub width: Option<u32>,
5959
pub height: Option<u32>,
6060
pub format: Option<ImageFormat>,
6161
}
6262

63-
#[derive(Clone)]
64-
#[non_exhaustive]
63+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
6564
pub struct Image {
6665
pub path: PathBuf,
6766
pub width: u32,
6867
pub height: u32,
6968
pub(crate) assets_dir: PathBuf,
7069
pub(crate) hash: String,
7170
pub(crate) options: Option<ImageOptions>,
72-
pub(crate) __cache_placeholder: OnceLock<ImagePlaceholder>,
7371
}
7472

75-
impl Hash for Image {
76-
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
77-
self.path.hash(state);
78-
self.assets_dir.hash(state);
79-
self.hash.hash(state);
80-
self.options.hash(state);
81-
}
82-
}
83-
84-
impl PartialEq for Image {
85-
fn eq(&self, other: &Self) -> bool {
86-
self.path == other.path
87-
&& self.assets_dir == other.assets_dir
88-
&& self.hash == other.hash
89-
&& self.options == other.options
90-
}
91-
}
92-
93-
impl Eq for Image {}
94-
9573
impl Image {
9674
/// Get a placeholder for the image, which can be used for low-quality image placeholders (LQIP) or similar techniques.
9775
///
9876
/// This uses the [ThumbHash](https://evanw.github.io/thumbhash/) algorithm to generate a very small placeholder image.
99-
pub fn placeholder(&self) -> &ImagePlaceholder {
100-
self.__cache_placeholder
101-
.get_or_init(|| get_placeholder(&self.path))
77+
pub fn placeholder(&self) -> ImagePlaceholder {
78+
get_placeholder(&self.path)
10279
}
10380
}
10481

crates/maudit/src/assets/script.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub struct Script {
88
pub path: PathBuf,
99
pub(crate) assets_dir: PathBuf,
1010
pub(crate) hash: String,
11+
pub(crate) included: bool,
1112
}
1213

1314
impl InternalAsset for Script {

crates/maudit/src/assets/style.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub struct Style {
1414
pub(crate) assets_dir: PathBuf,
1515
pub(crate) hash: String,
1616
pub(crate) tailwind: bool,
17+
pub(crate) included: bool,
1718
}
1819

1920
impl InternalAsset for Style {

0 commit comments

Comments
 (0)