Esbuild
Why choose esbuild?
In short: esbuild is written in go language, the compilation speed is fast, and it supports many environments.
Exactly how fast: Its compilation speed is more than 100 times
that of ordinary compilation plugins Its API can be accessed in three ways: command line, JavaScript and Go, and the document is still only one
Install
npm install esbuild
basic command
version
esbuild --version
Construct
esbuild app.jsx --bundle --outfile=out.js
quick conversion
echo 'let x: number = 1' | npx esbuild --loader=ts
# let x = 1;
API
Transform API
The conversion API calls only operate on a single string and do not access the file system. This makes it ideal for use in environments without a filesystem (like a browser), or as part of another toolchain. Here is a simple transformation:
echo 'let x: number = 1' | npx esbuild --loader=ts
# let x = 1;
Basic options:
advanced options:
- Banner
- Charset
- Color
- Drop
- Footer
- Global name
- Ignore annotations
- JSX
- JSX factory
- JSX fragment
- Keep names
- Legal comments
- Log level
- Log limit
- Mangle props
- Pure
- Source root
- Sourcefile
- Sources content
- Tree shaking
- Tsconfig raw
Build API
Build API calls to operate on one or more files in the file system. This allows files to reference each other and be bundled together. Here's a simple build:
echo 'let x: number = 1' > in.ts
esbuild in.ts --outfile=out.js
cat out.js
# let x = 1;
Basic options:
- Bundle
- Define
- Entry points
- External
- Format
- Inject
- Loader
- Minify
- Out is
- Outfile
- Platform
- Serve
- Sourcemap
- Splitting
- Target
- Watch
- Write
advanced options:
- Allow overwrite
- Analyze
- Asset names
- Banner
- Charset
- Chunk names
- Color
- Conditions
- Drop
- Entry names
- Footer
- Global name
- Ignore annotations
- Incremental
- JSX
- JSX factory
- JSX fragment
- Keep names
- Legal comments
- Log level
- Log limit
- Main fields
- Mangle props
- Metafile
- Node paths
- Out extension
- Outbase
- Preserve symlinks
- Public path
- Pure
- Resolve extensions
- Source root
- Sourcefile
- Sources content
- Stdin
- Tree shaking
- Tsconfig
- Working directory
basic option
Bundle
After enabling, package your delegates into a package (file)
For example,
you have the following files
/src/index.js
import {
doSomething } from './utils'
doSomething()
/src/utils.js
export function doSomething() {
}
Excuting an order
npx esbuild ./src/index.js --bundle --outfile=bundle.js
packaged is abundle.js
function doSomething() {
}
doSomething()
If the bundle is not enabled, it will be packaged bundle.js
like this
import {
doSomething } from "./utils";
doSomething();
Define
Define a globally accessible variable
as an example
index.js
console.log(**DEFINE**)
Excuting an order
npx esbuild ./src/index.js --bundle --outfile=./bundle.js "--define:__DEFINE__=\"define\"
Package result
bundle.js
console.log("define")
Entry points
It is the entry file list, the parameters followed by esbuild
are examples
index.js
console.log('index')
utils.js
console.log('utils')
execute script
npx esbuild index.js utils.js --outdir=out
# 或者
npx esbuild ./** --outdir=out
# 或者
npx esbuild out1=index.js out2=utils.js --outdir=out
External
Mark a file or package as an external file, which will be automatically excluded when building
an example
index.js
require("fsevents")
execute script
npx esbuild index.js --bundle --external:fsevents --platform=node --outfile=bundle.js
illustrate
This api is generally used for commenJs packages.
For example, the packaged bundle.js is used for the code of the node environment, so the content we packaged should not package the content of the node_modules package into bundle.js, but should use the bundle .js, you should include its dependencies in the local node_modules
Format
Determine the output format of the generated JavaScript file
Currently supports three parameters
- iife
self-invoking function
!(function() { // ...do something })();
- cjs
startJs
const xx = require("xx")
- esm
esModule
import xx from "xx"
Inject
Allows you to replace global variables with imports from another file
process-shim.js
export let process = {
cwd: () => ''
}
entry.js
console.log(process.cwd())
Excuting an order
npx esbuild entry.js --bundle --inject:./process-shim.js --outfile=bundle.js
bundle.js
let process = {
cwd: () => ""};
console.log(process.cwd());
illustrate
You can inject the parsing function of jsx syntax through this api.
For example, you can automatically import react and provide functions such as React.createElement.
For details, please refer to JSX DOC
Loader
This option changes how a given input file is interpreted. For example, the js loader interprets the file as JavaScript and the css loader interprets the file as css
npx esbuild index.js --bundle --loader:.png=dataurl --loader:.svg=text
# 或者
echo 'import index = require("./index")' | npx esbuild --loader=ts --bundle
# 或者
echo 'let x: number = 1' | npx esbuild --loader=ts
# let x = 1;
Notice
esbuild only converts ts syntax to js syntax without type verification
Minify
Generated code will be minimized
echo 'fn = obj => { return obj.x }' | npx esbuild --minify
# fn=n=>n.x;
# 或者
echo 'fn = obj => { return obj.x }' | npx esbuild --minify-whitespace
# fn=obj=>{return obj.x};
# 或者
echo 'fn = obj => { return obj.x }' | npx esbuild --minify-identifiers
#fn = (n) => {
# return n.x;
#};
# 或者
echo 'fn = obj => { return obj.x }' | npx esbuild --minify-syntax
#fn = (obj) => obj.x;
Notice
- When minification is enabled, you should probably also set the target option
By default, esbuild takes advantage of modern JavaScript features to make your code smaller
eg: a === undefined || a === = null ? 1 : a is ok Minified to a??1
If you don't want esbuild to take advantage of modern JavaScript features when minified, you should use an older language like:--target=es6
- In JavaScript template literals, the character escape sequence \n will be replaced with a newline
character String literals are also converted to template literals if the target supports them and doing so results in smaller output
. Not a bug, narrowing means you're asking for smaller output, the escape sequence \n takes two bytes, and the newline character takes one byte
- By default, esbuild will not minify the names of top-level declarations.
This is because esbuild has no idea what you're going to do with the output.
You might be injecting the minified code into some other code, in which case the minified top-level declaration names would be Unsafe
setting the output format (or enabling the binding, which will choose an output format for you if you haven't set it already) tells esbuild that the output will operate in its own scope, which means it can safely reduce top-level declaration names .
- Minification is not 100% safe for JavaScript code.
This is true for esbuild as well as other popular JavaScript minifiers such as terser. In
particular, esbuild is not designed to preserve the value of a function call .tostring()
so The reason for doing this is that if all the code in all functions has to be kept verbatim, the minification does almost nothing and is effectively useless However, this means that JavaScript
code that depends on the return value of .tostring() is in the May break when minimized
For example, some modes in the AngularJS framework are in
- By default, esbuild does not preserve the value of .name on function and class objects.
This is because most code does not depend on this attribute, and using shorter names is an important size optimization.
However, some code does depend on .name Attributes are registered and bound
If you need to rely on this option, you should enable the keep names option.
- Certain JavaScript features can disable esbuild's optimizations, including minification.
Specifically, using direct eval or with statements can prevent esbuild from renaming identifiers to smaller names, as these features cause identifier binding to happen at runtime, instead of compile time
which is probably irrelevant since most people don't do
Out is
The output directory for the build operation
npx esbuild index.js --bundle --outdir=out
Outfile
The output file of the build operation
npx esbuild index.js --bundle --outfile=bundle.js
Platform
By default, esbuild's binder is configured to generate code for the browser.
If your packaged code is intended to run in node, you should set platform to node.
# 自调用函数 (默认)
npx esbuild index.js --bundle --platform=browser
# 或者 commenJs
npx esbuild index.js --bundle --platform=node
# 或者 Es module
npx esbuild index.js --bundle --platform=neutral
Serve
An http server similar to webpack-server, which means that you don't have to execute esbuild execution commands all the time after changing the code
npx esbuild src/index.js --servedir=www --outdir=www/js --bundle
Then create the www directory, createindex.html
<script script src="js/index.js"></script>
or you just need to serve your js
npx esbuild src/index.js --outfile=out.js --bundle --serve=8000
index.html
<script src="http://localhost:8000/out.js"></script>
parameter
interface ServeOptions {
port?: number;
host?: string;
servedir?: string;
onRequest?: (args: ServeOnRequestArgs) => void;
}
Proxy server example
const esbuild = require('esbuild');
const http = require('http');
// 在一个随机的本地端口上启动esbuild服务器
esbuild.serve(
{
servedir: __dirname,
},
{
// ... 构建选项 ...
}
).then(result => {
// result 告诉我们 esbuild 的本地服务器在哪里
const {host, port} = result
// 然后在端口上启动代理服务器 3000
http.createServer((req, res) => {
const options = {
hostname: host,
port: port,
path: req.url,
method: req.method,
headers: req.headers,
}
// 将每个传入的请求转发给esbuild
const proxyReq = http.request(options, proxyRes => {
// 如果esbuild返回“未找到”,发送一个自定义404页面
if (proxyRes.statusCode === 404) {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('<h1>A custom 404 page</h1>');
return;
}
// 否则,将响应从esbuild转发到客户端
res.writeHead(proxyRes.statusCode, proxyRes.headers);
proxyRes.pipe(res, { end: true });
});
// 将请求体转发给esbuild
req.pipe(proxyReq, { end: true });
}).listen(3000);
});
Sourcemap
Let the packaged code have a source code mapping file, that is to say, you can view the source code when debugging in the browser
# linked (默认)
# 生成为一个单独的 .js.map 源映射, 输出文件 bundle.js 包含一个特殊的 //# sourceMappingURL= 注释,该注释指向 .js.map。这样,当您打开调试器时,浏览器就知道在哪里找到给定文件的源映射
npx esbuild index.js --sourcemap --outfile=bundle.js
# external
# 生成为一个单独的 .js.map 源映射, 但与 linked 模式不同的是, 输出文件 bundle.js 不包含 //# sourceMappingURL=
npx esbuild index.js --sourcemap=external --outfile=bundle.js
# inline
# 不生成 .js.map 文件,注解 //# sourceMappingURL= 后边跟着一串映射的 base64 数据
npx esbuild index.js --sourcemap=inline --outfile=bundle.js
# both
# inline 和 external 的结合
npx esbuild index.js --sourcemap=both --outfile=bundle.js
Notice
In the browser, you need to turn on the Enable source maps button in the settings
In nodejs, v12.12.0 has built-in support for the original map
node --enable-source-maps index.js
Splitting
Code splitting, packaging the same code into a common js
note: this api is still under development, currently only applicable to esm output format
npx esbuild index.js utils.js --bundle --splitting --outdir=out --format=esm
Target
The environment that the packaged js needs to support, the default is esnext, which is the latest javascript and css syntax and features, which means that the code packaged by default does not naturally support ie (that's good)
npx esbuild index.js --target=es2020,chrome58,firefox57,safari11,edge16,node12
Watch
esbuild listens for changes on the file system and rebuilds if a file change might invalidate the build
npx esbuild index.js --outfile=bundle.js --bundle --watch
When you use it in js, watch can be an object, for example:
require('esbuild').build({
entryPoints: ['index.js'],
outfile: 'bundle.js',
bundle: true,
watch: {
onRebuild(error, result) {
if (error) console.error('监听失败:', error)
else console.log('监听成功:', result)
// result.stop() 可以停止监听
},
},
}).then(result => {
console.log('监听中...')
})
Write
By default, the build api will automatically write the built content to the system file, write: false can prevent this behavior
for example
let result = require('esbuild').buildSync({
entryPoints: ['app.js'],
sourcemap: 'external',
write: false,
outdir: 'out',
})
for (let out of result.outputFiles) {
console.log(out.path, out.contents)
}
advanced options
Allow overwrite
Allow output files to overwrite input files (I guess no one would do that)
for example
npx esbuild index.js --outdir=. --allow-overwrite
# 这样你的 index.js 源代码会消失
Analyze
Generates an easy-to-read report on the bundle contents
npx esbuild --bundle index.js --outfile=bundle.js --minify --analyze
Asset names
When the loader option is file, add additional information to the packaged resource
# [name], [hash], [dir], [ext] 这四个顾名思义,任意组合
npx esbuild index.js --asset-names=assets/[name]-[hash] --loader:.png=file --bundle --outdir=out
Banner
Insert arbitrary strings at the beginning of generated JavaScript and CSS files, usually used to insert comments
There may be some problems running on git bash: https://github.com/evanw/esbuild/issues/2150
npx esbuild index.js --banner:js=//comment --banner:css=/*comment*/ --outfile=bundle.js
Charset
As the name suggests, set the character encoding
echo 'let a = 你好' | npx esbuild
# let a = \u4F60\u597D;
echo 'let a = 你好' | npx esbuild --charset=utf8
# let a = 你好;
Chunk names
The filename of the shared code block that is automatically generated when code splitting is enabled
# [name], [hash], [ext]
esbuild index.js --chunk-names=chunks/[name]-[hash] --bundle --outdir=out --splitting --format=esm
Color
Whether to enable color when building
echo 'typeof x == "null"' | npx esbuild --color=true
Conditions
To be honest, I don’t really understand the meaning and usefulness of this api, so the explanation here is relatively simple.
conditions allow you to redirect the same import path to different file locations
npx esbuild index.js --bundle --conditions=custom1,custom2
For example,
when we import "pkg/foo", it points to ./imported.mjs
When we require("pkg/foo"), it points to ./required.cjs
{
"name": "pkg",
"exports": {
"./foo": {
"import": "./imported.mjs",
"require": "./required.cjs",
"default": "./fallback.js"
}
}
}
Drop
Content that needs to be deleted before building, such as the console in the page
# 删除 debugger
npx esbuild index.js --drop:debugger
# 删除 console
npx esbuild index.js --drop:console
Entry names
Modify the entry file name. Generally, modifying the file name allows our users to use the latest functions.
# [name], [hash], [dir], [ext]
npx esbuild index.js --entry-names=[dir]/[name]-[hash] --bundle --outdir=out
Footer
Same issue as Banner API
Inserts arbitrary strings at the end of generated JavaScript and CSS files, usually for inserting comments
npx esbuild index.js --footer:js=//comment --footer:css=/*comment*/ --outfile=bundle.js
Global name
It only works when format
is iife
, setting the name of the global variable, that is, assigning iife
the self-invoking function to the variable
echo 'module.exports = "test"' | npx esbuild --format=iife --global-name=vue
# 或者
echo 'module.exports = "test"' | npx esbuild --format=iife --global-name='vue.test["xx"]'
Ignore annotations
Since JavaScript is a dynamic language, it can sometimes be very difficult for the compiler to identify unused code, so the community developed annotations to help tell the compiler which code should be considered side-effect free and can be removed. Currently esbuild supports two forms of side effect annotations
- An inline /* @PURE * / comment before a function call tells esbuild that the function call can be removed if the resulting value is not used.
- The sideEffects field of package.json is used to tell esbuild which files in your package can be deleted if all imported files in your package are not used in the end. This is a Webpack convention, and many libraries published to npm already include this field in their package definitions. You can read more about this field in the Webpack documentation.
npx esbuild index.js --bundle --ignore-annotations
Incremental
Call the build API of esbuild repeatedly with the same option to turn on this option
JSX
Handle JSX syntax, either convert JSX to JS (default), or keep JSX syntax in the output
echo '<div/>' | npx esbuild --jsx=preserve --loader=jsx
# <div />;
echo '<div/>' | npx esbuild --loader=jsx
# /* @__PURE__ */ React.createElement("div", null);
JSX factory
Sets the function called by the JSX element
echo '<div/>' | npx esbuild --jsx-factory=h --loader=jsx
# /* @__PURE__ */ h("div", null);
JSX fragment
Sets the function called by the JSX fragment
echo '<>content</>' | npx esbuild --jsx-fragment=Fragment --loader=jsx
# /* @__PURE__ */ React.createElement(Fragment, null, "content");
Keep names
Set the name attribute of the function to "fn", this API does not understand its meaning
npx esbuild index.js --minify --keep-names
Legal comments
- none
don't keep anylegal comment
- inline
keep alllegal comment
- eof
willlegal comment
move to the end of the file. - linked
movedlegal comment
into the .LEGAL.txt file and linked it with a comment. - external
willlegal comment
move into the .LEGAL.txt file, but don't link it
npx esbuild index.js --legal-comments=eof
Log level
log level
- silent
Do not display any log output - error
only displays errors - warning
only displays warnings and errors - info
shows warnings, errors, and output file summaries - debug
logs all messages and some additional messages. - verbose
generates lots of log messages
echo 'typeof x == "null"' | npx esbuild --log-level=error
Log limit
Control the number of printed logs, otherwise the console may be very stuck, the default is 10
npx esbuild index.js --log-limit=10
Main fields
When importing a package in node, define which field of package.json is imported
- main
- module
- browser
npx esbuild index.js --bundle --main-fields=module,main
Mangle props
Pass a regular expression to esbuild, tell esbuild to automatically rewrite all properties that match this regular expression (very destructive api, use less) for
example
index.js
let x = {
xx_: 'x' };
let y = {
xx_: "y" };
# 它会将所有的以 _ 结尾的属性全部改掉
npx esbuild index.js --mangle-props=_$
# let x = { a: "x" };
# let y = { b: "y" };
Metafile
Generate some metadata about the build in JSON format
npx esbuild index.js --bundle --metafile=meta.json --outfile=out.js
Node paths
Global directory listing. These paths are searched in addition to looking for the node_modules directory in all parent directories
NODE_PATH=someDir npx esbuild index.js --bundle --outfile=bundle.js
Out extension
Customize the extension of the files generated by esbuild
npx esbuild index.js --bundle --outdir=dist --out-extension:.js=.mjs
Outbase
base path
npx esbuild src/pages/home/index.ts src/pages/about/index.ts --bundle --outdir=out --outbase=src
Preserve symlinks
This setting mirrors the --preserve-symlinks setting in node. If you use that setting (or the similar resolve.symlinks setting in Webpack), you will likely need to enable this setting in esbuild too. It can be enabled like this:
npx esbuild index.js --bundle --preserve-symlinks --outfile=bundle.js
Public path
loader
Add a base path to the export string of each file loaded by this
npx esbuild index.js --bundle --loader:.png=file --public-path=https://www.example.com/v1 --outdir=out
Pure
Various JavaScript tools have a convention to precede a new or call expression with a special comment containing /* @ PURE / or / # PURE */, which means that the expression can be deleted if the resulting value is not used . It looks like this:
echo 'console.log("foo:", foo())' | npx esbuild --pure:console.log --minify
Resolve extensions
The file extension can be omitted when importing files after setting
npx esbuild index.js --bundle --resolve-extensions=.ts,.js
Source root
Set the mapping source code directory
npx esbuild index.js --sourcemap --source-root=https://raw.githubusercontent.com/some/repo/v1.2.3/
Sourcefile
Set up mapping source code files
cat index.js | npx esbuild --sourcefile=example.js --sourcemap
Sources content
Set the mapping source code content, do not know how to use
npx esbuild --bundle app.js --sourcemap --sources-content=false
Stdin
Usually used when there is no entry file, such as corresponding to piping a file to stdin on the command line
echo 'export * from "./another-file"' | npx esbuild --bundle --sourcefile=imaginary-file.js --loader=ts --format=cjs
Tree shaking
dead code removal
npx esbuild app.js --tree-shaking=true
Tsconfig
As the name suggests, setting tsconfig
npx esbuild index.ts --bundle --tsconfig=custom-tsconfig.json
Tsconfig raw
echo 'class Foo { foo }' | npx esbuild --loader=ts --tsconfig-raw='{"compilerOptions":{"useDefineForClassFields":true}}'
Working directory
Specify the working directory to use for the build
require('esbuild').buildSync({
entryPoints: ['file.js'],
absWorkingDir: process.cwd(),
outfile: 'out.js',
})
Content Types
content type, each content type has an associated default loader
which you can override
- JavaScript :
js
File extension:.js
,.cjs
,.mjs
You can configure target
properties to control the version of the compiled code
Syntax transform | Transformed when --target is below |
Example |
---|---|---|
Exponentiation operator | es2016 | a ** b |
Async functions | es2017 | async () => {} |
Spread properties | es2018 | let x = {…y} |
Rest properties | es2018 | let {…x} = y |
Optional catch binding | es2019 | try {} catch {} |
Optional chaining | es2020 | a?.b |
Nullish coalescing | es2020 | a ?? b |
import.meta | es2020 | import.meta |
Logical assignment operators | es2021 | a ??= b |
Class instance fields | esnext | class { x } |
Static class fields | esnext | class { static x } |
Private instance methods | esnext | class { #x() {} } |
Private instance fields | esnext | class { #x } |
Private static methods | esnext | class { static #x() {} } |
Private static fields | esnext | class { static #x } |
Ergonomic brand checks | esnext | #x in y |
Import assertions | esnext | import “x” assert {} |
Class static blocks | esnext | class { static {} } |
Also some syntax it won't convert
Syntax transform | Unsupported when --target is below |
Example |
---|---|---|
Asynchronous iteration | es2018 | for await (let x of y) {} |
Async generators | es2018 | async function* foo() {} |
BigInt | es2020 | 123n |
Hashbang grammar | esnext | #!/usr/bin/env node |
Top-level await | esnext | await import(x) |
Arbitrary module namespace identifiers | esnext | export {foo as ‘f o o’} |
- TypeScript :
ts
ortsx
file extensions: .ts, .tsx, .mts, .cts
TypeScript type declarations are parsed and ignored
Syntax feature | Example |
---|---|
Interface declarations | interface Foo {} |
Type declarations | type Foo = number |
Function declarations | function foo(): void; |
Ambient declarations | declare module ‘foo’ {} |
Type-only imports | import type {Type} from ‘foo’ |
Type-only exports | export type {Type} from ‘foo’ |
Type-only import specifiers | import {type Type} from ‘foo’ |
Type-only export specifiers | export {type Type} from ‘foo’ |
Only typescript syntax extensions are supported and are always converted to JavaScript
Syntax feature | Example | Notes |
---|---|---|
Namespaces | namespace Foo {} | |
Enums | enum Foo { A, B } | |
Const enums | const enum Foo { A, B } | |
Generic type parameters | (a: T): T => a | Not available with the tsx loader |
JSX with types | <Element/> | |
Type casts | a as B and a | |
Type imports | import {Type} from ‘foo’ | Handled by removing all unused imports |
Type exports | export {Type} from ‘foo’ | Handled by ignoring missing exports in TypeScript files |
Experimental decorators | @sealed class Foo {} | The emitDecoratorMetadata flag is not supported |
- JSX:
jsx
或tsx
- JSON:
json
- CSS:
css
- Text:
text
- Binary:
binary
- Base64:
base64
- Data URL:
dataurl
- External file:
file
Plugins
`Plugins`是新的,仍然处于实验阶段。在esbuild 1.0.0版本之前,它可能会随着新用例的出现而改变。您可以根据跟踪问题获得关于此特性的更新
Plugins 允许你将代码注入到构建过程的各个部分
寻找插件
https://github.com/esbuild/community-plugins
使用插件
一个esbuild插件是一个有名字和setup函数的对象。它们以数组的形式传递给构建API调用。每次构建API调用都运行一次setup函数
其他详情请查看Plugin