@dormer/cli 0.0.0-stage → 0.1.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 +201 -2
- package/bin/dormer.mjs +26 -0
- package/dist/cli.js +1301 -0
- package/dist/index.js +676 -0
- package/package.json +57 -4
- package/src/args.ts +68 -0
- package/src/backend.ts +142 -0
- package/src/cli.ts +45 -0
- package/src/commands/build.ts +105 -0
- package/src/commands/create.ts +132 -0
- package/src/commands/dev.ts +128 -0
- package/src/commands/doctor.ts +141 -0
- package/src/commands/inspect.ts +60 -0
- package/src/commands/package.ts +159 -0
- package/src/config.ts +151 -0
- package/src/host.ts +85 -0
- package/src/index.ts +8 -0
- package/src/resolve.ts +75 -0
- package/src/ui.ts +57 -0
- package/templates/react/README.md +34 -0
- package/templates/react/_gitignore +5 -0
- package/templates/react/dormer.config.json +16 -0
- package/templates/react/package.json +29 -0
- package/templates/react/renderer/index.html +12 -0
- package/templates/react/renderer/src/App.tsx +34 -0
- package/templates/react/renderer/src/main.tsx +6 -0
- package/templates/react/renderer/src/style.css +8 -0
- package/templates/react/renderer/src/vite-env.d.ts +2 -0
- package/templates/react/renderer/tsconfig.json +15 -0
- package/templates/react/renderer/vite.config.ts +7 -0
- package/templates/react/src/main.ts +34 -0
- package/templates/react/src/shared.ts +10 -0
- package/templates/react/tsconfig.json +15 -0
- package/templates/svelte/README.md +34 -0
- package/templates/svelte/_gitignore +5 -0
- package/templates/svelte/dormer.config.json +16 -0
- package/templates/svelte/package.json +27 -0
- package/templates/svelte/renderer/index.html +12 -0
- package/templates/svelte/renderer/src/App.svelte +32 -0
- package/templates/svelte/renderer/src/main.ts +5 -0
- package/templates/svelte/renderer/src/style.css +8 -0
- package/templates/svelte/renderer/src/vite-env.d.ts +2 -0
- package/templates/svelte/renderer/tsconfig.json +15 -0
- package/templates/svelte/renderer/vite.config.ts +7 -0
- package/templates/svelte/src/main.ts +34 -0
- package/templates/svelte/src/shared.ts +10 -0
- package/templates/svelte/tsconfig.json +15 -0
- package/templates/vanilla/README.md +34 -0
- package/templates/vanilla/_gitignore +5 -0
- package/templates/vanilla/dormer.config.json +16 -0
- package/templates/vanilla/package.json +24 -0
- package/templates/vanilla/renderer/index.html +12 -0
- package/templates/vanilla/renderer/src/main.ts +31 -0
- package/templates/vanilla/renderer/src/style.css +8 -0
- package/templates/vanilla/renderer/src/vite-env.d.ts +2 -0
- package/templates/vanilla/renderer/tsconfig.json +14 -0
- package/templates/vanilla/renderer/vite.config.ts +6 -0
- package/templates/vanilla/src/main.ts +34 -0
- package/templates/vanilla/src/shared.ts +10 -0
- package/templates/vanilla/tsconfig.json +15 -0
- package/templates/vue/README.md +34 -0
- package/templates/vue/_gitignore +5 -0
- package/templates/vue/dormer.config.json +16 -0
- package/templates/vue/package.json +27 -0
- package/templates/vue/renderer/index.html +12 -0
- package/templates/vue/renderer/src/App.vue +36 -0
- package/templates/vue/renderer/src/main.ts +5 -0
- package/templates/vue/renderer/src/style.css +8 -0
- package/templates/vue/renderer/src/vite-env.d.ts +8 -0
- package/templates/vue/renderer/tsconfig.json +15 -0
- package/templates/vue/renderer/vite.config.ts +7 -0
- package/templates/vue/src/main.ts +34 -0
- package/templates/vue/src/shared.ts +10 -0
- package/templates/vue/tsconfig.json +15 -0
package/README.md
CHANGED
|
@@ -1,3 +1,202 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @dormer/cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The `dormer` command: scaffold, run, build and package Dormer apps (Node.js backend + system
|
|
4
|
+
WebView renderer). TypeScript executed directly by Node's type stripping (Node 22.18+/24; on Node
|
|
5
|
+
22.6–22.17 the shim re-executes itself with `--experimental-strip-types`). Dependencies: `esbuild`
|
|
6
|
+
(backend bundling); `vite` is resolved from the user's project so their plugins share the instance.
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
dormer create <dir> --template vanilla|react|vue|svelte [--pm npm] [--link] [--force]
|
|
10
|
+
dormer dev [--port <n>] [--host <name>] [--pass-env <VAR>]...
|
|
11
|
+
dormer build [--minify] [--no-sourcemap] [--quiet]
|
|
12
|
+
dormer package [--target deb|rpm|appimage|app|dmg|nsis|msi]... [--out <dir>] [--skip-build] [--dry-run]
|
|
13
|
+
dormer doctor
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Every command has `--help`. Errors carry a fix hint; `DORMER_CLI_DEBUG=1` adds stack traces.
|
|
17
|
+
|
|
18
|
+
## Installing
|
|
19
|
+
|
|
20
|
+
`npm create dormer my-app -- --template react` (package `create-dormer`) scaffolds an app and installs
|
|
21
|
+
`@dormer/cli`, which carries the compiled CLI (`dist/cli.js`, built by `build.mjs` on `prepare`; Node does
|
|
22
|
+
not type-strip `.ts` under `node_modules`) and the templates (copied in by `prepack`). The native binaries
|
|
23
|
+
come as optional dependencies `@dormer/host-<platform>-<arch>` and `@dormer/bundle-<platform>-<arch>`,
|
|
24
|
+
generated by `scripts/npm-platform-packages.mjs`; `scripts/publish-npm.sh` publishes everything in
|
|
25
|
+
dependency order (rehearse against a local registry with `--registry`).
|
|
26
|
+
|
|
27
|
+
**Continuous publishing.** `.github/workflows/publish-npm.yml` publishes on every push to `main` or
|
|
28
|
+
`macos-bringup` that passes the tests (`cargo test`, `npm test`, the template e2e): the repository keeps
|
|
29
|
+
`0.<minor>.0` as its development version and each run publishes `0.<minor>.<run number>`
|
|
30
|
+
(`scripts/set-version.mjs` rewrites every package, template and internal dependency at publish time), so
|
|
31
|
+
`npm create dormer` installs the latest green commit. Binaries are built per platform in the same run.
|
|
32
|
+
It needs the `NPM_TOKEN` repository secret (granular token for the `dormer` org with 2FA bypass).
|
|
33
|
+
|
|
34
|
+
## App layout and `dormer.config.json`
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
my-app/
|
|
38
|
+
dormer.config.json
|
|
39
|
+
src/main.ts backend (Node sidecar), uses @dormer/api
|
|
40
|
+
renderer/ Vite project (index.html, vite.config.ts with @dormer/vite-plugin, src/)
|
|
41
|
+
dist/ owned by `dormer build`: renderer/, main.js(.map), dormer.config.json,
|
|
42
|
+
dormer-app.json, bundle.json (after `dormer package`)
|
|
43
|
+
.dormer/dev/main.js backend bundle used by `dormer dev`
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```jsonc
|
|
47
|
+
{
|
|
48
|
+
"name": "my-app", // required: app and package name
|
|
49
|
+
"identifier": "com.example.my-app", // required: reverse-DNS bundle identifier
|
|
50
|
+
"version": "0.1.0", // required: semver
|
|
51
|
+
"description": "…",
|
|
52
|
+
"entry": "src/main.ts", // backend entry (default)
|
|
53
|
+
"renderer": "renderer", // Vite root (default)
|
|
54
|
+
"windows": { "main": { "capabilities": ["app:default"] } }, // per-window capability sets (ADR-0006)
|
|
55
|
+
"firstWindow": { "label": "main", "width": 960, "height": 640, "backgroundColor": "auto" }, // host creates it before Node is ready (RISKS T22); "auto" = system light/dark
|
|
56
|
+
"csp": { "connectSrc": ["https://api.example.com"], "scriptSrc": ["'wasm-unsafe-eval'"], "workerSrc": ["blob:"] }, // page-side network, WebAssembly, blob workers: off by default
|
|
57
|
+
"node": { "version": "24", "binary": "/opt/node24/bin/node", // version pin; binary used by `package`
|
|
58
|
+
"icu": "small", "compileCache": true }, // icu "full" ships locale data for a small-icu Dormer Node (~31 MB); compile cache: default on when packaged
|
|
59
|
+
"backend": { "external": ["some-native-addon"] }, // extra bundling externals
|
|
60
|
+
"dev": { "port": 5173, "passEnv": ["MY_API_KEY"] },
|
|
61
|
+
"bundle": {
|
|
62
|
+
"targets": ["deb", "appimage"],
|
|
63
|
+
"icons": ["icons/icon.png"],
|
|
64
|
+
"sourceMaps": false, // ship *.map from dist/ (default false)
|
|
65
|
+
"linux": { "depends": ["libwebkit2gtk-4.1-0", "libgtk-3-0"] },
|
|
66
|
+
"macos": { "entitlements": "entitlements.plist", "signingIdentity": "Developer ID Application: …", "minimumSystemVersion": "13.0" },
|
|
67
|
+
"windows": { "signCommand": "signtool sign /fd sha256 /a %1" }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Commands run from any directory inside the app (the nearest `dormer.config.json` wins). Worked answers to
|
|
73
|
+
"how do I…" questions (caching, server-rendered pages, which web APIs a page gets) live in `docs/RECIPES.md`
|
|
74
|
+
and the `examples/*-probe` and `examples/cache-recipe` directories, each with a `run.sh` that proves them.
|
|
75
|
+
|
|
76
|
+
## Commands
|
|
77
|
+
|
|
78
|
+
### `create`
|
|
79
|
+
|
|
80
|
+
Copies `templates/<name>` (the monorepo's `templates/` directory, or the copy shipped inside the
|
|
81
|
+
package), substitutes `{{name}}`/`{{identifier}}`, renames `_gitignore` and prints next steps.
|
|
82
|
+
Templates depend on `@dormer/api`, `@dormer/renderer`, `@dormer/vite-plugin`, `@dormer/cli` at
|
|
83
|
+
`0.1.0`. `--link` (monorepo only) rewrites those to `file:` paths into the checkout, including the
|
|
84
|
+
transitive `@dormer/node-runtime` and `@dormer/ipc`, so a template installs and builds without
|
|
85
|
+
anything published. Outside the monorepo, `website/public/bootstrap.sh` (served at
|
|
86
|
+
https://dormer-docs.vercel.app/bootstrap.sh) performs the checkout, host build and `create --link` in one
|
|
87
|
+
command.
|
|
88
|
+
|
|
89
|
+
### `dev`
|
|
90
|
+
|
|
91
|
+
1. Picks a free port (preferred: `--port`, `dev.port`, 5173) and starts Vite programmatically on
|
|
92
|
+
`renderer/` with `strictPort`, so the URL handed to the host is exactly the one Vite serves.
|
|
93
|
+
2. Bundles the backend with esbuild in watch mode to `.dormer/dev/main.js`.
|
|
94
|
+
3. Launches `dormer-host --app-dir <app root> --entry .dormer/dev/main.js --node <current node>
|
|
95
|
+
--name <name> --dev --dev-url <vite url> --pass-env DORMER_DEV_URL --pass-env DORMER_APP_VERSION …`.
|
|
96
|
+
The backend reads `DORMER_DEV_URL` and calls `win.loadURL(devUrl)` (templates do this; in
|
|
97
|
+
production they call `win.loadApp("index.html")`).
|
|
98
|
+
4. When the backend bundle changes, the host is restarted. Closing the last window or Ctrl-C stops
|
|
99
|
+
Vite, the watcher and the host. Extra variables reach the sidecar through `--pass-env` / `dev.passEnv`;
|
|
100
|
+
`DORMER_SELFTEST` is forwarded automatically when set.
|
|
101
|
+
|
|
102
|
+
### `build`
|
|
103
|
+
|
|
104
|
+
`vite build` for the renderer → `dist/renderer`; esbuild for the backend → `dist/main.js` (+ map,
|
|
105
|
+
`--minify` optional); copies `dormer.config.json`; writes `dist/dormer-app.json`:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{ "manifestVersion": 1, "name": "…", "identifier": "…", "version": "…", "description": "…",
|
|
109
|
+
"entry": "main.js", "assets": "renderer", "node": { "version": "24" },
|
|
110
|
+
"externals": [], "windows": { "main": { "capabilities": ["app:default"] } },
|
|
111
|
+
"builtAt": "…", "cli": "0.1.0" }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Backend bundling rules (see `src/backend.ts`): one ESM file, `platform: node`, target `node22`.
|
|
115
|
+
**`@dormer/*` packages are bundled** (they are published as TypeScript sources, so bundling is what
|
|
116
|
+
makes the output runnable on plain Node without type stripping). Left external and listed in
|
|
117
|
+
`externals`: `node:*` builtins, `*.node` files, packages that look like native addons (`gypfile`,
|
|
118
|
+
`binary`, `napi` fields or a dependency on `node-gyp-build`, `bindings`, `node-pre-gyp`,
|
|
119
|
+
`prebuild-install`, …) and names in `backend.external`. A `createRequire` banner lets bundled
|
|
120
|
+
CommonJS `require()` calls (for example msgpackr's optional native accelerator) resolve from
|
|
121
|
+
`node_modules` next to the output at runtime; when such a package is absent the libraries fall
|
|
122
|
+
back to pure JavaScript.
|
|
123
|
+
|
|
124
|
+
### `package`
|
|
125
|
+
|
|
126
|
+
Builds (unless `--skip-build`), writes `dist/bundle.json`, then runs
|
|
127
|
+
`dormer-bundle --config dist/bundle.json --out <dir>` (the Rust tool from `tools/bundler`, found via
|
|
128
|
+
`DORMER_BUNDLE_BIN`, the cargo `target/{release,debug}` directory, then `PATH`). Missing binary:
|
|
129
|
+
instructions are printed and the exit code is 2; `--dry-run` only writes the file.
|
|
130
|
+
|
|
131
|
+
Interface of `bundle.json` (all paths absolute except `entry`, which is relative to `appDir`):
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"name": "my-app", "identifier": "com.example.my-app", "version": "0.1.0", "description": "…",
|
|
136
|
+
"appDir": "/abs/my-app/dist", "entry": "main.js",
|
|
137
|
+
"hostBinary": "/abs/dormer-host", "nodeBinary": "/abs/node",
|
|
138
|
+
"targets": ["deb", "appimage"],
|
|
139
|
+
"icons": ["/abs/my-app/icons/icon.png"],
|
|
140
|
+
"linux": { "depends": ["libwebkit2gtk-4.1-0", "libgtk-3-0"] },
|
|
141
|
+
"macos": { "entitlements": null, "signingIdentity": null, "minimumSystemVersion": "13.0" },
|
|
142
|
+
"windows": { "signCommand": null }
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`appDir` contains `renderer/` (served as `dormer://app/`), `main.js`, `dormer.config.json` and
|
|
147
|
+
`dormer-app.json` (whose `externals` lists native packages the bundler must copy from
|
|
148
|
+
`node_modules`). Default targets: Linux `deb, appimage`; macOS `app, dmg`; Windows `nsis`.
|
|
149
|
+
`nodeBinary` defaults to the Node running the CLI (`node.binary` / `DORMER_NODE_BIN` override); a
|
|
150
|
+
warning is printed when its major differs from `node.version`.
|
|
151
|
+
|
|
152
|
+
### `doctor`
|
|
153
|
+
|
|
154
|
+
Node ≥ 22; host binary resolution; Linux: `pkg-config --exists webkit2gtk-4.1` or
|
|
155
|
+
`ldconfig -p | grep libwebkit2gtk-4.1`, session D-Bus, display, NVIDIA hint
|
|
156
|
+
(`WEBKIT_DISABLE_DMABUF_RENDERER=1`), headless tools; macOS: Xcode CLT, OS version; Windows:
|
|
157
|
+
WebView2 Evergreen runtime registry key (`…\EdgeUpdate\Clients\{F3017226-…}\pv`). macOS and Windows
|
|
158
|
+
checks are **unverified** on those platforms. Prints the capability summary of `dormer.config.json`
|
|
159
|
+
when run inside an app. Exit 1 if any check fails.
|
|
160
|
+
|
|
161
|
+
## Host binary resolution
|
|
162
|
+
|
|
163
|
+
1. `DORMER_HOST_BIN`
|
|
164
|
+
2. `<cargo workspace>/target/{release,debug}/dormer-host`, searching for `Cargo.toml` upward from
|
|
165
|
+
the app root and from the CLI's own location (`CARGO_TARGET_DIR` honoured)
|
|
166
|
+
3. `node_modules/@dormer/host-<platform>-<arch>/bin/dormer-host` — the planned prebuilt package
|
|
167
|
+
(downloaded with checksums at install time; not published yet)
|
|
168
|
+
|
|
169
|
+
## Exit codes
|
|
170
|
+
|
|
171
|
+
| code | meaning |
|
|
172
|
+
|---|---|
|
|
173
|
+
| 0 | success |
|
|
174
|
+
| 1 | build failed, host/bundler exited non-zero, doctor found a failing check |
|
|
175
|
+
| 2 | required external binary missing (`dormer-host`, `dormer-bundle`) |
|
|
176
|
+
| 64 | usage error |
|
|
177
|
+
| 65 | `dormer.config.json` missing or invalid |
|
|
178
|
+
| 66 | required input missing (template, entry, renderer directory, Node binary) |
|
|
179
|
+
|
|
180
|
+
## Limitations
|
|
181
|
+
|
|
182
|
+
* **Dev restart model.** A backend change restarts the whole host process: windows are recreated
|
|
183
|
+
and renderer state is lost (Vite HMR itself keeps working between restarts). Restarting only the
|
|
184
|
+
Node sidecar while keeping windows needs host support (a "reload sidecar" control or restart-on-
|
|
185
|
+
exit with the same entry), which is on the Milestone 2/7 list.
|
|
186
|
+
* The templates still call `loadURL(process.env.DORMER_DEV_URL)` in dev although the host redirects
|
|
187
|
+
`loadApp()` to `--dev-url` itself; with a `firstWindow` the page is already loading and both calls
|
|
188
|
+
are no-ops, so the convention is harmless but redundant.
|
|
189
|
+
* `dormer package` ships the Node that runs the CLI unless `node.binary` (or `DORMER_NODE_BIN`) points
|
|
190
|
+
at another build, such as a small-icu Dormer build from `tools/node-build`; a published Dormer Node
|
|
191
|
+
dist that the CLI fetches by itself is not wired up yet.
|
|
192
|
+
* **Server-rendered pages (SSR)** work at the runtime level: run the framework's Node server inside the
|
|
193
|
+
backend, `win.loadURL("http://127.0.0.1:<port>/")`, and declare the origin in a capability
|
|
194
|
+
(`"origins": ["local", "http://127.0.0.1:*"]`); the page keeps the full bridge (`examples/ssr-probe`,
|
|
195
|
+
verified 2026-11-08 with plain HTML and with React 19 `renderToPipeableStream` + `hydrateRoot`). `dormer build` only knows the Vite single-page renderer, so a server build has to
|
|
196
|
+
be placed next to `dist/main.js` by the app's own build step.
|
|
197
|
+
|
|
198
|
+
## Tests
|
|
199
|
+
|
|
200
|
+
`npm test -w @dormer/cli` runs unit tests. `npm run test:templates` (root) runs
|
|
201
|
+
`test/e2e-linux.sh`: creates each template with `--link`, installs, builds, runs the built app
|
|
202
|
+
headless (Xvfb + session D-Bus) with `DORMER_SELFTEST=1`, and exercises `dormer dev` once.
|
package/bin/dormer.mjs
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Entry shim: the CLI is TypeScript executed by Node's built-in type stripping (Node 22.18+ / 24+).
|
|
3
|
+
// On Node 22.6–22.17 type stripping exists behind a flag, so re-execute with it once.
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const [major, minor] = process.versions.node.split(".").map(Number);
|
|
8
|
+
if (major < 22 || (major === 22 && minor < 6)) {
|
|
9
|
+
console.error(`dormer needs Node.js 22.6 or newer (22.18+/24 recommended); found ${process.version}`);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
// A published copy (installed under node_modules) runs the compiled CLI, dist/cli.js (see build.mjs); a
|
|
13
|
+
// source checkout keeps running src/cli.ts so edits take effect without a rebuild.
|
|
14
|
+
import { existsSync } from "node:fs";
|
|
15
|
+
const dist = new URL("../dist/cli.js", import.meta.url);
|
|
16
|
+
const installed = /[\\/]node_modules[\\/]/.test(fileURLToPath(import.meta.url));
|
|
17
|
+
if (installed && existsSync(dist)) { await import(dist.href); }
|
|
18
|
+
else {
|
|
19
|
+
if (!process.features?.typescript && !process.env.DORMER_CLI_REEXEC) {
|
|
20
|
+
const r = spawnSync(process.execPath, ["--experimental-strip-types", "--no-warnings", fileURLToPath(import.meta.url), ...process.argv.slice(2)], {
|
|
21
|
+
stdio: "inherit", env: { ...process.env, DORMER_CLI_REEXEC: "1" },
|
|
22
|
+
});
|
|
23
|
+
process.exit(r.status ?? 1);
|
|
24
|
+
}
|
|
25
|
+
await import("../src/cli.ts");
|
|
26
|
+
}
|