-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Import randomly becomes empty object with Webpack esbuild-loader and CJS output format after 0.14.5 #2037
Comments
What are the steps to reproduce the issue? Is it possible to reproduce this by just building with esbuild without Webpack involved? I don't know anything about the |
I will try reproducing this with esbuild alone, maybe in the next few days. The fact is, our internals is very tightly coupled to Webpack so it's hard to extract all the relevant parts and make it build. The quickest way to reproduce (most time-efficient for me :P) is still:
If you clone the repo, the two errors are coming from this file and this file, respectively. Because I will try to at least create a standalone website with barebones Docusaurus code tomorrow (it's getting late here). Hope that would make inspection easier. |
I was able to build it with esbuild using this code: require('esbuild').build({
stdin: {
contents: `
import ErrorBoundary from '@docusaurus/ErrorBoundary';
console.log('imported:', ErrorBoundary);
console.log('required:', require('@docusaurus/ErrorBoundary'));
import NavbarItem from '@theme/NavbarItem';
console.log('imported:', NavbarItem);
console.log('required:', require('@theme/NavbarItem'));
`,
resolveDir: __dirname,
},
bundle: true,
loader: {
'.js': 'jsx',
'.css': 'text',
},
plugins: [{
name: 'hacks',
setup(build) {
let mappings = {
'@docusaurus/': '@docusaurus/core/lib/client/exports/',
'@theme/': '@docusaurus/theme-classic/lib-next/theme/',
'@theme/Error': '@docusaurus/core/lib/client/theme-fallback/Error',
}
build.onResolve({ filter: /.*/ }, async args => {
if (args.pluginData === 'skipme') return
for (const mapping in mappings) {
if (!args.path.startsWith(mapping)) continue
const result = await build.resolve(args.path.replace(mapping, mappings[mapping]),
{ importer: args.importer, resolveDir: args.resolveDir, kind: args.kind, pluginData: 'skipme' })
if (!result.errors.length) return result
}
})
build.onResolve({ filter: /^@generated\// }, args => {
return { path: args.path, namespace: 'generated' }
})
build.onLoad({ filter: /.*/, namespace: 'generated' }, args => {
return { contents: '' }
})
},
}],
}) That prints this when run (with
I have no idea how their code base works. It doesn't appear to use normal npm path resolution rules. I just came up with some quick hacks to get it to build. In any case, the |
Indeed, we have some Webpack aliases, and your quick hack seems to get them. However, because the original error is quite random (it doesn't reproduce in every import site), I haven't found a way to reliably reproduce yet. Esbuild-loader's code seems so simple that I can be hardly convinced that it's their bug: their |
Sorry but I'm not going to debug this myself. This is a huge project and the build scripts run really slowly, and I don't have the patience to debug this. The problem also only manifests when the code base is built using other tools. It seems to work fine with esbuild alone. Someone should isolate the problem down to a simple case that demonstrates the problem (ideally just with esbuild alone) to move forward on this issue. |
Yes, I'm not asking you to debug it yourself at this stage🤦♂️ Excuse me for sending this issue so early. I'll keep it here for reference for now while I reduce it to a minimally reproducing demo. Thanks for understanding! |
Although I didn't look into your package, this issue looks very like this one: #1894 (comment) Before evan has some time to fix the behavior of esm-to-cjs, you can check the circular imports in your repo. |
The Docusaurus website is using
esbuild-loader
to leverage esbuild's speedy transpilation while avoiding architectural changes (since we are still built on Webpack). The configuration looks like this:The
isServer
is a flag distinguishing between server-side rendering and client-side rendering. This configuration has worked rather well until upgrading esbuild-loader from 2.16 to 2.18. The only (significant) change introduced in this version bump is upgrading esbuild from 0.13.15 to 0.14.23.To reproduce, clone the Docusaurus repo, upgrade
esbuild-loader
in thewebsite
folder to 2.18, and runyarn build --locale en
. The client build succeeds, but the server build (which is essentially running the Docusaurus app in Node throughReactDOMServer.renderToString
) errors with:The Minified React error is interpreted as:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
All the files that are imported as React components are in ESM format, so there shouldn't be an interop problem. Removing the
format: 'cjs'
line—and thus leveraging Webpack's own ESM bundling mechanisms—solves the bug. This makes me think that it's a problem with esbuild's emit being incompatible with Webpack. Leveraging Yarn'sresolutions
, I could confirm that build is successful with esbuild v0.14.3, but fails with v0.14.5, which turns our focus to the adjusted handling ofdefault
and__esModule
in #1849. While I admittedly don't know exactly what breaking changes that refactor brings, it claims to improve compatibility with Webpack while in reality breaks Webpack.I would admit that Docusaurus doesn't have good source maps and these SSR errors are very hard to debug, so an easy way to investigate is forgoing the server/client rendering distinction and always emitting CJS with
format: 'cjs'
. Since development mode only uses client rendering, we can now get HMR as well as full error messages in development. I'm able to narrow this problem down to two specific places.We have a line:
import ErrorBoundary from '@docusaurus/core/lib/client/exports/ErrorBoundary';
(using a Webpack alias which I've expanded here). TheErrorBoundary
file default-exports a class component. However, when it's imported, the result is an empty object{}
instead of a class. What's interesting is that this module is imported in three distinct places:App.tsx
andError.tsx
, both in the@docusaurus/core/lib/client
folderAnd the
App.tsx
imports the class successfully, while the other two import empty objects. I've tried adding another named exportexport {ErrorBoundary};
, but even when usingconsole.log(require('@docusaurus/ErrorBoundary')
, the result is an empty object. In theApp.tsx
where the import is successful, it logs the expectedAnother offending place is
import NavbarItem from '@theme/NavbarItem';
which also results in an empty object{}
being returned. But the default export of@theme/NavbarItem
, in this case, is a normal function, and this file is also imported in multiple places and only fails in one particular file.This error may be hard to investigate without a good understanding of Docusaurus' architecture, so I'd be more curious about how I'd debug this myself: for example, can I intercept esbuild's output? Are there compatibility flags? I'd be happy to assist any debugging efforts.
Reference: facebook/docusaurus#6235
The text was updated successfully, but these errors were encountered: