@znight/cli 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +482 -2
- package/bin/znight.mjs +17 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +13 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/dev/orphan-guard.d.mts +1 -0
- package/dist/dev/orphan-guard.mjs +47 -0
- package/dist/dev/orphan-guard.mjs.map +1 -0
- package/dist/index.d.mts +679 -0
- package/dist/index.mjs +1 -0
- package/dist/main-BWsiUsxN.mjs +3865 -0
- package/dist/main-BWsiUsxN.mjs.map +1 -0
- package/dist/orphan-Dn-2xMBd.mjs +45 -0
- package/dist/orphan-Dn-2xMBd.mjs.map +1 -0
- package/package.json +71 -4
- package/src/cli.ts +11 -0
- package/src/command.ts +217 -0
- package/src/commands/build.ts +159 -0
- package/src/commands/dev.ts +249 -0
- package/src/commands/generate.ts +304 -0
- package/src/commands/new.ts +245 -0
- package/src/commands/openapi.ts +160 -0
- package/src/commands/schematic.ts +110 -0
- package/src/commands/shared.ts +106 -0
- package/src/commands/start.ts +79 -0
- package/src/context.ts +126 -0
- package/src/dev/orphan-guard.ts +49 -0
- package/src/dev/orphan.ts +74 -0
- package/src/dev/supervisor.ts +175 -0
- package/src/errors.ts +64 -0
- package/src/index.ts +73 -0
- package/src/integrations/compiler.ts +360 -0
- package/src/integrations/openapi.ts +97 -0
- package/src/integrations/process.ts +180 -0
- package/src/integrations/prompts.ts +108 -0
- package/src/integrations/resolve.ts +38 -0
- package/src/integrations/vite.ts +362 -0
- package/src/integrations/watch.ts +243 -0
- package/src/main.ts +195 -0
- package/src/names.ts +106 -0
- package/src/output.ts +110 -0
- package/src/runtime.ts +86 -0
- package/src/scaffold/create.ts +65 -0
- package/src/scaffold/options.ts +103 -0
- package/src/scaffold/templates.ts +771 -0
- package/src/scaffold/versions.ts +25 -0
- package/src/schematics/index.ts +422 -0
- package/src/version.ts +20 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ugho STEPHAN
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,483 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @znight/cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The `znight` command line of [Znight](../../README.md): create a project, generate code, generate the
|
|
4
|
+
dependency injection container, run the development loop, build and start the application.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
bunx @znight/cli new my-service # or: npx @znight/cli new my-service, pnpm dlx @znight/cli new my-service
|
|
8
|
+
cd my-service
|
|
9
|
+
bun run dev # or: npm run dev
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
In a project, install it next to its peers (`znight new` does it for you):
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
# bun
|
|
16
|
+
bun add -d @znight/cli @znight/compiler vite
|
|
17
|
+
|
|
18
|
+
# npm
|
|
19
|
+
npm install -D @znight/cli @znight/compiler vite
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`vite`, `@znight/compiler` and `@znight/openapi` are (optional) peer dependencies: the CLI always
|
|
23
|
+
uses the versions installed in your project, and only loads them for the commands that need them
|
|
24
|
+
(`generate`, `dev`, `build`, `openapi`). Runs on Node.js >= 24 and Bun >= 1.3.
|
|
25
|
+
|
|
26
|
+
**Documentation:** [`@znight/cli`](https://github.com/znight-org/znight/blob/main/docs/packages/cli.md)
|
|
27
|
+
and [getting started](https://github.com/znight-org/znight/blob/main/docs/guide/getting-started.md).
|
|
28
|
+
**Examples:** every [example application](https://github.com/znight-org/znight/tree/main/examples)
|
|
29
|
+
runs on the CLI (`generate`, `dev`, `build`, `start` scripts);
|
|
30
|
+
[`storefront-api`](https://github.com/znight-org/znight/tree/main/examples/storefront-api) also
|
|
31
|
+
writes its committed `openapi.json` with `znight openapi` and checks it with `--check`.
|
|
32
|
+
|
|
33
|
+
- [Commands](#commands)
|
|
34
|
+
- [`znight new`](#znight-new)
|
|
35
|
+
- [`znight generate`](#znight-generate)
|
|
36
|
+
- [`znight g`](#znight-g)
|
|
37
|
+
- [`znight dev`](#znight-dev)
|
|
38
|
+
- [`znight build`](#znight-build)
|
|
39
|
+
- [`znight start`](#znight-start)
|
|
40
|
+
- [`znight openapi`](#znight-openapi)
|
|
41
|
+
- [Global options, exit codes, environment](#global-options-exit-codes-environment)
|
|
42
|
+
- [Programmatic API](#programmatic-api)
|
|
43
|
+
- [Compiler contract](#compiler-contract)
|
|
44
|
+
|
|
45
|
+
## Commands
|
|
46
|
+
|
|
47
|
+
| Command | Description |
|
|
48
|
+
| ----------------------------- | ---------------------------------------------------------------------------------------- |
|
|
49
|
+
| `znight new [name]` | Create a project (interactive, or from flags) |
|
|
50
|
+
| `znight generate` | Write the generated container (`src/.znight/container.generated.ts`) |
|
|
51
|
+
| `znight generate --check` | Exit with code 1 when the generated file is missing or out of date (CI) |
|
|
52
|
+
| `znight generate --watch` | Keep the generated file up to date (for `tsc --watch` and editors) |
|
|
53
|
+
| `znight g <schematic> <name>` | Generate a service, controller, consumer, config, guard, interceptor, factory or job |
|
|
54
|
+
| `znight dev` | Vite build in watch mode, full restart of the application after every rebuild |
|
|
55
|
+
| `znight build` | Production build (`vite build`); `--openapi` also writes `dist/openapi.json` |
|
|
56
|
+
| `znight start` | Run the production build |
|
|
57
|
+
| `znight openapi` | Write the OpenAPI document (`openapi.json`); `--check` exits with code 1 when stale (CI) |
|
|
58
|
+
|
|
59
|
+
`znight <command> --help` prints the options of a command; `znight help <command>` too.
|
|
60
|
+
|
|
61
|
+
## `znight new`
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
znight new my-service # asks the questions below
|
|
65
|
+
znight new my-service --yes # defaults, no question
|
|
66
|
+
znight new my-service --pm pnpm --adapter fastify --features config,observability,messaging
|
|
67
|
+
znight new my-worker --yes --features config,schedule,openapi
|
|
68
|
+
znight new . --yes --no-install # in the current (empty) directory
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
In a terminal, the CLI asks for the name, the package manager, the HTTP adapter, the features,
|
|
72
|
+
and whether to install the dependencies and initialise git. Flags answer the questions up front; with
|
|
73
|
+
`--yes` (or without a terminal, e.g. in CI) the defaults are used.
|
|
74
|
+
|
|
75
|
+
| Option | Default | Description |
|
|
76
|
+
| ------------------------------------------ | --------------------------------------------- | -------------------------------------------------------------------------------- |
|
|
77
|
+
| `[name]` | (asked) | npm package name (`my-service`, `@acme/api`), or `.` |
|
|
78
|
+
| `-y, --yes` | `false` | Do not ask questions |
|
|
79
|
+
| `--pm <bun\|npm\|pnpm\|yarn>` | the one running the CLI, else bun on Bun, npm | Package manager |
|
|
80
|
+
| `--adapter <node\|fastify\|express\|hono>` | `node` | HTTP adapter (`node`: the built-in `node:http` server) |
|
|
81
|
+
| `--features <list>` | `config,observability` | `config`, `observability`, `messaging`, `schedule`, `openapi`, or `all` / `none` |
|
|
82
|
+
| `--no-install` | install | Do not install the dependencies |
|
|
83
|
+
| `--no-git` | init | Do not run `git init` (skipped anyway inside a git repository) |
|
|
84
|
+
| `--dir <path>` | `./<name>` (unscoped) | Target directory |
|
|
85
|
+
| `--force` | `false` | Write into a non-empty directory (existing files are overwritten) |
|
|
86
|
+
|
|
87
|
+
The package manager defaults to the one that launched the CLI (`npx`, `pnpm dlx`, `yarn dlx`,
|
|
88
|
+
`bunx`). The target directory must be empty (`.git`, `.DS_Store` and editor folders are fine).
|
|
89
|
+
|
|
90
|
+
### The generated project
|
|
91
|
+
|
|
92
|
+
```txt
|
|
93
|
+
my-service/
|
|
94
|
+
├─ src/
|
|
95
|
+
│ ├─ main.ts # Application.create() with the plugins, then start()
|
|
96
|
+
│ ├─ api.ts # @OpenApiDocument: title, tags of openapi.json (openapi)
|
|
97
|
+
│ └─ app/
|
|
98
|
+
│ ├─ app.config.ts # @Config('app'): PORT, APP_GREETING (config)
|
|
99
|
+
│ ├─ greeting.schemas.ts # Zod schema of the request body
|
|
100
|
+
│ ├─ greeting.service.ts # @Injectable, uses AppConfig and Logger
|
|
101
|
+
│ ├─ greeting.controller.ts # GET /greetings/:name?excited, POST /greetings (validated body)
|
|
102
|
+
│ ├─ greeting.consumer.ts # @Consumer('greetings'), POST /greetings/async (messaging)
|
|
103
|
+
│ ├─ health.controller.ts # GET /health
|
|
104
|
+
│ └─ heartbeat.jobs.ts # @Cron('* * * * *'): a job every minute (schedule)
|
|
105
|
+
├─ test/
|
|
106
|
+
│ ├─ greeting.service.test.ts # TestBed: real factory, auto-mocked dependencies
|
|
107
|
+
│ ├─ greeting.controller.test.ts # TestApp: in-memory HTTP requests
|
|
108
|
+
│ └─ heartbeat.jobs.test.ts # TestApp + createTestClock(): virtual time (schedule)
|
|
109
|
+
├─ vite.config.ts # plugins: [znight(options)]
|
|
110
|
+
├─ znight.config.ts # compiler options, shared by znight(), the Bun preload and znight generate
|
|
111
|
+
├─ tsconfig.json # strict, experimentalDecorators, moduleResolution bundler, noEmit
|
|
112
|
+
├─ bunfig.toml # bun only: preloads @znight/compiler/bun before the tests
|
|
113
|
+
├─ package.json # type module; scripts dev, build, start, generate, typecheck, test
|
|
114
|
+
└─ .gitignore # node_modules, dist, .znight
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- `@znight/*` packages are pinned to `^<version of the CLI>` (they are released together).
|
|
118
|
+
- Tests run with `bun test` for Bun projects (the preload regenerates the container), with Vitest
|
|
119
|
+
otherwise (the `znight()` plugin of `vite.config.ts` regenerates it). Both, like
|
|
120
|
+
`znight generate`, read the compiler options of `znight.config.ts`.
|
|
121
|
+
- `typecheck` runs `znight generate && tsc --noEmit`: the generated container is git-ignored.
|
|
122
|
+
- The adapter's peer dependencies are added (`fastify`; `express` + `@types/express`; `hono` +
|
|
123
|
+
`@hono/node-server`), plus `light-my-request` (dev) for `TestApp.inject()` with the `node` and
|
|
124
|
+
`express` adapters. Yarn projects get a `.yarnrc.yml` with `nodeLinker: node-modules`.
|
|
125
|
+
- Bun projects run the application on Bun: their `dev` and `start` scripts pass `--runtime bun`
|
|
126
|
+
(`bun run` runs the `znight` binary, which has a `node` shebang, on Node.js).
|
|
127
|
+
- `schedule` adds `@znight/schedule`, `SchedulePlugin.configure()` **before** `HttpPlugin` (plugins
|
|
128
|
+
stop in reverse order: HTTP stops taking requests first, then running jobs drain), a sample job
|
|
129
|
+
and its test on a virtual clock (`createTestClock()` of `@znight/schedule/testing`).
|
|
130
|
+
- `openapi` adds `@znight/openapi`, `OpenApiPlugin.configure()` after `HttpPlugin` (`/openapi.json`
|
|
131
|
+
and the API reference at `/docs`), `src/api.ts` (`@OpenApiDocument`) and the `openapi` /
|
|
132
|
+
`openapi:check` scripts (commit `openapi.json`, check it in CI).
|
|
133
|
+
- The `TestApp` test configures `ObservabilityPlugin` with `metrics: { server: false }`: with
|
|
134
|
+
`prom-client` installed, the metrics server (`:9464`) would collide with `znight dev` or with other
|
|
135
|
+
test files running in parallel.
|
|
136
|
+
|
|
137
|
+
## `znight generate`
|
|
138
|
+
|
|
139
|
+
Runs the compiler and writes `src/.znight/container.generated.ts` when its content changed. Compiler
|
|
140
|
+
diagnostics are printed with code frames; any error exits with code 1 (and nothing is written).
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
znight generate # ✓ Generated src/.znight/container.generated.ts (12 providers, 30 files, 48ms)
|
|
144
|
+
znight generate --check # CI: exit 1 when the file is missing or out of date (never writes)
|
|
145
|
+
znight generate --watch # regenerate on every change (incremental: only changed files are re-parsed)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
| Option | Description |
|
|
149
|
+
| ------------------------------------------ | ----------------------------------------------------------------------------------------- |
|
|
150
|
+
| `--check` | Do not write; exit 1 when the generated files are missing or stale (each one is named) |
|
|
151
|
+
| `-w, --watch` | Regenerate on changes of the sources and of the files the compiler read (see below) |
|
|
152
|
+
| `--root <dir>` | Project root (default: the current directory) |
|
|
153
|
+
| `--config <file>` | Vite config to read the `znight()` options from, instead of `znight.config.*` (see below) |
|
|
154
|
+
| `--config-loader <bundle\|runner\|native>` | How Vite loads the config file |
|
|
155
|
+
|
|
156
|
+
The compiler options are looked up in this order, so that `znight generate`, `vite build` and the
|
|
157
|
+
Bun preload produce the same file:
|
|
158
|
+
|
|
159
|
+
1. `znight.config.ts` (or `.mts`, `.js`, `.mjs`) in the project root, or the file named by the
|
|
160
|
+
`ZNIGHT_CONFIG` environment variable — the file the [Bun preload](../compiler/README.md#preload-options)
|
|
161
|
+
reads (`loadZnightConfig()` of the compiler; its preload-only `logLevel` is ignored). Vite is not
|
|
162
|
+
loaded;
|
|
163
|
+
2. otherwise the options `znight()` compiles with in your Vite config: Vite resolves the config as
|
|
164
|
+
for `znight build`, and the CLI takes the options given to the plugin with what the plugin derives
|
|
165
|
+
from the config — the Vite `root` (the config's `root` is relative to the project root), the string
|
|
166
|
+
entries of `resolve.alias` and the runtime conditions (see [Compiler contract](#compiler-contract));
|
|
167
|
+
`--config <file>` skips step 1 and reads that Vite config;
|
|
168
|
+
3. otherwise the defaults (without a Vite config, Vite is not even loaded).
|
|
169
|
+
|
|
170
|
+
Share one options object between the Vite plugin and the preload with a `znight.config.ts`:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
// znight.config.ts
|
|
174
|
+
import type { ZnightOptions } from '@znight/compiler'
|
|
175
|
+
|
|
176
|
+
export default { reportUnused: true } satisfies ZnightOptions
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
// vite.config.ts
|
|
181
|
+
import { znight } from '@znight/compiler'
|
|
182
|
+
import { defineConfig } from 'vite'
|
|
183
|
+
import options from './znight.config.ts'
|
|
184
|
+
|
|
185
|
+
export default defineConfig({ plugins: [znight(options)] })
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`znight generate` compiles in `serve` mode (provider source locations kept) while `vite build` writes
|
|
189
|
+
the disk copy without them: run `znight generate --check` before `znight build`, as below.
|
|
190
|
+
|
|
191
|
+
In CI:
|
|
192
|
+
|
|
193
|
+
```sh
|
|
194
|
+
npx znight generate --check # or: npx znight generate, before tsc and the tests
|
|
195
|
+
npx tsc --noEmit
|
|
196
|
+
npx znight build
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`--check` names every generated file that is missing or out of date: the module and, in library
|
|
200
|
+
mode (`library: true`), `dist/znight.providers.json`. A build that cleans `dist/` (tsdown, `tsc --build
|
|
201
|
+
--clean`...) deletes that manifest, which `package.json` points to: run `znight generate` after such a
|
|
202
|
+
build (the Vite plugin writes it itself at the end of `vite build`).
|
|
203
|
+
|
|
204
|
+
`--watch` watches what the Vite plugin and the Bun preload watch: the `include` directories of the
|
|
205
|
+
project and of its source libraries (recursively), the project root (`package.json`, `tsconfig.json`,
|
|
206
|
+
lockfiles) and the directories of the files the last compile read (library manifests, compiled
|
|
207
|
+
libraries), refreshed after each compile. Only changes that can change the result trigger a compile,
|
|
208
|
+
by the rules of the Vite plugin (`isRelevantChange()` of the compiler: sources the compiler parses,
|
|
209
|
+
`.js` included; resolution config files; files it read; a file created where a relative import did
|
|
210
|
+
not resolve); a directory created, deleted or moved under an `include` directory triggers a rescan,
|
|
211
|
+
since `fs.watch` reports it alone, without its files.
|
|
212
|
+
|
|
213
|
+
## `znight g`
|
|
214
|
+
|
|
215
|
+
```sh
|
|
216
|
+
znight g service users/user # src/users/user.service.ts UserService
|
|
217
|
+
znight g controller order # src/order/order.controller.ts OrderController
|
|
218
|
+
znight g consumer orders --path src/messaging
|
|
219
|
+
znight g guard auth --flat # src/auth.guard.ts AuthGuard
|
|
220
|
+
znight g interceptor timing --aop # a method interceptor (@Intercept)
|
|
221
|
+
znight g config payment-gateway # @Config('paymentGateway'): PAYMENT_GATEWAY_* variables
|
|
222
|
+
znight g job reports/digest # src/reports/digest.jobs.ts DigestJobs
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
| Schematic (alias) | Generates |
|
|
226
|
+
| --------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
227
|
+
| `service` (`s`) | an `@Injectable()` class |
|
|
228
|
+
| `controller` (`co`) | an `@Controller('/<name>')` with `GET /` and `GET /:id` |
|
|
229
|
+
| `consumer` (`cs`) | an `@Consumer('<name>')` with a retried `@OnMessage()` handler (a Zod message schema when the project uses `zod`) |
|
|
230
|
+
| `config` (`cf`) | a `@Config('<name>')` class |
|
|
231
|
+
| `guard` (`gu`) | an `@Injectable()` `CanActivate` guard (with a commented `static enforces` line for route decisions) |
|
|
232
|
+
| `interceptor` (`itc`) | an `@Injectable()` `HttpInterceptor` (`--aop`: a `MethodInterceptor` for `@Intercept`) |
|
|
233
|
+
| `factory` (`f`) | a `@Factory()` with a `@Provides()` method |
|
|
234
|
+
| `job` (`j`) | a class of scheduled jobs (`<Name>Jobs`, `<name>.jobs.ts`) with a `@Cron` method (`@znight/schedule`) |
|
|
235
|
+
|
|
236
|
+
Names are converted: files are `kebab-case.<schematic>.ts`, classes `PascalCase<Schematic>`
|
|
237
|
+
(`UserProfile`, `user_profile` and `user-profile.service` all give `user-profile.service.ts` /
|
|
238
|
+
`UserProfileService`). Directories before the last `/` are relative to `src/`; without them the file
|
|
239
|
+
goes to `src/<name>/`. Nothing has to be registered afterwards: the compiler discovers the class.
|
|
240
|
+
|
|
241
|
+
Job names must be unique in the application (ZNIGHT041), so the `@Cron` of `job` is named after the
|
|
242
|
+
location of the file: its directories relative to `src/` (to the project root for a `--path` outside
|
|
243
|
+
`src/`), then the name, without the directory named after it. `znight g job digest` gives
|
|
244
|
+
`digest.daily`, `znight g job reports/digest` gives `reports.digest.daily` and
|
|
245
|
+
`znight g job digest --path src/emails` gives `emails.digest.daily`.
|
|
246
|
+
|
|
247
|
+
| Option | Description |
|
|
248
|
+
| --------------- | ---------------------------------------------------------- |
|
|
249
|
+
| `--path <dir>` | Directory of the file, relative to the current directory |
|
|
250
|
+
| `--flat` | Do not create a directory named after the element |
|
|
251
|
+
| `-f, --force` | Overwrite an existing file (refused otherwise) |
|
|
252
|
+
| `-d, --dry-run` | Print the file instead of writing it |
|
|
253
|
+
| `--aop` | `interceptor`: a method interceptor instead of an HTTP one |
|
|
254
|
+
| `--root <dir>` | Project root (default: the nearest `package.json`) |
|
|
255
|
+
|
|
256
|
+
`znight generate <schematic> <name>` and `znight generate:<schematic> <name>` are aliases.
|
|
257
|
+
|
|
258
|
+
## `znight dev`
|
|
259
|
+
|
|
260
|
+
```sh
|
|
261
|
+
znight dev
|
|
262
|
+
znight dev --runtime bun --clear
|
|
263
|
+
znight dev --inspect -- --verbose # arguments after -- go to the application
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
`znight dev` runs `vite build --watch` with your Vite config (no Vite dev server) and **restarts the
|
|
267
|
+
whole process** after every successful build. There is no partial hot replacement of providers: a
|
|
268
|
+
restart is fast and always consistent.
|
|
269
|
+
|
|
270
|
+
1. A build succeeds → after `--debounce` ms, the running process receives `SIGTERM` (the application
|
|
271
|
+
stops gracefully), gets `--kill-timeout` ms to exit (then `SIGKILL`), and the new build starts.
|
|
272
|
+
Bursts of builds cause a single restart; restarts never overlap.
|
|
273
|
+
2. A build fails → Vite prints the error and **the last good process keeps running**.
|
|
274
|
+
3. The compilation fails (`ZNIGHT` errors) → the `znight()` plugin prints the diagnostics; in watch
|
|
275
|
+
mode it does not fail the build (the bundle contains a module throwing them), so the CLI reads the
|
|
276
|
+
plugin's result (`plugin.api.result`) and does **not** restart: the last good process keeps
|
|
277
|
+
running (a restart still pending for a previous build is cancelled), and nothing starts until a
|
|
278
|
+
build compiles. With `--log-level silent` the CLI prints the diagnostics itself.
|
|
279
|
+
4. The application crashes or exits → the CLI says so and waits for the next change.
|
|
280
|
+
5. `Ctrl+C` (or `SIGTERM`, `SIGHUP` when the terminal is closed, `Ctrl+\` / `SIGQUIT`) → the
|
|
281
|
+
watcher closes and the application stops gracefully (`SIGINT` after `Ctrl+C`, `SIGTERM` otherwise);
|
|
282
|
+
a second signal kills it. The application runs in its own session and process group, so it
|
|
283
|
+
receives exactly one signal, and never the terminal's.
|
|
284
|
+
6. The CLI dies without stopping it (`kill -9`, the OOM killer) → the application stops by itself
|
|
285
|
+
(`SIGTERM`, then `SIGKILL` after `--kill-timeout`) within half a second instead of keeping its
|
|
286
|
+
port: the CLI preloads a small guard into it (`--import` on Node.js, `--preload` on Bun) that
|
|
287
|
+
watches the CLI process. It needs nothing from the application, and the processes the
|
|
288
|
+
application starts itself are left alone.
|
|
289
|
+
|
|
290
|
+
Diagnostics of the compiler are printed on every rebuild by the `znight()` plugin.
|
|
291
|
+
|
|
292
|
+
```txt
|
|
293
|
+
✗ ZNIGHT001: Missing provider for 'Clock' required by GreetingService
|
|
294
|
+
--> src/app/greeting.service.ts:12:30
|
|
295
|
+
✗ Compilation failed with 1 error. The last good build keeps running. Waiting for changes...
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
| Option | Default | Description |
|
|
299
|
+
| ------------------------------------------ | ----------------- | ------------------------------------------------------------------- |
|
|
300
|
+
| `--root <dir>` | current directory | Project root |
|
|
301
|
+
| `--config <file>` | `vite.config.*` | Vite config file |
|
|
302
|
+
| `--config-loader <bundle\|runner\|native>` | Vite's | How Vite loads the config file |
|
|
303
|
+
| `--mode <mode>` | `development` | Vite mode |
|
|
304
|
+
| `--runtime <auto\|node\|bun>` | `auto` | Runtime of the application (`auto`: the one running the CLI, below) |
|
|
305
|
+
| `--entry <file>` | entry chunk | File to run (default: the entry chunk of the build, `dist/main.js`) |
|
|
306
|
+
| `--inspect` | `false` | Start the application with `--inspect` |
|
|
307
|
+
| `--clear` | `false` | Clear the screen before each rebuild |
|
|
308
|
+
| `--debounce <ms>` | `100` | Delay between a build and the restart |
|
|
309
|
+
| `--kill-timeout <ms>` | `5000` | Time to stop after `SIGTERM` before `SIGKILL` |
|
|
310
|
+
| `--log-level <info\|warn\|error\|silent>` | `warn` | Vite log level |
|
|
311
|
+
|
|
312
|
+
The application runs with `NODE_ENV=development` (unless set when the CLI starts: the `NODE_ENV`
|
|
313
|
+
that Vite sets in the CLI process for its build is not passed on) and, on Node.js, with
|
|
314
|
+
`--enable-source-maps`, so stack traces point to your TypeScript files.
|
|
315
|
+
|
|
316
|
+
`--runtime auto` is the runtime running the CLI. Under `bun run` (or `bunx`) that is Node.js: the
|
|
317
|
+
`znight` binary has a `node` shebang, which Bun honours. Pass `--runtime bun` (as the `dev` and
|
|
318
|
+
`start` scripts of Bun projects created by `znight new` do), or run `bun --bun run dev`.
|
|
319
|
+
|
|
320
|
+
## `znight build`
|
|
321
|
+
|
|
322
|
+
```sh
|
|
323
|
+
znight build # ✓ Built dist/main.js 48.20 kB in 1.24s
|
|
324
|
+
znight build --mode staging
|
|
325
|
+
znight build --openapi # also dist/openapi.json (--openapi=<path> for another file)
|
|
326
|
+
znight build --openapi --openapi-prefix /api # HttpPlugin.configure({ prefix: '/api' })
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Runs `vite build` with your Vite config. With the defaults of the `znight()` plugin, `src/main.ts`
|
|
330
|
+
is built to `dist/main.js` (Node.js 24 target, source maps). Any compiler error fails the build
|
|
331
|
+
(exit code 1). The `root` of the Vite config is honoured, relative to the project root (`--root`),
|
|
332
|
+
like the Vite CLI run in the project directory; `znight dev` and `znight generate` resolve it the
|
|
333
|
+
same way.
|
|
334
|
+
|
|
335
|
+
| Option | Default | Description |
|
|
336
|
+
| ------------------------------------------ | ----------------- | ------------------------------ |
|
|
337
|
+
| `--root <dir>` | current directory | Project root |
|
|
338
|
+
| `--config <file>` | `vite.config.*` | Vite config file |
|
|
339
|
+
| `--config-loader <bundle\|runner\|native>` | Vite's | How Vite loads the config file |
|
|
340
|
+
| `--mode <mode>` | `production` | Vite mode |
|
|
341
|
+
| `--log-level <info\|warn\|error\|silent>` | `info` | Vite log level |
|
|
342
|
+
| `--openapi[=<path>]` | off | Also emit the OpenAPI document |
|
|
343
|
+
| `--openapi-prefix <path>` | none | With `--openapi`: HTTP prefix |
|
|
344
|
+
|
|
345
|
+
`--openapi` adds the `openapi()` Vite plugin of `@znight/openapi` to the build when the Vite config
|
|
346
|
+
does not register one: after the bundle is written, the document is emitted to `dist/openapi.json`
|
|
347
|
+
(next to `dist/main.js`, ready for `OpenApiPlugin.configure({ document: { file } })`), or to the
|
|
348
|
+
given path.
|
|
349
|
+
|
|
350
|
+
When `HttpPlugin` has a `prefix`, pass it with `--openapi-prefix` (like `znight openapi --prefix`):
|
|
351
|
+
it becomes the `servers` of the document (`[{ "url": "/api" }]`) unless `@OpenApiDocument` declares
|
|
352
|
+
servers, so the file is the document the application serves in `'build'` mode. Without it, the
|
|
353
|
+
document has no `servers`, and the API reference ("Test request", "Try it out") and generated
|
|
354
|
+
clients call `/greetings` instead of `/api/greetings`. When the Vite config registers its own
|
|
355
|
+
`openapi()`, that plugin emits the document with its options: `--openapi=<path>` and
|
|
356
|
+
`--openapi-prefix` are then ignored, with a warning (set `output` and `prefix` on that `openapi()`).
|
|
357
|
+
|
|
358
|
+
## `znight start`
|
|
359
|
+
|
|
360
|
+
```sh
|
|
361
|
+
znight start # node --enable-source-maps dist/main.js
|
|
362
|
+
znight start --runtime bun
|
|
363
|
+
znight start --entry dist/worker.js -- --verbose
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Runs the production build with `NODE_ENV=production` (unless set). On Linux and macOS the CLI
|
|
367
|
+
**replaces itself** with the runtime (`execve`): same PID, signals and exit code go straight to the
|
|
368
|
+
application, which makes `znight start` a good container entry point. Elsewhere the application runs
|
|
369
|
+
as a child process: `SIGINT`, `SIGTERM` and `SIGHUP` are forwarded and its exit code is returned
|
|
370
|
+
(`128 + n` when it is killed by a signal).
|
|
371
|
+
|
|
372
|
+
| Option | Default | Description |
|
|
373
|
+
| ----------------------------- | ------------------------------- | ----------------------------------------------- |
|
|
374
|
+
| `--root <dir>` | current directory | Project root (the working directory of the app) |
|
|
375
|
+
| `--entry <file>` | `dist/main.js`, `dist/main.mjs` | File to run |
|
|
376
|
+
| `--runtime <auto\|node\|bun>` | `auto` | Runtime (`auto`: the one running the CLI\*) |
|
|
377
|
+
|
|
378
|
+
\* Node.js under `bun run`, see [`znight dev`](#znight-dev).
|
|
379
|
+
|
|
380
|
+
## `znight openapi`
|
|
381
|
+
|
|
382
|
+
```sh
|
|
383
|
+
znight openapi # writes openapi.json at the project root
|
|
384
|
+
znight openapi -o docs/openapi.yaml # YAML, chosen by the extension (needs yaml)
|
|
385
|
+
znight openapi --check # CI: exit 1 when the file is missing or stale; never writes
|
|
386
|
+
znight openapi --prefix /api # servers default when @OpenApiDocument declares none
|
|
387
|
+
znight openapi --runtime bun # load the modules with Bun (default: the runtime running the CLI)
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Writes the OpenAPI document of the application with `@znight/openapi` (which must be installed in
|
|
391
|
+
the project). The application is compiled like `znight generate` does (compile errors are printed
|
|
392
|
+
and exit with code 1 before any module runs), then its generated module is loaded in a child
|
|
393
|
+
process (Bun: the compiler preload; Node.js: a Vite server in middleware mode), so module side
|
|
394
|
+
effects never reach the CLI. Nothing is instantiated. Outputs are only written when they change;
|
|
395
|
+
the file is byte-identical on Node.js and Bun. Commit it and run `znight openapi --check` in CI: an
|
|
396
|
+
API change shows up in the pull request.
|
|
397
|
+
|
|
398
|
+
| Option | Default | Description |
|
|
399
|
+
| ------------------------------------------ | ----------------- | ------------------------------------------------------------------ |
|
|
400
|
+
| `-o, --output <file>` | `openapi.json` | Output file, repeatable (`.yaml` / `.yml` write YAML) |
|
|
401
|
+
| `--check` | `false` | Do not write: exit with code 1 when an output is missing or stale |
|
|
402
|
+
| `--prefix <path>` | none | HTTP prefix: the `servers` default (`[{ url: prefix }]`) |
|
|
403
|
+
| `--runtime <auto\|node\|bun>` | `auto` | Runtime loading the application modules |
|
|
404
|
+
| `--converters <file>` | none | Module whose default export is a `SchemaConverter[]` |
|
|
405
|
+
| `--root <dir>` | current directory | Project root |
|
|
406
|
+
| `--config <file>` | `vite.config.*` | Vite config file (compiler options and the Node.js module loading) |
|
|
407
|
+
| `--config-loader <bundle\|runner\|native>` | Vite's | How Vite loads the config file |
|
|
408
|
+
|
|
409
|
+
## Global options, exit codes, environment
|
|
410
|
+
|
|
411
|
+
| Option | Description |
|
|
412
|
+
| ----------------------- | ------------------------------------------------- |
|
|
413
|
+
| `-h, --help` | Help (`znight --help`, `znight <command> --help`) |
|
|
414
|
+
| `-v, --version` | Version |
|
|
415
|
+
| `--color`, `--no-color` | Force or disable colors |
|
|
416
|
+
| `--debug` | Print the stack trace of unexpected errors |
|
|
417
|
+
|
|
418
|
+
| Exit code | Meaning |
|
|
419
|
+
| --------- | ------------------------------------------------------------------------------------------- |
|
|
420
|
+
| `0` | Success |
|
|
421
|
+
| `1` | Failure: compiler errors, failed build, stale generated file, missing build, file exists... |
|
|
422
|
+
| `2` | Invalid usage: unknown command or option (with a suggestion), missing or invalid argument |
|
|
423
|
+
| `130` | Cancelled (prompt cancelled, second `Ctrl+C` in `znight dev`) |
|
|
424
|
+
| other | `znight start`: the exit code of the application |
|
|
425
|
+
|
|
426
|
+
| Variable | Effect |
|
|
427
|
+
| ----------------------- | ----------------------------------------------------- |
|
|
428
|
+
| `NO_COLOR` | Disables colors (any non-empty value) |
|
|
429
|
+
| `FORCE_COLOR` | Forces colors (`0` / `false` disable them) |
|
|
430
|
+
| `ZNIGHT_DEBUG=1` | Same as `--debug` |
|
|
431
|
+
| `CI` | No prompts (`znight new` uses the flags and defaults) |
|
|
432
|
+
| `npm_config_user_agent` | Default package manager of `znight new` |
|
|
433
|
+
|
|
434
|
+
## Programmatic API
|
|
435
|
+
|
|
436
|
+
```ts
|
|
437
|
+
import { main, planSchematic, renderProject, SCHEMATICS } from '@znight/cli'
|
|
438
|
+
|
|
439
|
+
const code = await main(['generate', '--check'], { cwd: '/path/to/app' }) // exit code, never throws
|
|
440
|
+
|
|
441
|
+
const files = renderProject({
|
|
442
|
+
name: 'my-service',
|
|
443
|
+
packageManager: 'bun',
|
|
444
|
+
adapter: 'fastify',
|
|
445
|
+
features: ['config', 'observability'],
|
|
446
|
+
znightVersion: '0.1.0',
|
|
447
|
+
}) // [{ path, content }]
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
`main(argv, options)` accepts overrides for everything it touches (`cwd`, `env`, `stdout`, `stderr`,
|
|
451
|
+
`interactive`, `signals`, and `services`: `loadCompiler`, `loadVite`, `loadPrompts`, `processes`,
|
|
452
|
+
`watch`), which is how the CLI itself is tested. `AppSupervisor` (the restart logic of `znight dev`)
|
|
453
|
+
and the schematics are exported too.
|
|
454
|
+
|
|
455
|
+
## Compiler contract
|
|
456
|
+
|
|
457
|
+
The CLI drives the project's `@znight/compiler` through its programmatic API (spec §6.2) and only
|
|
458
|
+
relies on these shapes (each is optional; missing pieces are emulated):
|
|
459
|
+
|
|
460
|
+
| Export | Used for |
|
|
461
|
+
| ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
462
|
+
| `generate(options)` | `znight generate`; `written: string[]` tells whether the file changed |
|
|
463
|
+
| `generate({ ...options, check })` | `znight generate --check`; `stale: boolean`, `staleFiles: string[]` (else compared with `diskCode` / `library`) |
|
|
464
|
+
| `createCompiler(options)` | `znight generate --watch`: `generate(changes)` (`{ changed, added, removed }`), `isOutFile(file)`, `watchFiles` |
|
|
465
|
+
| `includeRoots(compiler)` | `--watch`: the directories to watch recursively (else the whole project) |
|
|
466
|
+
| `isRelevantChange(compiler, kind, file, watched, roots)` | `--watch`: the file events that trigger a compile (else every event but the generated module) |
|
|
467
|
+
| `compile(options)` | fallback of the above (writes `diskCode`, else `code`) |
|
|
468
|
+
| `formatDiagnostics(diagnostics, { color, cwd, summary: false })` | diagnostics output (a plain formatter otherwise) |
|
|
469
|
+
| `loadZnightConfig(root)` | `znight generate`: `{ file, options }` of `znight.config.*` / `ZNIGHT_CONFIG` |
|
|
470
|
+
|
|
471
|
+
`znight build` and `znight dev` use the `znight()` Vite plugin of the project's Vite config, found by
|
|
472
|
+
its name (`znight`) among the plugins of the config:
|
|
473
|
+
|
|
474
|
+
- `znight generate` reads `plugin.api.options` (or `plugin.api.getOptions()`) when the project has
|
|
475
|
+
no `znight.config.*`, with the `root`, `aliases` and `runtimeConditions` of the plugin's compiler
|
|
476
|
+
(`plugin.api.compiler.options`, created when Vite resolves the config; without it, the Vite root
|
|
477
|
+
and the string aliases of `resolve.alias`);
|
|
478
|
+
- `znight dev` reads `plugin.api.result` (`{ ok, diagnostics }`) once a build is written, to keep the
|
|
479
|
+
last good process running when the compilation failed.
|
|
480
|
+
|
|
481
|
+
## License
|
|
482
|
+
|
|
483
|
+
MIT
|
package/bin/znight.mjs
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Launcher of the `znight` binary. It is committed (unlike dist/) so that package managers link the
|
|
3
|
+
// binary at install time, even in this repository before the packages are built.
|
|
4
|
+
import { existsSync } from 'node:fs'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
|
|
7
|
+
const cli = new URL('../dist/cli.mjs', import.meta.url)
|
|
8
|
+
|
|
9
|
+
if (!existsSync(fileURLToPath(cli))) {
|
|
10
|
+
console.error(
|
|
11
|
+
'@znight/cli is not built: dist/cli.mjs is missing.\n' +
|
|
12
|
+
'In the Znight repository, build the packages first: bun install && bun run build',
|
|
13
|
+
)
|
|
14
|
+
process.exit(1)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
await import(cli.href)
|
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { n as main } from "./main-BWsiUsxN.mjs";
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
/**
|
|
5
|
+
* The `znight` binary. Everything lives in `main()`; this file only wires the process: arguments,
|
|
6
|
+
* exit code, and a safety net for handles left open by third-party code (prompts, watchers).
|
|
7
|
+
*/
|
|
8
|
+
const code = await main(process.argv.slice(2));
|
|
9
|
+
process.exitCode = code;
|
|
10
|
+
setTimeout(() => process.exit(code), 1e3).unref();
|
|
11
|
+
export {};
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=cli.mjs.map
|
package/dist/cli.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * The `znight` binary. Everything lives in `main()`; this file only wires the process: arguments,\n * exit code, and a safety net for handles left open by third-party code (prompts, watchers).\n */\nimport { main } from './main.ts'\n\nconst code = await main(process.argv.slice(2))\nprocess.exitCode = code\n// let pending output flush, then exit even if a library left a handle open\nsetTimeout(() => process.exit(code), 1000).unref()\n"],"mappings":";;;;;;;AAOA,MAAM,OAAO,MAAM,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC;AAC7C,QAAQ,WAAW;AAEnB,iBAAiB,QAAQ,KAAK,IAAI,GAAG,GAAI,CAAC,CAAC,MAAM"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { r as parseOrphanGuard, t as ORPHAN_GUARD_ENV } from "../orphan-Dn-2xMBd.mjs";
|
|
2
|
+
// src/dev/orphan-guard.ts
|
|
3
|
+
/**
|
|
4
|
+
* Preloaded into the application by `znight dev` (see `orphan.ts`): stops the application when the
|
|
5
|
+
* CLI that started it is gone without stopping it (`kill -9`, the OOM killer).
|
|
6
|
+
*
|
|
7
|
+
* Configured by `ZNIGHT_DEV_PARENT` (removed from the environment right away: the processes the
|
|
8
|
+
* application starts, `fork()` included, are left alone), and only in the process the CLI started
|
|
9
|
+
* (whose parent is the CLI). Every poll interval it checks that it was not re-parented and that the
|
|
10
|
+
* CLI still exists (an unref'd timer: it never keeps the application alive). Once the CLI is gone,
|
|
11
|
+
* the application gets SIGTERM (its process group when it leads one, as the CLI would send it), so
|
|
12
|
+
* it stops gracefully, then SIGKILL after the kill timeout.
|
|
13
|
+
*/
|
|
14
|
+
const settings = parseOrphanGuard(process.env[ORPHAN_GUARD_ENV]);
|
|
15
|
+
Reflect.deleteProperty(process.env, ORPHAN_GUARD_ENV);
|
|
16
|
+
if (settings !== void 0 && process.ppid === settings.parentPid) {
|
|
17
|
+
const { parentPid, pollMs, killTimeoutMs } = settings;
|
|
18
|
+
const poll = setInterval(() => {
|
|
19
|
+
if (process.ppid === parentPid && isAlive(parentPid)) return;
|
|
20
|
+
clearInterval(poll);
|
|
21
|
+
setTimeout(() => signal("SIGKILL"), killTimeoutMs).unref();
|
|
22
|
+
signal("SIGTERM");
|
|
23
|
+
}, pollMs);
|
|
24
|
+
poll.unref();
|
|
25
|
+
}
|
|
26
|
+
/** Whether `pid` exists (EPERM: it does, but is not the CLI, which runs as the same user). */
|
|
27
|
+
function isAlive(pid) {
|
|
28
|
+
try {
|
|
29
|
+
process.kill(pid, 0);
|
|
30
|
+
return true;
|
|
31
|
+
} catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Signals the process group of the application (it leads one when detached), else the process. */
|
|
36
|
+
function signal(name) {
|
|
37
|
+
try {
|
|
38
|
+
if (process.platform !== "win32") {
|
|
39
|
+
process.kill(-process.pid, name);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
} catch {}
|
|
43
|
+
process.kill(process.pid, name);
|
|
44
|
+
}
|
|
45
|
+
export {};
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=orphan-guard.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orphan-guard.mjs","names":[],"sources":["../../src/dev/orphan-guard.ts"],"sourcesContent":["/**\n * Preloaded into the application by `znight dev` (see `orphan.ts`): stops the application when the\n * CLI that started it is gone without stopping it (`kill -9`, the OOM killer).\n *\n * Configured by `ZNIGHT_DEV_PARENT` (removed from the environment right away: the processes the\n * application starts, `fork()` included, are left alone), and only in the process the CLI started\n * (whose parent is the CLI). Every poll interval it checks that it was not re-parented and that the\n * CLI still exists (an unref'd timer: it never keeps the application alive). Once the CLI is gone,\n * the application gets SIGTERM (its process group when it leads one, as the CLI would send it), so\n * it stops gracefully, then SIGKILL after the kill timeout.\n */\nimport { ORPHAN_GUARD_ENV, parseOrphanGuard } from './orphan.ts'\n\nconst settings = parseOrphanGuard(process.env[ORPHAN_GUARD_ENV])\nReflect.deleteProperty(process.env, ORPHAN_GUARD_ENV)\n\nif (settings !== undefined && process.ppid === settings.parentPid) {\n const { parentPid, pollMs, killTimeoutMs } = settings\n const poll = setInterval(() => {\n if (process.ppid === parentPid && isAlive(parentPid)) return\n clearInterval(poll)\n setTimeout(() => signal('SIGKILL'), killTimeoutMs).unref()\n signal('SIGTERM')\n }, pollMs)\n poll.unref()\n}\n\n/** Whether `pid` exists (EPERM: it does, but is not the CLI, which runs as the same user). */\nfunction isAlive(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n } catch {\n return false\n }\n}\n\n/** Signals the process group of the application (it leads one when detached), else the process. */\nfunction signal(name: NodeJS.Signals): void {\n try {\n if (process.platform !== 'win32') {\n process.kill(-process.pid, name)\n return\n }\n } catch {\n // not a group leader\n }\n process.kill(process.pid, name)\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAM,WAAW,iBAAiB,QAAQ,IAAI,iBAAiB;AAC/D,QAAQ,eAAe,QAAQ,KAAK,gBAAgB;AAEpD,IAAI,aAAa,KAAA,KAAa,QAAQ,SAAS,SAAS,WAAW;CACjE,MAAM,EAAE,WAAW,QAAQ,kBAAkB;CAC7C,MAAM,OAAO,kBAAkB;EAC7B,IAAI,QAAQ,SAAS,aAAa,QAAQ,SAAS,GAAG;EACtD,cAAc,IAAI;EAClB,iBAAiB,OAAO,SAAS,GAAG,aAAa,CAAC,CAAC,MAAM;EACzD,OAAO,SAAS;CAClB,GAAG,MAAM;CACT,KAAK,MAAM;AACb;;AAGA,SAAS,QAAQ,KAAsB;CACrC,IAAI;EACF,QAAQ,KAAK,KAAK,CAAC;EACnB,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,SAAS,OAAO,MAA4B;CAC1C,IAAI;EACF,IAAI,QAAQ,aAAa,SAAS;GAChC,QAAQ,KAAK,CAAC,QAAQ,KAAK,IAAI;GAC/B;EACF;CACF,QAAQ,CAER;CACA,QAAQ,KAAK,QAAQ,KAAK,IAAI;AAChC"}
|