frameshot-mcp 0.3.0 → 0.7.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/README.md +157 -47
- package/action.yml +144 -0
- package/dist/chunk-3LVWVDET.js +849 -0
- package/dist/chunk-47YJG5HR.js +690 -0
- package/dist/chunk-67JZQ6OI.js +819 -0
- package/dist/chunk-AZCGKIMU.js +850 -0
- package/dist/chunk-B3CLIGWU.js +786 -0
- package/dist/chunk-C6QSY4WR.js +811 -0
- package/dist/chunk-DX54PJKO.js +603 -0
- package/dist/chunk-EMCJGIMY.js +984 -0
- package/dist/chunk-FQNWGR62.js +849 -0
- package/dist/chunk-FTYTZW6D.js +203 -0
- package/dist/chunk-JGVKYXY2.js +857 -0
- package/dist/chunk-JYPEA4P2.js +846 -0
- package/dist/chunk-KHK35HDD.js +855 -0
- package/dist/chunk-Q7A3DLED.js +848 -0
- package/dist/chunk-SIA6XEHM.js +811 -0
- package/dist/chunk-ST35YDI6.js +834 -0
- package/dist/chunk-T5OBJK35.js +855 -0
- package/dist/chunk-U3GHS7KO.js +837 -0
- package/dist/chunk-WS2ASCD6.js +683 -0
- package/dist/chunk-WZMHVSUA.js +847 -0
- package/dist/chunk-ZZST6K7Y.js +987 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +407 -0
- package/dist/index.js +1077 -653
- package/dist/renderer.d.ts +297 -0
- package/dist/renderer.js +34 -0
- package/package.json +32 -7
- package/scripts/render-changed.mjs +233 -0
- package/scripts/setup-labels.sh +48 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// src/infrastructure/snapshot-store.ts
|
|
2
|
+
var SnapshotStore = class {
|
|
3
|
+
store = /* @__PURE__ */ new Map();
|
|
4
|
+
save(key, image) {
|
|
5
|
+
this.store.set(key, { image, timestamp: Date.now() });
|
|
6
|
+
}
|
|
7
|
+
get(key) {
|
|
8
|
+
return this.store.get(key);
|
|
9
|
+
}
|
|
10
|
+
list() {
|
|
11
|
+
return Array.from(this.store.entries()).map(([key, val]) => ({
|
|
12
|
+
key,
|
|
13
|
+
timestamp: val.timestamp
|
|
14
|
+
}));
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// src/use-cases/audit.ts
|
|
19
|
+
var AuditUseCase = class {
|
|
20
|
+
constructor(pool, htmlBuilder) {
|
|
21
|
+
this.pool = pool;
|
|
22
|
+
this.htmlBuilder = htmlBuilder;
|
|
23
|
+
}
|
|
24
|
+
pool;
|
|
25
|
+
htmlBuilder;
|
|
26
|
+
async auditA11y(code, framework, options = {}) {
|
|
27
|
+
const html = this.htmlBuilder.build(code, framework, {
|
|
28
|
+
darkMode: options.darkMode ?? false,
|
|
29
|
+
css: options.css ?? "",
|
|
30
|
+
tailwindVersion: options.tailwindVersion ?? "3"
|
|
31
|
+
});
|
|
32
|
+
const page = await this.pool.getPage("chromium");
|
|
33
|
+
const viewport = options.viewport ?? { width: 1280, height: 800 };
|
|
34
|
+
await this.pool.setViewport("chromium", viewport);
|
|
35
|
+
await page.setContent(html, { waitUntil: "load", timeout: 1e4 });
|
|
36
|
+
const axeSource = await import("axe-core").then((m) => m.source);
|
|
37
|
+
await page.addScriptTag({ content: axeSource });
|
|
38
|
+
const results = await page.evaluate(() => {
|
|
39
|
+
return window.axe.run();
|
|
40
|
+
});
|
|
41
|
+
return {
|
|
42
|
+
violations: results.violations.map(
|
|
43
|
+
(v) => ({
|
|
44
|
+
id: v.id,
|
|
45
|
+
impact: v.impact,
|
|
46
|
+
description: v.description,
|
|
47
|
+
helpUrl: v.helpUrl,
|
|
48
|
+
nodes: v.nodes.map((n) => ({
|
|
49
|
+
html: n.html,
|
|
50
|
+
target: n.target
|
|
51
|
+
}))
|
|
52
|
+
})
|
|
53
|
+
),
|
|
54
|
+
passes: results.passes.length,
|
|
55
|
+
incomplete: results.incomplete.length
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
async perfAudit(code, framework, options = {}) {
|
|
59
|
+
const html = this.htmlBuilder.build(code, framework, {
|
|
60
|
+
darkMode: options.darkMode ?? false,
|
|
61
|
+
css: options.css ?? "",
|
|
62
|
+
tailwindVersion: options.tailwindVersion ?? "3"
|
|
63
|
+
});
|
|
64
|
+
const page = await this.pool.getPage("chromium");
|
|
65
|
+
const viewport = options.viewport ?? { width: 1280, height: 800 };
|
|
66
|
+
await this.pool.setViewport("chromium", viewport);
|
|
67
|
+
const start = performance.now();
|
|
68
|
+
await page.setContent(html, { waitUntil: "load", timeout: 1e4 });
|
|
69
|
+
const renderTimeMs = Math.round(performance.now() - start);
|
|
70
|
+
const metrics = await page.evaluate(() => {
|
|
71
|
+
const all = document.querySelectorAll("*");
|
|
72
|
+
let maxDepth = 0;
|
|
73
|
+
for (const el of all) {
|
|
74
|
+
let depth = 0;
|
|
75
|
+
let node = el;
|
|
76
|
+
while (node) {
|
|
77
|
+
depth++;
|
|
78
|
+
node = node.parentElement;
|
|
79
|
+
}
|
|
80
|
+
if (depth > maxDepth) maxDepth = depth;
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
domElements: all.length,
|
|
84
|
+
domDepth: maxDepth,
|
|
85
|
+
scriptCount: document.querySelectorAll("script").length,
|
|
86
|
+
styleSheetCount: document.styleSheets.length,
|
|
87
|
+
imageCount: document.querySelectorAll("img").length,
|
|
88
|
+
totalDomSize: document.documentElement.outerHTML.length
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
return { renderTimeMs, ...metrics };
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// src/use-cases/screenshot.ts
|
|
96
|
+
var ScreenshotUseCase = class {
|
|
97
|
+
constructor(pool) {
|
|
98
|
+
this.pool = pool;
|
|
99
|
+
}
|
|
100
|
+
pool;
|
|
101
|
+
async screenshotUrl(url, options = {}) {
|
|
102
|
+
const engines = options.engines ?? ["chromium"];
|
|
103
|
+
const { waitForNetworkIdle = true } = options;
|
|
104
|
+
return Promise.all(
|
|
105
|
+
engines.map(async (engine) => {
|
|
106
|
+
const { width = 1280, height = 800 } = options.viewport ?? {};
|
|
107
|
+
const fullPage = options.fullPage ?? true;
|
|
108
|
+
const waitFor = options.waitFor ?? 0;
|
|
109
|
+
const page = await this.pool.getPage(engine);
|
|
110
|
+
await this.pool.setViewport(engine, { width, height });
|
|
111
|
+
const consoleErrors = [];
|
|
112
|
+
const onError = (msg) => {
|
|
113
|
+
if (msg.type() === "error") consoleErrors.push(msg.text());
|
|
114
|
+
};
|
|
115
|
+
page.on("console", onError);
|
|
116
|
+
page.on("pageerror", (err) => consoleErrors.push(err.message));
|
|
117
|
+
await page.goto(url, { waitUntil: "domcontentloaded", timeout: 15e3 });
|
|
118
|
+
if (waitForNetworkIdle) {
|
|
119
|
+
await page.waitForLoadState("networkidle", { timeout: 5e3 }).catch(() => {
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
if (options.waitForSelector) {
|
|
123
|
+
await page.waitForSelector(options.waitForSelector, {
|
|
124
|
+
timeout: 1e4
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
if (waitFor > 0) {
|
|
128
|
+
await page.waitForTimeout(waitFor);
|
|
129
|
+
}
|
|
130
|
+
const screenshot = await page.screenshot({ type: "png", fullPage });
|
|
131
|
+
const metrics = await page.evaluate(() => ({
|
|
132
|
+
w: document.documentElement.scrollWidth,
|
|
133
|
+
h: document.documentElement.scrollHeight
|
|
134
|
+
}));
|
|
135
|
+
page.removeListener("console", onError);
|
|
136
|
+
return {
|
|
137
|
+
engine,
|
|
138
|
+
image: screenshot.toString("base64"),
|
|
139
|
+
width: metrics.w,
|
|
140
|
+
height: metrics.h,
|
|
141
|
+
consoleErrors
|
|
142
|
+
};
|
|
143
|
+
})
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
async screenshotUrlWithRetry(url, options = {}) {
|
|
147
|
+
const { retryCount = 0, ...screenshotOpts } = options;
|
|
148
|
+
const maxAttempts = retryCount + 1;
|
|
149
|
+
let lastError;
|
|
150
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
151
|
+
try {
|
|
152
|
+
if (attempt > 0) {
|
|
153
|
+
const delay = 300 * 2 ** (attempt - 1);
|
|
154
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
155
|
+
}
|
|
156
|
+
return await this.screenshotUrl(url, screenshotOpts);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
lastError = error;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
throw lastError;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// src/use-cases/snapshot.ts
|
|
166
|
+
var SnapshotUseCase = class {
|
|
167
|
+
constructor(store, renderUseCase, diffUseCase) {
|
|
168
|
+
this.store = store;
|
|
169
|
+
this.renderUseCase = renderUseCase;
|
|
170
|
+
this.diffUseCase = diffUseCase;
|
|
171
|
+
}
|
|
172
|
+
store;
|
|
173
|
+
renderUseCase;
|
|
174
|
+
diffUseCase;
|
|
175
|
+
async save(key, code, framework, options = {}) {
|
|
176
|
+
const [result] = await this.renderUseCase.render(code, framework, {
|
|
177
|
+
...options,
|
|
178
|
+
engines: ["chromium"]
|
|
179
|
+
});
|
|
180
|
+
this.store.save(key, result.image);
|
|
181
|
+
return { image: result.image, width: result.width, height: result.height };
|
|
182
|
+
}
|
|
183
|
+
async check(key, code, framework, options = {}) {
|
|
184
|
+
const snapshot = this.store.get(key);
|
|
185
|
+
if (!snapshot) return null;
|
|
186
|
+
return this.diffUseCase.diffFromReference(
|
|
187
|
+
code,
|
|
188
|
+
framework,
|
|
189
|
+
snapshot.image,
|
|
190
|
+
options
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
list() {
|
|
194
|
+
return this.store.list();
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
export {
|
|
199
|
+
SnapshotStore,
|
|
200
|
+
AuditUseCase,
|
|
201
|
+
ScreenshotUseCase,
|
|
202
|
+
SnapshotUseCase
|
|
203
|
+
};
|