Skip to content

Commit

Permalink
base
Browse files Browse the repository at this point in the history
base + nativewind

base + restyle

base + tamagui

base + unistyles

react-navigation + stack + stylesheet

react-navigation + stack + restyle

react-navigation + stack + nativewind

react-navigation + stack + unistyle

react-navigation + stack + tamagui

react-navigation + tabs + restyle / stylesheet

react-navigation + tabs/drawer

expo-router stack stylesheet

expo-router

i18n

TEST

code review

>
  • Loading branch information
ludwig-pro authored and danstepanov committed Feb 22, 2024
1 parent f5def08 commit 1995ea9
Show file tree
Hide file tree
Showing 55 changed files with 1,042 additions and 2,666 deletions.
5 changes: 5 additions & 0 deletions .changeset/poor-worms-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'create-expo-stack': minor
---

Refactor the whole folder structure to reduce ejs complexity
20 changes: 19 additions & 1 deletion cli/__tests__/cli-integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { system } from 'gluegun';

import { version } from '../package.json';

import { test, expect } from 'bun:test';
import { test, expect, describe } from 'bun:test';
import * as path from 'node:path';

const cli = async (cmd) =>
Expand Down Expand Up @@ -622,3 +622,21 @@ for (const packageManager of packageManagers) {
expect(output).not.toContain('Installing dependencies');
});
}

describe(`internationalization`, () => {
// i18next
test(`generates a default project with bun and i18n`, async () => {
const output = await cli(`myTestProject --default --i18next --bun`);
expect(output).toContain('--i18next');
});
// --react-navigation i18next
test(`generates a project with bun, react-navigation and i18n`, async () => {
const output = await cli(`myTestProject --expo-router --drawer+tabs --i18next --bun`);
expect(output).toContain('--i18next');
});
// --expo-router i18next
test(`generates a project with bun, expo-router and i18n`, async () => {
const output = await cli(`myTestProject --react-navigation --drawer+tabs --i18next --bun`);
expect(output).toContain('--i18next');
});
});
153 changes: 43 additions & 110 deletions cli/src/templates/base/App.tsx.ejs
Original file line number Diff line number Diff line change
@@ -1,128 +1,61 @@
<% if (props.stylingPackage?.name === "nativewind") { %>
import './global.css';
<% } %>
import React from 'react';
<% if (props.stylingPackage?.name === "nativewind") {%>
import { Text, View } from 'react-native';
<% } else if (props.stylingPackage?.name === "tamagui") {%>
import { View } from 'react-native';
import { TamaguiProvider, Text, styled } from 'tamagui';
import config from './tamagui.config'
<% } else if (props.stylingPackage?.name === "restyle") {%>
import { ThemeProvider } from '@shopify/restyle';
import {theme, Text} from './theme';
<% } else if (props.stylingPackage?.name === "unistyles") {%>
import { createStyleSheet, useStyles } from 'react-native-unistyles'
import { Text, View } from 'react-native';
<% } else {%>
import { StyleSheet, Text, View } from 'react-native';
<% } %>
import { ScreenContent } from 'components/ScreenContent';
import { StatusBar } from 'expo-status-bar';

<% if (props.internalizationPackage?.name === "i18next") { %>
import { InternalizationExample } from 'components/InternalizationExample';
import './translation';
import { InternalizationExample } from 'components/InternalizationExample';
<% } %>
<% if (props.stylingPackage?.name === "nativewind") { %>
import './global.css';
<% } else if (props.stylingPackage?.name === "restyle") { %>
import { ThemeProvider } from '@shopify/restyle';
import { theme } from 'theme';
<% } else if (props.stylingPackage?.name === "tamagui") { %>
import { TamaguiProvider } from 'tamagui';
import config from './tamagui.config';
<% } %>
import { StatusBar } from 'expo-status-bar';
<% if (props.stylingPackage?.name === "nativewind") {%>
export default function App() {
return (
<View className={styles.container}>
<% if (props.internalizationPackage?.name === "i18next") { %>
<InternalizationExample />
<% } else { %>
<Text>Open up App.tsx to start working on your app!</Text>
<% } %>
<% if (props.stylingPackage?.name === "restyle") {%>
export default function App() {
return (
<ThemeProvider theme={theme}>
<ScreenContent title="Home" path="App.tsx">
<% if (props.internalizationPackage?.name === "i18next") { %>
<InternalizationExample />
<% } %>
</ScreenContent>
<StatusBar style="auto" />
</View >
</ThemeProvider>
);
}
const styles = {
container: 'flex flex-1 items-center justify-center bg-white',
};
}
<% } else if (props.stylingPackage?.name === "tamagui") {%>
const MyView = styled(View, {
name: "MyView",
flex: 1,
alignItems: 'center',
justifyContent: 'center',
});
export default function App() {
return (
<TamaguiProvider config={config}>
<MyView>
export default function App() {
return (
<TamaguiProvider config={config}>
<ScreenContent title="Home" path="App.tsx">
<% if (props.internalizationPackage?.name === "i18next") { %>
<InternalizationExample />
<% } else { %>
<Text>Open up App.tsx to start working on your app!</Text>
<% } %>
<StatusBar style="auto" />
</MyView>
</TamaguiProvider >
);
}
<% } else if (props.stylingPackage?.name === "restyle") {%>
export default function App() {
return (
<ThemeProvider theme={theme}>
<% if (props.internalizationPackage?.name === "i18next") { %>
<InternalizationExample />
<% } else { %>
<Text>Open up App.tsx to start working on your app!</Text>
<% } %>
<StatusBar style="auto" />
</ThemeProvider>
);
}
<% } else if (props.stylingPackage?.name === "unistyles") {%>
export default function App() {
const {styles} = useStyles(stylesheet)
return (
<View style={styles.container}>
<% if (props.internalizationPackage?.name === "i18next") { %>
<InternalizationExample />
<% } else { %>
<Text>Open up App.tsx to start working on your app!</Text>
<% } %>
<StatusBar style="auto" />
</View >
);
}
const stylesheet = createStyleSheet({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
<% } else {%>
export default function App() {
return (
<View style={styles.container}>
</ScreenContent>
<StatusBar style="auto" />
</TamaguiProvider>
);
}
<% } else { %>
export default function App() {
return (
<>
<ScreenContent title="Home" path="App.tsx">
<% if (props.internalizationPackage?.name === "i18next") { %>
<InternalizationExample />
<% } else { %>
<Text>Open up App.tsx to start working on your app!</Text>
<% } %>
<StatusBar style="auto" />
</View >
);
}
</ScreenContent>
<StatusBar style="auto" />
</>
);
}
<% } %>
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
<% } %>
23 changes: 23 additions & 0 deletions cli/src/templates/base/components/BackButton.tsx.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Feather } from '@expo/vector-icons';
import { Text, View, StyleSheet } from 'react-native';

export const BackButton = ({ onPress }: { onPress: () => void }) => {
return (
<View style={styles.backButton}>
<Feather name="chevron-left" size={16} color="#007AFF" />
<Text style={styles.backButtonText} onPress={onPress}>
Back
</Text>
</View>
);
};
const styles = StyleSheet.create({
backButton: {
flexDirection: 'row',
paddingLeft: 20,
},
backButtonText: {
color: '#007AFF',
marginLeft: 4,
},
});
40 changes: 40 additions & 0 deletions cli/src/templates/base/components/Button.tsx.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { forwardRef } from 'react';
import { StyleSheet, Text, TouchableOpacity, TouchableOpacityProps } from 'react-native';

type ButtonProps = {
onPress?: TouchableOpacityProps['onPress'];
title?: string;
} & TouchableOpacityProps;

export const Button = forwardRef<TouchableOpacity, ButtonProps>(({ onPress, title }, ref) => {
return (
<TouchableOpacity ref={ref} style={styles.button} onPress={onPress}>
<Text style={styles.buttonText}>{title}</Text>
</TouchableOpacity>
);
});

const styles = StyleSheet.create({
button: {
alignItems: 'center',
backgroundColor: '#6366F1',
borderRadius: 24,
elevation: 5,
flexDirection: 'row',
justifyContent: 'center',
padding: 16,
shadowColor: '#000',
shadowOffset: {
height: 2,
width: 0,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
},
buttonText: {
color: '#FFFFFF',
fontSize: 16,
fontWeight: '600',
textAlign: 'center',
},
});
55 changes: 55 additions & 0 deletions cli/src/templates/base/components/EditScreenInfo.tsx.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { StyleSheet, Text, View } from 'react-native';

<% if (props.internalizationPackage?.name === "i18next") { %>
import { useTranslation } from 'react-i18next';
<% } %>
export default function EditScreenInfo({ path }: { path: string }) {
<% if (props.internalizationPackage?.name === "i18next") { %>
const { t } = useTranslation();
const title = t('getStarted');
const description = t('changeCode')
<% } else { %>
const title = "Open up the code for this screen:"
const description = "Change any of the text, save the file, and your app will automatically update."
<% } %>
return (
<View style={styles.getStartedContainer}>
<Text style={styles.getStartedText}>{title}</Text>
<View style={[styles.codeHighlightContainer, styles.homeScreenFilename]}>
<Text>{path}</Text>
</View>
<Text style={styles.getStartedText}>{description}</Text>
</View>
);
}
const styles = StyleSheet.create({
codeHighlightContainer: {
borderRadius: 3,
paddingHorizontal: 4,
},
getStartedContainer: {
alignItems: 'center',
marginHorizontal: 50,
},
getStartedText: {
fontSize: 17,
lineHeight: 24,
textAlign: 'center',
},
helpContainer: {
alignItems: 'center',
marginHorizontal: 20,
marginTop: 15,
},
helpLink: {
paddingVertical: 15,
},
helpLinkText: {
textAlign: 'center',
},
homeScreenFilename: {
marginVertical: 7,
},
});
28 changes: 28 additions & 0 deletions cli/src/templates/base/components/HeaderButton.tsx.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import FontAwesome from '@expo/vector-icons/FontAwesome';
import { Pressable, StyleSheet } from 'react-native';

export const HeaderButton = ({ onPress }: { onPress?: () => void }) => {
return (
<Pressable onPress={onPress}>
{({ pressed }) => (
<FontAwesome
name="info-circle"
size={25}
color="gray"
style={[
styles.headerRight,
{
opacity: pressed ? 0.5 : 1,
},
]}
/>
)}
</Pressable>
);
};

export const styles = StyleSheet.create({
headerRight: {
marginRight: 15,
},
});
38 changes: 38 additions & 0 deletions cli/src/templates/base/components/ScreenContent.tsx.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { StyleSheet, Text, View } from 'react-native';

import EditScreenInfo from './EditScreenInfo';

type ScreenContentProps = {
title: string;
path: string;
children?: React.ReactNode;
};

export const ScreenContent = ({ title, path, children }: ScreenContentProps) => {
return (
<View style={styles.container}>
<Text style={styles.title}>{title}</Text>
<View style={styles.separator} />
<EditScreenInfo path={path} />
{children}
</View>
);
};

const styles = StyleSheet.create({
container: {
alignItems: 'center',
flex: 1,
justifyContent: 'center',
},
separator: {
backgroundColor: '#d1d5db',
height: 1,
marginVertical: 30,
width: '80%',
},
title: {
fontSize: 20,
fontWeight: 'bold',
},
});
Loading

0 comments on commit 1995ea9

Please sign in to comment.