@webiny/cli-standalone 6.6.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/bin.d.ts +3 -0
- package/bin.js +12 -0
- package/bin.js.map +1 -0
- package/features/ServeCommand.d.ts +15 -0
- package/features/ServeCommand.js +82 -0
- package/features/ServeCommand.js.map +1 -0
- package/features/WatchCommand.d.ts +18 -0
- package/features/WatchCommand.js +184 -0
- package/features/WatchCommand.js.map +1 -0
- package/features/WatchOutputGate.d.ts +49 -0
- package/features/WatchOutputGate.js +59 -0
- package/features/WatchOutputGate.js.map +1 -0
- package/features/WatchStartup.d.ts +40 -0
- package/features/WatchStartup.js +55 -0
- package/features/WatchStartup.js.map +1 -0
- package/features/WatchSummary.d.ts +55 -0
- package/features/WatchSummary.js +63 -0
- package/features/WatchSummary.js.map +1 -0
- package/features/serverProcesses.d.ts +10 -0
- package/features/serverProcesses.js +36 -0
- package/features/serverProcesses.js.map +1 -0
- package/features/terminalPrefix.d.ts +34 -0
- package/features/terminalPrefix.js +74 -0
- package/features/terminalPrefix.js.map +1 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +37 -0
- package/registerStandaloneFeatures.d.ts +2 -0
- package/registerStandaloneFeatures.js +13 -0
- package/registerStandaloneFeatures.js.map +1 -0
- package/services/DefaultAppsService.d.ts +5 -0
- package/services/DefaultAppsService.js +18 -0
- package/services/DefaultAppsService.js.map +1 -0
- package/services/GetProjectSdkService.d.ts +9 -0
- package/services/GetProjectSdkService.js +33 -0
- package/services/GetProjectSdkService.js.map +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Webiny
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @webiny/cli-standalone
|
|
2
|
+
|
|
3
|
+
> [!NOTE]
|
|
4
|
+
> This package is part of the [Webiny](https://www.webiny.com) monorepo.
|
|
5
|
+
> It’s **included in every Webiny project by default** and is not meant to be used as a standalone package.
|
|
6
|
+
|
|
7
|
+
📘 **Documentation:** [https://www.webiny.com/docs](https://www.webiny.com/docs)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
_This README file is automatically generated during the publish process._
|
package/bin.d.ts
ADDED
package/bin.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
3
|
+
import "@webiny/cli-core/utils/suppressPunycodeWarnings.js";
|
|
4
|
+
import { Cli } from "@webiny/cli-core";
|
|
5
|
+
import { ensureSameWebinyPackageVersions } from "@webiny/cli-core/utils/ensureSameWebinyPackageVersions.js";
|
|
6
|
+
import { registerStandaloneFeatures } from "./registerStandaloneFeatures.js";
|
|
7
|
+
process.env.WEBINY_HOSTING_TYPE = "standalone";
|
|
8
|
+
ensureSameWebinyPackageVersions();
|
|
9
|
+
const cli = await Cli.init({}, registerStandaloneFeatures);
|
|
10
|
+
await cli.run();
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=bin.js.map
|
package/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sources":["../src/bin.ts"],"sourcesContent":["#!/usr/bin/env node\nimport \"tsx/esm\";\n\n// Suppress punycode warnings. This is a known issue we can't fix.\nimport \"@webiny/cli-core/utils/suppressPunycodeWarnings.js\";\n\nimport { Cli } from \"@webiny/cli-core\";\nimport { ensureSameWebinyPackageVersions } from \"@webiny/cli-core/utils/ensureSameWebinyPackageVersions.js\";\nimport { registerStandaloneFeatures } from \"./registerStandaloneFeatures.js\";\n\n// Hosting-type marker — lets webiny.config.tsx branch on which CLI is running (e.g. SelfHostedAuth +\n// Admin.ApiUrl for server, Cognito for AWS). Read at build/watch time when the config is evaluated.\nprocess.env.WEBINY_HOSTING_TYPE = \"standalone\";\n\n// Ensure all @webiny/* packages use the same version.\nensureSameWebinyPackageVersions();\n\nconst cli = await Cli.init({}, registerStandaloneFeatures);\n\nawait cli.run();\n"],"names":["process","ensureSameWebinyPackageVersions","cli","Cli","registerStandaloneFeatures"],"mappings":";;;;;;AAYAA,QAAQ,GAAG,CAAC,mBAAmB,GAAG;AAGlCC;AAEA,MAAMC,MAAM,MAAMC,IAAI,IAAI,CAAC,CAAC,GAAGC;AAE/B,MAAMF,IAAI,GAAG"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { CliCommandFactory, GetProjectSdkService, StdioService, UiService } from "@webiny/cli-core/abstractions/index.js";
|
|
2
|
+
interface IServeCommandParams {
|
|
3
|
+
_: string[];
|
|
4
|
+
app?: string;
|
|
5
|
+
proxy?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare class StandaloneServeCommand implements CliCommandFactory.Interface<IServeCommandParams> {
|
|
8
|
+
private getProjectSdkService;
|
|
9
|
+
private stdioService;
|
|
10
|
+
private uiService;
|
|
11
|
+
constructor(getProjectSdkService: GetProjectSdkService.Interface, stdioService: StdioService.Interface, uiService: UiService.Interface);
|
|
12
|
+
execute(): Promise<CliCommandFactory.CommandDefinition<IServeCommandParams>>;
|
|
13
|
+
}
|
|
14
|
+
export declare const standaloneServeCommand: import("@webiny/di").Implementation<typeof StandaloneServeCommand>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { createImplementation } from "@webiny/di";
|
|
2
|
+
import { CliCommandFactory, GetProjectSdkService, StdioService, UiService } from "@webiny/cli-core/abstractions/index.js";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { colorForString, createPrefixer } from "./terminalPrefix.js";
|
|
5
|
+
import { reserveDevProxyPorts } from "@webiny/project-standalone/serve/devProxy/index.js";
|
|
6
|
+
class StandaloneServeCommand {
|
|
7
|
+
constructor(getProjectSdkService, stdioService, uiService){
|
|
8
|
+
this.getProjectSdkService = getProjectSdkService;
|
|
9
|
+
this.stdioService = stdioService;
|
|
10
|
+
this.uiService = uiService;
|
|
11
|
+
}
|
|
12
|
+
async execute() {
|
|
13
|
+
return {
|
|
14
|
+
name: "serve",
|
|
15
|
+
description: "Serves your built Webiny project on a single URL.\n\nSet PORT to choose which port that URL uses. Run `webiny-standalone build` first.",
|
|
16
|
+
examples: [
|
|
17
|
+
"serve",
|
|
18
|
+
"serve api",
|
|
19
|
+
"serve admin",
|
|
20
|
+
"PORT=8000 serve"
|
|
21
|
+
],
|
|
22
|
+
params: [
|
|
23
|
+
{
|
|
24
|
+
name: "app",
|
|
25
|
+
description: "Name of the app to serve (api or admin). Serves both if omitted.",
|
|
26
|
+
type: "string"
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
options: [
|
|
30
|
+
{
|
|
31
|
+
name: "proxy",
|
|
32
|
+
description: "Serve the project on a single URL (default). Turn off to run each app on its own port",
|
|
33
|
+
type: "boolean",
|
|
34
|
+
default: true
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
handler: async (params)=>{
|
|
38
|
+
const stdio = this.stdioService;
|
|
39
|
+
const devProxyUrls = await reserveDevProxyPorts({
|
|
40
|
+
apps: params.app ? [
|
|
41
|
+
params.app
|
|
42
|
+
] : [
|
|
43
|
+
"api",
|
|
44
|
+
"admin"
|
|
45
|
+
],
|
|
46
|
+
enabled: params.proxy
|
|
47
|
+
});
|
|
48
|
+
const projectSdk = await this.getProjectSdkService.execute();
|
|
49
|
+
const { serversWatcher } = await projectSdk.serve({
|
|
50
|
+
app: params.app
|
|
51
|
+
});
|
|
52
|
+
const processes = serversWatcher.prepare();
|
|
53
|
+
if (0 === processes.length) return;
|
|
54
|
+
stdio.getStdout().setMaxListeners(processes.length + 5);
|
|
55
|
+
stdio.getStderr().setMaxListeners(processes.length + 5);
|
|
56
|
+
for (const serverProcess of processes){
|
|
57
|
+
const prefix = chalk.hex(colorForString(serverProcess.name))(serverProcess.name);
|
|
58
|
+
serverProcess.pipeStdout((stdout)=>{
|
|
59
|
+
stdout.pipe(createPrefixer(prefix)).pipe(stdio.getStdout());
|
|
60
|
+
});
|
|
61
|
+
serverProcess.pipeStderr((stderr)=>{
|
|
62
|
+
stderr.pipe(createPrefixer(prefix)).pipe(stdio.getStderr());
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (devProxyUrls) this.uiService.info("Webiny is available at %s", devProxyUrls.url);
|
|
66
|
+
await Promise.all(processes.map((p)=>p.run()));
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const standaloneServeCommand = createImplementation({
|
|
72
|
+
abstraction: CliCommandFactory,
|
|
73
|
+
implementation: StandaloneServeCommand,
|
|
74
|
+
dependencies: [
|
|
75
|
+
GetProjectSdkService,
|
|
76
|
+
StdioService,
|
|
77
|
+
UiService
|
|
78
|
+
]
|
|
79
|
+
});
|
|
80
|
+
export { StandaloneServeCommand, standaloneServeCommand };
|
|
81
|
+
|
|
82
|
+
//# sourceMappingURL=ServeCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/ServeCommand.js","sources":["../../src/features/ServeCommand.ts"],"sourcesContent":["import { createImplementation } from \"@webiny/di\";\nimport {\n CliCommandFactory,\n GetProjectSdkService,\n StdioService,\n UiService\n} from \"@webiny/cli-core/abstractions/index.js\";\nimport chalk from \"chalk\";\nimport { colorForString, createPrefixer } from \"./terminalPrefix.js\";\nimport { reserveDevProxyPorts } from \"@webiny/project-standalone/serve/devProxy/index.js\";\n\ninterface IServeCommandParams {\n _: string[];\n app?: string;\n proxy?: boolean;\n}\n\nexport class StandaloneServeCommand implements CliCommandFactory.Interface<IServeCommandParams> {\n constructor(\n private getProjectSdkService: GetProjectSdkService.Interface,\n private stdioService: StdioService.Interface,\n private uiService: UiService.Interface\n ) {}\n\n async execute(): Promise<CliCommandFactory.CommandDefinition<IServeCommandParams>> {\n return {\n name: \"serve\",\n description: [\n \"Serves your built Webiny project on a single URL.\",\n \"\",\n \"Set PORT to choose which port that URL uses. Run `webiny-standalone build` first.\"\n ].join(\"\\n\"),\n examples: [\"serve\", \"serve api\", \"serve admin\", \"PORT=8000 serve\"],\n params: [\n {\n name: \"app\",\n description: \"Name of the app to serve (api or admin). Serves both if omitted.\",\n type: \"string\"\n }\n ],\n options: [\n {\n name: \"proxy\",\n description:\n \"Serve the project on a single URL (default). Turn off to run each app on its own port\",\n type: \"boolean\",\n default: true\n }\n ],\n handler: async (params: IServeCommandParams) => {\n const stdio = this.stdioService;\n\n // Ports only, no `pointAppsAtDevProxy`: serve runs what `webiny build` already\n // produced, so the admin bundle's API URL was fixed back then and has to have been\n // built with one that works behind the proxy.\n const devProxyUrls = await reserveDevProxyPorts({\n apps: params.app ? [params.app] : [\"api\", \"admin\"],\n enabled: params.proxy\n });\n\n const projectSdk = await this.getProjectSdkService.execute();\n\n // The project layer describes the server process(es) (lazy ServersWatcher); the CLI\n // prepares + runs them, owning terminal rendering (prefixing) and lifecycle — same\n // split as the watch command.\n const { serversWatcher } = await projectSdk.serve({ app: params.app as any });\n const processes = serversWatcher.prepare();\n if (processes.length === 0) {\n return;\n }\n\n stdio.getStdout().setMaxListeners(processes.length + 5);\n stdio.getStderr().setMaxListeners(processes.length + 5);\n\n for (const serverProcess of processes) {\n const prefix = chalk.hex(colorForString(serverProcess.name))(\n serverProcess.name\n );\n serverProcess.pipeStdout(stdout => {\n stdout.pipe(createPrefixer(prefix)).pipe(stdio.getStdout());\n });\n serverProcess.pipeStderr(stderr => {\n stderr.pipe(createPrefixer(prefix)).pipe(stdio.getStderr());\n });\n }\n\n if (devProxyUrls) {\n this.uiService.info(`Webiny is available at %s`, devProxyUrls.url);\n }\n\n await Promise.all(processes.map(p => p.run()));\n }\n };\n }\n}\n\nexport const standaloneServeCommand = createImplementation({\n abstraction: CliCommandFactory,\n implementation: StandaloneServeCommand,\n dependencies: [GetProjectSdkService, StdioService, UiService]\n});\n"],"names":["StandaloneServeCommand","getProjectSdkService","stdioService","uiService","params","stdio","devProxyUrls","reserveDevProxyPorts","projectSdk","serversWatcher","processes","serverProcess","prefix","chalk","colorForString","stdout","createPrefixer","stderr","Promise","p","standaloneServeCommand","createImplementation","CliCommandFactory","GetProjectSdkService","StdioService","UiService"],"mappings":";;;;;AAiBO,MAAMA;IACT,YACYC,oBAAoD,EACpDC,YAAoC,EACpCC,SAA8B,CACxC;aAHUF,oBAAoB,GAApBA;aACAC,YAAY,GAAZA;aACAC,SAAS,GAATA;IACT;IAEH,MAAM,UAA6E;QAC/E,OAAO;YACH,MAAM;YACN,aAAa;YAKb,UAAU;gBAAC;gBAAS;gBAAa;gBAAe;aAAkB;YAClE,QAAQ;gBACJ;oBACI,MAAM;oBACN,aAAa;oBACb,MAAM;gBACV;aACH;YACD,SAAS;gBACL;oBACI,MAAM;oBACN,aACI;oBACJ,MAAM;oBACN,SAAS;gBACb;aACH;YACD,SAAS,OAAOC;gBACZ,MAAMC,QAAQ,IAAI,CAAC,YAAY;gBAK/B,MAAMC,eAAe,MAAMC,qBAAqB;oBAC5C,MAAMH,OAAO,GAAG,GAAG;wBAACA,OAAO,GAAG;qBAAC,GAAG;wBAAC;wBAAO;qBAAQ;oBAClD,SAASA,OAAO,KAAK;gBACzB;gBAEA,MAAMI,aAAa,MAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO;gBAK1D,MAAM,EAAEC,cAAc,EAAE,GAAG,MAAMD,WAAW,KAAK,CAAC;oBAAE,KAAKJ,OAAO,GAAG;gBAAQ;gBAC3E,MAAMM,YAAYD,eAAe,OAAO;gBACxC,IAAIC,AAAqB,MAArBA,UAAU,MAAM,EAChB;gBAGJL,MAAM,SAAS,GAAG,eAAe,CAACK,UAAU,MAAM,GAAG;gBACrDL,MAAM,SAAS,GAAG,eAAe,CAACK,UAAU,MAAM,GAAG;gBAErD,KAAK,MAAMC,iBAAiBD,UAAW;oBACnC,MAAME,SAASC,MAAM,GAAG,CAACC,eAAeH,cAAc,IAAI,GACtDA,cAAc,IAAI;oBAEtBA,cAAc,UAAU,CAACI,CAAAA;wBACrBA,OAAO,IAAI,CAACC,eAAeJ,SAAS,IAAI,CAACP,MAAM,SAAS;oBAC5D;oBACAM,cAAc,UAAU,CAACM,CAAAA;wBACrBA,OAAO,IAAI,CAACD,eAAeJ,SAAS,IAAI,CAACP,MAAM,SAAS;oBAC5D;gBACJ;gBAEA,IAAIC,cACA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,6BAA6BA,aAAa,GAAG;gBAGrE,MAAMY,QAAQ,GAAG,CAACR,UAAU,GAAG,CAACS,CAAAA,IAAKA,EAAE,GAAG;YAC9C;QACJ;IACJ;AACJ;AAEO,MAAMC,yBAAyBC,qBAAqB;IACvD,aAAaC;IACb,gBAAgBtB;IAChB,cAAc;QAACuB;QAAsBC;QAAcC;KAAU;AACjE"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CliCommandFactory, DefaultAppsService, GetProjectSdkService, StdioService, UiService } from "@webiny/cli-core/abstractions/index.js";
|
|
2
|
+
interface IStandaloneWatchCommandParams {
|
|
3
|
+
_: string[];
|
|
4
|
+
app?: string;
|
|
5
|
+
package?: string | string[];
|
|
6
|
+
verbose?: boolean;
|
|
7
|
+
proxy?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class StandaloneWatchCommand implements CliCommandFactory.Interface<IStandaloneWatchCommandParams> {
|
|
10
|
+
private getProjectSdkService;
|
|
11
|
+
private stdioService;
|
|
12
|
+
private uiService;
|
|
13
|
+
private defaultAppsService;
|
|
14
|
+
constructor(getProjectSdkService: GetProjectSdkService.Interface, stdioService: StdioService.Interface, uiService: UiService.Interface, defaultAppsService: DefaultAppsService.Interface);
|
|
15
|
+
execute(): Promise<CliCommandFactory.CommandDefinition<IStandaloneWatchCommandParams>>;
|
|
16
|
+
}
|
|
17
|
+
export declare const standaloneWatchCommand: import("@webiny/di").Implementation<typeof StandaloneWatchCommand>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { createImplementation } from "@webiny/di";
|
|
2
|
+
import { CliCommandFactory, DefaultAppsService, GetProjectSdkService, StdioService, UiService } from "@webiny/cli-core/abstractions/index.js";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { colorForString, createPrefixer } from "./terminalPrefix.js";
|
|
5
|
+
import { createWatchServerPrefixer } from "./serverProcesses.js";
|
|
6
|
+
import { WatchSummary } from "./WatchSummary.js";
|
|
7
|
+
import { WatchOutputGate } from "./WatchOutputGate.js";
|
|
8
|
+
import { WatchStartup } from "./WatchStartup.js";
|
|
9
|
+
import { pointAppsAtDevProxy, reserveDevProxyPorts } from "@webiny/project-standalone/serve/devProxy/index.js";
|
|
10
|
+
class StandaloneWatchCommand {
|
|
11
|
+
constructor(getProjectSdkService, stdioService, uiService, defaultAppsService){
|
|
12
|
+
this.getProjectSdkService = getProjectSdkService;
|
|
13
|
+
this.stdioService = stdioService;
|
|
14
|
+
this.uiService = uiService;
|
|
15
|
+
this.defaultAppsService = defaultAppsService;
|
|
16
|
+
}
|
|
17
|
+
async execute() {
|
|
18
|
+
return {
|
|
19
|
+
name: "watch",
|
|
20
|
+
description: "Watches your Webiny project for code changes and serves it on a single URL.\n\nSet PORT to choose which port that URL uses. That is also what lets a tool like\nportless put the project on a domain instead:\n\n portless --force myadmin yarn webiny-standalone watch\n\nserves it on https://myadmin.localhost.",
|
|
21
|
+
examples: [
|
|
22
|
+
"watch",
|
|
23
|
+
"watch api",
|
|
24
|
+
"watch admin",
|
|
25
|
+
"watch -p my-package",
|
|
26
|
+
"PORT=4000 watch"
|
|
27
|
+
],
|
|
28
|
+
params: [
|
|
29
|
+
{
|
|
30
|
+
name: "app",
|
|
31
|
+
description: "Name of the app to watch (api or admin)",
|
|
32
|
+
type: "string"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
options: [
|
|
36
|
+
{
|
|
37
|
+
name: "package",
|
|
38
|
+
alias: "p",
|
|
39
|
+
description: "One or more packages to watch for code changes",
|
|
40
|
+
type: "string"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "verbose",
|
|
44
|
+
description: "Show all output as it happens, instead of holding it back until the project is up",
|
|
45
|
+
type: "boolean"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "proxy",
|
|
49
|
+
description: "Serve the project on a single URL (default). Turn off to run each app on its own port",
|
|
50
|
+
type: "boolean",
|
|
51
|
+
default: true
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
handler: async (params)=>{
|
|
55
|
+
const stdio = this.stdioService;
|
|
56
|
+
const ui = this.uiService;
|
|
57
|
+
let apps = [];
|
|
58
|
+
if (params.app) apps = [
|
|
59
|
+
params.app
|
|
60
|
+
];
|
|
61
|
+
else if (!params.package) {
|
|
62
|
+
apps = await this.defaultAppsService.execute();
|
|
63
|
+
if (0 === apps.length) return void ui.error("Please specify an app to watch, for example: %s", "webiny watch api");
|
|
64
|
+
}
|
|
65
|
+
const devProxyUrls = await reserveDevProxyPorts({
|
|
66
|
+
apps,
|
|
67
|
+
enabled: params.proxy
|
|
68
|
+
});
|
|
69
|
+
if (devProxyUrls) pointAppsAtDevProxy(devProxyUrls);
|
|
70
|
+
const projectSdk = await this.getProjectSdkService.execute();
|
|
71
|
+
if (!devProxyUrls && apps.length > 1 && process.env.PORT) {
|
|
72
|
+
ui.warning("%s is ignored when watching several apps at once. Set %s and %s instead.", "PORT", "WEBINY_API_PORT", "WEBINY_ADMIN_PORT");
|
|
73
|
+
delete process.env.PORT;
|
|
74
|
+
}
|
|
75
|
+
const gated = apps.length > 1 && !params.verbose;
|
|
76
|
+
const gate = new WatchOutputGate(stdio, ui, {
|
|
77
|
+
open: !gated,
|
|
78
|
+
onActivity: ()=>startup.noteOutput()
|
|
79
|
+
});
|
|
80
|
+
const summary = apps.length > 1 ? new WatchSummary(ui, Date.now(), ()=>startup.noteProgress()) : void 0;
|
|
81
|
+
if (devProxyUrls) summary?.setPublicUrl(devProxyUrls.url, {
|
|
82
|
+
showAppUrls: params.verbose
|
|
83
|
+
});
|
|
84
|
+
const startup = new WatchStartup(gate, summary);
|
|
85
|
+
if (summary) {
|
|
86
|
+
ui.emptyLine();
|
|
87
|
+
ui.info("Starting...");
|
|
88
|
+
}
|
|
89
|
+
const processLists = [];
|
|
90
|
+
const serverProcesses = [];
|
|
91
|
+
if (apps.length > 0) for (const app of apps){
|
|
92
|
+
summary?.expect(app);
|
|
93
|
+
const { packagesWatcher, serversWatcher } = await projectSdk.watch({
|
|
94
|
+
app: app
|
|
95
|
+
});
|
|
96
|
+
processLists.push({
|
|
97
|
+
app,
|
|
98
|
+
processes: packagesWatcher.prepare()
|
|
99
|
+
});
|
|
100
|
+
if (serversWatcher) serverProcesses.push(...serversWatcher.prepare());
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
const whitelist = Array.isArray(params.package) ? params.package : [
|
|
104
|
+
params.package
|
|
105
|
+
].filter(Boolean);
|
|
106
|
+
const { packagesWatcher } = await projectSdk.watch({
|
|
107
|
+
package: whitelist
|
|
108
|
+
});
|
|
109
|
+
processLists.push({
|
|
110
|
+
processes: packagesWatcher.prepare()
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const allProcesses = processLists.flatMap(({ app, processes })=>(processes.getProcesses ? processes.getProcesses() : []).map((process1)=>({
|
|
114
|
+
app,
|
|
115
|
+
process: process1
|
|
116
|
+
})));
|
|
117
|
+
if (0 === allProcesses.length && 0 === serverProcesses.length) return void ui.warning('No watch processes were started. Please ensure you have specified a valid "app" or "package" parameter.');
|
|
118
|
+
if (1 === allProcesses.length && 0 === serverProcesses.length) {
|
|
119
|
+
processLists[0].processes.setForkOptions({
|
|
120
|
+
stdio: "inherit",
|
|
121
|
+
env: process.env
|
|
122
|
+
});
|
|
123
|
+
ui.info("Watching %s package...", allProcesses[0].process.pkg.name);
|
|
124
|
+
await allProcesses[0].process.run();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (!summary) ui.info("Watching %s packages...", allProcesses.length);
|
|
128
|
+
const listenerCount = allProcesses.length + serverProcesses.length + 5;
|
|
129
|
+
stdio.getStdout().setMaxListeners(listenerCount);
|
|
130
|
+
stdio.getStderr().setMaxListeners(listenerCount);
|
|
131
|
+
for (const { app, process: watchProcess } of allProcesses){
|
|
132
|
+
const name = watchProcess.pkg.name;
|
|
133
|
+
const prefix = chalk.hex(colorForString(name))(name);
|
|
134
|
+
const signals = summary && app ? {
|
|
135
|
+
onUrl: (url)=>summary.reportUrl(app, url),
|
|
136
|
+
onReady: ()=>summary.reportReady(app)
|
|
137
|
+
} : {};
|
|
138
|
+
watchProcess.pipeStdout((stdout)=>{
|
|
139
|
+
stdout.pipe(createPrefixer(prefix, signals)).pipe(gate.sink(name, "stdout"));
|
|
140
|
+
});
|
|
141
|
+
watchProcess.pipeStderr((stderr)=>{
|
|
142
|
+
stderr.pipe(createPrefixer(prefix)).pipe(gate.sink(name, "stderr"));
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
for (const serverProcess of serverProcesses){
|
|
146
|
+
const name = serverProcess.name;
|
|
147
|
+
const prefix = chalk.hex(colorForString(name))(name);
|
|
148
|
+
const signals = summary ? {
|
|
149
|
+
onUrl: (url)=>summary.reportUrl(name, url),
|
|
150
|
+
onReady: ()=>summary.reportReady(name)
|
|
151
|
+
} : {};
|
|
152
|
+
serverProcess.pipeStdout((stdout)=>{
|
|
153
|
+
stdout.pipe(createWatchServerPrefixer(prefix, signals)).pipe(gate.sink(name, "stdout"));
|
|
154
|
+
});
|
|
155
|
+
serverProcess.pipeStderr((stderr)=>{
|
|
156
|
+
stderr.pipe(createWatchServerPrefixer(prefix)).pipe(gate.sink(name, "stderr"));
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
startup.begin();
|
|
160
|
+
try {
|
|
161
|
+
await Promise.all([
|
|
162
|
+
...allProcesses.map(({ process: process1 })=>process1.run().finally(()=>startup.abandon())),
|
|
163
|
+
...serverProcesses.map((p)=>p.run().finally(()=>startup.abandon()))
|
|
164
|
+
]);
|
|
165
|
+
} finally{
|
|
166
|
+
startup.dispose();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const standaloneWatchCommand = createImplementation({
|
|
173
|
+
abstraction: CliCommandFactory,
|
|
174
|
+
implementation: StandaloneWatchCommand,
|
|
175
|
+
dependencies: [
|
|
176
|
+
GetProjectSdkService,
|
|
177
|
+
StdioService,
|
|
178
|
+
UiService,
|
|
179
|
+
DefaultAppsService
|
|
180
|
+
]
|
|
181
|
+
});
|
|
182
|
+
export { StandaloneWatchCommand, standaloneWatchCommand };
|
|
183
|
+
|
|
184
|
+
//# sourceMappingURL=WatchCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/WatchCommand.js","sources":["../../src/features/WatchCommand.ts"],"sourcesContent":["import { createImplementation } from \"@webiny/di\";\nimport {\n CliCommandFactory,\n DefaultAppsService,\n GetProjectSdkService,\n StdioService,\n UiService\n} from \"@webiny/cli-core/abstractions/index.js\";\nimport chalk from \"chalk\";\nimport { colorForString, createPrefixer } from \"./terminalPrefix.js\";\nimport { createWatchServerPrefixer } from \"./serverProcesses.js\";\nimport { WatchSummary } from \"./WatchSummary.js\";\nimport { WatchOutputGate } from \"./WatchOutputGate.js\";\nimport { WatchStartup } from \"./WatchStartup.js\";\nimport { type Watch } from \"@webiny/project/abstractions/index.js\";\nimport { pointAppsAtDevProxy } from \"@webiny/project-standalone/serve/devProxy/index.js\";\nimport { reserveDevProxyPorts } from \"@webiny/project-standalone/serve/devProxy/index.js\";\n\ninterface IStandaloneWatchCommandParams {\n _: string[];\n app?: string;\n package?: string | string[];\n verbose?: boolean;\n proxy?: boolean;\n}\n\nexport class StandaloneWatchCommand implements CliCommandFactory.Interface<IStandaloneWatchCommandParams> {\n constructor(\n private getProjectSdkService: GetProjectSdkService.Interface,\n private stdioService: StdioService.Interface,\n private uiService: UiService.Interface,\n private defaultAppsService: DefaultAppsService.Interface\n ) {}\n\n async execute(): Promise<CliCommandFactory.CommandDefinition<IStandaloneWatchCommandParams>> {\n return {\n name: \"watch\",\n description: [\n \"Watches your Webiny project for code changes and serves it on a single URL.\",\n \"\",\n \"Set PORT to choose which port that URL uses. That is also what lets a tool like\",\n \"portless put the project on a domain instead:\",\n \"\",\n \" portless --force myadmin yarn webiny-standalone watch\",\n \"\",\n \"serves it on https://myadmin.localhost.\"\n ].join(\"\\n\"),\n examples: [\n \"watch\",\n \"watch api\",\n \"watch admin\",\n \"watch -p my-package\",\n \"PORT=4000 watch\"\n ],\n params: [\n {\n name: \"app\",\n description: \"Name of the app to watch (api or admin)\",\n type: \"string\"\n }\n ],\n options: [\n {\n name: \"package\",\n alias: \"p\",\n description: \"One or more packages to watch for code changes\",\n type: \"string\"\n },\n {\n name: \"verbose\",\n description:\n \"Show all output as it happens, instead of holding it back until the project is up\",\n type: \"boolean\"\n },\n {\n name: \"proxy\",\n description:\n \"Serve the project on a single URL (default). Turn off to run each app on its own port\",\n type: \"boolean\",\n default: true\n }\n ],\n handler: async (params: IStandaloneWatchCommandParams) => {\n const stdio = this.stdioService;\n const ui = this.uiService;\n\n // Decide which apps to watch. Unlike cli-aws (where each app watch attaches to its own\n // cloud environment and must be run separately), standalone watches every default app\n // in a single process, so a bare `webiny watch` is all a developer needs.\n let apps: string[] = [];\n if (params.app) {\n apps = [params.app];\n } else if (!params.package) {\n apps = await this.defaultAppsService.execute();\n\n if (apps.length === 0) {\n ui.error(\n `Please specify an app to watch, for example: %s`,\n \"webiny watch api\"\n );\n return;\n }\n }\n\n const devProxyUrls = await reserveDevProxyPorts({\n apps,\n enabled: params.proxy\n });\n\n // Before the `projectSdk.watch()` calls below, which is what matters: those are where\n // each app's workspace is prepared and its config re-rendered, so that is where these\n // URLs get picked up.\n if (devProxyUrls) {\n pointAppsAtDevProxy(devProxyUrls);\n }\n\n const projectSdk = await this.getProjectSdkService.execute();\n\n // Without the proxy, several apps in one process means a generic PORT injected by the\n // environment can only belong to one of them, so drop it and let each app fall back to\n // its own dedicated port. Same rule `webiny serve` applies when serving both apps at\n // once. Has to happen before the watchers are prepared below: that is where each forked\n // process snapshots the env.\n if (!devProxyUrls && apps.length > 1 && process.env.PORT) {\n ui.warning(\n `%s is ignored when watching several apps at once. Set %s and %s instead.`,\n \"PORT\",\n \"WEBINY_API_PORT\",\n \"WEBINY_ADMIN_PORT\"\n );\n delete process.env.PORT;\n }\n\n // With a single app the app's own startup line is easy enough to spot; with several, the\n // \"where is each app running\" answer would otherwise be buried in interleaved build output.\n const gated = apps.length > 1 && !params.verbose;\n\n // Everything goes through the gate so that piping into it, rather than straight into\n // stdout, is the only path — a plain `.pipe(stdout)` ends stdout as soon as the first\n // child stream ends, silencing the rest. When not gating it just passes output straight\n // through.\n const gate = new WatchOutputGate(stdio, ui, {\n open: !gated,\n onActivity: () => startup.noteOutput()\n });\n\n // The summary collects what the apps report; WatchStartup decides when that adds up to\n // \"started\" and releases both. Same path whether or not output is held back: a URL alone\n // doesn't mean an app finished starting, so `--verbose` waits for the ready markers too\n // rather than claiming \"Ready\" the moment rsbuild binds its port.\n const summary =\n apps.length > 1\n ? new WatchSummary(ui, Date.now(), () => startup.noteProgress())\n : undefined;\n\n if (devProxyUrls) {\n summary?.setPublicUrl(devProxyUrls.url, { showAppUrls: params.verbose });\n }\n\n const startup = new WatchStartup(gate, summary);\n\n // Printed before the `projectSdk.watch()` calls below, not after: those prepare each\n // app's workspace and evaluate its config, which is a slow, silent stretch. Announcing\n // the start afterwards left \"Ready in Xs\" reporting time the developer never saw pass.\n if (summary) {\n ui.emptyLine();\n ui.info(`Starting...`);\n }\n\n // Collect PackagesWatcher instances (one per app or for package-only mode) plus any\n // long-running server processes the hosting type attaches (e.g. the api HTTP server).\n // The app each watcher belongs to is kept alongside it so a dev server URL found in the\n // output can be attributed back to an app.\n const processLists: { app?: string; processes: Watch.BuildProcesses }[] = [];\n const serverProcesses: Watch.Process[] = [];\n\n if (apps.length > 0) {\n for (const app of apps) {\n summary?.expect(app);\n\n const { packagesWatcher, serversWatcher } = await projectSdk.watch({\n app: app as any\n });\n processLists.push({ app, processes: packagesWatcher.prepare() });\n if (serversWatcher) {\n serverProcesses.push(...serversWatcher.prepare());\n }\n }\n } else {\n const whitelist = Array.isArray(params.package)\n ? params.package\n : ([params.package].filter(Boolean) as string[]);\n const { packagesWatcher } = await projectSdk.watch({ package: whitelist });\n processLists.push({ processes: packagesWatcher.prepare() });\n }\n\n // Flatten processes from all watchers, each still tagged with its app.\n const allProcesses = processLists.flatMap(({ app, processes }) =>\n (processes.getProcesses ? processes.getProcesses() : []).map(process => ({\n app,\n process\n }))\n );\n\n if (allProcesses.length === 0 && serverProcesses.length === 0) {\n ui.warning(\n `No watch processes were started. Please ensure you have specified a valid \"app\" or \"package\" parameter.`\n );\n return;\n }\n\n // Fast path: a single build process and nothing else — inherit stdio, no prefixing.\n if (allProcesses.length === 1 && serverProcesses.length === 0) {\n processLists[0].processes.setForkOptions({\n stdio: \"inherit\",\n env: process.env\n });\n ui.info(`Watching %s package...`, allProcesses[0].process.pkg.name);\n await allProcesses[0].process.run();\n return;\n }\n\n if (!summary) {\n ui.info(`Watching %s packages...`, allProcesses.length);\n }\n\n const listenerCount = allProcesses.length + serverProcesses.length + 5;\n stdio.getStdout().setMaxListeners(listenerCount);\n stdio.getStderr().setMaxListeners(listenerCount);\n\n for (const { app, process: watchProcess } of allProcesses) {\n const name = watchProcess.pkg.name;\n const prefix = chalk.hex(colorForString(name))(name);\n\n // Only one package per app runs a dev server (admin's rsbuild); the rest never print\n // a URL or a ready line, so watching every package's stdout for them costs nothing\n // and avoids having to identify that package up front.\n const signals =\n summary && app\n ? {\n onUrl: (url: string) => summary.reportUrl(app, url),\n onReady: () => summary.reportReady(app)\n }\n : {};\n\n watchProcess.pipeStdout(stdout => {\n stdout\n .pipe(createPrefixer(prefix, signals))\n .pipe(gate.sink(name, \"stdout\"));\n });\n\n watchProcess.pipeStderr(stderr => {\n stderr.pipe(createPrefixer(prefix)).pipe(gate.sink(name, \"stderr\"));\n });\n }\n\n // Render the hosting type's server processes (filtered/prefixed) then run them alongside\n // the build watchers.\n for (const serverProcess of serverProcesses) {\n const name = serverProcess.name;\n const prefix = chalk.hex(colorForString(name))(name);\n const signals = summary\n ? {\n onUrl: (url: string) => summary.reportUrl(name, url),\n onReady: () => summary.reportReady(name)\n }\n : {};\n\n serverProcess.pipeStdout(stdout => {\n stdout\n .pipe(createWatchServerPrefixer(prefix, signals))\n .pipe(gate.sink(name, \"stdout\"));\n });\n serverProcess.pipeStderr(stderr => {\n stderr\n .pipe(createWatchServerPrefixer(prefix))\n .pipe(gate.sink(name, \"stderr\"));\n });\n }\n\n startup.begin();\n\n // A watch process settling at all during startup means something went wrong, so give up\n // holding output back and replay it: the reason belongs on screen, not in a buffer.\n try {\n await Promise.all([\n ...allProcesses.map(({ process }) =>\n process.run().finally(() => startup.abandon())\n ),\n ...serverProcesses.map(p => p.run().finally(() => startup.abandon()))\n ]);\n } finally {\n startup.dispose();\n }\n }\n };\n }\n}\n\nexport const standaloneWatchCommand = createImplementation({\n abstraction: CliCommandFactory,\n implementation: StandaloneWatchCommand,\n dependencies: [GetProjectSdkService, StdioService, UiService, DefaultAppsService]\n});\n"],"names":["StandaloneWatchCommand","getProjectSdkService","stdioService","uiService","defaultAppsService","params","stdio","ui","apps","devProxyUrls","reserveDevProxyPorts","pointAppsAtDevProxy","projectSdk","process","gated","gate","WatchOutputGate","startup","summary","WatchSummary","Date","undefined","WatchStartup","processLists","serverProcesses","app","packagesWatcher","serversWatcher","whitelist","Array","Boolean","allProcesses","processes","process1","listenerCount","watchProcess","name","prefix","chalk","colorForString","signals","url","stdout","createPrefixer","stderr","serverProcess","createWatchServerPrefixer","Promise","p","standaloneWatchCommand","createImplementation","CliCommandFactory","GetProjectSdkService","StdioService","UiService","DefaultAppsService"],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Writable } from "node:stream";
|
|
2
|
+
import { type StdioService, type UiService } from "@webiny/cli-core/abstractions/index.js";
|
|
3
|
+
type Target = "stdout" | "stderr";
|
|
4
|
+
/**
|
|
5
|
+
* Holds child process output back while the apps boot, so the startup burst (every package announcing
|
|
6
|
+
* its first build, the api waiting for that build to land, rsbuild's banner) doesn't bury the summary
|
|
7
|
+
* of what came up where.
|
|
8
|
+
*
|
|
9
|
+
* What gets dropped is startup chatter from a source that never printed anything resembling a problem.
|
|
10
|
+
* A source that did is replayed from that line on — the rest of the source, not just the matching line,
|
|
11
|
+
* so multi-line compiler diagnostics stay intact and in order. Anything that goes wrong before the apps
|
|
12
|
+
* are up (a process exiting, the wait timing out, too much output to hold) opens the gate and replays
|
|
13
|
+
* everything.
|
|
14
|
+
*
|
|
15
|
+
* So the guarantee is only as good as `PROBLEM` above. It errs towards replaying: worth widening if
|
|
16
|
+
* something turns up that a developer needed to see and didn't.
|
|
17
|
+
*/
|
|
18
|
+
export declare class WatchOutputGate {
|
|
19
|
+
private stdio;
|
|
20
|
+
private ui;
|
|
21
|
+
private options;
|
|
22
|
+
private readonly buffered;
|
|
23
|
+
/** Source -> index into `buffered` of the first line that looked like a problem. */
|
|
24
|
+
private readonly problemSources;
|
|
25
|
+
private opened;
|
|
26
|
+
/**
|
|
27
|
+
* `onActivity` fires for every line buffered while the gate is closed, so the caller can tell a
|
|
28
|
+
* still-booting app from a finished one and wait for the output to go quiet before opening.
|
|
29
|
+
*/
|
|
30
|
+
constructor(stdio: StdioService.Interface, ui: UiService.Interface, options?: {
|
|
31
|
+
open?: boolean;
|
|
32
|
+
onActivity?: () => void;
|
|
33
|
+
});
|
|
34
|
+
/**
|
|
35
|
+
* A writable to pipe one labelled child stream into. `source` groups lines that belong together
|
|
36
|
+
* (a package's build output, a server's log) so they can be replayed as a unit.
|
|
37
|
+
*/
|
|
38
|
+
sink(source: string, target: Target): Writable;
|
|
39
|
+
/**
|
|
40
|
+
* Start writing output through. Replays the sources that reported a problem — or, with
|
|
41
|
+
* `replayAll`, the entire buffer, for when the apps never reached a known-good state.
|
|
42
|
+
*/
|
|
43
|
+
open(options?: {
|
|
44
|
+
replayAll?: boolean;
|
|
45
|
+
}): void;
|
|
46
|
+
private accept;
|
|
47
|
+
private stream;
|
|
48
|
+
}
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Writable } from "node:stream";
|
|
2
|
+
import { stripAnsi } from "./terminalPrefix.js";
|
|
3
|
+
const PROBLEM = /\w*(?:error|warn|fail)\w*|deprecated/i;
|
|
4
|
+
const RESOLVED = /\b(?:no|0)\s+(?:\w+\s+)?(?:errors?|warnings?|problems?)\b/gi;
|
|
5
|
+
const looksLikeProblem = (line)=>PROBLEM.test(line.replace(RESOLVED, ""));
|
|
6
|
+
const MAX_BUFFERED_LINES = 5000;
|
|
7
|
+
class WatchOutputGate {
|
|
8
|
+
constructor(stdio, ui, options = {}){
|
|
9
|
+
this.stdio = stdio;
|
|
10
|
+
this.ui = ui;
|
|
11
|
+
this.options = options;
|
|
12
|
+
this.buffered = [];
|
|
13
|
+
this.problemSources = new Map();
|
|
14
|
+
this.opened = options.open ?? false;
|
|
15
|
+
}
|
|
16
|
+
sink(source, target) {
|
|
17
|
+
return new Writable({
|
|
18
|
+
write: (chunk, _encoding, callback)=>{
|
|
19
|
+
this.accept(source, target, chunk.toString());
|
|
20
|
+
callback();
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
open(options = {}) {
|
|
25
|
+
if (this.opened) return;
|
|
26
|
+
this.opened = true;
|
|
27
|
+
const replay = this.buffered.filter(({ source }, index)=>{
|
|
28
|
+
if (options.replayAll) return true;
|
|
29
|
+
const from = this.problemSources.get(source);
|
|
30
|
+
return void 0 !== from && index >= from;
|
|
31
|
+
});
|
|
32
|
+
this.buffered.length = 0;
|
|
33
|
+
if (0 === replay.length) return;
|
|
34
|
+
this.ui.warning("Reported while starting up:");
|
|
35
|
+
for (const { target, line } of replay)this.stream(target).write(`${line}\n`);
|
|
36
|
+
this.ui.text("");
|
|
37
|
+
}
|
|
38
|
+
accept(source, target, text) {
|
|
39
|
+
if (this.opened) return void this.stream(target).write(text);
|
|
40
|
+
for (const line of text.split(/\r?\n/))if (line.trim()) {
|
|
41
|
+
if (!this.problemSources.has(source) && looksLikeProblem(stripAnsi(line))) this.problemSources.set(source, this.buffered.length);
|
|
42
|
+
this.buffered.push({
|
|
43
|
+
source,
|
|
44
|
+
target,
|
|
45
|
+
line
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (this.buffered.length > MAX_BUFFERED_LINES) return void this.open({
|
|
49
|
+
replayAll: true
|
|
50
|
+
});
|
|
51
|
+
this.options.onActivity?.();
|
|
52
|
+
}
|
|
53
|
+
stream(target) {
|
|
54
|
+
return "stdout" === target ? this.stdio.getStdout() : this.stdio.getStderr();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export { WatchOutputGate };
|
|
58
|
+
|
|
59
|
+
//# sourceMappingURL=WatchOutputGate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/WatchOutputGate.js","sources":["../../src/features/WatchOutputGate.ts"],"sourcesContent":["import { Writable } from \"node:stream\";\nimport { type StdioService, type UiService } from \"@webiny/cli-core/abstractions/index.js\";\nimport { stripAnsi } from \"./terminalPrefix.js\";\n\n/**\n * Lines that make a source worth replaying once the gate opens. Substring matching, not whole words:\n * `\\berror\\b` misses `TypeError`, `SyntaxError` and every other named error class, which is exactly\n * the output that must not be swallowed. A false positive costs a few extra lines on screen.\n */\nconst PROBLEM = /\\w*(?:error|warn|fail)\\w*|deprecated/i;\n\n/**\n * Counts of zero, stripped before the match above runs. Tools announce a clean build as \"Found 0\n * errors.\" or \"0 errors, 0 warnings\", which would otherwise flag every successful compile. Stripped\n * rather than tested as a whole line, so \"3 errors and 0 warnings\" still counts as a problem.\n */\nconst RESOLVED = /\\b(?:no|0)\\s+(?:\\w+\\s+)?(?:errors?|warnings?|problems?)\\b/gi;\n\nconst looksLikeProblem = (line: string) => PROBLEM.test(line.replace(RESOLVED, \"\"));\n\n/**\n * Above this many buffered lines, give up on holding output back and switch to live. A startup this\n * noisy is worth watching as it happens, and it keeps the buffer from growing without bound.\n */\nconst MAX_BUFFERED_LINES = 5000;\n\ntype Target = \"stdout\" | \"stderr\";\n\ninterface IBufferedLine {\n source: string;\n target: Target;\n line: string;\n}\n\n/**\n * Holds child process output back while the apps boot, so the startup burst (every package announcing\n * its first build, the api waiting for that build to land, rsbuild's banner) doesn't bury the summary\n * of what came up where.\n *\n * What gets dropped is startup chatter from a source that never printed anything resembling a problem.\n * A source that did is replayed from that line on — the rest of the source, not just the matching line,\n * so multi-line compiler diagnostics stay intact and in order. Anything that goes wrong before the apps\n * are up (a process exiting, the wait timing out, too much output to hold) opens the gate and replays\n * everything.\n *\n * So the guarantee is only as good as `PROBLEM` above. It errs towards replaying: worth widening if\n * something turns up that a developer needed to see and didn't.\n */\nexport class WatchOutputGate {\n private readonly buffered: IBufferedLine[] = [];\n\n /** Source -> index into `buffered` of the first line that looked like a problem. */\n private readonly problemSources = new Map<string, number>();\n private opened: boolean;\n\n /**\n * `onActivity` fires for every line buffered while the gate is closed, so the caller can tell a\n * still-booting app from a finished one and wait for the output to go quiet before opening.\n */\n constructor(\n private stdio: StdioService.Interface,\n private ui: UiService.Interface,\n private options: { open?: boolean; onActivity?: () => void } = {}\n ) {\n this.opened = options.open ?? false;\n }\n\n /**\n * A writable to pipe one labelled child stream into. `source` groups lines that belong together\n * (a package's build output, a server's log) so they can be replayed as a unit.\n */\n sink(source: string, target: Target) {\n return new Writable({\n write: (chunk, _encoding, callback) => {\n this.accept(source, target, chunk.toString());\n callback();\n }\n });\n }\n\n /**\n * Start writing output through. Replays the sources that reported a problem — or, with\n * `replayAll`, the entire buffer, for when the apps never reached a known-good state.\n */\n open(options: { replayAll?: boolean } = {}) {\n if (this.opened) {\n return;\n }\n\n this.opened = true;\n\n const replay = this.buffered.filter(({ source }, index) => {\n if (options.replayAll) {\n return true;\n }\n\n const from = this.problemSources.get(source);\n return from !== undefined && index >= from;\n });\n\n this.buffered.length = 0;\n\n if (replay.length === 0) {\n return;\n }\n\n this.ui.warning(\"Reported while starting up:\");\n for (const { target, line } of replay) {\n this.stream(target).write(`${line}\\n`);\n }\n // Keeps the replayed block from running straight into whatever the caller prints next.\n this.ui.text(\"\");\n }\n\n private accept(source: string, target: Target, text: string) {\n if (this.opened) {\n this.stream(target).write(text);\n return;\n }\n\n // The prefixers upstream emit whole lines, but a readable side is free to coalesce them into\n // one chunk — so split again rather than assuming one chunk is one line.\n for (const line of text.split(/\\r?\\n/)) {\n if (!line.trim()) {\n continue;\n }\n\n if (!this.problemSources.has(source) && looksLikeProblem(stripAnsi(line))) {\n this.problemSources.set(source, this.buffered.length);\n }\n\n this.buffered.push({ source, target, line });\n }\n\n if (this.buffered.length > MAX_BUFFERED_LINES) {\n this.open({ replayAll: true });\n return;\n }\n\n this.options.onActivity?.();\n }\n\n private stream(target: Target) {\n return target === \"stdout\" ? this.stdio.getStdout() : this.stdio.getStderr();\n }\n}\n"],"names":["PROBLEM","RESOLVED","looksLikeProblem","line","MAX_BUFFERED_LINES","WatchOutputGate","stdio","ui","options","Map","source","target","Writable","chunk","_encoding","callback","replay","index","from","undefined","text","stripAnsi"],"mappings":";;AASA,MAAMA,UAAU;AAOhB,MAAMC,WAAW;AAEjB,MAAMC,mBAAmB,CAACC,OAAiBH,QAAQ,IAAI,CAACG,KAAK,OAAO,CAACF,UAAU;AAM/E,MAAMG,qBAAqB;AAwBpB,MAAMC;IAWT,YACYC,KAA6B,EAC7BC,EAAuB,EACvBC,UAAuD,CAAC,CAAC,CACnE;aAHUF,KAAK,GAALA;aACAC,EAAE,GAAFA;aACAC,OAAO,GAAPA;aAbK,QAAQ,GAAoB,EAAE;QAEmC,KACjE,cAAc,GAAG,IAAIC;QAYlC,IAAI,CAAC,MAAM,GAAGD,QAAQ,IAAI,IAAI;IAClC;IAMA,KAAKE,MAAc,EAAEC,MAAc,EAAE;QACjC,OAAO,IAAIC,SAAS;YAChB,OAAO,CAACC,OAAOC,WAAWC;gBACtB,IAAI,CAAC,MAAM,CAACL,QAAQC,QAAQE,MAAM,QAAQ;gBAC1CE;YACJ;QACJ;IACJ;IAMA,KAAKP,UAAmC,CAAC,CAAC,EAAE;QACxC,IAAI,IAAI,CAAC,MAAM,EACX;QAGJ,IAAI,CAAC,MAAM,GAAG;QAEd,MAAMQ,SAAS,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAEN,MAAM,EAAE,EAAEO;YAC7C,IAAIT,QAAQ,SAAS,EACjB,OAAO;YAGX,MAAMU,OAAO,IAAI,CAAC,cAAc,CAAC,GAAG,CAACR;YACrC,OAAOQ,AAASC,WAATD,QAAsBD,SAASC;QAC1C;QAEA,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG;QAEvB,IAAIF,AAAkB,MAAlBA,OAAO,MAAM,EACb;QAGJ,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;QAChB,KAAK,MAAM,EAAEL,MAAM,EAAER,IAAI,EAAE,IAAIa,OAC3B,IAAI,CAAC,MAAM,CAACL,QAAQ,KAAK,CAAC,GAAGR,KAAK,EAAE,CAAC;QAGzC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;IACjB;IAEQ,OAAOO,MAAc,EAAEC,MAAc,EAAES,IAAY,EAAE;QACzD,IAAI,IAAI,CAAC,MAAM,EAAE,YACb,IAAI,CAAC,MAAM,CAACT,QAAQ,KAAK,CAACS;QAM9B,KAAK,MAAMjB,QAAQiB,KAAK,KAAK,CAAC,SAC1B,IAAKjB,KAAK,IAAI;YAId,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAACO,WAAWR,iBAAiBmB,UAAUlB,QAC/D,IAAI,CAAC,cAAc,CAAC,GAAG,CAACO,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM;YAGxD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAEA;gBAAQC;gBAAQR;YAAK;;QAG9C,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAGC,oBAAoB,YAC3C,IAAI,CAAC,IAAI,CAAC;YAAE,WAAW;QAAK;QAIhC,IAAI,CAAC,OAAO,CAAC,UAAU;IAC3B;IAEQ,OAAOO,MAAc,EAAE;QAC3B,OAAOA,AAAW,aAAXA,SAAsB,IAAI,CAAC,KAAK,CAAC,SAAS,KAAK,IAAI,CAAC,KAAK,CAAC,SAAS;IAC9E;AACJ"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type WatchOutputGate } from "./WatchOutputGate.js";
|
|
2
|
+
import { type WatchSummary } from "./WatchSummary.js";
|
|
3
|
+
/**
|
|
4
|
+
* Decides when the startup phase is over, and releases the gate and the summary when it is.
|
|
5
|
+
*
|
|
6
|
+
* The gate knows what to replay and the summary knows what each app reported, but neither knows *when*
|
|
7
|
+
* to act, and answering that means three interacting timers over shared mutable state. That belongs in
|
|
8
|
+
* one object rather than a handful of closures in the command handler.
|
|
9
|
+
*
|
|
10
|
+
* The command feeds it two events — output was held back, and an app reported something — and one
|
|
11
|
+
* failure signal.
|
|
12
|
+
*/
|
|
13
|
+
export declare class WatchStartup {
|
|
14
|
+
private gate;
|
|
15
|
+
private summary;
|
|
16
|
+
private quietTimer;
|
|
17
|
+
private readyTimer;
|
|
18
|
+
private startupTimer;
|
|
19
|
+
private released;
|
|
20
|
+
constructor(gate: WatchOutputGate, summary: WatchSummary | undefined);
|
|
21
|
+
/** Start waiting. Arms the outer time limit on the whole startup phase. */
|
|
22
|
+
begin(): void;
|
|
23
|
+
/** A line was held back. Startup isn't finished while output is still arriving. */
|
|
24
|
+
noteOutput(): void;
|
|
25
|
+
/** An app reported a URL, or that it finished starting. */
|
|
26
|
+
noteProgress(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Startup never reached a known-good state — a watcher exited, or the wait ran long. Replay
|
|
29
|
+
* everything so the reason is on screen.
|
|
30
|
+
*
|
|
31
|
+
* Deliberately does not mark the phase released: if the apps do come up after this, the summary is
|
|
32
|
+
* still worth printing. This only stops holding output back.
|
|
33
|
+
*/
|
|
34
|
+
abandon(): void;
|
|
35
|
+
/** Nothing is waiting on these timers any more. */
|
|
36
|
+
dispose(): void;
|
|
37
|
+
private waitForQuiet;
|
|
38
|
+
private release;
|
|
39
|
+
private clearTimers;
|
|
40
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const STARTUP_TIMEOUT = 180000;
|
|
2
|
+
const QUIET_PERIOD = 750;
|
|
3
|
+
const READY_TIMEOUT = 15000;
|
|
4
|
+
class WatchStartup {
|
|
5
|
+
constructor(gate, summary){
|
|
6
|
+
this.gate = gate;
|
|
7
|
+
this.summary = summary;
|
|
8
|
+
this.released = false;
|
|
9
|
+
}
|
|
10
|
+
begin() {
|
|
11
|
+
this.startupTimer = setTimeout(()=>this.abandon(), STARTUP_TIMEOUT);
|
|
12
|
+
this.startupTimer.unref();
|
|
13
|
+
}
|
|
14
|
+
noteOutput() {
|
|
15
|
+
this.waitForQuiet();
|
|
16
|
+
}
|
|
17
|
+
noteProgress() {
|
|
18
|
+
if (this.released) return;
|
|
19
|
+
if (this.summary?.isSettled) return void this.waitForQuiet();
|
|
20
|
+
if (this.summary?.hasAllUrls && !this.readyTimer) {
|
|
21
|
+
this.readyTimer = setTimeout(()=>this.release(), READY_TIMEOUT);
|
|
22
|
+
this.readyTimer.unref();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
abandon() {
|
|
26
|
+
this.clearTimers();
|
|
27
|
+
this.gate.open({
|
|
28
|
+
replayAll: true
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
dispose() {
|
|
32
|
+
this.clearTimers();
|
|
33
|
+
}
|
|
34
|
+
waitForQuiet() {
|
|
35
|
+
if (this.released || !this.summary?.isSettled) return;
|
|
36
|
+
clearTimeout(this.quietTimer);
|
|
37
|
+
this.quietTimer = setTimeout(()=>this.release(), QUIET_PERIOD);
|
|
38
|
+
this.quietTimer.unref();
|
|
39
|
+
}
|
|
40
|
+
release() {
|
|
41
|
+
if (this.released) return;
|
|
42
|
+
this.released = true;
|
|
43
|
+
this.clearTimers();
|
|
44
|
+
this.gate.open();
|
|
45
|
+
this.summary?.print();
|
|
46
|
+
}
|
|
47
|
+
clearTimers() {
|
|
48
|
+
clearTimeout(this.quietTimer);
|
|
49
|
+
clearTimeout(this.readyTimer);
|
|
50
|
+
clearTimeout(this.startupTimer);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export { WatchStartup };
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=WatchStartup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/WatchStartup.js","sources":["../../src/features/WatchStartup.ts"],"sourcesContent":["import { type WatchOutputGate } from \"./WatchOutputGate.js\";\nimport { type WatchSummary } from \"./WatchSummary.js\";\n\n/**\n * How long to hold startup output back before giving up and showing it live. Generous, because the\n * first build compiles every package in the app — a developer staring at a silent terminal is worse\n * than a noisy one.\n */\nconst STARTUP_TIMEOUT = 180 * 1000;\n\n/**\n * How long the apps have to stay quiet, after they all report being up, before the summary is printed.\n * Reporting \"up\" and going quiet aren't the same moment: the api logs a little more after it starts\n * listening, and the last package build can land after that. Without this, those stragglers print\n * underneath the summary, which is exactly what the summary exists to avoid.\n */\nconst QUIET_PERIOD = 750;\n\n/**\n * Fallback for when the apps report their URLs but never say they finished starting — a tool changing\n * the wording of its ready line shouldn't mean output stays buffered until `STARTUP_TIMEOUT`.\n */\nconst READY_TIMEOUT = 15 * 1000;\n\n/**\n * Decides when the startup phase is over, and releases the gate and the summary when it is.\n *\n * The gate knows what to replay and the summary knows what each app reported, but neither knows *when*\n * to act, and answering that means three interacting timers over shared mutable state. That belongs in\n * one object rather than a handful of closures in the command handler.\n *\n * The command feeds it two events — output was held back, and an app reported something — and one\n * failure signal.\n */\nexport class WatchStartup {\n private quietTimer: NodeJS.Timeout | undefined;\n private readyTimer: NodeJS.Timeout | undefined;\n private startupTimer: NodeJS.Timeout | undefined;\n private released = false;\n\n constructor(\n private gate: WatchOutputGate,\n private summary: WatchSummary | undefined\n ) {}\n\n /** Start waiting. Arms the outer time limit on the whole startup phase. */\n begin() {\n this.startupTimer = setTimeout(() => this.abandon(), STARTUP_TIMEOUT);\n this.startupTimer.unref();\n }\n\n /** A line was held back. Startup isn't finished while output is still arriving. */\n noteOutput() {\n this.waitForQuiet();\n }\n\n /** An app reported a URL, or that it finished starting. */\n noteProgress() {\n if (this.released) {\n return;\n }\n\n if (this.summary?.isSettled) {\n this.waitForQuiet();\n return;\n }\n\n if (this.summary?.hasAllUrls && !this.readyTimer) {\n this.readyTimer = setTimeout(() => this.release(), READY_TIMEOUT);\n this.readyTimer.unref();\n }\n }\n\n /**\n * Startup never reached a known-good state — a watcher exited, or the wait ran long. Replay\n * everything so the reason is on screen.\n *\n * Deliberately does not mark the phase released: if the apps do come up after this, the summary is\n * still worth printing. This only stops holding output back.\n */\n abandon() {\n this.clearTimers();\n this.gate.open({ replayAll: true });\n }\n\n /** Nothing is waiting on these timers any more. */\n dispose() {\n this.clearTimers();\n }\n\n private waitForQuiet() {\n if (this.released || !this.summary?.isSettled) {\n return;\n }\n\n clearTimeout(this.quietTimer);\n this.quietTimer = setTimeout(() => this.release(), QUIET_PERIOD);\n this.quietTimer.unref();\n }\n\n private release() {\n if (this.released) {\n return;\n }\n\n this.released = true;\n this.clearTimers();\n\n // Both are idempotent, so the failure paths above can call into them too.\n this.gate.open();\n this.summary?.print();\n }\n\n private clearTimers() {\n clearTimeout(this.quietTimer);\n clearTimeout(this.readyTimer);\n clearTimeout(this.startupTimer);\n }\n}\n"],"names":["STARTUP_TIMEOUT","QUIET_PERIOD","READY_TIMEOUT","WatchStartup","gate","summary","setTimeout","clearTimeout"],"mappings":"AAQA,MAAMA,kBAAkB;AAQxB,MAAMC,eAAe;AAMrB,MAAMC,gBAAgB;AAYf,MAAMC;IAMT,YACYC,IAAqB,EACrBC,OAAiC,CAC3C;aAFUD,IAAI,GAAJA;aACAC,OAAO,GAAPA;aAJJ,QAAQ,GAAG;IAKhB;IAGH,QAAQ;QACJ,IAAI,CAAC,YAAY,GAAGC,WAAW,IAAM,IAAI,CAAC,OAAO,IAAIN;QACrD,IAAI,CAAC,YAAY,CAAC,KAAK;IAC3B;IAGA,aAAa;QACT,IAAI,CAAC,YAAY;IACrB;IAGA,eAAe;QACX,IAAI,IAAI,CAAC,QAAQ,EACb;QAGJ,IAAI,IAAI,CAAC,OAAO,EAAE,WAAW,YACzB,IAAI,CAAC,YAAY;QAIrB,IAAI,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC,UAAU,EAAE;YAC9C,IAAI,CAAC,UAAU,GAAGM,WAAW,IAAM,IAAI,CAAC,OAAO,IAAIJ;YACnD,IAAI,CAAC,UAAU,CAAC,KAAK;QACzB;IACJ;IASA,UAAU;QACN,IAAI,CAAC,WAAW;QAChB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,WAAW;QAAK;IACrC;IAGA,UAAU;QACN,IAAI,CAAC,WAAW;IACpB;IAEQ,eAAe;QACnB,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAChC;QAGJK,aAAa,IAAI,CAAC,UAAU;QAC5B,IAAI,CAAC,UAAU,GAAGD,WAAW,IAAM,IAAI,CAAC,OAAO,IAAIL;QACnD,IAAI,CAAC,UAAU,CAAC,KAAK;IACzB;IAEQ,UAAU;QACd,IAAI,IAAI,CAAC,QAAQ,EACb;QAGJ,IAAI,CAAC,QAAQ,GAAG;QAChB,IAAI,CAAC,WAAW;QAGhB,IAAI,CAAC,IAAI,CAAC,IAAI;QACd,IAAI,CAAC,OAAO,EAAE;IAClB;IAEQ,cAAc;QAClBM,aAAa,IAAI,CAAC,UAAU;QAC5BA,aAAa,IAAI,CAAC,UAAU;QAC5BA,aAAa,IAAI,CAAC,YAAY;IAClC;AACJ"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type UiService } from "@webiny/cli-core/abstractions/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Collects what each watched app reports while starting, and prints one summary of where everything
|
|
4
|
+
* ended up. With api and admin sharing a single `webiny watch` process, their startup lines are buried
|
|
5
|
+
* in interleaved build output, so the summary is the one place that answers "what is running where".
|
|
6
|
+
*
|
|
7
|
+
* Two signals per app, because they don't arrive together. The URL comes early (the admin dev server
|
|
8
|
+
* binds its port and announces it well before the first build lands) while "done starting" comes later.
|
|
9
|
+
* Both are sniffed from the child processes rather than predicted, because build times aren't knowable
|
|
10
|
+
* and, without the proxy, neither port is settled up front either: the api auto-advances from 3002 if
|
|
11
|
+
* it's taken, and the admin dev server does the same from 3001.
|
|
12
|
+
*
|
|
13
|
+
* When the proxy is running there is exactly one URL to open, so that's all the summary says. The
|
|
14
|
+
* per-app URLs are still collected (they're how the summary knows the apps came up) but printing them
|
|
15
|
+
* only invites someone to use one, which is the confusion the proxy exists to remove. `--verbose`
|
|
16
|
+
* shows them for when something needs debugging.
|
|
17
|
+
*
|
|
18
|
+
* This is a collector, not a scheduler. It reports state changes through `onChange` and leaves the
|
|
19
|
+
* caller to decide when to act on them.
|
|
20
|
+
*/
|
|
21
|
+
export declare class WatchSummary {
|
|
22
|
+
private ui;
|
|
23
|
+
private startedAt;
|
|
24
|
+
private onChange;
|
|
25
|
+
private readonly expected;
|
|
26
|
+
private readonly urls;
|
|
27
|
+
private readonly ready;
|
|
28
|
+
private publicUrl;
|
|
29
|
+
private showAppUrls;
|
|
30
|
+
private printed;
|
|
31
|
+
constructor(ui: UiService.Interface, startedAt: number, onChange?: () => void);
|
|
32
|
+
expect(app: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* The single URL the proxy serves everything on. Known up front rather than sniffed, because the
|
|
35
|
+
* proxy binds its port before any app starts. Takes over the summary: from here on the per-app
|
|
36
|
+
* URLs are printed only when asked for.
|
|
37
|
+
*/
|
|
38
|
+
setPublicUrl(url: string, options?: {
|
|
39
|
+
showAppUrls?: boolean;
|
|
40
|
+
}): void;
|
|
41
|
+
/**
|
|
42
|
+
* Records the first URL an app reports. Later ones are ignored — a dev server can announce several
|
|
43
|
+
* (rsbuild prints a `Network:` URL right after the `Local:` one), and a restart re-announces.
|
|
44
|
+
*/
|
|
45
|
+
reportUrl(app: string, url: string): void;
|
|
46
|
+
/** Records that an app finished starting: its server is listening, or its first build landed. */
|
|
47
|
+
reportReady(app: string): void;
|
|
48
|
+
/** Every app has said where it is. Enough to print, but not necessarily done booting. */
|
|
49
|
+
get hasAllUrls(): boolean;
|
|
50
|
+
/** Every app has said where it is *and* that it finished starting. */
|
|
51
|
+
get isSettled(): boolean;
|
|
52
|
+
print(): void;
|
|
53
|
+
/** Sub-second startups read better in milliseconds; anything longer in seconds. */
|
|
54
|
+
private elapsed;
|
|
55
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const ACRONYMS = {
|
|
2
|
+
api: "API"
|
|
3
|
+
};
|
|
4
|
+
const displayName = (app)=>ACRONYMS[app] ?? app.charAt(0).toUpperCase() + app.slice(1);
|
|
5
|
+
const literal = (text)=>text.replace(/%/g, "%%");
|
|
6
|
+
class WatchSummary {
|
|
7
|
+
constructor(ui, startedAt, onChange = ()=>void 0){
|
|
8
|
+
this.ui = ui;
|
|
9
|
+
this.startedAt = startedAt;
|
|
10
|
+
this.onChange = onChange;
|
|
11
|
+
this.expected = [];
|
|
12
|
+
this.urls = new Map();
|
|
13
|
+
this.ready = new Set();
|
|
14
|
+
this.showAppUrls = true;
|
|
15
|
+
this.printed = false;
|
|
16
|
+
}
|
|
17
|
+
expect(app) {
|
|
18
|
+
if (!this.expected.includes(app)) this.expected.push(app);
|
|
19
|
+
}
|
|
20
|
+
setPublicUrl(url, options = {}) {
|
|
21
|
+
this.publicUrl = url;
|
|
22
|
+
this.showAppUrls = options.showAppUrls ?? false;
|
|
23
|
+
}
|
|
24
|
+
reportUrl(app, url) {
|
|
25
|
+
if (!this.expected.includes(app) || this.urls.has(app)) return;
|
|
26
|
+
this.urls.set(app, url.replace(/\/+$/, ""));
|
|
27
|
+
this.onChange();
|
|
28
|
+
}
|
|
29
|
+
reportReady(app) {
|
|
30
|
+
if (!this.expected.includes(app) || this.ready.has(app)) return;
|
|
31
|
+
this.ready.add(app);
|
|
32
|
+
this.onChange();
|
|
33
|
+
}
|
|
34
|
+
get hasAllUrls() {
|
|
35
|
+
return this.urls.size === this.expected.length;
|
|
36
|
+
}
|
|
37
|
+
get isSettled() {
|
|
38
|
+
return this.hasAllUrls && this.ready.size === this.expected.length;
|
|
39
|
+
}
|
|
40
|
+
print() {
|
|
41
|
+
if (this.printed || !this.hasAllUrls) return;
|
|
42
|
+
this.printed = true;
|
|
43
|
+
this.ui.emptyLine();
|
|
44
|
+
this.ui.success("Ready in %s", this.elapsed());
|
|
45
|
+
if (this.publicUrl) this.ui.success("Webiny is available at %s", this.publicUrl);
|
|
46
|
+
if (this.showAppUrls) {
|
|
47
|
+
const names = this.expected.map(displayName);
|
|
48
|
+
const width = Math.max(...names.map((name)=>name.length));
|
|
49
|
+
const indent = this.publicUrl ? " " : "";
|
|
50
|
+
this.expected.forEach((app, index)=>{
|
|
51
|
+
this.ui.success(literal(`${indent}${names[index].padEnd(width)} ${this.urls.get(app)}`));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
this.ui.emptyLine();
|
|
55
|
+
}
|
|
56
|
+
elapsed() {
|
|
57
|
+
const ms = Date.now() - this.startedAt;
|
|
58
|
+
return ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(1)}s`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export { WatchSummary };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=WatchSummary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/WatchSummary.js","sources":["../../src/features/WatchSummary.ts"],"sourcesContent":["import { type UiService } from \"@webiny/cli-core/abstractions/index.js\";\n\n/** App names a plain capitalize would get wrong. */\nconst ACRONYMS: Record<string, string> = { api: \"API\" };\n\nconst displayName = (app: string) => ACRONYMS[app] ?? app.charAt(0).toUpperCase() + app.slice(1);\n\n/**\n * `UiService` colorizes `%s` placeholders, which is the wrong emphasis for a table of values — the\n * whole row ends up one colour. Passing the row as a literal keeps the text in the terminal's default\n * colour and leaves only the `┃` rail coloured, so escape any `%` the row happens to contain.\n */\nconst literal = (text: string) => text.replace(/%/g, \"%%\");\n\n/**\n * Collects what each watched app reports while starting, and prints one summary of where everything\n * ended up. With api and admin sharing a single `webiny watch` process, their startup lines are buried\n * in interleaved build output, so the summary is the one place that answers \"what is running where\".\n *\n * Two signals per app, because they don't arrive together. The URL comes early (the admin dev server\n * binds its port and announces it well before the first build lands) while \"done starting\" comes later.\n * Both are sniffed from the child processes rather than predicted, because build times aren't knowable\n * and, without the proxy, neither port is settled up front either: the api auto-advances from 3002 if\n * it's taken, and the admin dev server does the same from 3001.\n *\n * When the proxy is running there is exactly one URL to open, so that's all the summary says. The\n * per-app URLs are still collected (they're how the summary knows the apps came up) but printing them\n * only invites someone to use one, which is the confusion the proxy exists to remove. `--verbose`\n * shows them for when something needs debugging.\n *\n * This is a collector, not a scheduler. It reports state changes through `onChange` and leaves the\n * caller to decide when to act on them.\n */\nexport class WatchSummary {\n private readonly expected: string[] = [];\n private readonly urls = new Map<string, string>();\n private readonly ready = new Set<string>();\n private publicUrl: string | undefined;\n private showAppUrls = true;\n private printed = false;\n\n constructor(\n private ui: UiService.Interface,\n private startedAt: number,\n private onChange: () => void = () => undefined\n ) {}\n\n expect(app: string) {\n if (!this.expected.includes(app)) {\n this.expected.push(app);\n }\n }\n\n /**\n * The single URL the proxy serves everything on. Known up front rather than sniffed, because the\n * proxy binds its port before any app starts. Takes over the summary: from here on the per-app\n * URLs are printed only when asked for.\n */\n setPublicUrl(url: string, options: { showAppUrls?: boolean } = {}) {\n this.publicUrl = url;\n this.showAppUrls = options.showAppUrls ?? false;\n }\n\n /**\n * Records the first URL an app reports. Later ones are ignored — a dev server can announce several\n * (rsbuild prints a `Network:` URL right after the `Local:` one), and a restart re-announces.\n */\n reportUrl(app: string, url: string) {\n if (!this.expected.includes(app) || this.urls.has(app)) {\n return;\n }\n\n // The two sources disagree on trailing slashes (rsbuild prints one, the api runner doesn't) —\n // normalize so the summary doesn't look sloppy.\n this.urls.set(app, url.replace(/\\/+$/, \"\"));\n this.onChange();\n }\n\n /** Records that an app finished starting: its server is listening, or its first build landed. */\n reportReady(app: string) {\n if (!this.expected.includes(app) || this.ready.has(app)) {\n return;\n }\n\n this.ready.add(app);\n this.onChange();\n }\n\n /** Every app has said where it is. Enough to print, but not necessarily done booting. */\n get hasAllUrls() {\n return this.urls.size === this.expected.length;\n }\n\n /** Every app has said where it is *and* that it finished starting. */\n get isSettled() {\n return this.hasAllUrls && this.ready.size === this.expected.length;\n }\n\n print() {\n if (this.printed || !this.hasAllUrls) {\n return;\n }\n\n this.printed = true;\n\n this.ui.emptyLine();\n this.ui.success(`Ready in %s`, this.elapsed());\n\n if (this.publicUrl) {\n this.ui.success(`Webiny is available at %s`, this.publicUrl);\n }\n\n if (this.showAppUrls) {\n const names = this.expected.map(displayName);\n const width = Math.max(...names.map(name => name.length));\n // Indented when there's a public URL above them, so it stays clear which one to open.\n const indent = this.publicUrl ? \" \" : \"\";\n\n this.expected.forEach((app, index) => {\n this.ui.success(\n literal(`${indent}${names[index].padEnd(width)} ${this.urls.get(app)}`)\n );\n });\n }\n\n this.ui.emptyLine();\n }\n\n /** Sub-second startups read better in milliseconds; anything longer in seconds. */\n private elapsed() {\n const ms = Date.now() - this.startedAt;\n return ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(1)}s`;\n }\n}\n"],"names":["ACRONYMS","displayName","app","literal","text","WatchSummary","ui","startedAt","onChange","undefined","Map","Set","url","options","names","width","Math","name","indent","index","ms","Date"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Transform } from "node:stream";
|
|
2
|
+
import { type ICreatePrefixerOptions } from "./terminalPrefix.js";
|
|
3
|
+
/**
|
|
4
|
+
* Line-oriented Transform for a watch-mode server stream: drop Node `--watch` control chatter, turn
|
|
5
|
+
* the runner's "listening on <url>" into a clean first-time/reload line, and prefix everything else
|
|
6
|
+
* with `<prefix>: `. Real logs + error stacks pass through, just prefixed. Applied per stream — the
|
|
7
|
+
* `started` flag lives on the stdout instance (which carries the "listening" line); the stderr
|
|
8
|
+
* instance just drops noise + prefixes.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createWatchServerPrefixer(prefix: string, options?: ICreatePrefixerOptions): Transform;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Transform } from "node:stream";
|
|
2
|
+
import { LINE_BREAK, MAX_LINE_LENGTH } from "./terminalPrefix.js";
|
|
3
|
+
const WATCH_NOISE = /^(Restarting|Completed running|Failed running)\b|Waiting for file changes before restarting/;
|
|
4
|
+
const LISTENING = /listening on (\S+)/i;
|
|
5
|
+
function createWatchServerPrefixer(prefix, options = {}) {
|
|
6
|
+
const { onUrl, onReady } = options;
|
|
7
|
+
let started = false;
|
|
8
|
+
let buffer = "";
|
|
9
|
+
return new Transform({
|
|
10
|
+
transform (chunk, _encoding, callback) {
|
|
11
|
+
buffer += chunk.toString();
|
|
12
|
+
const lines = buffer.split(LINE_BREAK);
|
|
13
|
+
buffer = lines.pop() ?? "";
|
|
14
|
+
if (buffer.length > MAX_LINE_LENGTH) {
|
|
15
|
+
lines.push(buffer);
|
|
16
|
+
buffer = "";
|
|
17
|
+
}
|
|
18
|
+
for (const line of lines){
|
|
19
|
+
if (!line.trim() || WATCH_NOISE.test(line)) continue;
|
|
20
|
+
const listening = line.match(LISTENING);
|
|
21
|
+
if (listening) {
|
|
22
|
+
if (!started) onUrl?.(listening[1]);
|
|
23
|
+
onReady?.();
|
|
24
|
+
this.push(started ? `${prefix}: ↻ reloaded\n` : `${prefix}: ✔ listening on ${listening[1]}\n`);
|
|
25
|
+
started = true;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
this.push(`${prefix}: ${line}\n`);
|
|
29
|
+
}
|
|
30
|
+
callback();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export { createWatchServerPrefixer };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=serverProcesses.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/serverProcesses.js","sources":["../../src/features/serverProcesses.ts"],"sourcesContent":["import { Transform } from \"node:stream\";\nimport { type ICreatePrefixerOptions, LINE_BREAK, MAX_LINE_LENGTH } from \"./terminalPrefix.js\";\n\n// Node's built-in `--watch` prints control chatter we don't surface verbatim.\nconst WATCH_NOISE =\n /^(Restarting|Completed running|Failed running)\\b|Waiting for file changes before restarting/;\nconst LISTENING = /listening on (\\S+)/i;\n\n/**\n * Line-oriented Transform for a watch-mode server stream: drop Node `--watch` control chatter, turn\n * the runner's \"listening on <url>\" into a clean first-time/reload line, and prefix everything else\n * with `<prefix>: `. Real logs + error stacks pass through, just prefixed. Applied per stream — the\n * `started` flag lives on the stdout instance (which carries the \"listening\" line); the stderr\n * instance just drops noise + prefixes.\n */\nexport function createWatchServerPrefixer(prefix: string, options: ICreatePrefixerOptions = {}) {\n const { onUrl, onReady } = options;\n let started = false;\n let buffer = \"\";\n\n return new Transform({\n transform(chunk, _encoding, callback) {\n buffer += chunk.toString();\n const lines = buffer.split(LINE_BREAK);\n buffer = lines.pop() ?? \"\";\n\n if (buffer.length > MAX_LINE_LENGTH) {\n lines.push(buffer);\n buffer = \"\";\n }\n\n for (const line of lines) {\n if (!line.trim() || WATCH_NOISE.test(line)) {\n continue;\n }\n\n const listening = line.match(LISTENING);\n if (listening) {\n // For a server, listening *is* done starting — both signals come off this one line.\n if (!started) {\n onUrl?.(listening[1]);\n }\n onReady?.();\n this.push(\n started\n ? `${prefix}: ↻ reloaded\\n`\n : `${prefix}: ✔ listening on ${listening[1]}\\n`\n );\n started = true;\n continue;\n }\n\n this.push(`${prefix}: ${line}\\n`);\n }\n\n callback();\n }\n });\n}\n"],"names":["WATCH_NOISE","LISTENING","createWatchServerPrefixer","prefix","options","onUrl","onReady","started","buffer","Transform","chunk","_encoding","callback","lines","LINE_BREAK","MAX_LINE_LENGTH","line","listening"],"mappings":";;AAIA,MAAMA,cACF;AACJ,MAAMC,YAAY;AASX,SAASC,0BAA0BC,MAAc,EAAEC,UAAkC,CAAC,CAAC;IAC1F,MAAM,EAAEC,KAAK,EAAEC,OAAO,EAAE,GAAGF;IAC3B,IAAIG,UAAU;IACd,IAAIC,SAAS;IAEb,OAAO,IAAIC,UAAU;QACjB,WAAUC,KAAK,EAAEC,SAAS,EAAEC,QAAQ;YAChCJ,UAAUE,MAAM,QAAQ;YACxB,MAAMG,QAAQL,OAAO,KAAK,CAACM;YAC3BN,SAASK,MAAM,GAAG,MAAM;YAExB,IAAIL,OAAO,MAAM,GAAGO,iBAAiB;gBACjCF,MAAM,IAAI,CAACL;gBACXA,SAAS;YACb;YAEA,KAAK,MAAMQ,QAAQH,MAAO;gBACtB,IAAI,CAACG,KAAK,IAAI,MAAMhB,YAAY,IAAI,CAACgB,OACjC;gBAGJ,MAAMC,YAAYD,KAAK,KAAK,CAACf;gBAC7B,IAAIgB,WAAW;oBAEX,IAAI,CAACV,SACDF,QAAQY,SAAS,CAAC,EAAE;oBAExBX;oBACA,IAAI,CAAC,IAAI,CACLC,UACM,GAAGJ,OAAO,cAAc,CAAC,GACzB,GAAGA,OAAO,iBAAiB,EAAEc,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;oBAEvDV,UAAU;oBACV;gBACJ;gBAEA,IAAI,CAAC,IAAI,CAAC,GAAGJ,OAAO,EAAE,EAAEa,KAAK,EAAE,CAAC;YACpC;YAEAJ;QACJ;IACJ;AACJ"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Transform } from "node:stream";
|
|
2
|
+
/**
|
|
3
|
+
* Deterministically map a string (package/app name) to one of the palette colors, so the same name
|
|
4
|
+
* always gets the same prefix color across runs.
|
|
5
|
+
*/
|
|
6
|
+
export declare function colorForString(value: string): string;
|
|
7
|
+
export declare function stripAnsi(value: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Line terminators, bare `\r` included: progress output redraws with `\r` and never sends a newline,
|
|
10
|
+
* so splitting on `\n` alone would hold it back forever.
|
|
11
|
+
*/
|
|
12
|
+
export declare const LINE_BREAK: RegExp;
|
|
13
|
+
/**
|
|
14
|
+
* A line this long with no terminator in sight isn't a line any more. Flush it rather than let the
|
|
15
|
+
* buffer grow without bound.
|
|
16
|
+
*/
|
|
17
|
+
export declare const MAX_LINE_LENGTH = 8192;
|
|
18
|
+
export interface ICreatePrefixerOptions {
|
|
19
|
+
/**
|
|
20
|
+
* Called with the dev server URL the first time the stream announces one, so the caller can report
|
|
21
|
+
* where an app came up without having to predict its port.
|
|
22
|
+
*/
|
|
23
|
+
onUrl?: (url: string) => void;
|
|
24
|
+
/** Called when the stream reports that it finished starting. Fires again on later rebuilds. */
|
|
25
|
+
onReady?: () => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Line-oriented Transform that prepends `<prefix>: ` to every non-empty output line — used to label
|
|
29
|
+
* interleaved output from multiple child processes (watch build processes, serve api/admin servers).
|
|
30
|
+
*
|
|
31
|
+
* Partial lines are held back until their newline arrives: child output lands in arbitrary chunks, and
|
|
32
|
+
* prefixing each chunk as if it were a whole line breaks single log lines into several prefixed ones.
|
|
33
|
+
*/
|
|
34
|
+
export declare function createPrefixer(prefix: string, options?: ICreatePrefixerOptions): Transform;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Transform } from "node:stream";
|
|
2
|
+
const COLORS = [
|
|
3
|
+
"#00CC00",
|
|
4
|
+
"#00CC99",
|
|
5
|
+
"#00CCCC",
|
|
6
|
+
"#00CCFF",
|
|
7
|
+
"#3300CC",
|
|
8
|
+
"#3366CC",
|
|
9
|
+
"#33CC00",
|
|
10
|
+
"#33CC99",
|
|
11
|
+
"#6600CC",
|
|
12
|
+
"#66CC00",
|
|
13
|
+
"#9900CC",
|
|
14
|
+
"#99CC00",
|
|
15
|
+
"#CC0000",
|
|
16
|
+
"#CC0066",
|
|
17
|
+
"#CC3300",
|
|
18
|
+
"#CC6600",
|
|
19
|
+
"#CCCC00",
|
|
20
|
+
"#FF0000",
|
|
21
|
+
"#FF3300",
|
|
22
|
+
"#FF6600",
|
|
23
|
+
"#FF9900",
|
|
24
|
+
"#FFCC00"
|
|
25
|
+
];
|
|
26
|
+
function colorForString(value) {
|
|
27
|
+
let hash = 0;
|
|
28
|
+
for(let i = 0; i < value.length; i++){
|
|
29
|
+
hash = (hash << 5) - hash + value.charCodeAt(i);
|
|
30
|
+
hash |= 0;
|
|
31
|
+
}
|
|
32
|
+
return COLORS[Math.abs(hash) % COLORS.length];
|
|
33
|
+
}
|
|
34
|
+
const ANSI = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
35
|
+
const DEV_SERVER_URL = /\bLocal:\s+(https?:\/\/\S+)/;
|
|
36
|
+
const DEV_SERVER_READY = /\bready\b.*\bbuilt in\b/i;
|
|
37
|
+
function stripAnsi(value) {
|
|
38
|
+
return value.replace(ANSI, "");
|
|
39
|
+
}
|
|
40
|
+
const LINE_BREAK = /\r\n|\r|\n/;
|
|
41
|
+
const MAX_LINE_LENGTH = 8192;
|
|
42
|
+
function createPrefixer(prefix, options = {}) {
|
|
43
|
+
const { onUrl, onReady } = options;
|
|
44
|
+
let buffer = "";
|
|
45
|
+
return new Transform({
|
|
46
|
+
transform (chunk, _encoding, callback) {
|
|
47
|
+
buffer += chunk.toString();
|
|
48
|
+
const lines = buffer.split(LINE_BREAK);
|
|
49
|
+
buffer = lines.pop() ?? "";
|
|
50
|
+
if (buffer.length > MAX_LINE_LENGTH) {
|
|
51
|
+
lines.push(buffer);
|
|
52
|
+
buffer = "";
|
|
53
|
+
}
|
|
54
|
+
for (const line of lines)if (line.trim()) {
|
|
55
|
+
if (onUrl || onReady) {
|
|
56
|
+
const plain = stripAnsi(line);
|
|
57
|
+
const url = plain.match(DEV_SERVER_URL);
|
|
58
|
+
if (url) onUrl?.(url[1]);
|
|
59
|
+
else if (DEV_SERVER_READY.test(plain)) onReady?.();
|
|
60
|
+
}
|
|
61
|
+
this.push(`${prefix}: ${line}\n`);
|
|
62
|
+
}
|
|
63
|
+
callback();
|
|
64
|
+
},
|
|
65
|
+
flush (callback) {
|
|
66
|
+
if (buffer.trim()) this.push(`${prefix}: ${buffer}\n`);
|
|
67
|
+
buffer = "";
|
|
68
|
+
callback();
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
export { LINE_BREAK, MAX_LINE_LENGTH, colorForString, createPrefixer, stripAnsi };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=terminalPrefix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/terminalPrefix.js","sources":["../../src/features/terminalPrefix.ts"],"sourcesContent":["import { Transform } from \"node:stream\";\n\nconst COLORS = [\n \"#00CC00\",\n \"#00CC99\",\n \"#00CCCC\",\n \"#00CCFF\",\n \"#3300CC\",\n \"#3366CC\",\n \"#33CC00\",\n \"#33CC99\",\n \"#6600CC\",\n \"#66CC00\",\n \"#9900CC\",\n \"#99CC00\",\n \"#CC0000\",\n \"#CC0066\",\n \"#CC3300\",\n \"#CC6600\",\n \"#CCCC00\",\n \"#FF0000\",\n \"#FF3300\",\n \"#FF6600\",\n \"#FF9900\",\n \"#FFCC00\"\n];\n\n/**\n * Deterministically map a string (package/app name) to one of the palette colors, so the same name\n * always gets the same prefix color across runs.\n */\nexport function colorForString(value: string) {\n let hash = 0;\n for (let i = 0; i < value.length; i++) {\n hash = (hash << 5) - hash + value.charCodeAt(i);\n hash |= 0;\n }\n return COLORS[Math.abs(hash) % COLORS.length];\n}\n\n// SGR escape sequences, stripped before matching text so a colorized URL still parses. Assembled with\n// `fromCharCode` so the source holds no literal control character.\nconst ANSI = new RegExp(`${String.fromCharCode(27)}\\\\[[0-9;]*m`, \"g\");\n\n// The admin dev server (rsbuild) announces itself as `Local: http://localhost:3001`. Anchored on the\n// `Local:` label so the loopback URL wins over the `Network:` one printed right after it.\nconst DEV_SERVER_URL = /\\bLocal:\\s+(https?:\\/\\/\\S+)/;\n\n// ...and reports a finished compile as `ready built in 3.54s`. That lands seconds after the URL —\n// binding the port comes first, building the app second — so it, not the URL, is what \"done starting\"\n// means for the admin app.\nconst DEV_SERVER_READY = /\\bready\\b.*\\bbuilt in\\b/i;\n\nexport function stripAnsi(value: string) {\n return value.replace(ANSI, \"\");\n}\n\n/**\n * Line terminators, bare `\\r` included: progress output redraws with `\\r` and never sends a newline,\n * so splitting on `\\n` alone would hold it back forever.\n */\nexport const LINE_BREAK = /\\r\\n|\\r|\\n/;\n\n/**\n * A line this long with no terminator in sight isn't a line any more. Flush it rather than let the\n * buffer grow without bound.\n */\nexport const MAX_LINE_LENGTH = 8192;\n\nexport interface ICreatePrefixerOptions {\n /**\n * Called with the dev server URL the first time the stream announces one, so the caller can report\n * where an app came up without having to predict its port.\n */\n onUrl?: (url: string) => void;\n\n /** Called when the stream reports that it finished starting. Fires again on later rebuilds. */\n onReady?: () => void;\n}\n\n/**\n * Line-oriented Transform that prepends `<prefix>: ` to every non-empty output line — used to label\n * interleaved output from multiple child processes (watch build processes, serve api/admin servers).\n *\n * Partial lines are held back until their newline arrives: child output lands in arbitrary chunks, and\n * prefixing each chunk as if it were a whole line breaks single log lines into several prefixed ones.\n */\nexport function createPrefixer(prefix: string, options: ICreatePrefixerOptions = {}) {\n const { onUrl, onReady } = options;\n let buffer = \"\";\n\n return new Transform({\n transform(chunk, _encoding, callback) {\n buffer += chunk.toString();\n const lines = buffer.split(LINE_BREAK);\n buffer = lines.pop() ?? \"\";\n\n if (buffer.length > MAX_LINE_LENGTH) {\n lines.push(buffer);\n buffer = \"\";\n }\n\n for (const line of lines) {\n if (!line.trim()) {\n continue;\n }\n\n if (onUrl || onReady) {\n const plain = stripAnsi(line);\n\n const url = plain.match(DEV_SERVER_URL);\n if (url) {\n onUrl?.(url[1]);\n } else if (DEV_SERVER_READY.test(plain)) {\n onReady?.();\n }\n }\n\n this.push(`${prefix}: ${line}\\n`);\n }\n\n callback();\n },\n flush(callback) {\n if (buffer.trim()) {\n this.push(`${prefix}: ${buffer}\\n`);\n }\n buffer = \"\";\n callback();\n }\n });\n}\n"],"names":["COLORS","colorForString","value","hash","i","Math","ANSI","RegExp","String","DEV_SERVER_URL","DEV_SERVER_READY","stripAnsi","LINE_BREAK","MAX_LINE_LENGTH","createPrefixer","prefix","options","onUrl","onReady","buffer","Transform","chunk","_encoding","callback","lines","line","plain","url"],"mappings":";AAEA,MAAMA,SAAS;IACX;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACH;AAMM,SAASC,eAAeC,KAAa;IACxC,IAAIC,OAAO;IACX,IAAK,IAAIC,IAAI,GAAGA,IAAIF,MAAM,MAAM,EAAEE,IAAK;QACnCD,OAAQA,AAAAA,CAAAA,QAAQ,KAAKA,OAAOD,MAAM,UAAU,CAACE;QAC7CD,QAAQ;IACZ;IACA,OAAOH,MAAM,CAACK,KAAK,GAAG,CAACF,QAAQH,OAAO,MAAM,CAAC;AACjD;AAIA,MAAMM,OAAO,IAAIC,OAAO,GAAGC,OAAO,YAAY,CAAC,IAAI,WAAW,CAAC,EAAE;AAIjE,MAAMC,iBAAiB;AAKvB,MAAMC,mBAAmB;AAElB,SAASC,UAAUT,KAAa;IACnC,OAAOA,MAAM,OAAO,CAACI,MAAM;AAC/B;AAMO,MAAMM,aAAa;AAMnB,MAAMC,kBAAkB;AAoBxB,SAASC,eAAeC,MAAc,EAAEC,UAAkC,CAAC,CAAC;IAC/E,MAAM,EAAEC,KAAK,EAAEC,OAAO,EAAE,GAAGF;IAC3B,IAAIG,SAAS;IAEb,OAAO,IAAIC,UAAU;QACjB,WAAUC,KAAK,EAAEC,SAAS,EAAEC,QAAQ;YAChCJ,UAAUE,MAAM,QAAQ;YACxB,MAAMG,QAAQL,OAAO,KAAK,CAACP;YAC3BO,SAASK,MAAM,GAAG,MAAM;YAExB,IAAIL,OAAO,MAAM,GAAGN,iBAAiB;gBACjCW,MAAM,IAAI,CAACL;gBACXA,SAAS;YACb;YAEA,KAAK,MAAMM,QAAQD,MACf,IAAKC,KAAK,IAAI;gBAId,IAAIR,SAASC,SAAS;oBAClB,MAAMQ,QAAQf,UAAUc;oBAExB,MAAME,MAAMD,MAAM,KAAK,CAACjB;oBACxB,IAAIkB,KACAV,QAAQU,GAAG,CAAC,EAAE;yBACX,IAAIjB,iBAAiB,IAAI,CAACgB,QAC7BR;gBAER;gBAEA,IAAI,CAAC,IAAI,CAAC,GAAGH,OAAO,EAAE,EAAEU,KAAK,EAAE,CAAC;;YAGpCF;QACJ;QACA,OAAMA,QAAQ;YACV,IAAIJ,OAAO,IAAI,IACX,IAAI,CAAC,IAAI,CAAC,GAAGJ,OAAO,EAAE,EAAEI,OAAO,EAAE,CAAC;YAEtCA,SAAS;YACTI;QACJ;IACJ;AACJ"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./registerStandaloneFeatures.js";
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./registerStandaloneFeatures.js";
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@webiny/cli-standalone",
|
|
3
|
+
"version": "6.6.0-alpha.4",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": "./index.js",
|
|
7
|
+
"./*": "./*"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/webiny/webiny-js.git"
|
|
12
|
+
},
|
|
13
|
+
"description": "CLI commands for standalone Webiny projects.",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"bin": {
|
|
16
|
+
"webiny": "./bin.js"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@webiny/cli-core": "6.6.0-alpha.4",
|
|
20
|
+
"@webiny/di": "1.1.0",
|
|
21
|
+
"@webiny/project": "6.6.0-alpha.4",
|
|
22
|
+
"@webiny/project-standalone": "6.6.0-alpha.4",
|
|
23
|
+
"chalk": "6.0.0",
|
|
24
|
+
"tsx": "4.23.12"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@webiny/build-tools": "6.6.0-alpha.4",
|
|
28
|
+
"typescript": "7.0.2",
|
|
29
|
+
"vitest": "4.1.11"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"webiny": {
|
|
35
|
+
"publishFrom": "dist"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { serverGetProjectSdkService } from "./services/GetProjectSdkService.js";
|
|
2
|
+
import { serverDefaultAppsService } from "./services/DefaultAppsService.js";
|
|
3
|
+
import { standaloneWatchCommand } from "./features/WatchCommand.js";
|
|
4
|
+
import { standaloneServeCommand } from "./features/ServeCommand.js";
|
|
5
|
+
const registerStandaloneFeatures = (container)=>{
|
|
6
|
+
container.register(serverGetProjectSdkService).inSingletonScope();
|
|
7
|
+
container.register(serverDefaultAppsService).inSingletonScope();
|
|
8
|
+
container.register(standaloneWatchCommand).inSingletonScope();
|
|
9
|
+
container.register(standaloneServeCommand).inSingletonScope();
|
|
10
|
+
};
|
|
11
|
+
export { registerStandaloneFeatures };
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=registerStandaloneFeatures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registerStandaloneFeatures.js","sources":["../src/registerStandaloneFeatures.ts"],"sourcesContent":["import { Container } from \"@webiny/di\";\nimport { serverGetProjectSdkService } from \"./services/GetProjectSdkService.js\";\nimport { serverDefaultAppsService } from \"./services/DefaultAppsService.js\";\nimport { standaloneWatchCommand } from \"./features/WatchCommand.js\";\nimport { standaloneServeCommand } from \"./features/ServeCommand.js\";\n\nexport const registerStandaloneFeatures = (container: Container): void => {\n // Override GetProjectSdkService so server project features are wired into the ProjectSdk container.\n container.register(serverGetProjectSdkService).inSingletonScope();\n\n // Server default apps: api + admin (used by no-arg build and watch).\n container.register(serverDefaultAppsService).inSingletonScope();\n\n // Watch command without Lambda-specific options.\n container.register(standaloneWatchCommand).inSingletonScope();\n\n // Serve command: run built apps as long-running servers (production).\n container.register(standaloneServeCommand).inSingletonScope();\n};\n"],"names":["registerStandaloneFeatures","container","serverGetProjectSdkService","serverDefaultAppsService","standaloneWatchCommand","standaloneServeCommand"],"mappings":";;;;AAMO,MAAMA,6BAA6B,CAACC;IAEvCA,UAAU,QAAQ,CAACC,4BAA4B,gBAAgB;IAG/DD,UAAU,QAAQ,CAACE,0BAA0B,gBAAgB;IAG7DF,UAAU,QAAQ,CAACG,wBAAwB,gBAAgB;IAG3DH,UAAU,QAAQ,CAACI,wBAAwB,gBAAgB;AAC/D"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { DefaultAppsService } from "@webiny/cli-core/abstractions/index.js";
|
|
2
|
+
export declare class ServerDefaultAppsService implements DefaultAppsService.Interface {
|
|
3
|
+
execute(): Promise<string[]>;
|
|
4
|
+
}
|
|
5
|
+
export declare const serverDefaultAppsService: import("@webiny/di").Implementation<typeof ServerDefaultAppsService>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createImplementation } from "@webiny/di";
|
|
2
|
+
import { DefaultAppsService } from "@webiny/cli-core/abstractions/index.js";
|
|
3
|
+
class ServerDefaultAppsService {
|
|
4
|
+
async execute() {
|
|
5
|
+
return [
|
|
6
|
+
"api",
|
|
7
|
+
"admin"
|
|
8
|
+
];
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const serverDefaultAppsService = createImplementation({
|
|
12
|
+
abstraction: DefaultAppsService,
|
|
13
|
+
implementation: ServerDefaultAppsService,
|
|
14
|
+
dependencies: []
|
|
15
|
+
});
|
|
16
|
+
export { ServerDefaultAppsService, serverDefaultAppsService };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=DefaultAppsService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"services/DefaultAppsService.js","sources":["../../src/services/DefaultAppsService.ts"],"sourcesContent":["import { createImplementation } from \"@webiny/di\";\nimport { DefaultAppsService } from \"@webiny/cli-core/abstractions/index.js\";\n\nexport class ServerDefaultAppsService implements DefaultAppsService.Interface {\n async execute(): Promise<string[]> {\n return [\"api\", \"admin\"];\n }\n}\n\nexport const serverDefaultAppsService = createImplementation({\n abstraction: DefaultAppsService,\n implementation: ServerDefaultAppsService,\n dependencies: []\n});\n"],"names":["ServerDefaultAppsService","serverDefaultAppsService","createImplementation","DefaultAppsService"],"mappings":";;AAGO,MAAMA;IACT,MAAM,UAA6B;QAC/B,OAAO;YAAC;YAAO;SAAQ;IAC3B;AACJ;AAEO,MAAMC,2BAA2BC,qBAAqB;IACzD,aAAaC;IACb,gBAAgBH;IAChB,cAAc,EAAE;AACpB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ProjectSdk } from "@webiny/project";
|
|
2
|
+
import { CliParamsService, GetArgvService, GetProjectSdkService } from "@webiny/cli-core/abstractions/index.js";
|
|
3
|
+
export declare class ServerGetProjectSdkService implements GetProjectSdkService.Interface {
|
|
4
|
+
private readonly cliParamsService;
|
|
5
|
+
private readonly getArgvService;
|
|
6
|
+
constructor(cliParamsService: CliParamsService.Interface, getArgvService: GetArgvService.Interface);
|
|
7
|
+
execute(): Promise<ProjectSdk>;
|
|
8
|
+
}
|
|
9
|
+
export declare const serverGetProjectSdkService: import("@webiny/di").Implementation<typeof ServerGetProjectSdkService>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createImplementation } from "@webiny/di";
|
|
2
|
+
import { ProjectSdk } from "@webiny/project";
|
|
3
|
+
import { CliParamsService, GetArgvService, GetProjectSdkService } from "@webiny/cli-core/abstractions/index.js";
|
|
4
|
+
import { registerStandaloneProjectFeatures } from "@webiny/project-standalone";
|
|
5
|
+
class ServerGetProjectSdkService {
|
|
6
|
+
constructor(cliParamsService, getArgvService){
|
|
7
|
+
this.cliParamsService = cliParamsService;
|
|
8
|
+
this.getArgvService = getArgvService;
|
|
9
|
+
}
|
|
10
|
+
async execute() {
|
|
11
|
+
const cliParams = this.cliParamsService.get();
|
|
12
|
+
const argv = this.getArgvService.execute();
|
|
13
|
+
return ProjectSdk.init({
|
|
14
|
+
...argv,
|
|
15
|
+
cwd: cliParams.cwd,
|
|
16
|
+
logging: {
|
|
17
|
+
streamToStdout: argv.showLogs,
|
|
18
|
+
level: argv.logLevel
|
|
19
|
+
}
|
|
20
|
+
}, registerStandaloneProjectFeatures);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const serverGetProjectSdkService = createImplementation({
|
|
24
|
+
abstraction: GetProjectSdkService,
|
|
25
|
+
implementation: ServerGetProjectSdkService,
|
|
26
|
+
dependencies: [
|
|
27
|
+
CliParamsService,
|
|
28
|
+
GetArgvService
|
|
29
|
+
]
|
|
30
|
+
});
|
|
31
|
+
export { ServerGetProjectSdkService, serverGetProjectSdkService };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=GetProjectSdkService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"services/GetProjectSdkService.js","sources":["../../src/services/GetProjectSdkService.ts"],"sourcesContent":["import { createImplementation } from \"@webiny/di\";\nimport { ProjectSdk } from \"@webiny/project\";\nimport {\n CliParamsService,\n GetArgvService,\n GetProjectSdkService\n} from \"@webiny/cli-core/abstractions/index.js\";\nimport { registerStandaloneProjectFeatures } from \"@webiny/project-standalone\";\n\nexport class ServerGetProjectSdkService implements GetProjectSdkService.Interface {\n constructor(\n private readonly cliParamsService: CliParamsService.Interface,\n private readonly getArgvService: GetArgvService.Interface\n ) {}\n\n async execute() {\n const cliParams = this.cliParamsService.get();\n const argv = this.getArgvService.execute();\n\n return ProjectSdk.init(\n {\n ...argv,\n cwd: cliParams.cwd,\n logging: {\n streamToStdout: argv.showLogs,\n level: argv.logLevel\n }\n },\n registerStandaloneProjectFeatures\n );\n }\n}\n\nexport const serverGetProjectSdkService = createImplementation({\n abstraction: GetProjectSdkService,\n implementation: ServerGetProjectSdkService,\n dependencies: [CliParamsService, GetArgvService]\n});\n"],"names":["ServerGetProjectSdkService","cliParamsService","getArgvService","cliParams","argv","ProjectSdk","registerStandaloneProjectFeatures","serverGetProjectSdkService","createImplementation","GetProjectSdkService","CliParamsService","GetArgvService"],"mappings":";;;;AASO,MAAMA;IACT,YACqBC,gBAA4C,EAC5CC,cAAwC,CAC3D;aAFmBD,gBAAgB,GAAhBA;aACAC,cAAc,GAAdA;IAClB;IAEH,MAAM,UAAU;QACZ,MAAMC,YAAY,IAAI,CAAC,gBAAgB,CAAC,GAAG;QAC3C,MAAMC,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO;QAExC,OAAOC,WAAW,IAAI,CAClB;YACI,GAAGD,IAAI;YACP,KAAKD,UAAU,GAAG;YAClB,SAAS;gBACL,gBAAgBC,KAAK,QAAQ;gBAC7B,OAAOA,KAAK,QAAQ;YACxB;QACJ,GACAE;IAER;AACJ;AAEO,MAAMC,6BAA6BC,qBAAqB;IAC3D,aAAaC;IACb,gBAAgBT;IAChB,cAAc;QAACU;QAAkBC;KAAe;AACpD"}
|