CLI Reference
Juntos CLI Reference
The juntos command provides Rails-like commands for development, testing, building, and deployment. It can be invoked via:
npx juntos— Run from anywhere (auto-installs required packages)bin/juntos— Binstub in Rails projects (delegates to npx)
The CLI automatically installs required npm packages based on your options. For example, juntos dev -d dexie installs dexie if not present.
Table of Contents
- Juntos CLI Reference
Getting Started
Install a Demo
Download and set up a complete demo application:
npx github:ruby2js/juntos --demo blog # Install blog demo
npx github:ruby2js/juntos --demo blog my-blog # Install to my-blog/
npx github:ruby2js/juntos --list-demos # List all demos
Available demos: blog, chat, notes, photo-gallery, workflow, ssg-blog, astro-blog
Initialize in Existing Project
Add Juntos to an existing project:
npx github:ruby2js/juntos init # Current directory
npx github:ruby2js/juntos init my-app # Specific directory
This creates the configuration files needed for Juntos:
package.json(or merges dependencies into existing)vite.config.jsvitest.config.jstest/setup.mjsbin/juntos
juntos dev
Start a development server with hot reload.
npx juntos dev [options]
Options:
| Option | Description |
|---|---|
-d, --database ADAPTER |
Database adapter (dexie, sqlite, pglite, etc.) |
-p, --port PORT |
Server port (default: 5173) |
-o, --open |
Open browser automatically |
-h, --help |
Show help |
Examples:
npx juntos dev # Uses database.yml settings
npx juntos dev -d dexie # Browser with IndexedDB
npx juntos dev -d sqlite # Node.js with SQLite
npx juntos dev -p 8080 # Custom port
npx juntos dev -o # Open browser automatically
What it does:
- Loads database configuration (from
-dflag orconfig/database.yml) - Installs required packages if missing (e.g.,
dexiefor IndexedDB) - Starts Vite dev server with hot module reloading
- Watches Ruby files for changes and retranspiles automatically
juntos test
Run tests with Vitest.
npx juntos test [options] [files...]
Options:
| Option | Description |
|---|---|
-d, --database ADAPTER |
Database adapter for tests |
-h, --help |
Show help |
Examples:
npx juntos test # Run all tests
npx juntos test articles.test.mjs # Run specific test file
npx juntos test -d sqlite # Run tests with SQLite
npx juntos test test/models/ # Run tests in directory
What it does:
- Loads database configuration
- Installs vitest and database packages if missing
- Runs
vitest runwith any additional arguments passed through
This mirrors bin/rails test — tests can be run with Rails (bin/rails test), with Juntos (npx juntos test), or directly with Vitest (npx vitest).
juntos e2e
Run end-to-end tests with Playwright. Transpiles test/system/*.rb files to Playwright tests and runs them against a real browser.
npx juntos e2e [options] [args...]
Options:
| Option | Description |
|---|---|
-d, --database ADAPTER |
Database adapter for tests |
-h, --help |
Show help |
Additional arguments are passed through to npx playwright test (e.g., --headed, --ui, --workers=1).
Examples:
npx juntos e2e # Run all e2e tests
npx juntos e2e --headed # Run with visible browser
npx juntos e2e --ui # Open Playwright UI mode
npx juntos e2e -d sqlite # Run with SQLite database
What it does:
- Transpiles
test/system/*.rbfiles to.spec.mjs(Playwright format) - Installs
@playwright/testand Chromium if not present - Generates
playwright.config.jsif missing (configurestest/system/as test dir, startsjuntos devas web server) - Runs
npx playwright testwith any additional arguments
Two-tier testing model:
The same test/system/*.rb source files produce two outputs:
| Command | Output | Extension | Runner |
|---|---|---|---|
juntos test |
Vitest + jsdom | .test.mjs |
Fast, every commit |
juntos e2e |
Playwright | .spec.mjs |
Thorough, periodic |
See Testing for the full transform mapping and defined? Playwright guard.
juntos build
Build the app for deployment.
npx juntos build [options]
Options:
| Option | Description |
|---|---|
-d, --database ADAPTER |
Database adapter |
-t, --target TARGET |
Build target (browser, node, bun, vercel, cloudflare) |
-e, --environment ENV |
Environment (default: development) |
--sourcemap |
Generate source maps |
--base PATH |
Base public path for assets (e.g., /demos/blog/) |
-h, --help |
Show help |
Examples:
npx juntos build -d dexie # Browser build
npx juntos build -e production # Production (bundled, minified)
npx juntos build -t vercel -d neon # Vercel Edge build
npx juntos build -t cloudflare -d d1 # Cloudflare Workers build
npx juntos build -d dexie --base /app/ # Serve from subdirectory
npx juntos build --sourcemap # Include source maps
Output:
Creates the dist/ directory containing the built application. Production builds (-e production) are bundled, tree-shaken, minified, and fingerprinted by Vite.
juntos eject
Write transpiled JavaScript files to disk. Useful for debugging transformation issues or migrating away from Ruby source.
npx juntos eject [options]
Options:
| Option | Description |
|---|---|
--output, --out DIR |
Output directory (default: ejected/) |
-d, --database ADAPTER |
Database adapter |
-t, --target TARGET |
Build target |
--base PATH |
Base public path |
--include PATTERN |
Include only matching files (can be repeated) |
--exclude PATTERN |
Exclude matching files (can be repeated) |
--only FILES |
Comma-separated list of files to include |
-h, --help |
Show help |
Examples:
npx juntos eject # Output to ejected/
npx juntos eject --output dist/js # Custom output directory
npx juntos eject -d sqlite -t node # Eject for Node.js target
# Selective eject with filtering
npx juntos eject --include "app/models/*.rb"
npx juntos eject --include "app/views/articles/**/*" --exclude "**/test_*"
npx juntos eject --only app/models/article.rb,app/models/comment.rb
Filtering:
The --include, --exclude, and --only options let you eject a subset of files:
--include PATTERN— Only include files matching the pattern (can be repeated)--exclude PATTERN— Exclude files matching the pattern (can be repeated)--only FILES— Comma-separated list of specific files to include
Patterns support glob syntax:
*— Match any characters except/**— Match any characters including/?— Match a single character
Filtering in ruby2js.yml:
Instead of command-line flags, you can configure filtering in config/ruby2js.yml.
Top-level include/exclude patterns apply to both the Vite plugin and eject:
# Top-level: applies to Vite dev server AND eject
include:
- app/models/*.rb
- app/views/articles/**/*
- config/routes.rb
exclude:
- "**/*_test.rb"
eject:
output: ejected
Note: Migrations are never filtered — all migrations run regardless of include/exclude patterns, since excluded models may still depend on the schema they define.
CLI flags take precedence over the config file. This is useful for:
- Converting piece by piece: Start with a few models, verify they work, add more
- Partial migration: Only eject the parts of the app you want to convert
- Debugging: Isolate a specific file to inspect its transpilation
What it does:
- Transforms Ruby source files (models, controllers, views, routes, migrations, seeds)
- Applies include/exclude filters if specified
- Writes individual JavaScript files to the output directory
- Generates index files and runtime configuration
Output structure:
ejected/
app/
models/ # Transpiled models + index.js
helpers/ # Transpiled application helpers
views/ # Transpiled ERB templates
controllers/ # Transpiled Rails controllers
javascript/
controllers/ # Transpiled Stimulus controllers
config/
routes.js # Transpiled routes
db/
migrate/ # Transpiled migrations + index.js
seeds.js # Transpiled seeds
lib/
rails.js # Runtime library
active_record.mjs
Unlike juntos build, which produces bundled output optimized for deployment, eject produces unbundled individual files that mirror the source structure. This makes it useful for:
- Debugging: Inspect exactly what the Vite plugin produces
- Migration: Use the JavaScript output as a new codebase, leaving Ruby behind
- Incremental adoption: Convert your app piece by piece
juntos up
Build and run locally. Combines build and server in one command.
npx juntos up [options]
Options:
| Option | Description |
|---|---|
-d, --database ADAPTER |
Database adapter |
-t, --target TARGET |
Runtime target (browser, node, bun, deno) |
-p, --port PORT |
Server port (default: 3000) |
-h, --help |
Show help |
Examples:
npx juntos up -d dexie # Browser with IndexedDB
npx juntos up -d sqlite # Node.js with SQLite
npx juntos up -t bun -d postgres # Bun with PostgreSQL
What it does:
- Builds the app to
dist/ - Starts a preview server (browser) or runtime server (Node/Bun/Deno)
juntos server
Start production server (requires prior build).
npx juntos server [options]
Options:
| Option | Description |
|---|---|
-t, --target TARGET |
Runtime target (browser, node, bun, deno) |
-p, --port PORT |
Server port (default: 3000) |
-e, --environment ENV |
Environment (default: production) |
-h, --help |
Show help |
Examples:
npx juntos server # Start preview server
npx juntos server -t node # Start Node.js server
npx juntos server -p 8080 # Custom port
juntos render
Render pages without starting a server. Loads the app via Vite’s SSR module system, connects to the database, and dispatches GET requests through the router.
npx juntos render [options] PATH [PATH...]
Options:
| Option | Description |
|---|---|
-d, --database ADAPTER |
Database adapter |
-t, --target TARGET |
Build target |
--html |
Output full HTML content |
--check |
Only check if pages render (exit 0/1) |
--search TEXT |
Search for text in rendered output |
-v, --verbose |
Show detailed information |
-h, --help |
Show help |
Examples:
npx juntos render / # Summary: status, path, size
npx juntos render /studios /people /heats # Render multiple pages
npx juntos render --html /studios # Print full HTML to stdout
npx juntos render --check / /studios /people # Silent pass/fail (for CI)
npx juntos render --search "Students" /people # Check if text appears in output
Output modes:
| Mode | Behavior |
|---|---|
| (default) | 200 OK /path (12.3KB) per page |
--html |
Full HTML to stdout (pipe to file or tool) |
--check |
Silent; exit 0 if all pages render, 1 if any fail |
--search TEXT |
Reports whether text is found in each page |
What it does:
- Creates a Vite dev server in middleware mode (transforms
.rb/.erbon-the-fly) - Loads routes via
ssrLoadModule('config/routes.rb') - Connects to the database and runs any pending migrations
- For each path: matches route, calls controller action, wraps in layout, outputs result
This is useful for:
- CI checks:
juntos render --check /to verify pages render without starting a server - Content search:
juntos render --search "expected text" /pathfor regression checks - Debugging:
juntos render --html /path | grep patternto inspect rendered output - Smoke tests:
juntos render / /studios /peopleto quickly verify multiple pages
juntos db
Database management commands. Supports Rails-style colon syntax (db:migrate) or space syntax (db migrate).
npx juntos db <command> [options]
npx juntos db:command [options]
Commands:
| Command | Description |
|---|---|
migrate |
Run database migrations |
seed |
Run database seeds |
prepare |
Migrate + seed if fresh database |
reset |
Drop, create, migrate, and seed |
create |
Create database (D1, Turso) |
drop |
Delete database (D1, Turso, SQLite) |
Options:
| Option | Description |
|---|---|
-d, --database ADAPTER |
Database adapter (overrides database.yml) |
-e, --environment ENV |
Environment (default: development) |
-h, --help |
Show help |
db:migrate
Run database migrations.
npx juntos db:migrate -d sqlite # Local SQLite
npx juntos db:migrate -d d1 # D1 via Wrangler
npx juntos db:migrate -d neon # Neon PostgreSQL
db:seed
Run database seeds (always runs, unlike prepare).
npx juntos db:seed -d sqlite # Seed local SQLite
npx juntos db:seed -d d1 # Seed D1 database
db:prepare
Smart setup: migrate + seed only if database is fresh (no existing tables).
npx juntos db:prepare # Uses database.yml settings
npx juntos db:prepare -d sqlite # SQLite: migrate + seed if fresh
npx juntos db:prepare -d d1 # D1: create + migrate + seed if fresh
db:reset
Complete database reset: drop, create, migrate, and seed.
npx juntos db:reset # Reset development database
npx juntos db:reset -d d1 # Reset D1 database
Warning: This is destructive. D1 and Turso will prompt for confirmation before dropping.
db:create / db:drop
Create or delete databases. Support varies by adapter:
| Adapter | Support |
|---|---|
| D1 | Creates/deletes via Wrangler, saves ID to .env.local |
| Turso | Creates/deletes via turso CLI |
| SQLite | File created automatically, deleted with db:drop |
| Dexie | Created automatically in browser |
juntos deploy
Build and deploy to a serverless platform.
npx juntos deploy [options]
Options:
| Option | Description |
|---|---|
-t, --target TARGET |
Deploy target (vercel, cloudflare) |
-d, --database ADAPTER |
Database adapter |
-e, --environment ENV |
Environment (default: production) |
--skip-build |
Use existing dist/ |
-f, --force |
Clear remote build cache |
--sourcemap |
Generate source maps |
-h, --help |
Show help |
Examples:
npx juntos deploy -d neon # Vercel with Neon (target inferred)
npx juntos deploy -d d1 # Cloudflare with D1 (target inferred)
npx juntos deploy -t vercel -d neon # Explicit target
npx juntos deploy --force # Clear cache and deploy
What it does:
- Builds the app (unless
--skip-build) - Generates platform configuration (vercel.json or wrangler.toml)
- Verifies the build loads correctly
- Runs the platform CLI (vercel or wrangler)
juntos info
Show current Juntos configuration, or dump pre-analyzed metadata.
npx juntos info [options]
Options:
| Option | Description |
|---|---|
--metadata |
Dump pre-analyzed metadata as JSON (models, helpers, concerns, etc.) |
-v, --verbose |
Show detailed information |
-h, --help |
Show help |
Example output:
Juntos Configuration
========================================
Environment:
RAILS_ENV: development (default)
JUNTOS_DATABASE: (not set)
JUNTOS_TARGET: (not set)
Database Configuration:
config/database.yml (development):
adapter: dexie
database: blog_dev
Project:
Directory: blog
Rails app: Yes
node_modules: Installed
dist/: Not built
Metadata dump (--metadata):
The --metadata flag runs the same buildAppManifest() pre-analysis that juntos dev, juntos eject, and juntos render use, and prints the result as JSON. This is useful for debugging import resolution, association detection, and helper discovery.
npx juntos info --metadata # Full metadata as JSON
npx juntos info --metadata | jq .helpers # Just the helpers section
npx juntos info --metadata | jq '.models.Article' # One model's metadata
The output includes:
| Field | Description |
|---|---|
models |
Each model’s associations, scopes, enums, and source file |
concerns |
Concern modules detected in app/models/concerns/ |
helpers |
Application helper files and their exported method names |
controller_files |
Controller files detected during analysis |
current_attributes |
Current attributes parsed from test/test_helper.rb |
juntos doctor
Check environment and prerequisites.
npx juntos doctor
What it checks:
| Check | Requirement |
|---|---|
| Node.js | 18+ required |
| npm | Must be installed |
| Rails app | app/ and config/ directories |
| database.yml | Valid configuration |
| vite.config.js | Present |
| node_modules | Dependencies installed |
| dist/ | Build status |
juntos lint
Scan Ruby files for transpilation issues before they surface at runtime.
npx juntos lint [options] [files...]
Options:
| Option | Description |
|---|---|
--strict |
Enable strict warnings for rare but possible issues |
--summary |
Show untyped variable summary (for planning type hints) |
--suggest |
Auto-generate type hints in config/ruby2js.yml |
--disable RULE |
Disable a rule (can be repeated) |
--include PATTERN |
Include only matching files (glob, can be repeated) |
--exclude PATTERN |
Exclude matching files (glob, can be repeated) |
-h, --help |
Show help |
Examples:
npx juntos lint # Lint all Ruby source files
npx juntos lint app/models/article.rb # Lint a specific file
npx juntos lint --strict # Include strict warnings
npx juntos lint --disable ambiguous_method # Skip type-ambiguity warnings
npx juntos lint --include "app/models/**" # Only lint models
npx juntos lint --summary # Show untyped variable summary
npx juntos lint --suggest # Auto-generate type hints
What it does:
- Discovers Ruby files in
app/models/,app/controllers/,app/javascript/controllers/,app/views/,config/routes.rb, anddb/seeds.rb - Phase 1 — Structural checks: Walks the raw AST to detect patterns that cannot be transpiled to JavaScript (e.g.,
method_missing,eval,retry, operator method definitions likedef <=>) - Phase 2 — Type-ambiguity checks: Runs the full transpilation with lint mode enabled. The pragma filter reports methods whose JavaScript output depends on the receiver type (Array, Hash, Set, etc.) when neither a
# Pragma:annotation nor type inference can determine the type - Prints diagnostics with file, line, and column, then exits with code 1 if any errors are found
Output example:
app/models/article.rb:15:3 warning: ambiguous method 'delete' - receiver type unknown [ambiguous_method]
Consider: # Pragma: set or # Pragma: map or # Pragma: array
app/models/article.rb:42:5 warning: ambiguous method '<<' - receiver type unknown [ambiguous_method]
Consider: # Pragma: array or # Pragma: set or # Pragma: string
Linted 12 files: 0 errors, 2 warnings
Rules
| Rule | Severity | Description |
|---|---|---|
ambiguous_method |
warning | Method has different JavaScript behavior depending on receiver type (see below). |
method_missing |
error | method_missing has no JavaScript equivalent |
eval_call |
error | eval() cannot be safely transpiled |
instance_eval |
error | instance_eval cannot be transpiled |
singleton_method |
warning | def obj.method has limited JavaScript support |
retry_statement |
warning | retry has no direct JavaScript equivalent |
redo_statement |
warning | redo has no direct JavaScript equivalent |
ruby_catch_throw |
warning | Ruby catch/throw differs from JavaScript try/catch |
prepend_call |
warning | prepend has no JavaScript equivalent |
operator_method |
error | Operator method definitions (e.g., def <=>) cannot be transpiled — JavaScript has no operator overloading |
force_encoding |
warning | force_encoding has no JavaScript equivalent (JS strings are always UTF-16) |
parse_error |
error | File could not be parsed |
conversion_error |
error | File could not be converted |
Default vs strict ambiguous method warnings:
By default, the linter only warns about methods where the JavaScript fallthrough is silently wrong — operators like << (bitwise shift instead of append), +/-/&/| on arrays (arithmetic instead of set operations), dup (no JS equivalent), and delete/clear (wrong for certain types).
With --strict, additional warnings appear for methods where the default JavaScript is usually correct but could be wrong for less common types: include?, empty?, each/map/select/flat_map/each_with_index (block iteration with 2+ args on unknown types), [], []=, key?, merge, any?, replace, first, to_h, compact, and flatten.
Fixing ambiguous method warnings
When the lint reports an ambiguous method, it means the transpiler doesn’t know the receiver’s type and may produce incorrect JavaScript. There are three ways to fix this:
1. Add a pragma comment on the same line:
items.delete(x) # Pragma: array → items.splice(items.indexOf(x), 1)
items.delete(x) # Pragma: set → items.delete(x) (kept as-is for Set)
2. Initialize the variable so the type can be inferred:
items = [] # Pragma filter now knows items is an Array
items.delete(x) # → items.splice(items.indexOf(x), 1)
3. Use Sorbet-style annotations:
items = T.let([], Array)
items.delete(x) # → items.splice(items.indexOf(x), 1)
4. Add type hints in config/ruby2js.yml:
lint:
type_hints:
items: array
Type hints apply globally as a low-priority default. They are overridden by
local type inference and pragma comments. Use juntos lint --suggest to
auto-generate hints based on usage patterns.
See the Pragmas documentation for the full list of type disambiguation pragmas.
Configuration in ruby2js.yml
lint:
disable:
- ambiguous_method
- singleton_method
include:
- "app/models/**"
type_hints:
params: hash
positions: array
remaining_seats: number
The type_hints section maps variable names to their types. Supported types: array, hash, string, number, set, map, proc. These hints act as a global fallback — they are overridden by local type inference (e.g., items = []) and pragma comments (e.g., # Pragma: array).
Recommended workflow
- Run
juntos lint --summaryto see which variables produce the most warnings - Run
juntos lint --suggestto auto-generate type hints from usage patterns - Review
config/ruby2js.yml— adjust any incorrect guesses - Run
juntos lintto see remaining warnings - Add
# Pragma:comments for the remaining cases
juntos transform
Show transpiled JavaScript output for one or more files. Useful for debugging transpilation issues without running a full juntos eject.
npx juntos transform [options] <files...>
Options:
| Option | Description |
|---|---|
-d, --database ADAPTER |
Database adapter |
-t, --target TARGET |
Build target |
--intermediate |
Show intermediate Ruby (ERB files only) |
--e2e |
Use Playwright filter (test files only) |
-h, --help |
Show help |
Examples:
npx juntos transform app/models/studio.rb # Transform a model
npx juntos transform app/views/studios/_form.html.erb # Transform an ERB view
npx juntos transform --intermediate app/views/studios/edit.html.erb # Show intermediate Ruby
npx juntos transform test/system/studios_test.rb # Transform a test file
npx juntos transform --e2e test/system/studios_test.rb # Transform as Playwright test
npx juntos transform app/models/studio.rb app/models/tag.rb # Multiple files
What it does:
- Detects the file type from its path (model, controller, view, test, etc.)
- Applies the same filters and transformations used by
juntos devandjuntos eject - Prints the transpiled JavaScript to stdout
The --intermediate flag is useful for ERB files — it shows the Ruby code that the ERB compiler generates before JavaScript transpilation. This helps debug cases where the ERB-to-Ruby step produces unexpected output.
Section inference from path:
| Path pattern | Section |
|---|---|
*.html.erb, *.turbo_stream.erb |
ERB (with full metadata for helper imports) |
*.jsx.rb |
JSX (transformJsxRb) |
app/controllers/ |
controllers |
app/javascript/controllers/ |
stimulus |
test/, *_test.rb |
test (with full metadata) |
config/routes.rb |
routes |
Everything else (app/models/, db/migrate/, etc.) |
default |
Database Adapters
The CLI auto-installs the required npm package for each adapter:
| Adapter | npm Package | Targets |
|---|---|---|
dexie |
dexie | browser |
sqlite |
better-sqlite3 | node, bun |
sqljs |
sql.js | browser |
sqlite-wasm |
@sqlite.org/sqlite-wasm | browser |
wa-sqlite |
wa-sqlite | browser |
pglite |
@electric-sql/pglite | browser, node |
pg |
pg | node, bun, deno |
neon |
@neondatabase/serverless | vercel, node |
turso |
@libsql/client | vercel, node |
d1 |
(none - Cloudflare binding) | cloudflare |
mysql |
mysql2 | node, bun |
Aliases: Common variations are accepted automatically:
indexeddb→ dexiesqlite3,better_sqlite3→ sqlitesql.js→ sqljssqlite_wasm→ sqlite-wasmwa_sqlite→ wa-sqlitepostgres,postgresql→ pgmysql2→ mysql
This means existing Rails apps with adapter: sqlite3 in database.yml work without changes.
Default Targets
When target is not specified, it’s inferred from the database:
| Database | Default Target |
|---|---|
| dexie, sqljs, sqlite-wasm, wa-sqlite, pglite | browser |
| sqlite, pg, mysql | node |
| neon, turso | vercel |
| d1 | cloudflare |
Configuration
config/database.yml
development:
adapter: dexie
database: blog_dev
production:
adapter: neon
target: vercel
Environment Variables
| Variable | Description |
|---|---|
JUNTOS_TARGET |
Override target |
JUNTOS_DATABASE |
Override database adapter |
DATABASE_URL |
Database connection string |
D1_DATABASE_ID |
Cloudflare D1 database ID |
Environment variables in .env.local are automatically loaded.
Static Hosting
Browser builds (dexie, sqljs, pglite) produce static files that can be hosted anywhere:
npx juntos build -d dexie
cd dist && npx serve -s
For subdirectory hosting, use --base:
npx juntos build -d dexie --base /myapp/
Configure SPA fallback routing so client-side routes serve index.html:
- Netlify: Add
_redirects:/* /index.html 200 - Vercel: Add
vercel.json:{"rewrites": [{"source": "/(.*)", "destination": "/index.html"}]} - nginx:
try_files $uri $uri/ /index.html;