[
  {
    "path": ".github/FUNDING.yml",
    "content": "github: mfkrause\n"
  },
  {
    "path": ".github/actions/setup/action.yml",
    "content": "name: Setup\ndescription: Setup Node.js and install dependencies\n\nruns:\n  using: composite\n  steps:\n    - uses: pnpm/action-setup@v4\n\n    - name: Setup Node.js\n      uses: actions/setup-node@v4\n      with:\n        node-version-file: .nvmrc\n        cache: pnpm\n        cache-dependency-path: pnpm-lock.yaml\n\n    - name: Enable Corepack\n      run: corepack enable\n      shell: bash\n\n    - name: Install dependencies\n      run: pnpm install --frozen-lockfile\n      shell: bash\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\non:\n  workflow_dispatch:\n    inputs:\n      release_type:\n        description: 'Release type (patch, minor, major, or a semver version)'\n        required: false\n        type: string\n      npm_tag:\n        description: 'Optional npm dist-tag override (e.g. latest, v0, v1)'\n        required: false\n        type: string\n      github_make_latest:\n        description: 'Optional GitHub latest release override (true or false)'\n        required: false\n        type: string\n\npermissions:\n  contents: write\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Setup\n        uses: ./.github/actions/setup\n\n      - name: Lint files\n        run: pnpm lint\n\n      - name: Test formatting\n        run: pnpm format --check\n\n      - name: Typecheck files\n        run: pnpm typecheck\n\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Setup\n        uses: ./.github/actions/setup\n\n      - name: Run tests\n        run: pnpm test\n\n  build-release:\n    runs-on: ubuntu-latest\n    needs: [lint, test]\n    permissions:\n      contents: write\n      id-token: write\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Setup\n        uses: ./.github/actions/setup\n\n      - name: Build package\n        run: pnpm package build\n\n      - name: Configure Git\n        run: |\n          git config user.name \"github-actions[bot]\"\n          git config user.email \"github-actions[bot]@users.noreply.github.com\"\n\n      - name: Validate release type\n        if: ${{ inputs.release_type != '' }}\n        run: |\n          if [[ \"${{ inputs.release_type }}\" =~ ^(patch|minor|major)$ ]]; then\n            echo \"Valid release type: ${{ inputs.release_type }}\"\n          elif [[ \"${{ inputs.release_type }}\" =~ ^[0-9]+\\.[0-9]+\\.[0-9]+$ ]]; then\n            echo \"Valid semver version: ${{ inputs.release_type }}\"\n          else\n            echo \"Invalid input. Must be 'patch', 'minor', 'major', or a valid semver version (e.g., 1.2.3).\"\n            exit 1\n          fi\n\n      - name: Resolve release strategy\n        run: |\n          branch=\"${{ github.ref_name }}\"\n          release_type=\"${{ inputs.release_type }}\"\n          npm_tag_input=\"${{ inputs.npm_tag }}\"\n          github_make_latest_input=\"${{ inputs.github_make_latest }}\"\n          package_version=$(node -p \"require('./packages/react-native-boost/package.json').version\")\n          package_major=\"${package_version%%.*}\"\n\n          case \"$github_make_latest_input\" in\n            \"\"|true|false) ;;\n            *)\n              echo \"Invalid github_make_latest input. Must be 'true' or 'false' when provided.\"\n              exit 1\n              ;;\n          esac\n\n          if [[ \"$branch\" =~ ^v([0-9]+)$ ]]; then\n            branch_major=\"${BASH_REMATCH[1]}\"\n            release_npm_tag=\"${npm_tag_input:-v${branch_major}}\"\n            release_github_make_latest=\"${github_make_latest_input:-false}\"\n\n            if [[ \"$package_major\" != \"$branch_major\" ]]; then\n              echo \"Branch $branch expects package major $branch_major, but package.json is $package_version.\"\n              exit 1\n            fi\n\n            if [[ -n \"$release_type\" ]]; then\n              if [[ \"$release_type\" =~ ^[0-9]+\\.[0-9]+\\.[0-9]+$ ]]; then\n                release_major=\"${release_type%%.*}\"\n                if [[ \"$release_major\" != \"$branch_major\" ]]; then\n                  echo \"Explicit version $release_type does not match branch major $branch_major.\"\n                  exit 1\n                fi\n              elif [[ \"$release_type\" == \"major\" ]]; then\n                echo \"Major increments are not allowed on maintenance branch $branch.\"\n                exit 1\n              fi\n            fi\n          elif [[ \"$branch\" == \"main\" || \"$branch\" == \"master\" ]]; then\n            release_npm_tag=\"${npm_tag_input:-latest}\"\n            release_github_make_latest=\"${github_make_latest_input:-true}\"\n          else\n            if [[ -z \"$npm_tag_input\" || -z \"$github_make_latest_input\" ]]; then\n              echo \"Releases from branch '$branch' require explicit npm_tag and github_make_latest inputs.\"\n              exit 1\n            fi\n\n            release_npm_tag=\"$npm_tag_input\"\n            release_github_make_latest=\"$github_make_latest_input\"\n          fi\n\n          if [[ ! \"$release_npm_tag\" =~ ^[A-Za-z0-9._-]+$ ]]; then\n            echo \"Invalid npm tag '$release_npm_tag'. Use only letters, numbers, dots, underscores, and dashes.\"\n            exit 1\n          fi\n\n          echo \"RELEASE_NPM_TAG=$release_npm_tag\" >> \"$GITHUB_ENV\"\n          echo \"RELEASE_GITHUB_MAKE_LATEST=$release_github_make_latest\" >> \"$GITHUB_ENV\"\n          echo \"Release branch: $branch\"\n          echo \"npm tag: $release_npm_tag\"\n          echo \"GitHub make_latest: $release_github_make_latest\"\n\n      - name: Release\n        run: |\n          if [ -n \"${{ inputs.release_type }}\" ]; then\n            pnpm package release --increment \"${{ inputs.release_type }}\" --npm.tag \"$RELEASE_NPM_TAG\" --github.makeLatest \"$RELEASE_GITHUB_MAKE_LATEST\"\n          else\n            pnpm package release --npm.tag \"$RELEASE_NPM_TAG\" --github.makeLatest \"$RELEASE_GITHUB_MAKE_LATEST\"\n          fi\n        env:\n          GITHUB_TOKEN: ${{ github.token }}\n"
  },
  {
    "path": ".github/workflows/stale.yml",
    "content": "name: 'Close stale issues and PRs'\non:\n  schedule:\n    - cron: '30 1 * * *'\n\njobs:\n  stale:\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n      pull-requests: write\n    steps:\n      - uses: actions/stale@v9\n        with:\n          stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.'\n          stale-pr-message: 'This PR is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.'\n          days-before-stale: 60\n          days-before-close: 7\n          stale-issue-label: stale\n          stale-pr-label: stale\n          exempt-issue-labels: 'help wanted,in progress,pinned'\n          exempt-pr-labels: 'in progress,pinned'\n  stale-missing-info:\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n      pull-requests: write\n    steps:\n      - uses: actions/stale@v9\n        with:\n          any-of-labels: 'repro-missing'\n          stale-issue-message: 'This issue is stale because it is missing information. Please add the requested information or this will be closed in 7 days.'\n          stale-pr-message: 'This PR is stale because it is missing information. Please add the requested information or this will be closed in 7 days.'\n          days-before-stale: 14\n          days-before-close: 7\n          stale-issue-label: stale\n          stale-pr-label: stale\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test & build\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Setup\n        uses: ./.github/actions/setup\n\n      - name: Lint files\n        run: pnpm lint\n\n      - name: Test formatting\n        run: pnpm format --check\n\n      - name: Verify formatted code is unchanged\n        run: git diff --exit-code HEAD\n\n      - name: Typecheck files\n        run: pnpm typecheck\n\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Setup\n        uses: ./.github/actions/setup\n\n      - name: Run tests\n        run: pnpm test\n\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Setup\n        uses: ./.github/actions/setup\n\n      - name: Build plugin\n        run: pnpm package build\n"
  },
  {
    "path": ".gitignore",
    "content": "*.tgz\npackage-lock.json\nyarn.lock\n\n# OSX\n\n.DS_Store\n\n# VSCode\n.vscode/\njsconfig.json\n\n# node.js\n#\nnode_modules/\nnpm-debug.log\nyarn-debug.log\nyarn-error.log\n\n# pnpm\n.pnpm-store/\n\n# Turborepo\n.turbo/\n"
  },
  {
    "path": ".husky/commit-msg",
    "content": "npx --no-install commitlint --edit $1\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "npx --no-install lint-staged\n"
  },
  {
    "path": ".lintstagedrc",
    "content": "{\n  \"*.{js,mjs,cjs,jsx,ts,tsx}\": [\"pnpm exec oxlint --fix\", \"pnpm exec oxfmt --write\"],\n  \"*.json\": \"pnpm exec oxfmt --write\"\n}\n"
  },
  {
    "path": ".npmrc",
    "content": "node-linker=hoisted\n"
  },
  {
    "path": ".nvmrc",
    "content": "v24\n"
  },
  {
    "path": ".oxfmtrc.json",
    "content": "{\n  \"$schema\": \"./node_modules/oxfmt/configuration_schema.json\",\n  \"printWidth\": 120,\n  \"tabWidth\": 2,\n  \"useTabs\": false,\n  \"semi\": true,\n  \"singleQuote\": true,\n  \"quoteProps\": \"consistent\",\n  \"jsxSingleQuote\": false,\n  \"trailingComma\": \"es5\",\n  \"bracketSpacing\": true,\n  \"bracketSameLine\": true,\n  \"arrowParens\": \"always\",\n  \"endOfLine\": \"lf\",\n  \"sortPackageJson\": false,\n  \"ignorePatterns\": [\"CHANGELOG.md\", \"apps/docs/**/*.md\", \"apps/docs/**/*.mdx\", \"apps/docs/.docusaurus/**\"]\n}\n"
  },
  {
    "path": ".oxlintrc.json",
    "content": "{\n  \"$schema\": \"./node_modules/oxlint/configuration_schema.json\",\n  \"plugins\": [\"eslint\", \"typescript\", \"unicorn\"],\n  \"categories\": {\n    \"correctness\": \"error\"\n  },\n  \"env\": {\n    \"node\": true\n  },\n  \"ignorePatterns\": [\n    \"**/fixtures\",\n    \"**/*.config.{js,mjs,cjs}\",\n    \"**/scripts\",\n    \"**/__tests__\",\n    \"apps/docs/.docusaurus/**\"\n  ],\n  \"rules\": {\n    \"no-unused-vars\": [\n      \"error\",\n      {\n        \"argsIgnorePattern\": \"^_\",\n        \"varsIgnorePattern\": \"^_\",\n        \"caughtErrorsIgnorePattern\": \"^_\"\n      }\n    ],\n    \"@typescript-eslint/ban-ts-comment\": \"error\",\n    \"no-array-constructor\": \"error\",\n    \"@typescript-eslint/no-empty-object-type\": \"error\",\n    \"@typescript-eslint/no-explicit-any\": \"error\",\n    \"@typescript-eslint/no-namespace\": \"error\",\n    \"@typescript-eslint/no-require-imports\": \"error\",\n    \"@typescript-eslint/no-unnecessary-type-constraint\": \"error\",\n    \"@typescript-eslint/no-unsafe-function-type\": \"error\",\n    \"unicorn/catch-error-name\": \"error\",\n    \"unicorn/consistent-assert\": \"error\",\n    \"unicorn/consistent-date-clone\": \"error\",\n    \"unicorn/consistent-empty-array-spread\": \"error\",\n    \"unicorn/consistent-existence-index-check\": \"error\",\n    \"unicorn/consistent-function-scoping\": \"error\",\n    \"unicorn/empty-brace-spaces\": \"error\",\n    \"unicorn/error-message\": \"error\",\n    \"unicorn/escape-case\": \"error\",\n    \"unicorn/explicit-length-check\": \"error\",\n    \"unicorn/filename-case\": \"error\",\n    \"unicorn/new-for-builtins\": \"error\",\n    \"unicorn/no-accessor-recursion\": \"error\",\n    \"unicorn/no-anonymous-default-export\": \"error\",\n    \"unicorn/no-array-callback-reference\": \"error\",\n    \"unicorn/no-array-for-each\": \"error\",\n    \"unicorn/no-array-method-this-argument\": \"error\",\n    \"unicorn/no-array-reduce\": \"error\",\n    \"unicorn/no-await-expression-member\": \"error\",\n    \"unicorn/no-console-spaces\": \"error\",\n    \"unicorn/no-document-cookie\": \"error\",\n    \"unicorn/no-hex-escape\": \"error\",\n    \"unicorn/no-instanceof-builtins\": \"error\",\n    \"unicorn/no-length-as-slice-end\": \"error\",\n    \"unicorn/no-lonely-if\": \"error\",\n    \"unicorn/no-magic-array-flat-depth\": \"error\",\n    \"unicorn/no-negated-condition\": \"error\",\n    \"unicorn/no-negation-in-equality-check\": \"error\",\n    \"unicorn/no-new-buffer\": \"error\",\n    \"unicorn/no-object-as-default-parameter\": \"error\",\n    \"unicorn/no-process-exit\": \"error\",\n    \"unicorn/no-static-only-class\": \"error\",\n    \"unicorn/no-this-assignment\": \"error\",\n    \"unicorn/no-typeof-undefined\": \"error\",\n    \"unicorn/no-unreadable-array-destructuring\": \"error\",\n    \"unicorn/no-unreadable-iife\": \"error\",\n    \"unicorn/no-useless-promise-resolve-reject\": \"error\",\n    \"unicorn/no-useless-switch-case\": \"error\",\n    \"unicorn/no-useless-undefined\": \"error\",\n    \"unicorn/no-null\": \"off\",\n    \"unicorn/no-nested-ternary\": \"off\",\n    \"unicorn/no-abusive-eslint-disable\": \"off\",\n    \"unicorn/no-zero-fractions\": \"error\",\n    \"unicorn/number-literal-case\": \"error\",\n    \"unicorn/numeric-separators-style\": \"error\",\n    \"unicorn/prefer-add-event-listener\": \"error\",\n    \"unicorn/prefer-array-find\": \"error\",\n    \"unicorn/prefer-array-flat-map\": \"error\",\n    \"unicorn/prefer-array-flat\": \"error\",\n    \"unicorn/prefer-array-index-of\": \"error\",\n    \"unicorn/prefer-array-some\": \"error\",\n    \"unicorn/prefer-at\": \"error\",\n    \"unicorn/prefer-blob-reading-methods\": \"error\",\n    \"unicorn/prefer-code-point\": \"error\",\n    \"unicorn/prefer-date-now\": \"error\",\n    \"unicorn/prefer-default-parameters\": \"error\",\n    \"unicorn/prefer-dom-node-append\": \"error\",\n    \"unicorn/prefer-dom-node-dataset\": \"error\",\n    \"unicorn/prefer-dom-node-remove\": \"error\",\n    \"unicorn/prefer-dom-node-text-content\": \"error\",\n    \"unicorn/prefer-event-target\": \"error\",\n    \"unicorn/prefer-global-this\": \"error\",\n    \"unicorn/prefer-includes\": \"error\",\n    \"unicorn/prefer-keyboard-event-key\": \"error\",\n    \"unicorn/prefer-logical-operator-over-ternary\": \"error\",\n    \"unicorn/prefer-math-min-max\": \"error\",\n    \"unicorn/prefer-math-trunc\": \"error\",\n    \"unicorn/prefer-modern-dom-apis\": \"error\",\n    \"unicorn/prefer-modern-math-apis\": \"error\",\n    \"unicorn/prefer-module\": \"error\",\n    \"unicorn/prefer-native-coercion-functions\": \"error\",\n    \"unicorn/prefer-negative-index\": \"error\",\n    \"unicorn/prefer-node-protocol\": \"error\",\n    \"unicorn/prefer-number-properties\": \"error\",\n    \"unicorn/prefer-object-from-entries\": \"error\",\n    \"unicorn/prefer-optional-catch-binding\": \"error\",\n    \"unicorn/prefer-prototype-methods\": \"error\",\n    \"unicorn/prefer-query-selector\": \"error\",\n    \"unicorn/prefer-reflect-apply\": \"error\",\n    \"unicorn/prefer-regexp-test\": \"error\",\n    \"unicorn/prefer-set-has\": \"error\",\n    \"unicorn/prefer-spread\": \"error\",\n    \"unicorn/prefer-string-raw\": \"error\",\n    \"unicorn/prefer-string-replace-all\": \"error\",\n    \"unicorn/prefer-string-slice\": \"error\",\n    \"unicorn/prefer-string-trim-start-end\": \"error\",\n    \"unicorn/prefer-structured-clone\": \"error\",\n    \"unicorn/prefer-ternary\": \"error\",\n    \"unicorn/prefer-top-level-await\": \"off\",\n    \"unicorn/prefer-type-error\": \"error\",\n    \"unicorn/relative-url-style\": \"error\",\n    \"unicorn/require-array-join-separator\": \"error\",\n    \"unicorn/require-number-to-fixed-digits-argument\": \"error\",\n    \"unicorn/switch-case-braces\": \"error\",\n    \"unicorn/text-encoding-identifier-case\": \"error\",\n    \"unicorn/throw-new-error\": \"error\"\n  },\n  \"overrides\": [\n    {\n      \"files\": [\"packages/react-native-time-to-render/src/*.ts\"],\n      \"rules\": {\n        \"unicorn/filename-case\": \"off\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": ".zed/settings.json",
    "content": "{\n  \"lsp\": {\n    \"oxlint\": {\n      \"initialization_options\": {\n        \"settings\": {\n          \"configPath\": null,\n          \"run\": \"onType\",\n          \"disableNestedConfig\": false,\n          \"fixKind\": \"safe_fix\",\n          \"typeAware\": true,\n          \"unusedDisableDirectives\": \"deny\"\n        }\n      }\n    },\n    \"oxfmt\": {\n      \"initialization_options\": {\n        \"settings\": {\n          \"fmt.configPath\": null,\n          \"run\": \"onSave\"\n        }\n      }\n    }\n  },\n  \"languages\": {\n    \"CSS\": {\n      \"format_on_save\": \"on\",\n      \"prettier\": {\n        \"allowed\": false\n      },\n      \"formatter\": [\n        {\n          \"language_server\": {\n            \"name\": \"oxfmt\"\n          }\n        }\n      ]\n    },\n    \"GraphQL\": {\n      \"format_on_save\": \"on\",\n      \"prettier\": {\n        \"allowed\": false\n      },\n      \"formatter\": [\n        {\n          \"language_server\": {\n            \"name\": \"oxfmt\"\n          }\n        }\n      ]\n    },\n    \"Handlebars\": {\n      \"format_on_save\": \"on\",\n      \"prettier\": {\n        \"allowed\": false\n      },\n      \"formatter\": [\n        {\n          \"language_server\": {\n            \"name\": \"oxfmt\"\n          }\n        }\n      ]\n    },\n    \"HTML\": {\n      \"format_on_save\": \"on\",\n      \"prettier\": {\n        \"allowed\": false\n      },\n      \"formatter\": [\n        {\n          \"language_server\": {\n            \"name\": \"oxfmt\"\n          }\n        }\n      ]\n    },\n    \"JavaScript\": {\n      \"format_on_save\": \"on\",\n      \"prettier\": {\n        \"allowed\": false\n      },\n      \"formatter\": [\n        {\n          \"language_server\": {\n            \"name\": \"oxfmt\"\n          }\n        },\n        {\n          \"code_action\": \"source.fixAll.oxc\"\n        }\n      ]\n    },\n    \"JSON\": {\n      \"format_on_save\": \"on\",\n      \"prettier\": {\n        \"allowed\": false\n      },\n      \"formatter\": [\n        {\n          \"language_server\": {\n            \"name\": \"oxfmt\"\n          }\n        }\n      ]\n    },\n    \"JSON5\": {\n      \"format_on_save\": \"on\",\n      \"prettier\": {\n        \"allowed\": false\n      },\n      \"formatter\": [\n        {\n          \"language_server\": {\n            \"name\": \"oxfmt\"\n          }\n        }\n      ]\n    },\n    \"JSONC\": {\n      \"format_on_save\": \"on\",\n      \"prettier\": {\n        \"allowed\": false\n      },\n      \"formatter\": [\n        {\n          \"language_server\": {\n            \"name\": \"oxfmt\"\n          }\n        }\n      ]\n    },\n    \"Less\": {\n      \"format_on_save\": \"on\",\n      \"prettier\": {\n        \"allowed\": false\n      },\n      \"formatter\": [\n        {\n          \"language_server\": {\n            \"name\": \"oxfmt\"\n          }\n        }\n      ]\n    },\n    \"Markdown\": {\n      \"format_on_save\": \"on\",\n      \"prettier\": {\n        \"allowed\": false\n      },\n      \"formatter\": [\n        {\n          \"language_server\": {\n            \"name\": \"oxfmt\"\n          }\n        }\n      ]\n    },\n    \"MDX\": {\n      \"format_on_save\": \"on\",\n      \"prettier\": {\n        \"allowed\": false\n      },\n      \"formatter\": [\n        {\n          \"language_server\": {\n            \"name\": \"oxfmt\"\n          }\n        }\n      ]\n    },\n    \"SCSS\": {\n      \"format_on_save\": \"on\",\n      \"prettier\": {\n        \"allowed\": false\n      },\n      \"formatter\": [\n        {\n          \"language_server\": {\n            \"name\": \"oxfmt\"\n          }\n        }\n      ]\n    },\n    \"TypeScript\": {\n      \"format_on_save\": \"on\",\n      \"prettier\": {\n        \"allowed\": false\n      },\n      \"formatter\": [\n        {\n          \"language_server\": {\n            \"name\": \"oxfmt\"\n          }\n        }\n      ]\n    },\n    \"TSX\": {\n      \"format_on_save\": \"on\",\n      \"prettier\": {\n        \"allowed\": false\n      },\n      \"formatter\": [\n        {\n          \"language_server\": {\n            \"name\": \"oxfmt\"\n          }\n        }\n      ]\n    },\n    \"Vue.js\": {\n      \"format_on_save\": \"on\",\n      \"prettier\": {\n        \"allowed\": false\n      },\n      \"formatter\": [\n        {\n          \"language_server\": {\n            \"name\": \"oxfmt\"\n          }\n        }\n      ]\n    },\n    \"YAML\": {\n      \"format_on_save\": \"on\",\n      \"prettier\": {\n        \"allowed\": false\n      },\n      \"formatter\": [\n        {\n          \"language_server\": {\n            \"name\": \"oxfmt\"\n          }\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n- Demonstrating empathy and kindness toward other people\n- Being respectful of differing opinions, viewpoints, and experiences\n- Giving and gracefully accepting constructive feedback\n- Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n- Focusing on what is best not just for us as individuals, but for the overall\n  community\n\nExamples of unacceptable behavior include:\n\n- The use of sexualized language or imagery, and sexual attention or advances of\n  any kind\n- Trolling, insulting or derogatory comments, and personal or political attacks\n- Public or private harassment\n- Publishing others' private information, such as a physical or email address,\n  without their explicit permission\n- Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n[INSERT CONTACT METHOD].\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series of\nactions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within the\ncommunity.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.1, available at\n[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].\n\nCommunity Impact Guidelines were inspired by\n[Mozilla's code of conduct enforcement ladder][Mozilla CoC].\n\nFor answers to common questions about this code of conduct, see the FAQ at\n[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at\n[https://www.contributor-covenant.org/translations][translations].\n\n[homepage]: https://www.contributor-covenant.org\n[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html\n[Mozilla CoC]: https://github.com/mozilla/diversity\n[FAQ]: https://www.contributor-covenant.org/faq\n[translations]: https://www.contributor-covenant.org/translations\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nContributions are always welcome, no matter how large or small!\n\nWe want this community to be friendly and respectful to each other. Please follow it in all your interactions with the project. Before contributing, please read the [code of conduct](./CODE_OF_CONDUCT.md).\n\n## Development workflow\n\nTo get started with the project, run `pnpm install` in the root directory to install the required dependencies for each package:\n\n```sh\npnpm install\n```\n\n> While it's possible to use [`npm`](https://github.com/npm/cli), the tooling is built around [`pnpm`](https://pnpm.io/), so you'll have an easier time if you use `pnpm` for development.\n\nWhile developing, you can run the [example app](/example/) to test your changes.\n\nTo have package changes automatically reflected in the example app, run the package build watcher and Expo together:\n\n```sh\npnpm dev\n```\n\nThis runs `rollup -w` for `packages/react-native-boost` and `expo start` for `apps/example` in parallel.\n\nIf you change native code, you'll still need to rebuild the example app.\n\nTo start only the example app packager:\n\n```sh\npnpm example start\n```\n\nTo run the example app on Android:\n\n```sh\npnpm example android\n```\n\nTo run the example app on iOS:\n\n```sh\npnpm example ios\n```\n\nMake sure your code passes TypeScript and Oxlint. Run the following to verify:\n\n```sh\npnpm typecheck\npnpm lint\n```\n\nTo fix formatting errors, run the following:\n\n```sh\npnpm lint -- --fix\n```\n\nRemember to add tests for your change if possible. Run the unit tests by:\n\n```sh\npnpm test\n```\n\n### Commit message convention\n\nWe follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages:\n\n- `fix`: bug fixes, e.g. fix crash due to deprecated method.\n- `feat`: new features, e.g. add new method to the module.\n- `refactor`: code refactor, e.g. migrate from class components to hooks.\n- `docs`: changes into documentation, e.g. add usage example for the module..\n- `test`: adding or updating tests, e.g. add integration tests using detox.\n- `chore`: tooling changes, e.g. change CI config.\n\nOur pre-commit hooks verify that your commit message matches this format when committing.\n\n### Linting and tests\n\n[Oxlint](https://oxc.rs/docs/guide/usage/linter), [Oxfmt](https://oxc.rs/docs/guide/usage/formatter), [TypeScript](https://www.typescriptlang.org/)\n\nWe use [TypeScript](https://www.typescriptlang.org/) for type checking, and [Oxlint](https://oxc.rs/docs/guide/usage/linter) with [Oxfmt](https://oxc.rs/docs/guide/usage/formatter) for linting and formatting the code.\n\nOur pre-commit hooks verify that the linter and tests pass when committing.\n\n### Publishing to npm\n\nWe use [release-it](https://github.com/release-it/release-it) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc.\n\nTo publish new versions, run the following:\n\n```sh\npnpm package release\n```\n\n### Scripts\n\nThe `package.json` file contains various scripts for common tasks:\n\n- `pnpm install`: install all workspace dependencies.\n- `pnpm typecheck`: type-check files with TypeScript.\n- `pnpm lint`: lint files with Oxlint.\n- `pnpm example start`: start the Metro server for the example app.\n- `pnpm dev`: start the example app and watch/rebuild `react-native-boost` package changes.\n- `pnpm example android`: run the example app on Android.\n- `pnpm example ios`: run the example app on iOS.\n\n### Sending a pull request\n\n> **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github).\n\nWhen you're sending a pull request:\n\n- Prefer small pull requests focused on one change.\n- Verify that linters and tests are passing.\n- Review the documentation to make sure it looks good.\n- Follow the pull request template when opening a pull request.\n- For pull requests that change the API or implementation, discuss with maintainers first by opening an issue.\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) Kuatsu App Agency\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# 🚀 react-native-boost\n\n![npm bundle size](https://img.shields.io/bundlephobia/min/react-native-boost?style=flat-square) ![GitHub](https://img.shields.io/github/license/kuatsu/react-native-boost?style=flat-square) ![GitHub last commit](https://img.shields.io/github/last-commit/kuatsu/react-native-boost?style=flat-square)\n\nA powerful Babel plugin that automatically optimizes React Native apps through static source code analysis. It replaces standard React Native components with their native counterparts where possible, leading to significant performance improvements.\n\n- ⚡ Automatic performance optimization through source code analysis\n- 🔒 Safe optimizations that don't break your app\n- 🎯 Virtually zero runtime overhead\n- 📱 Cross-platform compatible\n- 🧪 Works seamlessly with Expo\n- 🎨 Configurable optimization strategies\n\n## Documentation\n\nThe documentation is available at [react-native-boost.oss.kuatsu.de](https://react-native-boost.oss.kuatsu.de).\n\n## Benchmark\n\nThe app in the `apps/example` directory serves as a benchmark for the performance of the plugin.\n\n<div align=\"center\">\n  <img src=\"./apps/docs/content/docs/information/img/benchmark-ios.png\" width=\"500\" />\n</div>\n\nMore benchmarks are available in the [docs](https://react-native-boost.oss.kuatsu.de/docs/information/benchmarks).\n\n## Compatibility\n\n| `react-native-boost` | React Native     |\n| -------------------- | ---------------- |\n| `0.x`                | All versions[^1] |\n| `1.x`                | `>=0.83`         |\n\n[^1]: Starting from React Native `0.80`, `react-native-boost@0` prints import deprecation warnings.\n\n## Installation\n\nInstall the package using your favorite package manager. Please **do not** install the package as a dev dependency. While the Babel plugin itself would work as a dev dependency, it relies on importing the runtime library (`react-native-boost/runtime`) into your code, which requires the package to be installed as a regular dependency. Read more [here](https://react-native-boost.oss.kuatsu.de/docs/runtime-library/).\n\n```sh\nnpm install react-native-boost\n# or\nyarn add react-native-boost\n```\n\nThen, add the plugin to your Babel configuration (`babel.config.js`):\n\n```js\nmodule.exports = {\n  plugins: ['react-native-boost/plugin'],\n};\n```\n\nIf you're using Expo and don't see the `babel.config.js` file, run the following command to create it:\n\n```sh\nnpx expo customize babel.config.js\n```\n\nFinally, restart your React Native development server and clear the bundler cache:\n\n```sh\nnpm start --clear\n# or\nyarn start --clear\n```\n\nThat's it! No imports in your code, rebuilding, or anything else is required.\n\nOptionally, you can configure the Babel plugin with a few options described in the [documentation](https://react-native-boost.oss.kuatsu.de/docs/configuration/configure).\n\n## How it works\n\nA technical rundown of how the plugin works can be found in the [docs](https://react-native-boost.oss.kuatsu.de/docs/information/how-it-works).\n\n## Contributing\n\nSee the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.\n\n## License\n\nMIT\n"
  },
  {
    "path": "apps/docs/.gitignore",
    "content": "# deps\n/node_modules\n\n# generated content\n.source\n\n# test & build\n/coverage\n/.next/\n/out/\n/build\n*.tsbuildinfo\n\n# misc\n.DS_Store\n*.pem\n/.pnp\n.pnp.js\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# others\n.env*.local\n.vercel\nnext-env.d.ts"
  },
  {
    "path": "apps/docs/README.md",
    "content": "# React Native Boost Docs\n\nThis app hosts the Fumadocs site for `react-native-boost`.\n\n## Development\n\n```bash\npnpm --filter react-native-boost-docs dev\n```\n\n## Build\n\n```bash\npnpm --filter react-native-boost-docs build\n```\n\n## Content Layout\n\n- `content/docs`: documentation pages and `meta.json` navigation files\n- `app/docs`: docs layout and page rendering routes\n- `lib/source.ts`: Fumadocs source loader configuration\n"
  },
  {
    "path": "apps/docs/app/(home)/layout.tsx",
    "content": "import { HomeLayout } from 'fumadocs-ui/layouts/home';\nimport { baseOptions } from '@/lib/layout.shared';\n\nexport default function Layout({ children }: LayoutProps<'/'>) {\n  return <HomeLayout {...baseOptions()}>{children}</HomeLayout>;\n}\n"
  },
  {
    "path": "apps/docs/app/(home)/page.tsx",
    "content": "import Link from 'next/link';\nimport { Rocket, ShieldCheck, Zap } from 'lucide-react';\n\nexport default function HomePage() {\n  return (\n    <div className=\"mx-auto flex w-full max-w-5xl flex-col justify-start gap-5 px-4 py-6 md:min-h-[90vh] md:justify-center md:py-10\">\n      <section className=\"rounded-2xl border border-fd-border bg-linear-to-br from-fd-primary/15 via-fd-background to-fd-background p-6 md:p-8\">\n        <p className=\"mb-3 inline-flex items-center gap-2 rounded-full bg-fd-primary/12 px-3 py-1 text-sm font-medium text-fd-primary\">\n          <Rocket className=\"size-4\" />\n          React Native Boost\n        </p>\n        <h1 className=\"text-3xl font-semibold tracking-tight md:text-4xl\">\n          Improve your app's performance with one line of code.\n        </h1>\n        <p className=\"mt-3 max-w-3xl text-fd-muted-foreground md:text-base\">\n          A Babel plugin that replaces analyzes your code and performs safe optimizations to reduce unnecessary runtime\n          overhead in React Native apps.\n        </p>\n\n        <div className=\"mt-5 flex items-center\">\n          <Link\n            href=\"/docs\"\n            className=\"inline-flex items-center rounded-xl bg-fd-primary px-5 py-2.5 text-base font-medium text-fd-primary-foreground transition-colors hover:opacity-90\">\n            Read docs\n          </Link>\n        </div>\n      </section>\n\n      <section className=\"grid gap-3 md:grid-cols-3\">\n        <article className=\"rounded-xl border border-fd-border bg-fd-card p-4\">\n          <p className=\"mb-2 inline-flex items-center gap-2 text-sm font-medium\">\n            <Zap className=\"size-4 text-fd-primary\" />\n            Faster renders\n          </p>\n          <p className=\"text-sm text-fd-muted-foreground\">\n            Removes runtime overhead from wrapper components to improve UI-heavy screens.\n          </p>\n        </article>\n        <article className=\"rounded-xl border border-fd-border bg-fd-card p-4\">\n          <p className=\"mb-2 inline-flex items-center gap-2 text-sm font-medium\">\n            <ShieldCheck className=\"size-4 text-fd-primary\" />\n            Safety first\n          </p>\n          <p className=\"text-sm text-fd-muted-foreground\">\n            Conservative analysis skips uncertain optimizations to reduce behavioral risk.\n          </p>\n        </article>\n        <article className=\"rounded-xl border border-fd-border bg-fd-card p-4\">\n          <p className=\"mb-2 inline-flex items-center gap-2 text-sm font-medium\">\n            <Rocket className=\"size-4 text-fd-primary\" />\n            Minimal setup\n          </p>\n          <p className=\"text-sm text-fd-muted-foreground\">\n            Install, add the Babel plugin, get instant improvements. No code changes required.\n          </p>\n        </article>\n      </section>\n    </div>\n  );\n}\n"
  },
  {
    "path": "apps/docs/app/api/search/route.ts",
    "content": "import { source } from '@/lib/source';\nimport { createFromSource } from 'fumadocs-core/search/server';\n\nexport const { GET } = createFromSource(source, {\n  // https://docs.orama.com/docs/orama-js/supported-languages\n  language: 'english',\n});\n"
  },
  {
    "path": "apps/docs/app/docs/[[...slug]]/page.tsx",
    "content": "import { getPageImage, source } from '@/lib/source';\nimport { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';\nimport { notFound } from 'next/navigation';\nimport { getMDXComponents } from '@/mdx-components';\nimport type { Metadata } from 'next';\nimport { createRelativeLink } from 'fumadocs-ui/mdx';\nimport type { TOCItemType } from 'fumadocs-core/toc';\nimport { LLMCopyButton, ViewOptions } from '@/components/ai/page-actions';\nimport { gitConfig } from '@/lib/layout.shared';\nimport { getAutoOptionSectionsToc } from '@/components/docs/auto-option-sections';\nimport { getRuntimeReferenceToc } from '@/components/docs/auto-runtime-reference';\n\nconst runtimeReferencePath = '../../packages/react-native-boost/src/runtime/index.ts';\nconst pluginTypesPath = '../../packages/react-native-boost/src/plugin/types/index.ts';\n\ntype TocInsertion = {\n  afterUrl: string;\n  items: TOCItemType[];\n};\n\nasync function getTocInsertions(slug: string): Promise<TocInsertion[]> {\n  if (slug === 'runtime-library') {\n    return [\n      {\n        afterUrl: '#api-reference',\n        items: getRuntimeReferenceToc(runtimeReferencePath),\n      },\n    ];\n  }\n\n  if (slug === 'configuration/configure') {\n    const [pluginOptionsToc, pluginOptimizationOptionsToc] = await Promise.all([\n      getAutoOptionSectionsToc({\n        path: pluginTypesPath,\n        name: 'PluginOptions',\n        idPrefix: 'plugin-options',\n        depth: 3,\n      }),\n      getAutoOptionSectionsToc({\n        path: pluginTypesPath,\n        name: 'PluginOptimizationOptions',\n        idPrefix: 'plugin-optimization-options',\n        depth: 3,\n      }),\n    ]);\n\n    return [\n      {\n        afterUrl: '#plugin-options',\n        items: pluginOptionsToc,\n      },\n      {\n        afterUrl: '#plugin-optimization-options',\n        items: pluginOptimizationOptionsToc,\n      },\n    ];\n  }\n\n  return [];\n}\n\nfunction mergeToc(baseToc: TOCItemType[], insertions: TocInsertion[]): TOCItemType[] {\n  if (insertions.length === 0) {\n    return baseToc;\n  }\n\n  const remainingInsertions = [...insertions];\n  const mergedToc: TOCItemType[] = [];\n\n  for (const item of baseToc) {\n    mergedToc.push(item);\n\n    for (let index = 0; index < remainingInsertions.length; index += 1) {\n      const insertion = remainingInsertions[index];\n      if (insertion.afterUrl !== item.url) {\n        continue;\n      }\n\n      mergedToc.push(...insertion.items);\n      remainingInsertions.splice(index, 1);\n      index -= 1;\n    }\n  }\n\n  return mergedToc;\n}\n\nexport default async function Page(props: PageProps<'/docs/[[...slug]]'>) {\n  const params = await props.params;\n  const page = source.getPage(params.slug);\n  if (!page) notFound();\n\n  const MDX = page.data.body;\n  const slug = page.slugs.join('/');\n  const tocInsertions = await getTocInsertions(slug);\n  const toc = mergeToc(page.data.toc ?? [], tocInsertions);\n\n  return (\n    <DocsPage toc={toc} full={page.data.full}>\n      <DocsTitle>{page.data.title}</DocsTitle>\n      <DocsDescription className=\"mb-0\">{page.data.description}</DocsDescription>\n      <div className=\"flex flex-row gap-2 items-center border-b pb-6\">\n        <LLMCopyButton markdownUrl={`${page.url}.mdx`} />\n        <ViewOptions\n          markdownUrl={`${page.url}.mdx`}\n          githubUrl={`https://github.com/${gitConfig.user}/${gitConfig.repo}/blob/${gitConfig.branch}/content/docs/${page.path}`}\n        />\n      </div>\n      <DocsBody>\n        <MDX\n          components={getMDXComponents({\n            // this allows you to link to other pages with relative file paths\n            a: createRelativeLink(source, page),\n          })}\n        />\n      </DocsBody>\n    </DocsPage>\n  );\n}\n\nexport async function generateStaticParams() {\n  return source.generateParams();\n}\n\nexport async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): Promise<Metadata> {\n  const params = await props.params;\n  const page = source.getPage(params.slug);\n  if (!page) notFound();\n\n  return {\n    title: page.data.title,\n    description: page.data.description,\n    openGraph: {\n      images: getPageImage(page).url,\n    },\n  };\n}\n"
  },
  {
    "path": "apps/docs/app/docs/layout.tsx",
    "content": "import { source } from '@/lib/source';\nimport { DocsLayout } from 'fumadocs-ui/layouts/docs';\nimport { baseOptions } from '@/lib/layout.shared';\n\nexport default function Layout({ children }: LayoutProps<'/docs'>) {\n  return (\n    <DocsLayout tree={source.getPageTree()} {...baseOptions()}>\n      {children}\n    </DocsLayout>\n  );\n}\n"
  },
  {
    "path": "apps/docs/app/global.css",
    "content": "@import 'tailwindcss';\n@import 'fumadocs-ui/css/neutral.css';\n@import 'fumadocs-ui/css/preset.css';\n\n:root {\n  --color-fd-primary: #ff9800;\n  --color-fd-primary-foreground: #2f1d00;\n  --color-fd-ring: #ff9800;\n}\n\n.dark {\n  --color-fd-primary: #ffb74d;\n  --color-fd-primary-foreground: #2f1d00;\n  --color-fd-ring: #ffb74d;\n}\n"
  },
  {
    "path": "apps/docs/app/layout.tsx",
    "content": "import { RootProvider } from 'fumadocs-ui/provider/next';\nimport './global.css';\nimport { Inter } from 'next/font/google';\nimport type { Metadata } from 'next';\n\nconst inter = Inter({\n  subsets: ['latin'],\n});\n\nexport const metadata: Metadata = {\n  metadataBase: new URL('https://react-native-boost.oss.kuatsu.de'),\n  title: {\n    default: 'React Native Boost',\n    template: '%s | React Native Boost',\n  },\n  description: 'A Babel plugin and runtime toolkit for React Native performance optimizations.',\n};\n\nexport default function Layout({ children }: LayoutProps<'/'>) {\n  return (\n    <html lang=\"en\" className={inter.className} suppressHydrationWarning>\n      <body className=\"flex flex-col min-h-screen\">\n        <RootProvider>{children}</RootProvider>\n      </body>\n    </html>\n  );\n}\n"
  },
  {
    "path": "apps/docs/app/llms-full.txt/route.ts",
    "content": "import { getLLMText, source } from '@/lib/source';\n\nexport const revalidate = false;\n\nexport async function GET() {\n  const scan = [];\n  for (const page of source.getPages()) {\n    scan.push(getLLMText(page));\n  }\n  const scanned = await Promise.all(scan);\n\n  return new Response(scanned.join('\\n\\n'));\n}\n"
  },
  {
    "path": "apps/docs/app/llms.mdx/docs/[[...slug]]/route.ts",
    "content": "import { getLLMText, source } from '@/lib/source';\nimport { notFound } from 'next/navigation';\n\nexport const revalidate = false;\n\nexport async function GET(_req: Request, { params }: RouteContext<'/llms.mdx/docs/[[...slug]]'>) {\n  const { slug } = await params;\n  const page = source.getPage(slug);\n  if (!page) notFound();\n\n  return new Response(await getLLMText(page), {\n    headers: {\n      'Content-Type': 'text/markdown',\n    },\n  });\n}\n\nexport function generateStaticParams() {\n  return source.generateParams();\n}\n"
  },
  {
    "path": "apps/docs/app/llms.txt/route.ts",
    "content": "import { source } from '@/lib/source';\n\nexport const revalidate = false;\n\nexport async function GET() {\n  const lines: string[] = [];\n  lines.push('# Documentation');\n  lines.push('');\n  for (const page of source.getPages()) {\n    lines.push(`- [${page.data.title}](${page.url}): ${page.data.description}`);\n  }\n  return new Response(lines.join('\\n'));\n}\n"
  },
  {
    "path": "apps/docs/app/og/docs/[...slug]/route.tsx",
    "content": "import { getPageImage, source } from '@/lib/source';\nimport { notFound } from 'next/navigation';\nimport { ImageResponse } from 'next/og';\nimport { generate as DefaultImage } from 'fumadocs-ui/og';\n\nexport const revalidate = false;\n\nexport async function GET(_req: Request, { params }: RouteContext<'/og/docs/[...slug]'>) {\n  const { slug } = await params;\n  const page = source.getPage(slug.slice(0, -1));\n  if (!page) notFound();\n\n  return new ImageResponse(<DefaultImage title={page.data.title} description={page.data.description} site=\"My App\" />, {\n    width: 1200,\n    height: 630,\n  });\n}\n\nexport function generateStaticParams() {\n  return source.getPages().map((page) => ({\n    lang: page.locale,\n    slug: getPageImage(page).segments,\n  }));\n}\n"
  },
  {
    "path": "apps/docs/components/ai/page-actions.tsx",
    "content": "'use client';\nimport { useMemo, useState } from 'react';\nimport { Check, ChevronDown, Copy, ExternalLinkIcon, TextIcon } from 'lucide-react';\nimport { cn } from '@/lib/cn';\nimport { useCopyButton } from 'fumadocs-ui/utils/use-copy-button';\nimport { buttonVariants } from 'fumadocs-ui/components/ui/button';\nimport { Popover, PopoverContent, PopoverTrigger } from 'fumadocs-ui/components/ui/popover';\n\nconst cache = new Map<string, string>();\n\nexport function LLMCopyButton({\n  /**\n   * A URL to fetch the raw Markdown/MDX content of page\n   */\n  markdownUrl,\n}: {\n  markdownUrl: string;\n}) {\n  const [isLoading, setLoading] = useState(false);\n  const [checked, onClick] = useCopyButton(async () => {\n    const cached = cache.get(markdownUrl);\n    if (cached) return navigator.clipboard.writeText(cached);\n\n    setLoading(true);\n\n    try {\n      await navigator.clipboard.write([\n        new ClipboardItem({\n          'text/plain': fetch(markdownUrl).then(async (res) => {\n            const content = await res.text();\n            cache.set(markdownUrl, content);\n\n            return content;\n          }),\n        }),\n      ]);\n    } finally {\n      setLoading(false);\n    }\n  });\n\n  return (\n    <button\n      disabled={isLoading}\n      className={cn(\n        buttonVariants({\n          color: 'secondary',\n          size: 'sm',\n          className: 'gap-2 [&_svg]:size-3.5 [&_svg]:text-fd-muted-foreground',\n        })\n      )}\n      onClick={onClick}>\n      {checked ? <Check /> : <Copy />}\n      Copy Markdown\n    </button>\n  );\n}\n\nexport function ViewOptions({\n  markdownUrl,\n  githubUrl,\n}: {\n  /**\n   * A URL to the raw Markdown/MDX content of page\n   */\n  markdownUrl: string;\n\n  /**\n   * Source file URL on GitHub\n   */\n  githubUrl: string;\n}) {\n  const items = useMemo(() => {\n    const pageUrl = (globalThis as { location?: { href: string } }).location?.href ?? 'loading';\n    const q = `Read ${pageUrl}, I want to ask questions about it.`;\n\n    return [\n      {\n        title: 'Open in GitHub',\n        href: githubUrl,\n        icon: (\n          <svg fill=\"currentColor\" role=\"img\" viewBox=\"0 0 24 24\">\n            <title>GitHub</title>\n            <path d=\"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\" />\n          </svg>\n        ),\n      },\n      {\n        title: 'View as Markdown',\n        href: markdownUrl,\n        icon: <TextIcon />,\n      },\n      {\n        title: 'Open in Scira AI',\n        href: `https://scira.ai/?${new URLSearchParams({\n          q,\n        })}`,\n        icon: (\n          <svg width=\"910\" height=\"934\" viewBox=\"0 0 910 934\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n            <title>Scira AI</title>\n            <path\n              d=\"M647.664 197.775C569.13 189.049 525.5 145.419 516.774 66.8849C508.048 145.419 464.418 189.049 385.884 197.775C464.418 206.501 508.048 250.131 516.774 328.665C525.5 250.131 569.13 206.501 647.664 197.775Z\"\n              fill=\"currentColor\"\n              stroke=\"currentColor\"\n              strokeWidth=\"8\"\n              strokeLinejoin=\"round\"\n            />\n            <path\n              d=\"M516.774 304.217C510.299 275.491 498.208 252.087 480.335 234.214C462.462 216.341 439.058 204.251 410.333 197.775C439.059 191.3 462.462 179.209 480.335 161.336C498.208 143.463 510.299 120.06 516.774 91.334C523.25 120.059 535.34 143.463 553.213 161.336C571.086 179.209 594.49 191.3 623.216 197.775C594.49 204.251 571.086 216.341 553.213 234.214C535.34 252.087 523.25 275.491 516.774 304.217Z\"\n              fill=\"currentColor\"\n              stroke=\"currentColor\"\n              strokeWidth=\"8\"\n              strokeLinejoin=\"round\"\n            />\n            <path\n              d=\"M857.5 508.116C763.259 497.644 710.903 445.288 700.432 351.047C689.961 445.288 637.605 497.644 543.364 508.116C637.605 518.587 689.961 570.943 700.432 665.184C710.903 570.943 763.259 518.587 857.5 508.116Z\"\n              stroke=\"currentColor\"\n              strokeWidth=\"20\"\n              strokeLinejoin=\"round\"\n            />\n            <path\n              d=\"M700.432 615.957C691.848 589.05 678.575 566.357 660.383 548.165C642.191 529.973 619.499 516.7 592.593 508.116C619.499 499.533 642.191 486.258 660.383 468.066C678.575 449.874 691.848 427.181 700.432 400.274C709.015 427.181 722.289 449.874 740.481 468.066C758.673 486.258 781.365 499.533 808.271 508.116C781.365 516.7 758.673 529.973 740.481 548.165C722.289 566.357 709.015 589.05 700.432 615.957Z\"\n              stroke=\"currentColor\"\n              strokeWidth=\"20\"\n              strokeLinejoin=\"round\"\n            />\n            <path\n              d=\"M889.949 121.237C831.049 114.692 798.326 81.9698 791.782 23.0692C785.237 81.9698 752.515 114.692 693.614 121.237C752.515 127.781 785.237 160.504 791.782 219.404C798.326 160.504 831.049 127.781 889.949 121.237Z\"\n              fill=\"currentColor\"\n              stroke=\"currentColor\"\n              strokeWidth=\"8\"\n              strokeLinejoin=\"round\"\n            />\n            <path\n              d=\"M791.782 196.795C786.697 176.937 777.869 160.567 765.16 147.858C752.452 135.15 736.082 126.322 716.226 121.237C736.082 116.152 752.452 107.324 765.16 94.6152C777.869 81.9065 786.697 65.5368 791.782 45.6797C796.867 65.5367 805.695 81.9066 818.403 94.6152C831.112 107.324 847.481 116.152 867.338 121.237C847.481 126.322 831.112 135.15 818.403 147.858C805.694 160.567 796.867 176.937 791.782 196.795Z\"\n              fill=\"currentColor\"\n              stroke=\"currentColor\"\n              strokeWidth=\"8\"\n              strokeLinejoin=\"round\"\n            />\n            <path\n              d=\"M760.632 764.337C720.719 814.616 669.835 855.1 611.872 882.692C553.91 910.285 490.404 924.255 426.213 923.533C362.022 922.812 298.846 907.419 241.518 878.531C184.19 849.643 134.228 808.026 95.4548 756.863C56.6815 705.7 30.1238 646.346 17.8129 583.343C5.50207 520.339 7.76433 455.354 24.4266 393.359C41.089 331.364 71.7099 274.001 113.947 225.658C156.184 177.315 208.919 139.273 268.117 114.442\"\n              stroke=\"currentColor\"\n              strokeWidth=\"30\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n            />\n          </svg>\n        ),\n      },\n      {\n        title: 'Open in ChatGPT',\n        href: `https://chatgpt.com/?${new URLSearchParams({\n          hints: 'search',\n          q,\n        })}`,\n        icon: (\n          <svg role=\"img\" viewBox=\"0 0 24 24\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n            <title>OpenAI</title>\n            <path d=\"M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z\" />\n          </svg>\n        ),\n      },\n      {\n        title: 'Open in Claude',\n        href: `https://claude.ai/new?${new URLSearchParams({\n          q,\n        })}`,\n        icon: (\n          <svg fill=\"currentColor\" role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n            <title>Anthropic</title>\n            <path d=\"M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5528h3.7442L10.5363 3.5409Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z\" />\n          </svg>\n        ),\n      },\n      {\n        title: 'Open in Cursor',\n        icon: (\n          <svg fill=\"currentColor\" role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n            <title>Cursor</title>\n            <path d=\"M11.503.131 1.891 5.678a.84.84 0 0 0-.42.726v11.188c0 .3.162.575.42.724l9.609 5.55a1 1 0 0 0 .998 0l9.61-5.55a.84.84 0 0 0 .42-.724V6.404a.84.84 0 0 0-.42-.726L12.497.131a1.01 1.01 0 0 0-.996 0M2.657 6.338h18.55c.263 0 .43.287.297.515L12.23 22.918c-.062.107-.229.064-.229-.06V12.335a.59.59 0 0 0-.295-.51l-9.11-5.257c-.109-.063-.064-.23.061-.23\" />\n          </svg>\n        ),\n        href: `https://cursor.com/link/prompt?${new URLSearchParams({\n          text: q,\n        })}`,\n      },\n    ];\n  }, [githubUrl, markdownUrl]);\n\n  return (\n    <Popover>\n      <PopoverTrigger\n        className={cn(\n          buttonVariants({\n            color: 'secondary',\n            size: 'sm',\n            className: 'gap-2',\n          })\n        )}>\n        Open\n        <ChevronDown className=\"size-3.5 text-fd-muted-foreground\" />\n      </PopoverTrigger>\n      <PopoverContent className=\"flex flex-col\">\n        {items.map((item) => (\n          <a\n            key={item.href}\n            href={item.href}\n            rel=\"noreferrer noopener\"\n            target=\"_blank\"\n            className=\"text-sm p-2 rounded-lg inline-flex items-center gap-2 hover:text-fd-accent-foreground hover:bg-fd-accent [&_svg]:size-4\">\n            {item.icon}\n            {item.title}\n            <ExternalLinkIcon className=\"text-fd-muted-foreground size-3.5 ms-auto\" />\n          </a>\n        ))}\n      </PopoverContent>\n    </Popover>\n  );\n}\n"
  },
  {
    "path": "apps/docs/components/docs/auto-option-sections.tsx",
    "content": "import type { DocEntry, TypeTableProps } from 'fumadocs-typescript';\nimport { typeGenerator } from '../../lib/type-generator';\nimport { ReferenceSections, buildEntryToc, renderInlineCode, toSlug, type HeadingLevel } from './reference-sections';\n\ntype AutoOptionSectionsProps = Pick<TypeTableProps, 'name' | 'path' | 'type'> & {\n  headingLevel?: HeadingLevel;\n  idPrefix?: string;\n};\n\ntype AutoOptionSectionsTocProps = Pick<TypeTableProps, 'name' | 'path' | 'type'> & {\n  idPrefix?: string;\n  depth?: HeadingLevel;\n};\n\nconst optionEntriesCache = new Map<string, Promise<DocEntry[]>>();\n\nfunction getEntriesCacheKey({ path, name, type }: Pick<TypeTableProps, 'path' | 'name' | 'type'>): string {\n  return `${path ?? ''}|${name ?? ''}|${type ?? ''}`;\n}\n\nasync function getOptionEntries(props: Pick<TypeTableProps, 'path' | 'name' | 'type'>): Promise<DocEntry[]> {\n  const cacheKey = getEntriesCacheKey(props);\n  const cachedEntries = optionEntriesCache.get(cacheKey);\n  if (cachedEntries != null) {\n    return cachedEntries;\n  }\n\n  const entriesPromise = typeGenerator.generateTypeTable(props).then((docs) => docs.flatMap((doc) => doc.entries));\n  optionEntriesCache.set(cacheKey, entriesPromise);\n  return entriesPromise;\n}\n\nfunction resolveIdPrefix(props: Pick<TypeTableProps, 'name' | 'type'>, idPrefix?: string): string {\n  const prefixSource = props.name ?? props.type ?? 'options';\n  return idPrefix ?? toSlug(prefixSource);\n}\n\nfunction readTagValues(entry: DocEntry, tagName: string): string[] {\n  const values: string[] = [];\n\n  for (const tag of entry.tags) {\n    if (tag.name !== tagName) {\n      continue;\n    }\n\n    const value = tag.text.trim();\n    if (value.length > 0) {\n      values.push(value);\n    }\n  }\n\n  return values;\n}\n\nexport async function AutoOptionSections({ headingLevel = 3, idPrefix, ...props }: AutoOptionSectionsProps) {\n  const entries = await getOptionEntries(props);\n  const resolvedIdPrefix = resolveIdPrefix(props, idPrefix);\n\n  return (\n    <ReferenceSections\n      entries={entries}\n      idPrefix={resolvedIdPrefix}\n      emptyMessage=\"Could not generate options for this type.\"\n      headingLevel={headingLevel}\n      renderMeta={(entry) => {\n        const defaultValues = readTagValues(entry, 'default');\n        const extraTags = entry.tags.filter((tag) => tag.name !== 'default');\n\n        return (\n          <>\n            <ul>\n              <li>\n                <strong>Type:</strong> <code>{entry.type}</code>\n              </li>\n              {defaultValues.length > 0 ? (\n                <li>\n                  <strong>Default:</strong> <code>{defaultValues[0]}</code>\n                </li>\n              ) : null}\n              {entry.required ? (\n                <li>\n                  <strong>Required:</strong> <code>true</code>\n                </li>\n              ) : null}\n            </ul>\n\n            {entry.deprecated ? <p>Deprecated.</p> : null}\n\n            {extraTags.length > 0 ? (\n              <>\n                <p>\n                  <strong>Additional Notes</strong>\n                </p>\n                <ul>\n                  {extraTags.map((tag, index) => (\n                    <li key={`${entry.name}-tag-${tag.name}-${index}`}>\n                      <strong>@{tag.name}:</strong>{' '}\n                      {renderInlineCode(tag.text, `${entry.name}-tag-${tag.name}-${index}`)}\n                    </li>\n                  ))}\n                </ul>\n              </>\n            ) : null}\n          </>\n        );\n      }}\n    />\n  );\n}\n\nexport async function getAutoOptionSectionsToc({ idPrefix, depth = 3, ...props }: AutoOptionSectionsTocProps) {\n  const entries = await getOptionEntries(props);\n  const resolvedIdPrefix = resolveIdPrefix(props, idPrefix);\n\n  return buildEntryToc({\n    entries,\n    idPrefix: resolvedIdPrefix,\n    depth,\n  });\n}\n"
  },
  {
    "path": "apps/docs/components/docs/auto-runtime-reference.tsx",
    "content": "import { existsSync } from 'node:fs';\nimport nodePath from 'node:path';\nimport type { TOCItemType } from 'fumadocs-core/toc';\nimport { Node, Project, type ExportedDeclarations, type JSDocTag } from 'ts-morph';\nimport {\n  ReferenceSections,\n  buildEntryToc,\n  getEntryId,\n  renderInlineCode,\n  type BaseReferenceEntry,\n} from './reference-sections';\n\ntype RuntimeExportKind = 'function' | 'component' | 'constant' | 'type';\n\ntype RuntimeParameter = {\n  name: string;\n  type: string;\n  description: string;\n};\n\ntype RuntimeTag = {\n  name: string;\n  text: string;\n};\n\ntype RuntimeExportEntry = BaseReferenceEntry & {\n  kind: RuntimeExportKind;\n  typeText: string;\n  parameters: RuntimeParameter[];\n  returnType?: string;\n  returnDescription?: string;\n  tags: RuntimeTag[];\n  sourceOrder: number;\n  declarationOrder: number;\n};\n\ntype AutoRuntimeReferenceProps = {\n  path: string;\n};\n\ntype SupportedDeclaration = Extract<\n  ExportedDeclarations,\n  | import('ts-morph').FunctionDeclaration\n  | import('ts-morph').VariableDeclaration\n  | import('ts-morph').TypeAliasDeclaration\n>;\n\ntype ParsedDeclarationDocumentation = {\n  description: string;\n  parameterDescriptions: Map<string, string>;\n  returnDescription?: string;\n  tags: RuntimeTag[];\n};\n\nconst GROUP_TITLES: Record<RuntimeExportKind, string> = {\n  function: 'Functions',\n  component: 'Components',\n  constant: 'Constants',\n  type: 'Types',\n};\n\nconst GROUP_ORDER: RuntimeExportKind[] = ['function', 'component', 'constant', 'type'];\nconst RUNTIME_GROUP_ID_PREFIX = 'runtime-group';\nconst RUNTIME_EXPORT_ID_PREFIX = 'runtime-export';\n\nlet cachedProject: Project | undefined;\nlet cachedTsConfigPath: string | undefined;\nconst runtimeEntriesCache = new Map<string, RuntimeExportEntry[]>();\n\nfunction resolveRepositoryRoot(startPath: string): string {\n  const candidates = [\n    startPath,\n    nodePath.resolve(startPath, '..'),\n    nodePath.resolve(startPath, '..', '..'),\n    nodePath.resolve(startPath, '..', '..', '..'),\n  ];\n\n  for (const candidate of candidates) {\n    if (existsSync(nodePath.join(candidate, 'packages/react-native-boost/src/runtime/index.ts'))) {\n      return candidate;\n    }\n  }\n\n  throw new Error('Could not resolve repository root for runtime docs generation.');\n}\n\nfunction getProject(tsConfigPath: string): Project {\n  if (cachedProject != null && cachedTsConfigPath === tsConfigPath) {\n    return cachedProject;\n  }\n\n  cachedProject = new Project({\n    tsConfigFilePath: tsConfigPath,\n  });\n  cachedTsConfigPath = tsConfigPath;\n  return cachedProject;\n}\n\nfunction isSupportedDeclaration(declaration: ExportedDeclarations): declaration is SupportedDeclaration {\n  return (\n    Node.isFunctionDeclaration(declaration) ||\n    Node.isVariableDeclaration(declaration) ||\n    Node.isTypeAliasDeclaration(declaration)\n  );\n}\n\nfunction readTagComment(tag: JSDocTag): string {\n  const comment = tag.getComment();\n\n  if (typeof comment === 'string') {\n    return comment.trim();\n  }\n\n  if (!Array.isArray(comment)) {\n    return '';\n  }\n\n  return comment\n    .map((part) => {\n      if (part == null) {\n        return '';\n      }\n\n      if (typeof part === 'string') {\n        return part;\n      }\n\n      if (typeof part.getText === 'function') {\n        return part.getText();\n      }\n\n      return '';\n    })\n    .join('')\n    .trim();\n}\n\nfunction normalizeParameterDescription(value: string): string {\n  return value.replace(/^\\s*-\\s*/, '').trim();\n}\n\nfunction readDeclarationDocumentation(declaration: SupportedDeclaration): ParsedDeclarationDocumentation {\n  const jsDocs = Node.isVariableDeclaration(declaration)\n    ? (declaration.getVariableStatement()?.getJsDocs() ?? [])\n    : declaration.getJsDocs();\n\n  const descriptions: string[] = [];\n  const parameterDescriptions = new Map<string, string>();\n  const tags: RuntimeTag[] = [];\n  let returnDescription: string | undefined;\n\n  for (const jsDoc of jsDocs) {\n    const description = jsDoc.getDescription().trim();\n    if (description.length > 0) {\n      descriptions.push(description);\n    }\n\n    for (const tag of jsDoc.getTags()) {\n      const tagName = tag.getTagName();\n      const text = readTagComment(tag);\n\n      if (Node.isJSDocParameterTag(tag)) {\n        parameterDescriptions.set(tag.getName(), normalizeParameterDescription(text));\n        continue;\n      }\n\n      if (Node.isJSDocReturnTag(tag)) {\n        returnDescription = text;\n        continue;\n      }\n\n      tags.push({\n        name: tagName,\n        text,\n      });\n    }\n  }\n\n  return {\n    description: descriptions.join('\\n\\n'),\n    parameterDescriptions,\n    returnDescription,\n    tags,\n  };\n}\n\nfunction readKind(declaration: SupportedDeclaration): RuntimeExportKind {\n  if (Node.isFunctionDeclaration(declaration)) {\n    return 'function';\n  }\n\n  if (Node.isTypeAliasDeclaration(declaration)) {\n    return 'type';\n  }\n\n  const sourceFilePath = declaration.getSourceFile().getFilePath().replaceAll('\\\\', '/');\n  if (sourceFilePath.includes('/runtime/components/')) {\n    return 'component';\n  }\n\n  return 'constant';\n}\n\nfunction readTypeText(declaration: SupportedDeclaration): string {\n  if (Node.isFunctionDeclaration(declaration)) {\n    const parameterSignature = declaration\n      .getParameters()\n      .map((parameter) => `${parameter.getName()}: ${parameter.getType().getText(parameter)}`)\n      .join(', ');\n    const returnType = declaration.getReturnType().getText(declaration);\n\n    return `(${parameterSignature}) => ${returnType}`;\n  }\n\n  if (Node.isTypeAliasDeclaration(declaration)) {\n    return declaration.getTypeNode()?.getText() ?? declaration.getType().getText(declaration);\n  }\n\n  return declaration.getType().getText(declaration);\n}\n\nfunction readParameters(\n  declaration: SupportedDeclaration,\n  parameterDescriptions: Map<string, string>\n): RuntimeParameter[] {\n  if (!Node.isFunctionDeclaration(declaration)) {\n    return [];\n  }\n\n  return declaration.getParameters().map((parameter) => ({\n    name: parameter.getName(),\n    type: parameter.getType().getText(parameter),\n    description: parameterDescriptions.get(parameter.getName()) ?? '',\n  }));\n}\n\nfunction selectDeclaration(\n  declarations: ExportedDeclarations[],\n  sourceOrderByPath: Map<string, number>\n): SupportedDeclaration | undefined {\n  const supportedDeclarations = declarations.filter((declaration) => isSupportedDeclaration(declaration));\n  if (supportedDeclarations.length === 0) {\n    return undefined;\n  }\n\n  return [...supportedDeclarations].sort((left, right) => {\n    const leftOrder = sourceOrderByPath.get(left.getSourceFile().getFilePath()) ?? Number.MAX_SAFE_INTEGER;\n    const rightOrder = sourceOrderByPath.get(right.getSourceFile().getFilePath()) ?? Number.MAX_SAFE_INTEGER;\n\n    if (leftOrder !== rightOrder) {\n      return leftOrder - rightOrder;\n    }\n\n    return left.getStart() - right.getStart();\n  })[0];\n}\n\nfunction getGroupHeadingId(group: RuntimeExportKind): string {\n  return getEntryId(RUNTIME_GROUP_ID_PREFIX, group);\n}\n\nfunction getRuntimeExportEntries(indexFilePath: string): RuntimeExportEntry[] {\n  const repositoryRoot = resolveRepositoryRoot(process.cwd());\n  const packageTsConfigPath = nodePath.join(repositoryRoot, 'packages/react-native-boost/tsconfig.json');\n  const docsRoot = nodePath.join(repositoryRoot, 'apps/docs');\n  const project = getProject(packageTsConfigPath);\n\n  const absoluteIndexFilePath = nodePath.resolve(docsRoot, indexFilePath);\n  const cachedEntries = runtimeEntriesCache.get(absoluteIndexFilePath);\n  if (cachedEntries != null) {\n    return cachedEntries;\n  }\n\n  const indexFile = project.getSourceFile(absoluteIndexFilePath) ?? project.addSourceFileAtPath(absoluteIndexFilePath);\n\n  const sourceOrderByPath = new Map<string, number>();\n  sourceOrderByPath.set(indexFile.getFilePath(), 0);\n  let sourceOrder = 1;\n\n  for (const exportDeclaration of indexFile.getExportDeclarations()) {\n    const sourceFile = exportDeclaration.getModuleSpecifierSourceFile();\n    if (sourceFile == null) {\n      continue;\n    }\n\n    const sourceFilePath = sourceFile.getFilePath();\n    if (!sourceOrderByPath.has(sourceFilePath)) {\n      sourceOrderByPath.set(sourceFilePath, sourceOrder);\n      sourceOrder += 1;\n    }\n  }\n\n  const entries: RuntimeExportEntry[] = [];\n\n  for (const [name, declarations] of indexFile.getExportedDeclarations()) {\n    const declaration = selectDeclaration(declarations, sourceOrderByPath);\n    if (declaration == null) {\n      continue;\n    }\n\n    const docs = readDeclarationDocumentation(declaration);\n    const kind = readKind(declaration);\n    const typeText = readTypeText(declaration);\n    const parameters = readParameters(declaration, docs.parameterDescriptions);\n    const returnType = Node.isFunctionDeclaration(declaration)\n      ? declaration.getReturnType().getText(declaration)\n      : undefined;\n\n    entries.push({\n      name,\n      kind,\n      typeText,\n      description: docs.description,\n      parameters,\n      returnType,\n      returnDescription: docs.returnDescription,\n      tags: docs.tags,\n      sourceOrder: sourceOrderByPath.get(declaration.getSourceFile().getFilePath()) ?? Number.MAX_SAFE_INTEGER,\n      declarationOrder: declaration.getStart(),\n    });\n  }\n\n  const sortedEntries = entries.sort((left, right) => {\n    if (left.sourceOrder !== right.sourceOrder) {\n      return left.sourceOrder - right.sourceOrder;\n    }\n\n    return left.declarationOrder - right.declarationOrder;\n  });\n\n  runtimeEntriesCache.set(absoluteIndexFilePath, sortedEntries);\n  return sortedEntries;\n}\n\nexport function getRuntimeReferenceToc(path: string): TOCItemType[] {\n  const entries = getRuntimeExportEntries(path);\n  const toc: TOCItemType[] = [];\n\n  for (const group of GROUP_ORDER) {\n    const groupEntries = entries.filter((entry) => entry.kind === group);\n    if (groupEntries.length === 0) {\n      continue;\n    }\n\n    toc.push({\n      title: GROUP_TITLES[group],\n      url: `#${getGroupHeadingId(group)}`,\n      depth: 3,\n    });\n\n    toc.push(\n      ...buildEntryToc({\n        entries: groupEntries,\n        idPrefix: RUNTIME_EXPORT_ID_PREFIX,\n        depth: 4,\n      })\n    );\n  }\n\n  return toc;\n}\n\nexport function AutoRuntimeReference({ path }: AutoRuntimeReferenceProps) {\n  const exports = getRuntimeExportEntries(path);\n\n  if (exports.length === 0) {\n    return <p>Could not generate runtime reference from the provided entry file.</p>;\n  }\n\n  return (\n    <>\n      {GROUP_ORDER.map((group) => {\n        const groupEntries = exports.filter((entry) => entry.kind === group);\n        if (groupEntries.length === 0) {\n          return null;\n        }\n\n        return (\n          <section key={group} className=\"mt-10 first:mt-0\">\n            <h2 id={getGroupHeadingId(group)}>{GROUP_TITLES[group]}</h2>\n\n            <ReferenceSections\n              entries={groupEntries}\n              idPrefix={RUNTIME_EXPORT_ID_PREFIX}\n              emptyMessage=\"No runtime exports found for this group.\"\n              headingLevel={3}\n              renderMeta={(entry) => {\n                const remarks = entry.tags.filter((tag) => tag.name === 'remarks' && tag.text.length > 0);\n                const additionalTags = entry.tags.filter((tag) => tag.name !== 'remarks' && tag.text.length > 0);\n\n                return (\n                  <>\n                    <ul>\n                      <li>\n                        <strong>Type:</strong> <code>{entry.typeText}</code>\n                      </li>\n                    </ul>\n\n                    {entry.parameters.length > 0 ? (\n                      <>\n                        <h4>Parameters</h4>\n                        <ul>\n                          {entry.parameters.map((parameter) => (\n                            <li key={`${entry.name}-parameter-${parameter.name}`}>\n                              <code>{parameter.name}</code>: <code>{parameter.type}</code>\n                              {parameter.description.length > 0 ? ' - ' : ''}\n                              {parameter.description.length > 0\n                                ? renderInlineCode(parameter.description, `${entry.name}-parameter-${parameter.name}`)\n                                : null}\n                            </li>\n                          ))}\n                        </ul>\n                      </>\n                    ) : null}\n\n                    {entry.returnType == null ? null : (\n                      <>\n                        <h4>Returns</h4>\n                        <p>\n                          <code>{entry.returnType}</code>\n                          {entry.returnDescription != null && entry.returnDescription.length > 0 ? ': ' : ''}\n                          {entry.returnDescription != null && entry.returnDescription.length > 0\n                            ? renderInlineCode(entry.returnDescription, `${entry.name}-returns`)\n                            : null}\n                        </p>\n                      </>\n                    )}\n\n                    {remarks.length > 0 ? (\n                      <>\n                        <h4>Notes</h4>\n                        {remarks.map((tag, index) => (\n                          <p key={`${entry.name}-remark-${index}`}>\n                            {renderInlineCode(tag.text, `${entry.name}-remark-${index}`)}\n                          </p>\n                        ))}\n                      </>\n                    ) : null}\n\n                    {additionalTags.length > 0 ? (\n                      <>\n                        <h4>Additional Tags</h4>\n                        <ul>\n                          {additionalTags.map((tag, index) => (\n                            <li key={`${entry.name}-tag-${tag.name}-${index}`}>\n                              <strong>@{tag.name}:</strong>{' '}\n                              {renderInlineCode(tag.text, `${entry.name}-tag-${tag.name}-${index}`)}\n                            </li>\n                          ))}\n                        </ul>\n                      </>\n                    ) : null}\n                  </>\n                );\n              }}\n            />\n          </section>\n        );\n      })}\n    </>\n  );\n}\n"
  },
  {
    "path": "apps/docs/components/docs/reference-sections.tsx",
    "content": "import type { TOCItemType } from 'fumadocs-core/toc';\nimport type { ReactNode } from 'react';\n\nexport type HeadingLevel = 2 | 3 | 4 | 5 | 6;\n\nexport type BaseReferenceEntry = {\n  name: string;\n  description: string;\n};\n\ntype ReferenceSectionsProps<TEntry extends BaseReferenceEntry> = {\n  entries: TEntry[];\n  idPrefix: string;\n  emptyMessage: string;\n  headingLevel?: HeadingLevel;\n  renderMeta: (entry: TEntry) => ReactNode;\n};\n\ntype BuildEntryTocProps<TEntry extends BaseReferenceEntry> = {\n  entries: TEntry[];\n  idPrefix: string;\n  depth: HeadingLevel;\n};\n\nexport function toSlug(value: string): string {\n  const slug = value\n    .toLowerCase()\n    .replaceAll(/[^a-z0-9]+/g, '-')\n    .replace(/^-+/, '')\n    .replace(/-+$/, '');\n\n  return slug.length > 0 ? slug : 'reference-entry';\n}\n\nexport function getEntryId(idPrefix: string, value: string): string {\n  return `${idPrefix}-${toSlug(value)}`;\n}\n\nfunction toParagraphs(value: string): string[] {\n  return value\n    .split(/\\n{2,}/)\n    .map((paragraph) => paragraph.replaceAll('\\n', ' ').trim())\n    .filter((paragraph) => paragraph.length > 0);\n}\n\nexport function renderInlineCode(value: string, keyPrefix: string): ReactNode {\n  const parts = value.split(/`([^`]+)`/g);\n\n  return parts.map((part, index) => {\n    if (index % 2 === 1) {\n      return <code key={`${keyPrefix}-code-${index}`}>{part}</code>;\n    }\n\n    return <span key={`${keyPrefix}-text-${index}`}>{part}</span>;\n  });\n}\n\nexport function buildEntryToc<TEntry extends BaseReferenceEntry>({\n  entries,\n  idPrefix,\n  depth,\n}: BuildEntryTocProps<TEntry>): TOCItemType[] {\n  return entries.map((entry) => ({\n    title: entry.name,\n    url: `#${getEntryId(idPrefix, entry.name)}`,\n    depth,\n  }));\n}\n\nexport function ReferenceSections<TEntry extends BaseReferenceEntry>({\n  entries,\n  idPrefix,\n  emptyMessage,\n  headingLevel = 3,\n  renderMeta,\n}: ReferenceSectionsProps<TEntry>) {\n  if (entries.length === 0) {\n    return <p>{emptyMessage}</p>;\n  }\n\n  const HeadingTag = `h${headingLevel}` as 'h2' | 'h3' | 'h4' | 'h5' | 'h6';\n\n  return (\n    <>\n      {entries.map((entry) => {\n        const descriptionParagraphs = toParagraphs(entry.description);\n\n        return (\n          <section key={entry.name} id={getEntryId(idPrefix, entry.name)} className=\"mt-8 first:mt-0\">\n            <HeadingTag>\n              <code>{entry.name}</code>\n            </HeadingTag>\n\n            {descriptionParagraphs.length > 0 ? (\n              descriptionParagraphs.map((paragraph, index) => (\n                <p key={`${entry.name}-description-${index}`}>\n                  {renderInlineCode(paragraph, `${entry.name}-description-${index}`)}\n                </p>\n              ))\n            ) : (\n              <p>No description provided.</p>\n            )}\n\n            {renderMeta(entry)}\n          </section>\n        );\n      })}\n    </>\n  );\n}\n"
  },
  {
    "path": "apps/docs/content/docs/configuration/boost-decorator.mdx",
    "content": "---\ntitle: \"Decorators\"\ndescription: Control optimization on individual JSX elements with @boost-ignore and @boost-force.\n---\n\n## @boost-ignore\n\nUse `@boost-ignore` to disable optimization on a specific element.\n\nIf a line containing `@boost-ignore` appears immediately before a JSX opening tag, that component is skipped.\n\n```jsx\n<Text>This will be optimized.</Text>\n{/* @boost-ignore */}\n<Text>This will not be optimized.</Text>\n```\n\n## @boost-force\n\nUse `@boost-force` to force optimization on a specific element, even if it would normally be skipped by a bailout rule (e.g. blacklisted props, unresolvable spreads, or ancestor checks).\n\nThe only check that `@boost-force` does **not** override is the `react-native` import check — the component must still be imported from `react-native`.\n\n```jsx\nconst Component = ({ props }) => {\n  return (\n    {/* @boost-force */}\n    <Text {...props}>This will be optimized despite having unresolvable spread props.</Text>\n  )\n}\n```\n\n<Callout type=\"warn\" title=\"Use with caution\">\n  `@boost-force` bypasses safety checks that exist to prevent behavioral changes.\n  Only use it when you are confident that the optimization is safe for your specific use case — for example,\n  when a wrapper component filters or handles props before passing them to the underlying native component.\n</Callout>\n"
  },
  {
    "path": "apps/docs/content/docs/configuration/configure.mdx",
    "content": "---\ntitle: Configure the Babel Plugin\ndescription: Control logging, ignores, and optimization behavior.\n---\n\nThe Babel plugin (`react-native-boost/plugin`) is the core of React Native Boost.\n\nDefaults are safe and usable out of the box, but you can tune behavior for your app.\n\n## Example Configuration\n\n```js\n// babel.config.js\nmodule.exports = {\n  plugins: [\n    [\n      'react-native-boost/plugin',\n      {\n        verbose: false,\n        silent: false,\n        ignores: ['node_modules/**'],\n        optimizations: {\n          text: true,\n          view: true,\n        },\n      },\n    ],\n  ],\n};\n```\n\n## Plugin Options\n\n<AutoOptionSections path=\"../../packages/react-native-boost/src/plugin/types/index.ts\" name=\"PluginOptions\" idPrefix=\"plugin-options\" />\n\n## Plugin Optimization Options\n\n<AutoOptionSections\n  path=\"../../packages/react-native-boost/src/plugin/types/index.ts\"\n  name=\"PluginOptimizationOptions\"\n  idPrefix=\"plugin-optimization-options\"\n/>\n\n## Environment-Specific Enablement\n\nYou can enable React Native Boost by environment with Babel `env` config:\n\n```js\nmodule.exports = {\n  env: {\n    development: {\n      plugins: ['react-native-boost/plugin'],\n    },\n  },\n};\n```\n\nSee Babel docs: https://babeljs.io/docs/options#env\n"
  },
  {
    "path": "apps/docs/content/docs/configuration/nativewind.mdx",
    "content": "---\ntitle: Nativewind Support\ndescription: Configure cssInterop for optimized components.\n---\n\nIf your app uses Nativewind, configure `cssInterop` for optimized components from\n`react-native-boost/runtime`.\n\n## Example\n\n```jsx\nimport { cssInterop } from 'nativewind';\nimport { NativeText, NativeView } from 'react-native-boost/runtime';\n\ncssInterop(NativeText, { className: 'style' });\ncssInterop(NativeView, { className: 'style' });\n```\n\nThis mirrors Nativewind's own mapping for `Text`/`View`.\n\n## Known Limitations\n\n### `select-*`\n\nNativewind maps `select-*` classes to `userSelect`. Native `Text` does not accept `userSelect` directly and uses the\n`selectable` prop.\n\n#### Recommended Usage\n\n```jsx\n// Avoid\n<Text className=\"select-auto\">Hello world</Text>\n\n// Use\n<Text selectable>Hello world</Text>\n// or\n<Text style={{ userSelect: 'auto' }}>Hello world</Text>\n```\n\nYou can map values with `userSelectToSelectableMap` from the runtime package.\n\n### `align-*`\n\nNativewind maps `align-*` classes to `verticalAlign`, while native `Text` expects `textAlignVertical`.\n\n#### Recommended Usage\n\n```jsx\n// Avoid\n<Text className=\"align-center\">Hello world</Text>\n\n// Use\n<Text style={{ textAlignVertical: 'center' }}>Hello world</Text>\n// or\n<Text style={{ verticalAlign: 'middle' }}>Hello world</Text>\n```\n\nYou can map values with `verticalAlignToTextAlignVerticalMap` from the runtime package.\n"
  },
  {
    "path": "apps/docs/content/docs/index.mdx",
    "content": "---\ntitle: Getting Started\ndescription: Install React Native Boost to boost your app's performance with one line of code.\n---\n\nimport { Tab, Tabs } from 'fumadocs-ui/components/tabs'\n\n## Introduction\n\nReact Native Boost consists of two pieces:\n\n- A Babel plugin that statically analyzes your source code and replaces safe `Text` and `View` components with their direct native counterparts, leading to significant performance improvements compared to the JS-based wrapper components.\n- A runtime package used by the plugin for cross-platform-safe imports and helper utilities.\n\nThe analyzer is intentionally strict and skips any optimizations that could lead to behavioral changes or other bugs.\n\n## Compatibility\n\n| `react-native-boost` | React Native |\n| --- | --- |\n| `0.x` | All versions[^1] |\n| `1.x` | `>=0.83` |\n\n[^1]: Starting from React Native `0.80`, `react-native-boost@0` prints import deprecation warnings. [See here.](https://github.com/react-native-community/discussions-and-proposals/discussions/893)\n\n## Getting Started\n\n1. Install React Native Boost:\n\n<Tabs items={['npm', 'pnpm', 'yarn', 'bun']} groupId=\"package-manager\" persist>\n  <Tab value=\"npm\">\n\n  ```bash\n  npm install react-native-boost\n  ```\n\n  </Tab>\n  <Tab value=\"pnpm\">\n\n  ```bash\n  pnpm add react-native-boost\n  ```\n\n  </Tab>\n  <Tab value=\"yarn\">\n\n  ```bash\n  yarn add react-native-boost\n  ```\n\n  </Tab>\n  <Tab value=\"bun\">\n\n  ```bash\n  bun add react-native-boost\n  ```\n\n  </Tab>\n</Tabs>\n\n2. If you use Expo and do not have a `babel.config.js` yet:\n\n```bash\nnpx expo customize babel.config.js\n```\n\n3. Add the plugin:\n\n```js\n// babel.config.js\nmodule.exports = {\n  plugins: ['react-native-boost/plugin'],\n};\n```\n\n4. Restart the development server and clear cache:\n\n<Tabs items={['npm', 'pnpm', 'yarn', 'bun']} groupId=\"package-manager\" persist>\n  <Tab value=\"npm\">\n\n  ```bash\n  npm start -- --clear\n  ```\n\n  </Tab>\n  <Tab value=\"pnpm\">\n\n  ```bash\n  pnpm start -- --clear\n  ```\n\n  </Tab>\n  <Tab value=\"yarn\">\n\n  ```bash\n  yarn start --clear\n  ```\n\n  </Tab>\n  <Tab value=\"bun\">\n\n  ```bash\n  bun run start --clear\n  ```\n\n  </Tab>\n</Tabs>\n\n<Callout title=\"Runtime Dependency\">\n  The Babel plugin imports optimized components via `react-native-boost/runtime`, so `react-native-boost` must be\n  available at runtime and can therefore **not** be installed as a dev dependency.\n</Callout>\n\n## Platform Support\n\nReact Native Boost supports iOS and Android projects. On Web, React Native Boost safely falls back to the default components, providing full cross-platform support.\n\n<Cards>\n  <Card title=\"How It Works\" href=\"/docs/information/how-it-works\" />\n  <Card title=\"Coverage & Bailouts\" href=\"/docs/information/optimization-coverage\" />\n  <Card title=\"Troubleshooting\" href=\"/docs/information/troubleshooting\" />\n  <Card title=\"Configuration\" href=\"/docs/configuration/configure\" />\n  <Card title=\"Runtime Library\" href=\"/docs/runtime-library\" />\n</Cards>\n"
  },
  {
    "path": "apps/docs/content/docs/information/benchmarks.mdx",
    "content": "---\ntitle: Benchmarks\ndescription: Benchmark results from the repository example app.\n---\n\nWe run benchmarks with the example app available in the repository to measure render-time improvements.\n\nIn recent runs, React Native Boost improved rendering performance on both iOS and Android, with gains up to ~50%\ndepending on component mix and screen structure.\n\n![React Native Boost iOS benchmarks](./img/benchmark-ios.png)\n![React Native Boost Android benchmarks](./img/benchmark-android.png)\n\nThe more `Text` and `View` components your UI renders (especially in lists), the more measurable the gains are likely\nto be.\n"
  },
  {
    "path": "apps/docs/content/docs/information/how-it-works.mdx",
    "content": "---\ntitle: How It Works\ndescription: Understand why React Native Boost makes your app faster, and how the Babel optimizer decides when transformations are safe.\n---\n\nReact Native components such as `Text` and `View` are JavaScript wrappers around their native implementations.\nThose wrappers handle many edge cases, but they also add a considerable amount of runtime overhead.\n\nReact Native Boost analyzes your code at build time and replaces these components with their native equivalents fully automatically. The plugin performs static analysis on your code to determine when it's safe to optimize a component and when it's not.\n\nOptimized components are imported from `react-native-boost/runtime`, not directly from `react-native`, which allows graceful fallback for web targets\nand other non-native environments when native internals are unavailable.\n\n## Static Analysis\n\nFor each candidate component, the plugin verifies conditions such as:\n\n- Import source checks (for example, imported from `react-native`)\n- Prop compatibility checks\n- Ancestor and context safety checks\n- Children/structure checks\n\nIf any safety requirement is not met, the component is left unchanged in order to avoid behavioral changes or other bugs.\n"
  },
  {
    "path": "apps/docs/content/docs/information/optimization-coverage.mdx",
    "content": "---\ntitle: Optimization Coverage\ndescription: What React Native Boost can optimize today, what it skips, and why.\n---\n\nReact Native Boost is conservative by design. If it cannot prove an optimization is safe within the possibilities of a Babel plugin, it skips it. While this means it'll often skip optimizations that would be safe in practice, it also means that you can trust that optimizations that do happen are safe and won't cause behavioral changes or other regressions.\n\n## At a Glance\n\n| Component | Optimized when... | Common bailout reasons |\n| --- | --- | --- |\n| `Text` | Imported from `react-native`, no blacklisted props, string-safe children | `contains blacklisted props`, `contains non-string children`, `is a direct child of expo-router Link with asChild` |\n| `View` | Imported from `react-native`, no blacklisted props, safe ancestor chain | `contains blacklisted props`, `has Text ancestor`, `has unresolved ancestor and dangerous optimization is disabled` |\n\n## Global Bailouts\n\nThese skip optimization before component-specific checks:\n\n- File path matches `ignores`\n- Line is marked with `@boost-ignore`\n\n<Callout title=\"No log for ignored files\">\n  Files skipped via `ignores` are filtered before optimizer checks, so you will not see per-component skip logs for\n  those files.\n</Callout>\n\n## Overriding Bailouts\n\nUse `@boost-force` to force optimization on a component that would otherwise be skipped. This bypasses all bailout checks except the `react-native` import check. See the [Decorators](/docs/configuration/boost-decorator#boost-force) page for details.\n\n## Text Coverage\n\n`Text` is optimized when all checks pass.\n\n### Text blacklisted props\n\nIf any of these are present, the `Text` node is skipped:\n\n- Interaction/responder props (`onPress`, `onLongPress`, `onResponder*`, `pressRetentionOffset`, etc.)\n- `selectionColor`\n- `id`, `nativeID`\n\n### Text structure checks\n\n- Children must be string-safe.\n- `Text` is skipped when used as a direct child of `expo-router` `Link` with `asChild`.\n\n```tsx\nimport { Link } from 'expo-router';\nimport { Text } from 'react-native';\n\n<Link asChild>\n  <Text>Open profile</Text>\n</Link>;\n```\n\n## View Coverage\n\n`View` has stricter safety checks because `View` inside text-like ancestors can break layout/semantics.\n\n### View blacklisted props\n\nIf any of these are present, the `View` node is skipped:\n\n- `style`\n- Accessibility props (`accessible`, `accessibilityLabel`, `accessibilityState`, `aria-*`)\n- `id`, `nativeID`\n\n### Ancestor safety checks\n\n`View` optimization depends on ancestor classification:\n\n- `safe`: optimize\n- `text`: skip (`has Text ancestor`)\n- `unknown`: skip by default\n\nSet `dangerouslyOptimizeViewWithUnknownAncestors: true` to optimize `unknown` ancestors too.\n\n<Callout type=\"warn\" title=\"Dangerous Mode\">\n  Enabling dangerous mode can increase optimization coverage, but it can also introduce regressions if unresolved\n  ancestors render Text wrappers.\n</Callout>\n\n## Spread Props: Resolvable vs Unresolvable\n\nUnresolvable spread props are treated as unsafe and cause bailouts.\n\n```tsx\n// Usually optimizable (resolvable object literal)\n<Text {...{ selectable: true }}>Hello</Text>\n\n// Usually skipped (cannot be statically resolved)\n<Text {...props}>Hello</Text>\n```\n\nSame rule applies to `View`.\n"
  },
  {
    "path": "apps/docs/content/docs/information/troubleshooting.mdx",
    "content": "---\ntitle: Troubleshooting\ndescription: Common setup and optimization issues, plus fast ways to diagnose them.\n---\n\n## Quick Diagnostic Flow\n\n1. Set `verbose: true` and `silent: false` in plugin config.\n2. Restart Metro with cache clear.\n3. Check skip reasons in logs.\n4. Compare with the coverage rules in [Optimization Coverage](/docs/information/optimization-coverage).\n\n## Common Issues\n\n### No optimization logs at all\n\nLikely causes:\n\n- Plugin not loaded in `babel.config.js`\n- `silent: true`\n- File matched by `ignores`\n\nQuick checks:\n\n```js\nmodule.exports = {\n  plugins: [\n    [\n      'react-native-boost/plugin',\n      {\n        verbose: true,\n        silent: false,\n      },\n    ],\n  ],\n};\n```\n\n```bash\nnpm start -- --clear\n```\n\n### Skip reason: `contains blacklisted props`\n\nThis is expected for unsupported prop sets.\n\nTypical cases:\n\n- `Text` with press/responder props\n- `View` with `style` or accessibility props\n\nFix options:\n\n- Keep component as-is (recommended when semantics matter)\n- Move unsupported behavior to a different node when possible\n- Use `@boost-ignore` for explicit clarity\n\n### Skip reason: `has unresolved ancestor and dangerous optimization is disabled`\n\n`View` is inside an ancestor React Native Boost cannot statically classify.\n\nOptions:\n\n- Keep default behavior (safest)\n- Refactor ancestor/component structure to be statically obvious\n- Enable `dangerouslyOptimizeViewWithUnknownAncestors` only if you can validate behavior carefully\n\n### Ignores do not work as expected in monorepos\n\n`ignores` are resolved from Babel's working directory.\n\nIn nested apps, you may need explicit parent paths:\n\n```js\nignores: ['../../node_modules/**'];\n```\n\n### Runtime import errors in app code\n\nThe plugin injects imports from `react-native-boost/runtime`.\n\nIf you installed `react-native-boost` as a dev dependency, runtime imports can fail in app builds.\n\nFix: install it as a regular dependency.\n"
  },
  {
    "path": "apps/docs/content/docs/meta.json",
    "content": "{\n  \"title\": \"React Native Boost\",\n  \"description\": \"Documentation for React Native Boost\",\n  \"root\": true,\n  \"pages\": [\n    \"index\",\n    \"---Information---\",\n    \"information/how-it-works\",\n    \"information/optimization-coverage\",\n    \"information/troubleshooting\",\n    \"information/benchmarks\",\n    \"---Configuration---\",\n    \"configuration/configure\",\n    \"configuration/nativewind\",\n    \"configuration/boost-decorator\",\n    \"---Runtime Library---\",\n    \"runtime-library/index\"\n  ]\n}\n"
  },
  {
    "path": "apps/docs/content/docs/runtime-library/index.mdx",
    "content": "---\ntitle: Runtime Library\ndescription: Runtime exports used by the Babel plugin and advanced integrations.\n---\n\n`react-native-boost/runtime` is used by the Babel plugin to apply optimizations safely across platforms.\n\nBesides re-exporting optimized native components with web-safe fallbacks, it also exposes helper utilities.\n\nDirect usage is supported but generally not recommended unless needed for advanced integrations (for example,\n[Nativewind setup](/docs/configuration/nativewind)).\n\n## API Reference\n\nThis section is automatically generated from runtime exports.\n\n<AutoRuntimeReference path=\"../../packages/react-native-boost/src/runtime/index.ts\" />\n"
  },
  {
    "path": "apps/docs/lib/cn.ts",
    "content": "export { twMerge as cn } from 'tailwind-merge';\n"
  },
  {
    "path": "apps/docs/lib/layout.shared.tsx",
    "content": "import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';\n\nexport const gitConfig = {\n  user: 'kuatsu',\n  repo: 'react-native-boost',\n  branch: 'main',\n};\n\nexport function baseOptions(): BaseLayoutProps {\n  return {\n    nav: {\n      title: 'React Native Boost',\n    },\n    githubUrl: `https://github.com/${gitConfig.user}/${gitConfig.repo}`,\n  };\n}\n"
  },
  {
    "path": "apps/docs/lib/source.ts",
    "content": "import { docs } from 'fumadocs-mdx:collections/server';\nimport { type InferPageType, loader } from 'fumadocs-core/source';\nimport { lucideIconsPlugin } from 'fumadocs-core/source/lucide-icons';\n\n// See https://fumadocs.dev/docs/headless/source-api for more info\nexport const source = loader({\n  baseUrl: '/docs',\n  source: docs.toFumadocsSource(),\n  plugins: [lucideIconsPlugin()],\n});\n\nexport function getPageImage(page: InferPageType<typeof source>) {\n  const segments = [...page.slugs, 'image.png'];\n\n  return {\n    segments,\n    url: `/og/docs/${segments.join('/')}`,\n  };\n}\n\nexport async function getLLMText(page: InferPageType<typeof source>) {\n  const processed = await page.data.getText('processed');\n\n  return `# ${page.data.title}\n\n${processed}`;\n}\n"
  },
  {
    "path": "apps/docs/lib/type-generator.ts",
    "content": "import 'server-only';\nimport { createFileSystemGeneratorCache, createGenerator, type Generator } from 'fumadocs-typescript';\n\nexport const typeGenerator: Generator = createGenerator({\n  cache: createFileSystemGeneratorCache('.next/fumadocs-typescript'),\n});\n"
  },
  {
    "path": "apps/docs/mdx-components.tsx",
    "content": "import defaultMdxComponents from 'fumadocs-ui/mdx';\nimport { AutoTypeTable } from 'fumadocs-typescript/ui';\nimport type { MDXComponents } from 'mdx/types';\nimport { AutoOptionSections } from '@/components/docs/auto-option-sections';\nimport { AutoRuntimeReference } from '@/components/docs/auto-runtime-reference';\nimport { typeGenerator } from '@/lib/type-generator';\n\nexport function getMDXComponents(components?: MDXComponents): MDXComponents {\n  return {\n    ...defaultMdxComponents,\n    AutoOptionSections,\n    AutoRuntimeReference,\n    AutoTypeTable: (props) => <AutoTypeTable {...props} generator={typeGenerator} />,\n    ...components,\n  };\n}\n"
  },
  {
    "path": "apps/docs/next.config.mjs",
    "content": "import { createMDX } from 'fumadocs-mdx/next';\n\nconst withMDX = createMDX();\n\n/** @type {import('next').NextConfig} */\nconst config = {\n  reactStrictMode: true,\n  async rewrites() {\n    return [\n      {\n        source: '/docs/:path*.mdx',\n        destination: '/llms.mdx/docs/:path*',\n      },\n    ];\n  },\n};\n\nexport default withMDX(config);\n"
  },
  {
    "path": "apps/docs/package.json",
    "content": "{\n  \"name\": \"react-native-boost-docs\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"build\": \"next build\",\n    \"dev\": \"next dev\",\n    \"start\": \"next start\",\n    \"typecheck\": \"pnpm run types:check\",\n    \"types:check\": \"fumadocs-mdx && next typegen && tsc --noEmit\",\n    \"postinstall\": \"fumadocs-mdx\"\n  },\n  \"dependencies\": {\n    \"fumadocs-core\": \"16.6.7\",\n    \"fumadocs-mdx\": \"14.2.8\",\n    \"fumadocs-typescript\": \"^5.1.4\",\n    \"fumadocs-ui\": \"16.6.7\",\n    \"lucide-react\": \"^0.575.0\",\n    \"next\": \"16.1.6\",\n    \"react\": \"^19.2.4\",\n    \"react-dom\": \"^19.2.4\",\n    \"ts-morph\": \"^27.0.2\",\n    \"tailwind-merge\": \"^3.5.0\"\n  },\n  \"devDependencies\": {\n    \"@tailwindcss/postcss\": \"^4.2.1\",\n    \"@types/mdx\": \"^2.0.13\",\n    \"@types/node\": \"^25.3.1\",\n    \"@types/react\": \"^19.2.14\",\n    \"@types/react-dom\": \"^19.2.3\",\n    \"postcss\": \"^8.5.6\",\n    \"tailwindcss\": \"^4.2.1\",\n    \"typescript\": \"^5.9.3\"\n  }\n}\n"
  },
  {
    "path": "apps/docs/postcss.config.mjs",
    "content": "const config = {\n  plugins: {\n    '@tailwindcss/postcss': {},\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "apps/docs/source.config.ts",
    "content": "import { defineConfig, defineDocs } from 'fumadocs-mdx/config';\nimport { metaSchema, pageSchema } from 'fumadocs-core/source/schema';\n\n// You can customise Zod schemas for frontmatter and `meta.json` here\n// see https://fumadocs.dev/docs/mdx/collections\nexport const docs = defineDocs({\n  dir: 'content/docs',\n  docs: {\n    schema: pageSchema,\n    postprocess: {\n      includeProcessedMarkdown: true,\n    },\n  },\n  meta: {\n    schema: metaSchema,\n  },\n});\n\nexport default defineConfig({\n  mdxOptions: {\n    // MDX options\n  },\n});\n"
  },
  {
    "path": "apps/docs/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"target\": \"ESNext\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"react-jsx\",\n    \"incremental\": true,\n    \"paths\": {\n      \"@/*\": [\"./*\"],\n      \"fumadocs-mdx:collections/*\": [\".source/*\"]\n    },\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ]\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\", \".next/dev/types/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "apps/example/.gitignore",
    "content": "# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files\n\n# dependencies\nnode_modules/\n\n# Expo\n.expo/\ndist/\nweb-build/\nexpo-env.d.ts\n\n# Native\n/ios\n/android\n*.orig.*\n*.jks\n*.p8\n*.p12\n*.key\n*.mobileprovision\n\n# Metro\n.metro-health-check*\n\n# debug\nnpm-debug.*\nyarn-debug.*\nyarn-error.*\n\n# macOS\n.DS_Store\n*.pem\n\n# local env files\n.env*.local\n\n# typescript\n*.tsbuildinfo\n"
  },
  {
    "path": "apps/example/app.json",
    "content": "{\n  \"expo\": {\n    \"name\": \"RN Boost\",\n    \"slug\": \"react-native-boost-example\",\n    \"version\": \"0.0.1\",\n    \"orientation\": \"portrait\",\n    \"icon\": \"./assets/icon.png\",\n    \"userInterfaceStyle\": \"light\",\n    \"newArchEnabled\": true,\n    \"splash\": {\n      \"image\": \"./assets/splash-icon.png\",\n      \"resizeMode\": \"contain\",\n      \"backgroundColor\": \"#ffffff\"\n    },\n    \"ios\": {\n      \"supportsTablet\": true,\n      \"bundleIdentifier\": \"com.kuatsu-mkrause.react-native-boost-example\"\n    },\n    \"android\": {\n      \"adaptiveIcon\": {\n        \"foregroundImage\": \"./assets/adaptive-icon.png\",\n        \"backgroundColor\": \"#ffffff\"\n      },\n      \"package\": \"com.kuatsumkrause.reactnativeboostexample\"\n    },\n    \"web\": {\n      \"favicon\": \"./assets/favicon.png\"\n    }\n  }\n}\n"
  },
  {
    "path": "apps/example/babel.config.js",
    "content": "module.exports = function (api) {\n  api.cache(true);\n  return {\n    presets: ['babel-preset-expo'],\n    plugins: [['react-native-boost/plugin', { ignores: ['node_modules/**', '../../node_modules/**'] }]],\n  };\n};\n"
  },
  {
    "path": "apps/example/index.ts",
    "content": "import { registerRootComponent } from 'expo';\n\nimport App from './src/app';\n\n// registerRootComponent calls AppRegistry.registerComponent('main', () => App);\n// It also ensures that whether you load the app in Expo Go or in a native build,\n// the environment is set up appropriately\nregisterRootComponent(App);\n"
  },
  {
    "path": "apps/example/package.json",
    "content": "{\n  \"name\": \"react-native-boost-example\",\n  \"version\": \"0.0.1\",\n  \"main\": \"index.ts\",\n  \"scripts\": {\n    \"start\": \"expo start\",\n    \"dev\": \"expo start\",\n    \"android\": \"rm -rf android && expo run:android\",\n    \"ios\": \"rm -rf ios && expo run:ios\",\n    \"web\": \"expo start --web\"\n  },\n  \"dependencies\": {\n    \"@expo/metro-runtime\": \"~55.0.6\",\n    \"expo\": \"^55.0.3\",\n    \"expo-status-bar\": \"~55.0.4\",\n    \"react\": \"19.2.0\",\n    \"react-dom\": \"19.2.0\",\n    \"react-native\": \"0.83.2\",\n    \"react-native-safe-area-context\": \"~5.6.0\",\n    \"react-native-boost\": \"workspace:*\",\n    \"react-native-time-to-render\": \"workspace:*\",\n    \"react-native-web\": \"^0.21.2\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.25.2\",\n    \"@types/react\": \"~19.2.14\",\n    \"typescript\": \"~5.9.3\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "apps/example/src/app.tsx",
    "content": "import { StatusBar } from 'expo-status-bar';\nimport { SafeAreaProvider } from 'react-native-safe-area-context';\nimport HomeScreen from './screens/home';\n\nexport default function App() {\n  return (\n    <SafeAreaProvider>\n      <StatusBar style=\"auto\" />\n      <HomeScreen />\n    </SafeAreaProvider>\n  );\n}\n"
  },
  {
    "path": "apps/example/src/components/measure-component.tsx",
    "content": "import React from 'react';\nimport { TimeToRenderView } from 'react-native-time-to-render';\nimport { Benchmark, BenchmarkStep } from '../types';\nimport { View } from 'react-native';\n\nexport interface BenchmarkProperties extends Benchmark {\n  onRenderTimeChange: (renderTime: number) => void;\n  step: BenchmarkStep;\n  markerName: string;\n}\nexport default function MeasureComponent(props: BenchmarkProperties) {\n  const optimizedViews = Array.from({ length: props.count }, (_, index) =>\n    React.cloneElement(props.optimizedComponent as React.ReactElement, { key: `optimized-${index}` })\n  );\n  const unoptimizedViews = Array.from({ length: props.count }, (_, index) =>\n    React.cloneElement(props.unoptimizedComponent as React.ReactElement, { key: `unoptimized-${index}` })\n  );\n\n  if (props.step === BenchmarkStep.Unoptimized) {\n    return (\n      <>\n        <TimeToRenderView\n          markerName={props.markerName}\n          onMarkerPainted={(event) => {\n            props.onRenderTimeChange(Math.round(event.nativeEvent.paintTime));\n          }}\n        />\n        <View style={{ display: 'none' }}>{unoptimizedViews}</View>\n      </>\n    );\n  }\n\n  return (\n    <>\n      <TimeToRenderView\n        markerName={props.markerName}\n        onMarkerPainted={(event) => {\n          props.onRenderTimeChange(Math.round(event.nativeEvent.paintTime));\n        }}\n      />\n      <View style={{ display: 'none' }}>{optimizedViews}</View>\n    </>\n  );\n}\n"
  },
  {
    "path": "apps/example/src/screens/home.tsx",
    "content": "import { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';\nimport { useMemo, useState } from 'react';\nimport { startMarker } from 'react-native-time-to-render';\nimport { Benchmark, BenchmarkStep } from '../types';\nimport MeasureComponent from '../components/measure-component';\nimport { getMarkerName } from '../utils/helpers';\n\nconst benchmarks = [\n  {\n    title: 'Text',\n    count: 10_000,\n    // @boost-ignore\n    unoptimizedComponent: <Text style={{ color: 'red' }}>Nice text</Text>,\n    optimizedComponent: <Text style={{ color: 'red' }}>Nice text</Text>,\n  },\n  {\n    title: 'View',\n    count: 10_000,\n    // @boost-ignore\n    unoptimizedComponent: <View style={{ borderWidth: 1, borderColor: 'red' }} />,\n    optimizedComponent: <View style={{ borderWidth: 1, borderColor: 'red' }} />,\n  },\n] satisfies Benchmark[];\n\nexport default function HomeScreen() {\n  const insets = useSafeAreaInsets();\n  const [currentStepIndex, setCurrentStepIndex] = useState(0);\n  const [runBenchmark, setRunBenchmark] = useState(false);\n  const [results, setResults] = useState<Record<number, { unoptimized: number | null; optimized: number | null }>>({});\n\n  const totalSteps = benchmarks.length * 2;\n  const currentBenchmark = useMemo(() => Math.floor(currentStepIndex / 2), [currentStepIndex]);\n  const currentStep = useMemo<BenchmarkStep>(\n    () => (currentStepIndex % 2 === 0 ? BenchmarkStep.Unoptimized : BenchmarkStep.Optimized),\n    [currentStepIndex]\n  );\n\n  const progress = useMemo<[number, number]>(() => {\n    return [currentStepIndex, totalSteps];\n  }, [currentStepIndex, totalSteps]);\n\n  const buttonTitle = useMemo(() => {\n    if (currentStepIndex === 0) {\n      return 'Start Benchmark';\n    }\n    if (currentStepIndex === totalSteps - 1) {\n      return 'Last Step';\n    }\n    return 'Next Step';\n  }, [currentStepIndex, totalSteps]);\n\n  const markerName = useMemo(\n    () => getMarkerName(benchmarks[currentBenchmark].title, currentStep),\n    [currentBenchmark, currentStep]\n  );\n\n  const resultRows = useMemo(() => {\n    return benchmarks.map((benchmark, index) => {\n      const value = results[index];\n      const unoptimized = value?.unoptimized ?? null;\n      const optimized = value?.optimized ?? null;\n      const gainPercent =\n        unoptimized === null || optimized === null || unoptimized === 0 ? null : (1 - optimized / unoptimized) * 100;\n      const gain = gainPercent === null ? 'N/A' : `${gainPercent.toFixed(2)}%`;\n\n      return {\n        title: benchmark.title,\n        unoptimizedText: unoptimized === null ? '--' : `${unoptimized}ms`,\n        optimizedText: optimized === null ? '--' : `${optimized}ms`,\n        gain,\n        gainPercent,\n      };\n    });\n  }, [results]);\n\n  const handleRun = (timestamp: number) => {\n    startMarker(markerName, timestamp);\n    setRunBenchmark(true);\n  };\n\n  const handleRenderTimeChange = (renderTime: number) => {\n    setRunBenchmark(false);\n\n    setResults((previousResults) => {\n      const baseResults = currentStepIndex === 0 ? {} : previousResults;\n      const previousBenchmarkResult = baseResults[currentBenchmark] ?? { unoptimized: null, optimized: null };\n\n      return {\n        ...baseResults,\n        [currentBenchmark]:\n          currentStep === BenchmarkStep.Unoptimized\n            ? { unoptimized: renderTime, optimized: null }\n            : { ...previousBenchmarkResult, optimized: renderTime },\n      };\n    });\n\n    setCurrentStepIndex((previousStepIndex) => (previousStepIndex + 1) % totalSteps);\n  };\n\n  return (\n    <SafeAreaView style={styles.container}>\n      <View style={styles.content}>\n        <View style={styles.headerCard}>\n          <Text style={styles.title}>React Native Boost Benchmark</Text>\n          <Text\n            style={\n              styles.subtitle\n            }>{`Step ${progress[0] + 1} / ${progress[1]}: ${benchmarks[currentBenchmark].title} (${currentStep})`}</Text>\n        </View>\n\n        <View style={styles.tableCard}>\n          <View style={[styles.tableRow, styles.tableHeader]}>\n            <Text style={[styles.tableCell, styles.benchmarkColumn, styles.tableHeaderText]}>Test</Text>\n            <Text style={[styles.tableCell, styles.metricColumn, styles.tableHeaderText]}>Unopt.</Text>\n            <Text style={[styles.tableCell, styles.metricColumn, styles.tableHeaderText]}>Opt.</Text>\n            <Text style={[styles.tableCell, styles.metricColumn, styles.tableHeaderText]}>Gain</Text>\n          </View>\n\n          {resultRows.map((row, index) => (\n            <View\n              key={row.title}\n              style={[\n                styles.tableRow,\n                index % 2 === 0 ? styles.tableStripeLight : styles.tableStripeDark,\n                index === currentBenchmark && styles.tableActiveRow,\n              ]}>\n              <Text style={[styles.tableCell, styles.benchmarkColumn, styles.benchmarkText]}>{row.title}</Text>\n              <Text style={[styles.tableCell, styles.metricColumn, styles.metricText]}>{row.unoptimizedText}</Text>\n              <Text style={[styles.tableCell, styles.metricColumn, styles.metricText]}>{row.optimizedText}</Text>\n              <Text\n                style={[\n                  styles.tableCell,\n                  styles.metricColumn,\n                  styles.metricText,\n                  row.gainPercent === null\n                    ? styles.gainNeutral\n                    : row.gainPercent >= 0\n                      ? styles.gainPositive\n                      : styles.gainNegative,\n                ]}>\n                {row.gain}\n              </Text>\n            </View>\n          ))}\n        </View>\n      </View>\n\n      <View style={[styles.footer, { bottom: insets.bottom + 16 }]}>\n        <Pressable\n          accessibilityRole=\"button\"\n          onPress={(event) => handleRun(event.nativeEvent.timestamp)}\n          style={({ pressed }) => [styles.runButton, pressed && styles.runButtonPressed]}>\n          <Text style={styles.runButtonText}>{buttonTitle}</Text>\n        </Pressable>\n      </View>\n\n      {runBenchmark && (\n        <MeasureComponent\n          key={markerName}\n          onRenderTimeChange={handleRenderTimeChange}\n          step={currentStep}\n          {...benchmarks[currentBenchmark]}\n          markerName={markerName}\n        />\n      )}\n    </SafeAreaView>\n  );\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    backgroundColor: '#fff8ef',\n    alignItems: 'center',\n    justifyContent: 'center',\n    paddingHorizontal: 16,\n  },\n  content: {\n    width: '100%',\n    maxWidth: 640,\n  },\n  headerCard: {\n    backgroundColor: '#ffffff',\n    borderRadius: 14,\n    borderWidth: 1,\n    borderColor: '#f1d4a3',\n    paddingHorizontal: 16,\n    paddingVertical: 14,\n    marginBottom: 12,\n  },\n  title: {\n    fontSize: 20,\n    fontWeight: '700',\n    color: '#5e3c0c',\n  },\n  subtitle: {\n    marginTop: 4,\n    fontSize: 13,\n    color: '#7a4b00',\n    fontWeight: '600',\n    textTransform: 'capitalize',\n  },\n  runButton: {\n    backgroundColor: '#ff9800',\n    borderRadius: 12,\n    paddingVertical: 12,\n    alignItems: 'center',\n    width: '100%',\n    maxWidth: 640,\n  },\n  runButtonPressed: {\n    transform: [{ scale: 0.985 }],\n    opacity: 0.95,\n  },\n  runButtonText: {\n    color: '#2f1d00',\n    fontSize: 15,\n    fontWeight: '700',\n  },\n  tableCard: {\n    borderRadius: 14,\n    overflow: 'hidden',\n    borderWidth: 1,\n    borderColor: '#f1d4a3',\n    backgroundColor: '#ffffff',\n  },\n  footer: {\n    position: 'absolute',\n    left: 16,\n    right: 16,\n    alignItems: 'center',\n  },\n  tableRow: {\n    flexDirection: 'row',\n    alignItems: 'center',\n    minHeight: 42,\n  },\n  tableHeader: {\n    backgroundColor: '#fff0d6',\n    borderBottomWidth: 1,\n    borderBottomColor: '#f1d4a3',\n  },\n  tableHeaderText: {\n    fontSize: 12,\n    fontWeight: '700',\n    color: '#7a4b00',\n  },\n  tableStripeLight: {\n    backgroundColor: '#ffffff',\n  },\n  tableStripeDark: {\n    backgroundColor: '#fffaf2',\n  },\n  tableActiveRow: {\n    backgroundColor: '#ffe8c2',\n  },\n  tableCell: {\n    paddingHorizontal: 10,\n    paddingVertical: 8,\n  },\n  benchmarkColumn: {\n    flex: 1.4,\n  },\n  metricColumn: {\n    flex: 1,\n    alignItems: 'flex-end',\n  },\n  benchmarkText: {\n    fontSize: 14,\n    color: '#4d3311',\n    fontWeight: '600',\n  },\n  metricText: {\n    fontSize: 13,\n    color: '#6e4c1d',\n    textAlign: 'right',\n  },\n  gainPositive: {\n    color: '#0d7a3b',\n    fontWeight: '700',\n  },\n  gainNegative: {\n    color: '#b42318',\n    fontWeight: '700',\n  },\n  gainNeutral: {\n    color: '#6e4c1d',\n  },\n});\n"
  },
  {
    "path": "apps/example/src/types/index.ts",
    "content": "export interface Benchmark {\n  title: string;\n  count: number;\n  optimizedComponent: React.ReactNode;\n  unoptimizedComponent: React.ReactNode;\n}\n\nexport enum BenchmarkStep {\n  Unoptimized = 'unoptimized',\n  Optimized = 'optimized',\n}\n"
  },
  {
    "path": "apps/example/src/utils/helpers.ts",
    "content": "import { BenchmarkStep } from '../types';\n\nexport const getMarkerName = (title: string, step: BenchmarkStep) => `${title}-${step}`;\n"
  },
  {
    "path": "apps/example/tsconfig.json",
    "content": "{\n  \"extends\": \"expo/tsconfig.base\",\n  \"compilerOptions\": {\n    \"strict\": true\n  }\n}\n"
  },
  {
    "path": "commitlint.config.mjs",
    "content": "export default { extends: ['@commitlint/config-conventional'] };\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-native-boost-monorepo\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"build\": \"pnpm -r --parallel --if-present run build\",\n    \"typecheck\": \"pnpm -r --parallel --if-present run typecheck\",\n    \"test\": \"pnpm -r --parallel --if-present run test\",\n    \"example\": \"pnpm --filter react-native-boost-example run\",\n    \"dev\": \"pnpm --parallel --stream --filter react-native-boost --filter react-native-boost-example run dev\",\n    \"docs\": \"pnpm --filter react-native-boost-docs run\",\n    \"package\": \"pnpm --filter react-native-boost run\",\n    \"lint\": \"oxlint .\",\n    \"format\": \"oxfmt .\",\n    \"prepare\": \"husky\"\n  },\n  \"packageManager\": \"pnpm@10.28.2\",\n  \"devDependencies\": {\n    \"@commitlint/cli\": \"^20.4.2\",\n    \"@commitlint/config-conventional\": \"^20.4.2\",\n    \"husky\": \"^9.1.7\",\n    \"lint-staged\": \"^16.2.7\",\n    \"oxfmt\": \"^0.35.0\",\n    \"oxlint\": \"^1.50.0\",\n    \"typescript\": \"~5.9.3\"\n  }\n}\n"
  },
  {
    "path": "packages/react-native-boost/.gitignore",
    "content": "/dist\n/plugin.*\n/runtime.*\n"
  },
  {
    "path": "packages/react-native-boost/CHANGELOG.md",
    "content": "# Changelog\n\n# [1.1.0](https://github.com/kuatsu/react-native-boost/compare/v1.0.0...v1.1.0) (2026-03-18)\n\n\n### Features\n\n* add `[@boost-force](https://github.com/boost-force)` decorator to enforce optimization ([eb7357c](https://github.com/kuatsu/react-native-boost/commit/eb7357c1fc296aa62658dcc010254c6f1b08e986))\n\n# [1.0.0](https://github.com/kuatsu/react-native-boost/compare/v0.6.2...v1.0.0) (2026-02-27)\n\n\n### Bug Fixes\n\n* **example:** fix benchmark state machine ([48bb689](https://github.com/kuatsu/react-native-boost/commit/48bb6891d27c6cd7971a45f36d8eda9278b4a54f))\n* **example:** fix node_modules being optimized ([9558680](https://github.com/kuatsu/react-native-boost/commit/9558680b6ce0221a367ef64f47cc0ca4dc1bbb67))\n* **view:** bail on unknown ancestors by default and add opt-in flag ([#17](https://github.com/kuatsu/react-native-boost/issues/17)) ([bf24fbd](https://github.com/kuatsu/react-native-boost/commit/bf24fbd3d241b0a38d91c5b2ff736c610b7e4132))\n\n\n### Features\n\n* bail on less props ([1bb7758](https://github.com/kuatsu/react-native-boost/commit/1bb77587185fd6ab5d8d7cb238b5c4dea46553ff))\n* **example:** prettier benchmark app ([51e47fd](https://github.com/kuatsu/react-native-boost/commit/51e47fd321507e9d461fd38951cbfaf058ececf2))\n* improve logging ([3c9fef4](https://github.com/kuatsu/react-native-boost/commit/3c9fef482c7f20056779cc02bf6e2ea010c59f69))\n* skip optimizing Text inside Expo Router Link with `asChild` ([#16](https://github.com/kuatsu/react-native-boost/issues/16)) ([ddc606d](https://github.com/kuatsu/react-native-boost/commit/ddc606d780863b199ab9bf3e6079fb00a366e066))\n* **text:** bail less, statically handle userSelect, improve spread prop ([5e93ae5](https://github.com/kuatsu/react-native-boost/commit/5e93ae545d63b9258f27f6831adfa6bba899a124))\n\n## [0.6.2](https://github.com/kuatsu/react-native-boost/compare/v0.6.1...v0.6.2) (2025-06-11)\n\n\n### Bug Fixes\n\n* fixed react-native 0.79 style flattening ([7b7a5a4](https://github.com/kuatsu/react-native-boost/commit/7b7a5a4def44676382014dfc26f9df1bc989f81e)), closes [#10](https://github.com/kuatsu/react-native-boost/issues/10)\n\n\n### Reverts\n\n* remove unused package export ([973084e](https://github.com/kuatsu/react-native-boost/commit/973084ee58f9473ffff23a62051d6d99da641456))\n\n## [0.6.1](https://github.com/kuatsu/react-native-boost/compare/v0.6.0...v0.6.1) (2025-06-11)\n\n\n### Bug Fixes\n\n* fixed type errors in tests ([59e3925](https://github.com/kuatsu/react-native-boost/commit/59e392562331e7e150caad431adc5569f9b7a88d))\n\n# [0.6.0](https://github.com/kuatsu/react-native-boost/compare/v0.5.7...v0.6.0) (2025-06-11)\n\n\n### Bug Fixes\n\n* fixed bundling issues on web ([efd9933](https://github.com/kuatsu/react-native-boost/commit/efd9933604a6053d5b64cb9faec714c0e3987410))\n\n## [0.5.7](https://github.com/kuatsu/react-native-boost/compare/v0.5.6...v0.5.7) (2025-06-11)\n\n## [0.5.6](https://github.com/kuatsu/react-native-boost/compare/v0.5.5...v0.5.6) (2025-02-26)\n\n## [0.5.5](https://github.com/kuatsu/react-native-boost/compare/v0.5.4...v0.5.5) (2025-02-25)\n\n\n### Bug Fixes\n\n* don't optimize views with indirect text ancestor (custom component) ([a8d5ced](https://github.com/kuatsu/react-native-boost/commit/a8d5ced7ee4047e9094571257389a2680bd6214e))\n\n## [0.5.4](https://github.com/kuatsu/react-native-boost/compare/v0.5.3...v0.5.4) (2025-02-25)\n\n\n### Bug Fixes\n\n* fixed optimizing aliased imports ([2eee88b](https://github.com/kuatsu/react-native-boost/commit/2eee88bce014744a13f75ea08d386fba61c5be7c))\n\n## [0.5.3](https://github.com/kuatsu/react-native-boost/compare/v0.5.2...v0.5.3) (2025-02-24)\n\n\n### Bug Fixes\n\n* fixed react-native-web bundling ([56ab1b3](https://github.com/kuatsu/react-native-boost/commit/56ab1b3b0985f413691edd1ce3de9a02593f7ff8))\n\n## [0.5.2](https://github.com/kuatsu/react-native-boost/compare/v0.5.1...v0.5.2) (2025-02-24)\n\n\n### Bug Fixes\n\n* allow text string children from variables ([622a201](https://github.com/kuatsu/react-native-boost/commit/622a2011f751f9f28cdda19bf7ea676f537b1fbf))\n\n## [0.5.1](https://github.com/kuatsu/react-native-boost/compare/v0.5.0...v0.5.1) (2025-02-24)\n\n\n### Bug Fixes\n\n* fixed text style flattening ([83ef501](https://github.com/kuatsu/react-native-boost/commit/83ef501aeaa30c7dc8a59a78e74c7a700fc4b4a3))\n\n# [0.5.0](https://github.com/kuatsu/react-native-boost/compare/v0.4.1...v0.5.0) (2025-02-24)\n\n\n### Features\n\n* **example:** allow running benchmarks in production ([c21ada0](https://github.com/kuatsu/react-native-boost/commit/c21ada06e1d7ae79fb77512b272da79a15f9fa32))\n* optimize components with accessibility props ([d71d027](https://github.com/kuatsu/react-native-boost/commit/d71d027ec613b8baa96e22f155cec317e4c54e13))\n\n## [0.4.1](https://github.com/kuatsu/react-native-boost/compare/v0.4.0...v0.4.1) (2025-02-24)\n\n# [0.4.0](https://github.com/kuatsu/react-native-boost/compare/v0.3.0...v0.4.0) (2025-02-24)\n\n\n### Bug Fixes\n\n* **docs:** fixed broken link ([a3dde5d](https://github.com/kuatsu/react-native-boost/commit/a3dde5d3c400d525028e18f4cdbcf88fcc373029))\n\n\n### Features\n\n* added `<View />` optimization support ([46ca834](https://github.com/kuatsu/react-native-boost/commit/46ca834b9d62f5a3abfca7061993f82cdae48deb))\n* added `ignores` config option ([7d58a9f](https://github.com/kuatsu/react-native-boost/commit/7d58a9f8db759d3babf747504645b9a4d6ee61bd))\n* **docs:** added documentation app w/ styled homepage ([9ce312b](https://github.com/kuatsu/react-native-boost/commit/9ce312b6b6dae38a9ccd3574e72806515a86fa21))\n\n# [0.3.0](https://github.com/kuatsu/react-native-boost/compare/v0.2.0...v0.3.0) (2025-02-24)\n\n\n### Features\n\n* fix `numberOfLines` prop at build time ([58c2993](https://github.com/kuatsu/react-native-boost/commit/58c299393abaf3a9fcbb2ca933cfa02e4bf08fb3))\n\n\n### Reverts\n\n* **example:** removed console.log ([4facb6e](https://github.com/kuatsu/react-native-boost/commit/4facb6ed5c773e9b2fef28779d288b43b56612dc))\n* removed wrong rollup config ([b4b69c0](https://github.com/kuatsu/react-native-boost/commit/b4b69c01c90a5e11659569a22c5c23805f9df753))\n\n# [0.2.0](https://github.com/kuatsu/react-native-boost/compare/v0.1.0...v0.2.0) (2025-02-23)\n\n\n### Features\n\n* **example:** added benchmark ([6e56b2a](https://github.com/kuatsu/react-native-boost/commit/6e56b2aaa5c9510d8be0a4898e86382ee637b0c3))\n* improved logging & [@boost-ignore](https://github.com/boost-ignore) decorator handling ([6f11cbb](https://github.com/kuatsu/react-native-boost/commit/6f11cbb5b1480b10cd20d2544fa334da1474f44b))\n\n# [0.1.0](https://github.com/kuatsu/react-native-boost/compare/v0.0.5...v0.1.0) (2025-02-23)\n\n\n### Bug Fixes\n\n* fixed `main` entry ([2727e69](https://github.com/kuatsu/react-native-boost/commit/2727e6965e2d6f7d5fbe308bf5ff5d4c63b8c06d))\n\n\n### Features\n\n* added ignore decorator comment ([3f7d0dc](https://github.com/kuatsu/react-native-boost/commit/3f7d0dc4a67623fee41f473ca588d6901c5b3e97))\n* allow text style prop ([e916da5](https://github.com/kuatsu/react-native-boost/commit/e916da5f6bfee0d5480b660fab70c6e0a67deace))\n\n## [0.0.5](https://github.com/kuatsu/react-native-boost/compare/v0.0.4...v0.0.5) (2025-02-23)\n\n\n### Features\n\n* minify bundle ([8fd6687](https://github.com/kuatsu/react-native-boost/commit/8fd66878599af4313d428687557bac22a832fd78))\n\n## [0.0.4](https://github.com/kuatsu/react-native-boost/compare/v0.0.3...v0.0.4) (2025-02-23)\n\n\n### Bug Fixes\n\n* re-add README to npm ([4f39ab5](https://github.com/kuatsu/react-native-boost/commit/4f39ab5162ab412a330aa60f0efa63604f94ec23))\n\n## 0.0.3 (2025-02-23)\n\n\n### Bug Fixes\n\n* added prettier ([fb73927](https://github.com/kuatsu/react-native-boost/commit/fb73927f2ca613709a2eb181903f52e39903159a))\n* **ci:** fixed release workflow ([42d1ce1](https://github.com/kuatsu/react-native-boost/commit/42d1ce1a0691831178a7ef2db78d0258ea4826b3))\n* fixed husky hooks ([d0540c9](https://github.com/kuatsu/react-native-boost/commit/d0540c94007e9f13ecd70a22b572084afe58ee0d))\n* fixed prettier ([b78e6b4](https://github.com/kuatsu/react-native-boost/commit/b78e6b4c47d0321fa2fa303d5197763aadd4f272))\n* fixed tsconfig ([29abcfc](https://github.com/kuatsu/react-native-boost/commit/29abcfcb48b8194d34bdf34af2db4a85fa6a15c5))\n* package resolution fix ([bb83508](https://github.com/kuatsu/react-native-boost/commit/bb8350860f2ac952e9fd00702c55357fef013438))\n* try to fix lockfile ([16bf4e4](https://github.com/kuatsu/react-native-boost/commit/16bf4e4d9a7bd00c28897ab6ef74377ad307cc00))\n\n\n### Features\n\n* **example:** added android implementation of benchmarking module ([1c5c4fb](https://github.com/kuatsu/react-native-boost/commit/1c5c4fb2d7165375dffa52f8b6ab0a338e7cdaf1))\n* **example:** initialized example app ([19608a9](https://github.com/kuatsu/react-native-boost/commit/19608a94f4e45cf39c13901e472f46181a95115b))\n* **example:** ios implementation of benchmarking turbo module ([cd54789](https://github.com/kuatsu/react-native-boost/commit/cd547896b58046a34499b9045c407d4dcf6a5434))\n* **example:** scaffolded new turbo module for benchmarks ([187650e](https://github.com/kuatsu/react-native-boost/commit/187650e2d5dc0f4e77520568c4da15c6cd4d602f))\n* improved plugin import ([0e97f1e](https://github.com/kuatsu/react-native-boost/commit/0e97f1eea615a2516066fa6a94c9b3685e6576ae))\n* initial commit ([3d3c0ad](https://github.com/kuatsu/react-native-boost/commit/3d3c0adcdcc35e3f641312f89292ee72b52142dc))\n* optional logging ([c4ad283](https://github.com/kuatsu/react-native-boost/commit/c4ad283db3e7af3f116ba66c90897f2f94362f97))\n* try to resolve spread attribute ([2aeb5a1](https://github.com/kuatsu/react-native-boost/commit/2aeb5a1d92f4600f87f6d638ae34db804640ae22))\n"
  },
  {
    "path": "packages/react-native-boost/LICENSE",
    "content": "MIT License\n\nCopyright (c) Kuatsu App Agency\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "packages/react-native-boost/package.json",
    "content": "{\n  \"name\": \"react-native-boost\",\n  \"description\": \"🚀 Boost your React Native app's performance with a single line of code\",\n  \"version\": \"1.1.0\",\n  \"main\": \"dist/index.js\",\n  \"module\": \"dist/esm/index.mjs\",\n  \"types\": \"dist/index.d.ts\",\n  \"exports\": {\n    \"./package.json\": \"./package.json\",\n    \"./runtime\": {\n      \"import\": {\n        \"types\": \"./dist/runtime/index.d.ts\",\n        \"default\": \"./runtime.mjs\"\n      },\n      \"default\": \"./runtime.js\"\n    },\n    \"./plugin\": {\n      \"import\": {\n        \"types\": \"./dist/plugin/index.d.ts\",\n        \"default\": \"./plugin.mjs\"\n      },\n      \"default\": \"./plugin.js\"\n    }\n  },\n  \"typesVersions\": {\n    \"*\": {\n      \"runtime\": [\n        \"dist/runtime/index.d.ts\"\n      ],\n      \"plugin\": [\n        \"dist/plugin/index.d.ts\"\n      ]\n    }\n  },\n  \"keywords\": [\n    \"react-native\",\n    \"ios\",\n    \"android\",\n    \"performance\",\n    \"optimization\",\n    \"bundle\",\n    \"optimize\"\n  ],\n  \"scripts\": {\n    \"clean\": \"rm -rf dist\",\n    \"build\": \"pnpm clean && rollup -c\",\n    \"build:watch\": \"rollup -c -w\",\n    \"dev\": \"pnpm build:watch\",\n    \"test\": \"vitest\",\n    \"typecheck\": \"tsc --noEmit\",\n    \"release\": \"release-it\",\n    \"prepack\": \"cp ../../README.md ./README.md\",\n    \"postpack\": \"rm ./README.md\"\n  },\n  \"files\": [\n    \"src\",\n    \"dist\",\n    \"runtime.d.ts\",\n    \"runtime.js\",\n    \"runtime.mjs\",\n    \"plugin.d.ts\",\n    \"plugin.js\",\n    \"plugin.mjs\",\n    \"!**/__tests__\",\n    \"!**/__fixtures__\",\n    \"!**/__mocks__\",\n    \"!**/.*\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/kuatsu/react-native-boost.git\"\n  },\n  \"author\": \"Kuatsu App Agency <hello@kuatsu.de>\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/kuatsu/react-native-boost/issues\"\n  },\n  \"homepage\": \"https://github.com/kuatsu/react-native-boost#readme\",\n  \"packageManager\": \"pnpm@10.28.2\",\n  \"publishConfig\": {\n    \"registry\": \"https://registry.npmjs.org\"\n  },\n  \"dependencies\": {\n    \"@babel/core\": \"^7.25.0\",\n    \"@babel/helper-module-imports\": \"^7.25.0\",\n    \"@babel/helper-plugin-utils\": \"^7.25.0\",\n    \"minimatch\": \"^10.0.1\"\n  },\n  \"devDependencies\": {\n    \"@babel/plugin-syntax-jsx\": \"^7.25.0\",\n    \"@babel/preset-typescript\": \"^7.25.0\",\n    \"@release-it/conventional-changelog\": \"^10.0.0\",\n    \"@rollup/plugin-alias\": \"^6.0.0\",\n    \"@rollup/plugin-node-resolve\": \"^16.0.0\",\n    \"@rollup/plugin-replace\": \"^6.0.2\",\n    \"@rollup/plugin-typescript\": \"^12.1.2\",\n    \"@types/babel__helper-module-imports\": \"^7.0.0\",\n    \"@types/babel__helper-plugin-utils\": \"^7.0.0\",\n    \"@types/node\": \"^24\",\n    \"babel-plugin-tester\": \"^12.0.0\",\n    \"esbuild-node-externals\": \"^1.18.0\",\n    \"react-native\": \"0.83.2\",\n    \"release-it\": \"^19.2.4\",\n    \"rollup\": \"^4.34.8\",\n    \"rollup-plugin-dts\": \"^6.1.1\",\n    \"rollup-plugin-esbuild\": \"^6.2.0\",\n    \"typescript\": \"~5.9.3\",\n    \"vitest\": \"^4.0.18\"\n  },\n  \"peerDependencies\": {\n    \"react\": \"*\",\n    \"react-native\": \">=0.83.0\"\n  },\n  \"release-it\": {\n    \"git\": {\n      \"commitMessage\": \"chore: release ${version}\",\n      \"tagName\": \"v${version}\"\n    },\n    \"npm\": {\n      \"publish\": true,\n      \"skipChecks\": true,\n      \"versionArgs\": [\n        \"--workspaces-update=false\"\n      ]\n    },\n    \"github\": {\n      \"release\": true\n    },\n    \"plugins\": {\n      \"@release-it/conventional-changelog\": {\n        \"preset\": {\n          \"name\": \"angular\"\n        },\n        \"infile\": \"CHANGELOG.md\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/react-native-boost/rollup.config.mjs",
    "content": "import path from 'path';\nimport resolve from '@rollup/plugin-node-resolve';\nimport replace from '@rollup/plugin-replace';\nimport esbuild from 'rollup-plugin-esbuild';\nimport dts from 'rollup-plugin-dts';\nimport fs from 'fs/promises';\n\nconst extensions = ['.js', '.ts', '.tsx'];\n\n// Treat all non-relative and non-absolute imports as external\nconst external = (id) => !id.startsWith('.') && !path.isAbsolute(id);\n\n// Custom plugin to generate entry point files\nfunction generateEntryPoints() {\n  return {\n    name: 'generate-entry-points',\n    writeBundle: async () => {\n      // Define entry point configurations\n      const entryPoints = [\n        {\n          name: 'runtime',\n          description: 'runtime',\n          paths: {\n            cjs: './dist/runtime/index',\n            esm: './dist/runtime/esm/index.mjs',\n            dts: './dist/runtime/index',\n          },\n        },\n        {\n          name: 'plugin',\n          description: 'plugin',\n          paths: {\n            cjs: './dist/plugin/index',\n            esm: './dist/plugin/esm/index.mjs',\n            dts: './dist/plugin/index',\n          },\n        },\n      ];\n\n      // Helper function to create entry point files\n      const createEntryPoint = async (config, format) => {\n        const { name, description, paths } = config;\n\n        switch (format) {\n          case 'cjs':\n            await fs.writeFile(`${name}.js`, `module.exports = require('${paths.cjs}');\\n`);\n            break;\n          case 'esm':\n            await fs.writeFile(`${name}.mjs`, `export * from '${paths.esm}';\\n`);\n            break;\n          case 'dts':\n            await fs.writeFile(`${name}.d.ts`, `export * from '${paths.dts}';\\n`);\n            break;\n        }\n      };\n\n      // Generate all entry points\n      for (const config of entryPoints) {\n        await createEntryPoint(config, 'cjs');\n        await createEntryPoint(config, 'esm');\n        await createEntryPoint(config, 'dts');\n      }\n    },\n  };\n}\n\nconst commonPlugins = [\n  resolve({ extensions }),\n  replace({\n    'preventAssignment': true,\n    'import.meta.env.MODE': JSON.stringify(process.env.NODE_ENV || 'development'),\n  }),\n  esbuild({\n    target: 'es2018',\n    tsconfig: 'tsconfig.json',\n  }),\n];\n\n// Add the entry point generator to the last build step\nconst lastBuildPlugins = [...commonPlugins, generateEntryPoints()];\n\nexport default [\n  // Runtime Code Build (CommonJS and ESM)\n  {\n    input: 'src/runtime/index.ts',\n    external,\n    plugins: commonPlugins,\n    output: [\n      { file: 'dist/runtime/index.js', format: 'cjs', sourcemap: true },\n      { file: 'dist/runtime/esm/index.mjs', format: 'esm', sourcemap: true },\n    ],\n  },\n  {\n    input: 'src/runtime/index.web.ts',\n    external,\n    plugins: commonPlugins,\n    output: [\n      { file: 'dist/runtime/index.web.js', format: 'cjs', sourcemap: true },\n      { file: 'dist/runtime/esm/index.web.mjs', format: 'esm', sourcemap: true },\n    ],\n  },\n  // Plugin Code Build (CommonJS and ESM)\n  {\n    input: 'src/plugin/index.ts',\n    external,\n    plugins: commonPlugins,\n    output: [\n      { file: 'dist/plugin/index.js', format: 'cjs', sourcemap: true },\n      { file: 'dist/plugin/esm/index.mjs', format: 'esm', sourcemap: true },\n    ],\n  },\n  // Runtime Type Declarations Bundle (creates a single file)\n  {\n    input: 'src/runtime/index.ts',\n    plugins: [dts()],\n    external,\n    output: { file: 'dist/runtime/index.d.ts', format: 'esm' },\n  },\n  {\n    input: 'src/runtime/index.web.ts',\n    plugins: [dts()],\n    external,\n    output: { file: 'dist/runtime/index.web.d.ts', format: 'esm' },\n  },\n  // Plugin Type Declarations Bundle (creates a single file)\n  {\n    input: 'src/plugin/index.ts',\n    plugins: [dts(), generateEntryPoints()],\n    external,\n    output: { file: 'dist/plugin/index.d.ts', format: 'esm' },\n  },\n];\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/index.ts",
    "content": "import { declare } from '@babel/helper-plugin-utils';\nimport { textOptimizer } from './optimizers/text';\nimport { PluginLogger, PluginOptions } from './types';\nimport { createLogger } from './utils/logger';\nimport { viewOptimizer } from './optimizers/view';\nimport { isIgnoredFile } from './utils/common';\n\nexport type { PluginOptimizationOptions, PluginOptions } from './types';\n\ntype PluginState = {\n  opts?: PluginOptions;\n  __reactNativeBoostLogger?: PluginLogger;\n};\n\nexport default declare((api) => {\n  api.assertVersion(7);\n\n  return {\n    name: 'react-native-boost',\n    visitor: {\n      JSXOpeningElement(path, state) {\n        const pluginState = state as PluginState;\n        const options = (pluginState.opts ?? {}) as PluginOptions;\n        const logger = getOrCreateLogger(pluginState, options);\n\n        if (isIgnoredFile(path, options.ignores ?? [])) return;\n        if (options.optimizations?.text !== false) textOptimizer(path, logger);\n        if (options.optimizations?.view !== false) viewOptimizer(path, logger, options);\n      },\n    },\n  };\n});\n\nfunction getOrCreateLogger(state: PluginState, options: PluginOptions): PluginLogger {\n  if (state.__reactNativeBoostLogger) {\n    return state.__reactNativeBoostLogger;\n  }\n\n  state.__reactNativeBoostLogger = createLogger({\n    verbose: options.verbose === true,\n    silent: options.silent === true,\n  });\n\n  return state.__reactNativeBoostLogger;\n}\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/basic/code.js",
    "content": "import { Text } from 'react-native';\n<Text />;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/basic/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\n<_NativeText allowFontScaling={true} ellipsizeMode={'tail'} />;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/complex-example/code.js",
    "content": "import { Text } from 'react-native';\n\nexport default function TextBenchmark(props) {\n  const optimizedViews = Array.from({ length: props.count }, (_, index) => <Text key={index}>Nice text</Text>);\n  const unoptimizedViews = Array.from({ length: props.count }, (_, index) => (\n    // @boost-ignore\n    <Text key={index}>Nice text</Text>\n  ));\n\n  if (props.status === 'pending') return <Text>Pending...</Text>;\n\n  return (\n    <View>\n      {optimizedViews}\n      {unoptimizedViews}\n    </View>\n  );\n}\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/complex-example/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\nexport default function TextBenchmark(props) {\n  const optimizedViews = Array.from(\n    {\n      length: props.count,\n    },\n    (_, index) => (\n      <_NativeText key={index} allowFontScaling={true} ellipsizeMode={'tail'}>\n        Nice text\n      </_NativeText>\n    )\n  );\n  const unoptimizedViews = Array.from(\n    {\n      length: props.count,\n    },\n    (_, index) => (\n      // @boost-ignore\n      <Text key={index}>Nice text</Text>\n    )\n  );\n  if (props.status === 'pending')\n    return (\n      <_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n        Pending...\n      </_NativeText>\n    );\n  return (\n    <View>\n      {optimizedViews}\n      {unoptimizedViews}\n    </View>\n  );\n}\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/default-props/code.js",
    "content": "import { Text } from 'react-native';\n\nconst someFunction = () => ({});\n\n<Text>Hello</Text>;\n<Text allowFontScaling={false}>No Scaling</Text>;\nconst unknownProps = someFunction();\n<Text {...unknownProps}>Unknown</Text>;\nconst partialProps = { color: 'blue', ellipsizeMode: 'clip' };\n<Text {...partialProps}>Partial props</Text>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/default-props/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\nconst someFunction = () => ({});\n<_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n  Hello\n</_NativeText>;\n<_NativeText allowFontScaling={false} ellipsizeMode={'tail'}>\n  No Scaling\n</_NativeText>;\nconst unknownProps = someFunction();\n<Text {...unknownProps}>Unknown</Text>;\nconst partialProps = {\n  color: 'blue',\n  ellipsizeMode: 'clip',\n};\n<_NativeText {...partialProps} allowFontScaling={true}>\n  Partial props\n</_NativeText>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/expo-router-link-alias-as-child/code.js",
    "content": "import { Text } from 'react-native';\nimport { Link as RouterLink } from 'expo-router';\n\n<>\n  <Text>This should be optimized</Text>\n  <RouterLink asChild>\n    <Text>This should NOT be optimized due to aliased Link asChild</Text>\n  </RouterLink>\n  <RouterLink>\n    <Text>This should be optimized (aliased Link without asChild)</Text>\n  </RouterLink>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/expo-router-link-alias-as-child/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\nimport { Link as RouterLink } from 'expo-router';\n<>\n  <_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n    This should be optimized\n  </_NativeText>\n  <RouterLink asChild>\n    <Text>This should NOT be optimized due to aliased Link asChild</Text>\n  </RouterLink>\n  <RouterLink>\n    <_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n      This should be optimized (aliased Link without asChild)\n    </_NativeText>\n  </RouterLink>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/expo-router-link-as-child/code.js",
    "content": "import { Text } from 'react-native';\nimport { Link } from 'expo-router';\n\n<>\n  <Text>This should be optimized</Text>\n  <Link asChild>\n    <Text>This should NOT be optimized due to Link asChild</Text>\n  </Link>\n  <Link>\n    <Text>This should be optimized (Link without asChild)</Text>\n  </Link>\n  <Link href=\"/home\" asChild>\n    <Text>This should NOT be optimized (Link with href and asChild)</Text>\n  </Link>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/expo-router-link-as-child/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\nimport { Link } from 'expo-router';\n<>\n  <_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n    This should be optimized\n  </_NativeText>\n  <Link asChild>\n    <Text>This should NOT be optimized due to Link asChild</Text>\n  </Link>\n  <Link>\n    <_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n      This should be optimized (Link without asChild)\n    </_NativeText>\n  </Link>\n  <Link href=\"/home\" asChild>\n    <Text>This should NOT be optimized (Link with href and asChild)</Text>\n  </Link>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/expo-router-link-as-child-false-static/code.js",
    "content": "import { Text } from 'react-native';\nimport { Link } from 'expo-router';\n\n<>\n  <Link asChild={false}>\n    <Text>This should be optimized because asChild is false</Text>\n  </Link>\n  <Link asChild={true}>\n    <Text>This should NOT be optimized because asChild is true</Text>\n  </Link>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/expo-router-link-as-child-false-static/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\nimport { Link } from 'expo-router';\n<>\n  <Link asChild={false}>\n    <_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n      This should be optimized because asChild is false\n    </_NativeText>\n  </Link>\n  <Link asChild={true}>\n    <Text>This should NOT be optimized because asChild is true</Text>\n  </Link>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/expo-router-link-as-child-nested-view/code.js",
    "content": "import { Text, View } from 'react-native';\nimport { Link } from 'expo-router';\n\n<Link asChild href=\"/home\">\n  <View>\n    <Text>This should be optimized because View is the direct child</Text>\n  </View>\n</Link>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/expo-router-link-as-child-nested-view/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text, View } from 'react-native';\nimport { Link } from 'expo-router';\n<Link asChild href=\"/home\">\n  <View>\n    <_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n      This should be optimized because View is the direct child\n    </_NativeText>\n  </View>\n</Link>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/expo-router-link-namespace-as-child/code.js",
    "content": "import { Text } from 'react-native';\nimport * as ExpoRouter from 'expo-router';\n\n<>\n  <ExpoRouter.Link asChild href=\"/home\">\n    <Text>This should NOT be optimized for namespace Link asChild</Text>\n  </ExpoRouter.Link>\n  <ExpoRouter.Link href=\"/home\">\n    <Text>This should be optimized without asChild</Text>\n  </ExpoRouter.Link>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/expo-router-link-namespace-as-child/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\nimport * as ExpoRouter from 'expo-router';\n<>\n  <ExpoRouter.Link asChild href=\"/home\">\n    <Text>This should NOT be optimized for namespace Link asChild</Text>\n  </ExpoRouter.Link>\n  <ExpoRouter.Link href=\"/home\">\n    <_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n      This should be optimized without asChild\n    </_NativeText>\n  </ExpoRouter.Link>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/flattens-styles-at-runtime/code.js",
    "content": "import { Text } from 'react-native';\n<Text style={{ color: 'red' }} />;\n<Text style={[{ color: 'red' }, { fontSize: 16, userSelect: 'auto' }]} />;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/flattens-styles-at-runtime/output.js",
    "content": "import { processTextStyle as _processTextStyle, NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\n<_NativeText\n  {..._processTextStyle({\n    color: 'red',\n  })}\n  allowFontScaling={true}\n  ellipsizeMode={'tail'}\n/>;\n<_NativeText\n  {..._processTextStyle([\n    {\n      color: 'red',\n    },\n    {\n      fontSize: 16,\n    },\n  ])}\n  selectable={true}\n  allowFontScaling={true}\n  ellipsizeMode={'tail'}\n/>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/force-comment/code.js",
    "content": "import { Text } from 'react-native';\n<>\n  <Text\n    onPress={() => {\n      console.log('pressed');\n    }}>\n    Normally skipped due to blacklisted prop\n  </Text>\n  {/* @boost-force */}\n  <Text\n    onPress={() => {\n      console.log('pressed');\n    }}>\n    Force optimized despite blacklisted prop\n  </Text>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/force-comment/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\n<>\n  <Text\n    onPress={() => {\n      console.log('pressed');\n    }}>\n    Normally skipped due to blacklisted prop\n  </Text>\n  {/* @boost-force */}\n  <_NativeText\n    onPress={() => {\n      console.log('pressed');\n    }}\n    allowFontScaling={true}\n    ellipsizeMode={'tail'}>\n    Force optimized despite blacklisted prop\n  </_NativeText>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/ignore-comment/code.js",
    "content": "import { Text } from 'react-native';\n<>\n  <Text>Optimize this</Text>\n  {/* @boost-ignore */}\n  <Text>But don't optimize this</Text>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/ignore-comment/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\n<>\n  <_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n    Optimize this\n  </_NativeText>\n  {/* @boost-ignore */}\n  <Text>But don't optimize this</Text>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/mixed-children-types/code.js",
    "content": "import { Text } from 'react-native';\nconst name = 'John';\n<Text>Hello {name}!</Text>;\n<Text>\n  Click here: <SomeComponent />\n</Text>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/mixed-children-types/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\nconst name = 'John';\n<_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n  Hello {name}!\n</_NativeText>;\n<Text>\n  Click here: <SomeComponent />\n</Text>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/nested-in-object-with-ignore-comment/code.js",
    "content": "import { Text } from 'react-native';\nconst benchmarks = [\n  {\n    title: 'Text',\n    count: 10_000,\n    optimizedComponent: <Text>Nice text</Text>,\n    // @boost-ignore\n    unoptimizedComponent: <Text>Nice text</Text>,\n  },\n];\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/nested-in-object-with-ignore-comment/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\nconst benchmarks = [\n  {\n    title: 'Text',\n    count: 10_000,\n    optimizedComponent: (\n      <_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n        Nice text\n      </_NativeText>\n    ),\n    // @boost-ignore\n    unoptimizedComponent: <Text>Nice text</Text>,\n  },\n];\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/non-react-native-import/code.js",
    "content": "import { Text } from 'some-other-package';\nimport { Text as RNText } from 'react-native';\n<Text>Hello, world!</Text>;\n<RNText>This is from React Native</RNText>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/non-react-native-import/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'some-other-package';\nimport { Text as RNText } from 'react-native';\n<Text>Hello, world!</Text>;\n<_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n  This is from React Native\n</_NativeText>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/normalize-accessibility-props/code.js",
    "content": "import { Text } from 'react-native';\n<Text aria-label=\"test\" accessibilityLabel=\"test\" />;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/normalize-accessibility-props/output.js",
    "content": "import {\n  processAccessibilityProps as _processAccessibilityProps,\n  NativeText as _NativeText,\n} from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\n<_NativeText\n  {..._processAccessibilityProps(\n    Object.assign(\n      {},\n      {\n        'aria-label': 'test',\n      },\n      {\n        accessibilityLabel: 'test',\n      }\n    )\n  )}\n  allowFontScaling={true}\n  ellipsizeMode={'tail'}\n/>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/normalize-accessibility-props-and-flatten-styles/code.js",
    "content": "import { Text } from 'react-native';\n<Text aria-label=\"test\" accessibilityLabel=\"test\" style={[{ color: 'red' }, { fontSize: 16 }]} />;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/normalize-accessibility-props-and-flatten-styles/output.js",
    "content": "import {\n  processAccessibilityProps as _processAccessibilityProps,\n  processTextStyle as _processTextStyle,\n  NativeText as _NativeText,\n} from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\n<_NativeText\n  {..._processAccessibilityProps(\n    Object.assign(\n      {},\n      {\n        'aria-label': 'test',\n      },\n      {\n        accessibilityLabel: 'test',\n      }\n    )\n  )}\n  {..._processTextStyle([\n    {\n      color: 'red',\n    },\n    {\n      fontSize: 16,\n    },\n  ])}\n  allowFontScaling={true}\n  ellipsizeMode={'tail'}\n/>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/number-of-lines/code.js",
    "content": "import { Text } from 'react-native';\n<Text numberOfLines={10}>10 lines</Text>;\n<Text numberOfLines={-10}>-10 lines</Text>;\n<Text numberOfLines={0}>0 lines</Text>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/number-of-lines/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\n<_NativeText numberOfLines={10} allowFontScaling={true} ellipsizeMode={'tail'}>\n  10 lines\n</_NativeText>;\n<_NativeText numberOfLines={0} allowFontScaling={true} ellipsizeMode={'tail'}>\n  -10 lines\n</_NativeText>;\n<_NativeText numberOfLines={0} allowFontScaling={true} ellipsizeMode={'tail'}>\n  0 lines\n</_NativeText>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/pressables/code.js",
    "content": "import { Text } from 'react-native';\n<Text>Hello, world!</Text>;\n<Text\n  onPress={() => {\n    console.log('pressed');\n  }}>\n  Hello, world!\n</Text>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/pressables/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\n<_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n  Hello, world!\n</_NativeText>;\n<Text\n  onPress={() => {\n    console.log('pressed');\n  }}>\n  Hello, world!\n</Text>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/resolvable-spread-props/code.js",
    "content": "import { Text } from 'react-native';\nconst props = {\n  children: 'Hello, world!',\n};\n<Text {...props} />;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/resolvable-spread-props/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\nconst props = {\n  children: 'Hello, world!',\n};\n<_NativeText {...props} allowFontScaling={true} ellipsizeMode={'tail'} />;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/text-content/code.js",
    "content": "import { Text } from 'react-native';\n<Text>Hello, world!</Text>;\nconst text = 'Hello again, world!';\n<Text>{text}</Text>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/text-content/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\n<_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n  Hello, world!\n</_NativeText>;\nconst text = 'Hello again, world!';\n<_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n  {text}\n</_NativeText>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/text-content-as-explicit-child-prop/code.js",
    "content": "import { Text } from 'react-native';\n<Text children=\"Hello, world!\" />;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/text-content-as-explicit-child-prop/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\n<_NativeText children=\"Hello, world!\" allowFontScaling={true} ellipsizeMode={'tail'} />;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/two-components/code.js",
    "content": "import { Text } from 'react-native';\n<Text />;\n<Text></Text>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/two-components/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\n<_NativeText allowFontScaling={true} ellipsizeMode={'tail'} />;\n<_NativeText allowFontScaling={true} ellipsizeMode={'tail'}></_NativeText>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/unresolvable-spread-props/code.js",
    "content": "import { Text } from 'react-native';\nfunction MyComponent(props) {\n  return <Text {...props}>Hello, world!</Text>;\n}\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/unresolvable-spread-props/output.js",
    "content": "import { Text } from 'react-native';\nfunction MyComponent(props) {\n  return <Text {...props}>Hello, world!</Text>;\n}\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/variable-child-no-string/code.js",
    "content": "import { Text } from 'react-native';\n<Text>Hello, world!</Text>;\nconst test = <Text>Test</Text>;\n<Text>{test}</Text>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/fixtures/variable-child-no-string/output.js",
    "content": "import { NativeText as _NativeText } from 'react-native-boost/runtime';\nimport { Text } from 'react-native';\n<_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n  Hello, world!\n</_NativeText>;\nconst test = (\n  <_NativeText allowFontScaling={true} ellipsizeMode={'tail'}>\n    Test\n  </_NativeText>\n);\n<Text>{test}</Text>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/__tests__/index.test.ts",
    "content": "import path from 'node:path';\nimport { pluginTester } from 'babel-plugin-tester';\nimport { generateTestPlugin } from '../../../utils/generate-test-plugin';\nimport { formatTestResult } from '../../../utils/format-test-result';\nimport { textOptimizer } from '..';\n\npluginTester({\n  plugin: generateTestPlugin(textOptimizer),\n  title: 'text',\n  fixtures: path.resolve(import.meta.dirname, 'fixtures'),\n  babelOptions: {\n    plugins: ['@babel/plugin-syntax-jsx'],\n  },\n  formatResult: formatTestResult,\n});\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/text/index.ts",
    "content": "import { NodePath, types as t } from '@babel/core';\nimport { HubFile, Optimizer, PluginLogger } from '../../types';\nimport PluginError from '../../utils/plugin-error';\nimport { BailoutCheck, getFirstBailoutReason } from '../../utils/helpers';\nimport {\n  addDefaultProperty,\n  addFileImportHint,\n  buildPropertiesFromAttributes,\n  hasAccessibilityProperty,\n  hasBlacklistedProperty,\n  isForcedLine,\n  isIgnoredLine,\n  isValidJSXComponent,\n  isReactNativeImport,\n  replaceWithNativeComponent,\n  isStringNode,\n  hasExpoRouterLinkParentWithAsChild,\n} from '../../utils/common';\nimport { RUNTIME_MODULE_NAME } from '../../utils/constants';\nimport { ACCESSIBILITY_PROPERTIES } from '../../utils/constants';\nimport { extractStyleAttribute, extractSelectableAndUpdateStyle } from '../../utils/common';\n\nexport const textBlacklistedProperties = new Set([\n  'id',\n  'nativeID',\n  'onLongPress',\n  'onPress',\n  'onPressIn',\n  'onPressOut',\n  'onResponderGrant',\n  'onResponderMove',\n  'onResponderRelease',\n  'onResponderTerminate',\n  'onResponderTerminationRequest',\n  'onStartShouldSetResponder',\n  'pressRetentionOffset',\n  'suppressHighlighting',\n  'selectionColor', // TODO: we can use react-native's internal `processColor` to process this at runtime\n]);\n\nexport const textOptimizer: Optimizer = (path, logger) => {\n  if (!isValidJSXComponent(path, 'Text')) return;\n  if (!isReactNativeImport(path, 'Text')) return;\n\n  const parent = path.parent as t.JSXElement;\n  const forced = isForcedLine(path);\n\n  const overridableChecks: BailoutCheck[] = [\n    {\n      reason: 'contains blacklisted props',\n      shouldBail: () => hasBlacklistedProperty(path, textBlacklistedProperties),\n    },\n    {\n      reason: 'is a direct child of expo-router Link with asChild',\n      shouldBail: () => hasExpoRouterLinkParentWithAsChild(path),\n    },\n    {\n      reason: 'contains non-string children',\n      shouldBail: () => hasInvalidChildren(path, parent),\n    },\n  ];\n\n  if (forced) {\n    const overriddenReason = getFirstBailoutReason(overridableChecks);\n\n    if (overriddenReason) {\n      logger.forced({ component: 'Text', path, reason: overriddenReason });\n    }\n  } else {\n    const skipReason = getFirstBailoutReason([\n      {\n        reason: 'line is marked with @boost-ignore',\n        shouldBail: () => isIgnoredLine(path),\n      },\n      ...overridableChecks,\n    ]);\n\n    if (skipReason) {\n      logger.skipped({ component: 'Text', path, reason: skipReason });\n      return;\n    }\n  }\n\n  const hub = path.hub as unknown;\n  const file = typeof hub === 'object' && hub !== null && 'file' in hub ? (hub.file as HubFile) : undefined;\n\n  if (!file) {\n    throw new PluginError('No file found in Babel hub');\n  }\n\n  logger.optimized({\n    component: 'Text',\n    path,\n  });\n\n  // Process props\n  fixNegativeNumberOfLines({ path, logger });\n  addDefaultProperty(path, 'allowFontScaling', t.booleanLiteral(true));\n  addDefaultProperty(path, 'ellipsizeMode', t.stringLiteral('tail'));\n  processProps(path, file);\n\n  // Replace the Text component with NativeText\n  replaceWithNativeComponent(path, parent, file, 'NativeText');\n};\n\n/**\n * Checks if the Text component has any invalid children or blacklisted properties.\n * This function combines the checks for both attribute-based children and JSX children.\n *\n * @param path - The path to the JSXOpeningElement.\n * @param parent - The parent JSX element.\n * @returns true if the component has invalid children or blacklisted properties.\n */\nfunction hasInvalidChildren(path: NodePath<t.JSXOpeningElement>, parent: t.JSXElement): boolean {\n  for (const attribute of path.node.attributes) {\n    if (t.isJSXSpreadAttribute(attribute)) continue; // Spread attributes are handled in hasBlacklistedProperty\n\n    if (\n      t.isJSXIdentifier(attribute.name) &&\n      attribute.value &&\n      // For a \"children\" attribute, optimization is allowed only if it is a string\n      attribute.name.name === 'children' &&\n      !isStringNode(path, attribute.value)\n    ) {\n      return true;\n    }\n  }\n\n  // Return true if any child is not a string node\n  return !parent.children.every((child) => isStringNode(path, child));\n}\n\n/**\n * Fixes negative numberOfLines values by setting them to 0.\n */\nfunction fixNegativeNumberOfLines({ path, logger }: { path: NodePath<t.JSXOpeningElement>; logger: PluginLogger }) {\n  for (const attribute of path.node.attributes) {\n    if (\n      t.isJSXAttribute(attribute) &&\n      t.isJSXIdentifier(attribute.name, { name: 'numberOfLines' }) &&\n      attribute.value &&\n      t.isJSXExpressionContainer(attribute.value)\n    ) {\n      let originalValue: number | undefined;\n      if (t.isNumericLiteral(attribute.value.expression)) {\n        originalValue = attribute.value.expression.value;\n      } else if (\n        t.isUnaryExpression(attribute.value.expression) &&\n        attribute.value.expression.operator === '-' &&\n        t.isNumericLiteral(attribute.value.expression.argument)\n      ) {\n        originalValue = -attribute.value.expression.argument.value;\n      }\n      if (originalValue !== undefined && originalValue < 0) {\n        logger.warning({\n          component: 'Text',\n          path,\n          message: `'numberOfLines' must be a non-negative number, received: ${originalValue}. The value will be set to 0.`,\n        });\n        attribute.value.expression = t.numericLiteral(0);\n      }\n    }\n  }\n}\n\n/**\n * Processes style and accessibility attributes, replacing them with optimized versions.\n */\nfunction processProps(path: NodePath<t.JSXOpeningElement>, file: HubFile) {\n  // Grab the up-to-date list of attributes\n  const currentAttributes = [...path.node.attributes];\n\n  const { styleExpr, styleAttribute } = extractStyleAttribute(currentAttributes);\n  const hasA11y = hasAccessibilityProperty(path, currentAttributes);\n\n  // ============================================\n  // 1. Prepare spread attributes (style / a11y)\n  // ============================================\n\n  const spreadAttributes: t.JSXSpreadAttribute[] = [];\n\n  // --- Accessibility ---\n  if (hasA11y) {\n    const accessibilityAttributes = currentAttributes.filter((attribute) => {\n      if (!t.isJSXAttribute(attribute)) return false;\n      return t.isJSXIdentifier(attribute.name) && ACCESSIBILITY_PROPERTIES.has(attribute.name.name as string);\n    });\n\n    const normalizeIdentifier = addFileImportHint({\n      file,\n      nameHint: 'processAccessibilityProps',\n      path,\n      importName: 'processAccessibilityProps',\n      moduleName: RUNTIME_MODULE_NAME,\n    });\n\n    const accessibilityObject = buildPropertiesFromAttributes(accessibilityAttributes);\n    const accessibilityExpr = t.callExpression(t.identifier(normalizeIdentifier.name), [accessibilityObject]);\n    spreadAttributes.push(t.jsxSpreadAttribute(accessibilityExpr));\n  }\n\n  // --- Style ---\n  let selectableAttribute: t.JSXAttribute | undefined;\n  if (styleExpr) {\n    // Attempt a compile-time extraction of `userSelect`\n    const selectableValue = extractSelectableAndUpdateStyle(styleExpr);\n\n    if (selectableValue != null) {\n      selectableAttribute = t.jsxAttribute(\n        t.jsxIdentifier('selectable'),\n        t.jsxExpressionContainer(t.booleanLiteral(selectableValue))\n      );\n    }\n\n    const flattenIdentifier = addFileImportHint({\n      file,\n      nameHint: 'processTextStyle',\n      path,\n      importName: 'processTextStyle',\n      moduleName: RUNTIME_MODULE_NAME,\n    });\n    const flattenedStyleExpr = t.callExpression(t.identifier(flattenIdentifier.name), [styleExpr]);\n    spreadAttributes.push(t.jsxSpreadAttribute(flattenedStyleExpr));\n  }\n\n  // ============================================\n  // 2. Collect the remaining (non-processed) attributes\n  // ============================================\n  const remainingAttributes: (t.JSXAttribute | t.JSXSpreadAttribute)[] = [];\n\n  for (const attribute of currentAttributes) {\n    // Skip the style attribute (we have replaced it with a spread)\n    if (styleAttribute && attribute === styleAttribute) continue;\n\n    // Skip accessibility attributes if we processed them\n    if (\n      hasA11y &&\n      t.isJSXAttribute(attribute) &&\n      t.isJSXIdentifier(attribute.name) &&\n      ACCESSIBILITY_PROPERTIES.has(attribute.name.name as string)\n    ) {\n      continue;\n    }\n\n    remainingAttributes.push(attribute);\n  }\n\n  path.node.attributes = [...spreadAttributes, selectableAttribute, ...remainingAttributes].filter(\n    (attribute): attribute is t.JSXAttribute | t.JSXSpreadAttribute => attribute !== undefined\n  );\n}\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/basic/code.js",
    "content": "import { View } from 'react-native';\n<View />;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/basic/output.js",
    "content": "import { NativeView as _NativeView } from 'react-native-boost/runtime';\nimport { View } from 'react-native';\n<_NativeView />;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/force-comment/code.js",
    "content": "import { View } from 'react-native';\n<>\n  <View style={{ flex: 1 }}>\n    <NotOptimized />\n  </View>\n  {/* @boost-force */}\n  <View style={{ flex: 1 }}>\n    <ForceOptimized />\n  </View>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/force-comment/output.js",
    "content": "import { NativeView as _NativeView } from 'react-native-boost/runtime';\nimport { View } from 'react-native';\n<>\n  <View\n    style={{\n      flex: 1,\n    }}>\n    <NotOptimized />\n  </View>\n  {/* @boost-force */}\n  <_NativeView\n    style={{\n      flex: 1,\n    }}>\n    <ForceOptimized />\n  </_NativeView>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/ignore-comment/code.js",
    "content": "import { View } from 'react-native';\n<>\n  <View>\n    <Optimized />\n  </View>\n  {/* @boost-ignore */}\n  <View>\n    <NotOptimized />\n  </View>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/ignore-comment/output.js",
    "content": "import { NativeView as _NativeView } from 'react-native-boost/runtime';\nimport { View } from 'react-native';\n<>\n  <_NativeView>\n    <Optimized />\n  </_NativeView>\n  {/* @boost-ignore */}\n  <View>\n    <NotOptimized />\n  </View>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/indirect-text-ancestor/code.js",
    "content": "import { Text, View } from 'react-native';\nconst Custom = ({ children }) => {\n  return <Text>{children}</Text>;\n};\n<>\n  <View>\n    <Optimized />\n  </View>\n  <Custom>\n    <View>\n      <NotOptimized />\n    </View>\n  </Custom>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/indirect-text-ancestor/output.js",
    "content": "import { NativeView as _NativeView } from 'react-native-boost/runtime';\nimport { Text, View } from 'react-native';\nconst Custom = ({ children }) => {\n  return <Text>{children}</Text>;\n};\n<>\n  <_NativeView>\n    <Optimized />\n  </_NativeView>\n  <Custom>\n    <View>\n      <NotOptimized />\n    </View>\n  </Custom>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/react-native-namespace-text-ancestor/code.js",
    "content": "import * as ReactNative from 'react-native';\nimport { View } from 'react-native';\n<ReactNative.Text>\n  <View>\n    <NotOptimized />\n  </View>\n</ReactNative.Text>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/react-native-namespace-text-ancestor/output.js",
    "content": "import * as ReactNative from 'react-native';\nimport { View } from 'react-native';\n<ReactNative.Text>\n  <View>\n    <NotOptimized />\n  </View>\n</ReactNative.Text>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/same-file-safe-ancestor/code.js",
    "content": "import { View } from 'react-native';\nconst Wrapper = ({ children }) => <>{children}</>;\n<Wrapper>\n  <View>\n    <Optimized />\n  </View>\n</Wrapper>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/same-file-safe-ancestor/output.js",
    "content": "import { NativeView as _NativeView } from 'react-native-boost/runtime';\nimport { View } from 'react-native';\nconst Wrapper = ({ children }) => <>{children}</>;\n<Wrapper>\n  <_NativeView>\n    <Optimized />\n  </_NativeView>\n</Wrapper>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/same-file-unknown-ancestor/code.js",
    "content": "import { View } from 'react-native';\nconst Wrapper = ({ children }) => <UnknownContainer>{children}</UnknownContainer>;\n<Wrapper>\n  <View>\n    <NotOptimized />\n  </View>\n</Wrapper>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/same-file-unknown-ancestor/output.js",
    "content": "import { View } from 'react-native';\nconst Wrapper = ({ children }) => <UnknownContainer>{children}</UnknownContainer>;\n<Wrapper>\n  <View>\n    <NotOptimized />\n  </View>\n</Wrapper>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/text-ancestor/code.js",
    "content": "import { Text, View } from 'react-native';\n<>\n  <View>\n    <Optimized />\n  </View>\n  <Text>\n    <View>\n      <NotOptimized />\n    </View>\n  </Text>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/text-ancestor/output.js",
    "content": "import { NativeView as _NativeView } from 'react-native-boost/runtime';\nimport { Text, View } from 'react-native';\n<>\n  <_NativeView>\n    <Optimized />\n  </_NativeView>\n  <Text>\n    <View>\n      <NotOptimized />\n    </View>\n  </Text>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/unknown-imported-ancestor/code.js",
    "content": "import { View } from 'react-native';\nimport { ExternalWrapper } from './ExternalWrapper';\n<>\n  <View>\n    <Optimized />\n  </View>\n  <ExternalWrapper>\n    <View>\n      <NotOptimized />\n    </View>\n  </ExternalWrapper>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/unknown-imported-ancestor/dangerous-output.js",
    "content": "import { NativeView as _NativeView } from 'react-native-boost/runtime';\nimport { View } from 'react-native';\nimport { ExternalWrapper } from './ExternalWrapper';\n<>\n  <_NativeView>\n    <Optimized />\n  </_NativeView>\n  <ExternalWrapper>\n    <_NativeView>\n      <NotOptimized />\n    </_NativeView>\n  </ExternalWrapper>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/unknown-imported-ancestor/output.js",
    "content": "import { NativeView as _NativeView } from 'react-native-boost/runtime';\nimport { View } from 'react-native';\nimport { ExternalWrapper } from './ExternalWrapper';\n<>\n  <_NativeView>\n    <Optimized />\n  </_NativeView>\n  <ExternalWrapper>\n    <View>\n      <NotOptimized />\n    </View>\n  </ExternalWrapper>\n</>;\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/unresolvable-spread-props/code.js",
    "content": "import { View } from 'react-native';\nfunction MyComponent(props) {\n  return (\n    <View {...props}>\n      <NotOptimized />\n    </View>\n  );\n}\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/fixtures/unresolvable-spread-props/output.js",
    "content": "import { View } from 'react-native';\nfunction MyComponent(props) {\n  return (\n    <View {...props}>\n      <NotOptimized />\n    </View>\n  );\n}\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/__tests__/index.test.ts",
    "content": "import path from 'node:path';\nimport { pluginTester } from 'babel-plugin-tester';\nimport { generateTestPlugin } from '../../../utils/generate-test-plugin';\nimport { formatTestResult } from '../../../utils/format-test-result';\nimport { viewOptimizer } from '..';\n\npluginTester({\n  plugin: generateTestPlugin(viewOptimizer),\n  title: 'view',\n  fixtures: path.resolve(import.meta.dirname, 'fixtures'),\n  babelOptions: {\n    plugins: ['@babel/plugin-syntax-jsx'],\n  },\n  formatResult: formatTestResult,\n});\n\npluginTester({\n  plugin: generateTestPlugin(viewOptimizer, {\n    dangerouslyOptimizeViewWithUnknownAncestors: true,\n  }),\n  title: 'view dangerous unknown ancestors',\n  babelOptions: {\n    plugins: ['@babel/plugin-syntax-jsx'],\n  },\n  formatResult: formatTestResult,\n  tests: [\n    {\n      title: 'optimizes View inside unresolved ancestor when enabled',\n      fixture: path.resolve(import.meta.dirname, 'fixtures/unknown-imported-ancestor/code.js'),\n      outputFixture: path.resolve(import.meta.dirname, 'fixtures/unknown-imported-ancestor/dangerous-output.js'),\n    },\n  ],\n});\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/optimizers/view/index.ts",
    "content": "import { types as t } from '@babel/core';\nimport { HubFile, Optimizer } from '../../types';\nimport PluginError from '../../utils/plugin-error';\nimport { BailoutCheck, getFirstBailoutReason } from '../../utils/helpers';\nimport {\n  hasBlacklistedProperty,\n  isForcedLine,\n  isIgnoredLine,\n  isValidJSXComponent,\n  isReactNativeImport,\n  replaceWithNativeComponent,\n  getViewAncestorClassification,\n  ViewAncestorClassification,\n} from '../../utils/common';\n\nexport const viewBlacklistedProperties = new Set([\n  // TODO: process a11y props at runtime\n  'accessible',\n  'accessibilityLabel',\n  'accessibilityState',\n  'aria-busy',\n  'aria-checked',\n  'aria-disabled',\n  'aria-expanded',\n  'aria-label',\n  'aria-selected',\n  'id',\n  'nativeID',\n  'style', // TODO: process style at runtime\n]);\n\nexport const viewOptimizer: Optimizer = (path, logger, options) => {\n  if (!isValidJSXComponent(path, 'View')) return;\n  if (!isReactNativeImport(path, 'View')) return;\n\n  let ancestorClassification: ViewAncestorClassification | undefined;\n  const getAncestorClassification = () => {\n    if (!ancestorClassification) {\n      ancestorClassification = getViewAncestorClassification(path);\n    }\n\n    return ancestorClassification;\n  };\n\n  const forced = isForcedLine(path);\n\n  const overridableChecks: BailoutCheck[] = [\n    {\n      reason: 'contains blacklisted props',\n      shouldBail: () => hasBlacklistedProperty(path, viewBlacklistedProperties),\n    },\n    {\n      reason: 'has Text ancestor',\n      shouldBail: () => getAncestorClassification() === 'text',\n    },\n    {\n      reason: 'has unresolved ancestor and dangerous optimization is disabled',\n      shouldBail: () =>\n        getAncestorClassification() === 'unknown' && options?.dangerouslyOptimizeViewWithUnknownAncestors !== true,\n    },\n  ];\n\n  if (forced) {\n    const overriddenReason = getFirstBailoutReason(overridableChecks);\n\n    if (overriddenReason) {\n      logger.forced({ component: 'View', path, reason: overriddenReason });\n    }\n  } else {\n    const skipReason = getFirstBailoutReason([\n      {\n        reason: 'line is marked with @boost-ignore',\n        shouldBail: () => isIgnoredLine(path),\n      },\n      ...overridableChecks,\n    ]);\n\n    if (skipReason) {\n      logger.skipped({ component: 'View', path, reason: skipReason });\n      return;\n    }\n  }\n\n  const hub = path.hub as unknown;\n  const file = typeof hub === 'object' && hub !== null && 'file' in hub ? (hub.file as HubFile) : undefined;\n\n  if (!file) {\n    throw new PluginError('No file found in Babel hub');\n  }\n\n  logger.optimized({\n    component: 'View',\n    path,\n  });\n\n  const parent = path.parent as t.JSXElement;\n\n  replaceWithNativeComponent(path, parent, file, 'NativeView');\n};\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/types/index.ts",
    "content": "import { NodePath, types as t } from '@babel/core';\n\nexport interface PluginOptimizationOptions {\n  /**\n   * Whether to optimize the `Text` component.\n   * @default true\n   */\n  text?: boolean;\n  /**\n   * Whether to optimize the `View` component.\n   * @default true\n   */\n  view?: boolean;\n}\n\nexport interface PluginOptions {\n  /**\n   * Paths to ignore from optimization.\n   *\n   * Patterns are resolved from Babel's current working directory.\n   * In nested monorepo apps, parent segments may be needed, for example `../../node_modules/**`.\n   * @default []\n   */\n  ignores?: string[];\n  /**\n   * Enables verbose logging.\n   *\n   * With `silent: false`, optimized components are logged by default.\n   * When enabled, skipped components and their skip reasons are also logged.\n   * @default false\n   */\n  verbose?: boolean;\n  /**\n   * Disables all plugin logs.\n   *\n   * When set to `true`, this overrides `verbose`.\n   * @default false\n   */\n  silent?: boolean;\n  /**\n   * Toggle individual optimizers.\n   *\n   * If omitted, all available optimizers are enabled.\n   */\n  optimizations?: PluginOptimizationOptions;\n  /**\n   * Opt-in flag that allows View optimization when ancestor components cannot be statically resolved.\n   *\n   * This increases optimization coverage, but may introduce behavioral differences\n   * when unresolved ancestors render React Native `Text` wrappers.\n   * Prefer targeted ignores first, and enable this only after verifying affected screens.\n   * @default false\n   */\n  dangerouslyOptimizeViewWithUnknownAncestors?: boolean;\n}\n\nexport type OptimizableComponent = 'Text' | 'View';\n\nexport interface OptimizationLogPayload {\n  component: OptimizableComponent;\n  path: NodePath<t.JSXOpeningElement>;\n}\n\nexport interface SkippedOptimizationLogPayload extends OptimizationLogPayload {\n  reason: string;\n}\n\nexport interface WarningLogPayload {\n  message: string;\n  component?: OptimizableComponent;\n  path?: NodePath<t.JSXOpeningElement>;\n}\n\nexport interface PluginLogger {\n  optimized: (payload: OptimizationLogPayload) => void;\n  skipped: (payload: SkippedOptimizationLogPayload) => void;\n  forced: (payload: SkippedOptimizationLogPayload) => void;\n  warning: (payload: WarningLogPayload) => void;\n}\n\nexport type Optimizer = (path: NodePath<t.JSXOpeningElement>, logger: PluginLogger, options?: PluginOptions) => void;\n\nexport type HubFile = t.File & {\n  opts: {\n    filename: string;\n  };\n  __hasImports?: Record<string, t.Identifier>;\n  __optimized?: boolean;\n};\n\n/**\n * Options for adding a file import hint.\n */\nexport interface FileImportOptions {\n  file: HubFile;\n  /** The name hint which also acts as the cache key to ensure the import is only added once (e.g. 'processAccessibilityProps') */\n  nameHint: string;\n  /** The current Babel NodePath */\n  path: NodePath;\n  /**\n   * The named import string (e.g. 'processAccessibilityProps'). Ignored if importType is \"default\".\n   */\n  importName: string;\n  /** The module to import from (e.g. 'react-native-boost/runtime') */\n  moduleName: string;\n  /**\n   * Determines which helper to use:\n   * - \"named\" (default) uses addNamed (requires importName)\n   * - \"default\" uses addDefault\n   */\n  importType?: 'named' | 'default';\n}\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/utils/__tests__/logger.test.ts",
    "content": "import { NodePath, types as t } from '@babel/core';\nimport { afterEach, describe, expect, it, vi } from 'vitest';\nimport { createLogger } from '../logger';\n\nconst originalEnv = {\n  NO_COLOR: process.env.NO_COLOR,\n  FORCE_COLOR: process.env.FORCE_COLOR,\n  CLICOLOR: process.env.CLICOLOR,\n  CLICOLOR_FORCE: process.env.CLICOLOR_FORCE,\n  COLORTERM: process.env.COLORTERM,\n  TERM: process.env.TERM,\n};\n\ndescribe('logger', () => {\n  afterEach(() => {\n    restoreEnvVar('NO_COLOR', originalEnv.NO_COLOR);\n    restoreEnvVar('FORCE_COLOR', originalEnv.FORCE_COLOR);\n    restoreEnvVar('CLICOLOR', originalEnv.CLICOLOR);\n    restoreEnvVar('CLICOLOR_FORCE', originalEnv.CLICOLOR_FORCE);\n    restoreEnvVar('COLORTERM', originalEnv.COLORTERM);\n    restoreEnvVar('TERM', originalEnv.TERM);\n    vi.restoreAllMocks();\n  });\n\n  it('logs optimized components by default', () => {\n    const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});\n    const logger = createLogger({\n      verbose: false,\n      silent: false,\n    });\n\n    const path = createMockPath('/app/screens/LoginScreen.tsx', 42);\n\n    logger.optimized({\n      component: 'Text',\n      path,\n    });\n\n    logger.skipped({\n      component: 'Text',\n      path,\n      reason: 'contains non-string children',\n    });\n\n    expect(consoleSpy).toHaveBeenCalledTimes(1);\n    expect(stripAnsi(String(consoleSpy.mock.calls[0][0]))).toContain(\n      'Optimized Text in /app/screens/LoginScreen.tsx:42'\n    );\n  });\n\n  it('logs skipped components and reasons when verbose is enabled', () => {\n    const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});\n    const logger = createLogger({\n      verbose: true,\n      silent: false,\n    });\n\n    const path = createMockPath('/app/screens/Settings.tsx', 10);\n\n    logger.skipped({\n      component: 'View',\n      path,\n      reason: 'has unresolved ancestor and dangerous optimization is disabled',\n    });\n\n    expect(consoleSpy).toHaveBeenCalledTimes(1);\n    expect(stripAnsi(String(consoleSpy.mock.calls[0][0]))).toContain(\n      'Skipped View in /app/screens/Settings.tsx:10 (has unresolved ancestor and dangerous optimization is disabled)'\n    );\n  });\n\n  it('disables all logs when silent is enabled', () => {\n    const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});\n    const logger = createLogger({\n      verbose: true,\n      silent: true,\n    });\n\n    const path = createMockPath('/app/screens/Profile.tsx', 7);\n\n    logger.optimized({\n      component: 'Text',\n      path,\n    });\n    logger.skipped({\n      component: 'View',\n      path,\n      reason: 'line is marked with @boost-ignore',\n    });\n    logger.warning({\n      component: 'Text',\n      path,\n      message: 'numberOfLines is invalid',\n    });\n\n    expect(consoleSpy).not.toHaveBeenCalled();\n  });\n\n  it('colorizes log levels when TERM supports colors even without TTY', () => {\n    delete process.env.NO_COLOR;\n    delete process.env.FORCE_COLOR;\n    delete process.env.CLICOLOR;\n    delete process.env.CLICOLOR_FORCE;\n    delete process.env.COLORTERM;\n    process.env.TERM = 'xterm-256color';\n\n    const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});\n    const logger = createLogger({\n      verbose: false,\n      silent: false,\n    });\n\n    const path = createMockPath('/app/screens/Color.tsx', 1);\n\n    logger.optimized({\n      component: 'Text',\n      path,\n    });\n\n    expect(consoleSpy).toHaveBeenCalledTimes(1);\n    expect(String(consoleSpy.mock.calls[0][0])).toContain('\\u001B[32m[optimized]\\u001B[0m');\n  });\n\n  it('does not colorize when NO_COLOR is set', () => {\n    process.env.NO_COLOR = '1';\n    process.env.TERM = 'xterm-256color';\n\n    const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});\n    const logger = createLogger({\n      verbose: false,\n      silent: false,\n    });\n\n    const path = createMockPath('/app/screens/NoColor.tsx', 1);\n\n    logger.optimized({\n      component: 'Text',\n      path,\n    });\n\n    expect(consoleSpy).toHaveBeenCalledTimes(1);\n    expect(String(consoleSpy.mock.calls[0][0])).not.toContain('\\u001B[32m[optimized]\\u001B[0m');\n  });\n});\n\nfunction createMockPath(filename: string, lineNumber: number): NodePath<t.JSXOpeningElement> {\n  return {\n    hub: {\n      file: {\n        opts: {\n          filename,\n        },\n      },\n    },\n    node: {\n      loc: {\n        start: {\n          line: lineNumber,\n        },\n      },\n    },\n  } as unknown as NodePath<t.JSXOpeningElement>;\n}\n\nfunction stripAnsi(value: string): string {\n  return value.replace(/\\u001B\\[[0-9;]*m/g, '');\n}\n\nfunction restoreEnvVar(\n  key: 'NO_COLOR' | 'FORCE_COLOR' | 'CLICOLOR' | 'CLICOLOR_FORCE' | 'COLORTERM' | 'TERM',\n  value: string | undefined\n): void {\n  if (value === undefined) {\n    delete process.env[key];\n    return;\n  }\n\n  process.env[key] = value;\n}\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/utils/common/attributes.ts",
    "content": "import { NodePath, types as t } from '@babel/core';\nimport { ACCESSIBILITY_PROPERTIES } from '../constants';\nimport { USER_SELECT_STYLE_TO_SELECTABLE_PROP } from '../constants';\n\n/**\n * Checks if the JSX element has a blacklisted property.\n *\n * @param path - The path to the JSXOpeningElement.\n * @param blacklist - The set of blacklisted properties.\n * @returns true if the JSX element has a blacklisted property.\n */\nexport const hasBlacklistedProperty = (path: NodePath<t.JSXOpeningElement>, blacklist: Set<string>): boolean => {\n  return path.node.attributes.some((attribute) => {\n    // Check direct attributes (e.g., onPress={handler})\n    if (t.isJSXAttribute(attribute) && t.isJSXIdentifier(attribute.name) && blacklist.has(attribute.name.name)) {\n      return true;\n    }\n\n    // Check spread attributes (e.g., {...props})\n    if (t.isJSXSpreadAttribute(attribute)) {\n      if (t.isIdentifier(attribute.argument)) {\n        const binding = path.scope.getBinding(attribute.argument.name);\n        let objectExpression: t.ObjectExpression | undefined;\n        if (binding) {\n          // If the binding node is a VariableDeclarator, use its initializer\n          if (t.isVariableDeclarator(binding.path.node)) {\n            objectExpression = binding.path.node.init as t.ObjectExpression;\n          } else if (t.isObjectExpression(binding.path.node)) {\n            objectExpression = binding.path.node;\n          }\n        }\n        if (objectExpression && t.isObjectExpression(objectExpression)) {\n          return objectExpression.properties.some((property) => {\n            if (t.isObjectProperty(property) && t.isIdentifier(property.key)) {\n              return blacklist.has(property.key.name);\n            }\n            return false;\n          });\n        }\n      }\n      // Bail if we can't resolve the spread attribute\n      return true;\n    }\n\n    // For other attribute types, assume no blacklisting\n    return false;\n  });\n};\n\n/**\n * Adds a default property to a JSX element if it's not already defined. It avoids adding a default\n * if it cannot statically determine whether the property is already set.\n *\n * @param path - The path to the JSXOpeningElement.\n * @param key - The property key.\n * @param value - The default value expression.\n */\nexport const addDefaultProperty = (path: NodePath<t.JSXOpeningElement>, key: string, value: t.Expression) => {\n  let propertyIsFound = false;\n  let hasUnresolvableSpread = false;\n\n  for (const attribute of path.node.attributes) {\n    if (t.isJSXAttribute(attribute) && attribute.name.name === key) {\n      propertyIsFound = true;\n      break;\n    }\n\n    if (t.isJSXSpreadAttribute(attribute)) {\n      if (t.isObjectExpression(attribute.argument)) {\n        const propertyInSpread = attribute.argument.properties.some(\n          (p) =>\n            (t.isObjectProperty(p) && t.isIdentifier(p.key) && p.key.name === key) ||\n            (t.isObjectProperty(p) && t.isStringLiteral(p.key) && p.key.value === key)\n        );\n        if (propertyInSpread) {\n          propertyIsFound = true;\n          break;\n        }\n      } else if (t.isIdentifier(attribute.argument)) {\n        const binding = path.scope.getBinding(attribute.argument.name);\n        if (\n          binding?.path.node &&\n          t.isVariableDeclarator(binding.path.node) &&\n          t.isObjectExpression(binding.path.node.init)\n        ) {\n          const propertyInSpread = binding.path.node.init.properties.some(\n            (p) =>\n              (t.isObjectProperty(p) && t.isIdentifier(p.key) && p.key.name === key) ||\n              (t.isObjectProperty(p) && t.isStringLiteral(p.key) && p.key.value === key)\n          );\n          if (propertyInSpread) {\n            propertyIsFound = true;\n            break;\n          }\n        } else {\n          hasUnresolvableSpread = true;\n          break;\n        }\n      } else {\n        hasUnresolvableSpread = true;\n        break;\n      }\n    }\n  }\n\n  if (!propertyIsFound && !hasUnresolvableSpread) {\n    path.node.attributes.push(t.jsxAttribute(t.jsxIdentifier(key), t.jsxExpressionContainer(value)));\n  }\n};\n\n/**\n * Helper that builds an Object.assign expression out of the existing JSX attributes.\n * It handles both plain JSXAttributes and spread attributes.\n *\n * @param attributes - The attributes to build the expression from.\n * @returns The Object.assign expression.\n */\nexport const buildPropertiesFromAttributes = (attributes: (t.JSXAttribute | t.JSXSpreadAttribute)[]): t.Expression => {\n  const arguments_: t.Expression[] = [];\n  for (const attribute of attributes) {\n    if (t.isJSXSpreadAttribute(attribute)) {\n      arguments_.push(attribute.argument);\n    } else if (t.isJSXAttribute(attribute)) {\n      const key = attribute.name.name;\n      let value: t.Expression;\n      if (!attribute.value) {\n        value = t.booleanLiteral(true);\n      } else if (t.isStringLiteral(attribute.value)) {\n        value = attribute.value;\n      } else if (t.isJSXExpressionContainer(attribute.value)) {\n        value = t.isJSXEmptyExpression(attribute.value.expression)\n          ? t.booleanLiteral(true)\n          : attribute.value.expression;\n      } else {\n        value = t.nullLiteral();\n      }\n      // If the key is not a valid JavaScript identifier (e.g. \"aria-label\"), use a string literal.\n      const validIdentifierRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;\n      const keyNode =\n        typeof key === 'string' && validIdentifierRegex.test(key) ? t.identifier(key) : t.stringLiteral(key.toString());\n\n      arguments_.push(t.objectExpression([t.objectProperty(keyNode, value)]));\n    }\n  }\n  if (arguments_.length === 0) {\n    return t.objectExpression([]);\n  }\n  return t.callExpression(t.memberExpression(t.identifier('Object'), t.identifier('assign')), [\n    t.objectExpression([]),\n    ...arguments_,\n  ]);\n};\n\n/**\n * Checks if the JSX element has an accessibility property.\n *\n * @param path - The NodePath for the JSXOpeningElement, used for scope lookup.\n * @param attributes - The attributes to check.\n * @returns true if the JSX element has an accessibility property.\n */\nexport const hasAccessibilityProperty = (\n  path: NodePath<t.JSXOpeningElement>,\n  attributes: (t.JSXAttribute | t.JSXSpreadAttribute)[]\n): boolean => {\n  for (const attribute of attributes) {\n    if (t.isJSXAttribute(attribute)) {\n      const key = attribute.name.name;\n      if (typeof key === 'string' && ACCESSIBILITY_PROPERTIES.has(key)) {\n        return true;\n      }\n    } else if (t.isJSXSpreadAttribute(attribute)) {\n      if (t.isObjectExpression(attribute.argument)) {\n        for (const property of attribute.argument.properties) {\n          if (\n            t.isObjectProperty(property) &&\n            t.isIdentifier(property.key) &&\n            ACCESSIBILITY_PROPERTIES.has(property.key.name)\n          ) {\n            return true;\n          }\n        }\n      } else if (t.isIdentifier(attribute.argument)) {\n        const binding = path.scope.getBinding(attribute.argument.name);\n        if (binding && t.isVariableDeclarator(binding.path.node)) {\n          const declarator = binding.path.node as t.VariableDeclarator;\n          if (declarator.init && t.isObjectExpression(declarator.init)) {\n            for (const property of declarator.init.properties) {\n              if (\n                t.isObjectProperty(property) &&\n                t.isIdentifier(property.key) &&\n                ACCESSIBILITY_PROPERTIES.has(property.key.name)\n              ) {\n                return true;\n              }\n            }\n            continue;\n          }\n        }\n        return true;\n      } else {\n        return true;\n      }\n    }\n  }\n  return false;\n};\n\n/**\n * Extracts the `style` attribute from a JSX attributes list.\n *\n * @returns An object containing the attribute node itself (if found) and the expression inside\n */\nexport function extractStyleAttribute(attributes: Array<t.JSXAttribute | t.JSXSpreadAttribute>): {\n  styleAttribute?: t.JSXAttribute;\n  styleExpr?: t.Expression;\n} {\n  for (const attribute of attributes) {\n    if (t.isJSXAttribute(attribute) && t.isJSXIdentifier(attribute.name, { name: 'style' })) {\n      if (\n        attribute.value &&\n        t.isJSXExpressionContainer(attribute.value) &&\n        !t.isJSXEmptyExpression(attribute.value.expression)\n      ) {\n        return {\n          styleAttribute: attribute,\n          styleExpr: attribute.value.expression,\n        };\n      }\n      return { styleAttribute: attribute };\n    }\n  }\n  return {};\n}\n\n/**\n * Attempts to statically extract the `userSelect` style property from a style expression.\n *\n * If the `userSelect` value can be resolved at compile-time, the property is removed from the\n * object literal (or array element) and its mapped boolean value for the native `selectable`\n * prop is returned. When the value is unknown or the expression is not statically analysable,\n * `undefined` is returned and no modification is made.\n */\nexport function extractSelectableAndUpdateStyle(styleExpr: t.Expression): boolean | undefined {\n  // Helper to process a single ObjectExpression\n  const handleObjectExpression = (objectExpr: t.ObjectExpression): boolean | undefined => {\n    let selectableValue: boolean | undefined;\n\n    objectExpr.properties = objectExpr.properties.filter((property) => {\n      if (\n        !t.isObjectProperty(property) ||\n        (!t.isIdentifier(property.key, { name: 'userSelect' }) &&\n          !(t.isStringLiteral(property.key) && property.key.value === 'userSelect'))\n      ) {\n        return true; // keep property\n      }\n\n      if (t.isStringLiteral(property.value)) {\n        const mapped = USER_SELECT_STYLE_TO_SELECTABLE_PROP[property.value.value];\n        if (mapped !== undefined) {\n          selectableValue = mapped;\n        }\n      }\n\n      // Remove the `userSelect` property\n      return false;\n    });\n\n    return selectableValue;\n  };\n\n  if (t.isObjectExpression(styleExpr)) {\n    return handleObjectExpression(styleExpr);\n  }\n\n  if (t.isArrayExpression(styleExpr)) {\n    let selectableValue: boolean | undefined;\n    for (const element of styleExpr.elements) {\n      if (element && t.isObjectExpression(element)) {\n        const value = handleObjectExpression(element);\n        if (value !== undefined) {\n          selectableValue = value; // prefer last defined value\n        }\n      }\n    }\n    return selectableValue;\n  }\n\n  return undefined; // not statically analysable\n}\n\n/**\n * Checks if a node represents a string value.\n */\nexport const isStringNode = (path: NodePath<t.JSXOpeningElement>, child: t.Node): boolean => {\n  if (t.isJSXText(child) || t.isStringLiteral(child)) return true;\n\n  if (t.isJSXExpressionContainer(child)) {\n    const expression = child.expression;\n    if (t.isIdentifier(expression)) {\n      const binding = path.scope.getBinding(expression.name);\n      if (binding && binding.path.node && t.isVariableDeclarator(binding.path.node)) {\n        return !!binding.path.node.init && t.isStringLiteral(binding.path.node.init);\n      }\n      return false;\n    }\n    if (t.isStringLiteral(expression)) return true;\n  }\n  return false;\n};\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/utils/common/base.ts",
    "content": "import { NodePath, types as t } from '@babel/core';\nimport { addDefault, addNamed } from '@babel/helper-module-imports';\nimport { FileImportOptions, HubFile } from '../../types';\nimport { RUNTIME_MODULE_NAME } from '../constants';\n\n/**\n * Adds a hint to the file object to ensure that a specific import is added only once and cached on the file object.\n *\n * @param opts - Object containing the function arguments:\n *   - file: The Babel file object (e.g. HubFile)\n *   - nameHint: The name hint which also acts as the cache key to ensure the import is only added once (e.g. 'processAccessibilityProps')\n *   - path: The current Babel NodePath\n *   - importName: The named import string (e.g. 'processAccessibilityProps'), used when importType is 'named'\n *   - moduleName: The module to import from (e.g. 'react-native-boost/runtime')\n *   - importType: Either 'named' (default) or 'default' to determine the type of import to use.\n *\n * @returns The identifier returned by addNamed or addDefault.\n */\nexport function addFileImportHint({\n  file,\n  nameHint,\n  path,\n  importName,\n  moduleName,\n  importType = 'named',\n}: FileImportOptions): t.Identifier {\n  if (!file.__hasImports?.[nameHint]) {\n    file.__hasImports = file.__hasImports || {};\n    file.__hasImports[nameHint] =\n      importType === 'default'\n        ? addDefault(path, moduleName, { nameHint })\n        : addNamed(path, importName, moduleName, { nameHint });\n  }\n  return file.__hasImports[nameHint];\n}\n\n/**\n * Replaces a component with its native counterpart.\n * This function handles both the opening and closing tags.\n *\n * @param path - The path to the JSXOpeningElement.\n * @param parent - The parent JSX element.\n * @param file - The Babel file object.\n * @param nativeComponentName - The name of the native component to import.\n * @param moduleName - The module to import the native component from.\n * @returns The identifier for the imported native component.\n */\nexport const replaceWithNativeComponent = (\n  path: NodePath<t.JSXOpeningElement>,\n  parent: t.JSXElement,\n  file: HubFile,\n  nativeComponentName: string\n): t.Identifier => {\n  // Add native component import (cached on file) to prevent duplicate imports\n  const nativeIdentifier = addFileImportHint({\n    file,\n    nameHint: nativeComponentName,\n    path,\n    importName: nativeComponentName,\n    moduleName: RUNTIME_MODULE_NAME,\n    importType: 'named',\n  });\n\n  // Get the current name of the component, which may be aliased (i.e. Text -> RNText)\n  const currentName = (path.node.name as t.JSXIdentifier).name;\n\n  // Replace the component with its native counterpart\n  const jsxName = path.node.name as t.JSXIdentifier;\n  jsxName.name = nativeIdentifier.name;\n\n  // If the element is not self-closing, update the closing element as well\n  if (\n    !path.node.selfClosing &&\n    parent.closingElement &&\n    t.isJSXIdentifier(parent.closingElement.name) &&\n    parent.closingElement.name.name === currentName\n  ) {\n    parent.closingElement.name.name = nativeIdentifier.name;\n  }\n\n  return nativeIdentifier;\n};\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/utils/common/index.ts",
    "content": "export * from './validation';\nexport * from './attributes';\nexport * from './base';\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/utils/common/validation.ts",
    "content": "import { NodePath, types as t } from '@babel/core';\nimport { ensureArray } from '../helpers';\nimport { HubFile } from '../../types';\nimport { minimatch } from 'minimatch';\nimport nodePath from 'node:path';\nimport PluginError from '../plugin-error';\n\n/**\n * Checks if the file is in the list of ignored files.\n *\n * @param path - The path to the JSXOpeningElement.\n * @param ignores - List of glob paths (absolute or relative to import.meta.dirname).\n * @returns true if the file matches any of the ignore patterns.\n */\nexport const isIgnoredFile = (path: NodePath<t.JSXOpeningElement>, ignores: string[]): boolean => {\n  const hub = path.hub as unknown;\n  const file = typeof hub === 'object' && hub !== null && 'file' in hub ? (hub.file as HubFile) : undefined;\n\n  if (!file) {\n    throw new PluginError('No file found in Babel hub');\n  }\n\n  const fileName = file.opts.filename;\n\n  // Use the current working directory which typically corresponds to the user's project root.\n  const baseDirectory = 'cwd' in file.opts ? (file.opts.cwd as string) : process.cwd();\n\n  // Iterate through the ignore patterns.\n  for (const pattern of ignores) {\n    // If the pattern is not absolute, join it with the baseDir\n    const absolutePattern = nodePath.isAbsolute(pattern) ? pattern : nodePath.join(baseDirectory, pattern);\n\n    // Check if the file name matches the glob pattern.\n    if (minimatch(fileName, absolutePattern, { dot: true })) {\n      return true;\n    }\n  }\n\n  return false;\n};\n\nexport const isForcedLine = (path: NodePath<t.JSXOpeningElement>): boolean => {\n  return hasDecoratorComment(path, '@boost-force');\n};\n\nexport const isIgnoredLine = (path: NodePath<t.JSXOpeningElement>): boolean => {\n  return hasDecoratorComment(path, '@boost-ignore');\n};\n\n/**\n * Checks if the JSX element has a preceding comment containing the given decorator string.\n *\n * Scans the JSXOpeningElement's own leading comments, the parent element's comments,\n * ObjectProperty containers, and backward siblings.\n */\nfunction hasDecoratorComment(path: NodePath<t.JSXOpeningElement>, decorator: string): boolean {\n  if (path.node.leadingComments?.some((comment) => comment.value.includes(decorator))) {\n    return true;\n  }\n\n  const jsxElementPath = path.parentPath;\n  if (jsxElementPath.node.leadingComments?.some((comment) => comment.value.includes(decorator))) {\n    return true;\n  }\n\n  // Check leading comments on the ObjectProperty (if the JSX element is a value inside an object literal).\n  const propertyPath = jsxElementPath.parentPath;\n  if (\n    propertyPath &&\n    propertyPath.isObjectProperty() &&\n    propertyPath.node.leadingComments?.some((comment) => comment.value.includes(decorator))\n  ) {\n    return true;\n  }\n\n  if (!jsxElementPath.parentPath) return false;\n\n  const containerPath = jsxElementPath.parentPath;\n  const siblings = ensureArray(containerPath.get('children'));\n  const index = siblings.findIndex((sibling) => sibling.node === jsxElementPath.node);\n  if (index === -1) return false;\n\n  for (let index_ = index - 1; index_ >= 0; index_--) {\n    const sibling = siblings[index_];\n    if (sibling.isJSXText() && sibling.node.value.trim() === '') {\n      continue;\n    }\n    if (sibling.isJSXExpressionContainer()) {\n      const expression = sibling.get('expression');\n      if (expression && expression.node) {\n        const comments = [\n          ...(expression.node.leadingComments || []),\n          ...(expression.node.trailingComments || []),\n          ...(expression.node.innerComments || []),\n        ].map((comment) => comment.value.trim());\n        if (comments.some((comment) => comment.includes(decorator))) {\n          return true;\n        }\n      }\n    }\n    if (\n      sibling.node.leadingComments &&\n      sibling.node.leadingComments.some((comment) => comment.value.includes(decorator))\n    ) {\n      return true;\n    }\n    break;\n  }\n  return false;\n}\n\n/**\n * Checks if the path represents a valid JSX component with the specified name.\n *\n * @param path - The NodePath to check.\n * @param componentName - The name of the component to validate against.\n * @returns true if the path is a valid JSX component with the specified name.\n */\nexport const isValidJSXComponent = (path: NodePath<t.JSXOpeningElement>, componentName: string): boolean => {\n  // Check if the node name is a JSX identifier\n  if (!t.isJSXIdentifier(path.node.name)) return false;\n\n  // Check if the parent is a JSX element\n  const parent = path.parent;\n  if (!t.isJSXElement(parent)) return false;\n\n  // For aliasing, we check if the underlying imported name matches the expected name\n  const componentIdentifier = path.node.name.name;\n  const binding = path.scope.getBinding(componentIdentifier);\n  if (!binding) return false;\n  if (\n    binding.kind === 'module' &&\n    t.isImportDeclaration(binding.path.parent) &&\n    t.isImportSpecifier(binding.path.node)\n  ) {\n    const imported = binding.path.node.imported;\n    if (t.isIdentifier(imported)) {\n      return imported.name === componentName;\n    }\n  }\n\n  // Fallback to string match if binding is not available\n  return path.node.name.name === componentName;\n};\n\n/**\n * Checks if the component is imported from 'react-native' and not from a custom module.\n *\n * @param path - The NodePath to check.\n * @param expectedImportedName - The expected import name of the component (we'll also check for aliased imports).\n * @returns true if the component is imported from 'react-native'\n */\nexport const isReactNativeImport = (path: NodePath<t.JSXOpeningElement>, expectedImportedName: string): boolean => {\n  if (!t.isJSXIdentifier(path.node.name)) return false;\n  const localName = path.node.name.name;\n  const binding = path.scope.getBinding(localName);\n  if (!binding) return false;\n  if (binding.kind === 'module') {\n    const importDeclaration = binding.path.parent;\n    if (!t.isImportDeclaration(importDeclaration)) return false;\n    // Verify it's imported from 'react-native'\n    if (importDeclaration.source.value !== 'react-native') return false;\n\n    // For named imports, check the imported name (not the alias)\n    if (t.isImportSpecifier(binding.path.node)) {\n      const imported = binding.path.node.imported;\n      if (t.isIdentifier(imported)) {\n        return imported.name === expectedImportedName;\n      }\n    }\n\n    // For default imports, we just assume it's valid if imported from react-native.\n    if (t.isImportDefaultSpecifier(binding.path.node)) {\n      return true;\n    }\n  }\n  return false;\n};\n\ntype AncestorClassification = 'safe' | 'text' | 'unknown';\nexport type ViewAncestorClassification = AncestorClassification;\ntype ScopeBinding = NonNullable<ReturnType<NodePath<t.Node>['scope']['getBinding']>>;\n\ntype AncestorAnalysisContext = {\n  componentCache: WeakMap<t.Node, AncestorClassification>;\n  componentInProgress: WeakSet<t.Node>;\n  renderExpressionInProgress: WeakSet<t.Node>;\n};\n\nexport const getViewAncestorClassification = (path: NodePath<t.JSXOpeningElement>): ViewAncestorClassification => {\n  return classifyViewAncestors(path);\n};\n\nfunction classifyViewAncestors(path: NodePath<t.JSXOpeningElement>): AncestorClassification {\n  const context: AncestorAnalysisContext = {\n    componentCache: new WeakMap<t.Node, AncestorClassification>(),\n    componentInProgress: new WeakSet<t.Node>(),\n    renderExpressionInProgress: new WeakSet<t.Node>(),\n  };\n\n  let classification: AncestorClassification = 'safe';\n  let ancestorPath: NodePath<t.Node> | null = path.parentPath.parentPath;\n\n  while (ancestorPath) {\n    if (ancestorPath.isJSXElement()) {\n      const ancestorClassification = classifyJSXElementAsAncestor(ancestorPath, context);\n      classification = mergeAncestorClassification(classification, ancestorClassification);\n\n      if (classification === 'text') return classification;\n    }\n\n    ancestorPath = ancestorPath.parentPath;\n  }\n\n  return classification;\n}\n\nfunction classifyJSXElementAsAncestor(\n  path: NodePath<t.JSXElement>,\n  context: AncestorAnalysisContext\n): AncestorClassification {\n  const openingElementName = path.node.openingElement.name;\n\n  if (t.isJSXIdentifier(openingElementName)) {\n    return classifyJSXIdentifierAsAncestor(path, openingElementName.name, context);\n  }\n\n  if (t.isJSXMemberExpression(openingElementName)) {\n    return classifyJSXMemberExpressionAsAncestor(path, openingElementName);\n  }\n\n  return 'unknown';\n}\n\nfunction classifyJSXIdentifierAsAncestor(\n  path: NodePath<t.JSXElement>,\n  identifierName: string,\n  context: AncestorAnalysisContext\n): AncestorClassification {\n  if (identifierName === 'Fragment') return 'safe';\n\n  const binding = path.scope.getBinding(identifierName);\n  if (!binding) return 'unknown';\n\n  return classifyBindingAsAncestor(binding, context);\n}\n\nfunction classifyJSXMemberExpressionAsAncestor(\n  path: NodePath<t.JSXElement>,\n  expression: t.JSXMemberExpression\n): AncestorClassification {\n  if (!t.isJSXIdentifier(expression.object) || !t.isJSXIdentifier(expression.property)) {\n    return 'unknown';\n  }\n\n  const binding = path.scope.getBinding(expression.object.name);\n  if (!binding || binding.kind !== 'module' || !t.isImportNamespaceSpecifier(binding.path.node)) {\n    return 'unknown';\n  }\n\n  const importDeclaration = binding.path.parent;\n  if (!t.isImportDeclaration(importDeclaration)) return 'unknown';\n\n  if (importDeclaration.source.value === 'react-native') {\n    return expression.property.name === 'Text' ? 'text' : 'safe';\n  }\n\n  if (importDeclaration.source.value === 'react' && expression.property.name === 'Fragment') {\n    return 'safe';\n  }\n\n  return 'unknown';\n}\n\nfunction classifyBindingAsAncestor(binding: ScopeBinding, context: AncestorAnalysisContext): AncestorClassification {\n  if (binding.kind === 'module') {\n    return classifyModuleBindingAsAncestor(binding);\n  }\n\n  return classifyLocalBindingAsAncestor(binding, context);\n}\n\nfunction classifyModuleBindingAsAncestor(binding: ScopeBinding): AncestorClassification {\n  const importDeclaration = binding.path.parent;\n  if (!t.isImportDeclaration(importDeclaration)) return 'unknown';\n\n  const source = importDeclaration.source.value;\n  if (source === 'react-native') {\n    if (t.isImportSpecifier(binding.path.node)) {\n      const importedName = getImportSpecifierImportedName(binding.path.node);\n      if (!importedName) return 'unknown';\n      return importedName === 'Text' ? 'text' : 'safe';\n    }\n\n    if (t.isImportNamespaceSpecifier(binding.path.node)) {\n      return 'safe';\n    }\n\n    return 'unknown';\n  }\n\n  if (source === 'react' && t.isImportSpecifier(binding.path.node)) {\n    const importedName = getImportSpecifierImportedName(binding.path.node);\n    if (importedName === 'Fragment') return 'safe';\n  }\n\n  return 'unknown';\n}\n\nfunction classifyLocalBindingAsAncestor(\n  binding: ScopeBinding,\n  context: AncestorAnalysisContext\n): AncestorClassification {\n  const cacheKey = binding.path.node;\n  const cached = context.componentCache.get(cacheKey);\n  if (cached) return cached;\n\n  if (context.componentInProgress.has(cacheKey)) {\n    return 'unknown';\n  }\n\n  context.componentInProgress.add(cacheKey);\n\n  let classification: AncestorClassification;\n  if (binding.path.isFunctionDeclaration()) {\n    classification = analyzeFunctionComponent(binding.path, context);\n  } else if (binding.path.isVariableDeclarator()) {\n    classification = analyzeVariableDeclaratorComponent(binding.path, context);\n  } else {\n    classification = 'unknown';\n  }\n\n  context.componentInProgress.delete(cacheKey);\n  context.componentCache.set(cacheKey, classification);\n\n  return classification;\n}\n\nfunction analyzeVariableDeclaratorComponent(\n  path: NodePath<t.VariableDeclarator>,\n  context: AncestorAnalysisContext\n): AncestorClassification {\n  const initPath = path.get('init');\n  if (!initPath.node) return 'unknown';\n\n  if (initPath.isArrowFunctionExpression() || initPath.isFunctionExpression()) {\n    return analyzeFunctionComponent(initPath, context);\n  }\n\n  if (initPath.isCallExpression()) {\n    return analyzeCallWrappedComponent(initPath, context);\n  }\n\n  if (initPath.isIdentifier()) {\n    const aliasBinding = path.scope.getBinding(initPath.node.name);\n    if (!aliasBinding) return 'unknown';\n\n    return classifyBindingAsAncestor(aliasBinding, context);\n  }\n\n  return 'unknown';\n}\n\nfunction analyzeCallWrappedComponent(\n  path: NodePath<t.CallExpression>,\n  context: AncestorAnalysisContext\n): AncestorClassification {\n  if (!isReactMemoOrForwardRefCall(path)) return 'unknown';\n\n  const [firstArgumentPath] = path.get('arguments');\n  if (!firstArgumentPath?.node) return 'unknown';\n\n  if (firstArgumentPath.isArrowFunctionExpression() || firstArgumentPath.isFunctionExpression()) {\n    return analyzeFunctionComponent(firstArgumentPath, context);\n  }\n\n  if (firstArgumentPath.isIdentifier()) {\n    const wrappedComponentBinding = path.scope.getBinding(firstArgumentPath.node.name);\n    if (!wrappedComponentBinding) return 'unknown';\n\n    return classifyBindingAsAncestor(wrappedComponentBinding, context);\n  }\n\n  if (firstArgumentPath.isCallExpression()) {\n    return analyzeCallWrappedComponent(firstArgumentPath, context);\n  }\n\n  return 'unknown';\n}\n\nfunction isReactMemoOrForwardRefCall(path: NodePath<t.CallExpression>): boolean {\n  const calleePath = path.get('callee');\n\n  if (calleePath.isIdentifier()) {\n    if (!isMemoOrForwardRefName(calleePath.node.name)) return false;\n\n    const binding = path.scope.getBinding(calleePath.node.name);\n    return isReactImportBinding(binding);\n  }\n\n  if (calleePath.isMemberExpression()) {\n    const objectPath = calleePath.get('object');\n    const propertyPath = calleePath.get('property');\n\n    if (!objectPath.isIdentifier() || !propertyPath.isIdentifier()) return false;\n    if (!isMemoOrForwardRefName(propertyPath.node.name)) return false;\n\n    const objectBinding = path.scope.getBinding(objectPath.node.name);\n    return isReactImportBinding(objectBinding);\n  }\n\n  return false;\n}\n\nfunction isMemoOrForwardRefName(name: string): boolean {\n  return name === 'memo' || name === 'forwardRef';\n}\n\nfunction isReactImportBinding(binding: ScopeBinding | undefined): binding is ScopeBinding {\n  if (!binding || binding.kind !== 'module') return false;\n\n  const importDeclaration = binding.path.parent;\n  return t.isImportDeclaration(importDeclaration) && importDeclaration.source.value === 'react';\n}\n\nfunction analyzeFunctionComponent(\n  path: NodePath<t.FunctionDeclaration | t.FunctionExpression | t.ArrowFunctionExpression>,\n  context: AncestorAnalysisContext\n): AncestorClassification {\n  const bodyPath = path.get('body');\n\n  if (!bodyPath.isBlockStatement()) {\n    return analyzeRenderExpression(bodyPath as NodePath<t.Node>, context);\n  }\n\n  let classification: AncestorClassification = 'safe';\n\n  for (const statementPath of bodyPath.get('body')) {\n    if (!statementPath.isReturnStatement()) continue;\n\n    const argumentPath = statementPath.get('argument');\n    if (!argumentPath.node) continue;\n\n    const returnClassification = analyzeRenderExpression(argumentPath as NodePath<t.Node>, context);\n    classification = mergeAncestorClassification(classification, returnClassification);\n\n    if (classification === 'text') return classification;\n  }\n\n  return classification;\n}\n\nfunction analyzeRenderExpression(path: NodePath<t.Node>, context: AncestorAnalysisContext): AncestorClassification {\n  if (path.isJSXFragment()) {\n    return analyzeJSXChildren(path.get('children'), context);\n  }\n\n  let classification: AncestorClassification = 'safe';\n  let hasJSX = false;\n\n  path.traverse({\n    JSXOpeningElement(jsxPath) {\n      hasJSX = true;\n\n      const jsxElementPath = jsxPath.parentPath;\n      if (!jsxElementPath.isJSXElement()) {\n        classification = mergeAncestorClassification(classification, 'unknown');\n        return;\n      }\n\n      const jsxClassification = classifyJSXElementAsAncestor(jsxElementPath, context);\n      classification = mergeAncestorClassification(classification, jsxClassification);\n\n      if (classification === 'text') {\n        jsxPath.stop();\n      }\n    },\n  });\n\n  if (hasJSX) return classification;\n\n  if (path.isIdentifier()) {\n    return analyzeIdentifierRenderExpression(path, context);\n  }\n\n  if (path.isMemberExpression() && isPropsChildrenMemberExpression(path.node)) {\n    return 'safe';\n  }\n\n  if (\n    path.isNullLiteral() ||\n    path.isBooleanLiteral() ||\n    path.isNumericLiteral() ||\n    path.isStringLiteral() ||\n    path.isBigIntLiteral()\n  ) {\n    return 'safe';\n  }\n\n  return 'unknown';\n}\n\nfunction analyzeJSXChildren(\n  children: Array<NodePath<t.JSXText | t.JSXExpressionContainer | t.JSXSpreadChild | t.JSXElement | t.JSXFragment>>,\n  context: AncestorAnalysisContext\n): AncestorClassification {\n  let classification: AncestorClassification = 'safe';\n\n  for (const childPath of children) {\n    if (childPath.isJSXElement()) {\n      const childClassification = classifyJSXElementAsAncestor(childPath, context);\n      classification = mergeAncestorClassification(classification, childClassification);\n    } else if (childPath.isJSXFragment()) {\n      const fragmentClassification = analyzeJSXChildren(childPath.get('children'), context);\n      classification = mergeAncestorClassification(classification, fragmentClassification);\n    } else if (childPath.isJSXExpressionContainer()) {\n      const expressionPath = childPath.get('expression');\n      if (!expressionPath.node || expressionPath.isJSXEmptyExpression()) continue;\n\n      const expressionClassification = analyzeRenderExpression(expressionPath as NodePath<t.Node>, context);\n      classification = mergeAncestorClassification(classification, expressionClassification);\n    } else if (childPath.isJSXSpreadChild()) {\n      classification = mergeAncestorClassification(classification, 'unknown');\n    }\n\n    if (classification === 'text') {\n      return classification;\n    }\n  }\n\n  return classification;\n}\n\nfunction analyzeIdentifierRenderExpression(\n  path: NodePath<t.Identifier>,\n  context: AncestorAnalysisContext\n): AncestorClassification {\n  if (path.node.name === 'children') return 'safe';\n\n  const binding = path.scope.getBinding(path.node.name);\n  if (!binding) return 'unknown';\n\n  if (binding.kind === 'param') {\n    return binding.identifier.name === 'children' ? 'safe' : 'unknown';\n  }\n\n  if (!binding.path.isVariableDeclarator()) return 'unknown';\n\n  const cacheKey = binding.path.node;\n  if (context.renderExpressionInProgress.has(cacheKey)) {\n    return 'unknown';\n  }\n\n  const initPath = binding.path.get('init');\n  if (!initPath.node) return 'unknown';\n\n  context.renderExpressionInProgress.add(cacheKey);\n  const classification = analyzeRenderExpression(initPath as NodePath<t.Node>, context);\n  context.renderExpressionInProgress.delete(cacheKey);\n\n  return classification;\n}\n\nfunction isPropsChildrenMemberExpression(expression: t.MemberExpression): boolean {\n  if (!t.isIdentifier(expression.object, { name: 'props' })) return false;\n  if (!t.isIdentifier(expression.property, { name: 'children' })) return false;\n  return !expression.computed;\n}\n\nfunction mergeAncestorClassification(\n  current: AncestorClassification,\n  next: AncestorClassification\n): AncestorClassification {\n  if (current === 'text' || next === 'text') return 'text';\n  if (current === 'unknown' || next === 'unknown') return 'unknown';\n  return 'safe';\n}\n\nfunction getImportSpecifierImportedName(specifier: t.ImportSpecifier): string | undefined {\n  if (t.isIdentifier(specifier.imported)) {\n    return specifier.imported.name;\n  }\n\n  if (t.isStringLiteral(specifier.imported)) {\n    return specifier.imported.value;\n  }\n\n  return undefined;\n}\n\n/**\n * Checks whether the closest JSX element ancestor is expo-router Link with a truthy asChild prop.\n *\n * We only bail on Text optimization when Link is effectively slotting that Text as the clickable child.\n */\nexport const hasExpoRouterLinkParentWithAsChild = (path: NodePath<t.JSXOpeningElement>): boolean => {\n  const textElementPath = path.parentPath;\n  if (!textElementPath.isJSXElement()) return false;\n\n  let ancestorPath: NodePath<t.Node> | null = textElementPath.parentPath;\n\n  while (ancestorPath) {\n    if (ancestorPath.isJSXElement()) {\n      if (!isExpoRouterLinkElement(ancestorPath)) return false;\n\n      return hasTruthyAsChildAttribute(ancestorPath.node.openingElement.attributes);\n    }\n\n    ancestorPath = ancestorPath.parentPath;\n  }\n\n  return false;\n};\n\nfunction isExpoRouterLinkElement(path: NodePath<t.JSXElement>): boolean {\n  const openingElementName = path.node.openingElement.name;\n\n  if (t.isJSXIdentifier(openingElementName)) {\n    const binding = path.scope.getBinding(openingElementName.name);\n    if (!binding || binding.kind !== 'module') return false;\n    if (!t.isImportSpecifier(binding.path.node)) return false;\n\n    const importDeclaration = binding.path.parent;\n    if (!t.isImportDeclaration(importDeclaration) || importDeclaration.source.value !== 'expo-router') return false;\n\n    const imported = binding.path.node.imported;\n    return t.isIdentifier(imported, { name: 'Link' }) || (t.isStringLiteral(imported) && imported.value === 'Link');\n  }\n\n  if (t.isJSXMemberExpression(openingElementName)) {\n    if (!t.isJSXIdentifier(openingElementName.object)) return false;\n    if (!t.isJSXIdentifier(openingElementName.property, { name: 'Link' })) return false;\n\n    const namespaceBinding = path.scope.getBinding(openingElementName.object.name);\n    if (!namespaceBinding || namespaceBinding.kind !== 'module') return false;\n    if (!t.isImportNamespaceSpecifier(namespaceBinding.path.node)) return false;\n\n    const importDeclaration = namespaceBinding.path.parent;\n    return t.isImportDeclaration(importDeclaration) && importDeclaration.source.value === 'expo-router';\n  }\n\n  return false;\n}\n\nfunction hasTruthyAsChildAttribute(attributes: (t.JSXAttribute | t.JSXSpreadAttribute)[]): boolean {\n  let asChildAttribute: t.JSXAttribute | undefined;\n\n  for (const attribute of attributes) {\n    if (t.isJSXAttribute(attribute) && t.isJSXIdentifier(attribute.name, { name: 'asChild' })) {\n      asChildAttribute = attribute;\n    }\n  }\n\n  if (!asChildAttribute) return false;\n\n  return isJSXAttributeValueTruthy(asChildAttribute.value);\n}\n\nfunction isJSXAttributeValueTruthy(value: t.JSXAttribute['value']): boolean {\n  if (!value) return true;\n  if (t.isStringLiteral(value)) return value.value.length > 0;\n  if (t.isJSXElement(value) || t.isJSXFragment(value)) return true;\n\n  if (t.isJSXExpressionContainer(value)) {\n    const staticTruthiness = getStaticExpressionTruthiness(value.expression);\n    return staticTruthiness ?? true;\n  }\n\n  return true;\n}\n\nfunction getStaticExpressionTruthiness(expression: t.Expression | t.JSXEmptyExpression): boolean | undefined {\n  if (t.isJSXEmptyExpression(expression)) return false;\n  if (t.isBooleanLiteral(expression)) return expression.value;\n  if (t.isNullLiteral(expression)) return false;\n  if (t.isStringLiteral(expression)) return expression.value.length > 0;\n  if (t.isNumericLiteral(expression)) return expression.value !== 0 && !Number.isNaN(expression.value);\n  if (t.isBigIntLiteral(expression)) return expression.value !== '0';\n  if (t.isIdentifier(expression, { name: 'undefined' })) return false;\n\n  if (t.isTemplateLiteral(expression) && expression.expressions.length === 0) {\n    return (expression.quasis[0]?.value.cooked ?? '').length > 0;\n  }\n\n  if (t.isUnaryExpression(expression, { operator: '!' })) {\n    const staticTruthiness = getStaticExpressionTruthiness(expression.argument);\n    return staticTruthiness === undefined ? undefined : !staticTruthiness;\n  }\n\n  return undefined;\n}\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/utils/constants.ts",
    "content": "export const RUNTIME_MODULE_NAME = 'react-native-boost/runtime';\n\n/**\n * The set of accessibility properties that need to be normalized.\n */\nexport const ACCESSIBILITY_PROPERTIES = new Set([\n  'accessibilityLabel',\n  'aria-label',\n  'accessibilityState',\n  'aria-busy',\n  'aria-checked',\n  'aria-disabled',\n  'aria-expanded',\n  'aria-selected',\n  'accessible',\n]);\n\n// Maps the `userSelect` values to the corresponding boolean for the `selectable` prop\nexport const USER_SELECT_STYLE_TO_SELECTABLE_PROP: Record<string, boolean> = {\n  auto: true,\n  text: true,\n  none: false,\n  contain: true,\n  all: true,\n};\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/utils/format-test-result.ts",
    "content": "import type { ResultFormatter } from 'babel-plugin-tester';\nimport { format, type FormatOptions } from 'oxfmt';\nimport oxfmtConfig from '../../../../../.oxfmtrc.json';\n\ntype RootOxfmtConfig = FormatOptions & {\n  $schema?: string;\n  ignorePatterns?: string[];\n};\n\nconst oxfmtOptions = buildOxfmtOptions(oxfmtConfig as RootOxfmtConfig);\n\nexport const formatTestResult: ResultFormatter = async (code, options) => {\n  const filepath = options?.filepath ?? 'output.js';\n  const result = await format(filepath, code, oxfmtOptions);\n\n  if (result.errors.length > 0) {\n    throw new Error(result.errors[0].message);\n  }\n\n  return result.code;\n};\n\nfunction buildOxfmtOptions(config: RootOxfmtConfig): FormatOptions {\n  const { $schema, ignorePatterns, ...oxfmtOptions } = config;\n  void $schema;\n  void ignorePatterns;\n\n  return oxfmtOptions;\n}\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/utils/generate-test-plugin.ts",
    "content": "import { declare } from '@babel/helper-plugin-utils';\nimport { Optimizer, PluginOptions } from '../types';\nimport { createLogger } from './logger';\n\nexport const generateTestPlugin = (optimizer: Optimizer, options: PluginOptions = {}) => {\n  const logger = createLogger({\n    verbose: false,\n    silent: true,\n  });\n\n  return declare((api) => {\n    api.assertVersion(7);\n\n    return {\n      name: 'react-native-boost',\n      visitor: {\n        JSXOpeningElement(path) {\n          optimizer(path, logger, options);\n        },\n      },\n    };\n  });\n};\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/utils/helpers.ts",
    "content": "export const ensureArray = <T>(value: T | T[]): T[] => {\n  if (Array.isArray(value)) return value;\n  return [value];\n};\n\nexport type BailoutCheck = {\n  reason: string;\n  shouldBail: () => boolean;\n};\n\nexport const getFirstBailoutReason = (checks: readonly BailoutCheck[]): string | null => {\n  for (const check of checks) {\n    if (check.shouldBail()) {\n      return check.reason;\n    }\n  }\n\n  return null;\n};\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/utils/logger.ts",
    "content": "import {\n  HubFile,\n  OptimizationLogPayload,\n  PluginLogger,\n  SkippedOptimizationLogPayload,\n  WarningLogPayload,\n} from '../types';\n\nconst LOG_PREFIX = '[react-native-boost]';\n\nconst ANSI_RESET = '\\u001B[0m';\nconst ANSI_GREEN = '\\u001B[32m';\nconst ANSI_YELLOW = '\\u001B[33m';\nconst ANSI_MAGENTA = '\\u001B[35m';\nconst ANSI_RED = '\\u001B[31m';\n\nexport const noopLogger: PluginLogger = {\n  optimized() {},\n  skipped() {},\n  forced() {},\n  warning() {},\n};\n\nexport const createLogger = ({ verbose, silent }: { verbose: boolean; silent: boolean }): PluginLogger => {\n  if (silent) return noopLogger;\n\n  return {\n    optimized(payload) {\n      writeLog('optimized', `Optimized ${payload.component} in ${formatPathLocation(payload.path)}`);\n    },\n    skipped(payload) {\n      if (!verbose) return;\n      writeLog('skipped', `Skipped ${payload.component} in ${formatPathLocation(payload.path)} (${payload.reason})`);\n    },\n    forced(payload) {\n      writeLog(\n        'forced',\n        `Force-optimized ${payload.component} in ${formatPathLocation(payload.path)} (skipped bailout: ${payload.reason})`\n      );\n    },\n    warning(payload) {\n      const context = formatWarningContext(payload);\n      const message = context.length > 0 ? `${context}: ${payload.message}` : payload.message;\n      writeLog('warning', message);\n    },\n  };\n};\n\nfunction formatWarningContext(payload: WarningLogPayload): string {\n  const location = formatPathLocation(payload.path);\n\n  if (payload.component && location.length > 0) {\n    return `${payload.component} in ${location}`;\n  }\n\n  if (payload.component) {\n    return payload.component;\n  }\n\n  return location;\n}\n\ntype LogLevel = 'optimized' | 'skipped' | 'forced' | 'warning';\n\nfunction writeLog(level: LogLevel, message: string): void {\n  const levelTag = formatLevel(level);\n  console.log(`${LOG_PREFIX} ${levelTag} ${message}`);\n}\n\nfunction formatLevel(level: LogLevel): string {\n  if (level === 'optimized') {\n    return colorize('[optimized]', ANSI_GREEN);\n  }\n\n  if (level === 'skipped') {\n    return colorize('[skipped]', ANSI_YELLOW);\n  }\n\n  if (level === 'forced') {\n    return colorize('[forced]', ANSI_RED);\n  }\n\n  return colorize('[warning]', ANSI_MAGENTA);\n}\n\nfunction colorize(value: string, colorCode: string): string {\n  if (!shouldUseColor()) return value;\n  return `${colorCode}${value}${ANSI_RESET}`;\n}\n\nfunction shouldUseColor(): boolean {\n  if (process.env.NO_COLOR != null) return false;\n\n  if (process.env.FORCE_COLOR === '0') return false;\n  if (process.env.FORCE_COLOR != null) return true;\n\n  if (process.env.CLICOLOR === '0') return false;\n  if (process.env.CLICOLOR_FORCE != null && process.env.CLICOLOR_FORCE !== '0') return true;\n\n  if (process.stdout?.isTTY === true || process.stderr?.isTTY === true) {\n    return true;\n  }\n\n  const colorTerm = process.env.COLORTERM;\n  if (colorTerm != null && colorTerm !== '') {\n    return true;\n  }\n\n  const term = process.env.TERM;\n  return term != null && term !== '' && term.toLowerCase() !== 'dumb';\n}\n\nfunction formatPathLocation(\n  payloadPath: OptimizationLogPayload['path'] | SkippedOptimizationLogPayload['path'] | undefined\n): string {\n  if (!payloadPath) return 'unknown file:unknown line';\n\n  const hub = payloadPath.hub as unknown;\n  const file = typeof hub === 'object' && hub !== null && 'file' in hub ? (hub.file as HubFile) : undefined;\n  const filename = file?.opts?.filename ?? 'unknown file';\n  const lineNumber = payloadPath.node.loc?.start.line ?? 'unknown line';\n\n  return `${filename}:${lineNumber}`;\n}\n"
  },
  {
    "path": "packages/react-native-boost/src/plugin/utils/plugin-error.ts",
    "content": "export default class PluginError extends Error {\n  constructor(message: string) {\n    super(`[react-native-boost] Babel plugin exception: ${message}`);\n    this.name = 'PluginError';\n  }\n}\n"
  },
  {
    "path": "packages/react-native-boost/src/runtime/__tests__/index.test.ts",
    "content": "import { vi, describe, it, expect } from 'vitest';\nimport {\n  processTextStyle,\n  processAccessibilityProps,\n  userSelectToSelectableMap,\n  verticalAlignToTextAlignVerticalMap,\n} from '..';\nimport { TextStyle } from 'react-native';\n\nvi.mock('../components/native-text', () => ({\n  NativeText: () => 'MockedNativeText',\n}));\n\nvi.mock('../components/native-view', () => ({\n  NativeView: () => 'MockedNativeView',\n}));\n\nvi.mock('react-native', () => ({\n  View: () => 'View',\n  Text: () => 'Text',\n  Platform: {\n    OS: 'ios',\n  },\n  StyleSheet: {\n    flatten: (style: any) => style,\n  },\n}));\n\ndescribe('processTextStyle', () => {\n  it('returns empty object for falsy style', () => {\n    expect(processTextStyle(null)).toEqual({});\n    expect(processTextStyle()).toEqual({});\n  });\n\n  it('caches computed props', () => {\n    const style = { color: 'red' };\n    const result1 = processTextStyle(style);\n    const result2 = processTextStyle(style);\n    expect(result1).toBe(result2);\n  });\n\n  it('converts numeric fontWeight to string', () => {\n    const style = { fontWeight: 400 } as const;\n    const result = processTextStyle(style);\n    expect(result.style).toBeDefined();\n    expect(result.style).toBeInstanceOf(Object);\n    expect((result.style as TextStyle).fontWeight).toBe('400');\n  });\n\n  it('maps userSelect to selectable and removes userSelect from style', () => {\n    const style = { userSelect: 'none', color: 'blue' } as const;\n    const result = processTextStyle(style);\n    expect(result.selectable).toBe(userSelectToSelectableMap['none']);\n    expect(result.style).toBeDefined();\n    expect(result.style).toBeInstanceOf(Object);\n    expect((result.style as TextStyle).userSelect).toBeUndefined();\n    expect((result.style as TextStyle).color).toBe('blue');\n  });\n\n  it('maps verticalAlign to textAlignVertical and removes verticalAlign from style', () => {\n    const style = { verticalAlign: 'top', fontSize: 16 } as const;\n    const result = processTextStyle(style);\n    expect(result.style).toBeDefined();\n    expect(result.style).toBeInstanceOf(Object);\n    expect((result.style as TextStyle).textAlignVertical).toBe(verticalAlignToTextAlignVerticalMap['top']);\n    expect((result.style as TextStyle).verticalAlign).toBeUndefined();\n  });\n\n  it('handles combination of properties', () => {\n    const style = {\n      fontWeight: 700,\n      userSelect: 'auto',\n      verticalAlign: 'middle',\n      margin: 10,\n    } as const;\n    const result = processTextStyle(style);\n    expect(result.style).toBeDefined();\n    expect(result.style).toBeInstanceOf(Object);\n    expect((result.style as TextStyle).fontWeight).toBe('700');\n    expect(result.selectable).toBe(userSelectToSelectableMap['auto']);\n    expect((result.style as TextStyle).textAlignVertical).toBe(verticalAlignToTextAlignVerticalMap['middle']);\n    expect((result.style as TextStyle).margin).toBe(10);\n    expect((result.style as TextStyle).userSelect).toBeUndefined();\n    expect((result.style as TextStyle).verticalAlign).toBeUndefined();\n  });\n});\n\ndescribe('processAccessibilityProps', () => {\n  it('sets default accessible to true and has no accessibilityLabel if not provided', () => {\n    const props = {};\n    const normalized = processAccessibilityProps(props);\n    expect(normalized.accessible).toBe(true);\n    expect(normalized.accessibilityLabel).toBeUndefined();\n    expect(normalized.accessibilityState).toBeUndefined();\n  });\n\n  it('merges accessibility labels using aria-label over accessibilityLabel', () => {\n    const props = {\n      'accessibilityLabel': 'Label one',\n      'aria-label': 'Label two',\n    };\n    const normalized = processAccessibilityProps(props);\n    expect(normalized.accessibilityLabel).toBe('Label two');\n  });\n\n  it('keeps accessibilityLabel if aria-label is not provided', () => {\n    const props = {\n      accessibilityLabel: 'Only label',\n    };\n    const normalized = processAccessibilityProps(props);\n    expect(normalized.accessibilityLabel).toBe('Only label');\n  });\n\n  it('creates accessibilityState from ARIA properties when accessibilityState is not provided', () => {\n    const props = {\n      'aria-busy': true,\n      'aria-disabled': false,\n      'aria-selected': true,\n    };\n    const normalized = processAccessibilityProps(props);\n    expect(normalized.accessibilityState).toEqual({\n      busy: true,\n      checked: undefined,\n      disabled: false,\n      expanded: undefined,\n      selected: true,\n    });\n  });\n\n  it('merges ARIA properties with existing accessibilityState', () => {\n    const props = {\n      'accessibilityState': { busy: false, checked: false },\n      'aria-busy': true, // should override busy\n      'aria-disabled': true, // new property\n    };\n    const normalized = processAccessibilityProps(props);\n    expect(normalized.accessibilityState).toEqual({\n      busy: true,\n      checked: false,\n      disabled: true,\n      expanded: undefined,\n      selected: undefined,\n    });\n  });\n\n  it('retains additional properties', () => {\n    const props = {\n      'foo': 'bar',\n      'aria-expanded': false,\n    };\n    const normalized = processAccessibilityProps(props);\n    expect(normalized.foo).toBe('bar');\n    expect(normalized.accessibilityState).toEqual({\n      busy: undefined,\n      checked: undefined,\n      disabled: undefined,\n      expanded: false,\n      selected: undefined,\n    });\n  });\n\n  it('uses provided accessible if it exists', () => {\n    const props = {\n      accessible: false,\n    };\n    const normalized = processAccessibilityProps(props);\n    expect(normalized.accessible).toBe(false);\n  });\n});\n"
  },
  {
    "path": "packages/react-native-boost/src/runtime/__tests__/mocks/react-native.ts",
    "content": "export const View = () => 'View';\nexport const Text = () => 'Text';\n\nexport const Platform = {\n  OS: 'ios',\n};\n\nexport const StyleSheet = {\n  flatten: <T>(style: T) => style,\n};\n"
  },
  {
    "path": "packages/react-native-boost/src/runtime/components/native-text.tsx",
    "content": "/* eslint-disable @typescript-eslint/no-require-imports,unicorn/prefer-module */\n\nimport type { ComponentType } from 'react';\nimport type { TextProps } from 'react-native';\n\nconst reactNative = require('react-native');\nconst isWeb = reactNative.Platform.OS === 'web';\n\nlet nativeText = reactNative.unstable_NativeText;\n\nif (isWeb || nativeText == null) {\n  // Fallback to regular Text component if unstable_NativeText is not available or we're on Web\n  nativeText = reactNative.Text;\n}\n\n/**\n * Native Text component with graceful fallback.\n *\n * @remarks\n * Uses `unstable_NativeText` on supported native runtimes and falls back to `Text`\n * on web or when the unstable export is unavailable.\n */\nexport const NativeText: ComponentType<TextProps> = nativeText;\n"
  },
  {
    "path": "packages/react-native-boost/src/runtime/components/native-view.tsx",
    "content": "/* eslint-disable @typescript-eslint/no-require-imports,unicorn/prefer-module */\n\nimport type { ComponentType } from 'react';\nimport type { ViewProps } from 'react-native';\n\nconst reactNative = require('react-native');\nconst isWeb = reactNative.Platform.OS === 'web';\n\nlet nativeView = reactNative.unstable_NativeView;\n\nif (isWeb || nativeView == null) {\n  // Fallback to regular View component if unstable_NativeView is not available or we're on Web\n  nativeView = reactNative.View;\n}\n\n/**\n * Native View component with graceful fallback.\n *\n * @remarks\n * Uses `unstable_NativeView` on supported native runtimes and falls back to `View`\n * on web or when the unstable export is unavailable.\n */\nexport const NativeView: ComponentType<ViewProps> = nativeView;\n"
  },
  {
    "path": "packages/react-native-boost/src/runtime/index.ts",
    "content": "import { TextProps, TextStyle, StyleSheet } from 'react-native';\nimport { GenericStyleProp } from './types';\nimport { userSelectToSelectableMap, verticalAlignToTextAlignVerticalMap } from './utils/constants';\n\nconst propsCache = new WeakMap();\n\n/**\n * Normalizes `Text` style values for `NativeText`.\n *\n * @param style - Style prop passed to a text-like component.\n * @returns Native-friendly text props. Returns an empty object when `style` is falsy or cannot be normalized.\n * @remarks\n * - Flattens style arrays via `StyleSheet.flatten`\n * - Converts numeric `fontWeight` values to string values\n * - Maps `userSelect` and `verticalAlign` to native-compatible props\n */\nexport function processTextStyle(style: GenericStyleProp<TextStyle>): Partial<TextProps> {\n  if (!style) return {};\n\n  // Cache the computed props\n  let props = propsCache.get(style);\n  if (props) return props;\n\n  props = {};\n  propsCache.set(style, props);\n\n  style = StyleSheet.flatten(style) as TextStyle;\n\n  if (!style) return {};\n\n  if (typeof style?.fontWeight === 'number') {\n    style.fontWeight = style.fontWeight.toString() as TextStyle['fontWeight'];\n  }\n\n  if (style?.userSelect != null) {\n    props.selectable = userSelectToSelectableMap[style.userSelect];\n    delete style.userSelect;\n  }\n\n  if (style?.verticalAlign != null) {\n    style.textAlignVertical = verticalAlignToTextAlignVerticalMap[\n      style.verticalAlign\n    ] as TextStyle['textAlignVertical'];\n    delete style.verticalAlign;\n  }\n\n  props.style = style;\n  return props;\n}\n\n/**\n * Normalizes accessibility and ARIA props for runtime native components.\n *\n * @param props - Accessibility and ARIA props.\n * @returns Props with normalized accessibility fields.\n * @remarks\n * - Merges `aria-label` with `accessibilityLabel`\n * - Merges ARIA state fields into `accessibilityState`\n * - Defaults `accessible` to `true` when omitted\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function processAccessibilityProps(props: Record<string, any>): Record<string, any> {\n  const {\n    accessibilityLabel,\n    ['aria-label']: ariaLabel,\n    accessibilityState,\n    ['aria-busy']: ariaBusy,\n    ['aria-checked']: ariaChecked,\n    ['aria-disabled']: ariaDisabled,\n    ['aria-expanded']: ariaExpanded,\n    ['aria-selected']: ariaSelected,\n    accessible,\n    ...restProperties\n  } = props;\n\n  // Merge label props: prefer the aria-label if defined.\n  const normalizedLabel = ariaLabel ?? accessibilityLabel;\n\n  // Merge the accessibilityState with any provided ARIA properties.\n  let normalizedState = accessibilityState;\n  if (ariaBusy != null || ariaChecked != null || ariaDisabled != null || ariaExpanded != null || ariaSelected != null) {\n    normalizedState =\n      normalizedState == null\n        ? {\n            busy: ariaBusy,\n            checked: ariaChecked,\n            disabled: ariaDisabled,\n            expanded: ariaExpanded,\n            selected: ariaSelected,\n          }\n        : {\n            busy: ariaBusy ?? normalizedState.busy,\n            checked: ariaChecked ?? normalizedState.checked,\n            disabled: ariaDisabled ?? normalizedState.disabled,\n            expanded: ariaExpanded ?? normalizedState.expanded,\n            selected: ariaSelected ?? normalizedState.selected,\n          };\n  }\n\n  // For the accessible prop, if not provided, default to `true`\n  const normalizedAccessible = accessible == null ? true : accessible;\n\n  return {\n    ...restProperties,\n    accessibilityLabel: normalizedLabel,\n    accessibilityState: normalizedState,\n    accessible: normalizedAccessible,\n  };\n}\n\nexport * from './types';\nexport * from './utils/constants';\nexport * from './components/native-text';\nexport * from './components/native-view';\n"
  },
  {
    "path": "packages/react-native-boost/src/runtime/index.web.ts",
    "content": "// This is a dummy file to ensure that nothing breaks when using the runtime in a web environment.\n\nimport { TextProps, TextStyle } from 'react-native';\nimport { GenericStyleProp } from './types';\n\nexport const processTextStyle = (style: GenericStyleProp<TextStyle>) => ({ style }) as Partial<TextProps>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function processAccessibilityProps(props: Record<string, any>): Record<string, any> {\n  return props;\n}\n\nexport * from './types';\nexport * from './utils/constants';\n\n// On Web, the native components are not available, so we use the standard components that'll be replaced by their DOM\n// equivalents by react-native-web.\n/* eslint-disable @typescript-eslint/no-require-imports,unicorn/prefer-module */\nexport const NativeText = require('react-native').Text;\nexport const NativeView = require('react-native').View;\n/* eslint-enable @typescript-eslint/no-require-imports,unicorn/prefer-module */\n"
  },
  {
    "path": "packages/react-native-boost/src/runtime/types/index.ts",
    "content": "/**\n * Recursive style prop shape accepted by runtime style helpers.\n *\n * @template T - Style object type.\n */\nexport type GenericStyleProp<T> = null | void | T | false | '' | ReadonlyArray<GenericStyleProp<T>>;\n"
  },
  {
    "path": "packages/react-native-boost/src/runtime/types/react-native.d.ts",
    "content": "declare module 'react-native/Libraries/Text/TextNativeComponent' {\n  export const NativeText: React.ComponentType<TextProps>;\n}\n\ndeclare module 'react-native/Libraries/Components/View/ViewNativeComponent' {\n  export default React.ComponentType<ViewProps>;\n}\n"
  },
  {
    "path": "packages/react-native-boost/src/runtime/utils/constants.ts",
    "content": "/**\n * Maps CSS-like `userSelect` values to React Native's `selectable` prop.\n */\nexport const userSelectToSelectableMap = {\n  auto: true,\n  text: true,\n  none: false,\n  contain: true,\n  all: true,\n};\n\n/**\n * Maps CSS-like `verticalAlign` values to React Native's `textAlignVertical`.\n */\nexport const verticalAlignToTextAlignVerticalMap = {\n  auto: 'auto',\n  top: 'top',\n  bottom: 'bottom',\n  middle: 'center',\n};\n"
  },
  {
    "path": "packages/react-native-boost/tsconfig.build.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"noEmit\": false,\n    \"outDir\": \"./dist\",\n    \"emitDeclarationOnly\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"node_modules\", \"plugin\", \"fixtures\", \"scripts\"]\n}\n"
  },
  {
    "path": "packages/react-native-boost/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {},\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"node_modules\", \"plugin\", \"fixtures\", \"scripts\"]\n}\n"
  },
  {
    "path": "packages/react-native-boost/vitest.config.ts",
    "content": "import { fileURLToPath } from 'node:url';\nimport { resolve } from 'node:path';\nimport { defineConfig } from 'vitest/config';\n\nconst runtimeMockPath = fileURLToPath(new URL('src/runtime/__tests__/mocks/react-native.ts', import.meta.url));\n\nexport default defineConfig({\n  test: {\n    // babel-plugin-tester requires it and describe to be set globally\n    globals: true,\n  },\n  resolve: {\n    alias: {\n      'react-native': resolve(runtimeMockPath),\n    },\n  },\n});\n"
  },
  {
    "path": "packages/react-native-time-to-render/.gitignore",
    "content": "/android/build\n"
  },
  {
    "path": "packages/react-native-time-to-render/LICENSE",
    "content": "MIT License\n\nCopyright (c) Kuatsu App Agency\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "packages/react-native-time-to-render/README.md",
    "content": "# Time to Render Benchmarking Module\n\nThis module is used by the React Native Boost example app to create render benchmarks.\n\n## Attribution\n\nThe module was taken from the `RTNTimeToRender` module by [@sammy-SC](https://github.com/sammy-SC) from [react-native-community/RNNewArchitectureApp](https://github.com/react-native-community/RNNewArchitectureApp/tree/new-architecture-benchmarks).\n"
  },
  {
    "path": "packages/react-native-time-to-render/TimeToRender.podspec",
    "content": "require \"json\"\n\npackage = JSON.parse(File.read(File.join(__dir__, \"package.json\")))\nfolly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'\n\nPod::Spec.new do |s|\n  s.name         = \"TimeToRender\"\n  s.version      = package[\"version\"]\n  s.summary      = package[\"description\"]\n  s.homepage     = package[\"homepage\"]\n  s.license      = package[\"license\"]\n  s.authors      = package[\"author\"]\n\n  s.platforms    = { :ios => min_ios_version_supported }\n  s.source       = { :git => \".git\", :tag => \"#{s.version}\" }\n\n  s.source_files = \"ios/**/*.{h,m,mm,cpp}\"\n  s.private_header_files = \"ios/generated/**/*.h\"\n\n  # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.\n  # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.\n  if respond_to?(:install_modules_dependencies, true)\n    install_modules_dependencies(s)\n  else\n    s.dependency \"React-Core\"\n\n    # Don't install the dependencies when we run `pod install` in the old architecture.\n    if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then\n      s.compiler_flags = folly_compiler_flags + \" -DRCT_NEW_ARCH_ENABLED=1\"\n      s.pod_target_xcconfig    = {\n          \"HEADER_SEARCH_PATHS\" => \"\\\"$(PODS_ROOT)/boost\\\"\",\n          \"OTHER_CPLUSPLUSFLAGS\" => \"-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1\",\n          \"CLANG_CXX_LANGUAGE_STANDARD\" => \"c++17\"\n      }\n      s.dependency \"React-RCTFabric\"\n      s.dependency \"React-Codegen\"\n      s.dependency \"RCT-Folly\"\n      s.dependency \"RCTRequired\"\n      s.dependency \"RCTTypeSafety\"\n      s.dependency \"ReactCommon/turbomodule/core\"\n    end\n  end\nend\n"
  },
  {
    "path": "packages/react-native-time-to-render/android/build.gradle",
    "content": "buildscript {\n  ext.getExtOrDefault = {name ->\n    return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['TimeToRender_' + name]\n  }\n\n  repositories {\n    google()\n    mavenCentral()\n  }\n\n  dependencies {\n    classpath \"com.android.tools.build:gradle:8.7.2\"\n    // noinspection DifferentKotlinGradleVersion\n    classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}\"\n  }\n}\n\n\ndef isNewArchitectureEnabled() {\n  return rootProject.hasProperty(\"newArchEnabled\") && rootProject.getProperty(\"newArchEnabled\") == \"true\"\n}\n\napply plugin: \"com.android.library\"\napply plugin: \"kotlin-android\"\n\nif (isNewArchitectureEnabled()) {\n  apply plugin: \"com.facebook.react\"\n}\n\ndef getExtOrIntegerDefault(name) {\n  return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties[\"TimeToRender_\" + name]).toInteger()\n}\n\ndef supportsNamespace() {\n  def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')\n  def major = parsed[0].toInteger()\n  def minor = parsed[1].toInteger()\n\n  // Namespace support was added in 7.3.0\n  return (major == 7 && minor >= 3) || major >= 8\n}\n\nandroid {\n  if (supportsNamespace()) {\n    namespace \"com.timetorender\"\n\n    sourceSets {\n      main {\n        manifest.srcFile \"src/main/AndroidManifestNew.xml\"\n      }\n    }\n  }\n\n  compileSdkVersion getExtOrIntegerDefault(\"compileSdkVersion\")\n\n  defaultConfig {\n    minSdkVersion getExtOrIntegerDefault(\"minSdkVersion\")\n    targetSdkVersion getExtOrIntegerDefault(\"targetSdkVersion\")\n    buildConfigField \"boolean\", \"IS_NEW_ARCHITECTURE_ENABLED\", isNewArchitectureEnabled().toString()\n  }\n\n  buildFeatures {\n    buildConfig true\n  }\n\n  buildTypes {\n    release {\n      minifyEnabled false\n    }\n  }\n\n  lintOptions {\n    disable \"GradleCompatible\"\n  }\n\n  compileOptions {\n    sourceCompatibility JavaVersion.VERSION_1_8\n    targetCompatibility JavaVersion.VERSION_1_8\n  }\n\n  sourceSets {\n    main {\n      if (isNewArchitectureEnabled()) {\n          java.srcDirs += [\n            \"generated/java\",\n            \"generated/jni\"\n          ]\n      }\n    }\n  }\n}\n\nrepositories {\n  mavenCentral()\n  google()\n}\n\ndef kotlin_version = getExtOrDefault(\"kotlinVersion\")\n\ndependencies {\n  implementation \"com.facebook.react:react-android\"\n  implementation \"org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version\"\n}\n\nif (isNewArchitectureEnabled()) {\n  react {\n    jsRootDir = file(\"../src/\")\n    libraryName = \"TimeToRender\"\n    codegenJavaPackageName = \"com.timetorender\"\n  }\n}\n"
  },
  {
    "path": "packages/react-native-time-to-render/android/gradle.properties",
    "content": "TimeToRender_kotlinVersion=2.0.21\nTimeToRender_minSdkVersion=24\nTimeToRender_targetSdkVersion=34\nTimeToRender_compileSdkVersion=35\nTimeToRender_ndkVersion=27.1.12297006\n"
  },
  {
    "path": "packages/react-native-time-to-render/android/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          package=\"com.timetorender\">\n</manifest>\n"
  },
  {
    "path": "packages/react-native-time-to-render/android/src/main/AndroidManifestNew.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n</manifest>\n"
  },
  {
    "path": "packages/react-native-time-to-render/android/src/main/java/com/timetorender/MarkerStore.kt",
    "content": "package com.timetorender\n\nimport java.util.Date\nimport kotlin.collections.mutableMapOf\nimport android.os.SystemClock\n\nclass MarkerStore(private val markers: MutableMap<String, Long> = mutableMapOf()) {\n\n    companion object {\n        val mainStore = MarkerStore()\n        fun JSTimeIntervalSinceStartup() = SystemClock.uptimeMillis()\n    }\n\n    fun startMarker(marker: String, timeSinceStartup: Long) {\n        markers.put(marker, timeSinceStartup)\n    }\n\n    fun endMarker(marker: String) : Long {\n        val markerStart = markers[marker]\n        val markerEnd = JSTimeIntervalSinceStartup()\n        markers.remove(marker)\n        return markerEnd - markerStart!!\n    }\n}\n"
  },
  {
    "path": "packages/react-native-time-to-render/android/src/main/java/com/timetorender/OnMarkerPaintedEvent.kt",
    "content": "package com.timetorender\n\nimport com.facebook.react.bridge.Arguments\nimport com.facebook.react.bridge.WritableMap\nimport com.facebook.react.uimanager.events.Event\n\ninternal class OnMarkerPaintedEvent(surfaceId: Int, viewId: Int, val paintTime: Long) : Event<OnMarkerPaintedEvent>(surfaceId, viewId) {\n\n    @Deprecated(\"\")\n    constructor(viewId: Int, paintTime: Long) : this(-1, viewId, paintTime) {\n    }\n\n    override fun getEventName() = EVENT_NAME\n\n    override protected fun getEventData(): WritableMap {\n        val eventData: WritableMap = Arguments.createMap()\n        eventData.putDouble(\"paintTime\", paintTime.toDouble())\n        eventData.putInt(\"target\", getViewTag())\n        return eventData\n    }\n\n    companion object {\n        const val EVENT_NAME = \"topMarkerPainted\"\n    }\n}\n"
  },
  {
    "path": "packages/react-native-time-to-render/android/src/main/java/com/timetorender/TimeToRenderModule.kt",
    "content": "package com.timetorender\n\nimport com.facebook.react.bridge.Promise\nimport com.facebook.react.bridge.ReactApplicationContext\nimport com.timetorender.NativeTimeToRenderSpec\n\nclass TimeToRenderModule(reactContext: ReactApplicationContext) : NativeTimeToRenderSpec(reactContext) {\n\n  override fun getName() = NAME\n\n  override fun startMarker(name: String, time: Double) {\n    MarkerStore.mainStore.startMarker(name, time.toLong())\n  }\n\n  companion object {\n    const val NAME = \"TimeToRender\"\n  }\n}\n"
  },
  {
    "path": "packages/react-native-time-to-render/android/src/main/java/com/timetorender/TimeToRenderPackage.kt",
    "content": "package com.timetorender;\n\nimport com.facebook.react.BaseReactPackage\nimport com.facebook.react.bridge.NativeModule\nimport com.facebook.react.bridge.ReactApplicationContext\nimport com.facebook.react.module.model.ReactModuleInfo;\nimport com.facebook.react.module.model.ReactModuleInfoProvider\nimport kotlin.collections.listOf\nimport com.facebook.react.uimanager.ViewManager\n\nclass TimeToRenderPackage : BaseReactPackage() {\n\n    override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> =\n            listOf(TimeToRenderViewManager(reactContext))\n\n    override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? =\n            if (name == TimeToRenderModule.NAME) {\n                TimeToRenderModule(reactContext)\n            } else {\n                null\n            }\n\n    override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {\n        mapOf(\n                TimeToRenderModule.NAME to ReactModuleInfo(\n                        TimeToRenderModule.NAME,\n                        TimeToRenderModule.NAME,\n                        false, // canOverrideExistingModule\n                        false, // needsEagerInit\n                        true, // hasConstants\n                        false, // isCxxModule\n                        true // isTurboModule\n                )\n        )\n    }\n}\n"
  },
  {
    "path": "packages/react-native-time-to-render/android/src/main/java/com/timetorender/TimeToRenderView.kt",
    "content": "package com.timetorender\n\nimport android.content.Context\nimport android.graphics.Canvas\nimport android.util.AttributeSet\nimport android.view.View\nimport com.facebook.react.bridge.ReactContext\nimport com.facebook.react.uimanager.UIManagerHelper\n\nclass TimeToRenderView  : View {\n    var _alreadyLogged = false\n    var _markerName: String? = \"\"\n    constructor(context: Context?) : super(context) {\n        configureComponent()\n    }\n\n    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {\n        configureComponent()\n    }\n\n    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        configureComponent()\n    }\n\n    fun setMarkerName(markerName: String?) {\n        _markerName = markerName\n    }\n\n    private fun configureComponent() {\n        // do nothing\n    }\n\n    override protected fun onDraw(canvas: Canvas) {\n        if (getParent() != null && !_alreadyLogged) {\n            _alreadyLogged = true\n            val paintTime = MarkerStore.mainStore.endMarker(_markerName!!)\n            android.util.Log.e(\"DAVID\", \"Logging end of marker: $paintTime\")\n            val reactContext: ReactContext = getContext() as ReactContext\n            val reactTag: Int = getId()\n            UIManagerHelper.getEventDispatcherForReactTag(reactContext, reactTag)\n                    ?.dispatchEvent(\n                            OnMarkerPaintedEvent(\n                                    UIManagerHelper.getSurfaceId(reactContext), reactTag, paintTime))\n        }\n    }\n}\n"
  },
  {
    "path": "packages/react-native-time-to-render/android/src/main/java/com/timetorender/TimeToRenderViewManager.kt",
    "content": "package com.timetorender\n\nimport com.facebook.react.bridge.ReactApplicationContext\nimport com.facebook.react.module.annotations.ReactModule\nimport com.facebook.react.uimanager.SimpleViewManager\nimport com.facebook.react.uimanager.ThemedReactContext\nimport com.facebook.react.uimanager.ViewManagerDelegate\nimport com.facebook.react.uimanager.annotations.ReactProp\nimport com.facebook.react.viewmanagers.TimeToRenderManagerDelegate\nimport com.facebook.react.viewmanagers.TimeToRenderManagerInterface\nimport kotlin.collections.mapOf\nimport kotlin.collections.mutableMapOf\n\n@ReactModule(name = TimeToRenderViewManager.NAME)\nclass TimeToRenderViewManager(context: ReactApplicationContext) : SimpleViewManager<TimeToRenderView>(), TimeToRenderManagerInterface<TimeToRenderView> {\n    // Documentation is incorrect in the next line:\n    private val delegate: ViewManagerDelegate<TimeToRenderView> = TimeToRenderManagerDelegate(this)\n\n    override fun getDelegate(): ViewManagerDelegate<TimeToRenderView> = delegate\n\n    override fun getName(): String = NAME\n\n    override fun createViewInstance(context: ThemedReactContext): TimeToRenderView = TimeToRenderView(context)\n\n    @ReactProp(name = \"markerName\")\n    override fun setMarkerName(view: TimeToRenderView, markerName: String?) {\n        view.setMarkerName(markerName)\n    }\n\n    companion object {\n        const val NAME = \"TimeToRender\"\n    }\n\n    override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any>? {\n        val baseEventTypeConstants: Map<String, Any>? = super.getExportedCustomDirectEventTypeConstants()\n        val eventTypeConstants: MutableMap<String, Any> = baseEventTypeConstants?.toMutableMap()\n                ?: mutableMapOf()\n        eventTypeConstants.put(\n                OnMarkerPaintedEvent.EVENT_NAME,\n                mutableMapOf<String, String>(\"registrationName\" to \"onMarkerPainted\"))\n\n        return eventTypeConstants\n    }\n}\n"
  },
  {
    "path": "packages/react-native-time-to-render/ios/MarkerPaintComponentView.h",
    "content": "#import <UIKit/UIKit.h>\n#import <React/RCTViewComponentView.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface MarkerPaintComponentView : RCTViewComponentView\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "packages/react-native-time-to-render/ios/MarkerPaintComponentView.mm",
    "content": "#import \"MarkerPaintComponentView.h\"\n#import \"MarkerStore.h\"\n\n#import <React/RCTConversions.h>\n\n#import <react/renderer/components/RNTimeToRenderSpec/ComponentDescriptors.h>\n#import <react/renderer/components/RNTimeToRenderSpec/EventEmitters.h>\n#import <react/renderer/components/RNTimeToRenderSpec/Props.h>\n#import <react/renderer/components/RNTimeToRenderSpec/RCTComponentViewHelpers.h>\n#import \"RCTFabricComponentsPlugins.h\"\n\n\nusing namespace facebook::react;\n\n@implementation MarkerPaintComponentView {\n  BOOL _alreadyLogged;\n}\n\n- (instancetype)initWithFrame:(CGRect)frame\n{\n  if (self = [super initWithFrame:frame]) {\n    static const auto defaultProps = std::make_shared<TimeToRenderProps const>();\n    _props = defaultProps;\n  }\n\n  return self;\n}\n\n- (void)prepareForRecycle\n{\n  [super prepareForRecycle];\n  _alreadyLogged = NO;\n}\n\n- (void)didMoveToWindow {\n  [super didMoveToWindow];\n\n  if (_alreadyLogged) {\n    return;\n  }\n\n  if (!self.window) {\n    return;\n  }\n\n  _alreadyLogged = YES;\n\n  NSString *markerName = RCTNSStringFromString(std::static_pointer_cast<TimeToRenderProps const>(_props)->markerName);\n\n  // However, we cannot do it right now: the views were just mounted but pixels\n  // were not drawn on the screen yet.\n  // They will be drawn for sure before the next tick of the main run loop.\n  // Let's wait for that and then report.\n  dispatch_async(dispatch_get_main_queue(), ^{\n    NSTimeInterval paintTime = [[MarkerStore mainStore] endMarker:markerName];\n    std::dynamic_pointer_cast<TimeToRenderEventEmitter const>(self->_eventEmitter)->onMarkerPainted({.paintTime = paintTime});\n  });\n}\n\n\n+ (ComponentDescriptorProvider)componentDescriptorProvider\n{\n  return concreteComponentDescriptorProvider<TimeToRenderComponentDescriptor>();\n}\n\n@end\n\nClass<RCTComponentViewProtocol> TimeToRenderCls(void)\n{\n  return MarkerPaintComponentView.class;\n}\n"
  },
  {
    "path": "packages/react-native-time-to-render/ios/MarkerStore.h",
    "content": "#import <Foundation/Foundation.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface MarkerStore : NSObject\n\n+ (id)mainStore;\n+ (NSTimeInterval)JSTimeIntervalSinceStartup;\n\n- (void)startMarker:(NSString *)marker timeSinceStartup:(NSTimeInterval)time;\n\n- (NSTimeInterval)endMarker:(NSString *)marker;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "packages/react-native-time-to-render/ios/MarkerStore.m",
    "content": "#import \"MarkerStore.h\"\n\n@implementation MarkerStore {\n    NSMutableDictionary <NSString *, NSNumber*> *_markers;\n}\n\n+ (id)mainStore {\n    static MarkerStore *mainMarkerStore = nil;\n    static dispatch_once_t onceToken;\n    dispatch_once(&onceToken, ^{\n        mainMarkerStore = [[self alloc] init];\n    });\n    return mainMarkerStore;\n}\n\n+ (NSTimeInterval)JSTimeIntervalSinceStartup {\n  return [[NSProcessInfo processInfo] systemUptime] * 1000;\n}\n\n- (id)init {\n  if (self = [super init]) {\n      _markers = [NSMutableDictionary new];\n  }\n  return self;\n}\n\n\n- (void)startMarker:(NSString *)marker timeSinceStartup:(NSTimeInterval)time {\n  [_markers setObject:@(time) forKey:marker];\n}\n\n- (NSTimeInterval)endMarker:(NSString *)marker {\n  NSTimeInterval markerStart = [_markers objectForKey:marker].doubleValue;\n  NSTimeInterval markerEnd = [MarkerStore JSTimeIntervalSinceStartup];\n  [_markers removeObjectForKey:marker];\n\n  return markerEnd - markerStart;\n}\n\n@end\n"
  },
  {
    "path": "packages/react-native-time-to-render/ios/PaintMarkerView.h",
    "content": "#import <UIKit/UIKit.h>\n#import <React/RCTView.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface PaintMarkerView : UIView\n\n@property (nonatomic, retain) NSString *markerName;\n@property (nonatomic, copy) RCTDirectEventBlock onMarkerPainted;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "packages/react-native-time-to-render/ios/PaintMarkerView.m",
    "content": "#import \"PaintMarkerView.h\"\n#import \"MarkerStore.h\"\n\n@implementation PaintMarkerView {\n  BOOL _alreadyLogged;\n}\n\n- (void)didMoveToWindow {\n  [super didMoveToWindow];\n\n  if (_alreadyLogged) {\n    return;\n  }\n\n  if (!self.window) {\n    return;\n  }\n\n  _alreadyLogged = YES;\n\n  // However, we cannot do it right now: the views were just mounted but pixels\n  // were not drawn on the screen yet.\n  // They will be drawn for sure before the next tick of the main run loop.\n  // Let's wait for that and then report.\n  dispatch_async(dispatch_get_main_queue(), ^{\n    NSTimeInterval paintTime = [[MarkerStore mainStore] endMarker:self.markerName];\n    self.onMarkerPainted(@{@\"paintTime\": @(paintTime)});\n  });\n}\n\n/*\n// Only override drawRect: if you perform custom drawing.\n// An empty implementation adversely affects performance during animation.\n- (void)drawRect:(CGRect)rect {\n    // Drawing code\n}\n*/\n\n@end\n"
  },
  {
    "path": "packages/react-native-time-to-render/ios/TimeToRender.h",
    "content": "\n#import \"RNTimeToRenderSpec/RNTimeToRenderSpec.h\"\n\n@interface TimeToRender : NSObject <NativeTimeToRenderSpec>\n\n@end\n"
  },
  {
    "path": "packages/react-native-time-to-render/ios/TimeToRender.mm",
    "content": "#import \"TimeToRender.h\"\n#import \"MarkerStore.h\"\n\n@implementation TimeToRender\nRCT_EXPORT_MODULE()\n\n- (void)startMarker:(NSString *)name time:(double)time {\n    [[MarkerStore mainStore] startMarker:name timeSinceStartup:time];\n}\n\n- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:\n    (const facebook::react::ObjCTurboModule::InitParams &)params\n{\n    return std::make_shared<facebook::react::NativeTimeToRenderSpecJSI>(params);\n}\n\n@end\n"
  },
  {
    "path": "packages/react-native-time-to-render/ios/TimeToRenderManager.h",
    "content": "#import <React/RCTViewManager.h>\n\n@interface TimeToRenderManager : RCTViewManager\n@end\n"
  },
  {
    "path": "packages/react-native-time-to-render/ios/TimeToRenderManager.m",
    "content": "#import <React/RCTViewManager.h>\n#import \"TimeToRenderManager.h\"\n#import \"PaintMarkerView.h\"\n\n@implementation TimeToRenderManager\n\nRCT_EXPORT_MODULE()\n\nRCT_EXPORT_VIEW_PROPERTY(markerName, NSString)\nRCT_EXPORT_VIEW_PROPERTY(onMarkerPainted, RCTDirectEventBlock)\n\n- (UIView *)view\n{\n  return [[PaintMarkerView alloc] init];\n}\n\n@end\n"
  },
  {
    "path": "packages/react-native-time-to-render/package.json",
    "content": "{\n  \"name\": \"react-native-time-to-render\",\n  \"version\": \"0.0.1\",\n  \"description\": \"Benchmarks the time to render\",\n  \"private\": true,\n  \"main\": \"src/index\",\n  \"codegenConfig\": {\n    \"name\": \"RNTimeToRenderSpec\",\n    \"type\": \"all\",\n    \"jsSrcsDir\": \"src\"\n  },\n  \"author\": \"Kuatsu App Agency <hello@kuatsu.de>\",\n  \"license\": \"MIT\",\n  \"homepage\": \"#readme\",\n  \"create-react-native-library\": {\n    \"type\": \"turbo-module\",\n    \"languages\": \"kotlin-objc\",\n    \"version\": \"0.48.2\"\n  }\n}\n"
  },
  {
    "path": "packages/react-native-time-to-render/react-native.config.js",
    "content": "/**\n * @type {import('@react-native-community/cli-types').UserDependencyConfig}\n */\nmodule.exports = {\n  dependency: {\n    platforms: {\n      android: {\n        cmakeListsPath: 'build/generated/source/codegen/jni/CMakeLists.txt',\n      },\n    },\n  },\n};\n"
  },
  {
    "path": "packages/react-native-time-to-render/src/NativeTimeToRender.ts",
    "content": "import type { TurboModule } from 'react-native';\nimport { TurboModuleRegistry } from 'react-native';\n\nexport interface Spec extends TurboModule {\n  startMarker(name: string, time: number): void;\n}\n\nexport default TurboModuleRegistry.getEnforcing<Spec>('TimeToRender');\n"
  },
  {
    "path": "packages/react-native-time-to-render/src/TimeToRenderNativeComponent.ts",
    "content": "import { codegenNativeComponent, type ViewProps } from 'react-native';\nimport { DirectEventHandler, Double } from 'react-native/Libraries/Types/CodegenTypes';\n\nexport interface NativeProps extends ViewProps {\n  markerName: string;\n  onMarkerPainted: DirectEventHandler<{ paintTime: Double }>;\n}\n\nexport default codegenNativeComponent<NativeProps>('TimeToRender');\n"
  },
  {
    "path": "packages/react-native-time-to-render/src/index.tsx",
    "content": "import TimeToRender from './NativeTimeToRender';\nexport { default as TimeToRenderView } from './TimeToRenderNativeComponent';\n\nexport function startMarker(name: string, time: number): void {\n  return TimeToRender.startMarker(name, time);\n}\n"
  },
  {
    "path": "pnpm-workspace.yaml",
    "content": "packages:\n  - apps/*\n  - packages/*\n\nonlyBuiltDependencies:\n  - core-js\n  - core-js-pure\n  - deasync\n  - esbuild\n  - sharp\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"lib\": [\"ESNext\"],\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"jsx\": \"react-native\",\n    \"target\": \"es2019\",\n    \"declaration\": true,\n    \"outDir\": \"./dist\",\n    \"types\": [\"node\"],\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"resolveJsonModule\": true,\n    \"noEmit\": true\n  }\n}\n"
  }
]