Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ coverage/
*.log
.DS_Store

# Native build output
# Native build output. `build/` would also hide the compiled Expo config
# plugin, which ships in the package, so that directory is un-ignored below.
android/build/
android/.gradle/
ios/build/
Expand All @@ -22,7 +23,8 @@ bugsee.properties
.env
.gradle/
local.properties
build/
**/build/**
!packages/react-native/plugin/build/**
.build/
.swiftpm/
*.xcodeproj/
Expand Down
2 changes: 1 addition & 1 deletion docs/design/plans/2026-09-16-implementation-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -4066,7 +4066,7 @@ The phase with the most native↔JS round-tripping, hence the most device testin
- [x] **13.1** Source maps: `bugsee-cli sourcemaps inject` then `debug-files upload --type sourcemaps`. Injection must happen on the **composed** Hermes map, after `compose-source-maps.js`, or the debug ID lands on a map nothing consults.
- [x] **13.2** dSYM upload as an Xcode **scheme post-action** running `bugsee-cli xcode post-action`. A build phase also works but needs `BUGSEE_BUILD_INFO_ALL_ACTIONS=1`, and Xcode 15+ defaults `ENABLE_USER_SCRIPT_SANDBOXING` to `YES`, which blocks it.
- [x] **13.3** Android mapping and NDK symbols — the Gradle plugin's job once applied. Write `android/bugsee.properties` with the **unprefixed** `app_token=` key (not `plugin.appToken`) at the *root* project, and default `plugin.ndk.enabled=true` for RN, since every RN app ships Hermes and `libreactnative.so` it did not write.
- [ ] **13.4** Expo config plugin, with a `prebuild --clean` test — the `.xcscheme` edit is the most fragile part and Expo has no helper for it.
- [x] **13.4** Expo config plugin, with a `prebuild --clean` test — the `.xcscheme` edit is the most fragile part and Expo has no helper for it. `CI=1 yarn expo prebuild --clean` in `examples/expo` exited 0 (CocoaPods installed) and left `bugsee-xcode.sh` on *Bundle React Native code and images* plus the Archive `Upload dSYMs` post-action.
- [ ] **13.5** End-to-end: a release build whose JS stack symbolicates in the dashboard. This is the only test that proves the whole chain.
- [ ] Review gate.

Expand Down
3 changes: 3 additions & 0 deletions examples/expo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ios/
android/
.expo/
8 changes: 8 additions & 0 deletions examples/expo/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const React = require('react');
const { Text, View } = require('react-native');

function App() {
return React.createElement(View, null, React.createElement(Text, null, 'Bugsee Expo'));
}

module.exports = App;
17 changes: 17 additions & 0 deletions examples/expo/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"expo": {
"name": "BugseeExpo",
"slug": "bugsee-expo",
"version": "1.0.0",
"orientation": "portrait",
"ios": {
"bundleIdentifier": "com.bugsee.expoexample"
},
"android": {
"package": "com.bugsee.expoexample"
},
"plugins": [
"@bugsee/react-native"
]
}
}
6 changes: 6 additions & 0 deletions examples/expo/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};
4 changes: 4 additions & 0 deletions examples/expo/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const { registerRootComponent } = require('expo');
const App = require('./App');

registerRootComponent(App);
3 changes: 3 additions & 0 deletions examples/expo/metro.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const { getDefaultConfig } = require('expo/metro-config');

module.exports = getDefaultConfig(__dirname);
19 changes: 19 additions & 0 deletions examples/expo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "bugsee-example-expo",
"version": "0.0.1",
"private": true,
"main": "index.js",
"scripts": {
"start": "expo start",
"prebuild": "expo prebuild --clean"
},
"dependencies": {
"@bugsee/react-native": "workspace:*",
"expo": "~57.0.26",
"react": "19.2.3",
"react-native": "0.86.3"
},
"devDependencies": {
"@babel/core": "^7.25.2"
}
}
4 changes: 4 additions & 0 deletions packages/react-native/app.plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Expo loads a package plugin from this file with Node's require().
const plugin = require('./plugin/build/index.js');

module.exports = plugin.default ?? plugin;
6 changes: 5 additions & 1 deletion packages/react-native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
"src",
"android",
"ios",
"app.plugin.js",
"plugin/build",
"scripts/hermes-sourcemaps.js",
"scripts/compose-then-inject.js",
"scripts/bugsee-xcode.sh",
Expand All @@ -17,7 +19,8 @@
"*.podspec"
],
"dependencies": {
"@bugsee/cli": "0.7.11"
"@bugsee/cli": "0.7.11",
"@expo/config-plugins": "~57.0.9"
},
"peerDependencies": {
"react": "*",
Expand All @@ -27,6 +30,7 @@
"react-native": "0.87.1"
},
"scripts": {
"build:plugin": "node ../../node_modules/typescript/lib/tsc.js -p plugin/tsconfig.json && node plugin/bake-native-versions.js",
"prepack": "node ../../scripts/cli-check-releasable-pins.ts"
},
"codegenConfig": {
Expand Down
23 changes: 23 additions & 0 deletions packages/react-native/plugin/bake-native-versions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Copy android.sdk and android.gradlePlugin from the repo-root
* native-versions.json into plugin/build. The published package cannot
* ship the repo-root file, so the plugin reads this copy at runtime.
*/
const { mkdirSync, readFileSync, writeFileSync } = require('node:fs');
const { dirname, join } = require('node:path');

const repoRoot = join(__dirname, '..', '..', '..');
const source = join(repoRoot, 'native-versions.json');
const parsed = JSON.parse(readFileSync(source, 'utf8'));
const sdk = parsed.android && parsed.android.sdk;
const gradlePlugin = parsed.android && parsed.android.gradlePlugin;
if (typeof sdk !== 'string' || typeof gradlePlugin !== 'string') {
throw new Error(`${source} is missing android.sdk or android.gradlePlugin`);
}
if (!/^[0-9A-Za-z.+_-]+$/.test(sdk) || !/^[0-9A-Za-z.+_-]+$/.test(gradlePlugin)) {
throw new Error(`refusing to bake native versions from ${source}`);
}

const out = join(__dirname, 'build', 'native-versions.baked.json');
mkdirSync(dirname(out), { recursive: true });
writeFileSync(out, `${JSON.stringify({ sdk, gradlePlugin }, null, 2)}\n`);
23 changes: 23 additions & 0 deletions packages/react-native/plugin/build/bundle-phase.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* The "Bundle React Native code and images" shell script from the bare
* example's pbxproj. It runs `bugsee-xcode.sh`, which makes
* `REACT_NATIVE_PATH` absolute with `cd`/`pwd` and injects after compose.
* The upload function in that script is not executed.
*/
export declare const BARE_BUNDLE_SCRIPT: string;
export declare function rewriteBundlePhase(script: string): string;
export interface ShellPhase {
isa?: string;
name?: string;
shellScript?: string;
}
export interface XcodeProjectLike {
hash?: {
project?: {
objects?: {
PBXShellScriptBuildPhase?: Record<string, ShellPhase | string | undefined>;
};
};
};
}
export declare function rewriteProjectBundlePhase(project: XcodeProjectLike): void;
78 changes: 78 additions & 0 deletions packages/react-native/plugin/build/bundle-phase.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/react-native/plugin/build/bundle-phase.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/react-native/plugin/build/dsym-script.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/** Node --print source. Prints the CLI package.json path, or '' when missing. */
export declare const RESOLVE_BUGSEE_CLI_PACKAGE: string;
export declare function resolveNativeCliPackageSource(packageJson: string): string;
export declare const DSYM_POST_ACTION_SCRIPT: string;
94 changes: 94 additions & 0 deletions packages/react-native/plugin/build/dsym-script.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/react-native/plugin/build/dsym-script.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions packages/react-native/plugin/build/gradle.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* The plugin marker is on Maven Central, not the Plugin Portal. Declaring
* any repositories block replaces Gradle's implicit Plugin Portal, so a
* missing block gets the portal, Google, and Maven Central together.
*/
export declare function ensureMavenCentral(settingsGradle: string): string;
export declare function ensureGradlePluginDeclared(projectBuildGradle: string, version: string): string;
/**
* `ndkVersion` is the baked `android.sdk`, or null when native crash
* reporting is explicitly off. Null strips a direct
* `implementation "com.bugsee:bugsee-android-ndk"` line from an earlier
* prebuild and excludes the wrapper's transitive `api` artifact. A
* configuration exclude does not drop a direct dependency. A later run
* with the option omitted or on removes that exclude and adds the
* implementation line. The wrapper `api` itself is left in place.
*
* On (omitted or a version) also writes `debugSymbolLevel 'SYMBOL_TABLE'`
* on existing debug and release build types when that block is absent.
* Off removes only the block this plugin inserted. Maven Hermes and
* `libreactnative.so` are pre-stripped; the comment does not claim those
* two are symbolicated. The Hermes preserve command and the finish hook
* are the JS source-map path, so they are written either way. The hook
* does not upload.
*/
export declare function ensureAppAppliesPlugin(appBuildGradle: string, ndkVersion: string | null): string;
Loading
Loading