docs(esm): add notes about default exports (#2036)

Co-authored-by: Pooya Parsa <pyapar@gmail.com>
This commit is contained in:
Tobias Diez 2021-11-29 13:48:13 +01:00 committed by GitHub
parent ac98373b4c
commit be1d8ff005
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -142,6 +142,68 @@ export default defineNuxtConfig({
}) })
``` ```
### Default exports
A dependency with CommonJS format, can use `module.exports` or `exports` to provide a default export:
```js [node_modules/cjs-pkg/index.js]
module.exports = { test: 123 }
// or
exports.test = 123
```
This normally works well if we `require` such dependency:
```js [test.cjs]
const pkg = require('cjs-pkg')
console.log(pkg) // { test: 123 }
```
[Node.js in native ESM mode](https://nodejs.org/api/esm.html#interoperability-with-commonjs), [typescript with `esModuleInterop` enabled](https://www.typescriptlang.org/tsconfig#esModuleInterop) and bundlers such as Webpack, provide a compatibility mechanism so that we can default import such library.
This mechanism is often referred to as "interop require default":
```js
import pkg from 'cjs-pkg'
console.log(pkg) // { test: 123 }
```
However, because of complexities for syntax detection and different bundle formats, there is always a chance that interop default fails and we end up with something like this:
```js
import pkg from 'cjs-pkg'
console.log(pkg) // { default: { test: 123 } }
```
Also when using dynamic import syntax (in both CJS and ESM files), we always have this situation:
```js
import('cjs-pkg').then(console.log) // [Module: null prototype] { default: { test: '123' } }
```
In this case, we need to manually inerop default export:
```js
// Static import
import { default as pkg } from 'cjs-pkg'
// Dynamic import
import('cjs-pkg').then(m => m.default || m).then(console.log)
```
For handling more complex situations and more safety, we recommand and internally use [mlly](https://github.com/unjs/mlly) in Nuxt 3 that can preserve named exports.
```js
import { interopDefault } from 'mlly'
// Assuming the shape is { default: { foo: 'bar' }, baz: 'qux' }
import myModule from 'my-module'
console.log(interopDefault(myModule)) // { foo: 'bar', baz: 'qux' }
```
## Library author guide ## Library author guide
The good news is that it's relatively simple to fix issues of ESM compatibility. There are really two main options: The good news is that it's relatively simple to fix issues of ESM compatibility. There are really two main options:
@ -228,7 +290,7 @@ const someFile = await resolvePath('my-lib', { url: import.meta.url })
### Best practices ### Best practices
- Prefer named exports rather than default export. This helps reduce CJS conflicts. - Prefer named exports rather than default export. This helps reduce CJS conflicts. (see [Default exports](#default-exports) section)
- Avoid depending on Node.js built-ins and CommonJS or Node.js-only dependencies as much as possible to make your library usable in Browsers and Edge Workers without needing Nitro polyfills. - Avoid depending on Node.js built-ins and CommonJS or Node.js-only dependencies as much as possible to make your library usable in Browsers and Edge Workers without needing Nitro polyfills.