# ESBuild
# Note
Note: All plugins require ESBuild version ^0.14.0 as peerDependencies.
# alias path
ESBuild plugin for alias replacement.
npm install esbuild-plugin-alias-path --save-dev
yarn add esbuild-plugin-alias-path --save-dev
pnpm install esbuild-plugin-alias-path --save-dev
import { build } from 'esbuild';
import { esbuildPluginAliasPath } from 'esbuild-plugin-alias-path';
(async () => {
const res = await build({
entryPoints: ['./src/main.ts'],
bundle: true,
outfile: './dist/main.js',
plugins: [
esbuildPluginAliasPath({
alias: { '@foo': './src/alias/foo.ts' },
}),
],
});
})();
// src/main.ts
import { FOO } from '@foo';
console.log(FOO);
// src/alias/foo.ts
export const FOO = 'foo';
# Configuration
export interface Options {
// alias pairs, default: {}
// {"replace-key": "replace-with"}
// if value of k-v pair is absolute path, it will be used directly or the path will be resolved with cwd
alias?: Record<string, string>;
// should this plugin be skipped
skip?: boolean;
// default: process.cwd()
cwd?: string;
}
You can also use syntax *
like @alias/*
:
esbuildPluginAliasPath({
alias: {
'@alias/*': path.resolve(__dirname, './src/alias'),
},
});
NOTE: Only files under the alias
dir will be resolved, the nested file will be ignored instead.
NOTE: In TypeScript project, compilerOptions.paths
in tsconfig.json
will be used by ESBuild automatically, so you will need this plugin only when you're going to replace the alias dynamically.
# clean
ESBuild plugin for cleaning up output/assets before building.
npm install esbuild-plugin-clean --save-dev
pnpm install esbuild-plugin-clean --save-dev
yarn add esbuild-plugin-clean --save-dev
import { build } from 'esbuild';
import { clean } from 'esbuild-plugin-clean';
(async () => {
const res = await build({
entryPoints: ['./demo.ts'],
bundle: true,
outfile: './dist/main.js',
plugins: [
clean({
patterns: ['./dist/*', './dist/assets/*.map.js'],
}),
],
});
})();
# Configuration
This plugin use del (opens new window) under the hood, so you can easily pass del options
to this plugin.
export interface CleanOptions {
/**
* file clean patterns (passed to `del`)
*
* @default: []
*/
patterns?: string | string[];
/**
* use dry-run mode to see what's going to happen
*
* remember to set `verbose: true`
*
* @default: false
*/
dryRun?: boolean;
/**
* extra options passed to `del`
*
* @default {}
*/
options?: DelOptions;
/**
* execute clean sync or async (use `del` or `del.sync` for cleaning up)
*
* @default: true
*/
sync?: boolean;
/**
* do cleaning in start / end / both
* maybe in some strange cases you will need it ? :P
*
* @default: "start"
*/
cleanOn?: 'start' | 'end' | 'both';
/**
* enable verbose logging
*
* @default false
*/
verbose?: boolean;
}
# compress
ESBuild plugin for output compression (gzip
/brotli
).
npm install esbuild-plugin-compress --save-dev
pnpm install esbuild-plugin-compress --save-dev
yarn add esbuild-plugin-compress --save-dev
You must set write
options to be false to use this plugin, as ESBuild only expose outputFile
info when setting false write
options.
import { compress } from 'esbuild-plugin-compress';
import { build } from 'esbuild';
import type { BuildOptions } from 'esbuild';
const baseOptions: BuildOptions = {
entryPoints: ['./src/index.ts'],
outfile: './build/index.js',
};
const compressOptions: BuildOptions = {
...baseOptions,
write: false,
plugins: [
...(baseOptions.plugins ?? []),
compress({
outputDir: 'dist',
}),
],
};
build(baseOptions).catch(() => process.exit(1));
build(compressOptions).catch(() => process.exit(1));
# Configuration
export interface CompressOptions {
// apply gzip compression
// default: true
gzip?: boolean;
// gzip options
// default: {}
gzipOptions?: ZlibOptions;
// apply brotli compression
// default: true
brotli?: boolean;
// brotli options
// default: {}
brotliOptions?: BrotliOptions;
// should remove origin files
// if no compression is specified, origin files will must be generated
// default: false
removeOrigin?: boolean;
// output dir name, relative to outdir/outfile(path.dirname(outfile))
// default: null, will generate compressed file at the same level of output file
outputDir?: string;
}
# copy
ESBuild plugin for assets copy handle.
Since version 1.0.1
, option keepStructure
is supported to control behavior of copying nested files(in both Option
level and AssetPair
level).
Since version 1.1.0
, option resolveFrom
is supported to control base path which is used to caclute the destination of copied file.
npm install esbuild-plugin-copy --save-dev
pnpm install esbuild-plugin-copy --save-dev
yarn add esbuild-plugin-copy --save-dev
import { build } from 'esbuild';
import { copy } from 'esbuild-plugin-copy';
(async () => {
const res = await build({
entryPoints: ['./src/main.ts'],
bundle: true,
outfile: './dist/main.js',
plugins: [
copy({
// this is equal to process.cwd(), which means we use cwd path as base path to resolve `to` path
// if not specified, this plugin uses ESBuild.build outdir/outfile options as base path.
resolveFrom: 'cwd',
assets: {
from: ['./assets/*'],
to: ['./assets', './tmp-assets'],
},
}),
],
});
})();
# Preserve file structure
import { copy } from 'esbuild-plugin-copy';
import { build } from 'esbuild';
(async () => {
const res = await build({
entryPoints: ['./src/index.ts'],
bundle: true,
outfile: './dist/main.js',
watch: true,
plugins: [
copy({
// Not Recommended!
// keepStructure: true,
assets: [
{
from: ['./node_modules/tinymce/skins/**/*'],
to: ['./preserved-structure/skins'],
keepStructure: true,
},
],
}),
],
});
})();
When you set root option
keepStructure: true
, it means keep structure for all asset pairs, but if some of your asset pairs use./path/*
which ends with/*
, defaultglobby
will not expand sub-dircetories like./path/foo
. So it's not recommended to use this in root-level.When you set
AssetPair.keepStructure
, it means keep structure for current assets pair.The final performance of the above configuration is as follows:
|-node_modules/tinymce/skins |--- content |----- dark |----- default |----- document |--- ui |----- oxide |----- oxide-dark
|- dist/preserved-structure/skins |--- content |----- dark |----- default |----- document |--- ui |----- oxide |----- oxide-dark
You can also use patterns with extension names like ./path/**/*.js
# File Glob
Note: This plugin doesnot expand directories by default, which means when you're using pattern dir/*
or dir/*.*
, you will only get the file inside dir/
like dir/index.md
. If you want to match the nested files like dir/path/to/index.md
, you will need to use pattern like dir/**/*
.
If you're using dir/*
and there are no files under this directory, you will got an warning:
i No files matched using current glob pattern: ./node_modules/tinymce/skins/*, maybe you need to configure globby by options.globbyOptions?
# Configuration
type MaybeArray<T> = T | T[];
export interface AssetPair {
/**
* from path is resolved based on `cwd`
*/
from: MaybeArray<string>;
/**
* to path is resolved based on `outdir` or `outfile` in your ESBuild options
*/
to: MaybeArray<string>;
/**
* use Keep-Structure mode for current assets pair
*
* Keep-Structure mode will used for current assets when one of the root-level keepStructure or asset-level keepSructure is true
*
* @default false
*/
keepStructure?: boolean;
}
export interface Options {
/**
* assets pair to copy
* @default []
*/
assets: MaybeArray<AssetPair>;
/**
* execute copy in `ESBuild.onStart` hook(not recommended)
*
* set to true if you want to execute in onStart hook, by default this plugin works in onEnd hook
* @default false
*/
copyOnStart: boolean;
/**
* enable verbose logging
*
* outputs from-path and to-path finally passed to `fs.copyFileSync` method
* @default false
*/
verbose: boolean;
/**
* options passed to `globby` when we 're globbing for files to copy
* @default {}
*/
globbyOptions: GlobbyOptions;
/**
* only execute copy operation once
*
* useful when you're using ESBuild.build watching mode
* @default false
*/
once: boolean;
/**
* use `Keep-Structure` mode for all assets pairs
*
* @default false
*/
keepStructure: boolean;
/**
* base path used to resolve relative `assets.to` path
* by default this plugin use `outdir` or `outfile` in your ESBuild options
* you can specify "cwd" or process.cwd() to resolve from current working directory,
* also, you can specify somewhere else to resolve from.
*
* @default "out"
*/
resolveFrom: 'cwd' | 'out' | string;
/**
* use dry run mode to see what's happening.
*
* remember to keep `verbose` open to see the output.
*
* @default false
*/
dryRun?: boolean;
}
# decorator
ESBuild plugin to handle TypeScript decorators compilation.
- Support compilation by
tsc
/@swc/core
. - Support typescript/swc config file load.
# install peer dependencies
# install @swc/core if you want to use swc as compiler
npm install typescript @swc/core --save-dev
npm install esbuild-plugin-decorator --save-dev
pnpm install esbuild-plugin-decorator --save-dev
yarn add esbuild-plugin-decorator --save-dev
import { build } from 'esbuild';
import { esbuildPluginDecorator } from 'esbuild-plugin-decorator';
(async () => {
const res = await build({
entryPoints: ['./src/main.ts'],
bundle: true,
outfile: './dist/main.js',
plugins: [
esbuildPluginDecorator({
tsconfigPath: 'tsconfig.json',
}),
],
});
})();
# Configuration
export interface ESBuildPluginDecoratorOptions {
// tsconfig.json path
// required even you're using swc as compiler
// if not specified, will search tsconfig.json(relative to cwd option) by default
tsconfigPath?: string;
// swc config path (.swcrc)
swcrcPath?: string;
// force specified compiler for all source code compilation
// (even no decorators are found)
// if set to false, plugin will be skipped when no decorators are found
force?: boolean;
// default: process.cwd()
cwd?: string;
// use typescript or @swc/core for decorator compilation
// default: tsc
compiler?: 'tsc' | 'swc';
// extra compile options
// will override config from config file
tscCompilerOptions?: TSCCompileOptions;
swcCompilerOptions?: SWCCompileOptions;
// verbose logging
// log info about plugin skipped / decorators not found / current configuration
// default: false
verbose?: boolean;
}
# filesize
ESBuild plugin for displaying output file size info.
npm install esbuild-plugin-filesize --save-dev
pnpm install esbuild-plugin-filesize --save-dev
yarn add esbuild-plugin-filesize --save-dev
import { build } from 'esbuild';
import { esbuildPluginFileSize } from 'esbuild-plugin-filesize';
(async () => {
const res = await build({
entryPoints: ['./src/main.ts'],
bundle: true,
outfile: './dist/main.js',
plugins: [esbuildPluginFileSize()],
});
})();
# Configuration
This plugin use filesize (opens new window) for file size formatting under the hood, so you can easily pass execa options to plugin.
export interface ESBuildPluginFileSizeOption {
// show terser minified size
showMinifiedSize?: boolean;
// show gzipped size
showGzippedSize?: boolean;
// show brotli size
showBrotliSize?: boolean;
// show plugin title
showPluginTitle?: boolean;
// filesize format option
// https://www.npmjs.com/package/filesize
format?: FileSizeFormatOption;
// display theme
theme?: 'light' | 'dark';
}
# ignore
ESBuild plugin for require/import ignore.
For more information, see Webpack IgnorePlugin (opens new window).
npm install esbuild-plugin-ignore-module --save-dev
pnpm install esbuild-plugin-ignore-module --save-dev
yarn add esbuild-plugin-ignore-module --save-dev
import { build } from 'esbuild';
import { ignore } from 'esbuild-plugin-ignore-module';
(async () => {
const res = await build({
entryPoints: ['./src/main.ts'],
bundle: true,
outfile: './dist/main.js',
plugins: [
ignore({
ignore: [
// ignore module 'chalk' import in shared.ts files
// file extension is required in RegExp
{
resourceRegExp: /chalk$/,
contextRegExp: /shared.ts$/,
},
// ignore module 'readline' from all import statement
{
resourceRegExp: /readline$/,
},
],
}),
],
});
})();
// demo.ts
import { SHARED } from './shared';
console.log(SHARED);
// shared.ts
import chalk from 'chalk';
import ora from 'ora';
ora().start();
export const SHARED = 'SHARED';
NOTE:
When there're pairs with same
resourceRegExp
, like:ignore: [ { resourceRegExp: /chalk$/, contextRegExp: /shared.ts$/, }, { resourceRegExp: /chalk$/, contextRegExp: /ora/, }, ],
Only the first resolved(start from entry) module will be ignored, in the example above, only
chalk
in shared.ts will be ignored. IfcontextRegExp
is not passed,chalk
imports in all files will be ignored.The reason is that exactly equal
resourceRegExp
will be processed by shared ESBuild filter (opens new window) handler.Remember to add file extensions in RegExp, because ESBuild plugin importer (opens new window) is an absolute full path.
# Configuration
export interface IgnorePattern {
// import/require statement to ignore
resourceRegExp: RegExp;
// importer of resourceRegExp to ignore
// example:
// resourceRegExp: /chalk$/ & contextRegExp: /ora
// this will ignore chalk import in ora internal files
// resourceRegExp: /^\.\/locale$/ & contextRegExp: /moment$/
// this will ignore any './locale' imports in moment internal files
contextRegExp?: RegExp;
}
export interface ESBuildPluginIgnoreOption {
// ignore patterns
ignore?: IgnorePattern | IgnorePattern[];
// verbose logging when module ignored.
verbose?: boolean;
}
# markdown-import
ESBuild plugin for markdown import(.md
files).
npm install esbuild-plugin-markdown-import --save-dev
pnpm install esbuild-plugin-markdown-import --save-dev
yarn add esbuild-plugin-markdown-import --save-dev
import { build } from 'esbuild';
import { markdown } from 'esbuild-plugin-markdown-import';
(async () => {
const res = await build({
entryPoints: ['./src/main.ts'],
bundle: true,
outfile: './dist/main.js',
plugins: [markdown()],
});
})();
// dmeo.ts
import md from 'hello.md';
console.log(md);
// typing.d.ts
declare module '*.md' {
// parsed html
export const html: string;
// raw .md file content
export const raw: string;
// markdown file name (basename)
export const filename: string;
}
// or
declare module '*.md' {
const result: {
raw: string;
html: string;
fileName: string;
};
export default result;
}
MarkDown content will be parsed to JSON and use ESBuild JSON loader:
{
"html": "<h1 id='hello'>Hello</h1>\n",
"raw": "# Hello\r\n",
"filename": "hello.md"
}
# Configuration
This plugin use marked (opens new window) under the hood, so you can easily pass execa options to plugin.
export interface Options {
// marked options, will be passed to marked parser
// default: {}
markedOptions?: MarkedOptions;
// use sync/async parser
// default: true
sync?: boolean;
// extra k-v pairs to push to generated json
// default: {}
extraJSONReturn?: Record<string, unknown>;
// transform marked parsed result(html)
transformParsedResult?: (result: string) => string;
// tranform markdown file raw before marked parse(plain text)
transformRawBeforeParse?: (raw: string) => string;
// tranform markdown file raw after marked parse(plain text)
transformRawAfterParse?: (raw: string) => string;
}
# node-externals
ESBuild plugin for node externals handle.
npm install esbuild-plugin-node-externals --save-dev
pnpm install esbuild-plugin-node-externals --save-dev
yarn add esbuild-plugin-node-externals --save-dev
import { build } from 'esbuild';
import { esbuildPluginNodeExternals } from 'esbuild-plugin-node-externals';
(async () => {
const res = await build({
entryPoints: ['.src/main.ts'],
bundle: true,
outfile: './dist/main.js',
plugins: [
esbuildPluginNodeExternals({
packagePaths: 'package.json',
include: ['SOME_PKG_YOU_WANT_TO_INCLUDE_AT_BUNDLE'],
}),
],
});
})();
# Configuration
export interface Options {
// you package.json path(s)
// will search for 'package.json' in same level with .git
packagePaths?: string | string[];
// should regard dependencies as externals
// default: true
withDeps?: boolean;
// should regard devDependencies as externals
// default: true
withDevDeps?: boolean;
// should regard peerDependencies as externals
// default: true
withPeerDeps?: boolean;
// should regard optionalDependencies as externals
// default: true
withOptDeps?: boolean;
// deps you want to include at bundle
include?: string[];
}
# run
ESBuild plugin that execute output file after build completed.
Note: this plugin require ESBuild version ^0.11.19 for the onEnd
hooks.
This plugin support only single-output(outfile) mode.
npm install esbuild-plugin-run --save-dev
pnpm install esbuild-plugin-run --save-dev
yarn add esbuild-plugin-run --save-dev
import { build } from 'esbuild';
import { run } from 'esbuild-plugin-run';
(async () => {
const res = await build({
entryPoints: ['./src/main.ts'],
bundle: true,
outfile: './dist/main.js',
plugins: [run()],
});
})();
- use
execa.node
as default script executor. - input
rs
/restart
to restart process. - input
cls
/clean
to clear console.
# Configuration
This plugin use execa (opens new window) under the hood, so you can easily pass execa options to plugin.
export interface RunOptions {
// execa options
execaOptions?: Options;
// use customRunner based on execa
customRunner?: (filePath: string) => ChildProcess | ExecaChildProcess<string> | any;
}
// use customRunner
run({
customRunner: (filePath) =>
execa.node(filePath, {
stdio: 'inherit',
}),
}),