mirror of
https://github.com/typst/typst
synced 2025-06-28 00:03:17 +08:00
Put HTTP server behind on-by-default feature flag (#5532)
This commit is contained in:
parent
8e4f5f21e0
commit
caa72f4ec2
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@ -64,6 +64,7 @@ jobs:
|
|||||||
components: clippy, rustfmt
|
components: clippy, rustfmt
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- run: cargo clippy --workspace --all-targets --all-features
|
- run: cargo clippy --workspace --all-targets --all-features
|
||||||
|
- run: cargo clippy --workspace --all-targets --no-default-features
|
||||||
- run: cargo fmt --check --all
|
- run: cargo fmt --check --all
|
||||||
- run: cargo doc --workspace --no-deps
|
- run: cargo doc --workspace --no-deps
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ shell-escape = { workspace = true }
|
|||||||
sigpipe = { workspace = true }
|
sigpipe = { workspace = true }
|
||||||
tar = { workspace = true }
|
tar = { workspace = true }
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
tiny_http = { workspace = true }
|
tiny_http = { workspace = true, optional = true }
|
||||||
toml = { workspace = true }
|
toml = { workspace = true }
|
||||||
ureq = { workspace = true }
|
ureq = { workspace = true }
|
||||||
xz2 = { workspace = true, optional = true }
|
xz2 = { workspace = true, optional = true }
|
||||||
@ -65,11 +65,14 @@ color-print = { workspace = true }
|
|||||||
semver = { workspace = true }
|
semver = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["embed-fonts"]
|
default = ["embed-fonts", "http-server"]
|
||||||
|
|
||||||
# Embeds some fonts into the binary, see typst-kit
|
# Embeds some fonts into the binary, see typst-kit
|
||||||
embed-fonts = ["typst-kit/embed-fonts"]
|
embed-fonts = ["typst-kit/embed-fonts"]
|
||||||
|
|
||||||
|
# Enables the built-in HTTP server for `typst watch` and HTML export.
|
||||||
|
http-server = ["dep:tiny_http"]
|
||||||
|
|
||||||
# Permits the CLI to update itself without a package manager.
|
# Permits the CLI to update itself without a package manager.
|
||||||
self-update = ["dep:self-replace", "dep:xz2", "dep:zip"]
|
self-update = ["dep:self-replace", "dep:xz2", "dep:zip"]
|
||||||
|
|
||||||
|
@ -98,20 +98,10 @@ pub struct WatchCommand {
|
|||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub args: CompileArgs,
|
pub args: CompileArgs,
|
||||||
|
|
||||||
/// Disables the built-in HTTP server for HTML export.
|
/// Arguments for the HTTP server.
|
||||||
#[clap(long)]
|
#[cfg(feature = "http-server")]
|
||||||
pub no_serve: bool,
|
#[clap(flatten)]
|
||||||
|
pub server: ServerArgs,
|
||||||
/// Disables the injected live reload script for HTML export. The HTML that
|
|
||||||
/// is written to disk isn't affected either way.
|
|
||||||
#[clap(long)]
|
|
||||||
pub no_reload: bool,
|
|
||||||
|
|
||||||
/// The port where HTML is served.
|
|
||||||
///
|
|
||||||
/// Defaults to the first free port in the range 3000-3005.
|
|
||||||
#[clap(long)]
|
|
||||||
pub port: Option<u16>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes a new project from a template.
|
/// Initializes a new project from a template.
|
||||||
@ -354,7 +344,7 @@ pub struct PackageArgs {
|
|||||||
pub package_cache_path: Option<PathBuf>,
|
pub package_cache_path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Common arguments to customize available fonts
|
/// Common arguments to customize available fonts.
|
||||||
#[derive(Debug, Clone, Parser)]
|
#[derive(Debug, Clone, Parser)]
|
||||||
pub struct FontArgs {
|
pub struct FontArgs {
|
||||||
/// Adds additional directories that are recursively searched for fonts.
|
/// Adds additional directories that are recursively searched for fonts.
|
||||||
@ -375,6 +365,26 @@ pub struct FontArgs {
|
|||||||
pub ignore_system_fonts: bool,
|
pub ignore_system_fonts: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Arguments for the HTTP server.
|
||||||
|
#[cfg(feature = "http-server")]
|
||||||
|
#[derive(Debug, Clone, Parser)]
|
||||||
|
pub struct ServerArgs {
|
||||||
|
/// Disables the built-in HTTP server for HTML export.
|
||||||
|
#[clap(long)]
|
||||||
|
pub no_serve: bool,
|
||||||
|
|
||||||
|
/// Disables the injected live reload script for HTML export. The HTML that
|
||||||
|
/// is written to disk isn't affected either way.
|
||||||
|
#[clap(long)]
|
||||||
|
pub no_reload: bool,
|
||||||
|
|
||||||
|
/// The port where HTML is served.
|
||||||
|
///
|
||||||
|
/// Defaults to the first free port in the range 3000-3005.
|
||||||
|
#[clap(long)]
|
||||||
|
pub port: Option<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! display_possible_values {
|
macro_rules! display_possible_values {
|
||||||
($ty:ty) => {
|
($ty:ty) => {
|
||||||
impl Display for $ty {
|
impl Display for $ty {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::ffi::OsString;
|
use std::ffi::OsStr;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@ -23,6 +23,7 @@ use crate::args::{
|
|||||||
CompileArgs, CompileCommand, DiagnosticFormat, Input, Output, OutputFormat,
|
CompileArgs, CompileCommand, DiagnosticFormat, Input, Output, OutputFormat,
|
||||||
PdfStandard, WatchCommand,
|
PdfStandard, WatchCommand,
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "http-server")]
|
||||||
use crate::server::HtmlServer;
|
use crate::server::HtmlServer;
|
||||||
use crate::timings::Timer;
|
use crate::timings::Timer;
|
||||||
|
|
||||||
@ -72,6 +73,7 @@ pub struct CompileConfig {
|
|||||||
/// watch` sessions with images.
|
/// watch` sessions with images.
|
||||||
pub export_cache: ExportCache,
|
pub export_cache: ExportCache,
|
||||||
/// Server for `typst watch` to HTML.
|
/// Server for `typst watch` to HTML.
|
||||||
|
#[cfg(feature = "http-server")]
|
||||||
pub server: Option<HtmlServer>,
|
pub server: Option<HtmlServer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,17 +141,18 @@ impl CompileConfig {
|
|||||||
PdfStandards::new(&list)?
|
PdfStandards::new(&list)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut server = None;
|
#[cfg(feature = "http-server")]
|
||||||
let mut watching = false;
|
let server = match watch {
|
||||||
if let Some(command) = watch {
|
Some(command)
|
||||||
watching = true;
|
if output_format == OutputFormat::Html && !command.server.no_serve =>
|
||||||
if output_format == OutputFormat::Html && !command.no_serve {
|
{
|
||||||
server = Some(HtmlServer::new(&input, command.port, !command.no_reload)?);
|
Some(HtmlServer::new(&input, &command.server)?)
|
||||||
}
|
}
|
||||||
}
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
watching,
|
watching: watch.is_some(),
|
||||||
input,
|
input,
|
||||||
output,
|
output,
|
||||||
output_format,
|
output_format,
|
||||||
@ -161,6 +164,7 @@ impl CompileConfig {
|
|||||||
diagnostic_format: args.process.diagnostic_format,
|
diagnostic_format: args.process.diagnostic_format,
|
||||||
open: args.open.clone(),
|
open: args.open.clone(),
|
||||||
export_cache: ExportCache::new(),
|
export_cache: ExportCache::new(),
|
||||||
|
#[cfg(feature = "http-server")]
|
||||||
server,
|
server,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -241,6 +245,7 @@ fn export_html(document: &HtmlDocument, config: &CompileConfig) -> SourceResult<
|
|||||||
let html = typst_html::html(document)?;
|
let html = typst_html::html(document)?;
|
||||||
let result = config.output.write(html.as_bytes());
|
let result = config.output.write(html.as_bytes());
|
||||||
|
|
||||||
|
#[cfg(feature = "http-server")]
|
||||||
if let Some(server) = &config.server {
|
if let Some(server) = &config.server {
|
||||||
server.update(html);
|
server.update(html);
|
||||||
}
|
}
|
||||||
@ -556,30 +561,37 @@ fn write_make_deps(world: &mut SystemWorld, config: &CompileConfig) -> StrResult
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Opens the output if desired, with:
|
/// Opens the output if desired.
|
||||||
/// - The default file viewer if `open` is `None`.
|
|
||||||
/// - The given viewer provided by `open` if it is `Some`.
|
|
||||||
///
|
|
||||||
/// If the file could not be opened, an error is returned.
|
|
||||||
fn open_output(config: &mut CompileConfig) -> StrResult<()> {
|
fn open_output(config: &mut CompileConfig) -> StrResult<()> {
|
||||||
let Some(open) = config.open.take() else { return Ok(()) };
|
let Some(viewer) = config.open.take() else { return Ok(()) };
|
||||||
|
|
||||||
let path = if let Some(server) = &config.server {
|
#[cfg(feature = "http-server")]
|
||||||
OsString::from(format!("http://{}", server.addr()))
|
if let Some(server) = &config.server {
|
||||||
} else if let Output::Path(path) = &config.output {
|
let url = format!("http://{}", server.addr());
|
||||||
// Some resource openers require the path to be canonicalized.
|
return open_path(OsStr::new(&url), viewer.as_deref());
|
||||||
path.canonicalize()
|
}
|
||||||
.map_err(|err| eco_format!("failed to canonicalize path ({err})"))?
|
|
||||||
.into_os_string()
|
|
||||||
} else {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(app) = &open {
|
// Can't open stdout.
|
||||||
open::with_detached(&path, app)
|
let Output::Path(path) = &config.output else { return Ok(()) };
|
||||||
.map_err(|err| eco_format!("failed to open file with {} ({})", app, err))
|
|
||||||
|
// Some resource openers require the path to be canonicalized.
|
||||||
|
let path = path
|
||||||
|
.canonicalize()
|
||||||
|
.map_err(|err| eco_format!("failed to canonicalize path ({err})"))?;
|
||||||
|
|
||||||
|
open_path(path.as_os_str(), viewer.as_deref())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opens the given file using:
|
||||||
|
///
|
||||||
|
/// - The default file viewer if `app` is `None`.
|
||||||
|
/// - The given viewer provided by `app` if it is `Some`.
|
||||||
|
fn open_path(path: &OsStr, viewer: Option<&str>) -> StrResult<()> {
|
||||||
|
if let Some(viewer) = viewer {
|
||||||
|
open::with_detached(path, viewer)
|
||||||
|
.map_err(|err| eco_format!("failed to open file with {} ({})", viewer, err))
|
||||||
} else {
|
} else {
|
||||||
open::that_detached(&path).map_err(|err| {
|
open::that_detached(path).map_err(|err| {
|
||||||
let openers = open::commands(path)
|
let openers = open::commands(path)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|command| command.get_program().to_string_lossy())
|
.map(|command| command.get_program().to_string_lossy())
|
||||||
|
@ -6,6 +6,7 @@ mod greet;
|
|||||||
mod init;
|
mod init;
|
||||||
mod package;
|
mod package;
|
||||||
mod query;
|
mod query;
|
||||||
|
#[cfg(feature = "http-server")]
|
||||||
mod server;
|
mod server;
|
||||||
mod terminal;
|
mod terminal;
|
||||||
mod timings;
|
mod timings;
|
||||||
|
@ -7,7 +7,7 @@ use parking_lot::{Condvar, Mutex, MutexGuard};
|
|||||||
use tiny_http::{Header, Request, Response, StatusCode};
|
use tiny_http::{Header, Request, Response, StatusCode};
|
||||||
use typst::diag::{bail, StrResult};
|
use typst::diag::{bail, StrResult};
|
||||||
|
|
||||||
use crate::args::Input;
|
use crate::args::{Input, ServerArgs};
|
||||||
|
|
||||||
/// Serves HTML with live reload.
|
/// Serves HTML with live reload.
|
||||||
pub struct HtmlServer {
|
pub struct HtmlServer {
|
||||||
@ -17,8 +17,9 @@ pub struct HtmlServer {
|
|||||||
|
|
||||||
impl HtmlServer {
|
impl HtmlServer {
|
||||||
/// Create a new HTTP server that serves live HTML.
|
/// Create a new HTTP server that serves live HTML.
|
||||||
pub fn new(input: &Input, port: Option<u16>, reload: bool) -> StrResult<Self> {
|
pub fn new(input: &Input, args: &ServerArgs) -> StrResult<Self> {
|
||||||
let (addr, server) = start_server(port)?;
|
let reload = !args.no_reload;
|
||||||
|
let (addr, server) = start_server(args.port)?;
|
||||||
|
|
||||||
let placeholder = PLACEHOLDER_HTML.replace("{INPUT}", &input.to_string());
|
let placeholder = PLACEHOLDER_HTML.replace("{INPUT}", &input.to_string());
|
||||||
let bucket = Arc::new(Bucket::new(placeholder));
|
let bucket = Arc::new(Bucket::new(placeholder));
|
||||||
|
@ -293,6 +293,7 @@ impl Status {
|
|||||||
out.reset()?;
|
out.reset()?;
|
||||||
writeln!(out, " {}", config.output)?;
|
writeln!(out, " {}", config.output)?;
|
||||||
|
|
||||||
|
#[cfg(feature = "http-server")]
|
||||||
if let Some(server) = &config.server {
|
if let Some(server) = &config.server {
|
||||||
out.set_color(&color)?;
|
out.set_color(&color)?;
|
||||||
write!(out, "serving at")?;
|
write!(out, "serving at")?;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user