diff --git a/README.md b/README.md index 0e2f5eb19..f9cbe7e41 100644 --- a/README.md +++ b/README.md @@ -48,13 +48,13 @@ Nothing makes it into Ignite unless it's been proven on projects that Infinite R | Library | Category | Version | Description | | -------------------------------- | -------------------- | ------- | ---------------------------------------------- | -| React Native | Mobile Framework | v0.81 | The best cross-platform mobile framework | +| React Native | Mobile Framework | v0.85 | The best cross-platform mobile framework | | React | UI Framework | v19 | The most popular UI framework in the world | -| TypeScript | Language | v5 | Static typechecking | +| TypeScript | Language | v6 | Static typechecking | | React Navigation | Navigation | v7 | Performant and consistent navigation framework | -| Expo | SDK | v55 | Allows (optional) Expo modules | -| Expo Font | Custom Fonts | v14 | Import custom fonts | -| Expo Localization | Internationalization | v17 | i18n support (including RTL!) | +| Expo | SDK | v56 | Allows (optional) Expo modules | +| Expo Font | Custom Fonts | v56 | Import custom fonts | +| Expo Localization | Internationalization | v56 | i18n support (including RTL!) | | RN Reanimated | Animations | v4 | Beautiful and performant animations | | MMKV | Persistence | v3 | State persistence | | apisauce | REST client | v3 | Communicate with back-end | diff --git a/boilerplate/app.config.ts b/boilerplate/app.config.ts index 658141ffd..b14a6361d 100644 --- a/boilerplate/app.config.ts +++ b/boilerplate/app.config.ts @@ -1,4 +1,22 @@ import { ExpoConfig, ConfigContext } from "@expo/config" +import fs from "fs" +import path from "path" + +/** + * Automatically discover all fonts under `assets/fonts` + * so we never have to hardcode them. + */ +function getFontPathsFromAssetsFolder(): string[] { + const fontsDir = path.resolve(__dirname, "assets/fonts") + if (!fs.existsSync(fontsDir)) { + console.warn("FONT_WARNING: No custom fonts detected.") + return [] + } + + return fs + .readdirSync(fontsDir) + .map((file) => `./assets/fonts/${file}`) +} /** * Use tsx/cjs here so we can use TypeScript for our Config Plugins @@ -16,6 +34,7 @@ import "tsx/cjs" */ module.exports = ({ config }: ConfigContext): Partial => { const existingPlugins = config.plugins ?? [] + const fontPaths = getFontPathsFromAssetsFolder() return { ...config, @@ -36,6 +55,14 @@ module.exports = ({ config }: ConfigContext): Partial => { ], }, }, - plugins: [...existingPlugins], + plugins: [ + ...existingPlugins, + [ + "expo-font", + { + fonts: fontPaths, + }, + ], + ], } } diff --git a/boilerplate/app.json b/boilerplate/app.json index a477e655f..8b78ade27 100644 --- a/boilerplate/app.json +++ b/boilerplate/app.json @@ -9,8 +9,6 @@ "updates": { "fallbackToCacheTimeout": 0 }, - "newArchEnabled": true, - "jsEngine": "hermes", "assetBundlePatterns": [ "**/*" ], @@ -21,8 +19,7 @@ "foregroundImage": "./assets/images/app-icon-android-adaptive-foreground.png", "backgroundImage": "./assets/images/app-icon-android-adaptive-background.png" }, - "allowBackup": false, - "edgeToEdgeEnabled": true + "allowBackup": false }, "ios": { "icon": "./assets/images/app-icon-ios.png", @@ -35,7 +32,6 @@ }, "plugins": [ "expo-localization", - "expo-font", [ "expo-splash-screen", { diff --git a/boilerplate/app/app.tsx b/boilerplate/app/app.tsx index b7fea621f..c741d35b6 100644 --- a/boilerplate/app/app.tsx +++ b/boilerplate/app/app.tsx @@ -19,17 +19,15 @@ if (__DEV__) { import "./utils/gestureHandler" import { useEffect, useState } from "react" -import { useFonts } from "expo-font" import * as Linking from "expo-linking" import { KeyboardProvider } from "react-native-keyboard-controller" import { initialWindowMetrics, SafeAreaProvider } from "react-native-safe-area-context" - import { AuthProvider } from "./context/AuthContext" // @demo remove-current-line import { initI18n } from "./i18n" import { AppNavigator } from "./navigators/AppNavigator" import { useNavigationPersistence } from "./navigators/navigationUtilities" import { ThemeProvider } from "./theme/context" -import { customFontsToLoad } from "./theme/typography" +import { useCustomFonts } from "./theme/typography" import { loadDateFnsLocale } from "./utils/formatDate" import * as storage from "./utils/storage" @@ -68,7 +66,7 @@ export function App() { isRestored: isNavigationStateRestored, } = useNavigationPersistence(storage, NAVIGATION_PERSISTENCE_KEY) - const [areFontsLoaded, fontLoadError] = useFonts(customFontsToLoad) + const [areFontsLoaded, fontLoadError] = useCustomFonts() const [isI18nInitialized, setIsI18nInitialized] = useState(false) useEffect(() => { @@ -83,7 +81,7 @@ export function App() { // In iOS: application:didFinishLaunchingWithOptions: // In Android: https://stackoverflow.com/a/45838109/204044 // You can replace with your own loading component if you wish. - if (!isNavigationStateRestored || !isI18nInitialized || (!areFontsLoaded && !fontLoadError)) { + if (!isNavigationStateRestored || !isI18nInitialized || !areFontsLoaded || fontLoadError) { return null } diff --git a/boilerplate/app/components/Screen.tsx b/boilerplate/app/components/Screen.tsx index 2eee3ecb2..b353b5379 100644 --- a/boilerplate/app/components/Screen.tsx +++ b/boilerplate/app/components/Screen.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useRef, useState } from "react" +import { ReactNode, useMemo, useRef, useState } from "react" import { KeyboardAvoidingView, KeyboardAvoidingViewProps, @@ -117,28 +117,16 @@ function useAutoPreset(props: AutoScreenProps): { const { preset, scrollEnabledToggleThreshold } = props const { percent = 0.92, point = 0 } = scrollEnabledToggleThreshold || {} - const scrollViewHeight = useRef(null) - const scrollViewContentHeight = useRef(null) - const [scrollEnabled, setScrollEnabled] = useState(true) + const [scrollViewHeight, setScrollViewHeight] = useState(null) + const [scrollViewContentHeight, setScrollViewContentHeight] = useState(null) - function updateScrollState() { - if (scrollViewHeight.current === null || scrollViewContentHeight.current === null) return - - // check whether content fits the screen then toggle scroll state according to it - const contentFitsScreen = (function () { - if (point) { - return scrollViewContentHeight.current < scrollViewHeight.current - point - } else { - return scrollViewContentHeight.current < scrollViewHeight.current * percent - } - })() - - // content is less than the size of the screen, so we can disable scrolling - if (scrollEnabled && contentFitsScreen) setScrollEnabled(false) - - // content is greater than the size of the screen, so let's enable scrolling - if (!scrollEnabled && !contentFitsScreen) setScrollEnabled(true) - } + const scrollEnabled = useMemo(() => { + if (scrollViewHeight === null || scrollViewContentHeight === null) return true + const contentFitsScreen = point + ? scrollViewContentHeight < scrollViewHeight - point + : scrollViewContentHeight < scrollViewHeight * percent + return !contentFitsScreen + }, [scrollViewHeight, scrollViewContentHeight, point, percent]) /** * @param {number} w - The width of the content. @@ -146,8 +134,7 @@ function useAutoPreset(props: AutoScreenProps): { */ function onContentSizeChange(w: number, h: number) { // update scroll-view content height - scrollViewContentHeight.current = h - updateScrollState() + setScrollViewContentHeight(h) } /** @@ -156,13 +143,9 @@ function useAutoPreset(props: AutoScreenProps): { function onLayout(e: LayoutChangeEvent) { const { height } = e.nativeEvent.layout // update scroll-view height - scrollViewHeight.current = height - updateScrollState() + setScrollViewHeight(height) } - // update scroll state on every render - if (preset === "auto") updateScrollState() - return { scrollEnabled: preset === "auto" ? scrollEnabled : true, onContentSizeChange, diff --git a/boilerplate/app/components/Toggle/Checkbox.tsx b/boilerplate/app/components/Toggle/Checkbox.tsx index 0d1ce9cb5..62eeaad9f 100644 --- a/boilerplate/app/components/Toggle/Checkbox.tsx +++ b/boilerplate/app/components/Toggle/Checkbox.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useCallback } from "react" +import { useEffect, useCallback, useState } from "react" import { Image, ImageStyle, Animated, StyleProp, View, ViewStyle } from "react-native" import { useAppTheme } from "@/theme/context" @@ -50,15 +50,15 @@ function CheckboxInput(props: CheckboxInputProps) { theme: { colors }, } = useAppTheme() - const opacity = useRef(new Animated.Value(0)) + const [opacity] = useState(() => new Animated.Value(0)) useEffect(() => { - Animated.timing(opacity.current, { + Animated.timing(opacity, { toValue: on ? 1 : 0, duration: 300, useNativeDriver: true, }).start() - }, [on]) + }, [on, opacity]) const offBackgroundColor = [ disabled && colors.palette.neutral400, @@ -98,7 +98,7 @@ function CheckboxInput(props: CheckboxInputProps) { $styles.toggleInner, { backgroundColor: onBackgroundColor }, $innerStyleOverride, - { opacity: opacity.current }, + { opacity }, ]} > new Animated.Value(0)) useEffect(() => { - Animated.timing(opacity.current, { + Animated.timing(opacity, { toValue: on ? 1 : 0, duration: 300, useNativeDriver: true, }).start() - }, [on]) + }, [on, opacity]) const offBackgroundColor = [ disabled && colors.palette.neutral400, @@ -86,7 +86,7 @@ function RadioInput(props: RadioInputProps) { $styles.toggleInner, { backgroundColor: onBackgroundColor }, $innerStyleOverride, - { opacity: opacity.current }, + { opacity }, ]} > new Animated.Value(on ? 1 : 0)) // Initial value is set based on isActive + const [opacity] = useState(() => new Animated.Value(0)) useEffect(() => { - Animated.timing(animate.current, { + Animated.timing(animate, { toValue: on ? 1 : 0, duration: 300, useNativeDriver: true, // Enable native driver for smoother animations }).start() - }, [on]) + }, [animate, on]) useEffect(() => { - Animated.timing(opacity.current, { + Animated.timing(opacity, { toValue: on ? 1 : 0, duration: 300, useNativeDriver: true, }).start() - }, [on]) + }, [opacity, on]) const knobSizeFallback = 2 @@ -137,7 +137,7 @@ function SwitchInput(props: SwitchInputProps) { : [offsetLeft, +(knobWidth || 0) + offsetRight] : [rtlAdjustment * offsetLeft, rtlAdjustment * (+(knobWidth || 0) + offsetRight)] - const $animatedSwitchKnob = animate.current.interpolate({ + const $animatedSwitchKnob = animate.interpolate({ inputRange: [0, 1], outputRange, }) @@ -149,7 +149,7 @@ function SwitchInput(props: SwitchInputProps) { $themedSwitchInner, { backgroundColor: onBackgroundColor }, $innerStyleOverride, - { opacity: opacity.current }, + { opacity }, ]} /> diff --git a/boilerplate/app/screens/DemoPodcastListScreen.tsx b/boilerplate/app/screens/DemoPodcastListScreen.tsx index ce2860228..b0fcc892c 100644 --- a/boilerplate/app/screens/DemoPodcastListScreen.tsx +++ b/boilerplate/app/screens/DemoPodcastListScreen.tsx @@ -32,9 +32,9 @@ import { isRTL } from "@/i18n" import { translate } from "@/i18n/translate" import { DemoTabScreenProps } from "@/navigators/navigationTypes" import type { EpisodeItem } from "@/services/api/types" -import type { ThemedStyle } from "@/theme/types" import { useAppTheme } from "@/theme/context" import { $styles } from "@/theme/styles" +import type { ThemedStyle } from "@/theme/types" import { delay } from "@/utils/delay" import { openLinkInBrowser } from "@/utils/openLinkInBrowser" @@ -146,9 +146,9 @@ const EpisodeCard = ({ const { isFavorite, datePublished, duration, parsedTitleAndSubtitle } = useEpisode(episode) const liked = useSharedValue(isFavorite ? 1 : 0) - const imageUri = useMemo(() => { - return rnrImages[Math.floor(Math.random() * rnrImages.length)] - }, []) + const [imageUri] = useState( + () => rnrImages[Math.floor(Math.random() * rnrImages.length)], + ) // Grey heart const animatedLikeButtonStyles = useAnimatedStyle(() => { @@ -176,8 +176,8 @@ const EpisodeCard = ({ const handlePressFavorite = useCallback(() => { onPressFavorite() - liked.value = withSpring(liked.value ? 0 : 1) - }, [liked, onPressFavorite]) + liked.set((prev) => withSpring(prev ? 0 : 1)) + }, [onPressFavorite, liked]) /** * Android has a "longpress" accessibility action. iOS does not, so we just have to use a hint. diff --git a/boilerplate/app/screens/LoginScreen.tsx b/boilerplate/app/screens/LoginScreen.tsx index 6756c71d6..4960c4f94 100644 --- a/boilerplate/app/screens/LoginScreen.tsx +++ b/boilerplate/app/screens/LoginScreen.tsx @@ -17,7 +17,7 @@ interface LoginScreenProps extends AppStackScreenProps<"Login"> {} export const LoginScreen: FC = () => { const authPasswordInput = useRef(null) - const [authPassword, setAuthPassword] = useState("") + const [authPassword, setAuthPassword] = useState("ign1teIsAwes0m3") const [isAuthPasswordHidden, setIsAuthPasswordHidden] = useState(true) const [isSubmitted, setIsSubmitted] = useState(false) const [attemptsCount, setAttemptsCount] = useState(0) @@ -32,7 +32,6 @@ export const LoginScreen: FC = () => { // Here is where you could fetch credentials from keychain or storage // and pre-fill the form fields. setAuthEmail("ignite@infinite.red") - setAuthPassword("ign1teIsAwes0m3") }, [setAuthEmail]) const error = isSubmitted ? validationError : "" diff --git a/boilerplate/app/theme/typography.ts b/boilerplate/app/theme/typography.ts index 05227c762..57b4cef17 100644 --- a/boilerplate/app/theme/typography.ts +++ b/boilerplate/app/theme/typography.ts @@ -1,30 +1,53 @@ // For documentation on fonts and typography, see docs/boilerplate/app/theme/typography.ts.md import { Platform } from "react-native" -import { - SpaceGrotesk_300Light as spaceGroteskLight, - SpaceGrotesk_400Regular as spaceGroteskRegular, - SpaceGrotesk_500Medium as spaceGroteskMedium, - SpaceGrotesk_600SemiBold as spaceGroteskSemiBold, - SpaceGrotesk_700Bold as spaceGroteskBold, -} from "@expo-google-fonts/space-grotesk" +import { FontSource, useFonts } from "expo-font" -export const customFontsToLoad = { - spaceGroteskLight, - spaceGroteskRegular, - spaceGroteskMedium, - spaceGroteskSemiBold, - spaceGroteskBold, +/** + * The naming here is important. Most font files come with + * a name like `SpaceGrotesk_300Light`, but iOS uses the font PostScript + * name (in this case `SpaceGrotesk-Light`). To keep the imports the + * same on both platforms use the PostScript name. + * + * For more info: https://docs.expo.dev/develop/user-interface/fonts/#how-to-determine-which-font-family-name-to-use + */ +export const customFontsToLoad: Record = + Platform.OS === "web" + ? { + "SpaceGrotesk-Light": require("../../assets/fonts/SpaceGrotesk-Light.ttf"), + "SpaceGrotesk-Regular": require("../../assets/fonts/SpaceGrotesk-Regular.ttf"), + "SpaceGrotesk-Medium": require("../../assets/fonts/SpaceGrotesk-Medium.ttf"), + "SpaceGrotesk-SemiBold": require("../../assets/fonts/SpaceGrotesk-SemiBold.ttf"), + "SpaceGrotesk-Bold": require("../../assets/fonts/SpaceGrotesk-Bold.ttf"), + } + : {} + +/** + * On iOS and Android, the fonts are embedded as part of the app binary + * using the expo config plugin in `app.config.ts`. See the project + * [`app.config.ts`](../../app.config.ts) for the expo-fonts configuration. The assets + * are added via the `app/assets/fonts` folder. This config plugin + * does NOT work for web, so we have to dynamically load the fonts via this hook. + * + * For more info: https://docs.expo.dev/versions/latest/sdk/font/ + */ +export const useCustomFonts = (): [boolean, Error | null] => { + const [areFontsLoaded, fontError] = useFonts(customFontsToLoad) + if (Platform.OS === "web") { + return [areFontsLoaded, fontError] + } + + // On native, fonts are precompiled and ready + return [true, null] } const fonts = { spaceGrotesk: { - // Cross-platform Google font. - light: "spaceGroteskLight", - normal: "spaceGroteskRegular", - medium: "spaceGroteskMedium", - semiBold: "spaceGroteskSemiBold", - bold: "spaceGroteskBold", + light: "SpaceGrotesk-Light", + normal: "SpaceGrotesk-Regular", + medium: "SpaceGrotesk-Medium", + semiBold: "SpaceGrotesk-SemiBold", + bold: "SpaceGrotesk-Bold", }, helveticaNeue: { // iOS only font. diff --git a/boilerplate/assets/fonts/SpaceGrotesk-Bold.ttf b/boilerplate/assets/fonts/SpaceGrotesk-Bold.ttf new file mode 100644 index 000000000..8a8611a58 Binary files /dev/null and b/boilerplate/assets/fonts/SpaceGrotesk-Bold.ttf differ diff --git a/boilerplate/assets/fonts/SpaceGrotesk-Light.ttf b/boilerplate/assets/fonts/SpaceGrotesk-Light.ttf new file mode 100644 index 000000000..0f03f08b3 Binary files /dev/null and b/boilerplate/assets/fonts/SpaceGrotesk-Light.ttf differ diff --git a/boilerplate/assets/fonts/SpaceGrotesk-Medium.ttf b/boilerplate/assets/fonts/SpaceGrotesk-Medium.ttf new file mode 100644 index 000000000..e530cf836 Binary files /dev/null and b/boilerplate/assets/fonts/SpaceGrotesk-Medium.ttf differ diff --git a/boilerplate/assets/fonts/SpaceGrotesk-Regular.ttf b/boilerplate/assets/fonts/SpaceGrotesk-Regular.ttf new file mode 100644 index 000000000..8215f81e1 Binary files /dev/null and b/boilerplate/assets/fonts/SpaceGrotesk-Regular.ttf differ diff --git a/boilerplate/assets/fonts/SpaceGrotesk-SemiBold.ttf b/boilerplate/assets/fonts/SpaceGrotesk-SemiBold.ttf new file mode 100644 index 000000000..e05b9673a Binary files /dev/null and b/boilerplate/assets/fonts/SpaceGrotesk-SemiBold.ttf differ diff --git a/boilerplate/package.json b/boilerplate/package.json index 48c9e419f..49f2fdd77 100644 --- a/boilerplate/package.json +++ b/boilerplate/package.json @@ -30,69 +30,69 @@ "build:android:preview": "eas build --profile preview --platform android --local", "build:android:prod": "eas build --profile production --platform android --local" }, - "dependencies": { - "@expo-google-fonts/space-grotesk": "^0.4.0", - "@expo/metro-runtime": "~55.0.6", + "dependencies": { + "@expo/metro-runtime": "~56.0.14", "@react-navigation/bottom-tabs": "^7.2.0", "@react-navigation/native": "^7.0.14", "@react-navigation/native-stack": "^7.2.0", "apisauce": "3.1.1", "date-fns": "^4.1.0", - "expo": "55.0.5", - "expo-application": "~55.0.8", - "expo-build-properties": "~55.0.9", - "expo-dev-client": "~55.0.11", - "expo-font": "~55.0.4", - "expo-linking": "~55.0.7", - "expo-localization": "~55.0.8", - "expo-splash-screen": "~55.0.10", - "expo-system-ui": "~55.0.9", + "expo": "^56.0.0", + "expo-application": "~56.0.3", + "expo-build-properties": "~56.0.17", + "expo-dev-client": "~56.0.19", + "expo-font": "~56.0.5", + "expo-linking": "~56.0.13", + "expo-localization": "~56.0.6", + "expo-splash-screen": "~56.0.10", + "expo-system-ui": "~56.0.5", "i18next": "^23.14.0", "intl-pluralrules": "^2.0.1", - "react": "19.2.0", - "react-dom": "19.2.0", + "react": "19.2.3", + "react-dom": "19.2.3", "react-i18next": "^15.0.1", - "react-native": "0.83.2", + "react-native": "0.85.3", "react-native-drawer-layout": "^4.0.1", "react-native-edge-to-edge": "~1.6.1", - "react-native-gesture-handler": "~2.30.0", - "react-native-keyboard-controller": "1.20.7", + "react-native-gesture-handler": "~2.31.1", + "react-native-keyboard-controller": "1.21.6", "react-native-mmkv": "3.3.3", - "react-native-reanimated": "4.2.1", - "react-native-safe-area-context": "~5.6.2", - "react-native-screens": "~4.23.0", + "react-native-reanimated": "4.3.1", + "react-native-safe-area-context": "~5.7.0", + "react-native-screens": "4.25.2", "react-native-web": "~0.21.0", - "react-native-worklets": "0.7.2", + "react-native-worklets": "0.8.3", "setimmediate": "^1.0.5" }, "devDependencies": { "@babel/core": "^7.20.0", "@babel/preset-env": "^7.20.0", "@babel/runtime": "^7.20.0", + "@react-native/jest-preset": "^0.85.0", "@testing-library/react-native": "^13.2.0", "@types/jest": "^29.5.14", "@types/react": "~19.2.10", "babel-jest": "^29.2.1", "dependency-cruiser": "^17.0.2", "eslint": "^8.57.0", - "eslint-config-expo": "~55.0.0", + "eslint-config-expo": "~56.0.4", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react-native": "^4.1.0", "eslint-plugin-reactotron": "^0.1.2", "jest": "~29.7.0", - "jest-expo": "~55.0.9", + "jest-expo": "~56.0.4", "prettier": "^3.3.3", - "react-test-renderer": "19.2.0", + "react-test-renderer": "19.2.3", "reactotron-core-client": "^2.9.4", "reactotron-react-js": "^3.3.11", "reactotron-react-native": "^5.0.5", "reactotron-react-native-mmkv": "^0.2.6", "ts-jest": "^29.1.1", "tsx": "^4.20.3", - "typescript": "~5.9.2" + "typescript": "~6.0.3" }, "engines": { - "node": ">=20.0.0" + "node": ">=20.19.4" } } \ No newline at end of file diff --git a/boilerplate/src/app/_layout.tsx b/boilerplate/src/app/_layout.tsx index 66b2b1750..7c52245c1 100644 --- a/boilerplate/src/app/_layout.tsx +++ b/boilerplate/src/app/_layout.tsx @@ -1,12 +1,11 @@ import { useEffect, useState } from "react" import { Slot, SplashScreen } from "expo-router" -import { useFonts } from "@expo-google-fonts/space-grotesk" import { KeyboardProvider } from "react-native-keyboard-controller" import { initialWindowMetrics, SafeAreaProvider } from "react-native-safe-area-context" import { initI18n } from "@/i18n" import { ThemeProvider } from "@/theme/context" -import { customFontsToLoad } from "@/theme/typography" +import { useCustomFonts } from "@/theme/typography" import { loadDateFnsLocale } from "@/utils/formatDate" SplashScreen.preventAutoHideAsync() @@ -19,7 +18,8 @@ if (__DEV__) { } export default function Root() { - const [fontsLoaded, fontError] = useFonts(customFontsToLoad) + + const [areFontsLoaded, fontLoadError] = useCustomFonts() const [isI18nInitialized, setIsI18nInitialized] = useState(false) useEffect(() => { @@ -28,11 +28,11 @@ export default function Root() { .then(() => loadDateFnsLocale()) }, []) - const loaded = fontsLoaded && isI18nInitialized + const loaded = areFontsLoaded && isI18nInitialized useEffect(() => { - if (fontError) throw fontError - }, [fontError]) + if (fontLoadError ) throw fontLoadError + }, [fontLoadError]) useEffect(() => { if (loaded) { diff --git a/boilerplate/tsconfig.json b/boilerplate/tsconfig.json index 88a3ea698..9692dedab 100644 --- a/boilerplate/tsconfig.json +++ b/boilerplate/tsconfig.json @@ -20,7 +20,7 @@ ], "skipLibCheck": true, "resolveJsonModule": true, - "baseUrl": ".", + "types": ["jest", "node"], "paths": { "@/*": ["./app/*"], "@assets/*": ["./assets/*"] diff --git a/docs/README.md b/docs/README.md index d4635bf5a..0e8ffacb6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -52,13 +52,13 @@ Nothing makes it into Ignite unless it's been proven on projects that Infinite R | Library | Category | Version | Description | | -------------------------------- | -------------------- | ------- | ---------------------------------------------- | -| React Native | Mobile Framework | v0.81 | The best cross-platform mobile framework | +| React Native | Mobile Framework | v0.85 | The best cross-platform mobile framework | | React | UI Framework | v19 | The most popular UI framework in the world | | TypeScript | Language | v5 | Static typechecking | | React Navigation | Navigation | v7 | Performant and consistent navigation framework | -| Expo | SDK | v55 | Allows (optional) Expo modules | -| Expo Font | Custom Fonts | v14 | Import custom fonts | -| Expo Localization | Internationalization | v17 | i18n support (including RTL!) | +| Expo | SDK | v56 | Allows (optional) Expo modules | +| Expo Font | Custom Fonts | v56 | Import custom fonts | +| Expo Localization | Internationalization | v56 | i18n support (including RTL!) | | RN Reanimated | Animations | v4 | Beautiful and performant animations | | MMKV | Persistence | v3 | State persistence | | apisauce | REST client | v3 | Communicate with back-end | diff --git a/package.json b/package.json index 02503c27b..2f4cb09aa 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "babel-eslint": "^10.1.0", "dependency-cruiser": "^17.0.2", "eslint": "^8.57.0", - "eslint-config-expo": "~9.2.0", + "eslint-config-expo": "~56.0.4", "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-prettier": "^5.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4f8f6afdb..1b3064f1f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,8 +55,8 @@ importers: specifier: ^8.57.0 version: 8.57.1 eslint-config-expo: - specifier: ~9.2.0 - version: 9.2.0(eslint@8.57.1)(typescript@5.8.3) + specifier: ~56.0.4 + version: 56.0.4(eslint@8.57.1)(typescript@5.8.3) eslint-config-prettier: specifier: ^9.1.0 version: 9.1.2(eslint@8.57.1) @@ -290,6 +290,12 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.12.2': resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -605,13 +611,13 @@ packages: typescript: optional: true - '@typescript-eslint/eslint-plugin@8.49.0': - resolution: {integrity: sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==} + '@typescript-eslint/eslint-plugin@8.60.1': + resolution: {integrity: sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.49.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/parser': ^8.60.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/parser@7.18.0': resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} @@ -623,12 +629,12 @@ packages: typescript: optional: true - '@typescript-eslint/parser@8.49.0': - resolution: {integrity: sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==} + '@typescript-eslint/parser@8.60.1': + resolution: {integrity: sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/project-service@8.49.0': resolution: {integrity: sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==} @@ -636,6 +642,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/project-service@8.60.1': + resolution: {integrity: sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/scope-manager@7.18.0': resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} engines: {node: ^18.18.0 || >=20.0.0} @@ -644,12 +656,22 @@ packages: resolution: {integrity: sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.60.1': + resolution: {integrity: sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/tsconfig-utils@8.49.0': resolution: {integrity: sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/tsconfig-utils@8.60.1': + resolution: {integrity: sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/type-utils@7.18.0': resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} engines: {node: ^18.18.0 || >=20.0.0} @@ -660,12 +682,12 @@ packages: typescript: optional: true - '@typescript-eslint/type-utils@8.49.0': - resolution: {integrity: sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==} + '@typescript-eslint/type-utils@8.60.1': + resolution: {integrity: sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/types@7.18.0': resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} @@ -675,6 +697,10 @@ packages: resolution: {integrity: sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.60.1': + resolution: {integrity: sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@7.18.0': resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} engines: {node: ^18.18.0 || >=20.0.0} @@ -690,6 +716,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/typescript-estree@8.60.1': + resolution: {integrity: sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/utils@7.18.0': resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} engines: {node: ^18.18.0 || >=20.0.0} @@ -703,6 +735,13 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/utils@8.60.1': + resolution: {integrity: sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/visitor-keys@7.18.0': resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} engines: {node: ^18.18.0 || >=20.0.0} @@ -711,6 +750,10 @@ packages: resolution: {integrity: sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.60.1': + resolution: {integrity: sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} @@ -1001,6 +1044,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + bare-events@2.8.2: resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==} peerDependencies: @@ -1061,6 +1108,10 @@ packages: brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -1486,8 +1537,8 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-config-expo@9.2.0: - resolution: {integrity: sha512-TQgmSx+2mRM7qUS0hB5kTDrHcSC35rA1UzOSgK5YRLmSkSMlKLmXkUrhwOpnyo9D/nHdf4ERRAySRYxgA6dlrw==} + eslint-config-expo@56.0.4: + resolution: {integrity: sha512-1OD7rJMxCchKHxq+U+OQsAxVtzAxeUb9875g6+15KsSD9fqKTgq7DEEWYwunzU9r9E8kYJ+mh7+j86vF9m9NMw==} peerDependencies: eslint: '>=8.10' @@ -1534,8 +1585,8 @@ packages: eslint-import-resolver-webpack: optional: true - eslint-plugin-expo@0.1.4: - resolution: {integrity: sha512-YA7yiMacQbLJySuyJA0Eb5V65obqp6fVOWtw1JdYDRWC5MeToPrnNvhGDpk01Bv3Vm4ownuzUfvi89MXi1d6cg==} + eslint-plugin-expo@1.0.3: + resolution: {integrity: sha512-C1v9NPvpDET36+7Klpp/+53Jl+VzOfpbDxpKtL/pAPhCDwTX0kW6Swo425PT0uc4AMT5jpQbB7hSKFjKOGMl4A==} engines: {node: '>=18.0.0'} peerDependencies: eslint: '>=8.10' @@ -1564,11 +1615,11 @@ packages: eslint-config-prettier: optional: true - eslint-plugin-react-hooks@5.2.0: - resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} - engines: {node: '>=10'} + eslint-plugin-react-hooks@7.1.1: + resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} + engines: {node: '>=18'} peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 eslint-plugin-react-native-globals@0.1.2: resolution: {integrity: sha512-9aEPf1JEpiTjcFAmmyw8eiIXmcNZOqaZyHO77wgm0/dWfT/oxC1SrIq8ET38pMxHYrcB6Uew+TzUVsBeczF88g==} @@ -1605,6 +1656,10 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + eslint@8.57.1: resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1908,6 +1963,12 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + hook-std@2.0.0: resolution: {integrity: sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g==} engines: {node: '>=8'} @@ -2599,6 +2660,10 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -3469,6 +3534,12 @@ packages: peerDependencies: typescript: '>=4.8.4' + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + ts-jest@29.4.6: resolution: {integrity: sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} @@ -3735,6 +3806,15 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + snapshots: '@babel/code-frame@7.27.1': @@ -3954,6 +4034,11 @@ snapshots: eslint: 8.57.1 eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.12.2': {} '@eslint/eslintrc@2.1.4': @@ -4474,18 +4559,18 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.60.1(@typescript-eslint/parser@8.60.1(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.49.0(eslint@8.57.1)(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.49.0 - '@typescript-eslint/type-utils': 8.49.0(eslint@8.57.1)(typescript@5.8.3) - '@typescript-eslint/utils': 8.49.0(eslint@8.57.1)(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.49.0 + '@typescript-eslint/parser': 8.60.1(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.60.1 + '@typescript-eslint/type-utils': 8.60.1(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/utils': 8.60.1(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.60.1 eslint: 8.57.1 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.8.3) + ts-api-utils: 2.5.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -4503,12 +4588,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.49.0(eslint@8.57.1)(typescript@5.8.3)': + '@typescript-eslint/parser@8.60.1(eslint@8.57.1)(typescript@5.8.3)': dependencies: - '@typescript-eslint/scope-manager': 8.49.0 - '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.49.0 + '@typescript-eslint/scope-manager': 8.60.1 + '@typescript-eslint/types': 8.60.1 + '@typescript-eslint/typescript-estree': 8.60.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.60.1 debug: 4.4.3 eslint: 8.57.1 typescript: 5.8.3 @@ -4524,6 +4609,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.60.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.60.1(typescript@5.8.3) + '@typescript-eslint/types': 8.60.1 + debug: 4.4.3 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@7.18.0': dependencies: '@typescript-eslint/types': 7.18.0 @@ -4534,10 +4628,19 @@ snapshots: '@typescript-eslint/types': 8.49.0 '@typescript-eslint/visitor-keys': 8.49.0 + '@typescript-eslint/scope-manager@8.60.1': + dependencies: + '@typescript-eslint/types': 8.60.1 + '@typescript-eslint/visitor-keys': 8.60.1 + '@typescript-eslint/tsconfig-utils@8.49.0(typescript@5.8.3)': dependencies: typescript: 5.8.3 + '@typescript-eslint/tsconfig-utils@8.60.1(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + '@typescript-eslint/type-utils@7.18.0(eslint@8.57.1)(typescript@5.8.3)': dependencies: '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.3) @@ -4550,14 +4653,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.49.0(eslint@8.57.1)(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.60.1(eslint@8.57.1)(typescript@5.8.3)': dependencies: - '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.49.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/types': 8.60.1 + '@typescript-eslint/typescript-estree': 8.60.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.60.1(eslint@8.57.1)(typescript@5.8.3) debug: 4.4.3 eslint: 8.57.1 - ts-api-utils: 2.1.0(typescript@5.8.3) + ts-api-utils: 2.5.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -4566,6 +4669,8 @@ snapshots: '@typescript-eslint/types@8.49.0': {} + '@typescript-eslint/types@8.60.1': {} + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.8.3)': dependencies: '@typescript-eslint/types': 7.18.0 @@ -4596,6 +4701,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.60.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/project-service': 8.60.1(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.60.1(typescript@5.8.3) + '@typescript-eslint/types': 8.60.1 + '@typescript-eslint/visitor-keys': 8.60.1 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.5.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@7.18.0(eslint@8.57.1)(typescript@5.8.3)': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) @@ -4618,6 +4738,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.60.1(eslint@8.57.1)(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@typescript-eslint/scope-manager': 8.60.1 + '@typescript-eslint/types': 8.60.1 + '@typescript-eslint/typescript-estree': 8.60.1(typescript@5.8.3) + eslint: 8.57.1 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@7.18.0': dependencies: '@typescript-eslint/types': 7.18.0 @@ -4628,6 +4759,11 @@ snapshots: '@typescript-eslint/types': 8.49.0 eslint-visitor-keys: 4.2.1 + '@typescript-eslint/visitor-keys@8.60.1': + dependencies: + '@typescript-eslint/types': 8.60.1 + eslint-visitor-keys: 5.0.1 + '@ungap/structured-clone@1.3.0': {} '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -4938,6 +5074,8 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + bare-events@2.8.2: {} bare-fs@4.5.2: @@ -4998,6 +5136,10 @@ snapshots: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -5520,16 +5662,16 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-expo@9.2.0(eslint@8.57.1)(typescript@5.8.3): + eslint-config-expo@56.0.4(eslint@8.57.1)(typescript@5.8.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3) - '@typescript-eslint/parser': 8.49.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.60.1(@typescript-eslint/parser@8.60.1(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/parser': 8.60.1(eslint@8.57.1)(typescript@5.8.3) eslint: 8.57.1 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1) - eslint-plugin-expo: 0.1.4(eslint@8.57.1)(typescript@5.8.3) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-plugin-expo: 1.0.3(eslint@8.57.1)(typescript@5.8.3) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.60.1(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) eslint-plugin-react: 7.37.5(eslint@8.57.1) - eslint-plugin-react-hooks: 5.2.0(eslint@8.57.1) + eslint-plugin-react-hooks: 7.1.1(eslint@8.57.1) globals: 16.5.0 transitivePeerDependencies: - eslint-import-resolver-webpack @@ -5574,18 +5716,18 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.60.1(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.49.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/parser': 8.60.1(eslint@8.57.1)(typescript@5.8.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-plugin-expo@0.1.4(eslint@8.57.1)(typescript@5.8.3): + eslint-plugin-expo@1.0.3(eslint@8.57.1)(typescript@5.8.3): dependencies: '@typescript-eslint/types': 8.49.0 '@typescript-eslint/utils': 8.49.0(eslint@8.57.1)(typescript@5.8.3) @@ -5623,7 +5765,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.60.1(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -5634,7 +5776,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.60.1(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -5646,7 +5788,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.49.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/parser': 8.60.1(eslint@8.57.1)(typescript@5.8.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -5661,9 +5803,16 @@ snapshots: optionalDependencies: eslint-config-prettier: 9.1.2(eslint@8.57.1) - eslint-plugin-react-hooks@5.2.0(eslint@8.57.1): + eslint-plugin-react-hooks@7.1.1(eslint@8.57.1): dependencies: + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 eslint: 8.57.1 + hermes-parser: 0.25.1 + zod: 4.4.3 + zod-validation-error: 4.0.2(zod@4.4.3) + transitivePeerDependencies: + - supports-color eslint-plugin-react-native-globals@0.1.2: {} @@ -5709,6 +5858,8 @@ snapshots: eslint-visitor-keys@4.2.1: {} + eslint-visitor-keys@5.0.1: {} + eslint@8.57.1: dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) @@ -6090,6 +6241,12 @@ snapshots: dependencies: function-bind: 1.1.2 + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + hook-std@2.0.0: {} hosted-git-info@2.8.9: {} @@ -6923,6 +7080,10 @@ snapshots: min-indent@1.0.1: {} + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.6 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -7819,6 +7980,10 @@ snapshots: dependencies: typescript: 5.8.3 + ts-api-utils@2.5.0(typescript@5.8.3): + dependencies: + typescript: 5.8.3 + ts-jest@29.4.6(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.5))(jest-util@29.7.0)(jest@29.7.0(@types/node@16.6.1)(ts-node@10.9.2(@types/node@16.6.1)(typescript@5.8.3)))(typescript@5.8.3): dependencies: bs-logger: 0.2.6 @@ -8126,3 +8291,9 @@ snapshots: yn@3.1.1: {} yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@4.4.3): + dependencies: + zod: 4.4.3 + + zod@4.4.3: {} diff --git a/src/commands/new.ts b/src/commands/new.ts index 948e56264..811865c4e 100644 --- a/src/commands/new.ts +++ b/src/commands/new.ts @@ -591,7 +591,7 @@ module.exports = { // find "expo-localization" line and append "expo-router" line after it packageJsonRaw = packageJsonRaw.replace( /"expo-localization": ".*",/g, - `"expo-localization": "${packageJsonParsed.dependencies["expo-localization"]}",${EOL} "expo-router": "~55.0.4",`, + `"expo-localization": "${packageJsonParsed.dependencies["expo-localization"]}",${EOL} "expo-router": "~56.2.9",`, ) // replace "main" entry point from App.js to "expo-router/entry"