Skip to content
Open
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: 5 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
EXPO_NO_TELEMETRY=true
EXPO_PUBLIC_GITHUB_RELEASES_URL=https://github.com/DodoraApp/DodoStream/releases
NODE_ENV=development
NODE_ENV=development

# Strip debug logger calls in production builds (defaults to true if not set)
# Set to 'false' to keep debug logs in production builds
STRIP_DEBUG_LOGS=true
10 changes: 10 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ module.exports = function (api) {

plugins.push('react-native-worklets/plugin');

// Strip debug calls in production builds
// For Expo: use caller.dev to detect production mode (when --dev false is used)
const stripDebugLogs = process.env.STRIP_DEBUG_LOGS !== 'false';
const isProduction = process.env.NODE_ENV === 'production';

if (isProduction && stripDebugLogs) {
console.log("Babel: Stripping debug calls from production build");
plugins.push('./plugins/babel-plugin-strip-debug-calls.js');
Comment on lines +10 to +14
Copy link

Choose a reason for hiding this comment

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

Bug: The Babel config requires both STRIP_DEBUG_LOGS and NODE_ENV === 'production', but NODE_ENV is not set during local EAS production builds, preventing log stripping.
Severity: MEDIUM

Suggested Fix

Rely on a single source of truth for enabling the plugin. Either use api.caller() as suggested by the code comment to detect the build mode, or only check process.env.STRIP_DEBUG_LOGS, which is correctly configured in eas.json for different build profiles. This will ensure consistent behavior across local and CI/CD builds.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: babel.config.js#L10-L14

Potential issue: In `babel.config.js`, the `transform-remove-console` plugin is only
enabled if both `stripDebugLogs` and `isProduction` are true. The `isProduction` flag
relies on `process.env.NODE_ENV === 'production'`. During a local EAS production build
(e.g., `eas build --local --profile production`), `eas.json` correctly sets
`STRIP_DEBUG_LOGS`, but `NODE_ENV` is not set and may default to `'development'`. This
causes the `isProduction` check to fail, preventing the plugin from running and leaving
debug logs in production builds created locally. This does not affect CI/CD builds where
`NODE_ENV` is explicitly set.

Did we get this right? 👍 / 👎 to inform future reviews.

}

return {
presets: ['babel-preset-expo'],
plugins,
Expand Down
3 changes: 2 additions & 1 deletion eas.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
"environment": "production",
"channel": "production",
"env": {
"APP_VARIANT": "prod"
"APP_VARIANT": "prod",
"STRIP_DEBUG_LOGS": "true"
},
"autoIncrement": true
},
Expand Down
103 changes: 103 additions & 0 deletions plugins/babel-plugin-strip-debug-calls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* Babel plugin to strip debug logger calls in production builds
*
* This plugin safely removes:
* - Imports from '@/utils/debug'
* - Variables initialized with useDebugLogger() or createDebugLogger()
* - All calls to those debug logger variables (regardless of variable name)
*
* Uses proper scope tracking and binding management to avoid removing unrelated code.
*/
module.exports = function ({ types: t }) {
return {
name: 'strip-debug-calls',
visitor: {
Program: {
exit(path, state) {
// Collect debug logger bindings
const debugLoggerBindings = new Set();
const debugImportNames = new Set();

// Find all imports from @/utils/debug
path.traverse({
ImportDeclaration(importPath) {
const source = importPath.node.source.value;
if (source !== '@/utils/debug') return;

// Track imported function names
importPath.node.specifiers.forEach(specifier => {
if (t.isImportSpecifier(specifier) || t.isImportDefaultSpecifier(specifier)) {
debugImportNames.add(specifier.local.name);
}
});
}
});

// Find all variable declarations that use the imported debug functions
path.traverse({
VariableDeclarator(declaratorPath) {
const init = declaratorPath.node.init;
const id = declaratorPath.node.id;

if (!init || !t.isIdentifier(id)) return;

// Check if it's a call to useDebugLogger or createDebugLogger
if (
t.isCallExpression(init) &&
t.isIdentifier(init.callee) &&
debugImportNames.has(init.callee.name)
) {
// Get the binding for this debug logger variable
const binding = declaratorPath.scope.getBinding(id.name);
if (binding) {
debugLoggerBindings.add(binding);
}
}
}
});

// Remove all references to debug loggers (calls to them)
debugLoggerBindings.forEach(binding => {
binding.referencePaths.forEach(refPath => {
// Only remove if it's being called
const parent = refPath.parent;
if (t.isCallExpression(parent) && parent.callee === refPath.node) {
const statement = refPath.getStatementParent();
if (statement) {
statement.remove();
}
}
});
});

// Remove the variable declarations for debug loggers
debugLoggerBindings.forEach(binding => {
const declarator = binding.path;
if (declarator && t.isVariableDeclarator(declarator.node)) {
const declaration = declarator.parentPath;
if (
declaration &&
t.isVariableDeclaration(declaration.node) &&
declaration.node.declarations.length === 1
) {
declaration.remove();
} else {
declarator.remove();
}
}
});

// Remove imports from @/utils/debug
path.traverse({
ImportDeclaration(importPath) {
const source = importPath.node.source.value;
if (source === '@/utils/debug') {
importPath.remove();
}
}
});
},
},
},
};
};
Loading