init: 初始化模板项目
This commit is contained in:
131
apps/expo/.gitignore
vendored
Normal file
131
apps/expo/.gitignore
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
.expo
|
||||
ios
|
||||
android
|
||||
|
||||
# OSX
|
||||
#
|
||||
.DS_Store
|
||||
|
||||
# Xcode
|
||||
#
|
||||
build/
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.xcuserstate
|
||||
project.xcworkspace
|
||||
|
||||
# Android/IntelliJ
|
||||
#
|
||||
build/
|
||||
.idea
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
*.hprof
|
||||
|
||||
# node.js
|
||||
#
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# BUCK
|
||||
buck-out/
|
||||
\.buckd/
|
||||
*.keystore
|
||||
|
||||
# fastlane
|
||||
#
|
||||
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
|
||||
# screenshots whenever they are needed.
|
||||
# For more information about the recommended setup visit:
|
||||
# https://docs.fastlane.tools/best-practices/source-control/
|
||||
|
||||
*/fastlane/report.xml
|
||||
*/fastlane/Preview.html
|
||||
*/fastlane/screenshots
|
||||
|
||||
# Bundle artifacts
|
||||
*.jsbundle
|
||||
|
||||
# CocoaPods
|
||||
/ios/Pods/
|
||||
|
||||
# Expo
|
||||
.expo/*
|
||||
web-build/
|
||||
|
||||
|
||||
# @generated expo-cli sync-ee1e620bf946655de5f4a4ea0da0b18cabc4cf78
|
||||
# The following patterns were generated by expo-cli
|
||||
|
||||
# OSX
|
||||
#
|
||||
.DS_Store
|
||||
|
||||
# Xcode
|
||||
#
|
||||
build/
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.xcuserstate
|
||||
project.xcworkspace
|
||||
|
||||
# Android/IntelliJ
|
||||
#
|
||||
build/
|
||||
.idea
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
*.hprof
|
||||
|
||||
# node.js
|
||||
#
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# BUCK
|
||||
buck-out/
|
||||
\.buckd/
|
||||
*.keystore
|
||||
!debug.keystore
|
||||
|
||||
# Bundle artifacts
|
||||
*.jsbundle
|
||||
|
||||
# CocoaPods
|
||||
/ios/Pods/
|
||||
|
||||
# Expo
|
||||
.expo/
|
||||
web-build/
|
||||
dist/
|
||||
|
||||
expo-env.d.ts
|
||||
# @end expo-cli
|
||||
38
apps/expo/app.json
Normal file
38
apps/expo/app.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"expo": {
|
||||
"name": "yourprojectsname",
|
||||
"slug": "yourprojectsname",
|
||||
"scheme": "yourprojectsname",
|
||||
"version": "1.0.0",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/icon.png",
|
||||
"userInterfaceStyle": "automatic",
|
||||
"splash": {
|
||||
"image": "./assets/splash.png",
|
||||
"resizeMode": "contain",
|
||||
"backgroundColor": "#ffffff"
|
||||
},
|
||||
"updates": {
|
||||
"fallbackToCacheTimeout": 0
|
||||
},
|
||||
"assetBundlePatterns": ["**/*"],
|
||||
"ios": {
|
||||
"supportsTablet": true,
|
||||
"bundleIdentifier": "com.yourprojectsname.app"
|
||||
},
|
||||
"android": {
|
||||
"adaptiveIcon": {
|
||||
"foregroundImage": "./assets/adaptive-icon.png",
|
||||
"backgroundColor": "#FFFFFF"
|
||||
},
|
||||
"package": "com.yourprojectsname.app"
|
||||
},
|
||||
"web": {
|
||||
"favicon": "./assets/favicon.png"
|
||||
},
|
||||
"plugins": ["expo-router", "expo-font"],
|
||||
"experiments": {
|
||||
"typedRoutes": true
|
||||
}
|
||||
}
|
||||
}
|
||||
48
apps/expo/app/_layout.tsx
Normal file
48
apps/expo/app/_layout.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useColorScheme } from 'react-native'
|
||||
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native'
|
||||
import { useFonts } from 'expo-font'
|
||||
import { SplashScreen, Stack } from 'expo-router'
|
||||
import { Provider } from 'app/provider'
|
||||
import { NativeToast } from '@my/ui/src/NativeToast'
|
||||
|
||||
export const unstable_settings = {
|
||||
// Ensure that reloading on `/user` keeps a back button present.
|
||||
initialRouteName: 'Home',
|
||||
}
|
||||
|
||||
// Prevent the splash screen from auto-hiding before asset loading is complete.
|
||||
SplashScreen.preventAutoHideAsync()
|
||||
|
||||
export default function App() {
|
||||
const [interLoaded, interError] = useFonts({
|
||||
Inter: require('@tamagui/font-inter/otf/Inter-Medium.otf'),
|
||||
InterBold: require('@tamagui/font-inter/otf/Inter-Bold.otf'),
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (interLoaded || interError) {
|
||||
// Hide the splash screen after the fonts have loaded (or an error was returned) and the UI is ready.
|
||||
SplashScreen.hideAsync()
|
||||
}
|
||||
}, [interLoaded, interError])
|
||||
|
||||
if (!interLoaded && !interError) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <RootLayoutNav />
|
||||
}
|
||||
|
||||
function RootLayoutNav() {
|
||||
const colorScheme = useColorScheme()
|
||||
|
||||
return (
|
||||
<Provider>
|
||||
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
||||
<Stack />
|
||||
<NativeToast />
|
||||
</ThemeProvider>
|
||||
</Provider>
|
||||
)
|
||||
}
|
||||
15
apps/expo/app/index.tsx
Normal file
15
apps/expo/app/index.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { HomeScreen } from 'app/features/home/screen'
|
||||
import { Stack } from 'expo-router'
|
||||
|
||||
export default function Screen() {
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: 'Home',
|
||||
}}
|
||||
/>
|
||||
<HomeScreen />
|
||||
</>
|
||||
)
|
||||
}
|
||||
7
apps/expo/app/types.d.ts
vendored
Normal file
7
apps/expo/app/types.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import { config } from '@my/config'
|
||||
|
||||
export type Conf = typeof config
|
||||
|
||||
declare module '@my/ui' {
|
||||
interface TamaguiCustomConfig extends Conf {}
|
||||
}
|
||||
21
apps/expo/app/user/[id].tsx
Normal file
21
apps/expo/app/user/[id].tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { UserDetailScreen } from 'app/features/user/detail-screen'
|
||||
import { Stack } from 'expo-router'
|
||||
import { useParams } from 'solito/navigation'
|
||||
|
||||
export default function Screen() {
|
||||
const { id } = useParams()
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: 'User',
|
||||
presentation: 'modal',
|
||||
animation: 'slide_from_right',
|
||||
gestureEnabled: true,
|
||||
gestureDirection: 'horizontal',
|
||||
}}
|
||||
/>
|
||||
<UserDetailScreen id={id as string} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
BIN
apps/expo/assets/adaptive-icon.png
Normal file
BIN
apps/expo/assets/adaptive-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
apps/expo/assets/favicon.png
Normal file
BIN
apps/expo/assets/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
apps/expo/assets/icon.png
Normal file
BIN
apps/expo/assets/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
apps/expo/assets/splash.png
Normal file
BIN
apps/expo/assets/splash.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
35
apps/expo/babel.config.js
Normal file
35
apps/expo/babel.config.js
Normal file
@@ -0,0 +1,35 @@
|
||||
module.exports = (api) => {
|
||||
api.cache(true)
|
||||
return {
|
||||
presets: [['babel-preset-expo', { jsxRuntime: 'automatic' }]],
|
||||
plugins: [
|
||||
[
|
||||
require.resolve('babel-plugin-module-resolver'),
|
||||
{
|
||||
root: ['../..'],
|
||||
alias: {
|
||||
// define aliases to shorten the import paths
|
||||
app: '../../packages/app',
|
||||
'@my/ui': '../../packages/ui',
|
||||
},
|
||||
extensions: ['.js', '.jsx', '.tsx', '.ios.js', '.android.js'],
|
||||
},
|
||||
],
|
||||
// if you want reanimated support
|
||||
// 'react-native-reanimated/plugin',
|
||||
...(process.env.EAS_BUILD_PLATFORM === 'android'
|
||||
? []
|
||||
: [
|
||||
[
|
||||
'@tamagui/babel-plugin',
|
||||
{
|
||||
components: ['@my/ui', 'tamagui'],
|
||||
config: '../../packages/config/src/tamagui.config.ts',
|
||||
logTimings: true,
|
||||
disableExtraction: process.env.NODE_ENV === 'development',
|
||||
},
|
||||
],
|
||||
]),
|
||||
],
|
||||
}
|
||||
}
|
||||
20
apps/expo/eas.json
Normal file
20
apps/expo/eas.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"build": {
|
||||
"development": {
|
||||
"distribution": "internal",
|
||||
"android": {
|
||||
"buildType": "apk"
|
||||
},
|
||||
"ios": {
|
||||
"simulator": true,
|
||||
"image": "latest"
|
||||
}
|
||||
},
|
||||
"production": {
|
||||
"distribution": "store",
|
||||
"android": {
|
||||
"buildType": "app-bundle"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
apps/expo/index.js
Normal file
7
apps/expo/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import 'setimmediate'
|
||||
|
||||
if (!global?.setImmediate) {
|
||||
global.setImmediate = setTimeout
|
||||
}
|
||||
|
||||
import 'expo-router/entry'
|
||||
27
apps/expo/metro.config.js
Normal file
27
apps/expo/metro.config.js
Normal file
@@ -0,0 +1,27 @@
|
||||
// Learn more https://docs.expo.dev/guides/monorepos
|
||||
// Learn more https://docs.expo.io/guides/customizing-metro
|
||||
/**
|
||||
* @type {import('expo/metro-config')}
|
||||
*/
|
||||
const { getDefaultConfig } = require('@expo/metro-config')
|
||||
const path = require('node:path')
|
||||
|
||||
const projectRoot = __dirname
|
||||
const workspaceRoot = path.resolve(projectRoot, '../..')
|
||||
|
||||
const config = getDefaultConfig(projectRoot)
|
||||
|
||||
// 1. Watch all files within the monorepo
|
||||
config.watchFolders = [workspaceRoot]
|
||||
// 2. Let Metro know where to resolve packages and in what order
|
||||
config.resolver.nodeModulesPaths = [
|
||||
path.resolve(projectRoot, 'node_modules'),
|
||||
path.resolve(workspaceRoot, 'node_modules'),
|
||||
]
|
||||
// 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths`
|
||||
config.resolver.disableHierarchicalLookup = true
|
||||
|
||||
config.transformer = { ...config.transformer, unstable_allowRequireContext: true }
|
||||
config.transformer.minifierPath = require.resolve('metro-minify-terser')
|
||||
|
||||
module.exports = config
|
||||
57
apps/expo/package.json
Normal file
57
apps/expo/package.json
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"name": "expo-app",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "npx expo start -c",
|
||||
"android": "npx expo run:android",
|
||||
"ios": "yarn fix-xcode-env && npx expo run:ios",
|
||||
"eject": "npx expo eject",
|
||||
"fix-xcode-env": "node scripts/fix-xcode-env.mjs",
|
||||
"prebuild": "yarn expo prebuild"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@expo/config-plugins": "~10.0.0",
|
||||
"@my/ui": "0.0.1",
|
||||
"@react-navigation/bottom-tabs": "^7.3.12",
|
||||
"@react-navigation/native": "^7.1.8",
|
||||
"app": "0.0.0",
|
||||
"babel-plugin-module-resolver": "^5.0.2",
|
||||
"burnt": "^0.12.2",
|
||||
"expo": "~53.0.8",
|
||||
"expo-constants": "~17.1.6",
|
||||
"expo-dev-client": "~5.1.8",
|
||||
"expo-font": "~13.3.1",
|
||||
"expo-linear-gradient": "~14.1.4",
|
||||
"expo-linking": "~7.1.4",
|
||||
"expo-router": "~5.0.6",
|
||||
"expo-splash-screen": "~0.30.8",
|
||||
"expo-status-bar": "~2.2.3",
|
||||
"expo-updates": "~0.28.12",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-native": "0.79.2",
|
||||
"react-native-gesture-handler": "~2.24.0",
|
||||
"react-native-safe-area-context": "5.4.0",
|
||||
"react-native-screens": "~4.10.0",
|
||||
"react-native-svg": "15.8.0",
|
||||
"react-native-web": "^0.20.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.24.6",
|
||||
"@expo/metro-config": "~0.20.0",
|
||||
"@tamagui/babel-plugin": "^1.132.18",
|
||||
"metro-minify-terser": "^0.81.0",
|
||||
"typescript": "~5.8.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"metro": "0.81.0",
|
||||
"metro-resolver": "0.81.0"
|
||||
},
|
||||
"overrides": {
|
||||
"metro": "0.81.0",
|
||||
"metro-resolver": "0.81.0"
|
||||
}
|
||||
}
|
||||
29
apps/expo/scripts/fix-xcode-env.mjs
Normal file
29
apps/expo/scripts/fix-xcode-env.mjs
Normal file
@@ -0,0 +1,29 @@
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { execSync } from 'node:child_process'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
|
||||
const iosDir = path.join(__dirname, '..', 'ios')
|
||||
const xcodePath = path.join(iosDir, '.xcode.env.local')
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
// Create ios directory if it doesn't exist
|
||||
await fs.mkdir(iosDir, { recursive: true })
|
||||
|
||||
// Get the path to the Node binary
|
||||
const nodePath = process.execPath
|
||||
|
||||
// Create or update the .xcode.env.local file
|
||||
const content = `export NODE_BINARY=${nodePath}\n`
|
||||
await fs.writeFile(xcodePath, content)
|
||||
} catch (error) {
|
||||
console.error('Error:', error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
14
apps/expo/tsconfig.json
Normal file
14
apps/expo/tsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base",
|
||||
"include": [
|
||||
"app/**/*.ts",
|
||||
"app/**/*.tsx",
|
||||
".expo/types/**/*.ts",
|
||||
"expo-env.d.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"references": []
|
||||
}
|
||||
Reference in New Issue
Block a user