SWC import modularization plugin (#34969)

This commit is contained in:
Heyang Zhou 2022-03-22 21:20:57 +08:00 committed by GitHub
parent 78831c3c84
commit 860c97ccf5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 653 additions and 6 deletions

View file

@ -233,6 +233,89 @@ module.exports = {
If you have feedback about `swcMinify`, please share it on the [feedback discussion](https://github.com/vercel/next.js/discussions/30237).
### Modularize Imports
Allows to modularize imports, similar to [babel-plugin-transform-imports](https://www.npmjs.com/package/babel-plugin-transform-imports).
Transforms member style imports:
```js
import { Row, Grid as MyGrid } from 'react-bootstrap'
import { merge } from 'lodash'
```
...into default style imports:
```js
import Row from 'react-bootstrap/lib/Row'
import MyGrid from 'react-bootstrap/lib/Grid'
import merge from 'lodash/merge'
```
Config for the above transform:
```js
// next.config.js
module.exports = {
experimental: {
modularizeImports: {
'react-bootstrap': {
transform: 'react-bootstrap/lib/{{member}}',
},
lodash: {
transform: 'lodash/{{member}}',
},
},
},
}
```
Advanced transformations:
- Using regular expressions
Similar to `babel-plugin-transform-imports`, but the transform is templated with [handlebars](https://docs.rs/handlebars) and regular expressions are in Rust [regex](https://docs.rs/regex/latest/regex/) crate's syntax.
The config:
```js
// next.config.js
module.exports = {
experimental: {
modularizeImports: {
'my-library/?(((\\w*)?/?)*)': {
transform: 'my-library/{{ matches.[1] }}/{{member}}',
},
},
},
}
```
Cause this code:
```js
import { MyModule } from 'my-library'
import { App } from 'my-library/components'
import { Header, Footer } from 'my-library/components/App'
```
To become:
```js
import MyModule from 'my-library/MyModule'
import App from 'my-library/components/App'
import Header from 'my-library/components/App/Header'
import Footer from 'my-library/components/App/Footer'
```
- Handlebars templating
This transform uses [handlebars](https://docs.rs/handlebars) to template the replacement import path in the `transform` field. These variables and helper functions are available:
1. `matches`: Has type `string[]`. All groups matched by the regular expression. `matches.[0]` is the full match.
2. `member`: Has type `string`. The name of the member import.
3. `lowerCase`, `upperCase`, `camelCase`: Helper functions to convert a string to lower, upper or camel cases.
## Unsupported Features
When your application has a `.babelrc` file, Next.js will automatically fall back to using Babel for transforming individual files. This ensures backwards compatibility with existing applications that leverage custom Babel plugins.

34
examples/modularize-imports/.gitignore vendored Normal file
View file

@ -0,0 +1,34 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local
# vercel
.vercel

View file

@ -0,0 +1,27 @@
# Modularize Imports Example
This example shows how to use the `modularizeImports` config option.
## Preview
Preview the example live on [StackBlitz](http://stackblitz.com/):
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/modularize-imports)
## Deploy your own
Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/modularize-imports&project-name=modularize-imports&repository-name=modularize-imports)
## How to use
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:
```bash
npx create-next-app --example modularize-imports modularize-imports-app
# or
yarn create next-app --example modularize-imports modularize-imports-app
```
Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).

View file

@ -0,0 +1,3 @@
export default function LeftHalf() {
return <span>Modularize</span>
}

View file

@ -0,0 +1,3 @@
export default function RightHalf() {
return <span>Imports</span>
}

View file

@ -0,0 +1,5 @@
// import LeftHalf from './LeftHalf'
// import RightHalf from './RightHalf'
// Remove the exports here so that we can verify that `modularize-imports` is working.
// export { LeftHalf, RightHalf };

View file

@ -0,0 +1,9 @@
module.exports = {
experimental: {
modularizeImports: {
'../components/halves': {
transform: '../components/halves/{{ member }}',
},
},
},
}

View file

@ -0,0 +1,13 @@
{
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"next": "latest",
"react": "^17.0.2",
"react-dom": "^17.0.2"
}
}

View file

@ -0,0 +1,10 @@
import { LeftHalf, RightHalf } from '../components/halves'
const Index = () => (
<div>
<LeftHalf />
<RightHalf />
</div>
)
export default Index

View file

@ -160,13 +160,34 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
dependencies = [
"block-padding",
"byte-tools",
"byteorder",
"generic-array 0.12.4",
]
[[package]]
name = "block-buffer"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
dependencies = [
"generic-array",
"generic-array 0.14.5",
]
[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
dependencies = [
"byte-tools",
]
[[package]]
@ -199,6 +220,12 @@ version = "3.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "byteorder"
version = "1.4.3"
@ -331,7 +358,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
dependencies = [
"generic-array",
"generic-array 0.14.5",
"typenum",
]
@ -428,13 +455,22 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
"generic-array 0.12.4",
]
[[package]]
name = "digest"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [
"block-buffer",
"block-buffer 0.10.2",
"crypto-common",
]
@ -462,6 +498,12 @@ dependencies = [
"syn",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "fastrand"
version = "1.7.0"
@ -514,6 +556,15 @@ dependencies = [
"byteorder",
]
[[package]]
name = "generic-array"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
dependencies = [
"typenum",
]
[[package]]
name = "generic-array"
version = "0.14.5"
@ -558,6 +609,20 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "handlebars"
version = "4.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d6a30320f094710245150395bc763ad23128d6a1ebbad7594dc4164b62c56b"
dependencies = [
"log",
"pest",
"pest_derive",
"quick-error",
"serde",
"serde_json",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
@ -775,6 +840,12 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "maplit"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]]
name = "matchers"
version = "0.1.0"
@ -892,6 +963,7 @@ dependencies = [
"easy-error",
"either",
"fxhash",
"handlebars",
"once_cell",
"pathdiff",
"radix_fmt",
@ -901,6 +973,7 @@ dependencies = [
"styled_components",
"swc",
"swc_atoms",
"swc_cached",
"swc_common",
"swc_css",
"swc_ecma_loader",
@ -1012,6 +1085,12 @@ version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "ordered-float"
version = "2.10.0"
@ -1097,6 +1176,49 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pest"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
dependencies = [
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pest_meta"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
dependencies = [
"maplit",
"pest",
"sha-1 0.8.2",
]
[[package]]
name = "petgraph"
version = "0.6.0"
@ -1267,6 +1389,12 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quote"
version = "1.0.15"
@ -1565,6 +1693,18 @@ dependencies = [
"serde",
]
[[package]]
name = "sha-1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
dependencies = [
"block-buffer 0.7.3",
"digest 0.8.1",
"fake-simd",
"opaque-debug",
]
[[package]]
name = "sha-1"
version = "0.10.0"
@ -1573,7 +1713,7 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures",
"digest",
"digest 0.10.3",
]
[[package]]
@ -2274,7 +2414,7 @@ dependencies = [
"once_cell",
"regex",
"serde",
"sha-1",
"sha-1 0.10.0",
"string_enum",
"swc_atoms",
"swc_common",
@ -2297,7 +2437,7 @@ dependencies = [
"hex",
"serde",
"serde_json",
"sha-1",
"sha-1 0.10.0",
"swc_common",
"swc_ecma_ast",
"swc_ecma_codegen",
@ -2692,6 +2832,12 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "ucd-trie"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unicode-bidi"
version = "0.3.7"

View file

@ -28,7 +28,9 @@ swc_ecma_loader = {version = "0.29.0", features = ["node", "lru"]}
swc_ecmascript = {version = "0.132.0", features = ["codegen", "minifier", "optimization", "parser", "react", "transforms", "typescript", "utils", "visit"]}
swc_node_base = "0.5.1"
swc_stylis = "0.96.1"
swc_cached = "0.1.1"
tracing = {version = "0.1.28", features = ["release_max_level_off"]}
handlebars = "4.2.1"
[dev-dependencies]
swc_ecma_transforms_testing = "0.69.0"

View file

@ -49,6 +49,7 @@ mod auto_cjs;
pub mod disallow_re_export_all_in_page;
pub mod emotion;
pub mod hook_optimizer;
pub mod modularize_imports;
pub mod next_dynamic;
pub mod next_ssg;
pub mod page_config;
@ -102,6 +103,9 @@ pub struct TransformOptions {
#[serde(default)]
pub emotion: Option<emotion::EmotionOptions>,
#[serde(default)]
pub modularize_imports: Option<modularize_imports::Config>,
}
pub fn custom_before_pass<'a, C: Comments + 'a>(
@ -191,6 +195,10 @@ pub fn custom_before_pass<'a, C: Comments + 'a>(
}
})
.unwrap_or_else(|| Either::Right(noop())),
match &opts.modularize_imports {
Some(config) => Either::Left(modularize_imports::modularize_imports(config.clone())),
None => Either::Right(noop()),
}
)
}

View file

@ -0,0 +1,235 @@
use std::borrow::Cow;
use std::collections::HashMap;
use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext};
use once_cell::sync::Lazy;
use regex::{Captures, Regex};
use serde::{Deserialize, Serialize};
use swc_cached::regex::CachedRegex;
use swc_ecmascript::ast::*;
use swc_ecmascript::visit::{noop_fold_type, Fold};
static DUP_SLASH_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"//").unwrap());
#[derive(Clone, Debug, Deserialize)]
#[serde(transparent)]
pub struct Config {
pub packages: HashMap<String, PackageConfig>,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PackageConfig {
pub transform: String,
#[serde(default)]
pub prevent_full_import: bool,
#[serde(default)]
pub skip_default_conversion: bool,
}
struct FoldImports {
renderer: handlebars::Handlebars<'static>,
packages: Vec<(CachedRegex, PackageConfig)>,
}
struct Rewriter<'a> {
renderer: &'a handlebars::Handlebars<'static>,
key: &'a str,
config: &'a PackageConfig,
group: Vec<&'a str>,
}
impl<'a> Rewriter<'a> {
fn rewrite(&self, old_decl: &ImportDecl) -> Vec<ImportDecl> {
if old_decl.type_only || old_decl.asserts.is_some() {
return vec![old_decl.clone()];
}
let mut out: Vec<ImportDecl> = Vec::with_capacity(old_decl.specifiers.len());
for spec in &old_decl.specifiers {
match spec {
ImportSpecifier::Named(named_spec) => {
#[derive(Serialize)]
#[serde(untagged)]
enum Data<'a> {
Plain(&'a str),
Array(&'a [&'a str]),
}
let mut ctx: HashMap<&str, Data> = HashMap::new();
ctx.insert("matches", Data::Array(&self.group[..]));
ctx.insert(
"member",
Data::Plain(
named_spec
.imported
.as_ref()
.map(|x| match x {
ModuleExportName::Ident(x) => x.as_ref(),
ModuleExportName::Str(x) => x.value.as_ref(),
})
.unwrap_or_else(|| named_spec.local.as_ref()),
),
);
let new_path = self
.renderer
.render_template(&self.config.transform, &ctx)
.unwrap_or_else(|e| {
panic!("error rendering template for '{}': {}", self.key, e);
});
let new_path = DUP_SLASH_REGEX.replace_all(&new_path, |_: &Captures| "/");
let specifier = if self.config.skip_default_conversion {
ImportSpecifier::Named(named_spec.clone())
} else {
ImportSpecifier::Default(ImportDefaultSpecifier {
local: named_spec.local.clone(),
span: named_spec.span,
})
};
out.push(ImportDecl {
specifiers: vec![specifier],
src: Str::from(new_path.as_ref()),
span: old_decl.span,
type_only: false,
asserts: None,
});
}
_ => {
if self.config.prevent_full_import {
panic!(
"import {:?} causes the entire module to be imported",
old_decl
);
} else {
// Give up
return vec![old_decl.clone()];
}
}
}
}
out
}
}
impl FoldImports {
fn should_rewrite<'a>(&'a self, name: &'a str) -> Option<Rewriter<'a>> {
for (regex, config) in &self.packages {
let group = regex.captures(name);
if let Some(group) = group {
let group = group
.iter()
.map(|x| x.map(|x| x.as_str()).unwrap_or_default())
.collect::<Vec<&str>>();
return Some(Rewriter {
renderer: &self.renderer,
key: name,
config,
group,
});
}
}
None
}
}
impl Fold for FoldImports {
noop_fold_type!();
fn fold_module(&mut self, mut module: Module) -> Module {
let mut new_items: Vec<ModuleItem> = vec![];
for item in module.body {
match item {
ModuleItem::ModuleDecl(ModuleDecl::Import(decl)) => {
match self.should_rewrite(&decl.src.value) {
Some(rewriter) => {
let rewritten = rewriter.rewrite(&decl);
new_items.extend(
rewritten
.into_iter()
.map(|x| ModuleItem::ModuleDecl(ModuleDecl::Import(x))),
);
}
None => new_items.push(ModuleItem::ModuleDecl(ModuleDecl::Import(decl))),
}
}
x => {
new_items.push(x);
}
}
}
module.body = new_items;
module
}
}
pub fn modularize_imports(config: Config) -> impl Fold {
let mut folder = FoldImports {
renderer: handlebars::Handlebars::new(),
packages: vec![],
};
folder
.renderer
.register_helper("lowerCase", Box::new(helper_lower_case));
folder
.renderer
.register_helper("upperCase", Box::new(helper_upper_case));
folder
.renderer
.register_helper("camelCase", Box::new(helper_camel_case));
for (mut k, v) in config.packages {
// XXX: Should we keep this hack?
if !k.starts_with('^') && !k.ends_with('$') {
k = format!("^{}$", k);
}
folder.packages.push((
CachedRegex::new(&k).expect("transform-imports: invalid regex"),
v,
));
}
folder
}
fn helper_lower_case(
h: &Helper<'_, '_>,
_: &Handlebars<'_>,
_: &Context,
_: &mut RenderContext<'_, '_>,
out: &mut dyn Output,
) -> HelperResult {
// get parameter from helper or throw an error
let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or("");
out.write(param.to_lowercase().as_ref())?;
Ok(())
}
fn helper_upper_case(
h: &Helper<'_, '_>,
_: &Handlebars<'_>,
_: &Context,
_: &mut RenderContext<'_, '_>,
out: &mut dyn Output,
) -> HelperResult {
// get parameter from helper or throw an error
let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or("");
out.write(param.to_uppercase().as_ref())?;
Ok(())
}
fn helper_camel_case(
h: &Helper<'_, '_>,
_: &Handlebars<'_>,
_: &Context,
_: &mut RenderContext<'_, '_>,
out: &mut dyn Output,
) -> HelperResult {
// get parameter from helper or throw an error
let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or("");
let value = if param.is_empty() || param.chars().next().unwrap().is_lowercase() {
Cow::Borrowed(param)
} else {
let mut it = param.chars();
let fst = it.next().unwrap();
Cow::Owned(fst.to_lowercase().chain(it).collect::<String>())
};
out.write(value.as_ref())?;
Ok(())
}

View file

@ -1,6 +1,7 @@
use next_swc::{
amp_attributes::amp_attributes,
emotion::{self, EmotionOptions},
modularize_imports::modularize_imports,
next_dynamic::next_dynamic,
next_ssg::next_ssg,
page_config::page_config_test,
@ -312,3 +313,46 @@ fn next_emotion_fixture(input: PathBuf) {
&output,
);
}
#[fixture("tests/fixture/modularize-imports/**/input.js")]
fn modularize_imports_fixture(input: PathBuf) {
use next_swc::modularize_imports::PackageConfig;
let output = input.parent().unwrap().join("output.js");
test_fixture(
syntax(),
&|_tr| {
modularize_imports(next_swc::modularize_imports::Config {
packages: vec![
(
"react-bootstrap".to_string(),
PackageConfig {
transform: "react-bootstrap/lib/{{member}}".into(),
prevent_full_import: false,
skip_default_conversion: false,
},
),
(
"my-library/?(((\\w*)?/?)*)".to_string(),
PackageConfig {
transform: "my-library/{{ matches.[1] }}/{{member}}".into(),
prevent_full_import: false,
skip_default_conversion: false,
},
),
(
"my-library-2".to_string(),
PackageConfig {
transform: "my-library-2/{{ camelCase member }}".into(),
prevent_full_import: false,
skip_default_conversion: true,
},
),
]
.into_iter()
.collect(),
})
},
&input,
&output,
);
}

View file

@ -0,0 +1,3 @@
import { MyModule } from 'my-library';
import { App } from 'my-library/components';
import { Header, Footer } from 'my-library/components/App';

View file

@ -0,0 +1,4 @@
import MyModule from 'my-library/MyModule';
import App from 'my-library/components/App';
import Header from 'my-library/components/App/Header';
import Footer from 'my-library/components/App/Footer';

View file

@ -0,0 +1,2 @@
import { Grid, Row, Col as Col1 } from 'react-bootstrap';
import { MyModule, Widget } from 'my-library-2';

View file

@ -0,0 +1,5 @@
import Grid from "react-bootstrap/lib/Grid";
import Row from "react-bootstrap/lib/Row";
import Col1 from "react-bootstrap/lib/Col";
import { MyModule } from 'my-library-2/myModule';
import { Widget } from 'my-library-2/widget';

View file

@ -62,6 +62,7 @@ fn test(input: &Path, minify: bool) {
relay: None,
shake_exports: None,
emotion: Some(assert_json("{}")),
modularize_imports: None,
};
let options = options.patch(&fm);

View file

@ -102,6 +102,7 @@ function getBaseSWCOptions({
: null,
removeConsole: nextConfig?.compiler?.removeConsole,
reactRemoveProperties: nextConfig?.compiler?.reactRemoveProperties,
modularizeImports: nextConfig?.experimental?.modularizeImports,
relay: nextConfig?.compiler?.relay,
emotion: getEmotionOptions(nextConfig, development),
}

View file

@ -1653,6 +1653,7 @@ export default async function getBaseWebpackConfig(
styledComponents: config.compiler?.styledComponents,
relay: config.compiler?.relay,
emotion: config.experimental?.emotion,
modularizeImports: config.experimental?.modularizeImports,
})
const cache: any = {

View file

@ -118,6 +118,14 @@ export interface ExperimentalConfig {
autoLabel?: 'dev-only' | 'always' | 'never'
labelFormat?: string
}
modularizeImports?: Record<
string,
{
transform: string
preventFullImport?: boolean
skipDefaultConversion?: boolean
}
>
}
/**