enable --root-dir for roc docs

This commit is contained in:
Luke Boswell 2025-01-08 13:43:42 +11:00
parent 8540aa1a14
commit 9c527f890f
No known key found for this signature in database
GPG key ID: F6DB3C9DB47377B0
4 changed files with 57 additions and 20 deletions

View file

@ -89,6 +89,7 @@ pub const FLAG_PP_HOST: &str = "host";
pub const FLAG_PP_PLATFORM: &str = "platform"; pub const FLAG_PP_PLATFORM: &str = "platform";
pub const FLAG_PP_DYLIB: &str = "lib"; pub const FLAG_PP_DYLIB: &str = "lib";
pub const FLAG_MIGRATE: &str = "migrate"; pub const FLAG_MIGRATE: &str = "migrate";
pub const FLAG_DOCS_ROOT: &str = "root-dir";
pub const VERSION: &str = env!("ROC_VERSION"); pub const VERSION: &str = env!("ROC_VERSION");
const DEFAULT_GENERATED_DOCS_DIR: &str = "generated-docs"; const DEFAULT_GENERATED_DOCS_DIR: &str = "generated-docs";
@ -184,6 +185,12 @@ pub fn build_app() -> Command {
.num_args(0..) .num_args(0..)
.allow_hyphen_values(true); .allow_hyphen_values(true);
let flag_docs_root_dir = Arg::new(FLAG_DOCS_ROOT)
.long(FLAG_DOCS_ROOT)
.help("Set a root directory path to be used as a prefix for URL links in the generated documentation files.")
.value_parser(value_parser!(Option<String>))
.required(false);
let build_target_values_parser = let build_target_values_parser =
PossibleValuesParser::new(Target::iter().map(Into::<&'static str>::into)); PossibleValuesParser::new(Target::iter().map(Into::<&'static str>::into));
@ -405,6 +412,7 @@ pub fn build_app() -> Command {
.required(false) .required(false)
.default_value(DEFAULT_ROC_FILENAME), .default_value(DEFAULT_ROC_FILENAME),
) )
.arg(flag_docs_root_dir)
) )
.subcommand(Command::new(CMD_GLUE) .subcommand(Command::new(CMD_GLUE)
.about("Generate glue code between a platform's Roc API and its host language") .about("Generate glue code between a platform's Roc API and its host language")

View file

@ -5,10 +5,10 @@ use roc_build::program::{check_file, CodeGenBackend};
use roc_cli::{ use roc_cli::{
build_app, default_linking_strategy, format_files, format_src, test, BuildConfig, FormatMode, build_app, default_linking_strategy, format_files, format_src, test, BuildConfig, FormatMode,
CMD_BUILD, CMD_CHECK, CMD_DEV, CMD_DOCS, CMD_FORMAT, CMD_GLUE, CMD_PREPROCESS_HOST, CMD_REPL, CMD_BUILD, CMD_CHECK, CMD_DEV, CMD_DOCS, CMD_FORMAT, CMD_GLUE, CMD_PREPROCESS_HOST, CMD_REPL,
CMD_RUN, CMD_TEST, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_DEV, FLAG_LIB, FLAG_MAIN, CMD_RUN, CMD_TEST, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_DEV, FLAG_DOCS_ROOT,
FLAG_MIGRATE, FLAG_NO_COLOR, FLAG_NO_HEADER, FLAG_NO_LINK, FLAG_OUTPUT, FLAG_PP_DYLIB, FLAG_LIB, FLAG_MAIN, FLAG_MIGRATE, FLAG_NO_COLOR, FLAG_NO_HEADER, FLAG_NO_LINK, FLAG_OUTPUT,
FLAG_PP_HOST, FLAG_PP_PLATFORM, FLAG_STDIN, FLAG_STDOUT, FLAG_TARGET, FLAG_TIME, FLAG_VERBOSE, FLAG_PP_DYLIB, FLAG_PP_HOST, FLAG_PP_PLATFORM, FLAG_STDIN, FLAG_STDOUT, FLAG_TARGET, FLAG_TIME,
GLUE_DIR, GLUE_SPEC, ROC_FILE, VERSION, FLAG_VERBOSE, GLUE_DIR, GLUE_SPEC, ROC_FILE, VERSION,
}; };
use roc_docs::generate_docs_html; use roc_docs::generate_docs_html;
use roc_error_macros::user_error; use roc_error_macros::user_error;
@ -324,7 +324,25 @@ fn main() -> io::Result<()> {
let root_path = matches.get_one::<PathBuf>(ROC_FILE).unwrap(); let root_path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
let out_dir = matches.get_one::<OsString>(FLAG_OUTPUT).unwrap(); let out_dir = matches.get_one::<OsString>(FLAG_OUTPUT).unwrap();
generate_docs_html(root_path.to_owned(), out_dir.as_ref()); let maybe_root_dir: Option<String> = {
if let Ok(root_dir) = std::env::var("ROC_DOCS_URL_ROOT") {
// if the env var is set, it should override the flag for now
// TODO -- confirm we no longer need this and remove
// once docs are migrated to individual repositories and not roc website
Some(root_dir)
} else {
matches
.get_one::<Option<String>>(FLAG_DOCS_ROOT)
.unwrap()
.clone()
}
};
generate_docs_html(
root_path.to_owned(),
out_dir.as_ref(),
maybe_root_dir.clone(),
);
Ok(0) Ok(0)
} }

View file

@ -22,7 +22,7 @@ use std::path::{Path, PathBuf};
const LINK_SVG: &str = include_str!("./static/link.svg"); const LINK_SVG: &str = include_str!("./static/link.svg");
pub fn generate_docs_html(root_file: PathBuf, build_dir: &Path) { pub fn generate_docs_html(root_file: PathBuf, build_dir: &Path, maybe_root_dir: Option<String>) {
let mut loaded_module = load_module_for_docs(root_file); let mut loaded_module = load_module_for_docs(root_file);
let exposed_module_docs = get_exposed_module_docs(&mut loaded_module); let exposed_module_docs = get_exposed_module_docs(&mut loaded_module);
@ -117,7 +117,7 @@ pub fn generate_docs_html(root_file: PathBuf, build_dir: &Path) {
.join("\n ") .join("\n ")
.as_str(), .as_str(),
) )
.replace("<!-- base -->", &base_url()) .replace("<!-- base -->", &base_url(maybe_root_dir.as_deref()))
.replace( .replace(
"<!-- Module links -->", "<!-- Module links -->",
render_sidebar(exposed_module_docs.iter().map(|(_, docs)| docs)).as_str(), render_sidebar(exposed_module_docs.iter().map(|(_, docs)| docs)).as_str(),
@ -156,7 +156,7 @@ pub fn generate_docs_html(root_file: PathBuf, build_dir: &Path) {
) )
.replace( .replace(
"<!-- Package Name -->", "<!-- Package Name -->",
render_name_link(package_name.as_str()).as_str(), render_name_link(package_name.as_str(), maybe_root_dir.as_deref()).as_str(),
) )
.replace("<!-- Package Name String -->", package_name.as_str()) .replace("<!-- Package Name String -->", package_name.as_str())
.replace( .replace(
@ -184,7 +184,7 @@ pub fn generate_docs_html(root_file: PathBuf, build_dir: &Path) {
) )
.replace( .replace(
"<!-- Package Name -->", "<!-- Package Name -->",
render_name_link(package_name.as_str()).as_str(), render_name_link(package_name.as_str(), maybe_root_dir.as_deref()).as_str(),
) )
.replace("<!-- Package Name String -->", package_name.as_str()) .replace("<!-- Package Name String -->", package_name.as_str())
.replace( .replace(
@ -194,6 +194,7 @@ pub fn generate_docs_html(root_file: PathBuf, build_dir: &Path) {
module_docs, module_docs,
&loaded_module, &loaded_module,
&all_exposed_symbols, &all_exposed_symbols,
maybe_root_dir.as_deref(),
) )
.as_str(), .as_str(),
); );
@ -294,6 +295,7 @@ fn render_module_documentation(
module: &ModuleDocumentation, module: &ModuleDocumentation,
root_module: &LoadedModule, root_module: &LoadedModule,
all_exposed_symbols: &VecSet<Symbol>, all_exposed_symbols: &VecSet<Symbol>,
maybe_root_dir: Option<&str>,
) -> String { ) -> String {
let mut buf = String::new(); let mut buf = String::new();
let module_name = module.name.as_str(); let module_name = module.name.as_str();
@ -356,6 +358,7 @@ fn render_module_documentation(
&module.scope, &module.scope,
docs, docs,
root_module, root_module,
maybe_root_dir,
); );
} }
@ -370,6 +373,7 @@ fn render_module_documentation(
&module.scope, &module.scope,
docs, docs,
root_module, root_module,
maybe_root_dir,
); );
} }
DocEntry::DetachedDoc(docs) => { DocEntry::DetachedDoc(docs) => {
@ -380,6 +384,7 @@ fn render_module_documentation(
&module.scope, &module.scope,
docs, docs,
root_module, root_module,
maybe_root_dir,
); );
} }
}; };
@ -413,19 +418,16 @@ where
buf.push('>'); buf.push('>');
} }
fn base_url() -> String { fn base_url(maybe_root_dir: Option<&str>) -> String {
// e.g. "builtins/" in "https://roc-lang.org/builtins/Str" match maybe_root_dir {
// Some(root_builtins_path) => {
// TODO make this a CLI flag to the `docs` subcommand instead of an env var
match std::env::var("ROC_DOCS_URL_ROOT") {
Ok(root_builtins_path) => {
let mut url_str = String::with_capacity(root_builtins_path.len() + 64); let mut url_str = String::with_capacity(root_builtins_path.len() + 64);
if !root_builtins_path.starts_with('/') { if !root_builtins_path.starts_with('/') {
url_str.push('/'); url_str.push('/');
} }
url_str.push_str(&root_builtins_path); url_str.push_str(root_builtins_path);
if !root_builtins_path.ends_with('/') { if !root_builtins_path.ends_with('/') {
url_str.push('/'); url_str.push('/');
@ -444,14 +446,19 @@ fn base_url() -> String {
} }
// TODO render version as well // TODO render version as well
fn render_name_link(name: &str) -> String { fn render_name_link(name: &str, maybe_root_dir: Option<&str>) -> String {
let mut buf = String::new(); let mut buf = String::new();
push_html(&mut buf, "h1", [("class", "pkg-full-name")], { push_html(&mut buf, "h1", [("class", "pkg-full-name")], {
let mut link_buf = String::new(); let mut link_buf = String::new();
// link to root (= docs overview page) // link to root (= docs overview page)
push_html(&mut link_buf, "a", [("href", base_url().as_str())], name); push_html(
&mut link_buf,
"a",
[("href", base_url(maybe_root_dir).as_str())],
name,
);
link_buf link_buf
}); });
@ -1141,6 +1148,7 @@ fn doc_url<'a>(
interns: &'a Interns, interns: &'a Interns,
mut module_name: &'a str, mut module_name: &'a str,
ident: &str, ident: &str,
maybe_root_dir: Option<&str>,
) -> Result<DocUrl, (String, LinkProblem)> { ) -> Result<DocUrl, (String, LinkProblem)> {
if module_name.is_empty() { if module_name.is_empty() {
// This is an unqualified lookup, so look for the ident // This is an unqualified lookup, so look for the ident
@ -1194,7 +1202,7 @@ fn doc_url<'a>(
} }
} }
let mut url = base_url(); let mut url = base_url(maybe_root_dir);
// Example: // Example:
// //
@ -1216,6 +1224,7 @@ fn markdown_to_html(
scope: &Scope, scope: &Scope,
markdown: &str, markdown: &str,
loaded_module: &LoadedModule, loaded_module: &LoadedModule,
maybe_root_dir: Option<&str>,
) { ) {
use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Tag::*}; use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Tag::*};
@ -1253,6 +1262,7 @@ fn markdown_to_html(
&loaded_module.interns, &loaded_module.interns,
module_name, module_name,
symbol_name, symbol_name,
maybe_root_dir,
) { ) {
Ok(DocUrl { url, title }) => Some((url.into(), title.into())), Ok(DocUrl { url, title }) => Some((url.into(), title.into())),
Err((link_markdown, problem)) => { Err((link_markdown, problem)) => {
@ -1287,6 +1297,7 @@ fn markdown_to_html(
&loaded_module.interns, &loaded_module.interns,
"", "",
type_name, type_name,
maybe_root_dir,
) { ) {
Ok(DocUrl { url, title }) => Some((url.into(), title.into())), Ok(DocUrl { url, title }) => Some((url.into(), title.into())),
Err((link_markdown, problem)) => { Err((link_markdown, problem)) => {

View file

@ -19,10 +19,10 @@ fn main() -> io::Result<()> {
) )
.get_matches(); .get_matches();
// Populate roc_files
generate_docs_html( generate_docs_html(
matches.get_one::<PathBuf>(ROC_FILE).unwrap().to_owned(), matches.get_one::<PathBuf>(ROC_FILE).unwrap().to_owned(),
&PathBuf::from("./generated-docs"), &PathBuf::from("./generated-docs"),
std::env::var("ROC_DOCS_URL_ROOT").ok(),
); );
Ok(()) Ok(())