"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.INTERNAL_CALLSITES_REGEX = exports.EXPO_DEBUG = void 0; exports.getDefaultConfig = getDefaultConfig; exports.loadAsync = loadAsync; function _config() { const data = require("@expo/config"); _config = function () { return data; }; return data; } function _paths() { const data = require("@expo/config/paths"); _paths = function () { return data; }; return data; } function _chalk() { const data = _interopRequireDefault(require("chalk")); _chalk = function () { return data; }; return data; } function _getenv() { const data = require("getenv"); _getenv = function () { return data; }; return data; } function _path() { const data = _interopRequireDefault(require("path")); _path = function () { return data; }; return data; } function _resolveFrom() { const data = _interopRequireDefault(require("resolve-from")); _resolveFrom = function () { return data; }; return data; } function _url() { const data = require("url"); _url = function () { return data; }; return data; } function _getModulesPaths() { const data = require("./getModulesPaths"); _getModulesPaths = function () { return data; }; return data; } function _getWatchFolders() { const data = require("./getWatchFolders"); _getWatchFolders = function () { return data; }; return data; } function _importMetroFromProject() { const data = require("./importMetroFromProject"); _importMetroFromProject = function () { return data; }; return data; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // Copyright 2021-present 650 Industries (Expo). All rights reserved. const EXPO_DEBUG = (0, _getenv().boolish)('EXPO_DEBUG', false); exports.EXPO_DEBUG = EXPO_DEBUG; const EXPO_USE_EXOTIC = (0, _getenv().boolish)('EXPO_USE_EXOTIC', false); // Import only the types here, the values will be imported from the project, at runtime. const INTERNAL_CALLSITES_REGEX = new RegExp(['/Libraries/Renderer/implementations/.+\\.js$', '/Libraries/BatchedBridge/MessageQueue\\.js$', '/Libraries/YellowBox/.+\\.js$', '/Libraries/LogBox/.+\\.js$', '/Libraries/Core/Timers/.+\\.js$', 'node_modules/react-devtools-core/.+\\.js$', 'node_modules/react-refresh/.+\\.js$', 'node_modules/scheduler/.+\\.js$', // Metro replaces `require()` with a different method, // we want to omit this method from the stack trace. // This is akin to most React tooling. '/metro/.*/polyfills/require.js$', // Hide frames related to a fast refresh. '/metro/.*/lib/bundle-modules/.+\\.js$', 'node_modules/react-native/Libraries/Utilities/HMRClient.js$', 'node_modules/eventemitter3/index.js', 'node_modules/event-target-shim/dist/.+\\.js$', // Ignore the log forwarder used in the expo package. '/expo/build/logs/RemoteConsole.js$', // Improve errors thrown by invariant (ex: `Invariant Violation: "main" has not been registered`). 'node_modules/invariant/.+\\.js$', // Remove babel runtime additions 'node_modules/regenerator-runtime/.+\\.js$', // Remove react native setImmediate ponyfill 'node_modules/promise/setimmediate/.+\\.js$', // Babel helpers that implement language features 'node_modules/@babel/runtime/.+\\.js$', // Hide Hermes internal bytecode '/InternalBytecode/InternalBytecode\\.js$', // Block native code invocations `\\[native code\\]`].join('|')); exports.INTERNAL_CALLSITES_REGEX = INTERNAL_CALLSITES_REGEX; function isUrl(value) { try { // eslint-disable-next-line no-new new (_url().URL)(value); return true; } catch { return false; } } function readIsLegacyImportsEnabled(projectRoot) { const config = (0, _config().getConfig)(projectRoot, { skipSDKVersionRequirement: true }); return (0, _config().isLegacyImportsEnabled)(config.exp); } function getProjectBabelConfigFile(projectRoot) { return _resolveFrom().default.silent(projectRoot, './babel.config.js') || _resolveFrom().default.silent(projectRoot, './.babelrc') || _resolveFrom().default.silent(projectRoot, './.babelrc.js'); } function getAssetPlugins(projectRoot) { const assetPlugins = []; let hashAssetFilesPath; try { hashAssetFilesPath = (0, _resolveFrom().default)(projectRoot, 'expo-asset/tools/hashAssetFiles'); } catch {// TODO: we should warn/throw an error if the user has expo-updates installed but does not // have hashAssetFiles available, or if the user is in managed workflow and does not have // hashAssetFiles available. but in a bare app w/o expo-updates, just using dev-client, // it is not needed } if (hashAssetFilesPath) { assetPlugins.push(hashAssetFilesPath); } return assetPlugins; } let hasWarnedAboutExotic = false; function getDefaultConfig(projectRoot, options = {}) { const isExotic = options.mode === 'exotic' || EXPO_USE_EXOTIC; if (isExotic && !hasWarnedAboutExotic) { hasWarnedAboutExotic = true; console.log(_chalk().default.gray(`\u203A Unstable feature ${_chalk().default.bold`EXPO_USE_EXOTIC`} is enabled. Bundling may not work as expected, and is subject to breaking changes.`)); } const MetroConfig = (0, _importMetroFromProject().importMetroConfigFromProject)(projectRoot); const reactNativePath = _path().default.dirname((0, _resolveFrom().default)(projectRoot, 'react-native/package.json')); try { // Set the `EXPO_METRO_CACHE_KEY_VERSION` variable for use in the custom babel transformer. // This hack is used because there doesn't appear to be anyway to resolve // `babel-preset-fbjs` relative to the project root later (in `metro-expo-babel-transformer`). const babelPresetFbjsPath = (0, _resolveFrom().default)(projectRoot, 'babel-preset-fbjs/package.json'); process.env.EXPO_METRO_CACHE_KEY_VERSION = String(require(babelPresetFbjsPath).version); } catch {// noop -- falls back to a hardcoded value. } const isLegacy = readIsLegacyImportsEnabled(projectRoot); // Deprecated -- SDK 41 -- if (options.target) { if (!isLegacy) { console.warn(_chalk().default.yellow(`The target option is deprecated. Learn more: http://expo.fyi/expo-extension-migration`)); delete options.target; } } else if (process.env.EXPO_TARGET) { console.error('EXPO_TARGET is deprecated. Learn more: http://expo.fyi/expo-extension-migration'); if (isLegacy) { // EXPO_TARGET is used by @expo/metro-config to determine the target when getDefaultConfig is // called from metro.config.js. // @ts-ignore options.target = process.env.EXPO_TARGET; } } else if (isLegacy) { // Fall back to guessing based on the project structure in legacy mode. options.target = (0, _config().getDefaultTarget)(projectRoot); } if (!options.target) { // Default to bare -- no .expo extension. options.target = 'bare'; } // End deprecated -- SDK 41 -- const { target } = options; if (!(target === 'managed' || target === 'bare')) { throw new Error(`Invalid target: '${target}'. Debug info: \n${JSON.stringify({ 'options.target': options.target, default: (0, _config().getDefaultTarget)(projectRoot) }, null, 2)}`); } const sourceExtsConfig = { isTS: true, isReact: true, isModern: false }; const sourceExts = target === 'bare' ? (0, _paths().getBareExtensions)([], sourceExtsConfig) : (0, _paths().getManagedExtensions)([], sourceExtsConfig); if (isExotic) { // Add support for cjs (without platform extensions). sourceExts.push('cjs'); } const babelConfigPath = getProjectBabelConfigFile(projectRoot); const isCustomBabelConfigDefined = !!babelConfigPath; const resolverMainFields = []; // Disable `react-native` in exotic mode, since library authors // use it to ship raw application code to the project. if (!isExotic) { resolverMainFields.push('react-native'); } resolverMainFields.push('browser', 'main'); const watchFolders = (0, _getWatchFolders().getWatchFolders)(projectRoot); // TODO: nodeModulesPaths does not work with the new Node.js package.json exports API, this causes packages like uuid to fail. Disabling for now. const nodeModulesPaths = (0, _getModulesPaths().getModulesPaths)(projectRoot); if (EXPO_DEBUG) { console.log(); console.log(`Expo Metro config:`); try { console.log(`- Version: ${require('../package.json').version}`); } catch {} console.log(`- Bundler target: ${target}`); console.log(`- Legacy: ${isLegacy}`); console.log(`- Extensions: ${sourceExts.join(', ')}`); console.log(`- React Native: ${reactNativePath}`); console.log(`- Babel config: ${babelConfigPath || 'babel-preset-expo (default)'}`); console.log(`- Resolver Fields: ${resolverMainFields.join(', ')}`); console.log(`- Watch Folders: ${watchFolders.join(', ')}`); console.log(`- Node Module Paths: ${nodeModulesPaths.join(', ')}`); console.log(`- Exotic: ${isExotic}`); console.log(); } const { // Remove the default reporter which metro always resolves to be the react-native-community/cli reporter. // This prints a giant React logo which is less accessible to users on smaller terminals. reporter, ...metroDefaultValues } = MetroConfig.getDefaultConfig.getDefaultValues(projectRoot); // Merge in the default config from Metro here, even though loadConfig uses it as defaults. // This is a convenience for getDefaultConfig use in metro.config.js, e.g. to modify assetExts. return MetroConfig.mergeConfig(metroDefaultValues, { watchFolders, resolver: { resolverMainFields, platforms: ['ios', 'android', 'native', 'testing'], assetExts: metroDefaultValues.resolver.assetExts.filter(assetExt => !sourceExts.includes(assetExt)), sourceExts, nodeModulesPaths }, serializer: { getModulesRunBeforeMainModule: () => [require.resolve(_path().default.join(reactNativePath, 'Libraries/Core/InitializeCore')) // TODO: Bacon: load Expo side-effects ], getPolyfills: () => require(_path().default.join(reactNativePath, 'rn-get-polyfills'))() }, server: { port: Number(process.env.RCT_METRO_PORT) || 8081 }, symbolicator: { customizeFrame: frame => { if (frame.file && isUrl(frame.file)) { return { ...frame, // HACK: This prevents Metro from attempting to read the invalid file URL it sent us. lineNumber: null, column: null, // This prevents the invalid frame from being shown by default. collapse: true }; } let collapse = Boolean(frame.file && INTERNAL_CALLSITES_REGEX.test(frame.file)); if (!collapse) { var _frame$file; // This represents the first frame of the stacktrace. // Often this looks like: `__r(0);`. // The URL will also be unactionable in the app and therefore not very useful to the developer. if (frame.column === 3 && frame.methodName === 'global code' && (_frame$file = frame.file) !== null && _frame$file !== void 0 && _frame$file.match(/^https?:\/\//g)) { collapse = true; } } return { ...(frame || {}), collapse }; } }, transformer: { // `require.context` support unstable_allowRequireContext: true, allowOptionalDependencies: true, babelTransformerPath: isExotic ? require.resolve('./transformer/metro-expo-exotic-babel-transformer') : isCustomBabelConfigDefined ? // If the user defined a babel config file in their project, // then use the default transformer. // Try to use the project copy before falling back on the global version _resolveFrom().default.silent(projectRoot, 'metro-react-native-babel-transformer') : // Otherwise, use a custom transformer that uses `babel-preset-expo` by default for projects. require.resolve('./transformer/metro-expo-babel-transformer'), assetRegistryPath: 'react-native/Libraries/Image/AssetRegistry', assetPlugins: getAssetPlugins(projectRoot) } }); } async function loadAsync(projectRoot, { reporter, target, ...metroOptions } = {}) { let defaultConfig = getDefaultConfig(projectRoot, { target }); if (reporter) { defaultConfig = { ...defaultConfig, reporter }; } const MetroConfig = (0, _importMetroFromProject().importMetroConfigFromProject)(projectRoot); return await MetroConfig.loadConfig({ cwd: projectRoot, projectRoot, ...metroOptions }, defaultConfig); } //# sourceMappingURL=ExpoMetroConfig.js.map