diff --git a/.eslintignore b/.eslintignore index 5b465972..923ce12e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,4 +3,4 @@ node_modules/ babel.config.js webpack.config.js docs - +scripts/cli/build/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index a5704bb0..6fdbfa9d 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,10 @@ GoogleService-Info.plist scripts/doppler_variables.sh .env env.json +.env*.local .vercel -.env*.local + +# baca-cli build +scripts/cli/build/ +scripts/cli/temp/ diff --git a/README.md b/README.md index fcc08060..9087c1b7 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ Logo

-[![MIT License](https://img.shields.io/npm/l/@binarapps/expo-ts-template?style=flat-square)](https://github.com/tterb/atomic-design-ui/blob/master/LICENSEs) -[![MIT License](https://img.shields.io/npm/v/@binarapps/expo-ts-template?style=flat-square)](https://img.shields.io/npm/v/@binarapps/expo-ts-template?style=flat-square) -[![MIT License](https://img.shields.io/npm/dt/@binarapps/expo-ts-template?style=flat-square)](https://img.shields.io/npm/dt/@binarapps/expo-ts-template?style=flat-square) -[![MIT License](https://img.shields.io/github/stars/binarapps/expo-ts-template?style=flat-square)](https://img.shields.io/github/stars/binarapps/expo-ts-template?style=flat-square) +[![MIT License](https://img.shields.io/npm/l/@binarapps/baca-react-native-template?style=flat-square)](https://github.com/tterb/atomic-design-ui/blob/master/LICENSEs) +[![MIT License](https://img.shields.io/npm/v/@binarapps/baca-react-native-template?style=flat-square)](https://img.shields.io/npm/v/@binarapps/baca-react-native-template?style=flat-square) +[![MIT License](https://img.shields.io/npm/dt/@binarapps/baca-react-native-template?style=flat-square)](https://img.shields.io/npm/dt/@binarapps/baca-react-native-template?style=flat-square) +[![MIT License](https://img.shields.io/github/stars/binarapps/baca-react-native-template?style=flat-square)](https://img.shields.io/github/stars/binarapps/baca-react-native-template?style=flat-square) [![runs with Expo Go](https://img.shields.io/badge/Runs%20with%20Expo%20Go-4630EB.svg?style=flat-square&logo=EXPO&labelColor=f3f3f3&logoColor=000)](https://expo.dev/client) # @binarapps/baca-react-native-template @@ -18,7 +18,7 @@ This is a template to be used with react native and expo. It includes all the ne ## Documentation -Check out our [documentation page](https://baca-docs.vercel.app/docs/bootstrap/intro), it contains: +Check out our [documentation page](https://baca-docs.vercel.app/docs/overview), it contains: - Bootstrapping project - tutorial how to easy setup from scratch - Deploying app @@ -28,15 +28,15 @@ Check out our [documentation page](https://baca-docs.vercel.app/docs/bootstrap/i ## Why to use? -We know there are a lot of project starters for react native, but we have some good features: +There are a lot of project starters for react native, we have some good features that other starters usually don't have: - Fully works with **EXPO GO** - Good for start of the project, later you can switch to expo-dev-client - Fully works on **WEB** - If you want to develop apps both on web and mobile this starter is good choice for you -- **Code generators** (create new screen / create new component) +- **Code generators** (create new screen / create new component and others) - **Fully + Strong typed** -- **App deployment documentation** (currently in progress, but it will be added in near future) +- **App deployment documentation** ## How it looks? @@ -48,35 +48,38 @@ We know there are a lot of project starters for react native, but we have some g -[![expo-ts-template binarapps](https://img.youtube.com/vi/NmTd5nXXTLI/0.jpg)](https://www.youtube.com/watch?v=NmTd5nXXTLI) +[![baca-react-native-template binarapps](https://img.youtube.com/vi/NmTd5nXXTLI/0.jpg)](https://www.youtube.com/watch?v=NmTd5nXXTLI) ## How to use? -We have prepared a detailed documentation for how to run project with this template - **[Bootstrap docs](https://baca-docs.vercel.app/docs/bootstrap/intro)** +We have prepared a detailed documentation for how to run project with this template - **[Bootstrap docs](https://baca-docs.vercel.app/docs/overview)** It's great for production project, but if you want to just test it, you can follow the quick steps (on the bottom). ### Quick steps: -- `npx create-expo-app --template=@binarapps/expo-ts-template name_of_your_app` +- `npx create-expo-app --template=@binarapps/baca-react-native-template name_of_your_app` - `cd name_of_your_app` - `yarn bootstrap` - the cli will ask you some questions about your app (you can fill all this data later) ## What's inside? -[![MIT License](https://img.shields.io/npm/types/@binarapps/expo-ts-template?style=flat-square)](https://img.shields.io/npm/types/@binarapps/expo-ts-template?style=flat-square) -[![MIT License](https://img.shields.io/github/package-json/dependency-version/binarapps/expo-ts-template/expo?style=flat-square)](https://img.shields.io/github/package-json/dependency-version/binarapps/expo-ts-template/expo?style=flat-square) -[![MIT License](https://img.shields.io/github/package-json/dependency-version/binarapps/expo-ts-template/@react-navigation/native?style=flat-square)](https://img.shields.io/github/package-json/dependency-version/binarapps/expo-ts-template/@react-navigation/native?style=flat-square) +[![MIT License](https://img.shields.io/npm/types/@binarapps/baca-react-native-template?style=flat-square)](https://img.shields.io/npm/types/@binarapps/baca-react-native-template?style=flat-square) +[![MIT License](https://img.shields.io/github/package-json/dependency-version/binarapps/baca-react-native-template/expo?style=flat-square)](https://img.shields.io/github/package-json/dependency-version/binarapps/baca-react-native-template/expo?style=flat-square) +[![MIT License](https://img.shields.io/github/package-json/dependency-version/binarapps/baca-react-native-template/@react-navigation/native?style=flat-square)](https://img.shields.io/github/package-json/dependency-version/binarapps/baca-react-native-template/@react-navigation/native?style=flat-square) ### Implemented custom features +- custom cli + - run `yarn baca` to see available options - generators: - - create screen - `yarn generate:screen` - - create component - `yarn generate:component` + - `yarn baca generate` | `yarn g` - support of multiple environments - production, staging, qa - eas configuration - update, build, submit +- deployment docs + - https://baca-docs.vercel.app/docs/overview - verifying code on pull request - pipelines - when creating pull request on github, there are tests, linters and types checks. If there will be some error you will be notified that something is wrong. - custom fonts @@ -117,11 +120,11 @@ It's great for production project, but if you want to just test it, you can foll - [Reanimated v2](https://github.com/software-mansion/react-native-reanimated) - Axios + React query - Fetching data from backend +- Jotai + - State management ## What is planned in the future? -- add some state management tool - in progress -- write docs (app deployment, app setup and more) - in progress - tutorial on how to use features - navigation - deepLinking @@ -136,7 +139,6 @@ It's great for production project, but if you want to just test it, you can foll - Create sample app and document the process of deployment - Improve mock server logic - add commit lint -- add script that display last update information (eas update) ### Implementations to add @@ -157,13 +159,13 @@ Please adhere to this project's `code of conduct`. Clone the project ```bash - git clone https://github.com/binarapps/expo-ts-template.git + git clone https://github.com/binarapps/baca-react-native-template.git ``` Go to the project directory ```bash - cd expo-ts-template + cd baca-react-native-template ``` Install dependencies diff --git a/docs/docs/OVERVIEW.md b/docs/docs/OVERVIEW.md new file mode 100644 index 00000000..0e48ea39 --- /dev/null +++ b/docs/docs/OVERVIEW.md @@ -0,0 +1,48 @@ +--- +id: overview +slug: /overview +title: Overview +sidebar_position: 1 +tags: + - Bootstrap + - Getting started + - Project structure +description: Bootstrap project structure, that is needed to start development your new project +--- + +# Overview + +**Welcome in BACA starter!** + +It is specialy designed to make react native apps development much easier and much faster. + +## Why to use? + +There are a lot of project starters for react native, we have some good features that other starters usually don't have: + +- Fully works with **EXPO GO** + - Good for start of the project, later you can switch to expo-dev-client +- Fully works on **WEB** + - If you want to develop apps both on web and mobile this starter is good choice for you +- **Code generators** (create new screen / create new component and others) +- **Fully + Strong typed** +- **App deployment documentation** + +## How to start? + +1. Create new app - follow [this docs](/docs/bootstrap/create-new-app) + + - At this point you can start development + - When you will need to show app to testers or client go to next step + +2. Fill needed data to start deployment - follow [this docs](/docs/bootstrap/testing) + + - This will make all required changes to the code base + +3. Prepare deployment - follow [this docs](/docs/deploy/intro) + + - Thanks to this docs you will have possibility to automatically deploy app to testers and + +4. Send app to review and publish it on app stores + + - We don't plan to make docs about that, but maybe in the future we will do that! diff --git a/docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx b/docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx index 9d0fe62c..95cd4b0e 100644 --- a/docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx +++ b/docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx @@ -1,7 +1,7 @@ --- id: bootstrap-testing slug: /bootstrap/testing -title: Bootstrap - testing +title: Prepare for testing sidebar_position: 2 tags: - Bootstrap @@ -103,6 +103,16 @@ If you have some domain, for example: example.com, your bundle id could be: `com open your app when tapped. It is only available in standalone apps. +
+ +> **Optional step** - You can run the bootstrap script later + +This can be found in developer apple console. + +> Image will be added in future + +
+
You can generate this data and save it in some notepad or somewhere else. @@ -115,7 +125,8 @@ You can generate this data and save it in some notepad or somewhere else. "slug": "expo_app_slug", "easProjectId": "xxx-xxx-xxx-xx", "scheme": "yourUrlScheme", - "adaptiveIconBackgroundColor": "#2E7AF0CC" + "adaptiveIconBackgroundColor": "#2E7AF0CC", + "appleTeamId": "1234XXXYYX" } ```
@@ -126,8 +137,15 @@ You can generate this data and save it in some notepad or somewhere else. ### **After you will collect all necessary data run this command:** +> If you don't have some value you can use default value, bootstrap script is collecting current data from code, so you can run this script multiple times +> **REMEBER** to have empty git repo because this script will do changes in your codebase + ```sh -yarn bootstrap:new_app +yarn b + +## OR + +yarn baca bootstrap ``` ## Potential issues diff --git a/docs/docs/bootstrap/BOOTSTRAP_INTRO.mdx b/docs/docs/bootstrap/CREATE_NEW_APP.mdx similarity index 79% rename from docs/docs/bootstrap/BOOTSTRAP_INTRO.mdx rename to docs/docs/bootstrap/CREATE_NEW_APP.mdx index 226de4bf..056755fb 100644 --- a/docs/docs/bootstrap/BOOTSTRAP_INTRO.mdx +++ b/docs/docs/bootstrap/CREATE_NEW_APP.mdx @@ -1,10 +1,11 @@ --- id: bootstrap -slug: /bootstrap/intro -title: Bootstrap - start development +slug: /bootstrap/create-new-app +title: Create new app sidebar_position: 1 tags: - Bootstrap + - Create new app - Getting started - Project structure description: Bootstrap project structure, that is needed to start development your new project @@ -16,7 +17,7 @@ import Details from '@site/src/components/Details' Bootstrap project structure, that is needed to start development -## **What you need to do to start development?** +## **What you need to do, to start development** --- @@ -62,7 +63,12 @@ If you do not have expo account → register on your company email. In terminal eas register ``` -You will be redirected to expo registration page. If something is not right please make sure you have eas cli installed - `npm install -g eas-cli` +You will be redirected to expo registration page. If something is not right please make sure you have eas cli installed + +```bash +npm install -g eas-cli + +``` Login to expo account on your local machine. @@ -112,22 +118,28 @@ eas whoami ### Step 4.Sync project with code. -Synchronize the newly created Expo Project to your app. +Please gather this data: + +1. **app name** - you can add **display name** from previous step, or add anything you want here, this name will be displayed for users later +2. **slug name** - created in 1-st point. +3. **owner** - organization picked from the list in 1-st point -In `app.json` file please insert the following: +:::note -1. **slug name** - created in 1-st point. -2. **owner** - organization picked from the list in 1-st point +If you will have issues with finding this values, please check [bootstrap testing docs](/docs/bootstrap/testing) -```json -{ - "expo": { - "owner": "your_organization_name", - "slug": "your_app_name" - } -} +::: + +If you gather all this data please run this command: + +```bash +yarn baca bootstrap --simple ``` +:::warning +Please verify all changes made with the script +::: + --- ### Step 5. Make environment variables setup - [tutorial](/docs/doppler-config) diff --git a/docs/docs/bootstrap/_category_.json b/docs/docs/bootstrap/_category_.json index 9be6a2fc..db02be29 100644 --- a/docs/docs/bootstrap/_category_.json +++ b/docs/docs/bootstrap/_category_.json @@ -1,6 +1,6 @@ { "label": "Start development", - "position": 1, + "position": 2, "collapsible": true, "collapsed": false, "link": { diff --git a/docs/docs/deploy/_category_.json b/docs/docs/deploy/_category_.json index 7f821806..1c146b5f 100644 --- a/docs/docs/deploy/_category_.json +++ b/docs/docs/deploy/_category_.json @@ -1,6 +1,6 @@ { "label": "Deployment", - "position": 2, + "position": 3, "collapsible": true, "collapsed": false, "link": { diff --git a/docs/docs/tutorials/ICONS.md b/docs/docs/tutorials/ICONS.md index 1b1e326f..5ee9b766 100644 --- a/docs/docs/tutorials/ICONS.md +++ b/docs/docs/tutorials/ICONS.md @@ -40,4 +40,4 @@ It is added to the app as a font generated by [icomoon app](https://icomoon.io/a 9. Generate new types for icons - - run script generating icon types `yarn generate:icon:types` + - run script generating icon types `yarn baca generate icon-types` diff --git a/docs/docs/tutorials/NOTIFICATIONS_SETUP.md b/docs/docs/tutorials/NOTIFICATIONS_SETUP.md index a4dfe463..785ec5f5 100644 --- a/docs/docs/tutorials/NOTIFICATIONS_SETUP.md +++ b/docs/docs/tutorials/NOTIFICATIONS_SETUP.md @@ -21,7 +21,7 @@ Expo notifications are already preconfigured in this template. However, you stil ## Usage in expo dev client (expo run:\[android:ios\]) 1. Make sure you have created your account in [expo.dev](http://expo.dev). -2. Follow [bootstrap](/docs/bootstrap/intro) docs +2. Follow [bootstrap](/docs/bootstrap/create-new-app) docs 3. Follow platform specific configuration. ### Android diff --git a/docs/docs/tutorials/SCRIPTS.md b/docs/docs/tutorials/SCRIPTS.md new file mode 100644 index 00000000..eccc98a0 --- /dev/null +++ b/docs/docs/tutorials/SCRIPTS.md @@ -0,0 +1,74 @@ +--- +id: scripts +slug: /scripts +title: Scripts +sidebar_position: 8 +tags: + - Scripts + - Expo + - React + - React Native +description: BACA - scripts +--- + + + +# Scripts + +Here is a list of useful scripts in this starter: + +- [yarn g](#generators) +- [yarn b](#bootstrap-new-app) +- [yarn generate:last:publish](#generate-last-update-info) + +## Generators + +Run this command to see magic: + +``` +yarn g +``` + +OR + +```bash +yarn baca generate +``` + +This will show you list of generators, run this to test it :) + +## Boostrap new app + +Check [boostrap docs](/docs/bootstrap/testing) to see more details. + +``` +yarn b +``` + +OR + +```bash +yarn baca:boostrap +``` + +## Envrionment variables + +Check [doppler documentantion](/docs/doppler-config) to see more details + +## Generate last update info + +This script is automatically executed after running `yarn update:production` (or staging|qa), this will return ids of last udpate: + +``` +BACA - 2.1.0: +- android: 8f6577b8-f1a6-46de-9471-8a0ff072ccc9 +- ios: a8bdc3aa-239a-48da-b76e-c259af5567b0 +``` + +You can easily share this with testers or clients, thanks to that users will be sure what version is currently in the app. + +## Generate icon types + +This script has to be executed when new icons where added to the icomoon.ttf icons set in case to provide proper types for components which use icons. + +If script won't be executed typescript will throw an error when trying to use newly added icon. diff --git a/docs/docs/tutorials/_category_.json b/docs/docs/tutorials/_category_.json index 95985d9e..4ec1db4c 100644 --- a/docs/docs/tutorials/_category_.json +++ b/docs/docs/tutorials/_category_.json @@ -1,6 +1,6 @@ { "label": "Tutorials", - "position": 3, + "position": 4, "collapsible": true, "collapsed": false, "link": { diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts index 95c575b1..4e7a919b 100644 --- a/docs/docusaurus.config.ts +++ b/docs/docusaurus.config.ts @@ -72,9 +72,13 @@ const config: Config = { { title: 'Docs', items: [ + { + label: 'Overview', + to: '/docs/overview', + }, { label: 'Intro', - to: '/docs/bootstrap/intro', + to: '/docs/bootstrap/create-new-app', }, ], }, @@ -106,6 +110,7 @@ const config: Config = { prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, + additionalLanguages: ['bash'], }, } satisfies Preset.ThemeConfig, } diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx index b54f693f..066b1fc0 100644 --- a/docs/src/pages/index.tsx +++ b/docs/src/pages/index.tsx @@ -18,7 +18,7 @@ function HomepageHeader() {

{siteConfig.tagline}

- + See docs
diff --git a/package.json b/package.json index 3b70b3ea..5443f517 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,11 @@ "license": "MIT", "scripts": { "android:dev-client": "IS_DEV=1 npx expo run:android", - "bootstrap:new_app": "yarn && node ./scripts/bootstrap.js", - "build:production:android": "yarn prepare:production && eas build --platform android --profile production", + "baca": "yarn build:baca-cli && node ./scripts/cli/build/scripts/cli", + "b": "yarn baca b", + "g": "yarn baca g", + "build:baca-cli": "npx tsc -p ./scripts/cli/tsconfig.cli.json", + "build:production:android": "yarnx prepare:production && eas build --platform android --profile production", "build:production:ios": "yarn prepare:production && eas build --platform ios --profile production", "build:production": "yarn prepare:production && eas build --platform all --profile production", "build:qa:android": "yarn prepare:qa && eas build --platform android --profile qa", @@ -44,20 +47,17 @@ "deploy:staging:android": "yarn prepare:staging && eas build --platform android --profile staging --auto-submit --non-interactive", "deploy:staging:ios": "yarn prepare:staging && eas build --platform ios --profile staging --auto-submit --non-interactive", "deploy:staging": "yarn prepare:staging && eas build --platform all --profile staging --auto-submit --non-interactive", - "eas-build-pre-install": "base64 --help && echo $ANDROID_FIREBASE_CONFIG | base64 --decode > google-services.json && cat google-services.json && echo $IOS_FIREBASE_CONFIG | base64 --decode > GoogleService-Info.plist && cat GoogleService-Info.plist", - "generate:component": "node ./scripts/create_new_component.js && yarn eslint src --fix && yarn tsc", + "eas-build-pre-install": "base64 --help && echo $ANDROID_FIREBASE_CONFIG | base64 --decode > google-services.json && cat google-services.json && echo $IOS_FIREBASE_CONFIG | base64 --decode > GoogleService-Info.plist && cat GoogleService-Info.plist", "generate:env:production": "scripts/generate_dotenv.sh production", "generate:env:qa": "scripts/generate_dotenv.sh qa", "generate:env:staging": "scripts/generate_dotenv.sh staging", "generate:google-services-config": "./scripts/generate_firebase_config.sh", - "generate:icon:types": "node ./scripts/generate_icon_types.js", "generate:last:publish": "node ./scripts/generate_last_update_id.js", "generate:query": "yarn orval --config ./orval.config.ts", - "generate:screen": "node ./scripts/create_new_screen.js && yarn eslint src --fix && yarn tsc", "ios:dev-client": "IS_DEV=1 npx expo run:ios", "lint:fix": "eslint src --fix", "lint": "eslint src && yarn tsc", - "postinstall": "patch-package", + "postinstall": "patch-package && yarn build:baca-cli", "prebuild:android": "IS_DEV=1 npx expo prebuild --clean -p android", "prebuild:ios": "IS_DEV=1 npx expo prebuild --clean -p ios", "prepare:env_file": "cp ./templates/doppler_variables_template.sh ./scripts/doppler_variables.sh", @@ -178,8 +178,10 @@ "babel-loader": "^9.1.3", "babel-plugin-module-resolver": "^5.0.0", "babel-preset-expo": "^10.0.0", + "commander": "^12.0.0", "cross-env": "^7.0.3", "dotenv": "^16.4.1", + "enquirer": "^2.4.1", "eslint": "^8.56.0", "eslint-config-prettier": "^8.8.0", "eslint-config-universe": "^11.2.0", @@ -198,11 +200,9 @@ "patch-package": "^7.0.1", "prettier": "^2.8.8", "pretty-quick": "^4.0.0", - "prompt-sync": "^4.2.0", "react-native-url-polyfill": "^2.0.0", "react-test-renderer": "^18.2.0", "readline": "^1.3.0", - "select-prompt": "^0.3.2", "text-encoding-polyfill": "^0.6.7", "typescript": "^5.3.0", "xmlhttprequest": "^1.8.0" diff --git a/scripts/README.md b/scripts/README.md index a4332363..b835ebfb 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -44,20 +44,23 @@ yarn prepare:qa yarn prepare:staging ``` -## 3. `generate_icon_types.sh` +## 3. generators -This script has to be executed when new icons where added to the icomoon.ttf icons set in case to provide proper types for components which use icons. -If script won't be executed typescript will throw an error when trying to use newly added icon. - -## 4. generators +Run this command to see all available commands ```bash -## Create new component -yarn generate:screen +yarn baca +``` -## Create new screen -yarn generate:component +Our custom cli for now contain this screens: -## Bootstrap the app -yarn bootstrap:new_app -``` +1. Generators + +- create new screen - `yarn baca generate screen` +- create new component - `yarn baca generate component` +- create icon types - `yarn baca generate icon-types` +- create theme - `yarn baca generate theme` + +2. Bootstrap the app + +- `yarn baca bootstrap` diff --git a/scripts/bootstrap.js b/scripts/bootstrap.js deleted file mode 100644 index f7919222..00000000 --- a/scripts/bootstrap.js +++ /dev/null @@ -1,200 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -const fs = require('fs') -const prompt = require('prompt-sync')() - -const { logger, addAfter } = require('./utils') - -const paths = { - appJson: './app.json', - appConfig: './app.config.ts', - readme: './README.md', - readmeTemplate: './templates/readme_template.md', - pullRequestTemplate: './.github/pull_request_template.md', - newPullReuestTemplate: './templates/pull_request_template.md', -} - -// 1. -const replaceReadme = (appName, organizationOwner) => { - let contents = fs.readFileSync(paths.readmeTemplate, 'utf8') - contents = contents.replaceAll('_NAME_', appName) - contents = contents.replaceAll('_OWNER_', organizationOwner) - - fs.writeFileSync(paths.readme, contents) -} - -// 2. -const setUpAppConfig = (appName, bundleId, androidPackageName, scheme, easId, androidIconColor) => { - let contents = fs.readFileSync(paths.appConfig, 'utf8') - - const appConfig = ` -export const APP_CONFIG = { - androidPackageName: '${androidPackageName}', // CONFIG: Add your android package name here - appName: '${appName}', // CONFIG: Add your app name here - easProjectId: '${easId}', // CONFIG: Add your eas project ID here - iosBundleIdentifier: '${bundleId}', // CONFIG: Add your ios bundle identifier here - scheme: '${scheme}', // CONFIG: Add your url scheme to link to your app - adaptiveIconBackgroundColor: '${androidIconColor}', // CONFIG: Add your url scheme to link to your app -} as const -` - - contents = contents.replace(/(\/\/ APP_CONFIG_START)[\s\S]*?(\/\/ APP_CONFIG_END)/g, '$1$2') - - contents = addAfter(contents, '// APP_CONFIG_START', `${appConfig}`) - fs.writeFileSync(paths.appConfig, contents) -} - -// 3. -const replatePullRequestTemplate = () => { - const contents = fs.readFileSync(paths.newPullReuestTemplate, 'utf8') - - fs.writeFileSync(paths.pullRequestTemplate, contents) -} - -// 4. -const changeAppJson = (appName, appSlug, organizationOwner) => { - const newAppJson = JSON.parse(fs.readFileSync(paths.appJson, 'utf8')) - newAppJson.expo.slug = appSlug - newAppJson.expo.name = appName - newAppJson.expo.owner = organizationOwner - newAppJson.version = '1.0.0' - fs.writeFileSync(paths.appJson, JSON.stringify(newAppJson, null, 2)) -} - -// 5. -const changePackageJson = (appName, organizationOwner) => { - const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8')) - packageJson.name = `@${organizationOwner}/${appName}` - packageJson.description = `App created from expo-template powered by binarapps` - packageJson.version = '1.0.0' - - delete packageJson.repository - delete packageJson.bugs - delete packageJson.keywords - - fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, 2)) -} - -// 6. -const removeIssueTemplates = () => { - fs.rm('./.github/ISSUE_TEMPLATE', { recursive: true, force: true }, () => {}) -} - -// 7. -const removeDocsFolder = () => { - fs.rm('./documentation', { recursive: true, force: true }, () => {}) -} - -const setUpProject = async ( - appName, - bundleId, - androidPackageName, - scheme, - easId, - organizationOwner, - androidIconColor, - appSlug -) => { - // START - logger.success('Start ...') - - // 1. Delete readme -> and create new, with new app name etc. - logger.info('Generating new readme file') - replaceReadme(appName, organizationOwner) - - // 2. Replace appName, bundleId, androidPackageName,scheme and easProjectId in app.config.ts file - logger.info('Change project variables in app.config.ts file') - setUpAppConfig(appName, bundleId, androidPackageName, scheme, easId, androidIconColor) - - // 3. Delete exist pull request template -> generate the new - logger.info('Generating new pull request template file') - replatePullRequestTemplate() - - // 4. Change app.json file - logger.info('Change app.json file') - changeAppJson(appName, appSlug, organizationOwner) - - // 5. Change package.json file - logger.info('Change package.json file') - changePackageJson(appName, organizationOwner) - - // 6. Remove issue templates - logger.info('Remove issue templates') - removeIssueTemplates() - - // 7. Remove docs folder - logger.info('Remove docs folder') - removeDocsFolder() - - //Finish - logger.success(`Config your project has been success`) -} - -const bootstrap = () => { - logger.info('Please give me this information to setup your project:') - const appName = prompt('App name: ') - if (!appName) { - return logger.error('Please write correct app name') - } - - const appSlug = prompt('App slug (from expo dashboard): ') - if (!appSlug) { - return logger.error('Please write app slug') - } - - const organizationOwner = prompt('Organization owner (from expo dashboard): ') - if (!organizationOwner) { - return logger.error('Please write organziation owner') - } - - const easId = prompt('EAS project ID (from expo dashboard): ') - if (!easId) { - return logger.error('Please write correct eas project ID') - } - - const androidIconColor = - prompt('Android adaptive icon color (you can leave it empty and fill it later): ') || - '#2E7AF0CC' - - const bundleId = prompt('Bundle ID (ios): ') - if (!bundleId) { - return logger.error('Please write correct bundle ID') - } - - const androidPackageName = prompt('Package name (android): ') - if (!androidPackageName) { - return logger.error('Please write correct android package name') - } - - const scheme = prompt('URL scheme (for deeplinking): ') - if (!scheme) { - return logger.error('Please write correct scheme') - } - - // 1. Setup project -> set ( appName, bundleId, androidPackageName, appScheme, easProjectId, organizationOwner, androidIconColor ) - setUpProject( - appName, - bundleId, - androidPackageName, - scheme, - easId, - organizationOwner, - androidIconColor, - appSlug - ) - - logger.info( - '\nYou can also add images right now, go to assets folder and replace images to match your app \n' - ) - logger.info('\nPlease verify the changes made by this script and commit it to your repository \n') -} - -bootstrap() - -// INSTRUCTION: -// 1. Delete readme and write the new one -// 2. Setup app.config.ts file -// 3. Setup pull_request_template.md -// 4. Setup app.json file -// 5. Setup package.json file -// 6. Remove issue templates -// 7. Remove docs folder diff --git a/scripts/cli/actions/bootstrap.ts b/scripts/cli/actions/bootstrap.ts new file mode 100644 index 00000000..38548722 --- /dev/null +++ b/scripts/cli/actions/bootstrap.ts @@ -0,0 +1,285 @@ +import { prompt } from 'enquirer' +import fs from 'fs' + +import { APP_CONFIG } from '../../../app.config' +import { + README_PATH, + APP_CONFIG_PATH, + NEW_PULL_REQUEST_TEMPLATE_PATH, + PULL_REQUEST_TEMPLATE_PATH, + APP_JSON_PATH, +} from '../constants' +import { logger, addAfter } from '../utils' + +const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8')) +const newAppJson = JSON.parse(fs.readFileSync(APP_JSON_PATH, 'utf8')) + +type SetupProjectProps = { + appName: string + bundleId: string + androidPackageName: string + scheme: string + easId: string + organizationOwner: string + androidIconColor: string + appSlug: string + appleTeamId: string +} + +// Check types of questions here: +// - https://github.com/enquirer/enquirer/blob/master/examples/enquirer/questions.js +type Questions = keyof SetupProjectProps + +type QuestionsObject = { + [k in Questions]: { + type: string + message: string + initial: string + simple?: boolean + order: number + } +} + +type Question = { + type: string + message: string + initial: string + order: number + name: string +} + +const questionsObject: QuestionsObject = { + appName: { + type: 'text', + message: 'What is your app name?', + initial: APP_CONFIG.appName, + simple: true, + order: 1, + }, + appSlug: { + type: 'text', + message: 'What is your expo app slug?', + initial: newAppJson.expo.slug, + simple: true, + order: 2, + }, + organizationOwner: { + type: 'text', + message: 'What is your expo organization owner?', + initial: newAppJson.expo.owner, + simple: true, + order: 3, + }, + bundleId: { + type: 'text', + message: 'What is your bundle Id?', + initial: APP_CONFIG.iosBundleIdentifier, + order: 4, + }, + androidPackageName: { + type: 'text', + message: 'What is your android package name?', + initial: APP_CONFIG.androidPackageName, + order: 5, + }, + scheme: { + type: 'text', + message: 'What is your scheme name?', + initial: APP_CONFIG.scheme, + order: 6, + }, + easId: { + type: 'text', + message: 'What is your eas id?', + initial: APP_CONFIG.easProjectId, + order: 7, + }, + androidIconColor: { + type: 'text', + message: 'What is your android icon color?', + initial: APP_CONFIG.adaptiveIconBackgroundColor, + order: 8, + }, + appleTeamId: { + type: 'text', + message: 'What is your apple team id? (Optional)', + // FIXME: GET IT FROM EAS.JSON + initial: '5764GC687R', + order: 9, + }, +} + +/** + * Replaces placeholders in the README file with the provided app name and organization owner. + * + * @param appName - The name of the app. + * @param organizationOwner - The owner of the organization. + */ +const replaceReadme = ({ appName, organizationOwner }: SetupProjectProps) => { + let contents = fs.readFileSync(README_PATH, 'utf-8') + + contents = contents.replaceAll('_NAME_', appName) + contents = contents.replaceAll('_OWNER_', organizationOwner) + + fs.writeFileSync(README_PATH, contents) +} + +/** + * Sets up the app configuration by updating the contents of the app config file. + * + * @param appName - The name of the app. + * @param bundleId - The bundle identifier for iOS. + * @param androidPackageName - The package name for Android. + * @param scheme - The URL scheme to link to the app. + * @param easId - The EAS project ID. + * @param androidIconColor - The background color for the adaptive icon on Android. + */ +const setUpAppConfig = ({ + appName, + bundleId, + androidPackageName, + scheme, + easId, + androidIconColor, +}: SetupProjectProps) => { + let contents = fs.readFileSync(APP_CONFIG_PATH, 'utf8') + + const appConfig = ` +export const APP_CONFIG = { + androidPackageName: '${androidPackageName}', // CONFIG: Add your android package name here + appName: '${appName}', // CONFIG: Add your app name here + easProjectId: '${easId}', // CONFIG: Add your eas project ID here + iosBundleIdentifier: '${bundleId}', // CONFIG: Add your ios bundle identifier here + scheme: '${scheme}', // CONFIG: Add your url scheme to link to your app + adaptiveIconBackgroundColor: '${androidIconColor}', // CONFIG: Add your android adaptive icon background color here +} as const +` + + contents = contents.replace(/(\/\/ APP_CONFIG_START)[\s\S]*?(\/\/ APP_CONFIG_END)/g, '$1$2') + + contents = addAfter(contents, '// APP_CONFIG_START', `${appConfig}`) + fs.writeFileSync(APP_CONFIG_PATH, contents) +} + +/** + * Replaces the contents of the pull request template file with the contents of a new pull request template file. + */ +const replacePullRequestTemplate = () => { + const contents = fs.readFileSync(NEW_PULL_REQUEST_TEMPLATE_PATH, 'utf8') + + fs.writeFileSync(PULL_REQUEST_TEMPLATE_PATH, contents) +} + +const changeAppJson = ({ appName, appSlug, organizationOwner }: SetupProjectProps) => { + newAppJson.expo.slug = appSlug + newAppJson.expo.name = appName + newAppJson.expo.owner = organizationOwner + newAppJson.expo.version = '1.0.0' + + fs.writeFileSync(APP_JSON_PATH, JSON.stringify(newAppJson, null, 2)) +} + +const changePackageJson = ({ appName, organizationOwner }: SetupProjectProps) => { + packageJson.name = `@${organizationOwner}/${appName}` + packageJson.description = `App created from expo-template powered by binarapps` + packageJson.version = '1.0.0' + + delete packageJson.repository + delete packageJson.bugs + delete packageJson.keywords + + fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, 2)) +} + +const removeIssueTemplates = () => { + fs.rm('./.github/ISSUE_TEMPLATE', { recursive: true, force: true }, () => {}) +} + +const removeDocsFolder = () => { + fs.rm('./docs', { recursive: true, force: true }, () => {}) +} + +// TODO: Implement changeEasJson and changeDeeplinkFiles functions +const changeEasJson = (config: SetupProjectProps) => {} +const changeDeeplinkFiles = (config: SetupProjectProps) => {} + +const setUpProject = async (config: SetupProjectProps) => { + // START + logger.success('Start bootstrapping ...') + + // 1. Delete readme -> and create new, with new app name etc. + logger.info('Generating new readme file') + replaceReadme(config) + + // 2. Replace appName, bundleId, androidPackageName,scheme and easProjectId in app.config.ts file + logger.info('Change project variables in app.config.ts file') + setUpAppConfig(config) + + // 3. Delete exist pull request template -> generate the new + logger.info('Generating new pull request template file') + replacePullRequestTemplate() + + // 4. Change app.json file + logger.info('Change app.json file') + changeAppJson(config) + + // 5. Change package.json file + logger.info('Change package.json file') + changePackageJson(config) + + // 6. Remove issue templates + logger.info('Remove issue templates') + removeIssueTemplates() + + // 7. Remove docs folder + logger.info('Remove docs folder') + removeDocsFolder() + + // 8. Change eas.json + changeEasJson(config) + + // 9. Change deeplink files + changeDeeplinkFiles(config) + + //Finish + logger.success(`Config your project has been success`) +} + +type BootstrapConfig = { isSimple: boolean } + +const sortQuestions = (questions: Question[]) => { + return questions.sort((a, b) => a.order - b.order) +} + +const getQuesstions = ({ isSimple }: BootstrapConfig) => { + const questions = Object.entries(questionsObject).map((value) => ({ + name: value[0], + ...value[1], + })) + + if (isSimple) { + return sortQuestions(questions.filter((question) => question.simple)) + } + + return sortQuestions(questions) +} + +export const bootstrap = async (config: BootstrapConfig) => { + try { + logger.info('Please give me this information to setup your project:') + + const answers = (await prompt(getQuesstions(config))) as unknown as SetupProjectProps + + await setUpProject(answers) + + logger.info( + '\nYou can also add images (splash screen, app icon, logos) right now, \nGo to `assets` folder and replace images to match your app.\n' + ) + logger.info('\nPlease verify the changes made by this script and commit it to your repository.') + } catch (e) { + logger.error( + '\nError while bootstraping project \nERROR:', + e ? e : "Couldn't find what's happened" + ) + } +} diff --git a/scripts/cli/actions/generate.ts b/scripts/cli/actions/generate.ts new file mode 100644 index 00000000..8ef67d07 --- /dev/null +++ b/scripts/cli/actions/generate.ts @@ -0,0 +1,53 @@ +import { prompt } from 'enquirer' + +import { generateIconTypes, generateScreen, generateTheme, generateComponent } from '../commands' + +export const generators = [ + { + title: 'Screen', + description: 'Generate new screen', + value: 'screen', + command: generateScreen, + }, + { + title: 'Component', + description: 'Generate new component', + value: 'component', + command: generateComponent, + }, + { + title: 'Icon types', + description: 'Generate new icon types - based on icon - `selection.json`', + value: 'icon-types', + command: generateIconTypes, + }, + { + title: 'Generate theme', + description: 'Generate new theme - based on figma variables', + value: 'generate-theme', + command: generateTheme, + }, +] as const + +export const generate = async () => { + const promptAnswer = await prompt({ + name: 'generator', + message: 'What do you want to generate?', + type: 'select', + choices: generators.map((generator) => ({ + name: generator.title, + value: generator.value, + })), + }) + + // @ts-expect-error: generator not found on promptAnswer + const answerValue = promptAnswer?.generator as string + + const command = generators.find((item) => item.title === answerValue)?.command + + if (command) { + command() + } else { + console.log('There was some issue while running the script ', answerValue) + } +} diff --git a/scripts/cli/actions/index.ts b/scripts/cli/actions/index.ts new file mode 100644 index 00000000..cb6fe6ad --- /dev/null +++ b/scripts/cli/actions/index.ts @@ -0,0 +1,7 @@ +import { bootstrap } from './bootstrap' +import { generate } from './generate' + +export const actions = { + generate, + bootstrap, +} diff --git a/scripts/create_new_component.js b/scripts/cli/commands/generateComponent.ts similarity index 51% rename from scripts/create_new_component.js rename to scripts/cli/commands/generateComponent.ts index a650cad8..882a95a2 100644 --- a/scripts/create_new_component.js +++ b/scripts/cli/commands/generateComponent.ts @@ -1,16 +1,17 @@ +import { prompt } from 'enquirer' + +import { COMPONENT_TEMPLATE_PATH, COMPONENTS_PATH } from '../constants' +import { logger } from '../utils' + /* eslint-disable @typescript-eslint/no-var-requires */ const fs = require('fs') -const prompt = require('prompt-sync')() -const selectPrompt = require('select-prompt') - -const { logger } = require('./utils') const paths = { - template: './templates/component_template.tsx', - componentsIndex: './src/components/index.ts', + template: COMPONENT_TEMPLATE_PATH, + componentsIndex: COMPONENTS_PATH, } -const createComponentFile = (name, type) => { +const createComponentFile = (name: string, type: string) => { const newCommonComponentPath = `./src/components/${name}.tsx` const newAtomicComponentPath = `./src/components/${type}s/${name}.tsx` const componentFromFile = fs.readFileSync(paths.template, 'utf8') @@ -23,7 +24,7 @@ const createComponentFile = (name, type) => { fs.writeFileSync(newAtomicComponentPath, componentContent) } -const addToIndex = (name, type) => { +const addToIndex = (name: string, type: string) => { const atomicComponentIndexPath = `./src/components/${type}s/index.ts` const newExport = ` export * from './${name}'` @@ -38,7 +39,7 @@ export * from './${name}'` fs.writeFileSync(atomicComponentIndexPath, contents + newExport) } -const generateComponent = async (name, type) => { +const generateNewComponent = async (name: string, type: string) => { // Generate Component file logger.info('Generating component files') createComponentFile(name, type) @@ -50,24 +51,30 @@ const generateComponent = async (name, type) => { logger.success(`Component ${name} created successfully`) } -const generateNewComponent = async () => { +export const generateComponent = async () => { const componentTypes = [ - { title: 'Atom', value: 'atom' }, - { title: 'Molecule', value: 'molecule' }, - { title: 'Organism', value: 'organism' }, - { title: 'Common', value: 'common' }, + { name: 'Molecule', value: 'molecule' }, + { name: 'Organism', value: 'organism' }, + { name: 'Common', value: 'common' }, ] - selectPrompt('Select type for new component', componentTypes, { - cursor: 0, - }).on('submit', async (type) => { - const name = prompt('What is component name? ') - if (!name) { - return logger.error('No component name passed') - } - // 1. New component -> component_name + component_type (atom | molecule | organism | common) - await generateComponent(name, type) - }) -} + const promptAnswer = await prompt([ + { + message: 'What is your component type?', + name: 'componentType', + type: 'select', + choices: componentTypes, + }, + { + message: 'What is your component name?', + name: 'componentName', + type: 'input', + }, + ]) + // @ts-expect-error: componentType not found on promptAnswer + const componentType = promptAnswer.componentType as string + // @ts-expect-error: componentName not found on promptAnswer + const componentName = promptAnswer.componentName as string -generateNewComponent() + await generateNewComponent(componentName, componentType) +} diff --git a/scripts/cli/commands/generateIconTypes.ts b/scripts/cli/commands/generateIconTypes.ts new file mode 100644 index 00000000..e0c23a56 --- /dev/null +++ b/scripts/cli/commands/generateIconTypes.ts @@ -0,0 +1,17 @@ +import fs from 'fs' + +const prefix = `export type IconNames = + | ` + +export const generateIconTypes = () => { + const json = fs.readFileSync('./assets/icomoon/selection.json') + + const types = JSON.parse(json.toString()) + .icons.map((icon: { properties: { name: string } }) => `'${icon.properties.name}'`) + .join('\n | ') + .concat('\n') + + const content = prefix + types + + fs.writeFileSync('./src/types/icon.d.ts', content) +} diff --git a/scripts/cli/commands/generateScreen.ts b/scripts/cli/commands/generateScreen.ts new file mode 100644 index 00000000..27a18dc7 --- /dev/null +++ b/scripts/cli/commands/generateScreen.ts @@ -0,0 +1,192 @@ +import { prompt } from 'enquirer' +// eslint-disable-next-line import/order +import fs from 'fs' + +import { APP_ROUTER_DIRECTORY, SCREENS_DIRECTORY } from '../constants' +import { getDirectoryNames, logger } from '../utils' + +const addAfter = (content: string, searchText: string, textToAdd: string) => { + return content.replace(searchText, searchText + textToAdd) +} + +/** + * Recursively prompts the user to select a subdirectory and calls itself with the selected subdirectory path. + * + * @param basePath - The path of the current directory. + */ +const selectPath = async (basePath: string): Promise => { + const subDirectories = getDirectoryNames(basePath) + const hasSubDirectories = subDirectories.length > 0 + + // Return the result when there are no subdirectories + if (!hasSubDirectories) { + return basePath + } + + const subDirectoryPrompt = subDirectories.map((directoryName) => ({ + name: directoryName, + value: directoryName, + })) + + if (basePath.includes('tabs')) { + subDirectoryPrompt.unshift({ name: '_New Tab_', value: 'new-tab' }) + } + + if (basePath !== APP_ROUTER_DIRECTORY) { + subDirectoryPrompt.unshift({ name: '.', value: '.' }) + } + + const promptAnswer = await prompt({ + name: 'subValue', + message: 'What screen type do you want to generate?', + type: 'select', + choices: subDirectoryPrompt, + }) + + const subValue = subDirectoryPrompt.find( + // @ts-expect-error: subValue not found on promptAnswer + (promptValue) => promptValue.name === promptAnswer.subValue + )?.value + + // Return the result when user selects current directory + if (subValue === '.') { + return basePath + } + // Recursively execute path selection when subValue is not the current directory + const subResult = await selectPath(`${basePath}/${subValue}`) + + return subResult +} + +/** + * Validates if a route with the given name already exists in the specified path. + * @param routeName - The name of the route. + * @param routePath - The path where the route should be generated. + * @throws Error if the route already exists in the specified path. + */ +const validateRoute = (routeName: string, routePath: string) => { + const filePath = `${routePath}/${routeName}.tsx` + if (fs.existsSync(filePath)) { + logger.error(`Route ${routeName} already exists in ${routePath}`) + } +} + +/** + * Creates a route file with the given route name and path. + * @param {string} routeName - The name of the route. + * @param {string} routePath - The path where the route file will be created. + */ +const createRouteFile = (routeName: string, routePath: string) => { + const screenName = `${routeName.charAt(0).toUpperCase() + routeName.slice(1)}Screen` + fs.writeFileSync( + `${routePath}/${routeName.toLowerCase()}.tsx`, + `import { ${screenName} } from '@baca/screens' + +export default ${screenName} +` + ) +} + +/** + * Validates if a screen with the given name already exists. + * @param screenName - The name of the screen. + * @throws Error if the screen already exists. + */ +const validateScreen = (screenName: string) => { + const filePath = `${SCREENS_DIRECTORY}/${screenName}.tsx` + if (fs.existsSync(filePath)) { + logger.error(`Screen ${screenName} already exists`) + } +} + +/** + * Creates a screen file with the given screen name. + * @param {string} screenName - The name of the screen. + */ +const createScreenFile = (screenName: string) => { + const screenFromFile = fs.readFileSync('./templates/screen_template.tsx', 'utf8') + const screenContent = screenFromFile.replaceAll('_NAME_', screenName) + + fs.writeFileSync(`${SCREENS_DIRECTORY}/${screenName}.tsx`, screenContent) +} + +const addToScreensIndex = (screenName: string) => { + const indexFilePath = `${SCREENS_DIRECTORY}/index.ts` + const indexFile = fs.readFileSync(indexFilePath, 'utf8') + const newIndexFile = indexFile.padEnd(indexFile.length) + `export * from './${screenName}'\n` + + fs.writeFileSync(indexFilePath, newIndexFile) +} + +const promptTabName = async () => { + const promptAnswer = await prompt({ + message: 'What is your screen name?', + name: 'tabName', + type: 'input', + }) + + // @ts-expect-error: generator not found on promptAnswer + const tabName = promptAnswer.tabName as string + + if (!tabName) { + throw new Error('Tab name is required') + } + return tabName +} + +const createNewNavTab = (tabName: string) => { + const navigationConfigFile = fs.readFileSync( + './src/navigation/tabNavigator/navigation-config.ts', + 'utf8' + ) + + const tabContent = ` + { + displayedName: '${tabName.charAt(0).toUpperCase() + tabName.slice(1)}', + icon: 'zzz-line', // CONFIG: Add your icon name here + iconFocused: 'zzz-fill', // CONFIG: Add your icon name here + id: '${tabName}', + name: '${tabName}', + },` + + const newContent = addAfter(navigationConfigFile, '// UPPER SIDE TABS', tabContent) + fs.writeFileSync('./src/navigation/tabNavigator/navigation-config.ts', newContent) +} + +/** + * Generates a screen based on user input. + * Prompts the user to enter a screen name and selects a screen path. + * Validates the screen name and path. + */ +export const generateScreen = async () => { + const promptAnswer = await prompt({ + message: 'What is your screen name?', + name: 'screenName', + type: 'input', + }) + + // @ts-expect-error: generator not found on promptAnswer + const routeName = promptAnswer.screenName as string + + let routePath = await selectPath(APP_ROUTER_DIRECTORY) + + const isNewTab = routePath.includes('(tabs)') && routePath.includes('new-tab') + if (isNewTab) { + const tabName = promptTabName() + createNewNavTab(routeName) + + const newTabPath = routePath.replace('/new-tab', `/${tabName}`) + routePath = newTabPath + + fs.mkdirSync(newTabPath) + } + + validateRoute(routeName, routePath) + createRouteFile(routeName, routePath) + + const screenName = `${routeName.charAt(0).toUpperCase() + routeName.slice(1)}Screen` + validateScreen(screenName) + createScreenFile(screenName) + + addToScreensIndex(screenName) +} diff --git a/scripts/generate_theme.js b/scripts/cli/commands/generateTheme.ts similarity index 56% rename from scripts/generate_theme.js rename to scripts/cli/commands/generateTheme.ts index a0162168..72367247 100644 --- a/scripts/generate_theme.js +++ b/scripts/cli/commands/generateTheme.ts @@ -1,41 +1,44 @@ #!/usr/bin/env node +/* eslint-disable @typescript-eslint/no-explicit-any */ // eslint-disable-next-line @typescript-eslint/no-var-requires -const fs = require('fs') -const json = fs.readFileSync('./assets/figma/variables.json') +import fs from 'fs' + +const json = fs.readFileSync('./assets/figma/variables.json') as unknown as string const figmaVariables = JSON.parse(json) -const collections = figmaVariables.collections +const collections = figmaVariables.collections as any -const primitivesCollection = collections.find((collection) => collection.name === '_Primitives') -const colorsCollection = collections.find((collection) => collection.name === '1. Color modes') +const primitivesCollection = collections.find( + (collection: any) => collection.name === '_Primitives' +) +const colorsCollection = collections.find((collection: any) => collection.name === '1. Color modes') -const primitivesColors = primitivesCollection.modes - .find((mode) => mode.name === 'Style') - .variables.filter((variable) => variable.type === 'color') - .map((color) => ({ name: color.name, value: color.value })) +const primitivesColors = primitivesCollection?.modes + ?.find((mode: any) => mode.name === 'Style') + ?.variables.filter((variable: any) => variable.type === 'color') + ?.map((color: any) => ({ name: color.name, value: color.value })) // colorMode could be either 'light' or 'dark' -const getModeColors = (colorMode) => { +const getModeColors = (colorMode: string) => { const modeName = colorMode === 'light' ? 'Light mode' : 'Dark mode' - const modeColors = colorsCollection.modes.find((mode) => mode.name === modeName)?.variables + const modeColors = colorsCollection?.modes.find((mode: any) => mode.name === modeName)?.variables const colors = modeColors - .map((variable) => { + ?.map((variable: any) => { if (variable.isAlias) { - if (variable.value.collection === '_Primitives') { - const primitiveColor = primitivesColors.find( - (color) => color.name === variable.value.name - ) + const valueName = variable.value.name + if (variable?.value?.collection === '_Primitives') { + const primitiveColor = primitivesColors?.find((color: any) => color.name === valueName) if (primitiveColor) { return { name: variable.name, value: primitiveColor.value } } return null } else { - const newColor = modeColors.find((color) => color.name === variable.value.name) + const newColor = modeColors.find((color: any) => color.name === valueName) if (newColor) { - const primitiveColor = primitivesColors.find( - (color) => color.name === newColor.value.name + const primitiveColor = primitivesColors?.find( + (color: any) => color.name === newColor.value.name ) if (primitiveColor) { return { name: variable.name, value: primitiveColor.value } @@ -49,11 +52,12 @@ const getModeColors = (colorMode) => { }) .filter(Boolean) - const colorsArray = colors.map((color) => { - return { [color.name.split('/').pop().split(' ').shift() || '']: color.value } + const colorsArray = colors?.map((color: any) => { + const keyName = color?.name?.split?.('/')?.pop()?.split(' ').shift() || '' + return { [keyName]: color?.value } }) - const colorsInMode = colorsArray.reduceRight((acc, color) => { + const colorsInMode = colorsArray?.reduceRight((acc: any, color: any) => { const [entries] = Object.entries(color) const nestedKeys = entries[0].split('-') @@ -63,6 +67,10 @@ const getModeColors = (colorMode) => { return mergeObjects(acc, newValue) }, {}) + if (!colorsInMode) { + return + } + const sortedColors = sortObject(colorsInMode) return sortedColors } @@ -70,11 +78,11 @@ const getModeColors = (colorMode) => { const light = getModeColors('light') const dark = getModeColors('dark') -const primitivesColorsArray = primitivesColors.map((color) => { +const primitivesColorsArray = primitivesColors?.map((color: any) => { return { [color.name.split('/').slice(1).join('-')]: color.value } }) -const primitives = primitivesColorsArray.reduceRight((acc, color) => { +const primitives = primitivesColorsArray?.reduceRight((acc: any, color: any) => { const [entries] = Object.entries(color) const colorName = entries[0].split('_') @@ -94,18 +102,20 @@ const primitives = primitivesColorsArray.reduceRight((acc, color) => { const theme = { darkMode: dark, lightMode: light, - primitives: sortObject(primitives), + primitives: primitives ? sortObject(primitives) : undefined, } const objectString = `export const themeColors = ${JSON.stringify(theme, null, 2)}` -// Specify the file path -const filePath = './src/design-system/config/colors.ts' +export const generateTheme = () => { + // Specify the file path + const filePath = './src/design-system/config/colors.ts' -fs.writeFileSync(filePath, objectString, 'utf-8') + fs.writeFileSync(filePath, objectString, 'utf-8') +} // Utils -function createNestedObject(keys, value) { +function createNestedObject(keys: any[], value: any) { if (keys.length === 0) { return value } @@ -113,15 +123,15 @@ function createNestedObject(keys, value) { const key = keys[0] const remainingKeys = keys.slice(1) - const nestedObject = {} + const nestedObject: any = {} nestedObject[key] = createNestedObject(remainingKeys, value) return nestedObject } -function mergeObjects(obj1, obj2) { +function mergeObjects(obj1: any, obj2: any) { // Initialize the result object - const result = {} + const result: any = {} // Loop through keys in obj1 for (const key in obj1) { @@ -146,7 +156,7 @@ function mergeObjects(obj1, obj2) { return result } -function sortObject(obj) { +function sortObject(obj?: object): any { if (typeof obj !== 'object' || obj === null) { return obj } @@ -155,11 +165,12 @@ function sortObject(obj) { return obj.map(sortObject) } - const sorted = {} + const sorted: any = {} Object.keys(obj) .sort() .forEach((key) => { - sorted[key] = sortObject(obj[key]) + const objectToSort = (obj as any)[key] + sorted[key] = sortObject(objectToSort) }) return sorted } diff --git a/scripts/cli/commands/index.ts b/scripts/cli/commands/index.ts new file mode 100644 index 00000000..e738d13e --- /dev/null +++ b/scripts/cli/commands/index.ts @@ -0,0 +1,4 @@ +export * from './generateComponent' +export * from './generateIconTypes' +export * from './generateScreen' +export * from './generateTheme' diff --git a/scripts/cli/constants.ts b/scripts/cli/constants.ts new file mode 100644 index 00000000..7b35d2f6 --- /dev/null +++ b/scripts/cli/constants.ts @@ -0,0 +1,16 @@ +export const CLI_ACTIONS = ['generate', 'g', 'bootstrap', 'b'] + +export const APP_ROUTER_DIRECTORY = 'app/(app)' +export const SCREENS_DIRECTORY = 'src/screens' + +// Paths +export const NAVIGATION_CONFIG_PATH = 'navigation/tabNavigator/navigation-config.ts' +export const APP_JSON_PATH = 'app.json' +export const APP_CONFIG_PATH = 'app.config.ts' +export const README_PATH = 'README.md' + +export const COMPONENTS_PATH = 'src/components/index.ts' +export const COMPONENT_TEMPLATE_PATH = 'templates/component_template.tsx' +export const README_TEMPLATE_PATH = 'templates/README.md' +export const PULL_REQUEST_TEMPLATE_PATH = '.github/pull_request_template.md' +export const NEW_PULL_REQUEST_TEMPLATE_PATH = 'templates/pull_request_template.md' diff --git a/scripts/cli/index.ts b/scripts/cli/index.ts new file mode 100644 index 00000000..0e0aa4c6 --- /dev/null +++ b/scripts/cli/index.ts @@ -0,0 +1,51 @@ +// #!/usr/bin/env node +import { Command } from 'commander' + +import { actions } from './actions' +import { generators } from './actions/generate' + +class CommandWithTrace extends Command { + createCommand(name: string) { + const cmd = new CommandWithTrace(name) + // Add an option to subcommands created using `.command()` + cmd.option('-t, --trace', 'display extra information when run command') + return cmd + } +} + +function inpectCommand(command: Command) { + // The option value is stored as property on command because we called .storeOptionsAsProperties() + + console.log(command.helpInformation()) +} + +const program = new CommandWithTrace('baca').action((options, command) => { + inpectCommand(command) +}) + +const generateGroup = program + .command('generate [params...]') + .description('Run generators, please check `g -h` to check available generators') + .alias('g') + .action(() => { + actions.generate() + }) + +generators.forEach((generator) => { + generateGroup + .command(generator.value) + .description(generator.description) + .action(() => generator.command()) +}) + +program + .command('bootstrap') + .option('-s, --simple', 'Run bootstrap simple with needed values') + .description('Bootstrap of new project') + .alias('b') + .action((buildTarget) => { + const isSimple = buildTarget.simple ?? false + actions.bootstrap({ isSimple }) + }) + +program.parse() diff --git a/scripts/cli/tsconfig.cli.json b/scripts/cli/tsconfig.cli.json new file mode 100644 index 00000000..0aa0ebac --- /dev/null +++ b/scripts/cli/tsconfig.cli.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "noImplicitAny": false, + "outDir": "build", + "strict": true, + "lib": ["ESNext"], + "target": "es6", + "module": "commonjs", + "sourceMap": false, + "esModuleInterop": true, + "moduleResolution": "node", + "skipLibCheck": true, + "allowJs": false + }, + "include": ["../../app.config.ts", "**/*"] +} diff --git a/scripts/cli/utils/content.ts b/scripts/cli/utils/content.ts new file mode 100644 index 00000000..105bd1c0 --- /dev/null +++ b/scripts/cli/utils/content.ts @@ -0,0 +1,34 @@ +/** + * Adds the specified text after the first occurrence of the search text in the content. + * + * @param content - The original content string. + * @param searchText - The text to search for in the content. + * @param textToAdd - The text to add after the first occurrence of the search text. + * @returns The modified content string with the text added. + */ +export const addAfter = (content: string, searchText: string, textToAdd: string) => { + return content.replace(searchText, searchText + textToAdd) +} + +/** + * Adds the specified text before the first occurrence of the search text in the content. + * + * @param content - The original content string. + * @param searchText - The text to search for in the content. + * @param textToAdd - The text to add before the first occurrence of the search text. + * @returns The modified content string with the text added before the first occurrence of the search text. + */ +export const addBefore = (content: string, searchText: string, textToAdd: string) => { + return content.replace(searchText, textToAdd + searchText) +} + +/** + * Deletes all occurrences of a specified search text from the given content. + * + * @param content - The content from which to delete the search text. + * @param searchText - The text to be deleted from the content. + * @returns The updated content with all occurrences of the search text removed. + */ +export const deleteText = (content: string, searchText: string) => { + return content.replace(searchText, '') +} diff --git a/scripts/cli/utils/getDirectoryNames.ts b/scripts/cli/utils/getDirectoryNames.ts new file mode 100644 index 00000000..494b7ba0 --- /dev/null +++ b/scripts/cli/utils/getDirectoryNames.ts @@ -0,0 +1,28 @@ +import fs from 'fs' + +import { logger } from './logger' + +/** + * Retrieves the names of all sub-directories in the specified directory. + * + * @param directoryPath - The path of the directory to retrieve directory names from. + * @returns An array of directory names. + */ +export const getDirectoryNames = (directoryPath: string): string[] => { + try { + if (directoryPath.includes('new-tab')) return [] + // Read the contents of the directory + const contents = fs.readdirSync(directoryPath) + + // Filter out only directories + const folderNames = contents.filter((item) => { + const stat = fs.statSync(`${directoryPath}/${item}`) + return stat.isDirectory() + }) + + return folderNames + } catch (err) { + logger.error(`Error reading directory: ${err}`) + return [] + } +} diff --git a/scripts/cli/utils/index.ts b/scripts/cli/utils/index.ts new file mode 100644 index 00000000..b2d0878f --- /dev/null +++ b/scripts/cli/utils/index.ts @@ -0,0 +1,3 @@ +export * from './getDirectoryNames' +export * from './content' +export * from './logger' diff --git a/scripts/cli/utils/logger.ts b/scripts/cli/utils/logger.ts new file mode 100644 index 00000000..f55b9dd1 --- /dev/null +++ b/scripts/cli/utils/logger.ts @@ -0,0 +1,19 @@ +const LCERROR = '\x1b[31m%s\x1b[0m' //red +const LCWARN = '\x1b[33m%s\x1b[0m' //yellow +const LCINFO = '\x1b[36m%s\x1b[0m' //cyan +const LCSUCCESS = '\x1b[32m%s\x1b[0m' //green + +export const logger = { + error: (message: string, ...optionalParams: unknown[]) => { + console.error(LCERROR, message, ...optionalParams) + }, + warn: (message: string, ...optionalParams: unknown[]) => { + console.warn(LCWARN, message, ...optionalParams) + }, + info: (message: string, ...optionalParams: unknown[]) => { + console.info(LCINFO, message, ...optionalParams) + }, + success: (message: string, ...optionalParams: unknown[]) => { + console.info(LCSUCCESS, message, ...optionalParams) + }, +} diff --git a/scripts/create_new_screen.js b/scripts/create_new_screen.js deleted file mode 100644 index 04579139..00000000 --- a/scripts/create_new_screen.js +++ /dev/null @@ -1,318 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -const fs = require('fs') -const prompt = require('prompt-sync')() -const selectPrompt = require('select-prompt') - -const Content = require('./contents/content') -const { addAfter, addBefore, execPromise, logger } = require('./utils') - -const enumsFileSrc = './src/navigation/config/enums.ts' -const screensFileSrc = './src/navigation/config/screens.ts' -const tabsFileSrc = './src/navigation/config/tabs.ts' -const typesFileSrc = './src/navigation/config/navigation.d.ts' -const screensIndexFileSrc = './src/screens/index.ts' - -/** - * @param {string} name - */ -const validateScreen = (name) => { - const enumsFile = require('./temp/enums') - - Object.values(enumsFile).forEach((enumValue) => { - if (enumValue[name]) { - logger.error(`Screen with name ${name} already exists`) - process.exit(1) - } - }) -} - -/** - * @typedef {{ - * "tabs", - * "tabs_new", - * "root", - * }} TYPES - */ - -const createScreenFile = (name) => { - const screenFromFile = fs.readFileSync('./templates/screen_template.tsx', 'utf8') - const screenContent = screenFromFile - .replaceAll('_NAME_', name) - .replace("// @ts-expect-error: it's a template and will be removed", '') - - fs.writeFileSync(`./src/screens/${name}Screen.tsx`, screenContent) -} - -/** - * @param {string} name - * @param {Object} screenType - * @param {string} screenType.value - * @param {keyof TYPES} screenType.type - */ -const addToEnums = (name, screenType) => { - const enumsFile = require('./temp/enums') - const contents = fs.readFileSync(enumsFileSrc, 'utf8') - const StackScreens = - screenType.type === 'root' ? 'RootStackScreens' : screenType.value + 'Screens' - - // 3. a) LOGIC WHEN ADDING NEW TAB - if (screenType.type === 'tabs_new') { - let newContents = addAfter( - contents, - 'export const BottomTabsScreens = {', - Content.bottomTab(screenType.value) - ) - - newContents = addBefore( - newContents, - '// ExamplesStack_SCREENS', - Content.tabEnum(screenType.value, StackScreens, name) - ) - - fs.writeFileSync(enumsFileSrc, newContents) - return - } - - // 1. a), 2. a) LOGIC WHEN ADDING ONLY NEW SCREEN - ROOT STACK AND BOTTOM TABS - const startIdx = contents.indexOf(`export const ${StackScreens} = {`) - const endIdxString = '} as const' - const endIdx = contents.indexOf(endIdxString, startIdx) + endIdxString.length - const rootStackScreensData = enumsFile[StackScreens] - rootStackScreensData[name] = name - - // Convert the updated object back to a string - const updatedRootStackScreensDataStr = `export const ${StackScreens} = ${JSON.stringify( - rootStackScreensData, - null, - 2 - )} as const` - - // Write the updated contents back to the file - fs.writeFileSync( - enumsFileSrc, - contents.substring(0, startIdx) + updatedRootStackScreensDataStr + contents.substring(endIdx) - ) -} - -/** - * @param {string} name - * @param {Object} screenType - * @param {string} screenType.value - * @param {keyof TYPES} screenType.type - */ -const addToScreens = (name, screenType) => { - const screenNameType = screenType.type === 'root' ? 'RootStack' : screenType.value - const newScreen = Content.screenOptions(name, screenNameType) - let contents = - screenType.type === 'root' - ? fs.readFileSync(screensFileSrc, 'utf8') - : fs.readFileSync(tabsFileSrc, 'utf8') - - if (screenType.type === 'tabs_new') { - // 3. c) Add new tab to bottomTabs and create stack with screen - contents = addBefore(contents, '// StackEnums', Content.screenEnumImport(screenType.value)) - contents = addBefore(contents, "} from '@baca/screens'", Content.screenNameImport(name)) - contents = addBefore( - contents, - '// ExamplesStack_SCREENS_START', - Content.newTab(screenType.value, newScreen) - ) - contents = addBefore(contents, '// BottomTab_SCREENS_END', Content.tabOptions(screenType.value)) - } else { - // 1. c), 2. c) Add screen to specific group (screens or tabs) - const typeToSearch = `// ${screenType.value}_SCREENS_END` - - typeToSearch.replace(screenType.value, screenType.value) - contents = addBefore(contents, typeToSearch, newScreen) - contents = addBefore(contents, "} from '@baca/screens'", Content.screenNameImport(name)) - } - - const path = screenType.type === 'root' ? screensFileSrc : tabsFileSrc - fs.writeFileSync(path, contents) -} - -/** - * @param {string} name - * @param {Object} screenType - * @param {string} screenType.value - * @param {keyof TYPES} screenType.type - */ -const addToTypes = (name, screenType) => { - let contents = fs.readFileSync(typesFileSrc, 'utf8') - // 1. b) Add screen to navigation types - if (screenType.type === 'root') { - contents = addAfter(contents, `// Root_${screenType.value}`, Content.newAuthorizedScreen(name)) - contents = addAfter(contents, ' // RootStack_SCREENS', Content.screenComposite(name)) - } - // 2. b) Add screen to certain params list in navigation types - if (screenType.type === 'tabs') { - contents = addAfter( - contents, - `type ${screenType.value}ParamList = {`, - Content.newAuthorizedScreen(name) - ) - contents = addAfter(contents, ` // ${screenType.value}_SCREENS`, Content.screenComposite(name)) - } - - // 3. b) Add stack param list with new screen - if (screenType.type === 'tabs_new') { - contents = addAfter(contents, '// PARAMS', Content.bottomTabParamsList(screenType.value, name)) - contents = addBefore( - contents, - ' // BottomTabScreenProps END', - Content.bottomTabScreenProps(screenType.value) - ) - contents = addBefore( - contents, - ' ExamplesStackParamList) = keyof RootStackParamList', - Content.screenCompositeKey(screenType.value) - ) - contents = addAfter( - contents, - '> = StackScreenProps<', - Content.screenCompositeValue(screenType.value) - ) - contents = addBefore( - contents, - '// MainTabParamList END', - Content.navigatorScreenParams(screenType.value) - ) - contents = addBefore(contents, '// WebTabParamListEnd', Content.webTabBar(screenType.value)) - contents = addBefore( - contents, - ` // HomeStack_SCREENS`, - Content.newBottomTabScreenComposite(screenType.value, name) - ) - } - - fs.writeFileSync(typesFileSrc, contents) -} - -const addToIndex = (name) => { - const newExport = ` -export * from './${name}Screen'` - - const contents = fs.readFileSync(screensIndexFileSrc, 'utf8') - - fs.writeFileSync(screensIndexFileSrc, contents + newExport) -} - -const makeFirstLetterUppercase = (name) => { - return name.charAt(0).toUpperCase() + name.slice(1) -} - -/** - * @param {string} name - * @param {Object} screenType - * @param {string} screenType.value - * @param {keyof TYPES} screenType.type - */ -const generateScreen = async (name, screenType) => { - // Make the first letter of the screen name uppercase - const newScreenName = makeFirstLetterUppercase(name) - - // VALIDATE IF SCREEN NAME IS VALID - validateScreen(newScreenName) - - // GENERATE SCREEN FILE - logger.info('Generating screen files') - createScreenFile(newScreenName) - - // ADD SCREEN TO INDEX, ENUMS, SCREENS, TYPES - addToIndex(newScreenName) - addToEnums(newScreenName, screenType) - addToScreens(newScreenName, screenType) - addToTypes(newScreenName, screenType) - - // Remove temp files - logger.info('Removing temp files') - await execPromise('rm -rf ./scripts/temp') - - // FISNISH - logger.success(`Screen ${name} created successfully`) -} - -const generateNewScreen = async () => { - logger.info('Creating temp files files') - await execPromise(`yarn tsc ${enumsFileSrc} --outDir ./scripts/temp --skipLibCheck`) - - const rootVsBottomTabs = [ - { title: 'Root stack', value: 'root' }, - { title: 'Bottom tabs', value: 'tabs' }, - ] - - const rootScreenTypes = [ - { title: 'Authorized', value: 'authorized' }, - { title: 'Not authorized', value: 'unauthorized' }, - { title: 'Modal', value: 'modals' }, - { title: 'Normal - authorized and not authorized', value: 'normal' }, - ] - - const enumsFile = require('./temp/enums') - - const bottomTabsScreens = Object.keys(enumsFile.BottomTabsScreens) - const bottomTabsTypes = bottomTabsScreens - .map((tab) => ({ title: tab, value: tab })) - .concat({ title: 'New tab', value: '_new' }) - - selectPrompt('Do you want this screen on root stack or on bottom tabs?', rootVsBottomTabs, { - cursor: 0, - }).on('submit', async (stackType) => { - if (stackType === 'root') { - selectPrompt('Select what type is this screen', rootScreenTypes, { - cursor: 1, - }).on('submit', async (screenType) => { - const name = prompt('What is screen name? ') - if (!name) { - return logger.error('No screen name passed') - } - // 1. NEW root screen -> screen_name + screen_type (authorized | not_authorized | modal | normal) - generateScreen(name, { type: 'root', value: screenType }) - }) - } - - if (stackType === 'tabs') { - selectPrompt('Select what type is this screen', bottomTabsTypes, { - cursor: 1, - }).on('submit', async (screenType) => { - const name = prompt('What is screen name? ') - if (!name) { - return console.log('No screen name passed') - } - - // Logic when adding new tab - if (screenType === '_new') { - const bottomTabName = prompt('What is bottom tab name? ') - // 2. New bottom tab screen - screen_name + bottom_tab_name - generateScreen(name, { type: 'tabs_new', value: bottomTabName + 'Stack' }) - return - } - - // 3. New bottom tab => screen_name + bottom_tab_name - generateScreen(name, { type: 'tabs', value: screenType }) - }) - } - }) -} - -generateNewScreen() - -// INSTRUCTION -// 0. Common -// a) index.ts -> export -// b) screen_name.ts -> screen_name component - -// 1. NEW root screen -> screen_name + screen_type (authorized | not_authorized | modal | normal) -// a) enums.ts -> const RootStackScreens = { -// b) navigation.d.ts -> type RootStackParamList = { -// c) screens.ts -> import + rootStackScreensData.authorized - -// 2. New bottom tab screen - screen_name + bottom_tab_name (ExampleStack) -// a) enums.ts -> const {bottom_tab_name}Screens = { -// b) navigation.d.ts -> type {bottom_tab_name}ParamList = { -// c) tabs.ts -> import + // HomeStack_SCREENS_END (before) - -// 3. New bottom tab => screen_name + bottom_tab_name -// a) enums.ts -> const {bottom_tab_name}StackScreens = { + export const BottomTabsScreens = { (after) -// b) navigation.d.ts -> type {bottom_tab_name}ParamList = { -// c) tabs.ts -> import + // ${bottom_tab_name}_SCREENS_END (before) diff --git a/scripts/generate_icon_types.js b/scripts/generate_icon_types.js deleted file mode 100644 index aaa50f06..00000000 --- a/scripts/generate_icon_types.js +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env node - -// eslint-disable-next-line @typescript-eslint/no-var-requires -const fs = require('fs') - -const prefix = `export type IconNames = - | ` -const json = fs.readFileSync('./assets/icomoon/selection.json') - -const types = JSON.parse(json) - .icons.map((icon) => `'${icon.properties.name}'`) - .join('\n | ') - .concat('\n') - -const content = prefix + types - -fs.writeFileSync('./src/types/icon.d.ts', content) diff --git a/src/components/README.md b/src/components/README.md index 3a7d493b..e2f39956 100644 --- a/src/components/README.md +++ b/src/components/README.md @@ -1,6 +1,6 @@ # Components -Components in [expo-ts-template](https://github.com/binarapps/expo-ts-template) follows [atomic design methodology](https://atomicdesign.bradfrost.com/). +Components in [baca-react-native-template](https://github.com/binarapps/baca-react-native-template) follows [atomic design methodology](https://atomicdesign.bradfrost.com/). For more information on such a design methodology, visit the page above. On the other hand, below you can find documentation of the implemented components diff --git a/src/navigation/tabNavigator/navigation-config.ts b/src/navigation/tabNavigator/navigation-config.ts index 1ca342d7..9fc6862e 100644 --- a/src/navigation/tabNavigator/navigation-config.ts +++ b/src/navigation/tabNavigator/navigation-config.ts @@ -12,6 +12,7 @@ type Tabs = Tab[] // name with '/' at the begging will not be resolved as 'bottom tab', will be as usual screen export const upperSideTabs: Tabs = [ + // UPPER SIDE TABS { displayedName: 'Home', icon: 'home-5-line', @@ -47,7 +48,6 @@ export const upperSideTabs: Tabs = [ id: 'profile', name: 'profile', }, - // In case you want to navigate to screen with params you can do this like this // { // displayedName: 'Details', diff --git a/src/types/icon.d.ts b/src/types/icon.d.ts index 1da43419..1de1dbb9 100644 --- a/src/types/icon.d.ts +++ b/src/types/icon.d.ts @@ -1,8 +1,3 @@ -/** - * @see https://remixicon.com/ - * You can find visually all icons that you need - */ - export type IconNames = | 'layout-right-2-line' | 'drag-drop-line' diff --git a/templates/readme_template.md b/templates/readme_template.md index aaf2a759..2e779565 100644 --- a/templates/readme_template.md +++ b/templates/readme_template.md @@ -9,26 +9,21 @@ Version in the `package.json` is one to one the latest expo on which the templat #### 1. Bootstrap - At start you should trigger script ``` -node ./scripts/bootstrap.js +yarn b ``` The script gives you opportunity to setup the essentialest variables for your project like app name, bundle ID or android package name -#### 2. Generate new screen +#### 2. Generators ``` -node ./scripts/create_new_screen.js +yarn g ``` -You can use this script for generate new common screen, tabs screen or new bottom tab, from screen template. You have possiblity to change screen template for your own. +Usage: -#### 3. Generate new component - -``` -node ./scripts/create_new_component.js -``` - -You can use this script for generate new common component (atom, molecule, organism, common) from component template. You have possiblity to change component template for your own. +- You can use this script to generate new common screen, tabs screen or new bottom tab, from screen template. You have possiblity to change screen template for your own. +- You can use this script for generate new common component (atom, molecule, organism, common) from component template. You have possiblity to change component template for your own. ## Run Locally @@ -89,14 +84,16 @@ It is added to the app as a font generated by [icomoon app](https://icomoon.io/a 9. Generate new types for icons -- run script generating icon types `yarn generate:icon:types` +- run script generating icon types `yarn g icon-types` and pick icon types + + ## Sync up with template: 1. Add template remote ```bash -git remote add template git@github.com:binarapps/expo-ts-template.git +git remote add template git@github.com:binarapps/baca-react-native-template.git ``` 2. Go to new branch (for safety reason) @@ -128,7 +125,7 @@ git commit -m "chore: sync up with template code" ```