Remove handlebars templating and instead use new html_node() function some string replacement

This commit is contained in:
Chadtech 2021-04-15 02:19:20 -04:00
parent f16d1619ea
commit 8036220b19
5 changed files with 199 additions and 276 deletions

31
Cargo.lock generated
View file

@ -991,12 +991,6 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "fs_extra"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
[[package]] [[package]]
name = "fuchsia-cprng" name = "fuchsia-cprng"
version = "0.1.1" version = "0.1.1"
@ -1401,20 +1395,6 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3"
[[package]]
name = "handlebars"
version = "3.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb0867bbc5a3da37a753e78021d5fcf8a4db00e18dd2dd90fd36e24190e162d"
dependencies = [
"log",
"pest",
"pest_derive",
"quick-error",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.9.1" version = "0.9.1"
@ -2467,12 +2447,6 @@ dependencies = [
"unicase", "unicase",
] ]
[[package]]
name = "quick-error"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda"
[[package]] [[package]]
name = "quickcheck" name = "quickcheck"
version = "0.8.5" version = "0.8.5"
@ -3005,8 +2979,6 @@ name = "roc_docs"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"fs_extra",
"handlebars",
"maplit", "maplit",
"pretty_assertions 0.5.1", "pretty_assertions 0.5.1",
"pulldown-cmark", "pulldown-cmark",
@ -3014,9 +2986,6 @@ dependencies = [
"roc_can", "roc_can",
"roc_collections", "roc_collections",
"roc_load", "roc_load",
"serde",
"serde_derive",
"serde_json",
] ]
[[package]] [[package]]

View file

@ -8,11 +8,6 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
handlebars = "3.4.0"
serde = "1.0.0"
serde_json = "1.0.39"
serde_derive = "1.0.75"
fs_extra = "1.2.0"
pulldown-cmark = { version = "0.8", default-features = false } pulldown-cmark = { version = "0.8", default-features = false }
roc_load = { path = "../compiler/load" } roc_load = { path = "../compiler/load" }
roc_builtins = { path = "../compiler/builtins" } roc_builtins = { path = "../compiler/builtins" }

View file

@ -1,13 +1,8 @@
extern crate fs_extra;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate pulldown_cmark; extern crate pulldown_cmark;
extern crate serde_json;
use roc_builtins::std::StdLib; use roc_builtins::std::StdLib;
use roc_can::builtins::builtin_defs_map; use roc_can::builtins::builtin_defs_map;
use roc_load::docs::DocTypeAnnotation;
use roc_load::docs::ModuleDocumentation; use roc_load::docs::ModuleDocumentation;
use roc_load::docs::{DocTypeAnnotation, Documentation};
use roc_load::file::LoadingProblem; use roc_load::file::LoadingProblem;
use std::fs; use std::fs;
@ -16,37 +11,6 @@ use bumpalo::Bump;
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
#[derive(Serialize)]
pub struct Template {
pub package_name: String,
pub package_version: String,
pub module_name: String,
pub module_docs: String,
pub module_entries: Vec<ModuleEntry>,
pub module_links: Vec<TemplateLink>,
}
#[derive(Serialize, Clone, Debug, PartialEq)]
pub struct ModuleEntry {
pub name: String,
pub type_vars: Vec<String>,
pub type_annotation: String,
pub docs: String,
}
#[derive(Serialize)]
pub struct TemplateLink {
pub name: String,
pub href: String,
pub classes: String,
pub entries: Vec<TemplateLinkEntry>,
}
#[derive(Serialize)]
pub struct TemplateLinkEntry {
name: String,
}
pub fn generate(filenames: Vec<PathBuf>, std_lib: StdLib, build_dir: &Path) { pub fn generate(filenames: Vec<PathBuf>, std_lib: StdLib, build_dir: &Path) {
let files_docs = files_to_documentations(filenames, std_lib); let files_docs = files_to_documentations(filenames, std_lib);
// //
@ -81,28 +45,213 @@ pub fn generate(filenames: Vec<PathBuf>, std_lib: StdLib, build_dir: &Path) {
) )
.expect("TODO gracefully handle failing to make the favicon"); .expect("TODO gracefully handle failing to make the favicon");
// Register handlebars template let template_html = include_str!("./static/index.html");
let mut handlebars = handlebars::Handlebars::new();
handlebars
.register_template_file("page", "./docs/src/templates/page.hbs")
.expect("TODO gracefully handle registering template failing");
// Write each package's module docs html file // Write each package's module docs html file
for module in &package.modules { for module in &package.modules {
let template = documentation_to_template_data(&package, module); let mut filename = String::new();
filename.push_str(module.name.as_str());
filename.push_str(".html");
let handlebars_data = handlebars::to_json(&template); let rendered_module = template_html
let filepath = build_dir.join(format!("{}.html", module.name)); .replace(
let mut output_file = "<!-- Module links -->",
fs::File::create(filepath).expect("TODO gracefully handle creating file failing"); render_module_links(&package.modules).as_str(),
handlebars )
.render_to_write("page", &handlebars_data, &mut output_file) .replace(
.expect("TODO gracefully handle writing file failing"); "<!-- Package Name and Version -->",
render_name_and_version(package.name.as_str(), package.version.as_str()).as_str(),
)
.replace(
"<!-- Module Docs -->",
render_main_content(&module).as_str(),
);
fs::write(build_dir.join(filename), rendered_module)
.expect("TODO gracefully handle failing to write html");
} }
println!("🎉 Docs generated in {}", build_dir.display()); println!("🎉 Docs generated in {}", build_dir.display());
} }
fn render_main_content(module: &ModuleDocumentation) -> String {
let mut buf = String::new();
buf.push_str(
html_node(
"h2",
vec![("class", "module-name")],
html_node("a", vec![("href", "/#")], module.name.as_str()).as_str(),
)
.as_str(),
);
buf.push_str(markdown_to_html(module.docs.clone()).as_str());
for entry in &module.entries {
let mut href = String::new();
href.push('#');
href.push_str(entry.name.as_str());
let name = entry.name.as_str();
let mut content = String::new();
content.push_str(html_node("a", vec![("href", href.as_str())], name).as_str());
for type_var in &entry.type_vars {
content.push(' ');
content.push_str(type_var.as_str());
}
if let Some(type_ann) = &entry.type_annotation {
content.push_str(" : ");
type_annotation_to_html(0, &mut content, &type_ann);
}
buf.push_str(html_node("h3", vec![("id", name)], content.as_str()).as_str());
if let Some(docs) = &entry.docs {
buf.push_str(docs.as_str());
}
}
buf
}
fn html_node(tag_name: &str, attrs: Vec<(&str, &str)>, content: &str) -> String {
let mut buf = String::new();
buf.push('<');
buf.push_str(tag_name);
for (key, value) in &attrs {
buf.push(' ');
buf.push_str(key);
buf.push_str("=\"");
buf.push_str(value);
buf.push('"');
}
if !&attrs.is_empty() {
buf.push(' ');
}
buf.push('>');
buf.push_str(content);
buf.push_str("</");
buf.push_str(tag_name);
buf.push('>');
buf
}
fn render_name_and_version(name: &str, version: &str) -> String {
let mut buf = String::new();
let mut href = String::new();
href.push('/');
href.push_str(name);
buf.push_str(
html_node(
"h1",
vec![("class", "pkg-full-name")],
html_node("a", vec![("href", href.as_str())], name).as_str(),
)
.as_str(),
);
let mut verions_href = String::new();
verions_href.push('/');
verions_href.push_str(name);
verions_href.push('/');
verions_href.push_str(version);
buf.push_str(
html_node(
"a",
vec![("class", "version"), ("href", verions_href.as_str())],
version,
)
.as_str(),
);
buf
}
fn render_module_links(modules: &[ModuleDocumentation]) -> String {
let mut buf = String::new();
for module in modules {
let mut sidebar_entry_content = String::new();
let name = module.name.as_str();
let href = {
let mut href_buf = String::new();
href_buf.push_str(name);
href_buf.push_str(".html");
href_buf
};
sidebar_entry_content.push_str(
html_node(
"a",
vec![("class", "sidebar-module-link"), ("href", href.as_str())],
name,
)
.as_str(),
);
let entries = {
let mut entries_buf = String::new();
for entry in &module.entries {
let mut entry_href = String::new();
entry_href.push_str(href.as_str());
entry_href.push('#');
entry_href.push_str(entry.name.as_str());
entries_buf.push_str(
html_node(
"a",
vec![("href", entry_href.as_str())],
entry.name.as_str(),
)
.as_str(),
);
}
entries_buf
};
sidebar_entry_content.push_str(
html_node(
"div",
vec![("class", "sidebar-sub-entries")],
entries.as_str(),
)
.as_str(),
);
buf.push_str(
html_node(
"div",
vec![("class", "sidebar-entry")],
sidebar_entry_content.as_str(),
)
.as_str(),
);
}
buf
}
pub fn files_to_documentations( pub fn files_to_documentations(
filenames: Vec<PathBuf>, filenames: Vec<PathBuf>,
std_lib: StdLib, std_lib: StdLib,
@ -134,58 +283,6 @@ pub fn files_to_documentations(
files_docs files_docs
} }
pub fn documentation_to_template_data(
doc: &Documentation,
module: &ModuleDocumentation,
) -> Template {
Template {
package_name: doc.name.clone(),
package_version: doc.version.clone(),
module_name: module.name.clone(),
module_docs: markdown_to_html(module.docs.clone()),
module_entries: module
.entries
.clone()
.into_iter()
.map(|entry| ModuleEntry {
name: entry.name.clone(),
type_vars: entry.type_vars,
type_annotation: match entry.type_annotation {
None => String::new(),
Some(type_ann) => {
let type_ann_html = &mut String::new();
type_ann_html.push_str(" : ");
type_annotation_to_html(0, type_ann_html, &type_ann);
type_ann_html.to_string()
}
},
docs: match entry.docs {
Some(docs) => markdown_to_html(docs),
None => String::new(),
},
})
.collect(),
module_links: doc
.modules
.clone()
.into_iter()
.map(|module_link| TemplateLink {
name: module_link.name.clone(),
href: format!("./{}.html", module_link.name),
classes: "".to_string(),
entries: module_link
.entries
.into_iter()
.map(|entry| TemplateLinkEntry { name: entry.name })
.collect(),
})
.collect(),
}
}
const INDENT: &str = "&nbsp;&nbsp;&nbsp;&nbsp;"; const INDENT: &str = "&nbsp;&nbsp;&nbsp;&nbsp;";
fn indent(buf: &mut String, times: usize) { fn indent(buf: &mut String, times: usize) {

View file

@ -1,72 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>The Roc Programming Language</title>
<meta name="description" content="A language for building fast, reliable software.">
<meta name="viewport" content="width=device-width">
<link rel="icon" href="favicon.svg">
<script type="text/javascript" src="search.js" defer></script>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<nav id="sidebar-nav">
<input id="module-search" aria-labelledby="search-link" type="text" placeholder="Search" />
<label for="module-search" id="search-link">Search</label>
<div class="module-links">
{{#each module_links as |link| ~}}
<div class="sidebar-entry">
<a class="sidebar-module-link" href="{{link.href}}">{{link.name}}</a>
<div class="sidebar-sub-entries">
{{#each link.entries as |entry| ~}}
<a class="{{link.classes}}" href="{{link.href}}#{{entry.name}}">{{entry.name}}</a>
{{/each~}}
</div>
</div>
{{/each~}}
</div>
</nav>
<div class="top-header-extension">
<!-- if the window gets big, this extends the purple bar on the top header to the left edge of the window -->
</div>
<header class="top-header">
<nav class="pkg-and-logo">
<a class="logo" href="/" aria-labelledby="logo-link">
<svg viewBox="0 -6 51 58" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="logo-link" role="img">
<title id="logo-link">Return to Roc packages</title>
<polygon role="presentation" points="0,0 23.8834,3.21052 37.2438,19.0101 45.9665,16.6324 50.5,22 45,22 44.0315,26.3689 26.4673,39.3424 27.4527,45.2132 17.655,53 23.6751,22.7086" />
</svg>
</a>
<h1 class="pkg-full-name">
<a href="/{{package_name}}">{{package_name}}</a>
</h1>
<a class="version" href="/{{package_name}}/{{package_version}}">{{package_version}}</a>
</nav>
<div class="top-header-triangle">
<!-- if the window gets big, this extends the purple bar on the top header to the left edge of the window -->
</div>
</header>
<main>
<h2 class="module-name"><a href="#">{{module_name}}</a></h2>
{{{module_docs}}}
{{#each module_entries as |entry| ~}}
<h3 id="{{entry.name}}">
<a href="#{{entry.name}}">{{entry.name}}</a>
{{#each entry.type_vars as |type_var| ~}}
{{type_var}}
{{/each~}}
{{{entry.type_annotation}}}
</h3>
{{{entry.docs}}}
{{/each~}}
</main>
<footer>
<p>Made by people who like to make nice things.</p>
<p>© 2020-present</p>
</footer>
</body>
</html>

View file

@ -1,66 +0,0 @@
#[macro_use]
extern crate pretty_assertions;
use roc_docs::{documentation_to_template_data, files_to_documentations, ModuleEntry};
use std::path::PathBuf;
#[cfg(test)]
mod test_docs {
use super::*;
#[test]
fn internal() {
let files_docs = files_to_documentations(
vec![PathBuf::from(r"tests/fixtures/Interface.roc")],
roc_builtins::std::standard_stdlib(),
);
let package = roc_load::docs::Documentation {
name: "roc/builtins".to_string(),
version: "1.0.0".to_string(),
docs: "Package introduction or README.".to_string(),
modules: files_docs,
};
let expected_entries = vec![
ModuleEntry {
name: "Block".to_string(),
type_vars: vec![ "elem".to_string() ],
type_annotation: " : <br>&nbsp;&nbsp;&nbsp;&nbsp;[<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Block elem<br>&nbsp;&nbsp;&nbsp;&nbsp;]".to_string(),
docs: "<p>This is a block</p>\n".to_string(),
},
ModuleEntry {
name: "singleline".to_string(),
type_vars: vec![],
type_annotation: "".to_string(),
docs: "<p>Single line documentation.</p>\n".to_string(),
},
ModuleEntry {
name: "multiline".to_string(),
type_vars: vec![],
type_annotation: "".to_string(),
docs: "<p>Multiline documentation.\nWithout any complex syntax yet!</p>\n".to_string(),
}, ModuleEntry {
name: "multiparagraph".to_string(),
type_vars: vec![],
type_annotation: "".to_string(),
docs: "<p>Multiparagraph documentation.</p>\n<p>Without any complex syntax yet!</p>\n".to_string(),
}, ModuleEntry {
name: "codeblock".to_string(),
type_vars: vec![],
type_annotation: "".to_string(),
docs: "<p>Turns &gt;&gt;&gt; into code block for now.</p>\n<pre><code class=\"language-roc\">codeblock</code></pre>\n".to_string(),
},
];
for module in &package.modules {
let template = documentation_to_template_data(&package, module);
assert_eq!(template.module_name, "Test");
template
.module_entries
.iter()
.zip(expected_entries.iter())
.for_each(|(x, y)| assert_eq!(x, y));
}
}
}