@wolfstar/cli 0.1.0 → 0.2.0-next-20260905171401
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 +33 -1
- package/dist/{build-Ct6zabKg.js → build-B6xw00Ee.js} +2 -2
- package/dist/{build-Ct6zabKg.js.map → build-B6xw00Ee.js.map} +1 -1
- package/dist/{build-DU76s-Ec.js → build-CvSvbsYN.js} +2 -2
- package/dist/{build-DU76s-Ec.js.map → build-CvSvbsYN.js.map} +1 -1
- package/dist/{builders-BzLMIm9Y.js → builders-D97Uqjwg.js} +3 -3
- package/dist/{builders-BzLMIm9Y.js.map → builders-D97Uqjwg.js.map} +1 -1
- package/dist/cli.js +3 -3
- package/dist/{dev-QUxuLcXj.js → dev-BCgaVd0m.js} +2 -2
- package/dist/{dev-QUxuLcXj.js.map → dev-BCgaVd0m.js.map} +1 -1
- package/dist/{dev-DWg8l23a.js → dev-CWA_JVTb.js} +2 -2
- package/dist/{dev-DWg8l23a.js.map → dev-CWA_JVTb.js.map} +1 -1
- package/dist/{info-DRTYYvVB.js → info-BqxpktLG.js} +2 -2
- package/dist/{info-DRTYYvVB.js.map → info-BqxpktLG.js.map} +1 -1
- package/dist/{info-B-8rAIn6.js → info-Czh4LUsD.js} +14 -2
- package/dist/info-Czh4LUsD.js.map +1 -0
- package/dist/tsdown-BEFq2API.js +225 -0
- package/dist/tsdown-BEFq2API.js.map +1 -0
- package/dist/{vite-D3vYFsqa.js → vite-27ZbreDz.js} +20 -10
- package/dist/vite-27ZbreDz.js.map +1 -0
- package/package.json +2 -2
- package/dist/info-B-8rAIn6.js.map +0 -1
- package/dist/tsdown-DlAs0rSI.js +0 -104
- package/dist/tsdown-DlAs0rSI.js.map +0 -1
- package/dist/vite-D3vYFsqa.js.map +0 -1
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ stars --help | --version
|
|
|
72
72
|
|
|
73
73
|
### `stars dev`
|
|
74
74
|
|
|
75
|
-
Watches the sources through the configured build tool (`tsdown` programmatically
|
|
75
|
+
Watches the sources through the configured build tool (`tsdown` programmatically, configured from your `stars.config`, `tsc -b --watch`, or a plain file watcher for JavaScript projects), starts the bot after the first successful build and restarts it after every following one. Failed builds keep the previous process running and wait for the next change; a crashed bot waits for the next change or a manual restart.
|
|
76
76
|
|
|
77
77
|
The bot runs as a child `node` process with `STARS_DEV=1` in its environment. Because `stars dev` already restarts the whole process, leave the framework's own `hmr` option disabled while using it.
|
|
78
78
|
|
|
@@ -126,6 +126,38 @@ Three `dev` options round out the dev loop (all documented in
|
|
|
126
126
|
- `dev.logFile` (default `.stars/dev.log`) mirrors the session's logs to disk, so a run can be read back after the
|
|
127
127
|
terminal UI is gone. Set it to `false` to disable it.
|
|
128
128
|
|
|
129
|
+
### The build
|
|
130
|
+
|
|
131
|
+
`tsdown` is configured from `stars.config`, and a base project configures nothing: the entry's directory, one output
|
|
132
|
+
file per source file, ESM on Node, `build.outDir`, the tsconfig (`src/tsconfig.json` or `tsconfig.json`), the
|
|
133
|
+
extension `build.output` implies, sourcemaps, unbundled dependencies, Nuxt's `~`/`@`/`~~`/`@@` alias prefixes and
|
|
134
|
+
the auto imports plugin are all filled in
|
|
135
|
+
(the [framework README](../http-framework#the-build-tsdown) lists every default). The `tsdown` block is for what they
|
|
136
|
+
cannot know:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
export default defineConfig({
|
|
140
|
+
entry: 'src/main.ts',
|
|
141
|
+
future: { compatibilityVersion: 4 },
|
|
142
|
+
tsdown: { plugins: [copyLocales()] }
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
With `future.compatibilityVersion: 3` an existing `tsdown.config.*` still drives the build and the block is merged
|
|
147
|
+
over it (values from `stars.config` win, `plugins` are appended); with `4` the block is the whole configuration. The
|
|
148
|
+
`vite` block works the same way for `build.tool: 'vite'`. `stars info` shows which file the build is configured from
|
|
149
|
+
and which options the block sets.
|
|
150
|
+
|
|
151
|
+
### Compatibility version
|
|
152
|
+
|
|
153
|
+
`future.compatibilityVersion` opts a project into the next major's defaults, the way Nuxt's own does (see the
|
|
154
|
+
[framework README](../http-framework#compatibility-version) for the full reference):
|
|
155
|
+
|
|
156
|
+
| Version | What it changes |
|
|
157
|
+
| ------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
158
|
+
| `3` (default) | Today's behaviour: a `tsdown.config.*` drives the build, auto imports off unless asked for |
|
|
159
|
+
| `4` | `tsdown` configured from `stars.config` alone, auto imports on and wired in, `'auto'` picks `tsdown` for TypeScript |
|
|
160
|
+
|
|
129
161
|
### Experimental flags
|
|
130
162
|
|
|
131
163
|
`experimental` in `stars.config.*` turns on work that is still landing (see the
|
|
@@ -9,7 +9,7 @@ var build_default = defineCommand({
|
|
|
9
9
|
},
|
|
10
10
|
args: { ...projectArgs },
|
|
11
11
|
async run({ args }) {
|
|
12
|
-
const { runBuild } = await import("./build-
|
|
12
|
+
const { runBuild } = await import("./build-CvSvbsYN.js");
|
|
13
13
|
await runBuild({
|
|
14
14
|
config: args.config,
|
|
15
15
|
cwd: args.cwd
|
|
@@ -19,4 +19,4 @@ var build_default = defineCommand({
|
|
|
19
19
|
|
|
20
20
|
//#endregion
|
|
21
21
|
export { build_default as default };
|
|
22
|
-
//# sourceMappingURL=build-
|
|
22
|
+
//# sourceMappingURL=build-B6xw00Ee.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-
|
|
1
|
+
{"version":3,"file":"build-B6xw00Ee.js","names":[],"sources":["../src/commands/build.ts"],"sourcesContent":["import { defineCommand } from 'citty';\nimport { projectArgs } from '../lib/args.js';\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'build',\n\t\tdescription: 'Build the project once with the configured build tool'\n\t},\n\targs: { ...projectArgs },\n\tasync run({ args }) {\n\t\tconst { runBuild } = await import('../lib/tasks/build.js');\n\t\tawait runBuild({ config: args.config, cwd: args.cwd });\n\t}\n});\n"],"mappings":";;;;AAGA,oBAAe,cAAc;CAC5B,MAAM;EACL,MAAM;EACN,aAAa;CACd;CACA,MAAM,EAAE,GAAG,YAAY;CACvB,MAAM,IAAI,EAAE,QAAQ;EACnB,MAAM,EAAE,aAAa,MAAM,OAAO;EAClC,MAAM,SAAS;GAAE,QAAQ,KAAK;GAAQ,KAAK,KAAK;EAAI,CAAC;CACtD;AACD,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as ExitCode, t as CliError } from "./errors-Bket6XFn.js";
|
|
2
2
|
import { i as shouldUseColor } from "./output-mode-ChrOf0LO.js";
|
|
3
3
|
import { n as resolveCwd } from "./args-Ch1KXUMn.js";
|
|
4
|
-
import { n as createBuilder, t as assertSupportedExperiments } from "./builders-
|
|
4
|
+
import { n as createBuilder, t as assertSupportedExperiments } from "./builders-D97Uqjwg.js";
|
|
5
5
|
import { t as prepareAutoImports } from "./prepare-D5NZ0w4t.js";
|
|
6
6
|
import { displayPath, loadStarsConfig } from "@wolfstar/http-framework/config";
|
|
7
7
|
import { createColors } from "colorette";
|
|
@@ -36,4 +36,4 @@ async function runBuild(options) {
|
|
|
36
36
|
|
|
37
37
|
//#endregion
|
|
38
38
|
export { runBuild };
|
|
39
|
-
//# sourceMappingURL=build-
|
|
39
|
+
//# sourceMappingURL=build-CvSvbsYN.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-
|
|
1
|
+
{"version":3,"file":"build-CvSvbsYN.js","names":[],"sources":["../src/lib/tasks/build.ts"],"sourcesContent":["import { createColors } from 'colorette';\nimport type { ProjectArgs } from '../args.js';\nimport { resolveCwd } from '../args.js';\nimport { assertSupportedExperiments, createBuilder } from '../builders/index.js';\nimport { loadStarsConfig } from '@wolfstar/http-framework/config';\nimport { displayPath } from '@wolfstar/http-framework/config';\nimport { CliError, ExitCode } from '../errors.js';\nimport { shouldUseColor } from '../output-mode.js';\nimport { prepareAutoImports } from './prepare.js';\n\nexport interface BuildTaskOptions extends ProjectArgs {\n\tstdout?: NodeJS.WritableStream;\n}\n\nexport async function runBuild(options: BuildTaskOptions): Promise<void> {\n\tconst stdout = options.stdout ?? process.stdout;\n\tconst colors = createColors({ useColor: shouldUseColor() });\n\tconst config = await loadStarsConfig({ cwd: resolveCwd(options), configFile: options.config });\n\tassertSupportedExperiments(config);\n\tawait prepareAutoImports(config);\n\n\tif (config.build.tool === 'none') {\n\t\tstdout.write(`${colors.dim('stars')} nothing to build, ${displayPath(config.root, config.entry)} runs as-is (build.tool is 'none')\\n`);\n\t\treturn;\n\t}\n\n\tconst builder = await createBuilder(config);\n\tbuilder.on('log', (level, text) => {\n\t\tconst paint = level === 'error' ? colors.red : level === 'warn' ? colors.yellow : (value: string) => value;\n\t\tstdout.write(`${paint(text)}\\n`);\n\t});\n\n\tstdout.write(`${colors.dim('stars')} building with ${colors.bold(config.build.tool)}…\\n`);\n\tconst outcome = await builder.build();\n\tif (!outcome.ok) {\n\t\tthrow new CliError(`Build failed${outcome.message ? `: ${outcome.message}` : ''}`, { code: 'BUILD_FAILED', exitCode: ExitCode.BuildFailed });\n\t}\n\n\tstdout.write(`${colors.dim('stars')} ${colors.green(`built in ${outcome.durationMs}ms`)} → ${displayPath(config.root, config.build.output)}\\n`);\n}\n"],"mappings":";;;;;;;;;AAcA,eAAsB,SAAS,SAA0C;CACxE,MAAM,SAAS,QAAQ,UAAU,QAAQ;CACzC,MAAM,SAAS,aAAa,EAAE,UAAU,eAAe,EAAE,CAAC;CAC1D,MAAM,SAAS,MAAM,gBAAgB;EAAE,KAAK,WAAW,OAAO;EAAG,YAAY,QAAQ;CAAO,CAAC;CAC7F,2BAA2B,MAAM;CACjC,MAAM,mBAAmB,MAAM;CAE/B,IAAI,OAAO,MAAM,SAAS,QAAQ;EACjC,OAAO,MAAM,GAAG,OAAO,IAAI,OAAO,EAAE,qBAAqB,YAAY,OAAO,MAAM,OAAO,KAAK,EAAE,qCAAqC;EACrI;CACD;CAEA,MAAM,UAAU,MAAM,cAAc,MAAM;CAC1C,QAAQ,GAAG,QAAQ,OAAO,SAAS;EAClC,MAAM,QAAQ,UAAU,UAAU,OAAO,MAAM,UAAU,SAAS,OAAO,UAAU,UAAkB;EACrG,OAAO,MAAM,GAAG,MAAM,IAAI,EAAE,GAAG;CAChC,CAAC;CAED,OAAO,MAAM,GAAG,OAAO,IAAI,OAAO,EAAE,iBAAiB,OAAO,KAAK,OAAO,MAAM,IAAI,EAAE,IAAI;CACxF,MAAM,UAAU,MAAM,QAAQ,MAAM;CACpC,IAAI,CAAC,QAAQ,IACZ,MAAM,IAAI,SAAS,eAAe,QAAQ,UAAU,KAAK,QAAQ,YAAY,MAAM;EAAE,MAAM;EAAgB,UAAU,SAAS;CAAY,CAAC;CAG5I,OAAO,MAAM,GAAG,OAAO,IAAI,OAAO,EAAE,GAAG,OAAO,MAAM,YAAY,QAAQ,WAAW,GAAG,EAAE,KAAK,YAAY,OAAO,MAAM,OAAO,MAAM,MAAM,EAAE,GAAG;AAC/I"}
|
|
@@ -21,11 +21,11 @@ async function createBuilder(config) {
|
|
|
21
21
|
}
|
|
22
22
|
switch (config.build.tool) {
|
|
23
23
|
case "tsdown": {
|
|
24
|
-
const { TsdownBuilder } = await import("./tsdown-
|
|
24
|
+
const { TsdownBuilder } = await import("./tsdown-BEFq2API.js");
|
|
25
25
|
return new TsdownBuilder(config);
|
|
26
26
|
}
|
|
27
27
|
case "vite": {
|
|
28
|
-
const { ViteBuilder } = await import("./vite-
|
|
28
|
+
const { ViteBuilder } = await import("./vite-27ZbreDz.js");
|
|
29
29
|
return new ViteBuilder(config);
|
|
30
30
|
}
|
|
31
31
|
case "tsc": {
|
|
@@ -41,4 +41,4 @@ async function createBuilder(config) {
|
|
|
41
41
|
|
|
42
42
|
//#endregion
|
|
43
43
|
export { createBuilder as n, assertSupportedExperiments as t };
|
|
44
|
-
//# sourceMappingURL=builders-
|
|
44
|
+
//# sourceMappingURL=builders-D97Uqjwg.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builders-
|
|
1
|
+
{"version":3,"file":"builders-D97Uqjwg.js","names":[],"sources":["../src/lib/builders/index.ts"],"sourcesContent":["import type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { CliError } from '../errors.js';\nimport type { Builder } from './types.js';\n\n/**\n * Fails on experiments the CLI cannot honour yet, before a command gets far enough to look like it worked.\n *\n * Called by `stars dev`/`stars build` up front, since a project whose build tool needs no build at all would\n * otherwise return early and never reach {@link createBuilder}.\n */\nexport function assertSupportedExperiments(config: ResolvedStarsConfig): void {\n\tif (config.experimental.enableNitro) {\n\t\tthrow new CliError('`experimental.enableNitro` is not implemented yet', {\n\t\t\tcode: 'EXPERIMENT_UNAVAILABLE',\n\t\t\thint: \"Nitro needs the framework's Fetch adapter (wolfstar-project/stars-components#81); until it lands, use `build.tool` 'tsdown', 'vite' or 'tsc'.\"\n\t\t});\n\t}\n}\n\nexport async function createBuilder(config: ResolvedStarsConfig): Promise<Builder> {\n\tassertSupportedExperiments(config);\n\n\t// The project runs its own build, so the CLI only watches what it writes (see `ExternalBuilder`).\n\tif (config.experimental.enableExternalVite) {\n\t\tconst { ExternalBuilder } = await import('./external.js');\n\t\treturn new ExternalBuilder(config);\n\t}\n\n\tswitch (config.build.tool) {\n\t\tcase 'tsdown': {\n\t\t\tconst { TsdownBuilder } = await import('./tsdown.js');\n\t\t\treturn new TsdownBuilder(config);\n\t\t}\n\t\tcase 'vite': {\n\t\t\tconst { ViteBuilder } = await import('./vite.js');\n\t\t\treturn new ViteBuilder(config);\n\t\t}\n\t\tcase 'tsc': {\n\t\t\tconst { TscBuilder } = await import('./tsc.js');\n\t\t\treturn new TscBuilder(config);\n\t\t}\n\t\tcase 'none': {\n\t\t\tconst { NoneBuilder } = await import('./none.js');\n\t\t\treturn new NoneBuilder(config);\n\t\t}\n\t}\n}\n\nexport type { Builder, BuilderEvents, BuildOutcome } from './types.js';\n"],"mappings":";;;;;;;;;AAUA,SAAgB,2BAA2B,QAAmC;CAC7E,IAAI,OAAO,aAAa,aACvB,MAAM,IAAI,SAAS,qDAAqD;EACvE,MAAM;EACN,MAAM;CACP,CAAC;AAEH;AAEA,eAAsB,cAAc,QAA+C;CAClF,2BAA2B,MAAM;CAGjC,IAAI,OAAO,aAAa,oBAAoB;EAC3C,MAAM,EAAE,oBAAoB,MAAM,OAAO;EACzC,OAAO,IAAI,gBAAgB,MAAM;CAClC;CAEA,QAAQ,OAAO,MAAM,MAArB;EACC,KAAK,UAAU;GACd,MAAM,EAAE,kBAAkB,MAAM,OAAO;GACvC,OAAO,IAAI,cAAc,MAAM;EAChC;EACA,KAAK,QAAQ;GACZ,MAAM,EAAE,gBAAgB,MAAM,OAAO;GACrC,OAAO,IAAI,YAAY,MAAM;EAC9B;EACA,KAAK,OAAO;GACX,MAAM,EAAE,eAAe,MAAM,OAAO;GACpC,OAAO,IAAI,WAAW,MAAM;EAC7B;EACA,KAAK,QAAQ;GACZ,MAAM,EAAE,gBAAgB,MAAM,OAAO;GACrC,OAAO,IAAI,YAAY,MAAM;EAC9B;CACD;AACD"}
|
package/dist/cli.js
CHANGED
|
@@ -6,9 +6,9 @@ import { defineCommand, renderUsage, runCommand } from "citty";
|
|
|
6
6
|
//#region src/cli.ts
|
|
7
7
|
const packageJson = readOwnPackageJson();
|
|
8
8
|
const subCommands = {
|
|
9
|
-
dev: () => import("./dev-
|
|
10
|
-
build: () => import("./build-
|
|
11
|
-
info: () => import("./info-
|
|
9
|
+
dev: () => import("./dev-BCgaVd0m.js").then((module) => module.default),
|
|
10
|
+
build: () => import("./build-B6xw00Ee.js").then((module) => module.default),
|
|
11
|
+
info: () => import("./info-BqxpktLG.js").then((module) => module.default),
|
|
12
12
|
codegen: () => import("./codegen-mYkL-GBf.js").then((module) => module.default),
|
|
13
13
|
prepare: () => import("./prepare-CVf88_Or.js").then((module) => module.default),
|
|
14
14
|
commands: () => import("./commands-BkGKs_Bg.js").then((module) => module.default)
|
|
@@ -16,7 +16,7 @@ var dev_default = defineCommand({
|
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
async run({ args }) {
|
|
19
|
-
const { runDev } = await import("./dev-
|
|
19
|
+
const { runDev } = await import("./dev-CWA_JVTb.js");
|
|
20
20
|
await runDev({
|
|
21
21
|
config: args.config,
|
|
22
22
|
cwd: args.cwd,
|
|
@@ -27,4 +27,4 @@ var dev_default = defineCommand({
|
|
|
27
27
|
|
|
28
28
|
//#endregion
|
|
29
29
|
export { dev_default as default };
|
|
30
|
-
//# sourceMappingURL=dev-
|
|
30
|
+
//# sourceMappingURL=dev-BCgaVd0m.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-
|
|
1
|
+
{"version":3,"file":"dev-BCgaVd0m.js","names":[],"sources":["../src/commands/dev.ts"],"sourcesContent":["import { defineCommand } from 'citty';\nimport { projectArgs } from '../lib/args.js';\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'dev',\n\t\tdescription: 'Build, run and restart the bot on changes'\n\t},\n\targs: {\n\t\t...projectArgs,\n\t\ttui: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Interactive terminal UI; use --no-tui (or STARS_TUI=plain) for plain line output',\n\t\t\tdefault: true\n\t\t}\n\t},\n\tasync run({ args }) {\n\t\tconst { runDev } = await import('../lib/tasks/dev.js');\n\t\tawait runDev({ config: args.config, cwd: args.cwd, tui: args.tui ? undefined : false });\n\t}\n});\n"],"mappings":";;;;AAGA,kBAAe,cAAc;CAC5B,MAAM;EACL,MAAM;EACN,aAAa;CACd;CACA,MAAM;EACL,GAAG;EACH,KAAK;GACJ,MAAM;GACN,aAAa;GACb,SAAS;EACV;CACD;CACA,MAAM,IAAI,EAAE,QAAQ;EACnB,MAAM,EAAE,WAAW,MAAM,OAAO;EAChC,MAAM,OAAO;GAAE,QAAQ,KAAK;GAAQ,KAAK,KAAK;GAAK,KAAK,KAAK,MAAM,SAAY;EAAM,CAAC;CACvF;AACD,CAAC"}
|
|
@@ -4,7 +4,7 @@ import { n as resolveCwd } from "./args-Ch1KXUMn.js";
|
|
|
4
4
|
import { n as createLineSplitter, t as ProcessSupervisor } from "./process-supervisor-CJOdmp0W.js";
|
|
5
5
|
import { r as resolveBinary } from "./project-DHF3qdm-.js";
|
|
6
6
|
import { n as resolveTscBinary } from "./tsc-BqZfDTD0.js";
|
|
7
|
-
import { n as createBuilder, t as assertSupportedExperiments } from "./builders-
|
|
7
|
+
import { n as createBuilder, t as assertSupportedExperiments } from "./builders-D97Uqjwg.js";
|
|
8
8
|
import { t as prepareAutoImports } from "./prepare-D5NZ0w4t.js";
|
|
9
9
|
import { t as Tunnel } from "./tunnel-B8Id4-lR.js";
|
|
10
10
|
import { loadStarsConfig } from "@wolfstar/http-framework/config";
|
|
@@ -601,4 +601,4 @@ async function resolveDevConfig(config) {
|
|
|
601
601
|
|
|
602
602
|
//#endregion
|
|
603
603
|
export { runDev };
|
|
604
|
-
//# sourceMappingURL=dev-
|
|
604
|
+
//# sourceMappingURL=dev-CWA_JVTb.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-DWg8l23a.js","names":["#nextId","#entries","#state","#errors","#run","#child","#pending","#setState","#handleLine","#setBuild","#onBuildSuccess","#onBuildFailure","#emitStatus","#onExit","#healthTimer","#checkHealth","#build","#health","#restarts","#lastRestartReason","#lastBuild","#lastExit","#stopped","#clearRestartTimer","#enqueue","#scheduleRestart","#pendingReason","#restartTimer","#queue","#stream","#write"],"sources":["../src/lib/log-buffer.ts","../src/lib/typechecker.ts","../src/lib/dev-service.ts","../src/lib/host.ts","../src/lib/log-file.ts","../src/lib/tasks/dev.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\n\nexport type LogSource = 'stars' | 'build' | 'app' | 'tsc' | 'tunnel';\nexport type LogLevel = 'debug' | 'info' | 'success' | 'warn' | 'error';\n\nexport interface LogEntry {\n\treadonly id: number;\n\treadonly time: number;\n\treadonly source: LogSource;\n\treadonly level: LogLevel;\n\treadonly text: string;\n}\n\nexport interface LogFilter {\n\tsource?: LogSource | null;\n\tlevel?: LogLevel | null;\n\tquery?: string | null;\n}\n\nexport type LogInput = Omit<LogEntry, 'id' | 'time'>;\n\n/**\n * A bounded, in-memory log store shared by the dev service and its renderers.\n */\nexport class LogBuffer extends EventEmitter<{ entry: [LogEntry]; clear: [] }> {\n\t#entries: LogEntry[] = [];\n\t#nextId = 1;\n\n\tpublic constructor(public readonly capacity = 2000) {\n\t\tsuper();\n\t}\n\n\tpublic push(input: LogInput): LogEntry {\n\t\tconst entry: LogEntry = { id: this.#nextId++, time: Date.now(), ...input };\n\t\tthis.#entries.push(entry);\n\t\tif (this.#entries.length > this.capacity) this.#entries.splice(0, this.#entries.length - this.capacity);\n\t\tthis.emit('entry', entry);\n\t\treturn entry;\n\t}\n\n\tpublic entries(): readonly LogEntry[] {\n\t\treturn this.#entries;\n\t}\n\n\tpublic filter(filter: LogFilter): LogEntry[] {\n\t\tconst query = filter.query?.toLowerCase() ?? null;\n\t\treturn this.#entries.filter(\n\t\t\t(entry) =>\n\t\t\t\t(!filter.source || entry.source === filter.source) &&\n\t\t\t\t(!filter.level || matchesLevel(entry.level, filter.level)) &&\n\t\t\t\t(!query || entry.text.toLowerCase().includes(query))\n\t\t);\n\t}\n\n\tpublic clear(): void {\n\t\tthis.#entries = [];\n\t\tthis.emit('clear');\n\t}\n}\n\nconst LEVEL_ORDER: Record<LogLevel, number> = { debug: 0, info: 1, success: 1, warn: 2, error: 3 };\n\n/**\n * Whether `level` is at least as severe as `minimum`.\n */\nexport function matchesLevel(level: LogLevel, minimum: LogLevel): boolean {\n\treturn LEVEL_ORDER[level] >= LEVEL_ORDER[minimum];\n}\n","import type { ResolvedStarsConfig, StarsTypechecker } from '@wolfstar/http-framework/config';\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport { EventEmitter } from 'node:events';\nimport { resolveTscBinary } from './builders/tsc.js';\nimport { CliError } from './errors.js';\nimport type { LogLevel } from './log-buffer.js';\nimport { createLineSplitter } from './process-supervisor.js';\nimport { resolveBinary } from './project.js';\n\nconst WATCH_START = /Starting (incremental )?compilation/;\nconst WATCH_DONE = /Found (\\d+) errors?\\. Watching for file changes/;\nconst ONE_SHOT_DONE = /Found (\\d+) errors?/;\nconst ERROR_LINE = /error TS\\d+:/;\n\nexport type TypecheckState = 'idle' | 'checking' | 'ok' | 'failed';\n\nexport interface TypecheckerEvents {\n\tlog: [level: LogLevel, text: string];\n\tstate: [state: TypecheckState, errors: number];\n}\n\nexport interface TypecheckCommand {\n\t/** The executable, and whether it is a script `node` has to run. */\n\treadonly command: string;\n\treadonly args: readonly string[];\n\treadonly node: boolean;\n\t/** Whether the checker watches the project itself, or has to be re-run after every build. */\n\treadonly watch: boolean;\n}\n\n/**\n * Runs a type checker next to the bot and reports type errors on their own channel, without ever blocking builds or\n * restarts: `stars dev` keeps running whatever the build tool produced, the way Seedcord's `hmr.typecheck` and\n * Vite's `checker` plugin do.\n *\n * `tsc` and `golar` (which forwards to TypeScript) watch the project themselves; `tsz` has no watch mode, so\n * {@link Typechecker.check} re-runs it after every build instead.\n */\nexport class Typechecker extends EventEmitter<TypecheckerEvents> {\n\t#child: ChildProcess | null = null;\n\t#state: TypecheckState = 'idle';\n\t#errors = 0;\n\t#pending = false;\n\n\tpublic constructor(private readonly config: ResolvedStarsConfig) {\n\t\tsuper();\n\t}\n\n\tpublic get state(): TypecheckState {\n\t\treturn this.#state;\n\t}\n\n\tpublic get errors(): number {\n\t\treturn this.#errors;\n\t}\n\n\t/** Whether the checker re-runs per build instead of watching on its own. */\n\tpublic get oneShot(): boolean {\n\t\treturn this.config.dev.typecheck.enabled && !resolveTypecheckCommand(this.config).watch;\n\t}\n\n\tpublic start(): void {\n\t\tif (!this.config.dev.typecheck.enabled) return;\n\t\tthis.#run();\n\t}\n\n\t/**\n\t * Re-runs a checker that cannot watch. Called after every successful build; a run already in flight is replaced,\n\t * so a fast series of builds only leaves the last check standing.\n\t */\n\tpublic check(): void {\n\t\tif (!this.oneShot) return;\n\t\tif (this.#child) {\n\t\t\tthis.#pending = true;\n\t\t\tthis.#child.kill();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#run();\n\t}\n\n\tpublic close(): Promise<void> {\n\t\tconst child = this.#child;\n\t\tthis.#child = null;\n\t\tthis.#pending = false;\n\t\tif (!child || child.exitCode !== null) return Promise.resolve();\n\n\t\treturn new Promise((resolve) => {\n\t\t\tchild.once('exit', () => resolve());\n\t\t\tchild.kill();\n\t\t});\n\t}\n\n\t#run(): void {\n\t\tconst { command, args, node, watch } = resolveTypecheckCommand(this.config);\n\t\tconst child = spawn(node ? process.execPath : command, node ? [command, ...args] : [...args], {\n\t\t\tcwd: this.config.root,\n\t\t\tenv: process.env,\n\t\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\t\twindowsHide: true\n\t\t});\n\t\tthis.#child = child;\n\t\tthis.#setState('checking', 0);\n\n\t\tlet errors = 0;\n\t\tconst handleLine = (line: string) => {\n\t\t\tif (ERROR_LINE.test(line)) errors++;\n\t\t\tthis.#handleLine(line);\n\t\t};\n\n\t\tconst stdout = createLineSplitter(handleLine);\n\t\tconst stderr = createLineSplitter(handleLine);\n\t\tchild.stdout?.on('data', stdout.push);\n\t\tchild.stderr?.on('data', stderr.push);\n\n\t\tchild.once('exit', (code) => {\n\t\t\tstdout.flush();\n\t\t\tstderr.flush();\n\t\t\tthis.#child = null;\n\n\t\t\tif (!watch && this.#state === 'checking') {\n\t\t\t\t// A one-shot checker only reports through its exit code when it prints no summary line.\n\t\t\t\tthis.#setState(code === 0 && errors === 0 ? 'ok' : 'failed', errors);\n\t\t\t}\n\n\t\t\tif (this.#pending) {\n\t\t\t\tthis.#pending = false;\n\t\t\t\tthis.#run();\n\t\t\t}\n\t\t});\n\t\tchild.once('error', (error: NodeJS.ErrnoException) => {\n\t\t\tthis.#child = null;\n\t\t\tthis.emit('log', 'error', error.code === 'ENOENT' ? `${command} is not installed` : error.message);\n\t\t\tthis.#setState('failed', this.#errors);\n\t\t});\n\t}\n\n\t#handleLine(line: string): void {\n\t\tconst text = line.trim();\n\t\tif (text.length === 0) return;\n\n\t\tif (WATCH_START.test(text)) {\n\t\t\tthis.#setState('checking', 0);\n\t\t\treturn;\n\t\t}\n\n\t\tif (ERROR_LINE.test(text)) {\n\t\t\tthis.emit('log', 'error', text);\n\t\t\treturn;\n\t\t}\n\n\t\tconst done = WATCH_DONE.exec(text) ?? ONE_SHOT_DONE.exec(text);\n\t\tif (done) {\n\t\t\tconst errors = Number(done[1]);\n\t\t\tthis.#setState(errors === 0 ? 'ok' : 'failed', errors);\n\t\t\tthis.emit('log', errors === 0 ? 'success' : 'error', errors === 0 ? 'No type errors' : `${errors} type error${errors === 1 ? '' : 's'}`);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.emit('log', 'info', text);\n\t}\n\n\t#setState(state: TypecheckState, errors: number): void {\n\t\tthis.#state = state;\n\t\tthis.#errors = errors;\n\t\tthis.emit('state', state, errors);\n\t}\n}\n\n/**\n * Builds the command line of the configured type checker, resolved from the project's own `node_modules`.\n *\n * @throws {CliError} when the checker is not installed in the project.\n */\nexport function resolveTypecheckCommand(config: ResolvedStarsConfig): TypecheckCommand {\n\tconst { checker, tsconfig } = config.dev.typecheck;\n\tconst project = tsconfig!;\n\n\tswitch (checker) {\n\t\tcase 'golar': {\n\t\t\t// `golar tsc` forwards everything after it to the TypeScript CLI, watch mode included.\n\t\t\tconst golar = resolveBinary(config.root, 'golar', 'golar');\n\t\t\tif (!golar) throw missing(config, 'golar', 'Install it with `pnpm add -D golar`');\n\n\t\t\treturn { command: golar, args: ['tsc', ...WATCH_ARGS, '-p', project], node: true, watch: true };\n\t\t}\n\n\t\tcase 'tsz': {\n\t\t\t// `tsz` is an early, tsc-compatible checker with no watch mode; `try-tsz` is its comparison harness.\n\t\t\tconst tsz = resolveBinary(config.root, '@mohsen-azimi/tsz-dev', 'tsz');\n\t\t\tif (tsz) return { command: tsz, args: ['--noEmit', '-p', project], node: true, watch: false };\n\n\t\t\tconst tryTsz = resolveBinary(config.root, 'try-tsz', 'try-tsz');\n\t\t\tif (tryTsz) return { command: tryTsz, args: ['-p', project], node: true, watch: false };\n\n\t\t\tthrow missing(config, 'tsz', 'Install it with `pnpm add -D @mohsen-azimi/tsz-dev` (or `try-tsz`)');\n\t\t}\n\n\t\tdefault: {\n\t\t\tconst tsc = resolveTscBinary(config.root);\n\t\t\tif (!tsc) throw missing(config, 'typescript', 'Install it with `pnpm add -D typescript`');\n\n\t\t\treturn { command: tsc, args: [...WATCH_ARGS, '-p', project], node: true, watch: true };\n\t\t}\n\t}\n}\n\nconst WATCH_ARGS = ['--noEmit', '--watch', '--preserveWatchOutput', '--pretty', 'false'] as const;\n\nfunction missing(config: ResolvedStarsConfig, name: string, install: string): CliError {\n\treturn new CliError(`\"${name}\" is not installed in ${config.root}`, {\n\t\tcode: 'DEPENDENCY_MISSING',\n\t\thint: `${install}, pick another \\`dev.typecheck.checker\\`, or set \\`dev.typecheck\\` to false.`\n\t});\n}\n\nexport type { StarsTypechecker };\n","import { EventEmitter } from 'node:events';\nimport type { Builder, BuildOutcome } from './builders/types.js';\nimport type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { LogBuffer, type LogLevel, type LogSource } from './log-buffer.js';\nimport { ProcessSupervisor, type ProcessExit, type ProcessState } from './process-supervisor.js';\nimport { Tunnel, type TunnelState } from './tunnel.js';\nimport { Typechecker, type TypecheckState } from './typechecker.js';\n\nexport type BuildState = 'idle' | 'building' | 'ok' | 'failed';\nexport type HealthState = 'unknown' | 'ok' | 'down';\nexport type RestartReason = 'initial' | 'build' | 'manual' | 'crash';\n\nexport interface DevStatus {\n\treadonly process: ProcessState;\n\treadonly build: BuildState;\n\treadonly health: HealthState;\n\treadonly pid: number | null;\n\treadonly startedAt: number | null;\n\treadonly restarts: number;\n\treadonly lastRestartReason: RestartReason | null;\n\treadonly lastBuild: BuildOutcome | null;\n\treadonly lastExit: ProcessExit | null;\n\treadonly url: string | null;\n\treadonly typecheck: TypecheckState;\n\treadonly typeErrors: number;\n\treadonly tunnel: TunnelState;\n\treadonly tunnelUrl: string | null;\n}\n\nexport interface DevServiceEvents {\n\tstatus: [status: DevStatus];\n}\n\nexport interface DevServiceOptions {\n\tbuilder: Builder;\n\tlogs?: LogBuffer;\n\t/** Overrides for tests. */\n\tsupervisor?: ProcessSupervisor;\n\ttypechecker?: Typechecker;\n\ttunnel?: Tunnel;\n\thealthInterval?: number;\n}\n\n/**\n * The headless heart of `stars dev`: builds, (re)starts the bot and exposes state and logs.\n * Renderers (plain or TUI) only subscribe to it, they never own behaviour.\n */\nexport class DevService extends EventEmitter<DevServiceEvents> {\n\tpublic readonly logs: LogBuffer;\n\tpublic readonly builder: Builder;\n\tpublic readonly supervisor: ProcessSupervisor;\n\tpublic readonly typechecker: Typechecker;\n\tpublic readonly tunnel: Tunnel;\n\n\t#build: BuildState = 'idle';\n\t#health: HealthState = 'unknown';\n\t#restarts = 0;\n\t#lastRestartReason: RestartReason | null = null;\n\t#lastBuild: BuildOutcome | null = null;\n\t#lastExit: ProcessExit | null = null;\n\t#restartTimer: NodeJS.Timeout | null = null;\n\t#healthTimer: NodeJS.Timeout | null = null;\n\t#pendingReason: RestartReason | null = null;\n\t#stopped = false;\n\t#queue: Promise<void> = Promise.resolve();\n\n\tpublic constructor(\n\t\tpublic readonly config: ResolvedStarsConfig,\n\t\toptions: DevServiceOptions\n\t) {\n\t\tsuper();\n\t\tthis.logs = options.logs ?? new LogBuffer();\n\t\tthis.builder = options.builder;\n\t\tthis.supervisor = options.supervisor ?? createSupervisor(config);\n\t\tthis.typechecker = options.typechecker ?? new Typechecker(config);\n\t\tthis.tunnel = options.tunnel ?? new Tunnel(config);\n\n\t\tthis.builder.on('start', () => this.#setBuild('building'));\n\t\tthis.builder.on('success', (outcome) => this.#onBuildSuccess(outcome));\n\t\tthis.builder.on('failure', (outcome) => this.#onBuildFailure(outcome));\n\t\tthis.builder.on('log', (level, text) => this.log('build', level, text));\n\n\t\tthis.supervisor.on('state', () => this.#emitStatus());\n\t\tthis.supervisor.on('stdout', (line) => this.log('app', 'info', line));\n\t\tthis.supervisor.on('stderr', (line) => this.log('app', 'error', line));\n\t\tthis.supervisor.on('error', (error) => this.log('stars', 'error', `Failed to start the bot: ${error.message}`));\n\t\tthis.supervisor.on('exit', (exit) => this.#onExit(exit));\n\n\t\tthis.typechecker.on('log', (level, text) => this.log('tsc', level, text));\n\t\tthis.typechecker.on('state', () => this.#emitStatus());\n\t\tthis.tunnel.on('log', (level, text) => this.log('tunnel', level, text));\n\t\tthis.tunnel.on('state', () => this.#emitStatus());\n\n\t\tif (config.dev.url && config.dev.health) {\n\t\t\tconst interval = options.healthInterval ?? 5000;\n\t\t\tthis.#healthTimer = setInterval(() => void this.#checkHealth(), interval);\n\t\t\tthis.#healthTimer.unref();\n\t\t}\n\t}\n\n\tpublic get status(): DevStatus {\n\t\treturn {\n\t\t\tprocess: this.supervisor.state,\n\t\t\tbuild: this.#build,\n\t\t\thealth: this.#health,\n\t\t\tpid: this.supervisor.pid,\n\t\t\tstartedAt: this.supervisor.startedAt,\n\t\t\trestarts: this.#restarts,\n\t\t\tlastRestartReason: this.#lastRestartReason,\n\t\t\tlastBuild: this.#lastBuild,\n\t\t\tlastExit: this.#lastExit,\n\t\t\turl: this.config.dev.url,\n\t\t\ttypecheck: this.typechecker.state,\n\t\t\ttypeErrors: this.typechecker.errors,\n\t\t\ttunnel: this.tunnel.state,\n\t\t\ttunnelUrl: this.tunnel.url\n\t\t};\n\t}\n\n\tpublic log(source: LogSource, level: LogLevel, text: string): void {\n\t\tthis.logs.push({ source, level, text });\n\t}\n\n\t/**\n\t * Starts watching; the bot starts after the first successful build.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tthis.#stopped = false;\n\t\tthis.log(\n\t\t\t'stars',\n\t\t\t'info',\n\t\t\t`Watching ${this.config.build.tool === 'none' ? 'sources' : `with ${this.config.build.tool}`}, entry ${this.config.entry}`\n\t\t);\n\t\tif (this.config.dev.typecheck.enabled) this.typechecker.start();\n\t\t// The tunnel comes up next to the build: neither waits for the other, and a failed tunnel never stops the bot.\n\t\tvoid this.tunnel.start();\n\t\tawait this.builder.watch();\n\t}\n\n\t/**\n\t * Restarts the bot immediately (used by the `r` key and by `SIGUSR2`).\n\t */\n\tpublic restart(reason: RestartReason = 'manual'): Promise<void> {\n\t\tthis.#clearRestartTimer();\n\t\treturn this.#enqueue(async () => {\n\t\t\tif (this.#stopped) return;\n\t\t\tthis.#lastRestartReason = reason;\n\t\t\tif (this.supervisor.running) {\n\t\t\t\tthis.#restarts++;\n\t\t\t\tthis.log('stars', 'info', `Restarting (${describeReason(reason)})`);\n\t\t\t\tawait this.supervisor.stop();\n\t\t\t} else {\n\t\t\t\tthis.log('stars', 'info', `Starting (${describeReason(reason)})`);\n\t\t\t}\n\n\t\t\tif (this.#stopped) return;\n\t\t\tthis.supervisor.start();\n\t\t\tthis.#health = 'unknown';\n\t\t\tthis.#emitStatus();\n\t\t});\n\t}\n\n\t/**\n\t * Stops the watcher and the bot. Safe to call more than once.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tthis.#stopped = true;\n\t\tthis.#clearRestartTimer();\n\t\tif (this.#healthTimer) clearInterval(this.#healthTimer);\n\t\tthis.#healthTimer = null;\n\t\tawait this.#enqueue(async () => {\n\t\t\tawait Promise.allSettled([this.builder.close(), this.supervisor.stop(), this.typechecker.close(), this.tunnel.close()]);\n\t\t});\n\t}\n\n\tpublic clearLogs(): void {\n\t\tthis.logs.clear();\n\t}\n\n\t#onBuildSuccess(outcome: BuildOutcome): void {\n\t\tthis.#lastBuild = outcome;\n\t\tthis.#setBuild('ok');\n\t\tif (this.#stopped) return;\n\t\t// A checker without a watch mode (`tsz`) only knows about the change once the build is through.\n\t\tthis.typechecker.check();\n\t\tthis.log('stars', 'success', this.builder.tool === 'none' ? 'Sources changed' : `Build succeeded in ${outcome.durationMs}ms`);\n\t\tthis.#scheduleRestart(this.supervisor.state === 'idle' ? 'initial' : 'build');\n\t}\n\n\t#onBuildFailure(outcome: BuildOutcome): void {\n\t\tthis.#lastBuild = outcome;\n\t\tthis.#setBuild('failed');\n\t\tthis.#clearRestartTimer();\n\t\tthis.log('stars', 'error', `Build failed${outcome.message ? `: ${outcome.message}` : ''}, waiting for changes`);\n\t}\n\n\t#onExit(exit: ProcessExit): void {\n\t\tthis.#lastExit = exit;\n\t\tthis.#health = 'unknown';\n\t\tif (!exit.requested) {\n\t\t\tconst how = exit.signal ? `signal ${exit.signal}` : `code ${exit.code}`;\n\t\t\tthis.log('stars', exit.code === 0 ? 'warn' : 'error', `The bot exited with ${how}, waiting for changes (press r to restart)`);\n\t\t}\n\t\tthis.#emitStatus();\n\t}\n\n\t#scheduleRestart(reason: RestartReason): void {\n\t\tthis.#pendingReason = reason;\n\t\tthis.#clearRestartTimer();\n\t\tthis.#restartTimer = setTimeout(() => {\n\t\t\tthis.#restartTimer = null;\n\t\t\tconst pending = this.#pendingReason ?? reason;\n\t\t\tthis.#pendingReason = null;\n\t\t\tvoid this.restart(pending);\n\t\t}, this.config.dev.debounce);\n\t}\n\n\t#clearRestartTimer(): void {\n\t\tif (this.#restartTimer) clearTimeout(this.#restartTimer);\n\t\tthis.#restartTimer = null;\n\t}\n\n\t#enqueue(task: () => Promise<void>): Promise<void> {\n\t\tthis.#queue = this.#queue.then(task, task);\n\t\treturn this.#queue;\n\t}\n\n\t#setBuild(state: BuildState): void {\n\t\tif (this.#build === state) return;\n\t\tthis.#build = state;\n\t\tthis.#emitStatus();\n\t}\n\n\tasync #checkHealth(): Promise<void> {\n\t\tif (!this.supervisor.running || !this.config.dev.url || !this.config.dev.health) return;\n\n\t\tlet next: HealthState;\n\t\ttry {\n\t\t\tconst response = await fetch(new URL(this.config.dev.health, this.config.dev.url), { signal: AbortSignal.timeout(2000) });\n\t\t\tnext = response.status < 500 ? 'ok' : 'down';\n\t\t} catch {\n\t\t\tnext = 'down';\n\t\t}\n\n\t\tif (next !== this.#health) {\n\t\t\tthis.#health = next;\n\t\t\tthis.#emitStatus();\n\t\t}\n\t}\n\n\t#emitStatus(): void {\n\t\tthis.emit('status', this.status);\n\t}\n}\n\nexport function createSupervisor(config: ResolvedStarsConfig): ProcessSupervisor {\n\treturn new ProcessSupervisor({\n\t\tcommand: process.execPath,\n\t\targs: [...config.dev.nodeArgs, config.build.output, ...config.dev.args],\n\t\tcwd: config.root,\n\t\tenv: { ...process.env, ...config.dev.env, STARS_DEV: '1', FORCE_COLOR: process.env.FORCE_COLOR ?? (process.stdout.isTTY ? '1' : undefined) },\n\t\tkillTimeout: config.dev.killTimeout\n\t});\n}\n\nexport function describeReason(reason: RestartReason): string {\n\tswitch (reason) {\n\t\tcase 'initial':\n\t\t\treturn 'first build';\n\t\tcase 'build':\n\t\t\treturn 'sources changed';\n\t\tcase 'manual':\n\t\t\treturn 'manual restart';\n\t\tcase 'crash':\n\t\t\treturn 'after crash';\n\t}\n}\n","import { lookup } from 'node:dns/promises';\n\n/**\n * Resolves `localhost` the way Vite's dev server does: Node's default DNS result order (`dns.lookup('localhost')`)\n * can disagree with the OS's own, \"verbatim\" resolution order — a well known source of a dev URL that prints fine\n * but is not the address the bot actually bound to (e.g. an IPv6-first system where the server listens on IPv4\n * only). When the two disagree this returns the OS's own address instead of the literal `localhost` hostname.\n */\nexport async function resolveLocalhost(): Promise<string> {\n\ttry {\n\t\tconst [ordered, verbatim] = await Promise.all([lookup('localhost'), lookup('localhost', { verbatim: true })]);\n\t\treturn ordered.family === verbatim.family && ordered.address === verbatim.address ? 'localhost' : verbatim.address;\n\t} catch {\n\t\treturn 'localhost';\n\t}\n}\n\n/**\n * Replaces a `localhost` hostname in `url` with {@link resolveLocalhost}'s result. Any other hostname (an explicit\n * `dev.url` override, a LAN address, …) is returned untouched.\n */\nexport async function withResolvedLocalhost(url: string): Promise<string> {\n\tconst parsed = new URL(url);\n\tif (parsed.hostname !== 'localhost') return url;\n\n\tconst host = await resolveLocalhost();\n\tif (host === 'localhost') return url;\n\n\tparsed.hostname = host.includes(':') ? `[${host}]` : host;\n\treturn parsed.href;\n}\n","import { createWriteStream, mkdirSync, type WriteStream } from 'node:fs';\nimport { dirname } from 'node:path';\nimport type { LogBuffer, LogEntry } from './log-buffer.js';\n\n/**\n * Mirrors a dev session's logs into a file, so a run can be read back after the terminal UI is gone (the TUI takes\n * over the alternate screen and its scrollback disappears on exit). Seedcord keeps a `logs/` directory for the same\n * reason; this writes a single, truncated-per-run file instead.\n */\nexport class LogFileWriter {\n\t#stream: WriteStream | null = null;\n\n\tpublic constructor(\n\t\tpublic readonly file: string,\n\t\tprivate readonly logs: LogBuffer\n\t) {}\n\n\tpublic open(): void {\n\t\tif (this.#stream) return;\n\n\t\tmkdirSync(dirname(this.file), { recursive: true });\n\t\tthis.#stream = createWriteStream(this.file, { flags: 'w' });\n\t\t// A broken pipe or a read-only directory must never take the dev session down with it.\n\t\tthis.#stream.on('error', () => this.close());\n\n\t\tfor (const entry of this.logs.entries()) this.#write(entry);\n\t\tthis.logs.on('entry', this.#write);\n\t}\n\n\tpublic close(): void {\n\t\tconst stream = this.#stream;\n\t\tthis.#stream = null;\n\t\tif (!stream) return;\n\n\t\tthis.logs.off('entry', this.#write);\n\t\tstream.end();\n\t}\n\n\t#write = (entry: LogEntry): void => {\n\t\tthis.#stream?.write(`${format(entry)}\\n`);\n\t};\n}\n\nfunction format(entry: LogEntry): string {\n\treturn `${new Date(entry.time).toISOString()} ${entry.level.padEnd(7)} ${entry.source.padEnd(6)} ${entry.text}`;\n}\n","import { loadStarsConfig, type ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport type { ProjectArgs } from '../args.js';\nimport { resolveCwd } from '../args.js';\nimport { assertSupportedExperiments, createBuilder } from '../builders/index.js';\nimport { DevService } from '../dev-service.js';\nimport { ExitCode } from '../errors.js';\nimport { withResolvedLocalhost } from '../host.js';\nimport { LogFileWriter } from '../log-file.js';\nimport { prefersReducedMotion, resolveOutputMode, shouldUseColor } from '../output-mode.js';\nimport { prepareAutoImports } from './prepare.js';\n\nexport interface DevTaskOptions extends ProjectArgs {\n\ttui?: boolean;\n}\n\nexport async function runDev(options: DevTaskOptions): Promise<void> {\n\tconst config = await resolveDevConfig(await loadStarsConfig({ cwd: resolveCwd(options), configFile: options.config }));\n\tassertSupportedExperiments(config);\n\tawait prepareAutoImports(config);\n\tconst mode = resolveOutputMode({ tui: options.tui });\n\tconst color = shouldUseColor();\n\n\tconst service = new DevService(config, { builder: await createBuilder(config) });\n\tconst logFile = config.dev.logFile ? new LogFileWriter(config.dev.logFile, service.logs) : null;\n\tlogFile?.open();\n\tconst renderer =\n\t\tmode === 'tui'\n\t\t\t? (await import('../../renderers/tui.js')).createTuiRenderer(service, { color, reducedMotion: prefersReducedMotion() })\n\t\t\t: (await import('../../renderers/plain.js')).createPlainRenderer(service, { color });\n\n\tlet exiting: Promise<never> | null = null;\n\tconst shutdown = (code: ExitCode): Promise<never> => {\n\t\texiting ??= (async () => {\n\t\t\trenderer.stop();\n\t\t\tawait service.stop();\n\t\t\tlogFile?.close();\n\t\t\tprocess.exit(code);\n\t\t})();\n\t\treturn exiting;\n\t};\n\n\tprocess.once('SIGINT', () => void shutdown(ExitCode.Interrupted));\n\tprocess.once('SIGTERM', () => void shutdown(ExitCode.Terminated));\n\tprocess.once('SIGHUP', () => void shutdown(ExitCode.Terminated));\n\tif (process.platform !== 'win32') process.on('SIGUSR2', () => void service.restart('manual'));\n\n\tconst finished = renderer.start().then(() => shutdown(ExitCode.Ok));\n\tawait service.start();\n\tawait finished;\n}\n\n/**\n * Resolves `dev.url`'s `localhost` to the address that is actually reachable (see {@link withResolvedLocalhost}),\n * the way Vite's dev server does when it starts. Only `stars dev` pays this DNS lookup; `stars info`/`stars build`\n * show or use the unresolved config as-is, since they never talk to the bot.\n */\nasync function resolveDevConfig(config: ResolvedStarsConfig): Promise<ResolvedStarsConfig> {\n\tif (!config.dev.url) return config;\n\n\tconst url = await withResolvedLocalhost(config.dev.url);\n\treturn url === config.dev.url ? config : { ...config, dev: { ...config.dev, url } };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dev-CWA_JVTb.js","names":["#nextId","#entries","#state","#errors","#run","#child","#pending","#setState","#handleLine","#setBuild","#onBuildSuccess","#onBuildFailure","#emitStatus","#onExit","#healthTimer","#checkHealth","#build","#health","#restarts","#lastRestartReason","#lastBuild","#lastExit","#stopped","#clearRestartTimer","#enqueue","#scheduleRestart","#pendingReason","#restartTimer","#queue","#stream","#write"],"sources":["../src/lib/log-buffer.ts","../src/lib/typechecker.ts","../src/lib/dev-service.ts","../src/lib/host.ts","../src/lib/log-file.ts","../src/lib/tasks/dev.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\n\nexport type LogSource = 'stars' | 'build' | 'app' | 'tsc' | 'tunnel';\nexport type LogLevel = 'debug' | 'info' | 'success' | 'warn' | 'error';\n\nexport interface LogEntry {\n\treadonly id: number;\n\treadonly time: number;\n\treadonly source: LogSource;\n\treadonly level: LogLevel;\n\treadonly text: string;\n}\n\nexport interface LogFilter {\n\tsource?: LogSource | null;\n\tlevel?: LogLevel | null;\n\tquery?: string | null;\n}\n\nexport type LogInput = Omit<LogEntry, 'id' | 'time'>;\n\n/**\n * A bounded, in-memory log store shared by the dev service and its renderers.\n */\nexport class LogBuffer extends EventEmitter<{ entry: [LogEntry]; clear: [] }> {\n\t#entries: LogEntry[] = [];\n\t#nextId = 1;\n\n\tpublic constructor(public readonly capacity = 2000) {\n\t\tsuper();\n\t}\n\n\tpublic push(input: LogInput): LogEntry {\n\t\tconst entry: LogEntry = { id: this.#nextId++, time: Date.now(), ...input };\n\t\tthis.#entries.push(entry);\n\t\tif (this.#entries.length > this.capacity) this.#entries.splice(0, this.#entries.length - this.capacity);\n\t\tthis.emit('entry', entry);\n\t\treturn entry;\n\t}\n\n\tpublic entries(): readonly LogEntry[] {\n\t\treturn this.#entries;\n\t}\n\n\tpublic filter(filter: LogFilter): LogEntry[] {\n\t\tconst query = filter.query?.toLowerCase() ?? null;\n\t\treturn this.#entries.filter(\n\t\t\t(entry) =>\n\t\t\t\t(!filter.source || entry.source === filter.source) &&\n\t\t\t\t(!filter.level || matchesLevel(entry.level, filter.level)) &&\n\t\t\t\t(!query || entry.text.toLowerCase().includes(query))\n\t\t);\n\t}\n\n\tpublic clear(): void {\n\t\tthis.#entries = [];\n\t\tthis.emit('clear');\n\t}\n}\n\nconst LEVEL_ORDER: Record<LogLevel, number> = { debug: 0, info: 1, success: 1, warn: 2, error: 3 };\n\n/**\n * Whether `level` is at least as severe as `minimum`.\n */\nexport function matchesLevel(level: LogLevel, minimum: LogLevel): boolean {\n\treturn LEVEL_ORDER[level] >= LEVEL_ORDER[minimum];\n}\n","import type { ResolvedStarsConfig, StarsTypechecker } from '@wolfstar/http-framework/config';\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport { EventEmitter } from 'node:events';\nimport { resolveTscBinary } from './builders/tsc.js';\nimport { CliError } from './errors.js';\nimport type { LogLevel } from './log-buffer.js';\nimport { createLineSplitter } from './process-supervisor.js';\nimport { resolveBinary } from './project.js';\n\nconst WATCH_START = /Starting (incremental )?compilation/;\nconst WATCH_DONE = /Found (\\d+) errors?\\. Watching for file changes/;\nconst ONE_SHOT_DONE = /Found (\\d+) errors?/;\nconst ERROR_LINE = /error TS\\d+:/;\n\nexport type TypecheckState = 'idle' | 'checking' | 'ok' | 'failed';\n\nexport interface TypecheckerEvents {\n\tlog: [level: LogLevel, text: string];\n\tstate: [state: TypecheckState, errors: number];\n}\n\nexport interface TypecheckCommand {\n\t/** The executable, and whether it is a script `node` has to run. */\n\treadonly command: string;\n\treadonly args: readonly string[];\n\treadonly node: boolean;\n\t/** Whether the checker watches the project itself, or has to be re-run after every build. */\n\treadonly watch: boolean;\n}\n\n/**\n * Runs a type checker next to the bot and reports type errors on their own channel, without ever blocking builds or\n * restarts: `stars dev` keeps running whatever the build tool produced, the way Seedcord's `hmr.typecheck` and\n * Vite's `checker` plugin do.\n *\n * `tsc` and `golar` (which forwards to TypeScript) watch the project themselves; `tsz` has no watch mode, so\n * {@link Typechecker.check} re-runs it after every build instead.\n */\nexport class Typechecker extends EventEmitter<TypecheckerEvents> {\n\t#child: ChildProcess | null = null;\n\t#state: TypecheckState = 'idle';\n\t#errors = 0;\n\t#pending = false;\n\n\tpublic constructor(private readonly config: ResolvedStarsConfig) {\n\t\tsuper();\n\t}\n\n\tpublic get state(): TypecheckState {\n\t\treturn this.#state;\n\t}\n\n\tpublic get errors(): number {\n\t\treturn this.#errors;\n\t}\n\n\t/** Whether the checker re-runs per build instead of watching on its own. */\n\tpublic get oneShot(): boolean {\n\t\treturn this.config.dev.typecheck.enabled && !resolveTypecheckCommand(this.config).watch;\n\t}\n\n\tpublic start(): void {\n\t\tif (!this.config.dev.typecheck.enabled) return;\n\t\tthis.#run();\n\t}\n\n\t/**\n\t * Re-runs a checker that cannot watch. Called after every successful build; a run already in flight is replaced,\n\t * so a fast series of builds only leaves the last check standing.\n\t */\n\tpublic check(): void {\n\t\tif (!this.oneShot) return;\n\t\tif (this.#child) {\n\t\t\tthis.#pending = true;\n\t\t\tthis.#child.kill();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#run();\n\t}\n\n\tpublic close(): Promise<void> {\n\t\tconst child = this.#child;\n\t\tthis.#child = null;\n\t\tthis.#pending = false;\n\t\tif (!child || child.exitCode !== null) return Promise.resolve();\n\n\t\treturn new Promise((resolve) => {\n\t\t\tchild.once('exit', () => resolve());\n\t\t\tchild.kill();\n\t\t});\n\t}\n\n\t#run(): void {\n\t\tconst { command, args, node, watch } = resolveTypecheckCommand(this.config);\n\t\tconst child = spawn(node ? process.execPath : command, node ? [command, ...args] : [...args], {\n\t\t\tcwd: this.config.root,\n\t\t\tenv: process.env,\n\t\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\t\twindowsHide: true\n\t\t});\n\t\tthis.#child = child;\n\t\tthis.#setState('checking', 0);\n\n\t\tlet errors = 0;\n\t\tconst handleLine = (line: string) => {\n\t\t\tif (ERROR_LINE.test(line)) errors++;\n\t\t\tthis.#handleLine(line);\n\t\t};\n\n\t\tconst stdout = createLineSplitter(handleLine);\n\t\tconst stderr = createLineSplitter(handleLine);\n\t\tchild.stdout?.on('data', stdout.push);\n\t\tchild.stderr?.on('data', stderr.push);\n\n\t\tchild.once('exit', (code) => {\n\t\t\tstdout.flush();\n\t\t\tstderr.flush();\n\t\t\tthis.#child = null;\n\n\t\t\tif (!watch && this.#state === 'checking') {\n\t\t\t\t// A one-shot checker only reports through its exit code when it prints no summary line.\n\t\t\t\tthis.#setState(code === 0 && errors === 0 ? 'ok' : 'failed', errors);\n\t\t\t}\n\n\t\t\tif (this.#pending) {\n\t\t\t\tthis.#pending = false;\n\t\t\t\tthis.#run();\n\t\t\t}\n\t\t});\n\t\tchild.once('error', (error: NodeJS.ErrnoException) => {\n\t\t\tthis.#child = null;\n\t\t\tthis.emit('log', 'error', error.code === 'ENOENT' ? `${command} is not installed` : error.message);\n\t\t\tthis.#setState('failed', this.#errors);\n\t\t});\n\t}\n\n\t#handleLine(line: string): void {\n\t\tconst text = line.trim();\n\t\tif (text.length === 0) return;\n\n\t\tif (WATCH_START.test(text)) {\n\t\t\tthis.#setState('checking', 0);\n\t\t\treturn;\n\t\t}\n\n\t\tif (ERROR_LINE.test(text)) {\n\t\t\tthis.emit('log', 'error', text);\n\t\t\treturn;\n\t\t}\n\n\t\tconst done = WATCH_DONE.exec(text) ?? ONE_SHOT_DONE.exec(text);\n\t\tif (done) {\n\t\t\tconst errors = Number(done[1]);\n\t\t\tthis.#setState(errors === 0 ? 'ok' : 'failed', errors);\n\t\t\tthis.emit('log', errors === 0 ? 'success' : 'error', errors === 0 ? 'No type errors' : `${errors} type error${errors === 1 ? '' : 's'}`);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.emit('log', 'info', text);\n\t}\n\n\t#setState(state: TypecheckState, errors: number): void {\n\t\tthis.#state = state;\n\t\tthis.#errors = errors;\n\t\tthis.emit('state', state, errors);\n\t}\n}\n\n/**\n * Builds the command line of the configured type checker, resolved from the project's own `node_modules`.\n *\n * @throws {CliError} when the checker is not installed in the project.\n */\nexport function resolveTypecheckCommand(config: ResolvedStarsConfig): TypecheckCommand {\n\tconst { checker, tsconfig } = config.dev.typecheck;\n\tconst project = tsconfig!;\n\n\tswitch (checker) {\n\t\tcase 'golar': {\n\t\t\t// `golar tsc` forwards everything after it to the TypeScript CLI, watch mode included.\n\t\t\tconst golar = resolveBinary(config.root, 'golar', 'golar');\n\t\t\tif (!golar) throw missing(config, 'golar', 'Install it with `pnpm add -D golar`');\n\n\t\t\treturn { command: golar, args: ['tsc', ...WATCH_ARGS, '-p', project], node: true, watch: true };\n\t\t}\n\n\t\tcase 'tsz': {\n\t\t\t// `tsz` is an early, tsc-compatible checker with no watch mode; `try-tsz` is its comparison harness.\n\t\t\tconst tsz = resolveBinary(config.root, '@mohsen-azimi/tsz-dev', 'tsz');\n\t\t\tif (tsz) return { command: tsz, args: ['--noEmit', '-p', project], node: true, watch: false };\n\n\t\t\tconst tryTsz = resolveBinary(config.root, 'try-tsz', 'try-tsz');\n\t\t\tif (tryTsz) return { command: tryTsz, args: ['-p', project], node: true, watch: false };\n\n\t\t\tthrow missing(config, 'tsz', 'Install it with `pnpm add -D @mohsen-azimi/tsz-dev` (or `try-tsz`)');\n\t\t}\n\n\t\tdefault: {\n\t\t\tconst tsc = resolveTscBinary(config.root);\n\t\t\tif (!tsc) throw missing(config, 'typescript', 'Install it with `pnpm add -D typescript`');\n\n\t\t\treturn { command: tsc, args: [...WATCH_ARGS, '-p', project], node: true, watch: true };\n\t\t}\n\t}\n}\n\nconst WATCH_ARGS = ['--noEmit', '--watch', '--preserveWatchOutput', '--pretty', 'false'] as const;\n\nfunction missing(config: ResolvedStarsConfig, name: string, install: string): CliError {\n\treturn new CliError(`\"${name}\" is not installed in ${config.root}`, {\n\t\tcode: 'DEPENDENCY_MISSING',\n\t\thint: `${install}, pick another \\`dev.typecheck.checker\\`, or set \\`dev.typecheck\\` to false.`\n\t});\n}\n\nexport type { StarsTypechecker };\n","import { EventEmitter } from 'node:events';\nimport type { Builder, BuildOutcome } from './builders/types.js';\nimport type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { LogBuffer, type LogLevel, type LogSource } from './log-buffer.js';\nimport { ProcessSupervisor, type ProcessExit, type ProcessState } from './process-supervisor.js';\nimport { Tunnel, type TunnelState } from './tunnel.js';\nimport { Typechecker, type TypecheckState } from './typechecker.js';\n\nexport type BuildState = 'idle' | 'building' | 'ok' | 'failed';\nexport type HealthState = 'unknown' | 'ok' | 'down';\nexport type RestartReason = 'initial' | 'build' | 'manual' | 'crash';\n\nexport interface DevStatus {\n\treadonly process: ProcessState;\n\treadonly build: BuildState;\n\treadonly health: HealthState;\n\treadonly pid: number | null;\n\treadonly startedAt: number | null;\n\treadonly restarts: number;\n\treadonly lastRestartReason: RestartReason | null;\n\treadonly lastBuild: BuildOutcome | null;\n\treadonly lastExit: ProcessExit | null;\n\treadonly url: string | null;\n\treadonly typecheck: TypecheckState;\n\treadonly typeErrors: number;\n\treadonly tunnel: TunnelState;\n\treadonly tunnelUrl: string | null;\n}\n\nexport interface DevServiceEvents {\n\tstatus: [status: DevStatus];\n}\n\nexport interface DevServiceOptions {\n\tbuilder: Builder;\n\tlogs?: LogBuffer;\n\t/** Overrides for tests. */\n\tsupervisor?: ProcessSupervisor;\n\ttypechecker?: Typechecker;\n\ttunnel?: Tunnel;\n\thealthInterval?: number;\n}\n\n/**\n * The headless heart of `stars dev`: builds, (re)starts the bot and exposes state and logs.\n * Renderers (plain or TUI) only subscribe to it, they never own behaviour.\n */\nexport class DevService extends EventEmitter<DevServiceEvents> {\n\tpublic readonly logs: LogBuffer;\n\tpublic readonly builder: Builder;\n\tpublic readonly supervisor: ProcessSupervisor;\n\tpublic readonly typechecker: Typechecker;\n\tpublic readonly tunnel: Tunnel;\n\n\t#build: BuildState = 'idle';\n\t#health: HealthState = 'unknown';\n\t#restarts = 0;\n\t#lastRestartReason: RestartReason | null = null;\n\t#lastBuild: BuildOutcome | null = null;\n\t#lastExit: ProcessExit | null = null;\n\t#restartTimer: NodeJS.Timeout | null = null;\n\t#healthTimer: NodeJS.Timeout | null = null;\n\t#pendingReason: RestartReason | null = null;\n\t#stopped = false;\n\t#queue: Promise<void> = Promise.resolve();\n\n\tpublic constructor(\n\t\tpublic readonly config: ResolvedStarsConfig,\n\t\toptions: DevServiceOptions\n\t) {\n\t\tsuper();\n\t\tthis.logs = options.logs ?? new LogBuffer();\n\t\tthis.builder = options.builder;\n\t\tthis.supervisor = options.supervisor ?? createSupervisor(config);\n\t\tthis.typechecker = options.typechecker ?? new Typechecker(config);\n\t\tthis.tunnel = options.tunnel ?? new Tunnel(config);\n\n\t\tthis.builder.on('start', () => this.#setBuild('building'));\n\t\tthis.builder.on('success', (outcome) => this.#onBuildSuccess(outcome));\n\t\tthis.builder.on('failure', (outcome) => this.#onBuildFailure(outcome));\n\t\tthis.builder.on('log', (level, text) => this.log('build', level, text));\n\n\t\tthis.supervisor.on('state', () => this.#emitStatus());\n\t\tthis.supervisor.on('stdout', (line) => this.log('app', 'info', line));\n\t\tthis.supervisor.on('stderr', (line) => this.log('app', 'error', line));\n\t\tthis.supervisor.on('error', (error) => this.log('stars', 'error', `Failed to start the bot: ${error.message}`));\n\t\tthis.supervisor.on('exit', (exit) => this.#onExit(exit));\n\n\t\tthis.typechecker.on('log', (level, text) => this.log('tsc', level, text));\n\t\tthis.typechecker.on('state', () => this.#emitStatus());\n\t\tthis.tunnel.on('log', (level, text) => this.log('tunnel', level, text));\n\t\tthis.tunnel.on('state', () => this.#emitStatus());\n\n\t\tif (config.dev.url && config.dev.health) {\n\t\t\tconst interval = options.healthInterval ?? 5000;\n\t\t\tthis.#healthTimer = setInterval(() => void this.#checkHealth(), interval);\n\t\t\tthis.#healthTimer.unref();\n\t\t}\n\t}\n\n\tpublic get status(): DevStatus {\n\t\treturn {\n\t\t\tprocess: this.supervisor.state,\n\t\t\tbuild: this.#build,\n\t\t\thealth: this.#health,\n\t\t\tpid: this.supervisor.pid,\n\t\t\tstartedAt: this.supervisor.startedAt,\n\t\t\trestarts: this.#restarts,\n\t\t\tlastRestartReason: this.#lastRestartReason,\n\t\t\tlastBuild: this.#lastBuild,\n\t\t\tlastExit: this.#lastExit,\n\t\t\turl: this.config.dev.url,\n\t\t\ttypecheck: this.typechecker.state,\n\t\t\ttypeErrors: this.typechecker.errors,\n\t\t\ttunnel: this.tunnel.state,\n\t\t\ttunnelUrl: this.tunnel.url\n\t\t};\n\t}\n\n\tpublic log(source: LogSource, level: LogLevel, text: string): void {\n\t\tthis.logs.push({ source, level, text });\n\t}\n\n\t/**\n\t * Starts watching; the bot starts after the first successful build.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tthis.#stopped = false;\n\t\tthis.log(\n\t\t\t'stars',\n\t\t\t'info',\n\t\t\t`Watching ${this.config.build.tool === 'none' ? 'sources' : `with ${this.config.build.tool}`}, entry ${this.config.entry}`\n\t\t);\n\t\tif (this.config.dev.typecheck.enabled) this.typechecker.start();\n\t\t// The tunnel comes up next to the build: neither waits for the other, and a failed tunnel never stops the bot.\n\t\tvoid this.tunnel.start();\n\t\tawait this.builder.watch();\n\t}\n\n\t/**\n\t * Restarts the bot immediately (used by the `r` key and by `SIGUSR2`).\n\t */\n\tpublic restart(reason: RestartReason = 'manual'): Promise<void> {\n\t\tthis.#clearRestartTimer();\n\t\treturn this.#enqueue(async () => {\n\t\t\tif (this.#stopped) return;\n\t\t\tthis.#lastRestartReason = reason;\n\t\t\tif (this.supervisor.running) {\n\t\t\t\tthis.#restarts++;\n\t\t\t\tthis.log('stars', 'info', `Restarting (${describeReason(reason)})`);\n\t\t\t\tawait this.supervisor.stop();\n\t\t\t} else {\n\t\t\t\tthis.log('stars', 'info', `Starting (${describeReason(reason)})`);\n\t\t\t}\n\n\t\t\tif (this.#stopped) return;\n\t\t\tthis.supervisor.start();\n\t\t\tthis.#health = 'unknown';\n\t\t\tthis.#emitStatus();\n\t\t});\n\t}\n\n\t/**\n\t * Stops the watcher and the bot. Safe to call more than once.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tthis.#stopped = true;\n\t\tthis.#clearRestartTimer();\n\t\tif (this.#healthTimer) clearInterval(this.#healthTimer);\n\t\tthis.#healthTimer = null;\n\t\tawait this.#enqueue(async () => {\n\t\t\tawait Promise.allSettled([this.builder.close(), this.supervisor.stop(), this.typechecker.close(), this.tunnel.close()]);\n\t\t});\n\t}\n\n\tpublic clearLogs(): void {\n\t\tthis.logs.clear();\n\t}\n\n\t#onBuildSuccess(outcome: BuildOutcome): void {\n\t\tthis.#lastBuild = outcome;\n\t\tthis.#setBuild('ok');\n\t\tif (this.#stopped) return;\n\t\t// A checker without a watch mode (`tsz`) only knows about the change once the build is through.\n\t\tthis.typechecker.check();\n\t\tthis.log('stars', 'success', this.builder.tool === 'none' ? 'Sources changed' : `Build succeeded in ${outcome.durationMs}ms`);\n\t\tthis.#scheduleRestart(this.supervisor.state === 'idle' ? 'initial' : 'build');\n\t}\n\n\t#onBuildFailure(outcome: BuildOutcome): void {\n\t\tthis.#lastBuild = outcome;\n\t\tthis.#setBuild('failed');\n\t\tthis.#clearRestartTimer();\n\t\tthis.log('stars', 'error', `Build failed${outcome.message ? `: ${outcome.message}` : ''}, waiting for changes`);\n\t}\n\n\t#onExit(exit: ProcessExit): void {\n\t\tthis.#lastExit = exit;\n\t\tthis.#health = 'unknown';\n\t\tif (!exit.requested) {\n\t\t\tconst how = exit.signal ? `signal ${exit.signal}` : `code ${exit.code}`;\n\t\t\tthis.log('stars', exit.code === 0 ? 'warn' : 'error', `The bot exited with ${how}, waiting for changes (press r to restart)`);\n\t\t}\n\t\tthis.#emitStatus();\n\t}\n\n\t#scheduleRestart(reason: RestartReason): void {\n\t\tthis.#pendingReason = reason;\n\t\tthis.#clearRestartTimer();\n\t\tthis.#restartTimer = setTimeout(() => {\n\t\t\tthis.#restartTimer = null;\n\t\t\tconst pending = this.#pendingReason ?? reason;\n\t\t\tthis.#pendingReason = null;\n\t\t\tvoid this.restart(pending);\n\t\t}, this.config.dev.debounce);\n\t}\n\n\t#clearRestartTimer(): void {\n\t\tif (this.#restartTimer) clearTimeout(this.#restartTimer);\n\t\tthis.#restartTimer = null;\n\t}\n\n\t#enqueue(task: () => Promise<void>): Promise<void> {\n\t\tthis.#queue = this.#queue.then(task, task);\n\t\treturn this.#queue;\n\t}\n\n\t#setBuild(state: BuildState): void {\n\t\tif (this.#build === state) return;\n\t\tthis.#build = state;\n\t\tthis.#emitStatus();\n\t}\n\n\tasync #checkHealth(): Promise<void> {\n\t\tif (!this.supervisor.running || !this.config.dev.url || !this.config.dev.health) return;\n\n\t\tlet next: HealthState;\n\t\ttry {\n\t\t\tconst response = await fetch(new URL(this.config.dev.health, this.config.dev.url), { signal: AbortSignal.timeout(2000) });\n\t\t\tnext = response.status < 500 ? 'ok' : 'down';\n\t\t} catch {\n\t\t\tnext = 'down';\n\t\t}\n\n\t\tif (next !== this.#health) {\n\t\t\tthis.#health = next;\n\t\t\tthis.#emitStatus();\n\t\t}\n\t}\n\n\t#emitStatus(): void {\n\t\tthis.emit('status', this.status);\n\t}\n}\n\nexport function createSupervisor(config: ResolvedStarsConfig): ProcessSupervisor {\n\treturn new ProcessSupervisor({\n\t\tcommand: process.execPath,\n\t\targs: [...config.dev.nodeArgs, config.build.output, ...config.dev.args],\n\t\tcwd: config.root,\n\t\tenv: { ...process.env, ...config.dev.env, STARS_DEV: '1', FORCE_COLOR: process.env.FORCE_COLOR ?? (process.stdout.isTTY ? '1' : undefined) },\n\t\tkillTimeout: config.dev.killTimeout\n\t});\n}\n\nexport function describeReason(reason: RestartReason): string {\n\tswitch (reason) {\n\t\tcase 'initial':\n\t\t\treturn 'first build';\n\t\tcase 'build':\n\t\t\treturn 'sources changed';\n\t\tcase 'manual':\n\t\t\treturn 'manual restart';\n\t\tcase 'crash':\n\t\t\treturn 'after crash';\n\t}\n}\n","import { lookup } from 'node:dns/promises';\n\n/**\n * Resolves `localhost` the way Vite's dev server does: Node's default DNS result order (`dns.lookup('localhost')`)\n * can disagree with the OS's own, \"verbatim\" resolution order — a well known source of a dev URL that prints fine\n * but is not the address the bot actually bound to (e.g. an IPv6-first system where the server listens on IPv4\n * only). When the two disagree this returns the OS's own address instead of the literal `localhost` hostname.\n */\nexport async function resolveLocalhost(): Promise<string> {\n\ttry {\n\t\tconst [ordered, verbatim] = await Promise.all([lookup('localhost'), lookup('localhost', { verbatim: true })]);\n\t\treturn ordered.family === verbatim.family && ordered.address === verbatim.address ? 'localhost' : verbatim.address;\n\t} catch {\n\t\treturn 'localhost';\n\t}\n}\n\n/**\n * Replaces a `localhost` hostname in `url` with {@link resolveLocalhost}'s result. Any other hostname (an explicit\n * `dev.url` override, a LAN address, …) is returned untouched.\n */\nexport async function withResolvedLocalhost(url: string): Promise<string> {\n\tconst parsed = new URL(url);\n\tif (parsed.hostname !== 'localhost') return url;\n\n\tconst host = await resolveLocalhost();\n\tif (host === 'localhost') return url;\n\n\tparsed.hostname = host.includes(':') ? `[${host}]` : host;\n\treturn parsed.href;\n}\n","import { createWriteStream, mkdirSync, type WriteStream } from 'node:fs';\nimport { dirname } from 'node:path';\nimport type { LogBuffer, LogEntry } from './log-buffer.js';\n\n/**\n * Mirrors a dev session's logs into a file, so a run can be read back after the terminal UI is gone (the TUI takes\n * over the alternate screen and its scrollback disappears on exit). Seedcord keeps a `logs/` directory for the same\n * reason; this writes a single, truncated-per-run file instead.\n */\nexport class LogFileWriter {\n\t#stream: WriteStream | null = null;\n\n\tpublic constructor(\n\t\tpublic readonly file: string,\n\t\tprivate readonly logs: LogBuffer\n\t) {}\n\n\tpublic open(): void {\n\t\tif (this.#stream) return;\n\n\t\tmkdirSync(dirname(this.file), { recursive: true });\n\t\tthis.#stream = createWriteStream(this.file, { flags: 'w' });\n\t\t// A broken pipe or a read-only directory must never take the dev session down with it.\n\t\tthis.#stream.on('error', () => this.close());\n\n\t\tfor (const entry of this.logs.entries()) this.#write(entry);\n\t\tthis.logs.on('entry', this.#write);\n\t}\n\n\tpublic close(): void {\n\t\tconst stream = this.#stream;\n\t\tthis.#stream = null;\n\t\tif (!stream) return;\n\n\t\tthis.logs.off('entry', this.#write);\n\t\tstream.end();\n\t}\n\n\t#write = (entry: LogEntry): void => {\n\t\tthis.#stream?.write(`${format(entry)}\\n`);\n\t};\n}\n\nfunction format(entry: LogEntry): string {\n\treturn `${new Date(entry.time).toISOString()} ${entry.level.padEnd(7)} ${entry.source.padEnd(6)} ${entry.text}`;\n}\n","import { loadStarsConfig, type ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport type { ProjectArgs } from '../args.js';\nimport { resolveCwd } from '../args.js';\nimport { assertSupportedExperiments, createBuilder } from '../builders/index.js';\nimport { DevService } from '../dev-service.js';\nimport { ExitCode } from '../errors.js';\nimport { withResolvedLocalhost } from '../host.js';\nimport { LogFileWriter } from '../log-file.js';\nimport { prefersReducedMotion, resolveOutputMode, shouldUseColor } from '../output-mode.js';\nimport { prepareAutoImports } from './prepare.js';\n\nexport interface DevTaskOptions extends ProjectArgs {\n\ttui?: boolean;\n}\n\nexport async function runDev(options: DevTaskOptions): Promise<void> {\n\tconst config = await resolveDevConfig(await loadStarsConfig({ cwd: resolveCwd(options), configFile: options.config }));\n\tassertSupportedExperiments(config);\n\tawait prepareAutoImports(config);\n\tconst mode = resolveOutputMode({ tui: options.tui });\n\tconst color = shouldUseColor();\n\n\tconst service = new DevService(config, { builder: await createBuilder(config) });\n\tconst logFile = config.dev.logFile ? new LogFileWriter(config.dev.logFile, service.logs) : null;\n\tlogFile?.open();\n\tconst renderer =\n\t\tmode === 'tui'\n\t\t\t? (await import('../../renderers/tui.js')).createTuiRenderer(service, { color, reducedMotion: prefersReducedMotion() })\n\t\t\t: (await import('../../renderers/plain.js')).createPlainRenderer(service, { color });\n\n\tlet exiting: Promise<never> | null = null;\n\tconst shutdown = (code: ExitCode): Promise<never> => {\n\t\texiting ??= (async () => {\n\t\t\trenderer.stop();\n\t\t\tawait service.stop();\n\t\t\tlogFile?.close();\n\t\t\tprocess.exit(code);\n\t\t})();\n\t\treturn exiting;\n\t};\n\n\tprocess.once('SIGINT', () => void shutdown(ExitCode.Interrupted));\n\tprocess.once('SIGTERM', () => void shutdown(ExitCode.Terminated));\n\tprocess.once('SIGHUP', () => void shutdown(ExitCode.Terminated));\n\tif (process.platform !== 'win32') process.on('SIGUSR2', () => void service.restart('manual'));\n\n\tconst finished = renderer.start().then(() => shutdown(ExitCode.Ok));\n\tawait service.start();\n\tawait finished;\n}\n\n/**\n * Resolves `dev.url`'s `localhost` to the address that is actually reachable (see {@link withResolvedLocalhost}),\n * the way Vite's dev server does when it starts. Only `stars dev` pays this DNS lookup; `stars info`/`stars build`\n * show or use the unresolved config as-is, since they never talk to the bot.\n */\nasync function resolveDevConfig(config: ResolvedStarsConfig): Promise<ResolvedStarsConfig> {\n\tif (!config.dev.url) return config;\n\n\tconst url = await withResolvedLocalhost(config.dev.url);\n\treturn url === config.dev.url ? config : { ...config, dev: { ...config.dev, url } };\n}\n"],"mappings":"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"}
|
|
@@ -16,7 +16,7 @@ var info_default = defineCommand({
|
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
async run({ args }) {
|
|
19
|
-
const { runInfo } = await import("./info-
|
|
19
|
+
const { runInfo } = await import("./info-Czh4LUsD.js");
|
|
20
20
|
await runInfo({
|
|
21
21
|
config: args.config,
|
|
22
22
|
cwd: args.cwd,
|
|
@@ -27,4 +27,4 @@ var info_default = defineCommand({
|
|
|
27
27
|
|
|
28
28
|
//#endregion
|
|
29
29
|
export { info_default as default };
|
|
30
|
-
//# sourceMappingURL=info-
|
|
30
|
+
//# sourceMappingURL=info-BqxpktLG.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"info-
|
|
1
|
+
{"version":3,"file":"info-BqxpktLG.js","names":[],"sources":["../src/commands/info.ts"],"sourcesContent":["import { defineCommand } from 'citty';\nimport { projectArgs } from '../lib/args.js';\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'info',\n\t\tdescription: 'Print the resolved project configuration and environment'\n\t},\n\targs: {\n\t\t...projectArgs,\n\t\tjson: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Print machine-readable JSON',\n\t\t\tdefault: false\n\t\t}\n\t},\n\tasync run({ args }) {\n\t\tconst { runInfo } = await import('../lib/tasks/info.js');\n\t\tawait runInfo({ config: args.config, cwd: args.cwd, json: args.json });\n\t}\n});\n"],"mappings":";;;;AAGA,mBAAe,cAAc;CAC5B,MAAM;EACL,MAAM;EACN,aAAa;CACd;CACA,MAAM;EACL,GAAG;EACH,MAAM;GACL,MAAM;GACN,aAAa;GACb,SAAS;EACV;CACD;CACA,MAAM,IAAI,EAAE,QAAQ;EACnB,MAAM,EAAE,YAAY,MAAM,OAAO;EACjC,MAAM,QAAQ;GAAE,QAAQ,KAAK;GAAQ,KAAK,KAAK;GAAK,MAAM,KAAK;EAAK,CAAC;CACtE;AACD,CAAC"}
|
|
@@ -29,7 +29,12 @@ function collectInfo(config) {
|
|
|
29
29
|
dev: config.dev,
|
|
30
30
|
codegen: config.codegen,
|
|
31
31
|
imports: config.imports,
|
|
32
|
-
experimental: config.experimental
|
|
32
|
+
experimental: config.experimental,
|
|
33
|
+
future: config.future,
|
|
34
|
+
options: {
|
|
35
|
+
tsdown: Object.keys(config.tsdown),
|
|
36
|
+
vite: Object.keys(config.vite)
|
|
37
|
+
}
|
|
33
38
|
};
|
|
34
39
|
}
|
|
35
40
|
function formatInfo(info, useColor) {
|
|
@@ -53,6 +58,8 @@ function formatInfo(info, useColor) {
|
|
|
53
58
|
]),
|
|
54
59
|
section("Build", [
|
|
55
60
|
row("tool", info.build.tool),
|
|
61
|
+
row("config", info.build.configFile ? show(info.build.configFile) : colors.dim("stars.config")),
|
|
62
|
+
row("options", describeOptions(info, colors)),
|
|
56
63
|
row("outDir", show(info.build.outDir)),
|
|
57
64
|
row("tsconfig", show(info.build.tsconfig)),
|
|
58
65
|
row("runs", show(info.build.output))
|
|
@@ -69,6 +76,7 @@ function formatInfo(info, useColor) {
|
|
|
69
76
|
row("log file", info.dev.logFile ? show(info.dev.logFile) : colors.dim("disabled"))
|
|
70
77
|
]),
|
|
71
78
|
section("Codegen", [row("i18n", info.codegen.i18n ? `${show(info.codegen.i18n.locales)} → ${show(info.codegen.i18n.output)}` : colors.dim("disabled"))]),
|
|
79
|
+
section("Future", [row("compat", `v${info.future.compatibilityVersion} defaults`)]),
|
|
72
80
|
section("Experimental", [
|
|
73
81
|
row("vite", flag(info.experimental.enableVite, colors)),
|
|
74
82
|
row("nitro", flag(info.experimental.enableNitro, colors)),
|
|
@@ -82,6 +90,10 @@ function formatInfo(info, useColor) {
|
|
|
82
90
|
])
|
|
83
91
|
].join("\n\n");
|
|
84
92
|
}
|
|
93
|
+
function describeOptions(info, colors) {
|
|
94
|
+
const names = info.build.tool === "vite" ? info.options.vite : info.options.tsdown;
|
|
95
|
+
return names.length > 0 ? names.join(", ") : colors.dim("none");
|
|
96
|
+
}
|
|
85
97
|
function flag(enabled, colors) {
|
|
86
98
|
return enabled ? colors.green("enabled") : colors.dim("disabled");
|
|
87
99
|
}
|
|
@@ -103,4 +115,4 @@ async function runInfo(options) {
|
|
|
103
115
|
|
|
104
116
|
//#endregion
|
|
105
117
|
export { runInfo };
|
|
106
|
-
//# sourceMappingURL=info-
|
|
118
|
+
//# sourceMappingURL=info-Czh4LUsD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"info-Czh4LUsD.js","names":[],"sources":["../src/lib/tasks/info.ts"],"sourcesContent":["import { createColors } from 'colorette';\nimport { arch, platform } from 'node:os';\nimport { relative } from 'node:path';\nimport type { ProjectArgs } from '../args.js';\nimport { resolveCwd } from '../args.js';\nimport { loadStarsConfig } from '@wolfstar/http-framework/config';\nimport { displayPath, type ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { resolveOutputMode, shouldUseColor } from '../output-mode.js';\nimport { findInstalledVersion } from '../project.js';\nimport { readOwnPackageJson } from '../version.js';\n\nexport interface InfoOptions extends ProjectArgs {\n\tjson?: boolean;\n\tstdout?: NodeJS.WritableStream;\n}\n\nexport interface ProjectInfo {\n\tcli: { version: string; node: string; platform: string; outputMode: 'tui' | 'plain' };\n\tproject: {\n\t\tcwd: string;\n\t\troot: string;\n\t\tconfigFile: string | null;\n\t\tname: string | null;\n\t\tversion: string | null;\n\t\tentry: string;\n\t\tframeworkVersion: string | null;\n\t};\n\tbuild: ResolvedStarsConfig['build'];\n\tdev: ResolvedStarsConfig['dev'];\n\tcodegen: ResolvedStarsConfig['codegen'];\n\timports: ResolvedStarsConfig['imports'];\n\texperimental: ResolvedStarsConfig['experimental'];\n\tfuture: ResolvedStarsConfig['future'];\n\t/**\n\t * The option names set in the `tsdown`/`vite` blocks. Only the names: the values hold plugins and callbacks,\n\t * which neither serialize to JSON nor read usefully on a terminal.\n\t */\n\toptions: { tsdown: string[]; vite: string[] };\n}\n\nexport function collectInfo(config: ResolvedStarsConfig): ProjectInfo {\n\treturn {\n\t\tcli: {\n\t\t\tversion: readOwnPackageJson().version,\n\t\t\tnode: process.version,\n\t\t\tplatform: `${platform()} ${arch()}`,\n\t\t\toutputMode: resolveOutputMode()\n\t\t},\n\t\tproject: {\n\t\t\tcwd: config.cwd,\n\t\t\troot: config.root,\n\t\t\tconfigFile: config.configFile,\n\t\t\tname: config.packageJson?.name ?? null,\n\t\t\tversion: config.packageJson?.version ?? null,\n\t\t\tentry: config.entry,\n\t\t\tframeworkVersion: findInstalledVersion(config.root, '@wolfstar/http-framework')\n\t\t},\n\t\tbuild: config.build,\n\t\tdev: config.dev,\n\t\tcodegen: config.codegen,\n\t\timports: config.imports,\n\t\texperimental: config.experimental,\n\t\tfuture: config.future,\n\t\toptions: { tsdown: Object.keys(config.tsdown), vite: Object.keys(config.vite) }\n\t};\n}\n\nexport function formatInfo(info: ProjectInfo, useColor: boolean): string {\n\tconst colors = createColors({ useColor });\n\tconst { root } = info.project;\n\tconst show = (path: string | null) => (path === null ? colors.dim('none') : displayPath(root, path));\n\tconst row = (label: string, value: string) => ` ${colors.dim(label.padEnd(12))}${value}`;\n\tconst section = (title: string, rows: string[]) => [colors.bold(title), ...rows].join('\\n');\n\n\treturn [\n\t\tsection(`stars ${colors.green(`v${info.cli.version}`)}`, [\n\t\t\trow('node', info.cli.node),\n\t\t\trow('platform', info.cli.platform),\n\t\t\trow('output', info.cli.outputMode),\n\t\t\trow(\n\t\t\t\t'framework',\n\t\t\t\tinfo.project.frameworkVersion\n\t\t\t\t\t? `@wolfstar/http-framework v${info.project.frameworkVersion}`\n\t\t\t\t\t: colors.yellow('@wolfstar/http-framework not installed')\n\t\t\t)\n\t\t]),\n\t\tsection('Project', [\n\t\t\trow(\n\t\t\t\t'name',\n\t\t\t\tinfo.project.name\n\t\t\t\t\t? `${info.project.name}${info.project.version ? colors.dim(` v${info.project.version}`) : ''}`\n\t\t\t\t\t: colors.dim('unnamed')\n\t\t\t),\n\t\t\trow('root', root),\n\t\t\trow(\n\t\t\t\t'config',\n\t\t\t\tinfo.project.configFile ? relative(root, info.project.configFile) || info.project.configFile : colors.dim('none (defaults)')\n\t\t\t),\n\t\t\trow('entry', show(info.project.entry))\n\t\t]),\n\t\tsection('Build', [\n\t\t\trow('tool', info.build.tool),\n\t\t\t// Which file the build tool is configured from: its own, or this project's `stars.config`.\n\t\t\trow('config', info.build.configFile ? show(info.build.configFile) : colors.dim('stars.config')),\n\t\t\trow('options', describeOptions(info, colors)),\n\t\t\trow('outDir', show(info.build.outDir)),\n\t\t\trow('tsconfig', show(info.build.tsconfig)),\n\t\t\trow('runs', show(info.build.output))\n\t\t]),\n\t\tsection('Dev', [\n\t\t\trow('watch', info.dev.watch.map(show).join(', ')),\n\t\t\trow('debounce', `${info.dev.debounce}ms`),\n\t\t\trow('node args', info.dev.nodeArgs.join(' ') || colors.dim('none')),\n\t\t\trow('args', info.dev.args.join(' ') || colors.dim('none')),\n\t\t\trow('url', info.dev.url ?? colors.dim('unknown, set dev.url or HTTP_PORT')),\n\t\t\trow('health', info.dev.health ?? colors.dim('none')),\n\t\t\trow(\n\t\t\t\t'typecheck',\n\t\t\t\tinfo.dev.typecheck.enabled ? `${info.dev.typecheck.checker} → ${show(info.dev.typecheck.tsconfig)}` : colors.dim('disabled')\n\t\t\t),\n\t\t\trow('tunnel', describeTunnel(info.dev.tunnel, colors)),\n\t\t\trow('log file', info.dev.logFile ? show(info.dev.logFile) : colors.dim('disabled'))\n\t\t]),\n\t\tsection('Codegen', [\n\t\t\trow('i18n', info.codegen.i18n ? `${show(info.codegen.i18n.locales)} → ${show(info.codegen.i18n.output)}` : colors.dim('disabled'))\n\t\t]),\n\t\tsection('Future', [row('compat', `v${info.future.compatibilityVersion} defaults`)]),\n\t\tsection('Experimental', [\n\t\t\trow('vite', flag(info.experimental.enableVite, colors)),\n\t\t\trow('nitro', flag(info.experimental.enableNitro, colors)),\n\t\t\trow('external', flag(info.experimental.enableExternalVite, colors))\n\t\t]),\n\t\tsection('Imports', [\n\t\t\trow('auto', info.imports.enabled ? colors.green('enabled') : colors.dim('disabled')),\n\t\t\trow('dirs', info.imports.dirs.map(show).join(', ')),\n\t\t\trow('presets', info.imports.presets.join(', ') || colors.dim('none')),\n\t\t\trow('dts', show(info.imports.dts))\n\t\t])\n\t].join('\\n\\n');\n}\n\nfunction describeOptions(info: ProjectInfo, colors: ReturnType<typeof createColors>): string {\n\tconst names = info.build.tool === 'vite' ? info.options.vite : info.options.tsdown;\n\treturn names.length > 0 ? names.join(', ') : colors.dim('none');\n}\n\nfunction flag(enabled: boolean, colors: ReturnType<typeof createColors>): string {\n\treturn enabled ? colors.green('enabled') : colors.dim('disabled');\n}\n\nfunction describeTunnel(tunnel: ResolvedStarsConfig['dev']['tunnel'], colors: ReturnType<typeof createColors>): string {\n\tswitch (tunnel.mode) {\n\t\tcase 'quick':\n\t\t\treturn 'cloudflared quick tunnel';\n\t\tcase 'url':\n\t\t\treturn tunnel.url;\n\t\tdefault:\n\t\t\treturn colors.dim('disabled');\n\t}\n}\n\nexport async function runInfo(options: InfoOptions): Promise<void> {\n\tconst stdout = options.stdout ?? process.stdout;\n\tconst config = await loadStarsConfig({ cwd: resolveCwd(options), configFile: options.config });\n\tconst info = collectInfo(config);\n\tstdout.write(`${options.json ? JSON.stringify(info, null, 2) : formatInfo(info, shouldUseColor())}\\n`);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { n as importFromProject } from "./project-DHF3qdm-.js";
|
|
2
|
+
import { dirname, extname, relative, resolve } from "node:path";
|
|
3
|
+
import { EventEmitter } from "node:events";
|
|
4
|
+
|
|
5
|
+
//#region src/lib/builders/tsdown.ts
|
|
6
|
+
const INSTALL_HINT = "Install it with `pnpm add -D tsdown`, or set `build.tool` to 'tsc' or 'none'.";
|
|
7
|
+
const TYPESCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
8
|
+
".ts",
|
|
9
|
+
".mts",
|
|
10
|
+
".cts"
|
|
11
|
+
]);
|
|
12
|
+
/** `./src/lib` and `../shared` are paths; `preact/compat` is a module id and must be left alone. */
|
|
13
|
+
const RELATIVE_PATH = /^\.\.?[/\\]/;
|
|
14
|
+
/**
|
|
15
|
+
* Builds through the project's own `tsdown`, configured from `stars.config`.
|
|
16
|
+
*
|
|
17
|
+
* From `future.compatibilityVersion` 4 on this is the whole configuration: the entry's directory, `build.outDir` and
|
|
18
|
+
* `build.tsconfig` produce the defaults a bot needs, the project's `tsdown` block is layered on top, and the auto
|
|
19
|
+
* imports plugin is wired in — one configuration file instead of two. At 3 the project's own `tsdown.config.*` is
|
|
20
|
+
* still loaded and everything here is merged over it by `tsdown` itself: scalars from `stars.config` win, and
|
|
21
|
+
* `plugins` are appended rather than replaced.
|
|
22
|
+
*/
|
|
23
|
+
var TsdownBuilder = class extends EventEmitter {
|
|
24
|
+
config;
|
|
25
|
+
tool = "tsdown";
|
|
26
|
+
#bundles = [];
|
|
27
|
+
#hadError = false;
|
|
28
|
+
#startedAt = 0;
|
|
29
|
+
constructor(config) {
|
|
30
|
+
super();
|
|
31
|
+
this.config = config;
|
|
32
|
+
}
|
|
33
|
+
async build() {
|
|
34
|
+
const tsdown = await this.#load();
|
|
35
|
+
const options = await this.#options();
|
|
36
|
+
this.#begin();
|
|
37
|
+
try {
|
|
38
|
+
this.#bundles = await tsdown.build(options);
|
|
39
|
+
return this.#finish(this.#hadError ? "The build reported errors" : null);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return this.#finish(error instanceof Error ? error.message : String(error));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async watch() {
|
|
45
|
+
const tsdown = await this.#load();
|
|
46
|
+
const options = await this.#options();
|
|
47
|
+
this.#begin();
|
|
48
|
+
this.#bundles = await tsdown.build(this.#watchOptions(options));
|
|
49
|
+
}
|
|
50
|
+
async close() {
|
|
51
|
+
const bundles = this.#bundles;
|
|
52
|
+
this.#bundles = [];
|
|
53
|
+
await Promise.allSettled(bundles.map((bundle) => bundle[Symbol.asyncDispose]()));
|
|
54
|
+
}
|
|
55
|
+
#load() {
|
|
56
|
+
return importFromProject(this.config.root, "tsdown", INSTALL_HINT);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The options handed to `tsdown.build()`, with the project's own `tsdown` block already layered on top of the
|
|
60
|
+
* defaults (and of the plugins `stars` contributes).
|
|
61
|
+
*/
|
|
62
|
+
async #options() {
|
|
63
|
+
const user = this.config.tsdown;
|
|
64
|
+
const defaults = this.config.build.configFile === null ? this.#defaults() : {};
|
|
65
|
+
const plugins = [...await this.#plugins(), ...toArray(user.plugins)];
|
|
66
|
+
const alias = {
|
|
67
|
+
...defaults.alias,
|
|
68
|
+
...this.#alias(user.alias)
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
...defaults,
|
|
72
|
+
...user,
|
|
73
|
+
...plugins.length > 0 ? { plugins } : {},
|
|
74
|
+
...Object.keys(alias).length > 0 ? { alias } : {},
|
|
75
|
+
cwd: this.config.root,
|
|
76
|
+
customLogger: this.#logger()
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* What `stars.config` alone says the build is — the configuration a bot would otherwise write out by hand, taken
|
|
81
|
+
* from the ones the WolfStar bots already keep next to theirs:
|
|
82
|
+
*
|
|
83
|
+
* - every source file next to the entry (minus tests), emitted one-to-one (`unbundle`) so the stores keep
|
|
84
|
+
* loading pieces from `dist/commands` and friends at runtime;
|
|
85
|
+
* - ESM on `platform: 'node'`, with the extension `build.output` — and so `stars dev`, `package.json#main` and
|
|
86
|
+
* `node dist/…` — expects;
|
|
87
|
+
* - sourcemaps and treeshaking on, minification off: a bot is deployed, not shipped, so readable stack traces
|
|
88
|
+
* are worth more than bytes;
|
|
89
|
+
* - dependencies left in `node_modules` rather than bundled into the output;
|
|
90
|
+
* - Nuxt's own alias prefixes: `~` and `@` for the entry's directory, `~~` and `@@` for the project root;
|
|
91
|
+
* - no declaration files: nothing consumes a bot's `dist/`, and emitting them roughly doubles the build. A
|
|
92
|
+
* project that wants them sets `tsdown: { dts: true }`.
|
|
93
|
+
*
|
|
94
|
+
* Every one of these is overridable through the project's `tsdown` block.
|
|
95
|
+
*/
|
|
96
|
+
#defaults() {
|
|
97
|
+
const { root, entry, build } = this.config;
|
|
98
|
+
const source = dirname(entry);
|
|
99
|
+
const sourceDir = relative(root, source) || ".";
|
|
100
|
+
const extensions = TYPESCRIPT_EXTENSIONS.has(extname(entry)) ? "{ts,mts,cts}" : "{js,mjs,cjs}";
|
|
101
|
+
return {
|
|
102
|
+
config: false,
|
|
103
|
+
entry: [`${sourceDir}/**/*.${extensions}`, `!${sourceDir}/**/*.{test,spec}.${extensions}`],
|
|
104
|
+
alias: {
|
|
105
|
+
"~": source,
|
|
106
|
+
"@": source,
|
|
107
|
+
"~~": root,
|
|
108
|
+
"@@": root
|
|
109
|
+
},
|
|
110
|
+
format: "esm",
|
|
111
|
+
platform: "node",
|
|
112
|
+
unbundle: true,
|
|
113
|
+
outDir: build.outDir,
|
|
114
|
+
outExtensions: () => ({ js: extname(build.output) }),
|
|
115
|
+
sourcemap: true,
|
|
116
|
+
clean: true,
|
|
117
|
+
treeshake: true,
|
|
118
|
+
minify: false,
|
|
119
|
+
dts: false,
|
|
120
|
+
deps: { skipNodeModulesBundle: true },
|
|
121
|
+
...build.tsconfig === null ? {} : { tsconfig: build.tsconfig }
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The project's own aliases, with relative targets resolved against the project root the way every other path in
|
|
126
|
+
* `stars.config` is. Module ids (`preact/compat`) and absolute paths are passed through untouched.
|
|
127
|
+
*/
|
|
128
|
+
#alias(alias) {
|
|
129
|
+
if (alias === null || typeof alias !== "object" || Array.isArray(alias)) return {};
|
|
130
|
+
return Object.fromEntries(Object.entries(alias).map(([key, value]) => [key, typeof value === "string" && RELATIVE_PATH.test(value) ? resolve(this.config.root, value) : value]));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The auto imports transform, the way `nuxt dev` wires its own into the build rather than asking the project to.
|
|
134
|
+
* `stars dev`/`stars build` regenerate its declaration file (`stars prepare`) before the first build.
|
|
135
|
+
*/
|
|
136
|
+
async #plugins() {
|
|
137
|
+
if (!this.config.imports.enabled) return [];
|
|
138
|
+
const { autoImports } = await import("@wolfstar/http-framework/auto-imports");
|
|
139
|
+
const { dirs, presets, exclude, dts } = this.config.imports;
|
|
140
|
+
return [await autoImports({
|
|
141
|
+
root: this.config.root,
|
|
142
|
+
dirs,
|
|
143
|
+
presets,
|
|
144
|
+
exclude,
|
|
145
|
+
dts
|
|
146
|
+
})];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Watch mode reports through the same events a single build does. The project's own `hooks`/`onSuccess` still
|
|
150
|
+
* run: they are the reason a build is watched at all in some projects.
|
|
151
|
+
*/
|
|
152
|
+
#watchOptions(options) {
|
|
153
|
+
const hooks = options.hooks ?? {};
|
|
154
|
+
const prepare = hooks["build:prepare"];
|
|
155
|
+
const success = options.onSuccess;
|
|
156
|
+
return {
|
|
157
|
+
...options,
|
|
158
|
+
watch: true,
|
|
159
|
+
hooks: {
|
|
160
|
+
...hooks,
|
|
161
|
+
"build:prepare": async (...args) => {
|
|
162
|
+
if (typeof prepare === "function") await prepare(...args);
|
|
163
|
+
if (this.#startedAt === 0) this.#begin();
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
onSuccess: async (...args) => {
|
|
167
|
+
if (typeof success === "function") await success(...args);
|
|
168
|
+
this.#finish(null);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
#begin() {
|
|
173
|
+
this.#hadError = false;
|
|
174
|
+
this.#startedAt = performance.now();
|
|
175
|
+
this.emit("start");
|
|
176
|
+
}
|
|
177
|
+
#finish(message) {
|
|
178
|
+
const outcome = {
|
|
179
|
+
ok: message === null && !this.#hadError,
|
|
180
|
+
durationMs: Math.round(performance.now() - this.#startedAt),
|
|
181
|
+
message
|
|
182
|
+
};
|
|
183
|
+
this.#startedAt = 0;
|
|
184
|
+
this.emit(outcome.ok ? "success" : "failure", outcome);
|
|
185
|
+
return outcome;
|
|
186
|
+
}
|
|
187
|
+
#logger() {
|
|
188
|
+
const log = (level, args) => {
|
|
189
|
+
const text = args.filter((argument) => argument !== void 0 && argument !== null && argument !== false && argument !== "").map((argument) => argument instanceof Error ? argument.stack ?? argument.message : String(argument)).join(" ");
|
|
190
|
+
this.emit("log", level, text);
|
|
191
|
+
};
|
|
192
|
+
const warned = /* @__PURE__ */ new Set();
|
|
193
|
+
return {
|
|
194
|
+
level: "info",
|
|
195
|
+
info: (...args) => log("info", args),
|
|
196
|
+
warn: (...args) => log("warn", args),
|
|
197
|
+
warnOnce: (...args) => {
|
|
198
|
+
const key = args.map(String).join(" ");
|
|
199
|
+
if (warned.has(key)) return;
|
|
200
|
+
warned.add(key);
|
|
201
|
+
log("warn", args);
|
|
202
|
+
},
|
|
203
|
+
error: (...args) => {
|
|
204
|
+
this.#hadError = true;
|
|
205
|
+
log("error", args);
|
|
206
|
+
if (this.#startedAt !== 0 && this.#bundles.length > 0) this.#finish(errorSummary(args));
|
|
207
|
+
},
|
|
208
|
+
success: (...args) => log("success", args),
|
|
209
|
+
clearScreen: () => {}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
function toArray(value) {
|
|
214
|
+
if (value === void 0 || value === null || value === false) return [];
|
|
215
|
+
return Array.isArray(value) ? value : [value];
|
|
216
|
+
}
|
|
217
|
+
function errorSummary(args) {
|
|
218
|
+
const first = args[0];
|
|
219
|
+
if (first instanceof Error) return first.message.split("\n")[0] ?? "Build failed";
|
|
220
|
+
return String(first ?? "Build failed").split("\n")[0] ?? "Build failed";
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
//#endregion
|
|
224
|
+
export { TsdownBuilder };
|
|
225
|
+
//# sourceMappingURL=tsdown-BEFq2API.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tsdown-BEFq2API.js","names":["#load","#options","#begin","#bundles","#finish","#hadError","#watchOptions","#defaults","#plugins","#alias","#logger","#startedAt"],"sources":["../src/lib/builders/tsdown.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport { dirname, extname, relative, resolve } from 'node:path';\nimport type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { importFromProject } from '../project.js';\nimport type { Builder, BuilderEvents, BuildOutcome } from './types.js';\n\ntype TsdownModule = typeof import('tsdown');\ntype TsdownLogger = import('tsdown').Logger;\ntype TsdownBundle = import('tsdown').TsdownBundle;\ntype TsdownInlineConfig = NonNullable<Parameters<TsdownModule['build']>[0]>;\ntype TsdownOptions = Record<string, unknown>;\ntype AnyFunction = (...args: never[]) => unknown;\n\nconst INSTALL_HINT = \"Install it with `pnpm add -D tsdown`, or set `build.tool` to 'tsc' or 'none'.\";\nconst TYPESCRIPT_EXTENSIONS = new Set(['.ts', '.mts', '.cts']);\n/** `./src/lib` and `../shared` are paths; `preact/compat` is a module id and must be left alone. */\nconst RELATIVE_PATH = /^\\.\\.?[/\\\\]/;\n\n/**\n * Builds through the project's own `tsdown`, configured from `stars.config`.\n *\n * From `future.compatibilityVersion` 4 on this is the whole configuration: the entry's directory, `build.outDir` and\n * `build.tsconfig` produce the defaults a bot needs, the project's `tsdown` block is layered on top, and the auto\n * imports plugin is wired in — one configuration file instead of two. At 3 the project's own `tsdown.config.*` is\n * still loaded and everything here is merged over it by `tsdown` itself: scalars from `stars.config` win, and\n * `plugins` are appended rather than replaced.\n */\nexport class TsdownBuilder extends EventEmitter<BuilderEvents> implements Builder {\n\tpublic readonly tool = 'tsdown' as const;\n\t#bundles: TsdownBundle[] = [];\n\t#hadError = false;\n\t#startedAt = 0;\n\n\tpublic constructor(private readonly config: ResolvedStarsConfig) {\n\t\tsuper();\n\t}\n\n\tpublic async build(): Promise<BuildOutcome> {\n\t\tconst tsdown = await this.#load();\n\t\tconst options = await this.#options();\n\t\tthis.#begin();\n\n\t\ttry {\n\t\t\tthis.#bundles = await tsdown.build(options as TsdownInlineConfig);\n\t\t\treturn this.#finish(this.#hadError ? 'The build reported errors' : null);\n\t\t} catch (error) {\n\t\t\treturn this.#finish(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\tpublic async watch(): Promise<void> {\n\t\tconst tsdown = await this.#load();\n\t\tconst options = await this.#options();\n\n\t\tthis.#begin();\n\t\tthis.#bundles = await tsdown.build(this.#watchOptions(options) as TsdownInlineConfig);\n\t}\n\n\tpublic async close(): Promise<void> {\n\t\tconst bundles = this.#bundles;\n\t\tthis.#bundles = [];\n\t\tawait Promise.allSettled(bundles.map((bundle) => bundle[Symbol.asyncDispose]()));\n\t}\n\n\t#load(): Promise<TsdownModule> {\n\t\treturn importFromProject<TsdownModule>(this.config.root, 'tsdown', INSTALL_HINT);\n\t}\n\n\t/**\n\t * The options handed to `tsdown.build()`, with the project's own `tsdown` block already layered on top of the\n\t * defaults (and of the plugins `stars` contributes).\n\t */\n\tasync #options(): Promise<TsdownOptions> {\n\t\tconst user = this.config.tsdown as TsdownOptions;\n\t\t// A `tsdown.config.*` (or `package.json#tsdown`) is the project's own; `build.configFile` is `null` from\n\t\t// compatibility version 4 on, where loading one is a configuration error rather than a fallback.\n\t\tconst defaults = this.config.build.configFile === null ? this.#defaults() : {};\n\t\tconst plugins = [...(await this.#plugins()), ...toArray(user.plugins)];\n\t\t// `plugins` and `alias` are added to rather than replaced: a project declaring one of its own would\n\t\t// otherwise silently drop the auto imports transform, or every `~`/`@` import in its sources.\n\t\tconst alias = { ...(defaults.alias as object), ...this.#alias(user.alias) };\n\n\t\treturn {\n\t\t\t...defaults,\n\t\t\t...user,\n\t\t\t...(plugins.length > 0 ? { plugins } : {}),\n\t\t\t...(Object.keys(alias).length > 0 ? { alias } : {}),\n\t\t\t// Last, and so not overridable: these two are how the CLI talks to `tsdown` rather than build options —\n\t\t\t// the project root it resolves everything from, and the logger the dev panel reads its build channel off.\n\t\t\tcwd: this.config.root,\n\t\t\tcustomLogger: this.#logger()\n\t\t};\n\t}\n\n\t/**\n\t * What `stars.config` alone says the build is — the configuration a bot would otherwise write out by hand, taken\n\t * from the ones the WolfStar bots already keep next to theirs:\n\t *\n\t * - every source file next to the entry (minus tests), emitted one-to-one (`unbundle`) so the stores keep\n\t * loading pieces from `dist/commands` and friends at runtime;\n\t * - ESM on `platform: 'node'`, with the extension `build.output` — and so `stars dev`, `package.json#main` and\n\t * `node dist/…` — expects;\n\t * - sourcemaps and treeshaking on, minification off: a bot is deployed, not shipped, so readable stack traces\n\t * are worth more than bytes;\n\t * - dependencies left in `node_modules` rather than bundled into the output;\n\t * - Nuxt's own alias prefixes: `~` and `@` for the entry's directory, `~~` and `@@` for the project root;\n\t * - no declaration files: nothing consumes a bot's `dist/`, and emitting them roughly doubles the build. A\n\t * project that wants them sets `tsdown: { dts: true }`.\n\t *\n\t * Every one of these is overridable through the project's `tsdown` block.\n\t */\n\t#defaults(): TsdownOptions {\n\t\tconst { root, entry, build } = this.config;\n\t\tconst source = dirname(entry);\n\t\tconst sourceDir = relative(root, source) || '.';\n\t\tconst extensions = TYPESCRIPT_EXTENSIONS.has(extname(entry)) ? '{ts,mts,cts}' : '{js,mjs,cjs}';\n\n\t\treturn {\n\t\t\tconfig: false,\n\t\t\tentry: [`${sourceDir}/**/*.${extensions}`, `!${sourceDir}/**/*.{test,spec}.${extensions}`],\n\t\t\t// The prefixes Nuxt gives every project, pointing at the same two places its own do: the source\n\t\t\t// directory and the project root. A project's tsconfig needs the matching `paths` for them to type.\n\t\t\talias: { '~': source, '@': source, '~~': root, '@@': root },\n\t\t\tformat: 'esm',\n\t\t\tplatform: 'node',\n\t\t\tunbundle: true,\n\t\t\toutDir: build.outDir,\n\t\t\toutExtensions: () => ({ js: extname(build.output) }),\n\t\t\tsourcemap: true,\n\t\t\tclean: true,\n\t\t\ttreeshake: true,\n\t\t\tminify: false,\n\t\t\tdts: false,\n\t\t\tdeps: { skipNodeModulesBundle: true },\n\t\t\t...(build.tsconfig === null ? {} : { tsconfig: build.tsconfig })\n\t\t};\n\t}\n\n\t/**\n\t * The project's own aliases, with relative targets resolved against the project root the way every other path in\n\t * `stars.config` is. Module ids (`preact/compat`) and absolute paths are passed through untouched.\n\t */\n\t#alias(alias: unknown): Record<string, unknown> {\n\t\tif (alias === null || typeof alias !== 'object' || Array.isArray(alias)) return {};\n\n\t\treturn Object.fromEntries(\n\t\t\tObject.entries(alias as Record<string, unknown>).map(([key, value]) => [\n\t\t\t\tkey,\n\t\t\t\ttypeof value === 'string' && RELATIVE_PATH.test(value) ? resolve(this.config.root, value) : value\n\t\t\t])\n\t\t);\n\t}\n\n\t/**\n\t * The auto imports transform, the way `nuxt dev` wires its own into the build rather than asking the project to.\n\t * `stars dev`/`stars build` regenerate its declaration file (`stars prepare`) before the first build.\n\t */\n\tasync #plugins(): Promise<unknown[]> {\n\t\tif (!this.config.imports.enabled) return [];\n\n\t\tconst { autoImports } = await import('@wolfstar/http-framework/auto-imports');\n\t\tconst { dirs, presets, exclude, dts } = this.config.imports;\n\t\treturn [await autoImports({ root: this.config.root, dirs, presets, exclude, dts })];\n\t}\n\n\t/**\n\t * Watch mode reports through the same events a single build does. The project's own `hooks`/`onSuccess` still\n\t * run: they are the reason a build is watched at all in some projects.\n\t */\n\t#watchOptions(options: TsdownOptions): TsdownOptions {\n\t\tconst hooks = (options.hooks ?? {}) as Record<string, unknown>;\n\t\tconst prepare = hooks['build:prepare'];\n\t\tconst success = options.onSuccess;\n\n\t\treturn {\n\t\t\t...options,\n\t\t\twatch: true,\n\t\t\thooks: {\n\t\t\t\t...hooks,\n\t\t\t\t'build:prepare': async (...args: never[]) => {\n\t\t\t\t\tif (typeof prepare === 'function') await (prepare as AnyFunction)(...args);\n\t\t\t\t\tif (this.#startedAt === 0) this.#begin();\n\t\t\t\t}\n\t\t\t},\n\t\t\tonSuccess: async (...args: never[]) => {\n\t\t\t\tif (typeof success === 'function') await (success as AnyFunction)(...args);\n\t\t\t\tthis.#finish(null);\n\t\t\t}\n\t\t};\n\t}\n\n\t#begin(): void {\n\t\tthis.#hadError = false;\n\t\tthis.#startedAt = performance.now();\n\t\tthis.emit('start');\n\t}\n\n\t#finish(message: string | null): BuildOutcome {\n\t\tconst outcome: BuildOutcome = {\n\t\t\tok: message === null && !this.#hadError,\n\t\t\tdurationMs: Math.round(performance.now() - this.#startedAt),\n\t\t\tmessage\n\t\t};\n\t\tthis.#startedAt = 0;\n\t\tthis.emit(outcome.ok ? 'success' : 'failure', outcome);\n\t\treturn outcome;\n\t}\n\n\t#logger(): TsdownLogger {\n\t\tconst log = (level: 'info' | 'warn' | 'error' | 'success', args: unknown[]) => {\n\t\t\t// tsdown passes its (possibly undefined) name label and other blanks as separate arguments.\n\t\t\tconst text = args\n\t\t\t\t.filter((argument) => argument !== undefined && argument !== null && argument !== false && argument !== '')\n\t\t\t\t.map((argument) => (argument instanceof Error ? (argument.stack ?? argument.message) : String(argument)))\n\t\t\t\t.join(' ');\n\t\t\tthis.emit('log', level, text);\n\t\t};\n\n\t\tconst warned = new Set<string>();\n\t\treturn {\n\t\t\tlevel: 'info',\n\t\t\tinfo: (...args) => log('info', args),\n\t\t\twarn: (...args) => log('warn', args),\n\t\t\twarnOnce: (...args) => {\n\t\t\t\tconst key = args.map(String).join(' ');\n\t\t\t\tif (warned.has(key)) return;\n\t\t\t\twarned.add(key);\n\t\t\t\tlog('warn', args);\n\t\t\t},\n\t\t\terror: (...args) => {\n\t\t\t\tthis.#hadError = true;\n\t\t\t\tlog('error', args);\n\t\t\t\t// In watch mode tsdown never calls `onSuccess` after an error, report the failure now.\n\t\t\t\tif (this.#startedAt !== 0 && this.#bundles.length > 0) this.#finish(errorSummary(args));\n\t\t\t},\n\t\t\tsuccess: (...args) => log('success', args),\n\t\t\tclearScreen: () => {}\n\t\t};\n\t}\n}\n\nfunction toArray(value: unknown): unknown[] {\n\tif (value === undefined || value === null || value === false) return [];\n\treturn Array.isArray(value) ? value : [value];\n}\n\nfunction errorSummary(args: unknown[]): string {\n\tconst first = args[0];\n\tif (first instanceof Error) return first.message.split('\\n')[0] ?? 'Build failed';\n\treturn String(first ?? 'Build failed').split('\\n')[0] ?? 'Build failed';\n}\n"],"mappings":"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"}
|
|
@@ -23,11 +23,7 @@ var ViteBuilder = class extends EventEmitter {
|
|
|
23
23
|
const vite = await this.#load();
|
|
24
24
|
this.#begin();
|
|
25
25
|
try {
|
|
26
|
-
await vite.build(
|
|
27
|
-
root: this.config.root,
|
|
28
|
-
logLevel: "warn",
|
|
29
|
-
customLogger: this.#logger(vite)
|
|
30
|
-
});
|
|
26
|
+
await vite.build(this.#options(vite));
|
|
31
27
|
return this.#finish(null);
|
|
32
28
|
} catch (error) {
|
|
33
29
|
return this.#finish(error instanceof Error ? error.message : String(error));
|
|
@@ -36,11 +32,13 @@ var ViteBuilder = class extends EventEmitter {
|
|
|
36
32
|
async watch() {
|
|
37
33
|
const vite = await this.#load();
|
|
38
34
|
this.#begin();
|
|
35
|
+
const options = this.#options(vite);
|
|
39
36
|
const result = await vite.build({
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
...options,
|
|
38
|
+
build: {
|
|
39
|
+
...options?.build,
|
|
40
|
+
watch: {}
|
|
41
|
+
}
|
|
44
42
|
});
|
|
45
43
|
this.#watcher = result;
|
|
46
44
|
result.on("event", ((event) => {
|
|
@@ -63,6 +61,18 @@ var ViteBuilder = class extends EventEmitter {
|
|
|
63
61
|
async #load() {
|
|
64
62
|
return importFromProject(this.config.root, "vite", INSTALL_HINT);
|
|
65
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* The inline config Vite merges into the project's own `vite.config.*`, with the `vite` block of `stars.config`
|
|
66
|
+
* layered on top — the way `vite: {}` in a Nuxt config is merged into Nuxt's own.
|
|
67
|
+
*/
|
|
68
|
+
#options(vite) {
|
|
69
|
+
return {
|
|
70
|
+
root: this.config.root,
|
|
71
|
+
logLevel: "warn",
|
|
72
|
+
customLogger: this.#logger(vite),
|
|
73
|
+
...this.config.vite
|
|
74
|
+
};
|
|
75
|
+
}
|
|
66
76
|
#logger(vite) {
|
|
67
77
|
return {
|
|
68
78
|
...vite.createLogger("warn", { allowClearScreen: false }),
|
|
@@ -90,4 +100,4 @@ var ViteBuilder = class extends EventEmitter {
|
|
|
90
100
|
|
|
91
101
|
//#endregion
|
|
92
102
|
export { ViteBuilder };
|
|
93
|
-
//# sourceMappingURL=vite-
|
|
103
|
+
//# sourceMappingURL=vite-27ZbreDz.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-27ZbreDz.js","names":["#load","#begin","#options","#finish","#watcher","#logger","#startedAt"],"sources":["../src/lib/builders/vite.ts"],"sourcesContent":["import type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { EventEmitter } from 'node:events';\nimport { importFromProject } from '../project.js';\nimport type { Builder, BuilderEvents, BuildOutcome } from './types.js';\n\ntype ViteModule = typeof import('vite');\ntype RollupWatcher =\n\tAwaited<ReturnType<ViteModule['build']>> extends infer Result\n\t\t? Result extends { close: () => Promise<void>; on: (...args: never[]) => unknown }\n\t\t\t? Result\n\t\t\t: never\n\t\t: never;\n\nconst INSTALL_HINT = \"Install it with `pnpm add -D vite`, or set `build.tool` to 'tsdown', 'tsc' or 'none'.\";\n\n/**\n * Builds through the project's own `vite`, honouring its `vite.config.*`.\n *\n * Guarded by `experimental.enableVite`: Vite replaces `tsdown` as the bundler and, with the framework's Fetch\n * adapter, the `node:http` listener too, so a project can serve its interactions endpoint the same way it serves\n * everything else.\n */\nexport class ViteBuilder extends EventEmitter<BuilderEvents> implements Builder {\n\tpublic readonly tool = 'vite' as const;\n\t#watcher: RollupWatcher | null = null;\n\t#startedAt = 0;\n\n\tpublic constructor(private readonly config: ResolvedStarsConfig) {\n\t\tsuper();\n\t}\n\n\tpublic async build(): Promise<BuildOutcome> {\n\t\tconst vite = await this.#load();\n\t\tthis.#begin();\n\n\t\ttry {\n\t\t\tawait vite.build(this.#options(vite));\n\t\t\treturn this.#finish(null);\n\t\t} catch (error) {\n\t\t\treturn this.#finish(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\tpublic async watch(): Promise<void> {\n\t\tconst vite = await this.#load();\n\t\tthis.#begin();\n\n\t\t// `build.watch` makes Vite return a rollup watcher instead of resolving once, which is what keeps the bot\n\t\t// rebuilding on change; every rebuild reports through the same events the other builders use.\n\t\tconst options = this.#options(vite);\n\t\tconst result = (await vite.build({ ...options, build: { ...options?.build, watch: {} } })) as unknown as RollupWatcher;\n\n\t\tthis.#watcher = result;\n\t\tresult.on(\n\t\t\t'event' as never,\n\t\t\t((event: { code: string; error?: Error }) => {\n\t\t\t\tswitch (event.code) {\n\t\t\t\t\tcase 'BUNDLE_START':\n\t\t\t\t\t\treturn this.#begin();\n\t\t\t\t\tcase 'BUNDLE_END':\n\t\t\t\t\t\treturn void this.#finish(null);\n\t\t\t\t\tcase 'ERROR':\n\t\t\t\t\t\treturn void this.#finish(event.error?.message ?? 'The build reported errors');\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}) as never\n\t\t);\n\t}\n\n\tpublic async close(): Promise<void> {\n\t\tconst watcher = this.#watcher;\n\t\tthis.#watcher = null;\n\t\tawait watcher?.close();\n\t}\n\n\tasync #load(): Promise<ViteModule> {\n\t\treturn importFromProject<ViteModule>(this.config.root, 'vite', INSTALL_HINT);\n\t}\n\n\t/**\n\t * The inline config Vite merges into the project's own `vite.config.*`, with the `vite` block of `stars.config`\n\t * layered on top — the way `vite: {}` in a Nuxt config is merged into Nuxt's own.\n\t */\n\t#options(vite: ViteModule): Parameters<ViteModule['build']>[0] {\n\t\treturn {\n\t\t\troot: this.config.root,\n\t\t\tlogLevel: 'warn',\n\t\t\tcustomLogger: this.#logger(vite),\n\t\t\t...this.config.vite\n\t\t} as Parameters<ViteModule['build']>[0];\n\t}\n\n\t#logger(vite: ViteModule) {\n\t\tconst logger = vite.createLogger('warn', { allowClearScreen: false });\n\t\treturn {\n\t\t\t...logger,\n\t\t\tinfo: (message: string) => this.emit('log', 'info', message),\n\t\t\twarn: (message: string) => this.emit('log', 'warn', message),\n\t\t\twarnOnce: (message: string) => this.emit('log', 'warn', message),\n\t\t\terror: (message: string) => this.emit('log', 'error', message)\n\t\t};\n\t}\n\n\t#begin(): void {\n\t\tthis.#startedAt = performance.now();\n\t\tthis.emit('start');\n\t}\n\n\t#finish(message: string | null): BuildOutcome {\n\t\tconst outcome: BuildOutcome = { ok: message === null, durationMs: Math.round(performance.now() - this.#startedAt), message };\n\t\tthis.#startedAt = 0;\n\t\tthis.emit(outcome.ok ? 'success' : 'failure', outcome);\n\t\treturn outcome;\n\t}\n}\n"],"mappings":";;;;AAaA,MAAM,eAAe;;;;;;;;AASrB,IAAa,cAAb,cAAiC,aAA+C;CAK3C;CAJpC,AAAgB,OAAO;CACvB,WAAiC;CACjC,aAAa;CAEb,AAAO,YAAY,AAAiB,QAA6B;EAChE,MAAM;EAD6B;CAEpC;CAEA,MAAa,QAA+B;EAC3C,MAAM,OAAO,MAAM,KAAKA,MAAM;EAC9B,KAAKC,OAAO;EAEZ,IAAI;GACH,MAAM,KAAK,MAAM,KAAKC,SAAS,IAAI,CAAC;GACpC,OAAO,KAAKC,QAAQ,IAAI;EACzB,SAAS,OAAO;GACf,OAAO,KAAKA,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;EAC3E;CACD;CAEA,MAAa,QAAuB;EACnC,MAAM,OAAO,MAAM,KAAKH,MAAM;EAC9B,KAAKC,OAAO;EAIZ,MAAM,UAAU,KAAKC,SAAS,IAAI;EAClC,MAAM,SAAU,MAAM,KAAK,MAAM;GAAE,GAAG;GAAS,OAAO;IAAE,GAAG,SAAS;IAAO,OAAO,CAAC;GAAE;EAAE,CAAC;EAExF,KAAKE,WAAW;EAChB,OAAO,GACN,WACE,UAA2C;GAC5C,QAAQ,MAAM,MAAd;IACC,KAAK,gBACJ,OAAO,KAAKH,OAAO;IACpB,KAAK;KACG,AAAK,KAAKE,QAAQ,IAAI;KAA7B;IACD,KAAK;KACG,AAAK,KAAKA,QAAQ,MAAM,OAAO,WAAW,2BAA2B;KAA5E;GAGF;EACD,EACD;CACD;CAEA,MAAa,QAAuB;EACnC,MAAM,UAAU,KAAKC;EACrB,KAAKA,WAAW;EAChB,MAAM,SAAS,MAAM;CACtB;CAEA,MAAMJ,QAA6B;EAClC,OAAO,kBAA8B,KAAK,OAAO,MAAM,QAAQ,YAAY;CAC5E;;;;;CAMA,SAAS,MAAsD;EAC9D,OAAO;GACN,MAAM,KAAK,OAAO;GAClB,UAAU;GACV,cAAc,KAAKK,QAAQ,IAAI;GAC/B,GAAG,KAAK,OAAO;EAChB;CACD;CAEA,QAAQ,MAAkB;EAEzB,OAAO;GACN,GAFc,KAAK,aAAa,QAAQ,EAAE,kBAAkB,MAAM,CAE1D;GACR,OAAO,YAAoB,KAAK,KAAK,OAAO,QAAQ,OAAO;GAC3D,OAAO,YAAoB,KAAK,KAAK,OAAO,QAAQ,OAAO;GAC3D,WAAW,YAAoB,KAAK,KAAK,OAAO,QAAQ,OAAO;GAC/D,QAAQ,YAAoB,KAAK,KAAK,OAAO,SAAS,OAAO;EAC9D;CACD;CAEA,SAAe;EACd,KAAKC,aAAa,YAAY,IAAI;EAClC,KAAK,KAAK,OAAO;CAClB;CAEA,QAAQ,SAAsC;EAC7C,MAAM,UAAwB;GAAE,IAAI,YAAY;GAAM,YAAY,KAAK,MAAM,YAAY,IAAI,IAAI,KAAKA,UAAU;GAAG;EAAQ;EAC3H,KAAKA,aAAa;EAClB,KAAK,KAAK,QAAQ,KAAK,YAAY,WAAW,OAAO;EACrD,OAAO;CACR;AACD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wolfstar/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0-next-20260905171401",
|
|
4
4
|
"description": "The stars command line interface for @wolfstar/http-framework projects: typed configuration, dev server, build and project info",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"colorette": "^2.0.20",
|
|
56
56
|
"ink": "^7.1.1",
|
|
57
57
|
"react": "^19.2.8",
|
|
58
|
-
"@wolfstar/http-framework": "^3.
|
|
58
|
+
"@wolfstar/http-framework": "^3.5.0-next-20260905171401"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@types/react": "^19.2.18",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"info-B-8rAIn6.js","names":[],"sources":["../src/lib/tasks/info.ts"],"sourcesContent":["import { createColors } from 'colorette';\nimport { arch, platform } from 'node:os';\nimport { relative } from 'node:path';\nimport type { ProjectArgs } from '../args.js';\nimport { resolveCwd } from '../args.js';\nimport { loadStarsConfig } from '@wolfstar/http-framework/config';\nimport { displayPath, type ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { resolveOutputMode, shouldUseColor } from '../output-mode.js';\nimport { findInstalledVersion } from '../project.js';\nimport { readOwnPackageJson } from '../version.js';\n\nexport interface InfoOptions extends ProjectArgs {\n\tjson?: boolean;\n\tstdout?: NodeJS.WritableStream;\n}\n\nexport interface ProjectInfo {\n\tcli: { version: string; node: string; platform: string; outputMode: 'tui' | 'plain' };\n\tproject: {\n\t\tcwd: string;\n\t\troot: string;\n\t\tconfigFile: string | null;\n\t\tname: string | null;\n\t\tversion: string | null;\n\t\tentry: string;\n\t\tframeworkVersion: string | null;\n\t};\n\tbuild: ResolvedStarsConfig['build'];\n\tdev: ResolvedStarsConfig['dev'];\n\tcodegen: ResolvedStarsConfig['codegen'];\n\timports: ResolvedStarsConfig['imports'];\n\texperimental: ResolvedStarsConfig['experimental'];\n}\n\nexport function collectInfo(config: ResolvedStarsConfig): ProjectInfo {\n\treturn {\n\t\tcli: {\n\t\t\tversion: readOwnPackageJson().version,\n\t\t\tnode: process.version,\n\t\t\tplatform: `${platform()} ${arch()}`,\n\t\t\toutputMode: resolveOutputMode()\n\t\t},\n\t\tproject: {\n\t\t\tcwd: config.cwd,\n\t\t\troot: config.root,\n\t\t\tconfigFile: config.configFile,\n\t\t\tname: config.packageJson?.name ?? null,\n\t\t\tversion: config.packageJson?.version ?? null,\n\t\t\tentry: config.entry,\n\t\t\tframeworkVersion: findInstalledVersion(config.root, '@wolfstar/http-framework')\n\t\t},\n\t\tbuild: config.build,\n\t\tdev: config.dev,\n\t\tcodegen: config.codegen,\n\t\timports: config.imports,\n\t\texperimental: config.experimental\n\t};\n}\n\nexport function formatInfo(info: ProjectInfo, useColor: boolean): string {\n\tconst colors = createColors({ useColor });\n\tconst { root } = info.project;\n\tconst show = (path: string | null) => (path === null ? colors.dim('none') : displayPath(root, path));\n\tconst row = (label: string, value: string) => ` ${colors.dim(label.padEnd(12))}${value}`;\n\tconst section = (title: string, rows: string[]) => [colors.bold(title), ...rows].join('\\n');\n\n\treturn [\n\t\tsection(`stars ${colors.green(`v${info.cli.version}`)}`, [\n\t\t\trow('node', info.cli.node),\n\t\t\trow('platform', info.cli.platform),\n\t\t\trow('output', info.cli.outputMode),\n\t\t\trow(\n\t\t\t\t'framework',\n\t\t\t\tinfo.project.frameworkVersion\n\t\t\t\t\t? `@wolfstar/http-framework v${info.project.frameworkVersion}`\n\t\t\t\t\t: colors.yellow('@wolfstar/http-framework not installed')\n\t\t\t)\n\t\t]),\n\t\tsection('Project', [\n\t\t\trow(\n\t\t\t\t'name',\n\t\t\t\tinfo.project.name\n\t\t\t\t\t? `${info.project.name}${info.project.version ? colors.dim(` v${info.project.version}`) : ''}`\n\t\t\t\t\t: colors.dim('unnamed')\n\t\t\t),\n\t\t\trow('root', root),\n\t\t\trow(\n\t\t\t\t'config',\n\t\t\t\tinfo.project.configFile ? relative(root, info.project.configFile) || info.project.configFile : colors.dim('none (defaults)')\n\t\t\t),\n\t\t\trow('entry', show(info.project.entry))\n\t\t]),\n\t\tsection('Build', [\n\t\t\trow('tool', info.build.tool),\n\t\t\trow('outDir', show(info.build.outDir)),\n\t\t\trow('tsconfig', show(info.build.tsconfig)),\n\t\t\trow('runs', show(info.build.output))\n\t\t]),\n\t\tsection('Dev', [\n\t\t\trow('watch', info.dev.watch.map(show).join(', ')),\n\t\t\trow('debounce', `${info.dev.debounce}ms`),\n\t\t\trow('node args', info.dev.nodeArgs.join(' ') || colors.dim('none')),\n\t\t\trow('args', info.dev.args.join(' ') || colors.dim('none')),\n\t\t\trow('url', info.dev.url ?? colors.dim('unknown, set dev.url or HTTP_PORT')),\n\t\t\trow('health', info.dev.health ?? colors.dim('none')),\n\t\t\trow(\n\t\t\t\t'typecheck',\n\t\t\t\tinfo.dev.typecheck.enabled ? `${info.dev.typecheck.checker} → ${show(info.dev.typecheck.tsconfig)}` : colors.dim('disabled')\n\t\t\t),\n\t\t\trow('tunnel', describeTunnel(info.dev.tunnel, colors)),\n\t\t\trow('log file', info.dev.logFile ? show(info.dev.logFile) : colors.dim('disabled'))\n\t\t]),\n\t\tsection('Codegen', [\n\t\t\trow('i18n', info.codegen.i18n ? `${show(info.codegen.i18n.locales)} → ${show(info.codegen.i18n.output)}` : colors.dim('disabled'))\n\t\t]),\n\t\tsection('Experimental', [\n\t\t\trow('vite', flag(info.experimental.enableVite, colors)),\n\t\t\trow('nitro', flag(info.experimental.enableNitro, colors)),\n\t\t\trow('external', flag(info.experimental.enableExternalVite, colors))\n\t\t]),\n\t\tsection('Imports', [\n\t\t\trow('auto', info.imports.enabled ? colors.green('enabled') : colors.dim('disabled')),\n\t\t\trow('dirs', info.imports.dirs.map(show).join(', ')),\n\t\t\trow('presets', info.imports.presets.join(', ') || colors.dim('none')),\n\t\t\trow('dts', show(info.imports.dts))\n\t\t])\n\t].join('\\n\\n');\n}\n\nfunction flag(enabled: boolean, colors: ReturnType<typeof createColors>): string {\n\treturn enabled ? colors.green('enabled') : colors.dim('disabled');\n}\n\nfunction describeTunnel(tunnel: ResolvedStarsConfig['dev']['tunnel'], colors: ReturnType<typeof createColors>): string {\n\tswitch (tunnel.mode) {\n\t\tcase 'quick':\n\t\t\treturn 'cloudflared quick tunnel';\n\t\tcase 'url':\n\t\t\treturn tunnel.url;\n\t\tdefault:\n\t\t\treturn colors.dim('disabled');\n\t}\n}\n\nexport async function runInfo(options: InfoOptions): Promise<void> {\n\tconst stdout = options.stdout ?? process.stdout;\n\tconst config = await loadStarsConfig({ cwd: resolveCwd(options), configFile: options.config });\n\tconst info = collectInfo(config);\n\tstdout.write(`${options.json ? JSON.stringify(info, null, 2) : formatInfo(info, shouldUseColor())}\\n`);\n}\n"],"mappings":"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"}
|
package/dist/tsdown-DlAs0rSI.js
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { n as importFromProject } from "./project-DHF3qdm-.js";
|
|
2
|
-
import { EventEmitter } from "node:events";
|
|
3
|
-
|
|
4
|
-
//#region src/lib/builders/tsdown.ts
|
|
5
|
-
const INSTALL_HINT = "Install it with `pnpm add -D tsdown`, or set `build.tool` to 'tsc' or 'none'.";
|
|
6
|
-
/**
|
|
7
|
-
* Builds through the project's own `tsdown`, honouring its `tsdown.config.*`.
|
|
8
|
-
*/
|
|
9
|
-
var TsdownBuilder = class extends EventEmitter {
|
|
10
|
-
config;
|
|
11
|
-
tool = "tsdown";
|
|
12
|
-
#bundles = [];
|
|
13
|
-
#hadError = false;
|
|
14
|
-
#startedAt = 0;
|
|
15
|
-
constructor(config) {
|
|
16
|
-
super();
|
|
17
|
-
this.config = config;
|
|
18
|
-
}
|
|
19
|
-
async build() {
|
|
20
|
-
const tsdown = await this.#load();
|
|
21
|
-
this.#begin();
|
|
22
|
-
try {
|
|
23
|
-
this.#bundles = await tsdown.build({
|
|
24
|
-
cwd: this.config.root,
|
|
25
|
-
customLogger: this.#logger()
|
|
26
|
-
});
|
|
27
|
-
return this.#finish(this.#hadError ? "The build reported errors" : null);
|
|
28
|
-
} catch (error) {
|
|
29
|
-
return this.#finish(error instanceof Error ? error.message : String(error));
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
async watch() {
|
|
33
|
-
const tsdown = await this.#load();
|
|
34
|
-
this.#begin();
|
|
35
|
-
this.#bundles = await tsdown.build({
|
|
36
|
-
cwd: this.config.root,
|
|
37
|
-
watch: true,
|
|
38
|
-
customLogger: this.#logger(),
|
|
39
|
-
hooks: { "build:prepare": () => {
|
|
40
|
-
if (this.#startedAt === 0) this.#begin();
|
|
41
|
-
} },
|
|
42
|
-
onSuccess: () => {
|
|
43
|
-
this.#finish(null);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
async close() {
|
|
48
|
-
const bundles = this.#bundles;
|
|
49
|
-
this.#bundles = [];
|
|
50
|
-
await Promise.allSettled(bundles.map((bundle) => bundle[Symbol.asyncDispose]()));
|
|
51
|
-
}
|
|
52
|
-
#load() {
|
|
53
|
-
return importFromProject(this.config.root, "tsdown", INSTALL_HINT);
|
|
54
|
-
}
|
|
55
|
-
#begin() {
|
|
56
|
-
this.#hadError = false;
|
|
57
|
-
this.#startedAt = performance.now();
|
|
58
|
-
this.emit("start");
|
|
59
|
-
}
|
|
60
|
-
#finish(message) {
|
|
61
|
-
const outcome = {
|
|
62
|
-
ok: message === null && !this.#hadError,
|
|
63
|
-
durationMs: Math.round(performance.now() - this.#startedAt),
|
|
64
|
-
message
|
|
65
|
-
};
|
|
66
|
-
this.#startedAt = 0;
|
|
67
|
-
this.emit(outcome.ok ? "success" : "failure", outcome);
|
|
68
|
-
return outcome;
|
|
69
|
-
}
|
|
70
|
-
#logger() {
|
|
71
|
-
const log = (level, args) => {
|
|
72
|
-
const text = args.filter((argument) => argument !== void 0 && argument !== null && argument !== false && argument !== "").map((argument) => argument instanceof Error ? argument.stack ?? argument.message : String(argument)).join(" ");
|
|
73
|
-
this.emit("log", level, text);
|
|
74
|
-
};
|
|
75
|
-
const warned = /* @__PURE__ */ new Set();
|
|
76
|
-
return {
|
|
77
|
-
level: "info",
|
|
78
|
-
info: (...args) => log("info", args),
|
|
79
|
-
warn: (...args) => log("warn", args),
|
|
80
|
-
warnOnce: (...args) => {
|
|
81
|
-
const key = args.map(String).join(" ");
|
|
82
|
-
if (warned.has(key)) return;
|
|
83
|
-
warned.add(key);
|
|
84
|
-
log("warn", args);
|
|
85
|
-
},
|
|
86
|
-
error: (...args) => {
|
|
87
|
-
this.#hadError = true;
|
|
88
|
-
log("error", args);
|
|
89
|
-
if (this.#startedAt !== 0 && this.#bundles.length > 0) this.#finish(errorSummary(args));
|
|
90
|
-
},
|
|
91
|
-
success: (...args) => log("success", args),
|
|
92
|
-
clearScreen: () => {}
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
function errorSummary(args) {
|
|
97
|
-
const first = args[0];
|
|
98
|
-
if (first instanceof Error) return first.message.split("\n")[0] ?? "Build failed";
|
|
99
|
-
return String(first ?? "Build failed").split("\n")[0] ?? "Build failed";
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
//#endregion
|
|
103
|
-
export { TsdownBuilder };
|
|
104
|
-
//# sourceMappingURL=tsdown-DlAs0rSI.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tsdown-DlAs0rSI.js","names":["#load","#begin","#bundles","#logger","#finish","#hadError","#startedAt"],"sources":["../src/lib/builders/tsdown.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { importFromProject } from '../project.js';\nimport type { Builder, BuilderEvents, BuildOutcome } from './types.js';\n\ntype TsdownModule = typeof import('tsdown');\ntype TsdownLogger = import('tsdown').Logger;\ntype TsdownBundle = import('tsdown').TsdownBundle;\n\nconst INSTALL_HINT = \"Install it with `pnpm add -D tsdown`, or set `build.tool` to 'tsc' or 'none'.\";\n\n/**\n * Builds through the project's own `tsdown`, honouring its `tsdown.config.*`.\n */\nexport class TsdownBuilder extends EventEmitter<BuilderEvents> implements Builder {\n\tpublic readonly tool = 'tsdown' as const;\n\t#bundles: TsdownBundle[] = [];\n\t#hadError = false;\n\t#startedAt = 0;\n\n\tpublic constructor(private readonly config: ResolvedStarsConfig) {\n\t\tsuper();\n\t}\n\n\tpublic async build(): Promise<BuildOutcome> {\n\t\tconst tsdown = await this.#load();\n\t\tthis.#begin();\n\n\t\ttry {\n\t\t\tthis.#bundles = await tsdown.build({ cwd: this.config.root, customLogger: this.#logger() });\n\t\t\treturn this.#finish(this.#hadError ? 'The build reported errors' : null);\n\t\t} catch (error) {\n\t\t\treturn this.#finish(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\tpublic async watch(): Promise<void> {\n\t\tconst tsdown = await this.#load();\n\n\t\tthis.#begin();\n\t\tthis.#bundles = await tsdown.build({\n\t\t\tcwd: this.config.root,\n\t\t\twatch: true,\n\t\t\tcustomLogger: this.#logger(),\n\t\t\thooks: {\n\t\t\t\t'build:prepare': () => {\n\t\t\t\t\tif (this.#startedAt === 0) this.#begin();\n\t\t\t\t}\n\t\t\t},\n\t\t\tonSuccess: () => {\n\t\t\t\tthis.#finish(null);\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic async close(): Promise<void> {\n\t\tconst bundles = this.#bundles;\n\t\tthis.#bundles = [];\n\t\tawait Promise.allSettled(bundles.map((bundle) => bundle[Symbol.asyncDispose]()));\n\t}\n\n\t#load(): Promise<TsdownModule> {\n\t\treturn importFromProject<TsdownModule>(this.config.root, 'tsdown', INSTALL_HINT);\n\t}\n\n\t#begin(): void {\n\t\tthis.#hadError = false;\n\t\tthis.#startedAt = performance.now();\n\t\tthis.emit('start');\n\t}\n\n\t#finish(message: string | null): BuildOutcome {\n\t\tconst outcome: BuildOutcome = {\n\t\t\tok: message === null && !this.#hadError,\n\t\t\tdurationMs: Math.round(performance.now() - this.#startedAt),\n\t\t\tmessage\n\t\t};\n\t\tthis.#startedAt = 0;\n\t\tthis.emit(outcome.ok ? 'success' : 'failure', outcome);\n\t\treturn outcome;\n\t}\n\n\t#logger(): TsdownLogger {\n\t\tconst log = (level: 'info' | 'warn' | 'error' | 'success', args: unknown[]) => {\n\t\t\t// tsdown passes its (possibly undefined) name label and other blanks as separate arguments.\n\t\t\tconst text = args\n\t\t\t\t.filter((argument) => argument !== undefined && argument !== null && argument !== false && argument !== '')\n\t\t\t\t.map((argument) => (argument instanceof Error ? (argument.stack ?? argument.message) : String(argument)))\n\t\t\t\t.join(' ');\n\t\t\tthis.emit('log', level, text);\n\t\t};\n\n\t\tconst warned = new Set<string>();\n\t\treturn {\n\t\t\tlevel: 'info',\n\t\t\tinfo: (...args) => log('info', args),\n\t\t\twarn: (...args) => log('warn', args),\n\t\t\twarnOnce: (...args) => {\n\t\t\t\tconst key = args.map(String).join(' ');\n\t\t\t\tif (warned.has(key)) return;\n\t\t\t\twarned.add(key);\n\t\t\t\tlog('warn', args);\n\t\t\t},\n\t\t\terror: (...args) => {\n\t\t\t\tthis.#hadError = true;\n\t\t\t\tlog('error', args);\n\t\t\t\t// In watch mode tsdown never calls `onSuccess` after an error, report the failure now.\n\t\t\t\tif (this.#startedAt !== 0 && this.#bundles.length > 0) this.#finish(errorSummary(args));\n\t\t\t},\n\t\t\tsuccess: (...args) => log('success', args),\n\t\t\tclearScreen: () => {}\n\t\t};\n\t}\n}\n\nfunction errorSummary(args: unknown[]): string {\n\tconst first = args[0];\n\tif (first instanceof Error) return first.message.split('\\n')[0] ?? 'Build failed';\n\treturn String(first ?? 'Build failed').split('\\n')[0] ?? 'Build failed';\n}\n"],"mappings":";;;;AASA,MAAM,eAAe;;;;AAKrB,IAAa,gBAAb,cAAmC,aAA+C;CAM7C;CALpC,AAAgB,OAAO;CACvB,WAA2B,CAAC;CAC5B,YAAY;CACZ,aAAa;CAEb,AAAO,YAAY,AAAiB,QAA6B;EAChE,MAAM;EAD6B;CAEpC;CAEA,MAAa,QAA+B;EAC3C,MAAM,SAAS,MAAM,KAAKA,MAAM;EAChC,KAAKC,OAAO;EAEZ,IAAI;GACH,KAAKC,WAAW,MAAM,OAAO,MAAM;IAAE,KAAK,KAAK,OAAO;IAAM,cAAc,KAAKC,QAAQ;GAAE,CAAC;GAC1F,OAAO,KAAKC,QAAQ,KAAKC,YAAY,8BAA8B,IAAI;EACxE,SAAS,OAAO;GACf,OAAO,KAAKD,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;EAC3E;CACD;CAEA,MAAa,QAAuB;EACnC,MAAM,SAAS,MAAM,KAAKJ,MAAM;EAEhC,KAAKC,OAAO;EACZ,KAAKC,WAAW,MAAM,OAAO,MAAM;GAClC,KAAK,KAAK,OAAO;GACjB,OAAO;GACP,cAAc,KAAKC,QAAQ;GAC3B,OAAO,EACN,uBAAuB;IACtB,IAAI,KAAKG,eAAe,GAAG,KAAKL,OAAO;GACxC,EACD;GACA,iBAAiB;IAChB,KAAKG,QAAQ,IAAI;GAClB;EACD,CAAC;CACF;CAEA,MAAa,QAAuB;EACnC,MAAM,UAAU,KAAKF;EACrB,KAAKA,WAAW,CAAC;EACjB,MAAM,QAAQ,WAAW,QAAQ,KAAK,WAAW,OAAO,OAAO,aAAa,CAAC,CAAC,CAAC;CAChF;CAEA,QAA+B;EAC9B,OAAO,kBAAgC,KAAK,OAAO,MAAM,UAAU,YAAY;CAChF;CAEA,SAAe;EACd,KAAKG,YAAY;EACjB,KAAKC,aAAa,YAAY,IAAI;EAClC,KAAK,KAAK,OAAO;CAClB;CAEA,QAAQ,SAAsC;EAC7C,MAAM,UAAwB;GAC7B,IAAI,YAAY,QAAQ,CAAC,KAAKD;GAC9B,YAAY,KAAK,MAAM,YAAY,IAAI,IAAI,KAAKC,UAAU;GAC1D;EACD;EACA,KAAKA,aAAa;EAClB,KAAK,KAAK,QAAQ,KAAK,YAAY,WAAW,OAAO;EACrD,OAAO;CACR;CAEA,UAAwB;EACvB,MAAM,OAAO,OAA8C,SAAoB;GAE9E,MAAM,OAAO,KACX,QAAQ,aAAa,aAAa,UAAa,aAAa,QAAQ,aAAa,SAAS,aAAa,EAAE,CAAC,CAC1G,KAAK,aAAc,oBAAoB,QAAS,SAAS,SAAS,SAAS,UAAW,OAAO,QAAQ,CAAE,CAAC,CACxG,KAAK,GAAG;GACV,KAAK,KAAK,OAAO,OAAO,IAAI;EAC7B;EAEA,MAAM,yBAAS,IAAI,IAAY;EAC/B,OAAO;GACN,OAAO;GACP,OAAO,GAAG,SAAS,IAAI,QAAQ,IAAI;GACnC,OAAO,GAAG,SAAS,IAAI,QAAQ,IAAI;GACnC,WAAW,GAAG,SAAS;IACtB,MAAM,MAAM,KAAK,IAAI,MAAM,CAAC,CAAC,KAAK,GAAG;IACrC,IAAI,OAAO,IAAI,GAAG,GAAG;IACrB,OAAO,IAAI,GAAG;IACd,IAAI,QAAQ,IAAI;GACjB;GACA,QAAQ,GAAG,SAAS;IACnB,KAAKD,YAAY;IACjB,IAAI,SAAS,IAAI;IAEjB,IAAI,KAAKC,eAAe,KAAK,KAAKJ,SAAS,SAAS,GAAG,KAAKE,QAAQ,aAAa,IAAI,CAAC;GACvF;GACA,UAAU,GAAG,SAAS,IAAI,WAAW,IAAI;GACzC,mBAAmB,CAAC;EACrB;CACD;AACD;AAEA,SAAS,aAAa,MAAyB;CAC9C,MAAM,QAAQ,KAAK;CACnB,IAAI,iBAAiB,OAAO,OAAO,MAAM,QAAQ,MAAM,IAAI,CAAC,CAAC,MAAM;CACnE,OAAO,OAAO,SAAS,cAAc,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM;AAC1D"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vite-D3vYFsqa.js","names":["#load","#begin","#logger","#finish","#watcher","#startedAt"],"sources":["../src/lib/builders/vite.ts"],"sourcesContent":["import type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { EventEmitter } from 'node:events';\nimport { importFromProject } from '../project.js';\nimport type { Builder, BuilderEvents, BuildOutcome } from './types.js';\n\ntype ViteModule = typeof import('vite');\ntype RollupWatcher =\n\tAwaited<ReturnType<ViteModule['build']>> extends infer Result\n\t\t? Result extends { close: () => Promise<void>; on: (...args: never[]) => unknown }\n\t\t\t? Result\n\t\t\t: never\n\t\t: never;\n\nconst INSTALL_HINT = \"Install it with `pnpm add -D vite`, or set `build.tool` to 'tsdown', 'tsc' or 'none'.\";\n\n/**\n * Builds through the project's own `vite`, honouring its `vite.config.*`.\n *\n * Guarded by `experimental.enableVite`: Vite replaces `tsdown` as the bundler and, with the framework's Fetch\n * adapter, the `node:http` listener too, so a project can serve its interactions endpoint the same way it serves\n * everything else.\n */\nexport class ViteBuilder extends EventEmitter<BuilderEvents> implements Builder {\n\tpublic readonly tool = 'vite' as const;\n\t#watcher: RollupWatcher | null = null;\n\t#startedAt = 0;\n\n\tpublic constructor(private readonly config: ResolvedStarsConfig) {\n\t\tsuper();\n\t}\n\n\tpublic async build(): Promise<BuildOutcome> {\n\t\tconst vite = await this.#load();\n\t\tthis.#begin();\n\n\t\ttry {\n\t\t\tawait vite.build({ root: this.config.root, logLevel: 'warn', customLogger: this.#logger(vite) });\n\t\t\treturn this.#finish(null);\n\t\t} catch (error) {\n\t\t\treturn this.#finish(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\tpublic async watch(): Promise<void> {\n\t\tconst vite = await this.#load();\n\t\tthis.#begin();\n\n\t\t// `build.watch` makes Vite return a rollup watcher instead of resolving once, which is what keeps the bot\n\t\t// rebuilding on change; every rebuild reports through the same events the other builders use.\n\t\tconst result = (await vite.build({\n\t\t\troot: this.config.root,\n\t\t\tlogLevel: 'warn',\n\t\t\tcustomLogger: this.#logger(vite),\n\t\t\tbuild: { watch: {} }\n\t\t})) as unknown as RollupWatcher;\n\n\t\tthis.#watcher = result;\n\t\tresult.on(\n\t\t\t'event' as never,\n\t\t\t((event: { code: string; error?: Error }) => {\n\t\t\t\tswitch (event.code) {\n\t\t\t\t\tcase 'BUNDLE_START':\n\t\t\t\t\t\treturn this.#begin();\n\t\t\t\t\tcase 'BUNDLE_END':\n\t\t\t\t\t\treturn void this.#finish(null);\n\t\t\t\t\tcase 'ERROR':\n\t\t\t\t\t\treturn void this.#finish(event.error?.message ?? 'The build reported errors');\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}) as never\n\t\t);\n\t}\n\n\tpublic async close(): Promise<void> {\n\t\tconst watcher = this.#watcher;\n\t\tthis.#watcher = null;\n\t\tawait watcher?.close();\n\t}\n\n\tasync #load(): Promise<ViteModule> {\n\t\treturn importFromProject<ViteModule>(this.config.root, 'vite', INSTALL_HINT);\n\t}\n\n\t#logger(vite: ViteModule) {\n\t\tconst logger = vite.createLogger('warn', { allowClearScreen: false });\n\t\treturn {\n\t\t\t...logger,\n\t\t\tinfo: (message: string) => this.emit('log', 'info', message),\n\t\t\twarn: (message: string) => this.emit('log', 'warn', message),\n\t\t\twarnOnce: (message: string) => this.emit('log', 'warn', message),\n\t\t\terror: (message: string) => this.emit('log', 'error', message)\n\t\t};\n\t}\n\n\t#begin(): void {\n\t\tthis.#startedAt = performance.now();\n\t\tthis.emit('start');\n\t}\n\n\t#finish(message: string | null): BuildOutcome {\n\t\tconst outcome: BuildOutcome = { ok: message === null, durationMs: Math.round(performance.now() - this.#startedAt), message };\n\t\tthis.#startedAt = 0;\n\t\tthis.emit(outcome.ok ? 'success' : 'failure', outcome);\n\t\treturn outcome;\n\t}\n}\n"],"mappings":";;;;AAaA,MAAM,eAAe;;;;;;;;AASrB,IAAa,cAAb,cAAiC,aAA+C;CAK3C;CAJpC,AAAgB,OAAO;CACvB,WAAiC;CACjC,aAAa;CAEb,AAAO,YAAY,AAAiB,QAA6B;EAChE,MAAM;EAD6B;CAEpC;CAEA,MAAa,QAA+B;EAC3C,MAAM,OAAO,MAAM,KAAKA,MAAM;EAC9B,KAAKC,OAAO;EAEZ,IAAI;GACH,MAAM,KAAK,MAAM;IAAE,MAAM,KAAK,OAAO;IAAM,UAAU;IAAQ,cAAc,KAAKC,QAAQ,IAAI;GAAE,CAAC;GAC/F,OAAO,KAAKC,QAAQ,IAAI;EACzB,SAAS,OAAO;GACf,OAAO,KAAKA,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;EAC3E;CACD;CAEA,MAAa,QAAuB;EACnC,MAAM,OAAO,MAAM,KAAKH,MAAM;EAC9B,KAAKC,OAAO;EAIZ,MAAM,SAAU,MAAM,KAAK,MAAM;GAChC,MAAM,KAAK,OAAO;GAClB,UAAU;GACV,cAAc,KAAKC,QAAQ,IAAI;GAC/B,OAAO,EAAE,OAAO,CAAC,EAAE;EACpB,CAAC;EAED,KAAKE,WAAW;EAChB,OAAO,GACN,WACE,UAA2C;GAC5C,QAAQ,MAAM,MAAd;IACC,KAAK,gBACJ,OAAO,KAAKH,OAAO;IACpB,KAAK;KACG,AAAK,KAAKE,QAAQ,IAAI;KAA7B;IACD,KAAK;KACG,AAAK,KAAKA,QAAQ,MAAM,OAAO,WAAW,2BAA2B;KAA5E;GAGF;EACD,EACD;CACD;CAEA,MAAa,QAAuB;EACnC,MAAM,UAAU,KAAKC;EACrB,KAAKA,WAAW;EAChB,MAAM,SAAS,MAAM;CACtB;CAEA,MAAMJ,QAA6B;EAClC,OAAO,kBAA8B,KAAK,OAAO,MAAM,QAAQ,YAAY;CAC5E;CAEA,QAAQ,MAAkB;EAEzB,OAAO;GACN,GAFc,KAAK,aAAa,QAAQ,EAAE,kBAAkB,MAAM,CAE1D;GACR,OAAO,YAAoB,KAAK,KAAK,OAAO,QAAQ,OAAO;GAC3D,OAAO,YAAoB,KAAK,KAAK,OAAO,QAAQ,OAAO;GAC3D,WAAW,YAAoB,KAAK,KAAK,OAAO,QAAQ,OAAO;GAC/D,QAAQ,YAAoB,KAAK,KAAK,OAAO,SAAS,OAAO;EAC9D;CACD;CAEA,SAAe;EACd,KAAKK,aAAa,YAAY,IAAI;EAClC,KAAK,KAAK,OAAO;CAClB;CAEA,QAAQ,SAAsC;EAC7C,MAAM,UAAwB;GAAE,IAAI,YAAY;GAAM,YAAY,KAAK,MAAM,YAAY,IAAI,IAAI,KAAKA,UAAU;GAAG;EAAQ;EAC3H,KAAKA,aAAa;EAClB,KAAK,KAAK,QAAQ,KAAK,YAAY,WAAW,OAAO;EACrD,OAAO;CACR;AACD"}
|