Skip to content
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

Default interop missing when importing a CommonJS module #7991

Merged
merged 21 commits into from Jul 2, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6f3403b
Add test case for missing default interop for commonjs import
thebriando Apr 21, 2022
86ca04e
Add interopDefault helper if the dep kind is require
thebriando Apr 21, 2022
e01a71f
Add test case for 'static' hybrid interop
lettertwo Apr 22, 2022
b24c60a
Update test description
thebriando Apr 22, 2022
1234fa0
Update test dirname
thebriando Apr 22, 2022
7f61d93
Fix dirname
lettertwo Apr 22, 2022
6ab2b2e
Update packages/core/integration-tests/test/scope-hoisting.js
thebriando Apr 25, 2022
7bf63b3
Store import kinds in imported symbols and only set symbols when kind…
thebriando May 9, 2022
ac10384
Remove unnecessary conditions
thebriando May 12, 2022
30eea7b
Only set symbols for esm deps when iterating through hoist_result.re_…
thebriando May 13, 2022
0f586a7
Add comments for new dep Map structure
thebriando May 23, 2022
3cc8069
Key by specifier + specifierType instead of keeping an array of deps
thebriando May 27, 2022
49136f0
Make import specifiers unique in packager
thebriando Jun 5, 2022
daee349
Track specifier types in wrapped_requires
thebriando Jun 7, 2022
48e40ad
Don't append specifier types to placeholders, since they are already …
thebriando Jun 16, 2022
949d94f
Merge remote-tracking branch 'origin/v2' into bdo/default-interop
thebriando Jun 16, 2022
4c5e7fa
Fix hoist unit tests
thebriando Jun 16, 2022
1498254
Merge branch 'v2' into bdo/default-interop
thebriando Jun 16, 2022
1e5896d
Merge branch 'v2' into bdo/default-interop
thebriando Jun 22, 2022
18888ba
Merge branch 'v2' into bdo/default-interop
thebriando Jun 28, 2022
22a1c9b
Merge branch 'v2' into bdo/default-interop
thebriando Jul 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,5 @@
import b from './b';
export const foo = b.foo; // <-- missing default interop
export const bar = (() => require('./b').foo)();

output = foo + bar;
@@ -0,0 +1 @@
module.exports = {foo: 1};
@@ -0,0 +1,5 @@
import b from './b';
const foo = b.foo;
const bar = require('./b').foo;

output = foo + bar;
@@ -0,0 +1 @@
module.exports = {foo: 1};
24 changes: 24 additions & 0 deletions packages/core/integration-tests/test/scope-hoisting.js
Expand Up @@ -4359,6 +4359,30 @@ describe('scope hoisting', function () {
assert.deepEqual(output.default, obj);
});

it('should add a default interop for a CJS module used in a hybrid module', async function () {
let b = await bundle(
path.join(
__dirname,
'/integration/scope-hoisting/commonjs/interop-commonjs-hybrid/a.js',
),
);

let output = await run(b);
assert.equal(output, 2);
});

it('should add a default interop for a CJS module used non-statically in a hybrid module', async function () {
let b = await bundle(
path.join(
__dirname,
'/integration/scope-hoisting/commonjs/interop-commonjs-hybrid-dynamic/a.js',
),
);

let output = await run(b);
assert.equal(output, 2);
});

it('should not insert default interop for wrapped CJS modules', async function () {
let b = await bundle(
path.join(
Expand Down
30 changes: 26 additions & 4 deletions packages/transformers/js/core/src/hoist.rs
Expand Up @@ -51,6 +51,7 @@ struct ImportedSymbol {
local: JsWord,
imported: JsWord,
loc: SourceLocation,
kind: ImportKind,
}

struct Hoist<'a> {
Expand Down Expand Up @@ -208,6 +209,7 @@ impl<'a> Fold for Hoist<'a> {
local: exported,
imported: match_export_name(&named.orig).0,
loc: SourceLocation::from(&self.collect.source_map, named.span),
kind: ImportKind::Import,
});
}
ExportSpecifier::Default(default) => {
Expand All @@ -216,6 +218,7 @@ impl<'a> Fold for Hoist<'a> {
local: default.exported.sym,
imported: js_word!("default"),
loc: SourceLocation::from(&self.collect.source_map, default.exported.span),
kind: ImportKind::Import,
});
}
ExportSpecifier::Namespace(namespace) => {
Expand All @@ -224,6 +227,7 @@ impl<'a> Fold for Hoist<'a> {
local: match_export_name(&namespace.name).0,
imported: "*".into(),
loc: SourceLocation::from(&self.collect.source_map, namespace.span),
kind: ImportKind::Import,
});
}
}
Expand All @@ -237,14 +241,18 @@ impl<'a> Fold for Hoist<'a> {
None => match_export_name(&named.orig).0,
};
if let Some(Import {
source, specifier, ..
source,
specifier,
kind,
..
}) = self.collect.imports.get(&id)
{
self.re_exports.push(ImportedSymbol {
source: source.clone(),
local: exported,
imported: specifier.clone(),
loc: SourceLocation::from(&self.collect.source_map, named.span),
kind: *kind,
});
} else {
// A variable will appear only once in the `exports` mapping but
Expand Down Expand Up @@ -285,6 +293,7 @@ impl<'a> Fold for Hoist<'a> {
local: "*".into(),
imported: "*".into(),
loc: SourceLocation::from(&self.collect.source_map, export.span),
kind: ImportKind::Import,
});
}
ModuleDecl::ExportDefaultExpr(export) => {
Expand Down Expand Up @@ -562,13 +571,15 @@ impl<'a> Fold for Hoist<'a> {
local: name,
imported: key.clone(),
loc: SourceLocation::from(&self.collect.source_map, member.span),
kind: *kind,
});
} else {
return Expr::Ident(self.get_import_ident(
member.span,
source,
&key,
SourceLocation::from(&self.collect.source_map, member.span),
*kind,
));
}
}
Expand Down Expand Up @@ -596,6 +607,7 @@ impl<'a> Fold for Hoist<'a> {
&source,
&key,
SourceLocation::from(&self.collect.source_map, member.span),
ImportKind::Require,
));
}
}
Expand Down Expand Up @@ -639,6 +651,7 @@ impl<'a> Fold for Hoist<'a> {
&source,
&("*".into()),
SourceLocation::from(&self.collect.source_map, call.span),
ImportKind::Require,
));
}

Expand All @@ -652,6 +665,7 @@ impl<'a> Fold for Hoist<'a> {
local: name.clone(),
imported: "*".into(),
loc: SourceLocation::from(&self.collect.source_map, call.span),
kind: ImportKind::DynamicImport,
});
}
return Expr::Ident(Ident::new(name, call.span));
Expand Down Expand Up @@ -733,6 +747,7 @@ impl<'a> Fold for Hoist<'a> {
local: name,
imported: specifier.clone(),
loc: loc.clone(),
kind: *kind,
});
} else if self.collect.non_static_access.contains_key(&id!(node)) {
let name: JsWord =
Expand All @@ -742,6 +757,7 @@ impl<'a> Fold for Hoist<'a> {
local: name,
imported: "*".into(),
loc: loc.clone(),
kind: *kind,
});
}
} else {
Expand All @@ -754,7 +770,7 @@ impl<'a> Fold for Hoist<'a> {
return self.get_require_ident(&node.sym);
}

return self.get_import_ident(node.span, source, specifier, loc.clone());
return self.get_import_ident(node.span, source, specifier, loc.clone(), *kind);
}
}
}
Expand Down Expand Up @@ -960,13 +976,15 @@ impl<'a> Hoist<'a> {
source: &JsWord,
imported: &JsWord,
loc: SourceLocation,
kind: ImportKind,
) -> Ident {
let new_name = self.get_import_name(source, imported);
self.imported_symbols.push(ImportedSymbol {
source: source.clone(),
local: new_name.clone(),
imported: imported.clone(),
loc,
kind,
});
Ident::new(new_name, span)
}
Expand Down Expand Up @@ -1007,13 +1025,17 @@ impl<'a> Hoist<'a> {
.get_non_const_binding_idents(&v.name, &mut non_const_bindings);

for ident in non_const_bindings {
if let Some(Import { specifier, .. }) = self.collect.imports.get(&id!(ident)) {
if let Some(Import {
specifier, kind, ..
}) = self.collect.imports.get(&id!(ident))
{
let require_id = self.get_require_ident(&ident.sym);
let import_id = self.get_import_ident(
v.span,
source,
specifier,
SourceLocation::from(&self.collect.source_map, v.span),
*kind,
);
self
.module_items
Expand Down Expand Up @@ -1047,7 +1069,7 @@ macro_rules! collect_visit_fn {
};
}

#[derive(Debug, PartialEq, Clone, Copy, Serialize)]
#[derive(Debug, Deserialize, PartialEq, Clone, Copy, Serialize)]
pub enum ImportKind {
Require,
Import,
Expand Down
135 changes: 81 additions & 54 deletions packages/transformers/js/src/JSTransformer.js
Expand Up @@ -706,51 +706,69 @@ export default (new Transformer({
asset.symbols.set(exported, local, convertLoc(loc));
}

let deps = new Map(
asset
.getDependencies()
.map(dep => [dep.meta.placeholder ?? dep.specifier, dep]),
);
for (let dep of deps.values()) {
dep.symbols.ensure();
let deps = new Map();
for (let dep of asset.getDependencies()) {
if (!deps.has(dep.meta.placeholder ?? dep.specifier)) {
deps.set(dep.meta.placeholder ?? dep.specifier, []);
}
deps.get(dep.meta.placeholder ?? dep.specifier)?.push(dep);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A possible alternative way of implementing this could be to make the map keyed by (dep.meta.placeholder ?? dep.specifier) + dep.specifierType, rather than an array of dependencies per specifier.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the main reason why I didn't add the kind to the key is because we only want to do the ImportKind specific stuff when we are looking at hoist_result.imported_symbols. Keying by specifier + specifierType would mean that we would have to change all the other things in the hoist_result to track the kind, but I'm not sure if we should do that.

}
for (let depArr of deps.values()) {
for (let dep of depArr) {
dep.symbols.ensure();
}
}

for (let {
source,
local,
imported,
loc,
kind,
} of hoist_result.imported_symbols) {
let dep = deps.get(source);
if (!dep) continue;
dep.symbols.set(imported, local, convertLoc(loc));
for (let dep of nullthrows(deps.get(source))) {
if (
!dep ||
((dep.meta.kind === 'Require' ||
dep.meta.kind === 'Import' ||
dep.meta.kind === 'DynamicImport') &&
dep.meta.kind !== kind)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dep.meta.kind is a DependencyKind rather than a ImportKind so I wasn't really sure what to do here..

) {
continue;
}
dep.symbols.set(imported, local, convertLoc(loc));
}
}

for (let {source, local, imported, loc} of hoist_result.re_exports) {
let dep = deps.get(source);
if (!dep) continue;
for (let dep of nullthrows(deps.get(source))) {
if (!dep) continue;

if (local === '*' && imported === '*') {
dep.symbols.set('*', '*', convertLoc(loc), true);
} else {
let reExportName =
dep.symbols.get(imported)?.local ??
`$${asset.id}$re_export$${local}`;
asset.symbols.set(local, reExportName);
dep.symbols.set(imported, reExportName, convertLoc(loc), true);
if (local === '*' && imported === '*') {
dep.symbols.set('*', '*', convertLoc(loc), true);
} else {
let reExportName =
dep.symbols.get(imported)?.local ??
`$${asset.id}$re_export$${local}`;
asset.symbols.set(local, reExportName);
dep.symbols.set(imported, reExportName, convertLoc(loc), true);
}
}
}

for (let specifier of hoist_result.wrapped_requires) {
let dep = deps.get(specifier);
if (!dep) continue;
dep.meta.shouldWrap = true;
for (let dep of nullthrows(deps.get(specifier))) {
if (!dep) continue;
dep.meta.shouldWrap = true;
}
}

for (let name in hoist_result.dynamic_imports) {
let dep = deps.get(hoist_result.dynamic_imports[name]);
if (!dep) continue;
dep.meta.promiseSymbol = name;
for (let dep of nullthrows(
deps.get(hoist_result.dynamic_imports[name]),
)) {
if (!dep) continue;
dep.meta.promiseSymbol = name;
}
}

if (hoist_result.self_references.length > 0) {
Expand Down Expand Up @@ -796,43 +814,52 @@ export default (new Transformer({
asset.meta.shouldWrap = hoist_result.should_wrap;
} else {
if (symbol_result) {
let deps = new Map(
asset
.getDependencies()
.map(dep => [dep.meta.placeholder ?? dep.specifier, dep]),
);
let deps = new Map();
for (let dep of asset.getDependencies()) {
if (!deps.has(dep.meta.placeholder ?? dep.specifier)) {
deps.set(dep.meta.placeholder ?? dep.specifier, []);
}
deps.get(dep.meta.placeholder ?? dep.specifier)?.push(dep);
}
asset.symbols.ensure();

for (let {exported, local, loc, source} of symbol_result.exports) {
let dep = source ? deps.get(source) : undefined;
asset.symbols.set(
exported,
`${dep?.id ?? ''}$${local}`,
convertLoc(loc),
);
if (dep != null) {
dep.symbols.ensure();
dep.symbols.set(
local,
`${dep?.id ?? ''}$${local}`,
convertLoc(loc),
true,
);
if (!deps.get(source)) {
asset.symbols.set(exported, `${local}`, convertLoc(loc));
} else {
for (let dep of nullthrows(deps.get(source))) {
asset.symbols.set(
exported,
`${dep?.id ?? ''}$${local}`,
convertLoc(loc),
);
if (dep != null) {
dep.symbols.ensure();
dep.symbols.set(
local,
`${dep?.id ?? ''}$${local}`,
convertLoc(loc),
true,
);
}
}
}
}

for (let {source, local, imported, loc} of symbol_result.imports) {
let dep = deps.get(source);
if (!dep) continue;
dep.symbols.ensure();
dep.symbols.set(imported, local, convertLoc(loc));
for (let dep of nullthrows(deps.get(source))) {
if (!dep) continue;
dep.symbols.ensure();
dep.symbols.set(imported, local, convertLoc(loc));
}
}

for (let {source, loc} of symbol_result.exports_all) {
let dep = deps.get(source);
if (!dep) continue;
dep.symbols.ensure();
dep.symbols.set('*', '*', convertLoc(loc), true);
for (let dep of nullthrows(deps.get(source))) {
if (!dep) continue;
dep.symbols.ensure();
dep.symbols.set('*', '*', convertLoc(loc), true);
}
}
}

Expand Down