multiclone 0.0.0-stage → 0.2.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 +78 -2
- package/agents/section-builder.md +37 -0
- package/commands/multiclone.md +15 -0
- package/dist/adapters/cli.js +129 -0
- package/dist/adapters/cli.js.map +1 -0
- package/dist/adapters/cliArgs.js +63 -0
- package/dist/adapters/cliArgs.js.map +1 -0
- package/dist/adapters/mcp.js +83 -0
- package/dist/adapters/mcp.js.map +1 -0
- package/dist/analyze/analyze.js +85 -0
- package/dist/analyze/analyze.js.map +1 -0
- package/dist/analyze/animations.js +340 -0
- package/dist/analyze/animations.js.map +1 -0
- package/dist/analyze/ast.js +182 -0
- package/dist/analyze/ast.js.map +1 -0
- package/dist/analyze/chunks.js +160 -0
- package/dist/analyze/chunks.js.map +1 -0
- package/dist/analyze/jobs/code.js +117 -0
- package/dist/analyze/jobs/code.js.map +1 -0
- package/dist/analyze/jobs/sections.js +356 -0
- package/dist/analyze/jobs/sections.js.map +1 -0
- package/dist/analyze/pretty.js +20 -0
- package/dist/analyze/pretty.js.map +1 -0
- package/dist/analyze/report.js +101 -0
- package/dist/analyze/report.js.map +1 -0
- package/dist/analyze/worker.js +71 -0
- package/dist/analyze/worker.js.map +1 -0
- package/dist/capture/capture.js +340 -0
- package/dist/capture/capture.js.map +1 -0
- package/dist/capture/collectors/a11y.js +92 -0
- package/dist/capture/collectors/a11y.js.map +1 -0
- package/dist/capture/collectors/animations.js +113 -0
- package/dist/capture/collectors/animations.js.map +1 -0
- package/dist/capture/collectors/breakpoints.js +31 -0
- package/dist/capture/collectors/breakpoints.js.map +1 -0
- package/dist/capture/collectors/consent.js +39 -0
- package/dist/capture/collectors/consent.js.map +1 -0
- package/dist/capture/collectors/coverage.js +121 -0
- package/dist/capture/collectors/coverage.js.map +1 -0
- package/dist/capture/collectors/dom.js +16 -0
- package/dist/capture/collectors/dom.js.map +1 -0
- package/dist/capture/collectors/fonts.js +154 -0
- package/dist/capture/collectors/fonts.js.map +1 -0
- package/dist/capture/collectors/frames.js +93 -0
- package/dist/capture/collectors/frames.js.map +1 -0
- package/dist/capture/collectors/index.js +61 -0
- package/dist/capture/collectors/index.js.map +1 -0
- package/dist/capture/collectors/interactions.js +207 -0
- package/dist/capture/collectors/interactions.js.map +1 -0
- package/dist/capture/collectors/intro.js +84 -0
- package/dist/capture/collectors/intro.js.map +1 -0
- package/dist/capture/collectors/listeners.js +84 -0
- package/dist/capture/collectors/listeners.js.map +1 -0
- package/dist/capture/collectors/motion.js +91 -0
- package/dist/capture/collectors/motion.js.map +1 -0
- package/dist/capture/collectors/network.js +288 -0
- package/dist/capture/collectors/network.js.map +1 -0
- package/dist/capture/collectors/probes.js +34 -0
- package/dist/capture/collectors/probes.js.map +1 -0
- package/dist/capture/collectors/runtime.js +38 -0
- package/dist/capture/collectors/runtime.js.map +1 -0
- package/dist/capture/collectors/scripts.js +98 -0
- package/dist/capture/collectors/scripts.js.map +1 -0
- package/dist/capture/collectors/sections.js +17 -0
- package/dist/capture/collectors/sections.js.map +1 -0
- package/dist/capture/collectors/shots.js +58 -0
- package/dist/capture/collectors/shots.js.map +1 -0
- package/dist/capture/collectors/snapshot.js +47 -0
- package/dist/capture/collectors/snapshot.js.map +1 -0
- package/dist/capture/collectors/sourcemaps.js +153 -0
- package/dist/capture/collectors/sourcemaps.js.map +1 -0
- package/dist/capture/collectors/storage.js +72 -0
- package/dist/capture/collectors/storage.js.map +1 -0
- package/dist/capture/context.js +40 -0
- package/dist/capture/context.js.map +1 -0
- package/dist/capture/hooks/candidates.js +104 -0
- package/dist/capture/hooks/candidates.js.map +1 -0
- package/dist/capture/hooks/consent.js +82 -0
- package/dist/capture/hooks/consent.js.map +1 -0
- package/dist/capture/hooks/recorders.js +507 -0
- package/dist/capture/hooks/recorders.js.map +1 -0
- package/dist/capture/hooks/sections.js +62 -0
- package/dist/capture/hooks/sections.js.map +1 -0
- package/dist/capture/hooks/styles.js +86 -0
- package/dist/capture/hooks/styles.js.map +1 -0
- package/dist/capture/probes.js +491 -0
- package/dist/capture/probes.js.map +1 -0
- package/dist/capture/report.js +115 -0
- package/dist/capture/report.js.map +1 -0
- package/dist/compare/compare.js +391 -0
- package/dist/compare/compare.js.map +1 -0
- package/dist/compare/image.js +105 -0
- package/dist/compare/image.js.map +1 -0
- package/dist/compare/probes.js +129 -0
- package/dist/compare/probes.js.map +1 -0
- package/dist/compare/report.js +99 -0
- package/dist/compare/report.js.map +1 -0
- package/dist/compare/session.js +139 -0
- package/dist/compare/session.js.map +1 -0
- package/dist/compare/shoot.js +62 -0
- package/dist/compare/shoot.js.map +1 -0
- package/dist/contracts/catalog.js +29 -0
- package/dist/contracts/catalog.js.map +1 -0
- package/dist/contracts/context.js +19 -0
- package/dist/contracts/context.js.map +1 -0
- package/dist/contracts/fields.js +16 -0
- package/dist/contracts/fields.js.map +1 -0
- package/dist/contracts/tools/analyze.js +38 -0
- package/dist/contracts/tools/analyze.js.map +1 -0
- package/dist/contracts/tools/capture.js +87 -0
- package/dist/contracts/tools/capture.js.map +1 -0
- package/dist/contracts/tools/compare.js +142 -0
- package/dist/contracts/tools/compare.js.map +1 -0
- package/dist/contracts/tools/doctor.js +23 -0
- package/dist/contracts/tools/doctor.js.map +1 -0
- package/dist/contracts/tools/inspect.js +41 -0
- package/dist/contracts/tools/inspect.js.map +1 -0
- package/dist/contracts/tools/method.js +60 -0
- package/dist/contracts/tools/method.js.map +1 -0
- package/dist/contracts/tools/mirror.js +53 -0
- package/dist/contracts/tools/mirror.js.map +1 -0
- package/dist/contracts/tools/project.js +61 -0
- package/dist/contracts/tools/project.js.map +1 -0
- package/dist/contracts/tools/session.js +195 -0
- package/dist/contracts/tools/session.js.map +1 -0
- package/dist/contracts/types.js +20 -0
- package/dist/contracts/types.js.map +1 -0
- package/dist/core/cdp.js +146 -0
- package/dist/core/cdp.js.map +1 -0
- package/dist/core/chrome.js +185 -0
- package/dist/core/chrome.js.map +1 -0
- package/dist/core/files.js +31 -0
- package/dist/core/files.js.map +1 -0
- package/dist/core/inpage.js +24 -0
- package/dist/core/inpage.js.map +1 -0
- package/dist/core/log.js +10 -0
- package/dist/core/log.js.map +1 -0
- package/dist/core/page.js +269 -0
- package/dist/core/page.js.map +1 -0
- package/dist/doctor.js +37 -0
- package/dist/doctor.js.map +1 -0
- package/dist/domain/breakpoints.js +47 -0
- package/dist/domain/breakpoints.js.map +1 -0
- package/dist/domain/budget.js +38 -0
- package/dist/domain/budget.js.map +1 -0
- package/dist/domain/compare.js +138 -0
- package/dist/domain/compare.js.map +1 -0
- package/dist/domain/coverage.js +96 -0
- package/dist/domain/coverage.js.map +1 -0
- package/dist/domain/motion.js +142 -0
- package/dist/domain/motion.js.map +1 -0
- package/dist/domain/paths.js +55 -0
- package/dist/domain/paths.js.map +1 -0
- package/dist/domain/result.js +38 -0
- package/dist/domain/result.js.map +1 -0
- package/dist/domain/routes.js +160 -0
- package/dist/domain/routes.js.map +1 -0
- package/dist/domain/sections.js +94 -0
- package/dist/domain/sections.js.map +1 -0
- package/dist/domain/snapshot.js +268 -0
- package/dist/domain/snapshot.js.map +1 -0
- package/dist/domain/sourcemap.js +95 -0
- package/dist/domain/sourcemap.js.map +1 -0
- package/dist/domain/stack.js +342 -0
- package/dist/domain/stack.js.map +1 -0
- package/dist/domain/tokens.js +183 -0
- package/dist/domain/tokens.js.map +1 -0
- package/dist/domain/types.js +2 -0
- package/dist/domain/types.js.map +1 -0
- package/dist/domain/viewports.js +45 -0
- package/dist/domain/viewports.js.map +1 -0
- package/dist/mirror/rewrite.js +56 -0
- package/dist/mirror/rewrite.js.map +1 -0
- package/dist/mirror/server.js +191 -0
- package/dist/mirror/server.js.map +1 -0
- package/dist/mirror/store.js +86 -0
- package/dist/mirror/store.js.map +1 -0
- package/dist/project/assets.js +69 -0
- package/dist/project/assets.js.map +1 -0
- package/dist/project/draft.js +860 -0
- package/dist/project/draft.js.map +1 -0
- package/dist/project/method.js +154 -0
- package/dist/project/method.js.map +1 -0
- package/dist/project/scaffold.js +142 -0
- package/dist/project/scaffold.js.map +1 -0
- package/dist/session/discover.js +53 -0
- package/dist/session/discover.js.map +1 -0
- package/dist/session/proc.js +113 -0
- package/dist/session/proc.js.map +1 -0
- package/dist/session/progress.js +83 -0
- package/dist/session/progress.js.map +1 -0
- package/dist/session/session.js +450 -0
- package/dist/session/session.js.map +1 -0
- package/dist/session/state.js +47 -0
- package/dist/session/state.js.map +1 -0
- package/dist/store/inspect.js +212 -0
- package/dist/store/inspect.js.map +1 -0
- package/dist/store/run.js +112 -0
- package/dist/store/run.js.map +1 -0
- package/dist/store/summary.js +69 -0
- package/dist/store/summary.js.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/install.sh +29 -0
- package/package.json +67 -4
- package/skills/multiclone/SKILL.md +123 -0
- package/skills/multiclone/references/capture-format.md +73 -0
- package/skills/multiclone/references/fidelity.md +54 -0
- package/skills/multiclone/references/quality.md +47 -0
- package/skills/multiclone/references/stacks.md +156 -0
- package/templates/react-vite/.prettierignore +5 -0
- package/templates/react-vite/.prettierrc.json +6 -0
- package/templates/react-vite/eslint.config.js +38 -0
- package/templates/react-vite/gitignore +4 -0
- package/templates/react-vite/index.html +12 -0
- package/templates/react-vite/package.json +36 -0
- package/templates/react-vite/src/app/App.tsx +15 -0
- package/templates/react-vite/src/app/Page.tsx +11 -0
- package/templates/react-vite/src/app/routes.ts +7 -0
- package/templates/react-vite/src/components/README.md +7 -0
- package/templates/react-vite/src/content/README.md +2 -0
- package/templates/react-vite/src/hooks/useActiveSection.ts +37 -0
- package/templates/react-vite/src/hooks/useInView.ts +45 -0
- package/templates/react-vite/src/hooks/useMediaQuery.ts +15 -0
- package/templates/react-vite/src/lib/cn.ts +4 -0
- package/templates/react-vite/src/lib/math.ts +21 -0
- package/templates/react-vite/src/lib/mc.ts +13 -0
- package/templates/react-vite/src/lib/media.ts +4 -0
- package/templates/react-vite/src/main.tsx +13 -0
- package/templates/react-vite/src/providers/SmoothScroll.tsx +24 -0
- package/templates/react-vite/src/styles/base.css +42 -0
- package/templates/react-vite/src/styles/draft.css +1 -0
- package/templates/react-vite/src/styles/fonts.css +1 -0
- package/templates/react-vite/src/styles/index.css +4 -0
- package/templates/react-vite/src/styles/tokens.css +3 -0
- package/templates/react-vite/src/vite-env.d.ts +1 -0
- package/templates/react-vite/tsconfig.app.json +29 -0
- package/templates/react-vite/tsconfig.json +4 -0
- package/templates/react-vite/tsconfig.node.json +19 -0
- package/templates/react-vite/vite.config.ts +37 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { writeJson } from '../../core/files.js';
|
|
3
|
+
const KEEP_PROPS = new Set([
|
|
4
|
+
'level',
|
|
5
|
+
'expanded',
|
|
6
|
+
'checked',
|
|
7
|
+
'selected',
|
|
8
|
+
'pressed',
|
|
9
|
+
'disabled',
|
|
10
|
+
'focusable',
|
|
11
|
+
'haspopup',
|
|
12
|
+
'modal',
|
|
13
|
+
'url',
|
|
14
|
+
'orientation',
|
|
15
|
+
'valuemin',
|
|
16
|
+
'valuemax',
|
|
17
|
+
'valuenow',
|
|
18
|
+
]);
|
|
19
|
+
const text = (value) => typeof value?.value === 'string' || typeof value?.value === 'number' ? String(value.value) : '';
|
|
20
|
+
/** Drops ignored and generic wrapper nodes, re-parenting children to the nearest kept ancestor. */
|
|
21
|
+
export function compactAxTree(nodes) {
|
|
22
|
+
const byId = new Map(nodes.map((node) => [node.nodeId, node]));
|
|
23
|
+
const keep = (node) => {
|
|
24
|
+
if (node.ignored)
|
|
25
|
+
return false;
|
|
26
|
+
const role = text(node.role);
|
|
27
|
+
return !((role === 'generic' || role === 'none' || role === 'InlineTextBox') &&
|
|
28
|
+
!text(node.name));
|
|
29
|
+
};
|
|
30
|
+
const out = [];
|
|
31
|
+
for (const node of nodes) {
|
|
32
|
+
if (!keep(node))
|
|
33
|
+
continue;
|
|
34
|
+
let parentId = node.parentId;
|
|
35
|
+
while (parentId !== undefined) {
|
|
36
|
+
const parent = byId.get(parentId);
|
|
37
|
+
if (!parent || keep(parent))
|
|
38
|
+
break;
|
|
39
|
+
parentId = parent.parentId;
|
|
40
|
+
}
|
|
41
|
+
const props = {};
|
|
42
|
+
for (const property of node.properties ?? []) {
|
|
43
|
+
if (KEEP_PROPS.has(property.name))
|
|
44
|
+
props[property.name] = property.value.value;
|
|
45
|
+
}
|
|
46
|
+
const name = text(node.name);
|
|
47
|
+
const description = text(node.description);
|
|
48
|
+
const value = text(node.value);
|
|
49
|
+
out.push({
|
|
50
|
+
id: node.nodeId,
|
|
51
|
+
role: text(node.role),
|
|
52
|
+
...(parentId === undefined ? {} : { parent: parentId }),
|
|
53
|
+
...(name ? { name: name.slice(0, 300) } : {}),
|
|
54
|
+
...(description ? { description: description.slice(0, 300) } : {}),
|
|
55
|
+
...(value ? { value: value.slice(0, 300) } : {}),
|
|
56
|
+
...(node.backendDOMNodeId === undefined ? {} : { backendId: node.backendDOMNodeId }),
|
|
57
|
+
...(Object.keys(props).length ? { props } : {}),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
async function tree(session) {
|
|
63
|
+
await session.send('Accessibility.enable').catch(() => undefined);
|
|
64
|
+
const { nodes } = await session.send('Accessibility.getFullAXTree', {}, 120_000);
|
|
65
|
+
await session.send('Accessibility.disable').catch(() => undefined);
|
|
66
|
+
return compactAxTree(nodes);
|
|
67
|
+
}
|
|
68
|
+
/** Roles and accessible names per frame (semantic components for the rebuild). */
|
|
69
|
+
export function a11yCollector() {
|
|
70
|
+
return {
|
|
71
|
+
id: 'a11y',
|
|
72
|
+
async top(context) {
|
|
73
|
+
const frames = [
|
|
74
|
+
{ url: context.run.finalUrl, nodes: await tree(context.cdp) },
|
|
75
|
+
];
|
|
76
|
+
for (const child of context.run.children.values()) {
|
|
77
|
+
if (child.type !== 'iframe' || child.viewport !== context.viewport.name)
|
|
78
|
+
continue;
|
|
79
|
+
try {
|
|
80
|
+
frames.push({ url: child.url, nodes: await tree(context.cdp.session(child.sessionId)) });
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Frame went away.
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const file = join('a11y', `${context.viewport.name}.json`);
|
|
87
|
+
await writeJson(join(context.dir, file), frames);
|
|
88
|
+
context.run.output('a11y', file);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=a11y.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a11y.js","sourceRoot":"","sources":["../../../src/capture/collectors/a11y.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { writeJson } from '../../core/files.js';
|
|
3
|
+
/** Resolves backend node ids to the in-page stable path via a tagged attribute probe. */
|
|
4
|
+
async function pathForBackendNode(cdp, backendNodeId) {
|
|
5
|
+
try {
|
|
6
|
+
const { object } = await cdp.send('DOM.resolveNode', {
|
|
7
|
+
backendNodeId,
|
|
8
|
+
});
|
|
9
|
+
if (!object.objectId)
|
|
10
|
+
return null;
|
|
11
|
+
const { result } = await cdp.send('Runtime.callFunctionOn', {
|
|
12
|
+
objectId: object.objectId,
|
|
13
|
+
returnByValue: true,
|
|
14
|
+
functionDeclaration: `function () {
|
|
15
|
+
const parts = [];
|
|
16
|
+
let node = this.nodeType === 1 ? this : this.parentElement;
|
|
17
|
+
for (let depth = 0; node && depth < 40; depth++) {
|
|
18
|
+
const tag = node.tagName.toLowerCase();
|
|
19
|
+
if (tag === 'html' || tag === 'body') { parts.unshift(tag); break; }
|
|
20
|
+
const id = node.getAttribute('id');
|
|
21
|
+
if (id && /^[A-Za-z][\\w-]*$/.test(id) && !/\\d{4,}|^:r|^radix-|^headlessui-/i.test(id)) { parts.unshift('#' + id); break; }
|
|
22
|
+
const parent = node.parentElement;
|
|
23
|
+
const same = parent ? Array.from(parent.children).filter((c) => c.tagName === node.tagName) : [];
|
|
24
|
+
parts.unshift(same.length > 1 ? tag + ':nth-of-type(' + (same.indexOf(node) + 1) + ')' : tag);
|
|
25
|
+
node = parent;
|
|
26
|
+
}
|
|
27
|
+
return parts.join(' > ');
|
|
28
|
+
}`,
|
|
29
|
+
});
|
|
30
|
+
await cdp.send('Runtime.releaseObject', { objectId: object.objectId }).catch(() => undefined);
|
|
31
|
+
return result.value ?? null;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The CDP Animation domain: full keyframes and timing for every CSS animation, transition and
|
|
39
|
+
* WAAPI animation as it starts, including scroll/view timelines. Writes animations.json.
|
|
40
|
+
*/
|
|
41
|
+
export function animationsCollector() {
|
|
42
|
+
const raw = new Map();
|
|
43
|
+
let phase = 'load';
|
|
44
|
+
let viewport = '';
|
|
45
|
+
return {
|
|
46
|
+
id: 'animations',
|
|
47
|
+
async setup(context) {
|
|
48
|
+
const { cdp } = context;
|
|
49
|
+
await cdp.send('DOM.enable');
|
|
50
|
+
await cdp.send('Animation.enable');
|
|
51
|
+
const capture = (params) => {
|
|
52
|
+
const animation = params.animation;
|
|
53
|
+
if (!animation)
|
|
54
|
+
return;
|
|
55
|
+
const key = `${viewport}|${animation.id}`;
|
|
56
|
+
if (raw.size < 5000 || raw.has(key))
|
|
57
|
+
raw.set(key, { animation, viewport, phase: raw.get(key)?.phase ?? phase });
|
|
58
|
+
};
|
|
59
|
+
cdp.on('Animation.animationStarted', capture);
|
|
60
|
+
cdp.on('Animation.animationUpdated', capture);
|
|
61
|
+
},
|
|
62
|
+
beforeNavigate(context) {
|
|
63
|
+
viewport = context.viewport.name;
|
|
64
|
+
phase = 'load';
|
|
65
|
+
return Promise.resolve();
|
|
66
|
+
},
|
|
67
|
+
top() {
|
|
68
|
+
phase = 'scroll';
|
|
69
|
+
return Promise.resolve();
|
|
70
|
+
},
|
|
71
|
+
explore() {
|
|
72
|
+
phase = 'interaction';
|
|
73
|
+
return Promise.resolve();
|
|
74
|
+
},
|
|
75
|
+
async finish(context) {
|
|
76
|
+
const paths = new Map();
|
|
77
|
+
const records = [];
|
|
78
|
+
for (const { animation, viewport: vp, phase: when } of raw.values()) {
|
|
79
|
+
const source = animation.source;
|
|
80
|
+
const node = source?.backendNodeId;
|
|
81
|
+
let target = null;
|
|
82
|
+
if (node !== undefined) {
|
|
83
|
+
if (!paths.has(node))
|
|
84
|
+
paths.set(node, await pathForBackendNode(context.cdp, node));
|
|
85
|
+
target = paths.get(node) ?? null;
|
|
86
|
+
}
|
|
87
|
+
records.push({
|
|
88
|
+
id: animation.id,
|
|
89
|
+
viewport: vp,
|
|
90
|
+
phase: when,
|
|
91
|
+
name: animation.name === '' ? (source?.keyframesRule?.name ?? '') : animation.name,
|
|
92
|
+
type: animation.type,
|
|
93
|
+
target,
|
|
94
|
+
delay: source?.delay ?? 0,
|
|
95
|
+
duration: source?.duration ?? 0,
|
|
96
|
+
iterations: source?.iterations ?? null,
|
|
97
|
+
direction: source?.direction ?? 'normal',
|
|
98
|
+
fill: source?.fill ?? 'none',
|
|
99
|
+
easing: source?.easing ?? 'linear',
|
|
100
|
+
keyframes: source?.keyframesRule?.keyframes ?? [],
|
|
101
|
+
scrollTimeline: animation.viewOrScrollTimeline ?? null,
|
|
102
|
+
startTime: Math.round(animation.startTime),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
records.sort((a, b) => a.viewport.localeCompare(b.viewport) ||
|
|
106
|
+
a.startTime - b.startTime ||
|
|
107
|
+
a.id.localeCompare(b.id));
|
|
108
|
+
await writeJson(join(context.dir, 'animations.json'), records);
|
|
109
|
+
context.run.output('animations', 'animations.json');
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=animations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animations.js","sourceRoot":"","sources":["../../../src/capture/collectors/animations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { writeJson } from '../../core/files.js';
|
|
4
|
+
import { extractBreakpoints, majorBreakpoints } from '../../domain/breakpoints.js';
|
|
5
|
+
/** Media-query widths from every captured stylesheet and inline <style>. Writes breakpoints.json. */
|
|
6
|
+
export function breakpointsCollector() {
|
|
7
|
+
let inline = '';
|
|
8
|
+
return {
|
|
9
|
+
id: 'breakpoints',
|
|
10
|
+
async top(context) {
|
|
11
|
+
if (!context.primary)
|
|
12
|
+
return;
|
|
13
|
+
inline = await context.page.evaluate(`Array.from(document.querySelectorAll('style')).map((s) => s.textContent || '').join('\\n')`);
|
|
14
|
+
},
|
|
15
|
+
async finish(context) {
|
|
16
|
+
const parts = [inline];
|
|
17
|
+
for (const entry of context.run.network) {
|
|
18
|
+
if (!entry.file || !entry.mimeType.includes('css'))
|
|
19
|
+
continue;
|
|
20
|
+
parts.push(await readFile(join(context.dir, entry.file), 'utf8').catch(() => ''));
|
|
21
|
+
}
|
|
22
|
+
const points = extractBreakpoints(parts.join('\n'));
|
|
23
|
+
await writeJson(join(context.dir, 'breakpoints.json'), {
|
|
24
|
+
major: majorBreakpoints(points),
|
|
25
|
+
all: points,
|
|
26
|
+
});
|
|
27
|
+
context.run.output('breakpoints', 'breakpoints.json');
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=breakpoints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"breakpoints.js","sourceRoot":"","sources":["../../../src/capture/collectors/breakpoints.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAGnF,qGAAqG;AACrG,MAAM,UAAU,oBAAoB;IAClC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,OAAO;QACL,EAAE,EAAE,aAAa;QACjB,KAAK,CAAC,GAAG,CAAC,OAAO;YACf,IAAI,CAAC,OAAO,CAAC,OAAO;gBAAE,OAAO;YAC7B,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,QAAQ,CAClC,4FAA4F,CAC7F,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,OAAO;YAClB,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,CAAC;YACvB,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;gBACxC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;oBAAE,SAAS;gBAC7D,KAAK,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YACpF,CAAC;YACD,MAAM,MAAM,GAAG,kBAAkB,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACpD,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,kBAAkB,CAAC,EAAE;gBACrD,KAAK,EAAE,gBAAgB,CAAC,MAAM,CAAC;gBAC/B,GAAG,EAAE,MAAM;aACZ,CAAC,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,EAAE,kBAAkB,CAAC,CAAC;QACxD,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { sleep, writeJson } from '../../core/files.js';
|
|
3
|
+
import { log } from '../../core/log.js';
|
|
4
|
+
import { dismissConsent } from '../hooks/consent.js';
|
|
5
|
+
/** Clicks away consent banners right after load, in the page and in attached iframes. */
|
|
6
|
+
export function consentCollector() {
|
|
7
|
+
const results = [];
|
|
8
|
+
return {
|
|
9
|
+
id: 'consent',
|
|
10
|
+
async loaded(context) {
|
|
11
|
+
const clicked = [];
|
|
12
|
+
for (let attempt = 0; attempt < 3 && !clicked.length; attempt++) {
|
|
13
|
+
if (attempt)
|
|
14
|
+
await sleep(1200);
|
|
15
|
+
clicked.push(...(await context.page.run(dismissConsent, undefined).catch(() => [])));
|
|
16
|
+
for (const child of context.run.children.values()) {
|
|
17
|
+
if (child.type !== 'iframe' || child.viewport !== context.viewport.name)
|
|
18
|
+
continue;
|
|
19
|
+
const session = context.cdp.session(child.sessionId);
|
|
20
|
+
const { result } = await session
|
|
21
|
+
.send('Runtime.evaluate', {
|
|
22
|
+
expression: `(${dismissConsent.toString()})()`,
|
|
23
|
+
returnByValue: true,
|
|
24
|
+
})
|
|
25
|
+
.catch(() => ({ result: { value: [] } }));
|
|
26
|
+
clicked.push(...(result.value ?? []).map((item) => `iframe:${item}`));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (clicked.length)
|
|
30
|
+
log(` consent dismissed (${clicked.join(', ')})`);
|
|
31
|
+
results.push({ viewport: context.viewport.name, clicked });
|
|
32
|
+
},
|
|
33
|
+
async finish(context) {
|
|
34
|
+
await writeJson(join(context.dir, 'consent.json'), results);
|
|
35
|
+
context.run.output('consent', 'consent.json');
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=consent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent.js","sourceRoot":"","sources":["../../../src/capture/collectors/consent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,yFAAyF;AACzF,MAAM,UAAU,gBAAgB;IAC9B,MAAM,OAAO,GAA8C,EAAE,CAAC;IAC9D,OAAO;QACL,EAAE,EAAE,SAAS;QACb,KAAK,CAAC,MAAM,CAAC,OAAO;YAClB,MAAM,OAAO,GAAa,EAAE,CAAC;YAC7B,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC;gBAChE,IAAI,OAAO;oBAAE,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC/B,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBACrF,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;oBAClD,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC,QAAQ,CAAC,IAAI;wBAAE,SAAS;oBAClF,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;oBACrD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,OAAO;yBAC7B,IAAI,CAAmC,kBAAkB,EAAE;wBAC1D,UAAU,EAAE,IAAI,cAAc,CAAC,QAAQ,EAAE,KAAK;wBAC9C,aAAa,EAAE,IAAI;qBACpB,CAAC;yBACD,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,EAAc,EAAE,EAAE,CAAC,CAAC,CAAC;oBACxD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC;gBACxE,CAAC;YACH,CAAC;YACD,IAAI,OAAO,CAAC,MAAM;gBAAE,GAAG,CAAC,0BAA0B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACzE,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,OAAO;YAClB,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC;YAC5D,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;QAChD,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { sleep, writeBytes, writeJson } from '../../core/files.js';
|
|
4
|
+
import { log } from '../../core/log.js';
|
|
5
|
+
import { coverageDelta, hotRanges, renderHotCode, } from '../../domain/coverage.js';
|
|
6
|
+
import { sectionsProbe } from '../hooks/sections.js';
|
|
7
|
+
const MAX_SECTIONS = 24;
|
|
8
|
+
const MAX_HOVERS = 12;
|
|
9
|
+
const IDLE_MS = 800;
|
|
10
|
+
async function take(context) {
|
|
11
|
+
const { result } = await context.cdp.send('Profiler.takePreciseCoverage', {}, 120_000);
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Precise-coverage diff: per section, the code that ran while scrolling through it but not while
|
|
16
|
+
* idle, and the same around hovering interactive candidates. Runs on deep viewports only.
|
|
17
|
+
* Writes motion-code/<section>.js, motion-code/hover-<n>.js and motion-code/index.json.
|
|
18
|
+
*/
|
|
19
|
+
export function coverageCollector() {
|
|
20
|
+
return {
|
|
21
|
+
id: 'coverage',
|
|
22
|
+
timeLimitMs: 120_000,
|
|
23
|
+
async explore(context) {
|
|
24
|
+
if (!context.deep)
|
|
25
|
+
return;
|
|
26
|
+
log(' coverage diff around scroll and hover');
|
|
27
|
+
const { cdp, page, viewport, stop } = context;
|
|
28
|
+
const sections = (context.run.sections.get(viewport.name) ??
|
|
29
|
+
(await page.runWith(sectionsProbe, { max: MAX_SECTIONS }))).slice(0, MAX_SECTIONS);
|
|
30
|
+
await cdp.send('Profiler.enable');
|
|
31
|
+
await cdp.send('Profiler.startPreciseCoverage', {
|
|
32
|
+
callCount: true,
|
|
33
|
+
detailed: true,
|
|
34
|
+
allowTriggeredUpdates: false,
|
|
35
|
+
});
|
|
36
|
+
const sources = new Map();
|
|
37
|
+
const sourceFor = (scriptId) => sources.get(scriptId);
|
|
38
|
+
const loadSources = async (ranges) => {
|
|
39
|
+
for (const range of ranges) {
|
|
40
|
+
if (sources.has(range.scriptId))
|
|
41
|
+
continue;
|
|
42
|
+
const script = context.run.scripts.find((s) => s.scriptId === range.scriptId);
|
|
43
|
+
const text = script?.file
|
|
44
|
+
? await readFile(join(context.dir, script.file), 'utf8').catch(() => '')
|
|
45
|
+
: await cdp
|
|
46
|
+
.send('Debugger.getScriptSource', {
|
|
47
|
+
scriptId: range.scriptId,
|
|
48
|
+
})
|
|
49
|
+
.then((r) => r.scriptSource)
|
|
50
|
+
.catch(() => '');
|
|
51
|
+
sources.set(range.scriptId, { file: script?.file ?? null, text });
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const index = [];
|
|
55
|
+
const write = async (name, kind, target, ranges) => {
|
|
56
|
+
if (!ranges.length)
|
|
57
|
+
return;
|
|
58
|
+
await loadSources(ranges);
|
|
59
|
+
const file = join('motion-code', `${name}.js`);
|
|
60
|
+
await writeBytes(join(context.dir, file), renderHotCode(ranges, sourceFor));
|
|
61
|
+
index.push({ name, kind, target, file, ranges: ranges.length });
|
|
62
|
+
context.run.output('coverage', file);
|
|
63
|
+
};
|
|
64
|
+
try {
|
|
65
|
+
let last = await take(context);
|
|
66
|
+
const window = async (action) => {
|
|
67
|
+
await action();
|
|
68
|
+
const now = await take(context);
|
|
69
|
+
const delta = coverageDelta(last, now);
|
|
70
|
+
last = now;
|
|
71
|
+
return delta;
|
|
72
|
+
};
|
|
73
|
+
for (const [position, section] of sections.entries()) {
|
|
74
|
+
if (stop.aborted)
|
|
75
|
+
break;
|
|
76
|
+
await page.scrollTo(Math.max(0, section.top - viewport.height), viewport, 8_000);
|
|
77
|
+
await sleep(400);
|
|
78
|
+
await take(context).then((now) => (last = now));
|
|
79
|
+
const idle = await window(() => sleep(IDLE_MS));
|
|
80
|
+
const scroll = await window(async () => {
|
|
81
|
+
const step = Math.max(80, Math.round((section.bottom - section.top + viewport.height) / 6));
|
|
82
|
+
for (let y = section.top - viewport.height; y <= section.bottom; y += step) {
|
|
83
|
+
if (stop.aborted)
|
|
84
|
+
break;
|
|
85
|
+
await page.scrollTo(Math.max(0, y), viewport, 4_000);
|
|
86
|
+
await sleep(120);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
const slug = `${String(position + 1).padStart(2, '0')}-${section.label}`;
|
|
90
|
+
await write(slug, 'scroll', section.path, hotRanges(idle, scroll, { minDelta: 2 }));
|
|
91
|
+
}
|
|
92
|
+
await page.jumpTo(0);
|
|
93
|
+
await sleep(300);
|
|
94
|
+
const candidates = (context.run.candidates.get(viewport.name) ?? [])
|
|
95
|
+
.filter((c) => !c.fixed || c.box[1] < viewport.height)
|
|
96
|
+
.slice(0, MAX_HOVERS);
|
|
97
|
+
for (const [position, candidate] of candidates.entries()) {
|
|
98
|
+
if (stop.aborted)
|
|
99
|
+
break;
|
|
100
|
+
const [x, y, w, h] = candidate.box;
|
|
101
|
+
const scrollY = await page.jumpTo(Math.max(0, y - viewport.height / 3));
|
|
102
|
+
await take(context).then((now) => (last = now));
|
|
103
|
+
const idle = await window(() => sleep(500));
|
|
104
|
+
const hover = await window(async () => {
|
|
105
|
+
await page.pointer(x + w / 2, y - scrollY + h / 2);
|
|
106
|
+
await sleep(500);
|
|
107
|
+
});
|
|
108
|
+
await page.pointer(1, 1);
|
|
109
|
+
await write(`hover-${String(position + 1).padStart(2, '0')}`, 'hover', candidate.path, hotRanges(idle, hover, { minDelta: 1 }));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
await cdp.send('Profiler.stopPreciseCoverage').catch(() => undefined);
|
|
114
|
+
await page.jumpTo(0).catch(() => 0);
|
|
115
|
+
await writeJson(join(context.dir, 'motion-code', 'index.json'), index);
|
|
116
|
+
context.run.output('coverage', 'motion-code/index.json');
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=coverage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../../src/capture/collectors/coverage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { writeBytes } from '../../core/files.js';
|
|
3
|
+
/** Hydrated outerHTML at the top, before scroll-driven state is left behind. */
|
|
4
|
+
export function domCollector() {
|
|
5
|
+
return {
|
|
6
|
+
id: 'dom',
|
|
7
|
+
async top(context) {
|
|
8
|
+
const file = join('dom', `${context.viewport.name}.html`);
|
|
9
|
+
const html = await context.page.evaluate(`'<!doctype html>\\n' + document.documentElement.outerHTML`);
|
|
10
|
+
await writeBytes(join(context.dir, file), html);
|
|
11
|
+
context.run.doms.set(context.viewport.name, file);
|
|
12
|
+
context.run.output('dom', file);
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=dom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom.js","sourceRoot":"","sources":["../../../src/capture/collectors/dom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGjD,gFAAgF;AAChF,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,EAAE,EAAE,KAAK;QACT,KAAK,CAAC,GAAG,CAAC,OAAO;YACf,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,CAAC;YAC1D,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,QAAQ,CACtC,2DAA2D,CAC5D,CAAC;YACF,MAAM,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;YAChD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAClD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAClC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { readJson, writeJson } from '../../core/files.js';
|
|
3
|
+
function fontsProbe(_arg, helpers) {
|
|
4
|
+
const clean = (value) => value.replace(/["']/g, '').trim();
|
|
5
|
+
const rules = new Map();
|
|
6
|
+
const visit = (list, base) => {
|
|
7
|
+
for (const rule of Array.from(list)) {
|
|
8
|
+
if (rule instanceof CSSFontFaceRule) {
|
|
9
|
+
const src = rule.style.getPropertyValue('src');
|
|
10
|
+
const sources = [];
|
|
11
|
+
for (const match of src.matchAll(/url\(\s*["']?([^"')]+)["']?\s*\)\s*(?:format\(\s*["']?([^"')]+)["']?\s*\))?/g)) {
|
|
12
|
+
try {
|
|
13
|
+
sources.push({ url: new URL(match[1] ?? '', base).href, format: match[2] ?? '' });
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
// Unresolvable reference.
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const key = [
|
|
20
|
+
clean(rule.style.getPropertyValue('font-family')),
|
|
21
|
+
rule.style.getPropertyValue('font-weight') || '400',
|
|
22
|
+
rule.style.getPropertyValue('font-style') || 'normal',
|
|
23
|
+
].join('|');
|
|
24
|
+
rules.set(key, [...(rules.get(key) ?? []), ...sources]);
|
|
25
|
+
}
|
|
26
|
+
else if ('cssRules' in rule) {
|
|
27
|
+
try {
|
|
28
|
+
visit(rule.cssRules, base);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Cross-origin nested rules.
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
for (const sheet of Array.from(document.styleSheets)) {
|
|
37
|
+
try {
|
|
38
|
+
visit(sheet.cssRules, sheet.href ?? location.href);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Cross-origin sheet without CORS: covered by CSS.getPlatformFontsForNode and network files.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const faces = [];
|
|
45
|
+
document.fonts.forEach((face) => {
|
|
46
|
+
const family = clean(face.family);
|
|
47
|
+
const weight = face.weight;
|
|
48
|
+
const style = face.style;
|
|
49
|
+
faces.push({
|
|
50
|
+
family,
|
|
51
|
+
weight,
|
|
52
|
+
style,
|
|
53
|
+
stretch: face.stretch,
|
|
54
|
+
unicodeRange: face.unicodeRange,
|
|
55
|
+
display: face.display,
|
|
56
|
+
status: face.status,
|
|
57
|
+
sources: rules.get([family, weight, style].join('|')) ??
|
|
58
|
+
rules.get([family, '400', style].join('|')) ??
|
|
59
|
+
[],
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
const usage = [];
|
|
63
|
+
const seen = new Set();
|
|
64
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
|
|
65
|
+
for (let node = walker.nextNode(); node && usage.length < 60; node = walker.nextNode()) {
|
|
66
|
+
const text = (node.textContent ?? '').trim();
|
|
67
|
+
const element = node.parentElement;
|
|
68
|
+
if (!text || !element)
|
|
69
|
+
continue;
|
|
70
|
+
const style = getComputedStyle(element);
|
|
71
|
+
const key = `${style.fontFamily}|${style.fontWeight}|${style.fontStyle}`;
|
|
72
|
+
if (seen.has(key) || style.visibility === 'hidden' || style.display === 'none')
|
|
73
|
+
continue;
|
|
74
|
+
seen.add(key);
|
|
75
|
+
usage.push({
|
|
76
|
+
path: helpers.path(element),
|
|
77
|
+
family: style.fontFamily,
|
|
78
|
+
weight: style.fontWeight,
|
|
79
|
+
style: style.fontStyle,
|
|
80
|
+
size: style.fontSize,
|
|
81
|
+
sample: text.slice(0, 60),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return { faces, usage };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Web fonts (document.fonts + @font-face sources mapped to captured files) and the platform font
|
|
88
|
+
* Chrome actually rendered for each distinct text style. Writes fonts.json (primary viewport).
|
|
89
|
+
*/
|
|
90
|
+
export function fontsCollector() {
|
|
91
|
+
return {
|
|
92
|
+
id: 'fonts',
|
|
93
|
+
async top(context) {
|
|
94
|
+
if (!context.primary)
|
|
95
|
+
return;
|
|
96
|
+
const { cdp, page, run } = context;
|
|
97
|
+
const probe = await page.runWith(fontsProbe, undefined);
|
|
98
|
+
await cdp.send('DOM.enable');
|
|
99
|
+
await cdp.send('CSS.enable');
|
|
100
|
+
const { root } = await cdp.send('DOM.getDocument', {
|
|
101
|
+
depth: 0,
|
|
102
|
+
});
|
|
103
|
+
const usage = [];
|
|
104
|
+
for (const item of probe.usage) {
|
|
105
|
+
let rendered = [];
|
|
106
|
+
if (!item.path.includes('>>>')) {
|
|
107
|
+
try {
|
|
108
|
+
const { nodeId } = await cdp.send('DOM.querySelector', {
|
|
109
|
+
nodeId: root.nodeId,
|
|
110
|
+
selector: item.path,
|
|
111
|
+
});
|
|
112
|
+
if (nodeId) {
|
|
113
|
+
({ fonts: rendered } = await cdp.send('CSS.getPlatformFontsForNode', {
|
|
114
|
+
nodeId,
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Node vanished or selector unsupported.
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
usage.push({ ...item, rendered });
|
|
123
|
+
}
|
|
124
|
+
await cdp.send('CSS.disable').catch(() => undefined);
|
|
125
|
+
const fileFor = new Map(run.network.map((entry) => [entry.url, entry.file]));
|
|
126
|
+
const faces = probe.faces.map((face) => ({
|
|
127
|
+
...face,
|
|
128
|
+
sources: face.sources.map((source) => ({
|
|
129
|
+
...source,
|
|
130
|
+
file: fileFor.get(source.url) ?? null,
|
|
131
|
+
})),
|
|
132
|
+
}));
|
|
133
|
+
await writeJson(join(context.dir, 'fonts.json'), { faces, usage });
|
|
134
|
+
context.run.output('fonts', 'fonts.json');
|
|
135
|
+
},
|
|
136
|
+
async finish(context) {
|
|
137
|
+
// Network files are only known now: fill in file paths captured after the probe ran.
|
|
138
|
+
const path = join(context.dir, 'fonts.json');
|
|
139
|
+
try {
|
|
140
|
+
const data = await readJson(path);
|
|
141
|
+
const fileFor = new Map(context.run.network.map((entry) => [entry.url, entry.file]));
|
|
142
|
+
for (const face of data.faces) {
|
|
143
|
+
for (const source of face.sources)
|
|
144
|
+
source.file = fileFor.get(source.url) ?? source.file;
|
|
145
|
+
}
|
|
146
|
+
await writeJson(path, data);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// fonts.json was not written (collector failed earlier).
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=fonts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fonts.js","sourceRoot":"","sources":["../../../src/capture/collectors/fonts.ts"],"names":[],"mappings":"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"}
|