diff --git a/bonnie.toml b/bonnie.toml index 5fa3551f6c..d1d084ee8b 100644 --- a/bonnie.toml +++ b/bonnie.toml @@ -36,7 +36,7 @@ site.subcommands.export.cmd = [ "bonnie copy-subcrates", "cd packages/perseus-cli", # Point this live version of the CLI at the given example - "find ../../website/website -not -path \"./.perseus/*\" -not -path \"./target/*\" | entr -s \"TEST_EXAMPLE=../../website/website cargo run -- export\"" + "find ../../website/website -not -path \"../../website/website/.perseus/*\" -not -path \"../../website/website/target/*\" | entr -s \"TEST_EXAMPLE=../../website/website cargo run -- export\"" ] site.subcommands.export.desc = "exports the site, watching for changes" site.subcommands.serve.cmd = [ diff --git a/packages/perseus/src/serve.rs b/packages/perseus/src/serve.rs index d347dba000..659962bef4 100644 --- a/packages/perseus/src/serve.rs +++ b/packages/perseus/src/serve.rs @@ -260,7 +260,6 @@ pub async fn get_page_for_template( // Handle build state (which might use revalidation or incremental) if template.uses_build_state() || template.is_basic() { // If the template uses incremental generation, that is its own contained process - // TODO separate out build paths pages, which are in the immutable store if template.uses_incremental() && was_incremental_match { // This template uses incremental generation, and this page was built and cached at runtime in the mutable store // Get the cached content if it exists (otherwise `None`) diff --git a/website/website/README.md b/website/website/README.md index 72141796e0..fd415359a6 100644 --- a/website/website/README.md +++ b/website/website/README.md @@ -1,11 +1,31 @@ # Website -> Note: the website is still under active development, and has not yet been deployed - This directory contains the website for Perseus, which is hosted at ! -TODO - ## Why is this in `website/website`? So that using the local version of Perseus rather than the most recently published release works without any further changes. In development, this is designed to work for the `examples/` directory, using `../../` to access `packages`. We mimic the same file structure here. + +## Comparisons + +The website includes a [comparisons page](https://arctic-hen7.github.io/perseus/comparisons), which compares Perseus to a number of other frameworks. Of course, there are _a lot_ of frameworks out there, so we highly encourage contributions to this! It's designed to be quite easy to contribute to, just add a new file called `website/website/comparisons/framework.json` (substituting `framework` for the name of the framework) and fill in the following framework details: + +- `name`: `String`, +- `supports_ssg`: `"full"`/`"partial"`/`"none"`, +- `supports_ssr`: `"full"`/`"partial"`/`"none"`, +- `supports_ssr_ssg_same_page`: `"full"`/`"partial"`/`"none"`, +- `supports_i18n`: `"full"`/`"partial"`/`"none"`, +- `supports_incremental`: `"full"`/`"partial"`/`"none"`, +- `supports_revalidation`: `"full"`/`"partial"`/`"none"`, +- `inbuilt_cli`: `"full"`/`"partial"`/`"none"`, +- `inbuilt_routing`: `"full"`/`"partial"`/`"none"`, +- `supports_shell`: `"full"`/`"partial"`/`"none"`, +- `supports_deployment`: `"full"`/`"partial"`/`"none"`, +- `supports_exporting`: `"full"`/`"partial"`/`"none"`, +- `language`: `String`, +- `homepage_lighthouse_desktop`: `u8`, +- `homepage_lighthouse_mobile`: `u8` + +### Lighthouse Scores + +For consistency, we generate all Lighthouse scores through [PageSpeed Insights](https://developers.google.com/speed/pagespeed/insights). As this metric can vary slightly between machines and runs, it's advised to run it more than once and take an average (rounding up). Maintainers will check these themselves, and if there's any major discrepancy (>5 points), you may be asked to provide a screenshot from your system. Maintainers reserve the right to determine the final verdict on which score to use in the event of a conflict. diff --git a/website/website/comparisons/gatsby.json b/website/website/comparisons/gatsby.json new file mode 100644 index 0000000000..96326c2b89 --- /dev/null +++ b/website/website/comparisons/gatsby.json @@ -0,0 +1,17 @@ +{ + "name": "GatsbyJS", + "supports_ssg": "full", + "supports_ssr": "none", + "supports_ssr_ssg_same_page": "none", + "supports_i18n": "partial", + "supports_incremental": "none", + "supports_revalidation": "none", + "inbuilt_cli": "full", + "inbuilt_routing": "full", + "supports_shell": "full", + "supports_deployment": "full", + "supports_exporting": "full", + "language": "JavaScript/TypeScript", + "homepage_lighthouse_desktop": 75, + "homepage_lighthouse_mobile": 45 +} diff --git a/website/website/comparisons/nextjs.json b/website/website/comparisons/nextjs.json new file mode 100644 index 0000000000..165f80b57e --- /dev/null +++ b/website/website/comparisons/nextjs.json @@ -0,0 +1,17 @@ +{ + "name": "NextJS", + "supports_ssg": "full", + "supports_ssr": "full", + "supports_ssr_ssg_same_page": "none", + "supports_i18n": "partial", + "supports_incremental": "full", + "supports_revalidation": "full", + "inbuilt_cli": "full", + "inbuilt_routing": "full", + "supports_shell": "full", + "supports_deployment": "full", + "supports_exporting": "full", + "language": "JavaScript/TypeScript", + "homepage_lighthouse_desktop": 100, + "homepage_lighthouse_mobile": 72 +} diff --git a/website/website/comparisons/perseus.json b/website/website/comparisons/perseus.json new file mode 100644 index 0000000000..cc1004d1ad --- /dev/null +++ b/website/website/comparisons/perseus.json @@ -0,0 +1,17 @@ +{ + "name": "Perseus", + "supports_ssg": "full", + "supports_ssr": "full", + "supports_ssr_ssg_same_page": "full", + "supports_i18n": "full", + "supports_incremental": "full", + "supports_revalidation": "full", + "inbuilt_cli": "full", + "inbuilt_routing": "full", + "supports_shell": "full", + "supports_deployment": "full", + "supports_exporting": "full", + "language": "Rust", + "homepage_lighthouse_desktop": 100, + "homepage_lighthouse_mobile": 95 +} diff --git a/website/website/src/components/comparisons.rs b/website/website/src/components/comparisons.rs index 8eea3a5d51..4f8ff3723b 100644 --- a/website/website/src/components/comparisons.rs +++ b/website/website/src/components/comparisons.rs @@ -1,6 +1,11 @@ +use perseus::GenericNode; +use serde::{Deserialize, Serialize}; +use sycamore::prelude::template; +use sycamore::prelude::Template as SycamoreTemplate; + /// A comparison for the comparisons table. Perseus itself also has an entry here. Note that any changes to the properties measured here /// must also be reflected in the rendering code, which generates a title row independently. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Comparison { // We deliberately preserve order so that Perseus always comes first // That allows us to scroll though the others and keep the first two columns constantly there @@ -17,10 +22,14 @@ pub struct Comparison { pub supports_deployment: FeatureSupport, pub supports_exporting: FeatureSupport, pub language: String, + // Ours are 100 and 95, respectively + pub homepage_lighthouse_desktop: u8, + pub homepage_lighthouse_mobile: u8, } /// The different levels of support for a feature. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] pub enum FeatureSupport { Full, Partial, @@ -36,24 +45,29 @@ impl FeatureSupport { } } -/// Returns all the current comparisons to Perseus for display in a table -pub fn get_comparisons() -> Vec { - vec![ - Comparison { - name: "NextJS".to_string(), - supports_ssg: FeatureSupport::Full, - supports_ssr: FeatureSupport::Full, - supports_ssr_ssg_same_page: FeatureSupport::None, - supports_i18n: FeatureSupport::Full, - supports_incremental: FeatureSupport::Full, - supports_revalidation: FeatureSupport::Full, - inbuilt_cli: FeatureSupport::Full, - inbuilt_routing: FeatureSupport::Full, - supports_shell: FeatureSupport::Full, - supports_deployment: FeatureSupport::Full, - supports_exporting: FeatureSupport::Full, - language: "JavaScript/TypeScript".to_string(), - }, - // TODO - ] +/// Renders a Lighthouse score to have a text color. If it's 100, then we use the appropriate emoji. +pub fn render_lighthouse_score(score: u8) -> SycamoreTemplate { + if score == 100 { + template! { + "💯" + } + } else if score >= 90 { + template! { + span(class = "text-green-500") { + (score) + } + } + } else if score >= 50 { + template! { + span(class = "text-amber-500") { + (score) + } + } + } else { + template! { + span(class = "text-red-500") { + (score) + } + } + } } diff --git a/website/website/src/components/container.rs b/website/website/src/components/container.rs index f023fd308c..6caba74b50 100644 --- a/website/website/src/components/container.rs +++ b/website/website/src/components/container.rs @@ -7,7 +7,6 @@ pub static COPYRIGHT_YEARS: &str = "2021"; #[component(NavLinks)] pub fn nav_links() -> SycamoreTemplate { template! { - // TODO fix overly left alignment here on mobile li(class = "m-3 p-1") { a(href = link!("/docs"), class = "px-2") { (t!("navlinks-docs")) } } diff --git a/website/website/src/components/github_svg.rs b/website/website/src/components/github_svg.rs index 6b10b56a8d..c55c6f1f00 100644 --- a/website/website/src/components/github_svg.rs +++ b/website/website/src/components/github_svg.rs @@ -1,2 +1 @@ pub static GITHUB_SVG: &str = r#""#; -// pub static GITHUB_SVG: &str = r#"

Test!

"#; diff --git a/website/website/src/components/info_svg.rs b/website/website/src/components/info_svg.rs new file mode 100644 index 0000000000..0adfc7c930 --- /dev/null +++ b/website/website/src/components/info_svg.rs @@ -0,0 +1 @@ +pub static INFO_SVG: &str = r#""#; diff --git a/website/website/src/components/mod.rs b/website/website/src/components/mod.rs index a60eef7d0f..b34e740cd4 100644 --- a/website/website/src/components/mod.rs +++ b/website/website/src/components/mod.rs @@ -1,4 +1,5 @@ -// pub mod comparisons; +pub mod comparisons; pub mod container; pub mod features_list; pub mod github_svg; +pub mod info_svg; diff --git a/website/website/src/templates/comparisons.rs b/website/website/src/templates/comparisons.rs index ef478d1900..b849826ab3 100644 --- a/website/website/src/templates/comparisons.rs +++ b/website/website/src/templates/comparisons.rs @@ -1,35 +1,361 @@ -// use crate::components::comparisons::{get_comparisons, Comparison, FeatureSupport}; +use crate::components::comparisons::{render_lighthouse_score, Comparison}; use crate::components::container::{Container, ContainerProps}; -use perseus::{t, GenericNode, Template}; +use crate::components::info_svg::INFO_SVG; +use perseus::{ + t, ErrorCause, GenericErrorWithCause, GenericNode, RenderFnResultWithCause, Template, +}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::fs; +use std::path::PathBuf; use std::rc::Rc; use sycamore::prelude::Template as SycamoreTemplate; use sycamore::prelude::*; +use walkdir::WalkDir; +use wasm_bindgen::JsCast; +struct ComparisonRowProps { + perseus_val: String, + comparison_val: StateHandle, + name: String, +} +#[component(ComparisonRow)] +fn comparison_row(props: ComparisonRowProps) -> SycamoreTemplate { + let ComparisonRowProps { + perseus_val, + comparison_val, + name, + } = props; + let name_2 = name.clone(); + let show_details = Signal::new(false); + + template! { + tr { + th(class = "text-left p-1 py-2 text-xs xs:text-base") { + div(class = "flex items-center") { + (t!(&format!("comparisons-table-headings.{}", name))) + span( + class = "ml-1", + on:click = cloned!((show_details) => move |_| { + show_details.set(!*show_details.get()) + }), + dangerously_set_inner_html = INFO_SVG + ) + } + p( + class = format!( + "italic font-normal {}", + if *show_details.get() { + "visible" + } else { + "hidden" + } + ) + ) { + (t!(&format!("comparisons-table-details.{}", name_2))) + } + } + td(class = "p-1 py-2 text-xs xs:text-base") { + (perseus_val) + } + // The only thing that could overflow is the comparison language (everything else is tested) + // Anything longer than 15 characters will overflow (by testing on smallest supported screen -- iPhone 5) + td(class = "p-1 py-2 text-xs xs:text-base break-words xs:break-normal") { + (comparison_val.get()) + } + } + } +} + +struct ComparisonTableProps { + comparison: StateHandle, + perseus_comparison: Comparison, +} +#[component(ComparisonTable)] +fn comparison_table(props: ComparisonTableProps) -> SycamoreTemplate { + let comparison = props.comparison.clone(); + let Comparison { + name: _perseus_name, // We'll use the translation ID + supports_ssg: perseus_supports_ssg, + supports_ssr: perseus_supports_ssr, + supports_ssr_ssg_same_page: perseus_supports_ssr_ssg_same_page, + supports_i18n: perseus_supports_i18n, + supports_incremental: perseus_supports_incremental, + supports_revalidation: perseus_supports_revalidation, + inbuilt_cli: perseus_inbuilt_cli, + inbuilt_routing: perseus_inbuilt_routing, + supports_shell: perseus_supports_shell, + supports_deployment: perseus_supports_deployment, + supports_exporting: perseus_supports_exporting, + language: perseus_language, + homepage_lighthouse_desktop: perseus_homepage_lighthouse_desktop, + homepage_lighthouse_mobile: perseus_homepage_lighthouse_mobile, + } = props.perseus_comparison; + // We now need to deconstruct the comparison with memos (actual pain) + let comparison_name = + create_memo(cloned!((comparison) => move || comparison.get().name.clone())); + let comparison_supports_ssg = create_memo( + cloned!((comparison) => move || comparison.get().supports_ssg.clone().render()), + ); + let comparison_supports_ssr = create_memo( + cloned!((comparison) => move || comparison.get().supports_ssr.clone().render()), + ); + let comparison_supports_ssr_ssg_same_page = create_memo( + cloned!((comparison) => move || comparison.get().supports_ssr_ssg_same_page.clone().render()), + ); + let comparison_supports_i18n = create_memo( + cloned!((comparison) => move || comparison.get().supports_i18n.clone().render()), + ); + let comparison_supports_incremental = create_memo( + cloned!((comparison) => move || comparison.get().supports_incremental.clone().render()), + ); + let comparison_supports_revalidation = create_memo( + cloned!((comparison) => move || comparison.get().supports_revalidation.clone().render()), + ); + let comparison_inbuilt_cli = + create_memo(cloned!((comparison) => move || comparison.get().inbuilt_cli.clone().render())); + let comparison_inbuilt_routing = create_memo( + cloned!((comparison) => move || comparison.get().inbuilt_routing.clone().render()), + ); + let comparison_supports_shell = create_memo( + cloned!((comparison) => move || comparison.get().supports_shell.clone().render()), + ); + let comparison_supports_deployment = create_memo( + cloned!((comparison) => move || comparison.get().supports_deployment.clone().render()), + ); + let comparison_supports_exporting = create_memo( + cloned!((comparison) => move || comparison.get().supports_exporting.clone().render()), + ); + let comparison_language = + create_memo(cloned!((comparison) => move || comparison.get().language.clone())); + let comparison_homepage_lighthouse_desktop = + create_memo(cloned!((comparison) => move || comparison.get().homepage_lighthouse_desktop)); + let comparison_homepage_lighthouse_mobile = + create_memo(cloned!((comparison) => move || comparison.get().homepage_lighthouse_mobile)); + + let show_details_homepage_lighthouse_desktop = Signal::new(false); + let show_details_homepage_lighthouse_mobile = Signal::new(false); + + template! { + table(class = "w-full overflow-x-scroll table-fixed border-collapse") { + thead(class = "mt-4 text-white bg-indigo-500 rounded-xl") { + th(class = "p-1 py-2 text-xs xs:text-base") { + (t!("comparisons-table-header")) + } + th(class = "p-1 py-2 text-xs xs:text-base") { + (t!("perseus")) + } + th(class = "p-1 py-2 text-xs xs:text-base") { + (comparison_name.get()) + } + } + tbody { + // One row for each comparison point + // One heading explaining it + // Then two cells, one Perseus, and the for the comparison + ComparisonRow(ComparisonRowProps { + perseus_val: perseus_language, + comparison_val: comparison_language.clone(), + name: "language".to_string() + }) + ComparisonRow(ComparisonRowProps { + perseus_val: perseus_supports_ssg.render(), + comparison_val: comparison_supports_ssg.clone(), + name: "supports_ssg".to_string() + }) + ComparisonRow(ComparisonRowProps { + perseus_val: perseus_supports_ssr.render(), + comparison_val: comparison_supports_ssr.clone(), + name: "supports_ssr".to_string() + }) + ComparisonRow(ComparisonRowProps { + perseus_val: perseus_supports_ssr_ssg_same_page.render(), + comparison_val: comparison_supports_ssr_ssg_same_page.clone(), + name: "supports_ssr_ssg_same_page".to_string() + }) + ComparisonRow(ComparisonRowProps { + perseus_val: perseus_supports_i18n.render(), + comparison_val: comparison_supports_i18n.clone(), + name: "supports_i18n".to_string() + }) + ComparisonRow(ComparisonRowProps { + perseus_val: perseus_supports_incremental.render(), + comparison_val: comparison_supports_incremental.clone(), + name: "supports_incremental".to_string() + }) + ComparisonRow(ComparisonRowProps { + perseus_val: perseus_supports_revalidation.render(), + comparison_val: comparison_supports_revalidation.clone(), + name: "supports_revalidation".to_string() + }) + ComparisonRow(ComparisonRowProps { + perseus_val: perseus_inbuilt_cli.render(), + comparison_val: comparison_inbuilt_cli.clone(), + name: "inbuilt_cli".to_string() + }) + ComparisonRow(ComparisonRowProps { + perseus_val: perseus_inbuilt_routing.render(), + comparison_val: comparison_inbuilt_routing.clone(), + name: "inbuilt_routing".to_string() + }) + ComparisonRow(ComparisonRowProps { + perseus_val: perseus_supports_shell.render(), + comparison_val: comparison_supports_shell.clone(), + name: "supports_shell".to_string() + }) + ComparisonRow(ComparisonRowProps { + perseus_val: perseus_supports_deployment.render(), + comparison_val: comparison_supports_deployment.clone(), + name: "supports_deployment".to_string() + }) + ComparisonRow(ComparisonRowProps { + perseus_val: perseus_supports_exporting.render(), + comparison_val: comparison_supports_exporting.clone(), + name: "supports_exporting".to_string() + }) + // These last two get special rendering for text colors and possible emoji + tr { + th(class = "text-left p-1 py-2 text-xs xs:text-base") { + div(class = "flex items-center") { + (t!("comparisons-table-headings.homepage_lighthouse_desktop")) + span( + class = "ml-1", + on:click = cloned!((show_details_homepage_lighthouse_desktop) => move |_| { + show_details_homepage_lighthouse_desktop.set(!*show_details_homepage_lighthouse_desktop.get()) + }), + dangerously_set_inner_html = INFO_SVG + ) + } + p( + class = format!( + "italic font-normal {}", + if *show_details_homepage_lighthouse_desktop.get() { + "visible" + } else { + "hidden" + } + ) + ) { + (t!("comparisons-table-details.homepage_lighthouse_desktop")) + } + } + td(class = "p-1 py-2 text-xs xs:text-base") { + (render_lighthouse_score(perseus_homepage_lighthouse_desktop)) + } + td(class = "p-1 py-2 text-xs xs:text-base") { + (render_lighthouse_score(*comparison_homepage_lighthouse_desktop.get())) + } + } + tr { + th(class = "text-left p-1 py-2 text-xs xs:text-base") { + div(class = "flex items-center") { + (t!("comparisons-table-headings.homepage_lighthouse_mobile")) + span( + class = "ml-1", + on:click = cloned!((show_details_homepage_lighthouse_mobile) => move |_| { + show_details_homepage_lighthouse_mobile.set(!*show_details_homepage_lighthouse_mobile.get()) + }), + dangerously_set_inner_html = INFO_SVG + ) + } + p( + class = format!( + "italic font-normal {}", + if *show_details_homepage_lighthouse_mobile.get() { + "visible" + } else { + "hidden" + } + ) + ) { + (t!("comparisons-table-details.homepage_lighthouse_mobile")) + } + } + td(class = "p-1 py-2 text-xs xs:text-base") { + (render_lighthouse_score(perseus_homepage_lighthouse_mobile)) + } + td(class = "p-1 py-2 text-xs xs:text-base") { + (render_lighthouse_score(*comparison_homepage_lighthouse_mobile.get())) + } + } + } + } + } +} + +#[derive(Serialize, Deserialize)] +pub struct ComparisonsPageProps { + pub comparisons: HashMap, + /// The comparison data for Perseus itself. + pub perseus_comparison: Comparison, +} #[component(ComparisonsPage)] -pub fn comparisons_page() -> SycamoreTemplate { +pub fn comparisons_page(props: ComparisonsPageProps) -> SycamoreTemplate { + let comparisons = props.comparisons.clone(); + let perseus_comparison = props.perseus_comparison; + let mut comparison_names: Vec = comparisons.keys().cloned().collect(); + comparison_names.sort(); + // The current comparison should be the first element in the list alphabetically + let curr_comparison_name = Signal::new(comparison_names[0].clone()); + + let select_options = SycamoreTemplate::new_fragment( + comparison_names + .iter() + .map(|name| { + let name = name.clone(); + let name_2 = name.clone(); + template! { + option(value = name) { + (name_2) + } + } + }) + .collect(), + ); + + let curr_comparison = create_memo(cloned!((curr_comparison_name, comparisons) => move || { + comparisons.get(&*curr_comparison_name.get()).unwrap().clone() + })); + template! { Container(ContainerProps { title: t!("perseus"), children: template! { div(class = "flex flex-col justify-center text-center dark:text-white mt-14 xs:mt-16 sm:mt-20 lg:mt-25") { div { - h1(class = "text-5xl xs:text-7xl sm:text-8xl md:text-9xl p-2 font-extrabold") { - "Comparisons" + h1(class = "text-5xl xs:text-7xl sm:text-8xl font-extrabold") { + (t!("comparisons-heading")) } br() p(class = "text-lg") { - "See how Perseus compares to other web development frameworks." - } - p(class = "italic") { - "Is there anything we're missing here? Please " - a(href = "https://github.com/arctic-hen7/perseus/issues/new/choose") { "open an issue" } - " and let us know!" + (t!("comparisons-subtitle")) } + p( + class = "italic px-1", + dangerously_set_inner_html = &t!("comparisons-extra") + ) } - br(class = "mb-24") - - p(class = "text-xl") { - (t!("comparisons-todo")) + br(class = "mb-2 sm:mb-16 md:mb-24") + div(class = "p-1") { + select( + class = "p-1 rounded-sm dark:bg-navy mb-4", + on:input = cloned!((curr_comparison_name) => move |event| { + let target: web_sys::HtmlInputElement = event.target().unwrap().unchecked_into(); + let new_comparison_name = target.value(); + curr_comparison_name.set(new_comparison_name); + }) + ) { + (select_options) + } + br() + div(class = "px-3 w-full sm:mr-auto sm:ml-auto sm:max-w-prose lg:max-w-3xl xl:max-w-4xl 2xl:max-w-5xl") { + div(class = "flex justify-center") { + ComparisonTable(ComparisonTableProps { + comparison: curr_comparison.clone(), + perseus_comparison + }) + } + } } } } @@ -39,9 +365,9 @@ pub fn comparisons_page() -> SycamoreTemplate { pub fn get_template() -> Template { Template::new("comparisons") - .template(Rc::new(|_| { + .template(Rc::new(|props| { template! { - ComparisonsPage() + ComparisonsPage(serde_json::from_str(&props.unwrap()).unwrap()) } })) .head(Rc::new(|_| { @@ -49,4 +375,48 @@ pub fn get_template() -> Template { title { (format!("{} | {}", t!("comparisons-title"), t!("perseus"))) } } })) + .build_state_fn(Rc::new(get_build_state)) +} + +pub async fn get_build_state(_path: String, _locale: String) -> RenderFnResultWithCause { + // Get all the comparisons from JSON + // This includes the special properties for Perseus itself + let mut perseus_comparison: Option = None; + let mut comparisons: HashMap = HashMap::new(); + + // Get the `comparisons/` directory in `website` (relative to `.perseus/`) + // This can have any file structure we want for organization, we just want the files + let comparisons_dir = PathBuf::from("../comparisons"); + // Loop through it + for entry in WalkDir::new(comparisons_dir) { + let entry = entry?; + let path = entry.path(); + // Ignore any empty directories or the like + if path.is_file() { + // There shouldn't be any non-Unicode comparison files + let path_str = path.to_str().unwrap(); + // Get the JSON contents and parse them as a comparison + let contents = fs::read_to_string(&path)?; + let comparison = serde_json::from_str::(&contents)?; + // If the file is `perseus.json`, we'll add this to a special variable, otherwise it gets added to the generic map + if path_str.ends_with("perseus.json") { + perseus_comparison = Some(comparison); + } else { + comparisons.insert(comparison.name.clone(), comparison); + } + } + } + + let props = ComparisonsPageProps { + comparisons, + perseus_comparison: match perseus_comparison { + Some(perseus_comparison) => perseus_comparison, + None => return Err(GenericErrorWithCause { + error: "perseus comparison data not recorded, please ensure `comparisons/perseus.json` exists".into(), + cause: ErrorCause::Server(None) + }) + } + }; + let props_str = serde_json::to_string(&props)?; + Ok(props_str) } diff --git a/website/website/src/templates/docs/generation.rs b/website/website/src/templates/docs/generation.rs index ad551f08a9..313cea874e 100644 --- a/website/website/src/templates/docs/generation.rs +++ b/website/website/src/templates/docs/generation.rs @@ -14,7 +14,6 @@ use walkdir::WalkDir; pub fn parse_md_to_html(markdown: &str) -> String { let mut opts = Options::empty(); - // TODO possibly enable further features here if necessary opts.insert(Options::ENABLE_STRIKETHROUGH); opts.insert(Options::ENABLE_TABLES); let parser = Parser::new_ext(markdown, opts); @@ -155,7 +154,6 @@ pub async fn get_build_state(path: String, locale: String) -> RenderFnResultWith let contents = fs::read_to_string(&fs_path)?; // Handle the directives to include code from another file // We only loop through the file's lines if it likely contains what we want - // TODO include only some lines let contents = if contents.contains("{{#") { let mut contents_with_incls = contents.clone(); for line in contents.lines() { diff --git a/website/website/tailwind.config.js b/website/website/tailwind.config.js index aec4f0d8a0..0c791a9cc6 100644 --- a/website/website/tailwind.config.js +++ b/website/website/tailwind.config.js @@ -1,6 +1,5 @@ const defaultTheme = require("tailwindcss/defaultTheme"); const colors = require("tailwindcss/colors"); -const plugin = require("tailwindcss/plugin"); module.exports = { purge: { @@ -26,7 +25,6 @@ module.exports = { }, colors: { // Full color palette - // TODO trim this down to what's needed transparent: "transparent", current: "currentColor", navy: "#001122", @@ -36,16 +34,10 @@ module.exports = { gray: colors.coolGray, red: colors.red, orange: colors.orange, + amber: colors.amber, yellow: colors.amber, green: colors.emerald, - blue: colors.blue, indigo: colors.indigo, - purple: colors.violet, - pink: colors.pink, - cyan: colors.cyan, - fuchsia: colors.fuchsia, - sky: colors.sky, - teal: colors.teal, }, }, variants: {}, diff --git a/website/website/translations/en-US.ftl b/website/website/translations/en-US.ftl index 8fca3c1119..84fa9de720 100644 --- a/website/website/translations/en-US.ftl +++ b/website/website/translations/en-US.ftl @@ -53,6 +53,9 @@ feature-exporting = comparisons-title = Comparisons to Other Frameworks comparisons-heading = Comparisons +comparisons-subtitle = See how Perseus compares to other web development frameworks. +comparisons-extra = Is there anything we're missing here? Please open an issue and let us know! +comparisons-table-header = Comparison comparisons-table-headings = .name = Name .supports_ssg = Static generation @@ -60,13 +63,30 @@ comparisons-table-headings = .supports_ssr_ssg_same_page = SSG & SSR in same page .supports_i18n = Internationalization .supports_incremental = Incremental generation - .revalidation_type = Revalidation + .supports_revalidation = Revalidation .inbuilt_cli = Inbuilt CLI - .routing_type = Routing support + .inbuilt_routing = Routing support .supports_shell = App shell .supports_deployment = Easy deployment .supports_exporting = Static exporting .language = Language + .homepage_lighthouse_desktop = Homepage Lighthouse Score (Desktop) + .homepage_lighthouse_mobile = Homepage Lighthouse Score (Mobile) +comparisons-table-details = + .language = The programming language this framework is built in. (Hint: Rust is the fastest!) + .supports_ssg = Whether or not the framework supports pre-rendering pages at build-time. + .supports_ssr = Whether or not the framework supports rendering pages dynamically when they're requested. + .supports_ssr_ssg_same_page = Whether or not the framework supports SSG and SSR (see above) in the very same page. + .supports_i18n = Whether or not the framework supports building a multilingual app. Some frameworks support this through plugins. + .supports_incremental = Whether or not the framework supports rendering pages on-demand and then caching them for future use. + .supports_revalidation = Whether or not the framework supports rebuilding pages that were built at build-time at request-time. Some frameworks can do this by time, or by custom logic. Perseus supports both. + .inbuilt_cli = Whether or not the framework has a command-line interface for convenient usage. Some frameworks use third-party CLIs. + .inbuilt_routing = Whether or not the framework supports inbuilt routing. Most frameworks have their own way of doing this if they do support it. + .supports_shell = Whether or not this framework has an app shell, which makes switching pages much faster and cleaner. + .supports_deployment = Whether or not this framework supports easy deployment. Perseus can deploy in one command. + .supports_exporting = Whether or not this framework can operate without a server, as a series of purely static files. + .homepage_lighthouse_desktop = The Lighthouse score out of 100 for desktop (higher is better). These are collected from Google's PageSpeed Insights tool. These are for the framework's website, and may not reflect the performance of all sites made with the framework. + .homepage_lighthouse_mobile = The Lighthouse score out of 100 for mobile (higher is better). These are collected from Google's PageSpeed Insights tool. These are for the framework's website, and may not reflect the performance of all sites made with the framework. docs-title-base = Perseus Docs docs-status = @@ -78,4 +98,3 @@ docs-version-switcher = .beta = v{ $version } (beta) .stable = v{ $version } (stable) .outdated = v{ $version } (outdated) -comparisons-todo = 🚧 This page is currently under construction. Check back soon to see how Perseus compares to other frameworks! 🚧