Skip to content

Commit

Permalink
refactor(next/dev): reusable start_server (#2455)
Browse files Browse the repository at this point in the history
  • Loading branch information
kwonoj committed Oct 28, 2022
1 parent 58935f7 commit c63eefe
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 183 deletions.
88 changes: 88 additions & 0 deletions crates/next-dev/src/devserver_options.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use std::{net::IpAddr, path::PathBuf};

#[cfg(feature = "cli")]
use clap::Parser;
use turbopack_cli_utils::issue::IssueSeverityCliOption;

#[derive(Debug)]
#[cfg_attr(feature = "cli", derive(Parser))]
#[cfg_attr(feature = "cli", clap(author, version, about, long_about = None))]
#[cfg_attr(feature = "serializable", derive(serde::Deserialize))]
#[cfg_attr(feature = "serializable", serde(rename_all = "camelCase"))]
pub struct DevServerOptions {
/// The directory of the Next.js application.
/// If no directory is provided, the current directory will be used.
#[cfg_attr(feature = "cli", clap(value_parser))]
#[cfg_attr(feature = "serializable", serde(default))]
pub dir: Option<PathBuf>,

/// The root directory of the project. Nothing outside of this directory can
/// be accessed. e. g. the monorepo root.
/// If no directory is provided, `dir` will be used.
#[cfg_attr(feature = "cli", clap(long, value_parser))]
#[cfg_attr(feature = "serializable", serde(default))]
pub root: Option<PathBuf>,

/// The port number on which to start the application
#[cfg_attr(
feature = "cli",
clap(short, long, value_parser, default_value_t = 3000)
)]
#[cfg_attr(feature = "serializable", serde(default = "default_port"))]
pub port: u16,

/// Hostname on which to start the application
#[cfg_attr(
feature = "cli",
clap(short = 'H', long, value_parser, default_value = "0.0.0.0")
)]
#[cfg_attr(feature = "serializable", serde(default = "default_host"))]
pub hostname: IpAddr,

/// Compile all, instead of only compiling referenced assets when their
/// parent asset is requested
#[cfg_attr(feature = "cli", clap(long))]
#[cfg_attr(feature = "serializable", serde(default))]
pub eager_compile: bool,

/// Don't open the browser automatically when the dev server has started.
#[cfg_attr(feature = "cli", clap(long))]
#[cfg_attr(feature = "serializable", serde(default))]
pub no_open: bool,

#[cfg_attr(feature = "cli", clap(short, long))]
#[cfg_attr(feature = "serializable", serde(default))]
/// Filter by issue severity.
pub log_level: Option<IssueSeverityCliOption>,

#[cfg_attr(feature = "cli", clap(long))]
#[cfg_attr(feature = "serializable", serde(default))]
/// Show all log messages without limit.
pub show_all: bool,

#[cfg_attr(feature = "cli", clap(long))]
#[cfg_attr(feature = "serializable", serde(default))]
/// Expand the log details.
pub log_detail: bool,

// Inherited options from next-dev, need revisit later.
// This is not supported by CLI yet.
#[cfg_attr(feature = "serializable", serde(default))]
pub allow_retry: bool,
#[cfg_attr(feature = "serializable", serde(default))]
pub dev: bool,
#[cfg_attr(feature = "serializable", serde(default))]
pub is_next_dev_command: bool,
#[cfg_attr(feature = "serializable", serde(default))]
pub server_components_external_packages: Vec<String>,
}

#[cfg(feature = "serializable")]
fn default_port() -> u16 {
3000
}

#[cfg(feature = "serializable")]
fn default_host() -> IpAddr {
IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0))
}
108 changes: 106 additions & 2 deletions crates/next-dev/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
#![feature(future_join)]
#![feature(min_specialization)]

pub mod devserver_options;
mod turbo_tasks_viz;

use std::{collections::HashSet, env::current_dir, net::IpAddr, path::MAIN_SEPARATOR, sync::Arc};
use std::{
collections::HashSet,
env::current_dir,
future::join,
net::IpAddr,
path::MAIN_SEPARATOR,
sync::Arc,
time::{Duration, Instant},
};

use anyhow::{anyhow, Context, Result};
use devserver_options::DevServerOptions;
use next_core::{
create_app_source, create_server_rendered_source, create_web_entry_source, env::load_env,
source_map::NextSourceMapTraceContentSourceVc,
};
use owo_colors::OwoColorize;
use turbo_tasks::{
primitives::StringsVc, RawVc, TransientInstance, TransientValue, TurboTasks, Value,
primitives::StringsVc, util::FormatDuration, RawVc, TransientInstance, TransientValue,
TurboTasks, Value,
};
use turbo_tasks_fs::{DiskFileSystemVc, FileSystemVc};
use turbo_tasks_memory::MemoryBackend;
Expand Down Expand Up @@ -280,3 +292,95 @@ pub fn register() {
next_core::register();
include!(concat!(env!("OUT_DIR"), "/register.rs"));
}

/// Start a devserver with the given options.
pub async fn start_server(options: &DevServerOptions) -> Result<()> {
let start = Instant::now();

#[cfg(feature = "tokio_console")]
console_subscriber::init();
register();

let dir = options
.dir
.as_ref()
.map(|dir| dir.canonicalize())
.unwrap_or_else(current_dir)
.context("project directory can't be found")?
.to_str()
.context("project directory contains invalid characters")?
.to_string();

let root_dir = if let Some(root) = options.root.as_ref() {
root.canonicalize()
.context("root directory can't be found")?
.to_str()
.context("root directory contains invalid characters")?
.to_string()
} else {
dir.clone()
};

let tt = TurboTasks::new(MemoryBackend::new());
let tt_clone = tt.clone();

let mut server = NextDevServerBuilder::new(tt, dir, root_dir)
.entry_request("src/index".into())
.eager_compile(options.eager_compile)
.hostname(options.hostname)
.port(options.port)
.log_detail(options.log_detail)
.show_all(options.show_all)
.log_level(
options
.log_level
.map_or_else(|| IssueSeverity::Warning, |l| l.0),
);

for package in options.server_components_external_packages.iter() {
server = server.server_component_external(package.to_string());
}

let server = server.build().await?;

{
let index_uri = if server.addr.ip().is_loopback() || server.addr.ip().is_unspecified() {
format!("http://localhost:{}", server.addr.port())
} else {
format!("http://{}", server.addr)
};
println!(
"{} - started server on {}:{}, url: {}",
"ready".green(),
server.addr.ip(),
server.addr.port(),
index_uri
);
if !options.no_open {
let _ = webbrowser::open(&index_uri);
}
}

let stats_future = async move {
println!(
"{event_type} - initial compilation {start}",
event_type = "event".purple(),
start = FormatDuration(start.elapsed()),
);

loop {
let (elapsed, _count) = tt_clone
.get_or_wait_update_info(Duration::from_millis(100))
.await;
println!(
"{event_type} - updated in {elapsed}",
event_type = "event".purple(),
elapsed = FormatDuration(elapsed),
);
}
};

join!(stats_future, async { server.future.await.unwrap() }).await;

Ok(())
}
Loading

0 comments on commit c63eefe

Please sign in to comment.