@d-zero/archaeologist 1.1.3 → 2.0.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.
@@ -0,0 +1,9 @@
1
+ import type { URLPair } from './types.js';
2
+ import type { PageHook } from '@d-zero/puppeteer-page-scan';
3
+ export type ChildProcessParams = {
4
+ list: readonly URLPair[];
5
+ dir: string;
6
+ useOldMode: boolean;
7
+ htmlDiffOnly: boolean;
8
+ hooks?: readonly PageHook[];
9
+ };
@@ -1,27 +1,16 @@
1
- import { writeFile, mkdir } from 'node:fs/promises';
1
+ import { writeFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { deal } from '@d-zero/puppeteer-dealer';
3
+ import { createChildProcess } from '@d-zero/puppeteer-dealer';
4
+ import { delay } from '@d-zero/shared/delay';
4
5
  import c from 'ansi-colors';
5
- import { analyzeUrlList } from './analize-url.js';
6
- import { diffImages } from './diff-images.js';
7
- import { diffTree } from './diff-tree.js';
8
- import { getData } from './get-data.js';
9
- import { score } from './output-utils.js';
10
- /**
11
- *
12
- * @param list
13
- * @param options
14
- */
15
- export async function analyze(list, options) {
16
- const results = [];
17
- const dir = path.resolve(process.cwd(), '.archaeologist');
18
- await mkdir(dir, { recursive: true }).catch(() => { });
19
- const urlInfo = analyzeUrlList(list);
20
- const useOldMode = urlInfo.hasAuth || urlInfo.hasNoSSL;
21
- await deal(list.map(([urlA]) => ({ id: null, url: urlA })), (_, done, total) => {
22
- return `${c.bold.magenta('🕵️ Archaeologist')} ${done}/${total}`;
23
- }, {
24
- async deal(page, _, urlA, logger, index) {
6
+ import { diffImages } from './modules/diff-images.js';
7
+ import { diffTree } from './modules/diff-tree.js';
8
+ import { getData } from './modules/get-data.js';
9
+ import { score } from './utils.js';
10
+ createChildProcess((param) => {
11
+ const { list, dir, htmlDiffOnly } = param;
12
+ return {
13
+ async eachPage({ page, url: urlA, index }, logger) {
25
14
  const urlPair = list.find(([url]) => url === urlA);
26
15
  if (!urlPair) {
27
16
  throw new Error(`Failed to find urlPair: ${urlA}`);
@@ -29,7 +18,7 @@ export async function analyze(list, options) {
29
18
  const dataPair = [];
30
19
  for (const url of urlPair) {
31
20
  const data = await getData(page, url, {
32
- ...options,
21
+ htmlDiffOnly,
33
22
  }, logger);
34
23
  dataPair.push(data);
35
24
  await delay(600);
@@ -94,18 +83,7 @@ export async function analyze(list, options) {
94
83
  target: [a.url, b.url],
95
84
  screenshots: screenshotResult,
96
85
  };
97
- results.push(result);
86
+ return result;
98
87
  },
99
- }, {
100
- ...options,
101
- headless: useOldMode ? 'shell' : true,
102
- });
103
- return results;
104
- }
105
- /**
106
- *
107
- * @param ms
108
- */
109
- function delay(ms) {
110
- return new Promise((resolve) => setTimeout(resolve, ms));
111
- }
88
+ };
89
+ });
@@ -0,0 +1,7 @@
1
+ import type { AnalyzeOptions, URLPair } from './types.js';
2
+ /**
3
+ *
4
+ * @param list
5
+ * @param options
6
+ */
7
+ export declare function analyze(list: readonly URLPair[], options?: AnalyzeOptions): Promise<void>;
@@ -0,0 +1,46 @@
1
+ import { mkdir } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { createProcess, deal } from '@d-zero/puppeteer-dealer';
4
+ import c from 'ansi-colors';
5
+ import { analyzeUrlList } from './modules/analize-url.js';
6
+ import { score } from './utils.js';
7
+ /**
8
+ *
9
+ * @param list
10
+ * @param options
11
+ */
12
+ export async function analyze(list, options) {
13
+ const results = [];
14
+ const dir = path.resolve(process.cwd(), '.archaeologist');
15
+ await mkdir(dir, { recursive: true }).catch(() => { });
16
+ const urlInfo = analyzeUrlList(list);
17
+ const useOldMode = urlInfo.hasAuth || urlInfo.hasNoSSL;
18
+ await deal(list.map(([urlA]) => ({ id: null, url: urlA })), (_, done, total) => {
19
+ return `${c.bold.magenta('🕵️ Archaeologist')} ${done}/${total}`;
20
+ }, () => {
21
+ return createProcess(path.resolve(import.meta.dirname, 'analyze-child-process.js'), {
22
+ list,
23
+ dir,
24
+ useOldMode,
25
+ htmlDiffOnly: options?.htmlDiffOnly ?? false,
26
+ hooks: options?.hooks ?? [],
27
+ }, {
28
+ ...options,
29
+ headless: useOldMode ? 'shell' : true,
30
+ });
31
+ }, (result) => {
32
+ results.push(result);
33
+ });
34
+ const output = [];
35
+ for (const result of results) {
36
+ output.push(c.gray(`${result.target.join(' vs ')}`));
37
+ for (const [sizeName, { image, dom }] of Object.entries(result.screenshots)) {
38
+ if (image) {
39
+ const { matches, file } = image;
40
+ output.push(` ${c.bgMagenta(` ${sizeName} `)} ${score(matches, 0.9)} ${file}`);
41
+ }
42
+ output.push(` ${c.bgBlueBright(' HTML ')}: ${score(dom.matches, 0.995)} ${dom.file}`);
43
+ }
44
+ }
45
+ process.stdout.write(output.join('\n') + '\n');
46
+ }
package/dist/cli.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import minimist from 'minimist';
3
- import { archaeologist } from './archaeologist.js';
3
+ import { analyze } from './analyze-main-process.js';
4
+ import { freeze } from './freeze-main-process.js';
4
5
  import { readConfig } from './read-config.js';
5
6
  const cli = minimist(process.argv.slice(2), {
6
7
  alias: {
@@ -9,7 +10,7 @@ const cli = minimist(process.argv.slice(2), {
9
10
  });
10
11
  if (cli.listfile?.length) {
11
12
  const { pairList, hooks } = await readConfig(cli.listfile);
12
- await archaeologist(pairList, {
13
+ await analyze(pairList, {
13
14
  hooks,
14
15
  limit: cli.limit ? Number.parseInt(cli.limit) : undefined,
15
16
  debug: !!cli.debug,
@@ -17,5 +18,15 @@ if (cli.listfile?.length) {
17
18
  });
18
19
  process.exit(0);
19
20
  }
21
+ if (cli.freeze) {
22
+ const { pairList, hooks } = await readConfig(cli.freeze);
23
+ const list = pairList.map(([urlA]) => urlA);
24
+ await freeze(list, {
25
+ hooks,
26
+ limit: cli.limit ? Number.parseInt(cli.limit) : undefined,
27
+ debug: !!cli.debug,
28
+ });
29
+ process.exit(0);
30
+ }
20
31
  process.stderr.write('Usage: archaeologist -f <listfile> [--limit <number>]\n');
21
32
  process.exit(1);
@@ -0,0 +1,3 @@
1
+ export type ChildProcessParams = {
2
+ dir: string;
3
+ };
@@ -0,0 +1,22 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { createChildProcess } from '@d-zero/puppeteer-dealer';
4
+ import { delay } from '@d-zero/shared/delay';
5
+ import { getData } from './modules/get-data.js';
6
+ createChildProcess((param) => {
7
+ const { dir } = param;
8
+ return {
9
+ async eachPage({ page, id, url }, logger) {
10
+ const data = await getData(page, url, {}, logger);
11
+ await delay(600);
12
+ for (const size of Object.values(data.screenshots)) {
13
+ const jsonFile = path.resolve(dir, `${id}_${size.id}.html`);
14
+ const ssFile = path.resolve(dir, `${id}_${size.id}.png`);
15
+ await writeFile(jsonFile, size.dom, 'utf8');
16
+ if (size.binary) {
17
+ await writeFile(ssFile, size.binary);
18
+ }
19
+ }
20
+ },
21
+ };
22
+ });
@@ -0,0 +1,7 @@
1
+ import type { FreezeOptions } from './types.js';
2
+ /**
3
+ *
4
+ * @param list
5
+ * @param options
6
+ */
7
+ export declare function freeze(list: readonly string[], options?: FreezeOptions): Promise<string>;
@@ -0,0 +1,34 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { zip } from '@d-zero/fs/zip';
4
+ import { createProcess, deal } from '@d-zero/puppeteer-dealer';
5
+ import { timestamp } from '@d-zero/shared/timestamp';
6
+ import c from 'ansi-colors';
7
+ import { analyzeUrlList } from './modules/analize-url.js';
8
+ /**
9
+ *
10
+ * @param list
11
+ * @param options
12
+ */
13
+ export async function freeze(list, options) {
14
+ const name = `${timestamp('YYYYMMDD')}.archae`;
15
+ const dir = path.resolve(process.cwd(), `.${name}`);
16
+ await mkdir(dir, { recursive: true }).catch(() => { });
17
+ const urlInfo = analyzeUrlList(list);
18
+ const useOldMode = urlInfo.hasAuth || urlInfo.hasNoSSL;
19
+ await deal(list.map((url) => ({ id: null, url })), (_, done, total) => {
20
+ return `${c.bold.magenta('🕵️ Archaeologist Freeze❄️')} ${done}/${total}`;
21
+ }, () => {
22
+ return createProcess(path.resolve(import.meta.dirname, 'freeze-child-process.js'), {
23
+ dir,
24
+ }, {
25
+ ...options,
26
+ headless: useOldMode ? 'shell' : true,
27
+ });
28
+ });
29
+ const urlListPath = path.resolve(dir, '_URL_LIST.json');
30
+ await writeFile(urlListPath, JSON.stringify(list, null, '\t'), 'utf8');
31
+ const zipPath = path.resolve(process.cwd(), `${name}.zip`);
32
+ await zip(zipPath, dir);
33
+ return zipPath;
34
+ }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { archaeologist } from './archaeologist.js';
1
+ export { analyze as archaeologist } from './analyze-main-process.js';
2
2
  export * from './types.js';
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { archaeologist } from './archaeologist.js';
1
+ export { analyze as archaeologist } from './analyze-main-process.js';
2
2
  export * from './types.js';
@@ -0,0 +1,11 @@
1
+ import type { URLPair } from '../types.js';
2
+ type AnalyzedUrlList = {
3
+ hasAuth: boolean;
4
+ hasNoSSL: boolean;
5
+ };
6
+ /**
7
+ *
8
+ * @param list
9
+ */
10
+ export declare function analyzeUrlList(list: readonly (URLPair | string)[]): AnalyzedUrlList;
11
+ export {};
@@ -8,6 +8,16 @@ export function analyzeUrlList(list) {
8
8
  hasNoSSL: false,
9
9
  };
10
10
  for (const urlPair of list) {
11
+ if (typeof urlPair === 'string') {
12
+ const urlObj = new URL(urlPair);
13
+ if (urlObj.username || urlObj.password) {
14
+ result.hasAuth = true;
15
+ }
16
+ if (urlObj.protocol === 'http:') {
17
+ result.hasNoSSL = true;
18
+ }
19
+ continue;
20
+ }
11
21
  for (const url of urlPair) {
12
22
  const urlObj = new URL(url);
13
23
  if (urlObj.username || urlObj.password) {
@@ -1,6 +1,6 @@
1
- import type { PageData } from './types.js';
2
- import type { Page } from '@d-zero/puppeteer-page';
1
+ import type { PageData } from '../types.js';
3
2
  import type { PageHook } from '@d-zero/puppeteer-screenshot';
3
+ import type { Page } from 'puppeteer';
4
4
  export interface GetDataOptions {
5
5
  readonly hooks?: readonly PageHook[];
6
6
  readonly htmlDiffOnly?: boolean;
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export type { PageHook } from '@d-zero/puppeteer-screenshot';
2
- import type { Screenshot } from '@d-zero/puppeteer-screenshot';
2
+ import type { PageHook, Screenshot } from '@d-zero/puppeteer-screenshot';
3
3
  export type PageData = {
4
4
  url: string;
5
5
  screenshots: Record<string, Screenshot & {
@@ -24,3 +24,15 @@ export type DOMResult = {
24
24
  diff: string | null;
25
25
  file: string;
26
26
  };
27
+ export interface ArchaeologistOptions extends AnalyzeOptions {
28
+ }
29
+ export interface AnalyzeOptions extends GeneralOptions {
30
+ readonly htmlDiffOnly?: boolean;
31
+ }
32
+ export interface FreezeOptions extends GeneralOptions {
33
+ }
34
+ interface GeneralOptions {
35
+ readonly hooks: readonly PageHook[];
36
+ readonly limit?: number;
37
+ readonly debug?: boolean;
38
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d-zero/archaeologist",
3
- "version": "1.1.3",
3
+ "version": "2.0.0",
4
4
  "description": "Uncover visual and HTML differences in web pages with precision",
5
5
  "author": "D-ZERO",
6
6
  "license": "MIT",
@@ -27,25 +27,27 @@
27
27
  "clean": "tsc --build --clean"
28
28
  },
29
29
  "dependencies": {
30
- "@d-zero/html-distiller": "1.0.2",
31
- "@d-zero/puppeteer-dealer": "0.3.0",
32
- "@d-zero/puppeteer-page-scan": "3.0.0",
33
- "@d-zero/puppeteer-screenshot": "3.0.0",
34
- "@d-zero/readtext": "1.1.2",
30
+ "@d-zero/fs": "0.2.0",
31
+ "@d-zero/html-distiller": "1.0.3",
32
+ "@d-zero/puppeteer-dealer": "0.4.0",
33
+ "@d-zero/puppeteer-page-scan": "4.0.0",
34
+ "@d-zero/puppeteer-screenshot": "3.0.1",
35
+ "@d-zero/readtext": "1.1.3",
36
+ "@d-zero/shared": "0.8.0",
35
37
  "ansi-colors": "4.1.3",
36
- "diff": "7.0.0",
38
+ "diff": "8.0.1",
37
39
  "front-matter": "4.0.2",
38
40
  "jimp": "1.6.0",
39
41
  "minimist": "1.2.8",
40
42
  "parse-diff": "0.11.1",
41
43
  "pixelmatch": "7.1.0",
42
- "pngjs": "7.0.0"
44
+ "pngjs": "7.0.0",
45
+ "puppeteer": "24.9.0"
43
46
  },
44
47
  "devDependencies": {
45
- "@d-zero/puppeteer-page": "0.3.0",
46
- "@types/diff": "7.0.1",
48
+ "@types/diff": "8.0.0",
47
49
  "@types/pixelmatch": "5.2.6",
48
50
  "@types/pngjs": "6.0.5"
49
51
  },
50
- "gitHead": "e4fd17857e31022d121527b00fd7f009dbdb2142"
52
+ "gitHead": "4e9cc7b87e0fef91b6f2d4edfb66ca9134b2491b"
51
53
  }
@@ -1,11 +0,0 @@
1
- import type { URLPair } from './types.js';
2
- type AnalyzedUrlList = {
3
- hasAuth: boolean;
4
- hasNoSSL: boolean;
5
- };
6
- /**
7
- *
8
- * @param list
9
- */
10
- export declare function analyzeUrlList(list: readonly URLPair[]): AnalyzedUrlList;
11
- export {};
package/dist/analyze.d.ts DELETED
@@ -1,14 +0,0 @@
1
- import type { Result, URLPair } from './types.js';
2
- import type { PageHook } from '@d-zero/puppeteer-screenshot';
3
- export interface AnalyzeOptions {
4
- readonly hooks: readonly PageHook[];
5
- readonly htmlDiffOnly?: boolean;
6
- readonly limit?: number;
7
- readonly debug?: boolean;
8
- }
9
- /**
10
- *
11
- * @param list
12
- * @param options
13
- */
14
- export declare function analyze(list: readonly URLPair[], options?: AnalyzeOptions): Promise<Result[]>;
@@ -1,10 +0,0 @@
1
- import type { AnalyzeOptions } from './analyze.js';
2
- import type { URLPair } from './types.js';
3
- export interface ArchaeologistOptions extends AnalyzeOptions {
4
- }
5
- /**
6
- *
7
- * @param list
8
- * @param options
9
- */
10
- export declare function archaeologist(list: readonly URLPair[], options?: ArchaeologistOptions): Promise<void>;
@@ -1,23 +0,0 @@
1
- import c from 'ansi-colors';
2
- import { analyze } from './analyze.js';
3
- import { score } from './output-utils.js';
4
- /**
5
- *
6
- * @param list
7
- * @param options
8
- */
9
- export async function archaeologist(list, options) {
10
- const results = await analyze(list, options);
11
- const output = [];
12
- for (const result of results) {
13
- output.push(c.gray(`${result.target.join(' vs ')}`));
14
- for (const [sizeName, { image, dom }] of Object.entries(result.screenshots)) {
15
- if (image) {
16
- const { matches, file } = image;
17
- output.push(` ${c.bgMagenta(` ${sizeName} `)} ${score(matches, 0.9)} ${file}`);
18
- }
19
- output.push(` ${c.bgBlueBright(' HTML ')}: ${score(dom.matches, 0.995)} ${dom.file}`);
20
- }
21
- }
22
- process.stdout.write(output.join('\n') + '\n');
23
- }
package/dist/freeze.d.ts DELETED
@@ -1,13 +0,0 @@
1
- import type { PageData } from './types.js';
2
- import type { PageHook } from '@d-zero/puppeteer-screenshot';
3
- export interface FreezeOptions {
4
- readonly hooks: readonly PageHook[];
5
- readonly limit?: number;
6
- readonly debug?: boolean;
7
- }
8
- /**
9
- *
10
- * @param list
11
- * @param options
12
- */
13
- export declare function freeze(list: readonly string[], options?: FreezeOptions): Promise<PageData[]>;
package/dist/freeze.js DELETED
@@ -1,34 +0,0 @@
1
- import { mkdir } from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { deal } from '@d-zero/puppeteer-dealer';
4
- import { delay } from '@d-zero/shared/delay';
5
- import c from 'ansi-colors';
6
- import { analyzeUrlList } from './analize-url.js';
7
- import { getData } from './get-data.js';
8
- /**
9
- *
10
- * @param list
11
- * @param options
12
- */
13
- export async function freeze(list, options) {
14
- const results = [];
15
- const dir = path.resolve(process.cwd(), '.archaeologist');
16
- await mkdir(dir, { recursive: true }).catch(() => { });
17
- const urlInfo = analyzeUrlList(list);
18
- const useOldMode = urlInfo.hasAuth || urlInfo.hasNoSSL;
19
- await deal(list.map((url) => ({ id: null, url })), (_, done, total) => {
20
- return `${c.bold.magenta('🕵️ Archaeologist Freeze❄️')} ${done}/${total}`;
21
- }, {
22
- async deal(page, _, url, logger) {
23
- const data = await getData(page, url, {
24
- ...options,
25
- }, logger);
26
- await delay(600);
27
- results.push(data);
28
- },
29
- }, {
30
- ...options,
31
- headless: useOldMode ? 'shell' : true,
32
- });
33
- return results;
34
- }
@@ -1,6 +0,0 @@
1
- /**
2
- *
3
- * @param matches
4
- * @param threshold
5
- */
6
- export declare function score(matches: number, threshold: number): string;
@@ -1,11 +0,0 @@
1
- import c from 'ansi-colors';
2
- /**
3
- *
4
- * @param matches
5
- * @param threshold
6
- */
7
- export function score(matches, threshold) {
8
- const color = matches > threshold ? c.green : c.red;
9
- const num = (matches * 100).toFixed(1);
10
- return c.bold(color(`${num}%`));
11
- }
File without changes
File without changes
File without changes
File without changes