stitch2 0.22.2 → 0.22.3
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/dist/report.js +74 -59
- package/package.json +1 -1
package/dist/report.js
CHANGED
|
@@ -68,72 +68,84 @@ const fresh = async () => {
|
|
|
68
68
|
return { browser, page, visits: 0 };
|
|
69
69
|
};
|
|
70
70
|
const pool = await Promise.all(Array.from({ length: Math.min(CONCURRENCY, todo.length) }, fresh));
|
|
71
|
-
let nextWorker = 0;
|
|
72
71
|
/** Component families cropped per visited screen, merged into the cache. */
|
|
73
72
|
const visitedKinds = {};
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
73
|
+
let cursor = 0;
|
|
74
|
+
// One screen per worker at a time: workers pull from the queue, so a page is never shared.
|
|
75
|
+
const runOne = async (s, worker) => {
|
|
76
|
+
try {
|
|
77
|
+
if (worker.visits > 0 && worker.visits % 50 === 0) {
|
|
78
|
+
// Chromium gets fragile after hundreds of page loads; a fresh browser keeps long runs alive.
|
|
79
|
+
await worker.page.close().catch(() => { });
|
|
80
|
+
await worker.browser.close().catch(() => { });
|
|
81
|
+
const next = await fresh();
|
|
82
|
+
worker.browser = next.browser;
|
|
83
|
+
worker.page = next.page;
|
|
84
|
+
}
|
|
85
|
+
worker.visits++;
|
|
86
|
+
const page = worker.page;
|
|
87
|
+
await page.setViewportSize({ width: s.width, height: 844 });
|
|
88
|
+
await page
|
|
89
|
+
.goto(`http://127.0.0.1:${server.port}/${s.path}`, { waitUntil: 'networkidle', timeout: 30_000 })
|
|
90
|
+
.catch(() => { });
|
|
91
|
+
await page.waitForTimeout(600);
|
|
92
|
+
const found = await page.evaluate(collectComponents, collectOptions());
|
|
93
|
+
known[s.path] = found.components;
|
|
94
|
+
visitedKinds[s.path] = new Set();
|
|
95
|
+
for (const [kind, sigs] of Object.entries(found.components)) {
|
|
96
|
+
if (kinds.length && !kinds.includes(kind.split(':')[0]))
|
|
97
|
+
continue;
|
|
98
|
+
visitedKinds[s.path].add(kind.split(':')[0]);
|
|
99
|
+
const files = [];
|
|
100
|
+
for (const [i, sig] of sigs.entries()) {
|
|
101
|
+
const file = `${safe(kind)}/${safe(s.path)}-${i}.png`;
|
|
102
|
+
mkdirSync(join(out, safe(kind)), { recursive: true });
|
|
103
|
+
try {
|
|
104
|
+
// Crop the element plus anything drawn outside it (a raised centre button above a tab bar).
|
|
105
|
+
const el = page.locator(sig.selector).first();
|
|
106
|
+
await el.scrollIntoViewIfNeeded({ timeout: 3000 });
|
|
107
|
+
const clip = await el.evaluate((node) => {
|
|
108
|
+
const r = node.getBoundingClientRect();
|
|
109
|
+
let top = r.top;
|
|
110
|
+
let bottom = r.bottom;
|
|
111
|
+
for (const c of node.querySelectorAll('*')) {
|
|
112
|
+
const q = c.getBoundingClientRect();
|
|
113
|
+
if (q.width && q.height) {
|
|
114
|
+
top = Math.min(top, q.top);
|
|
115
|
+
bottom = Math.max(bottom, q.bottom);
|
|
116
|
+
}
|
|
115
117
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
118
|
+
return {
|
|
119
|
+
x: Math.max(0, r.left),
|
|
120
|
+
y: Math.max(0, top),
|
|
121
|
+
width: r.width,
|
|
122
|
+
height: bottom - Math.max(0, top),
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
if (clip.width < 1 || clip.height < 1)
|
|
126
|
+
continue;
|
|
127
|
+
await page.screenshot({ path: join(out, file), clip });
|
|
128
|
+
files.push(file);
|
|
129
|
+
}
|
|
130
|
+
catch { }
|
|
128
131
|
}
|
|
129
|
-
|
|
132
|
+
crops[kind] ??= {};
|
|
133
|
+
crops[kind][s.path] = files;
|
|
130
134
|
}
|
|
131
|
-
|
|
132
|
-
crops[kind][s.path] = files;
|
|
135
|
+
visitedKinds[s.path].add('all');
|
|
133
136
|
}
|
|
134
|
-
|
|
137
|
+
catch (e) {
|
|
138
|
+
// A dead page must not kill the other workers: the screen keeps no cache entry and is retried next run.
|
|
139
|
+
delete known[s.path];
|
|
140
|
+
delete visitedKinds[s.path];
|
|
141
|
+
console.error(`consistency: skipping ${s.path} (${String(e).split('\n')[0]})`);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const drive = async (worker) => {
|
|
145
|
+
while (cursor < todo.length)
|
|
146
|
+
await runOne(todo[cursor++], worker);
|
|
135
147
|
};
|
|
136
|
-
await Promise.all(
|
|
148
|
+
await Promise.all(pool.map((w) => drive(w)));
|
|
137
149
|
// Remember what this run saw: visited screens with fresh mtimes, the rest untouched. Screens that are
|
|
138
150
|
// gone leave the cache; their rows leave the report with them.
|
|
139
151
|
const live = new Set(screens.map((s) => s.path));
|
|
@@ -143,6 +155,9 @@ for (const [path, hit] of Object.entries(cached)) {
|
|
|
143
155
|
next[path] = hit;
|
|
144
156
|
}
|
|
145
157
|
for (const s of todo) {
|
|
158
|
+
// Screens that failed keep no cache entry, so the next run retries them.
|
|
159
|
+
if (!known[s.path])
|
|
160
|
+
continue;
|
|
146
161
|
const screenCrops = {};
|
|
147
162
|
for (const [kind, byPath] of Object.entries(crops)) {
|
|
148
163
|
if (byPath[s.path])
|
package/package.json
CHANGED