@blazediff/bin 2.1.0 → 3.1.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 +33 -9
- package/dist/index.d.mts +13 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.js +193 -8
- package/dist/index.mjs +186 -15
- package/package.json +11 -13
- package/bin/blazediff.exe +0 -0
- package/binaries/blazediff-linux-arm64 +0 -0
- package/binaries/blazediff-linux-x64 +0 -0
- package/binaries/blazediff-macos-arm64 +0 -0
- package/binaries/blazediff-macos-x64 +0 -0
- package/binaries/blazediff-windows-arm64.exe +0 -0
- package/binaries/blazediff-windows-x64.exe +0 -0
- package/post_install.js +0 -40
package/README.md
CHANGED
|
@@ -10,12 +10,17 @@
|
|
|
10
10
|
The fastest single-threaded image diff in the world. Native Rust implementation with SIMD optimization, **3-4x faster** and **3x smaller** than [odiff](https://github.com/dmtrKovalenko/odiff).
|
|
11
11
|
|
|
12
12
|
**Features:**
|
|
13
|
+
- **PNG & JPEG support** - auto-detected by file extension
|
|
13
14
|
- SIMD-accelerated (NEON on ARM, SSE4.1 on x86)
|
|
14
15
|
- Block-based two-pass optimization
|
|
15
16
|
- YIQ perceptual color difference
|
|
16
17
|
- Anti-aliasing detection
|
|
17
18
|
- Cross-platform pre-built binaries (~700KB-900KB, no compilation required)
|
|
18
19
|
|
|
20
|
+
**Vendored Libraries:**
|
|
21
|
+
- [libspng](https://libspng.org/) - Fast PNG decoding/encoding with SIMD
|
|
22
|
+
- [libjpeg-turbo](https://libjpeg-turbo.org/) - High-performance JPEG codec with SIMD
|
|
23
|
+
|
|
19
24
|
## Installation
|
|
20
25
|
|
|
21
26
|
```bash
|
|
@@ -34,7 +39,7 @@ Pre-built [binaries](https://github.com/teimurjan/blazediff/tree/main/packages/b
|
|
|
34
39
|
|
|
35
40
|
### compare(basePath, comparePath, diffOutput, options?)
|
|
36
41
|
|
|
37
|
-
Compare two PNG
|
|
42
|
+
Compare two images (PNG or JPEG) and generate a diff image. Format is auto-detected from file extension.
|
|
38
43
|
|
|
39
44
|
<table>
|
|
40
45
|
<tr>
|
|
@@ -133,15 +138,24 @@ if (result.match) {
|
|
|
133
138
|
### CLI Usage
|
|
134
139
|
|
|
135
140
|
```bash
|
|
136
|
-
# Compare two images
|
|
141
|
+
# Compare two PNG images
|
|
137
142
|
npx blazediff expected.png actual.png diff.png
|
|
138
143
|
|
|
144
|
+
# Compare two JPEG images
|
|
145
|
+
npx blazediff expected.jpg actual.jpg diff.jpg
|
|
146
|
+
|
|
147
|
+
# Mixed formats (PNG input, JPEG output)
|
|
148
|
+
npx blazediff expected.png actual.png diff.jpg
|
|
149
|
+
|
|
139
150
|
# With options
|
|
140
151
|
npx blazediff expected.png actual.png diff.png --threshold 0.05 --antialiasing
|
|
141
152
|
|
|
142
|
-
# With higher compression (smaller output file, slower)
|
|
153
|
+
# With higher PNG compression (smaller output file, slower)
|
|
143
154
|
npx blazediff expected.png actual.png diff.png -c 6
|
|
144
155
|
|
|
156
|
+
# With JPEG quality setting
|
|
157
|
+
npx blazediff expected.jpg actual.jpg diff.jpg -q 85
|
|
158
|
+
|
|
145
159
|
# Output as JSON
|
|
146
160
|
npx blazediff expected.png actual.png diff.png --output-format json
|
|
147
161
|
```
|
|
@@ -149,24 +163,34 @@ npx blazediff expected.png actual.png diff.png --output-format json
|
|
|
149
163
|
### CLI Options
|
|
150
164
|
|
|
151
165
|
```
|
|
152
|
-
Usage: blazediff [OPTIONS] <IMAGE1> <IMAGE2>
|
|
166
|
+
Usage: blazediff [OPTIONS] <IMAGE1> <IMAGE2> [OUTPUT]
|
|
153
167
|
|
|
154
168
|
Arguments:
|
|
155
|
-
<IMAGE1> First image path
|
|
156
|
-
<IMAGE2> Second image path
|
|
157
|
-
|
|
169
|
+
<IMAGE1> First image path (PNG or JPEG)
|
|
170
|
+
<IMAGE2> Second image path (PNG or JPEG)
|
|
171
|
+
[OUTPUT] Output diff image path (optional, format detected from extension)
|
|
158
172
|
|
|
159
173
|
Options:
|
|
160
174
|
-t, --threshold <THRESHOLD> Color difference threshold (0.0-1.0) [default: 0.1]
|
|
161
175
|
-a, --antialiasing Enable anti-aliasing detection
|
|
162
176
|
--diff-mask Output only differences (transparent background)
|
|
163
177
|
--fail-on-layout Fail on layout (size) difference
|
|
164
|
-
-c, --compression <LEVEL> PNG compression level (0-9, 0=fastest
|
|
178
|
+
-c, --compression <LEVEL> PNG compression level (0-9, 0=fastest, 9=smallest) [default: 0]
|
|
179
|
+
-q, --quality <QUALITY> JPEG quality (1-100) [default: 90]
|
|
165
180
|
--output-format <FORMAT> Output format (json or text) [default: json]
|
|
166
181
|
-h, --help Print help
|
|
167
182
|
-V, --version Print version
|
|
168
183
|
```
|
|
169
184
|
|
|
185
|
+
### Supported Formats
|
|
186
|
+
|
|
187
|
+
| Format | Extensions | Notes |
|
|
188
|
+
|--------|------------|-------|
|
|
189
|
+
| PNG | `.png` | Lossless, supports transparency |
|
|
190
|
+
| JPEG | `.jpg`, `.jpeg` | Lossy, smaller file sizes |
|
|
191
|
+
|
|
192
|
+
Input images can be mixed formats (e.g., compare PNG to JPEG). Output format is determined by the output file extension.
|
|
193
|
+
|
|
170
194
|
### Exit Codes
|
|
171
195
|
|
|
172
196
|
- `0` - Images are identical
|
|
@@ -175,7 +199,7 @@ Options:
|
|
|
175
199
|
|
|
176
200
|
## Performance
|
|
177
201
|
|
|
178
|
-
Benchmarked on Apple M1 Pro with 5600x3200 4K images:
|
|
202
|
+
Benchmarked on Apple M1 Pro with 5600x3200 4K PNG images:
|
|
179
203
|
|
|
180
204
|
| Tool | Benchmark Time | vs blazediff |
|
|
181
205
|
|------|------|--------------|
|
package/dist/index.d.mts
CHANGED
|
@@ -9,6 +9,8 @@ interface BlazeDiffOptions {
|
|
|
9
9
|
failOnLayoutDiff?: boolean;
|
|
10
10
|
/** PNG compression level (0-9, 0=fastest/largest, 9=slowest/smallest) */
|
|
11
11
|
compression?: number;
|
|
12
|
+
/** JPEG quality (1-100). Default: 90 */
|
|
13
|
+
quality?: number;
|
|
12
14
|
}
|
|
13
15
|
type BlazeDiffResult = {
|
|
14
16
|
match: true;
|
|
@@ -26,7 +28,11 @@ type BlazeDiffResult = {
|
|
|
26
28
|
file: string;
|
|
27
29
|
};
|
|
28
30
|
/**
|
|
29
|
-
* Compare two PNG
|
|
31
|
+
* Compare two images (PNG or JPEG) and optionally generate a diff image.
|
|
32
|
+
*
|
|
33
|
+
* Uses native N-API bindings when available for ~10-100x better performance
|
|
34
|
+
* on small images (no process spawn overhead). Falls back to execFile if
|
|
35
|
+
* native bindings are unavailable.
|
|
30
36
|
*
|
|
31
37
|
* @example
|
|
32
38
|
* ```ts
|
|
@@ -46,5 +52,10 @@ type BlazeDiffResult = {
|
|
|
46
52
|
declare function compare(basePath: string, comparePath: string, diffOutput?: string, options?: BlazeDiffOptions): Promise<BlazeDiffResult>;
|
|
47
53
|
/** Get the path to the blazediff binary for direct CLI usage. */
|
|
48
54
|
declare function getBinaryPath(): string;
|
|
55
|
+
/**
|
|
56
|
+
* Check if native N-API bindings are available.
|
|
57
|
+
* Returns true if the native module loaded successfully.
|
|
58
|
+
*/
|
|
59
|
+
declare function hasNativeBinding(): boolean;
|
|
49
60
|
|
|
50
|
-
export { type BlazeDiffOptions, type BlazeDiffResult, compare, getBinaryPath };
|
|
61
|
+
export { type BlazeDiffOptions, type BlazeDiffResult, compare, getBinaryPath, hasNativeBinding };
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ interface BlazeDiffOptions {
|
|
|
9
9
|
failOnLayoutDiff?: boolean;
|
|
10
10
|
/** PNG compression level (0-9, 0=fastest/largest, 9=slowest/smallest) */
|
|
11
11
|
compression?: number;
|
|
12
|
+
/** JPEG quality (1-100). Default: 90 */
|
|
13
|
+
quality?: number;
|
|
12
14
|
}
|
|
13
15
|
type BlazeDiffResult = {
|
|
14
16
|
match: true;
|
|
@@ -26,7 +28,11 @@ type BlazeDiffResult = {
|
|
|
26
28
|
file: string;
|
|
27
29
|
};
|
|
28
30
|
/**
|
|
29
|
-
* Compare two PNG
|
|
31
|
+
* Compare two images (PNG or JPEG) and optionally generate a diff image.
|
|
32
|
+
*
|
|
33
|
+
* Uses native N-API bindings when available for ~10-100x better performance
|
|
34
|
+
* on small images (no process spawn overhead). Falls back to execFile if
|
|
35
|
+
* native bindings are unavailable.
|
|
30
36
|
*
|
|
31
37
|
* @example
|
|
32
38
|
* ```ts
|
|
@@ -46,5 +52,10 @@ type BlazeDiffResult = {
|
|
|
46
52
|
declare function compare(basePath: string, comparePath: string, diffOutput?: string, options?: BlazeDiffOptions): Promise<BlazeDiffResult>;
|
|
47
53
|
/** Get the path to the blazediff binary for direct CLI usage. */
|
|
48
54
|
declare function getBinaryPath(): string;
|
|
55
|
+
/**
|
|
56
|
+
* Check if native N-API bindings are available.
|
|
57
|
+
* Returns true if the native module loaded successfully.
|
|
58
|
+
*/
|
|
59
|
+
declare function hasNativeBinding(): boolean;
|
|
49
60
|
|
|
50
|
-
export { type BlazeDiffOptions, type BlazeDiffResult, compare, getBinaryPath };
|
|
61
|
+
export { type BlazeDiffOptions, type BlazeDiffResult, compare, getBinaryPath, hasNativeBinding };
|
package/dist/index.js
CHANGED
|
@@ -31,24 +31,182 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
compare: () => compare,
|
|
34
|
-
getBinaryPath: () => getBinaryPath
|
|
34
|
+
getBinaryPath: () => getBinaryPath,
|
|
35
|
+
hasNativeBinding: () => hasNativeBinding
|
|
35
36
|
});
|
|
36
37
|
module.exports = __toCommonJS(index_exports);
|
|
38
|
+
|
|
39
|
+
// ../../node_modules/.pnpm/tsup@8.5.0_jiti@2.6.0_postcss@8.5.6_tsx@4.20.6_typescript@5.9.2_yaml@2.8.1/node_modules/tsup/assets/cjs_shims.js
|
|
40
|
+
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
|
|
41
|
+
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
42
|
+
|
|
43
|
+
// src/index.ts
|
|
37
44
|
var import_node_child_process = require("child_process");
|
|
45
|
+
var import_node_fs = require("fs");
|
|
46
|
+
var import_node_module = require("module");
|
|
47
|
+
var import_node_os = __toESM(require("os"));
|
|
38
48
|
var import_node_path = __toESM(require("path"));
|
|
49
|
+
var import_node_url = require("url");
|
|
39
50
|
var import_node_util = require("util");
|
|
40
51
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
41
|
-
var
|
|
52
|
+
var PLATFORM_PACKAGES = {
|
|
53
|
+
"darwin-arm64": {
|
|
54
|
+
packageName: "@blazediff/bin-darwin-arm64",
|
|
55
|
+
packageDir: "bin-darwin-arm64"
|
|
56
|
+
},
|
|
57
|
+
"darwin-x64": {
|
|
58
|
+
packageName: "@blazediff/bin-darwin-x64",
|
|
59
|
+
packageDir: "bin-darwin-x64"
|
|
60
|
+
},
|
|
61
|
+
"linux-arm64": {
|
|
62
|
+
packageName: "@blazediff/bin-linux-arm64",
|
|
63
|
+
packageDir: "bin-linux-arm64"
|
|
64
|
+
},
|
|
65
|
+
"linux-x64": {
|
|
66
|
+
packageName: "@blazediff/bin-linux-x64",
|
|
67
|
+
packageDir: "bin-linux-x64"
|
|
68
|
+
},
|
|
69
|
+
"win32-arm64": {
|
|
70
|
+
packageName: "@blazediff/bin-win32-arm64",
|
|
71
|
+
packageDir: "bin-win32-arm64"
|
|
72
|
+
},
|
|
73
|
+
"win32-x64": {
|
|
74
|
+
packageName: "@blazediff/bin-win32-x64",
|
|
75
|
+
packageDir: "bin-win32-x64"
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
var nativeBinding = null;
|
|
79
|
+
var nativeBindingAttempted = false;
|
|
80
|
+
function tryLoadNativeBinding() {
|
|
81
|
+
if (nativeBindingAttempted) {
|
|
82
|
+
return nativeBinding;
|
|
83
|
+
}
|
|
84
|
+
nativeBindingAttempted = true;
|
|
85
|
+
const platform = import_node_os.default.platform();
|
|
86
|
+
const arch = import_node_os.default.arch();
|
|
87
|
+
const key = `${platform}-${arch}`;
|
|
88
|
+
const platformInfo = PLATFORM_PACKAGES[key];
|
|
89
|
+
if (!platformInfo) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const require2 = (0, import_node_module.createRequire)(importMetaUrl);
|
|
94
|
+
const binding = require2(platformInfo.packageName);
|
|
95
|
+
if (typeof binding?.compare === "function") {
|
|
96
|
+
nativeBinding = binding;
|
|
97
|
+
return binding;
|
|
98
|
+
}
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const currentDir = import_node_path.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
103
|
+
const packagesDir = import_node_path.default.resolve(currentDir, "..", "..");
|
|
104
|
+
const nodePath = import_node_path.default.join(
|
|
105
|
+
packagesDir,
|
|
106
|
+
platformInfo.packageDir,
|
|
107
|
+
"blazediff.node"
|
|
108
|
+
);
|
|
109
|
+
if ((0, import_node_fs.existsSync)(nodePath)) {
|
|
110
|
+
const require2 = (0, import_node_module.createRequire)(importMetaUrl);
|
|
111
|
+
const binding = require2(nodePath);
|
|
112
|
+
if (typeof binding?.compare === "function") {
|
|
113
|
+
nativeBinding = binding;
|
|
114
|
+
return binding;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} catch {
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
function convertNapiResult(result) {
|
|
122
|
+
if (result.matchResult) {
|
|
123
|
+
return { match: true };
|
|
124
|
+
}
|
|
125
|
+
if (result.reason === "layout-diff") {
|
|
126
|
+
return { match: false, reason: "layout-diff" };
|
|
127
|
+
}
|
|
128
|
+
if (result.reason === "pixel-diff") {
|
|
129
|
+
return {
|
|
130
|
+
match: false,
|
|
131
|
+
reason: "pixel-diff",
|
|
132
|
+
diffCount: result.diffCount ?? 0,
|
|
133
|
+
diffPercentage: result.diffPercentage ?? 0
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
match: false,
|
|
138
|
+
reason: "pixel-diff",
|
|
139
|
+
diffCount: result.diffCount ?? 0,
|
|
140
|
+
diffPercentage: result.diffPercentage ?? 0
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function convertToNapiOptions(options) {
|
|
144
|
+
return {
|
|
145
|
+
threshold: options?.threshold,
|
|
146
|
+
antialiasing: options?.antialiasing,
|
|
147
|
+
diffMask: options?.diffMask,
|
|
148
|
+
failOnLayout: options?.failOnLayoutDiff,
|
|
149
|
+
compression: options?.compression,
|
|
150
|
+
quality: options?.quality
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function resolveBinaryPath() {
|
|
154
|
+
const platform = import_node_os.default.platform();
|
|
155
|
+
const arch = import_node_os.default.arch();
|
|
156
|
+
const key = `${platform}-${arch}`;
|
|
157
|
+
const platformInfo = PLATFORM_PACKAGES[key];
|
|
158
|
+
if (!platformInfo) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`Unsupported platform: ${key}. Supported: ${Object.keys(PLATFORM_PACKAGES).join(", ")}`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
const binaryName = platform === "win32" ? "blazediff.exe" : "blazediff";
|
|
164
|
+
try {
|
|
165
|
+
const require2 = (0, import_node_module.createRequire)(importMetaUrl);
|
|
166
|
+
const packagePath = require2.resolve(
|
|
167
|
+
`${platformInfo.packageName}/package.json`
|
|
168
|
+
);
|
|
169
|
+
const packageDir = import_node_path.default.dirname(packagePath);
|
|
170
|
+
const binaryPath = import_node_path.default.join(packageDir, binaryName);
|
|
171
|
+
if ((0, import_node_fs.existsSync)(binaryPath)) {
|
|
172
|
+
return binaryPath;
|
|
173
|
+
}
|
|
174
|
+
} catch {
|
|
175
|
+
}
|
|
176
|
+
const currentDir = import_node_path.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
177
|
+
const packagesDir = import_node_path.default.resolve(currentDir, "..", "..");
|
|
178
|
+
const siblingPath = import_node_path.default.join(
|
|
179
|
+
packagesDir,
|
|
180
|
+
platformInfo.packageDir,
|
|
181
|
+
binaryName
|
|
182
|
+
);
|
|
183
|
+
if ((0, import_node_fs.existsSync)(siblingPath)) {
|
|
184
|
+
return siblingPath;
|
|
185
|
+
}
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Platform package ${platformInfo.packageName} is not installed. This usually means the optional dependency wasn't installed for your platform. Try reinstalling with: npm install @blazediff/bin`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
var cachedBinaryPath = null;
|
|
191
|
+
function getBinaryPathInternal() {
|
|
192
|
+
if (!cachedBinaryPath) {
|
|
193
|
+
cachedBinaryPath = resolveBinaryPath();
|
|
194
|
+
}
|
|
195
|
+
return cachedBinaryPath;
|
|
196
|
+
}
|
|
42
197
|
function buildArgs(diffOutput, options) {
|
|
43
198
|
const args = [];
|
|
44
199
|
if (diffOutput) args.push(diffOutput);
|
|
45
200
|
args.push("--output-format=json");
|
|
46
201
|
if (!options) return args;
|
|
47
|
-
if (options.threshold !== void 0)
|
|
202
|
+
if (options.threshold !== void 0)
|
|
203
|
+
args.push(`--threshold=${options.threshold}`);
|
|
48
204
|
if (options.antialiasing) args.push("--antialiasing");
|
|
49
205
|
if (options.diffMask) args.push("--diff-mask");
|
|
50
206
|
if (options.failOnLayoutDiff) args.push("--fail-on-layout");
|
|
51
|
-
if (options.compression !== void 0)
|
|
207
|
+
if (options.compression !== void 0)
|
|
208
|
+
args.push(`--compression=${options.compression}`);
|
|
209
|
+
if (options.quality !== void 0) args.push(`--quality=${options.quality}`);
|
|
52
210
|
return args;
|
|
53
211
|
}
|
|
54
212
|
function parseJsonOutput(text) {
|
|
@@ -66,10 +224,11 @@ function detectMissingFile(error, basePath, comparePath) {
|
|
|
66
224
|
if (error.includes(comparePath)) return comparePath;
|
|
67
225
|
return basePath;
|
|
68
226
|
}
|
|
69
|
-
async function
|
|
227
|
+
async function execFileCompare(basePath, comparePath, diffOutput, options) {
|
|
228
|
+
const binaryPath = getBinaryPathInternal();
|
|
70
229
|
const args = [basePath, comparePath, ...buildArgs(diffOutput, options)];
|
|
71
230
|
try {
|
|
72
|
-
await execFileAsync(
|
|
231
|
+
await execFileAsync(binaryPath, args);
|
|
73
232
|
return { match: true };
|
|
74
233
|
} catch (err) {
|
|
75
234
|
const { code, stdout, stderr } = err;
|
|
@@ -100,11 +259,37 @@ async function compare(basePath, comparePath, diffOutput, options) {
|
|
|
100
259
|
throw new Error(output || `blazediff exited with code ${code}`);
|
|
101
260
|
}
|
|
102
261
|
}
|
|
262
|
+
async function compare(basePath, comparePath, diffOutput, options) {
|
|
263
|
+
const binding = tryLoadNativeBinding();
|
|
264
|
+
if (binding) {
|
|
265
|
+
try {
|
|
266
|
+
const result = binding.compare(
|
|
267
|
+
basePath,
|
|
268
|
+
comparePath,
|
|
269
|
+
diffOutput ?? null,
|
|
270
|
+
convertToNapiOptions(options)
|
|
271
|
+
);
|
|
272
|
+
return convertNapiResult(result);
|
|
273
|
+
} catch (err) {
|
|
274
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
275
|
+
const missingFile = detectMissingFile(message, basePath, comparePath);
|
|
276
|
+
if (missingFile) {
|
|
277
|
+
return { match: false, reason: "file-not-exists", file: missingFile };
|
|
278
|
+
}
|
|
279
|
+
throw err;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return execFileCompare(basePath, comparePath, diffOutput, options);
|
|
283
|
+
}
|
|
103
284
|
function getBinaryPath() {
|
|
104
|
-
return
|
|
285
|
+
return getBinaryPathInternal();
|
|
286
|
+
}
|
|
287
|
+
function hasNativeBinding() {
|
|
288
|
+
return tryLoadNativeBinding() !== null;
|
|
105
289
|
}
|
|
106
290
|
// Annotate the CommonJS export names for ESM import in node:
|
|
107
291
|
0 && (module.exports = {
|
|
108
292
|
compare,
|
|
109
|
-
getBinaryPath
|
|
293
|
+
getBinaryPath,
|
|
294
|
+
hasNativeBinding
|
|
110
295
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -1,26 +1,170 @@
|
|
|
1
|
-
// ../../node_modules/.pnpm/tsup@8.5.0_jiti@2.6.0_postcss@8.5.6_tsx@4.20.6_typescript@5.9.2_yaml@2.8.1/node_modules/tsup/assets/esm_shims.js
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { fileURLToPath } from "url";
|
|
4
|
-
var getFilename = () => fileURLToPath(import.meta.url);
|
|
5
|
-
var getDirname = () => path.dirname(getFilename());
|
|
6
|
-
var __dirname = /* @__PURE__ */ getDirname();
|
|
7
|
-
|
|
8
1
|
// src/index.ts
|
|
9
2
|
import { execFile } from "child_process";
|
|
10
|
-
import
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
import os from "os";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
11
8
|
import { promisify } from "util";
|
|
12
9
|
var execFileAsync = promisify(execFile);
|
|
13
|
-
var
|
|
10
|
+
var PLATFORM_PACKAGES = {
|
|
11
|
+
"darwin-arm64": {
|
|
12
|
+
packageName: "@blazediff/bin-darwin-arm64",
|
|
13
|
+
packageDir: "bin-darwin-arm64"
|
|
14
|
+
},
|
|
15
|
+
"darwin-x64": {
|
|
16
|
+
packageName: "@blazediff/bin-darwin-x64",
|
|
17
|
+
packageDir: "bin-darwin-x64"
|
|
18
|
+
},
|
|
19
|
+
"linux-arm64": {
|
|
20
|
+
packageName: "@blazediff/bin-linux-arm64",
|
|
21
|
+
packageDir: "bin-linux-arm64"
|
|
22
|
+
},
|
|
23
|
+
"linux-x64": {
|
|
24
|
+
packageName: "@blazediff/bin-linux-x64",
|
|
25
|
+
packageDir: "bin-linux-x64"
|
|
26
|
+
},
|
|
27
|
+
"win32-arm64": {
|
|
28
|
+
packageName: "@blazediff/bin-win32-arm64",
|
|
29
|
+
packageDir: "bin-win32-arm64"
|
|
30
|
+
},
|
|
31
|
+
"win32-x64": {
|
|
32
|
+
packageName: "@blazediff/bin-win32-x64",
|
|
33
|
+
packageDir: "bin-win32-x64"
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var nativeBinding = null;
|
|
37
|
+
var nativeBindingAttempted = false;
|
|
38
|
+
function tryLoadNativeBinding() {
|
|
39
|
+
if (nativeBindingAttempted) {
|
|
40
|
+
return nativeBinding;
|
|
41
|
+
}
|
|
42
|
+
nativeBindingAttempted = true;
|
|
43
|
+
const platform = os.platform();
|
|
44
|
+
const arch = os.arch();
|
|
45
|
+
const key = `${platform}-${arch}`;
|
|
46
|
+
const platformInfo = PLATFORM_PACKAGES[key];
|
|
47
|
+
if (!platformInfo) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const require2 = createRequire(import.meta.url);
|
|
52
|
+
const binding = require2(platformInfo.packageName);
|
|
53
|
+
if (typeof binding?.compare === "function") {
|
|
54
|
+
nativeBinding = binding;
|
|
55
|
+
return binding;
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
61
|
+
const packagesDir = path.resolve(currentDir, "..", "..");
|
|
62
|
+
const nodePath = path.join(
|
|
63
|
+
packagesDir,
|
|
64
|
+
platformInfo.packageDir,
|
|
65
|
+
"blazediff.node"
|
|
66
|
+
);
|
|
67
|
+
if (existsSync(nodePath)) {
|
|
68
|
+
const require2 = createRequire(import.meta.url);
|
|
69
|
+
const binding = require2(nodePath);
|
|
70
|
+
if (typeof binding?.compare === "function") {
|
|
71
|
+
nativeBinding = binding;
|
|
72
|
+
return binding;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function convertNapiResult(result) {
|
|
80
|
+
if (result.matchResult) {
|
|
81
|
+
return { match: true };
|
|
82
|
+
}
|
|
83
|
+
if (result.reason === "layout-diff") {
|
|
84
|
+
return { match: false, reason: "layout-diff" };
|
|
85
|
+
}
|
|
86
|
+
if (result.reason === "pixel-diff") {
|
|
87
|
+
return {
|
|
88
|
+
match: false,
|
|
89
|
+
reason: "pixel-diff",
|
|
90
|
+
diffCount: result.diffCount ?? 0,
|
|
91
|
+
diffPercentage: result.diffPercentage ?? 0
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
match: false,
|
|
96
|
+
reason: "pixel-diff",
|
|
97
|
+
diffCount: result.diffCount ?? 0,
|
|
98
|
+
diffPercentage: result.diffPercentage ?? 0
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function convertToNapiOptions(options) {
|
|
102
|
+
return {
|
|
103
|
+
threshold: options?.threshold,
|
|
104
|
+
antialiasing: options?.antialiasing,
|
|
105
|
+
diffMask: options?.diffMask,
|
|
106
|
+
failOnLayout: options?.failOnLayoutDiff,
|
|
107
|
+
compression: options?.compression,
|
|
108
|
+
quality: options?.quality
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function resolveBinaryPath() {
|
|
112
|
+
const platform = os.platform();
|
|
113
|
+
const arch = os.arch();
|
|
114
|
+
const key = `${platform}-${arch}`;
|
|
115
|
+
const platformInfo = PLATFORM_PACKAGES[key];
|
|
116
|
+
if (!platformInfo) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`Unsupported platform: ${key}. Supported: ${Object.keys(PLATFORM_PACKAGES).join(", ")}`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
const binaryName = platform === "win32" ? "blazediff.exe" : "blazediff";
|
|
122
|
+
try {
|
|
123
|
+
const require2 = createRequire(import.meta.url);
|
|
124
|
+
const packagePath = require2.resolve(
|
|
125
|
+
`${platformInfo.packageName}/package.json`
|
|
126
|
+
);
|
|
127
|
+
const packageDir = path.dirname(packagePath);
|
|
128
|
+
const binaryPath = path.join(packageDir, binaryName);
|
|
129
|
+
if (existsSync(binaryPath)) {
|
|
130
|
+
return binaryPath;
|
|
131
|
+
}
|
|
132
|
+
} catch {
|
|
133
|
+
}
|
|
134
|
+
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
135
|
+
const packagesDir = path.resolve(currentDir, "..", "..");
|
|
136
|
+
const siblingPath = path.join(
|
|
137
|
+
packagesDir,
|
|
138
|
+
platformInfo.packageDir,
|
|
139
|
+
binaryName
|
|
140
|
+
);
|
|
141
|
+
if (existsSync(siblingPath)) {
|
|
142
|
+
return siblingPath;
|
|
143
|
+
}
|
|
144
|
+
throw new Error(
|
|
145
|
+
`Platform package ${platformInfo.packageName} is not installed. This usually means the optional dependency wasn't installed for your platform. Try reinstalling with: npm install @blazediff/bin`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
var cachedBinaryPath = null;
|
|
149
|
+
function getBinaryPathInternal() {
|
|
150
|
+
if (!cachedBinaryPath) {
|
|
151
|
+
cachedBinaryPath = resolveBinaryPath();
|
|
152
|
+
}
|
|
153
|
+
return cachedBinaryPath;
|
|
154
|
+
}
|
|
14
155
|
function buildArgs(diffOutput, options) {
|
|
15
156
|
const args = [];
|
|
16
157
|
if (diffOutput) args.push(diffOutput);
|
|
17
158
|
args.push("--output-format=json");
|
|
18
159
|
if (!options) return args;
|
|
19
|
-
if (options.threshold !== void 0)
|
|
160
|
+
if (options.threshold !== void 0)
|
|
161
|
+
args.push(`--threshold=${options.threshold}`);
|
|
20
162
|
if (options.antialiasing) args.push("--antialiasing");
|
|
21
163
|
if (options.diffMask) args.push("--diff-mask");
|
|
22
164
|
if (options.failOnLayoutDiff) args.push("--fail-on-layout");
|
|
23
|
-
if (options.compression !== void 0)
|
|
165
|
+
if (options.compression !== void 0)
|
|
166
|
+
args.push(`--compression=${options.compression}`);
|
|
167
|
+
if (options.quality !== void 0) args.push(`--quality=${options.quality}`);
|
|
24
168
|
return args;
|
|
25
169
|
}
|
|
26
170
|
function parseJsonOutput(text) {
|
|
@@ -38,10 +182,11 @@ function detectMissingFile(error, basePath, comparePath) {
|
|
|
38
182
|
if (error.includes(comparePath)) return comparePath;
|
|
39
183
|
return basePath;
|
|
40
184
|
}
|
|
41
|
-
async function
|
|
185
|
+
async function execFileCompare(basePath, comparePath, diffOutput, options) {
|
|
186
|
+
const binaryPath = getBinaryPathInternal();
|
|
42
187
|
const args = [basePath, comparePath, ...buildArgs(diffOutput, options)];
|
|
43
188
|
try {
|
|
44
|
-
await execFileAsync(
|
|
189
|
+
await execFileAsync(binaryPath, args);
|
|
45
190
|
return { match: true };
|
|
46
191
|
} catch (err) {
|
|
47
192
|
const { code, stdout, stderr } = err;
|
|
@@ -72,10 +217,36 @@ async function compare(basePath, comparePath, diffOutput, options) {
|
|
|
72
217
|
throw new Error(output || `blazediff exited with code ${code}`);
|
|
73
218
|
}
|
|
74
219
|
}
|
|
220
|
+
async function compare(basePath, comparePath, diffOutput, options) {
|
|
221
|
+
const binding = tryLoadNativeBinding();
|
|
222
|
+
if (binding) {
|
|
223
|
+
try {
|
|
224
|
+
const result = binding.compare(
|
|
225
|
+
basePath,
|
|
226
|
+
comparePath,
|
|
227
|
+
diffOutput ?? null,
|
|
228
|
+
convertToNapiOptions(options)
|
|
229
|
+
);
|
|
230
|
+
return convertNapiResult(result);
|
|
231
|
+
} catch (err) {
|
|
232
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
233
|
+
const missingFile = detectMissingFile(message, basePath, comparePath);
|
|
234
|
+
if (missingFile) {
|
|
235
|
+
return { match: false, reason: "file-not-exists", file: missingFile };
|
|
236
|
+
}
|
|
237
|
+
throw err;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return execFileCompare(basePath, comparePath, diffOutput, options);
|
|
241
|
+
}
|
|
75
242
|
function getBinaryPath() {
|
|
76
|
-
return
|
|
243
|
+
return getBinaryPathInternal();
|
|
244
|
+
}
|
|
245
|
+
function hasNativeBinding() {
|
|
246
|
+
return tryLoadNativeBinding() !== null;
|
|
77
247
|
}
|
|
78
248
|
export {
|
|
79
249
|
compare,
|
|
80
|
-
getBinaryPath
|
|
250
|
+
getBinaryPath,
|
|
251
|
+
hasNativeBinding
|
|
81
252
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blazediff/bin",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Native Rust binaries for blazediff - the fastest image diff in the world",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -16,15 +16,17 @@
|
|
|
16
16
|
"require": "./dist/index.js"
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
|
-
"bin": {
|
|
20
|
-
"blazediff": "bin/blazediff.exe"
|
|
21
|
-
},
|
|
22
19
|
"files": [
|
|
23
|
-
"dist"
|
|
24
|
-
"bin",
|
|
25
|
-
"binaries",
|
|
26
|
-
"post_install.js"
|
|
20
|
+
"dist"
|
|
27
21
|
],
|
|
22
|
+
"optionalDependencies": {
|
|
23
|
+
"@blazediff/bin-darwin-arm64": "3.1.0",
|
|
24
|
+
"@blazediff/bin-darwin-x64": "3.1.0",
|
|
25
|
+
"@blazediff/bin-linux-arm64": "3.1.0",
|
|
26
|
+
"@blazediff/bin-linux-x64": "3.1.0",
|
|
27
|
+
"@blazediff/bin-win32-arm64": "3.1.0",
|
|
28
|
+
"@blazediff/bin-win32-x64": "3.1.0"
|
|
29
|
+
},
|
|
28
30
|
"keywords": [
|
|
29
31
|
"image",
|
|
30
32
|
"comparison",
|
|
@@ -48,11 +50,7 @@
|
|
|
48
50
|
"typescript": "5.9.2"
|
|
49
51
|
},
|
|
50
52
|
"scripts": {
|
|
51
|
-
"postinstall": "node ./post_install.js",
|
|
52
53
|
"build": "tsup",
|
|
53
|
-
"
|
|
54
|
-
"build:rust:local": "cd rust && cargo build --release && cp target/release/blazediff ../binaries/blazediff-$(node -e \"console.log(process.platform === 'darwin' ? 'macos' : process.platform)-$(process.arch === 'arm64' ? 'arm64' : 'x64')\")",
|
|
55
|
-
"prepare-binaries": "node ./scripts/prepare-binaries.js",
|
|
56
|
-
"clean": "rm -rf dist bin"
|
|
54
|
+
"clean": "rm -rf dist"
|
|
57
55
|
}
|
|
58
56
|
}
|
package/bin/blazediff.exe
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/post_install.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
const os = require("os");
|
|
4
|
-
|
|
5
|
-
const binaries = {
|
|
6
|
-
"linux-x64": "blazediff-linux-x64",
|
|
7
|
-
"linux-arm64": "blazediff-linux-arm64",
|
|
8
|
-
"darwin-arm64": "blazediff-macos-arm64",
|
|
9
|
-
"darwin-x64": "blazediff-macos-x64",
|
|
10
|
-
"win32-x64": "blazediff-windows-x64.exe",
|
|
11
|
-
"win32-arm64": "blazediff-windows-arm64.exe",
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const platform = os.platform();
|
|
15
|
-
const arch = os.arch();
|
|
16
|
-
|
|
17
|
-
const binaryKey = `${platform}-${arch}`;
|
|
18
|
-
const binaryFile = binaries[binaryKey];
|
|
19
|
-
|
|
20
|
-
if (!binaryFile) {
|
|
21
|
-
console.error(
|
|
22
|
-
`blazediff: Sorry your platform or architecture is not supported. Supported: ${Object.keys(binaries).join(", ")}`,
|
|
23
|
-
);
|
|
24
|
-
process.exit(1);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const sourcePath = path.join(__dirname, "binaries", binaryFile);
|
|
28
|
-
const binDir = path.join(__dirname, "bin");
|
|
29
|
-
const destPath = path.join(binDir, "blazediff.exe");
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
if (!fs.existsSync(binDir)) {
|
|
33
|
-
fs.mkdirSync(binDir, { recursive: true });
|
|
34
|
-
}
|
|
35
|
-
fs.copyFileSync(sourcePath, destPath);
|
|
36
|
-
fs.chmodSync(destPath, 0o755);
|
|
37
|
-
} catch (err) {
|
|
38
|
-
console.error(`blazediff: failed to copy and link the binary file: ${err}`);
|
|
39
|
-
process.exit(1);
|
|
40
|
-
}
|