0.0.1-alpha.5
This commit is contained in:
15
crates/knowledge-howtos-server/Cargo.toml
Normal file
15
crates/knowledge-howtos-server/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
# file: crates/knowledge-howtos-server/Cargo.toml
|
||||
# version: 1
|
||||
|
||||
[package]
|
||||
name = "knowledge-howtos-server"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
knowledge-web-lib.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
8
crates/knowledge-howtos-server/src/main.rs
Normal file
8
crates/knowledge-howtos-server/src/main.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
// file: crates/knowledge-howtos-server/src/main.rs
|
||||
// version: 1
|
||||
|
||||
//! Thin launcher for the knowledge-howtos-server process.
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
return knowledge_web_lib::run_server(knowledge_web_lib::ServerSite::Howtos, "KNOWLEDGE_HOWTOS_BIND_ADDRESS");
|
||||
}
|
||||
15
crates/knowledge-pgr-server/Cargo.toml
Normal file
15
crates/knowledge-pgr-server/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
# file: crates/knowledge-pgr-server/Cargo.toml
|
||||
# version: 1
|
||||
|
||||
[package]
|
||||
name = "knowledge-pgr-server"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
knowledge-web-lib.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
8
crates/knowledge-pgr-server/src/main.rs
Normal file
8
crates/knowledge-pgr-server/src/main.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
// file: crates/knowledge-pgr-server/src/main.rs
|
||||
// version: 1
|
||||
|
||||
//! Thin launcher for the knowledge-pgr-server process.
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
return knowledge_web_lib::run_server(knowledge_web_lib::ServerSite::Pgr, "KNOWLEDGE_PGR_BIND_ADDRESS");
|
||||
}
|
||||
15
crates/knowledge-snippets-server/Cargo.toml
Normal file
15
crates/knowledge-snippets-server/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
# file: crates/knowledge-snippets-server/Cargo.toml
|
||||
# version: 1
|
||||
|
||||
[package]
|
||||
name = "knowledge-snippets-server"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
knowledge-web-lib.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
8
crates/knowledge-snippets-server/src/main.rs
Normal file
8
crates/knowledge-snippets-server/src/main.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
// file: crates/knowledge-snippets-server/src/main.rs
|
||||
// version: 1
|
||||
|
||||
//! Thin launcher for the knowledge-snippets-server process.
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
return knowledge_web_lib::run_server(knowledge_web_lib::ServerSite::Snippets, "KNOWLEDGE_SNIPPETS_BIND_ADDRESS");
|
||||
}
|
||||
20
crates/knowledge-web-lib/Cargo.toml
Normal file
20
crates/knowledge-web-lib/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
# file: crates/knowledge-web-lib/Cargo.toml
|
||||
# version: 1
|
||||
|
||||
[package]
|
||||
name = "knowledge-web-lib"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
actix-files = { version = "0.7.0", default-features = false }
|
||||
actix-web = { version = "4.15.0", default-features = false }
|
||||
knowledge-domain-lib.workspace = true
|
||||
maud = { version = "0.27.0", features = ["actix-web"] }
|
||||
serde = { version = "1.0.229", features = ["derive"] }
|
||||
serde_json = "1.0.151"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
195
crates/knowledge-web-lib/src/assets.rs
Normal file
195
crates/knowledge-web-lib/src/assets.rs
Normal file
@@ -0,0 +1,195 @@
|
||||
// file: crates/knowledge-web-lib/src/assets.rs
|
||||
// version: 1
|
||||
|
||||
//! Vite development and production asset resolution.
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize)]
|
||||
struct ManifestEntry {
|
||||
file: String,
|
||||
#[serde(default)]
|
||||
css: Vec<String>,
|
||||
#[serde(default, rename = "isEntry")]
|
||||
is_entry: bool,
|
||||
}
|
||||
|
||||
/// Internal resolved asset set consumed by the Maud renderer and Actix runtime.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) struct ViteAssets {
|
||||
scripts: Vec<String>,
|
||||
stylesheets: Vec<String>,
|
||||
static_assets_directory: Option<std::path::PathBuf>,
|
||||
static_mount_path: Option<String>,
|
||||
}
|
||||
|
||||
impl crate::ViteAssets {
|
||||
/// Build development asset references from a configured Vite origin.
|
||||
pub(crate) fn development(origin: &str) -> Result<crate::ViteAssets, String> {
|
||||
let normalized = origin.trim().trim_end_matches('/');
|
||||
if normalized.is_empty() {
|
||||
return Err("KNOWLEDGE_VITE_DEV_ORIGIN must not be empty".to_owned());
|
||||
}
|
||||
return Ok(crate::ViteAssets {
|
||||
scripts: vec![format!("{normalized}/@vite/client"), format!("{normalized}/src/main.ts")],
|
||||
stylesheets: Vec::new(),
|
||||
static_assets_directory: None,
|
||||
static_mount_path: None,
|
||||
});
|
||||
}
|
||||
|
||||
/// Resolve production asset references from Vite manifest JSON text.
|
||||
pub(crate) fn from_manifest_text(manifest: &str, asset_base: &str, static_assets_directory: std::path::PathBuf) -> Result<crate::ViteAssets, String> {
|
||||
let entries: std::collections::BTreeMap<String, ManifestEntry> = match serde_json::from_str(manifest) {
|
||||
Ok(value) => value,
|
||||
Err(error) => return Err(format!("invalid Vite manifest JSON: {error}")),
|
||||
};
|
||||
let entry = match entries.get("src/main.ts") {
|
||||
Some(value) => value,
|
||||
None => return Err("Vite manifest is missing src/main.ts".to_owned()),
|
||||
};
|
||||
if !entry.is_entry {
|
||||
return Err("Vite manifest src/main.ts is not marked as an entry".to_owned());
|
||||
}
|
||||
if entry.file.trim().is_empty() {
|
||||
return Err("Vite manifest src/main.ts has an empty JavaScript file".to_owned());
|
||||
}
|
||||
let javascript = match asset_url(asset_base, entry.file.as_str()) {
|
||||
Ok(value) => value,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let mut stylesheets = Vec::new();
|
||||
for stylesheet in &entry.css {
|
||||
let resolved = match asset_url(asset_base, stylesheet.as_str()) {
|
||||
Ok(value) => value,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
stylesheets.push(resolved);
|
||||
}
|
||||
let static_mount_path = local_static_mount_path(asset_base);
|
||||
return Ok(crate::ViteAssets {
|
||||
scripts: vec![javascript],
|
||||
stylesheets,
|
||||
static_assets_directory: Some(static_assets_directory),
|
||||
static_mount_path,
|
||||
});
|
||||
}
|
||||
|
||||
/// Return module script URLs in load order.
|
||||
pub(crate) fn scripts(&self) -> &[String] {
|
||||
return self.scripts.as_slice();
|
||||
}
|
||||
|
||||
/// Return production stylesheet URLs. Development CSS is injected by Vite.
|
||||
pub(crate) fn stylesheets(&self) -> &[String] {
|
||||
return self.stylesheets.as_slice();
|
||||
}
|
||||
|
||||
/// Return the local production assets directory when static serving is available.
|
||||
pub(crate) fn static_assets_directory(&self) -> Option<&std::path::Path> {
|
||||
match self.static_assets_directory.as_deref() {
|
||||
Some(value) => return Some(value),
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the local HTTP mount used for built assets, or `None` for an external asset base.
|
||||
pub(crate) fn static_mount_path(&self) -> Option<&str> {
|
||||
match self.static_mount_path.as_deref() {
|
||||
Some(value) => return Some(value),
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve the active Vite asset mode from process environment variables.
|
||||
pub(crate) fn load_assets_from_environment() -> Result<crate::ViteAssets, std::io::Error> {
|
||||
let development_origin = environment_value("KNOWLEDGE_VITE_DEV_ORIGIN");
|
||||
let manifest_path = environment_value("KNOWLEDGE_VITE_MANIFEST_PATH");
|
||||
match (development_origin, manifest_path) {
|
||||
(Some(_), Some(_)) => {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"configure either KNOWLEDGE_VITE_DEV_ORIGIN or KNOWLEDGE_VITE_MANIFEST_PATH, not both",
|
||||
));
|
||||
},
|
||||
(Some(origin), None) => {
|
||||
let assets = match crate::ViteAssets::development(origin.as_str()) {
|
||||
Ok(value) => value,
|
||||
Err(error) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, error)),
|
||||
};
|
||||
return Ok(assets);
|
||||
},
|
||||
(None, Some(path)) => {
|
||||
return load_manifest_assets(path.as_str());
|
||||
},
|
||||
(None, None) => {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "configure KNOWLEDGE_VITE_DEV_ORIGIN or KNOWLEDGE_VITE_MANIFEST_PATH"));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn environment_value(name: &str) -> Option<String> {
|
||||
match std::env::var(name) {
|
||||
Ok(value) => {
|
||||
if value.trim().is_empty() {
|
||||
return None;
|
||||
}
|
||||
return Some(value);
|
||||
},
|
||||
Err(_) => return None,
|
||||
}
|
||||
}
|
||||
|
||||
fn load_manifest_assets(path: &str) -> Result<crate::ViteAssets, std::io::Error> {
|
||||
let manifest_path = std::path::PathBuf::from(path);
|
||||
let text = match std::fs::read_to_string(manifest_path.as_path()) {
|
||||
Ok(value) => value,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let manifest_directory = match manifest_path.parent() {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Vite manifest path has no parent directory"));
|
||||
},
|
||||
};
|
||||
let output_root = match manifest_directory.parent() {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Vite manifest path must be located below an output root"));
|
||||
},
|
||||
};
|
||||
let asset_base = match environment_value("KNOWLEDGE_ASSET_BASE_PATH") {
|
||||
Some(value) => value,
|
||||
None => "/".to_owned(),
|
||||
};
|
||||
let assets = match crate::ViteAssets::from_manifest_text(text.as_str(), asset_base.as_str(), output_root.join("assets")) {
|
||||
Ok(value) => value,
|
||||
Err(error) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, error)),
|
||||
};
|
||||
return Ok(assets);
|
||||
}
|
||||
|
||||
fn asset_url(base: &str, relative: &str) -> Result<String, String> {
|
||||
let normalized_base = base.trim();
|
||||
let normalized_relative = relative.trim().trim_start_matches('/');
|
||||
if normalized_base.is_empty() {
|
||||
return Err("KNOWLEDGE_ASSET_BASE_PATH must not be empty".to_owned());
|
||||
}
|
||||
if normalized_relative.is_empty() {
|
||||
return Err("Vite manifest contains an empty asset path".to_owned());
|
||||
}
|
||||
if normalized_base == "/" {
|
||||
return Ok(format!("/{normalized_relative}"));
|
||||
}
|
||||
return Ok(format!("{}/{normalized_relative}", normalized_base.trim_end_matches('/')));
|
||||
}
|
||||
|
||||
fn local_static_mount_path(base: &str) -> Option<String> {
|
||||
let normalized = base.trim();
|
||||
if !normalized.starts_with('/') {
|
||||
return None;
|
||||
}
|
||||
if normalized == "/" {
|
||||
return Some("/assets".to_owned());
|
||||
}
|
||||
return Some(format!("{}/assets", normalized.trim_end_matches('/')));
|
||||
}
|
||||
27
crates/knowledge-web-lib/src/lib.rs
Normal file
27
crates/knowledge-web-lib/src/lib.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
// file: crates/knowledge-web-lib/src/lib.rs
|
||||
// version: 1
|
||||
|
||||
//! Shared Actix Web and Maud composition for the three Knowledge server launchers.
|
||||
//!
|
||||
//! The crate owns the minimal HTTP runtime, Maud rendering and Vite asset resolution shared by
|
||||
//! PGR, HOWTOs and snippets. It does not own authentication, administration or persistence.
|
||||
|
||||
mod assets;
|
||||
mod page;
|
||||
mod runtime;
|
||||
mod site;
|
||||
|
||||
/// Start one configured Knowledge Actix Web server.
|
||||
pub use self::runtime::run_server;
|
||||
/// Identify the public Knowledge facade owned by one server launcher.
|
||||
pub use self::site::ServerSite;
|
||||
|
||||
/// Internal asset description shared by the renderer and runtime.
|
||||
pub(crate) use self::assets::ViteAssets;
|
||||
/// Internal loader that resolves development or production assets from environment configuration.
|
||||
pub(crate) use self::assets::load_assets_from_environment;
|
||||
/// Internal Maud renderer for the public foundation page.
|
||||
pub(crate) use self::page::render_index_page;
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_tests;
|
||||
52
crates/knowledge-web-lib/src/page.rs
Normal file
52
crates/knowledge-web-lib/src/page.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
// file: crates/knowledge-web-lib/src/page.rs
|
||||
// version: 1
|
||||
|
||||
//! Maud rendering adapted from the static template-lab contract.
|
||||
|
||||
/// Render the minimal public page shared by the three server launchers.
|
||||
pub(crate) fn render_index_page(site: crate::ServerSite, assets: &crate::ViteAssets) -> maud::Markup {
|
||||
return maud::html! {
|
||||
(maud::DOCTYPE)
|
||||
html lang="fr" {
|
||||
head {
|
||||
meta charset="UTF-8";
|
||||
meta name="viewport" content="width=device-width, initial-scale=1";
|
||||
title { (site.page_title()) }
|
||||
@for stylesheet in assets.stylesheets() {
|
||||
link rel="stylesheet" href=(stylesheet);
|
||||
}
|
||||
}
|
||||
body {
|
||||
main class="container py-5 knowledge-template-shell" data-content-family=(family_slug(site.content_family())) {
|
||||
header class="mb-5" {
|
||||
p class="text-body-secondary mb-2" { "SaseDev Knowledge" }
|
||||
h1 class="display-5" { (site.heading()) }
|
||||
p class="lead mb-0" { (site.lead()) }
|
||||
}
|
||||
section class="row g-4" aria-label="Famille de contenu" {
|
||||
div class="col-12" {
|
||||
article class="card knowledge-family-card" {
|
||||
div class="card-body" {
|
||||
h2 class="h4" { (site.heading()) }
|
||||
p class="card-text" { (site.lead()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@for script in assets.scripts() {
|
||||
script type="module" src=(script) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn family_slug(family: knowledge_domain_lib::ContentFamily) -> &'static str {
|
||||
match family {
|
||||
knowledge_domain_lib::ContentFamily::Pgr => return "pgr",
|
||||
knowledge_domain_lib::ContentFamily::Howto => return "howtos",
|
||||
knowledge_domain_lib::ContentFamily::Snippet => return "snippets",
|
||||
knowledge_domain_lib::ContentFamily::ProjectLocal => return "project-local",
|
||||
}
|
||||
}
|
||||
73
crates/knowledge-web-lib/src/runtime.rs
Normal file
73
crates/knowledge-web-lib/src/runtime.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
// file: crates/knowledge-web-lib/src/runtime.rs
|
||||
// version: 1
|
||||
|
||||
//! Shared Actix Web process composition.
|
||||
|
||||
struct ServerState {
|
||||
site: crate::ServerSite,
|
||||
assets: crate::ViteAssets,
|
||||
}
|
||||
|
||||
/// Start one Knowledge HTTP server using environment-owned bind and asset configuration.
|
||||
///
|
||||
/// `bind_environment` names the launcher-specific environment variable containing the socket
|
||||
/// address, for example `127.0.0.1:18081`. The function never chooses a deployment address.
|
||||
pub fn run_server(site: crate::ServerSite, bind_environment: &str) -> std::io::Result<()> {
|
||||
let bind_address = match std::env::var(bind_environment) {
|
||||
Ok(value) => {
|
||||
if value.trim().is_empty() {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, format!("{bind_environment} must not be empty")));
|
||||
}
|
||||
value
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, format!("{bind_environment} must be configured")));
|
||||
},
|
||||
};
|
||||
let assets = match crate::load_assets_from_environment() {
|
||||
Ok(value) => value,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let static_assets_directory = match assets.static_assets_directory() {
|
||||
Some(value) => Some(value.to_path_buf()),
|
||||
None => None,
|
||||
};
|
||||
let static_mount_path = match assets.static_mount_path() {
|
||||
Some(value) => Some(value.to_owned()),
|
||||
None => None,
|
||||
};
|
||||
let state = actix_web::web::Data::new(ServerState { site, assets });
|
||||
return actix_web::rt::System::new().block_on(async move {
|
||||
let server = actix_web::HttpServer::new(move || {
|
||||
let app_state = state.clone();
|
||||
let static_directory = static_assets_directory.clone();
|
||||
let static_mount = static_mount_path.clone();
|
||||
return actix_web::App::new()
|
||||
.app_data(app_state)
|
||||
.route("/", actix_web::web::get().to(index))
|
||||
.route("/healthz", actix_web::web::get().to(health))
|
||||
.configure(move |configuration| {
|
||||
configure_static_assets(configuration, static_mount.as_deref(), static_directory.as_deref());
|
||||
});
|
||||
});
|
||||
let bound = match server.bind(bind_address.as_str()) {
|
||||
Ok(value) => value,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
return bound.run().await;
|
||||
});
|
||||
}
|
||||
|
||||
async fn index(state: actix_web::web::Data<ServerState>) -> maud::Markup {
|
||||
return crate::render_index_page(state.site, &state.assets);
|
||||
}
|
||||
|
||||
async fn health() -> actix_web::HttpResponse {
|
||||
return actix_web::HttpResponse::Ok().content_type("text/plain; charset=utf-8").body("ok");
|
||||
}
|
||||
|
||||
fn configure_static_assets(configuration: &mut actix_web::web::ServiceConfig, mount_path: Option<&str>, directory: Option<&std::path::Path>) {
|
||||
if let (Some(mount), Some(path)) = (mount_path, directory) {
|
||||
configuration.service(actix_files::Files::new(mount, path.to_path_buf()));
|
||||
}
|
||||
}
|
||||
53
crates/knowledge-web-lib/src/site.rs
Normal file
53
crates/knowledge-web-lib/src/site.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
// file: crates/knowledge-web-lib/src/site.rs
|
||||
// version: 1
|
||||
|
||||
//! Public facade identities used by the shared Web runtime.
|
||||
|
||||
/// Public Knowledge facade served by one launcher.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum ServerSite {
|
||||
/// Normative PGR facade.
|
||||
Pgr,
|
||||
/// Procedural HOWTOs facade.
|
||||
Howtos,
|
||||
/// Contextual snippets facade.
|
||||
Snippets,
|
||||
}
|
||||
|
||||
impl crate::ServerSite {
|
||||
/// Return the domain content family associated with this facade.
|
||||
pub fn content_family(self) -> knowledge_domain_lib::ContentFamily {
|
||||
match self {
|
||||
crate::ServerSite::Pgr => return knowledge_domain_lib::ContentFamily::Pgr,
|
||||
crate::ServerSite::Howtos => return knowledge_domain_lib::ContentFamily::Howto,
|
||||
crate::ServerSite::Snippets => return knowledge_domain_lib::ContentFamily::Snippet,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the page title used by the foundation renderer.
|
||||
pub fn page_title(self) -> &'static str {
|
||||
match self {
|
||||
crate::ServerSite::Pgr => return "SaseDev Knowledge — PGR",
|
||||
crate::ServerSite::Howtos => return "SaseDev Knowledge — HOWTOs",
|
||||
crate::ServerSite::Snippets => return "SaseDev Knowledge — Snippets",
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the main heading used by the foundation renderer.
|
||||
pub fn heading(self) -> &'static str {
|
||||
match self {
|
||||
crate::ServerSite::Pgr => return "Project Governance Rules",
|
||||
crate::ServerSite::Howtos => return "HOWTOs",
|
||||
crate::ServerSite::Snippets => return "Snippets",
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the short explanatory lead used by the foundation renderer.
|
||||
pub fn lead(self) -> &'static str {
|
||||
match self {
|
||||
crate::ServerSite::Pgr => return "Connaissance normative de gouvernance.",
|
||||
crate::ServerSite::Howtos => return "Guides procéduraux et explicatifs.",
|
||||
crate::ServerSite::Snippets => return "Fragments courts et contextualisés.",
|
||||
}
|
||||
}
|
||||
}
|
||||
72
crates/knowledge-web-lib/src/unit_tests.rs
Normal file
72
crates/knowledge-web-lib/src/unit_tests.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
// file: crates/knowledge-web-lib/src/unit_tests.rs
|
||||
// version: 1
|
||||
|
||||
//! Unit tests for shared Web composition primitives.
|
||||
|
||||
#[test]
|
||||
fn development_assets_include_vite_client_before_main_entry() {
|
||||
let assets = match crate::ViteAssets::development("http://127.0.0.1:5173/") {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
assert!(error.is_empty(), "unexpected development asset error: {error}");
|
||||
return;
|
||||
},
|
||||
};
|
||||
assert_eq!(assets.scripts().len(), 2);
|
||||
assert_eq!(assets.scripts()[0], "http://127.0.0.1:5173/@vite/client");
|
||||
assert_eq!(assets.scripts()[1], "http://127.0.0.1:5173/src/main.ts");
|
||||
assert!(assets.stylesheets().is_empty());
|
||||
assert!(assets.static_assets_directory().is_none());
|
||||
assert!(assets.static_mount_path().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn production_manifest_resolves_hashes_and_asset_base() {
|
||||
let manifest = r#"{
|
||||
"src/main.ts": {
|
||||
"file": "assets/main-abc.js",
|
||||
"css": ["assets/main-def.css"],
|
||||
"isEntry": true
|
||||
}
|
||||
}"#;
|
||||
let assets = match crate::ViteAssets::from_manifest_text(manifest, "/", std::path::PathBuf::from("/tmp/knowledge-assets")) {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
assert!(error.is_empty(), "unexpected manifest error: {error}");
|
||||
return;
|
||||
},
|
||||
};
|
||||
assert_eq!(assets.scripts().len(), 1);
|
||||
assert_eq!(assets.scripts()[0], "/assets/main-abc.js");
|
||||
assert_eq!(assets.stylesheets().len(), 1);
|
||||
assert_eq!(assets.stylesheets()[0], "/assets/main-def.css");
|
||||
assert_eq!(assets.static_assets_directory(), Some(std::path::Path::new("/tmp/knowledge-assets")));
|
||||
assert_eq!(assets.static_mount_path(), Some("/assets"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_requires_the_stable_main_entry() {
|
||||
let manifest = r#"{"index.html": {"file": "assets/index.js", "isEntry": true}}"#;
|
||||
let result = crate::ViteAssets::from_manifest_text(manifest, "/", std::path::PathBuf::from("/tmp/knowledge-assets"));
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn maud_page_uses_resolved_assets_and_site_identity() {
|
||||
let assets = match crate::ViteAssets::from_manifest_text(
|
||||
r#"{"src/main.ts": {"file": "assets/main.js", "css": ["assets/main.css"], "isEntry": true}}"#,
|
||||
"/",
|
||||
std::path::PathBuf::from("/tmp/knowledge-assets"),
|
||||
) {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
assert!(error.is_empty(), "unexpected manifest error: {error}");
|
||||
return;
|
||||
},
|
||||
};
|
||||
let rendered = crate::render_index_page(crate::ServerSite::Pgr, &assets).into_string();
|
||||
assert!(rendered.contains("SaseDev Knowledge — PGR"));
|
||||
assert!(rendered.contains("/assets/main.js"));
|
||||
assert!(rendered.contains("/assets/main.css"));
|
||||
assert!(rendered.contains("data-content-family=\"pgr\""));
|
||||
}
|
||||
Reference in New Issue
Block a user