or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?
Please give us some advice and help us improve HackMD.
Do you want to remove this version name and description?
Syncing
xxxxxxxxxx
Why is
export *
bad?When
export *
is used in libraries, especially when exporting from an external library, numerous issues can occur. It is almost always better to export specific named modules.Common problems it can introduce
API surface implicitly inherits dependency surfaces
Take for instance package
components
exports * from packagebutton
. It has a semver dependency onbutton
using^1.0.0
.The
button
package adds a minor feature. This bumps the package to^1.1.0
.Despite this following semver and being valid, when the user updates their dependencies, the
components
package does not minor bump, because it was not changed. However, due to the export *, it's contract implicitly inherits updates from thebutton
contract. This violates semver.This becomes far worse if the dependency is even looser; though it's unlikely, if
components
required>1.0.0
, major changes tobutton
can create implicit major shifts on thecomponents
api surface.This all becomes far more obvious if
components
exports named things frombutton
. While it may not catch non-name changes like argument changes, it will enforce explicit named resolutions and catch things like dropped methods or renames.Breaks tree shaking opportunities
The esbuild tool does not implement tree shaking through re-exported namespaces:
https://github.com/evanw/esbuild/issues/1420
This particular bug may be an edge case; normally
export * from './file'
re-exports 1 or more individual names. Whenexport * as foo from './file'
is used, the alias acts as a single named export, similar to exporting an enum or const object definition.Webpack 5 has an advanced optimization where it can trace usage even through this alias to tree shake out unused references, while esbuild is missing this. Avoiding
export *
aliasing specifically would work around the limitation, or just never usingexport *
would avoid the subtlety completely.Prevents loading packages in isolation
Consider this:
Package
theme
has a methodcreateTheme
Package
styling
exports * fromtheme
Package
fui-react
exports * from boththeme
andstyling
The user imports
createTheme
fromfui-react
. Does it resolve to thecreateTheme
exported by thetheme
package, or thecreateTheme
exported from thestyling
package?If these two exports resolve to the same module, this becomes non-ambiguous. But this means that module resolution can break the export, making it fragile and up to the resolution logic. If for example, the
styling
package requires a different version ofcreateTheme
than thetheme
package does, they can resolve to different implementations.It is far less error prone to avoid this issue altogether and avoid
export *
. Further, it's better to export named modules from the source, rather than indirectly through another library. Example:fui-react
exportscreateTheme
fromtheme
styling
exportscreateTheme
fromtheme
And,
fui-react
does NOT exportcreateTheme
fromstyling
By using named module resolution, we are forced to be explicit about the source of truth, resolving ambiguity. This also has performance benefits - now tools like bundlers must only traverse
theme
when importingcreateTheme
.esbuild does not retain export * in sub-folder path files
https://github.com/evanw/esbuild/issues/1737
If you
export * from 'library'
in a sub file (such as Fluent UI's/Utilities.js
file), it doesn't retain theexport *
and instead tries to re-emit the namespace through helper code. This makes browser bundles from esbuild unconsumable. Avoidingexport *
from external libraries works around the critical issue.api-extractor does not extract
export *
from external librariesIf you
export * from 'library'
, api extractor's output does not include the api surface of the external library, due to it being unpredictable. This means you don't detect an api surface change when it happens, since it can only truely be evaluated on the consumer side.The maintainers of api-extractor agree that this practice has all the previously mentioned problems and has no appetite to expand the api surface generated by external export * usage.
Because api-extrator detects no changes to an api surface due to external shift, there is less opportunity to enforce good practices elsewhere. For example, Beachball could detect api-extractor shifts and limit bump types, but this requires the extraction actually shifts.
How to prevent it from being reintroduced
Eslint rule:
Introducing
no-export-all