loaders: use pre-compiled loader-runner distributed with Next.js (vercel/turbo#3823)

The benefit of this depends on
https://github.com/vercel/next.js/pull/45962, but it remains compatible
with apps including `loader-runner`.

This first attempts to require `loader-runner` from the app's installed
version of Next.js, falling back to requiring the package directly. We
should probably eventually remove this fallback once all compatible
versions of Next.js include the precompiled version, as that has a more
predictable version of the package.

Test Plan: Linked a local copy of Next.js including
https://github.com/vercel/next.js/pull/45962 to an app without
`loader-runner` that uses loaders and verified loaders ran.
This commit is contained in:
Will Binns-Smith 2023-02-21 12:34:01 -08:00 committed by GitHub
parent 90675f2a7c
commit 166b3d62cc
4 changed files with 51 additions and 18 deletions

View file

@ -1,4 +1,5 @@
use anyhow::Result;
use turbo_tasks::primitives::StringVc;
use turbo_tasks_fs::FileSystemPathVc;
use turbopack_core::resolve::options::{ImportMapping, ImportMappingVc};
@ -19,3 +20,17 @@ pub async fn get_postcss_package_mapping(
])
.cell())
}
#[turbo_tasks::function]
pub async fn get_external_next_compiled_package_mapping(
package_name: StringVc,
) -> Result<ImportMappingVc> {
Ok(
ImportMapping::Alternatives(vec![ImportMapping::External(Some(format!(
"next/dist/compiled/{}",
&*package_name.await?
)))
.into()])
.cell(),
)
}

View file

@ -2,13 +2,13 @@ use core::{default::Default, result::Result::Ok};
use std::collections::HashMap;
use anyhow::Result;
use turbo_tasks::Value;
use turbo_tasks::{primitives::StringVc, Value};
use turbo_tasks_env::ProcessEnvVc;
use turbo_tasks_fs::FileSystemPathVc;
use turbopack::{
module_options::{
module_options_context::{ModuleOptionsContext, ModuleOptionsContextVc},
PostCssTransformOptions,
PostCssTransformOptions, WebpackLoadersOptions,
},
resolve_options_context::{ResolveOptionsContext, ResolveOptionsContextVc},
transition::TransitionsByNameVc,
@ -28,7 +28,7 @@ use super::transforms::get_next_client_transforms_rules;
use crate::{
embed_js::attached_next_js_package_path,
env::env_for_js,
next_build::get_postcss_package_mapping,
next_build::{get_external_next_compiled_package_mapping, get_postcss_package_mapping},
next_client::runtime_entry::{RuntimeEntriesVc, RuntimeEntry},
next_config::NextConfigVc,
next_import_map::{
@ -113,6 +113,16 @@ pub async fn get_client_module_options_context(
.await?
.is_found();
let options = &*next_config.webpack_loaders_options().await?;
let enable_webpack_loaders = WebpackLoadersOptions {
extension_to_loaders: options.clone(),
loader_runner_package: Some(get_external_next_compiled_package_mapping(StringVc::cell(
"loader-runner".to_owned(),
))),
placeholder_for_future_extensions: (),
}
.clone_if();
let module_options_context = ModuleOptionsContext {
preset_env_versions: Some(env),
execution_context: Some(execution_context),
@ -131,7 +141,7 @@ pub async fn get_client_module_options_context(
postcss_package: Some(get_postcss_package_mapping(project_path)),
..Default::default()
}),
enable_webpack_loaders: next_config.webpack_loaders_options().await?.clone_if(),
enable_webpack_loaders,
enable_typescript_transform: true,
rules: vec![(
foreign_code_context_condition(next_config).await?,

View file

@ -9,10 +9,7 @@ use turbo_tasks::{
};
use turbo_tasks_env::EnvMapVc;
use turbo_tasks_fs::json::parse_json_rope_with_source_context;
use turbopack::{
evaluate_context::node_evaluate_asset_context,
module_options::{WebpackLoadersOptions, WebpackLoadersOptionsVc},
};
use turbopack::evaluate_context::node_evaluate_asset_context;
use turbopack_core::{
asset::Asset,
context::AssetContext,
@ -446,6 +443,10 @@ pub enum RemoveConsoleConfig {
Config { exclude: Option<Vec<String>> },
}
#[derive(Default)]
#[turbo_tasks::value(transparent)]
pub struct WebpackExtensionToLoaders(IndexMap<String, WebpackLoaderConfigsVc>);
#[turbo_tasks::value_impl]
impl NextConfigVc {
#[turbo_tasks::function]
@ -500,20 +501,16 @@ impl NextConfigVc {
}
#[turbo_tasks::function]
pub async fn webpack_loaders_options(self) -> Result<WebpackLoadersOptionsVc> {
pub async fn webpack_loaders_options(self) -> Result<WebpackExtensionToLoadersVc> {
let this = self.await?;
let Some(turbo_loaders) = this.experimental.turbo.as_ref().and_then(|t| t.loaders.as_ref()) else {
return Ok(WebpackLoadersOptionsVc::cell(WebpackLoadersOptions::default()));
return Ok(WebpackExtensionToLoadersVc::cell(IndexMap::new()));
};
let mut extension_to_loaders = IndexMap::new();
for (ext, loaders) in turbo_loaders {
extension_to_loaders.insert(ext.clone(), WebpackLoaderConfigsVc::cell(loaders.clone()));
}
Ok(WebpackLoadersOptions {
extension_to_loaders,
..Default::default()
}
.cell())
Ok(WebpackExtensionToLoaders(extension_to_loaders).cell())
}
#[turbo_tasks::function]

View file

@ -3,7 +3,10 @@ use turbo_tasks::{primitives::StringVc, Value};
use turbo_tasks_env::ProcessEnvVc;
use turbo_tasks_fs::FileSystemPathVc;
use turbopack::{
module_options::{ModuleOptionsContext, ModuleOptionsContextVc, PostCssTransformOptions},
module_options::{
ModuleOptionsContext, ModuleOptionsContextVc, PostCssTransformOptions,
WebpackLoadersOptions,
},
resolve_options_context::{ResolveOptionsContext, ResolveOptionsContextVc},
};
use turbopack_core::{
@ -20,7 +23,7 @@ use super::{
resolve::ExternalCjsModulesResolvePluginVc, transforms::get_next_server_transforms_rules,
};
use crate::{
next_build::get_postcss_package_mapping,
next_build::{get_external_next_compiled_package_mapping, get_postcss_package_mapping},
next_config::NextConfigVc,
next_import_map::{get_next_build_import_map, get_next_server_import_map},
util::foreign_code_context_condition,
@ -173,7 +176,15 @@ pub async fn get_server_module_options_context(
postcss_package: Some(get_postcss_package_mapping(project_path)),
..Default::default()
});
let enable_webpack_loaders = next_config.webpack_loaders_options().await?.clone_if();
let options = &*next_config.webpack_loaders_options().await?;
let enable_webpack_loaders = WebpackLoadersOptions {
loader_runner_package: Some(get_external_next_compiled_package_mapping(StringVc::cell(
"loader-runner".to_owned(),
))),
extension_to_loaders: options.clone(),
placeholder_for_future_extensions: (),
}
.clone_if();
let module_options_context = match ty.into_value() {
ServerContextType::Pages { .. } | ServerContextType::PagesData { .. } => {