npm.io
1.6.1 • Published yesterdayCLI

@chrisluyi/daas-cli

Licence
Version
1.6.1
Deps
8
Size
218 kB
Vulns
0
Weekly
0

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.matchMedia
  • ResizeObserver
  • IntersectionObserver
  • window.scrollTo / scrollBy
  • URL.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-cli is not added to the app's devDependencies (it's resolved from the workspace root)
  • Each MFE has its own port in package.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:

  1. The CLI resolves a TargetContext from the string
  2. tsconfig.daas.json is generated (gitignored) with path aliases:
    • @region-config./src/config/regions/<region>
    • @platform-config./src/config/platforms/<platform>
    • @product-config./src/config/products/<product>
  3. 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.