daas-cli
Zero-config build/dev/test CLI for React micro-frontend (MFE) apps using rsbuild + Module Federation 2.
MFE apps have no build config files — no rsbuild.config.ts, no vitest.config.ts. The CLI owns all of it.
Packages
| Package | Version | Description |
|---|---|---|
@chrisluyi/daas-cli |
1.1.0 | CLI binary — install this as your only devDependency |
@chrisluyi/daas-rsbuild-config |
1.1.0 | rsbuild config factory + vitest config — arrives transitively |
@chrisluyi/daas-template |
1.0.0 | MFE scaffold templates (services / containers / components structure) |
Quickstart (3 commands)
mkdir my-mfe && cd my-mfe
echo '{"name":"my-mfe"}' > package.json
npx @chrisluyi/daas-cli init
bun install
daas dev
Your MFE is now running at http://localhost:3000 with Module Federation 2, React, hot-reload, and Vitest — zero config required.
Installation
# In your MFE project
bun add -D @chrisluyi/daas-cli
# or npm
npm install -D @chrisluyi/daas-cli
daas-cli is the only devDependency you need. It ships rsbuild, vitest, @testing-library/react, happy-dom, and all tooling as its own dependencies. Do not install these separately.
Commands
daas init [--template default]
daas dev [target] [--port <n>] [--json]
daas build [target] [--json]
daas test [--json]
daas info [--json]
daas init
Interactive scaffold powered by @clack/prompts. Prompts for remote config URL, port, and app name, then writes starter files.
┌ daas-cli — scaffold new MFE
│
◇ Remote config URL?
│ https://your-org.example.com/daas-config.json
│
◇ Dev server port?
│ 3001
│
◇ App name? (leave blank to use package.json name)
│ mfe-auth
│
◇ Does this app expose additional components beyond ./App?
│ No
│
◆ Scaffolding mfe-auth...
│ ✓ Created src/App.tsx
│ ✓ Created src/App.test.tsx
│ ✓ Created tsconfig.json
│ ✓ Updated package.json
│
└ Done! Run: bun install && daas dev
What gets created (minimal scaffold):
src/
├── App.tsx # minimal React component
└── App.test.tsx # smoke test
tsconfig.json # jsx: react-jsx, strict mode
package.json # scripts + daas config injected
--template default scaffolds the full opinionated structure:
src/
├── App.tsx # entry — renders AppContainer
├── containers/
│ └── AppContainer.tsx # data-fetching component
├── components/
│ └── App.tsx # presentational component
├── services/
│ └── index.ts # service layer stub
└── config/
├── regions/ # per-region config (for v1.5 targets)
├── platforms/ # per-platform config
└── products/ # per-product config
daas dev
Starts the rsbuild dev server with HMR and Module Federation 2 in remote mode.
daas dev # uses port from package.json#daas
daas dev --port 3002 # override port at runtime
daas dev sg-foo-mb-dev # v1.5: activate region/product/platform target
daas build
Production build to ./dist. Always fetches fresh remote config — fatal if network unavailable.
daas build
daas build sg-foo-mb-prod # v1.5: build for a specific target
daas build --json # machine-readable output (for CI / AI agents)
daas test
Runs Vitest with happy-dom, @testing-library/react, and @testing-library/jest-dom pre-configured. The following browser APIs are mocked automatically so MFE tests work without any extra setup:
window.matchMediaResizeObserverIntersectionObserverwindow.scrollTo/scrollByURL.createObjectURL/revokeObjectURL
daas test
daas test --json
Writing tests — import everything from @chrisluyi/daas-cli/test (no direct vitest or @testing-library imports needed):
import { render, screen, describe, test, expect, userEvent } from "@chrisluyi/daas-cli/test"
import MyComponent from "./MyComponent"
describe("MyComponent", () => {
test("renders a button", async () => {
render(<MyComponent />)
await userEvent.click(screen.getByRole("button"))
expect(screen.getByText("Clicked!")).toBeInTheDocument()
})
})
Adding extra setup — append your own setup files after the built-in mocks via package.json#daas.setupFiles:
{
"daas": {
"configUrl": "...",
"port": 3001,
"setupFiles": ["./src/test-setup.ts"]
}
}
Your setup file can override any of the default mocks or add new ones.
daas info
Prints the fully resolved project state — useful for debugging and AI agent diagnosis.
daas info --json
Example output:
{
"ok": true,
"command": "info",
"cliVersion": "1.1.0",
"appName": "mfe-auth",
"port": 3001,
"configUrl": "https://your-org.example.com/daas-config.json",
"manifest": {
"name": "mfe-auth",
"exposes": { "./App": "./src/App" }
},
"remoteJson": {
"version": "1.0.0",
"minCliVersion": "1.0.0",
"shared": { "react": "18.3.1", "react-dom": "18.3.1" },
"rsbuildConfigVersion": 1
},
"isMonorepo": false
}
Per-App Config (package.json#daas)
All MFE-specific configuration lives in package.json:
{
"name": "mfe-auth",
"scripts": {
"dev": "daas dev",
"build": "daas build",
"test": "daas test"
},
"devDependencies": {
"@chrisluyi/daas-cli": "^1.1.0"
},
"daas": {
"configUrl": "https://your-org.example.com/daas-config.json",
"port": 3001,
"coverage": {
"lines": 80,
"branches": 80
}
}
}
| Field | Required | Description |
|---|---|---|
configUrl |
Yes | URL of the remote JSON config owned by your platform team |
port |
Yes | Dev server port — must be unique per MFE in a monorepo |
coverage |
No | Vitest coverage thresholds |
setupFiles |
No | Extra Vitest setup files appended after the built-in browser mocks |
Optional Manifest (mf.manifest.json)
Only needed when exposing more than just ./App:
{
"name": "mfe-auth",
"exposes": {
"./App": "./src/App",
"./LoginButton": "./src/components/LoginButton"
}
}
If absent, name derives from package.json#name (scope stripped), and exposes defaults to { "./App": "./src/App" }.
Remote JSON (served by your platform team)
The URL in configUrl must serve a JSON file like this:
{
"version": "1.0.0",
"minCliVersion": "1.0.0",
"releaseNotesUrl": "https://your-org.example.com/daas-cli/releases",
"shared": {
"react": "18.3.1",
"react-dom": "18.3.1"
},
"rsbuildConfigVersion": 1
}
This controls which version of shared dependencies all your MFEs use via Module Federation 2's singleton negotiation. The CLI fetches this at dev startup (5-min cache) and always fresh at build time.
Monorepo Support
daas init detects monorepos by walking parent directories for bun.lockb or package.json#workspaces. In a monorepo:
daas-cliis not added to the app'sdevDependencies(it's resolved from the workspace root)- Each MFE has its own
portinpackage.json#daas
my-monorepo/
├── package.json # workspaces: ["packages/*"]
├── bun.lockb
└── packages/
├── mfe-auth/ # daas dev → port 3001
│ └── package.json # { "daas": { "port": 3001, ... } }
└── mfe-dashboard/ # daas dev → port 3002
└── package.json # { "daas": { "port": 3002, ... } }
v1.5 — Multi-Region Target System
Target strings let you select region/product/platform/environment at dev and build time:
daas dev sg-foo-mb-dev
│ │ │ └─ environment (dev / staging / prod)
│ │ └──── platform (mb = mobile, wb = web, etc.)
│ └──────── product (may contain hyphens)
└──────────── region (sg, us, eu, …)
When a target is provided:
- The CLI resolves a
TargetContextfrom the string tsconfig.daas.jsonis generated (gitignored) with path aliases:@region-config→./src/config/regions/<region>@platform-config→./src/config/platforms/<platform>@product-config→./src/config/products/<product>
- Environment variables are injected at build time:
DAAS_REGION,DAAS_PRODUCT,DAAS_PLATFORM,DAAS_ENV
Use daas init --template default to scaffold the config layer folder structure.
Exit Codes
| Code | Meaning | What to do |
|---|---|---|
0 |
Success | — |
1 |
General error (build/test failure) | Check output above the JSON |
2 |
Config error (missing configUrl, bad manifest) |
Run daas init or fix package.json#daas |
3 |
CLI too old (minCliVersion not met) |
Upgrade @chrisluyi/daas-cli |
4 |
Remote JSON fetch failed | Check network / VPN (only fatal in build) |
5 |
Config version mismatch | Upgrade @chrisluyi/daas-cli |
--json Output Contract
All commands except init support --json — always use this in CI and AI agent workflows.
Success:
{
"ok": true,
"command": "build",
"appName": "mfe-auth",
"output": "./dist",
"cliVersion": "1.1.0"
}
Error:
{
"ok": false,
"command": "build",
"exitCode": 4,
"error": "Could not fetch remote config: HTTP 503",
"suggestion": "Check network connectivity or VPN.",
"cliVersion": "1.1.0"
}
AI Agent Support (Claude Code)
Register the bundled skill so Claude Code understands daas-cli without hallucinating commands:
# Per-project
mkdir -p .claude/skills
cp node_modules/@chrisluyi/daas-cli/skill.md .claude/skills/daas-cli.md
# Or globally
cp node_modules/@chrisluyi/daas-cli/skill.md ~/.claude/skills/daas-cli.md
AI agent workflow:
# 1. Always start here to understand current state
daas info --json
# 2. Run commands and parse stdout JSON
daas build --json
# { "ok": true, ... } or { "ok": false, "exitCode": N, "error": "...", "suggestion": "..." }
Development (this repo)
bun install
bun test # run all tests
bun run build # build both packages to dist/
bun run lint # biome check
See docs/DEVELOPMENT.md for the full release workflow.