git-hash-art 0.7.0 → 0.9.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/ALGORITHM.md +323 -270
- package/CHANGELOG.md +18 -0
- package/bin/cli.js +17 -14
- package/bin/generateExamples.js +6 -14
- package/bin/generateVersionComparison.js +353 -0
- package/dist/browser.js +2398 -225
- package/dist/browser.js.map +1 -1
- package/dist/main.js +2398 -225
- package/dist/main.js.map +1 -1
- package/dist/module.js +2398 -225
- package/dist/module.js.map +1 -1
- package/package.json +2 -1
- package/src/lib/archetypes.ts +119 -0
- package/src/lib/canvas/colors.ts +110 -2
- package/src/lib/canvas/draw.ts +359 -9
- package/src/lib/canvas/shapes/affinity.ts +624 -0
- package/src/lib/canvas/shapes/procedural.ts +395 -32
- package/src/lib/render.ts +531 -155
package/CHANGELOG.md
CHANGED
|
@@ -4,11 +4,29 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
#### [0.9.0](https://github.com/gfargo/git-hash-art/compare/0.8.0...0.9.0)
|
|
8
|
+
|
|
9
|
+
- feat: new archetypes, depth-of-field, texture fills, constellations, background patterns [`#16`](https://github.com/gfargo/git-hash-art/pull/16)
|
|
10
|
+
- docs: update ALGORITHM.md with visual quality improvements [`#15`](https://github.com/gfargo/git-hash-art/pull/15)
|
|
11
|
+
- feat: add 4 archetypes, depth-of-field, texture fills, constellations, background patterns [`7f9b0fc`](https://github.com/gfargo/git-hash-art/commit/7f9b0fcbc4a17272dd6a62371e037fb0555c830b)
|
|
12
|
+
- add version comparison script [`9301176`](https://github.com/gfargo/git-hash-art/commit/9301176b4f1fe0af85d5d7f85e58db9be10ec382)
|
|
13
|
+
|
|
14
|
+
#### [0.8.0](https://github.com/gfargo/git-hash-art/compare/0.7.0...0.8.0)
|
|
15
|
+
|
|
16
|
+
> 19 March 2026
|
|
17
|
+
|
|
18
|
+
- feat: major visual quality improvements [`#14`](https://github.com/gfargo/git-hash-art/pull/14)
|
|
19
|
+
- fix: convert generateExamples.js to CJS require() syntax [`0662e61`](https://github.com/gfargo/git-hash-art/commit/0662e61ef82ef879b1e31929e640a797ca3278bd)
|
|
20
|
+
- chore: release v0.8.0 [`5f6f5e2`](https://github.com/gfargo/git-hash-art/commit/5f6f5e245a156bebc34003464c743eec4e456a74)
|
|
21
|
+
|
|
7
22
|
#### [0.7.0](https://github.com/gfargo/git-hash-art/compare/0.6.0...0.7.0)
|
|
8
23
|
|
|
24
|
+
> 19 March 2026
|
|
25
|
+
|
|
9
26
|
- feat: procedural shape generators and contrast enforcement [`#13`](https://github.com/gfargo/git-hash-art/pull/13)
|
|
10
27
|
- feat: procedural shapes and contrast enforcement [`4f018ab`](https://github.com/gfargo/git-hash-art/commit/4f018abb4c89849954ccc941ee7e9ab468231f78)
|
|
11
28
|
- docs: update ALGORITHM.md with procedural shapes and contrast enforcement [`3e044fd`](https://github.com/gfargo/git-hash-art/commit/3e044fd940fbc0f62e3a9696fa815145cf315f67)
|
|
29
|
+
- chore: release v0.7.0 [`1c0ad5e`](https://github.com/gfargo/git-hash-art/commit/1c0ad5e112c2c5547e4f83c7808e09ebb0db5e40)
|
|
12
30
|
|
|
13
31
|
#### [0.6.0](https://github.com/gfargo/git-hash-art/compare/0.5.0...0.6.0)
|
|
14
32
|
|
package/bin/cli.js
CHANGED
|
@@ -28,18 +28,18 @@ Examples:
|
|
|
28
28
|
|
|
29
29
|
function parseArgs(argv) {
|
|
30
30
|
const args = argv.slice(2);
|
|
31
|
-
const
|
|
32
|
-
const parsed = { command, hash: null, options: {} };
|
|
33
|
-
|
|
34
|
-
let i = 1;
|
|
35
|
-
if (command === "generate") {
|
|
36
|
-
parsed.hash = args[1];
|
|
37
|
-
i = 2;
|
|
38
|
-
}
|
|
31
|
+
const parsed = { command: null, hash: null, options: {} };
|
|
39
32
|
|
|
33
|
+
let i = 0;
|
|
40
34
|
while (i < args.length) {
|
|
41
35
|
const arg = args[i];
|
|
42
|
-
if (arg === "--
|
|
36
|
+
if (arg === "--help" || arg === "-h") {
|
|
37
|
+
parsed.command = "help";
|
|
38
|
+
i += 1;
|
|
39
|
+
} else if (arg === "--list-presets") {
|
|
40
|
+
parsed.command = "list-presets";
|
|
41
|
+
i += 1;
|
|
42
|
+
} else if (arg === "--width" && args[i + 1]) {
|
|
43
43
|
parsed.options.width = parseInt(args[i + 1], 10);
|
|
44
44
|
i += 2;
|
|
45
45
|
} else if (arg === "--height" && args[i + 1]) {
|
|
@@ -57,11 +57,14 @@ function parseArgs(argv) {
|
|
|
57
57
|
} else if (arg === "--grid" && args[i + 1]) {
|
|
58
58
|
parsed.options.gridSize = parseInt(args[i + 1], 10);
|
|
59
59
|
i += 2;
|
|
60
|
-
} else if (arg
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
} else if (!arg.startsWith("-") && !parsed.command) {
|
|
61
|
+
// First positional arg is the command
|
|
62
|
+
parsed.command = arg;
|
|
63
|
+
if (parsed.command === "generate" && args[i + 1] && !args[i + 1].startsWith("-")) {
|
|
64
|
+
parsed.hash = args[i + 1];
|
|
65
|
+
i += 2;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
65
68
|
i += 1;
|
|
66
69
|
} else {
|
|
67
70
|
i += 1;
|
package/bin/generateExamples.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { generateImageFromHash, saveImageToFile } = require('../dist/main.js');
|
|
5
|
+
const { PRESETS } = require('../dist/main.js');
|
|
5
6
|
|
|
6
7
|
const OUTPUT_DIR = './examples';
|
|
7
8
|
|
|
@@ -21,7 +22,7 @@ function generateTestCases() {
|
|
|
21
22
|
const imageBuffer = generateImageFromHash(testCase.hash, {
|
|
22
23
|
width: testCase.width,
|
|
23
24
|
height: testCase.height,
|
|
24
|
-
...testCase,
|
|
25
|
+
...testCase,
|
|
25
26
|
});
|
|
26
27
|
const outputPath = saveImageToFile(imageBuffer, OUTPUT_DIR, testCase.hash, label, testCase.width, testCase.height);
|
|
27
28
|
results.push({ label, hash: testCase.hash, outputPath, success: true });
|
|
@@ -47,13 +48,4 @@ function generateTestCases() {
|
|
|
47
48
|
});
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
// Run the test cases
|
|
51
51
|
generateTestCases();
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
// const gitHash = '1234567890abcdef1234567890abcdef12345678';
|
|
56
|
-
// const imageBuffer = generateImageFromHash(gitHash, { width: 1024, height: 1024 });
|
|
57
|
-
// const savedImagePath = saveImageToFile(imageBuffer, './output', gitHash, 'example', 1024, 1024);
|
|
58
|
-
// console.log(`Image saved to: ${savedImagePath}`);
|
|
59
|
-
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* generateVersionComparison.js
|
|
5
|
+
*
|
|
6
|
+
* Generates the same set of test images across every published version of
|
|
7
|
+
* git-hash-art, storing each version's output in its own directory.
|
|
8
|
+
*
|
|
9
|
+
* Output structure:
|
|
10
|
+
* examples/versions/<version>/ — one folder per version
|
|
11
|
+
*
|
|
12
|
+
* Versions that lack the expected API (generateImageFromHash, PRESETS, or
|
|
13
|
+
* saveImageToFile) are skipped with a warning.
|
|
14
|
+
*
|
|
15
|
+
* A local build from ./dist/main.js is also included as "local".
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* node bin/generateVersionComparison.js [--min-version 0.3.0] [--cache-dir /tmp/gha-versions]
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const { execSync } = require("child_process");
|
|
22
|
+
const fs = require("fs");
|
|
23
|
+
const path = require("path");
|
|
24
|
+
|
|
25
|
+
// ── Configuration ──────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
const PACKAGE_NAME = "git-hash-art";
|
|
28
|
+
const DEFAULT_MIN_VERSION = "0.2.0";
|
|
29
|
+
const DEFAULT_CACHE_DIR = path.join(
|
|
30
|
+
require("os").tmpdir(),
|
|
31
|
+
"git-hash-art-version-cache"
|
|
32
|
+
);
|
|
33
|
+
const OUTPUT_BASE = path.resolve("./examples/versions");
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Hardcoded test cases — identical across every version so the comparison
|
|
37
|
+
* is meaningful even if older versions shipped different PRESETS.
|
|
38
|
+
*/
|
|
39
|
+
const TEST_CASES = {
|
|
40
|
+
react: {
|
|
41
|
+
hash: "46192e59d42f741c761cbea79462a8b3815dd905",
|
|
42
|
+
width: 1024,
|
|
43
|
+
height: 1024,
|
|
44
|
+
},
|
|
45
|
+
angular: {
|
|
46
|
+
hash: "f31a6c3e94420f43c0cd323a5a6a99376ee59ff8",
|
|
47
|
+
width: 1024,
|
|
48
|
+
height: 1024,
|
|
49
|
+
gridSize: 6,
|
|
50
|
+
},
|
|
51
|
+
banner: {
|
|
52
|
+
hash: "d847ffd4269b22c54d6e85ad3c1892a298e961fb",
|
|
53
|
+
width: 1920,
|
|
54
|
+
height: 480,
|
|
55
|
+
gridSize: 8,
|
|
56
|
+
shapesPerLayer: 40,
|
|
57
|
+
},
|
|
58
|
+
"instagram-square": {
|
|
59
|
+
hash: "ff00ff00ff00ff00ff00ff00ff00ff00ff00ff0",
|
|
60
|
+
width: 1080,
|
|
61
|
+
height: 1080,
|
|
62
|
+
},
|
|
63
|
+
"twitter-header": {
|
|
64
|
+
hash: "7777777777777777777777777777777777777777",
|
|
65
|
+
width: 1500,
|
|
66
|
+
height: 500,
|
|
67
|
+
gridSize: 8,
|
|
68
|
+
shapesPerLayer: 35,
|
|
69
|
+
},
|
|
70
|
+
minimal: {
|
|
71
|
+
hash: "000000000000000000000000000000000fffffff",
|
|
72
|
+
width: 1024,
|
|
73
|
+
height: 1024,
|
|
74
|
+
layers: 3,
|
|
75
|
+
baseOpacity: 0.8,
|
|
76
|
+
shapesPerLayer: 15,
|
|
77
|
+
},
|
|
78
|
+
complex: {
|
|
79
|
+
hash: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
|
|
80
|
+
width: 2048,
|
|
81
|
+
height: 2048,
|
|
82
|
+
gridSize: 8,
|
|
83
|
+
layers: 7,
|
|
84
|
+
shapesPerLayer: 50,
|
|
85
|
+
minShapeSize: 30,
|
|
86
|
+
maxShapeSize: 250,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// ── CLI argument parsing ───────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
function parseArgs() {
|
|
93
|
+
const args = process.argv.slice(2);
|
|
94
|
+
const opts = {
|
|
95
|
+
minVersion: DEFAULT_MIN_VERSION,
|
|
96
|
+
cacheDir: DEFAULT_CACHE_DIR,
|
|
97
|
+
};
|
|
98
|
+
for (let i = 0; i < args.length; i++) {
|
|
99
|
+
if (args[i] === "--min-version" && args[i + 1]) {
|
|
100
|
+
opts.minVersion = args[++i];
|
|
101
|
+
} else if (args[i] === "--cache-dir" && args[i + 1]) {
|
|
102
|
+
opts.cacheDir = path.resolve(args[++i]);
|
|
103
|
+
} else if (args[i] === "--help" || args[i] === "-h") {
|
|
104
|
+
console.log(
|
|
105
|
+
`Usage: node bin/generateVersionComparison.js [options]\n\n` +
|
|
106
|
+
`Options:\n` +
|
|
107
|
+
` --min-version <ver> Minimum version to include (default: ${DEFAULT_MIN_VERSION})\n` +
|
|
108
|
+
` --cache-dir <path> Directory to cache installed versions\n` +
|
|
109
|
+
` (default: ${DEFAULT_CACHE_DIR})\n`
|
|
110
|
+
);
|
|
111
|
+
process.exit(0);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return opts;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
/** Naive semver comparison — works for simple x.y.z versions. */
|
|
120
|
+
function semverGte(a, b) {
|
|
121
|
+
const pa = a.split(".").map(Number);
|
|
122
|
+
const pb = b.split(".").map(Number);
|
|
123
|
+
for (let i = 0; i < 3; i++) {
|
|
124
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return true;
|
|
125
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return false;
|
|
126
|
+
}
|
|
127
|
+
return true; // equal
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Fetch the list of published versions from npm. */
|
|
131
|
+
function getPublishedVersions() {
|
|
132
|
+
const raw = execSync(`npm view ${PACKAGE_NAME} versions --json`, {
|
|
133
|
+
encoding: "utf-8",
|
|
134
|
+
});
|
|
135
|
+
const versions = JSON.parse(raw);
|
|
136
|
+
return Array.isArray(versions) ? versions : [versions];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Ensure a specific version is installed in the cache directory.
|
|
141
|
+
* Returns the path to the installed package's main entry.
|
|
142
|
+
*/
|
|
143
|
+
function ensureVersionInstalled(version, cacheDir) {
|
|
144
|
+
const versionDir = path.join(cacheDir, version);
|
|
145
|
+
const marker = path.join(versionDir, "node_modules", PACKAGE_NAME);
|
|
146
|
+
|
|
147
|
+
if (fs.existsSync(marker)) {
|
|
148
|
+
return marker; // already cached
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
fs.mkdirSync(versionDir, { recursive: true });
|
|
152
|
+
|
|
153
|
+
// Create a minimal package.json so npm install works
|
|
154
|
+
const pkgJson = path.join(versionDir, "package.json");
|
|
155
|
+
if (!fs.existsSync(pkgJson)) {
|
|
156
|
+
fs.writeFileSync(
|
|
157
|
+
pkgJson,
|
|
158
|
+
JSON.stringify({ name: `gha-test-${version}`, private: true }, null, 2)
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
console.log(` Installing ${PACKAGE_NAME}@${version}...`);
|
|
163
|
+
execSync(
|
|
164
|
+
`npm install ${PACKAGE_NAME}@${version} @napi-rs/canvas --no-save --legacy-peer-deps 2>&1`,
|
|
165
|
+
{ cwd: versionDir, encoding: "utf-8", stdio: "pipe" }
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
return marker;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Try to load the lib from a given path and validate it has the API we need.
|
|
173
|
+
* Returns { generateImageFromHash, saveImageToFile } or null.
|
|
174
|
+
*/
|
|
175
|
+
function tryLoadLib(libPath, label) {
|
|
176
|
+
try {
|
|
177
|
+
// Clear require cache so each version loads fresh
|
|
178
|
+
Object.keys(require.cache)
|
|
179
|
+
.filter((k) => k.includes(PACKAGE_NAME) || k.includes("git-hash-art"))
|
|
180
|
+
.forEach((k) => delete require.cache[k]);
|
|
181
|
+
|
|
182
|
+
const lib = require(libPath);
|
|
183
|
+
const { generateImageFromHash, saveImageToFile } = lib;
|
|
184
|
+
|
|
185
|
+
if (typeof generateImageFromHash !== "function") {
|
|
186
|
+
console.warn(` ⚠ ${label}: generateImageFromHash not found — skipping`);
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
if (typeof saveImageToFile !== "function") {
|
|
190
|
+
// Fallback: we can save manually if saveImageToFile is missing
|
|
191
|
+
return { generateImageFromHash, saveImageToFile: null };
|
|
192
|
+
}
|
|
193
|
+
return { generateImageFromHash, saveImageToFile };
|
|
194
|
+
} catch (err) {
|
|
195
|
+
console.warn(` ⚠ ${label}: failed to load — ${err.message}`);
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Save a PNG buffer to disk, with or without the lib's saveImageToFile. */
|
|
201
|
+
function saveImage(buffer, outputDir, label, testCase, saveImageToFile) {
|
|
202
|
+
if (saveImageToFile) {
|
|
203
|
+
return saveImageToFile(
|
|
204
|
+
buffer,
|
|
205
|
+
outputDir,
|
|
206
|
+
testCase.hash,
|
|
207
|
+
label,
|
|
208
|
+
testCase.width,
|
|
209
|
+
testCase.height
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
// Manual fallback
|
|
213
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
214
|
+
const shortHash = testCase.hash.slice(0, 8);
|
|
215
|
+
const filename = `${label}-${testCase.width}x${testCase.height}-${shortHash}.png`;
|
|
216
|
+
const outputPath = path.join(outputDir, filename);
|
|
217
|
+
fs.writeFileSync(outputPath, buffer);
|
|
218
|
+
return outputPath;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ── Per-version generation ─────────────────────────────────────────────────
|
|
222
|
+
|
|
223
|
+
function generateForVersion(lib, _versionLabel, outputDir) {
|
|
224
|
+
const results = [];
|
|
225
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
226
|
+
|
|
227
|
+
for (const [label, testCase] of Object.entries(TEST_CASES)) {
|
|
228
|
+
try {
|
|
229
|
+
const config = { ...testCase };
|
|
230
|
+
delete config.hash;
|
|
231
|
+
const buffer = lib.generateImageFromHash(testCase.hash, {
|
|
232
|
+
width: testCase.width,
|
|
233
|
+
height: testCase.height,
|
|
234
|
+
...config,
|
|
235
|
+
});
|
|
236
|
+
const outputPath = saveImage(
|
|
237
|
+
buffer,
|
|
238
|
+
outputDir,
|
|
239
|
+
label,
|
|
240
|
+
testCase,
|
|
241
|
+
lib.saveImageToFile
|
|
242
|
+
);
|
|
243
|
+
results.push({ label, success: true, outputPath });
|
|
244
|
+
} catch (err) {
|
|
245
|
+
results.push({ label, success: false, error: err.message });
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return results;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ── Main ───────────────────────────────────────────────────────────────────
|
|
253
|
+
|
|
254
|
+
async function main() {
|
|
255
|
+
const opts = parseArgs();
|
|
256
|
+
|
|
257
|
+
console.log(`\n🎨 git-hash-art Version Comparison Generator`);
|
|
258
|
+
console.log(` Min version : ${opts.minVersion}`);
|
|
259
|
+
console.log(` Cache dir : ${opts.cacheDir}`);
|
|
260
|
+
console.log(` Output dir : ${OUTPUT_BASE}\n`);
|
|
261
|
+
|
|
262
|
+
// Collect all versions to process (published + local)
|
|
263
|
+
const allPublished = getPublishedVersions();
|
|
264
|
+
const versions = allPublished.filter((v) => semverGte(v, opts.minVersion));
|
|
265
|
+
|
|
266
|
+
console.log(
|
|
267
|
+
`Found ${allPublished.length} published versions, ` +
|
|
268
|
+
`${versions.length} >= ${opts.minVersion}\n`
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
const summary = [];
|
|
272
|
+
|
|
273
|
+
// ── Published versions ──
|
|
274
|
+
for (const version of versions) {
|
|
275
|
+
console.log(`▸ ${version}`);
|
|
276
|
+
try {
|
|
277
|
+
const libPath = ensureVersionInstalled(version, opts.cacheDir);
|
|
278
|
+
const lib = tryLoadLib(libPath, version);
|
|
279
|
+
if (!lib) {
|
|
280
|
+
summary.push({ version, status: "skipped", reason: "incompatible API" });
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const outputDir = path.join(OUTPUT_BASE, version);
|
|
285
|
+
const results = generateForVersion(lib, version, outputDir);
|
|
286
|
+
const passed = results.filter((r) => r.success).length;
|
|
287
|
+
const failed = results.filter((r) => !r.success).length;
|
|
288
|
+
|
|
289
|
+
console.log(
|
|
290
|
+
` ✓ ${passed} images generated` +
|
|
291
|
+
(failed ? `, ✗ ${failed} failed` : "") +
|
|
292
|
+
` → ${outputDir}`
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
summary.push({ version, status: "done", passed, failed, outputDir });
|
|
296
|
+
} catch (err) {
|
|
297
|
+
console.error(` ✗ ${version}: ${err.message}`);
|
|
298
|
+
summary.push({ version, status: "error", reason: err.message });
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ── Local build ──
|
|
303
|
+
console.log(`\n▸ local (./dist/main.js)`);
|
|
304
|
+
const localLibPath = path.resolve("./dist/main.js");
|
|
305
|
+
if (fs.existsSync(localLibPath)) {
|
|
306
|
+
const lib = tryLoadLib(localLibPath, "local");
|
|
307
|
+
if (lib) {
|
|
308
|
+
const outputDir = path.join(OUTPUT_BASE, "local");
|
|
309
|
+
const results = generateForVersion(lib, "local", outputDir);
|
|
310
|
+
const passed = results.filter((r) => r.success).length;
|
|
311
|
+
const failed = results.filter((r) => !r.success).length;
|
|
312
|
+
console.log(
|
|
313
|
+
` ✓ ${passed} images generated` +
|
|
314
|
+
(failed ? `, ✗ ${failed} failed` : "") +
|
|
315
|
+
` → ${outputDir}`
|
|
316
|
+
);
|
|
317
|
+
summary.push({ version: "local", status: "done", passed, failed, outputDir });
|
|
318
|
+
} else {
|
|
319
|
+
summary.push({
|
|
320
|
+
version: "local",
|
|
321
|
+
status: "skipped",
|
|
322
|
+
reason: "could not load",
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
console.warn(` ⚠ Local build not found — run "yarn build" first`);
|
|
327
|
+
summary.push({
|
|
328
|
+
version: "local",
|
|
329
|
+
status: "skipped",
|
|
330
|
+
reason: "dist/main.js not found",
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// ── Summary ──
|
|
335
|
+
console.log(`\n${"─".repeat(60)}`);
|
|
336
|
+
console.log(`Summary:\n`);
|
|
337
|
+
for (const s of summary) {
|
|
338
|
+
if (s.status === "done") {
|
|
339
|
+
console.log(
|
|
340
|
+
` ✓ ${s.version.padEnd(10)} ${s.passed} images` +
|
|
341
|
+
(s.failed ? ` (${s.failed} failed)` : "")
|
|
342
|
+
);
|
|
343
|
+
} else {
|
|
344
|
+
console.log(` ⚠ ${s.version.padEnd(10)} ${s.status}: ${s.reason}`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
console.log(`\nOutput: ${OUTPUT_BASE}\n`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
main().catch((err) => {
|
|
351
|
+
console.error("Fatal error:", err);
|
|
352
|
+
process.exit(1);
|
|
353
|
+
});
|