rtgl 0.0.38 → 1.0.0-rc10

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.
Files changed (2) hide show
  1. package/cli.js +113 -49
  2. package/package.json +5 -5
package/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { build, scaffold, watch, examples } from "@rettangoli/fe/cli";
4
- import { generate, report, accept } from "@rettangoli/vt/cli";
5
- import { buildSite, watchSite, screenshotCommand, initSite } from "@rettangoli/sites/cli";
3
+ import { build, check, scaffold, watch, examples } from "@rettangoli/fe/cli";
4
+ import { generate, screenshot, report, accept } from "@rettangoli/vt/cli";
5
+ import { buildSite, watchSite, initSite } from "@rettangoli/sites/cli";
6
6
  import { buildSvg } from "@rettangoli/ui/cli";
7
7
  import { Command } from "commander";
8
8
  import { readFileSync, existsSync } from "fs";
@@ -25,11 +25,14 @@ function readConfig() {
25
25
  const configContent = readFileSync(configPath, "utf8");
26
26
  return yaml.load(configContent);
27
27
  } catch (error) {
28
- console.error("Error reading config file:", error.message);
29
- return null;
28
+ throw new Error(`Error reading config file "${configPath}": ${error.message}`);
30
29
  }
31
30
  }
32
31
 
32
+ function collectValues(value, previous = []) {
33
+ return [...previous, value];
34
+ }
35
+
33
36
  const program = new Command();
34
37
 
35
38
  program
@@ -103,6 +106,41 @@ Examples:
103
106
  build(options);
104
107
  });
105
108
 
109
+ feCommand
110
+ .command("check")
111
+ .description("Validate frontend component file contracts")
112
+ .option("--format <format>", "Output format: text or json", "text")
113
+ .addHelpText(
114
+ "after",
115
+ `
116
+
117
+ Examples:
118
+ $ rettangoli fe check
119
+ $ rettangoli fe check --format json
120
+ `,
121
+ )
122
+ .action((options) => {
123
+ const config = readConfig();
124
+
125
+ if (!config) {
126
+ throw new Error("rettangoli.config.yaml not found");
127
+ }
128
+
129
+ if (!config.fe?.dirs?.length) {
130
+ throw new Error("fe.dirs not found or empty in config");
131
+ }
132
+
133
+ const missingDirs = config.fe.dirs.filter(
134
+ (dir) => !existsSync(resolve(process.cwd(), dir)),
135
+ );
136
+ if (missingDirs.length > 0) {
137
+ throw new Error(`Directories do not exist: ${missingDirs.join(", ")}`);
138
+ }
139
+
140
+ options.dirs = config.fe.dirs;
141
+ check(options);
142
+ });
143
+
106
144
  feCommand
107
145
  .command("scaffold")
108
146
  .description("Scaffold UI components")
@@ -193,12 +231,15 @@ const vtCommand = program
193
231
 
194
232
  vtCommand
195
233
  .command("generate")
196
- .description("Generate visualizations")
197
- .option("--skip-screenshots", "Skip screenshot generation")
198
- .option("--screenshot-wait-time <time>", "Wait time between screenshots", parseInt, 0)
199
- .option("--concurrency <number>", "Number of concurrent screenshots", parseInt, 12)
200
- .option("--wait-event <name>", "Custom event name to wait for instead of networkidle (e.g., vt:ready)")
201
- .action((options) => {
234
+ .description("Generate candidate HTML pages only (no screenshots)")
235
+ .option("--concurrency <number>", "Number of parallel capture workers", parseInt)
236
+ .option("--timeout <ms>", "Global capture timeout in ms", parseInt)
237
+ .option("--wait-event <name>", "Custom event name to mark page ready (uses event wait strategy)")
238
+ .option("--folder <path>", "Run only specs under folder prefix (repeatable)", collectValues, [])
239
+ .option("--group <section-key>", "Run only one section key from vt.sections (repeatable)", collectValues, [])
240
+ .option("--item <spec-path>", "Run only one spec path relative to vt/specs (repeatable)", collectValues, [])
241
+ .option("--headed", "Run Playwright in headed mode")
242
+ .action(async (options) => {
202
243
  console.log(`rtgl v${packageJson.version}`);
203
244
  const config = readConfig();
204
245
 
@@ -208,17 +249,49 @@ vtCommand
208
249
 
209
250
  // Use vt.path from config, default to 'vt'
210
251
  options.vtPath = config.vt?.path || "vt";
252
+ if (options.headed) {
253
+ options.headless = false;
254
+ }
255
+ options.captureScreenshots = false;
256
+
257
+ await generate(options);
258
+ });
259
+
260
+ vtCommand
261
+ .command("screenshot")
262
+ .description("Generate candidate HTML pages and capture screenshots")
263
+ .option("--concurrency <number>", "Number of parallel capture workers", parseInt)
264
+ .option("--timeout <ms>", "Global capture timeout in ms", parseInt)
265
+ .option("--wait-event <name>", "Custom event name to mark page ready (uses event wait strategy)")
266
+ .option("--folder <path>", "Run only specs under folder prefix (repeatable)", collectValues, [])
267
+ .option("--group <section-key>", "Run only one section key from vt.sections (repeatable)", collectValues, [])
268
+ .option("--item <spec-path>", "Run only one spec path relative to vt/specs (repeatable)", collectValues, [])
269
+ .option("--headed", "Run Playwright in headed mode")
270
+ .action(async (options) => {
271
+ console.log(`rtgl v${packageJson.version}`);
272
+ const config = readConfig();
273
+
274
+ if (!config) {
275
+ throw new Error("rettangoli.config.yaml not found");
276
+ }
211
277
 
212
- generate(options);
278
+ options.vtPath = config.vt?.path || "vt";
279
+ if (options.headed) {
280
+ options.headless = false;
281
+ }
282
+ await screenshot(options);
213
283
  });
214
284
 
215
285
  vtCommand
216
286
  .command("report")
217
287
  .description("Create reports")
218
- .option("--compare-method <method>", "Comparison method: pixelmatch or md5", "pixelmatch")
219
- .option("--color-threshold <number>", "Color threshold for pixelmatch (0-1)", parseFloat, 0.1)
220
- .option("--diff-threshold <number>", "Max diff pixels percentage to pass (0-100)", parseFloat, 0.3)
221
- .action((options) => {
288
+ .option("--compare-method <method>", "Comparison method: pixelmatch or md5")
289
+ .option("--color-threshold <number>", "Color threshold for pixelmatch (0-1)", parseFloat)
290
+ .option("--diff-threshold <number>", "Max diff pixels percentage to pass (0-100)", parseFloat)
291
+ .option("--folder <path>", "Compare only screenshots under folder prefix (repeatable)", collectValues, [])
292
+ .option("--group <section-key>", "Compare only one section key from vt.sections (repeatable)", collectValues, [])
293
+ .option("--item <spec-path>", "Compare only one spec path relative to vt/specs (repeatable)", collectValues, [])
294
+ .action(async (options) => {
222
295
  const config = readConfig();
223
296
 
224
297
  if (!config) {
@@ -226,18 +299,21 @@ vtCommand
226
299
  }
227
300
 
228
301
  const vtPath = config.vt?.path || "vt";
229
- report({
302
+ await report({
230
303
  vtPath,
231
304
  compareMethod: options.compareMethod,
232
305
  colorThreshold: options.colorThreshold,
233
306
  diffThreshold: options.diffThreshold,
307
+ folder: options.folder,
308
+ group: options.group,
309
+ item: options.item,
234
310
  });
235
311
  });
236
312
 
237
313
  vtCommand
238
314
  .command("accept")
239
315
  .description("Accept changes")
240
- .action(() => {
316
+ .action(async () => {
241
317
  const config = readConfig();
242
318
 
243
319
  if (!config) {
@@ -245,7 +321,7 @@ vtCommand
245
321
  }
246
322
 
247
323
  const vtPath = config.vt?.path || "vt";
248
- accept({ vtPath });
324
+ await accept({ vtPath });
249
325
  });
250
326
 
251
327
  const sitesCommand = program.command("sites").description("Rettangoli Sites");
@@ -264,23 +340,19 @@ sitesCommand
264
340
  sitesCommand
265
341
  .command("build")
266
342
  .description("Build the site")
267
- .option("-r, --rootDir <path>", "Path to root directory", "./")
268
- .option("-o, --outputPath <path>", "Path to destination directory", "./_site")
269
- .option("-s, --screenshots", "Capture screenshots after build", false)
343
+ .option("-r, --root-dir <path>", "Path to root directory", "./")
344
+ .option("--rootDir <path>", "Deprecated alias for --root-dir")
345
+ .option("-o, --output-path <path>", "Path to destination directory", "./_site")
346
+ .option("--outputPath <path>", "Deprecated alias for --output-path")
347
+ .option("-q, --quiet", "Suppress non-error logs")
270
348
  .action(async (options) => {
271
- console.log("Building site with options:", options);
272
349
  await buildSite({
273
350
  rootDir: options.rootDir,
274
351
  outputPath: options.outputPath,
352
+ quiet: !!options.quiet,
275
353
  });
276
- console.log("Build completed successfully!");
277
-
278
- // If screenshots option is enabled, run screenshot command
279
- if (options.screenshots) {
280
- console.log("Capturing screenshots...");
281
- await screenshotCommand({
282
- rootDir: options.rootDir,
283
- });
354
+ if (!options.quiet) {
355
+ console.log("Build completed successfully!");
284
356
  }
285
357
  });
286
358
 
@@ -288,27 +360,19 @@ sitesCommand
288
360
  .command("watch")
289
361
  .description("Watch and rebuild site on changes")
290
362
  .option("-p, --port <port>", "The port to use", parseInt, 3001)
291
- .option("-r, --rootDir <path>", "Path to root directory", ".")
292
- .option("-s, --screenshots", "Enable automatic screenshot capture on page changes", false)
363
+ .option("-r, --root-dir <path>", "Path to root directory", ".")
364
+ .option("--rootDir <path>", "Deprecated alias for --root-dir")
365
+ .option("-o, --output-path <path>", "Path to destination directory", "./_site")
366
+ .option("--outputPath <path>", "Deprecated alias for --output-path")
367
+ .option("--reload-mode <mode>", "Reload mode: body (hot body replacement) or full (full-page reload)", "body")
368
+ .option("-q, --quiet", "Suppress non-error logs")
293
369
  .action(async (options) => {
294
- console.log("Starting watch mode with options:", options);
295
370
  watchSite({
296
371
  port: options.port,
297
372
  rootDir: options.rootDir,
298
- screenshots: options.screenshots,
299
- });
300
- });
301
-
302
- sitesCommand
303
- .command("screenshot")
304
- .description("Capture screenshots of all pages")
305
- .option("-p, --port <port>", "The port to use for temp server", parseInt, 3001)
306
- .option("-r, --rootDir <path>", "Path to root directory", ".")
307
- .action(async (options) => {
308
- console.log("Capturing screenshots with options:", options);
309
- await screenshotCommand({
310
- port: options.port,
311
- rootDir: options.rootDir,
373
+ outputPath: options.outputPath,
374
+ reloadMode: options.reloadMode,
375
+ quiet: !!options.quiet,
312
376
  });
313
377
  });
314
378
 
@@ -349,4 +413,4 @@ Examples:
349
413
  buildSvg(options);
350
414
  });
351
415
 
352
- program.parse();
416
+ await program.parseAsync();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rtgl",
3
- "version": "0.0.38",
3
+ "version": "1.0.0-rc10",
4
4
  "description": "CLI tool for Rettangoli - A frontend framework and development toolkit",
5
5
  "type": "module",
6
6
  "bin": {
@@ -47,9 +47,9 @@
47
47
  "dependencies": {
48
48
  "commander": "^14.0.0",
49
49
  "js-yaml": "^4.1.0",
50
- "@rettangoli/fe": "0.0.14",
51
- "@rettangoli/sites": "0.2.7",
52
- "@rettangoli/vt": "0.0.14",
53
- "@rettangoli/ui": "0.1.31"
50
+ "@rettangoli/fe": "1.0.0-rc3",
51
+ "@rettangoli/sites": "1.0.0-rc2",
52
+ "@rettangoli/vt": "1.0.0-rc4",
53
+ "@rettangoli/ui": "1.0.0-rc4"
54
54
  }
55
55
  }