Skip to content

Commit 56127ad

Browse files
committed
feat(homepage): build homepage
1 parent 971d92c commit 56127ad

File tree

14 files changed

+539
-137
lines changed

14 files changed

+539
-137
lines changed

crates/rari-doc/src/build.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use rari_types::globals::build_out_root;
77
use rayon::iter::{IntoParallelIterator, ParallelIterator};
88
use tracing::{error, span, Level};
99

10-
use crate::cached_readers::{blog_files, curriculum_files, generic_pages_files};
10+
use crate::cached_readers::{
11+
blog_files, contributor_spotlight_files, curriculum_files, generic_pages_files,
12+
};
1113
use crate::error::DocError;
1214
use crate::pages::build::{
1315
build_blog_post, build_contributor_spotlight, build_curriculum, build_doc, build_generic_page,
@@ -43,7 +45,7 @@ pub fn build_single_page(page: &Page) {
4345
serde_json::to_writer(buffed, &built_page).unwrap();
4446

4547
if let Some(in_path) = page.full_path().parent() {
46-
copy_additional_files(in_path, &out_path).unwrap();
48+
copy_additional_files(in_path, &out_path, page.full_path()).unwrap();
4749
}
4850
}
4951
Err(e) => {
@@ -94,3 +96,24 @@ pub fn build_generic_pages() -> Result<Vec<Cow<'static, str>>, DocError> {
9496
})
9597
.collect())
9698
}
99+
100+
pub fn build_contributor_spotlight_pages() -> Result<Vec<Cow<'static, str>>, DocError> {
101+
Ok(contributor_spotlight_files()
102+
.values()
103+
.map(|page| {
104+
build_single_page(page);
105+
Cow::Owned(page.url().to_string())
106+
})
107+
.collect())
108+
}
109+
110+
pub fn build_spas() -> Result<Vec<Cow<'static, str>>, DocError> {
111+
Ok(SPA::all()
112+
.iter()
113+
.filter_map(|(slug, locale)| SPA::from_slug(slug, *locale))
114+
.map(|page| {
115+
build_single_page(&page);
116+
Cow::Owned(page.url().to_string())
117+
})
118+
.collect())
119+
}

crates/rari-doc/src/cached_readers.rs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use std::path::{Path, PathBuf};
44
use std::sync::{Arc, LazyLock, OnceLock, RwLock};
55

66
use rari_types::globals::{
7-
blog_root, cache_content, content_root, content_translated_root, curriculum_root,
8-
generic_pages_root,
7+
blog_root, cache_content, content_root, content_translated_root, contributor_spotlight_root,
8+
curriculum_root, generic_pages_root,
99
};
1010
use rari_types::locale::Locale;
1111
use rari_utils::io::read_to_string;
@@ -15,6 +15,7 @@ use crate::error::DocError;
1515
use crate::html::sidebar::{MetaSidebar, Sidebar};
1616
use crate::pages::page::{Page, PageLike};
1717
use crate::pages::types::blog::{Author, AuthorFrontmatter, BlogPost, BlogPostBuildMeta};
18+
use crate::pages::types::contributors::ContributorSpotlight;
1819
use crate::pages::types::curriculum::{CurriculumIndexEntry, CurriculumPage};
1920
use crate::pages::types::doc::Doc;
2021
use crate::pages::types::generic::GenericPage;
@@ -31,7 +32,8 @@ type SidebarFilesCache = Arc<RwLock<HashMap<(String, Locale), Arc<MetaSidebar>>>
3132
pub static CACHED_SIDEBAR_FILES: LazyLock<SidebarFilesCache> =
3233
LazyLock::new(|| Arc::new(RwLock::new(HashMap::new())));
3334
pub static CACHED_CURRICULUM: OnceLock<CurriculumFiles> = OnceLock::new();
34-
pub static GENERIC_PAGES_FILES: OnceLock<GenericPagesFiles> = OnceLock::new();
35+
pub static GENERIC_PAGES_FILES: OnceLock<UrlToPageMap> = OnceLock::new();
36+
pub static CONTRIBUTOR_SPOTLIGHT_FILES: OnceLock<UrlToPageMap> = OnceLock::new();
3537

3638
#[derive(Debug, Default, Clone)]
3739
pub struct BlogFiles {
@@ -177,6 +179,30 @@ pub fn gather_curriculum() -> Result<CurriculumFiles, DocError> {
177179
}
178180
}
179181

182+
pub fn gather_contributre_spotlight() -> Result<HashMap<String, Page>, DocError> {
183+
if let Some(root) = contributor_spotlight_root() {
184+
Ok(read_docs_parallel::<ContributorSpotlight>(&[root], None)?
185+
.into_iter()
186+
.filter_map(|page| {
187+
if let Page::ContributorSpotlight(cs) = page {
188+
Some(cs)
189+
} else {
190+
None
191+
}
192+
})
193+
.flat_map(|cs| {
194+
Locale::all()
195+
.iter()
196+
.map(|locale| Page::ContributorSpotlight(Arc::new(cs.as_locale(*locale))))
197+
.collect::<Vec<_>>()
198+
})
199+
.map(|page| (page.url().to_ascii_lowercase(), page))
200+
.collect())
201+
} else {
202+
Err(DocError::NoGenericPagesRoot)
203+
}
204+
}
205+
180206
pub fn curriculum_files() -> Cow<'static, CurriculumFiles> {
181207
if cache_content() {
182208
Cow::Borrowed(CACHED_CURRICULUM.get_or_init(|| {
@@ -303,10 +329,10 @@ pub fn read_and_cache_doc_pages() -> Result<Vec<Page>, DocError> {
303329
Ok(docs)
304330
}
305331

306-
pub type GenericPagesFiles = HashMap<String, Page>;
332+
pub type UrlToPageMap = HashMap<String, Page>;
307333

308-
pub fn generic_pages_files() -> Cow<'static, GenericPagesFiles> {
309-
fn gather() -> GenericPagesFiles {
334+
pub fn generic_pages_files() -> Cow<'static, UrlToPageMap> {
335+
fn gather() -> UrlToPageMap {
310336
gather_generic_pages().unwrap_or_else(|e| {
311337
error!("{e}");
312338
Default::default()
@@ -318,3 +344,17 @@ pub fn generic_pages_files() -> Cow<'static, GenericPagesFiles> {
318344
Cow::Owned(gather())
319345
}
320346
}
347+
348+
pub fn contributor_spotlight_files() -> Cow<'static, UrlToPageMap> {
349+
fn gather() -> UrlToPageMap {
350+
gather_contributre_spotlight().unwrap_or_else(|e| {
351+
error!("{e}");
352+
Default::default()
353+
})
354+
}
355+
if cache_content() {
356+
Cow::Borrowed(CONTRIBUTOR_SPOTLIGHT_FILES.get_or_init(gather))
357+
} else {
358+
Cow::Owned(gather())
359+
}
360+
}

crates/rari-doc/src/html/rewriter.rs

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rari_l10n::l10n_json_data;
77
use rari_md::node_card::NoteCard;
88
use rari_types::fm_types::PageType;
99
use rari_types::locale::Locale;
10+
use tracing::warn;
1011
use url::Url;
1112

1213
use crate::error::DocError;
@@ -85,20 +86,28 @@ pub fn post_process_html<T: PageLike>(
8586
el.set_attribute("src", url.path())?;
8687
let file = page.full_path().parent().unwrap().join(&src);
8788
let (width, height) = if src.ends_with(".svg") {
88-
let meta = svg_metadata::Metadata::parse_file(&file)?;
89-
(
90-
meta.width
91-
.map(|width| width.width)
92-
.or(meta.view_box.map(|vb| vb.width))
93-
.map(|width| format!("{:.0}", width)),
94-
meta.height
95-
.map(|height| height.height)
96-
.or(meta.view_box.map(|vb| vb.height))
97-
.map(|height| format!("{:.0}", height)),
98-
)
99-
} else {
100-
let dim = imagesize::size(&file)?;
89+
match svg_metadata::Metadata::parse_file(&file) {
90+
Ok(meta) => (
91+
meta.width
92+
.map(|width| width.width)
93+
.or(meta.view_box.map(|vb| vb.width))
94+
.map(|width| format!("{:.0}", width)),
95+
meta.height
96+
.map(|height| height.height)
97+
.or(meta.view_box.map(|vb| vb.height))
98+
.map(|height| format!("{:.0}", height)),
99+
),
100+
Err(e) => {
101+
warn!("Error parsing {}: {e}", file.display());
102+
(None, None)
103+
}
104+
}
105+
} else if let Ok(dim) = imagesize::size(&file)
106+
.inspect_err(|e| warn!("Error opening {}: {e}", file.display()))
107+
{
101108
(Some(dim.width.to_string()), Some(dim.height.to_string()))
109+
} else {
110+
(None, None)
102111
};
103112
if let Some(width) = width {
104113
el.set_attribute("width", &width)?;
@@ -143,7 +152,7 @@ pub fn post_process_html<T: PageLike>(
143152
el.set_attribute("aria-current", "page")?;
144153
}
145154
if !Page::exists(resolved_href_no_hash) && !Page::ignore(href) {
146-
tracing::info!("{resolved_href_no_hash} {href}");
155+
tracing::debug!("{resolved_href_no_hash} {href}");
147156
let class = el.get_attribute("class").unwrap_or_default();
148157
el.set_attribute(
149158
"class",

crates/rari-doc/src/pages/build.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ pub fn build_content<T: PageLike>(page: &T) -> Result<PageContent, DocError> {
157157
let encoded_html = render_md_to_html(&ks_rendered_doc, page.locale())?;
158158
let html = decode_ref(&encoded_html, &templs)?;
159159
let post_processed_html = post_process_html(&html, page, false)?;
160+
160161
let mut fragment = Html::parse_fragment(&post_processed_html);
161162
if page.page_type() == PageType::Curriculum {
162163
bubble_up_curriculum_page(&mut fragment)?;
@@ -322,7 +323,8 @@ pub fn build_blog_post(post: &BlogPost) -> Result<BuiltDocy, DocError> {
322323
}
323324

324325
pub fn build_generic_page(page: &GenericPage) -> Result<BuiltDocy, DocError> {
325-
let PageContent { body, toc, .. } = build_content(page)?;
326+
let built = build_content(page);
327+
let PageContent { body, toc, .. } = built?;
326328
Ok(BuiltDocy::GenericPage(Box::new(JsonGenericPage {
327329
hy_data: JsonGenericHyData {
328330
sections: body,
@@ -399,11 +401,11 @@ pub fn build_contributor_spotlight(cs: &ContributorSpotlight) -> Result<BuiltDoc
399401
)))
400402
}
401403

402-
pub fn copy_additional_files(from: &Path, to: &Path) -> Result<(), DocError> {
404+
pub fn copy_additional_files(from: &Path, to: &Path, ignore: &Path) -> Result<(), DocError> {
403405
for from in fs::read_dir(from)?
404406
.filter_map(Result::ok)
405407
.map(|f| f.path())
406-
.filter(|p| p.is_file())
408+
.filter(|p| p.is_file() && p != ignore)
407409
{
408410
if let Some(filename) = from.file_name() {
409411
let to = to.to_path_buf().join(filename);

crates/rari-doc/src/pages/json.rs

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
use std::path::PathBuf;
22

3-
use chrono::NaiveDateTime;
3+
use chrono::{DateTime, NaiveDate, NaiveDateTime, Utc};
44
use rari_data::baseline::SupportStatusWithByKey;
55
use rari_types::locale::{Locale, Native};
66
use serde::{Deserialize, Serialize};
77

88
use super::types::contributors::Usernames;
99
use super::types::curriculum::{CurriculumIndexEntry, CurriculumSidebarEntry, Template, Topic};
1010
use crate::pages::types::blog::BlogMeta;
11-
use crate::pages::types::spa::BlogIndex;
1211
use crate::specs::Specification;
1312
use crate::utils::modified_dt;
1413

@@ -117,6 +116,11 @@ pub struct JsonDoc {
117116
pub browser_compat: Vec<String>,
118117
}
119118

119+
#[derive(Debug, Clone, Serialize)]
120+
pub struct BlogIndex {
121+
pub posts: Vec<BlogMeta>,
122+
}
123+
120124
#[derive(Debug, Clone, Serialize)]
121125
#[serde(untagged)]
122126
pub enum HyData {
@@ -246,6 +250,7 @@ pub enum BuiltDocy {
246250
ContributorSpotlight(Box<JsonContributorSpotlight>),
247251
BasicSPA(Box<JsonBasicSPA>),
248252
GenericPage(Box<JsonGenericPage>),
253+
HomePageSPA(Box<JsonHomePageSPA>),
249254
}
250255

251256
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
@@ -285,12 +290,72 @@ pub struct UrlNTitle {
285290
pub struct JsonBasicSPA {
286291
pub slug: &'static str,
287292
pub page_title: &'static str,
288-
pub page_description: &'static str,
293+
pub page_description: Option<&'static str>,
289294
pub only_follow: bool,
290295
pub no_indexing: bool,
291296
pub url: String,
292297
}
293298

299+
#[derive(Debug, Clone, Serialize)]
300+
#[serde(rename_all = "camelCase")]
301+
pub struct HomePageFeaturedArticle {
302+
pub mdn_url: String,
303+
pub summay: String,
304+
pub title: String,
305+
pub tag: Option<Parent>,
306+
}
307+
308+
#[derive(Debug, Clone, Serialize)]
309+
#[serde(rename_all = "camelCase")]
310+
pub struct HomePageFeaturedContributor {
311+
pub contributor_name: String,
312+
pub url: String,
313+
pub quote: String,
314+
}
315+
316+
#[derive(Debug, Clone, Serialize)]
317+
pub struct NameUrl {
318+
pub name: String,
319+
pub url: String,
320+
}
321+
322+
#[derive(Debug, Clone, Serialize)]
323+
pub struct HomePageLatestNewsItem {
324+
pub url: String,
325+
pub title: String,
326+
pub author: Option<String>,
327+
pub source: NameUrl,
328+
pub published_at: NaiveDate,
329+
}
330+
331+
#[derive(Debug, Clone, Serialize)]
332+
pub struct HomePageRecentContribution {
333+
pub number: i64,
334+
pub title: String,
335+
pub updated_at: DateTime<Utc>,
336+
pub url: String,
337+
pub repo: NameUrl,
338+
}
339+
340+
#[derive(Debug, Clone, Serialize)]
341+
pub struct ItemContainer<T>
342+
where
343+
T: Clone + Serialize,
344+
{
345+
pub items: Vec<T>,
346+
}
347+
#[derive(Debug, Clone, Serialize)]
348+
#[serde(rename_all = "camelCase")]
349+
pub struct JsonHomePageSPA {
350+
pub slug: &'static str,
351+
pub page_title: &'static str,
352+
pub page_description: Option<&'static str>,
353+
pub featured_articles: Vec<HomePageFeaturedArticle>,
354+
pub featured_contributor: Option<HomePageFeaturedContributor>,
355+
pub latest_news: ItemContainer<HomePageLatestNewsItem>,
356+
pub recent_contributions: ItemContainer<HomePageRecentContribution>,
357+
}
358+
294359
#[derive(Debug, Clone, Serialize)]
295360
#[serde(rename_all = "camelCase")]
296361
pub struct JsonGenericHyData {

crates/rari-doc/src/pages/types/contributors.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use std::path::{Path, PathBuf};
22
use std::sync::Arc;
33

44
use concat_in_place::strcat;
5-
use constcat::concat;
65
use rari_md::m2h;
76
use rari_types::error::EnvError;
87
use rari_types::fm_types::{FeatureStatus, PageType};
@@ -135,6 +134,23 @@ pub struct ContributorSpotlight {
135134
content_start: usize,
136135
}
137136

137+
impl ContributorSpotlight {
138+
pub fn as_locale(&self, locale: Locale) -> Self {
139+
let Self {
140+
mut meta,
141+
raw,
142+
content_start,
143+
} = self.clone();
144+
meta.locale = locale;
145+
meta.url = strcat!("/" locale.as_url_str() "/community/" meta.slug.as_str());
146+
Self {
147+
meta,
148+
raw,
149+
content_start,
150+
}
151+
}
152+
}
153+
138154
impl PageReader for ContributorSpotlight {
139155
fn read(path: impl Into<PathBuf>, locale: Option<Locale>) -> Result<Page, DocError> {
140156
read_contributor_spotlight(path, locale.unwrap_or_default())
@@ -206,7 +222,7 @@ impl PageLike for ContributorSpotlight {
206222
}
207223

208224
fn base_slug(&self) -> &str {
209-
concat!("/", Locale::EnUs.as_url_str(), "/")
225+
"/en-US/"
210226
}
211227

212228
fn trailing_slash(&self) -> bool {

crates/rari-doc/src/pages/types/generic.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,14 @@ impl GenericPage {
9292
}
9393

9494
pub fn as_locale(&self, locale: Locale) -> Self {
95-
let GenericPage {
95+
let Self {
9696
mut meta,
9797
raw,
9898
content_start,
9999
} = self.clone();
100100
meta.locale = locale;
101101
meta.url = strcat!("/" locale.as_url_str() "/" meta.slug.as_str());
102-
GenericPage {
102+
Self {
103103
meta,
104104
raw,
105105
content_start,

0 commit comments

Comments
 (0)