rtgl 1.0.0-rc8 → 1.0.1
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 +187 -9
- 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 {
|
|
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";
|
|
5
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")
|
|
@@ -334,7 +512,7 @@ sitesCommand
|
|
|
334
512
|
sitesCommand
|
|
335
513
|
.command("watch")
|
|
336
514
|
.description("Watch and rebuild site on changes")
|
|
337
|
-
.option("-p, --port <port>", "The port to use",
|
|
515
|
+
.option("-p, --port <port>", "The port to use", parsePortOption, 3001)
|
|
338
516
|
.option("-r, --root-dir <path>", "Path to root directory", ".")
|
|
339
517
|
.option("--rootDir <path>", "Deprecated alias for --root-dir")
|
|
340
518
|
.option("-o, --output-path <path>", "Path to destination directory", "./_site")
|
|
@@ -342,7 +520,7 @@ sitesCommand
|
|
|
342
520
|
.option("--reload-mode <mode>", "Reload mode: body (hot body replacement) or full (full-page reload)", "body")
|
|
343
521
|
.option("-q, --quiet", "Suppress non-error logs")
|
|
344
522
|
.action(async (options) => {
|
|
345
|
-
watchSite({
|
|
523
|
+
await watchSite({
|
|
346
524
|
port: options.port,
|
|
347
525
|
rootDir: options.rootDir,
|
|
348
526
|
outputPath: options.outputPath,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rtgl",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
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.1",
|
|
53
|
+
"@rettangoli/sites": "1.0.0-rc13",
|
|
54
|
+
"@rettangoli/vt": "1.0.1",
|
|
55
|
+
"@rettangoli/ui": "1.0.1"
|
|
54
56
|
}
|
|
55
57
|
}
|