diff --git a/.changeset/fluffy-mice-roll.md b/.changeset/fluffy-mice-roll.md new file mode 100644 index 000000000..3e35b7aab --- /dev/null +++ b/.changeset/fluffy-mice-roll.md @@ -0,0 +1,5 @@ +--- +"@callstack/repack-plugin-expo": minor +--- + +Add Expo Router + CNG Plugin diff --git a/.changeset/full-nails-grin.md b/.changeset/full-nails-grin.md new file mode 100644 index 000000000..913d1d0bf --- /dev/null +++ b/.changeset/full-nails-grin.md @@ -0,0 +1,5 @@ +--- +"@callstack/repack-plugin-expo": minor +--- + +Add Expo Managed tester application diff --git a/apps/tester-expo-app/.gitignore b/apps/tester-expo-app/.gitignore new file mode 100644 index 000000000..acf9c8560 --- /dev/null +++ b/apps/tester-expo-app/.gitignore @@ -0,0 +1,7 @@ +# Build artifacts +.expo +build + +# Native directories +/android +/ios diff --git a/apps/tester-expo-app/app.json b/apps/tester-expo-app/app.json new file mode 100644 index 000000000..2d4cb7954 --- /dev/null +++ b/apps/tester-expo-app/app.json @@ -0,0 +1,16 @@ +{ + "name": "tester-expo-app", + "slug": "tester-expo-app", + "scheme": "testerexpoapp", + "version": "0.0.1", + "android": { + "package": "com.testerexpoapp" + }, + "ios": { + "bundleIdentifier": "com.testerexpoapp" + }, + "plugins": [ + ["expo-router", { "root": "./src/screens" }], + "@callstack/repack-plugin-expo/plugin" + ] +} diff --git a/apps/tester-expo-app/index.js b/apps/tester-expo-app/index.js new file mode 100644 index 000000000..f3ddfdcc3 --- /dev/null +++ b/apps/tester-expo-app/index.js @@ -0,0 +1,4 @@ +import { AppRegistry } from 'react-native'; +import Application from './src/App.tsx'; + +AppRegistry.registerComponent('main', () => Application); diff --git a/apps/tester-expo-app/package.json b/apps/tester-expo-app/package.json new file mode 100644 index 000000000..fe4be72fb --- /dev/null +++ b/apps/tester-expo-app/package.json @@ -0,0 +1,37 @@ +{ + "name": "tester-expo-app", + "version": "0.0.1", + "private": true, + "main": "./index.js", + "scripts": { + "start": "react-native start", + "ios": "react-native run-ios --no-packager", + "android": "react-native run-android --no-packager", + "prebuild": "expo prebuild" + }, + "dependencies": { + "expo": "catalog:", + "expo-linking": "~7.1.7", + "expo-router": "~5.1.5", + "react": "19.0.0", + "react-native": "0.79.5", + "react-native-safe-area-context": "5.4.0", + "react-native-screens": "~4.11.1" + }, + "devDependencies": { + "@babel/core": "^7.25.2", + "@callstack/repack": "workspace:*", + "@callstack/repack-plugin-expo": "workspace:*", + "@react-native-community/cli": "catalog:testers", + "@react-native-community/cli-platform-android": "catalog:testers", + "@react-native-community/cli-platform-ios": "catalog:testers", + "@react-native/babel-preset": "catalog:testers", + "@react-native/typescript-config": "catalog:testers", + "@rspack/core": "catalog:", + "@swc/core": "^1.13.3", + "@swc/helpers": "catalog:", + "@types/react": "catalog:testers", + "react-native-test-app": "catalog:testers", + "typescript": "catalog:" + } +} diff --git a/apps/tester-expo-app/react-native.config.js b/apps/tester-expo-app/react-native.config.js new file mode 100644 index 000000000..ceb40d38d --- /dev/null +++ b/apps/tester-expo-app/react-native.config.js @@ -0,0 +1,17 @@ +const { configureProjects } = require('react-native-test-app'); + +const useWebpack = Boolean(process.env.USE_WEBPACK); + +module.exports = { + project: configureProjects({ + android: { + sourceDir: 'android', + }, + ios: { + sourceDir: 'ios', + }, + }), + commands: useWebpack + ? require('@callstack/repack/commands/webpack') + : require('@callstack/repack/commands/rspack'), +}; diff --git a/apps/tester-expo-app/rspack.config.mjs b/apps/tester-expo-app/rspack.config.mjs new file mode 100644 index 000000000..b4c6c665a --- /dev/null +++ b/apps/tester-expo-app/rspack.config.mjs @@ -0,0 +1,46 @@ +import { createRequire } from 'node:module'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import * as Repack from '@callstack/repack'; +import { ExpoPlugin } from '@callstack/repack-plugin-expo'; +import { IgnorePlugin } from '@rspack/core'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const require = createRequire(import.meta.url); + +export default Repack.defineRspackConfig({ + context: __dirname, + entry: './index.js', + resolve: { + ...Repack.getResolveOptions({ enablePackageExports: true }), + alias: { + // Alias both react and react-native to ensure that we don't end up with multiple versions + // of these libraries in the bundle. + // + // This is needed in these monorepo setups where there are multiple copies of react + // and react-native in the node_modules. + react: require.resolve('react'), + 'react-native': require.resolve('react-native'), + }, + }, + module: { + rules: [ + ...Repack.getJsTransformRules(), + ...Repack.getAssetTransformRules(), + ], + }, + plugins: [ + new Repack.RepackPlugin(), + new ExpoPlugin({ + router: { + root: resolve('./src/screens'), + }, + }), + + // Ignore @react-native-masked-view warnings + new IgnorePlugin({ resourceRegExp: /^@react-native-masked-view/ }), + ], +}); diff --git a/apps/tester-expo-app/src/App.tsx b/apps/tester-expo-app/src/App.tsx new file mode 100644 index 000000000..9f67fffff --- /dev/null +++ b/apps/tester-expo-app/src/App.tsx @@ -0,0 +1,8 @@ +import { ExpoRoot } from 'expo-router'; +import { ctx } from 'expo-router/_ctx'; + +console.log(ctx.keys()); + +export default function Application() { + return ; +} diff --git a/apps/tester-expo-app/src/screens/_layout.tsx b/apps/tester-expo-app/src/screens/_layout.tsx new file mode 100644 index 000000000..82f261edd --- /dev/null +++ b/apps/tester-expo-app/src/screens/_layout.tsx @@ -0,0 +1,10 @@ +import { Stack } from 'expo-router'; + +export default function RootLayout() { + return ( + + + + + ); +} diff --git a/apps/tester-expo-app/src/screens/about.tsx b/apps/tester-expo-app/src/screens/about.tsx new file mode 100644 index 000000000..f3a47163e --- /dev/null +++ b/apps/tester-expo-app/src/screens/about.tsx @@ -0,0 +1,18 @@ +import { useRouter } from 'expo-router'; +import { Button, ScrollView } from 'react-native'; + +export default function AboutScreen() { + const router = useRouter(); + + return ( + +