diff --git a/.changeset/dirty-years-joke.md b/.changeset/dirty-years-joke.md new file mode 100644 index 00000000..ad9ea0b1 --- /dev/null +++ b/.changeset/dirty-years-joke.md @@ -0,0 +1,5 @@ +--- +'@tanstack/devtools-event-client': patch +--- + +fix issue with the client not working in react native diff --git a/.changeset/major-lamps-float.md b/.changeset/major-lamps-float.md new file mode 100644 index 00000000..44887da4 --- /dev/null +++ b/.changeset/major-lamps-float.md @@ -0,0 +1,5 @@ +--- +'@tanstack/devtools-vite': patch +--- + +improved accuracy of go to source to go to exact column and also improved accuracy of enhanced console logs to go to exact console log location diff --git a/examples/react-native/expo-basic/.cursor/mcp.json b/examples/react-native/expo-basic/.cursor/mcp.json new file mode 100644 index 00000000..182bf937 --- /dev/null +++ b/examples/react-native/expo-basic/.cursor/mcp.json @@ -0,0 +1,11 @@ +{ + "mcpServers": { + "RadonAi": { + "url": "http://127.0.0.1:63266/mcp", + "type": "http", + "headers": { + "nonce": "7a6ba1e0-1f15-4d68-90df-3a71371f2694" + } + } + } +} diff --git a/examples/react-native/expo-basic/.gitignore b/examples/react-native/expo-basic/.gitignore new file mode 100644 index 00000000..f8c6c2e8 --- /dev/null +++ b/examples/react-native/expo-basic/.gitignore @@ -0,0 +1,43 @@ +# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +# dependencies +node_modules/ + +# Expo +.expo/ +dist/ +web-build/ +expo-env.d.ts + +# Native +.kotlin/ +*.orig.* +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision + +# Metro +.metro-health-check* + +# debug +npm-debug.* +yarn-debug.* +yarn-error.* + +# macOS +.DS_Store +*.pem + +# local env files +.env*.local + +# typescript +*.tsbuildinfo + +app-example + +# generated native folders +/ios +/android diff --git a/examples/react-native/expo-basic/.vscode/extensions.json b/examples/react-native/expo-basic/.vscode/extensions.json new file mode 100644 index 00000000..b7ed8377 --- /dev/null +++ b/examples/react-native/expo-basic/.vscode/extensions.json @@ -0,0 +1 @@ +{ "recommendations": ["expo.vscode-expo-tools"] } diff --git a/examples/react-native/expo-basic/.vscode/settings.json b/examples/react-native/expo-basic/.vscode/settings.json new file mode 100644 index 00000000..e2798e42 --- /dev/null +++ b/examples/react-native/expo-basic/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "editor.codeActionsOnSave": { + "source.fixAll": "explicit", + "source.organizeImports": "explicit", + "source.sortMembers": "explicit" + } +} diff --git a/examples/react-native/expo-basic/README.md b/examples/react-native/expo-basic/README.md new file mode 100644 index 00000000..48dd63ff --- /dev/null +++ b/examples/react-native/expo-basic/README.md @@ -0,0 +1,50 @@ +# Welcome to your Expo app šŸ‘‹ + +This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app). + +## Get started + +1. Install dependencies + + ```bash + npm install + ``` + +2. Start the app + + ```bash + npx expo start + ``` + +In the output, you'll find options to open the app in a + +- [development build](https://docs.expo.dev/develop/development-builds/introduction/) +- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/) +- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/) +- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo + +You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction). + +## Get a fresh project + +When you're ready, run: + +```bash +npm run reset-project +``` + +This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing. + +## Learn more + +To learn more about developing your project with Expo, look at the following resources: + +- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides). +- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web. + +## Join the community + +Join our community of developers creating universal apps. + +- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute. +- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions. diff --git a/examples/react-native/expo-basic/app.json b/examples/react-native/expo-basic/app.json new file mode 100644 index 00000000..ad1c7586 --- /dev/null +++ b/examples/react-native/expo-basic/app.json @@ -0,0 +1,48 @@ +{ + "expo": { + "name": "expo-basic", + "slug": "expo-basic", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/images/icon.png", + "scheme": "expobasic", + "userInterfaceStyle": "automatic", + "newArchEnabled": true, + "ios": { + "supportsTablet": true + }, + "android": { + "adaptiveIcon": { + "backgroundColor": "#E6F4FE", + "foregroundImage": "./assets/images/android-icon-foreground.png", + "backgroundImage": "./assets/images/android-icon-background.png", + "monochromeImage": "./assets/images/android-icon-monochrome.png" + }, + "edgeToEdgeEnabled": true, + "predictiveBackGestureEnabled": false + }, + "web": { + "output": "static", + "favicon": "./assets/images/favicon.png" + }, + "plugins": [ + "expo-router", + [ + "expo-splash-screen", + { + "image": "./assets/images/splash-icon.png", + "imageWidth": 200, + "resizeMode": "contain", + "backgroundColor": "#ffffff", + "dark": { + "backgroundColor": "#000000" + } + } + ] + ], + "experiments": { + "typedRoutes": true, + "reactCompiler": true + } + } +} diff --git a/examples/react-native/expo-basic/app/(tabs)/_layout.tsx b/examples/react-native/expo-basic/app/(tabs)/_layout.tsx new file mode 100644 index 00000000..fc9f887e --- /dev/null +++ b/examples/react-native/expo-basic/app/(tabs)/_layout.tsx @@ -0,0 +1,40 @@ +import { Tabs } from 'expo-router' +import React from 'react' + +import { HapticTab } from '@/components/haptic-tab' +import { IconSymbol } from '@/components/ui/icon-symbol' +import { Colors } from '@/constants/theme' +import { useColorScheme } from '@/hooks/use-color-scheme' + +export default function TabLayout() { + const colorScheme = useColorScheme() + + return ( + + ( + + ), + }} + /> + ( + + ), + }} + /> + + ) +} diff --git a/examples/react-native/expo-basic/app/(tabs)/explore.tsx b/examples/react-native/expo-basic/app/(tabs)/explore.tsx new file mode 100644 index 00000000..f04d1f83 --- /dev/null +++ b/examples/react-native/expo-basic/app/(tabs)/explore.tsx @@ -0,0 +1,126 @@ +import { Image } from 'expo-image' +import { Platform, StyleSheet } from 'react-native' + +import { Collapsible } from '@/components/ui/collapsible' +import { ExternalLink } from '@/components/external-link' +import ParallaxScrollView from '@/components/parallax-scroll-view' +import { ThemedText } from '@/components/themed-text' +import { ThemedView } from '@/components/themed-view' +import { IconSymbol } from '@/components/ui/icon-symbol' +import { Fonts } from '@/constants/theme' + +export default function TabTwoScreen() { + return ( + + } + > + + + Explore + + + + This app includes example code to help you get started. + + + + This app has two screens:{' '} + app/(tabs)/index.tsx{' '} + and{' '} + app/(tabs)/explore.tsx + + + The layout file in{' '} + app/(tabs)/_layout.tsx{' '} + sets up the tab navigator. + + + Learn more + + + + + You can open this project on Android, iOS, and the web. To open the + web version, press w{' '} + in the terminal running this project. + + + + + For static images, you can use the{' '} + @2x and{' '} + @3x suffixes to + provide files for different screen densities + + + + Learn more + + + + + This template has light and dark mode support. The{' '} + useColorScheme() hook + lets you inspect what the user's current color scheme is, and so + you can adjust UI colors accordingly. + + + Learn more + + + + + This template includes an example of an animated component. The{' '} + + components/HelloWave.tsx + {' '} + component uses the powerful{' '} + + react-native-reanimated + {' '} + library to create a waving hand animation. + + {Platform.select({ + ios: ( + + The{' '} + + components/ParallaxScrollView.tsx + {' '} + component provides a parallax effect for the header image. + + ), + })} + + + ) +} + +const styles = StyleSheet.create({ + headerImage: { + color: '#808080', + bottom: -90, + left: -35, + position: 'absolute', + }, + titleContainer: { + flexDirection: 'row', + gap: 8, + }, +}) diff --git a/examples/react-native/expo-basic/app/(tabs)/index.tsx b/examples/react-native/expo-basic/app/(tabs)/index.tsx new file mode 100644 index 00000000..f0fd3da0 --- /dev/null +++ b/examples/react-native/expo-basic/app/(tabs)/index.tsx @@ -0,0 +1,107 @@ +import { Image } from 'expo-image' +import { Platform, StyleSheet } from 'react-native' + +import { HelloWave } from '@/components/hello-wave' +import ParallaxScrollView from '@/components/parallax-scroll-view' +import { ThemedText } from '@/components/themed-text' +import { ThemedView } from '@/components/themed-view' +import { Link } from 'expo-router' + +export default function HomeScreen() { + return ( + + } + > + + Welcome! + + + + Step 1: Try it + + Edit{' '} + app/(tabs)/index.tsx{' '} + to see changes. Press{' '} + + {Platform.select({ + ios: 'cmd + d', + android: 'cmd + m', + web: 'F12', + })} + {' '} + to open developer tools. + + + + + + Step 2: Explore + + + + alert('Action pressed')} + /> + alert('Share pressed')} + /> + + alert('Delete pressed')} + /> + + + + + + {`Tap the Explore tab to learn more about what's included in this starter app.`} + + + + Step 3: Get a fresh start + + {`When you're ready, run `} + + npm run reset-project + {' '} + to get a fresh app{' '} + directory. This will move the current{' '} + app to{' '} + app-example. + + + + ) +} + +const styles = StyleSheet.create({ + titleContainer: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + }, + stepContainer: { + gap: 8, + marginBottom: 8, + }, + reactLogo: { + height: 178, + width: 290, + bottom: 0, + left: 0, + position: 'absolute', + }, +}) diff --git a/examples/react-native/expo-basic/app/_layout.tsx b/examples/react-native/expo-basic/app/_layout.tsx new file mode 100644 index 00000000..72a5eac8 --- /dev/null +++ b/examples/react-native/expo-basic/app/_layout.tsx @@ -0,0 +1,42 @@ +import { + DarkTheme, + DefaultTheme, + ThemeProvider, +} from '@react-navigation/native' +import { Stack } from 'expo-router' +import { StatusBar } from 'expo-status-bar' +import 'react-native-reanimated' +import { EventClient } from '@tanstack/devtools-event-client' +import { useColorScheme } from '@/hooks/use-color-scheme' + +const eventClient = new EventClient({ + pluginId: 'my-app', + debug: true, + // Uncomment the line below to see logs from the event client + // onLog: console.log, +}) + +eventClient.on('test', (pay) => { + console.log('Received test event with payload:', pay) +}) + +export const unstable_settings = { + anchor: '(tabs)', +} + +export default function RootLayout() { + const colorScheme = useColorScheme() + + return ( + + + + + + + + ) +} diff --git a/examples/react-native/expo-basic/app/modal.tsx b/examples/react-native/expo-basic/app/modal.tsx new file mode 100644 index 00000000..4ae16c92 --- /dev/null +++ b/examples/react-native/expo-basic/app/modal.tsx @@ -0,0 +1,29 @@ +import { Link } from 'expo-router' +import { StyleSheet } from 'react-native' + +import { ThemedText } from '@/components/themed-text' +import { ThemedView } from '@/components/themed-view' + +export default function ModalScreen() { + return ( + + This is a modal + + Go to home screen + + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + padding: 20, + }, + link: { + marginTop: 15, + paddingVertical: 15, + }, +}) diff --git a/examples/react-native/expo-basic/assets/images/android-icon-background.png b/examples/react-native/expo-basic/assets/images/android-icon-background.png new file mode 100644 index 00000000..5ffefc5b Binary files /dev/null and b/examples/react-native/expo-basic/assets/images/android-icon-background.png differ diff --git a/examples/react-native/expo-basic/assets/images/android-icon-foreground.png b/examples/react-native/expo-basic/assets/images/android-icon-foreground.png new file mode 100644 index 00000000..3a9e5016 Binary files /dev/null and b/examples/react-native/expo-basic/assets/images/android-icon-foreground.png differ diff --git a/examples/react-native/expo-basic/assets/images/android-icon-monochrome.png b/examples/react-native/expo-basic/assets/images/android-icon-monochrome.png new file mode 100644 index 00000000..77484ebd Binary files /dev/null and b/examples/react-native/expo-basic/assets/images/android-icon-monochrome.png differ diff --git a/examples/react-native/expo-basic/assets/images/favicon.png b/examples/react-native/expo-basic/assets/images/favicon.png new file mode 100644 index 00000000..408bd746 Binary files /dev/null and b/examples/react-native/expo-basic/assets/images/favicon.png differ diff --git a/examples/react-native/expo-basic/assets/images/icon.png b/examples/react-native/expo-basic/assets/images/icon.png new file mode 100644 index 00000000..7165a53c Binary files /dev/null and b/examples/react-native/expo-basic/assets/images/icon.png differ diff --git a/examples/react-native/expo-basic/assets/images/partial-react-logo.png b/examples/react-native/expo-basic/assets/images/partial-react-logo.png new file mode 100644 index 00000000..66fd9570 Binary files /dev/null and b/examples/react-native/expo-basic/assets/images/partial-react-logo.png differ diff --git a/examples/react-native/expo-basic/assets/images/react-logo.png b/examples/react-native/expo-basic/assets/images/react-logo.png new file mode 100644 index 00000000..9d72a9ff Binary files /dev/null and b/examples/react-native/expo-basic/assets/images/react-logo.png differ diff --git a/examples/react-native/expo-basic/assets/images/react-logo@2x.png b/examples/react-native/expo-basic/assets/images/react-logo@2x.png new file mode 100644 index 00000000..2229b130 Binary files /dev/null and b/examples/react-native/expo-basic/assets/images/react-logo@2x.png differ diff --git a/examples/react-native/expo-basic/assets/images/react-logo@3x.png b/examples/react-native/expo-basic/assets/images/react-logo@3x.png new file mode 100644 index 00000000..a99b2032 Binary files /dev/null and b/examples/react-native/expo-basic/assets/images/react-logo@3x.png differ diff --git a/examples/react-native/expo-basic/assets/images/splash-icon.png b/examples/react-native/expo-basic/assets/images/splash-icon.png new file mode 100644 index 00000000..03d6f6b6 Binary files /dev/null and b/examples/react-native/expo-basic/assets/images/splash-icon.png differ diff --git a/examples/react-native/expo-basic/components/external-link.tsx b/examples/react-native/expo-basic/components/external-link.tsx new file mode 100644 index 00000000..3cc7b121 --- /dev/null +++ b/examples/react-native/expo-basic/components/external-link.tsx @@ -0,0 +1,25 @@ +import { Href, Link } from 'expo-router' +import { openBrowserAsync, WebBrowserPresentationStyle } from 'expo-web-browser' +import { type ComponentProps } from 'react' + +type Props = Omit, 'href'> & { href: Href & string } + +export function ExternalLink({ href, ...rest }: Props) { + return ( + { + if (process.env.EXPO_OS !== 'web') { + // Prevent the default behavior of linking to the default browser on native. + event.preventDefault() + // Open the link in an in-app browser. + await openBrowserAsync(href, { + presentationStyle: WebBrowserPresentationStyle.AUTOMATIC, + }) + } + }} + /> + ) +} diff --git a/examples/react-native/expo-basic/components/haptic-tab.tsx b/examples/react-native/expo-basic/components/haptic-tab.tsx new file mode 100644 index 00000000..d7f0c3ea --- /dev/null +++ b/examples/react-native/expo-basic/components/haptic-tab.tsx @@ -0,0 +1,18 @@ +import { BottomTabBarButtonProps } from '@react-navigation/bottom-tabs' +import { PlatformPressable } from '@react-navigation/elements' +import * as Haptics from 'expo-haptics' + +export function HapticTab(props: BottomTabBarButtonProps) { + return ( + { + if (process.env.EXPO_OS === 'ios') { + // Add a soft haptic feedback when pressing down on the tabs. + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light) + } + props.onPressIn?.(ev) + }} + /> + ) +} diff --git a/examples/react-native/expo-basic/components/hello-wave.tsx b/examples/react-native/expo-basic/components/hello-wave.tsx new file mode 100644 index 00000000..c3da8547 --- /dev/null +++ b/examples/react-native/expo-basic/components/hello-wave.tsx @@ -0,0 +1,20 @@ +import Animated from 'react-native-reanimated' + +export function HelloWave() { + return ( + + šŸ‘‹ + + ) +} diff --git a/examples/react-native/expo-basic/components/parallax-scroll-view.tsx b/examples/react-native/expo-basic/components/parallax-scroll-view.tsx new file mode 100644 index 00000000..22fa1d41 --- /dev/null +++ b/examples/react-native/expo-basic/components/parallax-scroll-view.tsx @@ -0,0 +1,85 @@ +import type { PropsWithChildren, ReactElement } from 'react' +import { StyleSheet } from 'react-native' +import Animated, { + interpolate, + useAnimatedRef, + useAnimatedStyle, + useScrollOffset, +} from 'react-native-reanimated' + +import { ThemedView } from '@/components/themed-view' +import { useColorScheme } from '@/hooks/use-color-scheme' +import { useThemeColor } from '@/hooks/use-theme-color' + +const HEADER_HEIGHT = 250 + +type Props = PropsWithChildren<{ + headerImage: ReactElement + headerBackgroundColor: { dark: string; light: string } +}> + +export default function ParallaxScrollView({ + children, + headerImage, + headerBackgroundColor, +}: Props) { + const backgroundColor = useThemeColor({}, 'background') + const colorScheme = useColorScheme() ?? 'light' + const scrollRef = useAnimatedRef() + const scrollOffset = useScrollOffset(scrollRef) + const headerAnimatedStyle = useAnimatedStyle(() => { + return { + transform: [ + { + translateY: interpolate( + scrollOffset.value, + [-HEADER_HEIGHT, 0, HEADER_HEIGHT], + [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75], + ), + }, + { + scale: interpolate( + scrollOffset.value, + [-HEADER_HEIGHT, 0, HEADER_HEIGHT], + [2, 1, 1], + ), + }, + ], + } + }) + + return ( + + + {headerImage} + + {children} + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + header: { + height: HEADER_HEIGHT, + overflow: 'hidden', + }, + content: { + flex: 1, + padding: 32, + gap: 16, + overflow: 'hidden', + }, +}) diff --git a/examples/react-native/expo-basic/components/themed-text.tsx b/examples/react-native/expo-basic/components/themed-text.tsx new file mode 100644 index 00000000..db723d3f --- /dev/null +++ b/examples/react-native/expo-basic/components/themed-text.tsx @@ -0,0 +1,60 @@ +import { StyleSheet, Text, type TextProps } from 'react-native' + +import { useThemeColor } from '@/hooks/use-theme-color' + +export type ThemedTextProps = TextProps & { + lightColor?: string + darkColor?: string + type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link' +} + +export function ThemedText({ + style, + lightColor, + darkColor, + type = 'default', + ...rest +}: ThemedTextProps) { + const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text') + + return ( + + ) +} + +const styles = StyleSheet.create({ + default: { + fontSize: 16, + lineHeight: 24, + }, + defaultSemiBold: { + fontSize: 16, + lineHeight: 24, + fontWeight: '600', + }, + title: { + fontSize: 32, + fontWeight: 'bold', + lineHeight: 32, + }, + subtitle: { + fontSize: 20, + fontWeight: 'bold', + }, + link: { + lineHeight: 30, + fontSize: 16, + color: '#0a7ea4', + }, +}) diff --git a/examples/react-native/expo-basic/components/themed-view.tsx b/examples/react-native/expo-basic/components/themed-view.tsx new file mode 100644 index 00000000..028ec265 --- /dev/null +++ b/examples/react-native/expo-basic/components/themed-view.tsx @@ -0,0 +1,22 @@ +import { View, type ViewProps } from 'react-native' + +import { useThemeColor } from '@/hooks/use-theme-color' + +export type ThemedViewProps = ViewProps & { + lightColor?: string + darkColor?: string +} + +export function ThemedView({ + style, + lightColor, + darkColor, + ...otherProps +}: ThemedViewProps) { + const backgroundColor = useThemeColor( + { light: lightColor, dark: darkColor }, + 'background', + ) + + return +} diff --git a/examples/react-native/expo-basic/components/ui/collapsible.tsx b/examples/react-native/expo-basic/components/ui/collapsible.tsx new file mode 100644 index 00000000..bde89c29 --- /dev/null +++ b/examples/react-native/expo-basic/components/ui/collapsible.tsx @@ -0,0 +1,49 @@ +import { PropsWithChildren, useState } from 'react' +import { StyleSheet, TouchableOpacity } from 'react-native' + +import { ThemedText } from '@/components/themed-text' +import { ThemedView } from '@/components/themed-view' +import { IconSymbol } from '@/components/ui/icon-symbol' +import { Colors } from '@/constants/theme' +import { useColorScheme } from '@/hooks/use-color-scheme' + +export function Collapsible({ + children, + title, +}: PropsWithChildren & { title: string }) { + const [isOpen, setIsOpen] = useState(false) + const theme = useColorScheme() ?? 'light' + + return ( + + setIsOpen((value) => !value)} + activeOpacity={0.8} + > + + + {title} + + {isOpen && {children}} + + ) +} + +const styles = StyleSheet.create({ + heading: { + flexDirection: 'row', + alignItems: 'center', + gap: 6, + }, + content: { + marginTop: 6, + marginLeft: 24, + }, +}) diff --git a/examples/react-native/expo-basic/components/ui/icon-symbol.ios.tsx b/examples/react-native/expo-basic/components/ui/icon-symbol.ios.tsx new file mode 100644 index 00000000..ef726a55 --- /dev/null +++ b/examples/react-native/expo-basic/components/ui/icon-symbol.ios.tsx @@ -0,0 +1,32 @@ +import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols' +import { StyleProp, ViewStyle } from 'react-native' + +export function IconSymbol({ + name, + size = 24, + color, + style, + weight = 'regular', +}: { + name: SymbolViewProps['name'] + size?: number + color: string + style?: StyleProp + weight?: SymbolWeight +}) { + return ( + + ) +} diff --git a/examples/react-native/expo-basic/components/ui/icon-symbol.tsx b/examples/react-native/expo-basic/components/ui/icon-symbol.tsx new file mode 100644 index 00000000..5402815f --- /dev/null +++ b/examples/react-native/expo-basic/components/ui/icon-symbol.tsx @@ -0,0 +1,51 @@ +// Fallback for using MaterialIcons on Android and web. + +import MaterialIcons from '@expo/vector-icons/MaterialIcons' +import { SymbolWeight, SymbolViewProps } from 'expo-symbols' +import { ComponentProps } from 'react' +import { OpaqueColorValue, type StyleProp, type TextStyle } from 'react-native' + +type IconMapping = Record< + SymbolViewProps['name'], + ComponentProps['name'] +> +type IconSymbolName = keyof typeof MAPPING + +/** + * Add your SF Symbols to Material Icons mappings here. + * - see Material Icons in the [Icons Directory](https://icons.expo.fyi). + * - see SF Symbols in the [SF Symbols](https://developer.apple.com/sf-symbols/) app. + */ +const MAPPING = { + 'house.fill': 'home', + 'paperplane.fill': 'send', + 'chevron.left.forwardslash.chevron.right': 'code', + 'chevron.right': 'chevron-right', +} as IconMapping + +/** + * An icon component that uses native SF Symbols on iOS, and Material Icons on Android and web. + * This ensures a consistent look across platforms, and optimal resource usage. + * Icon `name`s are based on SF Symbols and require manual mapping to Material Icons. + */ +export function IconSymbol({ + name, + size = 24, + color, + style, +}: { + name: IconSymbolName + size?: number + color: string | OpaqueColorValue + style?: StyleProp + weight?: SymbolWeight +}) { + return ( + + ) +} diff --git a/examples/react-native/expo-basic/constants/theme.ts b/examples/react-native/expo-basic/constants/theme.ts new file mode 100644 index 00000000..a6212420 --- /dev/null +++ b/examples/react-native/expo-basic/constants/theme.ts @@ -0,0 +1,54 @@ +/** + * Below are the colors that are used in the app. The colors are defined in the light and dark mode. + * There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc. + */ + +import { Platform } from 'react-native' + +const tintColorLight = '#0a7ea4' +const tintColorDark = '#fff' + +export const Colors = { + light: { + text: '#11181C', + background: '#fff', + tint: tintColorLight, + icon: '#687076', + tabIconDefault: '#687076', + tabIconSelected: tintColorLight, + }, + dark: { + text: '#ECEDEE', + background: '#151718', + tint: tintColorDark, + icon: '#9BA1A6', + tabIconDefault: '#9BA1A6', + tabIconSelected: tintColorDark, + }, +} + +export const Fonts = Platform.select({ + ios: { + /** iOS `UIFontDescriptorSystemDesignDefault` */ + sans: 'system-ui', + /** iOS `UIFontDescriptorSystemDesignSerif` */ + serif: 'ui-serif', + /** iOS `UIFontDescriptorSystemDesignRounded` */ + rounded: 'ui-rounded', + /** iOS `UIFontDescriptorSystemDesignMonospaced` */ + mono: 'ui-monospace', + }, + default: { + sans: 'normal', + serif: 'serif', + rounded: 'normal', + mono: 'monospace', + }, + web: { + sans: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif", + serif: "Georgia, 'Times New Roman', serif", + rounded: + "'SF Pro Rounded', 'Hiragino Maru Gothic ProN', Meiryo, 'MS PGothic', sans-serif", + mono: "SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace", + }, +}) diff --git a/examples/react-native/expo-basic/eslint.config.js b/examples/react-native/expo-basic/eslint.config.js new file mode 100644 index 00000000..ea589baa --- /dev/null +++ b/examples/react-native/expo-basic/eslint.config.js @@ -0,0 +1,10 @@ +// https://docs.expo.dev/guides/using-eslint/ +const { defineConfig } = require('eslint/config') +const expoConfig = require('eslint-config-expo/flat') + +module.exports = defineConfig([ + expoConfig, + { + ignores: ['dist/*'], + }, +]) diff --git a/examples/react-native/expo-basic/hooks/use-color-scheme.ts b/examples/react-native/expo-basic/hooks/use-color-scheme.ts new file mode 100644 index 00000000..b370337a --- /dev/null +++ b/examples/react-native/expo-basic/hooks/use-color-scheme.ts @@ -0,0 +1 @@ +export { useColorScheme } from 'react-native' diff --git a/examples/react-native/expo-basic/hooks/use-color-scheme.web.ts b/examples/react-native/expo-basic/hooks/use-color-scheme.web.ts new file mode 100644 index 00000000..2a690de8 --- /dev/null +++ b/examples/react-native/expo-basic/hooks/use-color-scheme.web.ts @@ -0,0 +1,21 @@ +import { useEffect, useState } from 'react' +import { useColorScheme as useRNColorScheme } from 'react-native' + +/** + * To support static rendering, this value needs to be re-calculated on the client side for web + */ +export function useColorScheme() { + const [hasHydrated, setHasHydrated] = useState(false) + + useEffect(() => { + setHasHydrated(true) + }, []) + + const colorScheme = useRNColorScheme() + + if (hasHydrated) { + return colorScheme + } + + return 'light' +} diff --git a/examples/react-native/expo-basic/hooks/use-theme-color.ts b/examples/react-native/expo-basic/hooks/use-theme-color.ts new file mode 100644 index 00000000..7b78caa7 --- /dev/null +++ b/examples/react-native/expo-basic/hooks/use-theme-color.ts @@ -0,0 +1,21 @@ +/** + * Learn more about light and dark modes: + * https://docs.expo.dev/guides/color-schemes/ + */ + +import { Colors } from '@/constants/theme' +import { useColorScheme } from '@/hooks/use-color-scheme' + +export function useThemeColor( + props: { light?: string; dark?: string }, + colorName: keyof typeof Colors.light & keyof typeof Colors.dark, +) { + const theme = useColorScheme() ?? 'light' + const colorFromProps = props[theme] + + if (colorFromProps) { + return colorFromProps + } else { + return Colors[theme][colorName] + } +} diff --git a/examples/react-native/expo-basic/package.json b/examples/react-native/expo-basic/package.json new file mode 100644 index 00000000..aec638d7 --- /dev/null +++ b/examples/react-native/expo-basic/package.json @@ -0,0 +1,48 @@ +{ + "name": "expo-basic", + "main": "expo-router/entry", + "version": "1.0.0", + "private": true, + "scripts": { + "start": "expo start", + "reset-project": "node ./scripts/reset-project.js", + "android": "expo start --android", + "ios": "expo start --ios", + "web": "expo start --web", + "lint": "expo lint" + }, + "dependencies": { + "@expo/vector-icons": "^15.0.2", + "@react-navigation/bottom-tabs": "^7.4.0", + "@react-navigation/elements": "^2.6.3", + "@react-navigation/native": "^7.1.8", + "@tanstack/devtools-event-client": "0.2.4", + "expo": "~54.0.8", + "expo-constants": "~18.0.9", + "expo-font": "~14.0.8", + "expo-haptics": "~15.0.7", + "expo-image": "~3.0.8", + "expo-linking": "~8.0.8", + "expo-router": "~6.0.6", + "expo-splash-screen": "~31.0.10", + "expo-status-bar": "~3.0.8", + "expo-symbols": "~1.0.7", + "expo-system-ui": "~6.0.7", + "expo-web-browser": "~15.0.7", + "react": "19.1.0", + "react-dom": "19.1.0", + "react-native": "0.81.4", + "react-native-gesture-handler": "~2.28.0", + "react-native-reanimated": "~4.1.0", + "react-native-safe-area-context": "~5.6.0", + "react-native-screens": "~4.16.0", + "react-native-web": "~0.21.0", + "react-native-worklets": "0.5.1" + }, + "devDependencies": { + "@types/react": "^19.1.12", + "eslint": "^9.25.1", + "eslint-config-expo": "~10.0.0", + "typescript": "~5.9.2" + } +} diff --git a/examples/react-native/expo-basic/scripts/reset-project.js b/examples/react-native/expo-basic/scripts/reset-project.js new file mode 100644 index 00000000..18265ec3 --- /dev/null +++ b/examples/react-native/expo-basic/scripts/reset-project.js @@ -0,0 +1,112 @@ +#!/usr/bin/env node + +/** + * This script is used to reset the project to a blank state. + * It deletes or moves the /app, /components, /hooks, /scripts, and /constants directories to /app-example based on user input and creates a new /app directory with an index.tsx and _layout.tsx file. + * You can remove the `reset-project` script from package.json and safely delete this file after running it. + */ + +const fs = require('fs') +const path = require('path') +const readline = require('readline') + +const root = process.cwd() +const oldDirs = ['app', 'components', 'hooks', 'constants', 'scripts'] +const exampleDir = 'app-example' +const newAppDir = 'app' +const exampleDirPath = path.join(root, exampleDir) + +const indexContent = `import { Text, View } from "react-native"; + +export default function Index() { + return ( + + Edit app/index.tsx to edit this screen. + + ); +} +` + +const layoutContent = `import { Stack } from "expo-router"; + +export default function RootLayout() { + return ; +} +` + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, +}) + +const moveDirectories = async (userInput) => { + try { + if (userInput === 'y') { + // Create the app-example directory + await fs.promises.mkdir(exampleDirPath, { recursive: true }) + console.log(`šŸ“ /${exampleDir} directory created.`) + } + + // Move old directories to new app-example directory or delete them + for (const dir of oldDirs) { + const oldDirPath = path.join(root, dir) + if (fs.existsSync(oldDirPath)) { + if (userInput === 'y') { + const newDirPath = path.join(root, exampleDir, dir) + await fs.promises.rename(oldDirPath, newDirPath) + console.log(`āž”ļø /${dir} moved to /${exampleDir}/${dir}.`) + } else { + await fs.promises.rm(oldDirPath, { recursive: true, force: true }) + console.log(`āŒ /${dir} deleted.`) + } + } else { + console.log(`āž”ļø /${dir} does not exist, skipping.`) + } + } + + // Create new /app directory + const newAppDirPath = path.join(root, newAppDir) + await fs.promises.mkdir(newAppDirPath, { recursive: true }) + console.log('\nšŸ“ New /app directory created.') + + // Create index.tsx + const indexPath = path.join(newAppDirPath, 'index.tsx') + await fs.promises.writeFile(indexPath, indexContent) + console.log('šŸ“„ app/index.tsx created.') + + // Create _layout.tsx + const layoutPath = path.join(newAppDirPath, '_layout.tsx') + await fs.promises.writeFile(layoutPath, layoutContent) + console.log('šŸ“„ app/_layout.tsx created.') + + console.log('\nāœ… Project reset complete. Next steps:') + console.log( + `1. Run \`npx expo start\` to start a development server.\n2. Edit app/index.tsx to edit the main screen.${ + userInput === 'y' + ? `\n3. Delete the /${exampleDir} directory when you're done referencing it.` + : '' + }`, + ) + } catch (error) { + console.error(`āŒ Error during script execution: ${error.message}`) + } +} + +rl.question( + 'Do you want to move existing files to /app-example instead of deleting them? (Y/n): ', + (answer) => { + const userInput = answer.trim().toLowerCase() || 'y' + if (userInput === 'y' || userInput === 'n') { + moveDirectories(userInput).finally(() => rl.close()) + } else { + console.log("āŒ Invalid input. Please enter 'Y' or 'N'.") + rl.close() + } + }, +) diff --git a/examples/react-native/expo-basic/tsconfig.json b/examples/react-native/expo-basic/tsconfig.json new file mode 100644 index 00000000..ce27fee3 --- /dev/null +++ b/examples/react-native/expo-basic/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "expo/tsconfig.base", + "compilerOptions": { + "strict": true, + "paths": { + "@/*": ["./*"] + } + }, + "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"] +} diff --git a/examples/react/basic/package.json b/examples/react/basic/package.json index 6e46491b..39e4802d 100644 --- a/examples/react/basic/package.json +++ b/examples/react/basic/package.json @@ -9,14 +9,14 @@ "test:types": "tsc" }, "dependencies": { - "@tanstack/devtools-event-client": "^0.2.4", + "@tanstack/devtools-event-client": "0.2.4", "@tanstack/react-devtools": "^0.6.9", "@tanstack/react-query": "^5.83.0", "@tanstack/react-query-devtools": "^5.83.0", "@tanstack/react-router": "^1.131.2", "@tanstack/react-router-devtools": "^1.131.2", - "react": "^19.1.0", - "react-dom": "^19.1.0", + "react": "19.1.0", + "react-dom": "19.1.0", "zod": "^4.0.14" }, "devDependencies": { diff --git a/examples/react/custom-devtools/package.json b/examples/react/custom-devtools/package.json index 0e1dc105..7daa4be3 100644 --- a/examples/react/custom-devtools/package.json +++ b/examples/react/custom-devtools/package.json @@ -9,10 +9,10 @@ "test:types": "tsc" }, "dependencies": { - "@tanstack/devtools-event-client": "^0.2.4", + "@tanstack/devtools-event-client": "0.2.4", "@tanstack/react-devtools": "^0.6.9", - "react": "^19.1.0", - "react-dom": "^19.1.0" + "react": "19.1.0", + "react-dom": "19.1.0" }, "devDependencies": { "@types/react": "^19.1.12", diff --git a/examples/react/drizzle/package.json b/examples/react/drizzle/package.json index 1b9c0db7..70700767 100644 --- a/examples/react/drizzle/package.json +++ b/examples/react/drizzle/package.json @@ -11,9 +11,9 @@ "@tanstack/react-devtools": "^0.6.9", "@tanstack/react-router": "^1.131.2", "@tanstack/react-router-devtools": "^1.131.2", - "@tanstack/react-start": "^1.131.2", - "react": "^19.1.0", - "react-dom": "^19.1.0" + "@tanstack/react-start": "1.132.0-alpha.22", + "react": "19.1.0", + "react-dom": "19.1.0" }, "devDependencies": { "@tanstack/devtools-vite": "0.3.0", @@ -23,7 +23,7 @@ "drizzle-kit": "^0.31.4", "drizzle-orm": "^0.44.4", "pg": "^8.16.3", - "typescript": "5.8.3", + "typescript": "~5.9.2", "vite": "^7.0.6", "vite-plugin-inspect": "11.3.2" } diff --git a/examples/react/https/package.json b/examples/react/https/package.json index 17b7047f..223d1591 100644 --- a/examples/react/https/package.json +++ b/examples/react/https/package.json @@ -9,14 +9,14 @@ "test:types": "tsc" }, "dependencies": { - "@tanstack/devtools-event-client": "^0.2.4", + "@tanstack/devtools-event-client": "0.2.4", "@tanstack/react-devtools": "^0.6.9", "@tanstack/react-query": "^5.83.0", "@tanstack/react-query-devtools": "^5.83.0", "@tanstack/react-router": "^1.131.2", "@tanstack/react-router-devtools": "^1.131.2", - "react": "^19.1.0", - "react-dom": "^19.1.0", + "react": "19.1.0", + "react-dom": "19.1.0", "zod": "^4.0.14" }, "devDependencies": { diff --git a/examples/react/start/package.json b/examples/react/start/package.json index 42e1ce80..9cd69f81 100644 --- a/examples/react/start/package.json +++ b/examples/react/start/package.json @@ -23,19 +23,19 @@ "@prisma/studio-core": "^0.5.1", "@tailwindcss/vite": "^4.0.6", "@tanstack/devtools-event-bus": "^0.3.2", - "@tanstack/devtools-event-client": "^0.2.4", + "@tanstack/devtools-event-client": "0.2.4", "@tanstack/react-devtools": "^0.6.9", "@tanstack/react-query": "^5.83.0", "@tanstack/react-query-devtools": "^5.83.0", "@tanstack/react-router": "^1.131.2", "@tanstack/react-router-devtools": "^1.131.2", "@tanstack/react-router-with-query": "^1.130.2", - "@tanstack/react-start": "^1.131.2", + "@tanstack/react-start": "1.132.0-alpha.22", "@tanstack/router-plugin": "^1.121.2", "ai": "^5.0.44", "prisma": "^6.13.0", - "react": "^19.1.0", - "react-dom": "^19.1.0", + "react": "19.1.0", + "react-dom": "19.1.0", "tailwindcss": "^4.0.6", "vite-tsconfig-paths": "^5.1.4", "zod": "^4.0.14" @@ -48,7 +48,7 @@ "@types/react-dom": "^19.1.2", "@vitejs/plugin-react": "^4.5.2", "jsdom": "^26.1.0", - "typescript": "5.8.3", + "typescript": "~5.9.2", "vite": "^7.0.6", "vite-plugin-inspect": "11.3.2", "vitest": "^3.1.2", diff --git a/examples/react/time-travel/package.json b/examples/react/time-travel/package.json index e4b73c45..d5ec2e52 100644 --- a/examples/react/time-travel/package.json +++ b/examples/react/time-travel/package.json @@ -15,8 +15,8 @@ "@tanstack/react-query-devtools": "^5.83.0", "@tanstack/react-router": "^1.131.2", "@tanstack/react-router-devtools": "^1.131.2", - "react": "^19.1.0", - "react-dom": "^19.1.0", + "react": "19.1.0", + "react-dom": "19.1.0", "zod": "^4.0.14", "zustand": "^5.0.7" }, diff --git a/package.json b/package.json index 69e64de1..7eeb0e83 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "publint": "^0.3.12", "sherif": "^1.5.0", "size-limit": "^11.2.0", - "typescript": "5.8.3", + "typescript": "~5.9.2", "vite": "^7.0.6", "vitest": "^3.1.2" }, diff --git a/packages/devtools-vite/src/enhance-logs.test.ts b/packages/devtools-vite/src/enhance-logs.test.ts new file mode 100644 index 00000000..3aeeb190 --- /dev/null +++ b/packages/devtools-vite/src/enhance-logs.test.ts @@ -0,0 +1,157 @@ +import { describe, expect, test } from 'vitest' +import { enhanceConsoleLog } from './enhance-logs' + +const removeEmptySpace = (str: string) => { + return str.replace(/\s/g, '').trim() +} + +describe('remove-devtools', () => { + test('it adds enhanced console.logs to console.log()', () => { + const output = removeEmptySpace( + enhanceConsoleLog( + ` + console.log('This is a log') + `, + 'test.jsx', + 3000, + ).code, + ) + expect( + output.includes( + 'http://localhost:3000/__tsd/open-source?source=test.jsx', + ), + ).toEqual(true) + expect(output.includes('test.jsx:2:9')).toEqual(true) + expect(output.includes(removeEmptySpace("'This is a log'"))).toEqual(true) + }) + + test('it does not add enhanced console.logs to console.log that is not called', () => { + const output = removeEmptySpace( + enhanceConsoleLog( + ` + console.log + `, + 'test.jsx', + 3000, + ).code, + ) + expect(output).toBe( + removeEmptySpace(` + console.log + `), + ) + }) + + test('it does not add enhanced console.logs to console.log that is inside a comment', () => { + const output = removeEmptySpace( + enhanceConsoleLog( + ` + // console.log('This is a log') + `, + 'test.jsx', + 3000, + ).code, + ) + expect(output).toBe( + removeEmptySpace(` + // console.log('This is a log') + `), + ) + }) + + test('it does not add enhanced console.logs to console.log that is inside a multiline comment', () => { + const output = removeEmptySpace( + enhanceConsoleLog( + ` + /* + console.log('This is a log') + */ + `, + 'test.jsx', + 3000, + ).code, + ) + expect(output).toBe( + removeEmptySpace(` + /* + console.log('This is a log') + */ + `), + ) + }) + + test('it does not add enhanced console.error to console.error that is inside a comment', () => { + const output = removeEmptySpace( + enhanceConsoleLog( + ` + // console.error('This is a log') + `, + 'test.jsx', + 3000, + ).code, + ) + expect(output).toBe( + removeEmptySpace(` + // console.error('This is a log') + `), + ) + }) + + test('it does not add enhanced console.error to console.error that is inside a multiline comment', () => { + const output = removeEmptySpace( + enhanceConsoleLog( + ` + /* + console.error('This is a log') + */ + `, + 'test.jsx', + 3000, + ).code, + ) + expect(output).toBe( + removeEmptySpace(` + /* + console.error('This is a log') + */ + `), + ) + }) + + test('it adds enhanced console.error to console.error()', () => { + const output = removeEmptySpace( + enhanceConsoleLog( + ` + console.error('This is a log') + `, + 'test.jsx', + 3000, + ).code, + ) + console.log('output', output) + expect( + output.includes( + 'http://localhost:3000/__tsd/open-source?source=test.jsx', + ), + ).toEqual(true) + expect(output.includes('test.jsx:2:9')).toEqual(true) + expect(output.includes(removeEmptySpace("'This is a log'"))).toEqual(true) + }) + + test('it does not add enhanced console.error to console.error that is not called', () => { + const output = removeEmptySpace( + enhanceConsoleLog( + ` + console.log + `, + 'test.jsx', + 3000, + ).code, + ) + expect(output).toBe( + removeEmptySpace(` + console.log + `), + ) + }) +}) diff --git a/packages/devtools-vite/src/enhance-logs.ts b/packages/devtools-vite/src/enhance-logs.ts new file mode 100644 index 00000000..0e30cf73 --- /dev/null +++ b/packages/devtools-vite/src/enhance-logs.ts @@ -0,0 +1,70 @@ +import chalk from 'chalk' +import { normalizePath } from 'vite' +import { gen, parse, t, trav } from './babel' +import type { types as Babel } from '@babel/core' +import type { ParseResult } from '@babel/parser' + +const transform = ( + ast: ParseResult, + filePath: string, + port: number, +) => { + let didTransform = false + + trav(ast, { + CallExpression(path) { + const callee = path.node.callee + // Match console.log(...) or console.error(...) + if ( + callee.type === 'MemberExpression' && + callee.object.type === 'Identifier' && + callee.object.name === 'console' && + callee.property.type === 'Identifier' && + (callee.property.name === 'log' || callee.property.name === 'error') + ) { + const location = path.node.loc + if (!location) { + return + } + const [lineNumber, column] = [ + location.start.line, + location.start.column, + ] + const finalPath = `${filePath}:${lineNumber}:${column + 1}` + path.node.arguments.unshift( + t.stringLiteral( + `${chalk.magenta('LOG')} ${chalk.blueBright(`${finalPath} - http://localhost:${port}/__tsd/open-source?source=${encodeURIComponent(finalPath)}`)}\n → `, + ), + ) + didTransform = true + } + }, + }) + + return didTransform +} + +export function enhanceConsoleLog(code: string, id: string, port: number) { + const [filePath] = id.split('?') + // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain + const location = filePath?.replace(normalizePath(process.cwd()), '')! + + try { + const ast = parse(code, { + sourceType: 'module', + plugins: ['jsx', 'typescript'], + }) + const didTransform = transform(ast, location, port) + if (!didTransform) { + return { code } + } + return gen(ast, { + sourceMaps: true, + retainLines: true, + filename: id, + sourceFileName: filePath, + }) + } catch (e) { + return { code } + } +} diff --git a/packages/devtools-vite/src/inject-source.test.ts b/packages/devtools-vite/src/inject-source.test.ts index b145f799..fd6134f8 100644 --- a/packages/devtools-vite/src/inject-source.test.ts +++ b/packages/devtools-vite/src/inject-source.test.ts @@ -79,7 +79,7 @@ describe('inject source', () => { expect(output).toBe( removeEmptySpace(` export const Route = createFileRoute("/test")({ - component: function() { return
Hello World
; } + component: function() { return
Hello World
; } }); `), ) @@ -159,7 +159,7 @@ describe('inject source', () => { expect(output).toBe( removeEmptySpace(` export const Route = createFileRoute("/test")({ - component: function({...rest}) { return
Hello World
; } + component: function({...rest}) { return
Hello World
; } }); `), ) @@ -181,7 +181,7 @@ describe('inject source', () => { expect(output).toBe( removeEmptySpace(` export const Route = createFileRoute("/test")({ - component: () =>
Hello World
+ component: () =>
Hello World
}); `), ) @@ -261,7 +261,7 @@ describe('inject source', () => { expect(output).toBe( removeEmptySpace(` export const Route = createFileRoute("/test")({ - component: ({...rest}) =>
Hello World
+ component: ({...rest}) =>
Hello World
}); `), ) @@ -286,7 +286,7 @@ describe('inject source', () => { removeEmptySpace(` function Parent({ ...props }) { function Child({ ...props }) { - return
; + return
; } return ; } @@ -312,7 +312,7 @@ function test({...props }) { import Custom from "external"; function test({...props }) { - return ; + return ; }`), ) }) @@ -330,7 +330,7 @@ function test({...props }) { expect(output).toBe( removeEmptySpace(` function test(props) { - return
; } @@ -396,7 +396,7 @@ function test(props) { expect(output).toBe( removeEmptySpace(` function test({...props}) { - return
+ return
; } @@ -420,7 +420,7 @@ function test({...props}) { expect(output).toBe( removeEmptySpace(` function test({...rest}) { - return
+ return
; } @@ -462,7 +462,7 @@ function test({ children, ...rest }) { expect(output).toBe( removeEmptySpace(` function test({ ...props }) { - return
; }; @@ -629,7 +629,7 @@ function test({ children, ...rest }) { expect(output).toBe( removeEmptySpace(` const ButtonWithProps = function test({...props}) { - return
+ return
; }; @@ -653,7 +653,7 @@ function test({ children, ...rest }) { expect(output).toBe( removeEmptySpace(` const ButtonWithProps = function test({...rest}) { - return
+ return
; }; @@ -695,7 +695,7 @@ function test({ children, ...rest }) { expect(output).toBe( removeEmptySpace(` const ButtonWithProps = function test({ ...props }) { - return
; }; @@ -862,7 +862,7 @@ function test({ children, ...rest }) { expect(output).toBe( removeEmptySpace(` const ButtonWithProps = ({...props}) => { - return
+ return
; }; @@ -886,7 +886,7 @@ function test({ children, ...rest }) { expect(output).toBe( removeEmptySpace(` const ButtonWithProps = ({...rest}) => { - return
+ return
; }; @@ -908,7 +908,7 @@ function test({ children, ...rest }) { expect(output).toBe( removeEmptySpace(` const ButtonWithProps = ({ children, ...rest }) => { - return