From 1a2d6de861240320cf0c42ff65044b090668710e Mon Sep 17 00:00:00 2001 From: Stephen Hanson Date: Tue, 12 Dec 2023 11:04:45 -0600 Subject: [PATCH] Create App.tsx at end of "createApp" command, since will depend on other tasks (#25) When we create a new app using thoughtbelt, we will want to generate an `App.tsx` that has all of our providers and tools configured, including React Navigation, global state provider, styling provider, etc. To me, since this file will be tied to many different commands, it would make more sense for the creation of this file to be a step at the end of the `createApp` command, independent of any one command. If we run `thoughtbelt navigation` independently, we will not want to create a new `App.tsx` in that case, though it would likely be helpful to include some output directing the user to wrap their app in a `NavigationContainer`. What do you think? We don't yet have a mechanism for detecting if a given command is running independently as opposed to being a part of the main `createApp` (or other) command . --- src/commands/__tests__/createApp.test.ts | 8 ++++-- src/commands/createApp.ts | 4 +++ src/constants.ts | 1 + templates/createApp/App.tsx | 28 +++++++++++++++++++ .../createApp/src/components/Providers.tsx | 14 ++++++++++ 5 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 templates/createApp/App.tsx create mode 100644 templates/createApp/src/components/Providers.tsx diff --git a/src/commands/__tests__/createApp.test.ts b/src/commands/__tests__/createApp.test.ts index 11d1d8a..a5c706a 100644 --- a/src/commands/__tests__/createApp.test.ts +++ b/src/commands/__tests__/createApp.test.ts @@ -1,6 +1,6 @@ import { confirm } from '@inquirer/prompts'; -import { vol } from 'memfs'; -import { Mock, afterEach, test, vi } from 'vitest'; +import { fs, vol } from 'memfs'; +import { Mock, afterEach, expect, test, vi } from 'vitest'; import print from '../../util/print'; import { createApp } from '../createApp'; @@ -15,7 +15,7 @@ afterEach(() => { (print as Mock).mockReset(); }); -test("doesn't error", async () => { +test('creates app', async () => { (confirm as Mock).mockResolvedValueOnce(true); vi.spyOn(process, 'chdir').mockImplementation(() => { const json = { @@ -29,4 +29,6 @@ test("doesn't error", async () => { vol.fromJSON(json, './'); }); await createApp('MyApp', { testing: true }); + + expect(fs.readFileSync('App.tsx', 'utf8')).toMatch('expo-status-bar'); }); diff --git a/src/commands/createApp.ts b/src/commands/createApp.ts index 9265a55..f57affc 100644 --- a/src/commands/createApp.ts +++ b/src/commands/createApp.ts @@ -4,6 +4,7 @@ import ora from 'ora'; import { globals } from '../constants'; import addDependency from '../util/addDependency'; import addPackageJsonScripts from '../util/addPackageJsonScripts'; +import copyTemplateDirectory from '../util/copyTemplateDirectory'; import exec from '../util/exec'; import getUserPackageManager from '../util/getUserPackageManager'; import print from '../util/print'; @@ -30,6 +31,7 @@ export async function createApp(name: string | undefined, options: Options) { globals.interactive = interactive; globals.isTest = isTest; + globals.isCreateApp = true; const appName = name || (await getAppName()); await printIntro(); @@ -83,6 +85,8 @@ export async function createApp(name: string | undefined, options: Options) { await commit('Add jest, Testing Library'); } + await copyTemplateDirectory({ templateDir: 'createApp' }); + print(chalk.green(`\n\nšŸ‘– ${appName} successfully configured!`)); print(` diff --git a/src/constants.ts b/src/constants.ts index cf26cb0..42b6b14 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -4,6 +4,7 @@ import { fileURLToPath } from 'url'; export const globals = { interactive: true, isTest: false, + isCreateApp: false, }; // TSUP builds files without hierarchy to dist/, so the path is ultimately diff --git a/templates/createApp/App.tsx b/templates/createApp/App.tsx new file mode 100644 index 0000000..fd4ee47 --- /dev/null +++ b/templates/createApp/App.tsx @@ -0,0 +1,28 @@ +import { StatusBar } from 'expo-status-bar'; +import { StyleSheet, Text, View } from 'react-native'; +import Providers, { Provider } from 'src/components/Providers'; + +// Add providers to this array +const providers: Provider[] = [ + // CODEGEN:BELT:PROVIDERS - do not remove +]; + +export default function App() { + return ( + + + Open up App.js to start working on your app! + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#fff', + alignItems: 'center', + justifyContent: 'center', + }, +}); diff --git a/templates/createApp/src/components/Providers.tsx b/templates/createApp/src/components/Providers.tsx new file mode 100644 index 0000000..d61b5cd --- /dev/null +++ b/templates/createApp/src/components/Providers.tsx @@ -0,0 +1,14 @@ +import { ReactNode } from 'react'; + +export type Provider = (children: ReactNode) => ReactNode; + +type ProvidersProps = { + children: ReactNode; + providers: Provider[]; +}; + +export default function Providers({ children, providers }: ProvidersProps) { + return providers.reverse().reduce((acc, current) => { + return current(acc); + }, children); +}