@kaze-no-ryuu/argus 0.3.0 → 0.4.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 CHANGED
@@ -3,11 +3,10 @@
3
3
  **Does what you built match the real thing?** Argus compares your local site, page by page and at desktop, tablet and mobile sizes, against a **live reference site** and/or **design images**. It tells you how much changed and *which element* changed.
4
4
 
5
5
  ```bash
6
- npm i -D @kaze-no-ryuu/argus && npx playwright install chromium
7
- npx argus
6
+ npx @kaze-no-ryuu/argus
8
7
  ```
9
8
 
10
- The first run detects your framework, writes a config and adds an `argus` npm script. Every run after that starts your dev server, screenshots everything, diffs it and writes one HTML report.
9
+ That's the whole setup. The first run downloads Chromium if it isn't installed yet, detects your framework, writes a config and adds an `argus` npm script. Every run after that starts your dev server, screenshots everything, diffs it and writes one HTML report.
11
10
 
12
11
  ## Why Argus
13
12
 
@@ -37,12 +36,13 @@ Use the hosted services when you need a team review flow for screenshots that ch
37
36
  ## Quick start
38
37
 
39
38
  ```bash
40
- npm i -D @kaze-no-ryuu/argus
41
- npx playwright install chromium # once, downloads the browser
39
+ npm i -D @kaze-no-ryuu/argus # pin the version in your project (optional, recommended for CI)
42
40
  npx argus init # detects your framework, writes argus.config.json, adds an npm script
43
- npm run argus
41
+ npm run argus # first run downloads Chromium (~100 MB, once)
44
42
  ```
45
43
 
44
+ On a fresh Linux machine or CI image, Chromium also needs system libraries. Install them once with `npx playwright install-deps chromium` (needs root).
45
+
46
46
  Running `npx argus` without a config does the `init` step for you.
47
47
 
48
48
  > The npm package is `@kaze-no-ryuu/argus` and the command is `argus`. Without a local install, run `npx @kaze-no-ryuu/argus` instead. `npx argus` would fetch an unrelated package.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type Browser } from 'playwright';
1
2
  import type { Band, Box, Config, Log, Page, RawConfig, Result, RunOptions, Summary, Viewport } from './types.ts';
2
3
  export type * from './types.ts';
3
4
  export declare const CONFIG_FILE = "argus.config.json";
@@ -9,6 +10,8 @@ export declare const DEFAULTS: {
9
10
  settle: number;
10
11
  serverTimeout: number;
11
12
  };
13
+ export declare const isMissingBrowser: (e: unknown) => boolean;
14
+ export declare function launchBrowser(log: Log): Promise<Browser>;
12
15
  export declare function loadConfig(file?: string): Config;
13
16
  export declare function normalizeConfig(raw: RawConfig, root?: string): Config;
14
17
  export declare function pageName(p: string): string;
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { chromium } from 'playwright';
2
2
  import { compare } from 'odiff-bin';
3
- import { spawn, execSync } from 'node:child_process';
3
+ import { spawn, execSync, execFileSync } from 'node:child_process';
4
+ import { createRequire } from 'node:module';
4
5
  import fs from 'node:fs';
5
6
  import { setTimeout as sleep } from 'node:timers/promises';
6
7
  import os from 'node:os';
@@ -20,6 +21,23 @@ export const DEFAULTS = {
20
21
  serverTimeout: 120000,
21
22
  };
22
23
  const errMsg = (e) => (e instanceof Error ? e.message : String(e));
24
+ export const isMissingBrowser = (e) => /Executable doesn't exist/.test(errMsg(e));
25
+ // Launches Chromium, downloading it first if it isn't installed yet, so
26
+ // `npx argus` works without a separate `playwright install` step.
27
+ export async function launchBrowser(log) {
28
+ try {
29
+ return await chromium.launch();
30
+ }
31
+ catch (e) {
32
+ if (!isMissingBrowser(e))
33
+ throw e;
34
+ log('Chromium is not installed yet, downloading it (one time, ~100 MB)...');
35
+ const cli = path.join(path.dirname(createRequire(import.meta.url).resolve('playwright')), 'cli.js');
36
+ // Output goes to stderr so --json stdout stays clean.
37
+ execFileSync(process.execPath, [cli, 'install', 'chromium'], { stdio: ['ignore', 2, 2] });
38
+ return chromium.launch();
39
+ }
40
+ }
23
41
  // Fills defaults and resolves file paths relative to the config file.
24
42
  export function loadConfig(file = CONFIG_FILE) {
25
43
  const raw = JSON.parse(fs.readFileSync(file, 'utf8'));
@@ -391,7 +409,7 @@ export async function run(cfg, opts = {}) {
391
409
  }
392
410
  let browser;
393
411
  if (doShoot)
394
- browser = browserWs ? await chromium.connect(browserWs) : await chromium.launch();
412
+ browser = browserWs ? await chromium.connect(browserWs) : await launchBrowser(log);
395
413
  try {
396
414
  const ctxs = new Map();
397
415
  if (browser) {
package/dist/init.js CHANGED
@@ -3,6 +3,7 @@ import path from 'node:path';
3
3
  import readline from 'node:readline/promises';
4
4
  import { CONFIG_FILE, DEFAULTS } from './index.js';
5
5
  const PLACEHOLDER = 'https://example.com';
6
+ const SELF = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8')).name;
6
7
  const SKIP_DIRS = /(^|\/)(node_modules|dist|build|out|\.git|\.next|\.astro|\.svelte-kit|\.nuxt|coverage|argus)(\/|$)/;
7
8
  function list(cwd, dir) {
8
9
  try {
@@ -138,10 +139,12 @@ export async function init({ cwd = process.cwd(), remote, force = false, yes = f
138
139
  const text = fs.readFileSync(pkgPath, 'utf8');
139
140
  const pkg = JSON.parse(text);
140
141
  if (!pkg.scripts?.['argus']) {
141
- pkg.scripts = { ...pkg.scripts, 'argus': 'argus' };
142
+ // The `argus` bin only exists when the package is installed in the project.
143
+ const installed = SELF in { ...pkg.dependencies, ...pkg.devDependencies };
144
+ pkg.scripts = { ...pkg.scripts, argus: installed ? 'argus' : `npx ${SELF}` };
142
145
  const indent = text.match(/^[ \t]+(?=")/m)?.[0] ?? ' ';
143
146
  fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, indent)}\n`);
144
- log('added "argus" script to package.json');
147
+ log(`added "argus" script to package.json: ${pkg.scripts.argus}`);
145
148
  }
146
149
  }
147
150
  const gi = path.join(cwd, '.gitignore');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaze-no-ryuu/argus",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Argus: visual diff of your local site against a live site or design images. Auto-detects framework and dev server, desktop/tablet/mobile, pinpoints changed elements, HTML report + JSON for CI and AI agents.",
5
5
  "type": "module",
6
6
  "bin": {