Skip to content

Commit

Permalink
turbopack-cli: modularize code to support turbopack build (#5487)
Browse files Browse the repository at this point in the history
This work prepares for `turbopack build` by extracting code common
between dev and build, such as asset context.

This also makes the cli accept a variadic list of entries rather than an
unnamed directory parameter. The project directory is now available
under `--dir` or `-d`.

Test Plan: `cargo run -p turbopack-cli dev` with a `src/index.js`
present and `cargo run -p turbopack-cli dev src/entry.js` with
`src/entry.js` present.
  • Loading branch information
wbinnssmith committed Jul 20, 2023
1 parent f9467a6 commit 046ea29
Show file tree
Hide file tree
Showing 16 changed files with 441 additions and 270 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion crates/node-file-trace/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,11 @@ async fn run<B: Backend + 'static, F: Future<Output = ()>>(

let console_ui = ConsoleUi::new(log_options);
Vc::upcast::<Box<dyn IssueReporter>>(console_ui)
.report_issues(TransientInstance::new(issues), source)
.report_issues(
TransientInstance::new(issues),
source,
IssueSeverity::Error.cell(),
)
.await?;

if has_return_value {
Expand Down
4 changes: 2 additions & 2 deletions crates/turbopack-build/src/chunking_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,8 @@ where
let chunks = ecmascript_chunks
.iter()
.copied()
.map(|chunk| Vc::upcast(chunk))
.chain(css_chunks.iter().copied().map(|chunk| Vc::upcast(chunk)))
.map(Vc::upcast)
.chain(css_chunks.iter().copied().map(Vc::upcast))
.chain(other_chunks)
.collect();

Expand Down
3 changes: 2 additions & 1 deletion crates/turbopack-cli-utils/src/issue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ impl IssueReporter for ConsoleUi {
&self,
issues: TransientInstance<ReadRef<CapturedIssues>>,
source: TransientValue<RawVc>,
min_failing_severity: Vc<IssueSeverity>,
) -> Result<Vc<bool>> {
let issues = &*issues;
let LogOptions {
Expand Down Expand Up @@ -387,7 +388,7 @@ impl IssueReporter for ConsoleUi {
}

let severity = plain_issue.severity;
if severity == IssueSeverity::Fatal {
if severity <= *min_failing_severity.await? {
has_fatal = true;
}

Expand Down
1 change: 1 addition & 0 deletions crates/turbopack-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ turbo-tasks-fs = { workspace = true }
turbo-tasks-malloc = { workspace = true, default-features = false }
turbo-tasks-memory = { workspace = true }
turbopack = { workspace = true }
turbopack-build = { workspace = true }
turbopack-cli-utils = { workspace = true }
turbopack-core = { workspace = true }
turbopack-dev = { workspace = true }
Expand Down
2 changes: 2 additions & 0 deletions crates/turbopack-cli/benches/bundler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ impl Bundler for Turbopack {
let mut proc = Command::new(binary)
.args([
"dev",
"--dir",
test_dir
.to_str()
.ok_or_else(|| anyhow!("failed to convert test directory path to string"))?,
"src/index",
"--no-open",
"--port",
"0",
Expand Down
9 changes: 7 additions & 2 deletions crates/turbopack-cli/src/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@ impl Arguments {
}
}

#[derive(Debug, Args)]
#[derive(Debug, Args, Clone)]
pub struct CommonArguments {
/// The entrypoints of the project. Resolved relative to the project's
/// directory (`--dir`).
#[clap(value_parser)]
pub entries: Option<Vec<String>>,

/// The directory of the application.
/// If no directory is provided, the current directory will be used.
#[clap(value_parser)]
#[clap(short, long, value_parser)]
pub dir: Option<PathBuf>,

/// The root directory of the project. Nothing outside of this directory can
Expand Down
200 changes: 200 additions & 0 deletions crates/turbopack-cli/src/contexts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
use std::{collections::HashMap, fmt};

use anyhow::Result;
use turbo_tasks::{Value, Vc};
use turbo_tasks_fs::{FileSystem, FileSystemPath};
use turbopack::{
condition::ContextCondition,
module_options::{CustomEcmascriptTransformPlugins, JsxTransformOptions, ModuleOptionsContext},
resolve_options_context::ResolveOptionsContext,
ModuleAssetContext,
};
use turbopack_core::{
compile_time_defines,
compile_time_info::{CompileTimeDefines, CompileTimeInfo},
context::AssetContext,
environment::{BrowserEnvironment, Environment, ExecutionEnvironment},
resolve::options::{ImportMap, ImportMapping},
};
use turbopack_dev::react_refresh::assert_can_resolve_react_refresh;
use turbopack_ecmascript_plugins::transform::{
emotion::{EmotionTransformConfig, EmotionTransformer},
styled_components::{StyledComponentsTransformConfig, StyledComponentsTransformer},
styled_jsx::StyledJsxTransformer,
};
use turbopack_node::execution_context::ExecutionContext;

#[turbo_tasks::value(shared)]
pub enum NodeEnv {
Development,
Production,
}

impl fmt::Display for NodeEnv {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NodeEnv::Development => f.write_str("development"),
NodeEnv::Production => f.write_str("production"),
}
}
}

async fn foreign_code_context_condition() -> Result<ContextCondition> {
Ok(ContextCondition::InDirectory("node_modules".to_string()))
}

#[turbo_tasks::function]
pub async fn get_client_import_map(project_path: Vc<FileSystemPath>) -> Result<Vc<ImportMap>> {
let mut import_map = ImportMap::empty();

import_map.insert_singleton_alias("@swc/helpers", project_path);
import_map.insert_singleton_alias("styled-jsx", project_path);
import_map.insert_singleton_alias("react", project_path);
import_map.insert_singleton_alias("react-dom", project_path);

import_map.insert_wildcard_alias(
"@vercel/turbopack-ecmascript-runtime/",
ImportMapping::PrimaryAlternative(
"./*".to_string(),
Some(turbopack_ecmascript_runtime::embed_fs().root()),
)
.cell(),
);

Ok(import_map.cell())
}

#[turbo_tasks::function]
pub async fn get_client_resolve_options_context(
project_path: Vc<FileSystemPath>,
) -> Result<Vc<ResolveOptionsContext>> {
let next_client_import_map = get_client_import_map(project_path);
let module_options_context = ResolveOptionsContext {
enable_node_modules: Some(project_path.root().resolve().await?),
custom_conditions: vec!["development".to_string()],
import_map: Some(next_client_import_map),
browser: true,
module: true,
..Default::default()
};
Ok(ResolveOptionsContext {
enable_typescript: true,
enable_react: true,
rules: vec![(
foreign_code_context_condition().await?,
module_options_context.clone().cell(),
)],
..module_options_context
}
.cell())
}

#[turbo_tasks::function]
async fn get_client_module_options_context(
project_path: Vc<FileSystemPath>,
execution_context: Vc<ExecutionContext>,
env: Vc<Environment>,
) -> Result<Vc<ModuleOptionsContext>> {
let module_options_context = ModuleOptionsContext {
preset_env_versions: Some(env),
execution_context: Some(execution_context),
..Default::default()
};

let resolve_options_context = get_client_resolve_options_context(project_path);

let enable_react_refresh =
assert_can_resolve_react_refresh(project_path, resolve_options_context)
.await?
.is_found();

let enable_jsx = Some(
JsxTransformOptions {
react_refresh: enable_react_refresh,
..Default::default()
}
.cell(),
);

let custom_ecma_transform_plugins = Some(CustomEcmascriptTransformPlugins::cell(
CustomEcmascriptTransformPlugins {
source_transforms: vec![
Vc::cell(Box::new(
EmotionTransformer::new(&EmotionTransformConfig::default())
.expect("Should be able to create emotion transformer"),
) as _),
Vc::cell(Box::new(StyledComponentsTransformer::new(
&StyledComponentsTransformConfig::default(),
)) as _),
Vc::cell(Box::new(StyledJsxTransformer::new()) as _),
],
output_transforms: vec![],
},
));

let module_options_context = ModuleOptionsContext {
enable_jsx,
enable_postcss_transform: Some(Default::default()),
enable_typescript_transform: Some(Default::default()),
rules: vec![(
foreign_code_context_condition().await?,
module_options_context.clone().cell(),
)],
custom_ecma_transform_plugins,
..module_options_context
}
.cell();

Ok(module_options_context)
}

#[turbo_tasks::function]
pub fn get_client_asset_context(
project_path: Vc<FileSystemPath>,
execution_context: Vc<ExecutionContext>,
compile_time_info: Vc<CompileTimeInfo>,
) -> Vc<Box<dyn AssetContext>> {
let resolve_options_context = get_client_resolve_options_context(project_path);
let module_options_context = get_client_module_options_context(
project_path,
execution_context,
compile_time_info.environment(),
);

let context: Vc<Box<dyn AssetContext>> = Vc::upcast(ModuleAssetContext::new(
Vc::cell(HashMap::new()),
compile_time_info,
module_options_context,
resolve_options_context,
));

context
}

fn client_defines(node_env: &NodeEnv) -> Vc<CompileTimeDefines> {
compile_time_defines!(
process.turbopack = true,
process.env.NODE_ENV = node_env.to_string()
)
.cell()
}

#[turbo_tasks::function]
pub async fn get_client_compile_time_info(
browserslist_query: String,
node_env: Vc<NodeEnv>,
) -> Result<Vc<CompileTimeInfo>> {
Ok(
CompileTimeInfo::builder(Environment::new(Value::new(ExecutionEnvironment::Browser(
BrowserEnvironment {
dom: true,
web_worker: false,
service_worker: false,
browserslist_query,
}
.into(),
))))
.defines(client_defines(&*node_env.await?))
.cell(),
)
}
Loading

0 comments on commit 046ea29

Please sign in to comment.