Skip to content

fix: verify native prebuild fixtures - #426

Open
wanxiankai wants to merge 2 commits into
callstackincubator:nextfrom
wanxiankai:fix/verify-prebuild-fixtures
Open

fix: verify native prebuild fixtures#426
wanxiankai wants to merge 2 commits into
callstackincubator:nextfrom
wanxiankai:fix/verify-prebuild-fixtures

Conversation

@wanxiankai

Copy link
Copy Markdown

Summary

  • parse each Apple framework Info.plist and verify its executable and bundle identifier
  • add the competing .node fixture to the Babel plugin test
  • preserve Node.js module resolution precedence when JavaScript and native addon files share a basename

Test plan

  • pnpm run build
  • pnpm --filter react-native-node-api test (61 tests passed)
  • pnpm exec eslint packages/host/src/node/babel-plugin/plugin.ts packages/host/src/node/babel-plugin/plugin.test.ts packages/node-addon-examples/scripts/verify-prebuilds.mts
  • pnpm run prettier:check

Closes #424

Comment thread packages/node-addon-examples/scripts/verify-prebuilds.mts Outdated
@kraenhansen

Copy link
Copy Markdown
Collaborator

Sorry, this needs a rebase now 🙈

@kraenhansen kraenhansen self-assigned this Aug 13, 2026
@wanxiankai
wanxiankai force-pushed the fix/verify-prebuild-fixtures branch from 33ca234 to 2d4f74f Compare August 13, 2026 13:22
@wanxiankai

Copy link
Copy Markdown
Author

Rebased onto the latest next and force-pushed as 2d4f74f. I also switched the plist parser to the package-level @expo/plist export. The branch is now conflict-free.

Re-ran:

  • pnpm run build
  • pnpm --filter react-native-node-api test (61/61 passed)
  • targeted ESLint
  • pnpm run prettier:check

Everything passes locally. The GitHub Actions workflow is currently awaiting maintainer approval.

kraenhansen commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

I pushed a few follow-up refinements (moving the .js/.node precedence check into the shared isNodeApiModule utility instead of the Babel plugin's require.resolve() guard, zod-validating the parsed Info.plist, and reusing escapeBundleIdentifier instead of re-deriving the escaping regex). Thanks for finding and fixing the real bug here, @wanxiankai!


Partially generated by Claude Code

wanxiankai and others added 2 commits August 16, 2026 20:10
- 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 callstackincubator#424

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q1k6UQJPPaqKEKmnsRUatt
@kraenhansen
kraenhansen force-pushed the fix/verify-prebuild-fixtures branch from a848741 to 7e212f5 Compare August 16, 2026 18:22

@kraenhansen kraenhansen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified this against next and I think it fully closes #424:

  • Info.plist gap: verifyFrameworkInfoPlist asserts CFBundleExecutable/CFBundleIdentifier against exactly what writeFrameworkInfoPlist (packages/host/src/node/prebuilds/apple.ts) writes, reusing the same exported escapeBundleIdentifier helper on both sides so the check can't silently drift from the writer.
  • Missing .node fixture gap: the new my-addon.node fixture in plugin.test.ts genuinely exercises precedence, not just decorates the test. Before the isNodeApiModule guard added in path-utils.ts, adding that fixture alone (without the fix) would make the pre-existing shortcut check (fs.accessSync(modulePath + '.node')) return true first and fail the "does not touch required JS files" assertion — so the fixture and the fix are load-bearing together, confirming the original issue's claim that the old test passed for the wrong reason.

CI is green (Lint + Unit tests on ubuntu/macos/windows all success; the native app/device jobs show skipped, which is expected for a fork PR without the runner secrets/labels), mergeable_state is clean, and it's already got a maintainer approval.

One minor, non-blocking note for a possible follow-up: COLLIDING_SOURCE_EXTENSIONS includes .cjs/.mjs alongside .js/.json, with a comment attributing the list to "extensions Node's own require() resolves before ever trying .node". Node's documented LOAD_AS_FILE algorithm for an extensionless require() only tries .js, .json, then .node — not .mjs/.cjs — and per docs/HOW-IT-WORKS.md the actual runtime resolution here is Metro's bundler anyway, whose default sourceExts likewise don't include mjs/cjs. So a sibling foo.cjs/foo.mjs wouldn't actually shadow foo.node the way the guard assumes. This only makes the plugin more conservative than necessary (it could skip transforming a real addon require()), not a regression of the bug #424 describes, so it doesn't block merging — just a slightly inaccurate rationale comment worth tightening sometime.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Two test gaps: unverified prebuild Info.plist and a missing .node fixture in the Babel plugin tests

3 participants