@codemation/cli 0.0.22 → 0.0.24
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/CHANGELOG.md +16 -0
- package/dist/{CliBin-PdQvm7od.js → CliBin-CWXW_92Y.js} +99 -12
- package/dist/bin.js +1 -1
- package/dist/index.d.ts +6616 -203
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/src/CliProgramFactory.ts +3 -0
- package/src/commands/DevCommand.ts +20 -20
- package/src/dev/DevCliBannerRenderer.ts +39 -0
- package/src/dev/DevNextChildProcessOutputFilter.ts +30 -0
- package/src/dev/DevNextStartupBannerLineFilter.ts +25 -0
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as CliProgram, i as CliPathResolver, n as CliProgramFactory, o as ConsumerOutputBuilder, r as CodemationCliApplicationSession, s as ConsumerBuildOptionsParser, t as CliBin } from "./CliBin-
|
|
1
|
+
import { a as CliProgram, i as CliPathResolver, n as CliProgramFactory, o as ConsumerOutputBuilder, r as CodemationCliApplicationSession, s as ConsumerBuildOptionsParser, t as CliBin } from "./CliBin-CWXW_92Y.js";
|
|
2
2
|
import { CodemationPluginDiscovery } from "@codemation/host/server";
|
|
3
3
|
|
|
4
4
|
export { CliBin, CliPathResolver, CliProgram, CliProgramFactory, CodemationCliApplicationSession, CodemationPluginDiscovery, ConsumerBuildOptionsParser, ConsumerOutputBuilder };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codemation/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"reflect-metadata": "^0.2.2",
|
|
39
39
|
"typescript": "^5.9.3",
|
|
40
40
|
"ws": "^8.19.0",
|
|
41
|
-
"@codemation/next-host": "0.1.
|
|
42
|
-
"@codemation/host": "0.1.
|
|
41
|
+
"@codemation/next-host": "0.1.2",
|
|
42
|
+
"@codemation/host": "0.1.2"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/http-proxy": "^1.17.15",
|
package/src/CliProgramFactory.ts
CHANGED
|
@@ -21,6 +21,8 @@ import { HostPackageRootResolver } from "./database/HostPackageRootResolver";
|
|
|
21
21
|
import { PrismaMigrationDeployer } from "@codemation/host/persistence";
|
|
22
22
|
import { DevBootstrapSummaryFetcher } from "./dev/DevBootstrapSummaryFetcher";
|
|
23
23
|
import { DevCliBannerRenderer } from "./dev/DevCliBannerRenderer";
|
|
24
|
+
import { DevNextChildProcessOutputFilter } from "./dev/DevNextChildProcessOutputFilter";
|
|
25
|
+
import { DevNextStartupBannerLineFilter } from "./dev/DevNextStartupBannerLineFilter";
|
|
24
26
|
import { CliDevProxyServerFactory } from "./dev/CliDevProxyServerFactory";
|
|
25
27
|
import { DevApiRuntimeFactory } from "./dev/DevApiRuntimeFactory";
|
|
26
28
|
import { DevRebuildQueueFactory } from "./dev/DevRebuildQueueFactory";
|
|
@@ -100,6 +102,7 @@ export class CliProgramFactory {
|
|
|
100
102
|
new DevApiRuntimeFactory(devSessionServices.loopbackPortAllocator, appConfigLoader, pluginDiscovery),
|
|
101
103
|
new CliDevProxyServerFactory(),
|
|
102
104
|
new DevRebuildQueueFactory(),
|
|
105
|
+
new DevNextChildProcessOutputFilter(new DevNextStartupBannerLineFilter()),
|
|
103
106
|
);
|
|
104
107
|
return new CliProgram(
|
|
105
108
|
buildOptionsParser,
|
|
@@ -13,6 +13,7 @@ import type { DevBootstrapSummaryFetcher } from "../dev/DevBootstrapSummaryFetch
|
|
|
13
13
|
import type { CliDevProxyServer } from "../dev/CliDevProxyServer";
|
|
14
14
|
import type { CliDevProxyServerFactory } from "../dev/CliDevProxyServerFactory";
|
|
15
15
|
import type { DevCliBannerRenderer } from "../dev/DevCliBannerRenderer";
|
|
16
|
+
import type { DevNextChildProcessOutputFilter } from "../dev/DevNextChildProcessOutputFilter";
|
|
16
17
|
import { ConsumerEnvDotenvFilePredicate } from "../dev/ConsumerEnvDotenvFilePredicate";
|
|
17
18
|
import type { DevRebuildQueueFactory } from "../dev/DevRebuildQueueFactory";
|
|
18
19
|
import type { DevSourceWatcher } from "../dev/DevSourceWatcher";
|
|
@@ -49,6 +50,7 @@ export class DevCommand {
|
|
|
49
50
|
private readonly devApiRuntimeFactory: DevApiRuntimeFactory,
|
|
50
51
|
private readonly cliDevProxyServerFactory: CliDevProxyServerFactory,
|
|
51
52
|
private readonly devRebuildQueueFactory: DevRebuildQueueFactory,
|
|
53
|
+
private readonly devNextChildProcessOutputFilter: DevNextChildProcessOutputFilter,
|
|
52
54
|
) {}
|
|
53
55
|
|
|
54
56
|
async execute(
|
|
@@ -112,11 +114,16 @@ export class DevCommand {
|
|
|
112
114
|
await this.startPackagedUiWhenNeeded(prepared, processState, uiProxyBase);
|
|
113
115
|
this.bindShutdownSignalsToChildProcesses(processState, proxyServer);
|
|
114
116
|
await this.spawnDevUiWhenNeeded(prepared, processState, gatewayBaseUrl);
|
|
117
|
+
this.devCliBannerRenderer.renderGatewayListeningHint(
|
|
118
|
+
prepared.devMode === "watch-framework" ? prepared.nextPort : prepared.gatewayPort,
|
|
119
|
+
commandName,
|
|
120
|
+
prepared.devMode,
|
|
121
|
+
prepared.devMode === "watch-framework" ? prepared.gatewayPort : undefined,
|
|
122
|
+
);
|
|
115
123
|
await this.startWatcherForSourceRestart(prepared, processState, watcher, devMode, gatewayBaseUrl, proxyServer, {
|
|
116
124
|
commandName,
|
|
117
125
|
configPathOverride: args.configPathOverride,
|
|
118
126
|
});
|
|
119
|
-
this.logPackagedUiDevHintWhenNeeded(devMode, gatewayPort, commandName);
|
|
120
127
|
await stopPromise;
|
|
121
128
|
} finally {
|
|
122
129
|
if (previousDevelopmentServerToken === undefined) {
|
|
@@ -234,9 +241,10 @@ export class DevCommand {
|
|
|
234
241
|
});
|
|
235
242
|
state.currentPackagedUi = spawn(nextHostCommand.command, nextHostCommand.args, {
|
|
236
243
|
cwd: nextHostCommand.cwd,
|
|
237
|
-
...this.
|
|
244
|
+
...this.devDetachedChildSpawnPipeOptions(),
|
|
238
245
|
env: nextHostEnvironment,
|
|
239
246
|
});
|
|
247
|
+
this.devNextChildProcessOutputFilter.attach(state.currentPackagedUi);
|
|
240
248
|
state.currentPackagedUi.on("error", (error) => {
|
|
241
249
|
if (state.stopRequested || state.isRestartingUi) {
|
|
242
250
|
return;
|
|
@@ -276,14 +284,18 @@ export class DevCommand {
|
|
|
276
284
|
proxyServer.setBuildStatus("idle");
|
|
277
285
|
}
|
|
278
286
|
|
|
279
|
-
|
|
280
|
-
|
|
287
|
+
/**
|
|
288
|
+
* Next startup lines are filtered (see {@link DevNextChildProcessOutputFilter}) so the CLI can
|
|
289
|
+
* own the primary “open this URL” message without the internal loopback port dominating stdout.
|
|
290
|
+
*/
|
|
291
|
+
private devDetachedChildSpawnPipeOptions(): Readonly<{
|
|
292
|
+
stdio: ["ignore", "pipe", "pipe"];
|
|
281
293
|
detached: boolean;
|
|
282
294
|
windowsHide?: boolean;
|
|
283
295
|
}> {
|
|
284
296
|
return process.platform === "win32"
|
|
285
|
-
? { stdio: "
|
|
286
|
-
: { stdio: "
|
|
297
|
+
? { stdio: ["ignore", "pipe", "pipe"], detached: true, windowsHide: true }
|
|
298
|
+
: { stdio: ["ignore", "pipe", "pipe"], detached: true };
|
|
287
299
|
}
|
|
288
300
|
|
|
289
301
|
private bindShutdownSignalsToChildProcesses(
|
|
@@ -341,9 +353,10 @@ export class DevCommand {
|
|
|
341
353
|
});
|
|
342
354
|
state.currentDevUi = spawn("pnpm", ["exec", "next", "dev"], {
|
|
343
355
|
cwd: nextHostRoot,
|
|
344
|
-
...this.
|
|
356
|
+
...this.devDetachedChildSpawnPipeOptions(),
|
|
345
357
|
env: nextHostEnvironment,
|
|
346
358
|
});
|
|
359
|
+
this.devNextChildProcessOutputFilter.attach(state.currentDevUi);
|
|
347
360
|
state.currentDevUi.on("exit", (code) => {
|
|
348
361
|
const normalizedCode = code ?? 0;
|
|
349
362
|
if (state.stopRequested || state.isRestartingUi) {
|
|
@@ -591,17 +604,4 @@ export class DevCommand {
|
|
|
591
604
|
await this.consumerBuildArtifactsPublisher.publish(snapshot, discoveredPlugins);
|
|
592
605
|
this.cliLogger.debug(`Dev: consumer output published (${snapshot.buildVersion}).`);
|
|
593
606
|
}
|
|
594
|
-
|
|
595
|
-
private logPackagedUiDevHintWhenNeeded(
|
|
596
|
-
devMode: DevMode,
|
|
597
|
-
gatewayPort: number,
|
|
598
|
-
commandName: "dev" | "dev:plugin",
|
|
599
|
-
): void {
|
|
600
|
-
if (devMode !== "packaged-ui") {
|
|
601
|
-
return;
|
|
602
|
-
}
|
|
603
|
-
this.cliLogger.info(
|
|
604
|
-
`codemation ${commandName}: open http://127.0.0.1:${gatewayPort} — this uses the packaged @codemation/next-host UI. Use \`codemation ${commandName} --watch-framework\` only when working on the framework UI itself.`,
|
|
605
|
-
);
|
|
606
|
-
}
|
|
607
607
|
}
|
|
@@ -49,6 +49,45 @@ export class DevCliBannerRenderer {
|
|
|
49
49
|
this.renderRuntimeSummary(summary);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* URL to open in the browser: packaged UI uses the CLI gateway port; watch-framework uses the
|
|
54
|
+
* Next.js dev port (PORT). When they differ, {@link devGatewayPort} is shown in the footer.
|
|
55
|
+
*/
|
|
56
|
+
renderGatewayListeningHint(
|
|
57
|
+
browserPort: number,
|
|
58
|
+
commandName: "dev" | "dev:plugin",
|
|
59
|
+
devMode: "packaged-ui" | "watch-framework",
|
|
60
|
+
devGatewayPort?: number,
|
|
61
|
+
): void {
|
|
62
|
+
const url = `http://127.0.0.1:${browserPort}`;
|
|
63
|
+
const footer =
|
|
64
|
+
devMode === "watch-framework"
|
|
65
|
+
? chalk.dim(
|
|
66
|
+
devGatewayPort !== undefined && devGatewayPort !== browserPort
|
|
67
|
+
? `The dev gateway (API + runtime) is at http://127.0.0.1:${devGatewayPort}. Open the URL above for the Next.js UI (HMR).`
|
|
68
|
+
: "Open the URL above for the Next.js UI.",
|
|
69
|
+
)
|
|
70
|
+
: chalk.dim(
|
|
71
|
+
`The UI is served through this URL (not the internal Next port). Framework UI work in the monorepo: \`codemation ${commandName} --watch-framework\`.`,
|
|
72
|
+
);
|
|
73
|
+
const bodyLines = [
|
|
74
|
+
chalk.whiteBright.bold("Codemation is running"),
|
|
75
|
+
"",
|
|
76
|
+
`${chalk.hex("#9ca3af")("Open in your browser:")} ${chalk.greenBright.underline(url)}`,
|
|
77
|
+
"",
|
|
78
|
+
footer,
|
|
79
|
+
];
|
|
80
|
+
const box = boxen(bodyLines.join("\n"), {
|
|
81
|
+
padding: { top: 0, bottom: 0, left: 1, right: 1 },
|
|
82
|
+
margin: { top: 1, bottom: 0 },
|
|
83
|
+
borderStyle: "double",
|
|
84
|
+
borderColor: "green",
|
|
85
|
+
title: chalk.bold("Codemation dev"),
|
|
86
|
+
titleAlignment: "center",
|
|
87
|
+
});
|
|
88
|
+
process.stdout.write(`${box}\n`);
|
|
89
|
+
}
|
|
90
|
+
|
|
52
91
|
/**
|
|
53
92
|
* Shown after hot reload / watcher restarts (no figlet).
|
|
54
93
|
*/
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ChildProcess } from "node:child_process";
|
|
2
|
+
import { createInterface } from "node:readline";
|
|
3
|
+
|
|
4
|
+
import type { DevNextStartupBannerLineFilter } from "./DevNextStartupBannerLineFilter";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Attaches to a spawned Next child process and forwards streams while dropping
|
|
8
|
+
* {@link DevNextStartupBannerLineFilter} matches (startup banner only).
|
|
9
|
+
*/
|
|
10
|
+
export class DevNextChildProcessOutputFilter {
|
|
11
|
+
constructor(private readonly lineFilter: DevNextStartupBannerLineFilter) {}
|
|
12
|
+
|
|
13
|
+
attach(child: ChildProcess): void {
|
|
14
|
+
this.pipeFilteredStream(child.stdout, process.stdout);
|
|
15
|
+
this.pipeFilteredStream(child.stderr, process.stderr);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private pipeFilteredStream(source: NodeJS.ReadableStream | null, sink: NodeJS.WritableStream): void {
|
|
19
|
+
if (!source) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const rl = createInterface({ input: source, crlfDelay: Infinity });
|
|
23
|
+
rl.on("line", (line) => {
|
|
24
|
+
if (this.lineFilter.shouldSuppress(line)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
sink.write(`${line}\n`);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filters noisy Next.js `next start` / `next dev` startup lines so the CLI can
|
|
3
|
+
* surface the Codemation gateway URL as the primary “where to browse” signal.
|
|
4
|
+
*/
|
|
5
|
+
export class DevNextStartupBannerLineFilter {
|
|
6
|
+
shouldSuppress(line: string): boolean {
|
|
7
|
+
const t = line.replace(/\r$/, "").trimEnd();
|
|
8
|
+
if (t.length === 0) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
if (/^\s*▲\s+Next\.js/.test(t)) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
if (/^\s*-\s+Local:\s+/.test(t)) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
if (/^\s*-\s+Network:\s+/.test(t)) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
if (/^\s*✓\s+Ready\b/.test(t)) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|