Skip to content

Commit 46abf2b

Browse files
committed
feat(templ): template before html conversion!
also fix dt link injection
1 parent 11feeb3 commit 46abf2b

File tree

9 files changed

+47
-155
lines changed

9 files changed

+47
-155
lines changed

Diff for: Cargo.lock

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

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

+6-8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::borrow::Cow;
2+
13
use rari_types::fm_types::PageType;
24
use rari_types::globals::{base_url, content_branch, git_history, popularities};
35
use rari_types::locale::Locale;
@@ -129,17 +131,13 @@ pub fn make_toc(sections: &[BuildSection], with_h3: bool) -> Vec<TocEntry> {
129131
}
130132

131133
pub fn build_content<T: PageLike>(doc: &T) -> Result<PageContent, DocError> {
132-
let html = render_md_to_html(
133-
doc.content(),
134-
doc.locale(),
135-
Some(&doc.full_path().display()),
136-
)?;
137134
let ks_rendered_doc = if let Some(rari_env) = &doc.rari_env() {
138-
render(rari_env, &html)?
135+
Cow::Owned(render(rari_env, doc.content())?)
139136
} else {
140-
html
137+
Cow::Borrowed(doc.content())
141138
};
142-
let post_processed_html = post_process_html(&ks_rendered_doc, doc, false)?;
139+
let html = render_md_to_html(&ks_rendered_doc, doc.locale())?;
140+
let post_processed_html = post_process_html(&html, doc, false)?;
143141
let fragment = Html::parse_fragment(&post_processed_html);
144142
let (sections, summary) = split_sections(&fragment).expect("DOOM");
145143
let toc = make_toc(&sections, matches!(doc.page_type(), PageType::Curriculum));

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

+2-24
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,20 @@
11
use std::collections::HashMap;
2-
use std::fmt::Display;
32
use std::fs::read_to_string;
43
use std::path::{Path, PathBuf};
54
use std::sync::Arc;
65

76
use rari_md::m2h;
87
use rari_types::fm_types::{FeatureStatus, PageType};
9-
use rari_types::globals::deny_warnings;
108
use rari_types::locale::Locale;
119
use rari_types::RariEnv;
1210
use serde::{Deserialize, Serialize};
1311
use serde_yaml::Value;
14-
use tracing::warn;
1512
use validator::Validate;
1613

1714
use super::page::{Page, PageCategory, PageLike, PageReader};
1815
use crate::cached_readers::{page_from_static_files, CACHED_PAGE_FILES};
1916
use crate::error::DocError;
2017
use crate::resolve::{build_url, url_to_path_buf};
21-
use crate::templ::parser::{decode_ks, encode_ks};
2218
use crate::utils::{locale_and_typ_from_path, root_for_locale, split_fm, t_or_vec};
2319

2420
/*
@@ -238,26 +234,8 @@ fn read_doc(path: impl Into<PathBuf>) -> Result<Doc, DocError> {
238234
})
239235
}
240236

241-
pub fn render_md_to_html(
242-
input: &str,
243-
locale: Locale,
244-
path: Option<&impl Display>,
245-
) -> Result<String, DocError> {
246-
let (encoded, before) = encode_ks(input)?;
247-
let encoded_html = m2h(&encoded, locale)?;
248-
let (html, after) = decode_ks(&encoded_html)?;
249-
if before != after {
250-
if deny_warnings() {
251-
return Err(DocError::InvalidTempl(
252-
path.map(|s| s.to_string()).unwrap_or_default(),
253-
));
254-
}
255-
warn!(
256-
"invalid templ: {}",
257-
path.map(|s| s.to_string()).unwrap_or_default()
258-
);
259-
}
260-
237+
pub fn render_md_to_html(input: &str, locale: Locale) -> Result<String, DocError> {
238+
let html = m2h(input, locale)?;
261239
Ok(html)
262240
}
263241

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ pub fn post_process_html<T: PageLike>(
8787

8888
Ok(())
8989
}),
90-
element!("dt:first-child:not(a)", |el| {
90+
element!("dt[data-add-link]", |el| {
91+
el.remove_attribute("data-add-link");
9192
if let Some(id) = el.get_attribute("id") {
9293
el.prepend(&format!("<a href=\"#{id}\">"), ContentType::Html);
9394
el.append("</a>", ContentType::Html);

Diff for: crates/rari-doc/src/templ/parser.rs

-117
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
use std::fmt::Write;
2-
3-
use base64::engine::general_purpose::STANDARD;
4-
use base64::Engine;
51
use pest::iterators::Pair;
62
use pest::Parser;
73
use rari_types::{Arg, Quotes};
@@ -115,92 +111,6 @@ pub(crate) fn parse(input: &str) -> Result<Vec<Token>, DocError> {
115111
Ok(tokens)
116112
}
117113

118-
fn encode_macro(s: &str, out: &mut String) -> Result<(), DocError> {
119-
Ok(write!(
120-
out,
121-
"!::::{}::::!",
122-
STANDARD.encode(&s[2..(s.len() - 2)])
123-
)?)
124-
}
125-
126-
fn decode_macro(s: &str, out: &mut String) -> Result<(), DocError> {
127-
Ok(write!(
128-
out,
129-
"{{{{{}}}}}",
130-
std::str::from_utf8(&STANDARD.decode(s)?)?
131-
)?)
132-
}
133-
134-
pub(crate) fn encode_ks(input: &str) -> Result<(String, i32), DocError> {
135-
let tokens = parse(input)?;
136-
let mut encoded = String::with_capacity(input.len());
137-
let mut num_macros = 0;
138-
for token in tokens {
139-
match token {
140-
Token::Text(t) => {
141-
encoded.push_str(&input[t.start..t.end]);
142-
}
143-
Token::Macro(t) => {
144-
num_macros += 1;
145-
encode_macro(&input[t.start..t.end], &mut encoded)?
146-
}
147-
}
148-
}
149-
Ok((encoded, num_macros))
150-
}
151-
152-
fn _strip_escape_residues(s: &str) -> &str {
153-
let s = s.strip_prefix("&gt;").or(s.strip_prefix('>')).unwrap_or(s);
154-
let s = s
155-
.strip_suffix("!&lt;")
156-
.or(s.strip_suffix("!<"))
157-
.unwrap_or(s);
158-
s
159-
}
160-
161-
pub(crate) fn decode_ks(input: &str) -> Result<(String, i32), DocError> {
162-
let mut decoded = String::with_capacity(input.len());
163-
let mut num_macros = 0;
164-
// We're splitting only by `!-- ks___` because e.g. ks in a <pre> will be escaped.
165-
if !input.contains("!::::") {
166-
return Ok((input.to_string(), 0));
167-
}
168-
let mut frags = vec![];
169-
for frag in input.split("!::::") {
170-
let has_ks = frag.contains("::::!");
171-
for (i, sub_frag) in frag.splitn(2, "::::!").enumerate() {
172-
if i == 0 && has_ks {
173-
num_macros += 1;
174-
frags.push(sub_frag);
175-
//decode_macro(sub_frag, &mut decoded)?;
176-
} else {
177-
//decoded.push_str(strip_escape_residues(sub_frag))
178-
frags.push(sub_frag)
179-
}
180-
}
181-
}
182-
for i in 0..frags.len() {
183-
if i % 2 == 1
184-
&& i < frags.len() + 1
185-
&& frags[i - 1].ends_with("<p>")
186-
&& frags[i + 1].starts_with("</p>")
187-
{
188-
frags[i - 1] = frags[i - 1].strip_suffix("<p>").unwrap();
189-
frags[i + 1] = frags[i + 1].strip_prefix("</p>").unwrap();
190-
}
191-
}
192-
193-
for (i, frag) in frags.iter().enumerate() {
194-
if i % 2 == 1 {
195-
decode_macro(frag, &mut decoded)?;
196-
} else {
197-
decoded.push_str(frag)
198-
}
199-
}
200-
201-
Ok((decoded, num_macros))
202-
}
203-
204114
#[cfg(test)]
205115
mod test {
206116
use super::*;
@@ -236,31 +146,4 @@ mod test {
236146
let p = parse(r#"foo {{foo(0.1)}} bar"#);
237147
println!("{:#?}", p);
238148
}
239-
240-
#[test]
241-
fn ks_escape() -> Result<(), DocError> {
242-
let ks = r#"Foo {{jsxref("Array",,1,true) }}bar {{ foo }}"#;
243-
let enc = encode_ks(ks)?;
244-
let dec = decode_ks(&enc.0)?;
245-
assert_eq!(ks, dec.0);
246-
Ok(())
247-
}
248-
249-
#[test]
250-
fn ks_escape_2() -> Result<(), DocError> {
251-
let ks = r#"<{{jsxref("Array",,1,true) }}>bar {{ foo }}"#;
252-
let enc = encode_ks(ks)?;
253-
let dec = decode_ks(&enc.0)?;
254-
assert_eq!(ks, dec.0);
255-
Ok(())
256-
}
257-
258-
#[test]
259-
fn ks_escape_3() -> Result<(), DocError> {
260-
let ks = r#"{{foo}}{{foo-bar}}"#;
261-
let enc = encode_ks(ks)?;
262-
let dec = decode_ks(&enc.0)?;
263-
assert_eq!(ks, dec.0);
264-
Ok(())
265-
}
266149
}

Diff for: crates/rari-md/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ once_cell = "1"
1313
thiserror = "1"
1414
comrak = { version = "0.24", default-features = false, features = ["syntect"] }
1515
rari-types = { path = "../rari-types" }
16+
itertools = "0.13"

Diff for: crates/rari-md/src/bq.rs

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ pub(crate) fn is_callout<'a>(block_quote: &'a AstNode<'a>, locale: Locale) -> Op
9797
}
9898

9999
if text.starts_with(NoteCard::Warning.prefix_for_locale(locale)) {
100+
grand_child.detach();
100101
return Some(NoteCard::Warning);
101102
}
102103
if text.starts_with(NoteCard::Note.prefix_for_locale(locale)) {

Diff for: crates/rari-md/src/html.rs

+33-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use comrak::nodes::{
1616
TableAlignment,
1717
};
1818
use comrak::{ComrakOptions, ComrakPlugins, Options};
19+
use itertools::Itertools;
1920
use once_cell::sync::Lazy;
2021
use rari_types::locale::Locale;
2122

@@ -472,6 +473,28 @@ impl<'o> HtmlFormatter<'o> {
472473
}
473474
}
474475

476+
fn next_is_link<'a>(node: &'a AstNode<'a>) -> bool {
477+
if let Some(child) = node.children().next() {
478+
if matches!(child.data.borrow().value, NodeValue::Link(_)) {
479+
return true;
480+
}
481+
if let NodeValue::HtmlInline(res) = &child.data.borrow().value {
482+
return res.starts_with("<a ");
483+
}
484+
if matches!(child.data.borrow().value, NodeValue::Paragraph) {
485+
if let Some(child) = child.children().next() {
486+
if matches!(child.data.borrow().value, NodeValue::Link(_)) {
487+
return true;
488+
}
489+
if let NodeValue::HtmlInline(res) = &child.data.borrow().value {
490+
return res.starts_with("<a ");
491+
}
492+
}
493+
}
494+
}
495+
false
496+
}
497+
475498
fn format_node<'a>(
476499
&mut self,
477500
node: &'a AstNode<'a>,
@@ -568,6 +591,9 @@ impl<'o> HtmlFormatter<'o> {
568591
let mut id = String::from_utf8(text_content).unwrap();
569592
id = self.anchorizer.anchorize(id);
570593
write!(self.output, "<dt id=\"{}\"", id)?;
594+
if !Self::next_is_link(node) {
595+
write!(self.output, " data-add-link")?;
596+
}
571597
self.render_sourcepos(node)?;
572598
self.output.write_all(b">")?;
573599
} else {
@@ -682,10 +708,13 @@ impl<'o> HtmlFormatter<'o> {
682708
pre_attributes.extend(code_attributes);
683709
let with_code = if let Some(cls) = pre_attributes.get_mut("class") {
684710
if !ncb.info.is_empty() {
685-
*cls = format!(
686-
"brush: {} notranslate",
687-
ncb.info.strip_suffix("-nolint").unwrap_or(&ncb.info)
688-
);
711+
let langs = ncb
712+
.info
713+
.split_ascii_whitespace()
714+
.map(|s| s.strip_suffix("-nolint").unwrap_or(s))
715+
.join(" ");
716+
717+
*cls = format!("brush: {langs} notranslate",);
689718
&ncb.info != "plain"
690719
} else {
691720
*cls = "notranslate".to_string();

Diff for: crates/rari-md/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ where
3535
pub fn m2h(input: &str, locale: Locale) -> Result<String, MarkdownError> {
3636
let arena = Arena::new();
3737
let mut options = ComrakOptions::default();
38-
options.extension.tagfilter = true;
38+
options.extension.tagfilter = false;
3939
options.render.unsafe_ = true;
4040
options.extension.table = true;
4141
options.extension.autolink = true;

0 commit comments

Comments
 (0)