rtgl 1.0.0-rc3 → 1.0.0-rc32
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/cli.js +205 -39
- package/package.json +7 -5
package/cli.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { build, check, scaffold, watch, examples } from "@rettangoli/fe/cli";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { check as checkContracts } from "@rettangoli/check/cli";
|
|
5
|
+
import { build as buildBe, check as checkBe, watch as watchBe } from "@rettangoli/be/cli";
|
|
6
|
+
import { generate, screenshot, report, accept } from "@rettangoli/vt/cli";
|
|
7
|
+
import { buildSite, watchSite, initSite } from "@rettangoli/sites/cli";
|
|
6
8
|
import { buildSvg } from "@rettangoli/ui/cli";
|
|
7
|
-
import { Command } from "commander";
|
|
9
|
+
import { Command, InvalidArgumentError } from "commander";
|
|
8
10
|
import { readFileSync, existsSync } from "fs";
|
|
9
11
|
import { resolve } from "path";
|
|
10
12
|
import yaml from "js-yaml";
|
|
@@ -33,6 +35,27 @@ function collectValues(value, previous = []) {
|
|
|
33
35
|
return [...previous, value];
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
function parseIntegerOption(value) {
|
|
39
|
+
if (!/^-?\d+$/.test(String(value))) {
|
|
40
|
+
throw new InvalidArgumentError(`Expected an integer but received "${value}"`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const parsed = Number.parseInt(value, 10);
|
|
44
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
45
|
+
throw new InvalidArgumentError(`Expected a safe integer but received "${value}"`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return parsed;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function parsePortOption(value) {
|
|
52
|
+
const parsed = parseIntegerOption(value);
|
|
53
|
+
if (parsed < 1 || parsed > 65535) {
|
|
54
|
+
throw new InvalidArgumentError(`Port must be between 1 and 65535, received "${value}"`);
|
|
55
|
+
}
|
|
56
|
+
return parsed;
|
|
57
|
+
}
|
|
58
|
+
|
|
36
59
|
const program = new Command();
|
|
37
60
|
|
|
38
61
|
program
|
|
@@ -52,6 +75,39 @@ Examples:
|
|
|
52
75
|
`,
|
|
53
76
|
);
|
|
54
77
|
|
|
78
|
+
program
|
|
79
|
+
.command("check")
|
|
80
|
+
.description("Run Rettangoli static contract checks")
|
|
81
|
+
.option("--dir <path>", "Component directory to scan (repeatable)", collectValues, [])
|
|
82
|
+
.option("--format <format>", "Output format: text, json, or sarif", "text")
|
|
83
|
+
.option("--warn-as-error", "Treat warnings as errors")
|
|
84
|
+
.option("--no-yahtml", "Disable YAHTML attr/prop validation")
|
|
85
|
+
.option("--expr", "Enable expression scope/type checks")
|
|
86
|
+
.option("--watch", "Watch for file changes and re-run checks")
|
|
87
|
+
.option("--watch-interval-ms <ms>", "Watch poll interval in milliseconds", parseIntegerOption, 800)
|
|
88
|
+
.addHelpText(
|
|
89
|
+
"after",
|
|
90
|
+
`
|
|
91
|
+
|
|
92
|
+
Examples:
|
|
93
|
+
$ rtgl check
|
|
94
|
+
$ rtgl check --dir src/components --format json
|
|
95
|
+
$ rtgl check --warn-as-error
|
|
96
|
+
`,
|
|
97
|
+
)
|
|
98
|
+
.action(async (options) => {
|
|
99
|
+
await checkContracts({
|
|
100
|
+
cwd: process.cwd(),
|
|
101
|
+
dirs: options.dir,
|
|
102
|
+
format: options.format,
|
|
103
|
+
warnAsError: !!options.warnAsError,
|
|
104
|
+
includeYahtml: options.yahtml !== false,
|
|
105
|
+
includeExpression: !!options.expr,
|
|
106
|
+
watch: !!options.watch,
|
|
107
|
+
watchIntervalMs: Number.isFinite(options.watchIntervalMs) ? options.watchIntervalMs : 800,
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
55
111
|
const feCommand = program.command("fe").description("Frontend framework");
|
|
56
112
|
|
|
57
113
|
feCommand
|
|
@@ -169,7 +225,7 @@ Examples:
|
|
|
169
225
|
feCommand
|
|
170
226
|
.command("watch")
|
|
171
227
|
.description("Watch for changes")
|
|
172
|
-
.option("-p, --port <port>", "The port to use",
|
|
228
|
+
.option("-p, --port <port>", "The port to use", parsePortOption, 3001)
|
|
173
229
|
.option("-s, --setup-path <path>", "Custom setup file path")
|
|
174
230
|
.addHelpText(
|
|
175
231
|
"after",
|
|
@@ -225,16 +281,112 @@ feCommand
|
|
|
225
281
|
examples(options);
|
|
226
282
|
});
|
|
227
283
|
|
|
284
|
+
const beCommand = program.command("be").description("Backend framework");
|
|
285
|
+
|
|
286
|
+
beCommand
|
|
287
|
+
.command("build")
|
|
288
|
+
.description("Build backend method registry and app entry")
|
|
289
|
+
.option("-s, --setup-path <path>", "Custom setup file path")
|
|
290
|
+
.option("-m, --middleware-dir <path>", "Custom middleware directory path")
|
|
291
|
+
.option("-o, --outdir <path>", "Generated output directory")
|
|
292
|
+
.action((options) => {
|
|
293
|
+
const config = readConfig();
|
|
294
|
+
|
|
295
|
+
if (!config) {
|
|
296
|
+
throw new Error("rettangoli.config.yaml not found");
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (!config.be?.dirs?.length) {
|
|
300
|
+
throw new Error("be.dirs not found or empty in config");
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const missingDirs = config.be.dirs.filter(
|
|
304
|
+
(dir) => !existsSync(resolve(process.cwd(), dir)),
|
|
305
|
+
);
|
|
306
|
+
if (missingDirs.length > 0) {
|
|
307
|
+
throw new Error(`Directories do not exist: ${missingDirs.join(", ")}`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
options.dirs = config.be.dirs;
|
|
311
|
+
options.middlewareDir = options.middlewareDir || config.be.middlewareDir || "./src/middleware";
|
|
312
|
+
options.setup = options.setupPath || config.be.setup || "./src/setup.js";
|
|
313
|
+
options.outdir = options.outdir || config.be.outdir || "./.rtgl-be/generated";
|
|
314
|
+
options.domainErrors = config.be.domainErrors || {};
|
|
315
|
+
|
|
316
|
+
buildBe(options);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
beCommand
|
|
320
|
+
.command("check")
|
|
321
|
+
.description("Validate backend RPC contracts")
|
|
322
|
+
.option("--format <format>", "Output format: text or json", "text")
|
|
323
|
+
.option("-m, --middleware-dir <path>", "Custom middleware directory path")
|
|
324
|
+
.action((options) => {
|
|
325
|
+
const config = readConfig();
|
|
326
|
+
|
|
327
|
+
if (!config) {
|
|
328
|
+
throw new Error("rettangoli.config.yaml not found");
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (!config.be?.dirs?.length) {
|
|
332
|
+
throw new Error("be.dirs not found or empty in config");
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const missingDirs = config.be.dirs.filter(
|
|
336
|
+
(dir) => !existsSync(resolve(process.cwd(), dir)),
|
|
337
|
+
);
|
|
338
|
+
if (missingDirs.length > 0) {
|
|
339
|
+
throw new Error(`Directories do not exist: ${missingDirs.join(", ")}`);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
options.dirs = config.be.dirs;
|
|
343
|
+
options.middlewareDir = options.middlewareDir || config.be.middlewareDir || "./src/middleware";
|
|
344
|
+
|
|
345
|
+
checkBe(options);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
beCommand
|
|
349
|
+
.command("watch")
|
|
350
|
+
.description("Watch backend files and rebuild generated registry")
|
|
351
|
+
.option("-s, --setup-path <path>", "Custom setup file path")
|
|
352
|
+
.option("-m, --middleware-dir <path>", "Custom middleware directory path")
|
|
353
|
+
.option("-o, --outdir <path>", "Generated output directory")
|
|
354
|
+
.action((options) => {
|
|
355
|
+
const config = readConfig();
|
|
356
|
+
|
|
357
|
+
if (!config) {
|
|
358
|
+
throw new Error("rettangoli.config.yaml not found");
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (!config.be?.dirs?.length) {
|
|
362
|
+
throw new Error("be.dirs not found or empty in config");
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const missingDirs = config.be.dirs.filter(
|
|
366
|
+
(dir) => !existsSync(resolve(process.cwd(), dir)),
|
|
367
|
+
);
|
|
368
|
+
if (missingDirs.length > 0) {
|
|
369
|
+
throw new Error(`Directories do not exist: ${missingDirs.join(", ")}`);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
options.dirs = config.be.dirs;
|
|
373
|
+
options.middlewareDir = options.middlewareDir || config.be.middlewareDir || "./src/middleware";
|
|
374
|
+
options.setup = options.setupPath || config.be.setup || "./src/setup.js";
|
|
375
|
+
options.outdir = options.outdir || config.be.outdir || "./.rtgl-be/generated";
|
|
376
|
+
options.domainErrors = config.be.domainErrors || {};
|
|
377
|
+
|
|
378
|
+
watchBe(options);
|
|
379
|
+
});
|
|
380
|
+
|
|
228
381
|
const vtCommand = program
|
|
229
382
|
.command("vt")
|
|
230
383
|
.description("Rettangoli Visual Testing");
|
|
231
384
|
|
|
232
385
|
vtCommand
|
|
233
386
|
.command("generate")
|
|
234
|
-
.description("Generate
|
|
235
|
-
.option("--
|
|
236
|
-
.option("--
|
|
237
|
-
.option("--timeout <ms>", "Global capture timeout in ms", parseInt)
|
|
387
|
+
.description("Generate candidate HTML pages only (no screenshots)")
|
|
388
|
+
.option("--concurrency <number>", "Number of parallel capture workers", parseIntegerOption)
|
|
389
|
+
.option("--timeout <ms>", "Global capture timeout in ms", parseIntegerOption)
|
|
238
390
|
.option("--wait-event <name>", "Custom event name to mark page ready (uses event wait strategy)")
|
|
239
391
|
.option("--folder <path>", "Run only specs under folder prefix (repeatable)", collectValues, [])
|
|
240
392
|
.option("--group <section-key>", "Run only one section key from vt.sections (repeatable)", collectValues, [])
|
|
@@ -253,10 +405,36 @@ vtCommand
|
|
|
253
405
|
if (options.headed) {
|
|
254
406
|
options.headless = false;
|
|
255
407
|
}
|
|
408
|
+
options.captureScreenshots = false;
|
|
256
409
|
|
|
257
410
|
await generate(options);
|
|
258
411
|
});
|
|
259
412
|
|
|
413
|
+
vtCommand
|
|
414
|
+
.command("screenshot")
|
|
415
|
+
.description("Generate candidate HTML pages and capture screenshots")
|
|
416
|
+
.option("--concurrency <number>", "Number of parallel capture workers", parseIntegerOption)
|
|
417
|
+
.option("--timeout <ms>", "Global capture timeout in ms", parseIntegerOption)
|
|
418
|
+
.option("--wait-event <name>", "Custom event name to mark page ready (uses event wait strategy)")
|
|
419
|
+
.option("--folder <path>", "Run only specs under folder prefix (repeatable)", collectValues, [])
|
|
420
|
+
.option("--group <section-key>", "Run only one section key from vt.sections (repeatable)", collectValues, [])
|
|
421
|
+
.option("--item <spec-path>", "Run only one spec path relative to vt/specs (repeatable)", collectValues, [])
|
|
422
|
+
.option("--headed", "Run Playwright in headed mode")
|
|
423
|
+
.action(async (options) => {
|
|
424
|
+
console.log(`rtgl v${packageJson.version}`);
|
|
425
|
+
const config = readConfig();
|
|
426
|
+
|
|
427
|
+
if (!config) {
|
|
428
|
+
throw new Error("rettangoli.config.yaml not found");
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
options.vtPath = config.vt?.path || "vt";
|
|
432
|
+
if (options.headed) {
|
|
433
|
+
options.headless = false;
|
|
434
|
+
}
|
|
435
|
+
await screenshot(options);
|
|
436
|
+
});
|
|
437
|
+
|
|
260
438
|
vtCommand
|
|
261
439
|
.command("report")
|
|
262
440
|
.description("Create reports")
|
|
@@ -315,51 +493,39 @@ sitesCommand
|
|
|
315
493
|
sitesCommand
|
|
316
494
|
.command("build")
|
|
317
495
|
.description("Build the site")
|
|
318
|
-
.option("-r, --
|
|
319
|
-
.option("
|
|
320
|
-
.option("-
|
|
496
|
+
.option("-r, --root-dir <path>", "Path to root directory", "./")
|
|
497
|
+
.option("--rootDir <path>", "Deprecated alias for --root-dir")
|
|
498
|
+
.option("-o, --output-path <path>", "Path to destination directory", "./_site")
|
|
499
|
+
.option("--outputPath <path>", "Deprecated alias for --output-path")
|
|
500
|
+
.option("-q, --quiet", "Suppress non-error logs")
|
|
321
501
|
.action(async (options) => {
|
|
322
|
-
console.log("Building site with options:", options);
|
|
323
502
|
await buildSite({
|
|
324
503
|
rootDir: options.rootDir,
|
|
325
504
|
outputPath: options.outputPath,
|
|
505
|
+
quiet: !!options.quiet,
|
|
326
506
|
});
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
// If screenshots option is enabled, run screenshot command
|
|
330
|
-
if (options.screenshots) {
|
|
331
|
-
console.log("Capturing screenshots...");
|
|
332
|
-
await screenshotCommand({
|
|
333
|
-
rootDir: options.rootDir,
|
|
334
|
-
});
|
|
507
|
+
if (!options.quiet) {
|
|
508
|
+
console.log("Build completed successfully!");
|
|
335
509
|
}
|
|
336
510
|
});
|
|
337
511
|
|
|
338
512
|
sitesCommand
|
|
339
513
|
.command("watch")
|
|
340
514
|
.description("Watch and rebuild site on changes")
|
|
341
|
-
.option("-p, --port <port>", "The port to use",
|
|
342
|
-
.option("-r, --
|
|
343
|
-
.option("
|
|
344
|
-
.
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
rootDir: options.rootDir,
|
|
349
|
-
screenshots: options.screenshots,
|
|
350
|
-
});
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
sitesCommand
|
|
354
|
-
.command("screenshot")
|
|
355
|
-
.description("Capture screenshots of all pages")
|
|
356
|
-
.option("-p, --port <port>", "The port to use for temp server", parseInt, 3001)
|
|
357
|
-
.option("-r, --rootDir <path>", "Path to root directory", ".")
|
|
515
|
+
.option("-p, --port <port>", "The port to use", parsePortOption, 3001)
|
|
516
|
+
.option("-r, --root-dir <path>", "Path to root directory", ".")
|
|
517
|
+
.option("--rootDir <path>", "Deprecated alias for --root-dir")
|
|
518
|
+
.option("-o, --output-path <path>", "Path to destination directory", "./_site")
|
|
519
|
+
.option("--outputPath <path>", "Deprecated alias for --output-path")
|
|
520
|
+
.option("--reload-mode <mode>", "Reload mode: body (hot body replacement) or full (full-page reload)", "body")
|
|
521
|
+
.option("-q, --quiet", "Suppress non-error logs")
|
|
358
522
|
.action(async (options) => {
|
|
359
|
-
|
|
360
|
-
await screenshotCommand({
|
|
523
|
+
await watchSite({
|
|
361
524
|
port: options.port,
|
|
362
525
|
rootDir: options.rootDir,
|
|
526
|
+
outputPath: options.outputPath,
|
|
527
|
+
reloadMode: options.reloadMode,
|
|
528
|
+
quiet: !!options.quiet,
|
|
363
529
|
});
|
|
364
530
|
});
|
|
365
531
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rtgl",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-rc32",
|
|
4
4
|
"description": "CLI tool for Rettangoli - A frontend framework and development toolkit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -47,9 +47,11 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"commander": "^14.0.0",
|
|
49
49
|
"js-yaml": "^4.1.0",
|
|
50
|
-
"@rettangoli/
|
|
51
|
-
"@rettangoli/
|
|
52
|
-
"@rettangoli/
|
|
53
|
-
"@rettangoli/
|
|
50
|
+
"@rettangoli/check": "0.1.2",
|
|
51
|
+
"@rettangoli/be": "1.0.0-rc1",
|
|
52
|
+
"@rettangoli/fe": "1.0.0-rc5",
|
|
53
|
+
"@rettangoli/sites": "1.0.0-rc13",
|
|
54
|
+
"@rettangoli/vt": "1.0.0-rc16",
|
|
55
|
+
"@rettangoli/ui": "1.0.0-rc16"
|
|
54
56
|
}
|
|
55
57
|
}
|