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

@broxium/compiler

v1.7.0

Published

Brodox component compiler — TSX to ESM server + client bundles

Readme

@broxium/compiler

The Brodox component compiler. Takes a developer's component source files (TSX/JSX) and compiles them into two versioned ESM bundles — one for server-side rendering and one for browser hydration.

This is an internal package used by the Brodox platform. Developers building components do not use this directly; it is called automatically when a component version is approved in the Brodox dashboard.

Installation

npm install @broxium/compiler

Node.js 20+ is required.


What it does

When a component version is approved, BrodoxCompiler.compile() is called with the component's source files. It produces two output files in LIVE_COMPONENTS_PATH:

| Output file | Purpose | Format | |---|---|---| | {slug}-v{version}.server.esm.js | Loaded by the web engine via Node.js import() for SSR | Pure ESM, Node 20 target, readable (not minified) | | {slug}-v{version}.client.esm.js | Served to the browser for island hydration | Pure ESM, ES2020 / Chrome 90 / Firefox 88 / Safari 14, minified |

Both bundles treat react, react-dom, react/jsx-runtime, and @broxium/runtime as externals — they are never bundled in. The web engine's import map and the browser's @broxium/runtime static file provide these at runtime.


Usage

import { BrodoxCompiler } from '@broxium/compiler'

const compiler = new BrodoxCompiler()

const result = await compiler.compile({
  componentId: 42,
  slug: 'hero-banner',
  version: '1.0.4',
  files: [
    {
      path: 'App.tsx',
      content: `
        import { BrodoxImage } from '@broxium/runtime'
        export default function HeroBanner({ title, image }) {
          return (
            <section>
              <h1>{title}</h1>
              <BrodoxImage src={image} alt={title} width={1280} />
            </section>
          )
        }
      `,
    },
  ],
  outputDir: '/brodox-developer-drive/web-components/live-bundles',
})

console.log(result.serverJsName)  // "hero-banner-v1.0.4.server.esm.js"
console.log(result.clientJsName)  // "hero-banner-v1.0.4.client.esm.js"
console.log(result.serverJsPath)  // "/brodox-developer-drive/.../hero-banner-v1.0.4.server.esm.js"
console.log(result.compiledAt)    // Date object

API

new BrodoxCompiler()

Creates a new compiler instance. The instance is stateless and safe to reuse across multiple calls — the Brodox platform creates a single instance per process.

compiler.compile(input): Promise<CompileOutput>

Compiles a component's source files into server and client ESM bundles.

CompileInput

interface CompileInput {
  componentId: number           // DB row ID — not used in output naming, reserved for future use
  slug: string                  // Component slug, e.g. "hero-banner"
  version: string               // Semver string, e.g. "1.0.4"
  files: Array<{
    path: string                // Relative path within the component, e.g. "App.tsx" or "utils/format.ts"
    content: string             // Full source text
  }>
  outputDir: string             // Absolute path where output files are written
}

CompileOutput

interface CompileOutput {
  serverJsPath: string          // Absolute path to the server bundle
  clientJsPath: string          // Absolute path to the client bundle
  serverJsName: string          // Filename only: "{slug}-v{version}.server.esm.js"
  clientJsName: string          // Filename only: "{slug}-v{version}.client.esm.js"
  compiledAt: Date              // Timestamp of compilation
}

Entry point resolution

The compiler looks for the entry file by checking the files array in this priority order:

App.tsx → App.jsx → App.ts → App.js →
index.tsx → index.jsx → index.ts → index.js

If none of these are found, the compiler throws:

Error: No entry file found in component files for {slug}

Temporary directory

Source files are written to a temporary directory under os.tmpdir() before compilation. The directory is automatically cleaned up after compilation completes (or fails with an error that is re-thrown).


config.json format

Every component must include a config.json file alongside App.tsx. This file defines the props exposed in the website builder's property inspector panel.

The format is a flat object — prop name as key, field definition as value. Do not use a name/slug/props wrapper.

{
  "title": {
    "label": "Title",
    "type": "text",
    "default": "Hello World"
  },
  "count": {
    "label": "Item Count",
    "type": "number",
    "default": 3
  },
  "theme": {
    "label": "Theme",
    "type": "select",
    "options": ["light", "dark"],
    "default": "light"
  }
}

Supported field types

| type | Builder input | Extra keys | |---|---|---| | text | Text input | — | | textarea | Multiline textarea | — | | url | URL input (validated) | — | | number | Number input | — | | select | Dropdown | options: string[] | | color | Color picker + hex | — | | boolean | Checkbox | — | | range | Slider | min, max | | brodox-* | Custom Brodox field | varies |

Optional field keys

| Key | Type | Description | |---|---|---| | label | string | Display name in the inspector panel | | type | string | Input type (required) | | default | any | Initial value when component is first dropped | | render | "client" | "server" | Badge shown in inspector (cosmetic only) |

Common crash: If config.json is not a flat object (e.g. has a top-level name or props array key), the builder will crash with Cannot read properties of undefined (reading 'startsWith') when the component is dragged onto the canvas.


'use client' and 'use server' handling

The two esbuild builds use custom plugins to handle React-style directives:

Server bundle — clientStubPlugin

Any file whose first non-whitespace characters are 'use client' or "use client" is replaced with a stub:

import React from 'react'
export default function ClientStub() { return null }
export function getServerData() { return {} }

This means client-only sub-components silently render nothing on the server, which is correct — their interactive version will be rendered entirely by the browser.

Client bundle — serverStubPlugin

Any file whose first non-whitespace characters are 'use server' or "use server" is replaced with:

export default function ServerStub() { return null }

Server-only code never runs in the browser.

Entry file directive

The directive on the entry file (App.tsx) determines the renderMode stored in the Page Manifest:

| Entry file starts with | renderMode | Server renders | Client hydrates | Use when | |---|---|---|---|---| | 'use client' | client | No | Yes (full) | useState, useEffect, event handlers, browser APIs | | 'use server' | server | Yes (full) | No | Display-only, no interactivity needed | | (nothing) | both | Yes | Yes (islands only) | Static shell with <Client> islands for interactive parts |

Note: These directives are not the same as Next.js. 'use server' here means "exclude from client bundle" — it does not create a server action.

Common mistake — missing 'use client'

Using useState, useEffect, or any hook at the top level of the entry file without 'use client' causes the web engine to crash during SSR:

Cannot read properties of null (reading 'useState')

Fix: add 'use client' as the very first line of App.tsx.

'use client';

import { useState } from 'react';

export default function App() {
  const [count, setCount] = useState(0);
  // ...
}

Sub-file directives

You can split a multi-file component by marking individual files:

App.tsx          ← no directive (both: SSR shell + client hydration)
├── Counter.tsx  ← 'use client'  (stubbed on server, runs in browser)
└── DataTable.tsx← 'use server'  (stubbed in browser, runs on server)

External dependencies

The following packages are always treated as external in both bundles:

| Package | Why external | |---|---| | react | Provided by the web engine's import map / browser global | | react-dom | Same | | react/jsx-runtime | Same | | react/jsx-dev-runtime | Same | | @broxium/runtime | Provided by /static/broxium-runtime.js on the web engine |

Any other import in the component source will be bundled in. This means third-party libraries like date-fns, zod, or clsx are embedded in the output files. Dependencies must be whitelisted in the Brodox platform's allowed-package list before they can be used in a component.


Output file naming

Output files follow a deterministic naming convention:

{slug}-v{version}.server.esm.js
{slug}-v{version}.client.esm.js

Examples:

| Slug | Version | Server file | Client file | |---|---|---|---| | hero-banner | 1.0.4 | hero-banner-v1.0.4.server.esm.js | hero-banner-v1.0.4.client.esm.js | | product-grid | 2.1.0 | product-grid-v2.1.0.server.esm.js | product-grid-v2.1.0.client.esm.js |

Because the version is embedded in the filename, approving a new version always produces new files. The old files remain on disk for rollback purposes and are never overwritten.


Integration with BundleService

In the Brodox platform, BrodoxCompiler is called from BundleService in sub-app-brodox-site-engine-app. BundleService reads all source files from the component's file_path directory on disk and passes them to the compiler:

// BundleService.ts (simplified)
import { BrodoxCompiler } from '@broxium/compiler'

const compiler = new BrodoxCompiler()

async function compileBothBundles(folderPath, slug, version) {
  const files = await readComponentFiles(folderPath)  // reads all .tsx/.jsx/.ts/.js/.css/.json
  const result = await compiler.compile({
    componentId: 0,
    slug,
    version,
    files,
    outputDir: process.env.LIVE_COMPONENTS_PATH,
  })
  return result
}

Error handling

The compiler throws on any of the following conditions:

| Condition | Error message | |---|---| | No entry file found | No entry file found in component files for {slug} | | esbuild server build fails | esbuild error message (TypeScript error, import not found, etc.) | | esbuild client build fails | esbuild error message |

Errors from the server build are thrown before the client build is attempted. The temporary directory is cleaned up even when an error is thrown.

When BundleService catches a compilation error, it blocks the component from being approved and returns the error message to the reviewer.


runtimeServerStubPlugin — @broxium/runtime server stubs

During server bundle compilation, all @broxium/runtime imports are replaced by inline server-safe stubs so the server bundle has zero external dependencies.

Key stub behaviours:

| Export | Server stub renders | |---|---| | BrodoxLink | <a data-brodox-link href={href}> — the data-brodox-link attribute is required for the shell's click interceptor | | BrodoxImage | <img src="/api/image?..."> with srcset, or raw src if direct={true} | | Client | Empty placeholder div + sibling <script type="application/json"> with props | | Server | Transparent passthrough (Fragment) | | useRouter, useParams | Static no-ops (return empty objects) | | BrodoxHead, BrodoxFont | null |

Important: If you compile a component and the rendered <a> tags do not have data-brodox-link, the shell will not intercept clicks on those links and the browser will do a full page reload. This was fixed in v1.3.2 — ensure all projects use @broxium/compiler@^1.3.2 or later. Existing pre-1.3.2 server bundles must be patched manually or recompiled.


Package info

| | | |---|---| | Package | @broxium/compiler | | Version | 1.3.3 | | Formats | ESM (dist/index.mjs), CJS (dist/index.js) | | Types | dist/index.d.ts | | Runtime dependency | esbuild ^0.25 | | Node.js requirement | 20+ | | Side effects | Writes files to outputDir, creates/removes temp directory |

Changelog

| Version | Change | |---|---| | 1.3.3 | BrodoxImage stub: added direct prop support | | 1.3.2 | BrodoxLink stub: added data-brodox-link attribute | | 1.3.1 | Initial public release |