Skip to content

Commit

Permalink
Turbopack: Add BeforeResolve plugin type (#8165)
Browse files Browse the repository at this point in the history
This adds a new plugin type for replacing resolution _before_ it occurs.
This allows plugin authors to intercept requests for modules in place of
the typical resolution strategy.

This is used to replace `ImportMapping` replacers for `@next/font/local`
in an upcoming Next.js PR
  • Loading branch information
wbinnssmith authored and Neosoulink committed Jun 14, 2024
1 parent 7048dd3 commit a6152cd
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 29 deletions.
62 changes: 49 additions & 13 deletions crates/turbopack-core/src/resolve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use self::{
origin::{ResolveOrigin, ResolveOriginExt},
parse::Request,
pattern::Pattern,
plugin::BeforeResolvePlugin,
remap::{ExportsField, ImportsField},
};
use crate::{
Expand All @@ -38,7 +39,7 @@ use crate::{
reference_type::ReferenceType,
resolve::{
pattern::{read_matches, PatternMatch},
plugin::ResolvePlugin,
plugin::AfterResolvePlugin,
},
source::{OptionSource, Source, Sources},
};
Expand Down Expand Up @@ -66,6 +67,7 @@ pub enum ModuleResolveResultItem {
OutputAsset(Vc<Box<dyn OutputAsset>>),
External(String, ExternalType),
Ignore,
Error(Vc<String>),
Empty,
Custom(u8),
Unresolveable,
Expand Down Expand Up @@ -404,6 +406,7 @@ pub enum ResolveResultItem {
Source(Vc<Box<dyn Source>>),
External(String, ExternalType),
Ignore,
Error(Vc<String>),
Empty,
Custom(u8),
Unresolveable,
Expand Down Expand Up @@ -488,6 +491,9 @@ impl ValueToString for ResolveResult {
ResolveResultItem::Empty => {
result.push_str("empty");
}
ResolveResultItem::Error(_) => {
result.push_str("error");
}
ResolveResultItem::Custom(_) => {
result.push_str("custom");
}
Expand Down Expand Up @@ -674,6 +680,7 @@ impl ResolveResult {
}
ResolveResultItem::Ignore => ModuleResolveResultItem::Ignore,
ResolveResultItem::Empty => ModuleResolveResultItem::Empty,
ResolveResultItem::Error(e) => ModuleResolveResultItem::Error(e),
ResolveResultItem::Custom(u8) => {
ModuleResolveResultItem::Custom(u8)
}
Expand Down Expand Up @@ -1326,17 +1333,23 @@ pub async fn resolve_inline(
)
};
async {
let raw_result = resolve_internal(lookup_path, request, options)
.resolve()
.await?;
let result = handle_resolve_plugins(
lookup_path,
Value::new(reference_type),
request,
options,
raw_result,
)
.await?;
let reference_type = Value::new(reference_type);
let before_plugins_result =
handle_before_resolve_plugins(lookup_path, reference_type.clone(), request, options)
.await?;

let raw_result = match before_plugins_result {
Some(result) => result,
None => {
resolve_internal(lookup_path, request, options)
.resolve()
.await?
}
};

let result =
handle_after_resolve_plugins(lookup_path, reference_type, request, options, raw_result)
.await?;
Ok(result)
}
.instrument(span)
Expand Down Expand Up @@ -1386,7 +1399,29 @@ pub async fn url_resolve(
.await
}

async fn handle_resolve_plugins(
async fn handle_before_resolve_plugins(
lookup_path: Vc<FileSystemPath>,
reference_type: Value<ReferenceType>,
request: Vc<Request>,
options: Vc<ResolveOptions>,
) -> Result<Option<Vc<ResolveResult>>> {
for plugin in &options.await?.before_resolve_plugins {
let condition = plugin.before_resolve_condition().resolve().await?;
if !*condition.matches(request).await? {
continue;
}

if let Some(result) = *plugin
.before_resolve(lookup_path, reference_type.clone(), request)
.await?
{
return Ok(Some(result));
}
}
Ok(None)
}

async fn handle_after_resolve_plugins(
lookup_path: Vc<FileSystemPath>,
reference_type: Value<ReferenceType>,
request: Vc<Request>,
Expand Down Expand Up @@ -2553,6 +2588,7 @@ pub async fn handle_resolve_error(
.cell()
.emit();
}

result
}
Err(err) => {
Expand Down
6 changes: 4 additions & 2 deletions crates/turbopack-core/src/resolve/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ use turbo_tasks_fs::{glob::Glob, FileSystemPath};

use super::{
alias_map::{AliasMap, AliasTemplate},
plugin::BeforeResolvePlugin,
AliasPattern, ExternalType, ResolveResult, ResolveResultItem,
};
use crate::resolve::{parse::Request, plugin::ResolvePlugin};
use crate::resolve::{parse::Request, plugin::AfterResolvePlugin};

#[turbo_tasks::value(shared)]
#[derive(Hash, Debug)]
Expand Down Expand Up @@ -433,7 +434,8 @@ pub struct ResolveOptions {
/// An import map to use when a request is otherwise unresolveable.
pub fallback_import_map: Option<Vc<ImportMap>>,
pub resolved_map: Option<Vc<ResolvedMap>>,
pub plugins: Vec<Vc<Box<dyn ResolvePlugin>>>,
pub before_resolve_plugins: Vec<Vc<Box<dyn BeforeResolvePlugin>>>,
pub plugins: Vec<Vc<Box<dyn AfterResolvePlugin>>>,
pub placeholder_for_future_extensions: (),
}

Expand Down
48 changes: 43 additions & 5 deletions crates/turbopack-core/src/resolve/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ use crate::{

/// A condition which determines if the hooks of a resolve plugin gets called.
#[turbo_tasks::value]
pub struct ResolvePluginCondition {
pub struct AfterResolvePluginCondition {
root: Vc<FileSystemPath>,
glob: Vc<Glob>,
}

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

#[turbo_tasks::function]
Expand All @@ -39,10 +39,48 @@ impl ResolvePluginCondition {
}
}

/// A condition which determines if the hooks of a resolve plugin gets called.
#[turbo_tasks::value]
pub struct BeforeResolvePluginCondition {
glob: Vc<Glob>,
}

#[turbo_tasks::value_impl]
impl BeforeResolvePluginCondition {
#[turbo_tasks::function]
pub fn new(glob: Vc<Glob>) -> Vc<Self> {
BeforeResolvePluginCondition { glob }.cell()
}

#[turbo_tasks::function]
pub async fn matches(self: Vc<Self>, request: Vc<Request>) -> Result<Vc<bool>> {
Ok(Vc::cell(match request.await?.request() {
Some(request) => {
let this = self.await?;
let glob = this.glob.await?;
glob.execute(&request)
}
None => false,
}))
}
}

#[turbo_tasks::value_trait]
pub trait BeforeResolvePlugin {
fn before_resolve_condition(self: Vc<Self>) -> Vc<BeforeResolvePluginCondition>;

fn before_resolve(
self: Vc<Self>,
lookup_path: Vc<FileSystemPath>,
reference_type: Value<ReferenceType>,
request: Vc<Request>,
) -> Vc<ResolveResultOption>;
}

#[turbo_tasks::value_trait]
pub trait ResolvePlugin {
pub trait AfterResolvePlugin {
/// A condition which determines if the hooks gets called.
fn after_resolve_condition(self: Vc<Self>) -> Vc<ResolvePluginCondition>;
fn after_resolve_condition(self: Vc<Self>) -> Vc<AfterResolvePluginCondition>;

/// This hook gets called when a full filepath has been resolved and the
/// condition matches. If a value is returned it replaces the resolve
Expand Down
3 changes: 2 additions & 1 deletion crates/turbopack-resolve/src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ async fn base_resolve_options(
}
let import_map = import_map.cell();

let plugins = opt.plugins.clone();
let plugins = opt.after_resolve_plugins.clone();

let conditions = {
let mut conditions: ResolutionConditions = [
Expand Down Expand Up @@ -251,6 +251,7 @@ async fn base_resolve_options(
import_map: Some(import_map),
resolved_map: opt.resolved_map,
plugins,
before_resolve_plugins: opt.before_resolve_plugins.clone(),
..Default::default()
}
.into())
Expand Down
8 changes: 4 additions & 4 deletions crates/turbopack-resolve/src/resolve_options_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use turbopack_core::{
environment::Environment,
resolve::{
options::{ImportMap, ResolvedMap},
plugin::ResolvePlugin,
plugin::{AfterResolvePlugin, BeforeResolvePlugin},
},
};

Expand Down Expand Up @@ -69,9 +69,9 @@ pub struct ResolveOptionsContext {
/// context paths. The first matching is used.
pub rules: Vec<(ContextCondition, Vc<ResolveOptionsContext>)>,
#[serde(default)]
/// A list of plugins which get applied before (in the future) and after
/// resolving.
pub plugins: Vec<Vc<Box<dyn ResolvePlugin>>>,
/// Plugins which get applied before and after resolving.
pub after_resolve_plugins: Vec<Vc<Box<dyn AfterResolvePlugin>>>,
pub before_resolve_plugins: Vec<Vc<Box<dyn BeforeResolvePlugin>>>,
#[serde(default)]
pub placeholder_for_future_extensions: (),
}
Expand Down
11 changes: 7 additions & 4 deletions crates/turbopack/src/unsupported_sass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use turbopack_core::{
reference_type::ReferenceType,
resolve::{
parse::Request,
plugin::{ResolvePlugin, ResolvePluginCondition},
plugin::{AfterResolvePlugin, AfterResolvePluginCondition},
ResolveResultOption,
},
};
Expand All @@ -28,10 +28,13 @@ impl UnsupportedSassResolvePlugin {
}

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

#[turbo_tasks::function]
Expand Down

0 comments on commit a6152cd

Please sign in to comment.