add absolute links support

This commit is contained in:
joaofreires 2022-05-07 16:22:24 -03:00
parent 8cdb8d0367
commit c1a0af3cf6
No known key found for this signature in database
GPG Key ID: C21AD65E2F4A6C65
3 changed files with 42 additions and 14 deletions

View File

@ -516,6 +516,8 @@ pub struct HtmlConfig {
pub input_404: Option<String>, pub input_404: Option<String>,
/// Absolute url to site, used to emit correct paths for the 404 page, which might be accessed in a deeply nested directory /// Absolute url to site, used to emit correct paths for the 404 page, which might be accessed in a deeply nested directory
pub site_url: Option<String>, pub site_url: Option<String>,
/// Prepend the `site_url` in links with absolute path.
pub use_site_url_as_root: bool,
/// The DNS subdomain or apex domain at which your book will be hosted. This /// The DNS subdomain or apex domain at which your book will be hosted. This
/// string will be written to a file named CNAME in the root of your site, /// string will be written to a file named CNAME in the root of your site,
/// as required by GitHub Pages (see [*Managing a custom domain for your /// as required by GitHub Pages (see [*Managing a custom domain for your
@ -562,6 +564,7 @@ impl Default for HtmlConfig {
edit_url_template: None, edit_url_template: None,
input_404: None, input_404: None,
site_url: None, site_url: None,
use_site_url_as_root: false,
cname: None, cname: None,
live_reload_endpoint: None, live_reload_endpoint: None,
redirect: HashMap::new(), redirect: HashMap::new(),

View File

@ -55,10 +55,23 @@ impl HtmlHandlebars {
} }
let content = ch.content.clone(); let content = ch.content.clone();
let content = utils::render_markdown(&content, ctx.html_config.curly_quotes); let content = if ctx.html_config.use_site_url_as_root {
utils::render_markdown_with_path(
&content,
ctx.html_config.curly_quotes,
None,
ctx.html_config.site_url.as_ref(),
)
} else {
utils::render_markdown(&content, ctx.html_config.curly_quotes)
};
let fixed_content = let fixed_content = utils::render_markdown_with_path(
utils::render_markdown_with_path(&ch.content, ctx.html_config.curly_quotes, Some(path)); &ch.content,
ctx.html_config.curly_quotes,
Some(path),
None,
);
if !ctx.is_index && ctx.html_config.print.page_break { if !ctx.is_index && ctx.html_config.print.page_break {
// Add page break between chapters // Add page break between chapters
// See https://developer.mozilla.org/en-US/docs/Web/CSS/break-before and https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-before // See https://developer.mozilla.org/en-US/docs/Web/CSS/break-before and https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-before

View File

@ -96,13 +96,13 @@ pub fn unique_id_from_content(content: &str, id_counter: &mut HashMap<String, us
/// page go to the original location. Normal page rendering sets `path` to /// page go to the original location. Normal page rendering sets `path` to
/// None. Ideally, print page links would link to anchors on the print page, /// None. Ideally, print page links would link to anchors on the print page,
/// but that is very difficult. /// but that is very difficult.
fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>) -> Event<'a> { fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>, abs_url: Option<&String>) -> Event<'a> {
lazy_static! { lazy_static! {
static ref SCHEME_LINK: Regex = Regex::new(r"^[a-z][a-z0-9+.-]*:").unwrap(); static ref SCHEME_LINK: Regex = Regex::new(r"^[a-z][a-z0-9+.-]*:").unwrap();
static ref MD_LINK: Regex = Regex::new(r"(?P<link>.*)\.md(?P<anchor>#.*)?").unwrap(); static ref MD_LINK: Regex = Regex::new(r"(?P<link>.*)\.md(?P<anchor>#.*)?").unwrap();
} }
fn fix<'a>(dest: CowStr<'a>, path: Option<&Path>) -> CowStr<'a> { fn fix<'a>(dest: CowStr<'a>, path: Option<&Path>, abs_url: Option<&String>) -> CowStr<'a> {
if dest.starts_with('#') { if dest.starts_with('#') {
// Fragment-only link. // Fragment-only link.
if let Some(path) = path { if let Some(path) = path {
@ -139,12 +139,19 @@ fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>) -> Event<'a> {
} else { } else {
fixed_link.push_str(&dest); fixed_link.push_str(&dest);
}; };
return CowStr::from(fixed_link); if fixed_link.starts_with('/') {
fixed_link = match abs_url {
Some(abs_url) => format!("{}{}", abs_url.trim_end_matches('/'), &fixed_link),
None => fixed_link,
}
.into();
}
return CowStr::from(format!("{}", fixed_link));
} }
dest dest
} }
fn fix_html<'a>(html: CowStr<'a>, path: Option<&Path>) -> CowStr<'a> { fn fix_html<'a>(html: CowStr<'a>, path: Option<&Path>, abs_url: Option<&String>) -> CowStr<'a> {
// This is a terrible hack, but should be reasonably reliable. Nobody // This is a terrible hack, but should be reasonably reliable. Nobody
// should ever parse a tag with a regex. However, there isn't anything // should ever parse a tag with a regex. However, there isn't anything
// in Rust that I know of that is suitable for handling partial html // in Rust that I know of that is suitable for handling partial html
@ -160,7 +167,7 @@ fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>) -> Event<'a> {
HTML_LINK HTML_LINK
.replace_all(&html, |caps: &regex::Captures<'_>| { .replace_all(&html, |caps: &regex::Captures<'_>| {
let fixed = fix(caps[2].into(), path); let fixed = fix(caps[2].into(), path, abs_url);
format!("{}{}\"", &caps[1], fixed) format!("{}{}\"", &caps[1], fixed)
}) })
.into_owned() .into_owned()
@ -169,19 +176,19 @@ fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>) -> Event<'a> {
match event { match event {
Event::Start(Tag::Link(link_type, dest, title)) => { Event::Start(Tag::Link(link_type, dest, title)) => {
Event::Start(Tag::Link(link_type, fix(dest, path), title)) Event::Start(Tag::Link(link_type, fix(dest, path, abs_url), title))
} }
Event::Start(Tag::Image(link_type, dest, title)) => { Event::Start(Tag::Image(link_type, dest, title)) => {
Event::Start(Tag::Image(link_type, fix(dest, path), title)) Event::Start(Tag::Image(link_type, fix(dest, path, abs_url), title))
} }
Event::Html(html) => Event::Html(fix_html(html, path)), Event::Html(html) => Event::Html(fix_html(html, path, abs_url)),
_ => event, _ => event,
} }
} }
/// Wrapper around the pulldown-cmark parser for rendering markdown to HTML. /// Wrapper around the pulldown-cmark parser for rendering markdown to HTML.
pub fn render_markdown(text: &str, curly_quotes: bool) -> String { pub fn render_markdown(text: &str, curly_quotes: bool) -> String {
render_markdown_with_path(text, curly_quotes, None) render_markdown_with_path(text, curly_quotes, None, None)
} }
pub fn new_cmark_parser(text: &str, curly_quotes: bool) -> Parser<'_, '_> { pub fn new_cmark_parser(text: &str, curly_quotes: bool) -> Parser<'_, '_> {
@ -196,12 +203,17 @@ pub fn new_cmark_parser(text: &str, curly_quotes: bool) -> Parser<'_, '_> {
Parser::new_ext(text, opts) Parser::new_ext(text, opts)
} }
pub fn render_markdown_with_path(text: &str, curly_quotes: bool, path: Option<&Path>) -> String { pub fn render_markdown_with_path(
text: &str,
curly_quotes: bool,
path: Option<&Path>,
abs_url: Option<&String>,
) -> String {
let mut s = String::with_capacity(text.len() * 3 / 2); let mut s = String::with_capacity(text.len() * 3 / 2);
let p = new_cmark_parser(text, curly_quotes); let p = new_cmark_parser(text, curly_quotes);
let events = p let events = p
.map(clean_codeblock_headers) .map(clean_codeblock_headers)
.map(|event| adjust_links(event, path)) .map(|event| adjust_links(event, path, abs_url))
.flat_map(|event| { .flat_map(|event| {
let (a, b) = wrap_tables(event); let (a, b) = wrap_tables(event);
a.into_iter().chain(b) a.into_iter().chain(b)