Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/next-napi-bindings/src/next_api/analyze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ pub async fn write_analyze_data_with_issues_operation(
app_dir_only: bool,
) -> Result<Vc<WriteAnalyzeResult>> {
let analyze_data_op = write_analyze_data_with_issues_operation_inner(project, app_dir_only);
let filter = project.project().issue_filter();
let filter = project.project().issue_filter().await?;

let (_analyze_data, issues, effects) =
strongly_consistent_catch_collectables(analyze_data_op, filter).await?;
strongly_consistent_catch_collectables(analyze_data_op, &*filter).await?;

Ok(WriteAnalyzeResult { issues, effects }.cell())
}
Expand Down
12 changes: 7 additions & 5 deletions crates/next-napi-bindings/src/next_api/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,14 @@ impl Deref for ExternalEndpoint {
/// `OperationVc<OptionEndpoint>` and extracting ignore rules from its config.
async fn issue_filter_from_endpoint(
endpoint_op: OperationVc<OptionEndpoint>,
) -> Result<Vc<IssueFilter>> {
) -> Result<ReadRef<IssueFilter>> {
let endpoint_option = endpoint_op.connect().await?;
if let Some(ep) = &*endpoint_option {
Ok(ep.project().issue_filter())
ep.project().issue_filter().await
} else {
Ok(IssueFilter::warnings_and_foreign_errors().cell())
Ok(ReadRef::new_owned(
IssueFilter::warnings_and_foreign_errors(),
))
}
}

Expand All @@ -126,7 +128,7 @@ async fn get_written_endpoint_with_issues_operation(
let write_to_disk_op = endpoint_write_to_disk_operation(endpoint_op);
let filter = issue_filter_from_endpoint(endpoint_op).await?;
let (written, issues, effects) =
strongly_consistent_catch_collectables(write_to_disk_op, filter).await?;
strongly_consistent_catch_collectables(write_to_disk_op, &*filter).await?;
Ok(WrittenEndpointWithIssues {
written,
issues,
Expand Down Expand Up @@ -232,7 +234,7 @@ async fn subscribe_issues_and_diags_operation(
if should_include_issues {
let filter = issue_filter_from_endpoint(endpoint_op).await?;
let (changed_value, issues, effects) =
strongly_consistent_catch_collectables(changed_op, filter).await?;
strongly_consistent_catch_collectables(changed_op, &*filter).await?;
Ok(EndpointIssuesAndDiags {
changed: changed_value,
issues,
Expand Down
29 changes: 12 additions & 17 deletions crates/next-napi-bindings/src/next_api/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,6 @@ static SOURCE_MAP_PREFIX: LazyLock<String> = LazyLock::new(|| format!("{SOURCE_U
static SOURCE_MAP_PREFIX_PROJECT: LazyLock<String> =
LazyLock::new(|| format!("{SOURCE_URL_PROTOCOL}///[{PROJECT_FILESYSTEM_NAME}]/"));

/// Get the `Vc<IssueFilter>` for a `ProjectContainer`.
fn issue_filter_from_container(container: ResolvedVc<ProjectContainer>) -> Vc<IssueFilter> {
container.project().issue_filter()
}

#[napi(object)]
#[derive(Clone, Debug)]
pub struct NapiEnvVar {
Expand Down Expand Up @@ -1001,9 +996,9 @@ async fn get_entrypoints_with_issues_operation(
) -> Result<Vc<EntrypointsWithIssues>> {
let entrypoints_operation =
EntrypointsOperation::new(project_container_entrypoints_operation(container));
let filter = issue_filter_from_container(container);
let filter = container.project().issue_filter().await?;
let (entrypoints, issues, effects) =
strongly_consistent_catch_collectables(entrypoints_operation, filter).await?;
strongly_consistent_catch_collectables(entrypoints_operation, &*filter).await?;
Ok(EntrypointsWithIssues {
entrypoints,
issues,
Expand Down Expand Up @@ -1557,9 +1552,9 @@ async fn get_all_written_entrypoints_with_issues_operation(
app_dir_only,
write_phase,
));
let filter = issue_filter_from_container(container);
let filter = container.project().issue_filter().await?;
let (entrypoints, issues, effects) =
strongly_consistent_catch_collectables(entrypoints_operation, filter).await?;
strongly_consistent_catch_collectables(entrypoints_operation, &*filter).await?;
Ok(AllWrittenEntrypointsWithIssues {
entrypoints,
issues,
Expand Down Expand Up @@ -1640,9 +1635,9 @@ async fn emit_all_output_assets_once_with_issues_operation(
app_dir_only,
has_deferred_entrypoints,
));
let filter = issue_filter_from_container(container);
let filter = container.project().issue_filter().await?;
let (_, issues, effects) =
strongly_consistent_catch_collectables(entrypoints_operation, filter).await?;
strongly_consistent_catch_collectables(entrypoints_operation, &*filter).await?;

Ok(OperationResult { issues, effects }.cell())
}
Expand Down Expand Up @@ -1822,8 +1817,8 @@ async fn hmr_update_with_issues_operation(
) -> Result<Vc<HmrUpdateWithIssues>> {
let update_op = project_hmr_update_operation(project, chunk_name, target, state);
let update = update_op.read_strongly_consistent().await?;
let filter = project.issue_filter();
let issues = get_issues(update_op, filter).await?;
let filter = project.issue_filter().await?;
let issues = get_issues(update_op, &*filter).await?;
let effects = Arc::new(take_effects(update_op).await?);
Ok(HmrUpdateWithIssues {
update,
Expand Down Expand Up @@ -1958,8 +1953,8 @@ async fn get_hmr_chunk_names_with_issues_operation(
) -> Result<Vc<HmrChunkNamesWithIssues>> {
let hmr_chunk_names_op = project_hmr_chunk_names_operation(container, target);
let hmr_chunk_names = hmr_chunk_names_op.read_strongly_consistent().await?;
let filter = issue_filter_from_container(container);
let issues = get_issues(hmr_chunk_names_op, filter).await?;
let filter = container.project().issue_filter().await?;
let issues = get_issues(hmr_chunk_names_op, &*filter).await?;
let effects = Arc::new(take_effects(hmr_chunk_names_op).await?);
Ok(HmrChunkNamesWithIssues {
chunk_names: hmr_chunk_names,
Expand Down Expand Up @@ -2502,8 +2497,8 @@ async fn get_all_compilation_issues_operation(
container: ResolvedVc<ProjectContainer>,
) -> Result<Vc<OperationResult>> {
let inner_op = get_all_compilation_issues_inner_operation(container);
let filter = issue_filter_from_container(container);
let (_, issues, effects) = strongly_consistent_catch_collectables(inner_op, filter).await?;
let filter = container.project().issue_filter().await?;
let (_, issues, effects) = strongly_consistent_catch_collectables(inner_op, &*filter).await?;
Ok(OperationResult { issues, effects }.cell())
}

Expand Down
4 changes: 2 additions & 2 deletions crates/next-napi-bindings/src/next_api/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ pub fn root_task_dispose(
/// [consume]: turbo_tasks::CollectiblesSource::take_collectibles
pub async fn get_issues<T: Send>(
source: OperationVc<T>,
filter: Vc<IssueFilter>,
filter: &IssueFilter,
) -> Result<Arc<Vec<ReadRef<PlainIssue>>>> {
Ok(Arc::new(
source.peek_issues().get_plain_issues(filter).await?,
Expand Down Expand Up @@ -447,7 +447,7 @@ pub fn subscribe<T: 'static + Send + Sync, F: Future<Output = Result<T>> + Send,
// propagate any actual error results.
pub async fn strongly_consistent_catch_collectables<R: VcValueType + Send>(
source_op: OperationVc<R>,
filter: Vc<IssueFilter>,
filter: &IssueFilter,
) -> Result<(
Option<ReadRef<R>>,
Arc<Vec<ReadRef<PlainIssue>>>,
Expand Down
2 changes: 1 addition & 1 deletion turbopack/crates/turbopack-cli-utils/src/issue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ impl IssueReporter for ConsoleUi {
} = self.options;
let mut grouped_issues: GroupedIssues = FxHashMap::default();

let plain_issues = issues.get_plain_issues(IssueFilter::everything()).await?;
let plain_issues = issues.get_plain_issues(&IssueFilter::everything()).await?;
let issues = plain_issues
.iter()
.map(|plain_issue| {
Expand Down
109 changes: 57 additions & 52 deletions turbopack/crates/turbopack-core/src/issue/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,40 +285,57 @@ pub struct IssueFilter {
ignore_rules: Vec<IgnoreIssue>,
}

#[turbo_tasks::value_impl]
impl IssueFilter {
/// A filter that lets everything through.
#[turbo_tasks::function]
pub fn everything() -> Vc<Self> {
pub fn everything() -> Self {
IssueFilter {
severity: IssueSeverity::Info,
foreign_severity: IssueSeverity::Info,
ignore_rules: Vec::new(),
}
.cell()
}

/// Construct a filter with the standard warning/foreign-error severities.
pub fn warnings_and_foreign_errors() -> Self {
IssueFilter {
severity: IssueSeverity::Warning,
foreign_severity: IssueSeverity::Error,
ignore_rules: Vec::new(),
}
}

/// Set the ignore rules for this filter.
pub fn with_ignore_rules(mut self, rules: Vec<IgnoreIssue>) -> Self {
self.ignore_rules = rules;
self
}

/// Returns true if the issue is allowed by this filter.
#[turbo_tasks::function]
pub async fn matches(&self, issue: ResolvedVc<Box<dyn Issue>>) -> Result<Vc<bool>> {
let has_no_ignore_rules = self.ignore_rules.is_empty();
let is_everything = self.severity == IssueSeverity::Info
&& self.foreign_severity == IssueSeverity::Info
&& has_no_ignore_rules;
pub async fn matches(&self, issue: ResolvedVc<Box<dyn Issue>>) -> Result<bool> {
Ok(self.matches_all_fast_path()
|| self
.matches_ref_slow_path(&*issue.into_trait_ref().await?)
.await?)
}

if is_everything {
return Ok(Vc::cell(true));
}
pub async fn matches_ref(&self, issue: &dyn Issue) -> Result<bool> {
Ok(self.matches_all_fast_path() || self.matches_ref_slow_path(issue).await?)
}

let trait_ref = issue.into_trait_ref().await?;
fn matches_all_fast_path(&self) -> bool {
self.severity == IssueSeverity::Info
&& self.foreign_severity == IssueSeverity::Info
&& self.ignore_rules.is_empty()
}

async fn matches_ref_slow_path(&self, issue: &dyn Issue) -> Result<bool> {
// Fetch the file path once — it's used by both severity and ignore-rule
// checks.
let file_path = trait_ref.file_path().await?;
let file_path = issue.file_path().await?;

// Check severity first — this is cheap and avoids fetching
// title/description for issues that would be filtered out anyway.
let severity = trait_ref.severity();
let severity = issue.severity();
// NOTE: Lower severities are _more_ severe
let severity_allowed = if severity <= self.severity || severity <= self.foreign_severity {
// we need to check the path to see if it is foreign or not. Only await the
Expand All @@ -337,13 +354,13 @@ impl IssueFilter {
};

if !severity_allowed {
return Ok(Vc::cell(false));
return Ok(false);
}

// Check ignore rules — if any rule matches, the issue is dropped.
// Title and description are fetched lazily: only when a rule's path
// matches and the rule also specifies a title/description pattern.
if !has_no_ignore_rules {
if !self.ignore_rules.is_empty() {
let file_path_str = file_path.to_string();
let mut title_str: Option<String> = None;
let mut description_text: Option<Option<String>> = None;
Expand All @@ -354,49 +371,28 @@ impl IssueFilter {
}
if let Some(ref title_pat) = rule.title {
if title_str.is_none() {
title_str = Some(trait_ref.title().await?.to_unstyled_string());
title_str = Some(issue.title().await?.to_unstyled_string());
}
if !title_pat.matches(title_str.as_deref().unwrap()) {
continue;
}
}
if let Some(ref desc_pat) = rule.description {
if description_text.is_none() {
description_text = Some(
trait_ref
.description()
.await?
.map(|s| s.to_unstyled_string()),
);
description_text =
Some(issue.description().await?.map(|s| s.to_unstyled_string()));
}
match description_text.as_ref().unwrap().as_deref() {
Some(desc) if desc_pat.matches(desc) => {}
_ => continue,
}
}
// All specified fields matched — ignore this issue.
return Ok(Vc::cell(false));
return Ok(false);
}
}

Ok(Vc::cell(true))
}
}

impl IssueFilter {
/// Construct a filter with the standard warning/foreign-error severities.
pub fn warnings_and_foreign_errors() -> Self {
IssueFilter {
severity: IssueSeverity::Warning,
foreign_severity: IssueSeverity::Error,
ignore_rules: Vec::new(),
}
}

/// Set the ignore rules for this filter.
pub fn with_ignore_rules(mut self, rules: Vec<IgnoreIssue>) -> Self {
self.ignore_rules = rules;
self
Ok(true)
}
}

Expand All @@ -415,15 +411,12 @@ impl CapturedIssues {
}

// Returns all the issues as formatted `PlainIssues`.
pub async fn get_plain_issues(
&self,
filter: Vc<IssueFilter>,
) -> Result<Vec<ReadRef<PlainIssue>>> {
pub async fn get_plain_issues(&self, filter: &IssueFilter) -> Result<Vec<ReadRef<PlainIssue>>> {
let mut list = self
.issues
.iter()
.map(async |issue| {
if *filter.matches(**issue).await? {
if filter.matches(*issue).await? {
Ok(Some(
PlainIssue::from_issue(**issue, Some(*self.tracer)).await?,
))
Expand Down Expand Up @@ -970,12 +963,24 @@ impl PlainIssue {
issue: ResolvedVc<Box<dyn Issue>>,
import_tracer: Option<ResolvedVc<DelegatingImportTracer>>,
) -> Result<Vc<Self>> {
let trait_ref = issue.into_trait_ref().await?;
Ok(
Self::from_issue_ref(&*issue.into_trait_ref().await?, import_tracer)
.await?
.cell(),
)
}
}

impl PlainIssue {
pub async fn from_issue_ref(
trait_ref: &dyn Issue,
import_tracer: Option<ResolvedVc<DelegatingImportTracer>>,
) -> Result<Self> {
let severity = trait_ref.severity();
let file_path = trait_ref.file_path().await?;
let file_path_str = file_path.to_string_ref().await?;

Ok(Self::cell(Self {
Ok(Self {
severity,
file_path: file_path_str,
stage: trait_ref.stage(),
Expand Down Expand Up @@ -1010,7 +1015,7 @@ impl PlainIssue {
}
None => vec![],
},
}))
})
}
}

Expand Down
2 changes: 1 addition & 1 deletion turbopack/crates/turbopack-dev-server/src/update/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ impl GetContentFn {
async fn peek_issues<T: Send>(source: OperationVc<T>) -> Result<Vec<ReadRef<PlainIssue>>> {
let captured = source.peek_issues();

captured.get_plain_issues(IssueFilter::everything()).await
captured.get_plain_issues(&IssueFilter::everything()).await
}

fn extend_issues(issues: &mut Vec<ReadRef<PlainIssue>>, new_issues: Vec<ReadRef<PlainIssue>>) {
Expand Down
2 changes: 1 addition & 1 deletion turbopack/crates/turbopack-tests/tests/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ async fn snapshot_issues(

let plain_issues = run_result_op
.peek_issues()
.get_plain_issues(IssueFilter::everything())
.get_plain_issues(&IssueFilter::everything())
.await?;

turbopack_test_utils::snapshot::snapshot_issues(plain_issues, path.join("issues")?, &REPO_ROOT)
Expand Down
Loading