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)()); }