From d964e36e31047dee372a5bd95efe5fdf2ea61dec Mon Sep 17 00:00:00 2001 From: wanxiankai Date: Thu, 13 Aug 2026 11:14:02 +0800 Subject: [PATCH 1/5] fix: verify native prebuild fixtures --- .changeset/calm-bears-resolve.md | 5 ++++ .../host/src/node/babel-plugin/plugin.test.ts | 2 +- packages/host/src/node/babel-plugin/plugin.ts | 10 ++++++++ packages/node-addon-examples/package.json | 1 + .../scripts/verify-prebuilds.mts | 23 ++++++++++++++++++- pnpm-lock.yaml | 3 +++ 6 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 .changeset/calm-bears-resolve.md diff --git a/.changeset/calm-bears-resolve.md b/.changeset/calm-bears-resolve.md new file mode 100644 index 00000000..94aa61c0 --- /dev/null +++ b/.changeset/calm-bears-resolve.md @@ -0,0 +1,5 @@ +--- +"react-native-node-api": patch +--- + +Preserve Node.js module resolution precedence when a JavaScript file and native addon share a basename. diff --git a/packages/host/src/node/babel-plugin/plugin.test.ts b/packages/host/src/node/babel-plugin/plugin.test.ts index 7dae2979..d3fe5fac 100644 --- a/packages/host/src/node/babel-plugin/plugin.test.ts +++ b/packages/host/src/node/babel-plugin/plugin.test.ts @@ -129,7 +129,7 @@ describe("plugin", () => { itTransforms("and does not touch required JS files", { files: { "package.json": `{ "name": "my-package" }`, - // TODO: Add a ./my-addon.node to make this test complete + "my-addon.node": "// This is supposed to be a binary file", "my-addon.js": "// Some JS file", "index.js": ` const addon = require('./my-addon'); diff --git a/packages/host/src/node/babel-plugin/plugin.ts b/packages/host/src/node/babel-plugin/plugin.ts index 45e269e9..9829419f 100644 --- a/packages/host/src/node/babel-plugin/plugin.ts +++ b/packages/host/src/node/babel-plugin/plugin.ts @@ -1,4 +1,5 @@ import assert from "node:assert/strict"; +import { createRequire } from "node:module"; import path from "node:path"; import type { PluginObj, NodePath } from "@babel/core"; @@ -101,6 +102,7 @@ export function plugin(): PluginObj { } } else if ( !path.isAbsolute(id) && + !resolvesToNonNodeModule(id, this.filename) && isNodeApiModule(path.join(from, id)) ) { const relativePath = path.join(from, id); @@ -114,3 +116,11 @@ export function plugin(): PluginObj { }, }; } + +function resolvesToNonNodeModule(id: string, filename: string): boolean { + try { + return !createRequire(filename).resolve(id).endsWith(".node"); + } catch { + return false; + } +} diff --git a/packages/node-addon-examples/package.json b/packages/node-addon-examples/package.json index acfd70ec..47e9ff76 100644 --- a/packages/node-addon-examples/package.json +++ b/packages/node-addon-examples/package.json @@ -30,6 +30,7 @@ "bootstrap": "node --run copy-and-build" }, "devDependencies": { + "@expo/plist": "0.4.7", "cmake-rn": "workspace:*", "node-addon-examples": "github:nodejs/node-addon-examples#4b7dd86a85644610e6de80154df9acac9329b509", "gyp-to-cmake": "workspace:*", diff --git a/packages/node-addon-examples/scripts/verify-prebuilds.mts b/packages/node-addon-examples/scripts/verify-prebuilds.mts index 94b4e1bb..61ae9057 100644 --- a/packages/node-addon-examples/scripts/verify-prebuilds.mts +++ b/packages/node-addon-examples/scripts/verify-prebuilds.mts @@ -2,6 +2,7 @@ import fs from "node:fs"; import assert from "node:assert/strict"; import path from "node:path"; +import plistModule from "@expo/plist"; import { DIRS } from "./cmake-projects.mjs"; const EXPECTED_ANDROID_ARCHS = ["armeabi-v7a", "arm64-v8a", "x86_64", "x86"]; @@ -65,7 +66,27 @@ async function verifyApplePrebuild(dirent: fs.Dirent) { "Expected only directory and files in framework", ); if (file.name === "Info.plist") { - // TODO: Verify the contents of the Info.plist file + const libraryName = path.basename(frameworkDir, ".framework"); + const infoPlist: unknown = plistModule.default.parse( + await fs.promises.readFile( + path.join(frameworkDir, file.name), + "utf8", + ), + ); + assert( + typeof infoPlist === "object" && infoPlist !== null, + "Expected Info.plist to contain a dictionary", + ); + assert("CFBundleExecutable" in infoPlist); + assert("CFBundleIdentifier" in infoPlist); + assert.equal(infoPlist.CFBundleExecutable, libraryName); + assert.equal( + infoPlist.CFBundleIdentifier, + `com.callstackincubator.node-api.${libraryName}`.replace( + /[^A-Za-z0-9-.]/g, + "-", + ), + ); continue; } else { assert( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9632a67c..13230f65 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -281,6 +281,9 @@ importers: specifier: workspace:* version: link:../host devDependencies: + '@expo/plist': + specifier: 0.4.7 + version: 0.4.7 cmake-rn: specifier: workspace:* version: link:../cmake-rn From 1ff15bfdc905ecee061d9bf5a953a202603ec6ad Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Aug 2026 08:24:33 +0000 Subject: [PATCH 2/5] Move .js/.node precedence fix into isNodeApiModule, dedupe plist checks - Replace the Babel-transform-time require.resolve() guard with a check inside isNodeApiModule itself, so the fix lives in the shared utility (also used by findNodeAddonForBindings) instead of duplicating Node's module resolution algorithm via a second, independent code path that could diverge from what Metro actually resolves at runtime. - Verify the Info.plist contents with a zod schema instead of ad hoc "in" checks on an untyped object, matching how the rest of the repo validates untrusted structured data. - Reuse the exported escapeBundleIdentifier instead of re-deriving the bundle-identifier escaping regex inline in the verify script, so the two can't silently drift apart. Closes #424 Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Q1k6UQJPPaqKEKmnsRUatt --- .changeset/calm-bears-resolve.md | 2 +- packages/host/src/node/babel-plugin/plugin.ts | 10 ---- packages/host/src/node/index.ts | 1 + packages/host/src/node/path-utils.ts | 12 ++++ packages/node-addon-examples/package.json | 3 +- .../scripts/verify-prebuilds.mts | 57 ++++++++++++------- pnpm-lock.yaml | 3 + 7 files changed, 56 insertions(+), 32 deletions(-) diff --git a/.changeset/calm-bears-resolve.md b/.changeset/calm-bears-resolve.md index 94aa61c0..b274b102 100644 --- a/.changeset/calm-bears-resolve.md +++ b/.changeset/calm-bears-resolve.md @@ -2,4 +2,4 @@ "react-native-node-api": patch --- -Preserve Node.js module resolution precedence when a JavaScript file and native addon share a basename. +Preserve Node.js module resolution precedence when a JavaScript file and native addon share a basename: `require('./foo')` no longer gets rewritten to load a Node-API addon when a same-named `foo.js`/`.cjs`/`.mjs`/`.json` file exists alongside it, since that source file is what `require()` actually resolves to. An explicit `require('./foo.node')` is unaffected. diff --git a/packages/host/src/node/babel-plugin/plugin.ts b/packages/host/src/node/babel-plugin/plugin.ts index 9829419f..45e269e9 100644 --- a/packages/host/src/node/babel-plugin/plugin.ts +++ b/packages/host/src/node/babel-plugin/plugin.ts @@ -1,5 +1,4 @@ import assert from "node:assert/strict"; -import { createRequire } from "node:module"; import path from "node:path"; import type { PluginObj, NodePath } from "@babel/core"; @@ -102,7 +101,6 @@ export function plugin(): PluginObj { } } else if ( !path.isAbsolute(id) && - !resolvesToNonNodeModule(id, this.filename) && isNodeApiModule(path.join(from, id)) ) { const relativePath = path.join(from, id); @@ -116,11 +114,3 @@ export function plugin(): PluginObj { }, }; } - -function resolvesToNonNodeModule(id: string, filename: string): boolean { - try { - return !createRequire(filename).resolve(id).endsWith(".node"); - } catch { - return false; - } -} diff --git a/packages/host/src/node/index.ts b/packages/host/src/node/index.ts index 1c4c69a9..baee642c 100644 --- a/packages/host/src/node/index.ts +++ b/packages/host/src/node/index.ts @@ -20,6 +20,7 @@ export { createXCframework, createUniversalAppleLibrary, determineXCFrameworkFilename, + escapeBundleIdentifier, } from "./prebuilds/apple.js"; export { diff --git a/packages/host/src/node/path-utils.ts b/packages/host/src/node/path-utils.ts index 859de2de..1de2dd9a 100644 --- a/packages/host/src/node/path-utils.ts +++ b/packages/host/src/node/path-utils.ts @@ -59,6 +59,9 @@ export type NamingStrategy = { // Cache mapping package directory to package name across calls const packageNameCache = new Map(); +// Extensions Node's own require() resolves before ever trying `.node`. +const COLLIDING_SOURCE_EXTENSIONS = [".js", ".cjs", ".mjs", ".json"]; + /** * @param modulePath Batch-scans the path to the module to check (must be extensionless or end in .node) * @returns True if a platform specific prebuild exists for the module path, warns on unreadable modules. @@ -66,6 +69,15 @@ const packageNameCache = new Map(); * TODO: Consider checking for a specific platform extension. */ export function isNodeApiModule(modulePath: string): boolean { + if ( + !modulePath.endsWith(".node") && + COLLIDING_SOURCE_EXTENSIONS.some((extension) => + fs.existsSync(modulePath + extension), + ) + ) { + // An explicit require('./foo.node') has no such ambiguity to defer to. + return false; + } { // HACK: Take a shortcut (if applicable): existing `.node` files are addons try { diff --git a/packages/node-addon-examples/package.json b/packages/node-addon-examples/package.json index 47e9ff76..c33f2122 100644 --- a/packages/node-addon-examples/package.json +++ b/packages/node-addon-examples/package.json @@ -39,6 +39,7 @@ }, "dependencies": { "assert": "^2.1.0", - "react-native-node-api": "workspace:*" + "react-native-node-api": "workspace:*", + "zod": "^4.1.11" } } diff --git a/packages/node-addon-examples/scripts/verify-prebuilds.mts b/packages/node-addon-examples/scripts/verify-prebuilds.mts index 61ae9057..62e521c9 100644 --- a/packages/node-addon-examples/scripts/verify-prebuilds.mts +++ b/packages/node-addon-examples/scripts/verify-prebuilds.mts @@ -3,8 +3,21 @@ import assert from "node:assert/strict"; import path from "node:path"; import plistModule from "@expo/plist"; +import { escapeBundleIdentifier } from "react-native-node-api"; +import { z } from "zod"; + import { DIRS } from "./cmake-projects.mjs"; +// @expo/plist is CJS with an `export default`; under this genuine ESM +// (.mts) module's interop, the default import binds to the whole +// `module.exports`, nesting the real API one `.default` deeper. +const plist = plistModule.default; + +const FrameworkInfoPlistSchema = z.object({ + CFBundleExecutable: z.string(), + CFBundleIdentifier: z.string(), +}); + const EXPECTED_ANDROID_ARCHS = ["armeabi-v7a", "arm64-v8a", "x86_64", "x86"]; const EXPECTED_XCFRAMEWORK_PLATFORMS = [ @@ -38,6 +51,27 @@ async function verifyAndroidPrebuild(dirent: fs.Dirent) { } } +async function verifyFrameworkInfoPlist( + infoPlistPath: string, + libraryName: string, +) { + const contents = await fs.promises.readFile(infoPlistPath, "utf8"); + const parsed = FrameworkInfoPlistSchema.parse(plist.parse(contents)); + assert.equal( + parsed.CFBundleExecutable, + libraryName, + `Unexpected CFBundleExecutable in ${infoPlistPath}`, + ); + assert.equal( + parsed.CFBundleIdentifier, + // Mirrors the default writeFrameworkInfoPlist derives in + // packages/host/src/node/prebuilds/apple.ts, since none of the + // examples pass --apple-bundle-identifier. + escapeBundleIdentifier(`com.callstackincubator.node-api.${libraryName}`), + `Unexpected CFBundleIdentifier in ${infoPlistPath}`, + ); +} + async function verifyApplePrebuild(dirent: fs.Dirent) { console.log("Verifying Apple prebuild", dirent.name, "in", dirent.parentPath); for (const arch of EXPECTED_XCFRAMEWORK_PLATFORMS) { @@ -67,27 +101,10 @@ async function verifyApplePrebuild(dirent: fs.Dirent) { ); if (file.name === "Info.plist") { const libraryName = path.basename(frameworkDir, ".framework"); - const infoPlist: unknown = plistModule.default.parse( - await fs.promises.readFile( - path.join(frameworkDir, file.name), - "utf8", - ), - ); - assert( - typeof infoPlist === "object" && infoPlist !== null, - "Expected Info.plist to contain a dictionary", - ); - assert("CFBundleExecutable" in infoPlist); - assert("CFBundleIdentifier" in infoPlist); - assert.equal(infoPlist.CFBundleExecutable, libraryName); - assert.equal( - infoPlist.CFBundleIdentifier, - `com.callstackincubator.node-api.${libraryName}`.replace( - /[^A-Za-z0-9-.]/g, - "-", - ), + await verifyFrameworkInfoPlist( + path.join(frameworkDir, file.name), + libraryName, ); - continue; } else { assert( !file.name.endsWith(".node"), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 13230f65..375fe4c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -280,6 +280,9 @@ importers: react-native-node-api: specifier: workspace:* version: link:../host + zod: + specifier: ^4.1.11 + version: 4.4.3 devDependencies: '@expo/plist': specifier: 0.4.7 From 63a903b8ce74228ff62fbd4dd2562350fb10f5e2 Mon Sep 17 00:00:00 2001 From: wanxiankai Date: Mon, 24 Aug 2026 09:56:02 +0800 Subject: [PATCH 3/5] fix: match Node addon resolution extensions --- packages/host/src/node/path-utils.test.ts | 12 ++++++++++++ packages/host/src/node/path-utils.ts | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/host/src/node/path-utils.test.ts b/packages/host/src/node/path-utils.test.ts index 7d8e62fd..66419024 100644 --- a/packages/host/src/node/path-utils.test.ts +++ b/packages/host/src/node/path-utils.test.ts @@ -66,6 +66,18 @@ describe("isNodeApiModule", () => { assert(isNodeApiModule(path.join(tempDirectoryPath, "addon.node"))); }); + it("does not treat .cjs or .mjs files as shadowing .node", (context) => { + const tempDirectoryPath = setupTempDirectory(context, { + "cjs-addon.cjs": "// Some CommonJS file", + "cjs-addon.node": "// This is supposed to be a binary file", + "mjs-addon.mjs": "// Some ES module file", + "mjs-addon.node": "// This is supposed to be a binary file", + }); + + assert(isNodeApiModule(path.join(tempDirectoryPath, "cjs-addon"))); + assert(isNodeApiModule(path.join(tempDirectoryPath, "mjs-addon"))); + }); + it( "returns false when directory cannot be read due to permissions", // Skipping on Windows because there is no way to set ACLs on directories in Node.js on Windows without brittle powershell commands diff --git a/packages/host/src/node/path-utils.ts b/packages/host/src/node/path-utils.ts index 1de2dd9a..9ab3ac2f 100644 --- a/packages/host/src/node/path-utils.ts +++ b/packages/host/src/node/path-utils.ts @@ -60,7 +60,7 @@ export type NamingStrategy = { const packageNameCache = new Map(); // Extensions Node's own require() resolves before ever trying `.node`. -const COLLIDING_SOURCE_EXTENSIONS = [".js", ".cjs", ".mjs", ".json"]; +const COLLIDING_SOURCE_EXTENSIONS = [".js", ".json"]; /** * @param modulePath Batch-scans the path to the module to check (must be extensionless or end in .node) From 53579827e6143609eafeb84d6a74ca9fe2397040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Sun, 11 Oct 2026 23:34:40 +0200 Subject: [PATCH 4/5] Parse binary framework plists and verify the slices that were built xcodebuild emits binary Info.plist files for CMake framework targets, so read them with bplist-parser (falling back to @expo/plist for XML ones). Only assert CFBundleExecutable and that the executable exists, since the identifier and CFBundleName come from CMake rather than the host. Verify whichever slices were built instead of a fixed list, allowing dSYMs. Co-Authored-By: Claude Opus 5.5 (1M context) --- packages/host/src/node/index.ts | 1 - packages/node-addon-examples/package.json | 3 +- .../scripts/verify-prebuilds.mts | 75 ++++++++++--------- pnpm-lock.yaml | 17 ++++- 4 files changed, 55 insertions(+), 41 deletions(-) diff --git a/packages/host/src/node/index.ts b/packages/host/src/node/index.ts index baee642c..1c4c69a9 100644 --- a/packages/host/src/node/index.ts +++ b/packages/host/src/node/index.ts @@ -20,7 +20,6 @@ export { createXCframework, createUniversalAppleLibrary, determineXCFrameworkFilename, - escapeBundleIdentifier, } from "./prebuilds/apple.js"; export { diff --git a/packages/node-addon-examples/package.json b/packages/node-addon-examples/package.json index c33f2122..a30cddeb 100644 --- a/packages/node-addon-examples/package.json +++ b/packages/node-addon-examples/package.json @@ -31,9 +31,10 @@ }, "devDependencies": { "@expo/plist": "0.4.7", + "bplist-parser": "^0.5.0", "cmake-rn": "workspace:*", - "node-addon-examples": "github:nodejs/node-addon-examples#4b7dd86a85644610e6de80154df9acac9329b509", "gyp-to-cmake": "workspace:*", + "node-addon-examples": "github:nodejs/node-addon-examples#4b7dd86a85644610e6de80154df9acac9329b509", "read-pkg": "^9.0.1", "weak-node-api": "workspace:*" }, diff --git a/packages/node-addon-examples/scripts/verify-prebuilds.mts b/packages/node-addon-examples/scripts/verify-prebuilds.mts index 62e521c9..0a27b9d8 100644 --- a/packages/node-addon-examples/scripts/verify-prebuilds.mts +++ b/packages/node-addon-examples/scripts/verify-prebuilds.mts @@ -3,7 +3,7 @@ import assert from "node:assert/strict"; import path from "node:path"; import plistModule from "@expo/plist"; -import { escapeBundleIdentifier } from "react-native-node-api"; +import { parseBuffer } from "bplist-parser"; import { z } from "zod"; import { DIRS } from "./cmake-projects.mjs"; @@ -15,20 +15,16 @@ const plist = plistModule.default; const FrameworkInfoPlistSchema = z.object({ CFBundleExecutable: z.string(), - CFBundleIdentifier: z.string(), }); -const EXPECTED_ANDROID_ARCHS = ["armeabi-v7a", "arm64-v8a", "x86_64", "x86"]; +const KNOWN_ANDROID_ARCHS = ["armeabi-v7a", "arm64-v8a", "x86_64", "x86"]; -const EXPECTED_XCFRAMEWORK_PLATFORMS = [ - "ios-arm64", - "ios-arm64-simulator", - "macos-arm64_x86_64", - "tvos-arm64", - "tvos-arm64-simulator", - "xros-arm64", - "xros-arm64-simulator", -]; +async function readSubdirectories(dir: string) { + const entries = await fs.promises.readdir(dir, { withFileTypes: true }); + const subdirectories = entries.filter((entry) => entry.isDirectory()); + assert(subdirectories.length > 0, `Expected at least one slice in ${dir}`); + return subdirectories; +} async function verifyAndroidPrebuild(dirent: fs.Dirent) { console.log( @@ -37,8 +33,13 @@ async function verifyAndroidPrebuild(dirent: fs.Dirent) { "in", dirent.parentPath, ); - for (const arch of EXPECTED_ANDROID_ARCHS) { - const archDir = path.join(dirent.parentPath, dirent.name, arch); + const prebuildDir = path.join(dirent.parentPath, dirent.name); + for (const arch of await readSubdirectories(prebuildDir)) { + assert( + KNOWN_ANDROID_ARCHS.includes(arch.name), + `Unexpected Android arch: ${arch.name}`, + ); + const archDir = path.join(prebuildDir, arch.name); for (const file of await fs.promises.readdir(archDir, { withFileTypes: true, })) { @@ -51,38 +52,46 @@ async function verifyAndroidPrebuild(dirent: fs.Dirent) { } } -async function verifyFrameworkInfoPlist( - infoPlistPath: string, - libraryName: string, -) { - const contents = await fs.promises.readFile(infoPlistPath, "utf8"); - const parsed = FrameworkInfoPlistSchema.parse(plist.parse(contents)); +async function readPlist(plistPath: string): Promise { + const contents = await fs.promises.readFile(plistPath); + // xcodebuild emits binary plists, while the host writes XML ones + if (contents.subarray(0, 6).toString("ascii") === "bplist") { + const [parsed] = parseBuffer(contents); + return parsed; + } + return plist.parse(contents.toString("utf8")); +} + +async function verifyFrameworkInfoPlist(frameworkDir: string) { + const libraryName = path.basename(frameworkDir, ".framework"); + const infoPlistPath = path.join(frameworkDir, "Info.plist"); + const parsed = FrameworkInfoPlistSchema.parse(await readPlist(infoPlistPath)); assert.equal( parsed.CFBundleExecutable, libraryName, `Unexpected CFBundleExecutable in ${infoPlistPath}`, ); - assert.equal( - parsed.CFBundleIdentifier, - // Mirrors the default writeFrameworkInfoPlist derives in - // packages/host/src/node/prebuilds/apple.ts, since none of the - // examples pass --apple-bundle-identifier. - escapeBundleIdentifier(`com.callstackincubator.node-api.${libraryName}`), - `Unexpected CFBundleIdentifier in ${infoPlistPath}`, + assert( + fs.existsSync(path.join(frameworkDir, libraryName)), + `Missing executable ${libraryName} in ${frameworkDir}`, ); } async function verifyApplePrebuild(dirent: fs.Dirent) { console.log("Verifying Apple prebuild", dirent.name, "in", dirent.parentPath); - for (const arch of EXPECTED_XCFRAMEWORK_PLATFORMS) { - const archDir = path.join(dirent.parentPath, dirent.name, arch); - for (const file of await fs.promises.readdir(archDir, { + const prebuildDir = path.join(dirent.parentPath, dirent.name); + for (const slice of await readSubdirectories(prebuildDir)) { + const sliceDir = path.join(prebuildDir, slice.name); + for (const file of await fs.promises.readdir(sliceDir, { withFileTypes: true, })) { assert( file.isDirectory(), "Expected only directories in xcframework arch directory", ); + if (file.name === "dSYMs") { + continue; + } assert(file.name.endsWith(".framework"), "Expected framework directory"); const frameworkDir = path.join(file.parentPath, file.name); for (const file of await fs.promises.readdir(frameworkDir, { @@ -100,11 +109,7 @@ async function verifyApplePrebuild(dirent: fs.Dirent) { "Expected only directory and files in framework", ); if (file.name === "Info.plist") { - const libraryName = path.basename(frameworkDir, ".framework"); - await verifyFrameworkInfoPlist( - path.join(frameworkDir, file.name), - libraryName, - ); + await verifyFrameworkInfoPlist(frameworkDir); } else { assert( !file.name.endsWith(".node"), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 375fe4c8..135d5ff1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -287,6 +287,9 @@ importers: '@expo/plist': specifier: 0.4.7 version: 0.4.7 + bplist-parser: + specifier: ^0.5.0 + version: 0.5.0 cmake-rn: specifier: workspace:* version: link:../cmake-rn @@ -295,7 +298,7 @@ importers: version: link:../gyp-to-cmake node-addon-examples: specifier: github:nodejs/node-addon-examples#4b7dd86a85644610e6de80154df9acac9329b509 - version: git+https://git@github.com:nodejs/node-addon-examples.git#4b7dd86a85644610e6de80154df9acac9329b509 + version: https://codeload.github.com/nodejs/node-addon-examples/tar.gz/4b7dd86a85644610e6de80154df9acac9329b509 read-pkg: specifier: ^9.0.1 version: 9.0.1 @@ -2717,6 +2720,10 @@ packages: resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} engines: {node: '>=18'} + bplist-parser@0.5.0: + resolution: {integrity: sha512-owgQ0RGbndIGNrQkpHe99HVSslIK59t31nVyAPDRqhq9mNDc2kRl5HwYO292GuKjHqiJRdJNiaI63a02hjLFIQ==} + engines: {node: '>=20.19.0'} + brace-expansion@1.1.16: resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==} @@ -4076,8 +4083,8 @@ packages: resolution: {integrity: sha512-ekZMeaaIzSQTSpr7X2X3iJM7lTzgnx8ahAG9pJfT/7+14mlEM8ZYQ9cgCDvSSRbReFK0oHli3WrZdCiRsgAT9Q==} engines: {node: ^18 || ^20 || >= 21} - node-addon-examples@git+https://git@github.com:nodejs/node-addon-examples.git#4b7dd86a85644610e6de80154df9acac9329b509: - resolution: {commit: 4b7dd86a85644610e6de80154df9acac9329b509, repo: git@github.com:nodejs/node-addon-examples.git, type: git} + node-addon-examples@https://codeload.github.com/nodejs/node-addon-examples/tar.gz/4b7dd86a85644610e6de80154df9acac9329b509: + resolution: {tarball: https://codeload.github.com/nodejs/node-addon-examples/tar.gz/4b7dd86a85644610e6de80154df9acac9329b509} version: 1.0.0 node-api-headers@1.9.0: @@ -7728,6 +7735,8 @@ snapshots: transitivePeerDependencies: - supports-color + bplist-parser@0.5.0: {} + brace-expansion@1.1.16: dependencies: balanced-match: 1.0.2 @@ -9276,7 +9285,7 @@ snapshots: node-addon-api@8.9.0: {} - node-addon-examples@git+https://git@github.com:nodejs/node-addon-examples.git#4b7dd86a85644610e6de80154df9acac9329b509: + node-addon-examples@https://codeload.github.com/nodejs/node-addon-examples/tar.gz/4b7dd86a85644610e6de80154df9acac9329b509: dependencies: chalk: 5.6.2 cmake-js: 7.4.0 From b52cc14a0e9c6223240bbcae730a9ca29667b282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Sun, 11 Oct 2026 23:34:40 +0200 Subject: [PATCH 5/5] ci: verify example prebuilds in the iOS job Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/check.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 478d8634..59193362 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -199,6 +199,8 @@ jobs: env: CMAKE_RN_TRIPLETS: arm64;x86_64-apple-ios-sim FERRIC_TARGETS: aarch64-apple-ios-sim,x86_64-apple-ios + - name: Verify prebuilds + run: pnpm --filter @react-native-node-api/node-addon-examples run verify # Mirrors React Native's scripts/xcode/ccache.conf, which only applied # while ccache ran through their wrapper script (it sets CCACHE_CONFIGPATH # to it) and so no longer does now that it runs as a compiler launcher.