Skip to content

fix(es/module): Preserve order for import foo = require("mod") #5215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 75 additions & 29 deletions crates/swc_ecma_transforms_module/src/common_js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,21 @@ where
fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
let import_interop = self.config.import_interop();

let mut module_map = Default::default();

let mut has_ts_import_equals = false;

// handle `import foo = require("mod")`
n.iter_mut().for_each(|item| {
if let ModuleItem::ModuleDecl(module_decl) = item {
*item = self.handle_ts_import_equals(
module_decl.take(),
&mut module_map,
&mut has_ts_import_equals,
);
}
});

let mut strip = ModuleDeclStrip::new(self.const_var_kind);
n.visit_mut_with(&mut strip);

Expand All @@ -111,20 +126,21 @@ where
..
} = strip;

let has_module_decl = has_module_decl || has_ts_import_equals;

let is_export_assign = export_assign.is_some();

if has_module_decl && !import_interop.is_none() && !is_export_assign {
stmts.push(define_es_module(self.exports()).into())
}

let mut import_map = Default::default();
let mut lazy_record = Default::default();

// `import` -> `require`
// `export` -> `_export(exports, {});`
stmts.extend(
self.handle_import_export(
&mut import_map,
&mut module_map,
&mut lazy_record,
link,
export,
Expand Down Expand Up @@ -157,7 +173,7 @@ where
}

stmts.visit_mut_children_with(&mut ModuleRefRewriter {
import_map,
import_map: module_map,
lazy_record,
allow_top_level_this: self.config.allow_top_level_this,
is_global_this: true,
Expand Down Expand Up @@ -268,17 +284,14 @@ where
}

let mod_ident = private_ident!(local_name_for_src(&src));
let raw_mod_ident = link_flag
.need_raw_import()
.then(|| private_ident!(local_name_for_src(&src)));

let mut decl_mod_ident = false;

link_specifier_set.reduce(
import_map,
&mut export_obj_prop_list,
&mod_ident,
&raw_mod_ident,
&None,
&mut decl_mod_ident,
is_swc_default_helper || is_node_default,
);
Expand All @@ -288,9 +301,6 @@ where

if is_lazy {
lazy_record.insert(mod_ident.to_id());
if let Some(raw_mod_ident) = &raw_mod_ident {
lazy_record.insert(raw_mod_ident.to_id());
}
}

// require("mod");
Expand All @@ -304,21 +314,6 @@ where
import_expr
};

let import_assign = raw_mod_ident.map(|raw_mod_ident| {
let import_expr = import_expr.clone();
if is_lazy {
Stmt::Decl(Decl::Fn(lazy_require(
import_expr,
raw_mod_ident,
self.const_var_kind,
)))
} else {
Stmt::Decl(Decl::Var(
import_expr.into_var_decl(self.const_var_kind, raw_mod_ident.into()),
))
}
});

// _exportStar(require("mod"), exports);
let import_expr = if link_flag.export_star() {
helper_expr!(export_star, "exportStar").as_call(
Expand Down Expand Up @@ -364,10 +359,6 @@ where
)
};

if let Some(import_assign) = import_assign {
stmts.push(import_assign);
}

stmts.push(stmt);
} else {
stmts.push(import_expr.into_stmt());
Expand All @@ -393,6 +384,61 @@ where
export_stmts.into_iter().chain(stmts)
}

fn handle_ts_import_equals(
&self,
module_decl: ModuleDecl,
module_map: &mut ImportMap,
has_ts_import_equals: &mut bool,
) -> ModuleItem {
if let ModuleDecl::TsImportEquals(TsImportEqualsDecl {
span,
declare: false,
is_export,
is_type_only: false,
id,
module_ref:
TsModuleRef::TsExternalModuleRef(TsExternalModuleRef {
expr:
Str {
span: src_span,
value: src,
..
},
..
}),
}) = module_decl
{
*has_ts_import_equals = true;

let require = self
.resolver
.make_require_call(self.unresolved_mark, src, src_span);

if is_export {
// exports.foo = require("mod")
module_map.insert(id.to_id(), (self.exports(), Some(id.sym.clone())));

let assign_expr = AssignExpr {
span,
op: op!("="),
left: id.as_pat_or_expr(),
right: Box::new(require),
};

assign_expr.into_stmt()
} else {
// const foo = require("mod")
let mut var_decl = require.into_var_decl(self.const_var_kind, id.into());
var_decl.span = span;

Stmt::Decl(var_decl.into())
}
.into()
} else {
module_decl.into()
}
}

fn exports(&self) -> Ident {
quote_ident!(DUMMY_SP.apply_mark(self.unresolved_mark), "exports")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"use strict";
const _foo = require("foo");
module.exports = _foo;
const foo = require("foo");
module.exports = foo;
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,4 @@
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "foo", {
enumerable: true,
get: ()=>_foo
});
const _foo = require("foo");
exports.foo = require("foo");
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Object.defineProperty(exports, "__esModule", {
value: true
});
const _foo = require("foo");
const _foo1 = /*#__PURE__*/ _interopRequireDefault(require("foo"));
(0, _foo1.default)();
_foo();
const _foo = /*#__PURE__*/ _interopRequireDefault(require("foo"));
const bar = require("foo");
(0, _foo.default)();
bar();
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import A = require("a"); // print a
console.log(1);
import B = require("b"); // print b
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
define([
"require",
"exports",
"a",
"b"
], function(require, exports, _a, _b) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
console.log(1);
});
// print b
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
const A = require("a"); // print a
console.log(1);
const B = require("b"); // print b
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
(function(global, factory) {
if (typeof module === "object" && typeof module.exports === "object") factory(exports, require("a"), require("b"));
else if (typeof define === "function" && define.amd) define([
"exports",
"a",
"b"
], factory);
else if (global = typeof globalThis !== "undefined" ? globalThis : global || self) factory(global.input = {}, global.a, global.b);
})(this, function(exports, _a, _b) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
console.log(1);
});
// print b
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
Object.defineProperty(exports, "__esModule", {
value: true
});
const _assert = require("assert");
_assert(true);
const assert = require("assert");
assert(true);
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Object.defineProperty(exports, "__esModule", {
value: true
});
const _assert = require("assert");
const _assert1 = /*#__PURE__*/ _interopRequireDefault(require("assert"));
_assert(true);
(0, _assert1.default)(true);
const _assert = /*#__PURE__*/ _interopRequireDefault(require("assert"));
const assert = require("assert");
assert(true);
(0, _assert.default)(true);
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,9 @@
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
"$": ()=>_jquery,
jquery: ()=>_jquery
Object.defineProperty(exports, "jquery", {
enumerable: true,
get: ()=>exports["$"]
});
const _jquery = require("jquery");
_jquery(".hello");
exports["$"] = require("jquery");
(0, exports["$"])(".hello");
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use strict";
const _foo = require("foo");
_foo.bar = 1;
_foo.bar = 2;
module.exports = _foo;
const foo = require("foo");
foo.bar = 1;
foo.bar = 2;
module.exports = foo;