Command-line tool for creating and managing SibuJS projects.
npm install -g sibujs-cli
sibujs create my-appOr use it directly with npx (no install needed):
npx sibujs create my-appScaffold a new SibuJS project with Vite and TypeScript.
sibujs create my-appWithout flags the simplest possible app is created. Add flags to opt into features:
| Flag | Description |
|---|---|
--ui [theme] |
Add sibujs-ui with a theme color (includes Tailwind CSS). Themes: default, blue, green, red, orange, amber, yellow, teal, purple, violet, rose |
--router |
Add routing with example pages |
--tailwind |
Add Tailwind CSS without sibujs-ui |
Examples:
# Simplest app — no extras
sibujs create my-app
# With sibujs-ui using the blue theme (recommended)
sibujs create my-app --ui blue
# Full-featured project
sibujs create my-app --ui violet --router
# Tailwind only, no UI library
sibujs create my-app --tailwindThe generated project includes:
vite.config.ts— Vite dev server with optional Tailwind plugintsconfig.json— TypeScript with strict mode and bundler resolutionsrc/main.ts— Entry point that mounts the root componentsrc/App.ts— Starter app with a counter and todo list demosrc/app.css— Tailwind imports or base reset styles
When --router is selected the project also includes:
src/router.ts— Router setup with route definitionssrc/auth.ts— Simple auth state for the protected route examplesrc/pages/Home.ts— Home pagesrc/pages/About.ts— About pagesrc/pages/Login.ts— Login pagesrc/pages/Dashboard.ts— Protected dashboard with nested routes (Overview, Settings) usingOutlet
When --ui is selected, sibujs-ui and the default theme are added automatically.
Generate a new component file.
sibujs generate component MyButton
sibujs g component NavbarCreates a component file in src/components/ (if the directory exists) or src/. Names are converted to PascalCase automatically.
| Type | Description |
|---|---|
component |
Creates a SibuJS component function |
The name becomes both a filename and a function declaration, so it is
validated rather than coerced. A name is accepted when it is one or more
alphanumeric words separated by - or _, starting with a letter:
sibujs g component button # -> src/Button.ts export function Button()
sibujs g component my-card # -> src/MyCard.ts export function MyCard()
sibujs g component user_profile # -> src/UserProfile.ts export function UserProfile()Anything else is refused with a nonzero exit code, and nothing is written:
| Rejected | Example |
|---|---|
| path separators | x/y, x\y |
| traversal segments | ../Outside, .., ./x |
| absolute or drive paths | /abs/path, C:\outside |
| whitespace | my button |
| leading digits | 123-widget |
| dots | component.name |
| quotes and template markers | component", component` |
| control and NUL characters | comp\u0000onent |
| empty name | "" |
The resolved path is additionally checked to be a direct child of the output directory, on both POSIX and Windows path semantics, before any write happens. An existing component is never overwritten.
Reserved words are accepted because normalization capitalizes them into legal
identifiers: class becomes Class, and export function Class() is valid.
Start the Vite development server with hot module replacement.
sibujs dev
sibujs dev --port 4000
sibujs dev --host # expose on all interfaces
sibujs dev --host 0.0.0.0| Flag | Description |
|---|---|
--port <port> |
Port number |
--host [address] |
Host address (bare --host exposes on 0.0.0.0) |
Build the project for production using Vite.
sibujs build
sibujs build --ssr| Flag | Description |
|---|---|
--ssr |
Build for server-side rendering |
Serve the production build locally for testing.
sibujs preview
sibujs preview --port 5000| Flag | Description |
|---|---|
--port <port> |
Port number |
--host [address] |
Host address |
dev, build and preview run the Vite installed in your project. It is
resolved as a package, so pnpm, Yarn and hoisted monorepo layouts all work, and
it is executed directly with Node — never through a shell, and never through
npx, which would silently download Vite from the registry on a project that
does not have it. If Vite is missing, the command fails with a nonzero exit code
and tells you to install it.
Because no shell is involved, --host values are passed to Vite verbatim as a
single argument. Shell metacharacters in a host (;, &, |, $(...),
backticks, redirection, spaces) are inert text, not commands. Any valid IPv4,
IPv6, hostname or wildcard value is accepted unchanged.
--port is validated before anything is spawned: it must be a plain integer
from 1 to 65535. 0, negatives, decimals, values above 65535, empty values and
anything with extra characters are rejected with a clear message and a nonzero
exit code.
Lint source files for SibuJS best practices. Scans src/ by default, or specify files explicitly.
sibujs lint
sibujs lint src/App.ts src/components/Nav.tsBuilt-in rules:
| Rule | Description |
|---|---|
no-hooks-in-conditionals |
Prevents calling reactive primitives (signal, effect, derived, etc.) inside if/else branches, ternaries and &&/` |
no-direct-dom-mutation |
Warns against .innerHTML / .outerHTML assignment — use reactive bindings instead |
each-requires-key |
Ensures each() calls include a key option for efficient list updates |
Violations fail the command with exit code 1. Generated projects wire
sibujs lint into their lint script, and a lint step that always exits 0
gives CI false confidence.
sibujs lint # exits 1 if anything is reported
sibujs lint --warn-only # reports the same findings, exits 0The linter parses with the TypeScript compiler rather than scanning text, so
comments, strings, template literals, regular expressions and property names are
never mistaken for real code. TypeScript is resolved at runtime from the project
being linted (every project sibujs create generates has it) and is declared as
an optional peer dependency; if it cannot be found, sibujs lint says so and
exits nonzero rather than guessing.
each-requires-key accepts a key only when it can establish one statically —
an object literal with a key property, either { key: fn } or { key }.
A missing third argument, undefined, an object without key, a spread, or an
options variable whose contents are unknown are all reported. The rule never
assumes an opaque value supplies a key, because a missing key degrades list
reconciliation silently at runtime. Suppress a known-good dynamic case with a
comment:
// sibujs-disable-next-line each-requires-key
each(items, renderItem, optionsBuiltElsewhere);Directives are read from real comments only. They are extracted from the
parser's comment trivia, so text that merely looks like a directive inside a
string, template literal, regular expression, JSX text or JSX attribute has no
effect — including text that also contains // or /* */.
const note = "sibujs-disable"; // just a string; suppresses nothing
const re = /sibujs-disable/; // just a regex; suppresses nothingThe grammar is:
<directive> [ <rule-name> ] [ "--" <reason> ]
<directive> ::= sibujs-disable | sibujs-disable-next-line
<rule-name> ::= no-hooks-in-conditionals
| no-direct-dom-mutation
| each-requires-key
element.innerHTML = html; // sibujs-disable
element.innerHTML = html; // sibujs-disable no-direct-dom-mutation
// sibujs-disable-next-line
element.innerHTML = html;
// sibujs-disable-next-line no-direct-dom-mutation -- markup is trusted here
element.innerHTML = html;| Behavior | Rule |
|---|---|
sibujs-disable |
suppresses findings on the line the comment ends on, and only that line |
sibujs-disable-next-line |
suppresses findings on the immediately following physical line; blank lines are not skipped |
| a named rule | suppresses only that rule, never the others |
| no named rule | suppresses every rule on the targeted line |
-- |
everything after it is a free-text reason and is ignored |
Matching is token-based, not substring-based. None of these is a directive:
not-sibujs-disable, sibujs-disabled, sibujs-disable-something-else,
sibujs-disable-next-lines.
An unknown rule name makes the directive invalid, and it suppresses nothing — a typo must never silently switch off every rule.
Block comments work when the directive is the comment's only content, so both of these are directives:
/* sibujs-disable-next-line */
element.innerHTML = html;
/*
* sibujs-disable-next-line
*/
element.innerHTML = html;A block comment that mixes the directive with prose — a JSDoc description, for example — is not a directive, so documentation that mentions the syntax cannot disable a rule by accident.
Analyze the bundle size impact of all SibuJS and sibujs-ui imports in your project.
sibujs analyzeOutput shows each imported API, its usage count, and estimated tree-shaken size. Tag factories (div, span, button, etc.) share a single factory function so they add near-zero cost per additional tag.
sibujs create my-app --tailwind --router
cd my-app
sibujs dev # develop with HMR
sibujs g component MyHeader # generate a component
sibujs lint # check for common mistakes
sibujs analyze # review bundle impact
sibujs build # production build
sibujs preview # test the production build locally-
Node.js >= 22.12.0
Scaffolded projects depend on
sibujs4.0, which needs >= 22.3.0 for its SSR request isolation, and on Vite 8, which needs^20.19.0 || >= 22.12.0. The intersection is 22.12.0, and that is what both this CLI and the generatedpackage.jsondeclare. Below it, npm silently skips Vite's native bundler binding and the firstbuildfails with a "Cannot find native binding" error that says nothing about the real cause.
MIT