const crypto = require('node:crypto'); const fs = require('node:fs'); const path = require('node:path'); const { reportErrorToRemote } = require('./report-error-to-remote'); const VIRTUAL_ROOT = path.join(__dirname, '../.metro-virtual'); const VIRTUAL_ROOT_UNRESOLVED = path.join(VIRTUAL_ROOT, 'unresolved'); const handleResolveRequestError = ({ error, context, moduleName, platform, }) => { const errorMessage = `Unable to resolve module '${moduleName}' from '${context.originModulePath}'`; const syntheticError = new Error(errorMessage); syntheticError.stack = error.stack; reportErrorToRemote({ error: syntheticError }).catch((_reportError) => { // no-op }); if (process.env.NODE_ENV === 'production') throw error; if (platform === 'android') throw error; if (!__DEV__ && process.env.EXPO_PUBLIC_CREATE_ENV !== 'DEVELOPMENT') throw error; // Build a deterministic virtual file path for this failed request const key = `${moduleName}|${context.originModulePath}|${platform}`; const hash = crypto .createHash('sha256') .update(key) .digest('hex') .slice(0, 16); fs.mkdirSync(VIRTUAL_ROOT_UNRESOLVED, { recursive: true }); const vfile = path.join(VIRTUAL_ROOT_UNRESOLVED, `throw-${hash}.js`); // Serialize a safe payload for the client const payload = { moduleName, from: context.originModulePath, platform, originalMessage: String( error?.message ? error.message : 'Unknown resolve error' ), }; const code = [ '// Auto generated by custom Metro resolver', '(function(){', ` var info = ${JSON.stringify(payload)};`, " var msg = 'Unable to resolve \"' + info.moduleName + '\" from \"' + info.from + '\"';", " msg += '\\n\\n' + info.originalMessage;", ' var e = new Error(msg);', " e.name = 'ModuleResolveError';", " e.code = 'MODULE_RESOLVE_FAILED';", ' throw e;', '})();', 'export {};', // keep ESM shape harmlessly '', ].join('\n'); // Only write if content changed — avoids bumping mtime and triggering Metro rebuild loop const existingContent = fs.existsSync(vfile) ? fs.readFileSync(vfile, 'utf8') : null; if (existingContent !== code) { fs.writeFileSync(vfile, code, 'utf8'); } // Tell Metro to load our thrower as a real source file return { filePath: vfile, type: 'sourceFile', }; }; module.exports = { handleResolveRequestError, VIRTUAL_ROOT, VIRTUAL_ROOT_UNRESOLVED, };