From ce6353b7177a6e4100c9282cc7a77669824786ef Mon Sep 17 00:00:00 2001 From: William Candillon Date: Sat, 12 Sep 2026 15:08:05 +0200 Subject: [PATCH 1/3] =?UTF-8?q?chore(=E2=AC=86=EF=B8=8F):=20upgrade=20to?= =?UTF-8?q?=20Skia=20m154=20(#4054)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-skia-graphite.yml | 45 +++- .github/workflows/build-skia.yml | 45 +++- .gitmodules | 2 +- externals/depot_tools | 2 +- externals/skia | 2 +- packages/skia/package.json | 2 +- .../skia/scripts/install-skia-graphite.ts | 197 +++++++++++------- packages/skia/scripts/skia-configuration.ts | 6 + packages/skia/scripts/workflow-copy-libs.ts | 107 ---------- yarn.lock | 3 +- 10 files changed, 211 insertions(+), 200 deletions(-) delete mode 100644 packages/skia/scripts/workflow-copy-libs.ts diff --git a/.github/workflows/build-skia-graphite.yml b/.github/workflows/build-skia-graphite.yml index d40b60f3d4..b5c7d6629d 100644 --- a/.github/workflows/build-skia-graphite.yml +++ b/.github/workflows/build-skia-graphite.yml @@ -178,8 +178,43 @@ jobs: SK_GRAPHITE: 1 run: yarn build-skia ${{ matrix.target }} - - name: Package binaries - ${{ matrix.artifact_name }} - id: package + - name: Package Apple xcframeworks (per-framework zips) - ${{ matrix.artifact_name }} + if: startsWith(matrix.target, 'apple') + run: | + set -eo pipefail + ROOT="${{ matrix.package_root }}" + TAG="${DISPATCH_TAG}" + if [ -z "$TAG" ]; then + TAG="${SKIA_BRANCH_SLUG}" + fi + if [ -z "$TAG" ]; then + TAG="${GITHUB_REF_NAME:-}" + fi + if [ -z "$TAG" ]; then + TAG="${GITHUB_SHA}" + fi + mkdir -p release-assets + # SPM binaryTarget requires one .xcframework per .zip archive, so each + # framework in the bundle is zipped separately (ditto preserves the + # symlinks inside .xcframework, unlike the default `zip` tool) and its + # SPM checksum recorded for the future Package.swift binaryTargets. + MANIFEST="release-assets/${{ matrix.artifact_name }}-${TAG}.checksums.txt" + : > "$MANIFEST" + for fw in "$ROOT"/*.xcframework; do + NAME="$(basename "$fw" .xcframework)" + ZIP_NAME="${{ matrix.artifact_name }}-${NAME}-${TAG}.zip" + ZIP_PATH="release-assets/$ZIP_NAME" + ditto -c -k --sequesterRsrc --keepParent "$fw" "$ZIP_PATH" + CHECKSUM="$(swift package compute-checksum "$ZIP_PATH")" + echo "$ZIP_NAME $CHECKSUM" >> "$MANIFEST" + done + shell: bash + env: + DISPATCH_TAG: ${{ needs.prepare-release.outputs.tag_name }} + SKIA_BRANCH_SLUG: ${{ needs.prepare-release.outputs.skia_branch_slug }} + + - name: Package Android binaries - ${{ matrix.artifact_name }} + if: startsWith(matrix.target, 'android') run: | set -eo pipefail ROOT="${{ matrix.package_root }}" @@ -198,8 +233,6 @@ jobs: ARCHIVE="${{ matrix.artifact_name }}-${TAG}.tar.gz" mkdir -p release-assets tar -czf "release-assets/$ARCHIVE" -C "$PARENT" "$BASENAME" - echo "asset_path=release-assets/$ARCHIVE" >> "$GITHUB_OUTPUT" - echo "asset_name=$ARCHIVE" >> "$GITHUB_OUTPUT" shell: bash env: DISPATCH_TAG: ${{ needs.prepare-release.outputs.tag_name }} @@ -212,7 +245,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ needs.prepare-release.outputs.tag_name }} - files: ${{ steps.package.outputs.asset_path }} + files: release-assets/* prerelease: true - name: Upload binaries as workflow artifact (dry run) - ${{ matrix.artifact_name }} @@ -220,7 +253,7 @@ jobs: uses: actions/upload-artifact@v7 with: name: ${{ matrix.artifact_name }} - path: ${{ steps.package.outputs.asset_path }} + path: release-assets/ retention-days: 7 - name: Package Graphite Headers diff --git a/.github/workflows/build-skia.yml b/.github/workflows/build-skia.yml index 3cf67a0710..983a166d90 100644 --- a/.github/workflows/build-skia.yml +++ b/.github/workflows/build-skia.yml @@ -129,8 +129,43 @@ jobs: ZERO_AR_DATE: 1 run: yarn build-skia ${{ matrix.target }} - - name: Package binaries - ${{ matrix.artifact_name }} - id: package + - name: Package Apple xcframeworks (per-framework zips) - ${{ matrix.artifact_name }} + if: startsWith(matrix.target, 'apple') + run: | + set -eo pipefail + ROOT="${{ matrix.package_root }}" + TAG="${DISPATCH_TAG}" + if [ -z "$TAG" ]; then + TAG="${SKIA_BRANCH_SLUG}" + fi + if [ -z "$TAG" ]; then + TAG="${GITHUB_REF_NAME:-}" + fi + if [ -z "$TAG" ]; then + TAG="${GITHUB_SHA}" + fi + mkdir -p release-assets + # SPM binaryTarget requires one .xcframework per .zip archive, so each + # framework in the bundle is zipped separately (ditto preserves the + # symlinks inside .xcframework, unlike the default `zip` tool) and its + # SPM checksum recorded for the future Package.swift binaryTargets. + MANIFEST="release-assets/${{ matrix.artifact_name }}-${TAG}.checksums.txt" + : > "$MANIFEST" + for fw in "$ROOT"/*.xcframework; do + NAME="$(basename "$fw" .xcframework)" + ZIP_NAME="${{ matrix.artifact_name }}-${NAME}-${TAG}.zip" + ZIP_PATH="release-assets/$ZIP_NAME" + ditto -c -k --sequesterRsrc --keepParent "$fw" "$ZIP_PATH" + CHECKSUM="$(swift package compute-checksum "$ZIP_PATH")" + echo "$ZIP_NAME $CHECKSUM" >> "$MANIFEST" + done + shell: bash + env: + DISPATCH_TAG: ${{ needs.prepare-release.outputs.tag_name }} + SKIA_BRANCH_SLUG: ${{ needs.prepare-release.outputs.skia_branch_slug }} + + - name: Package Android binaries - ${{ matrix.artifact_name }} + if: startsWith(matrix.target, 'android') run: | set -eo pipefail ROOT="${{ matrix.package_root }}" @@ -149,8 +184,6 @@ jobs: ARCHIVE="${{ matrix.artifact_name }}-${TAG}.tar.gz" mkdir -p release-assets tar -czf "release-assets/$ARCHIVE" -C "$PARENT" "$BASENAME" - echo "asset_path=release-assets/$ARCHIVE" >> "$GITHUB_OUTPUT" - echo "asset_name=$ARCHIVE" >> "$GITHUB_OUTPUT" shell: bash env: DISPATCH_TAG: ${{ needs.prepare-release.outputs.tag_name }} @@ -163,7 +196,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ needs.prepare-release.outputs.tag_name }} - files: ${{ steps.package.outputs.asset_path }} + files: release-assets/* prerelease: true - name: Upload binaries as workflow artifact (dry run) - ${{ matrix.artifact_name }} @@ -171,5 +204,5 @@ jobs: uses: actions/upload-artifact@v7 with: name: ${{ matrix.artifact_name }} - path: ${{ steps.package.outputs.asset_path }} + path: release-assets/ retention-days: 7 diff --git a/.gitmodules b/.gitmodules index 66a508ee5c..caf2bf8a17 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "externals/skia"] path = externals/skia url = https://chromium.googlesource.com/skia/ - branch = chrome/m152 + branch = chrome/m154 [submodule "externals/depot_tools"] path = externals/depot_tools url = https://chromium.googlesource.com/chromium/tools/depot_tools.git diff --git a/externals/depot_tools b/externals/depot_tools index 6afa997717..36a8df4ad0 160000 --- a/externals/depot_tools +++ b/externals/depot_tools @@ -1 +1 @@ -Subproject commit 6afa997717b2c0e1382e1465bedbe1a6855b9388 +Subproject commit 36a8df4ad006eaa0572fb446edb8145fe5403592 diff --git a/externals/skia b/externals/skia index 2a9b593bab..9f9e6beb75 160000 --- a/externals/skia +++ b/externals/skia @@ -1 +1 @@ -Subproject commit 2a9b593bab4b2fd019fa494c8d401ff1fab0b883 +Subproject commit 9f9e6beb751091ef69163d2b4fdcd1e711b594ac diff --git a/packages/skia/package.json b/packages/skia/package.json index 815dcb70bc..ba3eab3e38 100644 --- a/packages/skia/package.json +++ b/packages/skia/package.json @@ -51,7 +51,6 @@ "clang-format-ios": "find apple/ -iname '*.h' -o -iname '*.mm' -o -iname '*.cpp' | xargs clang-format -i", "clang-format-android": "find android/cpp/ -iname '*.h' -o -iname '*.m' -o -iname '*.cpp' | xargs clang-format -i", "clang-format-common": "find cpp/ \\( -path 'cpp//skia' -prune \\) -o \\( -iname '*.h' -o -iname '*.m' -o -iname '*.cpp' \\) -print | xargs clang-format -i", - "workflow-copy-libs": "tsx ./scripts/workflow-copy-libs.ts", "cpplint": "cpplint --linelength=230 --filter=-legal/copyright,-whitespace/indent,-whitespace/comments,-whitespace/ending_newline,-build/include_order,-runtime/references,-readability/todo,-whitespace/blank_line,-whitespace/todo,-runtime/int,-build/c++11,-whitespace/parens --exclude=package/cpp/skia --exclude=package/ios --exclude=package/android/build --exclude=package/node_modules --recursive package" }, "repository": { @@ -116,6 +115,7 @@ "eslint-config-react-native-wcandillon": "4.0.1", "eslint-plugin-import": "2.32.0", "eslint-plugin-react-hooks": "^5.2.0", + "extract-zip": "^2.0.1", "jest": "30.0.0", "jest-diff": "29.7.0", "jest-environment-jsdom": "30.0.0", diff --git a/packages/skia/scripts/install-skia-graphite.ts b/packages/skia/scripts/install-skia-graphite.ts index 6d2b0264a0..1c6a02c9d5 100644 --- a/packages/skia/scripts/install-skia-graphite.ts +++ b/packages/skia/scripts/install-skia-graphite.ts @@ -15,53 +15,75 @@ import { existsSync, mkdirSync, readFileSync, - readdirSync, rmSync, writeFileSync, } from "fs"; import https from "https"; import path from "path"; +import extractZip from "extract-zip"; import { extract } from "tar"; import { copyHeaders } from "./skia-configuration"; import { fileOps } from "./utils"; +// The xcframeworks bundled for each Apple platform. Each one is published as +// its own .zip (SPM's binaryTarget requires one .xcframework per archive), so +// this list drives both the download loop below and, eventually, the +// per-framework binaryTargets in Package.swift. +const APPLE_FRAMEWORKS = [ + "libskia", + "libskottie", + "libskparagraph", + "libsksg", + "libskshaper", + "libskunicode_core", + "libskunicode_libgrapheme", + "libsvg", +] as const; + // Graphite configuration +// +// NOTE: the "apple" checksums below are placeholders (build-skia-graphite.yml +// now zips each xcframework individually - see its "Package Apple +// xcframeworks" step). They must be regenerated from that workflow's +// `*.checksums.txt` release asset once this GRAPHITE_CONFIG.version has +// actually been built by build-skia-graphite.yml, or `yarn install-skia-graphite` +// will fail on the Apple download step. const GRAPHITE_CONFIG = { - version: "m152", + version: "m154", checksums: { - "android-armeabi-v7a": - "edc363fb63d3e629d7023d63d505c2d075030dfc40e04890d988eaff5c31c2fc", - "android-arm64-v8a": - "cefc18191d46deec3e164f3b717b06c6ae7845eeec21fe11cca53339063cf1e2", - "android-x86": - "78041f4d58fafda1821c96f83e4818cce492a7047b16f267527e3e140d0b4c0d", - "android-x86_64": - "8cfcd40b87b42aa0aaaba89852aa51f3316c90debcdf24dcc5a9b7b562a8aa6f", - "apple-ios-xcframeworks": - "9a64915dd95bf22ab3f38624ac2fc7e4a14cfd155f218170317ccef6ab9dfc69", - "apple-macos-xcframeworks": - "995cc77d575368d2df08a16ed0345395ba66308b00ba57ef190e70eb710d74f9", + android: { + "armeabi-v7a": "REPLACE_WITH_REAL_CHECKSUM", + "arm64-v8a": "REPLACE_WITH_REAL_CHECKSUM", + "x86": "REPLACE_WITH_REAL_CHECKSUM", + "x86_64": "REPLACE_WITH_REAL_CHECKSUM", + }, + apple: { + ios: Object.fromEntries( + APPLE_FRAMEWORKS.map((name) => [name, "REPLACE_WITH_REAL_CHECKSUM"]) + ) as Record<(typeof APPLE_FRAMEWORKS)[number], string>, + macos: Object.fromEntries( + APPLE_FRAMEWORKS.map((name) => [name, "REPLACE_WITH_REAL_CHECKSUM"]) + ) as Record<(typeof APPLE_FRAMEWORKS)[number], string>, + }, }, } as const; // Dawn prebuilt binaries. These are the exact artifacts react-native-webgpu // links; both packages must consume the same Dawn build so that only one Dawn // copy exists in an app that installs both, which is why the release tag is -// pinned here rather than derived from GRAPHITE_CONFIG.version. -// -// STALE: this is still the m150 Dawn (63f25feec51e9351fb25222b6d5de1af791d7c4f) -// while m152's DEPS pins 1e897275172a23f27b0022fa6beae3084ed54a9b. Bump this to -// dawn-chrome-m152 (with new checksums) once react-native-webgpu publishes that -// release; until then Graphite installs pair m152 Skia with m150 Dawn. +// pinned here rather than derived from GRAPHITE_CONFIG.version. Skia's DEPS at +// chrome/m154 pins Dawn @ 3d786993a7ded64c4ebb4884b9b079db9ad0e580 - keep this +// tag aligned with whatever milestone react-native-webgpu's Package.swift +// pins (its dawnReleaseTag fatalError check enforces the match at build time). const DAWN_CONFIG = { - releaseTag: "dawn-chrome-m150", + releaseTag: "dawn-chrome-m154", baseUrl: "https://github.com/wcandillon/react-native-webgpu/releases/download", checksums: { - android: "dee507d4fe66b57f6d33c0dac8cfd1b1263fcd9a8997bfebf936b62341580e60", - apple: "5bacd90c56aa3144d8ba74ac2d769d28b28ebf99c67a4959185c7181d8aeaf31", + android: "6fe8766dc3711e1e41e8b9d919fadd83f0e364945e66c01f49f316f4a3d96b1f", + apple: "896575ffbc99610198a83d061f8c5a2789139b7f16c669fbf3a9f7a7ac9be123", }, } as const; @@ -189,6 +211,39 @@ const downloadAndExtract = async ( rmSync(tempFile, { force: true }); }; +// Download and extract a single-xcframework .zip asset (SPM-compatible +// packaging - see APPLE_FRAMEWORKS above) into destDir. +const downloadAndExtractZip = async ( + assetName: string, + destDir: string, + expectedChecksum: string, + urlOverride?: string +): Promise => { + const url = urlOverride ?? getDownloadUrl(assetName); + const tempFile = path.join(LIBS_DIR, `${assetName}.tmp`); + + console.log(` Downloading ${assetName}...`); + await downloadFile(url, tempFile); + + console.log(` Verifying checksum...`); + const actualChecksum = calculateFileChecksum(tempFile); + if (actualChecksum !== expectedChecksum) { + rmSync(tempFile, { force: true }); + throw new Error( + `Checksum mismatch for ${assetName}:\n` + + ` Expected: ${expectedChecksum}\n` + + ` Actual: ${actualChecksum}` + ); + } + console.log(` āœ“ Checksum verified`); + + console.log(` Extracting to ${destDir}...`); + mkdirSync(destDir, { recursive: true }); + await extractZip(tempFile, { dir: destDir }); + + rmSync(tempFile, { force: true }); +}; + // Checkout the Skia submodule to the correct branch const checkoutSkiaSubmodule = (): void => { const baseVersion = getBaseVersion(GRAPHITE_CONFIG.version); @@ -276,9 +331,10 @@ const downloadAndroidLibs = async (): Promise => { const releaseTag = getReleaseTag(GRAPHITE_CONFIG.version); for (const abi of androidAbis) { - const checksumKey = - `android-${abi.name}` as keyof typeof GRAPHITE_CONFIG.checksums; - const expectedChecksum = GRAPHITE_CONFIG.checksums[checksumKey]; + const expectedChecksum = + GRAPHITE_CONFIG.checksums.android[ + abi.name as keyof typeof GRAPHITE_CONFIG.checksums.android + ]; const assetName = `skia-graphite-${abi.asset}-${releaseTag}.tar.gz`; const destDir = path.join(LIBS_DIR, "android", abi.name); @@ -334,10 +390,12 @@ const downloadDawnLibs = async (): Promise => { rmSync(androidTempDir, { recursive: true, force: true }); // Apple: one xcframework carrying ios-device, ios-simulator and macos - // slices; the podspec vendors it from both platform dirs - const appleAsset = `dawn-apple-${DAWN_CONFIG.releaseTag}.xcframework.tar.gz`; + // slices; the podspec vendors it from both platform dirs. Published as a + // .zip (not .tar.gz) since it's the same SPM-compatible artifact + // react-native-webgpu's Package.swift binaryTarget links. + const appleAsset = `dawn-apple-${DAWN_CONFIG.releaseTag}.xcframework.zip`; const appleTempDir = path.join(LIBS_DIR, "dawn-apple-temp"); - await downloadAndExtract( + await downloadAndExtractZip( appleAsset, appleTempDir, DAWN_CONFIG.checksums.apple, @@ -357,59 +415,46 @@ const downloadDawnLibs = async (): Promise => { console.log(` āœ“ Dawn libraries downloaded`); }; -// Download Apple libraries -const downloadAppleLibs = async (): Promise => { - console.log(`\nšŸŽ Downloading Apple Graphite libraries...`); - +// Download Apple libraries. Each xcframework is published as its own zip +// (see APPLE_FRAMEWORKS), named +// `skia-graphite-apple--xcframeworks--.zip`. +const downloadApplePlatformLibs = async ( + platform: "ios" | "macos", + artifactPrefix: string +): Promise => { const releaseTag = getReleaseTag(GRAPHITE_CONFIG.version); - const iosDir = path.join(LIBS_DIR, "ios"); - const macosDir = path.join(LIBS_DIR, "macos"); - - // Clean and create destination directories - fileOps.rm(iosDir); - fileOps.rm(macosDir); - fileOps.mkdir(iosDir); - fileOps.mkdir(macosDir); - - // Download iOS xcframeworks - const iosAsset = `skia-graphite-apple-ios-xcframeworks-${releaseTag}.tar.gz`; - const iosTempDir = path.join(LIBS_DIR, "apple-ios-temp"); - await downloadAndExtract( - iosAsset, - iosTempDir, - GRAPHITE_CONFIG.checksums["apple-ios-xcframeworks"] - ); + const destDir = path.join(LIBS_DIR, platform); + fileOps.rm(destDir); + fileOps.mkdir(destDir); - const extractedIosDir = path.join(iosTempDir, "ios"); - if (existsSync(extractedIosDir)) { - const xcframeworks = readdirSync(extractedIosDir).filter( - (f) => f.endsWith(".xcframework") && f !== "libdawn_combined.xcframework" - ); - for (const xcf of xcframeworks) { - fileOps.cp(path.join(extractedIosDir, xcf), path.join(iosDir, xcf)); - } - } - rmSync(iosTempDir, { recursive: true, force: true }); + const checksums = GRAPHITE_CONFIG.checksums.apple[platform]; + for (const framework of APPLE_FRAMEWORKS) { + const assetName = `${artifactPrefix}-${framework}-${releaseTag}.zip`; + const tempDir = path.join(LIBS_DIR, `apple-${platform}-${framework}-temp`); - // Download macOS xcframeworks - const macosAsset = `skia-graphite-apple-macos-xcframeworks-${releaseTag}.tar.gz`; - const macosTempDir = path.join(LIBS_DIR, "apple-macos-temp"); - await downloadAndExtract( - macosAsset, - macosTempDir, - GRAPHITE_CONFIG.checksums["apple-macos-xcframeworks"] - ); + await downloadAndExtractZip(assetName, tempDir, checksums[framework]); - const extractedMacosDir = path.join(macosTempDir, "macos"); - if (existsSync(extractedMacosDir)) { - const xcframeworks = readdirSync(extractedMacosDir).filter( - (f) => f.endsWith(".xcframework") && f !== "libdawn_combined.xcframework" - ); - for (const xcf of xcframeworks) { - fileOps.cp(path.join(extractedMacosDir, xcf), path.join(macosDir, xcf)); + const xcfName = `${framework}.xcframework`; + const extracted = path.join(tempDir, xcfName); + if (!existsSync(extracted)) { + throw new Error(`Missing ${xcfName} in ${assetName}`); } + fileOps.cp(extracted, path.join(destDir, xcfName)); + rmSync(tempDir, { recursive: true, force: true }); } - rmSync(macosTempDir, { recursive: true, force: true }); +}; + +const downloadAppleLibs = async (): Promise => { + console.log(`\nšŸŽ Downloading Apple Graphite libraries...`); + + await downloadApplePlatformLibs( + "ios", + "skia-graphite-apple-ios-xcframeworks" + ); + await downloadApplePlatformLibs( + "macos", + "skia-graphite-apple-macos-xcframeworks" + ); console.log(` āœ“ Apple libraries downloaded`); }; diff --git a/packages/skia/scripts/skia-configuration.ts b/packages/skia/scripts/skia-configuration.ts index ccbb9de98a..f7941cb9e1 100644 --- a/packages/skia/scripts/skia-configuration.ts +++ b/packages/skia/scripts/skia-configuration.ts @@ -502,6 +502,12 @@ export const copyHeaders = () => { "../../externals/skia/third_party/externals/dawn/include", "./cpp/dawn/include" ); + // Dawn's cmake build (as of the Dawn revision pinned at chrome/m154) + // also generates a webgpu_upstream/ copy of webgpu_cpp.h and friends + // under its own path. Nothing in this repo or react-native-webgpu + // includes from webgpu_upstream/, and leaving it in trips the + // duplicate-header check below (same basenames as dawn/ and webgpu/). + fileOps.rm("./cpp/dawn/include/webgpu_upstream"); console.log(" - Fixing WebGPU header references..."); // Fix WebGPU header references diff --git a/packages/skia/scripts/workflow-copy-libs.ts b/packages/skia/scripts/workflow-copy-libs.ts deleted file mode 100644 index a7d8417a60..0000000000 --- a/packages/skia/scripts/workflow-copy-libs.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { existsSync, writeFileSync } from "fs"; - -import { ensureFolderExists, copyRecursiveSync } from "./utils"; -import { GRAPHITE } from "./skia-configuration"; -/** - * This build script prepares the npm build command by copying - * the Skia Binaries from the artifact folder into the libs folder - * in the checkout directory. This build script is run by the - * build-npm.yml workflow and does not require anything. - */ - -console.log("Copying Skia Binaries from artifacts to libs folder"); - -const suffix = GRAPHITE ? "-graphite" : ""; -const sources = [ - `./skia${suffix}-android-arm`, - `./skia${suffix}-android-arm-64`, - `./skia${suffix}-android-arm-x86`, - `./skia${suffix}-android-arm-x64`, -]; - -const destinations = ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"]; - -const androidFiles = [ - "libskia.a", - "libskshaper.a", - "libsvg.a", - "libskottie.a", - "libsksg.a", - "libskparagraph.a", - "libskunicode_core.a", - "libskunicode_icu.a", - "libjsonreader.a", - // Dawn library for Graphite builds - ...(GRAPHITE ? ["libdawn_combined.a"] : []), -]; - -const appleFiles = [ - "libskia.xcframework", - "libskshaper.xcframework", - "libsvg.xcframework", - "libskottie.xcframework", - "libsksg.xcframework", - "libskparagraph.xcframework", - "libskunicode_core.xcframework", - "libskunicode_libgrapheme.xcframework", - // Dawn library for Graphite builds - ...(GRAPHITE ? ["libdawn_combined.xcframework"] : []), -]; - -const copyFiles = (from: string, to: string, files: string[]) => { - ensureFolderExists(to); - files.forEach((f) => { - const source = "./artifacts/" + from + "/" + f; - const target = to + "/" + f; - if (!existsSync(source)) { - console.log( - "Copying failed, the artifact source", - source, - "was not found. Current dir is:", - process.cwd() - ); - process.exit(1); - } - if (!existsSync(to)) { - console.log( - "Copying failed, the destination", - to, - "was not found. Current dir is:", - process.cwd() - ); - process.exit(1); - } - copyRecursiveSync(source, target); - console.log("Copied", source, target); - }); -}; - -console.log("Copying android files..."); -destinations.forEach((d, i) => { - copyFiles(sources[i], "./libs/android/" + d, androidFiles); -}); - -console.log("Copying Apple files..."); -copyFiles(`skia${suffix}-apple-ios-xcframeworks`, "./libs/ios", appleFiles); -copyFiles(`skia${suffix}-apple-macos-xcframeworks`, "./libs/macos", appleFiles); - -if (GRAPHITE) { - writeFileSync("./libs/.graphite", ""); - console.log("Created libs/.graphite marker file"); -} - -// Copy skia-headers and skia-graphite-headers artifacts to ./cpp/ -if (GRAPHITE) { - [`skia${suffix}-headers`].forEach((headerArtifact) => { - const source = `./artifacts/${headerArtifact}`; - const target = "./cpp"; - if (existsSync(source)) { - copyRecursiveSync(source, target); - console.log(`Copied headers from ${source} to ${target}`); - } else { - console.log(`Header artifact ${source} not found, skipping.`); - } - }); -} - -console.log("Done copying artifacts."); diff --git a/yarn.lock b/yarn.lock index ed7bb4e259..31ee618f45 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9091,6 +9091,7 @@ __metadata: eslint-config-react-native-wcandillon: 4.0.1 eslint-plugin-import: 2.32.0 eslint-plugin-react-hooks: ^5.2.0 + extract-zip: ^2.0.1 jest: 30.0.0 jest-diff: 29.7.0 jest-environment-jsdom: 30.0.0 @@ -16589,7 +16590,7 @@ __metadata: languageName: node linkType: hard -"extract-zip@npm:2.0.1": +"extract-zip@npm:2.0.1, extract-zip@npm:^2.0.1": version: 2.0.1 resolution: "extract-zip@npm:2.0.1" dependencies: From a06254b45ccd132f9bf74fcb0b966f8332578aa7 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Sat, 12 Sep 2026 19:13:24 +0200 Subject: [PATCH 2/3] :wrench: (#4060) --- apps/example/ios/Podfile.lock | 144 +++++++++--------- apps/example/package.json | 2 +- packages/skia/cpp/rnskia/RNDawnContext.h | 3 +- packages/skia/package.json | 14 +- .../skia/scripts/install-skia-graphite.ts | 69 ++++++--- yarn.lock | 50 +++--- 6 files changed, 158 insertions(+), 124 deletions(-) diff --git a/apps/example/ios/Podfile.lock b/apps/example/ios/Podfile.lock index c2ac90f9f2..73499d6e30 100644 --- a/apps/example/ios/Podfile.lock +++ b/apps/example/ios/Podfile.lock @@ -2009,7 +2009,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - react-native-webgpu (0.8.0): + - react-native-webgpu (0.10.0): - boost - DoubleConversion - fast_float @@ -3239,89 +3239,89 @@ SPEC CHECKSUMS: fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 hermes-engine: 11b010917f5f15150b2c015abddef1573d2bb05d - RCT-Folly: 59ec0ac1f2f39672a0c6e6cecdd39383b764646f + RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 RCTDeprecation: a41bbdd9af30bf2e5715796b313e44ec43eefff1 RCTRequired: 7be34aabb0b77c3cefe644528df0fa0afad4e4d0 RCTSwiftUI: a6c7271c39098bf00dbdad8f8ed997a59bbfbe44 - RCTSwiftUIWrapper: 5ec163e8fde163d3fba714a992b50a266e1ece37 + RCTSwiftUIWrapper: ff9098ccf7727e58218f2f8ea110349863f43438 RCTTypeSafety: 27927d0ca04e419ed9467578b3e6297e37210b5c React: 4bc1f928568ad4bcfd147260f907b4ea5873a03b React-callinvoker: 87f8728235a0dc62e9dc19b3851c829d9347d015 - React-Core: 19e0183e28d7a6613ecacebd7525fe6650efa3b6 - React-CoreModules: 73cc86f2a0ff84b93d6325073ad2e4874d21ad40 - React-cxxreact: 4bf734645c77c9b86e2f3e933e0411cf2f14d1ba + React-Core: 76bed73b02821e5630e7f2cb2e82432ee964695d + React-CoreModules: 752dbfdaeb096658aa0adc4a03ba6214815a08df + React-cxxreact: b6798528aa601c6db66e6adc7e2da2b059c8be74 React-debug: 8978deb306f6f38c28b5091e52b0ac9f942b157e - React-defaultsnativemodule: 724eb9ec388d494f1e2057d83355ee8fe6f1d780 - React-domnativemodule: 9068f41092f725acd09950233d2847364c731947 - React-Fabric: 945cc8abf08d9d0966acef605bffce7b501c49d9 - React-FabricComponents: 4c4ad6f0d16c964a68f945e029505e2eeec6654b - React-FabricImage: a8b628fd98db21b9f8588e06f14a9194dda11b40 - React-featureflags: 0937601c1af1cc125851ec5bbf4654285d47a3e7 - React-featureflagsnativemodule: ac1a3e0353e1a6e15411b17ed6c7122adb0468a4 - React-graphics: cca521e06463608be46207a4aa160f8a7f725f8b - React-hermes: ec50b9fcea2c3bfdd42f8cec845eac3f35888572 - React-idlecallbacksnativemodule: effcae5b7b4473211adb154aaa321d5d9e2fbcc9 - React-ImageManager: b38459e538f1840fa5c3e7612a4bcb0029a3c366 - React-intersectionobservernativemodule: 8d33366661971200cf2e151727f6fe007b62ae7b - React-jserrorhandler: f94c688a0dbe2e045b91b992722b92e97d56f77f - React-jsi: 3216c876cd4c571a57909e22d77c8fd9530aa067 - React-jsiexecutor: 475563c0042841a85930a455d3199f6b1483a5fe - React-jsinspector: bc484fb32bf1b9fed80afe8793e614eba4f7b39e - React-jsinspectorcdp: 5a574d1d35016968a67e78e6b8a7917473ffbb77 - React-jsinspectornetwork: dce3a5a1351b527ee8c28ad4a8bdd211507e1a45 - React-jsinspectortracing: 65f6b166bd67e5adc31eba027e1570bacf7a3cc7 - React-jsitooling: d5463f5489a31640b0fa0ec4e31566ca8aa86c13 - React-jsitracing: 3c7fc18821aba64855acb8658aa857ca6a7fddf6 - React-logger: 6ac901f5c7f7321d2be1a40b203bccc2e23411e3 - React-Mapbuffer: 2e0e7cc5b7064eaed9c8b8afc3a87621cb7ef5cd - React-microtasksnativemodule: dd4d33b251b57e5027c572c6d0b45cbfbcfaa386 - react-native-safe-area-context: 54d812805f3c4e08a4580ad086cbde1d8780c2e4 - react-native-skia: 765e9975e9ccde5ce3c9a0f740f195108c7d0280 - react-native-webgpu: d7021164f316160273312cc13d9d4e0ea0a1308b - React-NativeModulesApple: 7f2f2fed3f6c858889eb61d09941be965d52df58 - React-networking: 43e5e6773ac2ca2a93261a1388fed269c9fce092 + React-defaultsnativemodule: 682b77ef4acfb298017be15f4f93c1d998deb174 + React-domnativemodule: 4c4b44f7eb68dbc3a2218db088bef318a7302017 + React-Fabric: b6f82a4d8498ce4475586f71ca8397a771fe292d + React-FabricComponents: c8695f4b11918a127c4560d66f7d3fdb01a17986 + React-FabricImage: d64f48830f63830e8ffaaf69fa487116856fbbf1 + React-featureflags: 2a46b229903e906d33dbaf9207ce57c59306c369 + React-featureflagsnativemodule: cba6c0814051a0934f8bcee4a436ee2a6bcc9754 + React-graphics: 3d0435051e1ab8904d065f8ffbe981a9fc202841 + React-hermes: 32fc9c231c1aa5c2fcfe851b0d19ee9269f88f4c + React-idlecallbacksnativemodule: f8ee42581795c4844d97147596bcc2d824c0f188 + React-ImageManager: e8f7377ef0585fd2df05559a17e01a03e187d5cf + React-intersectionobservernativemodule: b1bea12ca29accdd2eda60c87605a6030b894eb9 + React-jserrorhandler: 1a86df895b4eaf4e771abe8cf34cbb26d821f771 + React-jsi: adf8527fec197ad9d0480cc5b8945eb56de627f0 + React-jsiexecutor: 315fa2f879b43e3a9ca08f5f4b733472f7e4e8a4 + React-jsinspector: b4fd1933666bcb2549b566b40656c1e45e9d7632 + React-jsinspectorcdp: 80141710f2668e5b8f00417298d9b76b4abf90fa + React-jsinspectornetwork: 1d3ea717dbbec316cd8c21a0af53928a7bf74901 + React-jsinspectortracing: 4ce745374d4b2bfbd164cce9f8de8383d3d818a0 + React-jsitooling: fc4ac4c3b1f3f9f7fedf0c777c6ff3f244f568bd + React-jsitracing: bff08a6faeef4a9bd286487da191f5e5329e21a9 + React-logger: b8483fa08e0d62e430c76d864309d90576ca2f68 + React-Mapbuffer: 7b72a669e94662359dad4f42b5af005eb24b4e83 + React-microtasksnativemodule: cdc02da075f2857803ed63f24f5f72fc40e094c0 + react-native-safe-area-context: c00143b4823773bba23f2f19f85663ae89ceb460 + react-native-skia: 398d81d07392758762cf114280cceb24ebc9b6f3 + react-native-webgpu: 64a2e7f8241cdb08c0e43c7e51fbb9619109aa41 + React-NativeModulesApple: a2c3d2cbec893956a5b3e4060322db2984fff75b + React-networking: 3f98bd96893a294376e7e03730947a08d474c380 React-oscompat: 80166b66da22e7af7fad94474e9997bd52d4c8c6 - React-perflogger: 63c90e0d8c24df87ffa14dad01aeafc352847dd0 - React-performancecdpmetrics: 5a9b81c08f75045635127d626440d9ada01e774b - React-performancetimeline: 31cebfff69ec9174b3fb54b0606fcb12ef91cbad + React-perflogger: d6797918d2b1031e91a9d8f5e7fdd2c8728fb390 + React-performancecdpmetrics: 5570be61e2f97c4741c5d432c91570e8e5a39892 + React-performancetimeline: 5763499ae1991fc18dcf416e340ce7bc829bb298 React-RCTActionSheet: 3bd5f5db9f983cf38d51bb9a7a198e2ebea94821 - React-RCTAnimation: 346865a809fa5132f6c594c8b376c6cf46b44e88 - React-RCTAppDelegate: b2d1e0d3663c987f49f45094883b9e36fcbf0181 - React-RCTBlob: 74759ebb7ff9077d19f60c301782c1f8c3eb2813 - React-RCTFabric: 7b4b14dad21ca99333ebcbc0bf5c205647a315a8 - React-RCTFBReactNativeSpec: 39151968adb68b8c59f29a8bd4223d4d7780a793 - React-RCTImage: 60763f56e8a5e45d861d7c4777e428bb820ec52a - React-RCTLinking: 52aee78b0b3163167c7fcf58f80a42943c03a056 - React-RCTNetwork: f5e1e8ae5eff6982efff6289b06ec0a76d0a6ac2 - React-RCTRuntime: 0e99199322afd372e74b95ae5c58f4e074cc2855 - React-RCTSettings: 298bb40d3412bf32e0b4f0797e48416b0b7278a1 - React-RCTText: dfb74800e27d792d1188fa975a3b9807c3362e3e - React-RCTVibration: ffe5fd4f50a835e353a3b6869eb005dab11eea44 + React-RCTAnimation: 46a9978f27dc434dbeed16afa7b82619b690a9af + React-RCTAppDelegate: 62ecd60a2b2a8cae26ce6a066bfa59cfde97af01 + React-RCTBlob: 8285c859513023ee3cc8c806d9b59d4da078c4ba + React-RCTFabric: 05ed09347e938de985052f791a6a0698816d5761 + React-RCTFBReactNativeSpec: 83ba579fca9a51e774ac32578ef5dd3262edd7e2 + React-RCTImage: a5364d0f098692cfbf5bef1e8a63e7712ecb14b7 + React-RCTLinking: 34b63b0aa0e92d30f5d7aa2c255a8f95fa75ee8f + React-RCTNetwork: 1ef88b7a5310b8f915d3556b5b247def113191ed + React-RCTRuntime: ed29cf68a46782fec891e5afe1d8d758ca6ccd9b + React-RCTSettings: 2c45623d6c0f30851a123f621eb9d32298bcbb0c + React-RCTText: 0ee70f5dc18004b4d81b2c214267c6cbec058587 + React-RCTVibration: 88557e21e7cc3fe76b5b174cba28ff45c6def997 React-rendererconsistency: d280314a3e7f0097152f89e815b4de821c2be8b9 - React-renderercss: 8a1a346f3665fd5ea7a7be7b3b9f95d4743e1180 - React-rendererdebug: af74afdfb3d6c5382ebab35562efd8eb9e690473 - React-RuntimeApple: 06e33d291e72fd0c73ac47046c3536d77d5aeedd - React-RuntimeCore: 99273d2af072062eb07f0b2d2d4a0f2de697ea14 - React-runtimeexecutor: 2063c03c18810ee57939d138142e6493333360ef - React-RuntimeHermes: 2253a7f4c8d56b449230b330b0b15383ed4b3df4 - React-runtimescheduler: ff37ac6720a943da91645c06274282ac46b71f23 - React-timing: 831d7e081ba4c332ca5cccf389b88e363f13f2b4 - React-utils: 25db6c17598c4fed22b5956d7551bb8bddf1f95b - React-webperformancenativemodule: 57e41e6193cfb815bde0b5534bef68673f1270eb - ReactAppDependencyProvider: bfb12ead469222b022a2024f32aba47ce50de512 - ReactCodegen: 9ca1bd49eee1eccf6e427e406d2163f49e9c48c0 - ReactCommon: 05ad684db7d88e194272ae26baddf6300e30b8b7 - ReactNativeHost: e7e0a518b0120f0070b3e1f13c7006d3e0e8ee13 - ReactTestApp-DevSupport: 6994b53b5b81139a8ce63e0776c726c95de079a1 + React-renderercss: f8cbf83d95c2c2bbf893d37fe50c73f046584411 + React-rendererdebug: 37216ddfcd38e49d1e92bf9052ea4bc9d7b932e5 + React-RuntimeApple: 1c0e7cb8e1c2c5775585afcaaa666ec151629a8d + React-RuntimeCore: 925fe2ca24cf8e6ed87586dbb92827306b93b83f + React-runtimeexecutor: 962dae024f6df760d029512a7d99e3f73d570935 + React-RuntimeHermes: 19a7c59ec1bc9908516f0bbc29b49425f6ec64ba + React-runtimescheduler: 62f21127cd97f4d8f164eee5150d3ce53dd36f66 + React-timing: 8757bf6fb96227c264f2d1609f4ba5c68217b8ce + React-utils: 8ab26781c2f5c2f7fafb2022c8ab39d39f231b80 + React-webperformancenativemodule: 7953b7fe519f76fa595111fe18ff3d5de131bfe9 + ReactAppDependencyProvider: 0eb286cc274abb059ee601b862ebddac2e681d01 + ReactCodegen: b8e56b780fffe6edd6405be0af4a1e3049a937f7 + ReactCommon: ac934cb340aee91282ecd6f273a26d24d4c55cae + ReactNativeHost: eef98ec49b55d88ad4cabf5a4378a12b42b551ee + ReactTestApp-DevSupport: ea18f446cff64b6c9a3e28788600c82ecf51bde6 ReactTestApp-Resources: 1bd9ff10e4c24f2ad87101a32023721ae923bccf - RNGestureHandler: 77eecab5fd636666ca73a55bb61e2f1a685b7e84 - RNReanimated: d1a7a4c20eefc371e062990ce1debeaff4f1b9be - RNScreens: b2a5c76af24a02a2fd71bfce42780fdd9c79cc6d - RNSVG: ea9cbf6dcdbebdfff5822b0ad9311bbc4510a0b7 - RNWorklets: 5f6e5664c1819eac103ca75cc2f36191f55aa110 + RNGestureHandler: cd4be101cfa17ea6bbd438710caa02e286a84381 + RNReanimated: c26dfcd831add485c2ed93de9d7bfb90b035eeaa + RNScreens: 714e10b6b554f7dc7ad9f78dcf36dc8e3fc73415 + RNSVG: 11354d28dd6cb71a59570b68c91ba6772a2d781d + RNWorklets: b89b501d37972e6419d6f87effe41d6d76157648 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: 5bd0956bf9cb16f75101e78b5e852c7577bc5a45 + Yoga: 5456bb010373068fc92221140921b09d126b116e PODFILE CHECKSUM: d3796f1f719a0788265d1cd391be0a04200ce6cd -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/apps/example/package.json b/apps/example/package.json index bc2005a973..ad2fff4441 100644 --- a/apps/example/package.json +++ b/apps/example/package.json @@ -40,7 +40,7 @@ "react-native-screens": "^4.10.0", "react-native-svg": "patch:react-native-svg@npm%3A15.14.0#../../.yarn/patches/react-native-svg-npm-15.14.0-macos-uiimage-fix.patch", "react-native-web": "^0.21.2", - "react-native-webgpu": "0.8.0", + "react-native-webgpu": "0.10.0", "react-native-windows": "^0.75.0", "react-native-worklets": "0.8.3" }, diff --git a/packages/skia/cpp/rnskia/RNDawnContext.h b/packages/skia/cpp/rnskia/RNDawnContext.h index ac028fd255..3cb25c43ef 100644 --- a/packages/skia/cpp/rnskia/RNDawnContext.h +++ b/packages/skia/cpp/rnskia/RNDawnContext.h @@ -147,7 +147,8 @@ class DawnContext { vkBegin.newLayout = 0; beginAccessDesc.nextInChain = &vkBegin; #endif - bool success = memory.BeginAccess(texture, &beginAccessDesc); + bool success = + memory.BeginAccess(texture, &beginAccessDesc) == wgpu::Status::Success; if (success) { skgpu::graphite::BackendTexture betFromView = diff --git a/packages/skia/package.json b/packages/skia/package.json index ba3eab3e38..999c56c467 100644 --- a/packages/skia/package.json +++ b/packages/skia/package.json @@ -139,16 +139,16 @@ }, "dependencies": { "canvaskit-wasm": "0.41.0", - "react-native-skia-android": "152.0.0", - "react-native-skia-apple-ios": "152.0.0", - "react-native-skia-apple-macos": "152.0.0", - "react-native-skia-apple-tvos": "152.0.0", + "react-native-skia-android": "154.0.0", + "react-native-skia-apple-ios": "154.0.0", + "react-native-skia-apple-macos": "154.0.0", + "react-native-skia-apple-tvos": "154.0.0", "react-reconciler": "0.31.0" }, "graphiteDependencies": { - "react-native-skia-graphite-android": "152.0.0", - "react-native-skia-graphite-apple-ios": "152.0.0", - "react-native-skia-graphite-apple-macos": "152.0.0" + "react-native-skia-graphite-android": "154.0.0", + "react-native-skia-graphite-apple-ios": "154.0.0", + "react-native-skia-graphite-apple-macos": "154.0.0" }, "eslintIgnore": [ "node_modules/", diff --git a/packages/skia/scripts/install-skia-graphite.ts b/packages/skia/scripts/install-skia-graphite.ts index 1c6a02c9d5..8464e3202f 100644 --- a/packages/skia/scripts/install-skia-graphite.ts +++ b/packages/skia/scripts/install-skia-graphite.ts @@ -43,30 +43,63 @@ const APPLE_FRAMEWORKS = [ ] as const; // Graphite configuration -// -// NOTE: the "apple" checksums below are placeholders (build-skia-graphite.yml -// now zips each xcframework individually - see its "Package Apple -// xcframeworks" step). They must be regenerated from that workflow's -// `*.checksums.txt` release asset once this GRAPHITE_CONFIG.version has -// actually been built by build-skia-graphite.yml, or `yarn install-skia-graphite` -// will fail on the Apple download step. const GRAPHITE_CONFIG = { version: "m154", checksums: { android: { - "armeabi-v7a": "REPLACE_WITH_REAL_CHECKSUM", - "arm64-v8a": "REPLACE_WITH_REAL_CHECKSUM", - "x86": "REPLACE_WITH_REAL_CHECKSUM", - "x86_64": "REPLACE_WITH_REAL_CHECKSUM", + "armeabi-v7a": + "2035f570a696da94ee73c1ac9fcdc3e86e128e0c022ea54b3d15a91aa6310382", + "arm64-v8a": + "adbb7179c8e4d743c6b5459902937b29568fe11c5926f04d7710b70c55587d42", + "x86": "cb0f02edbb469f3f0e26edb8f61d41c0a3b9878346df2908a192f1de18892a52", + "x86_64": + "51892783c32c23fb67e1db2eb74700cbb21f66ed9710d5dbc3c603cbf7f1174a", }, apple: { - ios: Object.fromEntries( - APPLE_FRAMEWORKS.map((name) => [name, "REPLACE_WITH_REAL_CHECKSUM"]) - ) as Record<(typeof APPLE_FRAMEWORKS)[number], string>, - macos: Object.fromEntries( - APPLE_FRAMEWORKS.map((name) => [name, "REPLACE_WITH_REAL_CHECKSUM"]) - ) as Record<(typeof APPLE_FRAMEWORKS)[number], string>, - }, + ios: { + libskia: + "03c11d6891d9710f644f6a2108c494a1fe1a7b1105748686176fd7d9ae6cfa7e", + libskottie: + "f2bf18b9a3221661534764e5585ca8e2b82afeaeb9f88f1cd5177ffe7029550e", + libskparagraph: + "0f938255ab9f174af83f3cff56aa73235990154ea8d493c4bfc74ac94bffeb42", + libsksg: + "2ecb0c45a6822e5bfca0c78520273ac173a795c83a857fed775cd26d34b06950", + libskshaper: + "3c2553852cc94ba66537e3f5b923ecc4e77d3ec299ea875360bbfd72a0e375bf", + // eslint-disable-next-line camelcase + libskunicode_core: + "a6c42ec4cabf1e51c51d58490bc86904237fb37f5f2455119185882db05f62d2", + // eslint-disable-next-line camelcase + libskunicode_libgrapheme: + "2eac399778103b8b0ae653483bbd25d5cf4df6890bc5bf9db87d64b9e7e12215", + libsvg: + "3ce34cab4f82fa14da97a10eb497027af1bf59e210cde66e5f78af4a4c009cb7", + }, + macos: { + libskia: + "4077f56da97cec4462c0819201dda6c8fe5f375f1e3fcad2b1ddb44101ec857f", + libskottie: + "4d6bbf98f008993b28d327c16477ca4d0f6f9f60f7d6186814070ea4613b08e1", + libskparagraph: + "9b50c4fc121355564f5985b511a969f445e9e894c1d8594778c805355e674391", + libsksg: + "8303505887aaf3646933180953562a401935d5f2a2384819930b077fa56da801", + libskshaper: + "15339f4a5f4cab50e39d457ffb0d0a3c23ad552f0447a0d4404718600baeb260", + // eslint-disable-next-line camelcase + libskunicode_core: + "de677cdc80b76191dcbd199f1866d6b5b258cd9bf422ac8a8574492cf0a4a9cb", + // eslint-disable-next-line camelcase + libskunicode_libgrapheme: + "562daca2f2c26fbc35cbb3772429d61a4c40005768cc754f0bff4c2fc39f559a", + libsvg: + "0e4fb622a1ce003372a3a005ca3a5166659766046a13e9ab978e9a989d617845", + }, + } as Record< + "ios" | "macos", + Record<(typeof APPLE_FRAMEWORKS)[number], string> + >, }, } as const; diff --git a/yarn.lock b/yarn.lock index 31ee618f45..940a9f859e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9103,10 +9103,10 @@ __metadata: react-native: 0.83.1 react-native-builder-bob: 0.18.2 react-native-reanimated: 4.3.1 - react-native-skia-android: 152.0.0 - react-native-skia-apple-ios: 152.0.0 - react-native-skia-apple-macos: 152.0.0 - react-native-skia-apple-tvos: 152.0.0 + react-native-skia-android: 154.0.0 + react-native-skia-apple-ios: 154.0.0 + react-native-skia-apple-macos: 154.0.0 + react-native-skia-apple-tvos: 154.0.0 react-native-worklets: 0.8.3 react-reconciler: 0.31.0 rimraf: 3.0.2 @@ -16340,7 +16340,7 @@ __metadata: react-native-svg: "patch:react-native-svg@npm%3A15.14.0#../../.yarn/patches/react-native-svg-npm-15.14.0-macos-uiimage-fix.patch" react-native-test-app: 4.4.7 react-native-web: ^0.21.2 - react-native-webgpu: 0.8.0 + react-native-webgpu: 0.10.0 react-native-windows: ^0.75.0 react-native-worklets: 0.8.3 react-test-renderer: 19.0.0 @@ -27159,31 +27159,31 @@ __metadata: languageName: node linkType: hard -"react-native-skia-android@npm:152.0.0": - version: 152.0.0 - resolution: "react-native-skia-android@npm:152.0.0" - checksum: c01ae79040ea04997340861828d79fb4591ad922a50a94cbcdb1b4d7f4e7fa1ad9a892cdb87f40a059489350cb2cfdd647448570d109dcab25342d9d287d1bbc +"react-native-skia-android@npm:154.0.0": + version: 154.0.0 + resolution: "react-native-skia-android@npm:154.0.0" + checksum: 7428991fc10e5d489a17f5049579b3ac31e949a070ecbcd3b2bbcefdce43c137eb749ab5ecf08cd36c2f03828a893ab00499f994a1c6f2224f798a473471e818 languageName: node linkType: hard -"react-native-skia-apple-ios@npm:152.0.0": - version: 152.0.0 - resolution: "react-native-skia-apple-ios@npm:152.0.0" - checksum: 42ef9929664d197eaf1116e172573ad1ed68944b7349ee692bb542761fd4aa6d171d23a293ee64b0a1f0d0bf3c6cd2f1f32ef8bda2b4dd1749466af5f25dd8a3 +"react-native-skia-apple-ios@npm:154.0.0": + version: 154.0.0 + resolution: "react-native-skia-apple-ios@npm:154.0.0" + checksum: b627209b8bcd747238cb39d605527ad46569988aab6e025ac2118455031c98cf7da30a3da5af1295ba824336a322a99f46baa6e870f9452e623f430619143baf languageName: node linkType: hard -"react-native-skia-apple-macos@npm:152.0.0": - version: 152.0.0 - resolution: "react-native-skia-apple-macos@npm:152.0.0" - checksum: d8e220ce0ac0f41c35916c5b416cbdb6436ad4189884a70524949949df8990a43c5d116865932dd244dad412e77cab368f3f820e1400b3bd00736ee37435810b +"react-native-skia-apple-macos@npm:154.0.0": + version: 154.0.0 + resolution: "react-native-skia-apple-macos@npm:154.0.0" + checksum: be282528ca301cc69e7d94505cd0e8dc051eb22e1b69c15c4b5721bca18b9330c6c06bbf548a3d309150e7dd41d3118bef7d318a6664a2c367587f6fc778d8d9 languageName: node linkType: hard -"react-native-skia-apple-tvos@npm:152.0.0": - version: 152.0.0 - resolution: "react-native-skia-apple-tvos@npm:152.0.0" - checksum: ae80993eb127b5ee4502b0428204b980347d6cf31444de74984ae64373e1bc4a992b79ddae0aa9983cd3a6e5b979ae5d810f88eca92976b40907fdb578b508d5 +"react-native-skia-apple-tvos@npm:154.0.0": + version: 154.0.0 + resolution: "react-native-skia-apple-tvos@npm:154.0.0" + checksum: 8dcee573d91be4ddfc47112d0098840854c1c21fc0e5190f1d8f4495d0c2c86b3168c2070a089c3072ed6a5e5fa2639de60b7772ddb8f9993e1e93007e1409c3 languageName: node linkType: hard @@ -27338,9 +27338,9 @@ __metadata: languageName: node linkType: hard -"react-native-webgpu@npm:0.8.0": - version: 0.8.0 - resolution: "react-native-webgpu@npm:0.8.0" +"react-native-webgpu@npm:0.10.0": + version: 0.10.0 + resolution: "react-native-webgpu@npm:0.10.0" peerDependencies: react: "*" react-native: ">=0.81.0" @@ -27351,7 +27351,7 @@ __metadata: optional: true react-native-worklets: optional: true - checksum: 75a850ebf19d57395ca1b4ba7d4f6f24ff203e047c99493d1a3d4fbb4bbb08308a573ead0e2c88ef5de367d355e576d181b9513fb6cec93ab77e548211976c0c + checksum: eba53c650302110f8a84dc3041b4e5098adf68062e3dc98fff2094313d1dccdd15b89873dc131110eabf464c3124d83be9411fc37e18a726d6ef975080e46724 languageName: node linkType: hard From 67a637a3380ea556d1147f1e71d588f82eb0aed6 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Sun, 13 Sep 2026 09:22:00 +0200 Subject: [PATCH 3/3] :green_heart: --- apps/docs/static/img/skottie/text-prop.png | Bin 22779 -> 21867 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/docs/static/img/skottie/text-prop.png b/apps/docs/static/img/skottie/text-prop.png index 134952a4458bb7743588d2059218b7f93ca82147..8e1169c6e6875825c46915c5454548291b1d0ab2 100644 GIT binary patch literal 21867 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 literal 22779 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 Co4B_C