npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@zoliszabo/esbuild-flex

v0.3.0

Published

A thin wrapper around esbuild for managing multiple build configurations in a single config file

Downloads

54

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-flex

Usage

esbuild-flex can be used in two ways:

  1. CLI - Simple command-line interface for most use cases
  2. 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.js

3. 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 purposes
  • tags (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 logging
  • tags - Array of tags for filtering groups
  • groups - 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=production

Note: 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.