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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@selfage/bundler_cli

v5.7.2

Published

CLI for bundling and running bundled frontend or backend TypeScript files.

Downloads

32

Readme

@seflage/bundler_cli

Provides an extremely opinionated bundling tool for developing frontend and backend in TypeScript only, powered by browserify and uglify-js, supporting importing asset files without extra plugins. Command line tool name is bundage.

Note that despite TypeScript can compile with various options, we expect you to set "module": "commonjs" and "moduleResolution": "node", due to the use of browserify.

Install

npm install @selfage/bundler_cli

Quick Start

# Run TypeScript in Node.js (compile + bundle + execute)
bundage nrun my_script.ts

# Run tests in headless Chrome (compile + bundle + test in real browser)
bundage prun my_test.ts

# Debug tests visually in Chrome
bundage prun my_test.ts --no-headless --debug

# Bundle multiple web apps for production
bundage bwa -o ./dist

# Bundle a Node.js server with assets
bundage bns server.ts server.js -t ./dist

💡 Tip: Run bundage <command> -h to see all options for any command.

Quick Decision: Is This Tool Right for You?

✅ Use bundage if you:

  • Want to write everything in TypeScript (no HTML/CSS files)
  • Prefer programmatic DOM creation over templates
  • Value type safety over traditional web development patterns
  • Are building data-driven UIs or Node.js services

❌ Use webpack/Vite instead if you:

  • Need to write HTML templates and CSS files
  • Use frameworks like React, Vue, Angular, or Svelte
  • Want CSS preprocessors (Sass, Less, PostCSS)
  • Prefer the traditional separation of HTML/CSS/JS

Still unsure? Read the Philosophy section below.

Philosophy: TypeScript-First, No HTML/CSS Files

Everything is TypeScript

Unlike traditional web bundlers (webpack, Vite, Rollup), bundage assumes all your UI is created programmatically in TypeScript. There are no separate HTML or CSS files to author. After all, if I can develop backend in TypeScript only, why not for frontend?

What bundage does:

// app.ts - Your entire application
import { E } from '@selfage/element/factory';
import { Ref } from '@selfage/ref';
import image = require('./logo.png');

// Create DOM elements programmatically
let button = new Ref<HTMLButtonElement>();
let container = E.div(
  {
    class: 'container',
    style: `background: #f0f0f0; padding: 20px;`
  },
  E.h1({}, E.text('My App')),
  E.img({ src: image }),
  E.button({ ref: button }, E.text('Click Me'))
);
button.val.addEventListener('click', () => alert('Clicked!'));

document.body.append(container.element);

The bundler generates a minimal HTML shell that just loads your bundled JS:

<!-- Generated by bundage -->
<html>
  <head><script src="bundle.js"></script></head>
  <body></body>
</html>

Comparison with Other Bundlers

| Aspect | bundage (this) | webpack/Vite/Rollup | |--------|----------------|---------------------| | HTML Files | ❌ Auto-generated shell only | ✅ Author index.html directly | | CSS Files | ❌ Inline styles or CSS-in-JS | ✅ Import .css files | | DOM Creation | ✅ Programmatic (TypeScript) | 🔧 Template-based (HTML) | | Style Approach | TypeScript strings/objects | CSS modules, PostCSS, Sass | | Component Model | Type-safe factory functions | JSX, Vue templates, Svelte | | Asset Imports | ✅ Images, fonts, etc. | ✅ Images, CSS, fonts, etc. |

Traditional Approach (webpack/Vite)

<!-- index.html - You write this -->
<html>
  <body>
    <div class="container">
      <h1>My App</h1>
      <img src="./logo.png">
      <button id="myBtn">Click Me</button>
    </div>
  </body>
</html>
/* style.css - You write this */
.container {
  background: #f0f0f0;
  padding: 20px;
}
// app.ts - Just add behavior
document.getElementById('myBtn').onclick = () => alert('Clicked!');
// Entry point
import './style.css';
import './app.ts';

Why This Matters

✅ You should use bundage if:

  • You prefer type-safe DOM manipulation (no querySelector runtime errors)
  • You want everything in TypeScript (no context switching between HTML/CSS/TS)
  • You use component libraries like @selfage/element or similar
  • You're building programmatic UIs or data-driven interfaces
  • You want to avoid the complexity of JSX/template compilation

❌ You should NOT use bundage if:

  • You prefer writing HTML templates directly
  • Your designers provide static HTML/CSS files
  • You want to use CSS preprocessors (Sass, Less, PostCSS)
  • You're using frameworks like React, Vue, Angular, or Svelte
  • You need existing HTML/CSS from templates or themes

Asset Handling

What IS supported:

// Images, fonts, data files - any binary assets
import logoPath = require('./logo.png');
import fontPath = require('./font.woff2');
import dataPath = require('./config.json');

// Use in your TypeScript-generated DOM
E.img({ src: logoPath });

What is NOT supported:

// ❌ Cannot import CSS files
import './styles.css';  // Won't work

// ❌ Cannot use HTML files as templates
import template from './template.html';  // Won't work

Solution: Write styles inline or use CSS-in-JS:

// Inline styles
E.div(
  { style: `color: blue; font-size: 16px;` },
  E.text('Styled content')
);

// Or style programmatically
let div = E.div({}, E.text('Content'));
div.element.style.color = 'blue';
div.element.style.fontSize = '16px';

Real-World Example

bundage way:

// main.ts
import { E } from '@selfage/element/factory';

class App {
  public constructor(document: Document) {
    document.body.append(
      E.div(
        {
          class: 'app',
          style: 'max-width: 800px; margin: 0 auto;'
        },
        E.div(
          {
            class: 'header',
            style: 'background: #333; color: white; padding: 20px;'
          },
          E.h1({}, E.text('My application'))
        )
      )
    );
  }
}

new App(document);

Traditional way (Vite + React):

// App.tsx
import './App.css';

export function App() {
  return (
    <div className="app">
      <div className="header">
        <h1>My Application</h1>
      </div>
    </div>
  );
}
/* App.css */
.app { max-width: 800px; margin: 0 auto; }
.header { background: #333; color: white; padding: 20px; }

Summary

bundage is designed for developers who want to write everything in TypeScript, with type safety from top to bottom. If you prefer the traditional separation of HTML/CSS/JS, or use frameworks that rely on templates, this tool is not for you. Use webpack, Vite, or Rollup instead.

Why This Approach Excels for Testing

The TypeScript-first philosophy makes browser testing extremely simple:

Testing with bundage prun

// my_component_test.ts
import { E } from '@selfage/element/factory';

// Create component
let button = E.button({}, E.text('Click'));
button.addEventListener('click', () => alert('works!'));
document.body.append(button);

// Test it
button.click();  // Type-safe!
console.log('Button text:', button.textContent);

// Test with real assets
import imagePath = require('./test-image.png');
let img = E.img({ src: imagePath });
document.body.append(img);
console.log('Image loaded from:', img.src);
# Run in headless Chrome with one command
bundage prun my_component_test.ts -a .png

# Debug visually with Chrome open
bundage prun my_component_test.ts -a .png --no-headless --debug

vs. Traditional Testing (Jest + React Testing Library):

// Setup required: jest.config.js, babel config, etc.
import { render, fireEvent } from '@testing-library/react';

test('button works', () => {
  const { getByText } = render(<Button>Click</Button>);
  fireEvent.click(getByText('Click'));
  // Mock DOM, no real rendering
});

Advantages

| Feature | bundage prun | Jest + Testing Library | |---------|--------------|----------------------| | Setup | Zero config | Complex config files | | DOM | Real Chrome | jsdom (limited) | | Assets | Real loading | Mocked | | Debugging | Chrome DevTools | Node debugger | | Visual | --no-headless flag | Not possible | | Type Safety | Full TypeScript | Partial (test utils) |

Testing Comparison

bundage approach:

// Everything is type-safe TypeScript
import { E } from '@selfage/element/factory';
import { Ref } from '@selfage/ref';

let button = new Ref<HTMLButtonElement>();
let app = E.div(
  {},
  E.button({ ref: button }, E.text('Click'))
);
button.val.addEventListener('click', handleClick);

// Tests run in real Chrome
bundage prun test.ts -a .png .css --no-headless

Traditional approach:

// JSX needs compilation, types are partial
import { render } from '@testing-library/react';

function App() {
  return <div><button onClick={handleClick}>Click</button></div>;
}

// Tests run in jsdom (not real browser)
jest test.ts

Commands

bundage provides four main commands. Use -h flag to see detailed options for each:

bundage nrun -h    # Run in Node.js
bundage prun -h    # Run in Puppeteer (headless Chrome)
bundage bwa -h     # Bundle Web Apps
bundage bns -h     # Bundle Node Server

Command Overview

bundage nrun <sourceFile> [args...] (alias: runInNode)

  • Compiles, bundles, and runs TypeScript in Node.js
  • Pass-through args: bundage nrun script.ts -- --arg1 value1

bundage prun <sourceFile> [args...] (alias: runInPuppeteer)

  • Runs in headless Chrome with real DOM (based on @selfage/puppeteer_test_executor)
  • Additional flags: -p <port> (default: 8000), -nh (visible browser mode)
  • Pass-through args: bundage prun test.ts -- --test-flag

bundage bwa (alias: bundleWebApps)

  • Bundles multiple SPAs from config file (default: ./web_app_entries.yaml)
  • Generates HTML, minifies, and compresses with Gzip
  • Config schema: WebAppEntries
  • Additional flags: -ec <configFile>, -o <outDir>

bundage bns <serverSourceFile> <serverOutputFile> (alias: bundleNodeServer)

  • Bundles Node.js server and copies assets (NPM modules kept as external dependencies)
  • Additional flags: -f <fromDir>, -t <toDir>

Common Options

These options are available across most commands:

  • -e, --extra-files <files...> - Extra TypeScript files to bundle before the source file
  • -i, --inline-js <code...> - Inline JavaScript code to bundle before all files
  • -a, --asset-exts <exts...> - File extensions to treat as assets (e.g., -a .png .jpg)
    • Alternatively: add "assetExts": [".png", ".jpg"] in your package.json
  • -s, --skip-minify - Skip minification (useful for debugging)
  • -d, --debug - Include inline source maps and source code
  • -c, --tsconfig-file <file> - Path to tsconfig.json (defaults to ./tsconfig.json)

Options explained

Extra file

See this answer for how to use extra files to properly define global environment variable with the help of globalThis.

Asset files

As explained in all helper manuals, you can also import imagePath = require('./image.png'); with -a .png .gif .jpg flag, which doesn't really import the image but only import its path. When bundle for Node, it's the relative path from the bundled output file. When bundle for browser, it's the url path that is expected to be mapped to a relative path from the root directory. E.g., the url path would work if you start a http-server at --root-dir. Now you can load images, CSS files or assets without worrying about their actual URL paths -- they will be inferred at bundling time.

Equivalently, you can define "assetExts": [".png", ".gif", ".jpg"] in your package.json file to save you from typing the list everytime.

Debug

Note that --debug doesn't guarantee stack traces will be mapped to TypeScript source code. You could consider using source-map-support package. E.g., you can import 'source-map-support/register'; in your main file.

Puppeteer executor environment

Puppetter is essentially a headless Chrome. The term "Puppeteer executor environment" refers to the runtime environment provided by $ bundage prun or $ bundage pexe which is based on Puppeteer and provides APIs for testing purposes within browser context.

Pass-through arguments

Pass-through arguments are made available by writing them to the temp HTML file, which can then be accessed by the JS code running in it. See @selfage/puppeteer_executor_api#access-argv for more details.

Function APIs

Functions are made avaialble thanks to Puppeteer's exposeFunction. See @selfage/puppeteer_executor_api#all-apis for all available APIs. It's easier to use with @selfage/puppeteer_test_runner which cleans up after each test, e.g. closes the browser.

General API access

Each sub-command corresponds to an API as the following.

runInNode -> import { runInNode } from '@selfage/bundler_cli/runner_in_node';

runInPuppeteer -> import { runInPuppeteer } from '@selfage/bundler_cli/runner_in_puppeteer';

bundleWebApps -> import { bundleWebApps } from '@selfage/bundler_cli/web_apps_bundler';

bundleNodeServer -> import { bundleNodeServer } from '@selfage/bundler_cli/node_server_bundler';