@d-zero/archaeologist 1.0.0-alpha.3 → 1.0.1

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 CHANGED
@@ -31,4 +31,65 @@ https://example.com/c
31
31
  https://example.com/xyz/001
32
32
  ```
33
33
 
34
+ 上記のサンプルファイルの場合、以下のURLが比較されます。
35
+
36
+ | 比較元 | 比較対象 |
37
+ | ----------------------------- | ----------------------------------- |
38
+ | `https://example.com` | `https://stage.example.com` |
39
+ | `https://example.com/a` | `https://stage.example.com/a` |
40
+ | `https://example.com/b` | `https://stage.example.com/b` |
41
+ | `https://example.com/c` | `https://stage.example.com/c` |
42
+ | `https://example.com/xyz/001` | `https://stage.example.com/xyz/001` |
43
+
34
44
  実行した結果は`.archaeologist`ディレクトリに保存されます。
45
+
46
+ ## ページフック
47
+
48
+ [Frontmatter](https://jekyllrb.com/docs/front-matter/)の`hooks`に配列としてスクリプトファイルのパスを渡すと、ページを開いた後(厳密にはPuppetterの`waitUntil: 'networkidle0'`のタイミング直後)にそれらのスクリプトを実行します。スクリプトは配列の順番通りに逐次実行されます。
49
+
50
+ ```txt
51
+ ---
52
+ comparisonHost: https://stage.example.com
53
+ hooks:
54
+ - ./hook1.cjs
55
+ - ./hook2.mjs
56
+ ---
57
+
58
+ https://example.com
59
+ https://example.com/a
60
+
61
+ ```
62
+
63
+ フックスクリプトは、以下のようにエクスポートされた関数を持つモジュールとして定義します。
64
+
65
+ ```js
66
+ /**
67
+ * @type {import('@d-zero/archaeologist').PageHook}
68
+ */
69
+ export default async function (page, { name, width, resolution, log }) {
70
+ // 非同期処理可能
71
+ // page: PuppeteerのPageオブジェクト
72
+ // name: サイズ名('desktop' | 'mobile')
73
+ // width: ウィンドウ幅
74
+ // resolution: 解像度
75
+ // log: ロガー
76
+
77
+ // ログイン処理の例
78
+ log('login');
79
+ await page.type('#username', 'user');
80
+ await page.type('#password', 'pass');
81
+ await page.click('button[type="submit"]');
82
+ await page.waitForNavigation();
83
+ log('login done');
84
+ }
85
+ ```
86
+
87
+ 例のように、ページにログインする処理をフックスクリプトに記述することで、ユーザー認証が必要なページのスクリーンショットを撮影することができます。
88
+
89
+ ## 認証
90
+
91
+ ### Basic認証
92
+
93
+ Basic認証が必要なページの場合はURLにユーザー名とパスワードを含めます。
94
+
95
+ 例: `https://user:pass@example.com`
package/dist/analyze.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  import type { Result, URLPair } from './types.js';
2
- export declare function analyze(list: readonly URLPair[]): Promise<Result[]>;
2
+ import type { PageHook } from '@d-zero/puppeteer-screenshot';
3
+ export declare function analyze(list: readonly URLPair[], hooks: readonly PageHook[]): Promise<Result[]>;
package/dist/analyze.js CHANGED
@@ -8,7 +8,7 @@ import { diffImages } from './diff-images.js';
8
8
  import { diffTree } from './diff-tree.js';
9
9
  import { getData } from './get-data.js';
10
10
  import { label, score } from './output-utils.js';
11
- export async function analyze(list) {
11
+ export async function analyze(list, hooks) {
12
12
  const urlInfo = analyzeUrlList(list);
13
13
  const useOldMode = urlInfo.hasAuth && urlInfo.hasNoSSL;
14
14
  const browser = await puppeteer.launch({
@@ -35,7 +35,7 @@ export async function analyze(list) {
35
35
  return async () => {
36
36
  const dataPair = [];
37
37
  for (const url of urlPair) {
38
- const data = await getData(page, url, (phase, data) => {
38
+ const data = await getData(page, url, hooks, (phase, data) => {
39
39
  const outputUrl = c.gray(url);
40
40
  const sizeName = label(data.name);
41
41
  switch (phase) {
@@ -49,6 +49,11 @@ export async function analyze(list) {
49
49
  update(`%braille% ${outputUrl} ${sizeName}: %earth% ${type === 'open' ? 'Open' : 'Reload'} page`);
50
50
  break;
51
51
  }
52
+ case 'hook': {
53
+ const { message } = data;
54
+ update(`%braille% ${outputUrl} ${sizeName}: ${message}`);
55
+ break;
56
+ }
52
57
  case 'scroll': {
53
58
  update(`%braille% ${outputUrl} ${sizeName}: %propeller% Scroll the page`);
54
59
  break;
@@ -1,2 +1,3 @@
1
1
  import type { URLPair } from './types.js';
2
- export declare function archaeologist(list: readonly URLPair[]): Promise<void>;
2
+ import type { PageHook } from '@d-zero/puppeteer-screenshot';
3
+ export declare function archaeologist(list: readonly URLPair[], pageHooks?: readonly PageHook[]): Promise<void>;
@@ -1,8 +1,8 @@
1
1
  import c from 'ansi-colors';
2
2
  import { analyze } from './analyze.js';
3
3
  import { label, score } from './output-utils.js';
4
- export async function archaeologist(list) {
5
- const results = await analyze(list);
4
+ export async function archaeologist(list, pageHooks) {
5
+ const results = await analyze(list, pageHooks ?? []);
6
6
  const output = [];
7
7
  for (const result of results) {
8
8
  output.push(c.gray(`${result.target.join(' vs ')}`));
package/dist/cli.js CHANGED
@@ -1,28 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import fs from 'node:fs/promises';
3
- import { toList } from '@d-zero/readtext/list';
4
- import fm from 'front-matter';
5
2
  import minimist from 'minimist';
6
3
  import { archaeologist } from './archaeologist.js';
4
+ import { readConfig } from './read-config.js';
7
5
  const cli = minimist(process.argv.slice(2), {
8
6
  alias: {
9
7
  f: 'listfile',
10
8
  },
11
9
  });
12
10
  if (cli.listfile) {
13
- const fileContent = await fs.readFile(cli.listfile, 'utf8');
14
- const content =
15
- // @ts-ignore
16
- fm(fileContent);
17
- const urlList = toList(content.body);
18
- const pairList = urlList.map((urlStr) => {
19
- const url = new URL(urlStr);
20
- return [
21
- url.toString(),
22
- `${content.attributes.comparisonHost}${url.pathname}${url.search}`,
23
- ];
24
- });
25
- await archaeologist(pairList);
11
+ const { pairList, pageHooks } = await readConfig(cli.listfile);
12
+ await archaeologist(pairList, pageHooks);
26
13
  process.exit(0);
27
14
  }
28
15
  process.stdout.write('Usage: archaeologist -f <listfile>\n');
@@ -1,4 +1,4 @@
1
1
  import type { PageData } from './types.js';
2
- import type { Listener } from '@d-zero/puppeteer-screenshot';
2
+ import type { Listener, PageHook } from '@d-zero/puppeteer-screenshot';
3
3
  import type { Page } from 'puppeteer';
4
- export declare function getData(page: Page, url: string, listener: Listener): Promise<PageData>;
4
+ export declare function getData(page: Page, url: string, hooks: readonly PageHook[], listener: Listener): Promise<PageData>;
package/dist/get-data.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { distill } from '@d-zero/html-distiller';
2
2
  import { screenshot } from '@d-zero/puppeteer-screenshot';
3
- export async function getData(page, url, listener) {
3
+ export async function getData(page, url, hooks, listener) {
4
4
  const screenshots = await screenshot(page, url, {
5
5
  sizes: {
6
6
  desktop: {
@@ -11,6 +11,7 @@ export async function getData(page, url, listener) {
11
11
  resolution: 2,
12
12
  },
13
13
  },
14
+ hooks,
14
15
  listener,
15
16
  });
16
17
  const html = await page.content();
package/dist/index.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { archaeologist } from './archaeologist.js';
2
+ export * from './types.js';
package/dist/index.js CHANGED
@@ -1 +1,2 @@
1
1
  export { archaeologist } from './archaeologist.js';
2
+ export * from './types.js';
@@ -0,0 +1,4 @@
1
+ export declare function readConfig(filePath: string): Promise<{
2
+ pairList: [string, string][];
3
+ pageHooks: import("@d-zero/puppeteer-screenshot").PageHook[];
4
+ }>;
@@ -0,0 +1,23 @@
1
+ import fs from 'node:fs/promises';
2
+ import { toList } from '@d-zero/readtext/list';
3
+ import fm from 'front-matter';
4
+ import { readHooks } from './read-hooks.js';
5
+ export async function readConfig(filePath) {
6
+ const fileContent = await fs.readFile(filePath, 'utf8');
7
+ const content =
8
+ // @ts-ignore
9
+ fm(fileContent);
10
+ const urlList = toList(content.body);
11
+ const pairList = urlList.map((urlStr) => {
12
+ const url = new URL(urlStr);
13
+ return [
14
+ url.toString(),
15
+ `${content.attributes.comparisonHost}${url.pathname}${url.search}`,
16
+ ];
17
+ });
18
+ const pageHooks = await readHooks(content.attributes?.hooks ?? [], filePath);
19
+ return {
20
+ pairList,
21
+ pageHooks,
22
+ };
23
+ }
@@ -0,0 +1,2 @@
1
+ import type { PageHook } from '@d-zero/puppeteer-screenshot';
2
+ export declare function readHooks(hooks: readonly string[], listfile: string): Promise<PageHook[]>;
@@ -0,0 +1,20 @@
1
+ import path from 'node:path';
2
+ export async function readHooks(hooks, listfile) {
3
+ const listfileDir = path.dirname(listfile);
4
+ const pageHooks = await Promise.all(hooks.map(async (hook) => {
5
+ const hookAbsPath = path.isAbsolute(hook) ? hook : path.resolve(listfileDir, hook);
6
+ const { default: mod } = await import(hookAbsPath).catch((error) => {
7
+ if (error instanceof Error &&
8
+ 'code' in error &&
9
+ error.code === 'ERR_MODULE_NOT_FOUND') {
10
+ throw new Error(`Hook: ${hook} not found`, { cause: error });
11
+ }
12
+ throw error;
13
+ });
14
+ if (typeof mod !== 'function') {
15
+ throw new TypeError(`Hook ${hook} is not a function`);
16
+ }
17
+ return mod;
18
+ }));
19
+ return pageHooks;
20
+ }
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export type { PageHook } from '@d-zero/puppeteer-screenshot';
1
2
  import type { Screenshot } from '@d-zero/puppeteer-screenshot';
2
3
  export type PageData = {
3
4
  url: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d-zero/archaeologist",
3
- "version": "1.0.0-alpha.3",
3
+ "version": "1.0.1",
4
4
  "description": "Uncover visual and HTML differences in web pages with precision",
5
5
  "author": "D-ZERO",
6
6
  "license": "MIT",
@@ -26,10 +26,10 @@
26
26
  "clean": "tsc --build --clean"
27
27
  },
28
28
  "dependencies": {
29
- "@d-zero/dealer": "^1.0.0-alpha.2",
30
- "@d-zero/html-distiller": "^1.0.0-alpha.2",
31
- "@d-zero/puppeteer-screenshot": "^1.0.0-alpha.3",
32
- "@d-zero/readtext": "^1.0.0-alpha.2",
29
+ "@d-zero/dealer": "1.0.0",
30
+ "@d-zero/html-distiller": "1.0.0",
31
+ "@d-zero/puppeteer-screenshot": "1.0.1",
32
+ "@d-zero/readtext": "1.0.1",
33
33
  "ansi-colors": "4.1.3",
34
34
  "diff": "5.2.0",
35
35
  "front-matter": "4.0.2",
@@ -37,13 +37,13 @@
37
37
  "minimist": "1.2.8",
38
38
  "pixelmatch": "5.3.0",
39
39
  "pngjs": "7.0.0",
40
- "puppeteer": "22.6.5"
40
+ "puppeteer": "22.8.2"
41
41
  },
42
42
  "devDependencies": {
43
- "@types/diff": "5.0.9",
43
+ "@types/diff": "5.2.1",
44
44
  "@types/pixelmatch": "5.2.6",
45
- "@types/pngjs": "6.0.4",
46
- "puppeteer": "22.6.5"
45
+ "@types/pngjs": "6.0.5",
46
+ "puppeteer": "22.8.2"
47
47
  },
48
- "gitHead": "3544423004c3ee923790f78657a78dfc3c88771d"
48
+ "gitHead": "426ceebc46f7497a32da6f82677e518efa18245d"
49
49
  }