diff --git a/.changeset/heavy-apes-collect.md b/.changeset/heavy-apes-collect.md new file mode 100644 index 000000000000..901c8cb9a1fe --- /dev/null +++ b/.changeset/heavy-apes-collect.md @@ -0,0 +1,5 @@ +--- +swc_ecma_transforms_module: major +--- + +Feat out file extension resolution diff --git a/crates/swc/src/config/mod.rs b/crates/swc/src/config/mod.rs index 07553534b7dd..1a6bf69d288b 100644 --- a/crates/swc/src/config/mod.rs +++ b/crates/swc/src/config/mod.rs @@ -48,7 +48,7 @@ use swc_ecma_transforms::{ self, path::{ImportResolver, NodeImportResolver, Resolver}, rewriter::import_rewriter, - EsModuleConfig, + util, EsModuleConfig, }, optimization::{const_modules, json_parse, simplifier}, proposals::{ @@ -1400,22 +1400,39 @@ impl ModuleConfig { let base_url = base_url.to_path_buf(); let resolver = match config { - None => build_resolver(base_url, paths, false), + None => build_resolver(base_url, paths, false, &util::Config::default_js_ext()), Some(ModuleConfig::Es6(config)) | Some(ModuleConfig::NodeNext(config)) => { - build_resolver(base_url, paths, config.resolve_fully) - } - Some(ModuleConfig::CommonJs(config)) => { - build_resolver(base_url, paths, config.resolve_fully) - } - Some(ModuleConfig::Umd(config)) => { - build_resolver(base_url, paths, config.config.resolve_fully) - } - Some(ModuleConfig::Amd(config)) => { - build_resolver(base_url, paths, config.config.resolve_fully) - } - Some(ModuleConfig::SystemJs(config)) => { - build_resolver(base_url, paths, config.resolve_fully) + build_resolver( + base_url, + paths, + config.config.resolve_fully, + &config.config.out_file_extension, + ) } + Some(ModuleConfig::CommonJs(config)) => build_resolver( + base_url, + paths, + config.resolve_fully, + &config.out_file_extension, + ), + Some(ModuleConfig::Umd(config)) => build_resolver( + base_url, + paths, + config.config.resolve_fully, + &config.config.out_file_extension, + ), + Some(ModuleConfig::Amd(config)) => build_resolver( + base_url, + paths, + config.config.resolve_fully, + &config.config.out_file_extension, + ), + Some(ModuleConfig::SystemJs(config)) => build_resolver( + base_url, + paths, + config.config.resolve_fully, + &config.config.out_file_extension, + ), }; Some((base, resolver)) @@ -1733,6 +1750,7 @@ fn build_resolver( mut base_url: PathBuf, paths: CompiledPaths, resolve_fully: bool, + file_extension: &str, ) -> SwcImportResolver { static CACHE: Lazy> = Lazy::new(Default::default); @@ -1772,6 +1790,7 @@ fn build_resolver( swc_ecma_transforms::modules::path::Config { base_dir: Some(base_url.clone()), resolve_fully, + file_extension: file_extension.to_owned(), }, ); Arc::new(r) diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/amd/input/.swcrc b/crates/swc/tests/fixture/issues-3xxx/3067/amd/input/.swcrc new file mode 100644 index 000000000000..0c23ac61b6ef --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/amd/input/.swcrc @@ -0,0 +1,21 @@ +{ + "jsc": { + "parser": { + "syntax": "typescript", + "dynamicImport": true, + }, + "target": "es2020", + "baseUrl": ".", + "paths": { + "@print/c": ["./packages/c/src/index.js"], + }, + "externalHelpers": true, + }, + "module": { + "type": "amd", + "resolveFully": true, + // This should say "resolve this fully to .mjs". + // Normally should be paired with --out-file-extension in the cli + "outFileExtension": "mjs", + }, +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/amd/input/packages/c/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/amd/input/packages/c/src/index.ts new file mode 100644 index 000000000000..b4bf6ca8eba2 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/amd/input/packages/c/src/index.ts @@ -0,0 +1,6 @@ +// Simulate accessing a .js file in a third party package that shouldn't be edited +import something from 'lodash/dist/something.js' +export function displayC(): string { + something() + return 'Display C' +} diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/amd/input/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/amd/input/src/index.ts new file mode 100644 index 000000000000..a0f4ab81010f --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/amd/input/src/index.ts @@ -0,0 +1,14 @@ + +import { displayB } from './inner/b' +import { displayC } from '@print/c' +import { merge } from 'lodash' + +async function display() { + const displayA = await import('./inner/a').then(c => c.displayA) + console.log(displayA()) + console.log(displayB()) + console.log(displayC()) + const foo = merge({}, { a: 22 }) +} + +display() \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/amd/input/src/inner/a/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/amd/input/src/inner/a/index.ts new file mode 100644 index 000000000000..4bdca77ee750 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/amd/input/src/inner/a/index.ts @@ -0,0 +1,3 @@ +export function displayA() { + return 'Display A' +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/amd/input/src/inner/b/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/amd/input/src/inner/b/index.ts new file mode 100644 index 000000000000..c6b2dc8e918e --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/amd/input/src/inner/b/index.ts @@ -0,0 +1,3 @@ +export function displayB() { + return 'Display B' +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/amd/output/packages/c/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/amd/output/packages/c/src/index.ts new file mode 100644 index 000000000000..8e3b0d8eb446 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/amd/output/packages/c/src/index.ts @@ -0,0 +1,23 @@ +// Simulate accessing a .js file in a third party package that shouldn't be edited +define([ + "require", + "exports", + "@swc/helpers/_/_interop_require_default", + "lodash/dist/something.js" +], function(require, exports, _interop_require_default, _something) { + "use strict"; + Object.defineProperty(exports, "__esModule", { + value: true + }); + Object.defineProperty(exports, "displayC", { + enumerable: true, + get: function() { + return displayC; + } + }); + _something = /*#__PURE__*/ _interop_require_default._(_something); + function displayC() { + (0, _something.default)(); + return 'Display C'; + } +}); diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/amd/output/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/amd/output/src/index.ts new file mode 100644 index 000000000000..e7ec1868489b --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/amd/output/src/index.ts @@ -0,0 +1,25 @@ +define([ + "require", + "exports", + "@swc/helpers/_/_interop_require_wildcard", + "./inner/b/index.mjs", + "../packages/c/src/index.mjs", + "lodash" +], function(require, exports, _interop_require_wildcard, _b, _c, _lodash) { + "use strict"; + Object.defineProperty(exports, "__esModule", { + value: true + }); + async function display() { + const displayA = await new Promise((resolve, reject)=>require([ + "./inner/a/index.mjs" + ], (m)=>resolve(/*#__PURE__*/ _interop_require_wildcard._(m)), reject)).then((c)=>c.displayA); + console.log(displayA()); + console.log((0, _b.displayB)()); + console.log((0, _c.displayC)()); + const foo = (0, _lodash.merge)({}, { + a: 22 + }); + } + display(); +}); diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/amd/output/src/inner/a/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/amd/output/src/inner/a/index.ts new file mode 100644 index 000000000000..d2b8b393576a --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/amd/output/src/inner/a/index.ts @@ -0,0 +1,18 @@ +define([ + "require", + "exports" +], function(require, exports) { + "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-3xxx/3067/amd/output/src/inner/b/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/amd/output/src/inner/b/index.ts new file mode 100644 index 000000000000..98d433fd51a2 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/amd/output/src/inner/b/index.ts @@ -0,0 +1,18 @@ +define([ + "require", + "exports" +], function(require, exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { + value: true + }); + Object.defineProperty(exports, "displayB", { + enumerable: true, + get: function() { + return displayB; + } + }); + function displayB() { + return 'Display B'; + } +}); diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/input/.swcrc b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/input/.swcrc new file mode 100644 index 000000000000..b90bfbaca006 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/input/.swcrc @@ -0,0 +1,21 @@ +{ + "jsc": { + "parser": { + "syntax": "typescript", + "dynamicImport": true, + }, + "target": "es2020", + "baseUrl": ".", + "paths": { + "@print/c": ["./packages/c/src/index.js"], + }, + "externalHelpers": true, + }, + "module": { + "type": "commonjs", + "resolveFully": true, + // This should say "resolve this fully to .mjs". + // Normally should be paired with --out-file-extension in the cli + "outFileExtension": "mjs", + }, +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/input/packages/c/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/input/packages/c/src/index.ts new file mode 100644 index 000000000000..b4bf6ca8eba2 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/input/packages/c/src/index.ts @@ -0,0 +1,6 @@ +// Simulate accessing a .js file in a third party package that shouldn't be edited +import something from 'lodash/dist/something.js' +export function displayC(): string { + something() + return 'Display C' +} diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/input/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/input/src/index.ts new file mode 100644 index 000000000000..a0f4ab81010f --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/input/src/index.ts @@ -0,0 +1,14 @@ + +import { displayB } from './inner/b' +import { displayC } from '@print/c' +import { merge } from 'lodash' + +async function display() { + const displayA = await import('./inner/a').then(c => c.displayA) + console.log(displayA()) + console.log(displayB()) + console.log(displayC()) + const foo = merge({}, { a: 22 }) +} + +display() \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/input/src/inner/a/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/input/src/inner/a/index.ts new file mode 100644 index 000000000000..4bdca77ee750 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/input/src/inner/a/index.ts @@ -0,0 +1,3 @@ +export function displayA() { + return 'Display A' +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/input/src/inner/b/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/input/src/inner/b/index.ts new file mode 100644 index 000000000000..c6b2dc8e918e --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/input/src/inner/b/index.ts @@ -0,0 +1,3 @@ +export function displayB() { + return 'Display B' +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/output/packages/c/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/output/packages/c/src/index.ts new file mode 100644 index 000000000000..25ebc7ba3ba1 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/output/packages/c/src/index.ts @@ -0,0 +1,17 @@ +// Simulate accessing a .js file in a third party package that shouldn't be edited +"use strict"; +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "displayC", { + enumerable: true, + get: function() { + return displayC; + } +}); +const _interop_require_default = require("@swc/helpers/_/_interop_require_default"); +const _something = /*#__PURE__*/ _interop_require_default._(require("lodash/dist/something.js")); +function displayC() { + (0, _something.default)(); + return 'Display C'; +} diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/output/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/output/src/index.ts new file mode 100644 index 000000000000..8b0a5abc2145 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/output/src/index.ts @@ -0,0 +1,18 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { + value: true +}); +const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard"); +const _b = require("./inner/b/index.mjs"); +const _c = require("../packages/c/src/index.mjs"); +const _lodash = require("lodash"); +async function display() { + const displayA = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard._(require("./inner/a/index.mjs"))).then((c)=>c.displayA); + console.log(displayA()); + console.log((0, _b.displayB)()); + console.log((0, _c.displayC)()); + const foo = (0, _lodash.merge)({}, { + a: 22 + }); +} +display(); diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/output/src/inner/a/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/output/src/inner/a/index.ts new file mode 100644 index 000000000000..8537839f2cf5 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/output/src/inner/a/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-3xxx/3067/commonjs/output/src/inner/b/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/output/src/inner/b/index.ts new file mode 100644 index 000000000000..1bc1e9d062a4 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/commonjs/output/src/inner/b/index.ts @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "displayB", { + enumerable: true, + get: function() { + return displayB; + } +}); +function displayB() { + return 'Display B'; +} diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/es6/input/.swcrc b/crates/swc/tests/fixture/issues-3xxx/3067/es6/input/.swcrc new file mode 100644 index 000000000000..2b7ed2670853 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/es6/input/.swcrc @@ -0,0 +1,21 @@ +{ + "jsc": { + "parser": { + "syntax": "typescript", + "dynamicImport": true, + }, + "target": "es2020", + "baseUrl": ".", + "paths": { + "@print/c": ["./packages/c/src/index.js"], + }, + "externalHelpers": true, + }, + "module": { + "type": "es6", + "resolveFully": true, + // This should say "resolve this fully to .mjs". + // Normally should be paired with --out-file-extension in the cli + "outFileExtension": "mjs", + }, +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/es6/input/packages/c/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/es6/input/packages/c/src/index.ts new file mode 100644 index 000000000000..b4bf6ca8eba2 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/es6/input/packages/c/src/index.ts @@ -0,0 +1,6 @@ +// Simulate accessing a .js file in a third party package that shouldn't be edited +import something from 'lodash/dist/something.js' +export function displayC(): string { + something() + return 'Display C' +} diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/es6/input/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/es6/input/src/index.ts new file mode 100644 index 000000000000..a0f4ab81010f --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/es6/input/src/index.ts @@ -0,0 +1,14 @@ + +import { displayB } from './inner/b' +import { displayC } from '@print/c' +import { merge } from 'lodash' + +async function display() { + const displayA = await import('./inner/a').then(c => c.displayA) + console.log(displayA()) + console.log(displayB()) + console.log(displayC()) + const foo = merge({}, { a: 22 }) +} + +display() \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/es6/input/src/inner/a/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/es6/input/src/inner/a/index.ts new file mode 100644 index 000000000000..4bdca77ee750 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/es6/input/src/inner/a/index.ts @@ -0,0 +1,3 @@ +export function displayA() { + return 'Display A' +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/es6/input/src/inner/b/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/es6/input/src/inner/b/index.ts new file mode 100644 index 000000000000..c6b2dc8e918e --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/es6/input/src/inner/b/index.ts @@ -0,0 +1,3 @@ +export function displayB() { + return 'Display B' +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/es6/output/packages/c/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/es6/output/packages/c/src/index.ts new file mode 100644 index 000000000000..f606bd655eba --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/es6/output/packages/c/src/index.ts @@ -0,0 +1,6 @@ +// Simulate accessing a .js file in a third party package that shouldn't be edited +import something from "lodash/dist/something.js"; +export function displayC() { + something(); + return 'Display C'; +} diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/es6/output/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/es6/output/src/index.ts new file mode 100644 index 000000000000..dff2335e64f8 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/es6/output/src/index.ts @@ -0,0 +1,13 @@ +import { displayB } from "./inner/b/index.mjs"; +import { displayC } from "../packages/c/src/index.mjs"; +import { merge } from "lodash"; +async function display() { + const displayA = await import("./inner/a/index.mjs").then((c)=>c.displayA); + console.log(displayA()); + console.log(displayB()); + console.log(displayC()); + const foo = merge({}, { + a: 22 + }); +} +display(); diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/es6/output/src/inner/a/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/es6/output/src/inner/a/index.ts new file mode 100644 index 000000000000..16c71081ca83 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/es6/output/src/inner/a/index.ts @@ -0,0 +1,3 @@ +export function displayA() { + return 'Display A'; +} diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/es6/output/src/inner/b/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/es6/output/src/inner/b/index.ts new file mode 100644 index 000000000000..c539a7e7e8f0 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/es6/output/src/inner/b/index.ts @@ -0,0 +1,3 @@ +export function displayB() { + return 'Display B'; +} diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/input/.swcrc b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/input/.swcrc new file mode 100644 index 000000000000..fc792eb3449b --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/input/.swcrc @@ -0,0 +1,21 @@ +{ + "jsc": { + "parser": { + "syntax": "typescript", + "dynamicImport": true, + }, + "target": "es2020", + "baseUrl": ".", + "paths": { + "@print/c": ["./packages/c/src/index.js"], + }, + "externalHelpers": true, + }, + "module": { + "type": "nodenext", + "resolveFully": true, + // This should say "resolve this fully to .mjs". + // Normally should be paired with --out-file-extension in the cli + "outFileExtension": "mjs", + }, +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/input/packages/c/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/input/packages/c/src/index.ts new file mode 100644 index 000000000000..b4bf6ca8eba2 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/input/packages/c/src/index.ts @@ -0,0 +1,6 @@ +// Simulate accessing a .js file in a third party package that shouldn't be edited +import something from 'lodash/dist/something.js' +export function displayC(): string { + something() + return 'Display C' +} diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/input/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/input/src/index.ts new file mode 100644 index 000000000000..a0f4ab81010f --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/input/src/index.ts @@ -0,0 +1,14 @@ + +import { displayB } from './inner/b' +import { displayC } from '@print/c' +import { merge } from 'lodash' + +async function display() { + const displayA = await import('./inner/a').then(c => c.displayA) + console.log(displayA()) + console.log(displayB()) + console.log(displayC()) + const foo = merge({}, { a: 22 }) +} + +display() \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/input/src/inner/a/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/input/src/inner/a/index.ts new file mode 100644 index 000000000000..4bdca77ee750 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/input/src/inner/a/index.ts @@ -0,0 +1,3 @@ +export function displayA() { + return 'Display A' +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/input/src/inner/b/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/input/src/inner/b/index.ts new file mode 100644 index 000000000000..c6b2dc8e918e --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/input/src/inner/b/index.ts @@ -0,0 +1,3 @@ +export function displayB() { + return 'Display B' +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/output/packages/c/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/output/packages/c/src/index.ts new file mode 100644 index 000000000000..f606bd655eba --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/output/packages/c/src/index.ts @@ -0,0 +1,6 @@ +// Simulate accessing a .js file in a third party package that shouldn't be edited +import something from "lodash/dist/something.js"; +export function displayC() { + something(); + return 'Display C'; +} diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/output/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/output/src/index.ts new file mode 100644 index 000000000000..dff2335e64f8 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/output/src/index.ts @@ -0,0 +1,13 @@ +import { displayB } from "./inner/b/index.mjs"; +import { displayC } from "../packages/c/src/index.mjs"; +import { merge } from "lodash"; +async function display() { + const displayA = await import("./inner/a/index.mjs").then((c)=>c.displayA); + console.log(displayA()); + console.log(displayB()); + console.log(displayC()); + const foo = merge({}, { + a: 22 + }); +} +display(); diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/output/src/inner/a/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/output/src/inner/a/index.ts new file mode 100644 index 000000000000..16c71081ca83 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/output/src/inner/a/index.ts @@ -0,0 +1,3 @@ +export function displayA() { + return 'Display A'; +} diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/output/src/inner/b/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/output/src/inner/b/index.ts new file mode 100644 index 000000000000..c539a7e7e8f0 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/nodenext/output/src/inner/b/index.ts @@ -0,0 +1,3 @@ +export function displayB() { + return 'Display B'; +} diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/input/.swcrc b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/input/.swcrc new file mode 100644 index 000000000000..72a82b39aa38 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/input/.swcrc @@ -0,0 +1,21 @@ +{ + "jsc": { + "parser": { + "syntax": "typescript", + "dynamicImport": true, + }, + "target": "es2020", + "baseUrl": ".", + "paths": { + "@print/c": ["./packages/c/src/index.js"], + }, + "externalHelpers": true, + }, + "module": { + "type": "systemjs", + "resolveFully": true, + // This should say "resolve this fully to .mjs". + // Normally should be paired with --out-file-extension in the cli + "outFileExtension": "mjs", + }, +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/input/packages/c/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/input/packages/c/src/index.ts new file mode 100644 index 000000000000..b4bf6ca8eba2 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/input/packages/c/src/index.ts @@ -0,0 +1,6 @@ +// Simulate accessing a .js file in a third party package that shouldn't be edited +import something from 'lodash/dist/something.js' +export function displayC(): string { + something() + return 'Display C' +} diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/input/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/input/src/index.ts new file mode 100644 index 000000000000..a0f4ab81010f --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/input/src/index.ts @@ -0,0 +1,14 @@ + +import { displayB } from './inner/b' +import { displayC } from '@print/c' +import { merge } from 'lodash' + +async function display() { + const displayA = await import('./inner/a').then(c => c.displayA) + console.log(displayA()) + console.log(displayB()) + console.log(displayC()) + const foo = merge({}, { a: 22 }) +} + +display() \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/input/src/inner/a/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/input/src/inner/a/index.ts new file mode 100644 index 000000000000..4bdca77ee750 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/input/src/inner/a/index.ts @@ -0,0 +1,3 @@ +export function displayA() { + return 'Display A' +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/input/src/inner/b/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/input/src/inner/b/index.ts new file mode 100644 index 000000000000..c6b2dc8e918e --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/input/src/inner/b/index.ts @@ -0,0 +1,3 @@ +export function displayB() { + return 'Display B' +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/output/packages/c/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/output/packages/c/src/index.ts new file mode 100644 index 000000000000..5fd339a641fd --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/output/packages/c/src/index.ts @@ -0,0 +1,20 @@ +// Simulate accessing a .js file in a third party package that shouldn't be edited +System.register([ + "lodash/dist/something.js" +], function(_export, _context) { + "use strict"; + var something; + function displayC() { + something(); + return 'Display C'; + } + _export("displayC", displayC); + return { + setters: [ + function(_something) { + something = _something.default; + } + ], + execute: function() {} + }; +}); diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/output/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/output/src/index.ts new file mode 100644 index 000000000000..1129bce1defe --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/output/src/index.ts @@ -0,0 +1,33 @@ +System.register([ + "./inner/b/index.mjs", + "../packages/c/src/index.mjs", + "lodash" +], function(_export, _context) { + "use strict"; + var displayB, displayC, merge; + async function display() { + const displayA = await _context.import('./inner/a').then((c)=>c.displayA); + console.log(displayA()); + console.log(displayB()); + console.log(displayC()); + const foo = merge({}, { + a: 22 + }); + } + return { + setters: [ + function(_index) { + displayB = _index.displayB; + }, + function(_index) { + displayC = _index.displayC; + }, + function(_lodash) { + merge = _lodash.merge; + } + ], + execute: function() { + display(); + } + }; +}); diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/output/src/inner/a/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/output/src/inner/a/index.ts new file mode 100644 index 000000000000..bb95691cf409 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/output/src/inner/a/index.ts @@ -0,0 +1,11 @@ +System.register([], function(_export, _context) { + "use strict"; + function displayA() { + return 'Display A'; + } + _export("displayA", displayA); + return { + setters: [], + execute: function() {} + }; +}); diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/output/src/inner/b/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/output/src/inner/b/index.ts new file mode 100644 index 000000000000..7ce2db60fe23 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/systemjs/output/src/inner/b/index.ts @@ -0,0 +1,11 @@ +System.register([], function(_export, _context) { + "use strict"; + function displayB() { + return 'Display B'; + } + _export("displayB", displayB); + return { + setters: [], + execute: function() {} + }; +}); diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/umd/input/.swcrc b/crates/swc/tests/fixture/issues-3xxx/3067/umd/input/.swcrc new file mode 100644 index 000000000000..c6108d179f95 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/umd/input/.swcrc @@ -0,0 +1,21 @@ +{ + "jsc": { + "parser": { + "syntax": "typescript", + "dynamicImport": true, + }, + "target": "es2020", + "baseUrl": ".", + "paths": { + "@print/c": ["./packages/c/src/index.js"], + }, + "externalHelpers": true, + }, + "module": { + "type": "umd", + "resolveFully": true, + // This should say "resolve this fully to .mjs". + // Normally should be paired with --out-file-extension in the cli + "outFileExtension": "mjs", + }, +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/umd/input/packages/c/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/umd/input/packages/c/src/index.ts new file mode 100644 index 000000000000..b4bf6ca8eba2 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/umd/input/packages/c/src/index.ts @@ -0,0 +1,6 @@ +// Simulate accessing a .js file in a third party package that shouldn't be edited +import something from 'lodash/dist/something.js' +export function displayC(): string { + something() + return 'Display C' +} diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/umd/input/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/umd/input/src/index.ts new file mode 100644 index 000000000000..a0f4ab81010f --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/umd/input/src/index.ts @@ -0,0 +1,14 @@ + +import { displayB } from './inner/b' +import { displayC } from '@print/c' +import { merge } from 'lodash' + +async function display() { + const displayA = await import('./inner/a').then(c => c.displayA) + console.log(displayA()) + console.log(displayB()) + console.log(displayC()) + const foo = merge({}, { a: 22 }) +} + +display() \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/umd/input/src/inner/a/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/umd/input/src/inner/a/index.ts new file mode 100644 index 000000000000..4bdca77ee750 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/umd/input/src/inner/a/index.ts @@ -0,0 +1,3 @@ +export function displayA() { + return 'Display A' +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/umd/input/src/inner/b/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/umd/input/src/inner/b/index.ts new file mode 100644 index 000000000000..c6b2dc8e918e --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/umd/input/src/inner/b/index.ts @@ -0,0 +1,3 @@ +export function displayB() { + return 'Display B' +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/umd/output/packages/c/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/umd/output/packages/c/src/index.ts new file mode 100644 index 000000000000..a97d841d1cac --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/umd/output/packages/c/src/index.ts @@ -0,0 +1,26 @@ +// Simulate accessing a .js file in a third party package that shouldn't be edited +(function(global, factory) { + if (typeof module === "object" && typeof module.exports === "object") factory(exports, require("@swc/helpers/_/_interop_require_default"), require("lodash/dist/something.js")); + else if (typeof define === "function" && define.amd) define([ + "exports", + "@swc/helpers/_/_interop_require_default", + "lodash/dist/something.js" + ], factory); + else if (global = typeof globalThis !== "undefined" ? globalThis : global || self) factory(global.index = {}, global.interopRequireDefault, global.somethingJs); +})(this, function(exports, _interop_require_default, _something) { + "use strict"; + Object.defineProperty(exports, "__esModule", { + value: true + }); + Object.defineProperty(exports, "displayC", { + enumerable: true, + get: function() { + return displayC; + } + }); + _something = /*#__PURE__*/ _interop_require_default._(_something); + function displayC() { + (0, _something.default)(); + return 'Display C'; + } +}); diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/umd/output/src/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/umd/output/src/index.ts new file mode 100644 index 000000000000..8b6f3e87dc36 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/umd/output/src/index.ts @@ -0,0 +1,26 @@ +(function(global, factory) { + if (typeof module === "object" && typeof module.exports === "object") factory(exports, require("@swc/helpers/_/_interop_require_wildcard"), require("./inner/b/index.mjs"), require("../packages/c/src/index.mjs"), require("lodash")); + else if (typeof define === "function" && define.amd) define([ + "exports", + "@swc/helpers/_/_interop_require_wildcard", + "./inner/b/index.mjs", + "../packages/c/src/index.mjs", + "lodash" + ], factory); + else if (global = typeof globalThis !== "undefined" ? globalThis : global || self) factory(global.index = {}, global.interopRequireWildcard, global.indexMjs, global.indexMjs, global.lodash); +})(this, function(exports, _interop_require_wildcard, _b, _c, _lodash) { + "use strict"; + Object.defineProperty(exports, "__esModule", { + value: true + }); + async function display() { + const displayA = await import('./inner/a').then((c)=>c.displayA); + console.log(displayA()); + console.log((0, _b.displayB)()); + console.log((0, _c.displayC)()); + const foo = (0, _lodash.merge)({}, { + a: 22 + }); + } + display(); +}); diff --git a/crates/swc/tests/fixture/issues-3xxx/3067/umd/output/src/inner/a/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/umd/output/src/inner/a/index.ts new file mode 100644 index 000000000000..241ebf18040d --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/umd/output/src/inner/a/index.ts @@ -0,0 +1,21 @@ +(function(global, factory) { + if (typeof module === "object" && typeof module.exports === "object") factory(exports); + else if (typeof define === "function" && define.amd) define([ + "exports" + ], factory); + else if (global = typeof globalThis !== "undefined" ? globalThis : global || self) factory(global.index = {}); +})(this, function(exports) { + "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-3xxx/3067/umd/output/src/inner/b/index.ts b/crates/swc/tests/fixture/issues-3xxx/3067/umd/output/src/inner/b/index.ts new file mode 100644 index 000000000000..cfc3f5e2fa70 --- /dev/null +++ b/crates/swc/tests/fixture/issues-3xxx/3067/umd/output/src/inner/b/index.ts @@ -0,0 +1,21 @@ +(function(global, factory) { + if (typeof module === "object" && typeof module.exports === "object") factory(exports); + else if (typeof define === "function" && define.amd) define([ + "exports" + ], factory); + else if (global = typeof globalThis !== "undefined" ? globalThis : global || self) factory(global.index = {}); +})(this, function(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { + value: true + }); + Object.defineProperty(exports, "displayB", { + enumerable: true, + get: function() { + return displayB; + } + }); + function displayB() { + return 'Display B'; + } +}); diff --git a/crates/swc/tests/fixture/jsc-paths/vercel-site/1/input/.swcrc b/crates/swc/tests/fixture/jsc-paths/vercel-site/1/input/.swcrc index 39e4875e133d..a8a69cf5d7e7 100644 --- a/crates/swc/tests/fixture/jsc-paths/vercel-site/1/input/.swcrc +++ b/crates/swc/tests/fixture/jsc-paths/vercel-site/1/input/.swcrc @@ -9,5 +9,10 @@ "./*" ] } + }, + "module": { + "type": "es6", + "resolveFully": true, + "outFileExtension": "mjs" } } \ No newline at end of file diff --git a/crates/swc_ecma_transforms_module/src/lib.rs b/crates/swc_ecma_transforms_module/src/lib.rs index 2e377265b135..714d652e7cb3 100644 --- a/crates/swc_ecma_transforms_module/src/lib.rs +++ b/crates/swc_ecma_transforms_module/src/lib.rs @@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize}; use swc_common::{Span, SyntaxContext}; +use util::Config; pub use self::{amd::amd, common_js::common_js, system_js::system_js, umd::umd}; @@ -24,8 +25,8 @@ pub mod umd; #[derive(Debug, Default, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct EsModuleConfig { - #[serde(default)] - pub resolve_fully: bool, + #[serde(flatten, default)] + pub config: Config, } type SpanCtx = (Span, SyntaxContext); diff --git a/crates/swc_ecma_transforms_module/src/path.rs b/crates/swc_ecma_transforms_module/src/path.rs index eca3d29d62c4..df65484b5c36 100644 --- a/crates/swc_ecma_transforms_module/src/path.rs +++ b/crates/swc_ecma_transforms_module/src/path.rs @@ -96,10 +96,21 @@ where config: Config, } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct Config { pub base_dir: Option, pub resolve_fully: bool, + pub file_extension: String, +} + +impl Default for Config { + fn default() -> Config { + Config { + file_extension: crate::util::Config::default_js_ext(), + resolve_fully: bool::default(), + base_dir: Option::default(), + } + } } impl NodeImportResolver @@ -162,13 +173,13 @@ where }; let is_resolved_as_non_js = if let Some(ext) = target_path.extension() { - ext != "js" + ext.to_string_lossy() != self.config.file_extension } else { false }; let is_resolved_as_js = if let Some(ext) = target_path.extension() { - ext == "js" + ext.to_string_lossy() == self.config.file_extension } else { false }; @@ -191,27 +202,30 @@ where // Resolved: `./foo/index.js` if self.config.resolve_fully { - target_path.set_file_name("index.js"); + target_path.set_file_name(format!("index.{}", self.config.file_extension)); } else { target_path.set_file_name("index"); } - } else if is_resolved_as_index && is_resolved_as_js && orig_filename != "index.js" { + } else if is_resolved_as_index + && is_resolved_as_js + && orig_filename != format!("index.{}", self.config.file_extension) + { // Import: `./foo` // Resolved: `./foo/index.js` target_path.pop(); } else if is_resolved_as_non_js && self.config.resolve_fully && file_stem_matches { - target_path.set_extension("js"); + target_path.set_extension(self.config.file_extension.clone()); } else if !is_resolved_as_js && !is_resolved_as_index && !is_exact { target_path.set_file_name(orig_filename); } 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"); + target_path.set_extension(self.config.file_extension.clone()); } } else if self.config.resolve_fully && is_resolved_as_non_js { - target_path.set_extension("js"); + target_path.set_extension(self.config.file_extension.clone()); } else if is_resolved_as_non_js && is_resolved_as_index { if orig_filename == "index" { target_path.set_extension(""); diff --git a/crates/swc_ecma_transforms_module/src/system_js.rs b/crates/swc_ecma_transforms_module/src/system_js.rs index 526f0a50462b..c160e77525d9 100644 --- a/crates/swc_ecma_transforms_module/src/system_js.rs +++ b/crates/swc_ecma_transforms_module/src/system_js.rs @@ -8,6 +8,7 @@ use swc_ecma_utils::{ }; use swc_ecma_visit::{fold_pass, standard_only_fold, Fold, FoldWith, VisitWith}; +pub use super::util::Config as InnerConfig; use crate::{ path::Resolver, top_level_this::top_level_this, @@ -19,8 +20,8 @@ pub struct Config { #[serde(default)] pub allow_top_level_this: bool, - #[serde(default)] - pub resolve_fully: bool, + #[serde(flatten, default)] + pub config: InnerConfig, } struct SystemJs { diff --git a/crates/swc_ecma_transforms_module/src/util.rs b/crates/swc_ecma_transforms_module/src/util.rs index cc82cf48b6b2..062bd0faaac7 100644 --- a/crates/swc_ecma_transforms_module/src/util.rs +++ b/crates/swc_ecma_transforms_module/src/util.rs @@ -46,6 +46,15 @@ pub struct Config { #[serde(default)] pub resolve_fully: bool, + + #[serde(default = "Config::default_js_ext")] + pub out_file_extension: String, +} + +impl Config { + pub fn default_js_ext() -> String { + "js".to_string() + } } impl Default for Config { @@ -61,6 +70,7 @@ impl Default for Config { ignore_dynamic: false, preserve_import_meta: false, resolve_fully: false, + out_file_extension: "js".to_string(), } } } diff --git a/crates/swc_ecma_transforms_module/tests/path_node.rs b/crates/swc_ecma_transforms_module/tests/path_node.rs index a80a1d43a149..28582ddb95bb 100644 --- a/crates/swc_ecma_transforms_module/tests/path_node.rs +++ b/crates/swc_ecma_transforms_module/tests/path_node.rs @@ -106,6 +106,7 @@ fn paths_resolver(base_dir: &Path, rules: Vec<(String, Vec)>) -> JscPath swc_ecma_transforms_module::path::Config { base_dir: Some(base_dir), resolve_fully: true, + file_extension: swc_ecma_transforms_module::util::Config::default_js_ext(), }, ) } diff --git a/packages/core/__tests__/transform/issue_3067_test.mjs b/packages/core/__tests__/transform/issue_3067_test.mjs new file mode 100644 index 000000000000..0328516ac878 --- /dev/null +++ b/packages/core/__tests__/transform/issue_3067_test.mjs @@ -0,0 +1,294 @@ +import swc from "../.."; +import { dirname, join, resolve } from "path"; +import { platform } from "os"; +import { fileURLToPath } from "url"; +import { writeFileSync } from "fs"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +it("should work with outFileExtension (commonjs)", async () => { + if (process.platform === "win32") { + expect(true).toBeTruthy(); + return; + } + + const dir = join(__dirname, "..", "..", "tests", "issue-3067"); + const filename = join(dir, "src", "index.ts"); + console.log(filename); + const { code } = await swc.transformFile(filename, { + jsc: { + parser: { + syntax: "typescript", + dynamicImport: true, + }, + target: "es2020", + baseUrl: resolve("."), + paths: { + "@print/c": [join(dir, "./packages/c/src/index.js")], + }, + externalHelpers: true, + }, + module: { + type: "commonjs", + resolveFully: true, + outFileExtension: "mjs", + }, + }); + expect(code).toMatchInlineSnapshot(` + ""use strict"; + Object.defineProperty(exports, "__esModule", { + value: true + }); + const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard"); + const _b = require("./inner/b/index.mjs"); + const _c = require("../packages/c/src/index.mjs"); + const _lodash = require("lodash"); + async function display() { + const displayA = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard._(require("./inner/a/index.mjs"))).then((c)=>c.displayA); + console.log(displayA()); + console.log((0, _b.displayB)()); + console.log((0, _c.displayC)()); + const foo = (0, _lodash.merge)({}, { + a: 22 + }); + } + display(); + " + `); +}); + +// ESM Types +it.each([ + ['es6'], + ['nodenext'] +])("should work with outFileExtension (%s)", async (type) => { + if (process.platform === "win32") { + expect(true).toBeTruthy(); + return; + } + + const dir = join(__dirname, "..", "..", "tests", "issue-3067"); + const filename = join(dir, "src", "index.ts"); + console.log(filename); + const { code } = await swc.transformFile(filename, { + jsc: { + parser: { + syntax: "typescript", + dynamicImport: true, + }, + target: "es2020", + baseUrl: resolve("."), + paths: { + "@print/c": [join(dir, "./packages/c/src/index.js")], + }, + externalHelpers: true, + }, + module: { + type, + resolveFully: true, + outFileExtension: "mjs", + }, + }); + expect(code).toMatchInlineSnapshot(` + "import { displayB } from "./inner/b/index.mjs"; + import { displayC } from "../packages/c/src/index.mjs"; + import { merge } from "lodash"; + async function display() { + const displayA = await import("./inner/a/index.mjs").then((c)=>c.displayA); + console.log(displayA()); + console.log(displayB()); + console.log(displayC()); + const foo = merge({}, { + a: 22 + }); + } + display(); + " + `); +}); + +it("should work with outFileExtension (umd)", async () => { + if (process.platform === "win32") { + expect(true).toBeTruthy(); + return; + } + + const dir = join(__dirname, "..", "..", "tests", "issue-3067"); + const filename = join(dir, "src", "index.ts"); + console.log(filename); + const { code } = await swc.transformFile(filename, { + jsc: { + parser: { + syntax: "typescript", + dynamicImport: true, + }, + target: "es2020", + baseUrl: resolve("."), + paths: { + "@print/c": [join(dir, "./packages/c/src/index.js")], + }, + externalHelpers: true, + }, + module: { + type: "umd", + resolveFully: true, + outFileExtension: "mjs", + }, + }); + // TODO: it seems like dynamic import does not do a full resolve - this might be a fix for a different PR + expect(code).toMatchInlineSnapshot(` +"(function(global, factory) { + if (typeof module === "object" && typeof module.exports === "object") factory(exports, require("@swc/helpers/_/_interop_require_wildcard"), require("./inner/b/index.mjs"), require("../packages/c/src/index.mjs"), require("lodash")); + else if (typeof define === "function" && define.amd) define([ + "exports", + "@swc/helpers/_/_interop_require_wildcard", + "./inner/b/index.mjs", + "../packages/c/src/index.mjs", + "lodash" + ], factory); + else if (global = typeof globalThis !== "undefined" ? globalThis : global || self) factory(global.index = {}, global.interopRequireWildcard, global.indexMjs, global.indexMjs, global.lodash); +})(this, function(exports, _interop_require_wildcard, _b, _c, _lodash) { + "use strict"; + Object.defineProperty(exports, "__esModule", { + value: true + }); + async function display() { + const displayA = await import('./inner/a').then((c)=>c.displayA); + console.log(displayA()); + console.log((0, _b.displayB)()); + console.log((0, _c.displayC)()); + const foo = (0, _lodash.merge)({}, { + a: 22 + }); + } + display(); +}); +" +`); +}); + +it("should work with outFileExtension (amd)", async () => { + if (process.platform === "win32") { + expect(true).toBeTruthy(); + return; + } + + const dir = join(__dirname, "..", "..", "tests", "issue-3067"); + const filename = join(dir, "src", "index.ts"); + console.log(filename); + const { code } = await swc.transformFile(filename, { + jsc: { + parser: { + syntax: "typescript", + dynamicImport: true, + }, + target: "es2020", + baseUrl: resolve("."), + paths: { + "@print/c": [join(dir, "./packages/c/src/index.js")], + }, + externalHelpers: true, + }, + module: { + type: "amd", + resolveFully: true, + outFileExtension: "mjs", + }, + }); + expect(code).toMatchInlineSnapshot(` +"define([ + "require", + "exports", + "@swc/helpers/_/_interop_require_wildcard", + "./inner/b/index.mjs", + "../packages/c/src/index.mjs", + "lodash" +], function(require, exports, _interop_require_wildcard, _b, _c, _lodash) { + "use strict"; + Object.defineProperty(exports, "__esModule", { + value: true + }); + async function display() { + const displayA = await new Promise((resolve, reject)=>require([ + "./inner/a/index.mjs" + ], (m)=>resolve(/*#__PURE__*/ _interop_require_wildcard._(m)), reject)).then((c)=>c.displayA); + console.log(displayA()); + console.log((0, _b.displayB)()); + console.log((0, _c.displayC)()); + const foo = (0, _lodash.merge)({}, { + a: 22 + }); + } + display(); +}); +" +`); +}); + +it("should work with outFileExtension (systemjs)", async () => { + if (process.platform === "win32") { + expect(true).toBeTruthy(); + return; + } + + const dir = join(__dirname, "..", "..", "tests", "issue-3067"); + const filename = join(dir, "src", "index.ts"); + console.log(filename); + const { code } = await swc.transformFile(filename, { + jsc: { + parser: { + syntax: "typescript", + dynamicImport: true, + }, + target: "es2020", + baseUrl: resolve("."), + paths: { + "@print/c": [join(dir, "./packages/c/src/index.js")], + }, + externalHelpers: true, + }, + module: { + type: "systemjs", + resolveFully: true, + outFileExtension: "mjs", + }, + }); + writeFileSync('foo2.txt', code) + // TODO: it seems like dynamic import does not do a full resolve - this might be a fix for a different PR + expect(code).toMatchInlineSnapshot(` +"System.register([ + "./inner/b/index.mjs", + "../packages/c/src/index.mjs", + "lodash" +], function(_export, _context) { + "use strict"; + var displayB, displayC, merge; + async function display() { + const displayA = await _context.import('./inner/a').then((c)=>c.displayA); + console.log(displayA()); + console.log(displayB()); + console.log(displayC()); + const foo = merge({}, { + a: 22 + }); + } + return { + setters: [ + function(_index) { + displayB = _index.displayB; + }, + function(_index) { + displayC = _index.displayC; + }, + function(_lodash) { + merge = _lodash.merge; + } + ], + execute: function() { + display(); + } + }; +}); +" +`); +}); \ No newline at end of file diff --git a/packages/core/jest.config.js b/packages/core/jest.config.js index c26711a55ad1..02312bcfdc7d 100644 --- a/packages/core/jest.config.js +++ b/packages/core/jest.config.js @@ -25,6 +25,7 @@ module.exports = { : { displayName: "e2e tests", testMatch: ["**/e2e/**/?(*.)+(spec|test).[jt]s?(x)"], + moduleFileExtensions: ["js", "jsx", "mjs"], }, ].filter(Boolean), }; diff --git a/packages/core/package.json b/packages/core/package.json index ab3bed1aaece..c09cb9de8fa9 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -54,7 +54,7 @@ "build:wasm": "npm-run-all \"pack -- build ../../bindings/binding_core_wasm --scope swc {1} -t {2} --features plugin\" --", "build": "tsc -d && napi build --manifest-path ../../bindings/Cargo.toml --platform -p binding_core_node --js ./binding.js --dts ./binding.d.ts --release -o .", "build:dev": "tsc -d && napi build --manifest-path ../../bindings/Cargo.toml --platform -p binding_core_node --js ./binding.js --dts ./binding.d.ts -o .", - "test": "cross-env NODE_OPTIONS='--experimental-vm-modules' jest --config ./jest.config.js", + "test": "cross-env NODE_OPTIONS='--experimental-vm-modules ${NODE_OPTIONS}' jest --config ./jest.config.js", "version": "napi version --npm-dir scripts/npm" }, "peerDependencies": { diff --git a/packages/core/tests/issue-3067/packages/c/src/index.ts b/packages/core/tests/issue-3067/packages/c/src/index.ts new file mode 100644 index 000000000000..69c6583a8bfc --- /dev/null +++ b/packages/core/tests/issue-3067/packages/c/src/index.ts @@ -0,0 +1,3 @@ +export function displayC() { + return 'Display C' +} diff --git a/packages/core/tests/issue-3067/src/index.ts b/packages/core/tests/issue-3067/src/index.ts new file mode 100644 index 000000000000..a0f4ab81010f --- /dev/null +++ b/packages/core/tests/issue-3067/src/index.ts @@ -0,0 +1,14 @@ + +import { displayB } from './inner/b' +import { displayC } from '@print/c' +import { merge } from 'lodash' + +async function display() { + const displayA = await import('./inner/a').then(c => c.displayA) + console.log(displayA()) + console.log(displayB()) + console.log(displayC()) + const foo = merge({}, { a: 22 }) +} + +display() \ No newline at end of file diff --git a/packages/core/tests/issue-3067/src/inner/a/index.ts b/packages/core/tests/issue-3067/src/inner/a/index.ts new file mode 100644 index 000000000000..4bdca77ee750 --- /dev/null +++ b/packages/core/tests/issue-3067/src/inner/a/index.ts @@ -0,0 +1,3 @@ +export function displayA() { + return 'Display A' +} \ No newline at end of file diff --git a/packages/core/tests/issue-3067/src/inner/b/index.ts b/packages/core/tests/issue-3067/src/inner/b/index.ts new file mode 100644 index 000000000000..c6b2dc8e918e --- /dev/null +++ b/packages/core/tests/issue-3067/src/inner/b/index.ts @@ -0,0 +1,3 @@ +export function displayB() { + return 'Display B' +} \ No newline at end of file