-
Notifications
You must be signed in to change notification settings - Fork 27k
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-ecmascript): Add implementation for webpackIgnore and turbopackIgnore (revision of #69113) #69768
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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}, | ||
|
@@ -140,16 +140,56 @@ pub(crate) struct ImportMap { | |
/// True if the module is an ESM module due to top-level await. | ||
has_top_level_await: bool, | ||
|
||
/// Locations of webpackIgnore or turbopackIgnore comments | ||
/// This is a webpack feature that allows opting out of static | ||
/// imports, which we should respect. | ||
/// Locations of [webpack-style "magic comments"][magic] that override import behaviors. | ||
/// | ||
/// Most commonly, these are `/* webpackIgnore: true */` comments. See [ImportOverrides] for | ||
/// full details. | ||
/// | ||
/// [magic]: https://webpack.js.org/api/module-methods/#magic-comments | ||
overrides: HashMap<BytePos, ImportOverrides>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel Maybe |
||
} | ||
|
||
/// Represents a collection of [webpack-style "magic comments"][magic] that override import | ||
/// behaviors. | ||
/// | ||
/// [magic]: https://webpack.js.org/api/module-methods/#magic-comments | ||
#[derive(Debug)] | ||
pub(crate) struct ImportOverrides { | ||
/// Should we ignore this import expression when bundling? If so, the import expression will be | ||
/// left as-is in Turbopack's output. | ||
/// | ||
/// This is set by using either a `webpackIgnore` or `turbopackIgnore` comment. | ||
/// | ||
/// Example: | ||
/// ```js | ||
/// const a = import(/* webpackIgnore: true */ "a"); | ||
/// const b = import(/* turbopackIgnore: true */ "b"); | ||
/// ``` | ||
turbopack_ignores: HashMap<Span, bool>, | ||
pub ignore: bool, | ||
} | ||
|
||
impl ImportOverrides { | ||
pub const fn empty() -> Self { | ||
ImportOverrides { ignore: false } | ||
} | ||
|
||
pub fn empty_ref() -> &'static Self { | ||
// use `Self::empty` here as `Default::default` isn't const | ||
static DEFAULT_VALUE: ImportOverrides = ImportOverrides::empty(); | ||
&DEFAULT_VALUE | ||
} | ||
} | ||
|
||
impl Default for ImportOverrides { | ||
fn default() -> Self { | ||
ImportOverrides::empty() | ||
} | ||
} | ||
|
||
impl Default for &ImportOverrides { | ||
fn default() -> Self { | ||
ImportOverrides::empty_ref() | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
|
@@ -194,6 +234,10 @@ impl ImportMap { | |
None | ||
} | ||
|
||
pub fn get_overrides(&self, span: Span) -> &ImportOverrides { | ||
self.overrides.get(&span.lo).unwrap_or_default() | ||
} | ||
|
||
// TODO this could return &str instead of String to avoid cloning | ||
pub fn get_binding(&self, id: &Id) -> Option<(usize, Option<RcStr>)> { | ||
if let Some((i, i_sym)) = self.imports.get(id) { | ||
|
@@ -459,7 +503,7 @@ impl Visit for Analyzer<'_> { | |
}; | ||
|
||
// we are interested here in the last comment with a valid directive | ||
let ignore_statement = n | ||
let ignore_directive = n | ||
.args | ||
.first() | ||
.map(|arg| arg.span_lo()) | ||
|
@@ -478,10 +522,15 @@ impl Visit for Analyzer<'_> { | |
}) | ||
.next(); | ||
|
||
if let Some((callee_span, ignore_statement)) = callee_span.zip(ignore_statement) { | ||
self.data | ||
.turbopack_ignores | ||
.insert(*callee_span, ignore_statement); | ||
// potentially support more webpack magic comments in the future: | ||
// https://webpack.js.org/api/module-methods/#magic-comments | ||
if let Some((callee_span, ignore_directive)) = callee_span.zip(ignore_directive) { | ||
self.data.overrides.insert( | ||
callee_span.lo, | ||
ImportOverrides { | ||
ignore: ignore_directive, | ||
}, | ||
); | ||
}; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,6 +94,10 @@ pub struct EsmAssetReference { | |
pub origin: Vc<Box<dyn ResolveOrigin>>, | ||
pub request: Vc<Request>, | ||
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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That looks unused, could we remove that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch, I think this was left over from my refactoring efforts. |
||
pub issue_source: Option<Vc<IssueSource>>, | ||
pub export_name: Option<Vc<ModulePart>>, | ||
pub import_externals: bool, | ||
|
@@ -123,12 +127,14 @@ impl EsmAssetReference { | |
annotations: Value<ImportAnnotations>, | ||
export_name: Option<Vc<ModulePart>>, | ||
import_externals: bool, | ||
ignore: bool, | ||
) -> Vc<Self> { | ||
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<Vc<ModuleResolveResult>> { | ||
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 { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why having
WellKnownFunctionKind ::Require { ignore: true }
at all?This could just be
JsValue::Unknown
for a ignoredrequire
orimport
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might've been needed when this code wasn't bailing out as early before. I'll try removing it 👍