-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Conversation
Benchmark ResultsKitchen Sink ✅
Timings
Cold Bundles
Cached Bundles
React HackerNews ✅
Timings
Cold BundlesNo bundle changes detected. Cached BundlesNo bundle changes detected. AtlasKit Editor ✅
Timings
Cold Bundles
Cached Bundles
Three.js ✅
Timings
Cold Bundles
Cached BundlesNo bundle changes detected. |
Co-authored-by: Eric Eldredge <lettertwo@gmail.com>
Co-authored-by: Eric Eldredge <lettertwo@gmail.com>
Looks like there's a problem in the transformer: import b from './b';
const foo = b.foo;
const bar = require('./b').foo;
output = foo + bar; becomes const $aa66d08168f59e76$var$foo = $aa66d08168f59e76$import$61a9e60a12024cdc$2e2bcd8739ae039.foo;
import "aa66d08168f59e76:./b";
var $aa66d08168f59e76$require$bar = $aa66d08168f59e76$import$61a9e60a12024cdc$6a5cdcad01c973fa;
output = $aa66d08168f59e76$var$foo + $aa66d08168f59e76$require$bar; so both the require and the import usage get attributed to the same dependency. Furthermore, this dependency has And indeed for a (And there's probably a situation right now where the inverse happens, that |
Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>
…s match while iterating
7b6ab7a
to
7bf63b3
Compare
((dep.meta.kind === 'Require' || | ||
dep.meta.kind === 'Import' || | ||
dep.meta.kind === 'DynamicImport') && | ||
dep.meta.kind !== kind) |
There was a problem hiding this comment.
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..
for (let dep of nullthrows(deps.get(source))) { | ||
if ( | ||
(dep.meta.kind === 'Require' || | ||
dep.meta.kind === 'Import' || | ||
dep.meta.kind === 'DynamicImport') && | ||
dep.meta.kind !== kind | ||
) { | ||
continue; | ||
} | ||
dep.symbols.set(imported, local, convertLoc(loc)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be simplified to just
let dep = nullthrows(deps.get(source)).find(d => dep.meta.kind === kind)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So there's a case where dep.meta.kind
is Export
and kind
is import
and I think we should be setting the symbols in this case which is why I had the condition to continue if there's a mismatch and if dep.meta.kind
is an ImportKind
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this would be the case where you have both reexports and imports of the same specifier in a single asset. But it looks like currently import
always wins (so dep.meta.kind will be import
in asset.getDependencies()
). (Kind of strange anyway that we have dep.meta.kind
anyway. AFAICT dep.specifierType
would be sufficient for the packager, then there's only be esm or cjs)
And in this loop, kind
is always Import
or Require
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm in this test it looks like Export
wins for dep.meta.kind
so the symbols wouldn't get set if we have that single condition.
dep.sourcePath: /Users/bdo/parcel/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-import/d.js
dep.meta.kind: Export
it('supports simultaneous import and re-export of a symbol', async function () { |
for (let dep of nullthrows(deps.get(source))) { | ||
if (local === '*' && imported === '*') { | ||
dep.symbols.set('*', '*', convertLoc(loc), true); | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this shouldn't be set for all dependencies with that specified for only for the Import
one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we shouldn't set the symbols if kind
is Import
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This loop is applying hoist_result.re_exports
. Which can only come from ESM export .. from ...;
statements. So here we're only interested in the ESM dep
. For example in
export {a as b} from "foo";
const {x} = require("foo");
console.log(x)
if (!deps.has(dep.meta.placeholder ?? dep.specifier)) { | ||
deps.set(dep.meta.placeholder ?? dep.specifier, []); | ||
} | ||
deps.get(dep.meta.placeholder ?? dep.specifier)?.push(dep); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 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))) { | ||
dep.meta.shouldWrap = true; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dep.meta.shouldWrap
will get set for every dependency with that specifier. But it should have been for the require dep only.
I still find the approach of adding for loops problematic. I can't think of a case where an entry from |
So in this case
We get this error because there's two deps with './b' as the specifier but we only wrap the
|
This loop replaces all occurences of imports and requires as output by the transformer. But there's once again the problem that there's no difference between the twin ESM and CJS deps: there are two identical specifiers here, where the first should refer to the ESM dep and the second to the CJS dep. import "b49a022a1bb2bce3:./b";
const $b49a022a1bb2bce3$export$6a5cdcad01c973fa = $b49a022a1bb2bce3$import$61a9e60a12024cdc$2e2bcd8739ae039.foo; // <-- missing default interop
import "b49a022a1bb2bce3:./b";
const $b49a022a1bb2bce3$export$d927737047eb3867 = (()=>$b49a022a1bb2bce3$import$61a9e60a12024cdc$6a5cdcad01c973fa
)();
output = $b49a022a1bb2bce3$export$6a5cdcad01c973fa + $b49a022a1bb2bce3$export$d927737047eb3867; So the callback runs with |
So how does parcel/packages/transformers/js/src/JSTransformer.js Lines 754 to 758 in 49136f0
|
Trying to summarize the conversation:
|
…hashed with DependencyKind's
Thanks for fixing! Can we ge a release with this fix? I think we are affected in diegomura/react-pdf#1915 |
@carlobeltrame Yes! We will try to get one out soon. cc: @devongovett |
↪️ Pull Request
There's a case where we don't add a default interop for a cjs module in
getSymbolResolution
This causes an error at runtime since it tries to call default which would be undefined in this case
💻 Examples
Repl
🚨 Test instructions
✔️ PR Todo