From 7e8d72d03b312b7a48c17afa8d2a4d7f4e802a6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 14 Sep 2023 16:17:23 +0900 Subject: [PATCH] feat(es/module): Add an option to make resolver fully resolve `index.js` (#7945) **Description:** - `jsc.module.resolveFully` is added to support resolving as `.js`. **Related issue:** - Closes #7861 - Closes #7898 --- crates/swc/src/builder.rs | 4 +- crates/swc/src/config/mod.rs | 53 +++++-- crates/swc/tests/exec.rs | 2 +- .../deno/paths/issue-3447/input/.swcrc | 7 +- .../fixture/issues-3xxx/3882/input/.swcrc | 4 +- .../fixture/issues-4xxx/4120/1/input/.swcrc | 14 +- .../fixture/issues-4xxx/4225/1/input/.swcrc | 8 +- .../fixture/issues-4xxx/4225/2/input/.swcrc | 8 +- .../fixture/issues-4xxx/4226/1/input/.swcrc | 8 +- .../fixture/issues-4xxx/4226/2/input/.swcrc | 8 +- .../fixture/issues-5xxx/5556/input/.swcrc | 6 +- .../fixture/issues-5xxx/5557/input/.swcrc | 6 +- .../fixture/issues-5xxx/5558/2/input/.swcrc | 6 +- .../fixture/issues-7xxx/7898/1/input/.swcrc | 18 +++ .../fixture/issues-7xxx/7898/1/input/index.ts | 3 + .../issues-7xxx/7898/1/input/src/index.ts | 3 + .../issues-7xxx/7898/1/output/index.ts | 6 + .../issues-7xxx/7898/1/output/src/index.ts | 13 ++ .../fixture/issues-7xxx/7898/2/input/.swcrc | 15 ++ .../7898/2/input/packages/a/src/index.ts | 3 + .../issues-7xxx/7898/2/input/src/index.ts | 3 + .../7898/2/output/packages/a/src/index.ts | 13 ++ .../issues-7xxx/7898/2/output/src/index.ts | 6 + .../tests/fixture/issues-7xxx/7898/input/1.js | 3 - .../fixture/issues-7xxx/7898/output/1.js | 3 - crates/swc/tests/tsc.rs | 4 +- crates/swc_ecma_transforms_module/src/lib.rs | 9 ++ crates/swc_ecma_transforms_module/src/path.rs | 150 ++++++++++-------- .../src/system_js.rs | 3 + crates/swc_ecma_transforms_module/src/util.rs | 4 + .../tests/path_node.rs | 21 ++- .../tests/system_js.rs | 12 +- 32 files changed, 266 insertions(+), 160 deletions(-) create mode 100644 crates/swc/tests/fixture/issues-7xxx/7898/1/input/.swcrc create mode 100644 crates/swc/tests/fixture/issues-7xxx/7898/1/input/index.ts create mode 100644 crates/swc/tests/fixture/issues-7xxx/7898/1/input/src/index.ts create mode 100644 crates/swc/tests/fixture/issues-7xxx/7898/1/output/index.ts create mode 100644 crates/swc/tests/fixture/issues-7xxx/7898/1/output/src/index.ts create mode 100644 crates/swc/tests/fixture/issues-7xxx/7898/2/input/.swcrc create mode 100644 crates/swc/tests/fixture/issues-7xxx/7898/2/input/packages/a/src/index.ts create mode 100644 crates/swc/tests/fixture/issues-7xxx/7898/2/input/src/index.ts create mode 100644 crates/swc/tests/fixture/issues-7xxx/7898/2/output/packages/a/src/index.ts create mode 100644 crates/swc/tests/fixture/issues-7xxx/7898/2/output/src/index.ts delete mode 100644 crates/swc/tests/fixture/issues-7xxx/7898/input/1.js delete mode 100644 crates/swc/tests/fixture/issues-7xxx/7898/output/1.js diff --git a/crates/swc/src/builder.rs b/crates/swc/src/builder.rs index 86a3f3cd70ac..e335ee6fd386 100644 --- a/crates/swc/src/builder.rs +++ b/crates/swc/src/builder.rs @@ -187,8 +187,8 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> { (true, c.config.import_interop(), c.config.ignore_dynamic) } Some(ModuleConfig::SystemJs(_)) - | Some(ModuleConfig::Es6) - | Some(ModuleConfig::NodeNext) + | Some(ModuleConfig::Es6(..)) + | Some(ModuleConfig::NodeNext(..)) | None => (false, true.into(), true), }; diff --git a/crates/swc/src/config/mod.rs b/crates/swc/src/config/mod.rs index 9070331e49b9..1a79edfdf0eb 100644 --- a/crates/swc/src/config/mod.rs +++ b/crates/swc/src/config/mod.rs @@ -54,7 +54,7 @@ use swc_ecma_parser::{parse_file_as_expr, Syntax, TsConfig}; use swc_ecma_transforms::{ feature::FeatureFlag, hygiene, modules, - modules::{path::NodeImportResolver, rewriter::import_rewriter}, + modules::{path::NodeImportResolver, rewriter::import_rewriter, EsModuleConfig}, optimization::{const_modules, json_parse, simplifier}, pass::{noop, Optional}, proposals::{ @@ -419,7 +419,7 @@ impl Options { if matches!( cfg.module, - None | Some(ModuleConfig::Es6 | ModuleConfig::NodeNext) + None | Some(ModuleConfig::Es6(..) | ModuleConfig::NodeNext(..)) ) { c.module = true; } @@ -609,11 +609,11 @@ impl Options { ); let import_export_assign_config = match cfg.module { - Some(ModuleConfig::Es6) => TsImportExportAssignConfig::EsNext, + Some(ModuleConfig::Es6(..)) => TsImportExportAssignConfig::EsNext, Some(ModuleConfig::CommonJs(..)) | Some(ModuleConfig::Amd(..)) | Some(ModuleConfig::Umd(..)) => TsImportExportAssignConfig::Preserve, - Some(ModuleConfig::NodeNext) => TsImportExportAssignConfig::NodeNext, + Some(ModuleConfig::NodeNext(..)) => TsImportExportAssignConfig::NodeNext, // TODO: should Preserve for SystemJS _ => TsImportExportAssignConfig::Classic, }; @@ -1571,9 +1571,9 @@ pub enum ModuleConfig { #[serde(rename = "systemjs")] SystemJs(modules::system_js::Config), #[serde(rename = "es6")] - Es6, + Es6(EsModuleConfig), #[serde(rename = "nodenext")] - NodeNext, + NodeNext(EsModuleConfig), } impl ModuleConfig { @@ -1596,11 +1596,20 @@ impl ModuleConfig { let skip_resolver = base_url.as_os_str().is_empty() && paths.is_empty(); match config { - None | Some(ModuleConfig::Es6) | Some(ModuleConfig::NodeNext) => { + None => { if skip_resolver { Box::new(noop()) } else { - let resolver = build_resolver(base_url, paths); + let resolver = build_resolver(base_url, paths, false); + + Box::new(import_rewriter(base, resolver)) + } + } + Some(ModuleConfig::Es6(config)) | Some(ModuleConfig::NodeNext(config)) => { + if skip_resolver { + Box::new(noop()) + } else { + let resolver = build_resolver(base_url, paths, config.resolve_fully); Box::new(import_rewriter(base, resolver)) } @@ -1614,7 +1623,7 @@ impl ModuleConfig { comments, )) } else { - let resolver = build_resolver(base_url, paths); + let resolver = build_resolver(base_url, paths, config.resolve_fully); Box::new(modules::common_js::common_js_with_resolver( resolver, base, @@ -1635,7 +1644,7 @@ impl ModuleConfig { comments, )) } else { - let resolver = build_resolver(base_url, paths); + let resolver = build_resolver(base_url, paths, config.config.resolve_fully); Box::new(modules::umd::umd_with_resolver( cm, @@ -1657,7 +1666,7 @@ impl ModuleConfig { comments, )) } else { - let resolver = build_resolver(base_url, paths); + let resolver = build_resolver(base_url, paths, config.config.resolve_fully); Box::new(modules::amd::amd_with_resolver( resolver, @@ -1673,7 +1682,7 @@ impl ModuleConfig { if skip_resolver { Box::new(modules::system_js::system_js(unresolved_mark, config)) } else { - let resolver = build_resolver(base_url, paths); + let resolver = build_resolver(base_url, paths, config.resolve_fully); Box::new(modules::system_js::system_js_with_resolver( resolver, @@ -1999,8 +2008,12 @@ fn default_env_name() -> String { } } -fn build_resolver(mut base_url: PathBuf, paths: CompiledPaths) -> Box { - static CACHE: Lazy> = +fn build_resolver( + mut base_url: PathBuf, + paths: CompiledPaths, + resolve_fully: bool, +) -> Box { + static CACHE: Lazy> = Lazy::new(Default::default); // On Windows, we need to normalize path as UNC path. @@ -2017,7 +2030,7 @@ fn build_resolver(mut base_url: PathBuf, paths: CompiledPaths) -> Box Box Vec { ..Default::default() }, module: if entry.extension().unwrap() == "mjs" { - Some(ModuleConfig::Es6) + Some(ModuleConfig::Es6(Default::default())) } else { Some(ModuleConfig::CommonJs(Default::default())) }, diff --git a/crates/swc/tests/fixture/deno/paths/issue-3447/input/.swcrc b/crates/swc/tests/fixture/deno/paths/issue-3447/input/.swcrc index d3f9e8256cfd..33eded2cefa5 100644 --- a/crates/swc/tests/fixture/deno/paths/issue-3447/input/.swcrc +++ b/crates/swc/tests/fixture/deno/paths/issue-3447/input/.swcrc @@ -25,11 +25,6 @@ } }, "module": { - "type": "es6", - "strict": false, - "strictMode": true, - "lazy": false, - "noInterop": false, - "ignoreDynamic": true + "type": "es6" } } \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3882/input/.swcrc b/crates/swc/tests/fixture/issues-3xxx/3882/input/.swcrc index ef5b2e625a67..795b791fd37c 100644 --- a/crates/swc/tests/fixture/issues-3xxx/3882/input/.swcrc +++ b/crates/swc/tests/fixture/issues-3xxx/3882/input/.swcrc @@ -14,8 +14,6 @@ }, "module": { "type": "es6", - "strict": false, - "strictMode": false }, "minify": false -} +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-4xxx/4120/1/input/.swcrc b/crates/swc/tests/fixture/issues-4xxx/4120/1/input/.swcrc index c22c34d3cf76..ad2406392762 100644 --- a/crates/swc/tests/fixture/issues-4xxx/4120/1/input/.swcrc +++ b/crates/swc/tests/fixture/issues-4xxx/4120/1/input/.swcrc @@ -1,9 +1,7 @@ { "sourceMaps": true, "module": { - "type": "es6", - "strict": true, - "noInterop": false + "type": "es6" }, "env": { "targets": "node 12" @@ -72,11 +70,15 @@ }, "keepClassNames": false, "paths": { - "@/*": ["./*"], - "__shared-fixtures__/*": ["./test/__shared-fixtures__/*"] + "@/*": [ + "./*" + ], + "__shared-fixtures__/*": [ + "./test/__shared-fixtures__/*" + ] }, "baseUrl": ".", "loose": false }, "minify": true -} +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-4xxx/4225/1/input/.swcrc b/crates/swc/tests/fixture/issues-4xxx/4225/1/input/.swcrc index bc1bfd4f0a90..1f5a27923262 100644 --- a/crates/swc/tests/fixture/issues-4xxx/4225/1/input/.swcrc +++ b/crates/swc/tests/fixture/issues-4xxx/4225/1/input/.swcrc @@ -12,11 +12,7 @@ } }, "module": { - "type": "es6", - "strict": false, - "strictMode": false, - "lazy": false, - "noInterop": false + "type": "es6" }, "minify": false -} +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-4xxx/4225/2/input/.swcrc b/crates/swc/tests/fixture/issues-4xxx/4225/2/input/.swcrc index bc1bfd4f0a90..1f5a27923262 100644 --- a/crates/swc/tests/fixture/issues-4xxx/4225/2/input/.swcrc +++ b/crates/swc/tests/fixture/issues-4xxx/4225/2/input/.swcrc @@ -12,11 +12,7 @@ } }, "module": { - "type": "es6", - "strict": false, - "strictMode": false, - "lazy": false, - "noInterop": false + "type": "es6" }, "minify": false -} +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-4xxx/4226/1/input/.swcrc b/crates/swc/tests/fixture/issues-4xxx/4226/1/input/.swcrc index bc1bfd4f0a90..1f5a27923262 100644 --- a/crates/swc/tests/fixture/issues-4xxx/4226/1/input/.swcrc +++ b/crates/swc/tests/fixture/issues-4xxx/4226/1/input/.swcrc @@ -12,11 +12,7 @@ } }, "module": { - "type": "es6", - "strict": false, - "strictMode": false, - "lazy": false, - "noInterop": false + "type": "es6" }, "minify": false -} +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-4xxx/4226/2/input/.swcrc b/crates/swc/tests/fixture/issues-4xxx/4226/2/input/.swcrc index fb3eef64cd90..514f3474938e 100644 --- a/crates/swc/tests/fixture/issues-4xxx/4226/2/input/.swcrc +++ b/crates/swc/tests/fixture/issues-4xxx/4226/2/input/.swcrc @@ -12,11 +12,7 @@ } }, "module": { - "type": "es6", - "strict": false, - "strictMode": false, - "lazy": false, - "noInterop": false + "type": "es6" }, "minify": false -} +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-5xxx/5556/input/.swcrc b/crates/swc/tests/fixture/issues-5xxx/5556/input/.swcrc index f78f7ff0dc5e..1f5a27923262 100644 --- a/crates/swc/tests/fixture/issues-5xxx/5556/input/.swcrc +++ b/crates/swc/tests/fixture/issues-5xxx/5556/input/.swcrc @@ -12,11 +12,7 @@ } }, "module": { - "type": "es6", - "strict": false, - "strictMode": false, - "lazy": false, - "noInterop": false + "type": "es6" }, "minify": false } \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-5xxx/5557/input/.swcrc b/crates/swc/tests/fixture/issues-5xxx/5557/input/.swcrc index f78f7ff0dc5e..1f5a27923262 100644 --- a/crates/swc/tests/fixture/issues-5xxx/5557/input/.swcrc +++ b/crates/swc/tests/fixture/issues-5xxx/5557/input/.swcrc @@ -12,11 +12,7 @@ } }, "module": { - "type": "es6", - "strict": false, - "strictMode": false, - "lazy": false, - "noInterop": false + "type": "es6" }, "minify": false } \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-5xxx/5558/2/input/.swcrc b/crates/swc/tests/fixture/issues-5xxx/5558/2/input/.swcrc index f78f7ff0dc5e..1f5a27923262 100644 --- a/crates/swc/tests/fixture/issues-5xxx/5558/2/input/.swcrc +++ b/crates/swc/tests/fixture/issues-5xxx/5558/2/input/.swcrc @@ -12,11 +12,7 @@ } }, "module": { - "type": "es6", - "strict": false, - "strictMode": false, - "lazy": false, - "noInterop": false + "type": "es6" }, "minify": false } \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-7xxx/7898/1/input/.swcrc b/crates/swc/tests/fixture/issues-7xxx/7898/1/input/.swcrc new file mode 100644 index 000000000000..7858fd786d90 --- /dev/null +++ b/crates/swc/tests/fixture/issues-7xxx/7898/1/input/.swcrc @@ -0,0 +1,18 @@ +{ + "jsc": { + "parser": { + "syntax": "typescript" + }, + "target": "es2020", + "baseUrl": ".", + "paths": { + "@print/a": [ + "./packages/a/src/index.ts" + ] + } + }, + "module": { + "type": "commonjs", + "resolveFully": true + } +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-7xxx/7898/1/input/index.ts b/crates/swc/tests/fixture/issues-7xxx/7898/1/input/index.ts new file mode 100644 index 000000000000..af564378e3dc --- /dev/null +++ b/crates/swc/tests/fixture/issues-7xxx/7898/1/input/index.ts @@ -0,0 +1,3 @@ +import { displayA } from "@print/a"; + +console.log(`${displayA()}`); diff --git a/crates/swc/tests/fixture/issues-7xxx/7898/1/input/src/index.ts b/crates/swc/tests/fixture/issues-7xxx/7898/1/input/src/index.ts new file mode 100644 index 000000000000..1ff17252b735 --- /dev/null +++ b/crates/swc/tests/fixture/issues-7xxx/7898/1/input/src/index.ts @@ -0,0 +1,3 @@ +export function displayA() { + return "Display A"; +} 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 new file mode 100644 index 000000000000..5ac4603ec675 --- /dev/null +++ b/crates/swc/tests/fixture/issues-7xxx/7898/1/output/index.ts @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { + value: true +}); +const _a = require("./packages/a/src/index.js"); +console.log(`${(0, _a.displayA)()}`); diff --git a/crates/swc/tests/fixture/issues-7xxx/7898/1/output/src/index.ts b/crates/swc/tests/fixture/issues-7xxx/7898/1/output/src/index.ts new file mode 100644 index 000000000000..5dbfc50b9a22 --- /dev/null +++ b/crates/swc/tests/fixture/issues-7xxx/7898/1/output/src/index.ts @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "displayA", { + enumerable: true, + get: function() { + return displayA; + } +}); +function displayA() { + return "Display A"; +} diff --git a/crates/swc/tests/fixture/issues-7xxx/7898/2/input/.swcrc b/crates/swc/tests/fixture/issues-7xxx/7898/2/input/.swcrc new file mode 100644 index 000000000000..244c107cab82 --- /dev/null +++ b/crates/swc/tests/fixture/issues-7xxx/7898/2/input/.swcrc @@ -0,0 +1,15 @@ +{ + "jsc": { + "parser": { + "syntax": "typescript" + }, + "target": "es2020", + "baseUrl": ".", + "paths": { + "@print/a": ["./packages/a/src/index.ts"] + } + }, + "module": { + "type": "commonjs" + } +} diff --git a/crates/swc/tests/fixture/issues-7xxx/7898/2/input/packages/a/src/index.ts b/crates/swc/tests/fixture/issues-7xxx/7898/2/input/packages/a/src/index.ts new file mode 100644 index 000000000000..1ff17252b735 --- /dev/null +++ b/crates/swc/tests/fixture/issues-7xxx/7898/2/input/packages/a/src/index.ts @@ -0,0 +1,3 @@ +export function displayA() { + return "Display A"; +} diff --git a/crates/swc/tests/fixture/issues-7xxx/7898/2/input/src/index.ts b/crates/swc/tests/fixture/issues-7xxx/7898/2/input/src/index.ts new file mode 100644 index 000000000000..af564378e3dc --- /dev/null +++ b/crates/swc/tests/fixture/issues-7xxx/7898/2/input/src/index.ts @@ -0,0 +1,3 @@ +import { displayA } from "@print/a"; + +console.log(`${displayA()}`); diff --git a/crates/swc/tests/fixture/issues-7xxx/7898/2/output/packages/a/src/index.ts b/crates/swc/tests/fixture/issues-7xxx/7898/2/output/packages/a/src/index.ts new file mode 100644 index 000000000000..5dbfc50b9a22 --- /dev/null +++ b/crates/swc/tests/fixture/issues-7xxx/7898/2/output/packages/a/src/index.ts @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "displayA", { + enumerable: true, + get: function() { + return displayA; + } +}); +function displayA() { + return "Display A"; +} diff --git a/crates/swc/tests/fixture/issues-7xxx/7898/2/output/src/index.ts b/crates/swc/tests/fixture/issues-7xxx/7898/2/output/src/index.ts new file mode 100644 index 000000000000..da767706d2a2 --- /dev/null +++ b/crates/swc/tests/fixture/issues-7xxx/7898/2/output/src/index.ts @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { + value: true +}); +const _a = require("../packages/a/src"); +console.log(`${(0, _a.displayA)()}`); diff --git a/crates/swc/tests/fixture/issues-7xxx/7898/input/1.js b/crates/swc/tests/fixture/issues-7xxx/7898/input/1.js deleted file mode 100644 index 64a69664ebba..000000000000 --- a/crates/swc/tests/fixture/issues-7xxx/7898/input/1.js +++ /dev/null @@ -1,3 +0,0 @@ -info({ - ...(this?.$current?.$getBase() || {}), -}); \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-7xxx/7898/output/1.js b/crates/swc/tests/fixture/issues-7xxx/7898/output/1.js deleted file mode 100644 index 2010e34da06e..000000000000 --- a/crates/swc/tests/fixture/issues-7xxx/7898/output/1.js +++ /dev/null @@ -1,3 +0,0 @@ -import { _ as _object_spread } from "@swc/helpers/_/_object_spread"; -var _this_$current, _this; -info(_object_spread({}, ((_this = this) === null || _this === void 0 ? void 0 : (_this_$current = _this.$current) === null || _this_$current === void 0 ? void 0 : _this_$current.$getBase()) || {})); diff --git a/crates/swc/tests/tsc.rs b/crates/swc/tests/tsc.rs index ecf12920d7bd..7f2818b953ff 100644 --- a/crates/swc/tests/tsc.rs +++ b/crates/swc/tests/tsc.rs @@ -315,8 +315,8 @@ fn matrix(input: &Path) -> Vec { Self::Umd => ModuleConfig::Umd(Default::default()), Self::Amd => ModuleConfig::Amd(Default::default()), Self::SystemJs => ModuleConfig::SystemJs(Default::default()), - Self::Es6 => ModuleConfig::Es6, - Self::NodeNext => ModuleConfig::NodeNext, + Self::Es6 => ModuleConfig::Es6(Default::default()), + Self::NodeNext => ModuleConfig::NodeNext(Default::default()), } } } diff --git a/crates/swc_ecma_transforms_module/src/lib.rs b/crates/swc_ecma_transforms_module/src/lib.rs index 6c73f690ddce..bfc8b3869f22 100644 --- a/crates/swc_ecma_transforms_module/src/lib.rs +++ b/crates/swc_ecma_transforms_module/src/lib.rs @@ -3,6 +3,8 @@ #![allow(clippy::needless_lifetimes)] #![allow(clippy::vec_box)] +use serde::{Deserialize, Serialize}; + pub use self::{amd::amd, common_js::common_js, system_js::system_js, umd::umd}; #[macro_use] @@ -16,3 +18,10 @@ pub mod path; pub mod rewriter; pub mod system_js; pub mod umd; + +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields, rename_all = "camelCase")] +pub struct EsModuleConfig { + #[serde(default)] + pub resolve_fully: bool, +} diff --git a/crates/swc_ecma_transforms_module/src/path.rs b/crates/swc_ecma_transforms_module/src/path.rs index 57507cee7840..3f74d07bc723 100644 --- a/crates/swc_ecma_transforms_module/src/path.rs +++ b/crates/swc_ecma_transforms_module/src/path.rs @@ -86,21 +86,38 @@ where R: Resolve, { resolver: R, - base_dir: Option, + config: Config, +} + +#[derive(Debug, Clone, Default)] +pub struct Config { + pub base_dir: Option, + pub resolve_fully: bool, } impl NodeImportResolver where R: Resolve, { - #[deprecated(note = "Use `with_base_dir`")] + #[deprecated(note = "Use `with_config`")] pub fn new(resolver: R) -> Self { - Self::with_base_dir(resolver, None) + Self::with_config(resolver, Default::default()) } + #[deprecated(note = "Use `with_config`")] pub fn with_base_dir(resolver: R, base_dir: Option) -> Self { + Self::with_config( + resolver, + Config { + base_dir, + ..Default::default() + }, + ) + } + + pub fn with_config(resolver: R, config: Config) -> Self { #[cfg(not(target_arch = "wasm32"))] - if let Some(base_dir) = &base_dir { + if let Some(base_dir) = &config.base_dir { assert!( base_dir.is_absolute(), "base_dir(`{}`) must be absolute. Please ensure that `jsc.baseUrl` is specified \ @@ -115,7 +132,66 @@ where ); } - Self { resolver, base_dir } + Self { resolver, config } + } +} + +impl NodeImportResolver +where + R: Resolve, +{ + fn to_specifier(&self, mut target_path: PathBuf, orig_filename: Option<&str>) -> JsWord { + debug!( + "Creating a specifier for `{}` with original filename `{:?}`", + target_path.display(), + orig_filename + ); + + if let Some(orig_filename) = orig_filename { + let is_resolved_as_index = if let Some(stem) = target_path.file_stem() { + stem == "index" + } else { + false + }; + + let is_resolved_as_ts = if let Some(ext) = target_path.extension() { + ext == "ts" || ext == "tsx" + } else { + false + }; + + let is_exact = if let Some(filename) = target_path.file_name() { + filename == orig_filename + } else { + false + }; + + if !is_resolved_as_index && !is_exact { + target_path.set_file_name(orig_filename); + } else if is_resolved_as_ts && 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 { + target_path.set_extension("js"); + } else if is_resolved_as_ts && is_resolved_as_index { + if orig_filename == "index" { + target_path.set_extension(""); + } else { + target_path.pop(); + } + } + } else { + target_path.set_extension(""); + } + + if cfg!(target_os = "windows") { + target_path.display().to_string().replace('\\', "/").into() + } else { + target_path.display().to_string().into() + } } } @@ -157,10 +233,10 @@ where if v.starts_with(".") || v.starts_with("..") || v.is_absolute() { v } else { - return Ok(to_specifier(v, orig_filename)); + return Ok(self.to_specifier(v, orig_filename)); } } - FileName::Custom(s) => return Ok(to_specifier(s.into(), orig_filename)), + FileName::Custom(s) => return Ok(self.to_specifier(s.into(), orig_filename)), _ => { unreachable!( "Node path provider does not support using `{:?}` as a target file name", @@ -186,8 +262,8 @@ where }; if base.is_absolute() != target.is_absolute() { - base = Cow::Owned(absolute_path(self.base_dir.as_deref(), &base)?); - target = absolute_path(self.base_dir.as_deref(), &target)?; + base = Cow::Owned(absolute_path(self.config.base_dir.as_deref(), &base)?); + target = absolute_path(self.config.base_dir.as_deref(), &target)?; } debug!( @@ -206,7 +282,7 @@ where let rel_path = match rel_path { Some(v) => v, - None => return Ok(to_specifier(target, orig_filename)), + None => return Ok(self.to_specifier(target, orig_filename)), }; debug!("Relative path: {}", rel_path.display()); @@ -236,7 +312,7 @@ where Cow::Owned(format!("./{}", s)) }; - Ok(to_specifier(s.into_owned().into(), orig_filename)) + Ok(self.to_specifier(s.into_owned().into(), orig_filename)) } } @@ -270,55 +346,3 @@ fn absolute_path(base_dir: Option<&Path>, path: &Path) -> io::Result { Ok(absolute_path) } - -fn to_specifier(mut target_path: PathBuf, orig_filename: Option<&str>) -> JsWord { - debug!( - "Creating a specifier for `{}` with original filename `{:?}`", - target_path.display(), - orig_filename - ); - - if let Some(orig_filename) = orig_filename { - let is_resolved_as_index = if let Some(stem) = target_path.file_stem() { - stem == "index" - } else { - false - }; - - let is_resolved_as_ts = if let Some(ext) = target_path.extension() { - ext == "ts" || ext == "tsx" - } else { - false - }; - - let is_exact = if let Some(filename) = target_path.file_name() { - filename == orig_filename - } else { - false - }; - - if !is_resolved_as_index && !is_exact { - target_path.set_file_name(orig_filename); - } else if is_resolved_as_ts && is_exact { - if let Some(ext) = Path::new(orig_filename).extension() { - target_path.set_extension(ext); - } else { - target_path.set_extension("js"); - } - } else if is_resolved_as_ts && is_resolved_as_index { - if orig_filename == "index" { - target_path.set_extension(""); - } else { - target_path.pop(); - } - } - } else { - target_path.set_extension(""); - } - - if cfg!(target_os = "windows") { - target_path.display().to_string().replace('\\', "/").into() - } else { - target_path.display().to_string().into() - } -} diff --git a/crates/swc_ecma_transforms_module/src/system_js.rs b/crates/swc_ecma_transforms_module/src/system_js.rs index 520eb06b7de8..758e8c524e55 100644 --- a/crates/swc_ecma_transforms_module/src/system_js.rs +++ b/crates/swc_ecma_transforms_module/src/system_js.rs @@ -17,6 +17,9 @@ use crate::{ pub struct Config { #[serde(default)] pub allow_top_level_this: bool, + + #[serde(default)] + pub resolve_fully: bool, } struct SystemJs { diff --git a/crates/swc_ecma_transforms_module/src/util.rs b/crates/swc_ecma_transforms_module/src/util.rs index ee607d1b952f..823f9a2ef002 100644 --- a/crates/swc_ecma_transforms_module/src/util.rs +++ b/crates/swc_ecma_transforms_module/src/util.rs @@ -38,6 +38,9 @@ pub struct Config { pub ignore_dynamic: bool, #[serde(default)] pub preserve_import_meta: bool, + + #[serde(default)] + pub resolve_fully: bool, } impl Default for Config { @@ -52,6 +55,7 @@ impl Default for Config { no_interop: false, ignore_dynamic: false, preserve_import_meta: false, + resolve_fully: false, } } } diff --git a/crates/swc_ecma_transforms_module/tests/path_node.rs b/crates/swc_ecma_transforms_module/tests/path_node.rs index 6ce2a4388f76..32c80dc58ee7 100644 --- a/crates/swc_ecma_transforms_module/tests/path_node.rs +++ b/crates/swc_ecma_transforms_module/tests/path_node.rs @@ -84,24 +84,23 @@ fn issue_4730() { type JscPathsProvider = NodeImportResolver>; -fn paths_resolver( - base_url: impl AsRef, - rules: Vec<(String, Vec)>, -) -> JscPathsProvider { - let base_url = base_url - .as_ref() +fn paths_resolver(base_dir: &Path, rules: Vec<(String, Vec)>) -> JscPathsProvider { + let base_dir = base_dir .to_path_buf() .canonicalize() .expect("failed to canonicalize"); - dbg!(&base_url); + dbg!(&base_dir); - NodeImportResolver::with_base_dir( + NodeImportResolver::with_config( TsConfigResolver::new( NodeModulesResolver::new(swc_ecma_loader::TargetEnv::Node, Default::default(), true), - base_url.clone(), + base_dir.clone(), rules, ), - Some(base_url), + swc_ecma_transforms_module::path::Config { + base_dir: Some(base_dir), + resolve_fully: false, + }, ) } @@ -134,7 +133,7 @@ fn fixture(input_dir: PathBuf) { let rules = config.paths.clone().into_iter().collect(); let resolver = - paths_resolver(config.base_url.clone().unwrap_or(input_dir.clone()), rules); + paths_resolver(&config.base_url.clone().unwrap_or(input_dir.clone()), rules); import_rewriter(FileName::Real(index_path.clone()), resolver) }, diff --git a/crates/swc_ecma_transforms_module/tests/system_js.rs b/crates/swc_ecma_transforms_module/tests/system_js.rs index 40aeae243f1e..998178ab3cdf 100644 --- a/crates/swc_ecma_transforms_module/tests/system_js.rs +++ b/crates/swc_ecma_transforms_module/tests/system_js.rs @@ -45,7 +45,8 @@ test!( |tester| tr( tester, Config { - allow_top_level_this: true + allow_top_level_this: true, + ..Default::default() } ), allow_top_level_this_true, @@ -67,7 +68,8 @@ test!( |tester| tr( tester, Config { - allow_top_level_this: false + allow_top_level_this: false, + ..Default::default() } ), iife, @@ -94,7 +96,8 @@ test!( |tester| tr( tester, Config { - allow_top_level_this: false + allow_top_level_this: false, + ..Default::default() } ), top_level_this_false_class, @@ -133,7 +136,8 @@ test!( |tester| tr( tester, Config { - allow_top_level_this: false + allow_top_level_this: false, + ..Default::default() } ), allow_top_level_this_false,