@ubean/cli 0.6.2 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +676 -0
- package/README.zh-CN.md +635 -0
- package/dist/cli.js +13 -142
- package/package.json +14 -14
package/README.md
ADDED
|
@@ -0,0 +1,676 @@
|
|
|
1
|
+
# @ubean/cli
|
|
2
|
+
|
|
3
|
+
**English | [中文](./README.zh-CN.md)**
|
|
4
|
+
|
|
5
|
+
The ubean command line: project lifecycle commands, scaffolding, `.env` and config management, plus the side-effect-free library surface for programmatic scaffolding.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add -D ubean @ubean/cli
|
|
9
|
+
pnpm exec ubean dev
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Table of Contents
|
|
13
|
+
|
|
14
|
+
- [Package Positioning](#package-positioning)
|
|
15
|
+
- [Install](#install)
|
|
16
|
+
- [Quick Start](#quick-start)
|
|
17
|
+
- [Library vs Executable Entry](#library-vs-executable-entry)
|
|
18
|
+
- [Commands](#commands)
|
|
19
|
+
- [`ubean init`](#ubean-init)
|
|
20
|
+
- [`ubean dev`](#ubean-dev)
|
|
21
|
+
- [`ubean build`](#ubean-build)
|
|
22
|
+
- [`ubean preview`](#ubean-preview)
|
|
23
|
+
- [`ubean prepare`](#ubean-prepare)
|
|
24
|
+
- [`ubean analyze`](#ubean-analyze)
|
|
25
|
+
- [`ubean env`](#ubean-env)
|
|
26
|
+
- [`ubean config`](#ubean-config)
|
|
27
|
+
- [`ubean devtools`](#ubean-devtools)
|
|
28
|
+
- [Scaffolding](#scaffolding)
|
|
29
|
+
- [`ubean page`](#ubean-page)
|
|
30
|
+
- [`ubean api` / `layout` / `middleware` / `cron` / `plugin`](#ubean-api--layout--middleware--cron--plugin)
|
|
31
|
+
- [`ubean scaffold`](#ubean-scaffold)
|
|
32
|
+
- [Library API](#library-api)
|
|
33
|
+
- [Scaffold Contract](#scaffold-contract)
|
|
34
|
+
- [Bundle Budget Helpers](#bundle-budget-helpers)
|
|
35
|
+
- [Global Flags](#global-flags)
|
|
36
|
+
- [Type Safety](#type-safety)
|
|
37
|
+
- [Architecture Notes](#architecture-notes)
|
|
38
|
+
|
|
39
|
+
## Package Positioning
|
|
40
|
+
|
|
41
|
+
`@ubean/cli` owns the **engineering commands** — the parts that are not lifecycle orchestration. The
|
|
42
|
+
dev / build / preview lifecycles themselves live in `@ubean/build`'s Vite plugin (ADR-0012), and the
|
|
43
|
+
CLI commands are thin aliases over `vite dev | build | preview`.
|
|
44
|
+
|
|
45
|
+
| Responsible for | NOT responsible for |
|
|
46
|
+
| --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
47
|
+
| The executable: `dev` / `build` / `preview` / `prepare` / `init` / `page` / `env` / `config` / `devtools` / `analyze` | Dev request routing, dev app bootstrap, build orchestration, preview takeover — `@ubean/build` |
|
|
48
|
+
| Scaffolding (`scaffold` / `deleteScaffold` / `recoverScaffold` / `listScaffoldableFiles`) + file templates | Page/layout scanning and route metadata — `@ubean/scan` / `@ubean/vue` |
|
|
49
|
+
| The machine-readable scaffold catalog (`getScaffoldManifest`, `SCAFFOLD_CONTRACT_VERSION`) | Config shape and resolution — `@ubean/config` |
|
|
50
|
+
| Bundle-budget reporting (`ubean analyze`) and its baseline helpers | The build that produces the manifest — `@ubean/build` |
|
|
51
|
+
| `.env` file read/write and `ubean.config.ts` creation/display | Deployment platform presets — `@ubean/preset` |
|
|
52
|
+
| The `env-runner`-backed dev worker entry wiring | The worker entry source itself — `@ubean/build/vite` |
|
|
53
|
+
|
|
54
|
+
**Runtime dependency whitelist (main entry `@ubean/cli`):** `citty` (command parsing), `kolorist`
|
|
55
|
+
(colors), `pathe` (paths), plus `@ubean/shared` for the logger. The main entry deliberately does
|
|
56
|
+
**not** import Vite, the plugin, or the command implementations — it is a library surface for
|
|
57
|
+
scaffold / fs-ops / templates, and importing it has **no side effects**.
|
|
58
|
+
|
|
59
|
+
Optional peer dependencies: `@ubean/content`, `@ubean/devtools`, `@vitejs/plugin-vue`, `vite`. When
|
|
60
|
+
they are absent, the corresponding capability degrades (content collections are not
|
|
61
|
+
bootstrapped, DevTools is not mounted) rather than failing — but the plain Vite commands require
|
|
62
|
+
`vite` itself, and `vite dev` needs `@vitejs/plugin-vue` unless your config brings its own.
|
|
63
|
+
|
|
64
|
+
`env-runner` lives **only here** and must not be added as a dependency of `@ubean/build` — it triggers
|
|
65
|
+
pnpm peer-variant drift, which surfaces as `Plugin` type identity mismatches.
|
|
66
|
+
|
|
67
|
+
## Install
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pnpm add -D ubean @ubean/cli
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The `ubean` aggregator package depends on `@ubean/cli` and ships the `ubean` binary, which forwards
|
|
74
|
+
to `@ubean/cli/cli`. Installing the aggregator is enough — `@ubean/cli` is listed separately here only
|
|
75
|
+
for consumers that want the binary, or the library surface, without the framework.
|
|
76
|
+
|
|
77
|
+
The `@ubean/cli` package's own `bin` entry is temporarily named **`ubean-next`**; the user-facing
|
|
78
|
+
command is `ubean`.
|
|
79
|
+
|
|
80
|
+
## Quick Start
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
ubean init my-app --name my-app --template starter --preset node -y
|
|
84
|
+
cd my-app
|
|
85
|
+
pnpm install
|
|
86
|
+
pnpm dev
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Two conventions to know before running anything:
|
|
90
|
+
|
|
91
|
+
- Options accept `--flag value` and `--flag=value`; booleans can be negated with the `--no-` prefix
|
|
92
|
+
(`--no-ssr`, `--no-install`, `--no-write`).
|
|
93
|
+
- **Always spell long-form option names.** Only `-f`, `-y`, and `--pm` have short forms. citty reads
|
|
94
|
+
`init -n blog -t blog` as `{ n: true, t: true, _: ["blog", "blog"] }` with `template` / `preset` /
|
|
95
|
+
`name` all `undefined` (they fall back to `unify` / `standard` / the directory name), and reads
|
|
96
|
+
`-pm pnpm` as `-p -m pnpm`, silently leaving the package manager on its default. Write `--name` /
|
|
97
|
+
`--template` / `--preset` / `--pm`.
|
|
98
|
+
|
|
99
|
+
## Library vs Executable Entry
|
|
100
|
+
|
|
101
|
+
The package has two subpaths, and picking the wrong one has a visible consequence.
|
|
102
|
+
|
|
103
|
+
| Subpath | What it is | Side effects |
|
|
104
|
+
| ------------------ | -------------------------------------------------------------------------------- | --------------------------- |
|
|
105
|
+
| `@ubean/cli` (`.`) | Library surface: scaffold, fs-ops, templates, analyze helpers, scaffold manifest | **None** — import it freely |
|
|
106
|
+
| `@ubean/cli/cli` | The executable entry; **importing it runs `runMain(main)` at module top level** | Starts the CLI |
|
|
107
|
+
|
|
108
|
+
`bin/ubean-next.mjs` is a two-line forwarder — `import '../dist/cli.mjs';` — because a bare import of
|
|
109
|
+
the executable entry is all that is required.
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
// ✓ import the library entry from your own tooling
|
|
113
|
+
import { scaffold, getScaffoldManifest, createFsOps } from '@ubean/cli';
|
|
114
|
+
|
|
115
|
+
// ✗ never do this in library code — it launches the CLI
|
|
116
|
+
// import '@ubean/cli/cli';
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Commands
|
|
120
|
+
|
|
121
|
+
### `ubean init`
|
|
122
|
+
|
|
123
|
+
Initialize a new ubean project.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
ubean init [dir]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
| Argument | Description | Default |
|
|
130
|
+
| -------- | ----------------------- | ------- |
|
|
131
|
+
| `[dir]` | Directory to initialize | `.` |
|
|
132
|
+
|
|
133
|
+
| Option | Alias | Description | Default |
|
|
134
|
+
| ------------------ | ------ | ---------------------------------------------- | ---------- |
|
|
135
|
+
| `--force` | `-f` | Overwrite existing files | `false` |
|
|
136
|
+
| `--name` | — | Project name | — |
|
|
137
|
+
| `--template` | — | Project template (unify/minimal/starter/blog) | `unify` |
|
|
138
|
+
| `--preset` | — | Target preset (standard/node/cloudflare/…) | `standard` |
|
|
139
|
+
| `--packageManager` | `--pm` | Package manager (npm/pnpm/yarn/bun) | `npm` |
|
|
140
|
+
| `--git` | — | Initialize git repository (`--no-git` to skip) | — |
|
|
141
|
+
| `--yes` | `-y` | Skip prompts and use defaults | `false` |
|
|
142
|
+
|
|
143
|
+
Templates: `unify` (full-stack with islands/i18n/layouts/middleware — the default), `minimal`
|
|
144
|
+
(hello world page), `starter` (pages/api/layouts), `blog` (markdown blog structure).
|
|
145
|
+
|
|
146
|
+
The interactive preset menu offers three choices — `standard` (default), `node`, `cloudflare`. All
|
|
147
|
+
other platform presets must be passed explicitly: `cloudflare-dev`, `vercel`, `vercel-edge`,
|
|
148
|
+
`netlify`, `bun`, `deno`, `aws`, `azure` (11 presets in total).
|
|
149
|
+
|
|
150
|
+
Behavior notes: without `--yes` and in a TTY, the command prompts for name, template, preset, package
|
|
151
|
+
manager, and git initialization. An invalid `--packageManager` falls back to `npm`. Existing files are
|
|
152
|
+
only overwritten with `--force`; in non-interactive mode a non-empty directory aborts unless `--force`
|
|
153
|
+
is passed. Non-interactive mode is forced by `--yes`, a non-TTY stdin, or the `CI` / `NONINTERACTIVE`
|
|
154
|
+
environment variables.
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# Non-interactive with explicit choices
|
|
158
|
+
ubean init my-app --name my-app --template starter --preset node -y
|
|
159
|
+
|
|
160
|
+
# Use a preset outside the interactive menu
|
|
161
|
+
ubean init my-app --preset vercel-edge -y
|
|
162
|
+
|
|
163
|
+
# Skip git initialization
|
|
164
|
+
ubean init my-app --no-git
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### `ubean dev`
|
|
168
|
+
|
|
169
|
+
Start the development server with hot reload.
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
ubean dev
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
| Option | Description | Default |
|
|
176
|
+
| --------------- | ---------------------------------------------------------------- | ----------- |
|
|
177
|
+
| `--port` | Port to listen on | `9527` |
|
|
178
|
+
| `--host` | Host to listen on | `localhost` |
|
|
179
|
+
| `--strictPort` | Exit if the port is already in use, instead of auto-incrementing | `false` |
|
|
180
|
+
| `--verbose` | Show detailed startup logs (scan, codegen, diagnostics, reloads) | — |
|
|
181
|
+
| `--logRequests` | Log each request (`GET /path 200 12ms`); ignored in ssg/spa | — |
|
|
182
|
+
| `--open` | Open browser on startup | `false` |
|
|
183
|
+
| `--cwd` | Project root directory | `.` |
|
|
184
|
+
|
|
185
|
+
There is no `--https` option and no `-p` short alias. `--port` / `--host` override the `dev.port` /
|
|
186
|
+
`dev.host` fields in `ubean.config.ts`.
|
|
187
|
+
|
|
188
|
+
Behavior: the project is scanned, type definitions are generated into `.ubean/`, and the dev server
|
|
189
|
+
starts. Watched directories are `api`, `pages`, `middleware`, `layouts`, `plugins`, `app`, and
|
|
190
|
+
`routes` under `srcDir`; a change triggers a rescan and a reload. In fullstack/backend modes the
|
|
191
|
+
banner lists the Scalar UI (`/_scalar`) and OpenAPI schema (`/_openapi.json`) URLs, and OpenAPI types
|
|
192
|
+
are generated asynchronously into `.ubean/`.
|
|
193
|
+
|
|
194
|
+
In `ssg` mode, security response headers (CSP/HSTS/…) are disabled by default in dev — static output
|
|
195
|
+
ships no framework-injected headers (the hosting platform controls them), so dev mirrors that reality.
|
|
196
|
+
Explicitly setting `security.headers` in `ubean.config.ts` re-enables them as a CSP debugging console.
|
|
197
|
+
If the DevTools module is enabled, its URL appears in the banner.
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
ubean dev --port 3000 --host 0.0.0.0
|
|
201
|
+
ubean dev --strictPort
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### `ubean build`
|
|
205
|
+
|
|
206
|
+
Build the application for production.
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
ubean build
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
| Option | Description | Default |
|
|
213
|
+
| ------------- | -------------------------------------------------------------------------------------------- | ------- |
|
|
214
|
+
| `--preset` | Deployment preset (node, bun, deno, cloudflare, vercel, netlify, standard, ...) | — |
|
|
215
|
+
| `--mode` | App mode (fullstack, spa, ssg, backend); overrides `ubean.config.ts` | — |
|
|
216
|
+
| `--ssr` | Enable SSR in fullstack mode (`--no-ssr` to disable); only effective with `--mode fullstack` | — |
|
|
217
|
+
| `--ssg` | Shortcut for `--mode ssg` | `false` |
|
|
218
|
+
| `--outDir` | Output directory (overrides `ubean.config.ts` `build.outputDir`) | — |
|
|
219
|
+
| `--minify` | Minify output (`--no-minify` to disable) | `true` |
|
|
220
|
+
| `--sourcemap` | Generate sourcemaps | `false` |
|
|
221
|
+
| `--prerender` | Pre-render static pages (`--no-prerender` to disable) | — |
|
|
222
|
+
| `--cwd` | Project root directory | `.` |
|
|
223
|
+
|
|
224
|
+
There is no `--clean` option. `--preset` has no CLI default — it falls back to `build.preset` in
|
|
225
|
+
`ubean.config.ts`, which defaults to `node`.
|
|
226
|
+
|
|
227
|
+
`--mode ssg` (or `--ssg`) enables full prerendering via the **direct render path**: a minimal static
|
|
228
|
+
bundle renders each route without the Hono pipeline. `pages/404.vue` renders to `404.html` for static
|
|
229
|
+
hosts; i18n routes are expanded per strategy (e.g. `/about` + `/zh/about`). The temporary
|
|
230
|
+
`dist/server` bundle is removed afterwards — set `UBEAN_KEEP_SSR` to keep it. Page `loader` is not
|
|
231
|
+
executed in this mode (one-time warning); use fullstack prerendering when loaders are required.
|
|
232
|
+
|
|
233
|
+
`spa` and `backend` modes disable prerendering; fullstack with `ssr: false` also disables it.
|
|
234
|
+
`--prerender` / `--no-prerender` override the resolved value. Preset `build:before` / `build:after`
|
|
235
|
+
hooks are executed.
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
ubean build --preset cloudflare
|
|
239
|
+
ubean build --ssg
|
|
240
|
+
ubean build --no-ssr --sourcemap
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
`ubean build` ≡ `vite build`, with one difference: `vite build` **ignores `--outDir`** and always
|
|
244
|
+
writes `config.build.outputDir`.
|
|
245
|
+
|
|
246
|
+
### `ubean preview`
|
|
247
|
+
|
|
248
|
+
Preview the production build locally. Run `ubean build` first.
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
ubean preview
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
| Option | Description | Default |
|
|
255
|
+
| -------------- | ---------------------------------------------------------------- | ----------- |
|
|
256
|
+
| `--port` | Port to listen on | `9725` |
|
|
257
|
+
| `--host` | Host to listen on | `localhost` |
|
|
258
|
+
| `--strictPort` | Exit if the port is already in use, instead of auto-incrementing | `false` |
|
|
259
|
+
| `--outDir` | Output directory to preview (overrides `build.outputDir`) | — |
|
|
260
|
+
| `--cwd` | Project root directory | `.` |
|
|
261
|
+
|
|
262
|
+
There is no `-p` short alias. `--port` / `--host` override `preview.port` / `preview.host` (defaults
|
|
263
|
+
`9725` / `localhost`).
|
|
264
|
+
|
|
265
|
+
`spa` / `ssg` modes serve `dist/public/` with a built-in static file server (SPA falls back to
|
|
266
|
+
`index.html`; SSG resolves `<path>/index.html` or `<path>.html`). `fullstack` / `backend` modes spawn
|
|
267
|
+
the built `dist/server/server.mjs` with `PORT` and `HOST` environment variables. The `cloudflare`
|
|
268
|
+
preset is not supported for local preview — use `wrangler dev` instead; that platform preview is only
|
|
269
|
+
wired on the `ubean preview` side (via miniflare). Other fetch-handler presets run `handler.mjs` with
|
|
270
|
+
a warning. When the requested port is busy, the next free port is used unless `--strictPort` is set.
|
|
271
|
+
|
|
272
|
+
### `ubean prepare`
|
|
273
|
+
|
|
274
|
+
Generate type definitions and check project dependencies.
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
ubean prepare
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
| Option | Description | Default |
|
|
281
|
+
| ----------- | ---------------------------------------------------------------------------- | ------- |
|
|
282
|
+
| `--cwd` | Project root directory | `.` |
|
|
283
|
+
| `--install` | Prompt to install missing project dependencies (`--no-install` to only warn) | `true` |
|
|
284
|
+
|
|
285
|
+
The project is scanned and type definitions are generated into `.ubean/`. The dependency check
|
|
286
|
+
compares the resolved config against what the project must declare:
|
|
287
|
+
|
|
288
|
+
- **required** — core packages (`vue`, `vue-router`, `vue-i18n`), the packages behind every enabled
|
|
289
|
+
built-in module (`ui: true` → `@ubean/integrations` + `@vean/ui`), and those modules' required peers;
|
|
290
|
+
- **optional** — feature-gated packages (`@mdx-js/mdx` for MDX, `satori` + `@resvg/resvg-js` when an
|
|
291
|
+
OG-image convention file exists, `pagefind` for content search, `miniflare` for `cloudflare-dev`,
|
|
292
|
+
`nodemailer` for email).
|
|
293
|
+
|
|
294
|
+
Each missing entry names who requires it and why (`required by \`ui\``), followed by a copy-pasteable
|
|
295
|
+
install command. Build-time peers go to `devDependencies`, runtime peers to `dependencies`. On an
|
|
296
|
+
interactive terminal it asks before installing, using the project's package manager (detected from
|
|
297
|
+
lockfiles: pnpm > yarn > bun > npm), with `@ubean/*`packages pinned to`@^<ubean-version>`.
|
|
298
|
+
|
|
299
|
+
The same check also runs from the Vite plugin's `config` hook, so bare `vite dev` / `vite build`
|
|
300
|
+
(which never call `prepare`) are covered too. Set `UBEAN_SKIP_DEP_CHECK=1` to disable it. There is no
|
|
301
|
+
`--force` flag; missing dependencies and install failures are non-fatal warnings. `--no-install` skips
|
|
302
|
+
auto-installation only — the warning is still printed, which is the right behaviour in CI.
|
|
303
|
+
|
|
304
|
+
### `ubean analyze`
|
|
305
|
+
|
|
306
|
+
Report the client JS budget from the Vite client manifest. Run after `ubean build`.
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
ubean analyze
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
| Option | Description | Default |
|
|
313
|
+
| --------------- | ------------------------------------------------------------------------------------- | ----------------------------- |
|
|
314
|
+
| `--cwd` | Project root | `process.cwd()` |
|
|
315
|
+
| `--write` | Write `.ubean/bundle-baseline.json` (`--no-write` to disable) | `true` |
|
|
316
|
+
| `--out` | Baseline JSON output path | `.ubean/bundle-baseline.json` |
|
|
317
|
+
| `--check` | Committed baseline JSON to compare against (fails if gzip grows past `--maxIncrease`) | — |
|
|
318
|
+
| `--maxIncrease` | Allowed relative gzip growth when using `--check` | `0.05` |
|
|
319
|
+
|
|
320
|
+
Requires a client Vite manifest; it errors out (exit code 1) if no build output is found. It reports
|
|
321
|
+
total and entry gzip sizes plus the largest JS chunks. `--check` compares against a committed baseline
|
|
322
|
+
and fails when total or entry gzip size exceeds the allowed relative increase.
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
ubean build && ubean analyze
|
|
326
|
+
ubean analyze --no-write
|
|
327
|
+
ubean analyze --check benchmarks/bundle-baseline.json --no-write
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### `ubean env`
|
|
331
|
+
|
|
332
|
+
Manage `.env` files.
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
ubean env <add|remove|list|init>
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
| Subcommand | Arguments | Options |
|
|
339
|
+
| ------------ | --------------- | ------------------------------------------------------------ |
|
|
340
|
+
| `env add` | `<key> <value>` | `--file` (default `.env`), `--public`, `-f/--force`, `--dry` |
|
|
341
|
+
| `env remove` | `<key>` | `--file`, `--dry` |
|
|
342
|
+
| `env list` | — | `--file`, `--public` (show only public variables) |
|
|
343
|
+
| `env init` | — | `-f/--force`, `--dry` |
|
|
344
|
+
|
|
345
|
+
`--public` prefixes the key with `UBEAN_PUBLIC_` unless it already starts with `UBEAN_PUBLIC_` /
|
|
346
|
+
`VITE_` / `PUBLIC_`. `env list` marks keys with those prefixes as `[public]`. `env init` creates
|
|
347
|
+
`.env` and `.env.example` from a template.
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
ubean env init
|
|
351
|
+
ubean env add DATABASE_URL postgres://localhost/app
|
|
352
|
+
ubean env add API_URL https://api.example.com --public
|
|
353
|
+
ubean env list --public
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### `ubean config`
|
|
357
|
+
|
|
358
|
+
Manage `ubean.config.ts`.
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
ubean config <init|show|example|path>
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
| Subcommand | Options | Description |
|
|
365
|
+
| ---------------- | --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
|
|
366
|
+
| `config init` | `--preset` (default `standard`; any registered preset name — `vercel-edge`, `aws`, `azure`, …), `-f/--force`, `--dry` | Create a default `ubean.config.ts` |
|
|
367
|
+
| `config show` | — | Show the current config file location/content |
|
|
368
|
+
| `config example` | — | Show a full example configuration |
|
|
369
|
+
| `config path` | — | Print the resolved config path (exit 1 if none) |
|
|
370
|
+
|
|
371
|
+
There are no `--json` or `--env` flags.
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
ubean config init --preset node
|
|
375
|
+
ubean config init --dry
|
|
376
|
+
ubean config path
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### `ubean devtools`
|
|
380
|
+
|
|
381
|
+
Manage DevTools configuration and information. This command neither opens a browser nor starts a
|
|
382
|
+
server — DevTools itself runs inside the dev server.
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
ubean devtools <info|path>
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
| Subcommand | Options | Description |
|
|
389
|
+
| --------------- | ------------------------- | ----------------------------------------------------------- |
|
|
390
|
+
| `devtools info` | — | Show DevTools information (access, config, features) |
|
|
391
|
+
| `devtools path` | `--port` (default `9527`) | Print the DevTools URL: `http://localhost:<port>/_devtools` |
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
ubean devtools path
|
|
395
|
+
ubean devtools path --port 3000
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
In the browser, DevTools opens by visiting the URL from the dev server banner (default `/_devtools`)
|
|
399
|
+
while the dev server runs; it redirects to the devframe hub shell, which hosts the panel as an iframe
|
|
400
|
+
devframe.
|
|
401
|
+
|
|
402
|
+
## Scaffolding
|
|
403
|
+
|
|
404
|
+
Scaffold commands create, delete, recover, and list files. Shared conventions:
|
|
405
|
+
|
|
406
|
+
| Type | Base directory | Extension |
|
|
407
|
+
| ------------ | ----------------- | -------------------- |
|
|
408
|
+
| page / reuse | `src/pages/` | `.vue` / `.reuse.ts` |
|
|
409
|
+
| api | `src/routes/` | `.ts` |
|
|
410
|
+
| layout | `src/layouts/` | `.vue` |
|
|
411
|
+
| middleware | `src/middleware/` | `.ts` |
|
|
412
|
+
| cron | `src/crons/` | `.ts` |
|
|
413
|
+
| plugin | `src/plugins/` | `.ts` |
|
|
414
|
+
|
|
415
|
+
Cron files land in `src/crons/`, matching the scanner default (`DEFAULT_DIRS.crons = 'crons'`).
|
|
416
|
+
Numeric filename prefixes control registration order (`01.cleanup.ts`).
|
|
417
|
+
|
|
418
|
+
Shared semantics:
|
|
419
|
+
|
|
420
|
+
- `add` skips existing files unless `-f/--force` is passed (a `.bak` backup is created before
|
|
421
|
+
overwriting).
|
|
422
|
+
- `delete` creates a `.bak` backup by default; `-f/--force` deletes permanently without backup.
|
|
423
|
+
- `recovery` restores the most recent `.bak` backup; `--dry` only checks whether recovery is possible.
|
|
424
|
+
- `--dry` prints the action without writing files.
|
|
425
|
+
- Component names derive from the route path: dynamic segments become PascalCase (`[id]` → `Id`),
|
|
426
|
+
`[...slug]` → `AllSlug`, `[[page]]` → `PageOptional`.
|
|
427
|
+
|
|
428
|
+
### `ubean page`
|
|
429
|
+
|
|
430
|
+
Generic scaffold entry for all types.
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
ubean page <add|add-reuse|delete|recovery|list>
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
| Subcommand | Argument | Options |
|
|
437
|
+
| ----------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
438
|
+
| `page add <path>` | route path (e.g. `users/[id]`, `api/users`, `crons/daily`) | `--type` (page/api/layout/middleware/cron/plugin, default `page`), `--method` (default `GET`), `--schedule` (default `* * * * *`), `-f/--force`, `--dry` |
|
|
439
|
+
| `page add-reuse <path>` | route path | `-f/--force`, `--dry` |
|
|
440
|
+
| `page delete <path>` | route path | `--type` (… + `reuse`), `-f/--force`, `--dry` |
|
|
441
|
+
| `page recovery <path>` | route path | `--type`, `--dry` |
|
|
442
|
+
| `page list` | — | `--type` |
|
|
443
|
+
|
|
444
|
+
`--type` for `add` does **not** include `reuse` — use `page add-reuse` instead.
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
ubean page add about
|
|
448
|
+
ubean page add "users/[id]"
|
|
449
|
+
ubean page add api/users --type api --method POST
|
|
450
|
+
ubean page add daily-cleanup --type cron --schedule "0 0 * * *"
|
|
451
|
+
ubean page add dashboard --dry
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### `ubean api` / `layout` / `middleware` / `cron` / `plugin`
|
|
455
|
+
|
|
456
|
+
Dedicated commands, each with the subcommands `add`, `delete`, `recovery`, and `list`.
|
|
457
|
+
|
|
458
|
+
| Command | Argument | Extra options | Shared options |
|
|
459
|
+
| ---------------------- | -------- | ---------------------------------- | --------------------- |
|
|
460
|
+
| `ubean api add` | `<path>` | `--method` (default `GET`) | `-f/--force`, `--dry` |
|
|
461
|
+
| `ubean cron add` | `<path>` | `--schedule` (default `* * * * *`) | `-f/--force`, `--dry` |
|
|
462
|
+
| `ubean layout add` | `<path>` | — | `-f/--force`, `--dry` |
|
|
463
|
+
| `ubean middleware add` | `<path>` | — | `-f/--force`, `--dry` |
|
|
464
|
+
| `ubean plugin add` | `<path>` | — | `-f/--force`, `--dry` |
|
|
465
|
+
|
|
466
|
+
`delete <path>` takes `-f/--force` (permanent delete, no backup) and `--dry`; `recovery <path>` takes
|
|
467
|
+
`--dry`; `list` takes no options.
|
|
468
|
+
|
|
469
|
+
```bash
|
|
470
|
+
ubean api add users/[id] --method DELETE
|
|
471
|
+
ubean layout add admin
|
|
472
|
+
ubean middleware add auth
|
|
473
|
+
ubean cron add daily-cleanup --schedule "0 0 * * *"
|
|
474
|
+
ubean plugin add my-plugin
|
|
475
|
+
ubean api list
|
|
476
|
+
ubean api delete users/[id]
|
|
477
|
+
ubean api recovery users/[id]
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### `ubean scaffold`
|
|
481
|
+
|
|
482
|
+
Machine-readable scaffold catalog for studio and IDE integrations.
|
|
483
|
+
|
|
484
|
+
```bash
|
|
485
|
+
ubean scaffold describe
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
`describe` prints the scaffold JSON manifest to stdout. It contains `contractVersion` and a `types`
|
|
489
|
+
array, each entry with `type`, `cli`, `baseDir`, `extensions`, and `args`.
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
ubean scaffold describe
|
|
493
|
+
ubean scaffold describe | jq '.types[].type'
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
## Library API
|
|
497
|
+
|
|
498
|
+
Import from `@ubean/cli` — no side effects, no CLI startup.
|
|
499
|
+
|
|
500
|
+
```ts
|
|
501
|
+
import {
|
|
502
|
+
scaffold,
|
|
503
|
+
deleteScaffold,
|
|
504
|
+
recoverScaffold,
|
|
505
|
+
listScaffoldableFiles,
|
|
506
|
+
getScaffoldManifest,
|
|
507
|
+
SCAFFOLD_CONTRACT_VERSION,
|
|
508
|
+
createFsOps
|
|
509
|
+
} from '@ubean/cli';
|
|
510
|
+
|
|
511
|
+
const result = await scaffold({
|
|
512
|
+
cwd: process.cwd(),
|
|
513
|
+
type: 'api', // 'page' | 'api' | 'layout' | 'middleware' | 'reuse' | 'cron' | 'plugin'
|
|
514
|
+
path: 'users/[id]',
|
|
515
|
+
method: 'POST',
|
|
516
|
+
force: false,
|
|
517
|
+
dry: false // baseDir?: string overrides the default directory for this type
|
|
518
|
+
});
|
|
519
|
+
// → { created, deleted, restored, skipped, errors }
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
| Export | Description |
|
|
523
|
+
| ---------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
524
|
+
| `scaffold(options)` / `ScaffoldOptions` | Create files; `ScaffoldResult` = `{ created, deleted, restored, skipped, errors }` |
|
|
525
|
+
| `deleteScaffold(options)` | Delete with a `.bak` backup unless `force` |
|
|
526
|
+
| `recoverScaffold(options)` | Restore from the most recent `.bak` |
|
|
527
|
+
| `listScaffoldableFiles(cwd, type, baseDir?)` | List existing files of a type |
|
|
528
|
+
| `ScaffoldType` | `'page' \| 'api' \| 'layout' \| 'middleware' \| 'reuse' \| 'cron' \| 'plugin'` |
|
|
529
|
+
| `createFsOps(cwd)` / `FsOps` | Filesystem helpers bound to a project root (see below) |
|
|
530
|
+
| `toKebabCase` / `toPascalCase` / `toCamelCase` | Name casing used by the templates |
|
|
531
|
+
|
|
532
|
+
Template rendering, exported for tools that generate their own files:
|
|
533
|
+
|
|
534
|
+
```ts
|
|
535
|
+
import { renderTemplate, renderApiTemplate, API_TEMPLATE } from '@ubean/cli';
|
|
536
|
+
|
|
537
|
+
renderTemplate('Hello {{name}}', { variables: { name: 'ubean' } });
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
`renderPageTemplate`, `renderApiTemplate`, `renderMiddlewareTemplate`, `renderLayoutTemplate`,
|
|
541
|
+
`renderCronTemplate`, `renderPluginTemplate` each take their data interface (`PageTemplateData`,
|
|
542
|
+
`ApiTemplateData`, `MiddlewareTemplateData`, `LayoutTemplateData`, `CronTemplateData`,
|
|
543
|
+
`PluginTemplateData`); the raw `PAGE_TEMPLATE` / `API_TEMPLATE` / `MIDDLEWARE_TEMPLATE` /
|
|
544
|
+
`LAYOUT_TEMPLATE` / `CRON_TEMPLATE` / `PLUGIN_TEMPLATE` strings are exported too, along with
|
|
545
|
+
`TemplateVariables` and `TemplateOptions`.
|
|
546
|
+
|
|
547
|
+
`FsOps` (from `createFsOps(cwd)`) bundles `resolvePath`, `ensureDir`, `exists`, `readFile`,
|
|
548
|
+
`writeFile`, `appendFile`, `remove`, `copyFile`, `copyDir`, `readDir`, `readJson`, `writeJson`,
|
|
549
|
+
`createBackup`, `restoreBackup`, `removeBackup`, and `listFiles`. `FsOpOptions` (re-exported as
|
|
550
|
+
`FsOpsOptions`) and `BackupOptions` configure it.
|
|
551
|
+
|
|
552
|
+
## Scaffold Contract
|
|
553
|
+
|
|
554
|
+
`getScaffoldManifest()` returns the catalog that `ubean scaffold describe` prints — the integration
|
|
555
|
+
point for studio and IDE plugins.
|
|
556
|
+
|
|
557
|
+
```ts
|
|
558
|
+
import { getScaffoldManifest, SCAFFOLD_CONTRACT_VERSION } from '@ubean/cli';
|
|
559
|
+
|
|
560
|
+
const manifest = getScaffoldManifest();
|
|
561
|
+
// {
|
|
562
|
+
// contractVersion: 1,
|
|
563
|
+
// types: [{ type, cli, baseDir, extensions, args: [{ name, required, description, default? }] }]
|
|
564
|
+
// }
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
`SCAFFOLD_CONTRACT_VERSION` is `1` and is typed as the literal, so the manifest's own
|
|
568
|
+
`contractVersion` field is checked against it. Each `ScaffoldTypeManifest` records whether a dedicated
|
|
569
|
+
CLI command exists (`cli: true` for api/layout/middleware/cron/plugin; **`false` for `reuse`**, which
|
|
570
|
+
is library-only. Reuse routes are created with `scaffold({ type: 'reuse' })` or `ubean page add-reuse`).
|
|
571
|
+
|
|
572
|
+
## Bundle Budget Helpers
|
|
573
|
+
|
|
574
|
+
The `ubean analyze` implementation is exportable, so CI scripts can report or gate without spawning
|
|
575
|
+
the CLI.
|
|
576
|
+
|
|
577
|
+
```ts
|
|
578
|
+
import {
|
|
579
|
+
findClientManifest,
|
|
580
|
+
readViteManifest,
|
|
581
|
+
summarizeBundle,
|
|
582
|
+
compareBundleBaseline,
|
|
583
|
+
writeBundleBaseline
|
|
584
|
+
} from '@ubean/cli';
|
|
585
|
+
|
|
586
|
+
const manifest = findClientManifest(process.cwd());
|
|
587
|
+
const baseline = summarizeBundle(outDir, readViteManifest(manifest.path));
|
|
588
|
+
const violations = compareBundleBaseline(baseline, committedBaseline, { maxIncrease: 0.05 });
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
| Export | Description |
|
|
592
|
+
| ----------------------------------------------------- | -------------------------------------------------------------------- |
|
|
593
|
+
| `findClientManifest(cwd)` | Locate the client Vite manifest |
|
|
594
|
+
| `readViteManifest(manifestPath)` | Parse it → `Record<string, ManifestChunk>` |
|
|
595
|
+
| `summarizeBundle(outDir, manifest)` | → `BundleBaseline` (total + entry gzip) |
|
|
596
|
+
| `compareBundleBaseline(current, committed, options?)` | → budget violations; `options` carries the allowed relative increase |
|
|
597
|
+
| `writeBundleBaseline(filePath, baseline)` | Persist a baseline |
|
|
598
|
+
|
|
599
|
+
`BundleBaseline`, `BundleBudgetEntry`, and `ManifestChunk` are exported from the main entry. The
|
|
600
|
+
reported figures are gzip sizes; the relative-growth ceiling and the absolute `--max-total-kb` /
|
|
601
|
+
`--max-entry-kb` / `--max-chunk-kb` ceilings are separate gates.
|
|
602
|
+
|
|
603
|
+
## Global Flags
|
|
604
|
+
|
|
605
|
+
```bash
|
|
606
|
+
ubean --help
|
|
607
|
+
ubean <command> --help
|
|
608
|
+
ubean --version
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
`--debug` enables verbose logging (timestamps, call sites, log levels) and works whether it is placed
|
|
612
|
+
before or after the subcommand:
|
|
613
|
+
|
|
614
|
+
```bash
|
|
615
|
+
ubean --debug dev
|
|
616
|
+
ubean dev --debug
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
The CLI does **not** read `UBEAN_PORT` / `UBEAN_HOST` — use `--port` / `--host` or the `dev` /
|
|
620
|
+
`preview` fields in `ubean.config.ts`.
|
|
621
|
+
|
|
622
|
+
| Variable | Used by | Description | Default |
|
|
623
|
+
| --------------------------- | ----------------- | --------------------------------------------------------------- | --------- |
|
|
624
|
+
| `PORT` | production server | Server port in the generated production server entry | `9527` |
|
|
625
|
+
| `HOST` | production server | Server host in the generated production server entry | `0.0.0.0` |
|
|
626
|
+
| `CI` / `NONINTERACTIVE` | init | Forces non-interactive mode | — |
|
|
627
|
+
| `UBEAN_KEEP_SSR` | build (SSG mode) | Keeps the temporary `dist/server` bundle instead of deleting it | — |
|
|
628
|
+
| `UBEAN_SKIP_DEP_CHECK` | dependency check | Skips the project dependency check | — |
|
|
629
|
+
| `LOG_LEVEL` / `TSLOG_LEVEL` | logging | Logger minimum level (silly/trace/debug/info/warn/error/fatal) | `info` |
|
|
630
|
+
| `TSLOG_TYPE` | logging | Logger output format (json/pretty/hidden) | — |
|
|
631
|
+
|
|
632
|
+
Exit codes: `0` success, `1` general error. `config path` also exits `1` when no config file exists.
|
|
633
|
+
|
|
634
|
+
## Type Safety
|
|
635
|
+
|
|
636
|
+
- `ScaffoldType` is a string-literal union, so `scaffold({ type })` and `--type` values are checked at
|
|
637
|
+
compile time rather than at file-write time.
|
|
638
|
+
- `FsOps` is `ReturnType<typeof createFsOps>`, i.e. the helpers' shapes are derived from the factory
|
|
639
|
+
rather than hand-declared — they cannot drift.
|
|
640
|
+
- The template renderers are typed per template (`PageTemplateData`, `ApiTemplateData`, …), so a
|
|
641
|
+
missing variable is a type error, not a `{{placeholder}}` left in the output.
|
|
642
|
+
- `ScaffoldManifest` / `ScaffoldTypeManifest` / `ScaffoldArgManifest` describe the JSON the CLI emits;
|
|
643
|
+
`SCAFFOLD_CONTRACT_VERSION` is a literal type (`1`), not `number`, so `contractVersion` in the
|
|
644
|
+
manifest is pinned.
|
|
645
|
+
- Library exports are all declared in `dist/index.d.ts`; the executable entry ships an empty
|
|
646
|
+
declaration file (`cli.d.ts`) precisely because it has no API surface.
|
|
647
|
+
|
|
648
|
+
## Architecture Notes
|
|
649
|
+
|
|
650
|
+
- **The CLI does not own the dev/build/preview lifecycles.** ADR-0012 moved them into the Vite plugin:
|
|
651
|
+
`ubeanPlugin()` registers `client` and `ubean` environments plus `builder.buildApp` in its `config`
|
|
652
|
+
hook, so `vite dev | build | preview` is the complete toolchain and the CLI commands are thin
|
|
653
|
+
aliases. Both paths produce item-for-item identical output
|
|
654
|
+
(`packages/cli/test/build-contracts.test.ts`).
|
|
655
|
+
- **Two entry points with different side-effect contracts.** The main entry is a library; `./cli` runs
|
|
656
|
+
`runMain(main)` at module top level and must only ever be imported by a binary. This is why
|
|
657
|
+
`bin/ubean-next.mjs` is a bare `import '../dist/cli.mjs';`.
|
|
658
|
+
- **Projects without a user `vite.config.ts` are still supported.** The CLI injects the builtin plugin
|
|
659
|
+
in that case; the plugin does not register itself twice. When `UBEAN_BUILD_DRIVEN_BY_CLI` is set, the
|
|
660
|
+
plugin must not register its own `buildApp`, or the same build is orchestrated twice.
|
|
661
|
+
- **Codegen ownership is negotiated through environment variables.** `ubean dev` / `build` /
|
|
662
|
+
`prepare` set `UBEAN_CODEGEN_BY_CLI=1` (and the build path sets
|
|
663
|
+
`UBEAN_BUILD_DRIVEN_BY_CLI=1`), which makes the Vite plugin yield — without it, a bare `vite build`
|
|
664
|
+
would produce no `.ubean/*.d.ts` at all and user type-checks would fail.
|
|
665
|
+
- **`env-runner` must stay in this package.** It is not a dependency of `@ubean/build`; adding it there
|
|
666
|
+
triggers pnpm peer-variant drift, which appears as `Plugin` type identity mismatches.
|
|
667
|
+
- **The `ubean-next` binary name is temporary.** The user-facing command is `ubean`; renaming the
|
|
668
|
+
package's own `bin` entry is pending.
|
|
669
|
+
- **Cron scaffolding targets `src/crons/`** to match the scanner's `DEFAULT_DIRS.crons`, not
|
|
670
|
+
`src/tasks/` or any other plausible name.
|
|
671
|
+
- **There is deliberately no in-config rollback for the lifecycle migration.** `experimental.viteBuilder`
|
|
672
|
+
was deleted with the legacy two-`viteBuild` orchestration; rollback is a version rollback only.
|
|
673
|
+
- **Test suites run serially in this package.** Several suites build into the same shared example
|
|
674
|
+
directory (`examples/ubean-test`'s `dist/` and `.ubean/`) — building and previewing the same output
|
|
675
|
+
directory concurrently fails — so the package's Vitest config disables file parallelism. The fix is
|
|
676
|
+
per-suite directory isolation, not rewriting example sources.
|