turbopack_cli/
arguments.rs

1use std::{
2    net::IpAddr,
3    path::{Path, PathBuf},
4    str::FromStr,
5};
6
7use anyhow::anyhow;
8use clap::{Args, Parser, ValueEnum};
9use serde::{Deserialize, Serialize};
10use turbo_tasks::{NonLocalValue, TaskInput, trace::TraceRawVcs};
11use turbopack_core::issue::IssueSeverity;
12
13#[derive(Debug, Parser)]
14#[clap(author, version, about, long_about = None)]
15pub enum Arguments {
16    Build(BuildArguments),
17    Dev(DevArguments),
18}
19
20impl Arguments {
21    /// The directory of the application. see [CommonArguments]::dir
22    pub fn dir(&self) -> Option<&Path> {
23        match self {
24            Arguments::Build(args) => args.common.dir.as_deref(),
25            Arguments::Dev(args) => args.common.dir.as_deref(),
26        }
27    }
28
29    /// The number of worker threads to use. see [CommonArguments]::worker_threads
30    pub fn worker_threads(&self) -> Option<usize> {
31        match self {
32            Arguments::Build(args) => args.common.worker_threads,
33            Arguments::Dev(args) => args.common.worker_threads,
34        }
35    }
36}
37
38#[derive(
39    Copy,
40    Clone,
41    Debug,
42    ValueEnum,
43    PartialEq,
44    Eq,
45    Serialize,
46    Deserialize,
47    Hash,
48    TaskInput,
49    NonLocalValue,
50    TraceRawVcs,
51)]
52pub enum Target {
53    Browser,
54    Node,
55}
56
57#[derive(Debug, Args, Clone)]
58pub struct CommonArguments {
59    /// The entrypoints of the project. Resolved relative to the project's
60    /// directory (`--dir`).
61    #[clap(value_parser)]
62    pub entries: Option<Vec<String>>,
63
64    /// The directory of the application.
65    /// If no directory is provided, the current directory will be used.
66    #[clap(short, long, value_parser)]
67    pub dir: Option<PathBuf>,
68
69    /// The root directory of the project. Nothing outside of this directory can
70    /// be accessed. e. g. the monorepo root.
71    /// If no directory is provided, `dir` will be used.
72    #[clap(long, value_parser)]
73    pub root: Option<PathBuf>,
74
75    /// Filter by issue severity.
76    #[clap(short, long)]
77    pub log_level: Option<IssueSeverityCliOption>,
78
79    /// Show all log messages without limit.
80    #[clap(long)]
81    pub show_all: bool,
82
83    /// Expand the log details.
84    #[clap(long)]
85    pub log_detail: bool,
86
87    /// Whether to enable full task stats recording in Turbo Engine.
88    #[clap(long)]
89    pub full_stats: bool,
90
91    /// Whether to build for the `browser` or `node`
92    #[clap(long)]
93    pub target: Option<Target>,
94
95    /// Number of worker threads to use for parallel processing
96    #[clap(long)]
97    pub worker_threads: Option<usize>,
98    // Enable experimental garbage collection with the provided memory limit in
99    // MB.
100    // #[clap(long)]
101    // pub memory_limit: Option<usize>,
102}
103
104#[derive(Debug, Args)]
105#[clap(author, version, about, long_about = None)]
106pub struct DevArguments {
107    #[clap(flatten)]
108    pub common: CommonArguments,
109
110    /// The port number on which to start the application
111    /// Note: setting env PORT allows to configure port without explicit cli
112    /// args. However, this is temporary measure to conform with existing
113    /// next.js devserver and can be removed in the future.
114    #[clap(short, long, value_parser, default_value_t = 3000, env = "PORT")]
115    pub port: u16,
116
117    /// Hostname on which to start the application
118    #[clap(short = 'H', long, value_parser, default_value = "0.0.0.0")]
119    pub hostname: IpAddr,
120
121    /// Compile all, instead of only compiling referenced assets when their
122    /// parent asset is requested
123    #[clap(long)]
124    pub eager_compile: bool,
125
126    /// Don't open the browser automatically when the dev server has started.
127    #[clap(long)]
128    pub no_open: bool,
129
130    // ==
131    // = Inherited options from next-dev, need revisit later.
132    // ==
133    /// If port is not explicitly specified, use different port if it's already
134    /// in use.
135    #[clap(long)]
136    pub allow_retry: bool,
137}
138
139#[derive(Debug, Args)]
140#[clap(author, version, about, long_about = None)]
141pub struct BuildArguments {
142    #[clap(flatten)]
143    pub common: CommonArguments,
144
145    /// Don't generate sourcemaps.
146    #[clap(long)]
147    pub no_sourcemap: bool,
148
149    /// Don't minify build output.
150    #[clap(long)]
151    pub no_minify: bool,
152
153    /// Don't perform scope hoisting.
154    #[clap(long)]
155    pub no_scope_hoist: bool,
156
157    /// Drop the `TurboTasks` object upon exit. By default we intentionally leak this memory, as
158    /// we're about to exit the process anyways, but that can cause issues with valgrind or other
159    /// leak detectors.
160    #[clap(long, hide = true)]
161    pub force_memory_cleanup: bool,
162}
163
164#[derive(Clone, Copy, PartialEq, Eq, Debug)]
165pub struct IssueSeverityCliOption(pub IssueSeverity);
166
167impl serde::Serialize for IssueSeverityCliOption {
168    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
169        serializer.serialize_str(&self.0.to_string())
170    }
171}
172
173impl<'de> serde::Deserialize<'de> for IssueSeverityCliOption {
174    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
175        let s = String::deserialize(deserializer)?;
176        <IssueSeverityCliOption as std::str::FromStr>::from_str(&s)
177            .map_err(serde::de::Error::custom)
178    }
179}
180
181impl ValueEnum for IssueSeverityCliOption {
182    fn value_variants<'a>() -> &'a [Self] {
183        const VARIANTS: [IssueSeverityCliOption; 8] = [
184            IssueSeverityCliOption(IssueSeverity::Bug),
185            IssueSeverityCliOption(IssueSeverity::Fatal),
186            IssueSeverityCliOption(IssueSeverity::Error),
187            IssueSeverityCliOption(IssueSeverity::Warning),
188            IssueSeverityCliOption(IssueSeverity::Hint),
189            IssueSeverityCliOption(IssueSeverity::Note),
190            IssueSeverityCliOption(IssueSeverity::Suggestion),
191            IssueSeverityCliOption(IssueSeverity::Info),
192        ];
193        &VARIANTS
194    }
195
196    fn to_possible_value<'a>(&self) -> Option<clap::builder::PossibleValue> {
197        Some(clap::builder::PossibleValue::new(self.0.as_str()).help(self.0.as_help_str()))
198    }
199}
200
201impl FromStr for IssueSeverityCliOption {
202    type Err = anyhow::Error;
203
204    fn from_str(s: &str) -> Result<Self, Self::Err> {
205        <IssueSeverityCliOption as clap::ValueEnum>::from_str(s, true).map_err(|s| anyhow!("{}", s))
206    }
207}