From 448dc1b7354ea6669b7428405d9f53fa092543cb Mon Sep 17 00:00:00 2001 From: OJ Kwon <1210596+kwonoj@users.noreply.github.com> Date: Mon, 17 Apr 2023 17:11:13 -0700 Subject: [PATCH] feat(turbopack): support modularizeImports next.js config --- packages/next-swc/crates/next-core/Cargo.toml | 13 ++-- .../next-core/src/next_client/context.rs | 2 +- .../next-core/src/next_client/transforms.rs | 9 ++- .../crates/next-core/src/next_config.rs | 4 +- .../next-core/src/next_server/context.rs | 2 +- .../next-core/src/next_server/transforms.rs | 12 +++- .../next-core/src/next_shared/transforms.rs | 69 ++++++++++++++++++- packages/next/src/lib/turbopack-warning.ts | 1 + 8 files changed, 98 insertions(+), 14 deletions(-) diff --git a/packages/next-swc/crates/next-core/Cargo.toml b/packages/next-swc/crates/next-core/Cargo.toml index 25f697e989c0..44ebb6bbc883 100644 --- a/packages/next-swc/crates/next-core/Cargo.toml +++ b/packages/next-swc/crates/next-core/Cargo.toml @@ -22,6 +22,7 @@ indoc = { workspace = true } allsorts = { workspace = true } futures = { workspace = true } turbo-binding = { workspace = true, features = [ + "__swc_transform_modularize_imports", "__feature_auto_hash_map", "__turbo_tasks", "__turbo_tasks_bytes", @@ -36,19 +37,20 @@ turbo-binding = { workspace = true, features = [ "__turbopack_ecmascript", "__turbopack_env", "__turbopack_node", - ] } +] } turbo-tasks = { workspace = true } turbo-tasks-fs = { workspace = true } next-transform-strip-page-exports = { workspace = true } next-transform-font = { workspace = true } next-transform-dynamic = { workspace = true } -swc_core = { workspace = true, features = ["ecma_ast", "common"] } +swc_core = { workspace = true, features = [ + "ecma_ast", + "common", +] } [build-dependencies] -turbo-binding = { workspace = true, features = [ - "__turbo_tasks_build" -]} +turbo-binding = { workspace = true, features = ["__turbo_tasks_build"] } [features] next-font-local = [] @@ -61,4 +63,3 @@ dynamic_embed_contents = [ "turbo-binding/__turbo_tasks_fs_dynamic_embed_contents", "turbo-binding/__turbopack_dev_dynamic_embed_contents", ] - diff --git a/packages/next-swc/crates/next-core/src/next_client/context.rs b/packages/next-swc/crates/next-core/src/next_client/context.rs index 8192f1d06009..71dbec88e576 100644 --- a/packages/next-swc/crates/next-core/src/next_client/context.rs +++ b/packages/next-swc/crates/next-core/src/next_client/context.rs @@ -147,7 +147,7 @@ pub async fn get_client_module_options_context( ty: Value, next_config: NextConfigVc, ) -> Result { - let custom_rules = get_next_client_transforms_rules(ty.into_value()).await?; + let custom_rules = get_next_client_transforms_rules(next_config, ty.into_value()).await?; let resolve_options_context = get_client_resolve_options_context(project_path, ty, next_config, execution_context); let enable_react_refresh = diff --git a/packages/next-swc/crates/next-core/src/next_client/transforms.rs b/packages/next-swc/crates/next-core/src/next_client/transforms.rs index 6abc8c25690c..b8d40fb4aea4 100644 --- a/packages/next-swc/crates/next-core/src/next_client/transforms.rs +++ b/packages/next-swc/crates/next-core/src/next_client/transforms.rs @@ -4,19 +4,26 @@ use turbo_binding::turbopack::turbopack::module_options::ModuleRule; use crate::{ next_client::context::ClientContextType, + next_config::NextConfigVc, next_shared::transforms::{ get_next_dynamic_transform_rule, get_next_font_transform_rule, - get_next_pages_transforms_rule, + get_next_modularize_imports_rule, get_next_pages_transforms_rule, }, }; /// Returns a list of module rules which apply client-side, Next.js-specific /// transforms. pub async fn get_next_client_transforms_rules( + next_config: NextConfigVc, context_ty: ClientContextType, ) -> Result> { let mut rules = vec![]; + let modularize_imports_config = &next_config.await?.modularize_imports; + if let Some(modularize_imports_config) = modularize_imports_config { + rules.push(get_next_modularize_imports_rule(modularize_imports_config)); + } + rules.push(get_next_font_transform_rule()); let pages_dir = match context_ty { diff --git a/packages/next-swc/crates/next-core/src/next_config.rs b/packages/next-swc/crates/next-core/src/next_config.rs index f0da8dfd1fad..3b4b65d3701f 100644 --- a/packages/next-swc/crates/next-core/src/next_config.rs +++ b/packages/next-swc/crates/next-core/src/next_config.rs @@ -41,7 +41,7 @@ use turbo_tasks::{ }; use turbo_tasks_fs::json::parse_json_with_source_context; -use crate::embed_js::next_asset; +use crate::{embed_js::next_asset, next_shared::transforms::ModularizeImportPackageConfig}; #[turbo_tasks::value(serialization = "custom", eq = "manual")] #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] @@ -57,6 +57,7 @@ pub struct NextConfig { pub react_strict_mode: Option, pub rewrites: Rewrites, pub transpile_packages: Option>, + pub modularize_imports: Option>, // Partially supported pub compiler: Option, @@ -385,7 +386,6 @@ pub struct ExperimentalConfig { legacy_browsers: Option, manual_client_base_path: Option, middleware_prefetch: Option, - modularize_imports: Option, new_next_link_behavior: Option, next_script_workers: Option, optimistic_client_cache: Option, diff --git a/packages/next-swc/crates/next-core/src/next_server/context.rs b/packages/next-swc/crates/next-core/src/next_server/context.rs index e86bc45d5e2f..b857a9c5208b 100644 --- a/packages/next-swc/crates/next-core/src/next_server/context.rs +++ b/packages/next-swc/crates/next-core/src/next_server/context.rs @@ -209,7 +209,7 @@ pub async fn get_server_module_options_context( ty: Value, next_config: NextConfigVc, ) -> Result { - let custom_rules = get_next_server_transforms_rules(ty.into_value()).await?; + let custom_rules = get_next_server_transforms_rules(next_config, ty.into_value()).await?; let foreign_code_context_condition = foreign_code_context_condition(next_config).await?; let enable_postcss_transform = Some(PostCssTransformOptions { postcss_package: Some(get_postcss_package_mapping(project_path)), diff --git a/packages/next-swc/crates/next-core/src/next_server/transforms.rs b/packages/next-swc/crates/next-core/src/next_server/transforms.rs index b9c270a3d04f..8eca077da037 100644 --- a/packages/next-swc/crates/next-core/src/next_server/transforms.rs +++ b/packages/next-swc/crates/next-core/src/next_server/transforms.rs @@ -3,19 +3,27 @@ use next_transform_strip_page_exports::ExportFilter; use turbo_binding::turbopack::turbopack::module_options::ModuleRule; use crate::{ + next_config::NextConfigVc, next_server::context::ServerContextType, next_shared::transforms::{ get_next_dynamic_transform_rule, get_next_font_transform_rule, - get_next_pages_transforms_rule, + get_next_modularize_imports_rule, get_next_pages_transforms_rule, }, }; /// Returns a list of module rules which apply server-side, Next.js-specific /// transforms. pub async fn get_next_server_transforms_rules( + next_config: NextConfigVc, context_ty: ServerContextType, ) -> Result> { - let mut rules = vec![get_next_font_transform_rule()]; + let mut rules = vec![]; + + let modularize_imports_config = &next_config.await?.modularize_imports; + if let Some(modularize_imports_config) = modularize_imports_config { + rules.push(get_next_modularize_imports_rule(modularize_imports_config)); + } + rules.push(get_next_font_transform_rule()); let (is_server_components, pages_dir) = match context_ty { ServerContextType::Pages { pages_dir } => (false, Some(pages_dir)), diff --git a/packages/next-swc/crates/next-core/src/next_shared/transforms.rs b/packages/next-swc/crates/next-core/src/next_shared/transforms.rs index 89bc9ad43503..d4473e20640d 100644 --- a/packages/next-swc/crates/next-core/src/next_shared/transforms.rs +++ b/packages/next-swc/crates/next-core/src/next_shared/transforms.rs @@ -1,8 +1,10 @@ -use std::path::PathBuf; +use std::{collections::HashMap, path::PathBuf}; use anyhow::Result; +use indexmap::IndexMap; use next_transform_dynamic::{next_dynamic, NextDynamicMode}; use next_transform_strip_page_exports::{next_transform_strip_page_exports, ExportFilter}; +use serde::{Deserialize, Serialize}; use swc_core::{ common::{util::take::Take, FileName}, ecma::{ @@ -12,6 +14,7 @@ use swc_core::{ }, }; use turbo_binding::{ + swc::custom_transform::modularize_imports::{modularize_imports, PackageConfig}, turbo::tasks_fs::FileSystemPathVc, turbopack::{ core::reference_type::{ReferenceType, UrlReferenceSubType}, @@ -22,6 +25,7 @@ use turbo_binding::{ turbopack::module_options::{ModuleRule, ModuleRuleCondition, ModuleRuleEffect}, }, }; +use turbo_tasks::trace::TraceRawVcs; /// Returns a rule which applies the Next.js page export stripping transform. pub async fn get_next_pages_transforms_rule( @@ -188,3 +192,66 @@ fn unwrap_module_program(program: &mut Program) -> Program { }), } } + +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, TraceRawVcs)] +#[serde(rename_all = "camelCase")] +pub struct ModularizeImportPackageConfig { + pub transform: String, + #[serde(default)] + pub prevent_full_import: bool, + #[serde(default)] + pub skip_default_conversion: bool, +} + +/// Returns a rule which applies the Next.js modularize imports transform. +pub fn get_next_modularize_imports_rule( + modularize_imports_config: &IndexMap, +) -> ModuleRule { + let transformer = EcmascriptInputTransform::Custom(CustomTransformVc::cell(Box::new( + ModularizeImportsTransformer::new(modularize_imports_config), + ))); + ModuleRule::new( + module_rule_match_js_no_url(), + vec![ModuleRuleEffect::AddEcmascriptTransforms( + EcmascriptInputTransformsVc::cell(vec![transformer]), + )], + ) +} + +#[derive(Debug)] +struct ModularizeImportsTransformer { + packages: HashMap, +} + +impl ModularizeImportsTransformer { + fn new(packages: &IndexMap) -> Self { + Self { + packages: packages + .iter() + .map(|(k, v)| { + ( + k.clone(), + PackageConfig { + transform: v.transform.clone(), + prevent_full_import: v.prevent_full_import, + skip_default_conversion: v.skip_default_conversion, + }, + ) + }) + .collect(), + } + } +} + +impl CustomTransformer for ModularizeImportsTransformer { + fn transform(&self, program: &mut Program, _ctx: &TransformContext<'_>) -> Option { + let p = std::mem::replace(program, Program::Module(Module::dummy())); + *program = p.fold_with(&mut modularize_imports( + turbo_binding::swc::custom_transform::modularize_imports::Config { + packages: self.packages.clone(), + }, + )); + + None + } +} diff --git a/packages/next/src/lib/turbopack-warning.ts b/packages/next/src/lib/turbopack-warning.ts index 5bdd44bdd855..2d4261cc218f 100644 --- a/packages/next/src/lib/turbopack-warning.ts +++ b/packages/next/src/lib/turbopack-warning.ts @@ -7,6 +7,7 @@ const supportedTurbopackNextConfigOptions = [ 'configFileName', 'env', 'experimental.appDir', + 'modularizeImports', 'compiler.emotion', 'compiler.styledComponents', 'experimental.serverComponentsExternalPackages',