Skip to content

feat: add Unistyles v3 support#59

Merged
mfkrause merged 8 commits into
mainfrom
feat/unistyles
Jun 24, 2026
Merged

feat: add Unistyles v3 support#59
mfkrause merged 8 commits into
mainfrom
feat/unistyles

Conversation

@mfkrause

@mfkrause mfkrause commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Currently, React Native Boost is incompatible with Unistyles, see issue #58.

This PR adds a unistyles mode that routes elements that are provably styled by Unistyles stylesheet to Unistyles' own lean host, keeping reactivity while still skipping React Native's JS-based wrapper. Previously, Boost would optimize elements with unknown sources of style. Now, when unistyles mode is enabled, React Native Boost bails on components with unresolvable styles (e.g. props.style or styles imported from another file), because we can't reliably tell what to rewrite the component to. If unistyles mode is not enabled, the previous behavior stays unchanged.

It also fixes a pre-existing bug where descendants of an already-optimized element bailed (a rewritten host was treated as an unknown ancestor), so only the outermost element ever optimized. Nested optimization now cascades correctly.

Closes #58

Summary by CodeRabbit

  • New Features

    • Added Unistyles compatibility guidance to the getting started docs and a dedicated setup page.
    • Added an example screen showcasing Unistyles theme switching, responsive styling, and runtime status details.
    • Expanded the sample app navigation to include a new Unistyles demo entry.
  • Bug Fixes

    • Improved style handling so supported Unistyles and standard React Native cases are optimized correctly.
    • Added safeguards for unsupported or ambiguous styling patterns to avoid incorrect transformations.
  • Documentation

    • Clarified setup steps for projects using Unistyles or Nativewind.

@vercel

vercel Bot commented Jun 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
react-native-boost-docs Skipped Skipped Jun 24, 2026 2:01pm

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: de3e2eac-94cd-4523-ae4e-65ad30657f50

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Adds Unistyles v3 compatibility to the react-native-boost Babel plugin. The plugin now classifies each Text/View element's style origin as unistyles, plain, or unknown, routing Unistyles-sourced styles to Unistyles' own native host (preserving reactivity), processing plain styles as before, and bailing on unresolvable origins. Auto-detection of react-native-unistyles installation is supported alongside an explicit unistyles option.

Changes

Unistyles v3 Support

Layer / File(s) Summary
Types, constants, and Unistyles detection
packages/react-native-boost/src/plugin/types/index.ts, packages/react-native-boost/src/plugin/utils/constants.ts, packages/react-native-boost/src/plugin/utils/unistyles.ts, packages/react-native-boost/tsconfig.json
PluginOptions gains unistyles?: boolean; Optimizer type gains unistylesEnabled?; Unistyles module name/path constants added; isUnistylesInstalled walks package.json upward to auto-detect the library.
Optimized-host WeakMap and native component sourcing
packages/react-native-boost/src/plugin/utils/common/optimized-host.ts, packages/react-native-boost/src/plugin/utils/common/base.ts, packages/react-native-boost/src/plugin/utils/common/index.ts
New OptimizedHostKind WeakMap with markOptimizedHost/getOptimizedHostKind; replaceWithNativeComponent refactored to accept a NativeComponentSource override, derive import params from it, and record the host kind; UNISTYLES_TEXT_HOST and UNISTYLES_VIEW_HOST descriptors exported.
Style-origin classification and ancestor recognition
packages/react-native-boost/src/plugin/utils/common/validation.ts
Adds StyleOrigin, createStyleOriginResolver (lazily memoized), and classifyStyleOrigin (bounded-recursion, distinguishes plain/unistyles/unknown). Enhances classifyJSXElementAsAncestor and classifyModuleBindingAsAncestor with optimized-host early-exit paths so descendants of already-rewritten elements classify correctly.
Plugin entry point and test-plugin wiring
packages/react-native-boost/src/plugin/index.ts, packages/react-native-boost/src/plugin/utils/generate-test-plugin.ts
Plugin factory resolves options/platform once, computes unistylesEnabled via explicit option or auto-detection, emits a one-time warning on auto-detection, and passes the flag into both optimizers. generateTestPlugin and generateCombinedTestPlugin forward the flag accordingly.
Text optimizer Unistyles routing
packages/react-native-boost/src/plugin/optimizers/text/index.ts
textOptimizer accepts unistylesEnabled, classifies style origin, bails on 'unknown', and for 'unistyles' passes style by identity (skipping processTextStyle) while selecting UNISTYLES_TEXT_HOST as the replacement host. processProps gains passStyleByIdentity to gate style-transform generation.
View optimizer Unistyles routing
packages/react-native-boost/src/plugin/optimizers/view/index.ts
viewOptimizer accepts unistylesEnabled, adds style to the spread-guard set in Unistyles mode, bails on 'unknown' style origins, and selects UNISTYLES_VIEW_HOST when the origin resolves to 'unistyles'.
Optimizer tests and fixtures
packages/react-native-boost/src/plugin/optimizers/text/__tests__/*, packages/react-native-boost/src/plugin/optimizers/view/__tests__/*, packages/react-native-boost/src/plugin/__tests__/*
New pluginTester suites for text/view Unistyles mode and nesting. Fixture pairs cover: Unistyles style, aliased/optional/array/cast styles, plain RN style, literal style, no style, imported/unknown-style bail, spread bail, and mixed-origin nesting under lean view.
Example app: UnistylesDemo screen and setup
apps/example/src/screens/unistyles-demo/index.tsx, apps/example/src/unistyles.ts, apps/example/src/app.tsx, apps/example/src/navigation.ts, apps/example/src/screens/launcher.tsx, apps/example/babel.config.js, apps/example/index.ts, apps/example/package.json
Adds UnistylesDemoScreen with theme toggle and status readout; defines light/dark themes and breakpoints; registers them via StyleSheet.configure; wires the new route; adds launcher card; enables unistyles: true in babel config; adds dependencies.
Documentation updates
apps/docs/content/docs/compatibility/unistyles.mdx, apps/docs/content/docs/index.mdx, apps/docs/content/docs/configuration/configure.mdx, apps/docs/content/docs/meta.json, README.md
Rewrites unistyles.mdx with setup, how-it-works, and known-limitations sections. Adds Unistyles/Nativewind setup cards to getting-started guide. Adds unistyles: false to configure example and reorders compatibility nav.

Sequence Diagram(s)

sequenceDiagram
    participant BabelPlugin as Babel Plugin (index.ts)
    participant isUnistylesInstalled
    participant textOptimizer
    participant createStyleOriginResolver
    participant classifyStyleOrigin
    participant replaceWithNativeComponent

    BabelPlugin->>isUnistylesInstalled: isUnistylesInstalled(dirname)
    isUnistylesInstalled-->>BabelPlugin: true/false (unistylesEnabled)
    BabelPlugin->>textOptimizer: visit JSXOpeningElement(path, ..., unistylesEnabled)
    textOptimizer->>createStyleOriginResolver: createStyleOriginResolver(path, unistylesEnabled)
    createStyleOriginResolver->>classifyStyleOrigin: classify style expression
    classifyStyleOrigin-->>createStyleOriginResolver: 'unistyles' | 'plain' | 'unknown'
    createStyleOriginResolver-->>textOptimizer: getStyleOrigin()
    alt origin = 'unknown'
        textOptimizer-->>BabelPlugin: bail (no replacement)
    else origin = 'unistyles'
        textOptimizer->>replaceWithNativeComponent: (path, 'NativeText', UNISTYLES_TEXT_HOST)
        replaceWithNativeComponent-->>textOptimizer: JSX rewritten to Unistyles NativeText
    else origin = 'plain'
        textOptimizer->>replaceWithNativeComponent: (path, 'NativeText', undefined)
        replaceWithNativeComponent-->>textOptimizer: JSX rewritten to Boost NativeText
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • kuatsu/react-native-boost#23: Modifies the same textOptimizer processProps function in packages/react-native-boost/src/plugin/optimizers/text/index.ts, extending prop-processing logic in a way that directly overlaps with the passStyleByIdentity refactor introduced here.

Poem

🐇 Hoppity-hop through the style tree I go,
Checking each prop — is it Unistyles? Oh!
'unistyles' routes left, 'plain' routes right,
'unknown'? We bail, keeping reactivity tight.
The WeakMap remembers what ancestors were,
So descendants stay styled — no styles will blur! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding Unistyles v3 support.
Linked Issues check ✅ Passed The PR implements Unistyles mode, routes resolvable Unistyles styles to native wrappers, bails on unknown styles, and preserves plain RN behavior.
Out of Scope Changes check ✅ Passed No clear out-of-scope changes stand out; the docs, examples, tests, and refactors all support Unistyles support and nested optimization fixes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/unistyles

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mfkrause mfkrause marked this pull request as ready for review June 24, 2026 13:39

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/react-native-boost/src/plugin/index.ts`:
- Around line 28-30: The auto-detect hint in the Babel plugin is only tracked
per plugin instance, so it still logs once per JSX file instead of once per
build; move the latch from the local `unistylesHintLogged` variable in
`index.ts` to module scope and key it by the project root or equivalent shared
identifier used by the plugin. Also reorder the flow so `isIgnoredFile(...)` is
checked before any auto-detect warning logic, ensuring ignored files return
early and never trigger the hint. Use the existing `isUnistylesInstalled(...)`
and `unistylesEnabled` path as the entry point for the change.

In `@packages/react-native-boost/src/plugin/utils/unistyles.ts`:
- Around line 32-39: The package.json probe in unistyles.ts is swallowing all
read/parse failures and incorrectly falling back to parent manifests. Update the
loop in the package.json lookup logic to only continue when the current
package.json is missing, and rethrow malformed JSON or permission/read errors
instead of silently skipping them. Use the existing directory-walking code
around fs.readFileSync, JSON.parse, and nodePath.dirname to distinguish
file-not-found cases from other failures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2de5eadf-1df7-4ddf-8e3c-32a7551f0f7d

📥 Commits

Reviewing files that changed from the base of the PR and between 93c751b and 907a493.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (68)
  • README.md
  • apps/docs/content/docs/compatibility/unistyles.mdx
  • apps/docs/content/docs/configuration/configure.mdx
  • apps/docs/content/docs/index.mdx
  • apps/docs/content/docs/meta.json
  • apps/example/babel.config.js
  • apps/example/index.ts
  • apps/example/package.json
  • apps/example/src/app.tsx
  • apps/example/src/navigation.ts
  • apps/example/src/screens/launcher.tsx
  • apps/example/src/screens/unistyles-demo/index.tsx
  • apps/example/src/unistyles.ts
  • packages/react-native-boost/src/plugin/__tests__/fixtures-nesting-unistyles/cascades-view-text/code.js
  • packages/react-native-boost/src/plugin/__tests__/fixtures-nesting-unistyles/cascades-view-text/output.js
  • packages/react-native-boost/src/plugin/__tests__/fixtures-nesting-unistyles/mixed-origins-under-lean-view/code.js
  • packages/react-native-boost/src/plugin/__tests__/fixtures-nesting-unistyles/mixed-origins-under-lean-view/output.js
  • packages/react-native-boost/src/plugin/__tests__/fixtures-nesting/cascades-view-text/code.js
  • packages/react-native-boost/src/plugin/__tests__/fixtures-nesting/cascades-view-text/output.js
  • packages/react-native-boost/src/plugin/__tests__/fixtures-nesting/text-in-text-stays-bailed/code.js
  • packages/react-native-boost/src/plugin/__tests__/fixtures-nesting/text-in-text-stays-bailed/output.js
  • packages/react-native-boost/src/plugin/__tests__/nesting.test.ts
  • packages/react-native-boost/src/plugin/index.ts
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles-ts/unistyles-cast-style/code.tsx
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles-ts/unistyles-cast-style/output.tsx
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-aliased-style/code.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-aliased-style/output.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-imported-style-bails/code.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-imported-style-bails/output.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-literal-style/code.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-literal-style/output.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-no-style/code.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-no-style/output.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-optional-member-style/code.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-optional-member-style/output.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-plain-rn-style/code.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-plain-rn-style/output.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-style-array/code.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-style-array/output.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-style/code.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-style/output.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-unknown-style-bails/code.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures-unistyles/unistyles-unknown-style-bails/output.js
  • packages/react-native-boost/src/plugin/optimizers/text/__tests__/index.test.ts
  • packages/react-native-boost/src/plugin/optimizers/text/index.ts
  • packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures-unistyles/unistyles-imported-style-bails/code.js
  • packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures-unistyles/unistyles-imported-style-bails/output.js
  • packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures-unistyles/unistyles-no-style/code.js
  • packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures-unistyles/unistyles-no-style/output.js
  • packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures-unistyles/unistyles-plain-rn-style/code.js
  • packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures-unistyles/unistyles-plain-rn-style/output.js
  • packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures-unistyles/unistyles-style-in-resolvable-spread-bails/code.js
  • packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures-unistyles/unistyles-style-in-resolvable-spread-bails/output.js
  • packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures-unistyles/unistyles-style/code.js
  • packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures-unistyles/unistyles-style/output.js
  • packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures-unistyles/unistyles-unknown-style-bails/code.js
  • packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures-unistyles/unistyles-unknown-style-bails/output.js
  • packages/react-native-boost/src/plugin/optimizers/view/__tests__/index.test.ts
  • packages/react-native-boost/src/plugin/optimizers/view/index.ts
  • packages/react-native-boost/src/plugin/types/index.ts
  • packages/react-native-boost/src/plugin/utils/common/base.ts
  • packages/react-native-boost/src/plugin/utils/common/index.ts
  • packages/react-native-boost/src/plugin/utils/common/optimized-host.ts
  • packages/react-native-boost/src/plugin/utils/common/validation.ts
  • packages/react-native-boost/src/plugin/utils/constants.ts
  • packages/react-native-boost/src/plugin/utils/generate-test-plugin.ts
  • packages/react-native-boost/src/plugin/utils/unistyles.ts
  • packages/react-native-boost/tsconfig.json

Comment on lines +28 to +30
const autoDetectedUnistyles = options.unistyles === undefined && isUnistylesInstalled(dirname);
const unistylesEnabled = options.unistyles === true || autoDetectedUnistyles;
let unistylesHintLogged = false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major

🧩 Analysis chain

🌐 Web query:

With @babel/helper-plugin-utils's declare(...), is the plugin factory invoked once per transformed file or once per overall build session?

💡 Result:

The function you pass to @babel/helper-plugin-utils’s declare(...) (the “plugin factory” that returns the visitor object) is invoked once per Babel instance / transform run (i.e., once per file being transformed), not once per overall build session. Babel’s documentation describes declare(...) as wrapping a plugin definition so Babel can instantiate it; Babel then “instantiates” plugins during compilation rather than treating them as a single shared global for the whole process [1]. In practice, Babel calls the exported plugin function for each file being compiled (using the file’s options context like filename/dirname), then uses the returned visitor for that specific traversal [1][2]. So: expect declare(...) to run per transformed file (per transform call), not only once for the entire build/watch session. If you need true “run once per build” behavior, you should implement it outside the plugin (e.g., in the loader/build script) or use an external cache keyed by build/session rather than relying on declare(...) invocation frequency.

Citations:


🏁 Script executed:

sed -n '1,120p' packages/react-native-boost/src/plugin/index.ts

Repository: kuatsu/react-native-boost

Length of output: 2981


🏁 Script executed:

python3 - <<'PY'
from pathlib import Path
p = Path('packages/react-native-boost/src/plugin/index.ts')
for i, line in enumerate(p.read_text().splitlines(), 1):
    if 1 <= i <= 120:
        print(f"{i:4}: {line}")
PY

Repository: kuatsu/react-native-boost

Length of output: 3389


Make the auto-detect warning process-scoped. unistylesHintLogged resets for every Babel plugin instance, so the hint will still fire once per JSX file instead of once per build. Move the latch to module scope, keyed by the project root or equivalent. Also place the isIgnoredFile(...) check before this warning so ignored files short-circuit cleanly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react-native-boost/src/plugin/index.ts` around lines 28 - 30, The
auto-detect hint in the Babel plugin is only tracked per plugin instance, so it
still logs once per JSX file instead of once per build; move the latch from the
local `unistylesHintLogged` variable in `index.ts` to module scope and key it by
the project root or equivalent shared identifier used by the plugin. Also
reorder the flow so `isIgnoredFile(...)` is checked before any auto-detect
warning logic, ensuring ignored files return early and never trigger the hint.
Use the existing `isUnistylesInstalled(...)` and `unistylesEnabled` path as the
entry point for the change.

Comment on lines +32 to +39
// Walk up until the filesystem root, stopping at the first readable `package.json`.
for (;;) {
try {
return JSON.parse(fs.readFileSync(nodePath.join(directory, 'package.json'), 'utf8'));
} catch {
const parent = nodePath.dirname(directory);
if (parent === directory) return undefined;
directory = parent;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Don't swallow malformed local package.json files.

Lines 34-36 catch every failure and keep walking upward. If the nearest manifest exists but is invalid JSON or unreadable, the probe silently falls back to a parent workspace package.json, which can enable or disable Unistyles mode for the wrong project. Only treat missing-file cases as “keep walking”; rethrow parse and permission errors.

Suggested fix
 function readNearestPackageJson(fromDirectory: string): Record<string, unknown> | undefined {
   let directory = nodePath.resolve(fromDirectory);

   // Walk up until the filesystem root, stopping at the first readable `package.json`.
   for (;;) {
     try {
       return JSON.parse(fs.readFileSync(nodePath.join(directory, 'package.json'), 'utf8'));
-    } catch {
+    } catch (error) {
+      const code =
+        typeof error === 'object' && error !== null && 'code' in error
+          ? (error as { code?: string }).code
+          : undefined;
+      if (code !== 'ENOENT' && code !== 'ENOTDIR') {
+        throw error;
+      }
       const parent = nodePath.dirname(directory);
       if (parent === directory) return undefined;
       directory = parent;
     }
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Walk up until the filesystem root, stopping at the first readable `package.json`.
for (;;) {
try {
return JSON.parse(fs.readFileSync(nodePath.join(directory, 'package.json'), 'utf8'));
} catch {
const parent = nodePath.dirname(directory);
if (parent === directory) return undefined;
directory = parent;
// Walk up until the filesystem root, stopping at the first readable `package.json`.
for (;;) {
try {
return JSON.parse(fs.readFileSync(nodePath.join(directory, 'package.json'), 'utf8'));
} catch (error) {
const code =
typeof error === 'object' && error !== null && 'code' in error
? (error as { code?: string }).code
: undefined;
if (code !== 'ENOENT' && code !== 'ENOTDIR') {
throw error;
}
const parent = nodePath.dirname(directory);
if (parent === directory) return undefined;
directory = parent;
}
}
🧰 Tools
🪛 ast-grep (0.44.0)

[warning] 34-34: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(nodePath.join(directory, 'package.json'), 'utf8')
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react-native-boost/src/plugin/utils/unistyles.ts` around lines 32 -
39, The package.json probe in unistyles.ts is swallowing all read/parse failures
and incorrectly falling back to parent manifests. Update the loop in the
package.json lookup logic to only continue when the current package.json is
missing, and rethrow malformed JSON or permission/read errors instead of
silently skipping them. Use the existing directory-walking code around
fs.readFileSync, JSON.parse, and nodePath.dirname to distinguish file-not-found
cases from other failures.

@mfkrause mfkrause merged commit bb58f93 into main Jun 24, 2026
7 checks passed
@mfkrause mfkrause deleted the feat/unistyles branch June 24, 2026 14:02
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.

Unistyles v3 Support

1 participant