From daa078d99f4f42902ad195b94977d25d42e30647 Mon Sep 17 00:00:00 2001 From: Nicholas Yang Date: Thu, 31 Oct 2024 10:30:56 -0400 Subject: [PATCH] feat(trace): filtering by import type (#9357) --- crates/turbo-trace/src/import_finder.rs | 28 +++++++++++-- crates/turbo-trace/src/lib.rs | 2 +- crates/turbo-trace/src/tracer.rs | 22 +++++++++- crates/turborepo-lib/src/query/file.rs | 42 +++++++++++++++++-- crates/turborepo/tests/query.rs | 6 +++ ...s`_with_all_dependencies_(npm@10.5.0).snap | 23 ++++++++++ ...`_with_type_dependencies_(npm@10.5.0).snap | 20 +++++++++ ..._with_value_dependencies_(npm@10.5.0).snap | 20 +++++++++ ...tsx`_with_all_dependents_(npm@10.5.0).snap | 26 ++++++++++++ ...sx`_with_type_dependents_(npm@10.5.0).snap | 20 +++++++++ ...x`_with_value_dependents_(npm@10.5.0).snap | 23 ++++++++++ .../fixtures/turbo_trace/import_just_type.ts | 1 + .../fixtures/turbo_trace/import_just_value.ts | 1 + .../turbo_trace/import_value_and_type.ts | 2 + .../integration/fixtures/turbo_trace/link.tsx | 8 ++++ .../integration/fixtures/turbo_trace/types.ts | 1 + 16 files changed, 236 insertions(+), 9 deletions(-) create mode 100644 crates/turborepo/tests/snapshots/query__turbo_trace_get_`import_value_and_type.ts`_with_all_dependencies_(npm@10.5.0).snap create mode 100644 crates/turborepo/tests/snapshots/query__turbo_trace_get_`import_value_and_type.ts`_with_type_dependencies_(npm@10.5.0).snap create mode 100644 crates/turborepo/tests/snapshots/query__turbo_trace_get_`import_value_and_type.ts`_with_value_dependencies_(npm@10.5.0).snap create mode 100644 crates/turborepo/tests/snapshots/query__turbo_trace_get_`link.tsx`_with_all_dependents_(npm@10.5.0).snap create mode 100644 crates/turborepo/tests/snapshots/query__turbo_trace_get_`link.tsx`_with_type_dependents_(npm@10.5.0).snap create mode 100644 crates/turborepo/tests/snapshots/query__turbo_trace_get_`link.tsx`_with_value_dependents_(npm@10.5.0).snap create mode 100644 turborepo-tests/integration/fixtures/turbo_trace/import_just_type.ts create mode 100644 turborepo-tests/integration/fixtures/turbo_trace/import_just_value.ts create mode 100644 turborepo-tests/integration/fixtures/turbo_trace/import_value_and_type.ts create mode 100644 turborepo-tests/integration/fixtures/turbo_trace/link.tsx create mode 100644 turborepo-tests/integration/fixtures/turbo_trace/types.ts diff --git a/crates/turbo-trace/src/import_finder.rs b/crates/turbo-trace/src/import_finder.rs index ddfbf83875418..79543c7185417 100644 --- a/crates/turbo-trace/src/import_finder.rs +++ b/crates/turbo-trace/src/import_finder.rs @@ -2,12 +2,21 @@ use swc_common::{Span, Spanned}; use swc_ecma_ast::{Decl, ModuleDecl, Stmt}; use swc_ecma_visit::{Visit, VisitWith}; -#[derive(Default)] +use crate::tracer::ImportType; + pub struct ImportFinder { + import_type: ImportType, imports: Vec<(String, Span)>, } impl ImportFinder { + pub fn new(import_type: ImportType) -> Self { + Self { + import_type, + imports: Vec::new(), + } + } + pub fn imports(&self) -> &[(String, Span)] { &self.imports } @@ -16,8 +25,21 @@ impl ImportFinder { impl Visit for ImportFinder { fn visit_module_decl(&mut self, decl: &ModuleDecl) { if let ModuleDecl::Import(import) = decl { - self.imports - .push((import.src.value.to_string(), import.span)); + match self.import_type { + ImportType::All => { + self.imports + .push((import.src.value.to_string(), import.span)); + } + ImportType::Types if import.type_only => { + self.imports + .push((import.src.value.to_string(), import.span)); + } + ImportType::Values if !import.type_only => { + self.imports + .push((import.src.value.to_string(), import.span)); + } + _ => {} + } } } diff --git a/crates/turbo-trace/src/lib.rs b/crates/turbo-trace/src/lib.rs index c37074f75fb8d..e0b38cd1bd02b 100644 --- a/crates/turbo-trace/src/lib.rs +++ b/crates/turbo-trace/src/lib.rs @@ -2,4 +2,4 @@ mod import_finder; mod tracer; -pub use tracer::{TraceError, TraceResult, Tracer}; +pub use tracer::{ImportType, TraceError, TraceResult, Tracer}; diff --git a/crates/turbo-trace/src/tracer.rs b/crates/turbo-trace/src/tracer.rs index 24db1d2baa4d8..27e0602387b66 100644 --- a/crates/turbo-trace/src/tracer.rs +++ b/crates/turbo-trace/src/tracer.rs @@ -28,6 +28,7 @@ pub struct Tracer { source_map: Arc, cwd: AbsoluteSystemPathBuf, errors: Vec, + import_type: ImportType, } #[derive(Debug, Error, Diagnostic)] @@ -57,6 +58,17 @@ pub struct TraceResult { pub files: HashMap, } +/// The type of imports to trace. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ImportType { + /// Trace all imports. + All, + /// Trace only `import type` imports + Types, + /// Trace only `import` imports and not `import type` imports + Values, +} + impl Tracer { pub fn new( cwd: AbsoluteSystemPathBuf, @@ -72,17 +84,23 @@ impl Tracer { files, ts_config, cwd, + import_type: ImportType::All, errors: Vec::new(), source_map: Arc::new(SourceMap::default()), } } + pub fn set_import_type(&mut self, import_type: ImportType) { + self.import_type = import_type; + } + #[tracing::instrument(skip(resolver, source_map))] pub async fn get_imports_from_file( source_map: &SourceMap, errors: &mut Vec, resolver: &Resolver, file_path: &AbsoluteSystemPath, + import_type: ImportType, ) -> Option<(Vec, SeenFile)> { // Read the file content let Ok(file_content) = tokio::fs::read_to_string(&file_path).await else { @@ -130,7 +148,7 @@ impl Tracer { }; // Visit the AST and find imports - let mut finder = ImportFinder::default(); + let mut finder = ImportFinder::new(import_type); module.visit_with(&mut finder); // Convert found imports/requires to absolute paths and add them to files to // visit @@ -196,6 +214,7 @@ impl Tracer { &mut self.errors, file_resolver.as_ref().unwrap_or(resolver), &file_path, + self.import_type, ) .await else { @@ -306,6 +325,7 @@ impl Tracer { &mut errors, file_resolver.as_ref().unwrap_or(&resolver), &file, + shared_self.import_type, ) .await else { diff --git a/crates/turborepo-lib/src/query/file.rs b/crates/turborepo-lib/src/query/file.rs index 5fdd280e6a312..c1893d396ab3e 100644 --- a/crates/turborepo-lib/src/query/file.rs +++ b/crates/turborepo-lib/src/query/file.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use async_graphql::{Object, SimpleObject}; +use async_graphql::{Enum, Object, SimpleObject}; use camino::Utf8PathBuf; use itertools::Itertools; use swc_ecma_ast::EsVersion; @@ -146,6 +146,27 @@ impl TraceResult { } } +/// The type of imports to trace. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Enum)] +pub enum ImportType { + /// Trace all imports. + All, + /// Trace only `import type` imports + Types, + /// Trace only `import` imports and not `import type` imports + Values, +} + +impl From for turbo_trace::ImportType { + fn from(import_type: ImportType) -> Self { + match import_type { + ImportType::All => turbo_trace::ImportType::All, + ImportType::Types => turbo_trace::ImportType::Types, + ImportType::Values => turbo_trace::ImportType::Values, + } + } +} + #[Object] impl File { async fn contents(&self) -> Result { @@ -169,26 +190,39 @@ impl File { &self, depth: Option, ts_config: Option, + import_type: Option, ) -> Result { - let tracer = Tracer::new( + let mut tracer = Tracer::new( self.run.repo_root().to_owned(), vec![self.path.clone()], ts_config.map(Utf8PathBuf::from), ); + if let Some(import_type) = import_type { + tracer.set_import_type(import_type.into()); + } + let mut result = tracer.trace(depth).await; // Remove the file itself from the result result.files.remove(&self.path); TraceResult::new(result, self.run.clone()) } - async fn dependents(&self, ts_config: Option) -> Result { - let tracer = Tracer::new( + async fn dependents( + &self, + ts_config: Option, + import_type: Option, + ) -> Result { + let mut tracer = Tracer::new( self.run.repo_root().to_owned(), vec![self.path.clone()], ts_config.map(Utf8PathBuf::from), ); + if let Some(import_type) = import_type { + tracer.set_import_type(import_type.into()); + } + let mut result = tracer.reverse_trace().await; // Remove the file itself from the result result.files.remove(&self.path); diff --git a/crates/turborepo/tests/query.rs b/crates/turborepo/tests/query.rs index 18308f173a9f5..fdaf7ec8555ae 100644 --- a/crates/turborepo/tests/query.rs +++ b/crates/turborepo/tests/query.rs @@ -47,6 +47,12 @@ fn test_trace() -> Result<(), anyhow::Error> { "get `invalid.ts` with dependencies" => "query { file(path: \"invalid.ts\") { path dependencies { files { items { path } } errors { items { message } } } } }", "get `main.ts` with depth = 0" => "query { file(path: \"main.ts\") { path dependencies(depth: 1) { files { items { path } } } } }", "get `with_prefix.ts` with dependencies" => "query { file(path: \"with_prefix.ts\") { path dependencies { files { items { path } } } } }", + "get `import_value_and_type.ts` with all dependencies" => "query { file(path: \"import_value_and_type.ts\") { path dependencies(importType: ALL) { files { items { path } } } } }", + "get `import_value_and_type.ts` with type dependencies" => "query { file(path: \"import_value_and_type.ts\") { path dependencies(importType: TYPES) { files { items { path } } } } }", + "get `import_value_and_type.ts` with value dependencies" => "query { file(path: \"import_value_and_type.ts\") { path dependencies(importType: VALUES) { files { items { path } } } } }", + "get `link.tsx` with all dependents" => "query { file(path: \"link.tsx\") { path dependents(importType: ALL) { files { items { path } } } } }", + "get `link.tsx` with type dependents" => "query { file(path: \"link.tsx\") { path dependents(importType: TYPES) { files { items { path } } } } }", + "get `link.tsx` with value dependents" => "query { file(path: \"link.tsx\") { path dependents(importType: VALUES) { files { items { path } } } } }", ); Ok(()) diff --git a/crates/turborepo/tests/snapshots/query__turbo_trace_get_`import_value_and_type.ts`_with_all_dependencies_(npm@10.5.0).snap b/crates/turborepo/tests/snapshots/query__turbo_trace_get_`import_value_and_type.ts`_with_all_dependencies_(npm@10.5.0).snap new file mode 100644 index 0000000000000..46eabec764c43 --- /dev/null +++ b/crates/turborepo/tests/snapshots/query__turbo_trace_get_`import_value_and_type.ts`_with_all_dependencies_(npm@10.5.0).snap @@ -0,0 +1,23 @@ +--- +source: crates/turborepo/tests/query.rs +expression: query_output +--- +{ + "data": { + "file": { + "path": "import_value_and_type.ts", + "dependencies": { + "files": { + "items": [ + { + "path": "link.tsx" + }, + { + "path": "types.ts" + } + ] + } + } + } + } +} diff --git a/crates/turborepo/tests/snapshots/query__turbo_trace_get_`import_value_and_type.ts`_with_type_dependencies_(npm@10.5.0).snap b/crates/turborepo/tests/snapshots/query__turbo_trace_get_`import_value_and_type.ts`_with_type_dependencies_(npm@10.5.0).snap new file mode 100644 index 0000000000000..04f12c8b158c3 --- /dev/null +++ b/crates/turborepo/tests/snapshots/query__turbo_trace_get_`import_value_and_type.ts`_with_type_dependencies_(npm@10.5.0).snap @@ -0,0 +1,20 @@ +--- +source: crates/turborepo/tests/query.rs +expression: query_output +--- +{ + "data": { + "file": { + "path": "import_value_and_type.ts", + "dependencies": { + "files": { + "items": [ + { + "path": "types.ts" + } + ] + } + } + } + } +} diff --git a/crates/turborepo/tests/snapshots/query__turbo_trace_get_`import_value_and_type.ts`_with_value_dependencies_(npm@10.5.0).snap b/crates/turborepo/tests/snapshots/query__turbo_trace_get_`import_value_and_type.ts`_with_value_dependencies_(npm@10.5.0).snap new file mode 100644 index 0000000000000..1cc31c9e9f2e3 --- /dev/null +++ b/crates/turborepo/tests/snapshots/query__turbo_trace_get_`import_value_and_type.ts`_with_value_dependencies_(npm@10.5.0).snap @@ -0,0 +1,20 @@ +--- +source: crates/turborepo/tests/query.rs +expression: query_output +--- +{ + "data": { + "file": { + "path": "import_value_and_type.ts", + "dependencies": { + "files": { + "items": [ + { + "path": "link.tsx" + } + ] + } + } + } + } +} diff --git a/crates/turborepo/tests/snapshots/query__turbo_trace_get_`link.tsx`_with_all_dependents_(npm@10.5.0).snap b/crates/turborepo/tests/snapshots/query__turbo_trace_get_`link.tsx`_with_all_dependents_(npm@10.5.0).snap new file mode 100644 index 0000000000000..c4355ebb18e29 --- /dev/null +++ b/crates/turborepo/tests/snapshots/query__turbo_trace_get_`link.tsx`_with_all_dependents_(npm@10.5.0).snap @@ -0,0 +1,26 @@ +--- +source: crates/turborepo/tests/query.rs +expression: query_output +--- +{ + "data": { + "file": { + "path": "link.tsx", + "dependents": { + "files": { + "items": [ + { + "path": "import_just_type.ts" + }, + { + "path": "import_just_value.ts" + }, + { + "path": "import_value_and_type.ts" + } + ] + } + } + } + } +} diff --git a/crates/turborepo/tests/snapshots/query__turbo_trace_get_`link.tsx`_with_type_dependents_(npm@10.5.0).snap b/crates/turborepo/tests/snapshots/query__turbo_trace_get_`link.tsx`_with_type_dependents_(npm@10.5.0).snap new file mode 100644 index 0000000000000..5549ac43b44e1 --- /dev/null +++ b/crates/turborepo/tests/snapshots/query__turbo_trace_get_`link.tsx`_with_type_dependents_(npm@10.5.0).snap @@ -0,0 +1,20 @@ +--- +source: crates/turborepo/tests/query.rs +expression: query_output +--- +{ + "data": { + "file": { + "path": "link.tsx", + "dependents": { + "files": { + "items": [ + { + "path": "import_just_type.ts" + } + ] + } + } + } + } +} diff --git a/crates/turborepo/tests/snapshots/query__turbo_trace_get_`link.tsx`_with_value_dependents_(npm@10.5.0).snap b/crates/turborepo/tests/snapshots/query__turbo_trace_get_`link.tsx`_with_value_dependents_(npm@10.5.0).snap new file mode 100644 index 0000000000000..e25a771e6f9cb --- /dev/null +++ b/crates/turborepo/tests/snapshots/query__turbo_trace_get_`link.tsx`_with_value_dependents_(npm@10.5.0).snap @@ -0,0 +1,23 @@ +--- +source: crates/turborepo/tests/query.rs +expression: query_output +--- +{ + "data": { + "file": { + "path": "link.tsx", + "dependents": { + "files": { + "items": [ + { + "path": "import_just_value.ts" + }, + { + "path": "import_value_and_type.ts" + } + ] + } + } + } + } +} diff --git a/turborepo-tests/integration/fixtures/turbo_trace/import_just_type.ts b/turborepo-tests/integration/fixtures/turbo_trace/import_just_type.ts new file mode 100644 index 0000000000000..6f29b3cb73ccd --- /dev/null +++ b/turborepo-tests/integration/fixtures/turbo_trace/import_just_type.ts @@ -0,0 +1 @@ +import type { LinkProps } from "./link"; diff --git a/turborepo-tests/integration/fixtures/turbo_trace/import_just_value.ts b/turborepo-tests/integration/fixtures/turbo_trace/import_just_value.ts new file mode 100644 index 0000000000000..e1b79a7059213 --- /dev/null +++ b/turborepo-tests/integration/fixtures/turbo_trace/import_just_value.ts @@ -0,0 +1 @@ +import { Link } from "./link"; diff --git a/turborepo-tests/integration/fixtures/turbo_trace/import_value_and_type.ts b/turborepo-tests/integration/fixtures/turbo_trace/import_value_and_type.ts new file mode 100644 index 0000000000000..c4d9493d9bd5a --- /dev/null +++ b/turborepo-tests/integration/fixtures/turbo_trace/import_value_and_type.ts @@ -0,0 +1,2 @@ +import type { LinkProps } from "./types"; +import { Link } from "./link"; diff --git a/turborepo-tests/integration/fixtures/turbo_trace/link.tsx b/turborepo-tests/integration/fixtures/turbo_trace/link.tsx new file mode 100644 index 0000000000000..8654121b50e51 --- /dev/null +++ b/turborepo-tests/integration/fixtures/turbo_trace/link.tsx @@ -0,0 +1,8 @@ +export interface LinkProps { + children: React.ReactNode; + href: string; +} + +export const Link = ({ children, href }: LinkProps) => { + return {children}; +}; diff --git a/turborepo-tests/integration/fixtures/turbo_trace/types.ts b/turborepo-tests/integration/fixtures/turbo_trace/types.ts new file mode 100644 index 0000000000000..c3a0137b0d333 --- /dev/null +++ b/turborepo-tests/integration/fixtures/turbo_trace/types.ts @@ -0,0 +1 @@ +export type LinkProps = string;