From a87d78594414d7aeb8d746b22771c5e92f422738 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Wed, 15 Oct 2025 21:16:09 +0200 Subject: [PATCH 01/15] =?UTF-8?q?fix(=F0=9F=A7=AA):=20update=20timeout=20i?= =?UTF-8?q?n=20e2e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Increased timeout duration for WebSocket closure. --- apps/example/src/Tests/useClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/example/src/Tests/useClient.ts b/apps/example/src/Tests/useClient.ts index 70b68f5e00..debb09d74e 100644 --- a/apps/example/src/Tests/useClient.ts +++ b/apps/example/src/Tests/useClient.ts @@ -35,7 +35,7 @@ export const useClient = (): UseClient => { ws.close(); // incrementing retry to rerun the effect setRetry((r) => r + 1); - }, 500); + }, 1000); }; return () => { ws.close(); From 6021dabbcd8f128c353aa9b8baea019b6aba31b3 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Mon, 3 Nov 2025 18:31:30 +0100 Subject: [PATCH 02/15] :wrench: --- apps/example/index.html | 32 +++ apps/example/index.web.js | 21 ++ apps/example/metro.config.js | 46 +++- apps/example/package.json | 7 +- yarn.lock | 506 ++++++++++++++++++++++++++++++++++- 5 files changed, 607 insertions(+), 5 deletions(-) create mode 100644 apps/example/index.html create mode 100644 apps/example/index.web.js diff --git a/apps/example/index.html b/apps/example/index.html new file mode 100644 index 0000000000..150b8e2fde --- /dev/null +++ b/apps/example/index.html @@ -0,0 +1,32 @@ + + + + + + + React Native Skia Example + + + + +
+ + + diff --git a/apps/example/index.web.js b/apps/example/index.web.js new file mode 100644 index 0000000000..4cc088f47a --- /dev/null +++ b/apps/example/index.web.js @@ -0,0 +1,21 @@ +import { AppRegistry } from "react-native"; + +import App from "./src/App"; +import { name as appName } from "./app.json"; + +if (!Symbol.dispose) { + Symbol.dispose = Symbol.for("Symbol.dispose"); +} + +AppRegistry.registerComponent(appName, () => App); + +if (typeof document !== "undefined") { + const rootTag = + document.getElementById("root") ?? document.getElementById("app"); + if (!rootTag) { + throw new Error( + 'React Native Web root element with id "root" was not found in the document', + ); + } + AppRegistry.runApplication(appName, { rootTag }); +} diff --git a/apps/example/metro.config.js b/apps/example/metro.config.js index 96c8bc6572..51cadc9f4e 100644 --- a/apps/example/metro.config.js +++ b/apps/example/metro.config.js @@ -1,5 +1,7 @@ +const path = require("path"); const { makeMetroConfig } = require("@rnx-kit/metro-config"); -module.exports = makeMetroConfig({ + +const metroConfig = makeMetroConfig({ transformer: { getTransformOptions: async () => ({ transform: { @@ -9,3 +11,45 @@ module.exports = makeMetroConfig({ }), }, }); + +const resolver = (metroConfig.resolver = metroConfig.resolver ?? {}); + +resolver.platforms = Array.from( + new Set([...(resolver.platforms ?? []), "web"]), +); + +const webSourceExts = [ + "web.tsx", + "web.ts", + "web.jsx", + "web.js", + "web.mjs", + "web.cjs", +]; +resolver.sourceExts = Array.from( + new Set([...(resolver.sourceExts ?? []), ...webSourceExts]), +); + +if (process.env.IS_WEB_BUILD) { + const reactNativeWebPath = path.dirname( + require.resolve("react-native-web/package.json"), + ); + + resolver.extraNodeModules = { + ...(resolver.extraNodeModules ?? {}), + "react-native": path.join(reactNativeWebPath, "dist", "index.js"), + }; + + metroConfig.transformer = { + ...(metroConfig.transformer ?? {}), + assetRegistryPath: path.join( + reactNativeWebPath, + "dist", + "modules", + "AssetRegistry", + "index.js", + ), + }; +} + +module.exports = metroConfig; diff --git a/apps/example/package.json b/apps/example/package.json index 3b1ff9efb4..76850bc7c2 100644 --- a/apps/example/package.json +++ b/apps/example/package.json @@ -16,7 +16,8 @@ "pod:install": "cd ios && pod install", "test": "jest", "tsc": "tsc --noEmit", - "start": "react-native start" + "start": "react-native start", + "web": "IS_WEB_BUILD=true react-native start" }, "dependencies": { "@callstack/react-native-visionos": "^0.75.0", @@ -31,6 +32,7 @@ "cdt2d": "^1.0.0", "its-fine": "^2.0.0", "react": "19.0.0", + "react-dom": "19.0.0", "react-native": "0.78.0", "react-native-gesture-handler": "^2.24.0", "react-native-macos": "^0.78.3", @@ -38,6 +40,7 @@ "react-native-safe-area-context": "^5.2.0", "react-native-screens": "^4.9.1", "react-native-svg": "^15.11.2", + "react-native-web": "^0.21.2", "react-native-windows": "^0.75.0" }, "devDependencies": { @@ -55,6 +58,8 @@ "@rnx-kit/metro-config": "^2.0.0", "@types/jest": "^29.5.13", "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "@types/react-native-web": "^0.19.2", "@types/react-test-renderer": "^19.0.0", "eslint": "9.36.0", "eslint-config-react-native-wcandillon": "4.0.1", diff --git a/yarn.lock b/yarn.lock index e95fcd0e1a..48e5819083 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5733,7 +5733,7 @@ __metadata: languageName: node linkType: hard -"@jest/create-cache-key-function@npm:^29.6.3": +"@jest/create-cache-key-function@npm:^29.6.3, @jest/create-cache-key-function@npm:^29.7.0": version: 29.7.0 resolution: "@jest/create-cache-key-function@npm:29.7.0" dependencies: @@ -7812,6 +7812,13 @@ __metadata: languageName: node linkType: hard +"@react-native/assets-registry@npm:0.82.1": + version: 0.82.1 + resolution: "@react-native/assets-registry@npm:0.82.1" + checksum: f511a248f455c7fe2bae330ed15929294d5d25e5d8aa27e148588a554bde31a1ddf54d08ebc21128cabe9978be4604b2e8ad4343566c9840fc49e2b0d119cbc6 + languageName: node + linkType: hard + "@react-native/assets@npm:1.0.0": version: 1.0.0 resolution: "@react-native/assets@npm:1.0.0" @@ -8147,6 +8154,23 @@ __metadata: languageName: node linkType: hard +"@react-native/codegen@npm:0.82.1": + version: 0.82.1 + resolution: "@react-native/codegen@npm:0.82.1" + dependencies: + "@babel/core": ^7.25.2 + "@babel/parser": ^7.25.3 + glob: ^7.1.1 + hermes-parser: 0.32.0 + invariant: ^2.2.4 + nullthrows: ^1.1.1 + yargs: ^17.6.2 + peerDependencies: + "@babel/core": "*" + checksum: d965d9f387a62e27a75d80546a6c825b21a42b56738eedf294b59221c117345148daa0362af5b793a2d5ce3e089e3682dd4bdd9548235ab040543f0aa21d46f0 + languageName: node + linkType: hard + "@react-native/community-cli-plugin@npm:0.75.2": version: 0.75.2 resolution: "@react-native/community-cli-plugin@npm:0.75.2" @@ -8232,6 +8256,29 @@ __metadata: languageName: node linkType: hard +"@react-native/community-cli-plugin@npm:0.82.1": + version: 0.82.1 + resolution: "@react-native/community-cli-plugin@npm:0.82.1" + dependencies: + "@react-native/dev-middleware": 0.82.1 + debug: ^4.4.0 + invariant: ^2.2.4 + metro: ^0.83.1 + metro-config: ^0.83.1 + metro-core: ^0.83.1 + semver: ^7.1.3 + peerDependencies: + "@react-native-community/cli": "*" + "@react-native/metro-config": "*" + peerDependenciesMeta: + "@react-native-community/cli": + optional: true + "@react-native/metro-config": + optional: true + checksum: 680aef3270c56a73467ba40f7de416d32fef8b7e484421fccd437f940692eee76382e82eddd0a4749675b855d642fd6c83b147136bdcf03ee84f36ed38f7e0de + languageName: node + linkType: hard + "@react-native/debugger-frontend@npm:0.75.2": version: 0.75.2 resolution: "@react-native/debugger-frontend@npm:0.75.2" @@ -8260,6 +8307,23 @@ __metadata: languageName: node linkType: hard +"@react-native/debugger-frontend@npm:0.82.1": + version: 0.82.1 + resolution: "@react-native/debugger-frontend@npm:0.82.1" + checksum: b767c7586c782a130d3579a1d8c137a8c55361d579028e44a31b220c566ab793a83b256b39eb114a759e07031574cd142cae1bdc1ec80dc02e7a6a191409548e + languageName: node + linkType: hard + +"@react-native/debugger-shell@npm:0.82.1": + version: 0.82.1 + resolution: "@react-native/debugger-shell@npm:0.82.1" + dependencies: + cross-spawn: ^7.0.6 + fb-dotslash: 0.5.8 + checksum: 9b4ec7f413d5e776a7361f1a5e8ecc7dbc8b56c7fc119fec1895f4821071a7b95b2a3db0cef016614a1eb50288df0282c7f7cf5944e3934bc8694529556f44e8 + languageName: node + linkType: hard + "@react-native/dev-middleware@npm:0.75.2": version: 0.75.2 resolution: "@react-native/dev-middleware@npm:0.75.2" @@ -8340,6 +8404,26 @@ __metadata: languageName: node linkType: hard +"@react-native/dev-middleware@npm:0.82.1": + version: 0.82.1 + resolution: "@react-native/dev-middleware@npm:0.82.1" + dependencies: + "@isaacs/ttlcache": ^1.4.1 + "@react-native/debugger-frontend": 0.82.1 + "@react-native/debugger-shell": 0.82.1 + chrome-launcher: ^0.15.2 + chromium-edge-launcher: ^0.2.0 + connect: ^3.6.5 + debug: ^4.4.0 + invariant: ^2.2.4 + nullthrows: ^1.1.1 + open: ^7.0.3 + serve-static: ^1.16.2 + ws: ^6.2.3 + checksum: 0fed27cb7d7bd9e2e3b9cd20776000ec730ea6672779ccd971e831c67a4b25adcda9d82e0042d3f37e1311736add0d4bb51519c463ea81a11565a2bac1cee68c + languageName: node + linkType: hard + "@react-native/eslint-config@npm:0.78.0": version: 0.78.0 resolution: "@react-native/eslint-config@npm:0.78.0" @@ -8398,6 +8482,13 @@ __metadata: languageName: node linkType: hard +"@react-native/gradle-plugin@npm:0.82.1": + version: 0.82.1 + resolution: "@react-native/gradle-plugin@npm:0.82.1" + checksum: 7e7e2d768a8ff599dba5ef7b0a417e1d14a032a3344cc1e57852d4ebee1587dc877f83ae9dd4beae3b27fe2389d235227df12bd8aaa9be8b6ef1c7784419e0de + languageName: node + linkType: hard + "@react-native/js-polyfills@npm:0.75.2": version: 0.75.2 resolution: "@react-native/js-polyfills@npm:0.75.2" @@ -8426,6 +8517,13 @@ __metadata: languageName: node linkType: hard +"@react-native/js-polyfills@npm:0.82.1": + version: 0.82.1 + resolution: "@react-native/js-polyfills@npm:0.82.1" + checksum: 271d5bcff95d3867237ae4ec4745247c7048ea950912b3c31c8bbffd801c714509294b04d176c8121389788a192680f482b21e99ab24c9b2dcbce37acfdeaa5f + languageName: node + linkType: hard + "@react-native/metro-babel-transformer@npm:0.75.2": version: 0.75.2 resolution: "@react-native/metro-babel-transformer@npm:0.75.2" @@ -8522,6 +8620,13 @@ __metadata: languageName: node linkType: hard +"@react-native/normalize-colors@npm:0.82.1": + version: 0.82.1 + resolution: "@react-native/normalize-colors@npm:0.82.1" + checksum: d180cc6591989a3d490ad4454d63a19ec9be796314632adb29051515eb31e98fbdd12903c00750d4ce023306e159a2498867bf6f25bcf11a8ed48e5486482947 + languageName: node + linkType: hard + "@react-native/normalize-colors@npm:^0.74.1": version: 0.74.89 resolution: "@react-native/normalize-colors@npm:0.74.89" @@ -8587,6 +8692,23 @@ __metadata: languageName: node linkType: hard +"@react-native/virtualized-lists@npm:0.82.1": + version: 0.82.1 + resolution: "@react-native/virtualized-lists@npm:0.82.1" + dependencies: + invariant: ^2.2.4 + nullthrows: ^1.1.1 + peerDependencies: + "@types/react": ^19.1.1 + react: "*" + react-native: "*" + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 4961af57d477f16c1b7e0b584e9f54cef876bbb738b3e628b5dfc8ddc157f83bbe1b3e9d8a0c426f41018cdec5d1073f2ffe23b830e288c7d8b730753700608a + languageName: node + linkType: hard + "@react-navigation/bottom-tabs@npm:^7.2.1": version: 7.3.8 resolution: "@react-navigation/bottom-tabs@npm:7.3.8" @@ -10351,6 +10473,16 @@ __metadata: languageName: node linkType: hard +"@types/react-native-web@npm:^0.19.2": + version: 0.19.2 + resolution: "@types/react-native-web@npm:0.19.2" + dependencies: + "@types/react": "*" + react-native: "*" + checksum: d6c6bbaa4610dbc1588205b2a936d88938f3d24e6b6a79d238192ff1cf1dc1865864c9c5e00d500c108f0324b6464c3903e5b5aba9e69fc1ab5638ec81b0f163 + languageName: node + linkType: hard + "@types/react-reconciler@npm:0.28.9, @types/react-reconciler@npm:^0.28.0, @types/react-reconciler@npm:^0.28.9": version: 0.28.9 resolution: "@types/react-reconciler@npm:0.28.9" @@ -12444,6 +12576,15 @@ __metadata: languageName: node linkType: hard +"babel-plugin-syntax-hermes-parser@npm:0.32.0": + version: 0.32.0 + resolution: "babel-plugin-syntax-hermes-parser@npm:0.32.0" + dependencies: + hermes-parser: 0.32.0 + checksum: ec76abeefabf940e2d571db3b47d022a9be7602286133291e8e047d4855af6a8afc079e4631bc9a56209d751fad54b5199932a55753b1e2b56a719d20e2d5065 + languageName: node + linkType: hard + "babel-plugin-transform-flow-enums@npm:^0.0.2": version: 0.0.2 resolution: "babel-plugin-transform-flow-enums@npm:0.0.2" @@ -17438,6 +17579,8 @@ __metadata: "@testing-library/react-native": ^13.1.0 "@types/jest": ^29.5.13 "@types/react": ^19.0.0 + "@types/react-dom": ^19.0.0 + "@types/react-native-web": ^0.19.2 "@types/react-test-renderer": ^19.0.0 babel-plugin-transform-inline-environment-variables: ^0.4.4 cdt2d: ^1.0.0 @@ -17449,6 +17592,7 @@ __metadata: jest-transform-stub: ^2.0.0 prettier: 2.8.8 react: 19.0.0 + react-dom: 19.0.0 react-native: 0.78.0 react-native-gesture-handler: ^2.24.0 react-native-macos: ^0.78.3 @@ -17457,6 +17601,7 @@ __metadata: react-native-screens: ^4.9.1 react-native-svg: ^15.11.2 react-native-test-app: 4.4.7 + react-native-web: ^0.21.2 react-native-windows: ^0.75.0 react-test-renderer: 19.0.0 typescript: ^5.2.2 @@ -17834,6 +17979,15 @@ __metadata: languageName: node linkType: hard +"fb-dotslash@npm:0.5.8": + version: 0.5.8 + resolution: "fb-dotslash@npm:0.5.8" + bin: + dotslash: bin/dotslash + checksum: 5678efe96898294e41c983cb8ea28952539566df5f8bfd2913e8e146425d7d9999d2c458bb4f3e0b07b36b5bcd23cada0868d94509c8b2d4b17de8bf0641775a + languageName: node + linkType: hard + "fb-watchman@npm:^2.0.0, fb-watchman@npm:^2.0.2": version: 2.0.2 resolution: "fb-watchman@npm:2.0.2" @@ -19252,6 +19406,13 @@ __metadata: languageName: unknown linkType: soft +"hermes-compiler@npm:0.0.0": + version: 0.0.0 + resolution: "hermes-compiler@npm:0.0.0" + checksum: 8b6fc8a64c2fa18c9aa6ddb8831c92253b6a2f10adf7d5d8f361b574f07e91b64f0c44b1370665075c33c17dd71c02fd19422124a3d2aa1717c37006ab12a1f0 + languageName: node + linkType: hard + "hermes-estree@npm:0.22.0": version: 0.22.0 resolution: "hermes-estree@npm:0.22.0" @@ -19273,6 +19434,13 @@ __metadata: languageName: node linkType: hard +"hermes-estree@npm:0.32.0": + version: 0.32.0 + resolution: "hermes-estree@npm:0.32.0" + checksum: 7b0606a8d2cf4593634d01b0eae0764c0e4703bc5cd73cbb0547fb8dda9445a27a83345117c08eef64f6bdab1287e3c5a4e3001deed465a715d26f4e918c8b22 + languageName: node + linkType: hard + "hermes-parser@npm:0.22.0": version: 0.22.0 resolution: "hermes-parser@npm:0.22.0" @@ -19300,6 +19468,15 @@ __metadata: languageName: node linkType: hard +"hermes-parser@npm:0.32.0": + version: 0.32.0 + resolution: "hermes-parser@npm:0.32.0" + dependencies: + hermes-estree: 0.32.0 + checksum: 7ec172ec763ee5ba1d01f273084ab4c7ad7a543d1ed11e887ea3a9eba7c0b83854dde08e835e38f29b74146b5ce17e67d556774324a63f8afe16fb57021bfdcb + languageName: node + linkType: hard + "highlight.js@npm:^10.7.1": version: 10.7.3 resolution: "highlight.js@npm:10.7.3" @@ -19663,7 +19840,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^7.0.0, https-proxy-agent@npm:^7.0.1": +"https-proxy-agent@npm:^7.0.0, https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.5": version: 7.0.6 resolution: "https-proxy-agent@npm:7.0.6" dependencies: @@ -24090,6 +24267,18 @@ __metadata: languageName: node linkType: hard +"metro-babel-transformer@npm:0.83.3": + version: 0.83.3 + resolution: "metro-babel-transformer@npm:0.83.3" + dependencies: + "@babel/core": ^7.25.2 + flow-enums-runtime: ^0.0.6 + hermes-parser: 0.32.0 + nullthrows: ^1.1.1 + checksum: dd178409d1718dae12dfffb6572ebc5bb78f1e0d7e93dce829c945957f8a686cb1b4c466c69585d7b982b3937fbea28d5c53a80691f2fc66717a0bcc800bc5b8 + languageName: node + linkType: hard + "metro-cache-key@npm:0.80.12": version: 0.80.12 resolution: "metro-cache-key@npm:0.80.12" @@ -24117,6 +24306,15 @@ __metadata: languageName: node linkType: hard +"metro-cache-key@npm:0.83.3": + version: 0.83.3 + resolution: "metro-cache-key@npm:0.83.3" + dependencies: + flow-enums-runtime: ^0.0.6 + checksum: a6f9d2bf8b810f57d330d6f8f1ebf029e1224f426c5895f73d9bc1007482684048bfc7513a855626ee7f3ae72ca46e1b08cf983aefbfa84321bb7c0cef4ba4ae + languageName: node + linkType: hard + "metro-cache@npm:0.80.12": version: 0.80.12 resolution: "metro-cache@npm:0.80.12" @@ -24150,6 +24348,18 @@ __metadata: languageName: node linkType: hard +"metro-cache@npm:0.83.3": + version: 0.83.3 + resolution: "metro-cache@npm:0.83.3" + dependencies: + exponential-backoff: ^3.1.1 + flow-enums-runtime: ^0.0.6 + https-proxy-agent: ^7.0.5 + metro-core: 0.83.3 + checksum: 95606275411d85de071fd95171a9548406cd1154320850a554bf00207804f7844ed252f9750a802d6612ade839c579b23bd87927ae173f43c368e8f5d900149d + languageName: node + linkType: hard + "metro-config@npm:0.80.12, metro-config@npm:^0.80.3": version: 0.80.12 resolution: "metro-config@npm:0.80.12" @@ -24198,6 +24408,22 @@ __metadata: languageName: node linkType: hard +"metro-config@npm:0.83.3, metro-config@npm:^0.83.1": + version: 0.83.3 + resolution: "metro-config@npm:0.83.3" + dependencies: + connect: ^3.6.5 + flow-enums-runtime: ^0.0.6 + jest-validate: ^29.7.0 + metro: 0.83.3 + metro-cache: 0.83.3 + metro-core: 0.83.3 + metro-runtime: 0.83.3 + yaml: ^2.6.1 + checksum: a14b77668a9712abbcebe5bf6a0081f0fd46caf8d37405174f261765abcd44d7a99910533fcc05edde3de10f9b22820cc9910c7dee2b01e761692a0a322f2608 + languageName: node + linkType: hard + "metro-core@npm:0.80.12, metro-core@npm:^0.80.3": version: 0.80.12 resolution: "metro-core@npm:0.80.12" @@ -24231,6 +24457,17 @@ __metadata: languageName: node linkType: hard +"metro-core@npm:0.83.3, metro-core@npm:^0.83.1": + version: 0.83.3 + resolution: "metro-core@npm:0.83.3" + dependencies: + flow-enums-runtime: ^0.0.6 + lodash.throttle: ^4.1.1 + metro-resolver: 0.83.3 + checksum: d06871313310cd718094ecbae805bcacea3f325340f6dff3c5044b62457c4690dd729cdb938349bdd3c41efa6f28032ae07696467ef006d5509fec9045c1966f + languageName: node + linkType: hard + "metro-file-map@npm:0.80.12": version: 0.80.12 resolution: "metro-file-map@npm:0.80.12" @@ -24288,6 +24525,23 @@ __metadata: languageName: node linkType: hard +"metro-file-map@npm:0.83.3": + version: 0.83.3 + resolution: "metro-file-map@npm:0.83.3" + dependencies: + debug: ^4.4.0 + fb-watchman: ^2.0.0 + flow-enums-runtime: ^0.0.6 + graceful-fs: ^4.2.4 + invariant: ^2.2.4 + jest-worker: ^29.7.0 + micromatch: ^4.0.4 + nullthrows: ^1.1.1 + walker: ^1.0.7 + checksum: 0dea599206e93b6e8628be2aa98452d4dae16e805b810759ec8b50cebcd83f2d053f7e5865196d464f3793f86b3b5003830c6713f91bf62fa406a4af7c93a776 + languageName: node + linkType: hard + "metro-minify-terser@npm:0.80.12": version: 0.80.12 resolution: "metro-minify-terser@npm:0.80.12" @@ -24318,6 +24572,16 @@ __metadata: languageName: node linkType: hard +"metro-minify-terser@npm:0.83.3": + version: 0.83.3 + resolution: "metro-minify-terser@npm:0.83.3" + dependencies: + flow-enums-runtime: ^0.0.6 + terser: ^5.15.0 + checksum: 1de88b70b7c903147807baa46497491a87600594fd0868b6538bbb9d7785242cabfbe8bccf36cc2285d0e17be72445b512d00c496952a159572545f3e6bcb199 + languageName: node + linkType: hard + "metro-resolver@npm:0.80.12": version: 0.80.12 resolution: "metro-resolver@npm:0.80.12" @@ -24345,6 +24609,15 @@ __metadata: languageName: node linkType: hard +"metro-resolver@npm:0.83.3": + version: 0.83.3 + resolution: "metro-resolver@npm:0.83.3" + dependencies: + flow-enums-runtime: ^0.0.6 + checksum: de2ae5ced6239b004a97712f98934c6e830870d11614e2dba48250930214581f0746df8a4f0f1cb71060fe21c2cf919d3359106ad4f375c2500ba08e10922896 + languageName: node + linkType: hard + "metro-runtime@npm:0.80.12, metro-runtime@npm:^0.80.3": version: 0.80.12 resolution: "metro-runtime@npm:0.80.12" @@ -24375,6 +24648,16 @@ __metadata: languageName: node linkType: hard +"metro-runtime@npm:0.83.3, metro-runtime@npm:^0.83.1": + version: 0.83.3 + resolution: "metro-runtime@npm:0.83.3" + dependencies: + "@babel/runtime": ^7.25.0 + flow-enums-runtime: ^0.0.6 + checksum: dcbdc5502020d1e20cee1a3a8019323ab2f3ca2aa2d6ddb2b7a2b8547835a20b84fe4afc23c397f788584e108c70411db93df2f61322b44a4f0f119275052d03 + languageName: node + linkType: hard + "metro-source-map@npm:0.80.12, metro-source-map@npm:^0.80.3": version: 0.80.12 resolution: "metro-source-map@npm:0.80.12" @@ -24428,6 +24711,24 @@ __metadata: languageName: node linkType: hard +"metro-source-map@npm:0.83.3, metro-source-map@npm:^0.83.1": + version: 0.83.3 + resolution: "metro-source-map@npm:0.83.3" + dependencies: + "@babel/traverse": ^7.25.3 + "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3" + "@babel/types": ^7.25.2 + flow-enums-runtime: ^0.0.6 + invariant: ^2.2.4 + metro-symbolicate: 0.83.3 + nullthrows: ^1.1.1 + ob1: 0.83.3 + source-map: ^0.5.6 + vlq: ^1.0.0 + checksum: 5bf3b7a1561bc1f0ad6ab3b7b550d4b4581da31964a7f218727a3201576912076c909a2e50fba4dd3c649d79312324dec683a37228f4559811c37b69ecca8831 + languageName: node + linkType: hard + "metro-symbolicate@npm:0.80.12": version: 0.80.12 resolution: "metro-symbolicate@npm:0.80.12" @@ -24477,6 +24778,22 @@ __metadata: languageName: node linkType: hard +"metro-symbolicate@npm:0.83.3": + version: 0.83.3 + resolution: "metro-symbolicate@npm:0.83.3" + dependencies: + flow-enums-runtime: ^0.0.6 + invariant: ^2.2.4 + metro-source-map: 0.83.3 + nullthrows: ^1.1.1 + source-map: ^0.5.6 + vlq: ^1.0.0 + bin: + metro-symbolicate: src/index.js + checksum: 943cc2456d56ae2ed8369495c18966d91feff636b37909b5225ffb8ce2a50eba8fbedf116f3bea3059d431ebc621c9c9af8a8bfd181b0cd1fece051507e10ffd + languageName: node + linkType: hard + "metro-transform-plugins@npm:0.80.12": version: 0.80.12 resolution: "metro-transform-plugins@npm:0.80.12" @@ -24519,6 +24836,20 @@ __metadata: languageName: node linkType: hard +"metro-transform-plugins@npm:0.83.3": + version: 0.83.3 + resolution: "metro-transform-plugins@npm:0.83.3" + dependencies: + "@babel/core": ^7.25.2 + "@babel/generator": ^7.25.0 + "@babel/template": ^7.25.0 + "@babel/traverse": ^7.25.3 + flow-enums-runtime: ^0.0.6 + nullthrows: ^1.1.1 + checksum: 6f92b9dfa53bdb63e79038bbd4d68791379ab26cf874679e64563618c578eeed3a828795debf8076ffd518431dff53191990784fb619046bcc03fff114b0cb21 + languageName: node + linkType: hard + "metro-transform-worker@npm:0.80.12": version: 0.80.12 resolution: "metro-transform-worker@npm:0.80.12" @@ -24582,6 +24913,27 @@ __metadata: languageName: node linkType: hard +"metro-transform-worker@npm:0.83.3": + version: 0.83.3 + resolution: "metro-transform-worker@npm:0.83.3" + dependencies: + "@babel/core": ^7.25.2 + "@babel/generator": ^7.25.0 + "@babel/parser": ^7.25.3 + "@babel/types": ^7.25.2 + flow-enums-runtime: ^0.0.6 + metro: 0.83.3 + metro-babel-transformer: 0.83.3 + metro-cache: 0.83.3 + metro-cache-key: 0.83.3 + metro-minify-terser: 0.83.3 + metro-source-map: 0.83.3 + metro-transform-plugins: 0.83.3 + nullthrows: ^1.1.1 + checksum: fcb25ebc1ce703d830ef60c9af87325f996af4c3946325ab957b65ca59d12d181fe6c527c9ba1f932cd954d23a400052293117fe56f9a2727dfbc0a118e7bb27 + languageName: node + linkType: hard + "metro@npm:0.80.12, metro@npm:^0.80.3": version: 0.80.12 resolution: "metro@npm:0.80.12" @@ -24734,6 +25086,56 @@ __metadata: languageName: node linkType: hard +"metro@npm:0.83.3, metro@npm:^0.83.1": + version: 0.83.3 + resolution: "metro@npm:0.83.3" + dependencies: + "@babel/code-frame": ^7.24.7 + "@babel/core": ^7.25.2 + "@babel/generator": ^7.25.0 + "@babel/parser": ^7.25.3 + "@babel/template": ^7.25.0 + "@babel/traverse": ^7.25.3 + "@babel/types": ^7.25.2 + accepts: ^1.3.7 + chalk: ^4.0.0 + ci-info: ^2.0.0 + connect: ^3.6.5 + debug: ^4.4.0 + error-stack-parser: ^2.0.6 + flow-enums-runtime: ^0.0.6 + graceful-fs: ^4.2.4 + hermes-parser: 0.32.0 + image-size: ^1.0.2 + invariant: ^2.2.4 + jest-worker: ^29.7.0 + jsc-safe-url: ^0.2.2 + lodash.throttle: ^4.1.1 + metro-babel-transformer: 0.83.3 + metro-cache: 0.83.3 + metro-cache-key: 0.83.3 + metro-config: 0.83.3 + metro-core: 0.83.3 + metro-file-map: 0.83.3 + metro-resolver: 0.83.3 + metro-runtime: 0.83.3 + metro-source-map: 0.83.3 + metro-symbolicate: 0.83.3 + metro-transform-plugins: 0.83.3 + metro-transform-worker: 0.83.3 + mime-types: ^2.1.27 + nullthrows: ^1.1.1 + serialize-error: ^2.1.0 + source-map: ^0.5.6 + throat: ^5.0.0 + ws: ^7.5.10 + yargs: ^17.6.2 + bin: + metro: src/cli.js + checksum: 306d8c06b5a1a45e18df6e41f494bbc8b439700985429284eea7b3c3c82108e3c3795d859a8ab3ed7a85793d64e3160519be9aa84c6418d6ed37bd5ae4500b57 + languageName: node + linkType: hard + "micromark-core-commonmark@npm:^2.0.0": version: 2.0.3 resolution: "micromark-core-commonmark@npm:2.0.3" @@ -26282,6 +26684,15 @@ __metadata: languageName: node linkType: hard +"ob1@npm:0.83.3": + version: 0.83.3 + resolution: "ob1@npm:0.83.3" + dependencies: + flow-enums-runtime: ^0.0.6 + checksum: 20dfe91d48d0cadd97159cfd53f5abdca435b55d58b1f562e0687485e8f44f8a95e8ab3c835badd13d0d8c01e3d7b14d639a316aa4bf82841ac78b49611d4e5c + languageName: node + linkType: hard + "object-assign@npm:^4.0.1, object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" @@ -29561,6 +29972,16 @@ __metadata: languageName: node linkType: hard +"react-devtools-core@npm:^6.1.5": + version: 6.1.5 + resolution: "react-devtools-core@npm:6.1.5" + dependencies: + shell-quote: ^1.6.1 + ws: ^7 + checksum: b54f2d2416f5f5ca61b1741367865eab18b0040d7e4b3236693595803dfdf82ae02adbcb480acc5b9767748b615a2d5ce3af286cde3a7f8c193123c62c777428 + languageName: node + linkType: hard + "react-dom@npm:19.0.0": version: 19.0.0 resolution: "react-dom@npm:19.0.0" @@ -29974,6 +30395,25 @@ __metadata: languageName: node linkType: hard +"react-native-web@npm:^0.21.2": + version: 0.21.2 + resolution: "react-native-web@npm:0.21.2" + dependencies: + "@babel/runtime": ^7.18.6 + "@react-native/normalize-colors": ^0.74.1 + fbjs: ^3.0.4 + inline-style-prefixer: ^7.0.1 + memoize-one: ^6.0.0 + nullthrows: ^1.1.1 + postcss-value-parser: ^4.2.0 + styleq: ^0.1.3 + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + checksum: 3d8be3ee2bae2790949683d8002973882538a49d5182bdda2a38739d44f0a5918bf082427ad062c98b71d3585ab9664c406685ceafe2432bb99877188dc9782d + languageName: node + linkType: hard + "react-native-windows@npm:^0.75.0": version: 0.75.14 resolution: "react-native-windows@npm:0.75.14" @@ -30030,6 +30470,57 @@ __metadata: languageName: node linkType: hard +"react-native@npm:*": + version: 0.82.1 + resolution: "react-native@npm:0.82.1" + dependencies: + "@jest/create-cache-key-function": ^29.7.0 + "@react-native/assets-registry": 0.82.1 + "@react-native/codegen": 0.82.1 + "@react-native/community-cli-plugin": 0.82.1 + "@react-native/gradle-plugin": 0.82.1 + "@react-native/js-polyfills": 0.82.1 + "@react-native/normalize-colors": 0.82.1 + "@react-native/virtualized-lists": 0.82.1 + abort-controller: ^3.0.0 + anser: ^1.4.9 + ansi-regex: ^5.0.0 + babel-jest: ^29.7.0 + babel-plugin-syntax-hermes-parser: 0.32.0 + base64-js: ^1.5.1 + commander: ^12.0.0 + flow-enums-runtime: ^0.0.6 + glob: ^7.1.1 + hermes-compiler: 0.0.0 + invariant: ^2.2.4 + jest-environment-node: ^29.7.0 + memoize-one: ^5.0.0 + metro-runtime: ^0.83.1 + metro-source-map: ^0.83.1 + nullthrows: ^1.1.1 + pretty-format: ^29.7.0 + promise: ^8.3.0 + react-devtools-core: ^6.1.5 + react-refresh: ^0.14.0 + regenerator-runtime: ^0.13.2 + scheduler: 0.26.0 + semver: ^7.1.3 + stacktrace-parser: ^0.1.10 + whatwg-fetch: ^3.0.0 + ws: ^6.2.3 + yargs: ^17.6.2 + peerDependencies: + "@types/react": ^19.1.1 + react: ^19.1.1 + peerDependenciesMeta: + "@types/react": + optional: true + bin: + react-native: cli.js + checksum: 16c5945aae14bd8d7113d2751ad558d91a0bc8cfbffd8ad588722635aac404b512be5ef0f5953ce62b9808ae7ddcbefac909575bcf7da013d06146ab0d48f29a + languageName: node + linkType: hard + "react-native@npm:0.78.0": version: 0.78.0 resolution: "react-native@npm:0.78.0" @@ -31443,7 +31934,7 @@ __metadata: languageName: node linkType: hard -"scheduler@npm:^0.26.0": +"scheduler@npm:0.26.0, scheduler@npm:^0.26.0": version: 0.26.0 resolution: "scheduler@npm:0.26.0" checksum: c63a9f1c0e5089b537231cff6c11f75455b5c8625ae09535c1d7cd0a1b0c77ceecdd9f1074e5e063da5d8dc11e73e8033dcac3361791088be08a6e60c0283ed9 @@ -36276,6 +36767,15 @@ __metadata: languageName: node linkType: hard +"yaml@npm:^2.6.1": + version: 2.8.1 + resolution: "yaml@npm:2.8.1" + bin: + yaml: bin.mjs + checksum: 35b46150d48bc1da2fd5b1521a48a4fa36d68deaabe496f3c3fa9646d5796b6b974f3930a02c4b5aee6c85c860d7d7f79009416724465e835f40b87898c36de4 + languageName: node + linkType: hard + "yargs-parser@npm:^18.1.2": version: 18.1.3 resolution: "yargs-parser@npm:18.1.3" From 5277c9c48b655a39901a38afcd68309fd0495034 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Tue, 4 Nov 2025 06:06:36 +0100 Subject: [PATCH 03/15] :wrench: --- apps/example/index.html | 33 +++++--- apps/example/index.web.js | 20 ++--- apps/example/metro.config.js | 76 ++++++++++--------- apps/example/src/App.tsx | 1 + .../example/src/resolveAssetSourcePolyfill.js | 57 ++++++++++++++ 5 files changed, 129 insertions(+), 58 deletions(-) create mode 100644 apps/example/src/resolveAssetSourcePolyfill.js diff --git a/apps/example/index.html b/apps/example/index.html index 150b8e2fde..a2e3764367 100644 --- a/apps/example/index.html +++ b/apps/example/index.html @@ -2,31 +2,40 @@ - + - React Native Skia Example - - +
- + diff --git a/apps/example/index.web.js b/apps/example/index.web.js index 4cc088f47a..227a573aa4 100644 --- a/apps/example/index.web.js +++ b/apps/example/index.web.js @@ -3,19 +3,21 @@ import { AppRegistry } from "react-native"; import App from "./src/App"; import { name as appName } from "./app.json"; -if (!Symbol.dispose) { - Symbol.dispose = Symbol.for("Symbol.dispose"); -} - AppRegistry.registerComponent(appName, () => App); -if (typeof document !== "undefined") { - const rootTag = - document.getElementById("root") ?? document.getElementById("app"); +const rootTag = document.getElementById("root"); + +if (process.env.NODE_ENV !== "production") { if (!rootTag) { throw new Error( - 'React Native Web root element with id "root" was not found in the document', + 'Required HTML element with id "root" was not found in the document HTML.', ); } - AppRegistry.runApplication(appName, { rootTag }); } + +CanvasKitInit({ + locateFile: (file) => `https://unpkg.com/canvaskit-wasm/bin/full/${file}`, +}).then((CanvasKit) => { + window.CanvasKit = global.CanvasKit = CanvasKit; + AppRegistry.runApplication(appName, { rootTag }); +}); diff --git a/apps/example/metro.config.js b/apps/example/metro.config.js index 51cadc9f4e..e44064a8d7 100644 --- a/apps/example/metro.config.js +++ b/apps/example/metro.config.js @@ -1,6 +1,14 @@ const path = require("path"); +const { resolve: defaultResolve } = require("metro-resolver"); const { makeMetroConfig } = require("@rnx-kit/metro-config"); +const root = path.resolve(__dirname, "../.."); +const rnwPath = path.resolve(root, "node_modules/react-native-web"); +const assetRegistryPath = path.resolve( + root, + "node_modules/react-native-web/dist/modules/AssetRegistry/index", +); + const metroConfig = makeMetroConfig({ transformer: { getTransformOptions: async () => ({ @@ -12,44 +20,38 @@ const metroConfig = makeMetroConfig({ }, }); -const resolver = (metroConfig.resolver = metroConfig.resolver ?? {}); - -resolver.platforms = Array.from( - new Set([...(resolver.platforms ?? []), "web"]), -); - -const webSourceExts = [ - "web.tsx", - "web.ts", - "web.jsx", - "web.js", - "web.mjs", - "web.cjs", -]; -resolver.sourceExts = Array.from( - new Set([...(resolver.sourceExts ?? []), ...webSourceExts]), -); - -if (process.env.IS_WEB_BUILD) { - const reactNativeWebPath = path.dirname( - require.resolve("react-native-web/package.json"), - ); - - resolver.extraNodeModules = { - ...(resolver.extraNodeModules ?? {}), - "react-native": path.join(reactNativeWebPath, "dist", "index.js"), +function getWebMetroConfig(config) { + config.resolver = config.resolver ?? {}; + config.resolver.platforms = ["ios", "android", "web"]; + + const origResolveRequest = + config.resolver.resolveRequest ?? + ((context, moduleName, platform) => + defaultResolve(context, moduleName, platform)); + + config.resolver.resolveRequest = (contextRaw, moduleName, platform) => { + const context = { + ...contextRaw, + preferNativePlatform: false, + }; + + if (moduleName === "react-native") { + return { + filePath: path.resolve(rnwPath, "dist/index.js"), + type: "sourceFile", + }; + } + + // Let default config handle other modules + return origResolveRequest(context, moduleName, platform); }; - metroConfig.transformer = { - ...(metroConfig.transformer ?? {}), - assetRegistryPath: path.join( - reactNativeWebPath, - "dist", - "modules", - "AssetRegistry", - "index.js", - ), - }; + config.transformer = config.transformer ?? {}; + config.transformer.assetRegistryPath = assetRegistryPath; + + return config; } -module.exports = metroConfig; +module.exports = !!process.env.IS_WEB_BUILD + ? getWebMetroConfig(metroConfig) + : metroConfig; diff --git a/apps/example/src/App.tsx b/apps/example/src/App.tsx index 277e37330d..b11b743622 100644 --- a/apps/example/src/App.tsx +++ b/apps/example/src/App.tsx @@ -39,6 +39,7 @@ import { HomeScreen } from "./Home"; import type { StackParamList } from "./types"; import { useAssets } from "./Tests/useAssets"; import { Chess } from "./Examples/Chess"; +import "./resolveAssetSourcePolyfill"; const linking: LinkingOptions = { config: { diff --git a/apps/example/src/resolveAssetSourcePolyfill.js b/apps/example/src/resolveAssetSourcePolyfill.js new file mode 100644 index 0000000000..e8041f5cc2 --- /dev/null +++ b/apps/example/src/resolveAssetSourcePolyfill.js @@ -0,0 +1,57 @@ +import { Image, PixelRatio, Platform } from "react-native"; +import { getAssetByID } from "react-native-web/dist/modules/AssetRegistry"; + +// react-native-web does not support resolveAssetSource out of the box +// https://github.com/necolas/react-native-web/issues/1666 +if (Platform.OS === "web") { + function resolveAssetUri(source) { + let uri = null; + if (typeof source === "number") { + // get the URI from the packager + const asset = getAssetByID(source); + if (asset == null) { + throw new Error( + `Image: asset with ID "${source}" could not be found. Please check the image source or packager.`, + ); + } + // eslint-disable-next-line prefer-destructuring + let scale = asset.scales[0]; + if (asset.scales.length > 1) { + const preferredScale = PixelRatio.get(); + // Get the scale which is closest to the preferred scale + scale = asset.scales.reduce((prev, curr) => + Math.abs(curr - preferredScale) < Math.abs(prev - preferredScale) + ? curr + : prev, + ); + } + const scaleSuffix = scale !== 1 ? `@${scale}x` : ""; + uri = asset + ? `${asset.httpServerLocation}/${asset.name}${scaleSuffix}.${asset.type}` + : ""; + } else if (typeof source === "string") { + uri = source; + } else if (source && typeof source.uri === "string") { + // eslint-disable-next-line prefer-destructuring + uri = source.uri; + } + + if (uri) { + const svgDataUriPattern = /^(data:image\/svg\+xml;utf8,)(.*)/; + const match = uri.match(svgDataUriPattern); + // inline SVG markup may contain characters (e.g., #, ") that need to be escaped + if (match) { + const [, prefix, svg] = match; + const encodedSvg = encodeURIComponent(svg); + return `${prefix}${encodedSvg}`; + } + } + + return uri; + } + + Image.resolveAssetSource = (source) => { + const uri = resolveAssetUri(source) || ""; + return { uri }; + }; +} From 2998d94a420854736154c04a4cb5ef40e2e5e367 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Tue, 4 Nov 2025 07:03:07 +0100 Subject: [PATCH 04/15] :green_heart: --- apps/example/index.web.js | 2 +- packages/skia/src/views/SkiaPictureView.web.tsx | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/apps/example/index.web.js b/apps/example/index.web.js index 227a573aa4..8dec729e3b 100644 --- a/apps/example/index.web.js +++ b/apps/example/index.web.js @@ -10,7 +10,7 @@ const rootTag = document.getElementById("root"); if (process.env.NODE_ENV !== "production") { if (!rootTag) { throw new Error( - 'Required HTML element with id "root" was not found in the document HTML.', + 'Required HTML element with id "root" was not found in the document HTML.' ); } } diff --git a/packages/skia/src/views/SkiaPictureView.web.tsx b/packages/skia/src/views/SkiaPictureView.web.tsx index b990860166..a002923e8a 100644 --- a/packages/skia/src/views/SkiaPictureView.web.tsx +++ b/packages/skia/src/views/SkiaPictureView.web.tsx @@ -16,6 +16,18 @@ import type { ISkiaViewApiWeb } from "../specs/NativeSkiaModule.web"; import type { SkiaPictureViewNativeProps } from "./types"; import { SkiaViewNativeId } from "./SkiaViewNativeId"; +const dp2Pixel = (pd: number, rect?: SkRect) => { + if (!rect) { + return undefined; + } + return { + x: rect.x * pd, + y: rect.y * pd, + width: rect.width * pd, + height: rect.height * pd, + }; +}; + interface Renderer { onResize(): void; draw(picture: SkPicture): void; @@ -41,7 +53,7 @@ class WebGLRenderer implements Renderer { canvas!.clear(CanvasKit.TRANSPARENT); this.draw(picture); this.surface.ref.flush(); - return this.surface.makeImageSnapshot(rect); + return this.surface.makeImageSnapshot(dp2Pixel(this.pd, rect)); } onResize() { @@ -187,7 +199,7 @@ class StaticWebGLRenderer implements Renderer { const { surface, tempCanvas } = renderResult; try { - this.cachedImage = surface.makeImageSnapshot(rect); + this.cachedImage = surface.makeImageSnapshot(dp2Pixel(this.pd, rect)); } catch (error) { console.error("Error creating image snapshot:", error); } finally { From 923760b75198a28b35cd192c60aff3d34c0346ec Mon Sep 17 00:00:00 2001 From: William Candillon Date: Tue, 4 Nov 2025 07:17:52 +0100 Subject: [PATCH 05/15] :wrench: --- packages/skia/src/__tests__/setup.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/skia/src/__tests__/setup.ts b/packages/skia/src/__tests__/setup.ts index b527a91aeb..aa8f822051 100644 --- a/packages/skia/src/__tests__/setup.ts +++ b/packages/skia/src/__tests__/setup.ts @@ -9,8 +9,9 @@ import type { SkSurface, SkImage } from "../skia/types"; export const E2E = process.env.E2E === "true"; export const CI = process.env.CI === "true"; +export const WEB = process.env.WEB === "true"; export const itFailsE2e = E2E ? it.failing : it; -export const itRunsE2eOnly = E2E ? it : it.skip; +export const itRunsE2eOnly = E2E && !WEB ? it : it.skip; export const itRunsNodeOnly = E2E ? it.skip : it; export const itRunsCIAndNodeOnly = CI || !E2E ? it : it.skip; From f1e2bd9c4122c71953040b0a814bcde803fce100 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Tue, 4 Nov 2025 07:36:38 +0100 Subject: [PATCH 06/15] :wrench: --- .github/workflows/ci.yml | 72 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8bae814f3b..d98bd34291 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -340,6 +340,78 @@ jobs: path: apps/docs/static/ name: tests-docs-screenshots + test-web: + runs-on: macos-latest-xlarge + steps: + - name: Checkout + uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0 + with: + submodules: recursive + + - name: Setup + uses: ./.github/actions/setup + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Start Package Manager + working-directory: apps/example + run: E2E=true yarn web & + + - name: Install Playwright + run: | + npx --yes @playwright/test@latest install chromium + + - name: Wait for web app to be ready + run: | + echo "Waiting for http://localhost:8081 to become available..." + ready=false + for i in {1..120}; do + if curl --silent --fail http://localhost:8081 >/dev/null; then + echo "Web app is responding." + ready=true + break + fi + sleep 1 + done + + if [ "$ready" != true ]; then + echo "Web app did not respond within 120 seconds." + exit 1 + fi + + echo "Giving the app a few extra seconds to finish bundling..." + sleep 10 + + - name: Run e2e tests + working-directory: packages/skia + run: | + TMP_DIR="$(mktemp -d -t playwright-keepalive)" + SPEC="$TMP_DIR/keepalive.spec.js" + + cat <<'EOF' > "$SPEC" + const { test } = require('@playwright/test'); + + test('keep Expo web app available for Jest', async ({ page }) => { + await page.goto('http://localhost:8081', { waitUntil: 'networkidle' }); + await page.waitForTimeout(60 * 60 * 1000); + }); + EOF + + npx --yes @playwright/test@latest test "$SPEC" --project=chromium --timeout=0 --workers=1 --reporter=line & + PW_PID=$! + + cleanup() { + if kill -0 "$PW_PID" 2>/dev/null; then + kill "$PW_PID" || true + wait "$PW_PID" || true + fi + rm -rf "$TMP_DIR" + } + + trap cleanup EXIT + + WEB=true E2E=true CI=true yarn test -i Shaders + build-test-ios: runs-on: macos-latest-xlarge env: From cbb372b5139cc93cc0ab9c8e44801193f556cb33 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Tue, 4 Nov 2025 08:17:22 +0100 Subject: [PATCH 07/15] :wrench: --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d98bd34291..495366c703 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -410,7 +410,7 @@ jobs: trap cleanup EXIT - WEB=true E2E=true CI=true yarn test -i Shaders + WEB=true E2E=true CI=true yarn test -i Shader build-test-ios: runs-on: macos-latest-xlarge From 42b855f4985e22a37ffce13896747a8b07fb7a42 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Tue, 4 Nov 2025 08:25:09 +0100 Subject: [PATCH 08/15] Remove project specification from Playwright test command --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 495366c703..5a72847af5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -397,7 +397,7 @@ jobs: }); EOF - npx --yes @playwright/test@latest test "$SPEC" --project=chromium --timeout=0 --workers=1 --reporter=line & + npx --yes @playwright/test@latest test "$SPEC" --timeout=0 --workers=1 --reporter=line & PW_PID=$! cleanup() { From 96ac62870a3f3e8c1912eedc41121c957d34d4f1 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Tue, 4 Nov 2025 08:31:30 +0100 Subject: [PATCH 09/15] Remove specific test target from CI workflow --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a72847af5..919b7cf266 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -410,7 +410,7 @@ jobs: trap cleanup EXIT - WEB=true E2E=true CI=true yarn test -i Shader + WEB=true E2E=true CI=true yarn test build-test-ios: runs-on: macos-latest-xlarge From b8abb792bdc18334dd25f68d1b078466ee326771 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Tue, 4 Nov 2025 12:36:32 +0100 Subject: [PATCH 10/15] :wrench: --- .github/workflows/ci.yml | 142 +++++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 495366c703..180b406455 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -340,77 +340,77 @@ jobs: path: apps/docs/static/ name: tests-docs-screenshots - test-web: - runs-on: macos-latest-xlarge - steps: - - name: Checkout - uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0 - with: - submodules: recursive - - - name: Setup - uses: ./.github/actions/setup - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - - - name: Start Package Manager - working-directory: apps/example - run: E2E=true yarn web & - - - name: Install Playwright - run: | - npx --yes @playwright/test@latest install chromium - - - name: Wait for web app to be ready - run: | - echo "Waiting for http://localhost:8081 to become available..." - ready=false - for i in {1..120}; do - if curl --silent --fail http://localhost:8081 >/dev/null; then - echo "Web app is responding." - ready=true - break - fi - sleep 1 - done - - if [ "$ready" != true ]; then - echo "Web app did not respond within 120 seconds." - exit 1 - fi - - echo "Giving the app a few extra seconds to finish bundling..." - sleep 10 - - - name: Run e2e tests - working-directory: packages/skia - run: | - TMP_DIR="$(mktemp -d -t playwright-keepalive)" - SPEC="$TMP_DIR/keepalive.spec.js" - - cat <<'EOF' > "$SPEC" - const { test } = require('@playwright/test'); - - test('keep Expo web app available for Jest', async ({ page }) => { - await page.goto('http://localhost:8081', { waitUntil: 'networkidle' }); - await page.waitForTimeout(60 * 60 * 1000); - }); - EOF - - npx --yes @playwright/test@latest test "$SPEC" --project=chromium --timeout=0 --workers=1 --reporter=line & - PW_PID=$! - - cleanup() { - if kill -0 "$PW_PID" 2>/dev/null; then - kill "$PW_PID" || true - wait "$PW_PID" || true - fi - rm -rf "$TMP_DIR" - } - - trap cleanup EXIT - - WEB=true E2E=true CI=true yarn test -i Shader + # test-web: + # runs-on: macos-latest-xlarge + # steps: + # - name: Checkout + # uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0 + # with: + # submodules: recursive + + # - name: Setup + # uses: ./.github/actions/setup + # with: + # github_token: ${{ secrets.GITHUB_TOKEN }} + + # - name: Start Package Manager + # working-directory: apps/example + # run: E2E=true yarn web & + + # - name: Install Playwright + # run: | + # npx --yes @playwright/test@latest install chromium + + # - name: Wait for web app to be ready + # run: | + # echo "Waiting for http://localhost:8081 to become available..." + # ready=false + # for i in {1..120}; do + # if curl --silent --fail http://localhost:8081 >/dev/null; then + # echo "Web app is responding." + # ready=true + # break + # fi + # sleep 1 + # done + + # if [ "$ready" != true ]; then + # echo "Web app did not respond within 120 seconds." + # exit 1 + # fi + + # echo "Giving the app a few extra seconds to finish bundling..." + # sleep 10 + + # - name: Run e2e tests + # working-directory: packages/skia + # run: | + # TMP_DIR="$(mktemp -d -t playwright-keepalive)" + # SPEC="$TMP_DIR/keepalive.spec.js" + + # cat <<'EOF' > "$SPEC" + # const { test } = require('@playwright/test'); + + # test('keep Expo web app available for Jest', async ({ page }) => { + # await page.goto('http://localhost:8081', { waitUntil: 'networkidle' }); + # await page.waitForTimeout(60 * 60 * 1000); + # }); + # EOF + + # npx --yes @playwright/test@latest test "$SPEC" --project=chromium --timeout=0 --workers=1 --reporter=line & + # PW_PID=$! + + # cleanup() { + # if kill -0 "$PW_PID" 2>/dev/null; then + # kill "$PW_PID" || true + # wait "$PW_PID" || true + # fi + # rm -rf "$TMP_DIR" + # } + + # trap cleanup EXIT + + # WEB=true E2E=true CI=true yarn test -i Shader build-test-ios: runs-on: macos-latest-xlarge From 35f27394a2fe482cdea7749da18fcdfd403aa717 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Wed, 12 Nov 2025 15:43:58 +0100 Subject: [PATCH 11/15] Update ci.yml --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 180b406455..cab0f4825a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -261,7 +261,7 @@ jobs: env: cache-name: cache-apk with: - path: apps/example/android/app/build/outputs/apk/debug/app-debug.apk + path: apps/example/android/app/build/intermediates/apk/debug/app-debug.apk key: apk-${{ github.sha }} test-android: @@ -285,7 +285,7 @@ jobs: id: cache-apk uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: - path: apps/example/android/app/build/outputs/apk/debug/app-debug.apk + path: apps/example/android/app/build/intermediates/apk/debug/app-debug.apk key: apk-${{ github.sha }} - name: SKDs - download required images @@ -308,11 +308,11 @@ jobs: - name: Check APK existence uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0 with: - files: apps/example/android/app/build/outputs/apk/debug/app-debug.apk + files: apps/example/android/app/build/intermediates/apk/debug/app-debug.apk fail: true - name: Install APK - run: adb install -r apps/example/android/app/build/outputs/apk/debug/app-debug.apk + run: adb install -r apps/example/android/app/build/intermediates/apk/debug/app-debug.apk # - name: Set up environment # run: echo "PACKAGE_NAME=${{ env.PACKAGE_NAME }}" >> $GITHUB_ENV From 95c3cde5e8f103146717d02282db7e0e7dca78ae Mon Sep 17 00:00:00 2001 From: William Candillon Date: Wed, 12 Nov 2025 15:45:14 +0100 Subject: [PATCH 12/15] Update APK path in CI workflow for Android --- .github/workflows/ci-graphite.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-graphite.yml b/.github/workflows/ci-graphite.yml index d34580bc46..7b2fc9de61 100644 --- a/.github/workflows/ci-graphite.yml +++ b/.github/workflows/ci-graphite.yml @@ -169,7 +169,7 @@ jobs: env: cache-name: cache-apk with: - path: apps/example/android/app/build/outputs/apk/debug/app-debug.apk + path: apps/example/android/app/build/intermediates/apk/debug/app-debug.apk key: apk-${{ github.sha }} test-android-graphite: @@ -194,7 +194,7 @@ jobs: id: cache-apk uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: - path: apps/example/android/app/build/outputs/apk/debug/app-debug.apk + path: apps/example/android/app/build/intermediates/apk/debug/app-debug.apk key: apk-${{ github.sha }} - name: SKDs - download required images @@ -217,11 +217,11 @@ jobs: - name: Check APK existence uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0 with: - files: apps/example/android/app/build/outputs/apk/debug/app-debug.apk + files: apps/example/android/app/build/intermediates/apk/debug/app-debug.apk fail: true - name: Install APK - run: adb install -r apps/example/android/app/build/outputs/apk/debug/app-debug.apk + run: adb install -r apps/example/android/app/build/intermediates/apk/debug/app-debug.apk # - name: Set up environment # run: echo "PACKAGE_NAME=${{ env.PACKAGE_NAME }}" >> $GITHUB_ENV From 28628b380d9c758b3e66bb72417a02b54ef34c02 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Wed, 12 Nov 2025 16:26:36 +0100 Subject: [PATCH 13/15] Add step to locate APK files in CI workflow Added a step to find APK files in the CI workflow. --- .github/workflows/ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cab0f4825a..acb6bca169 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -256,6 +256,14 @@ jobs: run: | yarn turbo run build:android --concurrency 1 + - name: Find APK location + run: | + echo "Searching for APK files..." + find apps/example/android -name "*.apk" -type f || echo "No APK files found" + echo "Checking common output directories..." + ls -la apps/example/android/app/build/outputs/apk/debug/ 2>/dev/null || echo "outputs/apk/debug doesn't exist" + ls -la apps/example/android/app/build/intermediates/apk/debug/ 2>/dev/null || echo "intermediates/apk/debug doesn't exist" + - name: Cache apk uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 env: From bebc250aef4d0ba1ecf564f16abd54964d3b1555 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Wed, 12 Nov 2025 17:20:03 +0100 Subject: [PATCH 14/15] remove bogus change --- .github/workflows/ci-graphite.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-graphite.yml b/.github/workflows/ci-graphite.yml index 7b2fc9de61..d34580bc46 100644 --- a/.github/workflows/ci-graphite.yml +++ b/.github/workflows/ci-graphite.yml @@ -169,7 +169,7 @@ jobs: env: cache-name: cache-apk with: - path: apps/example/android/app/build/intermediates/apk/debug/app-debug.apk + path: apps/example/android/app/build/outputs/apk/debug/app-debug.apk key: apk-${{ github.sha }} test-android-graphite: @@ -194,7 +194,7 @@ jobs: id: cache-apk uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: - path: apps/example/android/app/build/intermediates/apk/debug/app-debug.apk + path: apps/example/android/app/build/outputs/apk/debug/app-debug.apk key: apk-${{ github.sha }} - name: SKDs - download required images @@ -217,11 +217,11 @@ jobs: - name: Check APK existence uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0 with: - files: apps/example/android/app/build/intermediates/apk/debug/app-debug.apk + files: apps/example/android/app/build/outputs/apk/debug/app-debug.apk fail: true - name: Install APK - run: adb install -r apps/example/android/app/build/intermediates/apk/debug/app-debug.apk + run: adb install -r apps/example/android/app/build/outputs/apk/debug/app-debug.apk # - name: Set up environment # run: echo "PACKAGE_NAME=${{ env.PACKAGE_NAME }}" >> $GITHUB_ENV From 3e92487a08b7b97ae0f24da29b19169365ed83be Mon Sep 17 00:00:00 2001 From: William Candillon Date: Wed, 12 Nov 2025 17:20:37 +0100 Subject: [PATCH 15/15] remove bogus change --- .github/workflows/ci.yml | 88 ++-------------------------------------- 1 file changed, 4 insertions(+), 84 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index acb6bca169..8bae814f3b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -256,20 +256,12 @@ jobs: run: | yarn turbo run build:android --concurrency 1 - - name: Find APK location - run: | - echo "Searching for APK files..." - find apps/example/android -name "*.apk" -type f || echo "No APK files found" - echo "Checking common output directories..." - ls -la apps/example/android/app/build/outputs/apk/debug/ 2>/dev/null || echo "outputs/apk/debug doesn't exist" - ls -la apps/example/android/app/build/intermediates/apk/debug/ 2>/dev/null || echo "intermediates/apk/debug doesn't exist" - - name: Cache apk uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 env: cache-name: cache-apk with: - path: apps/example/android/app/build/intermediates/apk/debug/app-debug.apk + path: apps/example/android/app/build/outputs/apk/debug/app-debug.apk key: apk-${{ github.sha }} test-android: @@ -293,7 +285,7 @@ jobs: id: cache-apk uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: - path: apps/example/android/app/build/intermediates/apk/debug/app-debug.apk + path: apps/example/android/app/build/outputs/apk/debug/app-debug.apk key: apk-${{ github.sha }} - name: SKDs - download required images @@ -316,11 +308,11 @@ jobs: - name: Check APK existence uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0 with: - files: apps/example/android/app/build/intermediates/apk/debug/app-debug.apk + files: apps/example/android/app/build/outputs/apk/debug/app-debug.apk fail: true - name: Install APK - run: adb install -r apps/example/android/app/build/intermediates/apk/debug/app-debug.apk + run: adb install -r apps/example/android/app/build/outputs/apk/debug/app-debug.apk # - name: Set up environment # run: echo "PACKAGE_NAME=${{ env.PACKAGE_NAME }}" >> $GITHUB_ENV @@ -348,78 +340,6 @@ jobs: path: apps/docs/static/ name: tests-docs-screenshots - # test-web: - # runs-on: macos-latest-xlarge - # steps: - # - name: Checkout - # uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0 - # with: - # submodules: recursive - - # - name: Setup - # uses: ./.github/actions/setup - # with: - # github_token: ${{ secrets.GITHUB_TOKEN }} - - # - name: Start Package Manager - # working-directory: apps/example - # run: E2E=true yarn web & - - # - name: Install Playwright - # run: | - # npx --yes @playwright/test@latest install chromium - - # - name: Wait for web app to be ready - # run: | - # echo "Waiting for http://localhost:8081 to become available..." - # ready=false - # for i in {1..120}; do - # if curl --silent --fail http://localhost:8081 >/dev/null; then - # echo "Web app is responding." - # ready=true - # break - # fi - # sleep 1 - # done - - # if [ "$ready" != true ]; then - # echo "Web app did not respond within 120 seconds." - # exit 1 - # fi - - # echo "Giving the app a few extra seconds to finish bundling..." - # sleep 10 - - # - name: Run e2e tests - # working-directory: packages/skia - # run: | - # TMP_DIR="$(mktemp -d -t playwright-keepalive)" - # SPEC="$TMP_DIR/keepalive.spec.js" - - # cat <<'EOF' > "$SPEC" - # const { test } = require('@playwright/test'); - - # test('keep Expo web app available for Jest', async ({ page }) => { - # await page.goto('http://localhost:8081', { waitUntil: 'networkidle' }); - # await page.waitForTimeout(60 * 60 * 1000); - # }); - # EOF - - # npx --yes @playwright/test@latest test "$SPEC" --project=chromium --timeout=0 --workers=1 --reporter=line & - # PW_PID=$! - - # cleanup() { - # if kill -0 "$PW_PID" 2>/dev/null; then - # kill "$PW_PID" || true - # wait "$PW_PID" || true - # fi - # rm -rf "$TMP_DIR" - # } - - # trap cleanup EXIT - - # WEB=true E2E=true CI=true yarn test -i Shader - build-test-ios: runs-on: macos-latest-xlarge env: