@d-zero/archaeologist 1.1.0 → 1.1.2
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/analyze.js +23 -72
- package/dist/archaeologist.js +3 -3
- package/dist/cli.js +2 -2
- package/dist/diff-images.d.ts +6 -6
- package/dist/diff-images.js +10 -12
- package/dist/get-data.d.ts +3 -3
- package/dist/get-data.js +3 -3
- package/dist/output-utils.d.ts +0 -2
- package/dist/output-utils.js +0 -3
- package/dist/read-config.d.ts +1 -1
- package/dist/read-config.js +4 -2
- package/dist/utils.d.ts +0 -1
- package/dist/utils.js +0 -3
- package/package.json +15 -14
package/dist/analyze.js
CHANGED
|
@@ -1,76 +1,31 @@
|
|
|
1
1
|
import { writeFile, mkdir } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { deal } from '@d-zero/dealer';
|
|
3
|
+
import { deal } from '@d-zero/puppeteer-dealer';
|
|
4
4
|
import c from 'ansi-colors';
|
|
5
|
-
import puppeteer from 'puppeteer';
|
|
6
5
|
import { analyzeUrlList } from './analize-url.js';
|
|
7
6
|
import { diffImages } from './diff-images.js';
|
|
8
7
|
import { diffTree } from './diff-tree.js';
|
|
9
8
|
import { getData } from './get-data.js';
|
|
10
|
-
import {
|
|
9
|
+
import { score } from './output-utils.js';
|
|
11
10
|
export async function analyze(list, options) {
|
|
12
|
-
const urlInfo = analyzeUrlList(list);
|
|
13
|
-
const useOldMode = urlInfo.hasAuth && urlInfo.hasNoSSL;
|
|
14
|
-
const browser = await puppeteer.launch({
|
|
15
|
-
headless: useOldMode ? 'shell' : true,
|
|
16
|
-
args: [
|
|
17
|
-
//
|
|
18
|
-
'--lang=ja',
|
|
19
|
-
'--no-zygote',
|
|
20
|
-
'--ignore-certificate-errors',
|
|
21
|
-
],
|
|
22
|
-
});
|
|
23
11
|
const results = [];
|
|
24
12
|
const dir = path.resolve(process.cwd(), '.archaeologist');
|
|
25
13
|
await mkdir(dir, { recursive: true }).catch(() => { });
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
14
|
+
const urlInfo = analyzeUrlList(list);
|
|
15
|
+
const useOldMode = urlInfo.hasAuth || urlInfo.hasNoSSL;
|
|
16
|
+
await deal(list.map(([urlA]) => ({ id: null, url: urlA })), (_, done, total) => {
|
|
17
|
+
return `${c.bold.magenta('🕵️ Archaeologist')} ${done}/${total}`;
|
|
18
|
+
}, {
|
|
19
|
+
async deal(page, _, urlA, logger, index) {
|
|
20
|
+
const urlPair = list.find(([url]) => url === urlA);
|
|
21
|
+
if (!urlPair) {
|
|
22
|
+
throw new Error(`Failed to find urlPair: ${urlA}`);
|
|
23
|
+
}
|
|
36
24
|
const dataPair = [];
|
|
37
25
|
for (const url of urlPair) {
|
|
38
26
|
const data = await getData(page, url, {
|
|
39
27
|
...options,
|
|
40
|
-
},
|
|
41
|
-
const outputUrl = c.gray(url);
|
|
42
|
-
const sizeName = label(data.name);
|
|
43
|
-
switch (phase) {
|
|
44
|
-
case 'setViewport': {
|
|
45
|
-
const { width } = data;
|
|
46
|
-
update(`%braille% ${outputUrl} ${sizeName}: ↔️ Change viewport size to ${width}px`);
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
case 'load': {
|
|
50
|
-
const { type } = data;
|
|
51
|
-
update(`%braille% ${outputUrl} ${sizeName}: %earth% ${type === 'open' ? 'Open' : 'Reload'} page`);
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
case 'hook': {
|
|
55
|
-
const { message } = data;
|
|
56
|
-
update(`%braille% ${outputUrl} ${sizeName}: ${message}`);
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
case 'scroll': {
|
|
60
|
-
update(`%braille% ${outputUrl} ${sizeName}: %propeller% Scroll the page`);
|
|
61
|
-
break;
|
|
62
|
-
}
|
|
63
|
-
case 'screenshotStart': {
|
|
64
|
-
update(`%braille% ${outputUrl} ${sizeName}: 📸 Take a screenshot`);
|
|
65
|
-
break;
|
|
66
|
-
}
|
|
67
|
-
case 'screenshotEnd': {
|
|
68
|
-
const { binary } = data;
|
|
69
|
-
update(`%braille% ${outputUrl} ${sizeName}: 📸 Screenshot taken (${binary.length} bytes)`);
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
});
|
|
28
|
+
}, logger);
|
|
74
29
|
dataPair.push(data);
|
|
75
30
|
await delay(600);
|
|
76
31
|
}
|
|
@@ -79,10 +34,10 @@ export async function analyze(list, options) {
|
|
|
79
34
|
throw new Error('Failed to get screenshots');
|
|
80
35
|
}
|
|
81
36
|
const screenshotResult = {};
|
|
82
|
-
const outputUrl = c.gray(urlPair
|
|
37
|
+
const outputUrl = 'vs ' + c.gray(urlPair[1]);
|
|
83
38
|
for (const [name, screenshotA] of Object.entries(a.screenshots)) {
|
|
84
39
|
const screenshotB = b.screenshots[name];
|
|
85
|
-
const sizeName =
|
|
40
|
+
const sizeName = c.bgMagenta(` ${name} `);
|
|
86
41
|
const id = `${index}_${name}`;
|
|
87
42
|
if (!screenshotB) {
|
|
88
43
|
throw new Error(`Failed to get screenshotB: ${id}`);
|
|
@@ -90,28 +45,28 @@ export async function analyze(list, options) {
|
|
|
90
45
|
const imageDiff = await diffImages(screenshotA, screenshotB, (phase, data) => {
|
|
91
46
|
switch (phase) {
|
|
92
47
|
case 'create': {
|
|
93
|
-
|
|
48
|
+
logger(`${sizeName} ${outputUrl} 🖼️ Create images`);
|
|
94
49
|
break;
|
|
95
50
|
}
|
|
96
51
|
case 'resize': {
|
|
97
52
|
const { width, height } = data;
|
|
98
|
-
|
|
53
|
+
logger(`${sizeName} ${outputUrl} ↔️ Resize images to ${width}x${height}`);
|
|
99
54
|
break;
|
|
100
55
|
}
|
|
101
56
|
case 'diff': {
|
|
102
|
-
|
|
57
|
+
logger(`${sizeName} ${outputUrl} 📊 Compare images`);
|
|
103
58
|
break;
|
|
104
59
|
}
|
|
105
60
|
}
|
|
106
61
|
});
|
|
107
62
|
let image = null;
|
|
108
63
|
if (imageDiff) {
|
|
109
|
-
|
|
64
|
+
logger(`${sizeName} ${outputUrl} 🧩 Matches ${score(imageDiff.matches, 0.9)}`);
|
|
110
65
|
await delay(1500);
|
|
111
66
|
await writeFile(path.resolve(dir, `${id}_a.png`), imageDiff.images.a);
|
|
112
67
|
await writeFile(path.resolve(dir, `${id}_b.png`), imageDiff.images.b);
|
|
113
68
|
const outFilePath = path.resolve(dir, `${id}_diff.png`);
|
|
114
|
-
|
|
69
|
+
logger(`${sizeName} ${outputUrl} 📊 Save diff image to ${path.relative(dir, outFilePath)}`);
|
|
115
70
|
await writeFile(outFilePath, imageDiff.images.diff);
|
|
116
71
|
image = {
|
|
117
72
|
matches: imageDiff.matches,
|
|
@@ -135,15 +90,11 @@ export async function analyze(list, options) {
|
|
|
135
90
|
screenshots: screenshotResult,
|
|
136
91
|
};
|
|
137
92
|
results.push(result);
|
|
138
|
-
};
|
|
139
|
-
}, {
|
|
140
|
-
limit: options?.limit,
|
|
141
|
-
debug: options?.debug,
|
|
142
|
-
header(_, done, total) {
|
|
143
|
-
return `${c.bold.magenta('🕵️ Archaeologist')} ${done}/${total}`;
|
|
144
93
|
},
|
|
94
|
+
}, {
|
|
95
|
+
...options,
|
|
96
|
+
headless: useOldMode ? 'shell' : true,
|
|
145
97
|
});
|
|
146
|
-
await browser.close();
|
|
147
98
|
return results;
|
|
148
99
|
}
|
|
149
100
|
function delay(ms) {
|
package/dist/archaeologist.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import c from 'ansi-colors';
|
|
2
2
|
import { analyze } from './analyze.js';
|
|
3
|
-
import {
|
|
3
|
+
import { score } from './output-utils.js';
|
|
4
4
|
export async function archaeologist(list, options) {
|
|
5
5
|
const results = await analyze(list, options);
|
|
6
6
|
const output = [];
|
|
@@ -9,9 +9,9 @@ export async function archaeologist(list, options) {
|
|
|
9
9
|
for (const [sizeName, { image, dom }] of Object.entries(result.screenshots)) {
|
|
10
10
|
if (image) {
|
|
11
11
|
const { matches, file } = image;
|
|
12
|
-
output.push(` ${
|
|
12
|
+
output.push(` ${c.bgMagenta(` ${sizeName} `)} ${score(matches, 0.9)} ${file}`);
|
|
13
13
|
}
|
|
14
|
-
output.push(` ${
|
|
14
|
+
output.push(` ${c.bgBlueBright(' HTML ')}: ${score(dom.matches, 0.995)} ${dom.file}`);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
process.stdout.write(output.join('\n') + '\n');
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ const cli = minimist(process.argv.slice(2), {
|
|
|
7
7
|
f: 'listfile',
|
|
8
8
|
},
|
|
9
9
|
});
|
|
10
|
-
if (cli.listfile) {
|
|
10
|
+
if (cli.listfile?.length) {
|
|
11
11
|
const { pairList, hooks } = await readConfig(cli.listfile);
|
|
12
12
|
await archaeologist(pairList, {
|
|
13
13
|
hooks,
|
|
@@ -17,5 +17,5 @@ if (cli.listfile) {
|
|
|
17
17
|
});
|
|
18
18
|
process.exit(0);
|
|
19
19
|
}
|
|
20
|
-
process.
|
|
20
|
+
process.stderr.write('Usage: archaeologist -f <listfile> [--limit <number>]\n');
|
|
21
21
|
process.exit(1);
|
package/dist/diff-images.d.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import type { Screenshot } from '@d-zero/puppeteer-screenshot';
|
|
2
2
|
export type DiffImagesPhase = {
|
|
3
3
|
create: {
|
|
4
|
-
a:
|
|
5
|
-
b:
|
|
4
|
+
a: Uint8Array;
|
|
5
|
+
b: Uint8Array;
|
|
6
6
|
};
|
|
7
7
|
resize: {
|
|
8
|
-
a:
|
|
9
|
-
b:
|
|
8
|
+
a: Uint8Array;
|
|
9
|
+
b: Uint8Array;
|
|
10
10
|
width: number;
|
|
11
11
|
height: number;
|
|
12
12
|
};
|
|
13
13
|
diff: {
|
|
14
|
-
a:
|
|
15
|
-
b:
|
|
14
|
+
a: Uint8Array;
|
|
15
|
+
b: Uint8Array;
|
|
16
16
|
};
|
|
17
17
|
};
|
|
18
18
|
type DiffImagesListener = (phase: keyof DiffImagesPhase, data: DiffImagesPhase[keyof DiffImagesPhase]) => void;
|
package/dist/diff-images.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Jimp from 'jimp';
|
|
1
|
+
import { Jimp, HorizontalAlign, VerticalAlign, JimpMime } from 'jimp';
|
|
2
2
|
import pixelmatch from 'pixelmatch';
|
|
3
3
|
import { PNG } from 'pngjs';
|
|
4
4
|
export async function diffImages(dataA, dataB, listener) {
|
|
@@ -6,13 +6,13 @@ export async function diffImages(dataA, dataB, listener) {
|
|
|
6
6
|
return null;
|
|
7
7
|
}
|
|
8
8
|
listener('create', { a: dataA.binary, b: dataB.binary });
|
|
9
|
-
const imgA = PNG.sync.read(dataA.binary);
|
|
10
|
-
const imgB = PNG.sync.read(dataB.binary);
|
|
9
|
+
const imgA = PNG.sync.read(Buffer.from(dataA.binary));
|
|
10
|
+
const imgB = PNG.sync.read(Buffer.from(dataB.binary));
|
|
11
11
|
const width = Math.max(imgA.width, imgB.width);
|
|
12
12
|
const height = Math.max(imgA.height, imgB.height);
|
|
13
13
|
listener('resize', { a: dataA.binary, b: dataB.binary, width, height });
|
|
14
|
-
const resizedA = await resizeImg(dataA.binary, width, height);
|
|
15
|
-
const resizedB = await resizeImg(dataB.binary, width, height);
|
|
14
|
+
const resizedA = await resizeImg(Buffer.from(dataA.binary), width, height);
|
|
15
|
+
const resizedB = await resizeImg(Buffer.from(dataB.binary), width, height);
|
|
16
16
|
listener('diff', { a: resizedA, b: resizedB });
|
|
17
17
|
const imgA_ = PNG.sync.read(resizedA);
|
|
18
18
|
const imgB_ = PNG.sync.read(resizedB);
|
|
@@ -33,12 +33,10 @@ export async function diffImages(dataA, dataB, listener) {
|
|
|
33
33
|
}
|
|
34
34
|
async function resizeImg(bin, width, height) {
|
|
35
35
|
const img = await Jimp.read(bin);
|
|
36
|
-
img.contain(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
reject(err);
|
|
41
|
-
resolve(buffer);
|
|
42
|
-
});
|
|
36
|
+
img.contain({
|
|
37
|
+
w: width,
|
|
38
|
+
h: height,
|
|
39
|
+
align: HorizontalAlign.LEFT | VerticalAlign.TOP,
|
|
43
40
|
});
|
|
41
|
+
return img.getBuffer(JimpMime.png);
|
|
44
42
|
}
|
package/dist/get-data.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { PageData } from './types.js';
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
2
|
+
import type { Page } from '@d-zero/puppeteer-page';
|
|
3
|
+
import type { PageHook } from '@d-zero/puppeteer-screenshot';
|
|
4
4
|
export interface GetDataOptions {
|
|
5
5
|
readonly hooks?: readonly PageHook[];
|
|
6
6
|
readonly htmlDiffOnly?: boolean;
|
|
7
7
|
}
|
|
8
|
-
export declare function getData(page: Page, url: string, options: GetDataOptions,
|
|
8
|
+
export declare function getData(page: Page, url: string, options: GetDataOptions, update: (log: string) => void): Promise<PageData>;
|
package/dist/get-data.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { distill } from '@d-zero/html-distiller';
|
|
2
|
-
import { screenshot } from '@d-zero/puppeteer-screenshot';
|
|
3
|
-
export async function getData(page, url, options,
|
|
2
|
+
import { screenshotListener, screenshot } from '@d-zero/puppeteer-screenshot';
|
|
3
|
+
export async function getData(page, url, options, update) {
|
|
4
4
|
const htmlDiffOnly = options.htmlDiffOnly ?? false;
|
|
5
5
|
const screenshots = await screenshot(page, url, {
|
|
6
6
|
sizes: {
|
|
@@ -13,7 +13,7 @@ export async function getData(page, url, options, listener) {
|
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
15
|
hooks: options?.hooks ?? [],
|
|
16
|
-
listener,
|
|
16
|
+
listener: screenshotListener(update),
|
|
17
17
|
domOnly: htmlDiffOnly,
|
|
18
18
|
});
|
|
19
19
|
const data = { url, screenshots: {} };
|
package/dist/output-utils.d.ts
CHANGED
package/dist/output-utils.js
CHANGED
package/dist/read-config.d.ts
CHANGED
package/dist/read-config.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { readPageHooks } from '@d-zero/puppeteer-page-scan';
|
|
2
4
|
import { toList } from '@d-zero/readtext/list';
|
|
3
5
|
import fm from 'front-matter';
|
|
4
|
-
import { readHooks } from './read-hooks.js';
|
|
5
6
|
export async function readConfig(filePath) {
|
|
6
7
|
const fileContent = await fs.readFile(filePath, 'utf8');
|
|
7
8
|
const content =
|
|
@@ -15,7 +16,8 @@ export async function readConfig(filePath) {
|
|
|
15
16
|
`${content.attributes.comparisonHost}${url.pathname}${url.search}`,
|
|
16
17
|
];
|
|
17
18
|
});
|
|
18
|
-
const
|
|
19
|
+
const baseDir = path.dirname(filePath);
|
|
20
|
+
const hooks = await readPageHooks(content.attributes?.hooks ?? [], baseDir);
|
|
19
21
|
return {
|
|
20
22
|
pairList,
|
|
21
23
|
hooks,
|
package/dist/utils.d.ts
CHANGED
package/dist/utils.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@d-zero/archaeologist",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Uncover visual and HTML differences in web pages with precision",
|
|
5
5
|
"author": "D-ZERO",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,28 +23,29 @@
|
|
|
23
23
|
],
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsc",
|
|
26
|
+
"watch": "tsc --watch",
|
|
26
27
|
"clean": "tsc --build --clean"
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
29
|
-
"@d-zero/
|
|
30
|
-
"@d-zero/
|
|
31
|
-
"@d-zero/puppeteer-
|
|
32
|
-
"@d-zero/
|
|
30
|
+
"@d-zero/html-distiller": "1.0.1",
|
|
31
|
+
"@d-zero/puppeteer-dealer": "0.2.0",
|
|
32
|
+
"@d-zero/puppeteer-page-scan": "2.0.0",
|
|
33
|
+
"@d-zero/puppeteer-screenshot": "2.0.0",
|
|
34
|
+
"@d-zero/readtext": "1.1.1",
|
|
33
35
|
"ansi-colors": "4.1.3",
|
|
34
|
-
"diff": "
|
|
36
|
+
"diff": "7.0.0",
|
|
35
37
|
"front-matter": "4.0.2",
|
|
36
|
-
"jimp": "
|
|
38
|
+
"jimp": "1.6.0",
|
|
37
39
|
"minimist": "1.2.8",
|
|
38
40
|
"parse-diff": "0.11.1",
|
|
39
|
-
"pixelmatch": "
|
|
40
|
-
"pngjs": "7.0.0"
|
|
41
|
-
"puppeteer": "22.12.0"
|
|
41
|
+
"pixelmatch": "6.0.0",
|
|
42
|
+
"pngjs": "7.0.0"
|
|
42
43
|
},
|
|
43
44
|
"devDependencies": {
|
|
44
|
-
"@
|
|
45
|
+
"@d-zero/puppeteer-page": "0.2.0",
|
|
46
|
+
"@types/diff": "6.0.0",
|
|
45
47
|
"@types/pixelmatch": "5.2.6",
|
|
46
|
-
"@types/pngjs": "6.0.5"
|
|
47
|
-
"puppeteer": "22.12.0"
|
|
48
|
+
"@types/pngjs": "6.0.5"
|
|
48
49
|
},
|
|
49
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "1eb1c03400580040119121e11ffb33acd876fe1b"
|
|
50
51
|
}
|