Skip to content

Commit 0b26aba

Browse files
committed
feat(sidebar): l10n support
also fixes baseline and id generation
1 parent 337deb8 commit 0b26aba

File tree

15 files changed

+126
-50
lines changed

15 files changed

+126
-50
lines changed

Diff for: Cargo.lock

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: crates/diff-test/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ itertools = "0.13"
1919
similar = "2"
2020
regex = "1"
2121
once_cell = "1"
22+
htmldiff = "0.1"

Diff for: crates/diff-test/src/main.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::path::Path;
77

88
use anyhow::{anyhow, Error};
99
use clap::{Args, Parser, Subcommand};
10+
use htmldiff::htmldiff;
1011
use ignore::types::TypesBuilder;
1112
use ignore::WalkBuilder;
1213
use itertools::Itertools;
@@ -157,7 +158,7 @@ fn make_key(path: &[PathIndex]) -> String {
157158
}
158159

159160
fn is_html(s: &str) -> bool {
160-
s.starts_with('<') && s.ends_with('>')
161+
s.trim_start().starts_with('<') && s.trim_end().ends_with('>')
161162
}
162163

163164
const IGNORE: &[&str] = &[
@@ -249,7 +250,7 @@ fn full_diff(lhs: &Value, rhs: &Value, path: &[PathIndex], diff: &mut BTreeMap<S
249250
// .to_string(),
250251
);
251252
} else {
252-
diff.insert(key, "differs".into());
253+
diff.insert(key, htmldiff(&lhs, &rhs));
253254
}
254255
}
255256
}

Diff for: crates/rari-data/src/baseline.rs

+4-16
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,22 @@ use std::fmt;
33
use std::marker::PhantomData;
44
use std::path::Path;
55

6-
use chrono::NaiveDate;
76
use rari_utils::io::read_to_string;
87
use serde::de::{self, value, SeqAccess, Visitor};
98
use serde::{Deserialize, Deserializer, Serialize};
109
use url::Url;
1110

1211
use crate::error::Error;
1312

13+
#[derive(Deserialize, Serialize, Clone, Debug)]
1414
pub struct WebFeatures {
1515
pub features: BTreeMap<String, FeatureData>,
1616
}
1717

1818
impl WebFeatures {
1919
pub fn from_file(path: &Path) -> Result<Self, Error> {
2020
let json_str = read_to_string(path)?;
21-
Ok(Self {
22-
features: serde_json::from_str(&json_str)?,
23-
})
21+
Ok(serde_json::from_str(&json_str)?)
2422
}
2523

2624
pub fn feature_status(&self, features: &[&str]) -> Option<&SupportStatus> {
@@ -45,13 +43,6 @@ impl WebFeatures {
4543

4644
#[derive(Deserialize, Serialize, Clone, Debug)]
4745
pub struct FeatureData {
48-
/** Alias identifier */
49-
#[serde(
50-
deserialize_with = "t_or_vec",
51-
default,
52-
skip_serializing_if = "Vec::is_empty"
53-
)]
54-
pub alias: Vec<String>,
5546
/** Specification */
5647
#[serde(
5748
deserialize_with = "t_or_vec",
@@ -76,9 +67,6 @@ pub struct FeatureData {
7667
skip_serializing_if = "Vec::is_empty"
7768
)]
7869
pub compat_features: Vec<String>,
79-
/** Usage stats */
80-
#[serde(deserialize_with = "t_or_vec", default)]
81-
pub usage_stats: Vec<Url>,
8270
}
8371

8472
#[derive(Deserialize, Serialize, Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
@@ -109,10 +97,10 @@ pub struct SupportStatus {
10997
pub baseline: Option<BaselineHighLow>,
11098
/// Date the feature achieved Baseline low status
11199
#[serde(skip_serializing_if = "Option::is_none")]
112-
pub baseline_low_date: Option<NaiveDate>,
100+
pub baseline_low_date: Option<String>,
113101
/// Date the feature achieved Baseline high status
114102
#[serde(skip_serializing_if = "Option::is_none")]
115-
pub baseline_high_date: Option<NaiveDate>,
103+
pub baseline_high_date: Option<String>,
116104
/// Browser versions that most-recently introduced the feature
117105
pub support: BTreeMap<BrowserIdentifier, String>,
118106
}

Diff for: crates/rari-deps/src/npm.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub fn get_package(
5252
let current_version = package_json["version"]
5353
.as_str()
5454
.ok_or(DepsError::WebRefMissingVersionError)?;
55-
current_version == latest_version
55+
current_version != latest_version
5656
} else {
5757
true
5858
};

Diff for: crates/rari-doc/src/baseline.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ static WEB_FEATURES: Lazy<Option<WebFeatures>> = Lazy::new(|| {
88
&data_dir()
99
.join("web-features")
1010
.join("package")
11-
.join("index.json"),
11+
.join("data.json"),
1212
);
1313
match web_features {
1414
Ok(web_features) => Some(web_features),

Diff for: crates/rari-doc/src/cached_readers.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
44
use std::sync::{Arc, RwLock};
55

66
use once_cell::sync::{Lazy, OnceCell};
7-
use rari_types::globals::{blog_root, cache_content, curriculum_root};
7+
use rari_types::globals::{blog_root, cache_content, content_root, curriculum_root};
88
use rari_types::locale::Locale;
99
use rari_utils::io::read_to_string;
1010
use tracing::error;
@@ -15,7 +15,7 @@ use crate::docs::page::{Page, PageLike};
1515
use crate::error::DocError;
1616
use crate::html::sidebar::{MetaSidebar, Sidebar};
1717
use crate::sidebars::jsref;
18-
use crate::utils::{root_for_locale, split_fm};
18+
use crate::utils::split_fm;
1919
use crate::walker::{read_docs_parallel, walk_builder};
2020

2121
pub static STATIC_PAGE_FILES: OnceCell<HashMap<PathBuf, Page>> = OnceCell::new();
@@ -50,9 +50,8 @@ pub fn read_sidebar(name: &str, locale: Locale, slug: &str) -> Result<Arc<MetaSi
5050
return Ok(sidebar.clone());
5151
}
5252
}
53-
let mut file = root_for_locale(locale)?.to_path_buf();
53+
let mut file = content_root().to_path_buf();
5454
file.push("sidebars");
55-
file.push(locale.as_folder_str());
5655
file.push(name);
5756
file.set_extension("yaml");
5857
let raw = read_to_string(&file)?;

Diff for: crates/rari-doc/src/docs/page.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,18 @@ fn doc_from_path_and_locale(path: &Path, locale: Locale) -> Result<Page, DocErro
100100
}
101101

102102
pub fn url_path_to_page(url_path: &str) -> Result<Page, DocError> {
103+
url_path_to_page_with_other_locale_and_fallback(url_path, None)
104+
}
105+
106+
pub fn url_path_to_page_with_other_locale_and_fallback(
107+
url_path: &str,
108+
locale: Option<Locale>,
109+
) -> Result<Page, DocError> {
103110
if let Some(dummy) = Dummy::from_url(url_path) {
104111
return Ok(dummy);
105112
}
106-
let (path, locale, typ) = url_path_to_path_buf(url_path)?;
113+
let (path, locale_from_url, typ) = url_path_to_path_buf(url_path)?;
114+
let locale = locale.unwrap_or(locale_from_url);
107115
match typ {
108116
PageCategory::Doc => {
109117
let doc = doc_from_path_and_locale(&path, locale);

Diff for: crates/rari-doc/src/helpers/subpages.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use rari_types::globals::deny_warnings;
99
use rari_types::locale::Locale;
1010

1111
use super::titles::api_page_title;
12-
use crate::docs::page::{Page, PageLike, PageReader};
12+
use crate::docs::page::{
13+
url_path_to_page_with_other_locale_and_fallback, Page, PageLike, PageReader,
14+
};
1315
use crate::error::DocError;
1416
use crate::redirects::resolve_redirect;
1517
use crate::templ::templs::badges::{write_deprecated, write_experimental, write_non_standard};
@@ -60,14 +62,19 @@ impl SubPagesSorter {
6062

6163
pub fn write_li_with_badges(
6264
out: &mut impl Write,
63-
page: &impl PageLike,
65+
page: &Page,
6466
locale: Locale,
6567
) -> Result<(), DocError> {
68+
let locale_page = if locale != Default::default() {
69+
&url_path_to_page_with_other_locale_and_fallback(page.url(), Some(locale))?
70+
} else {
71+
page
72+
};
6673
write!(
6774
out,
6875
"<li><a href=\"{}\">{}</a>",
69-
page.url(),
70-
html_escape::encode_safe(page.short_title().unwrap_or(page.title()))
76+
locale_page.url(),
77+
html_escape::encode_safe(locale_page.short_title().unwrap_or(locale_page.title()))
7178
)?;
7279
if page.status().contains(&FeatureStatus::Experimental) {
7380
write_experimental(out, locale)?;

Diff for: crates/rari-doc/src/html/modifier.rs

+29
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use std::borrow::Cow;
2+
use std::collections::HashSet;
3+
14
use ego_tree::NodeId;
25
use html5ever::{namespace_url, ns, QualName};
36
use rari_md::anchor::anchorize;
@@ -33,6 +36,13 @@ pub fn remove_attribute(html: &mut Html, node_id: NodeId, key: &str) {
3336
}
3437

3538
pub fn add_missing_ids(html: &mut Html) -> Result<(), DocError> {
39+
let selector = Selector::parse("*[id]").unwrap();
40+
let mut ids = html
41+
.select(&selector)
42+
.filter_map(|el| el.attr("id"))
43+
.map(Cow::Borrowed)
44+
.collect::<HashSet<_>>();
45+
3646
let selector = Selector::parse("*[data-update-id], h2:not([id]), h3:not([id])").unwrap();
3747
let subs =
3848
html.select(&selector)
@@ -45,6 +55,25 @@ pub fn add_missing_ids(html: &mut Html) -> Result<(), DocError> {
4555
el.text().collect::<String>()
4656
};
4757
let id = anchorize(&text);
58+
if ids.contains(id.as_str()) {
59+
let (prefix, mut count) = if let Some((prefix, counter)) = id.rsplit_once('_') {
60+
if counter.chars().all(|c| c.is_ascii_digit()) {
61+
let count = counter.parse::<i64>().unwrap_or_default() + 1;
62+
(prefix, count)
63+
} else {
64+
(id.as_str(), 2)
65+
}
66+
} else {
67+
(id.as_str(), 2)
68+
};
69+
let mut id = format!("{prefix}_{count}");
70+
while ids.contains(id.as_str()) && count < 666 {
71+
count += 1;
72+
id = format!("{prefix}_{count}");
73+
}
74+
}
75+
76+
ids.insert(Cow::Owned(id.clone()));
4877
(el.id(), id)
4978
})
5079
.collect::<Vec<_>>();

Diff for: crates/rari-doc/src/html/rewriter.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub fn post_process_html<T: PageLike>(
5050
let mut element_content_handlers = vec![
5151
element!("*[id]", |el| {
5252
if let Some(id) = el.get_attribute("id") {
53-
if !ids.contains(id.as_str()) {
53+
if ids.contains(id.as_str()) {
5454
let (prefix, mut count) = if let Some((prefix, counter)) = id.rsplit_once('_') {
5555
if counter.chars().all(|c| c.is_ascii_digit()) {
5656
let count = counter.parse::<i64>().unwrap_or_default() + 1;
@@ -62,10 +62,15 @@ pub fn post_process_html<T: PageLike>(
6262
(id.as_str(), 2)
6363
};
6464
let mut id = format!("{prefix}_{count}");
65-
while !ids.insert(id) && count < 666 {
65+
while ids.contains(&id) && count < 666 {
6666
count += 1;
6767
id = format!("{prefix}_{count}");
6868
}
69+
70+
if !ids.contains(&id) && count < 666 {
71+
el.set_attribute("id", &id)?;
72+
ids.insert(id);
73+
}
6974
} else {
7075
ids.insert(id);
7176
}

Diff for: crates/rari-doc/src/html/sidebar.rs

+38-16
Original file line numberDiff line numberDiff line change
@@ -126,18 +126,41 @@ pub fn build_sidebars(doc: &Doc) -> Result<Option<String>, DocError> {
126126

127127
#[derive(Serialize, Deserialize, Default, Debug)]
128128
#[serde(transparent)]
129+
pub struct SidebarL10n {
130+
l10n: HashMap<Locale, HashMap<String, String>>,
131+
}
132+
133+
impl SidebarL10n {
134+
pub fn lookup<'a, 'b: 'a>(&'b self, key: &'a str, locale: Locale) -> &'a str {
135+
let s = self.l10n.get(&locale).and_then(|l| l.get(key));
136+
if locale == Default::default() || s.is_some() {
137+
return s.map(|s| s.as_str()).unwrap_or(key);
138+
}
139+
self.l10n
140+
.get(&Default::default())
141+
.and_then(|l| l.get(key))
142+
.map(|s| s.as_str())
143+
.unwrap_or(key)
144+
}
145+
}
146+
147+
#[derive(Serialize, Deserialize, Default, Debug)]
129148
pub struct Sidebar {
130-
pub entries: Vec<SidebarEntry>,
149+
pub sidebar: Vec<SidebarEntry>,
150+
#[serde(default)]
151+
pub l10n: SidebarL10n,
131152
}
132153

133-
#[derive(Debug)]
154+
#[derive(Debug, Default)]
134155
pub struct MetaSidebar {
135156
pub entries: Vec<SidebarMetaEntry>,
157+
pub l10n: SidebarL10n,
136158
}
137159
impl From<Sidebar> for MetaSidebar {
138160
fn from(value: Sidebar) -> Self {
139161
MetaSidebar {
140-
entries: value.entries.into_iter().map(Into::into).collect(),
162+
entries: value.sidebar.into_iter().map(Into::into).collect(),
163+
l10n: value.l10n,
141164
}
142165
}
143166
}
@@ -147,7 +170,7 @@ impl MetaSidebar {
147170
let mut out = String::new();
148171
out.push_str("<ol>");
149172
for entry in &self.entries {
150-
entry.render(&mut out, locale)?;
173+
entry.render(&mut out, locale, &self.l10n)?;
151174
}
152175
out.push_str("</ol>");
153176
Ok(out)
@@ -342,7 +365,12 @@ impl From<SidebarEntry> for SidebarMetaEntry {
342365
}
343366

344367
impl SidebarMetaEntry {
345-
pub fn render(&self, out: &mut String, locale: Locale) -> Result<(), DocError> {
368+
pub fn render(
369+
&self,
370+
out: &mut String,
371+
locale: Locale,
372+
l10n: &SidebarL10n,
373+
) -> Result<(), DocError> {
346374
out.push_str("<li");
347375
if self.section {
348376
out.push_str(" class=\"section\"");
@@ -363,21 +391,15 @@ impl SidebarMetaEntry {
363391
link: Some(link),
364392
title,
365393
} => {
366-
render_link_via_page(
367-
out,
368-
link,
369-
Some(locale),
370-
title.as_deref(),
371-
self.code,
372-
None,
373-
true,
374-
)?;
394+
let title = title.as_ref().map(|t| l10n.lookup(t.as_str(), locale));
395+
render_link_via_page(out, link, Some(locale), title, self.code, None, true)?;
375396
}
376397
SidebarMetaEntryContent::Link { link: None, title } => {
398+
let title = title.as_ref().map(|t| l10n.lookup(t.as_str(), locale));
377399
if self.code {
378400
out.push_str("<code>");
379401
}
380-
out.push_str(title.as_deref().unwrap_or_default());
402+
out.push_str(title.unwrap_or_default());
381403
if self.code {
382404
out.push_str("</code>");
383405
}
@@ -406,7 +428,7 @@ impl SidebarMetaEntry {
406428
match &self.children {
407429
MetaChildren::Children(children) => {
408430
for child in children {
409-
child.render(out, locale)?;
431+
child.render(out, locale, l10n)?;
410432
}
411433
}
412434
MetaChildren::ListSubPages(url, page_types) => {

0 commit comments

Comments
 (0)