feat(turbopack): embed build time info, emits next features telemetry event (#53028)

### What?

closes WEB-1301. To collect some information inside of rust binary, embed it as build-time constant. It supersedes existing target triple embedding as well.
This commit is contained in:
OJ Kwon 2023-07-26 10:49:34 -07:00 committed by GitHub
parent f1bd1eda8e
commit 974accc2c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 285 additions and 21 deletions

50
Cargo.lock generated
View file

@ -2640,6 +2640,12 @@ version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
[[package]]
name = "is_debug"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06d198e9919d9822d5f7083ba8530e04de87841eaf21ead9af8f2304efd57c89"
[[package]] [[package]]
name = "isahc" name = "isahc"
version = "1.7.2" version = "1.7.2"
@ -3378,6 +3384,7 @@ dependencies = [
"once_cell", "once_cell",
"serde", "serde",
"serde_json", "serde_json",
"shadow-rs",
"tokio", "tokio",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
@ -3548,6 +3555,7 @@ dependencies = [
"sentry", "sentry",
"serde", "serde",
"serde_json", "serde_json",
"shadow-rs",
"tracing", "tracing",
"tracing-chrome", "tracing-chrome",
"tracing-futures", "tracing-futures",
@ -3743,6 +3751,15 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "num_threads"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "objc" name = "objc"
version = "0.2.7" version = "0.2.7"
@ -5189,6 +5206,18 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "shadow-rs"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "970538704756fd0bb4ec8cb89f80674afb661e7c0fe716f9ba5be57717742300"
dependencies = [
"const_format",
"is_debug",
"time 0.3.20",
"tzdb",
]
[[package]] [[package]]
name = "sharded-slab" name = "sharded-slab"
version = "0.1.4" version = "0.1.4"
@ -6870,6 +6899,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
dependencies = [ dependencies = [
"itoa", "itoa",
"libc",
"num_threads",
"serde", "serde",
"time-core", "time-core",
"time-macros 0.2.8", "time-macros 0.2.8",
@ -7983,6 +8014,25 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "tz-rs"
version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33851b15c848fad2cf4b105c6bb66eb9512b6f6c44a4b13f57c53c73c707e2b4"
dependencies = [
"const_fn",
]
[[package]]
name = "tzdb"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec758958f2fb5069cd7fae385be95cc8eceb8cdfd270c7d14de6034f0108d99e"
dependencies = [
"iana-time-zone",
"tz-rs",
]
[[package]] [[package]]
name = "ucd-trie" name = "ucd-trie"
version = "0.1.5" version = "0.1.5"

View file

@ -121,6 +121,7 @@ serde = { version = "1.0.152", features = ["derive"] }
serde_json = "1.0.93" serde_json = "1.0.93"
serde_qs = "0.11.0" serde_qs = "0.11.0"
serde_yaml = "0.9.17" serde_yaml = "0.9.17"
shadow-rs = { version = "0.23.0", default-features = false, features = ["tzdb"] }
syn = "1.0.107" syn = "1.0.107"
tempfile = "3.3.0" tempfile = "3.3.0"
thiserror = "1.0.38" thiserror = "1.0.38"

View file

@ -50,6 +50,7 @@ turbo-tasks = { workspace = true }
once_cell = { workspace = true } once_cell = { workspace = true }
serde = "1" serde = "1"
serde_json = "1" serde_json = "1"
shadow-rs = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
tracing-futures = "0.2.5" tracing-futures = "0.2.5"
tracing-subscriber = { workspace = true } tracing-subscriber = { workspace = true }
@ -84,6 +85,8 @@ sentry = { version = "0.27.0", default-features = false, features = [
napi-build = "2" napi-build = "2"
serde = "1" serde = "1"
serde_json = "1" serde_json = "1"
# It is not a mistake this dependency is specified in dep / build-dep both.
shadow-rs = { workspace = true }
turbopack-binding = { workspace = true, features = [ turbopack-binding = { workspace = true, features = [
"__turbo_tasks_build" "__turbo_tasks_build"
]} ]}

View file

@ -10,22 +10,15 @@ use turbopack_binding::turbo::tasks_build::generate_register;
extern crate napi_build; extern crate napi_build;
fn main() { fn main() {
// Emit current platform's target-triple into a text file to create static const // Generates, stores build-time information as static values.
// in util.rs // There are some places relying on correct values for this (i.e telemetry),
let out_dir = env::var("OUT_DIR").expect("Outdir should exist"); // So failing build if this fails.
let dest_path = Path::new(&out_dir).join("triple.txt"); shadow_rs::new().expect("Should able to generate build time information");
let mut f =
BufWriter::new(File::create(dest_path).expect("Failed to create target triple text"));
write!(
f,
"{}",
env::var("TARGET").expect("Target should be specified")
)
.expect("Failed to write target triple text");
// Emit current package.json's version field into a text file to create static // Emit current package.json's version field into a text file to create static
// const in util.rs This is being used to set correct release version for // const in util.rs This is being used to set correct release version for
// the sentry's crash reporter. // the sentry's crash reporter.
let out_dir = env::var("OUT_DIR").expect("Outdir should exist");
let pkg_file = let pkg_file =
File::open(Path::new("../../package.json")).expect("Should able to open package.json"); File::open(Path::new("../../package.json")).expect("Should able to open package.json");
let json: serde_json::Value = serde_json::from_reader(pkg_file).unwrap(); let json: serde_json::Value = serde_json::from_reader(pkg_file).unwrap();

View file

@ -58,6 +58,9 @@ pub mod turbopack;
pub mod turbotrace; pub mod turbotrace;
pub mod util; pub mod util;
// Declare build-time information variables generated in build.rs
shadow_rs::shadow!(build);
// don't use turbo malloc (`mimalloc`) on linux-musl-aarch64 because of the // don't use turbo malloc (`mimalloc`) on linux-musl-aarch64 because of the
// compile error // compile error
#[cfg(not(any( #[cfg(not(any(

View file

@ -41,6 +41,9 @@ pub struct NapiProjectOptions {
/// The contents of next.config.js, serialized to JSON. /// The contents of next.config.js, serialized to JSON.
pub next_config: String, pub next_config: String,
/// The contents of ts/config read by load-jsconfig, serialized to JSON.
pub js_config: String,
/// A map of environment variables to use when compiling code. /// A map of environment variables to use when compiling code.
pub env: Vec<NapiEnvVar>, pub env: Vec<NapiEnvVar>,
} }
@ -58,6 +61,7 @@ impl From<NapiProjectOptions> for ProjectOptions {
project_path: val.project_path, project_path: val.project_path,
watch: val.watch, watch: val.watch,
next_config: val.next_config, next_config: val.next_config,
js_config: val.js_config,
env: val env: val
.env .env
.into_iter() .into_iter()

View file

@ -41,13 +41,12 @@ use sentry::ClientOptions;
use tracing_chrome::{ChromeLayerBuilder, FlushGuard}; use tracing_chrome::{ChromeLayerBuilder, FlushGuard};
use tracing_subscriber::{filter, prelude::*, util::SubscriberInitExt, Layer}; use tracing_subscriber::{filter, prelude::*, util::SubscriberInitExt, Layer};
static TARGET_TRIPLE: &str = include_str!(concat!(env!("OUT_DIR"), "/triple.txt"));
#[allow(unused)] #[allow(unused)]
static PACKAGE_VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/package.txt")); static PACKAGE_VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/package.txt"));
#[napi] #[napi]
pub fn get_target_triple() -> String { pub fn get_target_triple() -> String {
TARGET_TRIPLE.to_owned() crate::build::BUILD_TARGET.to_string()
} }
pub trait MapErr<T>: Into<Result<T, anyhow::Error>> { pub trait MapErr<T>: Into<Result<T, anyhow::Error>> {

View file

@ -23,6 +23,7 @@ next-core = { workspace = true }
once_cell = { workspace = true } once_cell = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
shadow-rs = { workspace = true }
tokio = { workspace = true, features = ["full"] } tokio = { workspace = true, features = ["full"] }
turbopack-binding = { workspace = true, features = [ turbopack-binding = { workspace = true, features = [
"__turbo_tasks_memory", "__turbo_tasks_memory",
@ -42,6 +43,8 @@ tracing = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter", "json"] } tracing-subscriber = { workspace = true, features = ["env-filter", "json"] }
[build-dependencies] [build-dependencies]
# It is not a mistake this dependency is specified in dep / build-dep both.
shadow-rs = { workspace = true }
turbopack-binding = { workspace = true, features = [ turbopack-binding = { workspace = true, features = [
"__turbo_tasks_build" "__turbo_tasks_build"
]} ]}

View file

@ -1,5 +1,10 @@
use turbopack_binding::turbo::tasks_build::generate_register; use turbopack_binding::turbo::tasks_build::generate_register;
fn main() { fn main() {
// Generates, stores build-time information as static values.
// There are some places relying on correct values for this (i.e telemetry),
// So failing build if this fails.
shadow_rs::new().expect("Should able to generate build time information");
generate_register(); generate_register();
} }

View file

@ -8,6 +8,9 @@ mod pages;
pub mod project; pub mod project;
pub mod route; pub mod route;
// Declare build-time information variables generated in build.rs
shadow_rs::shadow!(build);
pub fn register() { pub fn register() {
next_core::register(); next_core::register();
turbopack_binding::turbopack::build::register(); turbopack_binding::turbopack::build::register();

View file

@ -7,8 +7,9 @@ use next_core::{
get_edge_chunking_context, get_edge_compile_time_info, get_edge_chunking_context, get_edge_compile_time_info,
mode::NextMode, mode::NextMode,
next_client::{get_client_chunking_context, get_client_compile_time_info}, next_client::{get_client_chunking_context, get_client_compile_time_info},
next_config::NextConfig, next_config::{JsConfig, NextConfig},
next_server::{get_server_chunking_context, get_server_compile_time_info}, next_server::{get_server_chunking_context, get_server_compile_time_info},
next_telemetry::NextFeatureTelemetry,
util::NextSourceConfig, util::NextSourceConfig,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -23,8 +24,8 @@ use turbopack_binding::{
turbopack::{ turbopack::{
build::BuildChunkingContext, build::BuildChunkingContext,
core::{ core::{
chunk::ChunkingContext, compile_time_info::CompileTimeInfo, environment::ServerAddr, chunk::ChunkingContext, compile_time_info::CompileTimeInfo, diagnostics::DiagnosticExt,
PROJECT_FILESYSTEM_NAME, environment::ServerAddr, PROJECT_FILESYSTEM_NAME,
}, },
dev::DevChunkingContext, dev::DevChunkingContext,
ecmascript::chunk::EcmascriptChunkingContext, ecmascript::chunk::EcmascriptChunkingContext,
@ -35,6 +36,7 @@ use turbopack_binding::{
use crate::{ use crate::{
app::{AppProject, OptionAppProject}, app::{AppProject, OptionAppProject},
build,
entrypoints::Entrypoints, entrypoints::Entrypoints,
pages::PagesProject, pages::PagesProject,
route::{Endpoint, Route}, route::{Endpoint, Route},
@ -53,6 +55,9 @@ pub struct ProjectOptions {
/// The contents of next.config.js, serialized to JSON. /// The contents of next.config.js, serialized to JSON.
pub next_config: String, pub next_config: String,
/// The contents of ts/config read by load-jsconfig, serialized to JSON.
pub js_config: String,
/// A map of environment variables to use when compiling code. /// A map of environment variables to use when compiling code.
pub env: Vec<(String, String)>, pub env: Vec<(String, String)>,
@ -92,12 +97,14 @@ impl ProjectContainer {
let this = self.await?; let this = self.await?;
let options = this.state.get(); let options = this.state.get();
let next_config = NextConfig::from_string(Vc::cell(options.next_config.clone())); let next_config = NextConfig::from_string(Vc::cell(options.next_config.clone()));
let js_config = JsConfig::from_string(Vc::cell(options.js_config.clone()));
let env: Vc<EnvMap> = Vc::cell(options.env.iter().cloned().collect()); let env: Vc<EnvMap> = Vc::cell(options.env.iter().cloned().collect());
Ok(Project { Ok(Project {
root_path: options.root_path.clone(), root_path: options.root_path.clone(),
project_path: options.project_path.clone(), project_path: options.project_path.clone(),
watch: options.watch, watch: options.watch,
next_config, next_config,
js_config,
env: Vc::upcast(env), env: Vc::upcast(env),
browserslist_query: "last 1 Chrome versions, last 1 Firefox versions, last 1 Safari \ browserslist_query: "last 1 Chrome versions, last 1 Firefox versions, last 1 Safari \
versions, last 1 Edge versions" versions, last 1 Edge versions"
@ -128,6 +135,9 @@ pub struct Project {
/// Next config. /// Next config.
next_config: Vc<NextConfig>, next_config: Vc<NextConfig>,
/// Js/Tsconfig read by load-jsconfig
js_config: Vc<JsConfig>,
/// A map of environment variables to use when compiling code. /// A map of environment variables to use when compiling code.
env: Vc<Box<dyn ProcessEnv>>, env: Vc<Box<dyn ProcessEnv>>,
@ -225,6 +235,11 @@ impl Project {
Ok(self.await?.next_config) Ok(self.await?.next_config)
} }
#[turbo_tasks::function]
pub(super) async fn js_config(self: Vc<Self>) -> Result<Vc<JsConfig>> {
Ok(self.await?.js_config)
}
#[turbo_tasks::function] #[turbo_tasks::function]
pub(super) fn execution_context(self: Vc<Self>) -> Vc<ExecutionContext> { pub(super) fn execution_context(self: Vc<Self>) -> Vc<ExecutionContext> {
let node_root = self.node_root(); let node_root = self.node_root();
@ -305,6 +320,84 @@ impl Project {
) )
} }
/// Emit a telemetry event corresponding to webpack configuration telemetry
/// (https://github.com/vercel/next.js/blob/9da305fe320b89ee2f8c3cfb7ecbf48856368913/packages/next/src/build/webpack-config.ts#L2516)
/// to detect which feature is enabled.
#[turbo_tasks::function]
async fn collect_project_feature_telemetry(self: Vc<Self>) -> Result<Vc<()>> {
let emit_event = |feature_name: &str, enabled: bool| {
NextFeatureTelemetry::new(feature_name.to_string(), enabled)
.cell()
.emit();
};
// First, emit an event for the binary target triple.
// This is different to webpack-config; when this is being called,
// it is always using SWC so we don't check swc here.
emit_event(build::BUILD_TARGET, true);
// Go over jsconfig and report enabled features.
let compiler_options = self.js_config().compiler_options().await?;
let compiler_options = compiler_options.as_object();
let experimental_decorators_enabled = compiler_options
.as_ref()
.and_then(|compiler_options| compiler_options.get("experimentalDecorators"))
.is_some();
let jsx_import_source_enabled = compiler_options
.as_ref()
.and_then(|compiler_options| compiler_options.get("jsxImportSource"))
.is_some();
emit_event("swcExperimentalDecorators", experimental_decorators_enabled);
emit_event("swcImportSource", jsx_import_source_enabled);
// Go over config and report enabled features.
// [TODO]: useSwcLoader is not being reported as it is not directly corresponds (it checks babel config existence)
// need to confirm what we'll do with turbopack.
let config = self.next_config();
emit_event("swcMinify", *config.swc_minify().await?);
emit_event(
"skipMiddlewareUrlNormalize",
*config.skip_middleware_url_normalize().await?,
);
emit_event(
"skipTrailingSlashRedirect",
*config.skip_trailing_slash_redirect().await?,
);
let config = &config.await?;
emit_event("modularizeImports", config.modularize_imports.is_some());
emit_event("transpilePackages", config.transpile_packages.is_some());
emit_event("turbotrace", config.experimental.turbotrace.is_some());
// compiler options
let compiler_options = config.compiler.as_ref();
let swc_relay_enabled = compiler_options.and_then(|c| c.relay.as_ref()).is_some();
let styled_components_enabled = compiler_options
.map(|c| c.styled_components.is_some())
.unwrap_or_default();
let react_remove_properties_enabled = compiler_options
.and_then(|c| c.react_remove_properties)
.unwrap_or_default();
let remove_console_enabled = compiler_options
.map(|c| c.remove_console.is_some())
.unwrap_or_default();
let emotion_enabled = compiler_options
.map(|c| c.emotion.is_some())
.unwrap_or_default();
emit_event("swcRelay", swc_relay_enabled);
emit_event("swcStyledComponents", styled_components_enabled);
emit_event("swcReactRemoveProperties", react_remove_properties_enabled);
emit_event("swcRemoveConsole", remove_console_enabled);
emit_event("swcEmotion", emotion_enabled);
Ok(unit())
}
#[turbo_tasks::function] #[turbo_tasks::function]
pub(super) fn ssr_chunking_context(self: Vc<Self>) -> Vc<BuildChunkingContext> { pub(super) fn ssr_chunking_context(self: Vc<Self>) -> Vc<BuildChunkingContext> {
self.server_chunking_context().with_layer("ssr".to_string()) self.server_chunking_context().with_layer("ssr".to_string())
@ -349,6 +442,8 @@ impl Project {
/// provided page_extensions). /// provided page_extensions).
#[turbo_tasks::function] #[turbo_tasks::function]
pub async fn entrypoints(self: Vc<Self>) -> Result<Vc<Entrypoints>> { pub async fn entrypoints(self: Vc<Self>) -> Result<Vc<Entrypoints>> {
self.collect_project_feature_telemetry().await?;
let mut routes = IndexMap::new(); let mut routes = IndexMap::new();
let app_project = self.app_project(); let app_project = self.app_project();
let pages_project = self.pages_project(); let pages_project = self.pages_project();

View file

@ -117,12 +117,14 @@ pub struct NextConfig {
public_runtime_config: IndexMap<String, serde_json::Value>, public_runtime_config: IndexMap<String, serde_json::Value>,
server_runtime_config: IndexMap<String, serde_json::Value>, server_runtime_config: IndexMap<String, serde_json::Value>,
static_page_generation_timeout: f64, static_page_generation_timeout: f64,
swc_minify: bool, swc_minify: Option<bool>,
target: Option<String>, target: Option<String>,
trailing_slash: bool, trailing_slash: bool,
typescript: TypeScriptConfig, typescript: TypeScriptConfig,
use_file_system_public_routes: bool, use_file_system_public_routes: bool,
webpack: Option<serde_json::Value>, webpack: Option<serde_json::Value>,
skip_middleware_url_normalize: Option<bool>,
skip_trailing_slash_redirect: Option<bool>,
} }
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, TraceRawVcs)] #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, TraceRawVcs)]
@ -446,15 +448,13 @@ pub struct ExperimentalConfig {
runtime: Option<serde_json::Value>, runtime: Option<serde_json::Value>,
scroll_restoration: Option<bool>, scroll_restoration: Option<bool>,
shared_pool: Option<bool>, shared_pool: Option<bool>,
skip_middleware_url_normalize: Option<bool>,
skip_trailing_slash_redirect: Option<bool>,
sri: Option<serde_json::Value>, sri: Option<serde_json::Value>,
swc_file_reading: Option<bool>, swc_file_reading: Option<bool>,
swc_minify: Option<bool>, swc_minify: Option<bool>,
swc_minify_debug_options: Option<serde_json::Value>, swc_minify_debug_options: Option<serde_json::Value>,
swc_trace_profiling: Option<bool>, swc_trace_profiling: Option<bool>,
transpile_packages: Option<Vec<String>>, transpile_packages: Option<Vec<String>>,
turbotrace: Option<serde_json::Value>, pub turbotrace: Option<serde_json::Value>,
url_imports: Option<serde_json::Value>, url_imports: Option<serde_json::Value>,
web_vitals_attribution: Option<serde_json::Value>, web_vitals_attribution: Option<serde_json::Value>,
worker_threads: Option<bool>, worker_threads: Option<bool>,
@ -653,6 +653,25 @@ impl NextConfig {
self.await?.sass_options.clone().unwrap_or_default(), self.await?.sass_options.clone().unwrap_or_default(),
)) ))
} }
#[turbo_tasks::function]
pub async fn swc_minify(self: Vc<Self>) -> Result<Vc<bool>> {
Ok(Vc::cell(self.await?.swc_minify.unwrap_or(false)))
}
#[turbo_tasks::function]
pub async fn skip_middleware_url_normalize(self: Vc<Self>) -> Result<Vc<bool>> {
Ok(Vc::cell(
self.await?.skip_middleware_url_normalize.unwrap_or(false),
))
}
#[turbo_tasks::function]
pub async fn skip_trailing_slash_redirect(self: Vc<Self>) -> Result<Vc<bool>> {
Ok(Vc::cell(
self.await?.skip_trailing_slash_redirect.unwrap_or(false),
))
}
} }
fn next_configs() -> Vc<Vec<String>> { fn next_configs() -> Vc<Vec<String>> {
@ -799,6 +818,34 @@ pub async fn has_next_config(context: Vc<FileSystemPath>) -> Result<Vc<bool>> {
))) )))
} }
/// A subset of ts/jsconfig that next.js implicitly
/// interops with.
#[turbo_tasks::value(serialization = "custom", eq = "manual")]
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JsConfig {
compiler_options: Option<serde_json::Value>,
}
#[turbo_tasks::value_impl]
impl JsConfig {
#[turbo_tasks::function]
pub async fn from_string(string: Vc<String>) -> Result<Vc<Self>> {
let string = string.await?;
let config: JsConfig = serde_json::from_str(&string)
.with_context(|| format!("failed to parse next.config.js: {}", string))?;
Ok(config.cell())
}
#[turbo_tasks::function]
pub async fn compiler_options(self: Vc<Self>) -> Result<Vc<serde_json::Value>> {
Ok(Vc::cell(
self.await?.compiler_options.clone().unwrap_or_default(),
))
}
}
#[turbo_tasks::value] #[turbo_tasks::value]
struct OutdatedConfigIssue { struct OutdatedConfigIssue {
path: Vc<FileSystemPath>, path: Vc<FileSystemPath>,

View file

@ -5,6 +5,48 @@ use turbopack_binding::{
turbopack::core::diagnostics::{Diagnostic, DiagnosticPayload}, turbopack::core::diagnostics::{Diagnostic, DiagnosticPayload},
}; };
/// A struct represent telemetry event if certain feature of next.js
/// is enabled, such as next.config.swcMinify.
/// This is an equivalent representation of the following code:
/// https://github.com/vercel/next.js/blob/9da305fe320b89ee2f8c3cfb7ecbf48856368913/packages/next/src/build/webpack-config.ts#L2516
#[turbo_tasks::value(shared)]
pub struct NextFeatureTelemetry {
pub event_name: String,
pub feature_name: String,
pub enabled: bool,
}
impl NextFeatureTelemetry {
pub fn new(feature_name: String, enabled: bool) -> Self {
NextFeatureTelemetry {
event_name: "EVENT_BUILD_FEATURE_USAGE".to_string(),
feature_name,
enabled,
}
}
}
#[turbo_tasks::value_impl]
impl Diagnostic for NextFeatureTelemetry {
#[turbo_tasks::function]
fn category(&self) -> Vc<String> {
Vc::cell("NextFeatureTelemetry_category_tbd".to_string())
}
#[turbo_tasks::function]
fn name(&self) -> Vc<String> {
Vc::cell(self.event_name.clone())
}
#[turbo_tasks::function]
fn payload(&self) -> Vc<DiagnosticPayload> {
Vc::cell(HashMap::from([(
self.feature_name.clone(),
self.enabled.to_string(),
)]))
}
}
/// A struct represent telemetry event for the feature usage, /// A struct represent telemetry event for the feature usage,
/// referred as `importing` a certain module. (i.e importing @next/image) /// referred as `importing` a certain module. (i.e importing @next/image)
#[turbo_tasks::value(shared)] #[turbo_tasks::value(shared)]

View file

@ -340,6 +340,17 @@ interface ProjectOptions {
*/ */
nextConfig: NextConfigComplete nextConfig: NextConfigComplete
/**
* Jsconfig, or tsconfig contents.
*
* Next.js implicitly requires to read it to support few options
* https://nextjs.org/docs/architecture/nextjs-compiler#legacy-decorators
* https://nextjs.org/docs/architecture/nextjs-compiler#importsource
*/
jsConfig: {
compilerOptions: object
}
/** /**
* A map of environment variables to use when compiling code. * A map of environment variables to use when compiling code.
*/ */
@ -561,6 +572,7 @@ function bindingToApi(binding: any, _wasm: boolean) {
return { return {
...options, ...options,
nextConfig: await serializeNextConfig(options.nextConfig), nextConfig: await serializeNextConfig(options.nextConfig),
jsConfig: JSON.stringify(options.jsConfig ?? {}),
env: Object.entries(options.env).map(([name, value]) => ({ env: Object.entries(options.env).map(([name, value]) => ({
name, name,
value, value,

View file

@ -226,6 +226,9 @@ const nextDev: CliCommand = async (argv) => {
if (experimentalTurbo) { if (experimentalTurbo) {
const { loadBindings } = const { loadBindings } =
require('../build/swc') as typeof import('../build/swc') require('../build/swc') as typeof import('../build/swc')
const { default: loadJsConfig } =
require('../build/load-jsconfig') as typeof import('../build/load-jsconfig')
const { jsConfig } = await loadJsConfig(dir, config)
resetEnv() resetEnv()
let bindings = await loadBindings() let bindings = await loadBindings()
@ -236,6 +239,7 @@ const nextDev: CliCommand = async (argv) => {
projectPath: dir, projectPath: dir,
rootPath: args['--root'] ?? findRootDir(dir) ?? dir, rootPath: args['--root'] ?? findRootDir(dir) ?? dir,
nextConfig: config, nextConfig: config,
jsConfig,
env: { env: {
NEXT_PUBLIC_ENV_VAR: 'world', NEXT_PUBLIC_ENV_VAR: 'world',
}, },