diff --git a/Cargo.lock b/Cargo.lock
index a53fcbb1054c..cb8a07fbbea0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4505,6 +4505,7 @@ dependencies = [
"pathdiff",
"serde",
"serde_json",
+ "swc_atoms",
"swc_cached",
"swc_common",
"tracing",
diff --git a/crates/swc/src/config/mod.rs b/crates/swc/src/config/mod.rs
index 6d98016e0117..0767d390ab8e 100644
--- a/crates/swc/src/config/mod.rs
+++ b/crates/swc/src/config/mod.rs
@@ -619,7 +619,7 @@ impl Options {
&plugin_name,
)?;
- let path = if let FileName::Real(value) = resolved_path {
+ let path = if let FileName::Real(value) = resolved_path.filename {
value
} else {
anyhow::bail!("Failed to resolve plugin path: {:?}", resolved_path);
diff --git a/crates/swc/tests/fixture/deno/paths/ext/002/output/index.js b/crates/swc/tests/fixture/deno/paths/ext/002/output/index.js
index 1d68a591e3a7..507538549ede 100644
--- a/crates/swc/tests/fixture/deno/paths/ext/002/output/index.js
+++ b/crates/swc/tests/fixture/deno/paths/ext/002/output/index.js
@@ -1,2 +1,2 @@
-import styles from "./foo.ts/index.js";
+import styles from "./foo.ts/index";
console.log(styles);
diff --git a/crates/swc/tests/fixture/issues-3xxx/3547/1/output/index.js b/crates/swc/tests/fixture/issues-3xxx/3547/1/output/index.js
index c0c453c2eba3..645392b5af79 100644
--- a/crates/swc/tests/fixture/issues-3xxx/3547/1/output/index.js
+++ b/crates/swc/tests/fixture/issues-3xxx/3547/1/output/index.js
@@ -1,2 +1,2 @@
-import { NekoRoute } from "./src/lib/structures/route/index.js";
+import { NekoRoute } from "./src/lib/structures/route";
console.log(NekoRoute);
diff --git a/crates/swc/tests/fixture/issues-7xxx/7829/1/output/1.js b/crates/swc/tests/fixture/issues-7xxx/7829/1/output/1.js
index 90ba05422402..f11774c35786 100644
--- a/crates/swc/tests/fixture/issues-7xxx/7829/1/output/1.js
+++ b/crates/swc/tests/fixture/issues-7xxx/7829/1/output/1.js
@@ -1,2 +1,2 @@
-import { fn } from "./libs/pkg/src";
+import { fn } from "./libs/pkg/src/index.ts";
console.log(fn);
diff --git a/crates/swc/tests/fixture/issues-7xxx/7898/1/output/index.ts b/crates/swc/tests/fixture/issues-7xxx/7898/1/output/index.ts
index 5ac4603ec675..e080b6248a43 100644
--- a/crates/swc/tests/fixture/issues-7xxx/7898/1/output/index.ts
+++ b/crates/swc/tests/fixture/issues-7xxx/7898/1/output/index.ts
@@ -2,5 +2,5 @@
Object.defineProperty(exports, "__esModule", {
value: true
});
-const _a = require("./packages/a/src/index.js");
+const _a = require("./packages/a/src/index.ts");
console.log(`${(0, _a.displayA)()}`);
diff --git a/crates/swc/tests/fixture/issues-8xxx/8184/1/input/.swcrc b/crates/swc/tests/fixture/issues-8xxx/8184/1/input/.swcrc
new file mode 100644
index 000000000000..f114fa4de9d5
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8184/1/input/.swcrc
@@ -0,0 +1,14 @@
+{
+ "jsc": {
+ "parser": {
+ "syntax": "typescript",
+ "tsx": true
+ },
+ "baseUrl": ".",
+ "paths": {
+ "~/*": [
+ "./src/*"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-8xxx/8184/1/input/src/a/index.jsx b/crates/swc/tests/fixture/issues-8xxx/8184/1/input/src/a/index.jsx
new file mode 100644
index 000000000000..087faf3708f8
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8184/1/input/src/a/index.jsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const a = props => {
+ return (
+
+
+
+ );
+};
+
+a.propTypes = {
+
+};
+
+export default a;
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-8xxx/8184/1/input/src/b/index.js b/crates/swc/tests/fixture/issues-8xxx/8184/1/input/src/b/index.js
new file mode 100644
index 000000000000..9ecad11d9720
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8184/1/input/src/b/index.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const b = props => {
+ return (
+
+
+
+ );
+};
+
+b.propTypes = {
+
+};
+
+export default b;
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-8xxx/8184/1/input/src/c/index.ts b/crates/swc/tests/fixture/issues-8xxx/8184/1/input/src/c/index.ts
new file mode 100644
index 000000000000..036a98fae7fb
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8184/1/input/src/c/index.ts
@@ -0,0 +1 @@
+export default (a) => a
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-8xxx/8184/1/input/src/d/index.tsx b/crates/swc/tests/fixture/issues-8xxx/8184/1/input/src/d/index.tsx
new file mode 100644
index 000000000000..f58f5a18ef85
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8184/1/input/src/d/index.tsx
@@ -0,0 +1,10 @@
+import * as React from 'react';
+
+interface IAppProps {
+}
+
+const App: React.FunctionComponent = (props) => {
+ return 123
;
+};
+
+export default App;
diff --git a/crates/swc/tests/fixture/issues-8xxx/8184/1/input/src/index.ts b/crates/swc/tests/fixture/issues-8xxx/8184/1/input/src/index.ts
new file mode 100644
index 000000000000..474527bed405
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8184/1/input/src/index.ts
@@ -0,0 +1,6 @@
+import a from '~/a';
+import b from '~/b';
+import c from '~/c';
+import d from '~/d';
+
+console.log(a, b, c, d);
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-8xxx/8184/1/output/src/b/index.js b/crates/swc/tests/fixture/issues-8xxx/8184/1/output/src/b/index.js
new file mode 100644
index 000000000000..0bfb1a010bcc
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8184/1/output/src/b/index.js
@@ -0,0 +1,6 @@
+import React from "react";
+var b = function(props) {
+ return /*#__PURE__*/ React.createElement("div", null);
+};
+b.propTypes = {};
+export default b;
diff --git a/crates/swc/tests/fixture/issues-8xxx/8184/1/output/src/c/index.ts b/crates/swc/tests/fixture/issues-8xxx/8184/1/output/src/c/index.ts
new file mode 100644
index 000000000000..8b5c009f0bf7
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8184/1/output/src/c/index.ts
@@ -0,0 +1,3 @@
+export default function(a) {
+ return a;
+};
diff --git a/crates/swc/tests/fixture/issues-8xxx/8184/1/output/src/d/index.tsx b/crates/swc/tests/fixture/issues-8xxx/8184/1/output/src/d/index.tsx
new file mode 100644
index 000000000000..c39078020570
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8184/1/output/src/d/index.tsx
@@ -0,0 +1,5 @@
+import * as React from "react";
+var App = function(props) {
+ return /*#__PURE__*/ React.createElement("div", null, "123");
+};
+export default App;
diff --git a/crates/swc/tests/fixture/issues-8xxx/8184/1/output/src/index.ts b/crates/swc/tests/fixture/issues-8xxx/8184/1/output/src/index.ts
new file mode 100644
index 000000000000..5d6a58ab6729
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8184/1/output/src/index.ts
@@ -0,0 +1,5 @@
+import a from "./a";
+import b from "./b";
+import c from "./c";
+import d from "./d";
+console.log(a, b, c, d);
diff --git a/crates/swc_bundler/examples/path.rs b/crates/swc_bundler/examples/path.rs
index 7857b6902bc3..9c5f33b95a58 100644
--- a/crates/swc_bundler/examples/path.rs
+++ b/crates/swc_bundler/examples/path.rs
@@ -5,6 +5,7 @@ use swc_bundler::{BundleKind, Bundler, Config, Hook, Load, ModuleData, ModuleRec
use swc_common::{sync::Lrc, FileName, FilePathMapping, Globals, SourceMap, Span};
use swc_ecma_ast::KeyValueProp;
use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
+use swc_ecma_loader::resolve::Resolution;
use swc_ecma_parser::{parse_file_as_module, Syntax};
fn main() {
@@ -87,7 +88,7 @@ impl Load for PathLoader {
struct PathResolver;
impl Resolve for PathResolver {
- fn resolve(&self, base: &FileName, module_specifier: &str) -> Result {
+ fn resolve(&self, base: &FileName, module_specifier: &str) -> Result {
assert!(
module_specifier.starts_with('.'),
"We are not using node_modules within this example"
@@ -98,12 +99,15 @@ impl Resolve for PathResolver {
_ => unreachable!(),
};
- Ok(FileName::Real(
- base.parent()
- .unwrap()
- .join(module_specifier)
- .with_extension("js"),
- ))
+ Ok(Resolution {
+ filename: FileName::Real(
+ base.parent()
+ .unwrap()
+ .join(module_specifier)
+ .with_extension("js"),
+ ),
+ slug: None,
+ })
}
}
diff --git a/crates/swc_bundler/src/bundler/finalize.rs b/crates/swc_bundler/src/bundler/finalize.rs
index 6e0d34716b63..50a48f80e359 100644
--- a/crates/swc_bundler/src/bundler/finalize.rs
+++ b/crates/swc_bundler/src/bundler/finalize.rs
@@ -382,7 +382,7 @@ where
.resolver
.resolve(&FileName::Real(self.base.clone()), &import.src.value)
{
- Ok(v) => match v {
+ Ok(v) => match v.filename {
FileName::Real(v) => v,
_ => panic!("rename_bundles called with non-path module"),
},
diff --git a/crates/swc_bundler/src/bundler/import/mod.rs b/crates/swc_bundler/src/bundler/import/mod.rs
index cd66513d752d..b3ff4db49d40 100644
--- a/crates/swc_bundler/src/bundler/import/mod.rs
+++ b/crates/swc_bundler/src/bundler/import/mod.rs
@@ -57,6 +57,7 @@ where
let path = self
.resolver
.resolve(base, module_specifier)
+ .map(|v| v.filename)
.with_context(|| format!("failed to resolve {} from {}", module_specifier, base))?;
let path = Lrc::new(path);
diff --git a/crates/swc_bundler/src/bundler/tests.rs b/crates/swc_bundler/src/bundler/tests.rs
index 9d9f2b2314fa..aa107a8bc463 100644
--- a/crates/swc_bundler/src/bundler/tests.rs
+++ b/crates/swc_bundler/src/bundler/tests.rs
@@ -5,6 +5,7 @@ use anyhow::Error;
use indexmap::IndexMap;
use swc_common::{collections::ARandomState, sync::Lrc, FileName, SourceMap, Span, GLOBALS};
use swc_ecma_ast::*;
+use swc_ecma_loader::resolve::Resolution;
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput};
use swc_ecma_utils::drop_span;
use swc_ecma_visit::VisitMutWith;
@@ -52,7 +53,7 @@ impl Load for Loader {
pub struct Resolver;
impl Resolve for Resolver {
- fn resolve(&self, _: &FileName, s: &str) -> Result {
+ fn resolve(&self, _: &FileName, s: &str) -> Result {
assert!(s.starts_with("./"));
let path = PathBuf::from(s.to_string())
@@ -61,7 +62,10 @@ impl Resolve for Resolver {
.unwrap()
.into();
- Ok(FileName::Real(path))
+ Ok(Resolution {
+ filename: FileName::Real(path),
+ slug: None,
+ })
}
}
diff --git a/crates/swc_bundler/tests/common/mod.rs b/crates/swc_bundler/tests/common/mod.rs
index 5cf80d034eb7..5468aaa63ea7 100644
--- a/crates/swc_bundler/tests/common/mod.rs
+++ b/crates/swc_bundler/tests/common/mod.rs
@@ -17,6 +17,7 @@ use swc_common::{
FileName, Mark, SourceMap,
};
use swc_ecma_ast::{EsVersion, Program};
+use swc_ecma_loader::resolve::Resolution;
use swc_ecma_parser::{parse_file_as_module, Syntax, TsConfig};
use swc_ecma_transforms_base::{
helpers::{inject_helpers, Helpers, HELPERS},
@@ -273,10 +274,8 @@ impl NodeResolver {
None => bail!("not found"),
}
}
-}
-impl Resolve for NodeResolver {
- fn resolve(&self, base: &FileName, target: &str) -> Result {
+ fn resolve_inner(&self, base: &FileName, target: &str) -> Result {
if let Ok(v) = Url::parse(target) {
return Ok(FileName::Custom(v.to_string()));
}
@@ -337,3 +336,13 @@ impl Resolve for NodeResolver {
.and_then(|p| self.wrap(p))
}
}
+
+impl Resolve for NodeResolver {
+ fn resolve(&self, base: &FileName, module_specifier: &str) -> Result {
+ self.resolve_inner(base, module_specifier)
+ .map(|filename| Resolution {
+ filename,
+ slug: None,
+ })
+ }
+}
diff --git a/crates/swc_ecma_loader/Cargo.toml b/crates/swc_ecma_loader/Cargo.toml
index 9ce9c7008090..3f0f6426afab 100644
--- a/crates/swc_ecma_loader/Cargo.toml
+++ b/crates/swc_ecma_loader/Cargo.toml
@@ -36,6 +36,7 @@ serde = { version = "1", features = ["derive"] }
serde_json = { version = "1.0.64", optional = true }
tracing = "0.1.37"
+swc_atoms = { version = "0.6.5", path = "../swc_atoms" }
swc_cached = { version = "0.3.18", optional = true, path = "../swc_cached" }
swc_common = { version = "0.33.14", path = "../swc_common" }
diff --git a/crates/swc_ecma_loader/src/resolve.rs b/crates/swc_ecma_loader/src/resolve.rs
index d427e6788622..049baf2f1687 100644
--- a/crates/swc_ecma_loader/src/resolve.rs
+++ b/crates/swc_ecma_loader/src/resolve.rs
@@ -1,13 +1,20 @@
use std::sync::Arc;
use anyhow::Error;
+use swc_atoms::Atom;
use swc_common::{
sync::{Send, Sync},
FileName,
};
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Resolution {
+ pub filename: FileName,
+ pub slug: Option,
+}
+
pub trait Resolve: Send + Sync {
- fn resolve(&self, base: &FileName, module_specifier: &str) -> Result;
+ fn resolve(&self, base: &FileName, module_specifier: &str) -> Result;
}
macro_rules! impl_ref {
@@ -16,7 +23,7 @@ macro_rules! impl_ref {
where
R: ?Sized + Resolve,
{
- fn resolve(&self, base: &FileName, src: &str) -> Result {
+ fn resolve(&self, base: &FileName, src: &str) -> Result {
(**self).resolve(base, src)
}
}
diff --git a/crates/swc_ecma_loader/src/resolvers/lru.rs b/crates/swc_ecma_loader/src/resolvers/lru.rs
index fef9bce818e7..49d3ac35814a 100644
--- a/crates/swc_ecma_loader/src/resolvers/lru.rs
+++ b/crates/swc_ecma_loader/src/resolvers/lru.rs
@@ -5,14 +5,14 @@ use lru::LruCache;
use parking_lot::Mutex;
use swc_common::FileName;
-use crate::resolve::Resolve;
+use crate::resolve::{Resolution, Resolve};
#[derive(Debug)]
pub struct CachingResolver
where
R: Resolve,
{
- cache: Mutex>,
+ cache: Mutex>,
inner: R,
}
@@ -43,7 +43,7 @@ impl Resolve for CachingResolver
where
R: Resolve,
{
- fn resolve(&self, base: &FileName, src: &str) -> Result {
+ fn resolve(&self, base: &FileName, src: &str) -> Result {
{
let mut lock = self.cache.lock();
//
diff --git a/crates/swc_ecma_loader/src/resolvers/node.rs b/crates/swc_ecma_loader/src/resolvers/node.rs
index 80ade412a995..e08a8bd1b4b5 100644
--- a/crates/swc_ecma_loader/src/resolvers/node.rs
+++ b/crates/swc_ecma_loader/src/resolvers/node.rs
@@ -23,7 +23,10 @@ use swc_common::{
};
use tracing::{debug, trace, Level};
-use crate::{resolve::Resolve, TargetEnv, NODE_BUILTINS};
+use crate::{
+ resolve::{Resolution, Resolve},
+ TargetEnv, NODE_BUILTINS,
+};
static PACKAGE: &str = "package.json";
@@ -409,10 +412,8 @@ impl NodeModulesResolver {
Ok(None)
}
-}
-impl Resolve for NodeModulesResolver {
- fn resolve(&self, base: &FileName, target: &str) -> Result {
+ fn resolve_filename(&self, base: &FileName, target: &str) -> Result {
debug!(
"Resolving {} from {:#?} for {:#?}",
target, base, self.target_env
@@ -527,3 +528,13 @@ impl Resolve for NodeModulesResolver {
file_name
}
}
+
+impl Resolve for NodeModulesResolver {
+ fn resolve(&self, base: &FileName, module_specifier: &str) -> Result {
+ self.resolve_filename(base, module_specifier)
+ .map(|filename| Resolution {
+ filename,
+ slug: None,
+ })
+ }
+}
diff --git a/crates/swc_ecma_loader/src/resolvers/tsc.rs b/crates/swc_ecma_loader/src/resolvers/tsc.rs
index 357c5550f0df..f8cd57409c0c 100644
--- a/crates/swc_ecma_loader/src/resolvers/tsc.rs
+++ b/crates/swc_ecma_loader/src/resolvers/tsc.rs
@@ -4,7 +4,7 @@ use anyhow::{bail, Context, Error};
use swc_common::FileName;
use tracing::{debug, info, trace, warn, Level};
-use crate::resolve::Resolve;
+use crate::resolve::{Resolution, Resolve};
#[derive(Debug)]
enum Pattern {
@@ -104,7 +104,7 @@ where
&self,
base: &FileName,
module_specifier: &str,
- ) -> Result {
+ ) -> Result {
let res = self.inner.resolve(base, module_specifier).with_context(|| {
format!(
"failed to resolve `{module_specifier}` from `{base}` using inner \
@@ -117,7 +117,7 @@ where
Ok(resolved) => {
info!(
"Resolved `{}` as `{}` from `{}`",
- module_specifier, resolved, base
+ module_specifier, resolved.filename, base
);
let is_base_in_node_modules = if let FileName::Real(v) = base {
@@ -128,7 +128,7 @@ where
} else {
false
};
- let is_target_in_node_modules = if let FileName::Real(v) = &resolved {
+ let is_target_in_node_modules = if let FileName::Real(v) = &resolved.filename {
v.components().any(|c| match c {
Component::Normal(v) => v == "node_modules",
_ => false,
@@ -139,7 +139,10 @@ where
// If node_modules is in path, we should return module specifier.
if !is_base_in_node_modules && is_target_in_node_modules {
- return Ok(FileName::Real(module_specifier.into()));
+ return Ok(Resolution {
+ filename: FileName::Real(module_specifier.into()),
+ ..resolved
+ });
}
Ok(resolved)
@@ -157,7 +160,7 @@ impl Resolve for TsConfigResolver
where
R: Resolve,
{
- fn resolve(&self, base: &FileName, module_specifier: &str) -> Result {
+ fn resolve(&self, base: &FileName, module_specifier: &str) -> Result {
let _tracing = if cfg!(debug_assertions) {
Some(
tracing::span!(
@@ -219,7 +222,7 @@ where
let mut errors = vec![];
for target in to {
- let mut replaced = target.replace('*', extra);
+ let replaced = target.replace('*', extra);
let _tracing = if cfg!(debug_assertions) {
Some(
@@ -250,17 +253,22 @@ where
Err(err) => err,
});
- if cfg!(target_os = "windows") {
- replaced = replaced.replace('/', "\\");
- }
-
if to.len() == 1 {
info!(
"Using `{}` for `{}` because the length of the jsc.paths entry is \
1",
replaced, module_specifier
);
- return Ok(FileName::Real(replaced.into()));
+ return Ok(Resolution {
+ slug: Some(
+ replaced
+ .split([std::path::MAIN_SEPARATOR, '/'])
+ .last()
+ .unwrap()
+ .into(),
+ ),
+ filename: FileName::Real(replaced.into()),
+ });
}
}
@@ -278,8 +286,15 @@ where
}
let tp = Path::new(&to[0]);
+ let slug = to[0]
+ .split([std::path::MAIN_SEPARATOR, '/'])
+ .last()
+ .map(|v| v.into());
if tp.is_absolute() {
- return Ok(FileName::Real(tp.into()));
+ return Ok(Resolution {
+ filename: FileName::Real(tp.into()),
+ slug,
+ });
}
if let Ok(res) = self.resolve(&self.base_url_filename, &format!("./{}", &to[0]))
@@ -287,7 +302,10 @@ where
return Ok(res);
}
- return Ok(FileName::Real(self.base_url.join(&to[0])));
+ return Ok(Resolution {
+ filename: FileName::Real(self.base_url.join(&to[0])),
+ slug,
+ });
}
}
}
diff --git a/crates/swc_ecma_loader/tests/node_resolver.rs b/crates/swc_ecma_loader/tests/node_resolver.rs
index 65b1102d3424..cedea43bec17 100644
--- a/crates/swc_ecma_loader/tests/node_resolver.rs
+++ b/crates/swc_ecma_loader/tests/node_resolver.rs
@@ -41,7 +41,7 @@ fn basic_import() {
// Expect
assert_eq!(
- resolved,
+ resolved.filename,
FileName::Real(PathBuf::from("node_modules/jquery/index.js"))
);
});
@@ -60,7 +60,7 @@ fn hoisting() {
// Expect
assert_eq!(
- resolved,
+ resolved.filename,
FileName::Real(PathBuf::from("../../node_modules/jquery/index.js"))
);
});
@@ -76,7 +76,7 @@ fn builtin_modules() {
.expect("should resolve");
// Expect
- assert_eq!(resolved, FileName::Custom("node:path".to_string()));
+ assert_eq!(resolved.filename, FileName::Custom("node:path".to_string()));
// When
let resolved = node_resolver
@@ -84,7 +84,7 @@ fn builtin_modules() {
.expect("should resolve");
// Expect
- assert_eq!(resolved, FileName::Custom("node:path".to_string()));
+ assert_eq!(resolved.filename, FileName::Custom("node:path".to_string()));
}
#[test]
@@ -100,7 +100,7 @@ fn browser_overwrite() {
// Expect
assert_eq!(
- resolved,
+ resolved.filename,
FileName::Real(PathBuf::from("node_modules/jquery/browser.js"))
);
});
diff --git a/crates/swc_ecma_loader/tests/tsc_resolver.rs b/crates/swc_ecma_loader/tests/tsc_resolver.rs
index 5ff4cd0c68e0..7722bb35398f 100644
--- a/crates/swc_ecma_loader/tests/tsc_resolver.rs
+++ b/crates/swc_ecma_loader/tests/tsc_resolver.rs
@@ -4,7 +4,10 @@ use std::collections::HashMap;
use anyhow::{anyhow, Error};
use swc_common::{collections::AHashMap, FileName};
-use swc_ecma_loader::{resolve::Resolve, resolvers::tsc::TsConfigResolver};
+use swc_ecma_loader::{
+ resolve::{Resolution, Resolve},
+ resolvers::tsc::TsConfigResolver,
+};
#[test]
fn base_dir_exact() {}
@@ -34,7 +37,13 @@ fn exact() {
.resolve(&FileName::Anon, "jquery")
.expect("should resolve");
- assert_eq!(resolved, FileName::Custom("success".into()));
+ assert_eq!(
+ resolved,
+ Resolution {
+ filename: FileName::Custom("success".into()),
+ slug: None
+ }
+ );
}
{
@@ -64,7 +73,13 @@ fn pattern_1() {
.resolve(&FileName::Anon, "folder1/file2")
.expect("should resolve");
- assert_eq!(resolved, FileName::Custom("success-2".into()));
+ assert_eq!(
+ resolved,
+ Resolution {
+ filename: FileName::Custom("success-2".into()),
+ slug: None
+ }
+ );
}
{
@@ -72,18 +87,28 @@ fn pattern_1() {
.resolve(&FileName::Anon, "folder2/file3")
.expect("should resolve");
- assert_eq!(resolved, FileName::Custom("success-3".into()));
+ assert_eq!(
+ resolved,
+ Resolution {
+ filename: FileName::Custom("success-3".into()),
+ slug: None
+ }
+ );
}
}
struct TestResolver(AHashMap);
impl Resolve for TestResolver {
- fn resolve(&self, _: &FileName, src: &str) -> Result {
+ fn resolve(&self, _: &FileName, src: &str) -> Result {
self.0
.get(src)
.cloned()
.map(FileName::Custom)
+ .map(|v| Resolution {
+ filename: v,
+ slug: None,
+ })
.ok_or_else(|| anyhow!("failed to resolve `{}`", src))
}
}
diff --git a/crates/swc_ecma_transforms_module/src/path.rs b/crates/swc_ecma_transforms_module/src/path.rs
index 7563dc37ce47..2166dd994c6a 100644
--- a/crates/swc_ecma_transforms_module/src/path.rs
+++ b/crates/swc_ecma_transforms_module/src/path.rs
@@ -13,7 +13,7 @@ use pathdiff::diff_paths;
use swc_atoms::JsWord;
use swc_common::{FileName, Mark, Span, DUMMY_SP};
use swc_ecma_ast::*;
-use swc_ecma_loader::resolve::Resolve;
+use swc_ecma_loader::resolve::{Resolution, Resolve};
use swc_ecma_utils::{quote_ident, ExprFactory};
use tracing::{debug, info, warn, Level};
@@ -155,8 +155,8 @@ where
false
};
- let is_resolved_as_ts = if let Some(ext) = target_path.extension() {
- ext == "ts" || ext == "tsx"
+ let is_resolved_as_non_js = if let Some(ext) = target_path.extension() {
+ ext != "js"
} else {
false
};
@@ -173,17 +173,31 @@ where
false
};
- if !is_resolved_as_js && !is_resolved_as_index && !is_exact {
+ if orig_filename == "index" {
+ // Import: `./foo/index`
+ // Resolved: `./foo/index.js`
+
+ if self.config.resolve_fully {
+ target_path.set_file_name("index.js");
+ } else {
+ target_path.set_file_name("index");
+ }
+ } else if is_resolved_as_index && is_resolved_as_js && orig_filename != "index.js" {
+ // Import: `./foo`
+ // Resolved: `./foo/index.js`
+
+ target_path.pop();
+ } else if !is_resolved_as_js && !is_resolved_as_index && !is_exact {
target_path.set_file_name(orig_filename);
- } else if is_resolved_as_ts && is_exact {
+ } else if is_resolved_as_non_js && is_exact {
if let Some(ext) = Path::new(orig_filename).extension() {
target_path.set_extension(ext);
} else {
target_path.set_extension("js");
}
- } else if self.config.resolve_fully && is_resolved_as_ts {
+ } else if self.config.resolve_fully && is_resolved_as_non_js {
target_path.set_extension("js");
- } else if is_resolved_as_ts && is_resolved_as_index {
+ } else if is_resolved_as_non_js && is_resolved_as_index {
if orig_filename == "index" {
target_path.set_extension("");
} else {
@@ -216,7 +230,7 @@ where
None
};
- let orig_filename = module_specifier.split('/').last();
+ let orig_slug = module_specifier.split('/').last();
let target = self.resolver.resolve(base, module_specifier);
let mut target = match target {
@@ -230,13 +244,19 @@ where
// Bazel uses symlink
//
// https://github.com/swc-project/swc/issues/8265
- if let FileName::Real(resolved) = &target {
+ if let FileName::Real(resolved) = &target.filename {
if let Ok(orig) = read_link(resolved) {
- target = FileName::Real(orig);
+ target.filename = FileName::Real(orig);
}
}
- info!("Resolved to {}", target);
+ let Resolution {
+ filename: target,
+ slug,
+ } = target;
+ let slug = slug.as_deref().or(orig_slug);
+
+ info!("Resolved as {target:?} with slug = {slug:?}");
let mut target = match target {
FileName::Real(v) => {
@@ -244,10 +264,10 @@ where
if v.starts_with(".") || v.starts_with("..") || v.is_absolute() {
v
} else {
- return Ok(self.to_specifier(v, orig_filename));
+ return Ok(self.to_specifier(v, slug));
}
}
- FileName::Custom(s) => return Ok(self.to_specifier(s.into(), orig_filename)),
+ FileName::Custom(s) => return Ok(self.to_specifier(s.into(), slug)),
_ => {
unreachable!(
"Node path provider does not support using `{:?}` as a target file name",
@@ -293,7 +313,7 @@ where
let rel_path = match rel_path {
Some(v) => v,
- None => return Ok(self.to_specifier(target, orig_filename)),
+ None => return Ok(self.to_specifier(target, slug)),
};
debug!("Relative path: {}", rel_path.display());
@@ -323,7 +343,7 @@ where
Cow::Owned(format!("./{}", s))
};
- Ok(self.to_specifier(s.into_owned().into(), orig_filename))
+ Ok(self.to_specifier(s.into_owned().into(), slug))
}
}
diff --git a/crates/swc_ecma_transforms_module/tests/fixture-manual/issue-4730/output/index.js b/crates/swc_ecma_transforms_module/tests/fixture-manual/issue-4730/output/index.js
index adeea09e9a98..0ae50685d6dd 100644
--- a/crates/swc_ecma_transforms_module/tests/fixture-manual/issue-4730/output/index.js
+++ b/crates/swc_ecma_transforms_module/tests/fixture-manual/issue-4730/output/index.js
@@ -1,6 +1,6 @@
-import { displayB } from "../packages/b/src";
+import { displayB } from "../packages/b/src/index.js";
async function display() {
- const displayA = await import("../packages/a/src").then((c)=>c.displayA);
+ const displayA = await import("../packages/a/src/index.js").then((c)=>c.displayA);
console.log(displayA());
console.log(displayB());
}
diff --git a/crates/swc_ecma_transforms_module/tests/path_node.rs b/crates/swc_ecma_transforms_module/tests/path_node.rs
index 32c80dc58ee7..28200a1f3fd6 100644
--- a/crates/swc_ecma_transforms_module/tests/path_node.rs
+++ b/crates/swc_ecma_transforms_module/tests/path_node.rs
@@ -51,7 +51,7 @@ fn issue_4730() {
.join("packages")
.join("a")
.join("src")
- .join("index.ts")
+ .join("index.js")
.display()
.to_string()],
);
@@ -62,7 +62,7 @@ fn issue_4730() {
.join("packages")
.join("b")
.join("src")
- .join("index.ts")
+ .join("index.js")
.display()
.to_string()],
);
diff --git a/node-swc/__tests__/transform/issue_4730_test.mjs b/node-swc/__tests__/transform/issue_4730_test.mjs
index 59ecfd9a3e42..825e6cb0cab2 100644
--- a/node-swc/__tests__/transform/issue_4730_test.mjs
+++ b/node-swc/__tests__/transform/issue_4730_test.mjs
@@ -23,8 +23,8 @@ it("should work", async () => {
target: "es2020",
baseUrl: resolve("."),
paths: {
- "@print/a": [join(dir, "./packages/a/src/index.ts")],
- "@print/b": [join(dir, "./packages/b/src/index.ts")],
+ "@print/a": [join(dir, "./packages/a/src/index.js")],
+ "@print/b": [join(dir, "./packages/b/src/index.js")],
},
externalHelpers: true,
},
@@ -38,9 +38,9 @@ it("should work", async () => {
value: true
});
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
- const _b = require("../packages/b/src");
+ const _b = require("../packages/b/src/index.js");
async function display() {
- const displayA = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard._(require("../packages/a/src"))).then((c)=>c.displayA);
+ const displayA = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard._(require("../packages/a/src/index.js"))).then((c)=>c.displayA);
console.log(displayA());
console.log((0, _b.displayB)());
}