diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 72010ad2..23f90c7a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -116,7 +116,7 @@ If possible, do your best to avoid breaking older browser releases. Any change to the HTML or styling is encouraged to manually check on as many browsers and platforms that you can. Unfortunately at this time we don't have any automated UI or browser testing, so your assistance in testing is appreciated. -## Updating higlight.js +## Updating highlight.js The following are instructions for updating [highlight.js](https://highlightjs.org/). diff --git a/Cargo.lock b/Cargo.lock index 10fae527..90b8073f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -809,10 +809,10 @@ dependencies = [ "futures-util", "gitignore", "handlebars", - "lazy_static", "log", "memchr", "notify", + "once_cell", "opener", "predicates", "pretty_assertions", @@ -997,6 +997,12 @@ dependencies = [ "libc", ] +[[package]] +name = "once_cell" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" + [[package]] name = "opaque-debug" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index b1c095d3..5b564ec9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,9 +20,9 @@ anyhow = "1.0.28" chrono = "0.4" clap = { version = "3.0", features = ["cargo"] } clap_complete = "3.0" +once_cell = "1" env_logger = "0.9.0" handlebars = "4.0" -lazy_static = "1.0" log = "0.4" memchr = "2.0" opener = "0.5" @@ -65,3 +65,7 @@ search = ["elasticlunr-rs", "ammonia"] [[bin]] doc = false name = "mdbook" + +[[example]] +name = "nop-preprocessor" +test = true diff --git a/examples/nop-preprocessor.rs b/examples/nop-preprocessor.rs index ace40093..a5e47daa 100644 --- a/examples/nop-preprocessor.rs +++ b/examples/nop-preprocessor.rs @@ -101,4 +101,58 @@ mod nop_lib { renderer != "not-supported" } } + + #[cfg(test)] + mod test { + use super::*; + + #[test] + fn nop_preprocessor_run() { + let input_json = r##"[ + { + "root": "/path/to/book", + "config": { + "book": { + "authors": ["AUTHOR"], + "language": "en", + "multilingual": false, + "src": "src", + "title": "TITLE" + }, + "preprocessor": { + "nop": {} + } + }, + "renderer": "html", + "mdbook_version": "0.4.21" + }, + { + "sections": [ + { + "Chapter": { + "name": "Chapter 1", + "content": "# Chapter 1\n", + "number": [1], + "sub_items": [], + "path": "chapter_1.md", + "source_path": "chapter_1.md", + "parent_names": [] + } + } + ], + "__non_exhaustive": null + } + ]"##; + let input_json = input_json.as_bytes(); + + let (ctx, book) = mdbook::preprocess::CmdPreprocessor::parse_input(input_json).unwrap(); + let expected_book = book.clone(); + let result = Nop::new().run(&ctx, book); + assert!(result.is_ok()); + + // The nop-preprocessor should not have made any changes to the book content. + let actual_book = result.unwrap(); + assert_eq!(actual_book, expected_book); + } + } } diff --git a/src/book/summary.rs b/src/book/summary.rs index 02f67c6f..b2784ce5 100644 --- a/src/book/summary.rs +++ b/src/book/summary.rs @@ -454,7 +454,7 @@ impl<'a> SummaryParser<'a> { items.push(item); } Some(Event::Start(Tag::List(..))) => { - // Skip this tag after comment bacause it is not nested. + // Skip this tag after comment because it is not nested. if items.is_empty() { continue; } diff --git a/src/cmd/serve.rs b/src/cmd/serve.rs index bafbfd52..00eaa46b 100644 --- a/src/cmd/serve.rs +++ b/src/cmd/serve.rs @@ -89,8 +89,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> { let input_404 = book .config .get("output.html.input-404") - .map(toml::Value::as_str) - .and_then(std::convert::identity) // flatten + .and_then(toml::Value::as_str) .map(ToString::to_string); let file_404 = get_404_output_file(&input_404); diff --git a/src/preprocess/index.rs b/src/preprocess/index.rs index d8a4e375..004b7eda 100644 --- a/src/preprocess/index.rs +++ b/src/preprocess/index.rs @@ -4,8 +4,8 @@ use std::path::Path; use super::{Preprocessor, PreprocessorContext}; use crate::book::{Book, BookItem}; use crate::errors::*; -use lazy_static::lazy_static; use log::warn; +use once_cell::sync::Lazy; /// A preprocessor for converting file name `README.md` to `index.md` since /// `README.md` is the de facto index file in markdown-based documentation. @@ -68,9 +68,8 @@ fn warn_readme_name_conflict>(readme_path: P, index_path: P) { } fn is_readme_file>(path: P) -> bool { - lazy_static! { - static ref RE: Regex = Regex::new(r"(?i)^readme$").unwrap(); - } + static RE: Lazy = Lazy::new(|| Regex::new(r"(?i)^readme$").unwrap()); + RE.is_match( path.as_ref() .file_stem() diff --git a/src/preprocess/links.rs b/src/preprocess/links.rs index 81575e86..c2c81f52 100644 --- a/src/preprocess/links.rs +++ b/src/preprocess/links.rs @@ -10,8 +10,8 @@ use std::path::{Path, PathBuf}; use super::{Preprocessor, PreprocessorContext}; use crate::book::{Book, BookItem}; -use lazy_static::lazy_static; use log::{error, warn}; +use once_cell::sync::Lazy; const ESCAPE_CHAR: char = '\\'; const MAX_LINK_NESTED_DEPTH: usize = 10; @@ -410,19 +410,20 @@ impl<'a> Iterator for LinkIter<'a> { fn find_links(contents: &str) -> LinkIter<'_> { // lazily compute following regex // r"\\\{\{#.*\}\}|\{\{#([a-zA-Z0-9]+)\s*([^}]+)\}\}")?; - lazy_static! { - static ref RE: Regex = Regex::new( + static RE: Lazy = Lazy::new(|| { + Regex::new( r"(?x) # insignificant whitespace mode - \\\{\{\#.*\}\} # match escaped link - | # or - \{\{\s* # link opening parens and whitespace - \#([a-zA-Z0-9_]+) # link type - \s+ # separating whitespace - ([^}]+) # link target path and space separated properties - \}\} # link closing parens" + \\\{\{\#.*\}\} # match escaped link + | # or + \{\{\s* # link opening parens and whitespace + \#([a-zA-Z0-9_]+) # link type + \s+ # separating whitespace + ([^}]+) # link target path and space separated properties + \}\} # link closing parens", ) - .unwrap(); - } + .unwrap() + }); + LinkIter(RE.captures_iter(contents)) } diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index 154564ce..f2b01063 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -14,8 +14,8 @@ use std::path::{Path, PathBuf}; use crate::utils::fs::get_404_output_file; use handlebars::Handlebars; -use lazy_static::lazy_static; use log::{debug, trace, warn}; +use once_cell::sync::Lazy; use regex::{Captures, Regex}; use serde_json::json; @@ -780,9 +780,8 @@ fn make_data( /// Goes through the rendered HTML, making sure all header tags have /// an anchor respectively so people can link to sections directly. fn build_header_links(html: &str) -> String { - lazy_static! { - static ref BUILD_HEADER_LINKS: Regex = Regex::new(r"(.*?)").unwrap(); - } + static BUILD_HEADER_LINKS: Lazy = + Lazy::new(|| Regex::new(r"(.*?)").unwrap()); let mut id_counter = HashMap::new(); @@ -823,10 +822,8 @@ fn insert_link_into_header( // ``` // This function replaces all commas by spaces in the code block classes fn fix_code_blocks(html: &str) -> String { - lazy_static! { - static ref FIX_CODE_BLOCKS: Regex = - Regex::new(r##"]+)class="([^"]+)"([^>]*)>"##).unwrap(); - } + static FIX_CODE_BLOCKS: Lazy = + Lazy::new(|| Regex::new(r##"]+)class="([^"]+)"([^>]*)>"##).unwrap()); FIX_CODE_BLOCKS .replace_all(html, |caps: &Captures<'_>| { @@ -849,10 +846,9 @@ fn add_playground_pre( playground_config: &Playground, edition: Option, ) -> String { - lazy_static! { - static ref ADD_PLAYGROUND_PRE: Regex = - Regex::new(r##"((?s)]?class="([^"]+)".*?>(.*?))"##).unwrap(); - } + static ADD_PLAYGROUND_PRE: Lazy = + Lazy::new(|| Regex::new(r##"((?s)]?class="([^"]+)".*?>(.*?))"##).unwrap()); + ADD_PLAYGROUND_PRE .replace_all(html, |caps: &Captures<'_>| { let text = &caps[1]; @@ -915,9 +911,7 @@ fn add_playground_pre( } fn hide_lines(content: &str) -> String { - lazy_static! { - static ref BORING_LINES_REGEX: Regex = Regex::new(r"^(\s*)#(.?)(.*)$").unwrap(); - } + static BORING_LINES_REGEX: Lazy = Lazy::new(|| Regex::new(r"^(\s*)#(.?)(.*)$").unwrap()); let mut result = String::with_capacity(content.len()); let mut lines = content.lines().peekable(); diff --git a/src/renderer/html_handlebars/helpers/navigation.rs b/src/renderer/html_handlebars/helpers/navigation.rs index f57e9ae9..b184c441 100644 --- a/src/renderer/html_handlebars/helpers/navigation.rs +++ b/src/renderer/html_handlebars/helpers/navigation.rs @@ -148,15 +148,12 @@ fn render( trace!("Render template"); - _h.template() - .ok_or_else(|| RenderError::new("Error with the handlebars template")) - .and_then(|t| { - let local_ctx = Context::wraps(&context)?; - let mut local_rc = rc.clone(); - t.render(r, &local_ctx, &mut local_rc, out) - })?; - - Ok(()) + let t = _h + .template() + .ok_or_else(|| RenderError::new("Error with the handlebars template"))?; + let local_ctx = Context::wraps(&context)?; + let mut local_rc = rc.clone(); + t.render(r, &local_ctx, &mut local_rc, out) } pub fn previous( diff --git a/src/renderer/html_handlebars/helpers/toc.rs b/src/renderer/html_handlebars/helpers/toc.rs index 0884d30a..e96e6ef6 100644 --- a/src/renderer/html_handlebars/helpers/toc.rs +++ b/src/renderer/html_handlebars/helpers/toc.rs @@ -117,35 +117,35 @@ impl HelperDef for RenderToc { } // Link - let path_exists = if let Some(path) = - item.get("path") - .and_then(|p| if p.is_empty() { None } else { Some(p) }) - { - out.write(" { + out.write("")?; + path_exists = true; } - - out.write(">")?; - true - } else { - out.write("
")?; - false - }; + _ => { + out.write("
")?; + path_exists = false; + } + } if !self.no_section_label { // Section does not necessarily exist diff --git a/src/renderer/html_handlebars/search.rs b/src/renderer/html_handlebars/search.rs index f88893a0..a9e2f5ca 100644 --- a/src/renderer/html_handlebars/search.rs +++ b/src/renderer/html_handlebars/search.rs @@ -3,6 +3,7 @@ use std::collections::{HashMap, HashSet}; use std::path::Path; use elasticlunr::{Index, IndexBuilder}; +use once_cell::sync::Lazy; use pulldown_cmark::*; use crate::book::{Book, BookItem}; @@ -10,7 +11,6 @@ use crate::config::Search; use crate::errors::*; use crate::theme::searcher; use crate::utils; -use lazy_static::lazy_static; use log::{debug, warn}; use serde::Serialize; @@ -267,21 +267,19 @@ fn write_to_json(index: Index, search_config: &Search, doc_urls: Vec) -> } fn clean_html(html: &str) -> String { - lazy_static! { - static ref AMMONIA: ammonia::Builder<'static> = { - let mut clean_content = HashSet::new(); - clean_content.insert("script"); - clean_content.insert("style"); - let mut builder = ammonia::Builder::new(); - builder - .tags(HashSet::new()) - .tag_attributes(HashMap::new()) - .generic_attributes(HashSet::new()) - .link_rel(None) - .allowed_classes(HashMap::new()) - .clean_content_tags(clean_content); - builder - }; - } + static AMMONIA: Lazy> = Lazy::new(|| { + let mut clean_content = HashSet::new(); + clean_content.insert("script"); + clean_content.insert("style"); + let mut builder = ammonia::Builder::new(); + builder + .tags(HashSet::new()) + .tag_attributes(HashMap::new()) + .generic_attributes(HashSet::new()) + .link_rel(None) + .allowed_classes(HashMap::new()) + .clean_content_tags(clean_content); + builder + }); AMMONIA.clean(html).to_string() } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 13bc07bf..4a3da661 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -4,8 +4,8 @@ pub mod fs; mod string; pub(crate) mod toml_ext; use crate::errors::Error; -use lazy_static::lazy_static; use log::error; +use once_cell::sync::Lazy; use pulldown_cmark::{html, CodeBlockKind, CowStr, Event, Options, Parser, Tag}; use regex::Regex; @@ -21,9 +21,7 @@ pub use self::string::{ /// Replaces multiple consecutive whitespace characters with a single space character. pub fn collapse_whitespace(text: &str) -> Cow<'_, str> { - lazy_static! { - static ref RE: Regex = Regex::new(r"\s\s+").unwrap(); - } + static RE: Lazy = Lazy::new(|| Regex::new(r"\s\s+").unwrap()); RE.replace_all(text, " ") } @@ -52,9 +50,7 @@ pub fn id_from_content(content: &str) -> String { let mut content = content.to_string(); // Skip any tags or html-encoded stuff - lazy_static! { - static ref HTML: Regex = Regex::new(r"(<.*?>)").unwrap(); - } + static HTML: Lazy = Lazy::new(|| Regex::new(r"(<.*?>)").unwrap()); content = HTML.replace_all(&content, "").into(); const REPL_SUB: &[&str] = &["<", ">", "&", "'", """]; for sub in REPL_SUB { @@ -97,10 +93,9 @@ pub fn unique_id_from_content(content: &str, id_counter: &mut HashMap(event: Event<'a>, path: Option<&Path>, abs_url: Option<&String>) -> Event<'a> { - lazy_static! { - static ref SCHEME_LINK: Regex = Regex::new(r"^[a-z][a-z0-9+.-]*:").unwrap(); - static ref MD_LINK: Regex = Regex::new(r"(?P.*)\.md(?P#.*)?").unwrap(); - } + static SCHEME_LINK: Lazy = Lazy::new(|| Regex::new(r"^[a-z][a-z0-9+.-]*:").unwrap()); + static MD_LINK: Lazy = + Lazy::new(|| Regex::new(r"(?P.*)\.md(?P#.*)?").unwrap()); fn fix<'a>(dest: CowStr<'a>, path: Option<&Path>, abs_url: Option<&String>) -> CowStr<'a> { if dest.starts_with('#') { @@ -160,10 +155,8 @@ fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>, abs_url: Option<&Stri // There are dozens of HTML tags/attributes that contain paths, so // feel free to add more tags if desired; these are the only ones I // care about right now. - lazy_static! { - static ref HTML_LINK: Regex = - Regex::new(r#"(<(?:a|img) [^>]*?(?:src|href)=")([^"]+?)""#).unwrap(); - } + static HTML_LINK: Lazy = + Lazy::new(|| Regex::new(r#"(<(?:a|img) [^>]*?(?:src|href)=")([^"]+?)""#).unwrap()); HTML_LINK .replace_all(&html, |caps: ®ex::Captures<'_>| { diff --git a/src/utils/string.rs b/src/utils/string.rs index bc854347..6dafe260 100644 --- a/src/utils/string.rs +++ b/src/utils/string.rs @@ -1,4 +1,4 @@ -use lazy_static::lazy_static; +use once_cell::sync::Lazy; use regex::Regex; use std::ops::Bound::{Excluded, Included, Unbounded}; use std::ops::RangeBounds; @@ -24,10 +24,10 @@ pub fn take_lines>(s: &str, range: R) -> String { } } -lazy_static! { - static ref ANCHOR_START: Regex = Regex::new(r"ANCHOR:\s*(?P[\w_-]+)").unwrap(); - static ref ANCHOR_END: Regex = Regex::new(r"ANCHOR_END:\s*(?P[\w_-]+)").unwrap(); -} +static ANCHOR_START: Lazy = + Lazy::new(|| Regex::new(r"ANCHOR:\s*(?P[\w_-]+)").unwrap()); +static ANCHOR_END: Lazy = + Lazy::new(|| Regex::new(r"ANCHOR_END:\s*(?P[\w_-]+)").unwrap()); /// Take anchored lines from a string. /// Lines containing anchor are ignored.