@rettangoli/vt 0.0.10 → 0.0.12
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/package.json +1 -1
- package/src/cli/generate.js +33 -30
- package/src/cli/report.js +3 -4
- package/src/common.js +24 -12
package/package.json
CHANGED
package/src/cli/generate.js
CHANGED
|
@@ -9,9 +9,8 @@ import {
|
|
|
9
9
|
readYaml,
|
|
10
10
|
} from "../common.js";
|
|
11
11
|
|
|
12
|
-
const libraryTemplatesPath = new URL(
|
|
13
|
-
const libraryStaticPath = new URL(
|
|
14
|
-
|
|
12
|
+
const libraryTemplatesPath = new URL("./templates", import.meta.url).pathname;
|
|
13
|
+
const libraryStaticPath = new URL("./static", import.meta.url).pathname;
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
16
|
* Main function that orchestrates the entire process
|
|
@@ -21,14 +20,14 @@ async function main(options) {
|
|
|
21
20
|
skipScreenshots = false,
|
|
22
21
|
vtPath = "./vt",
|
|
23
22
|
screenshotWaitTime = 0,
|
|
24
|
-
port = 3001
|
|
23
|
+
port = 3001,
|
|
25
24
|
} = options;
|
|
26
25
|
|
|
27
26
|
const specsPath = join(vtPath, "specs");
|
|
28
27
|
const mainConfigPath = "rettangoli.config.yaml";
|
|
29
28
|
const siteOutputPath = join(".rettangoli", "vt", "_site");
|
|
30
29
|
const candidatePath = join(siteOutputPath, "candidate");
|
|
31
|
-
|
|
30
|
+
|
|
32
31
|
// Read VT config from main rettangoli.config.yaml
|
|
33
32
|
let configData = {};
|
|
34
33
|
try {
|
|
@@ -38,32 +37,30 @@ async function main(options) {
|
|
|
38
37
|
console.log("Main config file not found, using defaults");
|
|
39
38
|
}
|
|
40
39
|
|
|
40
|
+
const configUrl = configData.url;
|
|
41
|
+
|
|
41
42
|
// Clear candidate directory
|
|
42
43
|
await rm(candidatePath, { recursive: true, force: true });
|
|
43
44
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
44
45
|
|
|
45
46
|
// Copy static files from library to site directory
|
|
46
47
|
await cp(libraryStaticPath, siteOutputPath, { recursive: true });
|
|
47
|
-
|
|
48
|
+
|
|
48
49
|
// Copy user's static files if they exist
|
|
49
50
|
const userStaticPath = join(vtPath, "static");
|
|
50
51
|
if (existsSync(userStaticPath)) {
|
|
51
52
|
await cp(userStaticPath, siteOutputPath, { recursive: true });
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
//
|
|
55
|
+
// Resolve template paths
|
|
55
56
|
const localTemplatesPath = join(vtPath, "templates");
|
|
56
|
-
|
|
57
57
|
const defaultTemplatePath = existsSync(join(localTemplatesPath, "default.html"))
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
// Resolve index template path
|
|
58
|
+
? join(localTemplatesPath, "default.html")
|
|
59
|
+
: join(libraryTemplatesPath, "default.html");
|
|
62
60
|
const indexTemplatePath = existsSync(join(localTemplatesPath, "index.html"))
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
? join(localTemplatesPath, "index.html")
|
|
62
|
+
: join(libraryTemplatesPath, "index.html");
|
|
65
63
|
|
|
66
|
-
// Build template configuration for per-file/section templates
|
|
67
64
|
const templateConfig = {
|
|
68
65
|
defaultTemplate: defaultTemplatePath,
|
|
69
66
|
vtPath: vtPath,
|
|
@@ -74,40 +71,46 @@ async function main(options) {
|
|
|
74
71
|
specsPath,
|
|
75
72
|
defaultTemplatePath,
|
|
76
73
|
candidatePath,
|
|
77
|
-
templateConfig
|
|
74
|
+
templateConfig,
|
|
78
75
|
);
|
|
79
76
|
|
|
80
|
-
// Generate overview page
|
|
77
|
+
// Generate overview page (includes all files, skipped or not)
|
|
81
78
|
generateOverview(
|
|
82
79
|
generatedFiles,
|
|
83
80
|
indexTemplatePath,
|
|
84
81
|
join(siteOutputPath, "index.html"),
|
|
85
|
-
configData
|
|
82
|
+
configData,
|
|
86
83
|
);
|
|
87
84
|
|
|
85
|
+
// Take screenshots (only for non-skipped files)
|
|
88
86
|
if (!skipScreenshots) {
|
|
89
|
-
//
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
vtPath,
|
|
93
|
-
port
|
|
87
|
+
// Filter out files with skipScreenshot: true in frontmatter
|
|
88
|
+
const filesToScreenshot = generatedFiles.filter(
|
|
89
|
+
(file) => !file.frontMatter?.skipScreenshot
|
|
94
90
|
);
|
|
91
|
+
|
|
92
|
+
const skippedCount = generatedFiles.length - filesToScreenshot.length;
|
|
93
|
+
if (skippedCount > 0) {
|
|
94
|
+
console.log(`Skipping screenshots for ${skippedCount} files`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const server = configUrl ? null : startWebServer(siteOutputPath, vtPath, port);
|
|
95
98
|
try {
|
|
96
|
-
// Take screenshots with specified concurrency
|
|
97
99
|
await takeScreenshots(
|
|
98
|
-
|
|
100
|
+
filesToScreenshot,
|
|
99
101
|
`http://localhost:${port}`,
|
|
100
102
|
candidatePath,
|
|
101
103
|
24,
|
|
102
|
-
screenshotWaitTime
|
|
104
|
+
screenshotWaitTime,
|
|
105
|
+
configUrl,
|
|
103
106
|
);
|
|
104
107
|
} finally {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
+
if (server) {
|
|
109
|
+
server.close();
|
|
110
|
+
console.log("Server stopped");
|
|
111
|
+
}
|
|
108
112
|
}
|
|
109
113
|
}
|
|
110
|
-
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
export default main;
|
package/src/cli/report.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import crypto from "crypto";
|
|
4
|
-
import sharp from "sharp";
|
|
5
4
|
import { Liquid } from "liquidjs";
|
|
6
5
|
import { cp } from "node:fs/promises";
|
|
7
6
|
|
|
@@ -227,19 +226,19 @@ async function main(options = {}) {
|
|
|
227
226
|
};
|
|
228
227
|
console.log(JSON.stringify(logData, null, 2));
|
|
229
228
|
});
|
|
230
|
-
|
|
229
|
+
|
|
231
230
|
// Summary at the end
|
|
232
231
|
console.log(`\nSummary:`);
|
|
233
232
|
console.log(`Total images: ${results.length}`);
|
|
234
233
|
console.log(`Mismatched images: ${mismatchingItems.length}`);
|
|
235
|
-
|
|
234
|
+
|
|
236
235
|
// Generate HTML report
|
|
237
236
|
await generateReport({
|
|
238
237
|
results: mismatchingItems,
|
|
239
238
|
templatePath,
|
|
240
239
|
outputPath,
|
|
241
240
|
});
|
|
242
|
-
if(mismatchingItems.length > 0){
|
|
241
|
+
if(mismatchingItems.length > 0){
|
|
243
242
|
console.error("Error: there are more than 0 mismatching item.")
|
|
244
243
|
process.exit(1);
|
|
245
244
|
}
|
package/src/common.js
CHANGED
|
@@ -83,9 +83,8 @@ function getAllFiles(dirPath, arrayOfFiles = []) {
|
|
|
83
83
|
* Extract frontmatter from content
|
|
84
84
|
*/
|
|
85
85
|
function extractFrontMatter(content) {
|
|
86
|
-
const frontMatterRegex = /^---\s*\n([\s\S]*?)\n---\s
|
|
86
|
+
const frontMatterRegex = /^---\s*\n([\s\S]*?)\n---\s*(\r?\n|$)/;
|
|
87
87
|
const match = content.match(frontMatterRegex);
|
|
88
|
-
|
|
89
88
|
if (!match) {
|
|
90
89
|
return {
|
|
91
90
|
content: content,
|
|
@@ -180,7 +179,6 @@ function startWebServer(artifactsDir, staticDir, port) {
|
|
|
180
179
|
const server = http.createServer((req, res) => {
|
|
181
180
|
const url = new URL(req.url, `http://localhost:${port}`);
|
|
182
181
|
let path = url.pathname;
|
|
183
|
-
|
|
184
182
|
// Default to index.html for root path
|
|
185
183
|
if (path === "/") {
|
|
186
184
|
path = "/index.html";
|
|
@@ -249,7 +247,8 @@ async function takeScreenshots(
|
|
|
249
247
|
serverUrl,
|
|
250
248
|
screenshotsDir,
|
|
251
249
|
concurrency = 8,
|
|
252
|
-
waitTime = 0
|
|
250
|
+
waitTime = 0,
|
|
251
|
+
configUrl = undefined,
|
|
253
252
|
) {
|
|
254
253
|
// Ensure screenshots directory exists
|
|
255
254
|
ensureDirectoryExists(screenshotsDir);
|
|
@@ -258,13 +257,13 @@ async function takeScreenshots(
|
|
|
258
257
|
console.log("Launching browser to take screenshots...");
|
|
259
258
|
const browser = await chromium.launch();
|
|
260
259
|
|
|
261
|
-
const takeAndSaveScreenshot = async (page, basePath, suffix =
|
|
260
|
+
const takeAndSaveScreenshot = async (page, basePath, suffix = "") => {
|
|
262
261
|
const finalPath = suffix ? `${basePath}-${suffix}` : basePath;
|
|
263
262
|
const tempPngPath = join(screenshotsDir, `${finalPath}.png`);
|
|
264
263
|
const screenshotPath = join(screenshotsDir, `${finalPath}.webp`);
|
|
265
264
|
ensureDirectoryExists(dirname(screenshotPath));
|
|
266
265
|
|
|
267
|
-
await page.screenshot({ path: tempPngPath, fullPage: true});
|
|
266
|
+
await page.screenshot({ path: tempPngPath, fullPage: true });
|
|
268
267
|
await sharp(tempPngPath).webp({ quality: 85 }).toFile(screenshotPath);
|
|
269
268
|
|
|
270
269
|
if (existsSync(tempPngPath)) {
|
|
@@ -285,13 +284,26 @@ async function takeScreenshots(
|
|
|
285
284
|
// Create a new context and page for each file (for parallelism)
|
|
286
285
|
const context = await browser.newContext();
|
|
287
286
|
const page = await context.newPage();
|
|
288
|
-
let screenshotIndex = 0;
|
|
289
287
|
|
|
290
288
|
try {
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
289
|
+
const envVars = {};
|
|
290
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
291
|
+
if (key.startsWith('RTGL_VT_')) {
|
|
292
|
+
envVars[key] = value;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (Object.keys(envVars).length > 0) {
|
|
297
|
+
await page.addInitScript((vars) => {
|
|
298
|
+
Object.assign(window, vars);
|
|
299
|
+
}, envVars);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const frontMatterUrl = file.frontMatter?.url;
|
|
303
|
+
const constructedUrl = convertToHtmlExtension(`${serverUrl}/candidate/${file.path.replace(/\\/g, "/")}`);
|
|
304
|
+
const url = frontMatterUrl ?? configUrl ?? constructedUrl;
|
|
305
|
+
const fileUrl = url.startsWith("http") ? url : new URL(url, serverUrl).href;
|
|
306
|
+
|
|
295
307
|
console.log(`Navigating to ${fileUrl}`);
|
|
296
308
|
await page.goto(fileUrl, { waitUntil: "networkidle" });
|
|
297
309
|
if (waitTime > 0) {
|
|
@@ -300,7 +312,7 @@ async function takeScreenshots(
|
|
|
300
312
|
const baseName = removeExtension(file.path);
|
|
301
313
|
|
|
302
314
|
const initialScreenshotPath = await takeAndSaveScreenshot(page, baseName);
|
|
303
|
-
console.log(`
|
|
315
|
+
console.log(`Screenshot saved: ${initialScreenshotPath}`);
|
|
304
316
|
|
|
305
317
|
const stepContext = {
|
|
306
318
|
baseName,
|