react-native-debug-toolkit 3.3.3 → 3.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +48 -13
  2. package/README.zh-CN.md +48 -13
  3. package/android/src/main/java/com/reactnativedebugtoolkit/DebugToolkitDevConnectModule.java +0 -187
  4. package/bin/debug-toolkit.js +1 -16
  5. package/ios/DebugToolkitDevConnect.h +0 -12
  6. package/ios/DebugToolkitDevConnect.mm +0 -354
  7. package/lib/commonjs/core/initialize.js +8 -1
  8. package/lib/commonjs/core/initialize.js.map +1 -1
  9. package/lib/commonjs/features/devConnect/DevConnectTab.js +18 -470
  10. package/lib/commonjs/features/devConnect/DevConnectTab.js.map +1 -1
  11. package/lib/commonjs/features/devConnect/devConnectPreferences.js +0 -12
  12. package/lib/commonjs/features/devConnect/devConnectPreferences.js.map +1 -1
  13. package/lib/commonjs/features/devConnect/devConnectUtils.js +2 -57
  14. package/lib/commonjs/features/devConnect/devConnectUtils.js.map +1 -1
  15. package/lib/commonjs/features/devConnect/index.js +1 -23
  16. package/lib/commonjs/features/devConnect/index.js.map +1 -1
  17. package/lib/commonjs/features/devConnect/nativeDevConnect.js +1 -103
  18. package/lib/commonjs/features/devConnect/nativeDevConnect.js.map +1 -1
  19. package/lib/commonjs/index.js +7 -0
  20. package/lib/commonjs/index.js.map +1 -1
  21. package/lib/commonjs/ui/DebugView.js +2 -0
  22. package/lib/commonjs/ui/DebugView.js.map +1 -1
  23. package/lib/commonjs/ui/panel/FloatPanelView.js +22 -10
  24. package/lib/commonjs/ui/panel/FloatPanelView.js.map +1 -1
  25. package/lib/commonjs/ui/panel/tabPersistence.js +17 -0
  26. package/lib/commonjs/ui/panel/tabPersistence.js.map +1 -0
  27. package/lib/commonjs/utils/createDebugTab.js +21 -0
  28. package/lib/commonjs/utils/createDebugTab.js.map +1 -0
  29. package/lib/commonjs/utils/debugPreferences.js +0 -1
  30. package/lib/commonjs/utils/debugPreferences.js.map +1 -1
  31. package/lib/module/core/initialize.js +8 -1
  32. package/lib/module/core/initialize.js.map +1 -1
  33. package/lib/module/features/devConnect/DevConnectTab.js +21 -473
  34. package/lib/module/features/devConnect/DevConnectTab.js.map +1 -1
  35. package/lib/module/features/devConnect/devConnectPreferences.js +1 -12
  36. package/lib/module/features/devConnect/devConnectPreferences.js.map +1 -1
  37. package/lib/module/features/devConnect/devConnectUtils.js +1 -53
  38. package/lib/module/features/devConnect/devConnectUtils.js.map +1 -1
  39. package/lib/module/features/devConnect/index.js +5 -9
  40. package/lib/module/features/devConnect/index.js.map +1 -1
  41. package/lib/module/features/devConnect/nativeDevConnect.js +1 -100
  42. package/lib/module/features/devConnect/nativeDevConnect.js.map +1 -1
  43. package/lib/module/index.js +1 -0
  44. package/lib/module/index.js.map +1 -1
  45. package/lib/module/ui/DebugView.js +2 -0
  46. package/lib/module/ui/DebugView.js.map +1 -1
  47. package/lib/module/ui/panel/FloatPanelView.js +22 -10
  48. package/lib/module/ui/panel/FloatPanelView.js.map +1 -1
  49. package/lib/module/ui/panel/tabPersistence.js +13 -0
  50. package/lib/module/ui/panel/tabPersistence.js.map +1 -0
  51. package/lib/module/utils/createDebugTab.js +17 -0
  52. package/lib/module/utils/createDebugTab.js.map +1 -0
  53. package/lib/module/utils/debugPreferences.js +0 -1
  54. package/lib/module/utils/debugPreferences.js.map +1 -1
  55. package/lib/typescript/src/core/initialize.d.ts +2 -0
  56. package/lib/typescript/src/core/initialize.d.ts.map +1 -1
  57. package/lib/typescript/src/features/devConnect/DevConnectTab.d.ts.map +1 -1
  58. package/lib/typescript/src/features/devConnect/devConnectPreferences.d.ts +0 -2
  59. package/lib/typescript/src/features/devConnect/devConnectPreferences.d.ts.map +1 -1
  60. package/lib/typescript/src/features/devConnect/devConnectUtils.d.ts +0 -20
  61. package/lib/typescript/src/features/devConnect/devConnectUtils.d.ts.map +1 -1
  62. package/lib/typescript/src/features/devConnect/index.d.ts +2 -2
  63. package/lib/typescript/src/features/devConnect/index.d.ts.map +1 -1
  64. package/lib/typescript/src/features/devConnect/nativeDevConnect.d.ts +0 -25
  65. package/lib/typescript/src/features/devConnect/nativeDevConnect.d.ts.map +1 -1
  66. package/lib/typescript/src/features/devConnect/types.d.ts +1 -3
  67. package/lib/typescript/src/features/devConnect/types.d.ts.map +1 -1
  68. package/lib/typescript/src/index.d.ts +2 -0
  69. package/lib/typescript/src/index.d.ts.map +1 -1
  70. package/lib/typescript/src/ui/DebugView.d.ts +4 -2
  71. package/lib/typescript/src/ui/DebugView.d.ts.map +1 -1
  72. package/lib/typescript/src/ui/panel/FloatPanelView.d.ts.map +1 -1
  73. package/lib/typescript/src/ui/panel/tabPersistence.d.ts +3 -0
  74. package/lib/typescript/src/ui/panel/tabPersistence.d.ts.map +1 -0
  75. package/lib/typescript/src/utils/createDebugTab.d.ts +18 -0
  76. package/lib/typescript/src/utils/createDebugTab.d.ts.map +1 -0
  77. package/lib/typescript/src/utils/debugPreferences.d.ts +0 -1
  78. package/lib/typescript/src/utils/debugPreferences.d.ts.map +1 -1
  79. package/package.json +2 -4
  80. package/src/core/initialize.ts +17 -1
  81. package/src/features/devConnect/DevConnectTab.tsx +17 -381
  82. package/src/features/devConnect/devConnectPreferences.ts +0 -15
  83. package/src/features/devConnect/devConnectUtils.ts +1 -81
  84. package/src/features/devConnect/index.ts +2 -9
  85. package/src/features/devConnect/nativeDevConnect.ts +1 -136
  86. package/src/features/devConnect/types.ts +1 -3
  87. package/src/index.ts +2 -0
  88. package/src/ui/DebugView.tsx +5 -1
  89. package/src/ui/panel/FloatPanelView.tsx +22 -10
  90. package/src/ui/panel/tabPersistence.ts +22 -0
  91. package/src/utils/createDebugTab.ts +32 -0
  92. package/src/utils/debugPreferences.ts +0 -1
  93. package/scripts/android-debug-bundle.gradle +0 -23
  94. package/scripts/eas-postinstall.sh +0 -6
  95. package/scripts/embed-android.js +0 -116
  96. package/scripts/embed-expo.js +0 -109
  97. package/scripts/embed-ios.js +0 -119
  98. package/scripts/embed.js +0 -224
@@ -1,7 +1,7 @@
1
1
  import { DevConnectTab } from './DevConnectTab';
2
2
  import { loadDevConnectPreferences } from './devConnectPreferences';
3
- import { DEFAULT_DAEMON_PORT, DEFAULT_METRO_PORT, extractSubnetPrefix } from './devConnectUtils';
4
- import { getDeviceLocalIp, isNativeDevConnectAvailable } from './nativeDevConnect';
3
+ import { DEFAULT_DAEMON_PORT, extractSubnetPrefix } from './devConnectUtils';
4
+ import { getDeviceLocalIp } from './nativeDevConnect';
5
5
  import { isSimulator } from './platformDetect';
6
6
  import { daemonClient } from '../../utils/DaemonClient';
7
7
  import type { DebugFeature, DebugFeatureListener } from '../../types';
@@ -9,11 +9,9 @@ import type { DevConnectFeatureControls, DevConnectSettingsPatch, DevConnectStat
9
9
 
10
10
  export type { DevConnectState } from './types';
11
11
  export {
12
- buildMetroUrls,
13
12
  normalizeComputerHost,
14
13
  normalizePort,
15
14
  parseComputerTarget,
16
- parseMetroQrPayload,
17
15
  } from './devConnectUtils';
18
16
  export {
19
17
  loadDevConnectPreferences,
@@ -21,7 +19,6 @@ export {
21
19
  saveComputerHost,
22
20
  saveComputerTarget,
23
21
  saveDaemonPort,
24
- saveMetroPort,
25
22
  } from './devConnectPreferences';
26
23
  export { nativeIsDebugBuild } from './nativeDevConnect';
27
24
 
@@ -30,9 +27,7 @@ export const createDevConnectFeature = (): DebugFeature<DevConnectState> => {
30
27
  let state: DevConnectState = {
31
28
  isSimulator: isSimulator(),
32
29
  computerHost: '',
33
- metroPort: DEFAULT_METRO_PORT,
34
30
  daemonPort: DEFAULT_DAEMON_PORT,
35
- nativeMetroAvailable: isNativeDevConnectAvailable(),
36
31
  streaming: daemonClient.isConnected(),
37
32
  };
38
33
 
@@ -62,9 +57,7 @@ export const createDevConnectFeature = (): DebugFeature<DevConnectState> => {
62
57
  state = {
63
58
  ...state,
64
59
  computerHost: preferences.computerHost,
65
- metroPort: preferences.metroPort,
66
60
  daemonPort: preferences.daemonPort,
67
- nativeMetroAvailable: isNativeDevConnectAvailable(),
68
61
  };
69
62
 
70
63
  if (!state.isSimulator) {
@@ -1,55 +1,14 @@
1
1
  import { NativeModules } from 'react-native';
2
2
 
3
- import { buildMetroTarget } from './devConnectUtils';
4
-
5
- export interface NativeDiagnostics {
6
- persistedMetroHost: string | null;
7
- appDelegateClass: string;
8
- // Metro host switching only works in Debug builds (RN strips the packager machinery in
9
- // Release). False means the Remote JS Bundle controls should be disabled.
10
- isDebugBuild: boolean;
11
- hasEmbeddedBundle?: boolean;
12
- embeddedFirstHookInstalled?: boolean;
13
- packagerHookInstalled?: boolean;
14
- bundleRootHookInstalled?: boolean;
15
- }
16
-
17
3
  interface DebugToolkitDevConnectNativeModule {
18
- applyMetroHost: (hostPort: string) => Promise<{ hostPort?: string } | void>;
19
- resetMetroHost: () => Promise<void>;
20
- getMetroHost?: () => Promise<string | null>;
21
4
  getLocalIp?: () => Promise<string | null>;
22
5
  isDebugBuild?: () => Promise<boolean>;
23
6
  getPreference?: (key: string) => Promise<string | null>;
24
- getDiagnostics?: () => Promise<NativeDiagnostics>;
25
7
  }
26
8
 
27
- type MetroBundleFailureReason =
28
- | 'invalid_target'
29
- | 'native_unavailable'
30
- | 'fetch_unavailable'
31
- | 'metro_unreachable'
32
- | 'native_error';
33
-
34
- export type MetroBundleResult =
35
- | { ok: true; hostPort: string }
36
- | { ok: false; reason: MetroBundleFailureReason; error?: string; statusUrl?: string };
37
-
38
- type FetchLike = (
39
- url: string,
40
- init: { method: string },
41
- ) => Promise<{
42
- ok?: boolean;
43
- text?: () => Promise<string>;
44
- }>;
45
-
46
9
  function getNativeModule(): DebugToolkitDevConnectNativeModule | null {
47
10
  const nativeModule = NativeModules.DebugToolkitDevConnect as Partial<DebugToolkitDevConnectNativeModule> | undefined;
48
- if (
49
- nativeModule &&
50
- typeof nativeModule.applyMetroHost === 'function' &&
51
- typeof nativeModule.resetMetroHost === 'function'
52
- ) {
11
+ if (nativeModule && typeof nativeModule.isDebugBuild === 'function') {
53
12
  return nativeModule as DebugToolkitDevConnectNativeModule;
54
13
  }
55
14
  return null;
@@ -59,88 +18,6 @@ export function isNativeDevConnectAvailable(): boolean {
59
18
  return getNativeModule() !== null;
60
19
  }
61
20
 
62
- async function checkMetroStatus(statusUrl: string): Promise<MetroBundleResult | null> {
63
- const fetchImpl = globalThis.fetch as FetchLike | undefined;
64
- if (!fetchImpl) {
65
- return {
66
- ok: false,
67
- reason: 'fetch_unavailable',
68
- statusUrl,
69
- error: 'global fetch is not available',
70
- };
71
- }
72
-
73
- try {
74
- const response = await fetchImpl(statusUrl, { method: 'GET' });
75
- const body = typeof response.text === 'function' ? await response.text() : '';
76
- if (response.ok === false || !body.includes('packager-status:running')) {
77
- return {
78
- ok: false,
79
- reason: 'metro_unreachable',
80
- statusUrl,
81
- error: body || 'Metro status endpoint did not report running',
82
- };
83
- }
84
- return null;
85
- } catch (error) {
86
- return {
87
- ok: false,
88
- reason: 'metro_unreachable',
89
- statusUrl,
90
- error: error instanceof Error ? error.message : String(error),
91
- };
92
- }
93
- }
94
-
95
- export async function applyMetroBundle(host: string, port: string): Promise<MetroBundleResult> {
96
- const target = buildMetroTarget(host, port);
97
- if (!target) {
98
- return { ok: false, reason: 'invalid_target' };
99
- }
100
-
101
- const nativeModule = getNativeModule();
102
- if (!nativeModule) {
103
- return { ok: false, reason: 'native_unavailable' };
104
- }
105
-
106
- const statusError = await checkMetroStatus(target.statusUrl);
107
- if (statusError) {
108
- return statusError;
109
- }
110
-
111
- try {
112
- const result = await nativeModule.applyMetroHost(target.hostPort);
113
- return {
114
- ok: true,
115
- hostPort: result && typeof result.hostPort === 'string' ? result.hostPort : target.hostPort,
116
- };
117
- } catch (error) {
118
- return {
119
- ok: false,
120
- reason: 'native_error',
121
- error: error instanceof Error ? error.message : String(error),
122
- };
123
- }
124
- }
125
-
126
- export async function resetMetroBundle(): Promise<MetroBundleResult | { ok: true }> {
127
- const nativeModule = getNativeModule();
128
- if (!nativeModule) {
129
- return { ok: false, reason: 'native_unavailable' };
130
- }
131
-
132
- try {
133
- await nativeModule.resetMetroHost();
134
- return { ok: true };
135
- } catch (error) {
136
- return {
137
- ok: false,
138
- reason: 'native_error',
139
- error: error instanceof Error ? error.message : String(error),
140
- };
141
- }
142
- }
143
-
144
21
  export async function getDeviceLocalIp(): Promise<string | null> {
145
22
  const nativeModule = getNativeModule();
146
23
  if (!nativeModule?.getLocalIp) {
@@ -166,15 +43,3 @@ export async function nativeIsDebugBuild(): Promise<boolean | null> {
166
43
  return null;
167
44
  }
168
45
  }
169
-
170
- export async function getNativeDiagnostics(): Promise<NativeDiagnostics | null> {
171
- const nativeModule = getNativeModule();
172
- if (!nativeModule?.getDiagnostics) {
173
- return null;
174
- }
175
- try {
176
- return await nativeModule.getDiagnostics();
177
- } catch {
178
- return null;
179
- }
180
- }
@@ -1,15 +1,13 @@
1
1
  export interface DevConnectState {
2
2
  isSimulator: boolean;
3
3
  computerHost: string;
4
- metroPort: string;
5
4
  daemonPort: string;
6
5
  subnetPrefix?: string;
7
- nativeMetroAvailable: boolean;
8
6
  streaming: boolean;
9
7
  }
10
8
 
11
9
  export type DevConnectSettingsPatch = Partial<
12
- Pick<DevConnectState, 'computerHost' | 'metroPort' | 'daemonPort'>
10
+ Pick<DevConnectState, 'computerHost' | 'daemonPort'>
13
11
  >;
14
12
 
15
13
  export interface DevConnectFeatureControls {
package/src/index.ts CHANGED
@@ -28,6 +28,8 @@ export { useNavigationLogger } from './features/navigation/useNavigationLogger';
28
28
 
29
29
  // Utilities
30
30
  export { safeStringify } from './utils/safeStringify';
31
+ export { createDebugTab } from './utils/createDebugTab';
32
+ export type { CreateDebugTabOptions } from './utils/createDebugTab';
31
33
  export { copyToComputer, logToComputer, fmt } from './utils/copyToComputer';
32
34
  export type { CopyResult, CopyOptions, CopyMethod } from './utils/copyToComputer';
33
35
  export { createDebugDeviceReport } from './utils/deviceReport';
@@ -3,7 +3,7 @@ import { DebugToolkitProvider } from '../core/DebugToolkitProvider';
3
3
  import { initializeDebugToolkit } from '../core/initialize';
4
4
  import type { FeatureConfigs } from '../core/initialize';
5
5
  import { useNavigationLogger } from '../features/navigation/useNavigationLogger';
6
- import type { EnvironmentConfig, NavigationContainerRef } from '../types';
6
+ import type { AnyDebugFeature, EnvironmentConfig, NavigationContainerRef } from '../types';
7
7
 
8
8
  // --- Types ---
9
9
 
@@ -14,6 +14,8 @@ export interface DebugViewProps {
14
14
  * Set to `false` to disable a feature.
15
15
  */
16
16
  features?: Partial<FeatureConfigs>;
17
+ /** Custom tabs/features appended after built-in features. */
18
+ customFeatures?: AnyDebugFeature[];
17
19
  /** Navigation container ref for route tracking. */
18
20
  navigationRef?: React.RefObject<NavigationContainerRef | null>;
19
21
  /** Environment configs for runtime host switching. */
@@ -38,6 +40,7 @@ function NavigationLoggerInner({
38
40
  export function DebugView({
39
41
  children,
40
42
  features,
43
+ customFeatures,
41
44
  navigationRef,
42
45
  environments,
43
46
  enabled,
@@ -66,6 +69,7 @@ export function DebugView({
66
69
  let cancelled = false;
67
70
  initializeDebugToolkit({
68
71
  features: resolvedFeatures,
72
+ customFeatures,
69
73
  enabled,
70
74
  }).then((toolkit) => {
71
75
  if (!cancelled) {
@@ -11,6 +11,7 @@ import { FloatIcon } from '../floating/FloatIcon';
11
11
  import { DebugPanel } from './DebugPanel';
12
12
  import { FeatureTabBar } from './FeatureTabBar';
13
13
  import type { TabItem } from './FeatureTabBar';
14
+ import { resolveStoredTabIndex } from './tabPersistence';
14
15
  import { useTabAnimation } from './useTabAnimation';
15
16
 
16
17
  // ─── Error Boundary ────────────────────────────────────
@@ -53,25 +54,32 @@ export function FloatPanelView({ features, panelOpen, onOpenPanel, onClosePanel,
53
54
 
54
55
  // Restore last tab on mount
55
56
  useEffect(() => {
57
+ if (tabLoaded.current || features.length === 0) return;
56
58
  let mounted = true;
57
59
  getPreference(KEYS.lastTab).then((val) => {
58
- if (!mounted || !val) return;
59
- const idx = parseInt(val, 10);
60
- if (!isNaN(idx) && idx >= 0) {
61
- setActiveTab(idx);
62
- tabLoaded.current = true;
60
+ if (!mounted || tabLoaded.current) return;
61
+ const idx = resolveStoredTabIndex(features, val);
62
+ setActiveTab(idx);
63
+ const featureName = features[idx]?.name;
64
+ if (featureName && featureName !== val) {
65
+ setPreference(KEYS.lastTab, featureName);
63
66
  }
67
+ tabLoaded.current = true;
64
68
  });
65
69
  return () => { mounted = false; };
66
- }, []);
70
+ }, [features]);
67
71
 
68
72
  const { contentOpacity, contentTranslateX, panHandlers, switchTab } = useTabAnimation({
69
73
  activeTab,
70
74
  tabCount: features.length,
71
75
  onTabChange: useCallback((index: number) => {
76
+ tabLoaded.current = true;
72
77
  setActiveTab(index);
73
- setPreference(KEYS.lastTab, String(index));
74
- }, []),
78
+ const featureName = features[index]?.name;
79
+ if (featureName) {
80
+ setPreference(KEYS.lastTab, featureName);
81
+ }
82
+ }, [features]),
75
83
  });
76
84
 
77
85
  // Feature subscription → re-render on data changes
@@ -96,10 +104,14 @@ export function FloatPanelView({ features, panelOpen, onOpenPanel, onClosePanel,
96
104
  // Clamp activeTab if features shrink
97
105
  useEffect(() => {
98
106
  if (features.length > 0 && activeTab >= features.length) {
107
+ tabLoaded.current = true;
99
108
  setActiveTab(0);
100
- setPreference(KEYS.lastTab, '0');
109
+ const featureName = features[0]?.name;
110
+ if (featureName) {
111
+ setPreference(KEYS.lastTab, featureName);
112
+ }
101
113
  }
102
- }, [features.length, activeTab]);
114
+ }, [features, activeTab]);
103
115
 
104
116
  // Badge (first feature that returns one)
105
117
  const envBadge = features.map((f) => f.badge?.()).find((b) => b != null) ?? null;
@@ -0,0 +1,22 @@
1
+ import type { AnyDebugFeature } from '../../types';
2
+
3
+ export function resolveStoredTabIndex(
4
+ features: AnyDebugFeature[],
5
+ storedTab: string | null,
6
+ ): number {
7
+ if (!storedTab) return 0;
8
+
9
+ const nameIndex = features.findIndex((feature) => feature.name === storedTab);
10
+ if (nameIndex >= 0) return nameIndex;
11
+
12
+ const legacyIndex = Number(storedTab);
13
+ if (
14
+ Number.isInteger(legacyIndex) &&
15
+ legacyIndex >= 0 &&
16
+ legacyIndex < features.length
17
+ ) {
18
+ return legacyIndex;
19
+ }
20
+
21
+ return 0;
22
+ }
@@ -0,0 +1,32 @@
1
+ import type { ComponentType } from 'react';
2
+ import type { DebugFeature, DebugFeatureListener, DebugFeatureRenderProps } from '../types';
3
+
4
+ export interface CreateDebugTabOptions<TSnapshot> {
5
+ name: string;
6
+ label: string;
7
+ getSnapshot: () => TSnapshot;
8
+ render?: ComponentType<DebugFeatureRenderProps<TSnapshot>>;
9
+ setup?: () => void;
10
+ cleanup?: () => void;
11
+ clear?: () => void;
12
+ subscribe?: (listener: DebugFeatureListener) => () => void;
13
+ badge?: () => { label: string; color: string } | null;
14
+ }
15
+
16
+ const noop = () => {};
17
+
18
+ export function createDebugTab<TSnapshot>(
19
+ options: CreateDebugTabOptions<TSnapshot>,
20
+ ): DebugFeature<TSnapshot> {
21
+ return {
22
+ name: options.name,
23
+ label: options.label,
24
+ setup: options.setup ?? noop,
25
+ cleanup: options.cleanup ?? noop,
26
+ getSnapshot: options.getSnapshot,
27
+ clear: options.clear,
28
+ subscribe: options.subscribe,
29
+ renderContent: options.render,
30
+ badge: options.badge,
31
+ };
32
+ }
@@ -89,6 +89,5 @@ export const KEYS = {
89
89
  networkLogs: '@react_native_debug_toolkit/network_logs',
90
90
  trackLogs: '@react_native_debug_toolkit/track_logs',
91
91
  computerHost: '@react_native_debug_toolkit/computer_host',
92
- metroPort: '@react_native_debug_toolkit/metro_port',
93
92
  daemonPort: '@react_native_debug_toolkit/daemon_port',
94
93
  } as const;
@@ -1,23 +0,0 @@
1
- // react-native-debug-toolkit: Debug bundle generation for Android
2
- // Applied via: apply from: "../../node_modules/react-native-debug-toolkit/scripts/android-debug-bundle.gradle"
3
-
4
- tasks.register('generateDebugBundle', Exec) {
5
- commandLine 'npx', 'react-native', 'bundle',
6
- '--platform', 'android',
7
- '--dev', 'true',
8
- '--entry-file', project.ext.has('debugToolkitEntryFile')
9
- ? project.ext.debugToolkitEntryFile : 'index.js',
10
- '--bundle-output', "${projectDir}/src/main/assets/index.android.bundle",
11
- '--assets-dest', "${projectDir}/src/main/res"
12
- }
13
-
14
- afterEvaluate {
15
- // Disable for release variants
16
- tasks.matching { it.name.contains('Release') }.configureEach {
17
- tasks.named('generateDebugBundle').get().enabled = false
18
- }
19
- // Wire as preBuild dependency for debug variants
20
- android.applicationVariants.matching { it.buildType.name == 'debug' }.configureEach {
21
- preBuildProvider.configure { dependsOn 'generateDebugBundle' }
22
- }
23
- }
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env bash
2
- # react-native-debug-toolkit: EAS Build postInstall hook
3
- # Usage in eas.json:
4
- # "postInstall": "bash node_modules/react-native-debug-toolkit/scripts/eas-postinstall.sh"
5
- set -e
6
- npx debug-toolkit embed --yes
@@ -1,116 +0,0 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
-
6
- const APPLY_FROM_MARKER =
7
- '// react-native-debug-toolkit: debug bundle embed';
8
- const APPLY_FROM_LINE =
9
- APPLY_FROM_MARKER +
10
- '\napply from: "../../node_modules/react-native-debug-toolkit/scripts/android-debug-bundle.gradle"';
11
-
12
- function findBuildGradle(projectRoot) {
13
- const gradlePath = path.join(projectRoot, 'android', 'app', 'build.gradle');
14
- const gradleKtsPath = path.join(
15
- projectRoot,
16
- 'android',
17
- 'app',
18
- 'build.gradle.kts'
19
- );
20
-
21
- if (fs.existsSync(gradleKtsPath)) {
22
- return 'android/app/build.gradle.kts';
23
- }
24
- if (fs.existsSync(gradlePath)) {
25
- return 'android/app/build.gradle';
26
- }
27
- return null;
28
- }
29
-
30
- function ensureAssetsDir(projectRoot) {
31
- const assetsDir = path.join(
32
- projectRoot,
33
- 'android',
34
- 'app',
35
- 'src',
36
- 'main',
37
- 'assets'
38
- );
39
- if (!fs.existsSync(assetsDir)) {
40
- fs.mkdirSync(assetsDir, { recursive: true });
41
- console.log(' Created android/app/src/main/assets/');
42
- }
43
- }
44
-
45
- function injectGradleApply(projectRoot, buildGradleRelPath, entryFile, options) {
46
- const fullPath = path.join(projectRoot, buildGradleRelPath);
47
- let content = fs.readFileSync(fullPath, 'utf-8');
48
-
49
- // Idempotent check
50
- if (content.includes(APPLY_FROM_MARKER)) {
51
- console.log(
52
- ` ${buildGradleRelPath}: apply-from already present. Skipping.`
53
- );
54
- return { buildGradle: buildGradleRelPath, entryFile };
55
- }
56
-
57
- // Set entry file as ext property if non-default
58
- let entryFileExt = '';
59
- if (entryFile && entryFile !== 'index.js') {
60
- entryFileExt = `\nproject.ext.debugToolkitEntryFile = '${entryFile}'`;
61
- }
62
-
63
- content += '\n\n' + APPLY_FROM_LINE + entryFileExt + '\n';
64
- fs.writeFileSync(fullPath, content);
65
-
66
- ensureAssetsDir(projectRoot);
67
- console.log(` ${buildGradleRelPath}: Injected apply-from for debug bundle.`);
68
-
69
- return { buildGradle: buildGradleRelPath, entryFile };
70
- }
71
-
72
- function undoGradleApply(projectRoot, config) {
73
- if (!config.android) {
74
- console.log(' No Android configuration found. Skipping.');
75
- return;
76
- }
77
-
78
- const fullPath = path.join(projectRoot, config.android.buildGradle);
79
- if (!fs.existsSync(fullPath)) {
80
- console.log(` ${config.android.buildGradle} not found. Skipping.`);
81
- return;
82
- }
83
-
84
- let content = fs.readFileSync(fullPath, 'utf-8');
85
- if (!content.includes(APPLY_FROM_MARKER)) {
86
- console.log(' apply-from not found. Skipping.');
87
- return;
88
- }
89
-
90
- // Remove the marker line, apply-from line, and optional entryFile ext
91
- const lines = content.split('\n');
92
- const filtered = lines.filter((line) => {
93
- if (line === APPLY_FROM_MARKER) return false;
94
- if (
95
- line.startsWith('apply from:') &&
96
- line.includes('react-native-debug-toolkit/scripts/android-debug-bundle.gradle')
97
- )
98
- return false;
99
- if (line.startsWith("project.ext.debugToolkitEntryFile = '")) return false;
100
- return true;
101
- });
102
-
103
- // Trim trailing empty lines introduced by our injection
104
- while (filtered.length > 0 && filtered[filtered.length - 1].trim() === '') {
105
- filtered.pop();
106
- }
107
-
108
- fs.writeFileSync(fullPath, filtered.join('\n') + '\n');
109
- console.log(' Removed apply-from from build.gradle.');
110
- }
111
-
112
- module.exports = {
113
- findBuildGradle,
114
- injectGradleApply,
115
- undoGradleApply,
116
- };
@@ -1,109 +0,0 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
-
6
- function isExpoProject(projectRoot) {
7
- // app.json with expo field
8
- const appJsonPath = path.join(projectRoot, 'app.json');
9
- if (fs.existsSync(appJsonPath)) {
10
- try {
11
- const appJson = JSON.parse(fs.readFileSync(appJsonPath, 'utf-8'));
12
- if (appJson.expo) return true;
13
- } catch (_) {}
14
- }
15
-
16
- // app.config.js or app.config.ts
17
- if (
18
- fs.existsSync(path.join(projectRoot, 'app.config.js')) ||
19
- fs.existsSync(path.join(projectRoot, 'app.config.ts'))
20
- ) {
21
- return true;
22
- }
23
-
24
- // expo in package.json dependencies
25
- const pkgPath = path.join(projectRoot, 'package.json');
26
- if (fs.existsSync(pkgPath)) {
27
- try {
28
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
29
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
30
- if (deps.expo) return true;
31
- } catch (_) {}
32
- }
33
-
34
- return false;
35
- }
36
-
37
- function isExpoGo(projectRoot) {
38
- // Expo Go doesn't support embedded bundles
39
- // If no ios/ or android/ dirs, likely Expo Go
40
- const hasNativeDirs =
41
- fs.existsSync(path.join(projectRoot, 'ios')) ||
42
- fs.existsSync(path.join(projectRoot, 'android'));
43
-
44
- if (!hasNativeDirs) {
45
- return true;
46
- }
47
- return false;
48
- }
49
-
50
- function isPrebuilt(projectRoot) {
51
- // Dev client / bare workflow after prebuild
52
- return (
53
- fs.existsSync(path.join(projectRoot, 'ios')) &&
54
- fs.existsSync(path.join(projectRoot, 'android'))
55
- );
56
- }
57
-
58
- function getExpoEntryPoint(projectRoot) {
59
- const appJsonPath = path.join(projectRoot, 'app.json');
60
- if (fs.existsSync(appJsonPath)) {
61
- try {
62
- const appJson = JSON.parse(fs.readFileSync(appJsonPath, 'utf-8'));
63
- if (appJson.expo && appJson.expo.entryPoint) {
64
- return appJson.expo.entryPoint;
65
- }
66
- } catch (_) {}
67
- }
68
-
69
- // Expo default with .expo virtual entry
70
- const virtualEntry = path.join(
71
- projectRoot,
72
- '.expo',
73
- '.virtual-metro-entry'
74
- );
75
- if (fs.existsSync(virtualEntry)) {
76
- return '.expo/.virtual-metro-entry';
77
- }
78
-
79
- return null;
80
- }
81
-
82
- function checkExpo(projectRoot, options) {
83
- const expo = isExpoProject(projectRoot);
84
- if (!expo) {
85
- return { expo: false, skip: false };
86
- }
87
-
88
- if (isExpoGo(projectRoot)) {
89
- return {
90
- expo: true,
91
- skip: true,
92
- reason:
93
- 'Expo Go does not support embedded bundles. Use expo-dev-client or run npx expo prebuild first.',
94
- };
95
- }
96
-
97
- if (!isPrebuilt(projectRoot)) {
98
- return {
99
- expo: true,
100
- skip: true,
101
- reason:
102
- 'Native directories not found. Run npx expo prebuild first, then retry.',
103
- };
104
- }
105
-
106
- return { expo: true, skip: false, entryPoint: getExpoEntryPoint(projectRoot) };
107
- }
108
-
109
- module.exports = { isExpoProject, checkExpo, getExpoEntryPoint };