@zoliszabo/esbuild-flex
v0.3.0
Published
A thin wrapper around esbuild for managing multiple build configurations in a single config file
Downloads
54
Maintainers
Readme
esbuild-flex
A thin wrapper around esbuild for managing multiple build configurations in a single config file.
Run different esbuild configurations for different parts of your project - each with its own entry points, output directory, and options. Perfect for projects that need separate builds for client/server code, modern/legacy browsers, or different output formats.
Features
- Multiple configurations (groups) - different settings for different parts of your app;
- Supports all esbuild entryPoints formats (
string[],{in, out}[],Record<string, string>) and options; - Tagging of configurations for selective building.
Installation
npm install --save-dev esbuild-flexUsage
esbuild-flex can be used in two ways:
- CLI - Simple command-line interface for most use cases
- Programmatic API - JavaScript API for advanced scenarios and custom tooling
CLI Usage
The CLI is the simplest way to use esbuild-flex. It reads your config file and runs the build.
1. Create esbuild-flex.config.js
Add the @type comment for full autocomplete and type checking in your editor (see full example):
/** @type {import('esbuild-flex').FlexConfig} */
module.exports = {
// Global defaults (apply to all groups)
target: 'es2018',
sourcemap: false,
minify: true,
groups: [
// Group 1: Simple string array with globs
{
name: 'Main scripts',
entryPoints: [
'src/app.js',
'src/components/*.js',
],
outdir: 'dist/js',
},
// Group 2: Using {in, out} for explicit names
{
name: 'Pages',
entryPoints: [
{ in: 'src/home.ts', out: 'home-page' },
{ in: 'src/about.ts', out: 'about-page' },
],
outdir: 'dist/js',
bundle: true, // Override global setting
},
// Group 3: Record format
{
name: 'Admin',
entryPoints: {
'admin-app': 'src/admin/app.js',
'admin-dashboard': 'src/admin/dashboard.js',
},
outdir: 'dist/admin',
},
// Group 4: Using esbuild's entryNames
{
name: 'Utilities',
entryPoints: ['src/utils/**/*.js'],
outdir: 'dist/utils',
outbase: 'src/utils',
entryNames: '[dir]/[name].min',
},
]
};2. Run the build
# One-time build
npx esbuild-flex
# Watch mode
npx esbuild-flex --watch
# Custom config file
npx esbuild-flex --config=custom.config.js
# Watch with custom config
npx esbuild-flex --watch --config=custom.config.js
# Verbose mode (shows debug logs)
npx esbuild-flex --verbose
# Silent mode (only errors and warnings)
npx esbuild-flex --silent
# Combine options
npx esbuild-flex --watch --verbose --config=custom.config.js3. Add to package.json scripts
{
"scripts": {
"build": "esbuild-flex",
"dev": "esbuild-flex --watch"
}
}CLI Options
| Option | Description |
|--------|-------------|
| --watch | Enable watch mode - automatically rebuild on file changes |
| --config=<path> | Specify a custom config file path (default: esbuild-flex.config.js) |
| --tags=<tag1,tag2> | Build only groups with matching tags (comma-separated) |
| --verbose | Enable verbose logging (shows debug information) |
| --silent | Silent mode (only show errors and warnings) |
Note: --verbose and --silent cannot be used together.
Programmatic API Usage
For advanced use cases, you can use esbuild-flex programmatically in your Node.js scripts. This is useful when you need to:
- Integrate esbuild-flex into custom build tools
- Dynamically generate configurations
- Run builds conditionally based on runtime logic
- Orchestrate complex build pipelines with pre/post-build tasks
Basic Programmatic Usage
const { build } = require('esbuild-flex');
// Using a config file
async function runBuild() {
try {
await build({
configFile: './esbuild-flex.config.js'
});
console.log('Build completed successfully!');
} catch (error) {
console.error('Build failed:', error);
process.exit(1);
}
}
runBuild();Inline Configuration
You can also pass the configuration directly without a config file:
const { build } = require('esbuild-flex');
async function runBuild() {
await build({
config: {
target: 'es2020',
sourcemap: true,
groups: [
{
name: 'App',
entryPoints: ['src/app.js'],
outdir: 'dist',
bundle: true,
}
]
}
});
}
runBuild();Watch Mode Programmatically
const { build } = require('esbuild-flex');
async function startWatching() {
await build({
configFile: './esbuild-flex.config.js',
watch: true
});
console.log('Watching for changes...');
}
startWatching();Filtering by Tags Programmatically
const { build } = require('esbuild-flex');
async function buildProduction() {
await build({
configFile: './esbuild-flex.config.js',
tags: ['production']
});
}
buildProduction();Dynamic Configuration
Generate configurations dynamically based on environment or other factors:
const { build } = require('esbuild-flex');
async function buildForEnvironment(env) {
const isDev = env === 'development';
await build({
config: {
target: isDev ? 'es2020' : 'es2015',
sourcemap: isDev,
minify: !isDev,
groups: [
{
name: `${env} build`,
entryPoints: ['src/app.js'],
outdir: `dist/${env}`,
bundle: true,
define: {
'process.env.NODE_ENV': JSON.stringify(env)
}
}
]
}
});
}
// Usage
buildForEnvironment(process.env.NODE_ENV || 'development');Integration with Custom Build Scripts
const { build } = require('esbuild-flex');
const fs = require('fs').promises;
async function customBuildPipeline() {
// Pre-build tasks
console.log('Running pre-build tasks...');
await fs.mkdir('dist', { recursive: true });
// Run esbuild-flex
console.log('Building with esbuild-flex...');
await build({
configFile: './esbuild-flex.config.js'
});
// Post-build tasks
console.log('Running post-build tasks...');
await fs.copyFile('public/index.html', 'dist/index.html');
console.log('Build pipeline completed!');
}
customBuildPipeline().catch(error => {
console.error('Build pipeline failed:', error);
process.exit(1);
});Using Callbacks
You can use callbacks to track build progress and handle results programmatically:
const { build } = require('esbuild-flex');
await build({
configFile: './esbuild-flex.config.js',
// Called before each group starts building
onBuildStart: (group) => {
console.log(`Building: ${group.name}`);
},
// Called after each group finishes
onBuildEnd: (group, result) => {
if (result.errors.length > 0) {
console.error(`Failed: ${group.name}`);
} else {
console.log(`Success: ${group.name}`);
}
},
// Called after all groups finish (non-watch mode only)
onAllBuildsComplete: (results) => {
console.log(`Built ${results.length} groups`);
}
});See full examples:
API Options
The build() function accepts an options object with the following properties:
| Option | Type | Description |
|--------|------|-------------|
| config | FlexConfig | Inline configuration object (alternative to configFile) |
| configFile | string | Path to config file (default: 'esbuild-flex.config.js') |
| watch | boolean | Enable watch mode (default: false) |
| tags | string[] | Filter groups by tags |
| onBuildStart | (group) => void | Callback invoked before each group starts building |
| onBuildEnd | (group, result) => void | Callback invoked after each group finishes building |
| onAllBuildsComplete | (results) => void | Callback invoked after all groups finish (non-watch mode only) |
Note: You must provide either config or configFile, but not both.
CLI vs Programmatic API: When to Use Which?
Use the CLI when:
- You have a straightforward build setup
- You want to run builds from npm scripts
- You prefer configuration files over code
- You don't need custom pre/post-build logic
Use the Programmatic API when:
- You need to integrate esbuild-flex into custom tooling
- You want to generate configurations dynamically
- You need to run conditional builds based on runtime logic
- You want to handle build results or errors programmatically
- You're building a meta-build tool or framework
Configuration
Root Level Options
Any esbuild option can be set at the root level as a global default:
module.exports = {
target: 'es2020',
sourcemap: true,
minify: false,
bundle: false,
// ... any esbuild option
groups: [ /* ... */ ]
};Group Options
Each group must have:
entryPoints(required) - Any esbuild entryPoints format
Each group can optionally have:
name(string) - For logging purposestags(string[]) - Tags for selective building/watching- Any esbuild option to override global config
esbuild-flex Specific Options
Only three options are specific to esbuild-flex and won't be passed to esbuild:
name- Group label for loggingtags- Array of tags for filtering groupsgroups- Array of group configurations
Everything else is passed directly to esbuild.
Selective Building with Tags
You can tag groups and build only specific groups using the --tags flag:
module.exports = {
groups: [
{
name: 'JavaScript',
tags: ['dev', 'js'],
entryPoints: ['src/**/*.js'],
outdir: 'dist/js',
},
{
name: 'Stylesheets',
tags: ['dev', 'css'],
entryPoints: ['src/**/*.css'],
outdir: 'dist/css',
},
{
name: 'Production bundle',
tags: ['production'],
entryPoints: ['src/app.js'],
outdir: 'dist',
bundle: true,
minify: true,
}
]
};Build only specific groups:
# Build only JavaScript groups
npx esbuild-flex --tags=js
# Build only CSS groups
npx esbuild-flex --tags=css
# Build groups tagged with 'dev' (both JS and CSS)
npx esbuild-flex --tags=dev
# Build multiple tags (groups matching ANY of these tags)
npx esbuild-flex --tags=js,css
# Watch only production builds
npx esbuild-flex --watch --tags=productionNote: Groups without tags will only be built when no --tags filter is specified.
Examples
Example 1: Non-bundled scripts with minification
module.exports = {
minify: true,
groups: [
{
name: 'Legacy scripts',
entryPoints: ['public/js/*.js'],
outdir: 'public/dist/js',
}
]
};Example 2: Bundled app with source maps
module.exports = {
bundle: true,
sourcemap: true,
groups: [
{
name: 'App bundle',
entryPoints: ['src/index.js'],
outdir: 'dist',
}
]
};Example 3: Multiple targets
module.exports = {
groups: [
{
name: 'Modern browsers',
entryPoints: ['src/app.js'],
outdir: 'dist/modern',
target: 'es2020',
},
{
name: 'Legacy browsers',
entryPoints: ['src/app.js'],
outdir: 'dist/legacy',
target: 'es2015',
}
]
};Example 4: With plugins (e.g., SCSS)
const { sassPlugin } = require('esbuild-sass-plugin');
module.exports = {
groups: [
{
name: 'Styles',
entryPoints: ['src/styles/*.scss'],
outdir: 'dist/css',
plugins: [sassPlugin()],
}
]
};License
MIT
Contributing
Issues and PRs welcome!
Note: This README was mostly AI-generated and while it has been verified, it may contain errors or inaccuracies. If you find any issues, please report them or submit a PR.
