Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(turbopack): emit MODULE_FEATURE telemetry from turbopack #52356

Merged
merged 7 commits into from
Jul 24, 2023
20 changes: 12 additions & 8 deletions packages/next-swc/crates/napi/src/next_api/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use next_api::route::{Endpoint, WrittenEndpoint};
use turbo_tasks::Vc;
use turbopack_binding::turbopack::core::error::PrettyPrintError;

use super::utils::{get_issues, subscribe, NapiIssue, RootTask, TurbopackResult, VcArc};
use super::utils::{
get_diagnostics, get_issues, subscribe, NapiDiagnostic, NapiIssue, RootTask, TurbopackResult,
VcArc,
};

#[napi(object)]
#[derive(Default)]
Expand Down Expand Up @@ -71,20 +74,21 @@ pub async fn endpoint_write_to_disk(
) -> napi::Result<TurbopackResult<NapiWrittenEndpoint>> {
let turbo_tasks = endpoint.turbo_tasks().clone();
let endpoint = ***endpoint;
let (written, issues) = turbo_tasks
let (written, issues, diags) = turbo_tasks
.run_once(async move {
let write_to_disk = endpoint.write_to_disk();
let issues = get_issues(write_to_disk).await?;
let diags = get_diagnostics(write_to_disk).await?;
let written = write_to_disk.strongly_consistent().await?;
Ok((written, issues))
Ok((written, issues, diags))
})
.await
.map_err(|e| napi::Error::from_reason(PrettyPrintError(&e).to_string()))?;
// TODO diagnostics
Ok(TurbopackResult {
result: NapiWrittenEndpoint::from(&*written),
issues: issues.iter().map(|i| NapiIssue::from(&**i)).collect(),
diagnostics: vec![],
diagnostics: diags.iter().map(|d| NapiDiagnostic::from(d)).collect(),
})
}

Expand All @@ -103,16 +107,16 @@ pub fn endpoint_changed_subscribe(
move || async move {
let changed = endpoint.changed();
let issues = get_issues(changed).await?;
let diags = get_diagnostics(changed).await?;
changed.await?;
// TODO diagnostics
Ok(issues)
Ok((issues, diags))
},
|ctx| {
let issues = ctx.value;
let (issues, diags) = ctx.value;
Ok(vec![TurbopackResult {
result: (),
issues: issues.iter().map(|i| NapiIssue::from(&**i)).collect(),
diagnostics: vec![],
diagnostics: diags.iter().map(|d| NapiDiagnostic::from(d)).collect(),
}])
},
)
Expand Down
13 changes: 9 additions & 4 deletions packages/next-swc/crates/napi/src/next_api/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ use turbopack_binding::{
use super::{
endpoint::ExternalEndpoint,
utils::{
get_issues, serde_enum_to_string, subscribe, NapiDiagnostic, NapiIssue, RootTask, VcArc,
get_diagnostics, get_issues, serde_enum_to_string, subscribe, NapiDiagnostic, NapiIssue,
RootTask, VcArc,
},
};
use crate::register;
Expand Down Expand Up @@ -226,12 +227,16 @@ pub fn project_entrypoints_subscribe(
move || async move {
let entrypoints = project.entrypoints();
let issues = get_issues(entrypoints).await?;
let diags = get_diagnostics(entrypoints).await?;

let entrypoints = entrypoints.strongly_consistent().await?;

// TODO peek_issues and diagnostics
Ok((entrypoints, issues))
Ok((entrypoints, issues, diags))
},
move |ctx| {
let (entrypoints, issues) = ctx.value;
let (entrypoints, issues, diags) = ctx.value;

Ok(vec![NapiEntrypoints {
routes: entrypoints
.routes
Expand All @@ -249,7 +254,7 @@ pub fn project_entrypoints_subscribe(
.iter()
.map(|issue| NapiIssue::from(&**issue))
.collect(),
diagnostics: vec![],
diagnostics: diags.iter().map(|d| NapiDiagnostic::from(d)).collect(),
}])
},
)
Expand Down
38 changes: 35 additions & 3 deletions packages/next-swc/crates/napi/src/next_api/utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{future::Future, ops::Deref, sync::Arc};
use std::{collections::HashMap, future::Future, ops::Deref, sync::Arc};

use anyhow::{anyhow, Context, Result};
use napi::{
Expand All @@ -7,10 +7,11 @@ use napi::{
JsFunction, JsObject, JsUnknown, NapiRaw, NapiValue, Status,
};
use serde::Serialize;
use turbo_tasks::{unit, ReadRef, TaskId, TurboTasks, Vc};
use turbo_tasks::{unit, ReadRef, TaskId, TryJoinIterExt, TurboTasks, Vc};
use turbopack_binding::{
turbo::{tasks_fs::FileContent, tasks_memory::MemoryBackend},
turbopack::core::{
diagnostics::{Diagnostic, DiagnosticContextExt, PlainDiagnostic},
error::PrettyPrintError,
issue::{IssueContextExt, PlainIssue, PlainIssueSource, PlainSource},
source_pos::SourcePos,
Expand Down Expand Up @@ -85,6 +86,23 @@ pub async fn get_issues<T>(source: Vc<T>) -> Result<Vec<ReadRef<PlainIssue>>> {
issues.get_plain_issues().await
}

/// Collect [turbopack::core::diagnostics::Diagnostic] from given source,
/// returns [turbopack::core::diagnostics::PlainDiagnostic]
pub async fn get_diagnostics<T>(source: Vc<T>) -> Result<Vec<ReadRef<PlainDiagnostic>>> {
let captured_diags = source
.peek_diagnostics()
.await?
.strongly_consistent()
.await?;

captured_diags
.diagnostics
.iter()
.map(|d| d.into_plain())
.try_join()
.await
}

#[napi(object)]
pub struct NapiIssue {
pub severity: String,
Expand Down Expand Up @@ -178,7 +196,21 @@ impl From<SourcePos> for NapiSourcePos {
}

#[napi(object)]
pub struct NapiDiagnostic {}
pub struct NapiDiagnostic {
pub category: String,
pub name: String,
pub payload: HashMap<String, String>,
}

impl NapiDiagnostic {
pub fn from(diagnostic: &PlainDiagnostic) -> Self {
Self {
category: diagnostic.category.clone(),
name: diagnostic.name.clone(),
payload: diagnostic.payload.clone(),
}
}
}

pub struct TurbopackResult<T: ToNapiValue> {
pub result: T,
Expand Down
1 change: 1 addition & 0 deletions packages/next-swc/crates/next-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ mod next_route_matcher;
pub mod next_server;
pub mod next_server_component;
pub mod next_shared;
pub mod next_telemetry;
mod page_loader;
mod page_source;
pub mod pages_structure;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ use crate::{
get_next_client_resolved_map, mdx_import_source_file,
},
next_shared::{
resolve::UnsupportedModulesResolvePlugin,
resolve::{ModuleFeatureReportResolvePlugin, UnsupportedModulesResolvePlugin},
transforms::{
emotion::get_emotion_transform_plugin, get_relay_transform_plugin,
styled_components::get_styled_components_transform_plugin,
Expand Down Expand Up @@ -145,9 +145,10 @@ pub async fn get_client_resolve_options_context(
resolved_map: Some(next_client_resolved_map),
browser: true,
module: true,
plugins: vec![Vc::upcast(UnsupportedModulesResolvePlugin::new(
project_path,
))],
plugins: vec![
Vc::upcast(ModuleFeatureReportResolvePlugin::new(project_path)),
Vc::upcast(UnsupportedModulesResolvePlugin::new(project_path)),
],
..Default::default()
};
Ok(ResolveOptionsContext {
Expand Down
17 changes: 11 additions & 6 deletions packages/next-swc/crates/next-core/src/next_edge/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ use turbopack_binding::{
};

use crate::{
mode::NextMode, next_client::context::get_client_assets_path, next_config::NextConfig,
next_import_map::get_next_edge_import_map, next_server::context::ServerContextType,
next_shared::resolve::UnsupportedModulesResolvePlugin, util::foreign_code_context_condition,
mode::NextMode,
next_client::context::get_client_assets_path,
next_config::NextConfig,
next_import_map::get_next_edge_import_map,
next_server::context::ServerContextType,
next_shared::resolve::{ModuleFeatureReportResolvePlugin, UnsupportedModulesResolvePlugin},
util::foreign_code_context_condition,
};

fn defines() -> CompileTimeDefines {
Expand Down Expand Up @@ -93,9 +97,10 @@ pub async fn get_edge_resolve_options_context(
import_map: Some(next_edge_import_map),
module: true,
browser: true,
plugins: vec![Vc::upcast(UnsupportedModulesResolvePlugin::new(
project_path,
))],
plugins: vec![
Vc::upcast(ModuleFeatureReportResolvePlugin::new(project_path)),
Vc::upcast(UnsupportedModulesResolvePlugin::new(project_path)),
],
..Default::default()
};

Expand Down
12 changes: 10 additions & 2 deletions packages/next-swc/crates/next-core/src/next_server/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ use crate::{
next_import_map::{get_next_server_import_map, mdx_import_source_file},
next_server::resolve::ExternalPredicate,
next_shared::{
resolve::UnsupportedModulesResolvePlugin,
resolve::{ModuleFeatureReportResolvePlugin, UnsupportedModulesResolvePlugin},
transforms::{
emotion::get_emotion_transform_plugin, get_relay_transform_plugin,
styled_components::get_styled_components_transform_plugin,
Expand Down Expand Up @@ -97,6 +97,7 @@ pub async fn get_server_resolve_options_context(
get_next_server_import_map(project_path, ty, mode, next_config, execution_context);
let foreign_code_context_condition = foreign_code_context_condition(next_config).await?;
let root_dir = project_path.root().resolve().await?;
let module_feature_report_resolve_plugin = ModuleFeatureReportResolvePlugin::new(project_path);
let unsupported_modules_resolve_plugin = UnsupportedModulesResolvePlugin::new(project_path);
let server_component_externals_plugin = ExternalCjsModulesResolvePlugin::new(
project_path,
Expand All @@ -118,6 +119,7 @@ pub async fn get_server_resolve_options_context(
custom_conditions: vec![mode.node_env().to_string(), "node".to_string()],
import_map: Some(next_server_import_map),
plugins: vec![
Vc::upcast(module_feature_report_resolve_plugin),
Vc::upcast(external_cjs_modules_plugin),
Vc::upcast(unsupported_modules_resolve_plugin),
],
Expand Down Expand Up @@ -146,6 +148,7 @@ pub async fn get_server_resolve_options_context(
],
import_map: Some(next_server_import_map),
plugins: vec![
Vc::upcast(module_feature_report_resolve_plugin),
Vc::upcast(server_component_externals_plugin),
Vc::upcast(unsupported_modules_resolve_plugin),
],
Expand Down Expand Up @@ -175,6 +178,7 @@ pub async fn get_server_resolve_options_context(
],
import_map: Some(next_server_import_map),
plugins: vec![
Vc::upcast(module_feature_report_resolve_plugin),
Vc::upcast(server_component_externals_plugin),
Vc::upcast(unsupported_modules_resolve_plugin),
],
Expand All @@ -197,6 +201,7 @@ pub async fn get_server_resolve_options_context(
custom_conditions: vec![mode.node_env().to_string(), "node".to_string()],
import_map: Some(next_server_import_map),
plugins: vec![
Vc::upcast(module_feature_report_resolve_plugin),
Vc::upcast(server_component_externals_plugin),
Vc::upcast(unsupported_modules_resolve_plugin),
],
Expand All @@ -218,7 +223,10 @@ pub async fn get_server_resolve_options_context(
enable_node_externals: true,
module: true,
custom_conditions: vec![mode.node_env().to_string()],
plugins: vec![Vc::upcast(unsupported_modules_resolve_plugin)],
plugins: vec![
Vc::upcast(module_feature_report_resolve_plugin),
Vc::upcast(unsupported_modules_resolve_plugin),
],
..Default::default()
};
ResolveOptionsContext {
Expand Down
74 changes: 73 additions & 1 deletion packages/next-swc/crates/next-core/src/next_shared/resolve.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};

use anyhow::Result;
use lazy_static::lazy_static;
Expand All @@ -7,6 +7,7 @@ use turbo_tasks_fs::glob::Glob;
use turbopack_binding::{
turbo::tasks_fs::FileSystemPath,
turbopack::core::{
diagnostics::DiagnosticExt,
issue::{unsupported_module::UnsupportedModuleIssue, IssueExt},
resolve::{
parse::Request,
Expand All @@ -17,9 +18,27 @@ use turbopack_binding::{
},
};

use crate::next_telemetry::ModuleFeatureTelemetry;

lazy_static! {
static ref UNSUPPORTED_PACKAGES: HashSet<&'static str> = ["@vercel/og"].into();
static ref UNSUPPORTED_PACKAGE_PATHS: HashSet<(&'static str, &'static str)> = [].into();
// Set of the features we want to track, following existing references in webpack/plugins/telemetry-plugin.
static ref FEATURE_MODULES: HashMap<&'static str, Vec<&'static str>> = HashMap::from([
(
"next",
vec![
"/image",
"/future/image",
"/legacy/image",
"/script",
"/dynamic",
"/font/google",
"/font/local"
]
),
("@next", vec!["/font/google", "/font/local"])
]);
}

#[turbo_tasks::value]
Expand Down Expand Up @@ -82,3 +101,56 @@ impl ResolvePlugin for UnsupportedModulesResolvePlugin {
Ok(ResolveResultOption::none())
}
}

/// A resolver plugin tracks the usage of certain import paths, emit
/// telemetry events if there is a match.
#[turbo_tasks::value]
pub(crate) struct ModuleFeatureReportResolvePlugin {
root: Vc<FileSystemPath>,
}

#[turbo_tasks::value_impl]
impl ModuleFeatureReportResolvePlugin {
#[turbo_tasks::function]
pub fn new(root: Vc<FileSystemPath>) -> Vc<Self> {
ModuleFeatureReportResolvePlugin { root }.cell()
}
}

#[turbo_tasks::value_impl]
impl ResolvePlugin for ModuleFeatureReportResolvePlugin {
#[turbo_tasks::function]
fn after_resolve_condition(&self) -> Vc<ResolvePluginCondition> {
ResolvePluginCondition::new(self.root.root(), Glob::new("**".to_string()))
}

#[turbo_tasks::function]
async fn after_resolve(
&self,
_fs_path: Vc<FileSystemPath>,
_context: Vc<FileSystemPath>,
request: Vc<Request>,
) -> Result<Vc<ResolveResultOption>> {
if let Request::Module {
module,
path,
query: _,
} = &*request.await?
{
let feature_module = FEATURE_MODULES.get(module.as_str());
if let Some(feature_module) = feature_module {
let sub_path = feature_module
.iter()
.find(|sub_path| path.is_match(sub_path));

if let Some(sub_path) = sub_path {
ModuleFeatureTelemetry::new(format!("{}{}", module, sub_path), 1)
.cell()
.emit();
}
}
}

Ok(ResolveResultOption::none())
}
}