diff --git a/dev-packages/utils/index.js b/dev-packages/utils/index.js new file mode 100644 index 0000000000..f053ebf797 --- /dev/null +++ b/dev-packages/utils/index.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/dev-packages/utils/metro.js b/dev-packages/utils/metro.js new file mode 100644 index 0000000000..496fb232bc --- /dev/null +++ b/dev-packages/utils/metro.js @@ -0,0 +1,62 @@ +const path = require('path'); +const exclusionList = require('metro-config/src/defaults/exclusionList'); + +/** + * Packages used by the sample apps + */ +const getMonorepoPackages = monorepoRoot => { + return { + '@sentry/react-native': path.resolve(monorepoRoot, 'packages/core'), + }; +}; + +/** + * Block given packages present in the monorepo packages to avoid conflicts with the sample apps + */ +const getBlockList = (monorepoPackages, excludedPackages) => { + return Object.values(monorepoPackages) + .map(p => excludedPackages.map(e => new RegExp(`${p}/node_modules/${e}/.*`))) + .flat(); +}; + +const withMonorepo = config => { + const projectRoot = config.projectRoot; + if (!projectRoot) { + throw new Error('projectRoot is required'); + } + + const monorepoRoot = path.resolve(projectRoot, '../..'); + const monorepoPackages = getMonorepoPackages(monorepoRoot); + + config.resolver = config.resolver || {}; + + const blockList = [ + ...((Array.isArray(config.resolver.blockList) && config.resolver.blockList) || + (!!config.resolver.blockList && [config.resolver.blockList]) || + []), + ...getBlockList(monorepoPackages, ['react-native', 'react']), + new RegExp('.*\\android\\.*'), // Required for Windows in order to run the Sample. + ]; + config.resolver.blockList = exclusionList(blockList); + + config.watchFolders = [...(config.watchFolders || []), projectRoot, ...Object.values(monorepoPackages)]; + + config.resolver.extraNodeModules = { + ...config.resolver.extraNodeModules, + ...monorepoPackages, + 'react-native': path.resolve(projectRoot, 'node_modules/react-native'), + react: path.resolve(projectRoot, 'node_modules/react'), + }; + + config.resolver.nodeModulesPaths = [ + ...(config.resolver.nodeModulesPaths || []), + path.resolve(projectRoot, 'node_modules'), + ...Object.values(monorepoPackages).map(p => path.resolve(p, 'node_modules')), + ]; + + return config; +}; + +module.exports = { + withMonorepo, +}; diff --git a/dev-packages/utils/package.json b/dev-packages/utils/package.json new file mode 100644 index 0000000000..22079deb0c --- /dev/null +++ b/dev-packages/utils/package.json @@ -0,0 +1,11 @@ +{ + "name": "sentry-react-native-samples-utils", + "version": "0.0.1", + "description": "Internal Samples Utils", + "main": "index.js", + "license": "MIT", + "private": true, + "dependencies": { + "metro-config": "^0.81.0" + } +} diff --git a/package.json b/package.json index ad78342b42..fe74ebeeb2 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "packages/core", "dev-packages/e2e-tests", "dev-packages/type-check", + "dev-packages/utils", "samples/react-native", "samples/react-native-macos", "samples/expo", diff --git a/performance-tests/TestAppSentry/metro.config.js b/performance-tests/TestAppSentry/metro.config.js index e86e1db671..0c9c988fe2 100644 --- a/performance-tests/TestAppSentry/metro.config.js +++ b/performance-tests/TestAppSentry/metro.config.js @@ -1,15 +1,4 @@ -const path = require('path'); -const exclusionList = require('metro-config/src/defaults/exclusionList'); - -const projectRoot = __dirname; -const monorepoRoot = path.resolve(projectRoot, '../..'); - -// Only list the packages within your monorepo that your app uses. No need to add anything else. -// If your monorepo tooling can give you the list of monorepo workspaces linked -// in your app workspace, you can automate this list instead of hardcoding them. -const monorepoPackages = { - '@sentry/react-native': path.resolve(monorepoRoot, 'packages/core'), -}; +const { withMonorepo } = require('sentry-react-native-samples-utils/metro'); /** * Metro configuration for React Native @@ -17,7 +6,7 @@ const monorepoPackages = { * * @format */ -module.exports = { +module.exports = withMonorepo({ transformer: { getTransformOptions: async () => ({ transform: { @@ -27,24 +16,4 @@ module.exports = { }), }, projectRoot: __dirname, - // 1. Watch the local app directory, and only the shared packages (limiting the scope and speeding it up) - // Note how we change this from `monorepoRoot` to `projectRoot`. This is part of the optimization! - watchFolders: [projectRoot, ...Object.values(monorepoPackages)], - resolver: { - blockList: exclusionList([ - ...Object.values(monorepoPackages).map(p => new RegExp(`${p}/node_modules/react-native/.*`)), - ]), - // Add the monorepo workspaces as `extraNodeModules` to Metro. - // If your monorepo tooling creates workspace symlinks in the `node_modules` directory, - // you can either add symlink support to Metro or set the `extraNodeModules` to avoid the symlinks. - // See: https://metrobundler.dev/docs/configuration/#extranodemodules - extraNodeModules: { - ...monorepoPackages, - 'react-native': path.resolve(projectRoot, 'node_modules/react-native'), - }, - nodeModulesPaths: [ - path.resolve(projectRoot, 'node_modules'), - ...Object.values(monorepoPackages).map(p => path.resolve(p, 'node_modules')), - ], - }, -}; +}); diff --git a/performance-tests/TestAppSentry/package.json b/performance-tests/TestAppSentry/package.json index 5733fc3132..182ddc72fe 100644 --- a/performance-tests/TestAppSentry/package.json +++ b/performance-tests/TestAppSentry/package.json @@ -15,7 +15,8 @@ "devDependencies": { "@babel/core": "^7.12.9", "@babel/runtime": "^7.12.5", - "metro-react-native-babel-preset": "^0.72.3" + "metro-react-native-babel-preset": "^0.72.3", + "sentry-react-native-samples-utils": "workspace:^" }, "jest": { "preset": "react-native" diff --git a/samples/expo/metro.config.js b/samples/expo/metro.config.js index 338804f753..48b3993d2e 100644 --- a/samples/expo/metro.config.js +++ b/samples/expo/metro.config.js @@ -1,9 +1,9 @@ // Learn more https://docs.expo.io/guides/customizing-metro const { getDefaultConfig } = require('@expo/metro-config'); -const path = require('path'); - const { getSentryExpoConfig } = require('@sentry/react-native/metro'); +const { withMonorepo } = require('sentry-react-native-samples-utils/metro'); + /** @type {import('expo/metro-config').MetroConfig} */ const config = getSentryExpoConfig(__dirname, { // [Web-only]: Enables CSS support in Metro. @@ -12,40 +12,4 @@ const config = getSentryExpoConfig(__dirname, { annotateReactComponents: true, }); -const projectRoot = __dirname; -const monorepoRoot = path.resolve(projectRoot, '../..'); - -// Only list the packages within your monorepo that your app uses. No need to add anything else. -// If your monorepo tooling can give you the list of monorepo workspaces linked -// in your app workspace, you can automate this list instead of hardcoding them. -const monorepoPackages = { - '@sentry/react-native': path.resolve(monorepoRoot, 'packages/core'), -}; - -const exclusionList = [...Object.values(monorepoPackages).map(p => new RegExp(`${p}/node_modules/react-native/.*`))]; - -if (config.resolver.blacklistRE) { - config.resolver.blacklistRE.push(...exclusionList); -} else { - config.resolver.blacklistRE = exclusionList; -} - -// 1. Watch the local app directory, and only the shared packages (limiting the scope and speeding it up) -// Note how we change this from `monorepoRoot` to `projectRoot`. This is part of the optimization! -config.watchFolders = [projectRoot, ...Object.values(monorepoPackages)]; - -// Add the monorepo workspaces as `extraNodeModules` to Metro. -// If your monorepo tooling creates workspace symlinks in the `node_modules` directory, -// you can either add symlink support to Metro or set the `extraNodeModules` to avoid the symlinks. -// See: https://metrobundler.dev/docs/configuration/#extranodemodules -config.resolver.extraNodeModules = { - ...monorepoPackages, - 'react-native': path.resolve(projectRoot, 'node_modules/react-native'), -}; - -config.resolver.nodeModulesPaths = [ - path.resolve(projectRoot, 'node_modules'), - ...Object.values(monorepoPackages).map(p => path.resolve(p, 'node_modules')), -]; - -module.exports = config; +module.exports = withMonorepo(config); diff --git a/samples/expo/package.json b/samples/expo/package.json index d11903f86a..b0b5f64047 100644 --- a/samples/expo/package.json +++ b/samples/expo/package.json @@ -37,7 +37,8 @@ "@babel/core": "^7.26.0", "@babel/preset-env": "^7.26.0", "@sentry/babel-plugin-component-annotate": "^2.18.0", - "@types/node": "20.10.4" + "@types/node": "20.10.4", + "sentry-react-native-samples-utils": "workspace:^" }, "overrides": { "react-refresh": "~0.14.0" diff --git a/samples/react-native-macos/metro.config.js b/samples/react-native-macos/metro.config.js index 363086b13c..270023086b 100644 --- a/samples/react-native-macos/metro.config.js +++ b/samples/react-native-macos/metro.config.js @@ -1,17 +1,7 @@ -const path = require('path'); const { withSentryConfig } = require('@sentry/react-native/metro'); const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); -const exclusionList = require('metro-config/src/defaults/exclusionList'); -const projectRoot = __dirname; -const monorepoRoot = path.resolve(projectRoot, '../..'); - -// Only list the packages within your monorepo that your app uses. No need to add anything else. -// If your monorepo tooling can give you the list of monorepo workspaces linked -// in your app workspace, you can automate this list instead of hardcoding them. -const monorepoPackages = { - '@sentry/react-native': path.resolve(monorepoRoot, 'packages/core'), -}; +const { withMonorepo } = require('sentry-react-native-samples-utils/metro'); /** * Metro configuration @@ -19,52 +9,12 @@ const monorepoPackages = { * * @type {import('metro-config').MetroConfig} */ -const config = { - projectRoot: __dirname, - // 1. Watch the local app directory, and only the shared packages (limiting the scope and speeding it up) - // Note how we change this from `monorepoRoot` to `projectRoot`. This is part of the optimization! - watchFolders: [projectRoot, ...Object.values(monorepoPackages)], - resolver: { - resolverMainFields: ['react-native', 'main'], - resolveRequest: (context, moduleName, platform) => { - if (moduleName.includes('promise/')) { - return context.resolveRequest( - { - ...context, - // Ensures the promise module is resolved from the sample's node_modules. - allowHaste: false, - disableHierarchicalLookup: true, - }, - moduleName, - platform, - ); - } - return context.resolveRequest(context, moduleName, platform); - }, - blockList: exclusionList([ - new RegExp('.*\\android\\.*'), // Required for Windows in order to run the Sample. - ...Object.values(monorepoPackages).map( - p => new RegExp(`${p}/node_modules/react-native/.*`), - ), - ]), - // Add the monorepo workspaces as `extraNodeModules` to Metro. - // If your monorepo tooling creates workspace symlinks in the `node_modules` directory, - // you can either add symlink support to Metro or set the `extraNodeModules` to avoid the symlinks. - // See: https://metrobundler.dev/docs/configuration/#extranodemodules - extraNodeModules: { - ...monorepoPackages, - 'react-native': path.resolve(projectRoot, 'node_modules/react-native'), - }, - nodeModulesPaths: [ - path.resolve(projectRoot, 'node_modules'), - ...Object.values(monorepoPackages).map(p => - path.resolve(p, 'node_modules'), - ), - ], - }, -}; +const config = {}; + +const mergedConfig = mergeConfig(getDefaultConfig(__dirname), config); -const m = mergeConfig(getDefaultConfig(__dirname), config); -module.exports = withSentryConfig(m, { +const sentryConfig = withSentryConfig(mergedConfig, { annotateReactComponents: true, }); + +module.exports = withMonorepo(sentryConfig); diff --git a/samples/react-native-macos/package.json b/samples/react-native-macos/package.json index 823b167ccf..03a3f6de05 100644 --- a/samples/react-native-macos/package.json +++ b/samples/react-native-macos/package.json @@ -49,6 +49,7 @@ "eslint": "^8.19.0", "jest": "^29.6.3", "prettier": "2.8.8", + "sentry-react-native-samples-utils": "workspace:^", "typescript": "5.0.4" }, "engines": { diff --git a/samples/react-native/metro.config.js b/samples/react-native/metro.config.js index 7ecc754c00..587aa3a7c8 100644 --- a/samples/react-native/metro.config.js +++ b/samples/react-native/metro.config.js @@ -1,17 +1,7 @@ -const path = require('path'); const { withSentryConfig } = require('@sentry/react-native/metro'); const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); -const exclusionList = require('metro-config/src/defaults/exclusionList'); -const projectRoot = __dirname; -const monorepoRoot = path.resolve(projectRoot, '../..'); - -// Only list the packages within your monorepo that your app uses. No need to add anything else. -// If your monorepo tooling can give you the list of monorepo workspaces linked -// in your app workspace, you can automate this list instead of hardcoding them. -const monorepoPackages = { - '@sentry/react-native': path.resolve(monorepoRoot, 'packages/core'), -}; +const { withMonorepo } = require('sentry-react-native-samples-utils/metro'); /** * Metro configuration @@ -19,52 +9,12 @@ const monorepoPackages = { * * @type {import('metro-config').MetroConfig} */ -const config = { - projectRoot: __dirname, - // 1. Watch the local app directory, and only the shared packages (limiting the scope and speeding it up) - // Note how we change this from `monorepoRoot` to `projectRoot`. This is part of the optimization! - watchFolders: [projectRoot, ...Object.values(monorepoPackages)], - resolver: { - resolverMainFields: ['react-native', 'main'], - resolveRequest: (context, moduleName, platform) => { - if (moduleName.includes('promise/')) { - return context.resolveRequest( - { - ...context, - // Ensures the promise module is resolved from the sample's node_modules. - allowHaste: false, - disableHierarchicalLookup: true, - }, - moduleName, - platform, - ); - } - return context.resolveRequest(context, moduleName, platform); - }, - blockList: exclusionList([ - new RegExp('.*\\android\\.*'), // Required for Windows in order to run the Sample. - ...Object.values(monorepoPackages).map( - p => new RegExp(`${p}/node_modules/react-native/.*`), - ), - ]), - // Add the monorepo workspaces as `extraNodeModules` to Metro. - // If your monorepo tooling creates workspace symlinks in the `node_modules` directory, - // you can either add symlink support to Metro or set the `extraNodeModules` to avoid the symlinks. - // See: https://metrobundler.dev/docs/configuration/#extranodemodules - extraNodeModules: { - ...monorepoPackages, - 'react-native': path.resolve(projectRoot, 'node_modules/react-native'), - }, - nodeModulesPaths: [ - path.resolve(projectRoot, 'node_modules'), - ...Object.values(monorepoPackages).map(p => - path.resolve(p, 'node_modules'), - ), - ], - }, -}; +const config = {}; + +const mergedConfig = mergeConfig(getDefaultConfig(__dirname), config); -const m = mergeConfig(getDefaultConfig(__dirname), config); -module.exports = withSentryConfig(m, { +const sentryConfig = withSentryConfig(mergedConfig, { annotateReactComponents: true, }); + +module.exports = withMonorepo(sentryConfig); diff --git a/samples/react-native/package.json b/samples/react-native/package.json index 80ac941a39..53914f03ef 100644 --- a/samples/react-native/package.json +++ b/samples/react-native/package.json @@ -63,6 +63,7 @@ "patch-package": "^8.0.0", "prettier": "2.8.8", "react-test-renderer": "18.3.1", + "sentry-react-native-samples-utils": "workspace:^", "typescript": "5.0.4" }, "engines": { diff --git a/yarn.lock b/yarn.lock index c91f7de625..6f25ab9650 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9484,6 +9484,7 @@ __metadata: metro-react-native-babel-preset: ^0.72.3 react: 18.1.0 react-native: 0.70.15 + sentry-react-native-samples-utils: "workspace:^" languageName: unknown linkType: soft @@ -24200,6 +24201,7 @@ __metadata: react-native-safe-area-context: 4.12.0 react-native-screens: ~4.0.0 react-native-web: ~0.19.13 + sentry-react-native-samples-utils: "workspace:^" typescript: ^5.3.2 languageName: unknown linkType: soft @@ -24242,6 +24244,7 @@ __metadata: react-native-vector-icons: ^10.0.3 react-redux: ^8.1.3 redux: ^4.2.1 + sentry-react-native-samples-utils: "workspace:^" typescript: 5.0.4 languageName: unknown linkType: soft @@ -24290,10 +24293,19 @@ __metadata: react-redux: ^8.1.3 react-test-renderer: 18.3.1 redux: ^4.2.1 + sentry-react-native-samples-utils: "workspace:^" typescript: 5.0.4 languageName: unknown linkType: soft +"sentry-react-native-samples-utils@workspace:^, sentry-react-native-samples-utils@workspace:dev-packages/utils": + version: 0.0.0-use.local + resolution: "sentry-react-native-samples-utils@workspace:dev-packages/utils" + dependencies: + metro-config: ^0.81.0 + languageName: unknown + linkType: soft + "sentry-react-native-type-check@workspace:dev-packages/type-check": version: 0.0.0-use.local resolution: "sentry-react-native-type-check@workspace:dev-packages/type-check"