Skip to content
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

Implement named_import_transform #54530

Merged
merged 2 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/next-swc/crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub mod amp_attributes;
mod auto_cjs;
pub mod cjs_optimizer;
pub mod disallow_re_export_all_in_page;
pub mod named_import_transform;
pub mod next_dynamic;
pub mod next_ssg;
pub mod page_config;
Expand Down Expand Up @@ -128,6 +129,9 @@ pub struct TransformOptions {
#[serde(default)]
pub modularize_imports: Option<modularize_imports::Config>,

#[serde(default)]
pub auto_modularize_imports: Option<named_import_transform::Config>,

#[serde(default)]
pub font_loaders: Option<next_transform_font::Config>,

Expand Down Expand Up @@ -245,6 +249,10 @@ where
Some(config) => Either::Left(shake_exports::shake_exports(config.clone())),
None => Either::Right(noop()),
},
match &opts.auto_modularize_imports {
Some(config) => Either::Left(named_import_transform::named_import_transform(config.clone())),
None => Either::Right(noop()),
},
opts.emotion
.as_ref()
.and_then(|config| {
Expand Down
83 changes: 83 additions & 0 deletions packages/next-swc/crates/core/src/named_import_transform.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use serde::Deserialize;
use turbopack_binding::swc::core::{
common::DUMMY_SP,
ecma::{ast::*, visit::Fold},
};

#[derive(Clone, Debug, Deserialize)]
pub struct Config {
pub packages: Vec<String>,
}

pub fn named_import_transform(config: Config) -> impl Fold {
NamedImportTransform {
packages: config.packages,
}
}

#[derive(Debug, Default)]
struct NamedImportTransform {
packages: Vec<String>,
}

impl Fold for NamedImportTransform {
fn fold_import_decl(&mut self, decl: ImportDecl) -> ImportDecl {
// Match named imports and check if it's included in the packages
let src_value = decl.src.value.clone();

if self.packages.iter().any(|p| src_value == *p) {
let mut specifier_names = vec![];

// Skip the transform if the default or namespace import is present
let mut skip_transform = false;

for specifier in &decl.specifiers {
match specifier {
ImportSpecifier::Named(specifier) => {
// Push the import name as string to the vec
if let Some(imported) = &specifier.imported {
match imported {
ModuleExportName::Ident(ident) => {
specifier_names.push(ident.sym.to_string());
}
ModuleExportName::Str(str_) => {
specifier_names.push(str_.value.to_string());
}
}
} else {
specifier_names.push(specifier.local.sym.to_string());
}
}
ImportSpecifier::Default(_) => {
skip_transform = true;
break;
}
ImportSpecifier::Namespace(_) => {
skip_transform = true;
break;
}
}
}

if !skip_transform {
let new_src = format!(
"barrel-optimize-loader?names={}!{}",
specifier_names.join(","),
src_value
);

// Create a new import declaration, keep everything the same except the source
let mut new_decl = decl.clone();
new_decl.src = Box::new(Str {
span: DUMMY_SP,
value: new_src.into(),
raw: None,
});

return new_decl;
}
}

decl
}
}
27 changes: 27 additions & 0 deletions packages/next-swc/crates/core/tests/fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::{env::current_dir, path::PathBuf};
use next_swc::{
amp_attributes::amp_attributes,
cjs_optimizer::cjs_optimizer,
named_import_transform::named_import_transform,
next_dynamic::next_dynamic,
next_ssg::next_ssg,
page_config::page_config_test,
Expand Down Expand Up @@ -454,6 +455,32 @@ fn cjs_optimize_fixture(input: PathBuf) {
);
}

#[fixture("tests/fixture/named-import-transform/**/input.js")]
fn named_import_transform_fixture(input: PathBuf) {
let output = input.parent().unwrap().join("output.js");
test_fixture(
syntax(),
&|_tr| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();

chain!(
resolver(unresolved_mark, top_level_mark, false),
named_import_transform(json(
r#"
{
"packages": ["foo", "bar"]
}
"#
))
)
},
&input,
&output,
Default::default(),
);
}

fn json<T>(s: &str) -> T
where
T: DeserializeOwned,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { A, B, C as F } from 'foo'
import D from 'bar'
import E from 'baz'
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { A, B, C as F } from "barrel-optimize-loader?names=A,B,C!foo";
import D from 'bar';
import E from 'baz';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { A, B, C as F } from 'foo'
import { D } from 'bar'
import E from 'baz'
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { A, B, C as F } from "barrel-optimize-loader?names=A,B,C!foo";
import { D } from "barrel-optimize-loader?names=D!bar";
import E from 'baz';
1 change: 1 addition & 0 deletions packages/next-swc/crates/core/tests/full.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ fn test(input: &Path, minify: bool) {
app_dir: None,
server_actions: None,
cjs_require_optimizer: None,
auto_modularize_imports: None,
};

let unresolved_mark = Mark::new();
Expand Down
5 changes: 5 additions & 0 deletions packages/next/src/build/swc/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,11 @@ export function getLoaderSWCOptions({
},
},
}
baseOptions.autoModularizeImports = {
packages: [
// TODO: Add a list of packages that should be optimized by default
],
}

const isNextDist = nextDistPath.test(filename)

Expand Down
1 change: 1 addition & 0 deletions packages/next/src/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1936,6 +1936,7 @@ export default async function getBaseWebpackConfig(
'next-invalid-import-error-loader',
'next-metadata-route-loader',
'modularize-import-loader',
'barrel-optimize-loader',
].reduce((alias, loader) => {
// using multiple aliases to replace `resolveLoader.modules`
alias[loader] = path.join(__dirname, 'webpack', 'loaders', loader)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default function transformSource(this: any, source: string) {
// const { names }: any = this.getOptions()
// const { resourcePath } = this
return source
}
12 changes: 12 additions & 0 deletions test/development/basic/auto-modularize-imports/app/layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
}

export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
11 changes: 11 additions & 0 deletions test/development/basic/auto-modularize-imports/app/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use client'

import { IceCream } from 'lucide-react'

export default function Page() {
return (
<div>
<IceCream />
</div>
)
}