Add support for new URL (vercel/turbo#2944)

Implements `new URL('./path', import.meta.url)`, so that the referenced file acts as a static output file, and transforms the URL into to point to that file in the graph.

This is, well, complicated. As you'll see in the comments, we have an "inert" asset that acts as a placeholder until the module chunking algorithm gives us access to a `ChunkingContextVc`. It's unfortunately not available when we're doing the file parsing, but we can insert a reference to that inert asset. That inert asset will eventually create a `UrlAssetChunk` instance, and with that we can finally generate the correct output path for the URL referenced file.

We actually discussed whether this could use a `StaticAsset`/`StaticModuleAsset` (a `StaticAsset` can be inserted at parse time), but it turns out that won't give us the necessary control to generate the final `URL` instance. A `StaticModuleAsset` always outputs an absolute path as it's default export, and that just won't work when we're trying to reference the file in node SSR.

Fixes WEB-142.
Pending WEB-279 for full node SSR support.

Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
This commit is contained in:
Justin Ridgewell 2022-12-14 18:44:32 -05:00 committed by GitHub
parent 650b80366b
commit f590196931
3 changed files with 22 additions and 7 deletions

View file

@ -18,6 +18,7 @@ use turbopack_core::{
chunk::{dev::DevChunkingContextVc, ChunkingContextVc}, chunk::{dev::DevChunkingContextVc, ChunkingContextVc},
context::AssetContextVc, context::AssetContextVc,
environment::{BrowserEnvironment, EnvironmentIntention, EnvironmentVc, ExecutionEnvironment}, environment::{BrowserEnvironment, EnvironmentIntention, EnvironmentVc, ExecutionEnvironment},
reference_type::{ReferenceType, UrlReferenceSubType},
resolve::{parse::RequestVc, pattern::Pattern}, resolve::{parse::RequestVc, pattern::Pattern},
}; };
use turbopack_ecmascript::{EcmascriptInputTransform, EcmascriptInputTransformsVc}; use turbopack_ecmascript::{EcmascriptInputTransform, EcmascriptInputTransformsVc};
@ -120,6 +121,9 @@ pub async fn add_next_transforms_to_pages(
module_options_context.custom_rules.push(ModuleRule::new( module_options_context.custom_rules.push(ModuleRule::new(
ModuleRuleCondition::all(vec![ ModuleRuleCondition::all(vec![
ModuleRuleCondition::ResourcePathInExactDirectory(pages_dir.await?), ModuleRuleCondition::ResourcePathInExactDirectory(pages_dir.await?),
ModuleRuleCondition::not(ModuleRuleCondition::ReferenceType(ReferenceType::Url(
UrlReferenceSubType::Undefined,
))),
ModuleRuleCondition::any(vec![ ModuleRuleCondition::any(vec![
ModuleRuleCondition::ResourcePathEndsWith(".js".to_string()), ModuleRuleCondition::ResourcePathEndsWith(".js".to_string()),
ModuleRuleCondition::ResourcePathEndsWith(".jsx".to_string()), ModuleRuleCondition::ResourcePathEndsWith(".jsx".to_string()),
@ -146,12 +150,17 @@ pub async fn add_next_font_transform(
let mut module_options_context = module_options_context.await?.clone_value(); let mut module_options_context = module_options_context.await?.clone_value();
module_options_context.custom_rules.push(ModuleRule::new( module_options_context.custom_rules.push(ModuleRule::new(
// TODO: Only match in pages (not pages/api), app/, etc. // TODO: Only match in pages (not pages/api), app/, etc.
ModuleRuleCondition::all(vec![
ModuleRuleCondition::not(ModuleRuleCondition::ReferenceType(ReferenceType::Url(
UrlReferenceSubType::Undefined,
))),
ModuleRuleCondition::any(vec![ ModuleRuleCondition::any(vec![
ModuleRuleCondition::ResourcePathEndsWith(".js".to_string()), ModuleRuleCondition::ResourcePathEndsWith(".js".to_string()),
ModuleRuleCondition::ResourcePathEndsWith(".jsx".to_string()), ModuleRuleCondition::ResourcePathEndsWith(".jsx".to_string()),
ModuleRuleCondition::ResourcePathEndsWith(".ts".to_string()), ModuleRuleCondition::ResourcePathEndsWith(".ts".to_string()),
ModuleRuleCondition::ResourcePathEndsWith(".tsx".to_string()), ModuleRuleCondition::ResourcePathEndsWith(".tsx".to_string()),
]), ]),
]),
vec![ModuleRuleEffect::AddEcmascriptTransforms( vec![ModuleRuleEffect::AddEcmascriptTransforms(
EcmascriptInputTransformsVc::cell(vec![EcmascriptInputTransform::NextJsFont( EcmascriptInputTransformsVc::cell(vec![EcmascriptInputTransform::NextJsFont(
StringsVc::cell(font_loaders), StringsVc::cell(font_loaders),

View file

@ -12,6 +12,7 @@ use turbopack_core::{
asset::AssetVc, asset::AssetVc,
chunk::{dev::DevChunkingContextVc, ChunkingContextVc}, chunk::{dev::DevChunkingContextVc, ChunkingContextVc},
context::AssetContextVc, context::AssetContextVc,
reference_type::{EntryReferenceSubType, ReferenceType},
source_asset::SourceAssetVc, source_asset::SourceAssetVc,
virtual_asset::VirtualAssetVc, virtual_asset::VirtualAssetVc,
}; };
@ -166,7 +167,10 @@ async fn create_server_rendered_source_for_file(
intermediate_output_path: FileSystemPathVc, intermediate_output_path: FileSystemPathVc,
) -> Result<ContentSourceVc> { ) -> Result<ContentSourceVc> {
let source_asset = SourceAssetVc::new(page_file).into(); let source_asset = SourceAssetVc::new(page_file).into();
let entry_asset = context.process(source_asset); let entry_asset = context.process(
source_asset,
Value::new(ReferenceType::Entry(EntryReferenceSubType::Page)),
);
let chunking_context = DevChunkingContextVc::builder( let chunking_context = DevChunkingContextVc::builder(
context_path, context_path,

View file

@ -5,6 +5,7 @@ use turbo_tasks_fs::FileSystemPathVc;
use turbopack::ecmascript::EcmascriptModuleAssetVc; use turbopack::ecmascript::EcmascriptModuleAssetVc;
use turbopack_core::{ use turbopack_core::{
chunk::{ChunkGroupVc, ChunkableAssetVc}, chunk::{ChunkGroupVc, ChunkableAssetVc},
reference_type::{EntryReferenceSubType, ReferenceType},
resolve::{origin::PlainResolveOriginVc, parse::RequestVc}, resolve::{origin::PlainResolveOriginVc, parse::RequestVc},
}; };
use turbopack_dev_server::{ use turbopack_dev_server::{
@ -42,8 +43,9 @@ pub async fn create_web_entry_source(
let entries = entry_requests let entries = entry_requests
.into_iter() .into_iter()
.map(|request| async move { .map(|request| async move {
let ty = Value::new(ReferenceType::Entry(EntryReferenceSubType::Web));
Ok(origin Ok(origin
.resolve_asset(request, origin.resolve_options()) .resolve_asset(request, origin.resolve_options(ty.clone()), ty)
.primary_assets() .primary_assets()
.await? .await?
.first() .first()