@ressjs/cli 0.6.0-experimental.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/ress.js +2 -0
- package/dist/api/app.d.ts +4 -0
- package/dist/api/app.js +189 -0
- package/dist/api/build.d.ts +8 -0
- package/dist/api/build.js +113 -0
- package/dist/api/dev.d.ts +3 -0
- package/dist/api/dev.js +6 -0
- package/dist/api/errors.d.ts +37 -0
- package/dist/api/errors.js +119 -0
- package/dist/api/extend.d.ts +29 -0
- package/dist/api/extend.js +117 -0
- package/dist/api/index.d.ts +22 -0
- package/dist/api/index.js +16 -0
- package/dist/api/inspect.d.ts +75 -0
- package/dist/api/inspect.js +170 -0
- package/dist/api/options.d.ts +29 -0
- package/dist/api/options.js +152 -0
- package/dist/api/reporter.d.ts +74 -0
- package/dist/api/reporter.js +12 -0
- package/dist/api/start.d.ts +3 -0
- package/dist/api/start.js +6 -0
- package/dist/api/types.d.ts +82 -0
- package/dist/api/types.js +1 -0
- package/dist/cli/argv.d.ts +63 -0
- package/dist/cli/argv.js +148 -0
- package/dist/cli/commands/build.d.ts +2 -0
- package/dist/cli/commands/build.js +7 -0
- package/dist/cli/commands/dev.d.ts +2 -0
- package/dist/cli/commands/dev.js +10 -0
- package/dist/cli/commands/info.d.ts +2 -0
- package/dist/cli/commands/info.js +17 -0
- package/dist/cli/commands/start.d.ts +2 -0
- package/dist/cli/commands/start.js +10 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +62 -0
- package/dist/cli/reporters/format.d.ts +24 -0
- package/dist/cli/reporters/format.js +50 -0
- package/dist/cli/reporters/info-format.d.ts +15 -0
- package/dist/cli/reporters/info-format.js +49 -0
- package/dist/cli/reporters/json.d.ts +12 -0
- package/dist/cli/reporters/json.js +13 -0
- package/dist/cli/reporters/terminal.d.ts +35 -0
- package/dist/cli/reporters/terminal.js +118 -0
- package/dist/cli/shutdown.d.ts +2 -0
- package/dist/cli/shutdown.js +17 -0
- package/package.json +59 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { LogLevel, RessMode } from '../api/types.js';
|
|
2
|
+
export interface DevFlags {
|
|
3
|
+
port?: number;
|
|
4
|
+
host?: string | boolean;
|
|
5
|
+
strictPort?: boolean;
|
|
6
|
+
base?: string;
|
|
7
|
+
root?: string;
|
|
8
|
+
config?: string;
|
|
9
|
+
mode?: RessMode;
|
|
10
|
+
server?: string | false;
|
|
11
|
+
logLevel?: LogLevel;
|
|
12
|
+
open?: boolean;
|
|
13
|
+
clearScreen?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface BuildFlags {
|
|
16
|
+
root?: string;
|
|
17
|
+
config?: string;
|
|
18
|
+
mode?: RessMode;
|
|
19
|
+
outDir?: string;
|
|
20
|
+
target?: 'all' | 'client' | 'ssr';
|
|
21
|
+
clean?: boolean;
|
|
22
|
+
logLevel?: LogLevel;
|
|
23
|
+
}
|
|
24
|
+
export interface StartFlags {
|
|
25
|
+
port?: number;
|
|
26
|
+
host?: string | boolean;
|
|
27
|
+
strictPort?: boolean;
|
|
28
|
+
base?: string;
|
|
29
|
+
root?: string;
|
|
30
|
+
config?: string;
|
|
31
|
+
outDir?: string;
|
|
32
|
+
server?: string | false;
|
|
33
|
+
logLevel?: LogLevel;
|
|
34
|
+
}
|
|
35
|
+
export interface InfoFlags {
|
|
36
|
+
root?: string;
|
|
37
|
+
config?: string;
|
|
38
|
+
mode?: RessMode;
|
|
39
|
+
json?: boolean;
|
|
40
|
+
routes?: boolean;
|
|
41
|
+
variants?: boolean;
|
|
42
|
+
configOnly?: boolean;
|
|
43
|
+
}
|
|
44
|
+
export type ParsedCommand = {
|
|
45
|
+
command: 'dev';
|
|
46
|
+
flags: DevFlags;
|
|
47
|
+
} | {
|
|
48
|
+
command: 'build';
|
|
49
|
+
flags: BuildFlags;
|
|
50
|
+
} | {
|
|
51
|
+
command: 'start';
|
|
52
|
+
flags: StartFlags;
|
|
53
|
+
} | {
|
|
54
|
+
command: 'info';
|
|
55
|
+
flags: InfoFlags;
|
|
56
|
+
} | {
|
|
57
|
+
command: 'help';
|
|
58
|
+
text: string;
|
|
59
|
+
} | {
|
|
60
|
+
command: 'version';
|
|
61
|
+
text: string;
|
|
62
|
+
};
|
|
63
|
+
export declare function parseArgv(argv: readonly string[], version: string): ParsedCommand;
|
package/dist/cli/argv.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Definición de los cuatro comandos y sus argumentos.
|
|
3
|
+
*
|
|
4
|
+
* Usa `commander`: la CLI tiene cuatro subcomandos con muchos alias, pares
|
|
5
|
+
* `--x`/`--no-x` y un flag de valor opcional (`--host`). A diferencia de
|
|
6
|
+
* `create-ress-app` —que F-017 dejó sin dependencias porque se descarga entero
|
|
7
|
+
* en cada `npx`—, `@ressjs/cli` se instala una vez como dependencia del
|
|
8
|
+
* proyecto y se reusa en cada `npm run dev`: el costo de una dependencia bien
|
|
9
|
+
* mantenida se paga una sola vez, no en cada invocación.
|
|
10
|
+
*
|
|
11
|
+
* Un argumento desconocido o un valor de enum inválido produce `RESS_BAD_USAGE`
|
|
12
|
+
* (código 2), nunca la salida cruda de `commander`.
|
|
13
|
+
*/
|
|
14
|
+
import { Command, Option } from 'commander';
|
|
15
|
+
import { RessError } from '../api/errors.js';
|
|
16
|
+
const LOG_LEVELS = ['silent', 'error', 'warn', 'info', 'debug'];
|
|
17
|
+
function normalizeServer(raw) {
|
|
18
|
+
if (raw === undefined)
|
|
19
|
+
return undefined;
|
|
20
|
+
if (raw === 'false')
|
|
21
|
+
return false;
|
|
22
|
+
return raw;
|
|
23
|
+
}
|
|
24
|
+
function fail(message) {
|
|
25
|
+
throw RessError.badUsage(message);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* `writeOut` se deja intacto: `--help` y `--version` se imprimen con el
|
|
29
|
+
* formateador de `commander`, directo a stdout, como cualquier CLI. Sólo se
|
|
30
|
+
* suprime `writeErr`: los errores de uso reales se reformatean como
|
|
31
|
+
* `RessError` para que tengan el mismo aspecto que cualquier otro error del
|
|
32
|
+
* framework, en vez del formato propio de `commander`.
|
|
33
|
+
*/
|
|
34
|
+
function quietErrors() {
|
|
35
|
+
return { writeErr: () => { } };
|
|
36
|
+
}
|
|
37
|
+
function baseCommand(name, description) {
|
|
38
|
+
return new Command(name)
|
|
39
|
+
.description(description)
|
|
40
|
+
.exitOverride((err) => {
|
|
41
|
+
if (err.code?.startsWith('commander.help') || err.code === 'commander.version') {
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
fail(err.message.replace(/^error: /, ''));
|
|
45
|
+
})
|
|
46
|
+
.configureOutput(quietErrors());
|
|
47
|
+
}
|
|
48
|
+
export function parseArgv(argv, version) {
|
|
49
|
+
const program = new Command('ress')
|
|
50
|
+
.description('La CLI de ress.js: dev, build, start e info.')
|
|
51
|
+
.version(version, '-V, --version')
|
|
52
|
+
.exitOverride((err) => {
|
|
53
|
+
throw err;
|
|
54
|
+
})
|
|
55
|
+
.configureOutput(quietErrors());
|
|
56
|
+
let result;
|
|
57
|
+
const dev = baseCommand('dev', 'Levanta el servidor de desarrollo con recarga en caliente')
|
|
58
|
+
.option('-p, --port <n>', 'Puerto de escucha', (v) => parsePort(v))
|
|
59
|
+
.option('-H, --host [host]', 'Sin valor equivale a 0.0.0.0')
|
|
60
|
+
.option('--strict-port', 'Falla si el puerto está ocupado')
|
|
61
|
+
.option('--no-strict-port', 'Prueba el siguiente puerto libre')
|
|
62
|
+
.option('--base <path>', 'Prefijo de servido')
|
|
63
|
+
.option('--root <dir>', 'Raíz del proyecto')
|
|
64
|
+
.addOption(new Option('-c, --config <file>', 'Archivo de configuración'))
|
|
65
|
+
.addOption(new Option('-m, --mode <mode>', 'Modo de configuración').choices(['development', 'production']))
|
|
66
|
+
.option('--server <file>', 'Archivo de servidor del proyecto ("false" para desactivar)')
|
|
67
|
+
.addOption(new Option('-l, --log-level <lvl>', 'Detalle de la salida').choices(LOG_LEVELS))
|
|
68
|
+
.option('-o, --open', 'Abre el navegador al arrancar')
|
|
69
|
+
.option('--clear-screen', 'Limpia la pantalla en cada recarga')
|
|
70
|
+
.option('--no-clear-screen', 'Conserva el scrollback')
|
|
71
|
+
.action((opts) => {
|
|
72
|
+
result = { command: 'dev', flags: { ...opts, server: normalizeServer(opts.server) } };
|
|
73
|
+
});
|
|
74
|
+
const build = baseCommand('build', 'Produce el build de producción, cliente y servidor')
|
|
75
|
+
.option('--root <dir>', 'Raíz del proyecto')
|
|
76
|
+
.addOption(new Option('-c, --config <file>', 'Archivo de configuración'))
|
|
77
|
+
.addOption(new Option('-m, --mode <mode>', 'Modo de configuración').choices(['development', 'production']))
|
|
78
|
+
.option('--out-dir <dir>', 'Raíz de salida')
|
|
79
|
+
.addOption(new Option('--target <t>', 'Qué entorno construir').choices(['all', 'client', 'ssr']))
|
|
80
|
+
.option('--no-clean', 'Conserva la salida anterior')
|
|
81
|
+
.addOption(new Option('-l, --log-level <lvl>', 'Detalle de la salida').choices(LOG_LEVELS))
|
|
82
|
+
.action((opts) => {
|
|
83
|
+
result = { command: 'build', flags: opts };
|
|
84
|
+
});
|
|
85
|
+
const start = baseCommand('start', 'Sirve el build de producción')
|
|
86
|
+
.option('-p, --port <n>', 'Puerto de escucha', (v) => parsePort(v))
|
|
87
|
+
.option('-H, --host [host]', 'En producción el default es abierto')
|
|
88
|
+
.option('--strict-port', 'Falla si el puerto está ocupado')
|
|
89
|
+
.option('--no-strict-port', 'Prueba el siguiente puerto libre')
|
|
90
|
+
.option('--base <path>', 'Prefijo de servido')
|
|
91
|
+
.option('--root <dir>', 'Raíz del proyecto')
|
|
92
|
+
.addOption(new Option('-c, --config <file>', 'Archivo de configuración'))
|
|
93
|
+
.option('--out-dir <dir>', 'Build a servir')
|
|
94
|
+
.option('--server <file>', 'Archivo de servidor del proyecto ("false" para desactivar)')
|
|
95
|
+
.addOption(new Option('-l, --log-level <lvl>', 'Detalle de la salida').choices(LOG_LEVELS))
|
|
96
|
+
.action((opts) => {
|
|
97
|
+
result = { command: 'start', flags: { ...opts, server: normalizeServer(opts.server) } };
|
|
98
|
+
});
|
|
99
|
+
const info = baseCommand('info', 'Reporta qué detectó el framework en este proyecto')
|
|
100
|
+
.option('--root <dir>', 'Raíz del proyecto')
|
|
101
|
+
.addOption(new Option('-c, --config <file>', 'Archivo de configuración'))
|
|
102
|
+
.addOption(new Option('-m, --mode <mode>', 'Modo con el que resolver la config').choices(['development', 'production']))
|
|
103
|
+
.option('--json', 'Salida legible por máquina')
|
|
104
|
+
.option('--routes', 'Sólo la sección de rutas')
|
|
105
|
+
.option('--variants', 'Sólo la sección de variantes')
|
|
106
|
+
.option('--config-only', 'Sólo la configuración resuelta')
|
|
107
|
+
.action((opts) => {
|
|
108
|
+
result = { command: 'info', flags: opts };
|
|
109
|
+
});
|
|
110
|
+
program.addCommand(dev);
|
|
111
|
+
program.addCommand(build);
|
|
112
|
+
program.addCommand(start);
|
|
113
|
+
program.addCommand(info);
|
|
114
|
+
try {
|
|
115
|
+
program.parse(argv, { from: 'user' });
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
if (err instanceof RessError)
|
|
119
|
+
throw err;
|
|
120
|
+
const commanderErr = err;
|
|
121
|
+
// El texto ya salió por stdout de la mano de `commander` (`writeOut` no se
|
|
122
|
+
// tocó): acá sólo se señala que no hay nada más que hacer.
|
|
123
|
+
if (commanderErr.code?.startsWith('commander.help')) {
|
|
124
|
+
return { command: 'help', text: '' };
|
|
125
|
+
}
|
|
126
|
+
if (commanderErr.code === 'commander.version') {
|
|
127
|
+
return { command: 'version', text: '' };
|
|
128
|
+
}
|
|
129
|
+
if (commanderErr.code === 'commander.unknownCommand' || commanderErr.code === 'commander.unknownOption') {
|
|
130
|
+
fail(commanderErr.message?.replace(/^error: /, '') ?? 'comando o argumento desconocido');
|
|
131
|
+
}
|
|
132
|
+
if (commanderErr.code === 'commander.missingArgument' || commanderErr.code === 'commander.invalidArgument') {
|
|
133
|
+
fail(commanderErr.message?.replace(/^error: /, '') ?? 'argumento inválido');
|
|
134
|
+
}
|
|
135
|
+
throw err;
|
|
136
|
+
}
|
|
137
|
+
if (!result) {
|
|
138
|
+
return { command: 'help', text: program.helpInformation() };
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
function parsePort(value) {
|
|
143
|
+
const parsed = Number(value);
|
|
144
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
145
|
+
fail(`--port tiene que ser un entero no negativo; se recibió "${value}".`);
|
|
146
|
+
}
|
|
147
|
+
return parsed;
|
|
148
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { buildApp } from '../../api/index.js';
|
|
2
|
+
import { createTerminalReporter } from '../reporters/terminal.js';
|
|
3
|
+
export async function buildCommand(flags) {
|
|
4
|
+
const reporter = createTerminalReporter({ logLevel: flags.logLevel });
|
|
5
|
+
await buildApp({ ...flags, reporter });
|
|
6
|
+
return 0;
|
|
7
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { startDevServer } from '../../api/index.js';
|
|
2
|
+
import { createTerminalReporter } from '../reporters/terminal.js';
|
|
3
|
+
import { registerShutdown } from '../shutdown.js';
|
|
4
|
+
export async function devCommand(flags) {
|
|
5
|
+
const { server: serverFile, ...rest } = flags;
|
|
6
|
+
const reporter = createTerminalReporter({ logLevel: flags.logLevel });
|
|
7
|
+
const server = await startDevServer({ ...rest, serverFile, reporter });
|
|
8
|
+
registerShutdown(() => server.close());
|
|
9
|
+
return new Promise(() => { }); // dev no retorna hasta la señal
|
|
10
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { inspectProject } from '../../api/index.js';
|
|
2
|
+
import { detectFormatEnv } from '../reporters/format.js';
|
|
3
|
+
import { formatProjectInfo } from '../reporters/info-format.js';
|
|
4
|
+
import { printJson } from '../reporters/json.js';
|
|
5
|
+
export async function infoCommand(flags) {
|
|
6
|
+
const info = await inspectProject(flags);
|
|
7
|
+
// El diagnóstico nunca aborta por un proyecto roto: sale 0 aun con
|
|
8
|
+
// `config.valid === false` o rutas en conflicto. Ese es el punto de `info`.
|
|
9
|
+
if (flags.json) {
|
|
10
|
+
printJson(info);
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
const env = detectFormatEnv(Boolean(process.stdout.isTTY));
|
|
14
|
+
process.stdout.write(formatProjectInfo(info, { routes: flags.routes, variants: flags.variants, configOnly: flags.configOnly }, env) +
|
|
15
|
+
'\n');
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { startProdServer } from '../../api/index.js';
|
|
2
|
+
import { createTerminalReporter } from '../reporters/terminal.js';
|
|
3
|
+
import { registerShutdown } from '../shutdown.js';
|
|
4
|
+
export async function startCommand(flags) {
|
|
5
|
+
const { server: serverFile, ...rest } = flags;
|
|
6
|
+
const reporter = createTerminalReporter({ logLevel: flags.logLevel });
|
|
7
|
+
const server = await startProdServer({ ...rest, serverFile, reporter });
|
|
8
|
+
registerShutdown(() => server.close());
|
|
9
|
+
return new Promise(() => { }); // start no retorna hasta la señal
|
|
10
|
+
}
|
package/dist/cli/main.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Punto de entrada. Único bloque de captura de errores del paquete: mapea
|
|
4
|
+
* `RessError` a su código de salida y al formato de error del reporter. Un
|
|
5
|
+
* `try/catch` por comando sería la primera duplicación — por eso vive acá y no
|
|
6
|
+
* en `commands/*`.
|
|
7
|
+
*/
|
|
8
|
+
import { parseArgv } from './argv.js';
|
|
9
|
+
import { devCommand } from './commands/dev.js';
|
|
10
|
+
import { buildCommand } from './commands/build.js';
|
|
11
|
+
import { startCommand } from './commands/start.js';
|
|
12
|
+
import { infoCommand } from './commands/info.js';
|
|
13
|
+
import { formatError } from './reporters/terminal.js';
|
|
14
|
+
import { detectFormatEnv } from './reporters/format.js';
|
|
15
|
+
import { RessError } from '../api/errors.js';
|
|
16
|
+
import { cliVersion } from '../api/inspect.js';
|
|
17
|
+
function dispatch(parsed) {
|
|
18
|
+
switch (parsed.command) {
|
|
19
|
+
case 'dev':
|
|
20
|
+
return devCommand(parsed.flags);
|
|
21
|
+
case 'build':
|
|
22
|
+
return buildCommand(parsed.flags);
|
|
23
|
+
case 'start':
|
|
24
|
+
return startCommand(parsed.flags);
|
|
25
|
+
case 'info':
|
|
26
|
+
return infoCommand(parsed.flags);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
30
|
+
// El parseo de argumentos también puede fallar (comando desconocido,
|
|
31
|
+
// argumento contradictorio) y su error tiene que pasar por el mismo formato
|
|
32
|
+
// y la misma tabla de códigos de salida que cualquier otro `RessError` — si
|
|
33
|
+
// quedara afuera del bloque, `ress dev --nope` saldría con una traza cruda y
|
|
34
|
+
// código 1 en vez del 2 que le corresponde a un error de uso.
|
|
35
|
+
try {
|
|
36
|
+
const version = await cliVersion();
|
|
37
|
+
const parsed = parseArgv(argv, version);
|
|
38
|
+
if (parsed.command === 'help' || parsed.command === 'version') {
|
|
39
|
+
if (parsed.text)
|
|
40
|
+
process.stdout.write(parsed.text + '\n');
|
|
41
|
+
process.exitCode = 0;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
process.exitCode = await dispatch(parsed);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
const env = detectFormatEnv(Boolean(process.stderr.isTTY));
|
|
48
|
+
if (error instanceof RessError) {
|
|
49
|
+
process.stderr.write(formatError(error, env) + '\n');
|
|
50
|
+
process.exitCode = error.exitCode;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// No identificado por el framework: se muestra la traza tal cual. Ocultarla
|
|
54
|
+
// sería peor que un error sin formatear (UC-12).
|
|
55
|
+
process.stderr.write(String(error instanceof Error ? (error.stack ?? error.message) : error) + '\n');
|
|
56
|
+
process.exitCode = 1;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
main().catch((error) => {
|
|
60
|
+
process.stderr.write(String(error?.stack ?? error) + '\n');
|
|
61
|
+
process.exitCode = 1;
|
|
62
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detección de entorno de salida y utilidades de formato.
|
|
3
|
+
*
|
|
4
|
+
* Todo lo que decide si hay color, cursor o animaciones vive acá, en un solo
|
|
5
|
+
* lugar: `terminal.ts` sólo lo consulta.
|
|
6
|
+
*/
|
|
7
|
+
import { styleText } from 'node:util';
|
|
8
|
+
export interface FormatEnv {
|
|
9
|
+
isTTY: boolean;
|
|
10
|
+
color: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function detectFormatEnv(streamIsTTY: boolean, env?: NodeJS.ProcessEnv): FormatEnv;
|
|
13
|
+
type Style = Parameters<typeof styleText>[0];
|
|
14
|
+
export declare function paint(env: FormatEnv, style: Style, text: string): string;
|
|
15
|
+
export declare function bold(env: FormatEnv, text: string): string;
|
|
16
|
+
export declare function dim(env: FormatEnv, text: string): string;
|
|
17
|
+
export declare function green(env: FormatEnv, text: string): string;
|
|
18
|
+
export declare function red(env: FormatEnv, text: string): string;
|
|
19
|
+
export declare function yellow(env: FormatEnv, text: string): string;
|
|
20
|
+
export declare function cyan(env: FormatEnv, text: string): string;
|
|
21
|
+
/** `1234567` → `'1.2 MB'`. Sin dependencias: es una tabla de cuatro casos. */
|
|
22
|
+
export declare function formatBytes(bytes: number): string;
|
|
23
|
+
export declare function formatDuration(ms: number): string;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detección de entorno de salida y utilidades de formato.
|
|
3
|
+
*
|
|
4
|
+
* Todo lo que decide si hay color, cursor o animaciones vive acá, en un solo
|
|
5
|
+
* lugar: `terminal.ts` sólo lo consulta.
|
|
6
|
+
*/
|
|
7
|
+
import { styleText } from 'node:util';
|
|
8
|
+
export function detectFormatEnv(streamIsTTY, env = process.env) {
|
|
9
|
+
const isTTY = streamIsTTY && !env.CI;
|
|
10
|
+
const color = isTTY && !env.NO_COLOR;
|
|
11
|
+
return { isTTY, color };
|
|
12
|
+
}
|
|
13
|
+
export function paint(env, style, text) {
|
|
14
|
+
if (!env.color)
|
|
15
|
+
return text;
|
|
16
|
+
return styleText(style, text);
|
|
17
|
+
}
|
|
18
|
+
export function bold(env, text) {
|
|
19
|
+
return paint(env, 'bold', text);
|
|
20
|
+
}
|
|
21
|
+
export function dim(env, text) {
|
|
22
|
+
return paint(env, 'dim', text);
|
|
23
|
+
}
|
|
24
|
+
export function green(env, text) {
|
|
25
|
+
return paint(env, 'green', text);
|
|
26
|
+
}
|
|
27
|
+
export function red(env, text) {
|
|
28
|
+
return paint(env, 'red', text);
|
|
29
|
+
}
|
|
30
|
+
export function yellow(env, text) {
|
|
31
|
+
return paint(env, 'yellow', text);
|
|
32
|
+
}
|
|
33
|
+
export function cyan(env, text) {
|
|
34
|
+
return paint(env, 'cyan', text);
|
|
35
|
+
}
|
|
36
|
+
/** `1234567` → `'1.2 MB'`. Sin dependencias: es una tabla de cuatro casos. */
|
|
37
|
+
export function formatBytes(bytes) {
|
|
38
|
+
if (bytes < 1024)
|
|
39
|
+
return `${bytes} B`;
|
|
40
|
+
if (bytes < 1024 * 1024)
|
|
41
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
42
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
43
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
44
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
45
|
+
}
|
|
46
|
+
export function formatDuration(ms) {
|
|
47
|
+
if (ms < 1000)
|
|
48
|
+
return `${Math.round(ms)} ms`;
|
|
49
|
+
return `${(ms / 1000).toFixed(2)} s`;
|
|
50
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formato de `ress info` en modo humano.
|
|
3
|
+
*
|
|
4
|
+
* Aparte de `terminal.ts` porque no traduce eventos: recibe el `ProjectInfo`
|
|
5
|
+
* completo de una sola vez y decide qué secciones mostrar según las banderas
|
|
6
|
+
* `--routes` / `--variants` / `--config-only`.
|
|
7
|
+
*/
|
|
8
|
+
import type { ProjectInfo } from '../../api/inspect.js';
|
|
9
|
+
import { type FormatEnv } from './format.js';
|
|
10
|
+
export interface InfoSections {
|
|
11
|
+
routes?: boolean;
|
|
12
|
+
variants?: boolean;
|
|
13
|
+
configOnly?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function formatProjectInfo(info: ProjectInfo, sections: InfoSections, env: FormatEnv): string;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { bold, dim, formatBytes, green, red } from './format.js';
|
|
2
|
+
export function formatProjectInfo(info, sections, env) {
|
|
3
|
+
const only = sections.routes || sections.variants || sections.configOnly;
|
|
4
|
+
const lines = [];
|
|
5
|
+
if (!only || sections.configOnly) {
|
|
6
|
+
lines.push('', bold(env, ' Sistema'));
|
|
7
|
+
lines.push(` node ${info.versions.node}`);
|
|
8
|
+
lines.push(` os ${info.platform.os}`);
|
|
9
|
+
lines.push(` cwd ${info.platform.cwd}`);
|
|
10
|
+
lines.push('', bold(env, ' Paquetes'));
|
|
11
|
+
for (const [name, version] of Object.entries(info.versions.packages)) {
|
|
12
|
+
lines.push(` ${name.padEnd(24)} ${version}`);
|
|
13
|
+
}
|
|
14
|
+
lines.push('', bold(env, ' Configuración'));
|
|
15
|
+
lines.push(` archivo ${info.config.file ?? '(autodetectado)'}`);
|
|
16
|
+
lines.push(` válida ${info.config.valid ? green(env, '✓ sí') : red(env, '✖ no')}`);
|
|
17
|
+
if (!info.config.valid) {
|
|
18
|
+
for (const e of info.config.errors ?? [])
|
|
19
|
+
lines.push(` · ${e.message}`);
|
|
20
|
+
}
|
|
21
|
+
for (const [key, entry] of Object.entries(info.config.sources)) {
|
|
22
|
+
lines.push(` ${key.padEnd(15)} ${JSON.stringify(entry.value)} ${dim(env, entry.origin)}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (!only || sections.routes) {
|
|
26
|
+
lines.push('', bold(env, ` Rutas (${info.routes.count})`));
|
|
27
|
+
for (const r of info.routes.entries)
|
|
28
|
+
lines.push(` ${r.pattern.padEnd(24)} ${r.kind.padEnd(8)} ${r.file}`);
|
|
29
|
+
for (const c of info.routes.conflicts) {
|
|
30
|
+
lines.push(red(env, ` ⚠ conflicto ${c.pattern}: ${c.files.join(', ')}`));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (!only || sections.variants) {
|
|
34
|
+
lines.push('', bold(env, ' Variantes'));
|
|
35
|
+
lines.push(` declaradas ${info.variants.declared.join(', ') || '(ninguna)'}`);
|
|
36
|
+
lines.push(` enumeradas ${info.variants.tags.join(', ') || '(ninguna)'}`);
|
|
37
|
+
}
|
|
38
|
+
if (!only) {
|
|
39
|
+
lines.push('', bold(env, ' Build'));
|
|
40
|
+
lines.push(` ${info.build.outDir}/ ${info.build.present ? 'presente' : 'ausente'}`);
|
|
41
|
+
if (info.build.present) {
|
|
42
|
+
lines.push(` manifest v${info.build.manifestVersion} · ${info.build.compatible ? 'compatible' : 'incompatible'}`);
|
|
43
|
+
lines.push(` generado ${info.build.builtAt}`);
|
|
44
|
+
lines.push(` tamaños client ${formatBytes(info.build.sizes.client)} · server ${formatBytes(info.build.sizes.server)}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
lines.push('');
|
|
48
|
+
return lines.join('\n');
|
|
49
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reporter JSON — `--json` no es un segundo camino de datos, es otro
|
|
3
|
+
* `RessReporter` sobre los mismos eventos. Acumula y no imprime nada por sí
|
|
4
|
+
* mismo: quien lo usa (`info.ts`) decide cuándo volcar el JSON final, después
|
|
5
|
+
* de tener el resultado completo de `inspectProject`.
|
|
6
|
+
*/
|
|
7
|
+
import type { RessEvent, RessReporter } from '../../api/reporter.js';
|
|
8
|
+
export declare function createJsonReporter(): RessReporter & {
|
|
9
|
+
events: RessEvent[];
|
|
10
|
+
};
|
|
11
|
+
/** Vuelca un único objeto JSON a stdout. Usado por `ress info --json`. */
|
|
12
|
+
export declare function printJson(value: unknown, stdout?: NodeJS.WritableStream): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function createJsonReporter() {
|
|
2
|
+
const events = [];
|
|
3
|
+
return {
|
|
4
|
+
events,
|
|
5
|
+
event(e) {
|
|
6
|
+
events.push(e);
|
|
7
|
+
},
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/** Vuelca un único objeto JSON a stdout. Usado por `ress info --json`. */
|
|
11
|
+
export function printJson(value, stdout = process.stdout) {
|
|
12
|
+
stdout.write(JSON.stringify(value, null, 2) + '\n');
|
|
13
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* El reporter de terminal.
|
|
3
|
+
*
|
|
4
|
+
* Único módulo del paquete que llama a `console`/`process.stdout` — la regla
|
|
5
|
+
* que el test de arquitectura verifica. Traduce cada `RessEvent` a una línea de
|
|
6
|
+
* texto; nada de lo que hay acá vive en la API.
|
|
7
|
+
*
|
|
8
|
+
* Todo lo informativo va a stdout; advertencias y errores, a stderr. Es lo que
|
|
9
|
+
* permite `ress info --json > info.json` sin que un warning corrompa el archivo.
|
|
10
|
+
*/
|
|
11
|
+
import type { RessEvent, RessReporter } from '../../api/reporter.js';
|
|
12
|
+
import type { RessError } from '../../api/errors.js';
|
|
13
|
+
import type { LogLevel } from '../../api/types.js';
|
|
14
|
+
import { type FormatEnv } from './format.js';
|
|
15
|
+
export interface TerminalReporterOptions {
|
|
16
|
+
logLevel?: LogLevel;
|
|
17
|
+
clearScreen?: boolean;
|
|
18
|
+
stdout?: NodeJS.WritableStream;
|
|
19
|
+
stderr?: NodeJS.WritableStream;
|
|
20
|
+
env?: NodeJS.ProcessEnv;
|
|
21
|
+
}
|
|
22
|
+
export declare function createTerminalReporter(opts?: TerminalReporterOptions): RessReporter;
|
|
23
|
+
interface Sinks {
|
|
24
|
+
out: (line: string) => void;
|
|
25
|
+
debugOut: (line: string) => void;
|
|
26
|
+
warnOut: (line: string) => void;
|
|
27
|
+
errOut: (line: string) => void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Un caso por variante de `RessEvent`, a propósito: es lo que el test de
|
|
31
|
+
* reporter recorre para comprobar que ninguna se quedó sin formato.
|
|
32
|
+
*/
|
|
33
|
+
export declare function formatEvent(e: RessEvent, env: FormatEnv, sinks: Sinks): void;
|
|
34
|
+
export declare function formatError(error: RessError, env: FormatEnv): string;
|
|
35
|
+
export {};
|