From 75509e6f1737f1b0265892206936cb06645f63e1 Mon Sep 17 00:00:00 2001 From: Alexander Lyon Date: Tue, 20 Aug 2024 13:49:12 +0200 Subject: [PATCH] add implementation for webpackIgnore and turbopackIgnore --- crates/next-api/src/app.rs | 1 + crates/next-api/src/dynamic_imports.rs | 1 + crates/next-api/src/pages.rs | 1 + .../src/next_client/runtime_entry.rs | 1 + .../turbopack-cli-utils/src/runtime_entry.rs | 1 + .../src/analyzer/graph.rs | 8 +- .../src/analyzer/imports.rs | 10 +- .../turbopack-ecmascript/src/analyzer/mod.rs | 35 ++-- .../src/analyzer/well_known.rs | 24 ++- .../crates/turbopack-ecmascript/src/lib.rs | 10 +- .../crates/turbopack-ecmascript/src/parse.rs | 6 +- .../src/references/amd.rs | 2 + .../src/references/cjs.rs | 11 ++ .../src/references/esm/base.rs | 10 ++ .../src/references/esm/dynamic.rs | 5 + .../src/references/mod.rs | 167 ++++++++++++------ .../src/references/require_context.rs | 2 +- .../locals/chunk_item.rs | 2 +- .../side_effect_optimization/locals/module.rs | 2 +- .../src/tree_shake/asset.rs | 16 +- .../src/typescript/mod.rs | 16 +- .../turbopack-resolve/src/ecmascript.rs | 29 ++- .../turbopack/ignore/import/input/index.js | 27 +++ .../turbopack/ignore/import/input/util.js | 1 + .../turbopack/ignore/require/input/index.js | 19 ++ .../turbopack/ignore/require/input/util.js | 1 + 26 files changed, 303 insertions(+), 105 deletions(-) create mode 100644 turbopack/crates/turbopack-tests/tests/execution/turbopack/ignore/import/input/index.js create mode 100644 turbopack/crates/turbopack-tests/tests/execution/turbopack/ignore/import/input/util.js create mode 100644 turbopack/crates/turbopack-tests/tests/execution/turbopack/ignore/require/input/index.js create mode 100644 turbopack/crates/turbopack-tests/tests/execution/turbopack/ignore/require/input/util.js diff --git a/crates/next-api/src/app.rs b/crates/next-api/src/app.rs index cfd030bdf2489..e8d2107c0df91 100644 --- a/crates/next-api/src/app.rs +++ b/crates/next-api/src/app.rs @@ -572,6 +572,7 @@ impl AppProject { ))), None, IssueSeverity::Error.cell(), + false, ) .resolve() .await? diff --git a/crates/next-api/src/dynamic_imports.rs b/crates/next-api/src/dynamic_imports.rs index 72c1d3d8bdaae..3656d6a3c44b0 100644 --- a/crates/next-api/src/dynamic_imports.rs +++ b/crates/next-api/src/dynamic_imports.rs @@ -292,6 +292,7 @@ async fn build_dynamic_imports_map_for_module( Value::new(EcmaScriptModulesReferenceSubType::DynamicImport), IssueSeverity::Error.cell(), None, + false, ) .first_module() .await?; diff --git a/crates/next-api/src/pages.rs b/crates/next-api/src/pages.rs index 3ca0e447eab53..cd5636a7e9f31 100644 --- a/crates/next-api/src/pages.rs +++ b/crates/next-api/src/pages.rs @@ -575,6 +575,7 @@ impl PagesProject { Value::new(EcmaScriptModulesReferenceSubType::Undefined), IssueSeverity::Error.cell(), None, + false, ) .first_module() .await? diff --git a/crates/next-core/src/next_client/runtime_entry.rs b/crates/next-core/src/next_client/runtime_entry.rs index 2bba0e124677c..44cc2dd6be308 100644 --- a/crates/next-core/src/next_client/runtime_entry.rs +++ b/crates/next-core/src/next_client/runtime_entry.rs @@ -38,6 +38,7 @@ impl RuntimeEntry { request, None, IssueSeverity::Error.cell(), + false, ) .resolve() .await? diff --git a/turbopack/crates/turbopack-cli-utils/src/runtime_entry.rs b/turbopack/crates/turbopack-cli-utils/src/runtime_entry.rs index 8d20decbdb90b..9417286cd184b 100644 --- a/turbopack/crates/turbopack-cli-utils/src/runtime_entry.rs +++ b/turbopack/crates/turbopack-cli-utils/src/runtime_entry.rs @@ -38,6 +38,7 @@ impl RuntimeEntry { request, None, IssueSeverity::Error.cell(), + false, ) .resolve() .await? diff --git a/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs b/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs index 49913f2dada31..05aca7172a992 100644 --- a/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs +++ b/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs @@ -1242,7 +1242,13 @@ impl VisitAstPath for Analyzer<'_> { if let Some(require_var_id) = extract_var_from_umd_factory(callee, &n.args) { self.add_value( require_var_id, - JsValue::WellKnownFunction(WellKnownFunctionKind::Require), + JsValue::WellKnownFunction(WellKnownFunctionKind::Require { + ignore: self + .eval_context + .imports + .get_ignore(n.callee.span()) + .unwrap_or_default(), + }), ); } } diff --git a/turbopack/crates/turbopack-ecmascript/src/analyzer/imports.rs b/turbopack/crates/turbopack-ecmascript/src/analyzer/imports.rs index 4850531e8243d..ef376817d3633 100644 --- a/turbopack/crates/turbopack-ecmascript/src/analyzer/imports.rs +++ b/turbopack/crates/turbopack-ecmascript/src/analyzer/imports.rs @@ -6,7 +6,7 @@ use std::{ use indexmap::{IndexMap, IndexSet}; use once_cell::sync::Lazy; use swc_core::{ - common::{comments::Comments, source_map::SmallPos, Span, Spanned}, + common::{comments::Comments, source_map::SmallPos, BytePos, Span, Spanned}, ecma::{ ast::*, atoms::{js_word, JsWord}, @@ -149,7 +149,7 @@ pub(crate) struct ImportMap { /// const a = import(/* webpackIgnore: true */ "a"); /// const b = import(/* turbopackIgnore: true */ "b"); /// ``` - turbopack_ignores: HashMap, + pub turbopack_ignores: HashMap, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -194,6 +194,10 @@ impl ImportMap { None } + pub fn get_ignore(&self, span: Span) -> Option { + self.turbopack_ignores.get(&span.lo).copied() + } + // TODO this could return &str instead of String to avoid cloning pub fn get_binding(&self, id: &Id) -> Option<(usize, Option)> { if let Some((i, i_sym)) = self.imports.get(id) { @@ -481,7 +485,7 @@ impl Visit for Analyzer<'_> { if let Some((callee_span, ignore_statement)) = callee_span.zip(ignore_statement) { self.data .turbopack_ignores - .insert(*callee_span, ignore_statement); + .insert(callee_span.lo, ignore_statement); }; } diff --git a/turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs b/turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs index 8592a670e80bd..6d7b880ff8889 100644 --- a/turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs +++ b/turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs @@ -1627,11 +1627,11 @@ impl JsValue { format!("path.resolve({cwd})"), "The Node.js path.resolve method: https://nodejs.org/api/path.html#pathresolvepaths", ), - WellKnownFunctionKind::Import => ( + WellKnownFunctionKind::Import { .. } => ( "import".to_string(), "The dynamic import() method from the ESM specification: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports" ), - WellKnownFunctionKind::Require => ("require".to_string(), "The require method from CommonJS"), + WellKnownFunctionKind::Require { .. } => ("require".to_string(), "The require method from CommonJS"), WellKnownFunctionKind::RequireResolve => ("require.resolve".to_string(), "The require.resolve method from CommonJS"), WellKnownFunctionKind::RequireContext => ("require.context".to_string(), "The require.context method from webpack"), WellKnownFunctionKind::RequireContextRequire(..) => ("require.context(...)".to_string(), "The require.context(...) method from webpack: https://webpack.js.org/api/module-methods/#requirecontext"), @@ -3626,8 +3626,14 @@ pub enum WellKnownFunctionKind { PathDirname, /// `0` is the current working directory. PathResolve(Box), - Import, - Require, + /// Import and Require can be ignored at compile time using the `turbopackIgnore` directive. + /// This is functionality that was introduced in webpack, so we also support `webpackIgnore`. + Import { + ignore: bool, + }, + Require { + ignore: bool, + }, RequireResolve, RequireContext, RequireContextRequire(Vc), @@ -3656,8 +3662,8 @@ pub enum WellKnownFunctionKind { impl WellKnownFunctionKind { pub fn as_define_name(&self) -> Option<&[&str]> { match self { - Self::Import => Some(&["import"]), - Self::Require => Some(&["require"]), + Self::Import { .. } => Some(&["import"]), + Self::Require { .. } => Some(&["require"]), Self::RequireResolve => Some(&["require", "resolve"]), Self::RequireContext => Some(&["require", "context"]), Self::Define => Some(&["define"]), @@ -3704,7 +3710,7 @@ pub mod test_utils { let mut new_value = match v { JsValue::Call( _, - box JsValue::WellKnownFunction(WellKnownFunctionKind::Import), + box JsValue::WellKnownFunction(WellKnownFunctionKind::Import { .. }), ref args, ) => match &args[0] { JsValue::Constant(v) => JsValue::Module(ModuleValue { @@ -3740,8 +3746,12 @@ pub mod test_utils { Err(err) => v.into_unknown(true, PrettyPrintError(&err).to_string()), }, JsValue::FreeVar(ref var) => match &**var { - "import" => JsValue::WellKnownFunction(WellKnownFunctionKind::Import), - "require" => JsValue::WellKnownFunction(WellKnownFunctionKind::Require), + "import" => { + JsValue::WellKnownFunction(WellKnownFunctionKind::Import { ignore: false }) + } + "require" => { + JsValue::WellKnownFunction(WellKnownFunctionKind::Require { ignore: false }) + } "define" => JsValue::WellKnownFunction(WellKnownFunctionKind::Define), "__dirname" => "__dirname".into(), "__filename" => "__filename".into(), @@ -3772,7 +3782,7 @@ mod tests { use std::{mem::take, path::PathBuf, time::Instant}; use swc_core::{ - common::Mark, + common::{comments::SingleThreadedComments, Mark}, ecma::{ ast::EsVersion, parser::parse_file_as_program, transforms::base::resolver, visit::VisitMutWith, @@ -3809,11 +3819,12 @@ mod tests { r.block_on(async move { let fm = cm.load_file(&input).unwrap(); + let comments = SingleThreadedComments::default(); let mut m = parse_file_as_program( &fm, Default::default(), EsVersion::latest(), - None, + Some(&comments), &mut vec![], ) .map_err(|err| err.into_diagnostic(handler).emit())?; @@ -3823,7 +3834,7 @@ mod tests { m.visit_mut_with(&mut resolver(unresolved_mark, top_level_mark, false)); let eval_context = - EvalContext::new(&m, unresolved_mark, top_level_mark, None, None); + EvalContext::new(&m, unresolved_mark, top_level_mark, Some(&comments), None); let mut var_graph = create_graph(&m, &eval_context); diff --git a/turbopack/crates/turbopack-ecmascript/src/analyzer/well_known.rs b/turbopack/crates/turbopack-ecmascript/src/analyzer/well_known.rs index 04bd40eb43563..c62b790c5961a 100644 --- a/turbopack/crates/turbopack-ecmascript/src/analyzer/well_known.rs +++ b/turbopack/crates/turbopack-ecmascript/src/analyzer/well_known.rs @@ -57,12 +57,12 @@ pub async fn well_known_function_call( WellKnownFunctionKind::PathJoin => path_join(args), WellKnownFunctionKind::PathDirname => path_dirname(args), WellKnownFunctionKind::PathResolve(cwd) => path_resolve(*cwd, args), - WellKnownFunctionKind::Import => JsValue::unknown( + WellKnownFunctionKind::Import { .. } => JsValue::unknown( JsValue::call(Box::new(JsValue::WellKnownFunction(kind)), args), true, "import() is not supported", ), - WellKnownFunctionKind::Require => require(args), + WellKnownFunctionKind::Require { ignore } => require(args, ignore), WellKnownFunctionKind::RequireContextRequire(value) => { require_context_require(value, args).await? } @@ -322,7 +322,11 @@ pub fn path_dirname(mut args: Vec) -> JsValue { ) } -pub fn require(args: Vec) -> JsValue { +/// Resolve the contents of a require call, throwing errors +/// if we come across any unsupported syntax. +/// +/// `ignore` is true if the require call is marked with `turbopackIgnore` or `webpackIgnore`. +pub fn require(args: Vec, ignore: bool) -> JsValue { if args.len() == 1 { if let Some(s) = args[0].as_str() { JsValue::Module(ModuleValue { @@ -332,7 +336,9 @@ pub fn require(args: Vec) -> JsValue { } else { JsValue::unknown( JsValue::call( - Box::new(JsValue::WellKnownFunction(WellKnownFunctionKind::Require)), + Box::new(JsValue::WellKnownFunction(WellKnownFunctionKind::Require { + ignore, + })), args, ), true, @@ -342,7 +348,9 @@ pub fn require(args: Vec) -> JsValue { } else { JsValue::unknown( JsValue::call( - Box::new(JsValue::WellKnownFunction(WellKnownFunctionKind::Require)), + Box::new(JsValue::WellKnownFunction(WellKnownFunctionKind::Require { + ignore, + })), args, ), true, @@ -520,13 +528,13 @@ pub fn path_to_file_url(args: Vec) -> JsValue { pub fn well_known_function_member(kind: WellKnownFunctionKind, prop: JsValue) -> (JsValue, bool) { let new_value = match (kind, prop.as_str()) { - (WellKnownFunctionKind::Require, Some("resolve")) => { + (WellKnownFunctionKind::Require { .. }, Some("resolve")) => { JsValue::WellKnownFunction(WellKnownFunctionKind::RequireResolve) } - (WellKnownFunctionKind::Require, Some("cache")) => { + (WellKnownFunctionKind::Require { .. }, Some("cache")) => { JsValue::WellKnownObject(WellKnownObjectKind::RequireCache) } - (WellKnownFunctionKind::Require, Some("context")) => { + (WellKnownFunctionKind::Require { .. }, Some("context")) => { JsValue::WellKnownFunction(WellKnownFunctionKind::RequireContext) } (WellKnownFunctionKind::RequireContextRequire(val), Some("resolve")) => { diff --git a/turbopack/crates/turbopack-ecmascript/src/lib.rs b/turbopack/crates/turbopack-ecmascript/src/lib.rs index e58b298322451..3ae92cd42f0e5 100644 --- a/turbopack/crates/turbopack-ecmascript/src/lib.rs +++ b/turbopack/crates/turbopack-ecmascript/src/lib.rs @@ -366,7 +366,7 @@ impl EcmascriptParsable for EcmascriptModuleAsset { impl EcmascriptAnalyzable for EcmascriptModuleAsset { #[turbo_tasks::function] fn analyze(self: Vc) -> Vc { - analyse_ecmascript_module(self, None, None) + analyse_ecmascript_module(self, None) } /// Generates module contents without an analysis pass. This is useful for @@ -419,6 +419,7 @@ impl EcmascriptModuleAsset { pub fn new( source: Vc>, asset_context: Vc>, + ty: Value, transforms: Vc, options: Vc, @@ -430,6 +431,7 @@ impl EcmascriptModuleAsset { ty: ty.into_value(), transforms, options, + compile_time_info, inner_assets: None, last_successful_parse: Default::default(), @@ -442,6 +444,7 @@ impl EcmascriptModuleAsset { asset_context: Vc>, ty: Value, transforms: Vc, + options: Vc, compile_time_info: Vc, inner_assets: Vc, @@ -463,6 +466,11 @@ impl EcmascriptModuleAsset { Ok(self.await?.source) } + #[turbo_tasks::function] + pub fn analyze(self: Vc) -> Vc { + analyse_ecmascript_module(self, None) + } + #[turbo_tasks::function] pub async fn options(self: Vc) -> Result> { Ok(self.await?.options) diff --git a/turbopack/crates/turbopack-ecmascript/src/parse.rs b/turbopack/crates/turbopack-ecmascript/src/parse.rs index 9161f1e4d63b8..1c99733c878b8 100644 --- a/turbopack/crates/turbopack-ecmascript/src/parse.rs +++ b/turbopack/crates/turbopack-ecmascript/src/parse.rs @@ -206,7 +206,7 @@ async fn parse_internal( FileContent::Content(file) => match file.content().to_str() { Ok(string) => { let transforms = &*transforms.await?; - match parse_content( + match parse_file_content( string.into_owned(), fs_path_vc, fs_path, @@ -246,7 +246,7 @@ async fn parse_internal( }) } -async fn parse_content( +async fn parse_file_content( string: String, fs_path_vc: Vc, fs_path: &FileSystemPath, @@ -433,7 +433,7 @@ async fn parse_content( &parsed_program, unresolved_mark, top_level_mark, - None, + Some(&comments), Some(source), ); diff --git a/turbopack/crates/turbopack-ecmascript/src/references/amd.rs b/turbopack/crates/turbopack-ecmascript/src/references/amd.rs index 1b295b46f6d63..ef577175e7a32 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/amd.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/amd.rs @@ -65,6 +65,7 @@ impl ModuleReference for AmdDefineAssetReference { self.request, Some(self.issue_source), try_to_severity(self.in_try), + /* ignore */ false, ) } } @@ -160,6 +161,7 @@ impl CodeGenerateable for AmdDefineWithDependenciesCodeGen { *request, Some(self.issue_source), try_to_severity(self.in_try), + false, ), Value::new(ChunkItem), ) diff --git a/turbopack/crates/turbopack-ecmascript/src/references/cjs.rs b/turbopack/crates/turbopack-ecmascript/src/references/cjs.rs index 2d1ef42c70d9b..b7d922e81b9b1 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/cjs.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/cjs.rs @@ -56,6 +56,7 @@ impl ModuleReference for CjsAssetReference { self.request, Some(self.issue_source), try_to_severity(self.in_try), + false, ) } } @@ -81,6 +82,7 @@ pub struct CjsRequireAssetReference { pub path: Vc, pub issue_source: Vc, pub in_try: bool, + pub ignore_import: bool, } #[turbo_tasks::value_impl] @@ -92,6 +94,7 @@ impl CjsRequireAssetReference { path: Vc, issue_source: Vc, in_try: bool, + ignore_import: bool, ) -> Vc { Self::cell(CjsRequireAssetReference { origin, @@ -99,6 +102,7 @@ impl CjsRequireAssetReference { path, issue_source, in_try, + ignore_import, }) } } @@ -112,6 +116,7 @@ impl ModuleReference for CjsRequireAssetReference { self.request, Some(self.issue_source), try_to_severity(self.in_try), + self.ignore_import, ) } } @@ -145,6 +150,7 @@ impl CodeGenerateable for CjsRequireAssetReference { self.request, Some(self.issue_source), try_to_severity(self.in_try), + self.ignore_import, ), Value::new(ChunkItem), ) @@ -188,6 +194,7 @@ pub struct CjsRequireResolveAssetReference { pub path: Vc, pub issue_source: Vc, pub in_try: bool, + pub ignore: bool, } #[turbo_tasks::value_impl] @@ -199,6 +206,7 @@ impl CjsRequireResolveAssetReference { path: Vc, issue_source: Vc, in_try: bool, + ignore: bool, ) -> Vc { Self::cell(CjsRequireResolveAssetReference { origin, @@ -206,6 +214,7 @@ impl CjsRequireResolveAssetReference { path, issue_source, in_try, + ignore, }) } } @@ -219,6 +228,7 @@ impl ModuleReference for CjsRequireResolveAssetReference { self.request, Some(self.issue_source), try_to_severity(self.in_try), + self.ignore, ) } } @@ -252,6 +262,7 @@ impl CodeGenerateable for CjsRequireResolveAssetReference { self.request, Some(self.issue_source), try_to_severity(self.in_try), + self.ignore, ), Value::new(ChunkItem), ) diff --git a/turbopack/crates/turbopack-ecmascript/src/references/esm/base.rs b/turbopack/crates/turbopack-ecmascript/src/references/esm/base.rs index b0e813bc6c1a5..e515305a4f5ce 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/esm/base.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/esm/base.rs @@ -94,6 +94,10 @@ pub struct EsmAssetReference { pub origin: Vc>, pub request: Vc, pub annotations: ImportAnnotations, + /// True if the import should be ignored + /// This can happen for example when the webpackIgnore or turbopackIgnore + /// directives are present + pub ignore: bool, pub issue_source: Option>, pub export_name: Option>, pub import_externals: bool, @@ -123,12 +127,14 @@ impl EsmAssetReference { annotations: Value, export_name: Option>, import_externals: bool, + ignore: bool, ) -> Vc { Self::cell(EsmAssetReference { origin, request, issue_source, annotations: annotations.into_value(), + ignore, export_name, import_externals, }) @@ -144,6 +150,9 @@ impl EsmAssetReference { impl ModuleReference for EsmAssetReference { #[turbo_tasks::function] async fn resolve_reference(&self) -> Result> { + if self.ignore { + return Ok(ModuleResolveResult::ignored().cell()); + } let ty = if matches!(self.annotations.module_type(), Some("json")) { EcmaScriptModulesReferenceSubType::ImportWithType(ImportWithType::Json) } else if let Some(part) = &self.export_name { @@ -176,6 +185,7 @@ impl ModuleReference for EsmAssetReference { Value::new(ty), IssueSeverity::Error.cell(), self.issue_source, + self.ignore, )) } } diff --git a/turbopack/crates/turbopack-ecmascript/src/references/esm/dynamic.rs b/turbopack/crates/turbopack-ecmascript/src/references/esm/dynamic.rs index ed7b68e2e1645..93f6acc1a74c1 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/esm/dynamic.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/esm/dynamic.rs @@ -31,6 +31,7 @@ pub struct EsmAsyncAssetReference { pub issue_source: Vc, pub in_try: bool, pub import_externals: bool, + pub ignore: bool, } #[turbo_tasks::value_impl] @@ -43,6 +44,7 @@ impl EsmAsyncAssetReference { issue_source: Vc, in_try: bool, import_externals: bool, + ignore: bool, ) -> Vc { Self::cell(EsmAsyncAssetReference { origin, @@ -51,6 +53,7 @@ impl EsmAsyncAssetReference { issue_source, in_try, import_externals, + ignore, }) } } @@ -65,6 +68,7 @@ impl ModuleReference for EsmAsyncAssetReference { Value::new(EcmaScriptModulesReferenceSubType::DynamicImport), try_to_severity(self.in_try), Some(self.issue_source), + self.ignore, ) } } @@ -104,6 +108,7 @@ impl CodeGenerateable for EsmAsyncAssetReference { Value::new(EcmaScriptModulesReferenceSubType::DynamicImport), try_to_severity(self.in_try), Some(self.issue_source), + self.ignore, ), if matches!( *chunking_context.environment().chunk_loading().await?, diff --git a/turbopack/crates/turbopack-ecmascript/src/references/mod.rs b/turbopack/crates/turbopack-ecmascript/src/references/mod.rs index 665cf4ef69b2f..64121eaf8a90b 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/mod.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/mod.rs @@ -17,7 +17,7 @@ pub mod util; use std::{ borrow::Cow, - collections::{BTreeMap, HashMap, HashSet}, + collections::{BTreeMap, HashMap}, future::Future, mem::take, pin::Pin, @@ -365,13 +365,26 @@ struct AnalysisState<'a> { } impl<'a> AnalysisState<'a> { - async fn link_value(&self, value: JsValue) -> Result { + /// Links a value to the graph, returning the linked value. + /// + /// * `in_try` is true if the value is in a try block. + /// * `ignore` is true if the value is ignored. This is perhaps the case when the webpackIgnore + /// or turbopackIgnore directives are present. + async fn link_value(&self, value: JsValue, ignore: bool) -> Result { let fun_args_values = self.fun_args_values.lock().clone(); link( self.var_graph, value.clone(), &early_value_visitor, - &|value| value_visitor(self.origin, value, self.compile_time_info, self.var_graph), + &|value| { + value_visitor( + self.origin, + value, + self.compile_time_info, + self.var_graph, + ignore, + ) + }, fun_args_values, ) .await @@ -395,13 +408,12 @@ where pub(crate) async fn analyse_ecmascript_module( module: Vc, part: Option>, - ignored_spans: Option>>, ) -> Result> { let span = { let module = module.ident().to_string().await?.to_string(); tracing::info_span!("analyse ecmascript module", module = module) }; - let result = analyse_ecmascript_module_internal(module, part, ignored_spans) + let result = analyse_ecmascript_module_internal(module, part) .instrument(span) .await; @@ -417,7 +429,6 @@ pub(crate) async fn analyse_ecmascript_module( pub(crate) async fn analyse_ecmascript_module_internal( module: Vc, part: Option>, - _webpack_ignored_effects: Option>>, ) -> Result> { let raw_module = module.await?; @@ -645,6 +656,7 @@ pub(crate) async fn analyse_ecmascript_module_internal( } }, import_externals, + false, ); import_references.push(r); @@ -914,7 +926,7 @@ pub(crate) async fn analyse_ecmascript_module_internal( span: _, in_try: _, } => { - let condition = analysis_state.link_value(condition).await?; + let condition = analysis_state.link_value(condition, false).await?; macro_rules! inactive { ($block:ident) => { @@ -1061,7 +1073,13 @@ pub(crate) async fn analyse_ecmascript_module_internal( continue; } } - let func = analysis_state.link_value(func).await?; + + let func = analysis_state + .link_value( + func, + eval_context.imports.get_ignore(span).unwrap_or_default(), + ) + .await?; handle_call( &ast_path, @@ -1089,8 +1107,8 @@ pub(crate) async fn analyse_ecmascript_module_internal( continue; } } - let mut obj = analysis_state.link_value(obj).await?; - let prop = analysis_state.link_value(prop).await?; + let mut obj = analysis_state.link_value(obj, false).await?; + let prop = analysis_state.link_value(prop, false).await?; if let JsValue::Array { items: ref mut values, @@ -1100,7 +1118,7 @@ pub(crate) async fn analyse_ecmascript_module_internal( { if matches!(prop.as_str(), Some("map" | "forEach" | "filter")) { if let [EffectArg::Closure(value, block)] = &mut args[..] { - *value = analysis_state.link_value(take(value)).await?; + *value = analysis_state.link_value(take(value), false).await?; if let JsValue::Function(_, func_ident, _) = value { let mut closure_arg = JsValue::alternatives(take(values)); if mutable { @@ -1124,7 +1142,10 @@ pub(crate) async fn analyse_ecmascript_module_internal( } let func = analysis_state - .link_value(JsValue::member(Box::new(obj.clone()), Box::new(prop))) + .link_value( + JsValue::member(Box::new(obj.clone()), Box::new(prop)), + false, + ) .await?; handle_call( @@ -1155,8 +1176,8 @@ pub(crate) async fn analyse_ecmascript_module_internal( span, in_try: _, } => { - let obj = analysis_state.link_value(obj).await?; - let prop = analysis_state.link_value(prop).await?; + let obj = analysis_state.link_value(obj, false).await?; + let prop = analysis_state.link_value(prop, false).await?; handle_member(&ast_path, obj, prop, span, &analysis_state, &mut analysis).await?; } @@ -1183,7 +1204,7 @@ pub(crate) async fn analyse_ecmascript_module_internal( ast_path, span, } => { - let arg = analysis_state.link_value(arg).await?; + let arg = analysis_state.link_value(arg, false).await?; handle_typeof(&ast_path, arg, span, &analysis_state, &mut analysis).await?; } Effect::ImportMeta { @@ -1304,7 +1325,7 @@ async fn handle_call) + Send + Sync>( JsValue::unknown_empty(true, "spread is not supported yet") } }; - state.link_value(value).await + state.link_value(value, false).await } }) .try_join() @@ -1331,7 +1352,7 @@ async fn handle_call) + Send + Sync>( .await?; } } - JsValue::WellKnownFunction(WellKnownFunctionKind::Import) => { + JsValue::WellKnownFunction(WellKnownFunctionKind::Import { ignore }) => { let args = linked_args(args).await?; if args.len() == 1 { let pat = js_value_to_pattern(&args[0]); @@ -1358,6 +1379,7 @@ async fn handle_call) + Send + Sync>( issue_source(source, span), in_try, state.import_externals, + ignore, )); return Ok(()); } @@ -1370,7 +1392,7 @@ async fn handle_call) + Send + Sync>( ), ) } - JsValue::WellKnownFunction(WellKnownFunctionKind::Require) => { + JsValue::WellKnownFunction(WellKnownFunctionKind::Require { ignore }) => { let args = linked_args(args).await?; if args.len() == 1 { let pat = js_value_to_pattern(&args[0]); @@ -1394,6 +1416,7 @@ async fn handle_call) + Send + Sync>( Vc::cell(ast_path.to_vec()), issue_source(source, span), in_try, + ignore, )); return Ok(()); } @@ -1441,6 +1464,7 @@ async fn handle_call) + Send + Sync>( Vc::cell(ast_path.to_vec()), issue_source(source, span), in_try, + false, )); return Ok(()); } @@ -1519,14 +1543,17 @@ async fn handle_call) + Send + Sync>( let args = linked_args(args).await?; let linked_func_call = state - .link_value(JsValue::call( - Box::new(JsValue::WellKnownFunction( - WellKnownFunctionKind::PathResolve(Box::new( - parent_path.path.as_str().into(), + .link_value( + JsValue::call( + Box::new(JsValue::WellKnownFunction( + WellKnownFunctionKind::PathResolve(Box::new( + parent_path.path.as_str().into(), + )), )), - )), - args.clone(), - )) + args.clone(), + ), + false, + ) .await?; let pat = js_value_to_pattern(&linked_func_call); @@ -1555,10 +1582,13 @@ async fn handle_call) + Send + Sync>( } let args = linked_args(args).await?; let linked_func_call = state - .link_value(JsValue::call( - Box::new(JsValue::WellKnownFunction(WellKnownFunctionKind::PathJoin)), - args.clone(), - )) + .link_value( + JsValue::call( + Box::new(JsValue::WellKnownFunction(WellKnownFunctionKind::PathJoin)), + args.clone(), + ), + false, + ) .await?; let pat = js_value_to_pattern(&linked_func_call); if !pat.has_constant_parts() { @@ -1591,7 +1621,7 @@ async fn handle_call) + Send + Sync>( if pat.is_match_ignore_dynamic("node") && args.len() >= 2 { let first_arg = JsValue::member(Box::new(args[1].clone()), Box::new(0_f64.into())); - let first_arg = state.link_value(first_arg).await?; + let first_arg = state.link_value(first_arg, false).await?; let pat = js_value_to_pattern(&first_arg); let dynamic = !pat.has_constant_parts(); if dynamic { @@ -1712,7 +1742,7 @@ async fn handle_call) + Send + Sync>( let args = linked_args(args).await?; if args.len() == 1 { - let first_arg = state.link_value(args[0].clone()).await?; + let first_arg = state.link_value(args[0].clone(), false).await?; if let Some(s) = first_arg.as_str() { // TODO this resolving should happen within Vc let current_context = origin @@ -1742,7 +1772,7 @@ async fn handle_call) + Send + Sync>( let args = linked_args(args).await?; if args.len() == 1 { - let first_arg = state.link_value(args[0].clone()).await?; + let first_arg = state.link_value(args[0].clone(), false).await?; if let Some(s) = first_arg.as_str() { analysis .add_reference(NodeBindingsReference::new(origin.origin_path(), s.into())); @@ -1783,15 +1813,18 @@ async fn handle_call) + Send + Sync>( pat } else { let linked_func_call = state - .link_value(JsValue::call( - Box::new(JsValue::WellKnownFunction( - WellKnownFunctionKind::PathJoin, - )), - vec![ - JsValue::FreeVar("__dirname".into()), - pkg_or_dir.clone(), - ], - )) + .link_value( + JsValue::call( + Box::new(JsValue::WellKnownFunction( + WellKnownFunctionKind::PathJoin, + )), + vec![ + JsValue::FreeVar("__dirname".into()), + pkg_or_dir.clone(), + ], + ), + false, + ) .await?; js_value_to_pattern(&linked_func_call) }; @@ -1836,14 +1869,19 @@ async fn handle_call) + Send + Sync>( Pattern::Constant(format!("{p}/intl").into()) } else { let linked_func_call = state - .link_value(JsValue::call( - Box::new(JsValue::WellKnownFunction(WellKnownFunctionKind::PathJoin)), - vec![ - JsValue::FreeVar("__dirname".into()), - p.into(), - "intl".into(), - ], - )) + .link_value( + JsValue::call( + Box::new(JsValue::WellKnownFunction( + WellKnownFunctionKind::PathJoin, + )), + vec![ + JsValue::FreeVar("__dirname".into()), + p.into(), + "intl".into(), + ], + ), + false, + ) .await?; js_value_to_pattern(&linked_func_call) }; @@ -1966,9 +2004,10 @@ async fn handle_member( } } match (obj, prop) { - (JsValue::WellKnownFunction(WellKnownFunctionKind::Require), JsValue::Constant(s)) - if s.as_str() == Some("cache") => - { + ( + JsValue::WellKnownFunction(WellKnownFunctionKind::Require { .. }), + JsValue::Constant(s), + ) if s.as_str() == Some("cache") => { analysis.add_code_gen( CjsRequireCacheAccess { path: Vc::cell(ast_path.to_vec()), @@ -2090,6 +2129,7 @@ async fn handle_free_var_reference( None => None, }, state.import_externals, + true, ) .resolve() .await?; @@ -2313,8 +2353,10 @@ async fn value_visitor( v: JsValue, compile_time_info: Vc, var_graph: &VarGraph, + ignore: bool, ) -> Result<(JsValue, bool)> { - let (mut v, modified) = value_visitor_inner(origin, v, compile_time_info, var_graph).await?; + let (mut v, modified) = + value_visitor_inner(origin, v, compile_time_info, var_graph, ignore).await?; v.normalize_shallow(); Ok((v, modified)) } @@ -2324,6 +2366,7 @@ async fn value_visitor_inner( v: JsValue, compile_time_info: Vc, var_graph: &VarGraph, + ignore: bool, ) -> Result<(JsValue, bool)> { // This check is just an optimization if v.get_defineable_name_len().is_some() { @@ -2373,9 +2416,9 @@ async fn value_visitor_inner( "__dirname" => as_abs_path(origin.origin_path().parent()).await?, "__filename" => as_abs_path(origin.origin_path()).await?, - "require" => JsValue::WellKnownFunction(WellKnownFunctionKind::Require), + "require" => JsValue::WellKnownFunction(WellKnownFunctionKind::Require { ignore }), "define" => JsValue::WellKnownFunction(WellKnownFunctionKind::Define), - "import" => JsValue::WellKnownFunction(WellKnownFunctionKind::Import), + "import" => JsValue::WellKnownFunction(WellKnownFunctionKind::Import { ignore }), "process" => JsValue::WellKnownObject(WellKnownObjectKind::NodeProcess), "Object" => JsValue::WellKnownObject(WellKnownObjectKind::GlobalObject), "Buffer" => JsValue::WellKnownObject(WellKnownObjectKind::NodeBuffer), @@ -2409,9 +2452,15 @@ async fn require_resolve_visitor( Ok(if args.len() == 1 { let pat = js_value_to_pattern(&args[0]); let request = Request::parse(Value::new(pat.clone())); - let resolved = cjs_resolve(origin, request, None, IssueSeverity::Warning.cell()) - .resolve() - .await?; + let resolved = cjs_resolve( + origin, + request, + None, + IssueSeverity::Warning.cell(), + /* ignore */ false, + ) + .resolve() + .await?; let mut values = resolved .primary_modules() .await? @@ -2544,6 +2593,8 @@ impl StaticAnalyser { } } +/// A visitor that walks the AST and collects information about the various +/// references a module makes to other parts of the code. struct ModuleReferencesVisitor<'a> { eval_context: &'a EvalContext, old_analyser: StaticAnalyser, diff --git a/turbopack/crates/turbopack-ecmascript/src/references/require_context.rs b/turbopack/crates/turbopack-ecmascript/src/references/require_context.rs index 6c1db7bd6412d..7dcd6a9d7c0a9 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/require_context.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/require_context.rs @@ -176,7 +176,7 @@ impl RequireContextMap { for (context_relative, path) in list { if let Some(origin_relative) = origin_path.get_relative_path_to(&*path.await?) { let request = Request::parse(Value::new(origin_relative.clone().into())); - let result = cjs_resolve(origin, request, issue_source, issue_severity); + let result = cjs_resolve(origin, request, issue_source, issue_severity, false); map.insert( context_relative.clone(), diff --git a/turbopack/crates/turbopack-ecmascript/src/side_effect_optimization/locals/chunk_item.rs b/turbopack/crates/turbopack-ecmascript/src/side_effect_optimization/locals/chunk_item.rs index 13eb72cdef06f..d547bab77f80d 100644 --- a/turbopack/crates/turbopack-ecmascript/src/side_effect_optimization/locals/chunk_item.rs +++ b/turbopack/crates/turbopack-ecmascript/src/side_effect_optimization/locals/chunk_item.rs @@ -13,7 +13,7 @@ use crate::{ EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkPlaceable, EcmascriptChunkType, }, - EcmascriptAnalyzable, EcmascriptModuleContent, + EcmascriptModuleContent, }; /// The chunk item for [EcmascriptModuleLocalsModule]. diff --git a/turbopack/crates/turbopack-ecmascript/src/side_effect_optimization/locals/module.rs b/turbopack/crates/turbopack-ecmascript/src/side_effect_optimization/locals/module.rs index b276c96017476..0cc104ef0b999 100644 --- a/turbopack/crates/turbopack-ecmascript/src/side_effect_optimization/locals/module.rs +++ b/turbopack/crates/turbopack-ecmascript/src/side_effect_optimization/locals/module.rs @@ -19,7 +19,7 @@ use crate::{ async_module::OptionAsyncModule, esm::{EsmExport, EsmExports}, }, - EcmascriptAnalyzable, EcmascriptModuleAsset, + EcmascriptModuleAsset, }; /// A module derived from an original ecmascript module that only contains the diff --git a/turbopack/crates/turbopack-ecmascript/src/tree_shake/asset.rs b/turbopack/crates/turbopack-ecmascript/src/tree_shake/asset.rs index 0afbf04d58ff1..4eaf6fea17856 100644 --- a/turbopack/crates/turbopack-ecmascript/src/tree_shake/asset.rs +++ b/turbopack/crates/turbopack-ecmascript/src/tree_shake/asset.rs @@ -1,7 +1,4 @@ -use std::collections::HashSet; - use anyhow::{Context, Result}; -use swc_core::common::Span; use turbo_tasks::Vc; use turbopack_core::{ asset::{Asset, AssetContent}, @@ -62,11 +59,7 @@ impl EcmascriptAnalyzable for EcmascriptModulePartAsset { async fn analyze(self: Vc) -> Result> { let this = self.await?; let part = this.part; - Ok(analyse_ecmascript_module( - this.full_module, - Some(part), - None, - )) + Ok(analyse_ecmascript_module(this.full_module, Some(part))) } #[turbo_tasks::function] @@ -138,7 +131,7 @@ impl Module for EcmascriptModulePartAsset { async fn references(&self) -> Result> { let split_data = split_module(self.full_module).await?; - let analyze = analyze(self.full_module, self.part, None).await?; + let analyze = analyze(self.full_module, self.part).await?; let (deps, entrypoints) = match &*split_data { SplitResult::Ok { @@ -271,7 +264,7 @@ impl EcmascriptModulePartAsset { pub(super) async fn analyze(self: Vc) -> Result> { let this = self.await?; - Ok(analyze(this.full_module, this.part, None)) + Ok(analyze(this.full_module, this.part)) } } @@ -279,9 +272,8 @@ impl EcmascriptModulePartAsset { fn analyze( module: Vc, part: Vc, - ignored_spans: Option>>, ) -> Result> { - Ok(analyse_ecmascript_module(module, Some(part), ignored_spans)) + Ok(analyse_ecmascript_module(module, Some(part))) } #[turbo_tasks::value_impl] diff --git a/turbopack/crates/turbopack-ecmascript/src/typescript/mod.rs b/turbopack/crates/turbopack-ecmascript/src/typescript/mod.rs index f9f5da82b40a5..08a43a2098941 100644 --- a/turbopack/crates/turbopack-ecmascript/src/typescript/mod.rs +++ b/turbopack/crates/turbopack-ecmascript/src/typescript/mod.rs @@ -182,7 +182,13 @@ impl CompilerReference { impl ModuleReference for CompilerReference { #[turbo_tasks::function] fn resolve_reference(&self) -> Vc { - cjs_resolve(self.origin, self.request, None, IssueSeverity::Error.cell()) + cjs_resolve( + self.origin, + self.request, + None, + IssueSeverity::Error.cell(), + false, + ) } } @@ -251,7 +257,13 @@ impl TsNodeRequireReference { impl ModuleReference for TsNodeRequireReference { #[turbo_tasks::function] fn resolve_reference(&self) -> Vc { - cjs_resolve(self.origin, self.request, None, IssueSeverity::Error.cell()) + cjs_resolve( + self.origin, + self.request, + None, + IssueSeverity::Error.cell(), + false, + ) } } diff --git a/turbopack/crates/turbopack-resolve/src/ecmascript.rs b/turbopack/crates/turbopack-resolve/src/ecmascript.rs index 9ccbcb72c0fc1..00518053395b9 100644 --- a/turbopack/crates/turbopack-resolve/src/ecmascript.rs +++ b/turbopack/crates/turbopack-resolve/src/ecmascript.rs @@ -80,12 +80,22 @@ pub async fn esm_resolve( ty: Value, issue_severity: Vc, issue_source: Option>, + ignore: bool, ) -> Result> { let ty = Value::new(ReferenceType::EcmaScriptModules(ty.into_value())); let options = apply_esm_specific_options(origin.resolve_options(ty.clone()), ty.clone()) .resolve() .await?; - specific_resolve(origin, request, options, ty, issue_severity, issue_source).await + specific_resolve( + origin, + request, + options, + ty, + issue_severity, + issue_source, + ignore, + ) + .await } #[turbo_tasks::function] @@ -94,13 +104,23 @@ pub async fn cjs_resolve( request: Vc, issue_source: Option>, issue_severity: Vc, + ignore: bool, ) -> Result> { // TODO pass CommonJsReferenceSubType let ty = Value::new(ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined)); let options = apply_cjs_specific_options(origin.resolve_options(ty.clone())) .resolve() .await?; - specific_resolve(origin, request, options, ty, issue_severity, issue_source).await + specific_resolve( + origin, + request, + options, + ty, + issue_severity, + issue_source, + ignore, + ) + .await } async fn specific_resolve( @@ -110,7 +130,12 @@ async fn specific_resolve( reference_type: Value, issue_severity: Vc, issue_source: Option>, + ignore: bool, ) -> Result> { + if ignore { + return Ok(ModuleResolveResult::ignored().cell()); + } + let result = origin.resolve_asset(request, options, reference_type.clone()); handle_resolve_error( diff --git a/turbopack/crates/turbopack-tests/tests/execution/turbopack/ignore/import/input/index.js b/turbopack/crates/turbopack-tests/tests/execution/turbopack/ignore/import/input/index.js new file mode 100644 index 0000000000000..eaa515b3ab0e9 --- /dev/null +++ b/turbopack/crates/turbopack-tests/tests/execution/turbopack/ignore/import/input/index.js @@ -0,0 +1,27 @@ +it('should ignore webpackIgnore comments', async () => { + const _webpackImportIgnore = await import( + /* webpackIgnore: true */ './ignore.js' + ) + // just the fact that this doesn't fail in bundling is enough +}) + +it('should ignore turbopackIgnore comments', async () => { + const _turbopackImportIgnore = await import( + /* turbopackIgnore: true */ './ignore.js' + ) + // just the fact that this doesn't fail in bundling is enough +}) + +it('should not ignore webpackIgnore comments', async () => { + const webpackImportNotIgnore = await import( + /* webpackIgnore: false */ './util.js' + ) + expect(webpackImportNotIgnore).toBeDefined() +}) + +it('should not ignore turbopackIgnore comments', async () => { + const turbopackImportNotIgnore = await import( + /* turbopackIgnore: false */ './util.js' + ) + expect(turbopackImportNotIgnore).toBeDefined() +}) diff --git a/turbopack/crates/turbopack-tests/tests/execution/turbopack/ignore/import/input/util.js b/turbopack/crates/turbopack-tests/tests/execution/turbopack/ignore/import/input/util.js new file mode 100644 index 0000000000000..5871e78b6c1a5 --- /dev/null +++ b/turbopack/crates/turbopack-tests/tests/execution/turbopack/ignore/import/input/util.js @@ -0,0 +1 @@ +export default "util"; diff --git a/turbopack/crates/turbopack-tests/tests/execution/turbopack/ignore/require/input/index.js b/turbopack/crates/turbopack-tests/tests/execution/turbopack/ignore/require/input/index.js new file mode 100644 index 0000000000000..887835a7c8b2c --- /dev/null +++ b/turbopack/crates/turbopack-tests/tests/execution/turbopack/ignore/require/input/index.js @@ -0,0 +1,19 @@ +it('should ignore webpackIgnore comments', async () => { + const _webpackImportIgnore = require(/* webpackIgnore: true */ './ignore.js') + // just the fact that this doesn't fail in bundling is enough +}) + +it('should ignore turbopackIgnore comments', async () => { + const _turbopackImportIgnore = require(/* turbopackIgnore: true */ './ignore.js') + // just the fact that this doesn't fail in bundling is enough +}) + +it('should not ignore webpackIgnore comments', async () => { + const webpackImportNotIgnore = require(/* webpackIgnore: false */ './util.js') + expect(webpackImportNotIgnore).toBeDefined() +}) + +it('should not ignore turbopackIgnore comments', async () => { + const turbopackImportNotIgnore = require(/* turbopackIgnore: false */ './util.js') + expect(turbopackImportNotIgnore).toBeDefined() +}) diff --git a/turbopack/crates/turbopack-tests/tests/execution/turbopack/ignore/require/input/util.js b/turbopack/crates/turbopack-tests/tests/execution/turbopack/ignore/require/input/util.js new file mode 100644 index 0000000000000..5871e78b6c1a5 --- /dev/null +++ b/turbopack/crates/turbopack-tests/tests/execution/turbopack/ignore/require/input/util.js @@ -0,0 +1 @@ +export default "util";