@shopify/react-native-skia 2.4.21 → 2.5.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.
Files changed (35) hide show
  1. package/android/CMakeLists.txt +12 -49
  2. package/android/build.gradle +37 -1
  3. package/apple/SkiaCVPixelBufferUtils.h +6 -2
  4. package/apple/SkiaCVPixelBufferUtils.mm +208 -36
  5. package/apple/SkiaUIView.mm +4 -0
  6. package/cpp/api/JsiSkColor.h +53 -1
  7. package/cpp/api/recorder/Drawings.h +1 -1
  8. package/cpp/rnwgpu/ArrayBuffer.h +7 -2
  9. package/lib/commonjs/dom/types/Drawings.d.ts +1 -0
  10. package/lib/commonjs/dom/types/Drawings.js.map +1 -1
  11. package/lib/commonjs/renderer/Canvas.d.ts +2 -0
  12. package/lib/commonjs/renderer/Canvas.js +0 -3
  13. package/lib/commonjs/renderer/Canvas.js.map +1 -1
  14. package/lib/commonjs/sksg/Container.native.js +3 -5
  15. package/lib/commonjs/sksg/Container.native.js.map +1 -1
  16. package/lib/commonjs/sksg/Recorder/commands/Drawing.js +2 -2
  17. package/lib/commonjs/sksg/Recorder/commands/Drawing.js.map +1 -1
  18. package/lib/module/dom/types/Drawings.d.ts +1 -0
  19. package/lib/module/dom/types/Drawings.js.map +1 -1
  20. package/lib/module/renderer/Canvas.d.ts +2 -0
  21. package/lib/module/renderer/Canvas.js +0 -3
  22. package/lib/module/renderer/Canvas.js.map +1 -1
  23. package/lib/module/sksg/Container.native.js +3 -5
  24. package/lib/module/sksg/Container.native.js.map +1 -1
  25. package/lib/module/sksg/Recorder/commands/Drawing.js +2 -2
  26. package/lib/module/sksg/Recorder/commands/Drawing.js.map +1 -1
  27. package/lib/typescript/src/dom/types/Drawings.d.ts +1 -0
  28. package/lib/typescript/src/renderer/Canvas.d.ts +2 -0
  29. package/package.json +10 -30
  30. package/react-native-skia.podspec +98 -63
  31. package/src/dom/types/Drawings.ts +1 -0
  32. package/src/renderer/Canvas.tsx +2 -3
  33. package/src/sksg/Container.native.ts +3 -4
  34. package/src/sksg/Recorder/commands/Drawing.ts +3 -2
  35. package/scripts/install-skia.mjs +0 -728
@@ -1,728 +0,0 @@
1
- import fs from "fs";
2
- import https from "https";
3
- import path from "path";
4
- import os from "os";
5
- import crypto from "crypto";
6
- import { spawn } from "child_process";
7
- import { fileURLToPath } from "url";
8
-
9
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
-
11
- // Allow skipping download via environment variable (useful for CI builds)
12
- if (process.env.SKIP_SKIA_DOWNLOAD === '1' || process.env.SKIP_SKIA_DOWNLOAD === 'true') {
13
- console.log("⏭️ Skipping Skia download (SKIP_SKIA_DOWNLOAD is set)");
14
- process.exit(0);
15
- }
16
-
17
- const repo = "shopify/react-native-skia";
18
-
19
- const packageJsonPath = path.join(__dirname, "..", "package.json");
20
-
21
- const getPackageJson = () => {
22
- return JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
23
- };
24
-
25
- const getSkiaConfig = (graphite = false) => {
26
- const packageJson = getPackageJson();
27
- return graphite ? packageJson["skia-graphite"] : packageJson.skia;
28
- };
29
-
30
- const getSkiaVersion = (graphite = false) => {
31
- const packageJson = getPackageJson();
32
- const skiaConfig = getSkiaConfig(graphite);
33
- return skiaConfig?.version || packageJson.skiaVersion;
34
- };
35
-
36
- const updateSkiaChecksums = (checksums, graphite = false) => {
37
- const packageJson = getPackageJson();
38
- const field = graphite ? "skia-graphite" : "skia";
39
-
40
- if (!packageJson[field]) {
41
- packageJson[field] = { version: packageJson[field]?.version || "m142", checksums: {} };
42
- }
43
-
44
- packageJson[field].checksums = checksums;
45
-
46
- fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf8");
47
- };
48
-
49
- const GRAPHITE = !!process.env.SK_GRAPHITE;
50
- const prefix = GRAPHITE ? "skia-graphite" : "skia";
51
-
52
- // Build artifact names based on platform and Graphite mode
53
- const names = [
54
- `${prefix}-android-arm`,
55
- `${prefix}-android-arm-64`,
56
- `${prefix}-android-arm-x64`,
57
- `${prefix}-android-arm-x86`,
58
- `${prefix}-apple-ios-xcframeworks`,
59
- `${prefix}-apple-macos-xcframeworks`,
60
- ];
61
-
62
- // Add tvOS only for non-Graphite builds
63
- if (!GRAPHITE) {
64
- names.push(`${prefix}-apple-tvos-xcframeworks`);
65
- }
66
-
67
- // Note: macCatalyst is now included in the iOS xcframeworks, no separate download needed
68
-
69
- if (GRAPHITE) {
70
- names.push(`${prefix}-headers`);
71
- }
72
-
73
- const skiaVersion = getSkiaVersion(GRAPHITE);
74
- const releaseTag = GRAPHITE ? `skia-graphite-${skiaVersion}` : `skia-${skiaVersion}`;
75
- console.log(
76
- `📦 Downloading Skia prebuilt binaries for ${releaseTag}`
77
- );
78
-
79
- const runCommand = (command, args, options = {}) => {
80
- return new Promise((resolve, reject) => {
81
- const child = spawn(command, args, {
82
- stdio: ["ignore", "inherit", "inherit"],
83
- ...options,
84
- });
85
- child.on("error", (error) => {
86
- reject(error);
87
- });
88
- child.on("close", (code) => {
89
- if (code === 0) {
90
- resolve();
91
- } else {
92
- reject(new Error(`Command ${command} exited with code ${code}`));
93
- }
94
- });
95
- });
96
- };
97
-
98
- const runCommandWithOutput = (command, args, options = {}) => {
99
- return new Promise((resolve, reject) => {
100
- const child = spawn(command, args, {
101
- stdio: ["ignore", "pipe", "pipe"],
102
- ...options,
103
- });
104
- let stdout = "";
105
- let stderr = "";
106
- child.stdout.on("data", (data) => {
107
- stdout += data.toString();
108
- });
109
- child.stderr.on("data", (data) => {
110
- stderr += data.toString();
111
- });
112
- child.on("error", (error) => {
113
- reject(error);
114
- });
115
- child.on("close", (code) => {
116
- if (code === 0) {
117
- resolve(stdout.trim());
118
- } else {
119
- reject(new Error(`Command ${command} exited with code ${code}: ${stderr}`));
120
- }
121
- });
122
- });
123
- };
124
-
125
- const skiaDir = path.resolve(__dirname, "../../../externals/skia");
126
-
127
- const checkoutSkiaBranch = async (version) => {
128
- // Extract base version (e.g., "m144a" -> "m144", "m142" -> "m142")
129
- const baseVersion = version.match(/^(m\d+)/)?.[1] || version;
130
- const branchName = `chrome/${baseVersion}`;
131
-
132
- // Check if the skia directory exists and is a git repo
133
- // (won't exist when installed via npm - submodule is not included in the package)
134
- if (!fs.existsSync(skiaDir) || !fs.existsSync(path.join(skiaDir, ".git"))) {
135
- return;
136
- }
137
-
138
- console.log(`🔀 Checking out Skia branch: ${branchName}`);
139
-
140
- try {
141
- // Get current branch/commit
142
- const currentRef = await runCommandWithOutput("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: skiaDir });
143
-
144
- if (currentRef === branchName) {
145
- console.log(` ✓ Already on branch ${branchName}`);
146
- return;
147
- }
148
-
149
- // Fetch the branch from origin
150
- console.log(` Fetching branch ${branchName} from origin...`);
151
- try {
152
- await runCommand("git", ["fetch", "origin", `${branchName}:${branchName}`], { cwd: skiaDir });
153
- } catch (e) {
154
- // Branch might already exist locally, try to update it
155
- await runCommand("git", ["fetch", "origin", branchName], { cwd: skiaDir });
156
- }
157
-
158
- // Checkout the branch (use -f to discard local changes in the submodule)
159
- console.log(` Checking out ${branchName}...`);
160
- await runCommand("git", ["checkout", "-f", branchName], { cwd: skiaDir });
161
-
162
- console.log(` ✓ Successfully checked out ${branchName}`);
163
- } catch (error) {
164
- throw new Error(`Failed to checkout branch ${branchName}: ${error.message}`);
165
- }
166
- };
167
-
168
- const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
169
-
170
- const downloadToFile = (url, destPath, maxRetries = 5) => {
171
- fs.mkdirSync(path.dirname(destPath), { recursive: true });
172
-
173
- const attemptDownload = (retryCount = 0) => {
174
- return new Promise((resolve, reject) => {
175
- const request = (currentUrl) => {
176
- https
177
- .get(currentUrl, { headers: { "User-Agent": "node" } }, (res) => {
178
- if (
179
- res.statusCode &&
180
- [301, 302, 303, 307, 308].includes(res.statusCode)
181
- ) {
182
- const { location } = res.headers;
183
- if (location) {
184
- res.resume();
185
- request(location);
186
- } else {
187
- reject(new Error(`Redirect without location for ${currentUrl}`));
188
- }
189
- return;
190
- }
191
-
192
- if (res.statusCode !== 200) {
193
- const error = new Error(
194
- `Failed to download: ${res.statusCode} ${res.statusMessage}`
195
- );
196
- error.statusCode = res.statusCode;
197
- reject(error);
198
- res.resume();
199
- return;
200
- }
201
-
202
- const fileStream = fs.createWriteStream(destPath);
203
- res.pipe(fileStream);
204
-
205
- fileStream.on("finish", () => {
206
- fileStream.close((err) => {
207
- if (err) {
208
- // If closing the stream errors, perform the same cleanup and reject.
209
- fileStream.destroy();
210
- fs.unlink(destPath, () => reject(err));
211
- } else {
212
- resolve();
213
- }
214
- });
215
- });
216
-
217
- const cleanup = (error) => {
218
- fileStream.destroy();
219
- fs.unlink(destPath, () => reject(error));
220
- };
221
-
222
- res.on("error", cleanup);
223
- fileStream.on("error", cleanup);
224
- })
225
- .on("error", reject);
226
- };
227
-
228
- request(url);
229
- });
230
- };
231
-
232
- const downloadWithRetry = async (retryCount = 0) => {
233
- try {
234
- await attemptDownload(retryCount);
235
- } catch (error) {
236
- const isRateLimit = error.statusCode === 403 || error.message.includes('rate limit');
237
- const shouldRetry = retryCount < maxRetries && (isRateLimit || error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT');
238
-
239
- if (shouldRetry) {
240
- const delay = Math.pow(2, retryCount) * 1000; // 1s, 2s, 4s, 8s, 16s
241
- console.log(` ⚠️ Download failed (${error.message}), retrying in ${delay/1000}s... (attempt ${retryCount + 1}/${maxRetries})`);
242
- await sleep(delay);
243
- return downloadWithRetry(retryCount + 1);
244
- } else {
245
- throw error;
246
- }
247
- }
248
- };
249
-
250
- return downloadWithRetry();
251
- };
252
-
253
- const extractTarGz = async (archivePath, destDir) => {
254
- fs.mkdirSync(destDir, { recursive: true });
255
-
256
- const args = [
257
- "-xzf",
258
- archivePath,
259
- "-C",
260
- destDir,
261
- ];
262
- const candidates =
263
- process.platform === "win32"
264
- ? [
265
- "tar.exe",
266
- path.join(
267
- process.env.SystemRoot ?? "C:\\Windows",
268
- "System32",
269
- "tar.exe"
270
- ),
271
- "bsdtar.exe",
272
- "bsdtar",
273
- ]
274
- : ["tar"];
275
-
276
- let lastError;
277
- for (const candidate of candidates) {
278
- try {
279
- await runCommand(candidate, args);
280
- return;
281
- } catch (err) {
282
- if (err.code === "ENOENT") {
283
- lastError = new Error(`Command ${candidate} not found`);
284
- continue;
285
- }
286
- lastError = err;
287
- }
288
- }
289
-
290
- throw new Error(
291
- `Failed to extract ${path.basename(
292
- archivePath
293
- )}. Please install a compatible tar binary. Last error: ${
294
- lastError?.message ?? "unknown error"
295
- }`
296
- );
297
- };
298
-
299
- // Helper function to download and extract a tar.gz file
300
- const downloadAndExtract = async (url, destDir, tempDir) => {
301
- const archivePath = path.join(tempDir, path.basename(url));
302
- await downloadToFile(url, archivePath);
303
- try {
304
- await extractTarGz(archivePath, destDir);
305
- } finally {
306
- if (fs.existsSync(archivePath)) {
307
- fs.unlinkSync(archivePath);
308
- }
309
- }
310
- };
311
-
312
- const artifactsDir = path.resolve(
313
- __dirname,
314
- "../../../packages/skia/artifacts"
315
- );
316
-
317
- const libsDir = path.resolve(__dirname, "../libs");
318
-
319
- // Function to calculate the checksum of a directory
320
- const calculateDirectoryChecksum = (directory) => {
321
- if (!fs.existsSync(directory)) {
322
- return null;
323
- }
324
-
325
- const hash = crypto.createHash("sha256");
326
- const files = [];
327
-
328
- const collectFiles = (dir) => {
329
- const entries = fs.readdirSync(dir, { withFileTypes: true });
330
- for (const entry of entries) {
331
- const fullPath = path.join(dir, entry.name);
332
- if (entry.isDirectory()) {
333
- collectFiles(fullPath);
334
- } else if (entry.isFile()) {
335
- files.push(fullPath);
336
- }
337
- }
338
- };
339
-
340
- collectFiles(directory);
341
- files.sort();
342
-
343
- for (const file of files) {
344
- const relativePath = path.relative(directory, file);
345
- hash.update(relativePath);
346
- hash.update(fs.readFileSync(file));
347
- }
348
-
349
- return hash.digest("hex");
350
- };
351
-
352
- // Function to calculate all library checksums
353
- const calculateLibraryChecksums = () => {
354
- const checksums = {};
355
-
356
- // Android architectures
357
- const androidArchs = ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"];
358
- for (const arch of androidArchs) {
359
- const archDir = path.join(libsDir, "android", arch);
360
- const checksum = calculateDirectoryChecksum(archDir);
361
- if (checksum) {
362
- const checksumKey = `android-${arch}`;
363
- checksums[checksumKey] = checksum;
364
- }
365
- }
366
-
367
- // Apple platforms - calculate separate checksums for each platform
368
- // Note: maccatalyst is included in iOS xcframeworks, not a separate artifact
369
- const applePlatforms = GRAPHITE
370
- ? ["ios", "macos"]
371
- : ["ios", "tvos", "macos"];
372
-
373
- for (const platform of applePlatforms) {
374
- const platformDir = path.join(libsDir, "apple", platform);
375
- const checksum = calculateDirectoryChecksum(platformDir);
376
- if (checksum) {
377
- checksums[`apple-${platform}-xcframeworks`] = checksum;
378
- }
379
- }
380
-
381
- return checksums;
382
- };
383
-
384
- // Function to verify if checksums match
385
- const verifyChecksums = () => {
386
- const skiaConfig = getSkiaConfig(GRAPHITE);
387
- const expectedChecksums = skiaConfig?.checksums || {};
388
-
389
- // If no checksums in package.json, we need to download
390
- if (Object.keys(expectedChecksums).length === 0) {
391
- console.log("⚠️ No checksums found in package.json");
392
- return false;
393
- }
394
-
395
- const actualChecksums = calculateLibraryChecksums();
396
-
397
- // Check if all expected checksums match
398
- for (const [key, expectedChecksum] of Object.entries(expectedChecksums)) {
399
- const actualChecksum = actualChecksums[key];
400
- if (actualChecksum !== expectedChecksum) {
401
- console.log(`⚠️ Checksum mismatch for ${key}`);
402
- console.log(` Expected: ${expectedChecksum}`);
403
- console.log(` Actual: ${actualChecksum || "missing"}`);
404
- return false;
405
- }
406
- }
407
-
408
- console.log("✅ All checksums match");
409
- return true;
410
- };
411
-
412
- // Function to check if prebuilt binaries are already installed
413
- const areBinariesInstalled = () => {
414
- if (!fs.existsSync(libsDir)) {
415
- return false;
416
- }
417
-
418
- // Check for Android libraries
419
- const androidDir = path.join(libsDir, "android");
420
- const androidArchs = ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"];
421
- for (const arch of androidArchs) {
422
- const archDir = path.join(androidDir, arch);
423
- if (!fs.existsSync(archDir) || fs.readdirSync(archDir).length === 0) {
424
- return false;
425
- }
426
- }
427
-
428
- // Check for Apple platform frameworks
429
- const applePlatforms = GRAPHITE
430
- ? ["ios", "macos"]
431
- : ["ios", "tvos", "macos", "maccatalyst"];
432
-
433
- for (const platform of applePlatforms) {
434
- const platformDir = path.join(libsDir, "apple", platform);
435
- if (!fs.existsSync(platformDir) || fs.readdirSync(platformDir).length === 0) {
436
- return false;
437
- }
438
- }
439
-
440
- return true;
441
- };
442
-
443
- // Function to clear directory contents
444
- const clearDirectory = (directory) => {
445
- if (fs.existsSync(directory)) {
446
- fs.readdirSync(directory).forEach((file) => {
447
- const curPath = path.join(directory, file);
448
- if (fs.lstatSync(curPath).isDirectory()) {
449
- clearDirectory(curPath);
450
- fs.rmdirSync(curPath);
451
- } else {
452
- fs.unlinkSync(curPath);
453
- }
454
- });
455
- //console.log(`❌ ${directory}`);
456
- } else {
457
- console.log(`Directory ${directory} does not exist, creating it...`);
458
- fs.mkdirSync(directory, { recursive: true });
459
- }
460
- };
461
-
462
- const main = async () => {
463
- // Ensure the skia submodule is on the correct branch for copying headers
464
- await checkoutSkiaBranch(skiaVersion);
465
-
466
- // Check if binaries are installed and checksums match
467
- if (areBinariesInstalled() && verifyChecksums()) {
468
- console.log("✅ Prebuilt binaries already installed with matching checksums, skipping download");
469
- return;
470
- }
471
-
472
- if (areBinariesInstalled()) {
473
- console.log("⚠️ Binaries installed but checksums don't match, re-downloading...");
474
- }
475
-
476
- console.log("🧹 Clearing existing artifacts...");
477
- clearDirectory(artifactsDir);
478
-
479
- console.log(`⬇️ Downloading release assets to ${artifactsDir}`);
480
- const tempDownloadDir = fs.mkdtempSync(
481
- path.join(os.tmpdir(), "skia-download-")
482
- );
483
-
484
- for (const artifactName of names) {
485
- const assetName = `${artifactName}-${releaseTag}.tar.gz`;
486
- const extractDir = path.join(artifactsDir, artifactName);
487
- const downloadUrl = `https://github.com/${repo}/releases/download/${releaseTag}/${assetName}`;
488
-
489
- console.log(` Downloading ${assetName}...`);
490
- try {
491
- await downloadAndExtract(downloadUrl, extractDir, tempDownloadDir);
492
- console.log(` ✓ ${assetName} extracted`);
493
- } catch (error) {
494
- console.error(` ✗ Failed to download ${assetName}:`, error);
495
- fs.rmSync(tempDownloadDir, { recursive: true, force: true });
496
- throw error;
497
- }
498
- }
499
- fs.rmSync(tempDownloadDir, { recursive: true, force: true });
500
-
501
- // Copy artifacts to libs folder in the required structure
502
- console.log("📦 Copying artifacts to libs folder...");
503
- clearDirectory(libsDir);
504
-
505
- // Create android directory structure
506
- const androidDir = path.join(libsDir, "android");
507
- fs.mkdirSync(androidDir, { recursive: true });
508
-
509
- // Copy android artifacts
510
- const androidArchs = GRAPHITE ? [
511
- { artifact: `${prefix}-android-arm`, srcSubdir: "arm", dest: "armeabi-v7a" },
512
- { artifact: `${prefix}-android-arm-64`, srcSubdir: "arm64", dest: "arm64-v8a" },
513
- { artifact: `${prefix}-android-arm-x86`, srcSubdir: "x86", dest: "x86" },
514
- { artifact: `${prefix}-android-arm-x64`, srcSubdir: "x64", dest: "x86_64" },
515
- ] : [
516
- { artifact: `${prefix}-android-arm`, srcSubdir: "armeabi-v7a", dest: "armeabi-v7a" },
517
- { artifact: `${prefix}-android-arm-64`, srcSubdir: "arm64-v8a", dest: "arm64-v8a" },
518
- { artifact: `${prefix}-android-arm-x86`, srcSubdir: "x86", dest: "x86" },
519
- { artifact: `${prefix}-android-arm-x64`, srcSubdir: "x86_64", dest: "x86_64" },
520
- ];
521
-
522
- androidArchs.forEach(({ artifact, srcSubdir, dest }) => {
523
- // The tar file extracts to artifactName/srcSubdir
524
- const srcDir = path.join(artifactsDir, artifact, srcSubdir);
525
- const destDir = path.join(androidDir, dest);
526
- console.log(` Checking ${srcDir} -> ${destDir}`);
527
- if (fs.existsSync(srcDir)) {
528
- console.log(` ✓ Copying ${artifact}/${srcSubdir}`);
529
- fs.mkdirSync(destDir, { recursive: true });
530
-
531
- const copyDir = (srcPath, destPath) => {
532
- fs.mkdirSync(destPath, { recursive: true });
533
- fs.readdirSync(srcPath).forEach((file) => {
534
- const srcFile = path.join(srcPath, file);
535
- const destFile = path.join(destPath, file);
536
- const stat = fs.lstatSync(srcFile);
537
-
538
- // Skip sockets and other special files
539
- if (stat.isSocket() || stat.isFIFO() || stat.isCharacterDevice() || stat.isBlockDevice()) {
540
- return;
541
- }
542
-
543
- if (stat.isDirectory()) {
544
- copyDir(srcFile, destFile);
545
- } else {
546
- fs.copyFileSync(srcFile, destFile);
547
- }
548
- });
549
- };
550
-
551
- copyDir(srcDir, destDir);
552
- } else {
553
- console.log(` ✗ Source directory not found: ${srcDir}`);
554
- }
555
- });
556
-
557
- // Create apple directory structure - now per-platform
558
- const appleDir = path.join(libsDir, "apple");
559
- fs.mkdirSync(appleDir, { recursive: true });
560
-
561
- // Define the platform artifacts to process
562
- // Note: maccatalyst is included in iOS xcframeworks, not a separate artifact
563
- const applePlatformArtifacts = GRAPHITE
564
- ? [
565
- { artifact: `${prefix}-apple-ios-xcframeworks`, srcSubdir: "ios", dest: "ios" },
566
- { artifact: `${prefix}-apple-macos-xcframeworks`, srcSubdir: "macos", dest: "macos" },
567
- ]
568
- : [
569
- { artifact: `${prefix}-apple-ios-xcframeworks`, srcSubdir: "ios", dest: "ios" },
570
- { artifact: `${prefix}-apple-tvos-xcframeworks`, srcSubdir: "tvos", dest: "tvos" },
571
- { artifact: `${prefix}-apple-macos-xcframeworks`, srcSubdir: "macos", dest: "macos" },
572
- ];
573
-
574
- applePlatformArtifacts.forEach(({ artifact, srcSubdir, dest }) => {
575
- // The tar file extracts to artifact_name/srcSubdir (e.g., skia-apple-ios-xcframeworks/ios)
576
- const appleSrcDir = path.join(artifactsDir, artifact, srcSubdir);
577
- const destDir = path.join(appleDir, dest);
578
-
579
- console.log(` Checking ${appleSrcDir} -> ${destDir}`);
580
- if (fs.existsSync(appleSrcDir)) {
581
- console.log(` ✓ Copying ${artifact}/${srcSubdir}`);
582
- fs.mkdirSync(destDir, { recursive: true });
583
-
584
- // Copy all xcframeworks
585
- fs.readdirSync(appleSrcDir).forEach((item) => {
586
- const srcPath = path.join(appleSrcDir, item);
587
- const destPath = path.join(destDir, item);
588
-
589
- if (fs.lstatSync(srcPath).isDirectory()) {
590
- // Copy directory recursively
591
- const copyDir = (src, dest) => {
592
- fs.mkdirSync(dest, { recursive: true });
593
- fs.readdirSync(src).forEach((file) => {
594
- const srcFile = path.join(src, file);
595
- const destFile = path.join(dest, file);
596
- if (fs.lstatSync(srcFile).isDirectory()) {
597
- copyDir(srcFile, destFile);
598
- } else {
599
- fs.copyFileSync(srcFile, destFile);
600
- }
601
- });
602
- };
603
- copyDir(srcPath, destPath);
604
- } else {
605
- fs.copyFileSync(srcPath, destPath);
606
- }
607
- });
608
- } else {
609
- console.log(` ✗ Source directory not found: ${appleSrcDir}`);
610
- }
611
- });
612
-
613
- // Create or remove Graphite marker files based on build type
614
- const androidMarkerFile = path.join(androidDir, "graphite.enabled");
615
- // Apple marker file stays at the apple root level (libs/apple/graphite.enabled)
616
- const appleMarkerFile = path.join(appleDir, "graphite.enabled");
617
-
618
- if (GRAPHITE) {
619
- // Create marker files for Graphite builds
620
- fs.writeFileSync(androidMarkerFile, "");
621
- fs.writeFileSync(appleMarkerFile, "");
622
- console.log("✓ Created Graphite marker files");
623
- } else {
624
- // Ensure marker files don't exist for non-Graphite builds
625
- if (fs.existsSync(androidMarkerFile)) {
626
- fs.unlinkSync(androidMarkerFile);
627
- }
628
- if (fs.existsSync(appleMarkerFile)) {
629
- fs.unlinkSync(appleMarkerFile);
630
- }
631
- }
632
-
633
- // Copy Graphite headers if using Graphite
634
- if (GRAPHITE) {
635
- console.log("📦 Copying Graphite headers...");
636
- const cppDir = path.resolve(__dirname, "../cpp");
637
- const headersSrcDir = path.join(artifactsDir, `${prefix}-headers`);
638
-
639
- console.log(` Looking for headers in: ${headersSrcDir}`);
640
- console.log(` Headers dir exists: ${fs.existsSync(headersSrcDir)}`);
641
-
642
- if (fs.existsSync(headersSrcDir)) {
643
- console.log(` Contents: ${fs.readdirSync(headersSrcDir).join(", ")}`);
644
-
645
- // The asset contains packages/skia/cpp structure, so we need to navigate into it
646
- const packagesDir = path.join(headersSrcDir, "packages", "skia", "cpp");
647
- console.log(` Looking for packages dir: ${packagesDir}`);
648
- console.log(` Packages dir exists: ${fs.existsSync(packagesDir)}`);
649
-
650
- if (fs.existsSync(packagesDir)) {
651
- console.log(` Packages contents: ${fs.readdirSync(packagesDir).join(", ")}`);
652
-
653
- // Copy dawn/include
654
- const dawnIncludeSrc = path.join(packagesDir, "dawn", "include");
655
- const dawnIncludeDest = path.join(cppDir, "dawn", "include");
656
- console.log(` Dawn source: ${dawnIncludeSrc}`);
657
- console.log(` Dawn source exists: ${fs.existsSync(dawnIncludeSrc)}`);
658
-
659
- if (fs.existsSync(dawnIncludeSrc)) {
660
- const copyDir = (src, dest) => {
661
- fs.mkdirSync(dest, { recursive: true });
662
- fs.readdirSync(src).forEach((file) => {
663
- const srcFile = path.join(src, file);
664
- const destFile = path.join(dest, file);
665
- if (fs.lstatSync(srcFile).isDirectory()) {
666
- copyDir(srcFile, destFile);
667
- } else {
668
- fs.copyFileSync(srcFile, destFile);
669
- }
670
- });
671
- };
672
- copyDir(dawnIncludeSrc, dawnIncludeDest);
673
- console.log(" ✓ Dawn headers copied");
674
- } else {
675
- console.log(" ✗ Dawn headers not found");
676
- }
677
-
678
- // Copy graphite headers
679
- const graphiteSrc = path.join(packagesDir, "skia", "src", "gpu", "graphite");
680
- const graphiteDest = path.join(cppDir, "skia", "src", "gpu", "graphite");
681
- console.log(` Graphite source: ${graphiteSrc}`);
682
- console.log(` Graphite source exists: ${fs.existsSync(graphiteSrc)}`);
683
-
684
- if (fs.existsSync(graphiteSrc)) {
685
- const copyDir = (src, dest) => {
686
- fs.mkdirSync(dest, { recursive: true });
687
- fs.readdirSync(src).forEach((file) => {
688
- const srcFile = path.join(src, file);
689
- const destFile = path.join(dest, file);
690
- if (fs.lstatSync(srcFile).isDirectory()) {
691
- copyDir(srcFile, destFile);
692
- } else {
693
- fs.copyFileSync(srcFile, destFile);
694
- }
695
- });
696
- };
697
- copyDir(graphiteSrc, graphiteDest);
698
- console.log(" ✓ Graphite headers copied");
699
- } else {
700
- console.log(" ✗ Graphite headers not found");
701
- }
702
- } else {
703
- console.log(" ✗ Packages directory not found in headers asset");
704
- }
705
- } else {
706
- console.log(" ✗ Headers directory not found");
707
- }
708
- }
709
-
710
- console.log("✅ Completed installation of Skia prebuilt binaries.");
711
-
712
- // Calculate and update checksums in package.json
713
- console.log("🔐 Calculating and updating checksums...");
714
- const newChecksums = calculateLibraryChecksums();
715
- updateSkiaChecksums(newChecksums, GRAPHITE);
716
- console.log("✅ Checksums updated in package.json");
717
-
718
- // Clean up artifacts directory
719
- console.log("🗑️ Cleaning up artifacts directory...");
720
- clearDirectory(artifactsDir);
721
- fs.rmdirSync(artifactsDir);
722
- };
723
-
724
- // Run the main function
725
- main().catch((error) => {
726
- console.error("❌ Error:", error);
727
- process.exit(1);
728
- });