From 1066adc8d05f8a4de31a0ad661cdc980799b600f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sza=C5=82owski?= Date: Sat, 9 Mar 2024 13:53:06 +0100 Subject: [PATCH 01/47] feat: init baca cli --- .eslintignore | 2 +- package.json | 1 + scripts/cli/actions/bootstrap.ts | 3 + scripts/cli/actions/generate.ts | 39 ++++++++++ scripts/cli/actions/index.ts | 19 +++++ scripts/cli/build/actions/bootstrap.js | 7 ++ scripts/cli/build/actions/generate.js | 76 +++++++++++++++++++ scripts/cli/build/actions/index.js | 20 +++++ scripts/cli/build/actions/parseAction.js | 11 +++ .../cli/build/commands/generate.command.js | 1 + .../cli/build/commands/generateIconTypes.js | 22 ++++++ scripts/cli/build/constants/index.js | 4 + scripts/cli/build/index.js | 34 +++++++++ scripts/cli/build/types/index.js | 2 + scripts/cli/commands/generateIconTypes.ts | 17 +++++ scripts/cli/commands/generateScreen.ts | 3 + scripts/cli/commands/index.ts | 2 + scripts/cli/constants/index.ts | 1 + scripts/cli/index.ts | 14 ++++ scripts/cli/tsconfig.cli.json | 13 ++++ scripts/cli/types/index.ts | 2 + src/types/icon.d.ts | 5 -- yarn.lock | 5 ++ 23 files changed, 297 insertions(+), 6 deletions(-) create mode 100644 scripts/cli/actions/bootstrap.ts create mode 100644 scripts/cli/actions/generate.ts create mode 100644 scripts/cli/actions/index.ts create mode 100644 scripts/cli/build/actions/bootstrap.js create mode 100644 scripts/cli/build/actions/generate.js create mode 100644 scripts/cli/build/actions/index.js create mode 100644 scripts/cli/build/actions/parseAction.js create mode 100644 scripts/cli/build/commands/generate.command.js create mode 100644 scripts/cli/build/commands/generateIconTypes.js create mode 100644 scripts/cli/build/constants/index.js create mode 100644 scripts/cli/build/index.js create mode 100644 scripts/cli/build/types/index.js create mode 100644 scripts/cli/commands/generateIconTypes.ts create mode 100644 scripts/cli/commands/generateScreen.ts create mode 100644 scripts/cli/commands/index.ts create mode 100644 scripts/cli/constants/index.ts create mode 100644 scripts/cli/index.ts create mode 100644 scripts/cli/tsconfig.cli.json create mode 100644 scripts/cli/types/index.ts 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/package.json b/package.json index e636b4da..a718c382 100644 --- a/package.json +++ b/package.json @@ -172,6 +172,7 @@ "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", "eslint": "^8.56.0", diff --git a/scripts/cli/actions/bootstrap.ts b/scripts/cli/actions/bootstrap.ts new file mode 100644 index 00000000..419e225e --- /dev/null +++ b/scripts/cli/actions/bootstrap.ts @@ -0,0 +1,3 @@ +export const bootstrap = () => { + console.log('Bootstrap') +} diff --git a/scripts/cli/actions/generate.ts b/scripts/cli/actions/generate.ts new file mode 100644 index 00000000..eeab04f3 --- /dev/null +++ b/scripts/cli/actions/generate.ts @@ -0,0 +1,39 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import selectPrompt from 'select-prompt' + +import { generateIconTypes } from '../commands/generateIconTypes' + +const generatePrompts = [ + { + title: 'Screen', + value: 'screen', + }, + { + title: 'Component', + value: 'component', + }, + { + title: 'Icon types', + value: 'icon-types', + }, +] + +const commands = { + screen: () => { + console.log('Generate Screen') + }, + component: () => { + console.log('Generate Component') + }, + 'icon-types': generateIconTypes, +} + +export const generate = () => { + selectPrompt('What do you want to generate?', generatePrompts).on( + 'submit', + async (value: keyof typeof commands) => { + commands[value]() + } + ) +} diff --git a/scripts/cli/actions/index.ts b/scripts/cli/actions/index.ts new file mode 100644 index 00000000..a089e1eb --- /dev/null +++ b/scripts/cli/actions/index.ts @@ -0,0 +1,19 @@ +import { bootstrap } from './bootstrap' +import { generate } from './generate' +import { CLI_ACTIONS } from '../constants' +import { CliActions } from '../types' + +const actions = { + generate, + g: generate, + bootstrap, + b: bootstrap, +} + +export const parseAction = (action: CliActions) => { + if (!CLI_ACTIONS.includes(action)) { + console.error('\x1b[31m%s\x1b[0m', `Invalid action ${action}`) + return + } + actions[action]() +} diff --git a/scripts/cli/build/actions/bootstrap.js b/scripts/cli/build/actions/bootstrap.js new file mode 100644 index 00000000..99a3125d --- /dev/null +++ b/scripts/cli/build/actions/bootstrap.js @@ -0,0 +1,7 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +exports.bootstrap = void 0 +const bootstrap = () => { + console.log('Bootstrap') +} +exports.bootstrap = bootstrap diff --git a/scripts/cli/build/actions/generate.js b/scripts/cli/build/actions/generate.js new file mode 100644 index 00000000..79265a0b --- /dev/null +++ b/scripts/cli/build/actions/generate.js @@ -0,0 +1,76 @@ +'use strict' +var __awaiter = + (this && this.__awaiter) || + function (thisArg, _arguments, P, generator) { + function adopt(value) { + return value instanceof P + ? value + : new P(function (resolve) { + resolve(value) + }) + } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { + try { + step(generator.next(value)) + } catch (e) { + reject(e) + } + } + function rejected(value) { + try { + step(generator['throw'](value)) + } catch (e) { + reject(e) + } + } + function step(result) { + result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected) + } + step((generator = generator.apply(thisArg, _arguments || [])).next()) + }) + } +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod } + } +Object.defineProperty(exports, '__esModule', { value: true }) +exports.generate = void 0 +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +const select_prompt_1 = __importDefault(require('select-prompt')) +const generateIconTypes_1 = require('../commands/generateIconTypes') +const generatePrompts = [ + { + title: 'Screen', + value: 'screen', + }, + { + title: 'Component', + value: 'component', + }, + { + title: 'Icon types', + value: 'icon-types', + }, +] +const commands = { + screen: () => { + console.log('Generate Screen') + }, + component: () => { + console.log('Generate Component') + }, + 'icon-types': generateIconTypes_1.generateIconTypes, +} +const generate = () => { + ;(0, select_prompt_1.default)('What do you want to generate?', generatePrompts).on( + 'submit', + (value) => + __awaiter(void 0, void 0, void 0, function* () { + commands[value]() + }) + ) +} +exports.generate = generate diff --git a/scripts/cli/build/actions/index.js b/scripts/cli/build/actions/index.js new file mode 100644 index 00000000..9a92f899 --- /dev/null +++ b/scripts/cli/build/actions/index.js @@ -0,0 +1,20 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +exports.parseAction = void 0 +const bootstrap_1 = require('./bootstrap') +const generate_1 = require('./generate') +const constants_1 = require('../constants') +const actions = { + generate: generate_1.generate, + g: generate_1.generate, + bootstrap: bootstrap_1.bootstrap, + b: bootstrap_1.bootstrap, +} +const parseAction = (action) => { + if (!constants_1.CLI_ACTIONS.includes(action)) { + console.error('\x1b[31m%s\x1b[0m', `Invalid action ${action}`) + return + } + actions[action]() +} +exports.parseAction = parseAction diff --git a/scripts/cli/build/actions/parseAction.js b/scripts/cli/build/actions/parseAction.js new file mode 100644 index 00000000..b2439ccd --- /dev/null +++ b/scripts/cli/build/actions/parseAction.js @@ -0,0 +1,11 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +exports.parseAction = void 0 +const constants_1 = require('../constants') +const parseAction = (action) => { + if (!constants_1.CLI_ACTIONS.includes(action)) { + console.error('\x1b[31m%s\x1b[0m', `Invalid action ${action}`) + return + } +} +exports.parseAction = parseAction diff --git a/scripts/cli/build/commands/generate.command.js b/scripts/cli/build/commands/generate.command.js new file mode 100644 index 00000000..ccacec30 --- /dev/null +++ b/scripts/cli/build/commands/generate.command.js @@ -0,0 +1 @@ +'use strict' diff --git a/scripts/cli/build/commands/generateIconTypes.js b/scripts/cli/build/commands/generateIconTypes.js new file mode 100644 index 00000000..bb8c96c2 --- /dev/null +++ b/scripts/cli/build/commands/generateIconTypes.js @@ -0,0 +1,22 @@ +'use strict' +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod } + } +Object.defineProperty(exports, '__esModule', { value: true }) +exports.generateIconTypes = void 0 +const fs_1 = __importDefault(require('fs')) +const prefix = `export type IconNames = + | ` +const generateIconTypes = () => { + const json = fs_1.default.readFileSync('./assets/icomoon/selection.json') + const types = JSON.parse(json.toString()) + .icons.map((icon) => `'${icon.properties.name}'`) + .join('\n | ') + .concat('\n') + const content = prefix + types + fs_1.default.writeFileSync('./src/types/icon.d.ts', content) + console.log('Icon types generated') +} +exports.generateIconTypes = generateIconTypes diff --git a/scripts/cli/build/constants/index.js b/scripts/cli/build/constants/index.js new file mode 100644 index 00000000..37e91333 --- /dev/null +++ b/scripts/cli/build/constants/index.js @@ -0,0 +1,4 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +exports.CLI_ACTIONS = void 0 +exports.CLI_ACTIONS = ['generate', 'g', 'bootstrap', 'b'] diff --git a/scripts/cli/build/index.js b/scripts/cli/build/index.js new file mode 100644 index 00000000..389511b8 --- /dev/null +++ b/scripts/cli/build/index.js @@ -0,0 +1,34 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +const commander_1 = require('commander') +const actions_1 = require('./actions') +const program = new commander_1.Command() +program + .argument('') + .option('-d, --debug', 'Set the debug level') + .action((action, options, command) => { + ;(0, actions_1.parseAction)(action) + // TODO: Parse all actions + // Start with generate + // console.log({ action, options, command }) + // if (!['g', 'generate'].includes(action)) { + // console.log({ action }) + // throw new Error('Invalid action') + // } + // if (options.debug) { + // console.error('Called %s with options %o', command.name(), options) + // } + // selectPrompt('What do you want to generate?', [ + // { + // title: 'Screen', + // value: 'screen', + // }, + // { + // title: 'Component', + // value: 'component', + // }, + // ]).on('submit', async (value: string) => { + // console.log(`You want me to generate a ${value}!`) + // }) + }) +program.parse() diff --git a/scripts/cli/build/types/index.js b/scripts/cli/build/types/index.js new file mode 100644 index 00000000..0a483a32 --- /dev/null +++ b/scripts/cli/build/types/index.js @@ -0,0 +1,2 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) 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..fc248719 --- /dev/null +++ b/scripts/cli/commands/generateScreen.ts @@ -0,0 +1,3 @@ +export const generateScreen = () => { + console.log('generateScreen') +} diff --git a/scripts/cli/commands/index.ts b/scripts/cli/commands/index.ts new file mode 100644 index 00000000..a20c51e2 --- /dev/null +++ b/scripts/cli/commands/index.ts @@ -0,0 +1,2 @@ +export * from './generateIconTypes' +export * from './generateScreen' diff --git a/scripts/cli/constants/index.ts b/scripts/cli/constants/index.ts new file mode 100644 index 00000000..31fd1f28 --- /dev/null +++ b/scripts/cli/constants/index.ts @@ -0,0 +1 @@ +export const CLI_ACTIONS = ['generate', 'g', 'bootstrap', 'b'] diff --git a/scripts/cli/index.ts b/scripts/cli/index.ts new file mode 100644 index 00000000..2efb2a2e --- /dev/null +++ b/scripts/cli/index.ts @@ -0,0 +1,14 @@ +import { Command } from 'commander' + +import { parseAction } from './actions' + +const program = new Command() + +program + .argument('') + .option('-d, --debug', 'Set the debug level') + .action((action, options, command) => { + parseAction(action) + }) + +program.parse() diff --git a/scripts/cli/tsconfig.cli.json b/scripts/cli/tsconfig.cli.json new file mode 100644 index 00000000..5fb1b807 --- /dev/null +++ b/scripts/cli/tsconfig.cli.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "rootDir": ".", + "outDir": "build", + "strict": true, + "target": "es6", + "module": "commonjs", + "sourceMap": false, + "esModuleInterop": true, + "moduleResolution": "node", + "skipLibCheck": true + } +} diff --git a/scripts/cli/types/index.ts b/scripts/cli/types/index.ts new file mode 100644 index 00000000..dbaa4ee3 --- /dev/null +++ b/scripts/cli/types/index.ts @@ -0,0 +1,2 @@ +// TODO: get the union from CLI_ACTIONS const +export type CliActions = 'generate' | 'g' | 'bootstrap' | 'b' 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/yarn.lock b/yarn.lock index a994f169..7b7c66ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5456,6 +5456,11 @@ commander@2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== +commander@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.0.0.tgz#b929db6df8546080adfd004ab215ed48cf6f2592" + integrity sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA== + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" From 8100efff0cefbfaf67cf94c0a04f04c928231788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sza=C5=82owski?= Date: Sat, 9 Mar 2024 16:22:51 +0100 Subject: [PATCH 02/47] feat: add screen generator --- package.json | 1 + scripts/cli/actions/generate.ts | 5 +- scripts/cli/actions/index.ts | 5 + scripts/cli/build/actions/generate.js | 5 +- scripts/cli/build/actions/index.js | 5 + .../cli/build/commands/generateIconTypes.js | 1 - scripts/cli/build/commands/generateScreen.js | 162 ++++++++++++++++++ scripts/cli/build/commands/index.js | 31 ++++ scripts/cli/build/constants/index.js | 9 +- scripts/cli/build/index.js | 22 --- scripts/cli/build/utils/getDirectoryNames.js | 31 ++++ scripts/cli/build/utils/getFolderNames.js | 31 ++++ scripts/cli/build/utils/index.js | 30 ++++ scripts/cli/commands/generateScreen.ts | 134 ++++++++++++++- scripts/cli/constants/index.ts | 4 + scripts/cli/tsconfig.cli.json | 1 + scripts/cli/utils/getDirectoryNames.ts | 25 +++ scripts/cli/utils/index.ts | 1 + templates/screen_template.tsx | 17 +- yarn.lock | 5 + 20 files changed, 479 insertions(+), 46 deletions(-) create mode 100644 scripts/cli/build/commands/generateScreen.js create mode 100644 scripts/cli/build/commands/index.js create mode 100644 scripts/cli/build/utils/getDirectoryNames.js create mode 100644 scripts/cli/build/utils/getFolderNames.js create mode 100644 scripts/cli/build/utils/index.js create mode 100644 scripts/cli/utils/getDirectoryNames.ts create mode 100644 scripts/cli/utils/index.ts diff --git a/package.json b/package.json index a718c382..23e05e34 100644 --- a/package.json +++ b/package.json @@ -163,6 +163,7 @@ "@testing-library/jest-native": "^5.4.2", "@testing-library/react-native": "^12.4.3", "@types/jest": "^29.5.3", + "@types/prompt-sync": "^4.2.3", "@types/react": "~18.2.45", "@types/react-native": "^0.72.2", "@types/react-test-renderer": "^18.0.0", diff --git a/scripts/cli/actions/generate.ts b/scripts/cli/actions/generate.ts index eeab04f3..093ed69f 100644 --- a/scripts/cli/actions/generate.ts +++ b/scripts/cli/actions/generate.ts @@ -3,6 +3,7 @@ import selectPrompt from 'select-prompt' import { generateIconTypes } from '../commands/generateIconTypes' +import { generateScreen } from '../commands/generateScreen' const generatePrompts = [ { @@ -20,9 +21,7 @@ const generatePrompts = [ ] const commands = { - screen: () => { - console.log('Generate Screen') - }, + screen: generateScreen, component: () => { console.log('Generate Component') }, diff --git a/scripts/cli/actions/index.ts b/scripts/cli/actions/index.ts index a089e1eb..6b025266 100644 --- a/scripts/cli/actions/index.ts +++ b/scripts/cli/actions/index.ts @@ -10,6 +10,11 @@ const actions = { b: bootstrap, } +/** + * Parses and executes the specified CLI action. + * + * @param action - The CLI action to parse and execute. + */ export const parseAction = (action: CliActions) => { if (!CLI_ACTIONS.includes(action)) { console.error('\x1b[31m%s\x1b[0m', `Invalid action ${action}`) diff --git a/scripts/cli/build/actions/generate.js b/scripts/cli/build/actions/generate.js index 79265a0b..12d8d7fa 100644 --- a/scripts/cli/build/actions/generate.js +++ b/scripts/cli/build/actions/generate.js @@ -41,6 +41,7 @@ exports.generate = void 0 // @ts-ignore const select_prompt_1 = __importDefault(require('select-prompt')) const generateIconTypes_1 = require('../commands/generateIconTypes') +const generateScreen_1 = require('../commands/generateScreen') const generatePrompts = [ { title: 'Screen', @@ -56,9 +57,7 @@ const generatePrompts = [ }, ] const commands = { - screen: () => { - console.log('Generate Screen') - }, + screen: generateScreen_1.generateScreen, component: () => { console.log('Generate Component') }, diff --git a/scripts/cli/build/actions/index.js b/scripts/cli/build/actions/index.js index 9a92f899..cc412521 100644 --- a/scripts/cli/build/actions/index.js +++ b/scripts/cli/build/actions/index.js @@ -10,6 +10,11 @@ const actions = { bootstrap: bootstrap_1.bootstrap, b: bootstrap_1.bootstrap, } +/** + * Parses and executes the specified CLI action. + * + * @param action - The CLI action to parse and execute. + */ const parseAction = (action) => { if (!constants_1.CLI_ACTIONS.includes(action)) { console.error('\x1b[31m%s\x1b[0m', `Invalid action ${action}`) diff --git a/scripts/cli/build/commands/generateIconTypes.js b/scripts/cli/build/commands/generateIconTypes.js index bb8c96c2..9717bc93 100644 --- a/scripts/cli/build/commands/generateIconTypes.js +++ b/scripts/cli/build/commands/generateIconTypes.js @@ -17,6 +17,5 @@ const generateIconTypes = () => { .concat('\n') const content = prefix + types fs_1.default.writeFileSync('./src/types/icon.d.ts', content) - console.log('Icon types generated') } exports.generateIconTypes = generateIconTypes diff --git a/scripts/cli/build/commands/generateScreen.js b/scripts/cli/build/commands/generateScreen.js new file mode 100644 index 00000000..ad4fcad1 --- /dev/null +++ b/scripts/cli/build/commands/generateScreen.js @@ -0,0 +1,162 @@ +'use strict' +var __awaiter = + (this && this.__awaiter) || + function (thisArg, _arguments, P, generator) { + function adopt(value) { + return value instanceof P + ? value + : new P(function (resolve) { + resolve(value) + }) + } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { + try { + step(generator.next(value)) + } catch (e) { + reject(e) + } + } + function rejected(value) { + try { + step(generator['throw'](value)) + } catch (e) { + reject(e) + } + } + function step(result) { + result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected) + } + step((generator = generator.apply(thisArg, _arguments || [])).next()) + }) + } +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod } + } +Object.defineProperty(exports, '__esModule', { value: true }) +exports.generateScreen = void 0 +const fs_1 = __importDefault(require('fs')) +const prompt_sync_1 = __importDefault(require('prompt-sync')) +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +const select_prompt_1 = __importDefault(require('select-prompt')) +const constants_1 = require('../constants') +const utils_1 = require('../utils') +/** + * 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 = (basePath) => + new Promise((resolve) => { + let result = basePath + const subDirectories = (0, utils_1.getDirectoryNames)(basePath) + if (subDirectories.length) { + const subDirectoryPrompt = subDirectories.map((directoryName) => ({ + title: directoryName, + value: directoryName, + })) + if (basePath !== constants_1.APP_ROUTER_DIRECTORY) { + subDirectoryPrompt.unshift({ title: '.', value: '.' }) + } + ;(0, select_prompt_1.default)( + `Select a directory (${basePath.split(process.cwd())[1]})`, + subDirectoryPrompt, + { + cursor: 0, + } + ).on('submit', (subValue) => { + if (subValue === '.') { + result = basePath + resolve(result) // Return the result when subValue is '.' + return + } + selectPath(`${basePath}/${subValue}`).then((subResult) => { + result = subResult + resolve(result) // Return the result when subValue is not '.' + }) + }) + } else { + resolve(result) // Return the result when there are no subdirectories + } + }) +/** + * 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, routePath) => { + const filePath = `${routePath}/${routeName}.tsx` + if (fs_1.default.existsSync(filePath)) { + throw new 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, routePath) => { + // TODO: Check if its the tab - if so - add under new directory + const screenName = `${routeName.charAt(0).toUpperCase() + routeName.slice(1)}Screen` + fs_1.default.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) => { + const filePath = `${constants_1.SCREENS_DIRECTORY}/${screenName}.tsx` + if (fs_1.default.existsSync(filePath)) { + throw new Error(`Screen ${screenName} already exists in ${constants_1.SCREENS_DIRECTORY}`) + } +} +/** + * Creates a screen file with the given screen name. + * @param {string} screenName - The name of the screen. + */ +const createScreenFile = (baseName) => { + const screenName = `${baseName.charAt(0).toUpperCase() + baseName.slice(1)}Screen` + const screenFromFile = fs_1.default.readFileSync('./templates/screen_template.tsx', 'utf8') + const screenContent = screenFromFile + .replaceAll('_NAME_', screenName) + .replace("// @ts-expect-error: it's a template and will be removed", '') + fs_1.default.writeFileSync(`${constants_1.SCREENS_DIRECTORY}/${screenName}.tsx`, screenContent, { + encoding: 'utf-8', + flag: 'w', + }) +} +const addToScreensIndex = (baseName) => { + const screenName = `${baseName.charAt(0).toUpperCase() + baseName.slice(1)}Screen` + const indexFilePath = `${constants_1.SCREENS_DIRECTORY}/index.ts` + const indexFile = fs_1.default.readFileSync(indexFilePath, 'utf8') + const newIndexFile = indexFile.padEnd(indexFile.length) + `export * from './${screenName}'\n` + console.log({ indexFile, newIndexFile }) + fs_1.default.writeFileSync(indexFilePath, newIndexFile) +} +/** + * 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. + */ +const generateScreen = () => + __awaiter(void 0, void 0, void 0, function* () { + const screenName = (0, prompt_sync_1.default)()('Enter screen name: ') + const routePath = yield selectPath(constants_1.APP_ROUTER_DIRECTORY) + validateRoute(screenName, routePath) + createRouteFile(screenName, routePath) + validateScreen(screenName) + createScreenFile(screenName) + addToScreensIndex(screenName) + }) +exports.generateScreen = generateScreen diff --git a/scripts/cli/build/commands/index.js b/scripts/cli/build/commands/index.js new file mode 100644 index 00000000..c38b9d85 --- /dev/null +++ b/scripts/cli/build/commands/index.js @@ -0,0 +1,31 @@ +'use strict' +var __createBinding = + (this && this.__createBinding) || + (Object.create + ? function (o, m, k, k2) { + if (k2 === undefined) k2 = k + var desc = Object.getOwnPropertyDescriptor(m, k) + if (!desc || ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { + enumerable: true, + get: function () { + return m[k] + }, + } + } + Object.defineProperty(o, k2, desc) + } + : function (o, m, k, k2) { + if (k2 === undefined) k2 = k + o[k2] = m[k] + }) +var __exportStar = + (this && this.__exportStar) || + function (m, exports) { + for (var p in m) + if (p !== 'default' && !Object.prototype.hasOwnProperty.call(exports, p)) + __createBinding(exports, m, p) + } +Object.defineProperty(exports, '__esModule', { value: true }) +__exportStar(require('./generateIconTypes'), exports) +__exportStar(require('./generateScreen'), exports) diff --git a/scripts/cli/build/constants/index.js b/scripts/cli/build/constants/index.js index 37e91333..3b00dc1b 100644 --- a/scripts/cli/build/constants/index.js +++ b/scripts/cli/build/constants/index.js @@ -1,4 +1,11 @@ 'use strict' Object.defineProperty(exports, '__esModule', { value: true }) -exports.CLI_ACTIONS = void 0 +exports.NAVIGATION_CONFIG_PATH = + exports.SCREENS_DIRECTORY = + exports.APP_ROUTER_DIRECTORY = + exports.CLI_ACTIONS = + void 0 exports.CLI_ACTIONS = ['generate', 'g', 'bootstrap', 'b'] +exports.APP_ROUTER_DIRECTORY = `${process.cwd()}/app/(app)` +exports.SCREENS_DIRECTORY = `${process.cwd()}/src/screens` +exports.NAVIGATION_CONFIG_PATH = `${process.cwd()}/navigation/tabNavigator/navigation-config.ts` diff --git a/scripts/cli/build/index.js b/scripts/cli/build/index.js index 389511b8..1b59c541 100644 --- a/scripts/cli/build/index.js +++ b/scripts/cli/build/index.js @@ -8,27 +8,5 @@ program .option('-d, --debug', 'Set the debug level') .action((action, options, command) => { ;(0, actions_1.parseAction)(action) - // TODO: Parse all actions - // Start with generate - // console.log({ action, options, command }) - // if (!['g', 'generate'].includes(action)) { - // console.log({ action }) - // throw new Error('Invalid action') - // } - // if (options.debug) { - // console.error('Called %s with options %o', command.name(), options) - // } - // selectPrompt('What do you want to generate?', [ - // { - // title: 'Screen', - // value: 'screen', - // }, - // { - // title: 'Component', - // value: 'component', - // }, - // ]).on('submit', async (value: string) => { - // console.log(`You want me to generate a ${value}!`) - // }) }) program.parse() diff --git a/scripts/cli/build/utils/getDirectoryNames.js b/scripts/cli/build/utils/getDirectoryNames.js new file mode 100644 index 00000000..667950de --- /dev/null +++ b/scripts/cli/build/utils/getDirectoryNames.js @@ -0,0 +1,31 @@ +'use strict' +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod } + } +Object.defineProperty(exports, '__esModule', { value: true }) +exports.getDirectoryNames = void 0 +const fs_1 = __importDefault(require('fs')) +/** + * 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. + */ +const getDirectoryNames = (directoryPath) => { + try { + // Read the contents of the directory + const contents = fs_1.default.readdirSync(directoryPath) + // Filter out only directories + const folderNames = contents.filter((item) => { + const stat = fs_1.default.statSync(`${directoryPath}/${item}`) + return stat.isDirectory() + }) + return folderNames + } catch (err) { + console.error(`Error reading directory: ${err}`) + return [] + } +} +exports.getDirectoryNames = getDirectoryNames diff --git a/scripts/cli/build/utils/getFolderNames.js b/scripts/cli/build/utils/getFolderNames.js new file mode 100644 index 00000000..78b9895b --- /dev/null +++ b/scripts/cli/build/utils/getFolderNames.js @@ -0,0 +1,31 @@ +'use strict' +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod } + } +Object.defineProperty(exports, '__esModule', { value: true }) +exports.getFolderNames = void 0 +const fs_1 = __importDefault(require('fs')) +/** + * Retrieves the names of all folders in the specified directory. + * + * @param directoryPath - The path of the directory to retrieve folder names from. + * @returns An array of folder names. + */ +const getFolderNames = (directoryPath) => { + try { + // Read the contents of the directory + const contents = fs_1.default.readdirSync(directoryPath) + // Filter out only directories + const folderNames = contents.filter((item) => { + const stat = fs_1.default.statSync(`${directoryPath}/${item}`) + return stat.isDirectory() + }) + return folderNames + } catch (err) { + console.error(`Error reading directory: ${err}`) + return [] + } +} +exports.getFolderNames = getFolderNames diff --git a/scripts/cli/build/utils/index.js b/scripts/cli/build/utils/index.js new file mode 100644 index 00000000..187d247e --- /dev/null +++ b/scripts/cli/build/utils/index.js @@ -0,0 +1,30 @@ +'use strict' +var __createBinding = + (this && this.__createBinding) || + (Object.create + ? function (o, m, k, k2) { + if (k2 === undefined) k2 = k + var desc = Object.getOwnPropertyDescriptor(m, k) + if (!desc || ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { + enumerable: true, + get: function () { + return m[k] + }, + } + } + Object.defineProperty(o, k2, desc) + } + : function (o, m, k, k2) { + if (k2 === undefined) k2 = k + o[k2] = m[k] + }) +var __exportStar = + (this && this.__exportStar) || + function (m, exports) { + for (var p in m) + if (p !== 'default' && !Object.prototype.hasOwnProperty.call(exports, p)) + __createBinding(exports, m, p) + } +Object.defineProperty(exports, '__esModule', { value: true }) +__exportStar(require('./getDirectoryNames'), exports) diff --git a/scripts/cli/commands/generateScreen.ts b/scripts/cli/commands/generateScreen.ts index fc248719..e797f85b 100644 --- a/scripts/cli/commands/generateScreen.ts +++ b/scripts/cli/commands/generateScreen.ts @@ -1,3 +1,133 @@ -export const generateScreen = () => { - console.log('generateScreen') +import fs from 'fs' +import prompt from 'prompt-sync' +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import selectPrompt from 'select-prompt' + +import { APP_ROUTER_DIRECTORY, SCREENS_DIRECTORY } from '../constants' +import { getDirectoryNames } from '../utils' + +/** + * 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 = (basePath: string) => + new Promise((resolve) => { + let result = basePath + const subDirectories = getDirectoryNames(basePath) + if (subDirectories.length) { + const subDirectoryPrompt = subDirectories.map((directoryName) => ({ + title: directoryName, + value: directoryName, + })) + + if (basePath !== APP_ROUTER_DIRECTORY) { + subDirectoryPrompt.unshift({ title: '.', value: '.' }) + } + + selectPrompt(`Select a directory (${basePath.split(process.cwd())[1]})`, subDirectoryPrompt, { + cursor: 0, + }).on('submit', (subValue: string) => { + if (subValue === '.') { + result = basePath + resolve(result) // Return the result when subValue is '.' + return + } + selectPath(`${basePath}/${subValue}`).then((subResult) => { + result = subResult + resolve(result) // Return the result when subValue is not '.' + }) + }) + } else { + resolve(result) // Return the result when there are no subdirectories + } + }) + +/** + * 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)) { + throw new 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) => { + // TODO: Check if its the tab - if so - add under new directory + 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)) { + throw new Error(`Screen ${screenName} already exists in ${SCREENS_DIRECTORY}`) + } +} + +/** + * Creates a screen file with the given screen name. + * @param {string} screenName - The name of the screen. + */ +const createScreenFile = (baseName: string) => { + const screenName = `${baseName.charAt(0).toUpperCase() + baseName.slice(1)}Screen` + const screenFromFile = fs.readFileSync('./templates/screen_template.tsx', 'utf8') + const screenContent = screenFromFile + .replaceAll('_NAME_', screenName) + .replace("// @ts-expect-error: it's a template and will be removed", '') + + fs.writeFileSync(`${SCREENS_DIRECTORY}/${screenName}.tsx`, screenContent, { + encoding: 'utf-8', + flag: 'w', + }) +} + +const addToScreensIndex = (baseName: string) => { + const screenName = `${baseName.charAt(0).toUpperCase() + baseName.slice(1)}Screen` + const indexFilePath = `${SCREENS_DIRECTORY}/index.ts` + const indexFile = fs.readFileSync(indexFilePath, 'utf8') + const newIndexFile = indexFile.padEnd(indexFile.length) + `export * from './${screenName}'\n` + + console.log({ indexFile, newIndexFile }) + + fs.writeFileSync(indexFilePath, newIndexFile) +} + +/** + * 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 screenName = prompt()('Enter screen name: ') + const routePath = await selectPath(APP_ROUTER_DIRECTORY) + + validateRoute(screenName, routePath) + createRouteFile(screenName, routePath) + + validateScreen(screenName) + createScreenFile(screenName) + + addToScreensIndex(screenName) } diff --git a/scripts/cli/constants/index.ts b/scripts/cli/constants/index.ts index 31fd1f28..3c04f4e9 100644 --- a/scripts/cli/constants/index.ts +++ b/scripts/cli/constants/index.ts @@ -1 +1,5 @@ export const CLI_ACTIONS = ['generate', 'g', 'bootstrap', 'b'] + +export const APP_ROUTER_DIRECTORY = `${process.cwd()}/app/(app)` +export const SCREENS_DIRECTORY = `${process.cwd()}/src/screens` +export const NAVIGATION_CONFIG_PATH = `${process.cwd()}/navigation/tabNavigator/navigation-config.ts` diff --git a/scripts/cli/tsconfig.cli.json b/scripts/cli/tsconfig.cli.json index 5fb1b807..1382bc7a 100644 --- a/scripts/cli/tsconfig.cli.json +++ b/scripts/cli/tsconfig.cli.json @@ -3,6 +3,7 @@ "rootDir": ".", "outDir": "build", "strict": true, + "lib": ["ESNext"], "target": "es6", "module": "commonjs", "sourceMap": false, diff --git a/scripts/cli/utils/getDirectoryNames.ts b/scripts/cli/utils/getDirectoryNames.ts new file mode 100644 index 00000000..acdc7a3f --- /dev/null +++ b/scripts/cli/utils/getDirectoryNames.ts @@ -0,0 +1,25 @@ +import fs from 'fs' + +/** + * 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 { + // 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) { + console.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..c92afe7c --- /dev/null +++ b/scripts/cli/utils/index.ts @@ -0,0 +1 @@ +export * from './getDirectoryNames' diff --git a/templates/screen_template.tsx b/templates/screen_template.tsx index 0e6ad613..113ac472 100644 --- a/templates/screen_template.tsx +++ b/templates/screen_template.tsx @@ -1,24 +1,13 @@ -import { Button, Center, Text } from '@baca/design-system' -import { useCallback, useTranslation } from '@baca/hooks' - +import { Center, Text } from '@baca/design-system' +import { useTranslation } from '@baca/hooks' // @ts-expect-error: it's a template and will be removed -export const _NAME_Screen = (props: _NAME_ScreenProps): JSX.Element => { - const { - navigation: { navigate }, - } = props +export const _NAME_ = (): JSX.Element => { const { t } = useTranslation() - const navigateToDetails = useCallback(() => { - navigate('Home') - }, [navigate]) - return (
_NAME_ {t('hello')} -
) } diff --git a/yarn.lock b/yarn.lock index 7b7c66ec..a5706e23 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3683,6 +3683,11 @@ dependencies: undici-types "~5.26.4" +"@types/prompt-sync@^4.2.3": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@types/prompt-sync/-/prompt-sync-4.2.3.tgz#b6a9fe88fc4b4cacb8ab59f87f2614d6e674e177" + integrity sha512-Ox77gCSx0YyeakGt/qfOZUSFNSSi+sh3ABoGOiCwiO2KODx492BJnUm9oIXS+AHJtqp12iM4RduY6viTJ9bYwA== + "@types/prop-types@*": version "15.7.11" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563" From 3704957e52a2a5ceed4f1e4d712bd7e1c71bfbf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sza=C5=82owski?= Date: Sat, 9 Mar 2024 17:08:04 +0100 Subject: [PATCH 03/47] feat: handle tab navigation config --- scripts/cli/build/commands/generateScreen.js | 173 +++++++++++------- scripts/cli/commands/generateScreen.ts | 80 +++++--- .../tabNavigator/navigation-config.ts | 2 +- 3 files changed, 162 insertions(+), 93 deletions(-) diff --git a/scripts/cli/build/commands/generateScreen.js b/scripts/cli/build/commands/generateScreen.js index ad4fcad1..2db602bd 100644 --- a/scripts/cli/build/commands/generateScreen.js +++ b/scripts/cli/build/commands/generateScreen.js @@ -44,6 +44,9 @@ const prompt_sync_1 = __importDefault(require('prompt-sync')) const select_prompt_1 = __importDefault(require('select-prompt')) const constants_1 = require('../constants') const utils_1 = require('../utils') +const addAfter = (content, searchText, textToAdd) => { + return content.replace(searchText, searchText + textToAdd) +} /** * Recursively prompts the user to select a subdirectory and calls itself with the selected subdirectory path. * @@ -51,13 +54,15 @@ const utils_1 = require('../utils') */ const selectPath = (basePath) => new Promise((resolve) => { - let result = basePath const subDirectories = (0, utils_1.getDirectoryNames)(basePath) if (subDirectories.length) { const subDirectoryPrompt = subDirectories.map((directoryName) => ({ title: directoryName, value: directoryName, })) + if (basePath.includes('tabs')) { + subDirectoryPrompt.unshift({ title: '_New Tab_', value: 'new-tab' }) + } if (basePath !== constants_1.APP_ROUTER_DIRECTORY) { subDirectoryPrompt.unshift({ title: '.', value: '.' }) } @@ -68,81 +73,98 @@ const selectPath = (basePath) => cursor: 0, } ).on('submit', (subValue) => { + // Return the result when subValue is '.' if (subValue === '.') { - result = basePath - resolve(result) // Return the result when subValue is '.' + resolve(basePath) return } + // Return the result when subValue is not '.' selectPath(`${basePath}/${subValue}`).then((subResult) => { - result = subResult - resolve(result) // Return the result when subValue is not '.' + resolve(subResult) }) }) + // Return the result when there are no subdirectories } else { - resolve(result) // Return the result when there are no subdirectories + resolve(basePath) } }) -/** - * 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, routePath) => { - const filePath = `${routePath}/${routeName}.tsx` - if (fs_1.default.existsSync(filePath)) { - throw new Error(`Route ${routeName} already exists in ${routePath}`) +// /** +// * 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)) { +// throw new 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} +// `, +// { +// encoding: 'utf-8', +// flag: 'w', +// } +// ) +// } +// /** +// * 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)) { +// throw new Error(`Screen ${screenName} already exists in ${SCREENS_DIRECTORY}`) +// } +// } +// /** +// * 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) +// .replace("// @ts-expect-error: it's a template and will be removed", '') +// 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 createNewNavTab = () => { + const tabName = (0, prompt_sync_1.default)()('Enter tab name: ') + if (!tabName) { + throw new Error('Tab name is required') } -} -/** - * 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, routePath) => { - // TODO: Check if its the tab - if so - add under new directory - const screenName = `${routeName.charAt(0).toUpperCase() + routeName.slice(1)}Screen` - fs_1.default.writeFileSync( - `${routePath}/${routeName.toLowerCase()}.tsx`, - `import { ${screenName} } from '@baca/screens' - -export default ${screenName} -` + const navigationConfigFile = fs_1.default.readFileSync( + './src/navigation/tabNavigator/navigation-config.ts', + 'utf8' ) -} -/** - * 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) => { - const filePath = `${constants_1.SCREENS_DIRECTORY}/${screenName}.tsx` - if (fs_1.default.existsSync(filePath)) { - throw new Error(`Screen ${screenName} already exists in ${constants_1.SCREENS_DIRECTORY}`) - } -} -/** - * Creates a screen file with the given screen name. - * @param {string} screenName - The name of the screen. - */ -const createScreenFile = (baseName) => { - const screenName = `${baseName.charAt(0).toUpperCase() + baseName.slice(1)}Screen` - const screenFromFile = fs_1.default.readFileSync('./templates/screen_template.tsx', 'utf8') - const screenContent = screenFromFile - .replaceAll('_NAME_', screenName) - .replace("// @ts-expect-error: it's a template and will be removed", '') - fs_1.default.writeFileSync(`${constants_1.SCREENS_DIRECTORY}/${screenName}.tsx`, screenContent, { - encoding: 'utf-8', - flag: 'w', - }) -} -const addToScreensIndex = (baseName) => { - const screenName = `${baseName.charAt(0).toUpperCase() + baseName.slice(1)}Screen` - const indexFilePath = `${constants_1.SCREENS_DIRECTORY}/index.ts` - const indexFile = fs_1.default.readFileSync(indexFilePath, 'utf8') - const newIndexFile = indexFile.padEnd(indexFile.length) + `export * from './${screenName}'\n` - console.log({ indexFile, newIndexFile }) - fs_1.default.writeFileSync(indexFilePath, newIndexFile) + 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_1.default.writeFileSync('./src/navigation/tabNavigator/navigation-config.ts', newContent) } /** * Generates a screen based on user input. @@ -151,12 +173,21 @@ const addToScreensIndex = (baseName) => { */ const generateScreen = () => __awaiter(void 0, void 0, void 0, function* () { - const screenName = (0, prompt_sync_1.default)()('Enter screen name: ') + const routeName = (0, prompt_sync_1.default)()('Enter screen name: ') + if (!routeName) { + throw new Error('Screen name is required') + } const routePath = yield selectPath(constants_1.APP_ROUTER_DIRECTORY) - validateRoute(screenName, routePath) - createRouteFile(screenName, routePath) - validateScreen(screenName) - createScreenFile(screenName) - addToScreensIndex(screenName) + console.log({ routeName, routePath }) + const isNewTab = routePath.includes('tabs') && routePath.includes('new-tab') + if (isNewTab) { + createNewNavTab() + } + // validateRoute(routeName, routePath) + // createRouteFile(routeName, routePath) + // const screenName = `${routeName.charAt(0).toUpperCase() + routeName.slice(1)}Screen` + // validateScreen(screenName) + // createScreenFile(screenName) + // addToScreensIndex(screenName) }) exports.generateScreen = generateScreen diff --git a/scripts/cli/commands/generateScreen.ts b/scripts/cli/commands/generateScreen.ts index e797f85b..e1d14d3d 100644 --- a/scripts/cli/commands/generateScreen.ts +++ b/scripts/cli/commands/generateScreen.ts @@ -7,6 +7,10 @@ import selectPrompt from 'select-prompt' import { APP_ROUTER_DIRECTORY, SCREENS_DIRECTORY } from '../constants' import { getDirectoryNames } 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. * @@ -14,7 +18,6 @@ import { getDirectoryNames } from '../utils' */ const selectPath = (basePath: string) => new Promise((resolve) => { - let result = basePath const subDirectories = getDirectoryNames(basePath) if (subDirectories.length) { const subDirectoryPrompt = subDirectories.map((directoryName) => ({ @@ -22,6 +25,10 @@ const selectPath = (basePath: string) => value: directoryName, })) + if (basePath.includes('tabs')) { + subDirectoryPrompt.unshift({ title: '_New Tab_', value: 'new-tab' }) + } + if (basePath !== APP_ROUTER_DIRECTORY) { subDirectoryPrompt.unshift({ title: '.', value: '.' }) } @@ -29,18 +36,19 @@ const selectPath = (basePath: string) => selectPrompt(`Select a directory (${basePath.split(process.cwd())[1]})`, subDirectoryPrompt, { cursor: 0, }).on('submit', (subValue: string) => { + // Return the result when subValue is '.' if (subValue === '.') { - result = basePath - resolve(result) // Return the result when subValue is '.' + resolve(basePath) return } + // Return the result when subValue is not '.' selectPath(`${basePath}/${subValue}`).then((subResult) => { - result = subResult - resolve(result) // Return the result when subValue is not '.' + resolve(subResult) }) }) + // Return the result when there are no subdirectories } else { - resolve(result) // Return the result when there are no subdirectories + resolve(basePath) } }) @@ -63,14 +71,17 @@ const validateRoute = (routeName: string, routePath: string) => { * @param {string} routePath - The path where the route file will be created. */ const createRouteFile = (routeName: string, routePath: string) => { - // TODO: Check if its the tab - if so - add under new directory const screenName = `${routeName.charAt(0).toUpperCase() + routeName.slice(1)}Screen` fs.writeFileSync( `${routePath}/${routeName.toLowerCase()}.tsx`, `import { ${screenName} } from '@baca/screens' export default ${screenName} -` +`, + { + encoding: 'utf-8', + flag: 'w', + } ) } @@ -90,42 +101,69 @@ const validateScreen = (screenName: string) => { * Creates a screen file with the given screen name. * @param {string} screenName - The name of the screen. */ -const createScreenFile = (baseName: string) => { - const screenName = `${baseName.charAt(0).toUpperCase() + baseName.slice(1)}Screen` +const createScreenFile = (screenName: string) => { const screenFromFile = fs.readFileSync('./templates/screen_template.tsx', 'utf8') const screenContent = screenFromFile .replaceAll('_NAME_', screenName) .replace("// @ts-expect-error: it's a template and will be removed", '') - fs.writeFileSync(`${SCREENS_DIRECTORY}/${screenName}.tsx`, screenContent, { - encoding: 'utf-8', - flag: 'w', - }) + fs.writeFileSync(`${SCREENS_DIRECTORY}/${screenName}.tsx`, screenContent) } -const addToScreensIndex = (baseName: string) => { - const screenName = `${baseName.charAt(0).toUpperCase() + baseName.slice(1)}Screen` +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` - console.log({ indexFile, newIndexFile }) - fs.writeFileSync(indexFilePath, newIndexFile) } +const createNewNavTab = () => { + const tabName = prompt()('Enter tab name: ') + if (!tabName) { + throw new Error('Tab name is required') + } + + 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 screenName = prompt()('Enter screen name: ') + const routeName = prompt()('Enter screen name: ') + if (!routeName) { + throw new Error('Screen name is required') + } + const routePath = await selectPath(APP_ROUTER_DIRECTORY) - validateRoute(screenName, routePath) - createRouteFile(screenName, routePath) + const isNewTab = routePath.includes('tabs') && routePath.includes('new-tab') + if (isNewTab) { + createNewNavTab() + } + + validateRoute(routeName, routePath) + createRouteFile(routeName, routePath) + const screenName = `${routeName.charAt(0).toUpperCase() + routeName.slice(1)}Screen` validateScreen(screenName) createScreenFile(screenName) diff --git a/src/navigation/tabNavigator/navigation-config.ts b/src/navigation/tabNavigator/navigation-config.ts index 061f97d3..8d89f0b3 100644 --- a/src/navigation/tabNavigator/navigation-config.ts +++ b/src/navigation/tabNavigator/navigation-config.ts @@ -14,6 +14,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-3-line', @@ -49,7 +50,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', From 26bee65a4594f718e4567faeea10125e2076bb99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sza=C5=82owski?= Date: Sat, 9 Mar 2024 19:39:03 +0100 Subject: [PATCH 04/47] feat: handle tab creation --- package.json | 2 + scripts/cli/actions/index.ts | 6 +- scripts/cli/build/actions/index.js | 9 +- scripts/cli/build/commands/generateScreen.js | 202 +++++++++---------- scripts/cli/build/constants.js | 11 + scripts/cli/build/index.js | 9 +- scripts/cli/build/types.js | 2 + scripts/cli/build/utils/content.js | 38 ++++ scripts/cli/build/utils/getDirectoryNames.js | 4 +- scripts/cli/build/utils/index.js | 2 + scripts/cli/build/utils/logger.js | 21 ++ scripts/cli/commands/generateScreen.ts | 92 +++++---- scripts/cli/constants.ts | 5 + scripts/cli/constants/index.ts | 5 - scripts/cli/index.ts | 11 +- scripts/cli/types/index.ts | 2 - scripts/cli/utils/content.ts | 34 ++++ scripts/cli/utils/getDirectoryNames.ts | 5 +- scripts/cli/utils/index.ts | 2 + scripts/cli/utils/logger.ts | 19 ++ 20 files changed, 306 insertions(+), 175 deletions(-) create mode 100644 scripts/cli/build/constants.js create mode 100644 scripts/cli/build/types.js create mode 100644 scripts/cli/build/utils/content.js create mode 100644 scripts/cli/build/utils/logger.js create mode 100644 scripts/cli/constants.ts delete mode 100644 scripts/cli/constants/index.ts delete mode 100644 scripts/cli/types/index.ts create mode 100644 scripts/cli/utils/content.ts create mode 100644 scripts/cli/utils/logger.ts diff --git a/package.json b/package.json index 23e05e34..52b49106 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,8 @@ "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", + "baca:generate": "node ./scripts/cli/build generate", + "generate:query": "yarn orval --config ./orval.config.ts", "generate:component": "node ./scripts/create_new_component.js && yarn eslint src --fix && yarn tsc", "generate:env:production": "scripts/generate_dotenv.sh production", "generate:env:qa": "scripts/generate_dotenv.sh qa", diff --git a/scripts/cli/actions/index.ts b/scripts/cli/actions/index.ts index 6b025266..1fe5edeb 100644 --- a/scripts/cli/actions/index.ts +++ b/scripts/cli/actions/index.ts @@ -1,7 +1,7 @@ import { bootstrap } from './bootstrap' import { generate } from './generate' import { CLI_ACTIONS } from '../constants' -import { CliActions } from '../types' +import { logger } from '../utils' const actions = { generate, @@ -15,9 +15,9 @@ const actions = { * * @param action - The CLI action to parse and execute. */ -export const parseAction = (action: CliActions) => { +export const executeAction = (action: keyof typeof actions) => { if (!CLI_ACTIONS.includes(action)) { - console.error('\x1b[31m%s\x1b[0m', `Invalid action ${action}`) + logger.error(`Action ${action} is not supported.`) return } actions[action]() diff --git a/scripts/cli/build/actions/index.js b/scripts/cli/build/actions/index.js index cc412521..8ee49f5c 100644 --- a/scripts/cli/build/actions/index.js +++ b/scripts/cli/build/actions/index.js @@ -1,9 +1,10 @@ 'use strict' Object.defineProperty(exports, '__esModule', { value: true }) -exports.parseAction = void 0 +exports.executeAction = void 0 const bootstrap_1 = require('./bootstrap') const generate_1 = require('./generate') const constants_1 = require('../constants') +const utils_1 = require('../utils') const actions = { generate: generate_1.generate, g: generate_1.generate, @@ -15,11 +16,11 @@ const actions = { * * @param action - The CLI action to parse and execute. */ -const parseAction = (action) => { +const executeAction = (action) => { if (!constants_1.CLI_ACTIONS.includes(action)) { - console.error('\x1b[31m%s\x1b[0m', `Invalid action ${action}`) + utils_1.logger.error(`Action ${action} is not supported.`) return } actions[action]() } -exports.parseAction = parseAction +exports.executeAction = executeAction diff --git a/scripts/cli/build/commands/generateScreen.js b/scripts/cli/build/commands/generateScreen.js index 2db602bd..f129abfd 100644 --- a/scripts/cli/build/commands/generateScreen.js +++ b/scripts/cli/build/commands/generateScreen.js @@ -55,102 +55,99 @@ const addAfter = (content, searchText, textToAdd) => { const selectPath = (basePath) => new Promise((resolve) => { const subDirectories = (0, utils_1.getDirectoryNames)(basePath) - if (subDirectories.length) { - const subDirectoryPrompt = subDirectories.map((directoryName) => ({ - title: directoryName, - value: directoryName, - })) - if (basePath.includes('tabs')) { - subDirectoryPrompt.unshift({ title: '_New Tab_', value: 'new-tab' }) - } - if (basePath !== constants_1.APP_ROUTER_DIRECTORY) { - subDirectoryPrompt.unshift({ title: '.', value: '.' }) - } - ;(0, select_prompt_1.default)( - `Select a directory (${basePath.split(process.cwd())[1]})`, - subDirectoryPrompt, - { - cursor: 0, - } - ).on('submit', (subValue) => { - // Return the result when subValue is '.' - if (subValue === '.') { - resolve(basePath) - return - } - // Return the result when subValue is not '.' - selectPath(`${basePath}/${subValue}`).then((subResult) => { - resolve(subResult) - }) - }) - // Return the result when there are no subdirectories - } else { + const hasSubDirectories = subDirectories.length > 0 + // Return the result when there are no subdirectories + if (!hasSubDirectories) { resolve(basePath) + return + } + const subDirectoryPrompt = subDirectories.map((directoryName) => ({ + title: directoryName, + value: directoryName, + })) + if (basePath.includes('tabs')) { + subDirectoryPrompt.unshift({ title: '_New Tab_', value: 'new-tab' }) + } + if (basePath !== constants_1.APP_ROUTER_DIRECTORY) { + subDirectoryPrompt.unshift({ title: '.', value: '.' }) } + ;(0, select_prompt_1.default)(`Select a directory (${basePath})`, subDirectoryPrompt, { + cursor: 0, + }).on('submit', (subValue) => { + // Return the result when user selects current directory + if (subValue === '.') { + resolve(basePath) + return + } + // Recursively execute path selection when subValue is not the current directory + selectPath(`${basePath}/${subValue}`).then((subResult) => { + resolve(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)) { -// throw new 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} -// `, -// { -// encoding: 'utf-8', -// flag: 'w', -// } -// ) -// } -// /** -// * 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)) { -// throw new Error(`Screen ${screenName} already exists in ${SCREENS_DIRECTORY}`) -// } -// } -// /** -// * 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) -// .replace("// @ts-expect-error: it's a template and will be removed", '') -// 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 createNewNavTab = () => { +/** + * 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, routePath) => { + const filePath = `${routePath}/${routeName}.tsx` + if (fs_1.default.existsSync(filePath)) { + utils_1.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, routePath) => { + const screenName = `${routeName.charAt(0).toUpperCase() + routeName.slice(1)}Screen` + fs_1.default.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) => { + const filePath = `${constants_1.SCREENS_DIRECTORY}/${screenName}.tsx` + if (fs_1.default.existsSync(filePath)) { + utils_1.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) => { + const screenFromFile = fs_1.default.readFileSync('./templates/screen_template.tsx', 'utf8') + const screenContent = screenFromFile + .replaceAll('_NAME_', screenName) + .replace("// @ts-expect-error: it's a template and will be removed", '') + fs_1.default.writeFileSync(`${constants_1.SCREENS_DIRECTORY}/${screenName}.tsx`, screenContent) +} +const addToScreensIndex = (screenName) => { + const indexFilePath = `${constants_1.SCREENS_DIRECTORY}/index.ts` + const indexFile = fs_1.default.readFileSync(indexFilePath, 'utf8') + const newIndexFile = indexFile.padEnd(indexFile.length) + `export * from './${screenName}'\n` + fs_1.default.writeFileSync(indexFilePath, newIndexFile) +} +const promptTabName = () => { const tabName = (0, prompt_sync_1.default)()('Enter tab name: ') if (!tabName) { throw new Error('Tab name is required') } + return tabName +} +const createNewNavTab = (tabName) => { const navigationConfigFile = fs_1.default.readFileSync( './src/navigation/tabNavigator/navigation-config.ts', 'utf8' @@ -174,20 +171,21 @@ const createNewNavTab = () => { const generateScreen = () => __awaiter(void 0, void 0, void 0, function* () { const routeName = (0, prompt_sync_1.default)()('Enter screen name: ') - if (!routeName) { - throw new Error('Screen name is required') - } - const routePath = yield selectPath(constants_1.APP_ROUTER_DIRECTORY) - console.log({ routeName, routePath }) - const isNewTab = routePath.includes('tabs') && routePath.includes('new-tab') + let routePath = yield selectPath(constants_1.APP_ROUTER_DIRECTORY) + const isNewTab = routePath.includes('(tabs)') && routePath.includes('new-tab') if (isNewTab) { - createNewNavTab() + const tabName = promptTabName() + createNewNavTab(tabName) + const newTabPath = routePath.replace('/new-tab', `/${tabName}`) + routePath = newTabPath + console.log({ newTabPath, routePath }) + fs_1.default.mkdirSync(newTabPath) } - // validateRoute(routeName, routePath) - // createRouteFile(routeName, routePath) - // const screenName = `${routeName.charAt(0).toUpperCase() + routeName.slice(1)}Screen` - // validateScreen(screenName) - // createScreenFile(screenName) - // addToScreensIndex(screenName) + validateRoute(routeName, routePath) + createRouteFile(routeName, routePath) + const screenName = `${routeName.charAt(0).toUpperCase() + routeName.slice(1)}Screen` + validateScreen(screenName) + createScreenFile(screenName) + addToScreensIndex(screenName) }) exports.generateScreen = generateScreen diff --git a/scripts/cli/build/constants.js b/scripts/cli/build/constants.js new file mode 100644 index 00000000..2004b55a --- /dev/null +++ b/scripts/cli/build/constants.js @@ -0,0 +1,11 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +exports.NAVIGATION_CONFIG_PATH = + exports.SCREENS_DIRECTORY = + exports.APP_ROUTER_DIRECTORY = + exports.CLI_ACTIONS = + void 0 +exports.CLI_ACTIONS = ['generate', 'g', 'bootstrap', 'b'] +exports.APP_ROUTER_DIRECTORY = 'app/(app)' +exports.SCREENS_DIRECTORY = 'src/screens' +exports.NAVIGATION_CONFIG_PATH = 'navigation/tabNavigator/navigation-config.ts' diff --git a/scripts/cli/build/index.js b/scripts/cli/build/index.js index 1b59c541..26dba5af 100644 --- a/scripts/cli/build/index.js +++ b/scripts/cli/build/index.js @@ -3,10 +3,7 @@ Object.defineProperty(exports, '__esModule', { value: true }) const commander_1 = require('commander') const actions_1 = require('./actions') const program = new commander_1.Command() -program - .argument('') - .option('-d, --debug', 'Set the debug level') - .action((action, options, command) => { - ;(0, actions_1.parseAction)(action) - }) +program.argument('').action((action) => { + ;(0, actions_1.executeAction)(action) +}) program.parse() diff --git a/scripts/cli/build/types.js b/scripts/cli/build/types.js new file mode 100644 index 00000000..0a483a32 --- /dev/null +++ b/scripts/cli/build/types.js @@ -0,0 +1,2 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) diff --git a/scripts/cli/build/utils/content.js b/scripts/cli/build/utils/content.js new file mode 100644 index 00000000..88b6e409 --- /dev/null +++ b/scripts/cli/build/utils/content.js @@ -0,0 +1,38 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +exports.deleteText = exports.addBefore = exports.addAfter = void 0 +/** + * 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. + */ +const addAfter = (content, searchText, textToAdd) => { + return content.replace(searchText, searchText + textToAdd) +} +exports.addAfter = addAfter +/** + * 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. + */ +const addBefore = (content, searchText, textToAdd) => { + return content.replace(searchText, textToAdd + searchText) +} +exports.addBefore = addBefore +/** + * 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. + */ +const deleteText = (content, searchText) => { + return content.replace(searchText, '') +} +exports.deleteText = deleteText diff --git a/scripts/cli/build/utils/getDirectoryNames.js b/scripts/cli/build/utils/getDirectoryNames.js index 667950de..aa233144 100644 --- a/scripts/cli/build/utils/getDirectoryNames.js +++ b/scripts/cli/build/utils/getDirectoryNames.js @@ -7,6 +7,7 @@ var __importDefault = Object.defineProperty(exports, '__esModule', { value: true }) exports.getDirectoryNames = void 0 const fs_1 = __importDefault(require('fs')) +const logger_1 = require('./logger') /** * Retrieves the names of all sub-directories in the specified directory. * @@ -15,6 +16,7 @@ const fs_1 = __importDefault(require('fs')) */ const getDirectoryNames = (directoryPath) => { try { + if (directoryPath.includes('new-tab')) return [] // Read the contents of the directory const contents = fs_1.default.readdirSync(directoryPath) // Filter out only directories @@ -24,7 +26,7 @@ const getDirectoryNames = (directoryPath) => { }) return folderNames } catch (err) { - console.error(`Error reading directory: ${err}`) + logger_1.logger.error(`Error reading directory: ${err}`) return [] } } diff --git a/scripts/cli/build/utils/index.js b/scripts/cli/build/utils/index.js index 187d247e..3b11ce9e 100644 --- a/scripts/cli/build/utils/index.js +++ b/scripts/cli/build/utils/index.js @@ -28,3 +28,5 @@ var __exportStar = } Object.defineProperty(exports, '__esModule', { value: true }) __exportStar(require('./getDirectoryNames'), exports) +__exportStar(require('./content'), exports) +__exportStar(require('./logger'), exports) diff --git a/scripts/cli/build/utils/logger.js b/scripts/cli/build/utils/logger.js new file mode 100644 index 00000000..e0100c0a --- /dev/null +++ b/scripts/cli/build/utils/logger.js @@ -0,0 +1,21 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +exports.logger = void 0 +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 +exports.logger = { + error: (message, ...optionalParams) => { + console.error(LCERROR, message, ...optionalParams) + }, + warn: (message, ...optionalParams) => { + console.warn(LCWARN, message, ...optionalParams) + }, + info: (message, ...optionalParams) => { + console.info(LCINFO, message, ...optionalParams) + }, + success: (message, ...optionalParams) => { + console.info(LCSUCCESS, message, ...optionalParams) + }, +} diff --git a/scripts/cli/commands/generateScreen.ts b/scripts/cli/commands/generateScreen.ts index e1d14d3d..b2d70637 100644 --- a/scripts/cli/commands/generateScreen.ts +++ b/scripts/cli/commands/generateScreen.ts @@ -5,7 +5,7 @@ import prompt from 'prompt-sync' import selectPrompt from 'select-prompt' import { APP_ROUTER_DIRECTORY, SCREENS_DIRECTORY } from '../constants' -import { getDirectoryNames } from '../utils' +import { getDirectoryNames, logger } from '../utils' const addAfter = (content: string, searchText: string, textToAdd: string) => { return content.replace(searchText, searchText + textToAdd) @@ -19,37 +19,40 @@ const addAfter = (content: string, searchText: string, textToAdd: string) => { const selectPath = (basePath: string) => new Promise((resolve) => { const subDirectories = getDirectoryNames(basePath) - if (subDirectories.length) { - const subDirectoryPrompt = subDirectories.map((directoryName) => ({ - title: directoryName, - value: directoryName, - })) - - if (basePath.includes('tabs')) { - subDirectoryPrompt.unshift({ title: '_New Tab_', value: 'new-tab' }) - } - - if (basePath !== APP_ROUTER_DIRECTORY) { - subDirectoryPrompt.unshift({ title: '.', value: '.' }) - } + const hasSubDirectories = subDirectories.length > 0 - selectPrompt(`Select a directory (${basePath.split(process.cwd())[1]})`, subDirectoryPrompt, { - cursor: 0, - }).on('submit', (subValue: string) => { - // Return the result when subValue is '.' - if (subValue === '.') { - resolve(basePath) - return - } - // Return the result when subValue is not '.' - selectPath(`${basePath}/${subValue}`).then((subResult) => { - resolve(subResult) - }) - }) - // Return the result when there are no subdirectories - } else { + // Return the result when there are no subdirectories + if (!hasSubDirectories) { resolve(basePath) + return + } + + const subDirectoryPrompt = subDirectories.map((directoryName) => ({ + title: directoryName, + value: directoryName, + })) + + if (basePath.includes('tabs')) { + subDirectoryPrompt.unshift({ title: '_New Tab_', value: 'new-tab' }) + } + + if (basePath !== APP_ROUTER_DIRECTORY) { + subDirectoryPrompt.unshift({ title: '.', value: '.' }) } + + selectPrompt(`Select a directory (${basePath})`, subDirectoryPrompt, { + cursor: 0, + }).on('submit', (subValue: string) => { + // Return the result when user selects current directory + if (subValue === '.') { + resolve(basePath) + return + } + // Recursively execute path selection when subValue is not the current directory + selectPath(`${basePath}/${subValue}`).then((subResult) => { + resolve(subResult) + }) + }) }) /** @@ -61,7 +64,7 @@ const selectPath = (basePath: string) => const validateRoute = (routeName: string, routePath: string) => { const filePath = `${routePath}/${routeName}.tsx` if (fs.existsSync(filePath)) { - throw new Error(`Route ${routeName} already exists in ${routePath}`) + logger.error(`Route ${routeName} already exists in ${routePath}`) } } @@ -77,11 +80,7 @@ const createRouteFile = (routeName: string, routePath: string) => { `import { ${screenName} } from '@baca/screens' export default ${screenName} -`, - { - encoding: 'utf-8', - flag: 'w', - } +` ) } @@ -93,7 +92,7 @@ export default ${screenName} const validateScreen = (screenName: string) => { const filePath = `${SCREENS_DIRECTORY}/${screenName}.tsx` if (fs.existsSync(filePath)) { - throw new Error(`Screen ${screenName} already exists in ${SCREENS_DIRECTORY}`) + logger.error(`Screen ${screenName} already exists`) } } @@ -118,12 +117,15 @@ const addToScreensIndex = (screenName: string) => { fs.writeFileSync(indexFilePath, newIndexFile) } -const createNewNavTab = () => { +const promptTabName = () => { const tabName = prompt()('Enter tab name: ') 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' @@ -149,15 +151,17 @@ const createNewNavTab = () => { */ export const generateScreen = async () => { const routeName = prompt()('Enter screen name: ') - if (!routeName) { - throw new Error('Screen name is required') - } + let routePath = await selectPath(APP_ROUTER_DIRECTORY) - const routePath = await selectPath(APP_ROUTER_DIRECTORY) - - const isNewTab = routePath.includes('tabs') && routePath.includes('new-tab') + const isNewTab = routePath.includes('(tabs)') && routePath.includes('new-tab') if (isNewTab) { - createNewNavTab() + const tabName = promptTabName() + createNewNavTab(tabName) + + const newTabPath = routePath.replace('/new-tab', `/${tabName}`) + routePath = newTabPath + console.log({ newTabPath, routePath }) + fs.mkdirSync(newTabPath) } validateRoute(routeName, routePath) diff --git a/scripts/cli/constants.ts b/scripts/cli/constants.ts new file mode 100644 index 00000000..0cc00849 --- /dev/null +++ b/scripts/cli/constants.ts @@ -0,0 +1,5 @@ +export const CLI_ACTIONS = ['generate', 'g', 'bootstrap', 'b'] + +export const APP_ROUTER_DIRECTORY = 'app/(app)' +export const SCREENS_DIRECTORY = 'src/screens' +export const NAVIGATION_CONFIG_PATH = 'navigation/tabNavigator/navigation-config.ts' diff --git a/scripts/cli/constants/index.ts b/scripts/cli/constants/index.ts deleted file mode 100644 index 3c04f4e9..00000000 --- a/scripts/cli/constants/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const CLI_ACTIONS = ['generate', 'g', 'bootstrap', 'b'] - -export const APP_ROUTER_DIRECTORY = `${process.cwd()}/app/(app)` -export const SCREENS_DIRECTORY = `${process.cwd()}/src/screens` -export const NAVIGATION_CONFIG_PATH = `${process.cwd()}/navigation/tabNavigator/navigation-config.ts` diff --git a/scripts/cli/index.ts b/scripts/cli/index.ts index 2efb2a2e..d4d712d2 100644 --- a/scripts/cli/index.ts +++ b/scripts/cli/index.ts @@ -1,14 +1,11 @@ import { Command } from 'commander' -import { parseAction } from './actions' +import { executeAction } from './actions' const program = new Command() -program - .argument('') - .option('-d, --debug', 'Set the debug level') - .action((action, options, command) => { - parseAction(action) - }) +program.argument('').action((action) => { + executeAction(action) +}) program.parse() diff --git a/scripts/cli/types/index.ts b/scripts/cli/types/index.ts deleted file mode 100644 index dbaa4ee3..00000000 --- a/scripts/cli/types/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -// TODO: get the union from CLI_ACTIONS const -export type CliActions = 'generate' | 'g' | 'bootstrap' | 'b' 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 index acdc7a3f..494b7ba0 100644 --- a/scripts/cli/utils/getDirectoryNames.ts +++ b/scripts/cli/utils/getDirectoryNames.ts @@ -1,5 +1,7 @@ import fs from 'fs' +import { logger } from './logger' + /** * Retrieves the names of all sub-directories in the specified directory. * @@ -8,6 +10,7 @@ import fs from 'fs' */ export const getDirectoryNames = (directoryPath: string): string[] => { try { + if (directoryPath.includes('new-tab')) return [] // Read the contents of the directory const contents = fs.readdirSync(directoryPath) @@ -19,7 +22,7 @@ export const getDirectoryNames = (directoryPath: string): string[] => { return folderNames } catch (err) { - console.error(`Error reading directory: ${err}`) + logger.error(`Error reading directory: ${err}`) return [] } } diff --git a/scripts/cli/utils/index.ts b/scripts/cli/utils/index.ts index c92afe7c..b2d0878f 100644 --- a/scripts/cli/utils/index.ts +++ b/scripts/cli/utils/index.ts @@ -1 +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) + }, +} From 2b68ed5176c69d4e5c3cafec2c63d1319c78085a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sza=C5=82owski?= Date: Sat, 9 Mar 2024 19:41:52 +0100 Subject: [PATCH 05/47] feat: migrate bootstrap script to typescript --- .github/pull_request_template.md | 47 +--- package.json | 16 +- scripts/README.md | 11 +- scripts/bootstrap.js | 200 ---------------- scripts/cli/actions/bootstrap.ts | 206 +++++++++++++++- scripts/cli/build/actions/bootstrap.js | 196 ++++++++++++++- scripts/cli/build/constants.js | 15 +- scripts/cli/constants.ts | 8 + scripts/create_new_screen.js | 318 ------------------------- scripts/generate_icon_types.js | 17 -- 10 files changed, 439 insertions(+), 595 deletions(-) delete mode 100644 scripts/bootstrap.js delete mode 100644 scripts/create_new_screen.js delete mode 100644 scripts/generate_icon_types.js diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index c3e00217..f2df2a52 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,44 +1,11 @@ -# Description +# Describe your changes -Please include a summary of the changes and the related issue. Please also include relevant motivation and context. List any dependencies that are required for this change. +# Issue ticket number or link -Fixes # (issue) +# Checklist before requesting a review -## Type of change +- [ ] I did self-review of my code. +- [ ] Do the tests work ? +- [ ] Does lint work ?, it doesn't find any bugs or problems. -Please delete options that are not relevant. - -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] This change requires a documentation update - -# How Has This Been Tested? - -Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration - -- [ ] Test A -- [ ] Test B - -# Screenshot(s) - -Please attach some screenshot or a movie if provided changes affects UI. - -**Test Configuration**: - -- Firmware version: -- Hardware: -- Toolchain: -- SDK: - -# Checklist: - -- [ ] Add correct label to your pull request -- [ ] My code follows the style guidelines of this project -- [ ] I have performed a self-review of my code -- [ ] I have commented my code, particularly in hard-to-understand areas -- [ ] I have made corresponding changes to the documentation -- [ ] My changes generate no new warnings -- [ ] I have added tests that prove my fix is effective or that my feature works -- [ ] New and existing unit tests pass locally with my changes -- [ ] Any dependent changes have been merged and published in downstream modules +# Do any blockers exist ? if yes, describe it below. diff --git a/package.json b/package.json index 52b49106..ce2ddaf2 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,9 @@ "license": "MIT", "scripts": { "android:dev-client": "IS_DEV=1 npx expo run:android", - "bootstrap:new_app": "yarn && node ./scripts/bootstrap.js", + "baca:bootstrap": "node ./scripts/cli/build bootstrap", + "baca:generate": "node ./scripts/cli/build generate", + "build:baca-cli": "npx tsc -p ./scripts/cli/tsconfig.cli.json", "build:production:android": "yarn 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", @@ -43,20 +45,16 @@ "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", - "baca:generate": "node ./scripts/cli/build generate", "generate:query": "yarn orval --config ./orval.config.ts", "generate:component": "node ./scripts/create_new_component.js && yarn eslint src --fix && yarn tsc", "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:query": "yarn orval --config ./orval.config.ts", - "generate:screen": "node ./scripts/create_new_screen.js && yarn eslint src --fix && yarn tsc", - "lint:fix": "eslint src --fix", "lint": "eslint src && yarn tsc", - "postinstall": "patch-package", + "lint:fix": "eslint src --fix", + "login": "expo login", + "logout": "expo logout", + "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", diff --git a/scripts/README.md b/scripts/README.md index cb481bef..03911a21 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -44,20 +44,15 @@ yarn prepare:qa yarn prepare:staging ``` -## 3. `generate_icon_types.sh` - -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 ```bash ## Create new component -yarn generate:screen +yarn generate:component ## Create new screen -yarn generate:component +yarn baca:generate ## Bootstrap the app -yarn bootstrap:new_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 index 419e225e..64a3b09f 100644 --- a/scripts/cli/actions/bootstrap.ts +++ b/scripts/cli/actions/bootstrap.ts @@ -1,3 +1,207 @@ +import fs from 'fs' +import promptSync from 'prompt-sync' + +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 prompt = promptSync() + +/** + * 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: string, organizationOwner: string) => { + 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: string, + bundleId: string, + androidPackageName: string, + scheme: string, + easId: string, + androidIconColor: string +) => { + 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 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(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: string, appSlug: string, organizationOwner: string) => { + const newAppJson = JSON.parse(fs.readFileSync(APP_JSON_PATH, 'utf8')) + newAppJson.expo.slug = appSlug + newAppJson.expo.name = appName + newAppJson.expo.owner = organizationOwner + newAppJson.version = '1.0.0' + fs.writeFileSync(APP_JSON_PATH, JSON.stringify(newAppJson, null, 2)) +} + +const changePackageJson = (appName: string, organizationOwner: string) => { + 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)) +} + +const removeIssueTemplates = () => { + fs.rm('./.github/ISSUE_TEMPLATE', { recursive: true, force: true }, () => {}) +} + +const removeDocsFolder = () => { + fs.rm('./documentation', { recursive: true, force: true }, () => {}) +} + +const setUpProject = async ( + appName: string, + bundleId: string, + androidPackageName: string, + scheme: string, + easId: string, + organizationOwner: string, + androidIconColor: string, + appSlug: string +) => { + // 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') + replacePullRequestTemplate() + + // 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`) +} + export const bootstrap = () => { - console.log('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') + } + + 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') } diff --git a/scripts/cli/build/actions/bootstrap.js b/scripts/cli/build/actions/bootstrap.js index 99a3125d..8c364e69 100644 --- a/scripts/cli/build/actions/bootstrap.js +++ b/scripts/cli/build/actions/bootstrap.js @@ -1,7 +1,201 @@ 'use strict' +var __awaiter = + (this && this.__awaiter) || + function (thisArg, _arguments, P, generator) { + function adopt(value) { + return value instanceof P + ? value + : new P(function (resolve) { + resolve(value) + }) + } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { + try { + step(generator.next(value)) + } catch (e) { + reject(e) + } + } + function rejected(value) { + try { + step(generator['throw'](value)) + } catch (e) { + reject(e) + } + } + function step(result) { + result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected) + } + step((generator = generator.apply(thisArg, _arguments || [])).next()) + }) + } +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod } + } Object.defineProperty(exports, '__esModule', { value: true }) exports.bootstrap = void 0 +const fs_1 = __importDefault(require('fs')) +const prompt_sync_1 = __importDefault(require('prompt-sync')) +const constants_1 = require('../constants') +const utils_1 = require('../utils') +const prompt = (0, prompt_sync_1.default)() +/** + * 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) => { + let contents = fs_1.default.readFileSync(constants_1.README_PATH, 'utf-8') + contents = contents.replaceAll('_NAME_', appName) + contents = contents.replaceAll('_OWNER_', organizationOwner) + fs_1.default.writeFileSync(constants_1.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) => { + let contents = fs_1.default.readFileSync(constants_1.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 url scheme to link to your app +} as const +` + contents = contents.replace(/(\/\/ APP_CONFIG_START)[\s\S]*?(\/\/ APP_CONFIG_END)/g, '$1$2') + contents = (0, utils_1.addAfter)(contents, '// APP_CONFIG_START', `${appConfig}`) + fs_1.default.writeFileSync(constants_1.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_1.default.readFileSync(constants_1.NEW_PULL_REQUEST_TEMPLATE_PATH, 'utf8') + fs_1.default.writeFileSync(constants_1.PULL_REQUEST_TEMPLATE_PATH, contents) +} +const changeAppJson = (appName, appSlug, organizationOwner) => { + const newAppJson = JSON.parse(fs_1.default.readFileSync(constants_1.APP_JSON_PATH, 'utf8')) + newAppJson.expo.slug = appSlug + newAppJson.expo.name = appName + newAppJson.expo.owner = organizationOwner + newAppJson.version = '1.0.0' + fs_1.default.writeFileSync(constants_1.APP_JSON_PATH, JSON.stringify(newAppJson, null, 2)) +} +const changePackageJson = (appName, organizationOwner) => { + const packageJson = JSON.parse(fs_1.default.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_1.default.writeFileSync('./package.json', JSON.stringify(packageJson, null, 2)) +} +const removeIssueTemplates = () => { + fs_1.default.rm('./.github/ISSUE_TEMPLATE', { recursive: true, force: true }, () => {}) +} +const removeDocsFolder = () => { + fs_1.default.rm('./documentation', { recursive: true, force: true }, () => {}) +} +const setUpProject = ( + appName, + bundleId, + androidPackageName, + scheme, + easId, + organizationOwner, + androidIconColor, + appSlug +) => + __awaiter(void 0, void 0, void 0, function* () { + // START + utils_1.logger.success('Start ...') + // 1. Delete readme -> and create new, with new app name etc. + utils_1.logger.info('Generating new readme file') + replaceReadme(appName, organizationOwner) + // 2. Replace appName, bundleId, androidPackageName,scheme and easProjectId in app.config.ts file + utils_1.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 + utils_1.logger.info('Generating new pull request template file') + replacePullRequestTemplate() + // 4. Change app.json file + utils_1.logger.info('Change app.json file') + changeAppJson(appName, appSlug, organizationOwner) + // 5. Change package.json file + utils_1.logger.info('Change package.json file') + changePackageJson(appName, organizationOwner) + // 6. Remove issue templates + utils_1.logger.info('Remove issue templates') + removeIssueTemplates() + // 7. Remove docs folder + utils_1.logger.info('Remove docs folder') + removeDocsFolder() + //Finish + utils_1.logger.success(`Config your project has been success`) + }) const bootstrap = () => { - console.log('Bootstrap') + utils_1.logger.info('Please give me this information to setup your project:') + const appName = prompt('App name: ') + if (!appName) { + return utils_1.logger.error('Please write correct app name') + } + const appSlug = prompt('App slug (from expo dashboard): ') + if (!appSlug) { + return utils_1.logger.error('Please write app slug') + } + const organizationOwner = prompt('Organization owner (from expo dashboard): ') + if (!organizationOwner) { + return utils_1.logger.error('Please write organziation owner') + } + const easId = prompt('EAS project ID (from expo dashboard): ') + if (!easId) { + return utils_1.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 utils_1.logger.error('Please write correct bundle ID') + } + const androidPackageName = prompt('Package name (android): ') + if (!androidPackageName) { + return utils_1.logger.error('Please write correct android package name') + } + const scheme = prompt('URL scheme (for deeplinking): ') + if (!scheme) { + return utils_1.logger.error('Please write correct scheme') + } + setUpProject( + appName, + bundleId, + androidPackageName, + scheme, + easId, + organizationOwner, + androidIconColor, + appSlug + ) + utils_1.logger.info( + '\nYou can also add images right now, go to assets folder and replace images to match your app \n' + ) + utils_1.logger.info( + '\nPlease verify the changes made by this script and commit it to your repository \n' + ) } exports.bootstrap = bootstrap diff --git a/scripts/cli/build/constants.js b/scripts/cli/build/constants.js index 2004b55a..8ded1cf5 100644 --- a/scripts/cli/build/constants.js +++ b/scripts/cli/build/constants.js @@ -1,6 +1,12 @@ 'use strict' Object.defineProperty(exports, '__esModule', { value: true }) -exports.NAVIGATION_CONFIG_PATH = +exports.NEW_PULL_REQUEST_TEMPLATE_PATH = + exports.PULL_REQUEST_TEMPLATE_PATH = + exports.README_TEMPLATE_PATH = + exports.README_PATH = + exports.APP_CONFIG_PATH = + exports.APP_JSON_PATH = + exports.NAVIGATION_CONFIG_PATH = exports.SCREENS_DIRECTORY = exports.APP_ROUTER_DIRECTORY = exports.CLI_ACTIONS = @@ -8,4 +14,11 @@ exports.NAVIGATION_CONFIG_PATH = exports.CLI_ACTIONS = ['generate', 'g', 'bootstrap', 'b'] exports.APP_ROUTER_DIRECTORY = 'app/(app)' exports.SCREENS_DIRECTORY = 'src/screens' +// Paths exports.NAVIGATION_CONFIG_PATH = 'navigation/tabNavigator/navigation-config.ts' +exports.APP_JSON_PATH = 'app.json' +exports.APP_CONFIG_PATH = 'app.config.ts' +exports.README_PATH = 'README.md' +exports.README_TEMPLATE_PATH = 'templates/README.md' +exports.PULL_REQUEST_TEMPLATE_PATH = '.github/pull_request_template.md' +exports.NEW_PULL_REQUEST_TEMPLATE_PATH = 'templates/pull_request_template.md' diff --git a/scripts/cli/constants.ts b/scripts/cli/constants.ts index 0cc00849..d856b514 100644 --- a/scripts/cli/constants.ts +++ b/scripts/cli/constants.ts @@ -2,4 +2,12 @@ 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 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/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) From 1f0e49f41989511097e4824cf139a77a4fcb2c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sza=C5=82owski?= Date: Sat, 9 Mar 2024 19:42:31 +0100 Subject: [PATCH 06/47] feat: code cleanup --- .github/pull_request_template.md | 47 +++- .gitignore | 5 +- scripts/cli/actions/generate.ts | 6 +- scripts/cli/build/actions/bootstrap.js | 201 ------------------ scripts/cli/build/actions/generate.js | 75 ------- scripts/cli/build/actions/index.js | 26 --- scripts/cli/build/actions/parseAction.js | 11 - .../cli/build/commands/generate.command.js | 1 - .../cli/build/commands/generateIconTypes.js | 21 -- scripts/cli/build/commands/generateScreen.js | 191 ----------------- scripts/cli/build/commands/index.js | 31 --- scripts/cli/build/constants.js | 24 --- scripts/cli/build/constants/index.js | 11 - scripts/cli/build/index.js | 9 - scripts/cli/build/types.js | 2 - scripts/cli/build/types/index.js | 2 - scripts/cli/build/utils/content.js | 38 ---- scripts/cli/build/utils/getDirectoryNames.js | 33 --- scripts/cli/build/utils/getFolderNames.js | 31 --- scripts/cli/build/utils/index.js | 32 --- scripts/cli/build/utils/logger.js | 21 -- scripts/cli/commands/generateScreen.ts | 2 +- 22 files changed, 49 insertions(+), 771 deletions(-) delete mode 100644 scripts/cli/build/actions/bootstrap.js delete mode 100644 scripts/cli/build/actions/generate.js delete mode 100644 scripts/cli/build/actions/index.js delete mode 100644 scripts/cli/build/actions/parseAction.js delete mode 100644 scripts/cli/build/commands/generate.command.js delete mode 100644 scripts/cli/build/commands/generateIconTypes.js delete mode 100644 scripts/cli/build/commands/generateScreen.js delete mode 100644 scripts/cli/build/commands/index.js delete mode 100644 scripts/cli/build/constants.js delete mode 100644 scripts/cli/build/constants/index.js delete mode 100644 scripts/cli/build/index.js delete mode 100644 scripts/cli/build/types.js delete mode 100644 scripts/cli/build/types/index.js delete mode 100644 scripts/cli/build/utils/content.js delete mode 100644 scripts/cli/build/utils/getDirectoryNames.js delete mode 100644 scripts/cli/build/utils/getFolderNames.js delete mode 100644 scripts/cli/build/utils/index.js delete mode 100644 scripts/cli/build/utils/logger.js diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f2df2a52..c3e00217 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,11 +1,44 @@ -# Describe your changes +# Description -# Issue ticket number or link +Please include a summary of the changes and the related issue. Please also include relevant motivation and context. List any dependencies that are required for this change. -# Checklist before requesting a review +Fixes # (issue) -- [ ] I did self-review of my code. -- [ ] Do the tests work ? -- [ ] Does lint work ?, it doesn't find any bugs or problems. +## Type of change -# Do any blockers exist ? if yes, describe it below. +Please delete options that are not relevant. + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +# How Has This Been Tested? + +Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration + +- [ ] Test A +- [ ] Test B + +# Screenshot(s) + +Please attach some screenshot or a movie if provided changes affects UI. + +**Test Configuration**: + +- Firmware version: +- Hardware: +- Toolchain: +- SDK: + +# Checklist: + +- [ ] Add correct label to your pull request +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes +- [ ] Any dependent changes have been merged and published in downstream modules diff --git a/.gitignore b/.gitignore index c49f35d8..473b3704 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,7 @@ GoogleService-Info.plist scripts/doppler_variables.sh .env -env.json \ No newline at end of file +env.json + +# baca-cli build +scripts/cli/build/ \ No newline at end of file diff --git a/scripts/cli/actions/generate.ts b/scripts/cli/actions/generate.ts index 093ed69f..66ca715b 100644 --- a/scripts/cli/actions/generate.ts +++ b/scripts/cli/actions/generate.ts @@ -4,6 +4,7 @@ import selectPrompt from 'select-prompt' import { generateIconTypes } from '../commands/generateIconTypes' import { generateScreen } from '../commands/generateScreen' +import { logger } from '../utils' const generatePrompts = [ { @@ -11,7 +12,7 @@ const generatePrompts = [ value: 'screen', }, { - title: 'Component', + title: 'Component TBD', value: 'component', }, { @@ -23,7 +24,8 @@ const generatePrompts = [ const commands = { screen: generateScreen, component: () => { - console.log('Generate Component') + logger.info('Not implemented yet.') + logger.info('Please use the `yarn generate:component` command.') }, 'icon-types': generateIconTypes, } diff --git a/scripts/cli/build/actions/bootstrap.js b/scripts/cli/build/actions/bootstrap.js deleted file mode 100644 index 8c364e69..00000000 --- a/scripts/cli/build/actions/bootstrap.js +++ /dev/null @@ -1,201 +0,0 @@ -'use strict' -var __awaiter = - (this && this.__awaiter) || - function (thisArg, _arguments, P, generator) { - function adopt(value) { - return value instanceof P - ? value - : new P(function (resolve) { - resolve(value) - }) - } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { - try { - step(generator.next(value)) - } catch (e) { - reject(e) - } - } - function rejected(value) { - try { - step(generator['throw'](value)) - } catch (e) { - reject(e) - } - } - function step(result) { - result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected) - } - step((generator = generator.apply(thisArg, _arguments || [])).next()) - }) - } -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod } - } -Object.defineProperty(exports, '__esModule', { value: true }) -exports.bootstrap = void 0 -const fs_1 = __importDefault(require('fs')) -const prompt_sync_1 = __importDefault(require('prompt-sync')) -const constants_1 = require('../constants') -const utils_1 = require('../utils') -const prompt = (0, prompt_sync_1.default)() -/** - * 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) => { - let contents = fs_1.default.readFileSync(constants_1.README_PATH, 'utf-8') - contents = contents.replaceAll('_NAME_', appName) - contents = contents.replaceAll('_OWNER_', organizationOwner) - fs_1.default.writeFileSync(constants_1.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) => { - let contents = fs_1.default.readFileSync(constants_1.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 url scheme to link to your app -} as const -` - contents = contents.replace(/(\/\/ APP_CONFIG_START)[\s\S]*?(\/\/ APP_CONFIG_END)/g, '$1$2') - contents = (0, utils_1.addAfter)(contents, '// APP_CONFIG_START', `${appConfig}`) - fs_1.default.writeFileSync(constants_1.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_1.default.readFileSync(constants_1.NEW_PULL_REQUEST_TEMPLATE_PATH, 'utf8') - fs_1.default.writeFileSync(constants_1.PULL_REQUEST_TEMPLATE_PATH, contents) -} -const changeAppJson = (appName, appSlug, organizationOwner) => { - const newAppJson = JSON.parse(fs_1.default.readFileSync(constants_1.APP_JSON_PATH, 'utf8')) - newAppJson.expo.slug = appSlug - newAppJson.expo.name = appName - newAppJson.expo.owner = organizationOwner - newAppJson.version = '1.0.0' - fs_1.default.writeFileSync(constants_1.APP_JSON_PATH, JSON.stringify(newAppJson, null, 2)) -} -const changePackageJson = (appName, organizationOwner) => { - const packageJson = JSON.parse(fs_1.default.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_1.default.writeFileSync('./package.json', JSON.stringify(packageJson, null, 2)) -} -const removeIssueTemplates = () => { - fs_1.default.rm('./.github/ISSUE_TEMPLATE', { recursive: true, force: true }, () => {}) -} -const removeDocsFolder = () => { - fs_1.default.rm('./documentation', { recursive: true, force: true }, () => {}) -} -const setUpProject = ( - appName, - bundleId, - androidPackageName, - scheme, - easId, - organizationOwner, - androidIconColor, - appSlug -) => - __awaiter(void 0, void 0, void 0, function* () { - // START - utils_1.logger.success('Start ...') - // 1. Delete readme -> and create new, with new app name etc. - utils_1.logger.info('Generating new readme file') - replaceReadme(appName, organizationOwner) - // 2. Replace appName, bundleId, androidPackageName,scheme and easProjectId in app.config.ts file - utils_1.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 - utils_1.logger.info('Generating new pull request template file') - replacePullRequestTemplate() - // 4. Change app.json file - utils_1.logger.info('Change app.json file') - changeAppJson(appName, appSlug, organizationOwner) - // 5. Change package.json file - utils_1.logger.info('Change package.json file') - changePackageJson(appName, organizationOwner) - // 6. Remove issue templates - utils_1.logger.info('Remove issue templates') - removeIssueTemplates() - // 7. Remove docs folder - utils_1.logger.info('Remove docs folder') - removeDocsFolder() - //Finish - utils_1.logger.success(`Config your project has been success`) - }) -const bootstrap = () => { - utils_1.logger.info('Please give me this information to setup your project:') - const appName = prompt('App name: ') - if (!appName) { - return utils_1.logger.error('Please write correct app name') - } - const appSlug = prompt('App slug (from expo dashboard): ') - if (!appSlug) { - return utils_1.logger.error('Please write app slug') - } - const organizationOwner = prompt('Organization owner (from expo dashboard): ') - if (!organizationOwner) { - return utils_1.logger.error('Please write organziation owner') - } - const easId = prompt('EAS project ID (from expo dashboard): ') - if (!easId) { - return utils_1.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 utils_1.logger.error('Please write correct bundle ID') - } - const androidPackageName = prompt('Package name (android): ') - if (!androidPackageName) { - return utils_1.logger.error('Please write correct android package name') - } - const scheme = prompt('URL scheme (for deeplinking): ') - if (!scheme) { - return utils_1.logger.error('Please write correct scheme') - } - setUpProject( - appName, - bundleId, - androidPackageName, - scheme, - easId, - organizationOwner, - androidIconColor, - appSlug - ) - utils_1.logger.info( - '\nYou can also add images right now, go to assets folder and replace images to match your app \n' - ) - utils_1.logger.info( - '\nPlease verify the changes made by this script and commit it to your repository \n' - ) -} -exports.bootstrap = bootstrap diff --git a/scripts/cli/build/actions/generate.js b/scripts/cli/build/actions/generate.js deleted file mode 100644 index 12d8d7fa..00000000 --- a/scripts/cli/build/actions/generate.js +++ /dev/null @@ -1,75 +0,0 @@ -'use strict' -var __awaiter = - (this && this.__awaiter) || - function (thisArg, _arguments, P, generator) { - function adopt(value) { - return value instanceof P - ? value - : new P(function (resolve) { - resolve(value) - }) - } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { - try { - step(generator.next(value)) - } catch (e) { - reject(e) - } - } - function rejected(value) { - try { - step(generator['throw'](value)) - } catch (e) { - reject(e) - } - } - function step(result) { - result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected) - } - step((generator = generator.apply(thisArg, _arguments || [])).next()) - }) - } -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod } - } -Object.defineProperty(exports, '__esModule', { value: true }) -exports.generate = void 0 -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -const select_prompt_1 = __importDefault(require('select-prompt')) -const generateIconTypes_1 = require('../commands/generateIconTypes') -const generateScreen_1 = require('../commands/generateScreen') -const generatePrompts = [ - { - title: 'Screen', - value: 'screen', - }, - { - title: 'Component', - value: 'component', - }, - { - title: 'Icon types', - value: 'icon-types', - }, -] -const commands = { - screen: generateScreen_1.generateScreen, - component: () => { - console.log('Generate Component') - }, - 'icon-types': generateIconTypes_1.generateIconTypes, -} -const generate = () => { - ;(0, select_prompt_1.default)('What do you want to generate?', generatePrompts).on( - 'submit', - (value) => - __awaiter(void 0, void 0, void 0, function* () { - commands[value]() - }) - ) -} -exports.generate = generate diff --git a/scripts/cli/build/actions/index.js b/scripts/cli/build/actions/index.js deleted file mode 100644 index 8ee49f5c..00000000 --- a/scripts/cli/build/actions/index.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict' -Object.defineProperty(exports, '__esModule', { value: true }) -exports.executeAction = void 0 -const bootstrap_1 = require('./bootstrap') -const generate_1 = require('./generate') -const constants_1 = require('../constants') -const utils_1 = require('../utils') -const actions = { - generate: generate_1.generate, - g: generate_1.generate, - bootstrap: bootstrap_1.bootstrap, - b: bootstrap_1.bootstrap, -} -/** - * Parses and executes the specified CLI action. - * - * @param action - The CLI action to parse and execute. - */ -const executeAction = (action) => { - if (!constants_1.CLI_ACTIONS.includes(action)) { - utils_1.logger.error(`Action ${action} is not supported.`) - return - } - actions[action]() -} -exports.executeAction = executeAction diff --git a/scripts/cli/build/actions/parseAction.js b/scripts/cli/build/actions/parseAction.js deleted file mode 100644 index b2439ccd..00000000 --- a/scripts/cli/build/actions/parseAction.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict' -Object.defineProperty(exports, '__esModule', { value: true }) -exports.parseAction = void 0 -const constants_1 = require('../constants') -const parseAction = (action) => { - if (!constants_1.CLI_ACTIONS.includes(action)) { - console.error('\x1b[31m%s\x1b[0m', `Invalid action ${action}`) - return - } -} -exports.parseAction = parseAction diff --git a/scripts/cli/build/commands/generate.command.js b/scripts/cli/build/commands/generate.command.js deleted file mode 100644 index ccacec30..00000000 --- a/scripts/cli/build/commands/generate.command.js +++ /dev/null @@ -1 +0,0 @@ -'use strict' diff --git a/scripts/cli/build/commands/generateIconTypes.js b/scripts/cli/build/commands/generateIconTypes.js deleted file mode 100644 index 9717bc93..00000000 --- a/scripts/cli/build/commands/generateIconTypes.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod } - } -Object.defineProperty(exports, '__esModule', { value: true }) -exports.generateIconTypes = void 0 -const fs_1 = __importDefault(require('fs')) -const prefix = `export type IconNames = - | ` -const generateIconTypes = () => { - const json = fs_1.default.readFileSync('./assets/icomoon/selection.json') - const types = JSON.parse(json.toString()) - .icons.map((icon) => `'${icon.properties.name}'`) - .join('\n | ') - .concat('\n') - const content = prefix + types - fs_1.default.writeFileSync('./src/types/icon.d.ts', content) -} -exports.generateIconTypes = generateIconTypes diff --git a/scripts/cli/build/commands/generateScreen.js b/scripts/cli/build/commands/generateScreen.js deleted file mode 100644 index f129abfd..00000000 --- a/scripts/cli/build/commands/generateScreen.js +++ /dev/null @@ -1,191 +0,0 @@ -'use strict' -var __awaiter = - (this && this.__awaiter) || - function (thisArg, _arguments, P, generator) { - function adopt(value) { - return value instanceof P - ? value - : new P(function (resolve) { - resolve(value) - }) - } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { - try { - step(generator.next(value)) - } catch (e) { - reject(e) - } - } - function rejected(value) { - try { - step(generator['throw'](value)) - } catch (e) { - reject(e) - } - } - function step(result) { - result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected) - } - step((generator = generator.apply(thisArg, _arguments || [])).next()) - }) - } -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod } - } -Object.defineProperty(exports, '__esModule', { value: true }) -exports.generateScreen = void 0 -const fs_1 = __importDefault(require('fs')) -const prompt_sync_1 = __importDefault(require('prompt-sync')) -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -const select_prompt_1 = __importDefault(require('select-prompt')) -const constants_1 = require('../constants') -const utils_1 = require('../utils') -const addAfter = (content, searchText, textToAdd) => { - 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 = (basePath) => - new Promise((resolve) => { - const subDirectories = (0, utils_1.getDirectoryNames)(basePath) - const hasSubDirectories = subDirectories.length > 0 - // Return the result when there are no subdirectories - if (!hasSubDirectories) { - resolve(basePath) - return - } - const subDirectoryPrompt = subDirectories.map((directoryName) => ({ - title: directoryName, - value: directoryName, - })) - if (basePath.includes('tabs')) { - subDirectoryPrompt.unshift({ title: '_New Tab_', value: 'new-tab' }) - } - if (basePath !== constants_1.APP_ROUTER_DIRECTORY) { - subDirectoryPrompt.unshift({ title: '.', value: '.' }) - } - ;(0, select_prompt_1.default)(`Select a directory (${basePath})`, subDirectoryPrompt, { - cursor: 0, - }).on('submit', (subValue) => { - // Return the result when user selects current directory - if (subValue === '.') { - resolve(basePath) - return - } - // Recursively execute path selection when subValue is not the current directory - selectPath(`${basePath}/${subValue}`).then((subResult) => { - resolve(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, routePath) => { - const filePath = `${routePath}/${routeName}.tsx` - if (fs_1.default.existsSync(filePath)) { - utils_1.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, routePath) => { - const screenName = `${routeName.charAt(0).toUpperCase() + routeName.slice(1)}Screen` - fs_1.default.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) => { - const filePath = `${constants_1.SCREENS_DIRECTORY}/${screenName}.tsx` - if (fs_1.default.existsSync(filePath)) { - utils_1.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) => { - const screenFromFile = fs_1.default.readFileSync('./templates/screen_template.tsx', 'utf8') - const screenContent = screenFromFile - .replaceAll('_NAME_', screenName) - .replace("// @ts-expect-error: it's a template and will be removed", '') - fs_1.default.writeFileSync(`${constants_1.SCREENS_DIRECTORY}/${screenName}.tsx`, screenContent) -} -const addToScreensIndex = (screenName) => { - const indexFilePath = `${constants_1.SCREENS_DIRECTORY}/index.ts` - const indexFile = fs_1.default.readFileSync(indexFilePath, 'utf8') - const newIndexFile = indexFile.padEnd(indexFile.length) + `export * from './${screenName}'\n` - fs_1.default.writeFileSync(indexFilePath, newIndexFile) -} -const promptTabName = () => { - const tabName = (0, prompt_sync_1.default)()('Enter tab name: ') - if (!tabName) { - throw new Error('Tab name is required') - } - return tabName -} -const createNewNavTab = (tabName) => { - const navigationConfigFile = fs_1.default.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_1.default.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. - */ -const generateScreen = () => - __awaiter(void 0, void 0, void 0, function* () { - const routeName = (0, prompt_sync_1.default)()('Enter screen name: ') - let routePath = yield selectPath(constants_1.APP_ROUTER_DIRECTORY) - const isNewTab = routePath.includes('(tabs)') && routePath.includes('new-tab') - if (isNewTab) { - const tabName = promptTabName() - createNewNavTab(tabName) - const newTabPath = routePath.replace('/new-tab', `/${tabName}`) - routePath = newTabPath - console.log({ newTabPath, routePath }) - fs_1.default.mkdirSync(newTabPath) - } - validateRoute(routeName, routePath) - createRouteFile(routeName, routePath) - const screenName = `${routeName.charAt(0).toUpperCase() + routeName.slice(1)}Screen` - validateScreen(screenName) - createScreenFile(screenName) - addToScreensIndex(screenName) - }) -exports.generateScreen = generateScreen diff --git a/scripts/cli/build/commands/index.js b/scripts/cli/build/commands/index.js deleted file mode 100644 index c38b9d85..00000000 --- a/scripts/cli/build/commands/index.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict' -var __createBinding = - (this && this.__createBinding) || - (Object.create - ? function (o, m, k, k2) { - if (k2 === undefined) k2 = k - var desc = Object.getOwnPropertyDescriptor(m, k) - if (!desc || ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { - enumerable: true, - get: function () { - return m[k] - }, - } - } - Object.defineProperty(o, k2, desc) - } - : function (o, m, k, k2) { - if (k2 === undefined) k2 = k - o[k2] = m[k] - }) -var __exportStar = - (this && this.__exportStar) || - function (m, exports) { - for (var p in m) - if (p !== 'default' && !Object.prototype.hasOwnProperty.call(exports, p)) - __createBinding(exports, m, p) - } -Object.defineProperty(exports, '__esModule', { value: true }) -__exportStar(require('./generateIconTypes'), exports) -__exportStar(require('./generateScreen'), exports) diff --git a/scripts/cli/build/constants.js b/scripts/cli/build/constants.js deleted file mode 100644 index 8ded1cf5..00000000 --- a/scripts/cli/build/constants.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict' -Object.defineProperty(exports, '__esModule', { value: true }) -exports.NEW_PULL_REQUEST_TEMPLATE_PATH = - exports.PULL_REQUEST_TEMPLATE_PATH = - exports.README_TEMPLATE_PATH = - exports.README_PATH = - exports.APP_CONFIG_PATH = - exports.APP_JSON_PATH = - exports.NAVIGATION_CONFIG_PATH = - exports.SCREENS_DIRECTORY = - exports.APP_ROUTER_DIRECTORY = - exports.CLI_ACTIONS = - void 0 -exports.CLI_ACTIONS = ['generate', 'g', 'bootstrap', 'b'] -exports.APP_ROUTER_DIRECTORY = 'app/(app)' -exports.SCREENS_DIRECTORY = 'src/screens' -// Paths -exports.NAVIGATION_CONFIG_PATH = 'navigation/tabNavigator/navigation-config.ts' -exports.APP_JSON_PATH = 'app.json' -exports.APP_CONFIG_PATH = 'app.config.ts' -exports.README_PATH = 'README.md' -exports.README_TEMPLATE_PATH = 'templates/README.md' -exports.PULL_REQUEST_TEMPLATE_PATH = '.github/pull_request_template.md' -exports.NEW_PULL_REQUEST_TEMPLATE_PATH = 'templates/pull_request_template.md' diff --git a/scripts/cli/build/constants/index.js b/scripts/cli/build/constants/index.js deleted file mode 100644 index 3b00dc1b..00000000 --- a/scripts/cli/build/constants/index.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict' -Object.defineProperty(exports, '__esModule', { value: true }) -exports.NAVIGATION_CONFIG_PATH = - exports.SCREENS_DIRECTORY = - exports.APP_ROUTER_DIRECTORY = - exports.CLI_ACTIONS = - void 0 -exports.CLI_ACTIONS = ['generate', 'g', 'bootstrap', 'b'] -exports.APP_ROUTER_DIRECTORY = `${process.cwd()}/app/(app)` -exports.SCREENS_DIRECTORY = `${process.cwd()}/src/screens` -exports.NAVIGATION_CONFIG_PATH = `${process.cwd()}/navigation/tabNavigator/navigation-config.ts` diff --git a/scripts/cli/build/index.js b/scripts/cli/build/index.js deleted file mode 100644 index 26dba5af..00000000 --- a/scripts/cli/build/index.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' -Object.defineProperty(exports, '__esModule', { value: true }) -const commander_1 = require('commander') -const actions_1 = require('./actions') -const program = new commander_1.Command() -program.argument('').action((action) => { - ;(0, actions_1.executeAction)(action) -}) -program.parse() diff --git a/scripts/cli/build/types.js b/scripts/cli/build/types.js deleted file mode 100644 index 0a483a32..00000000 --- a/scripts/cli/build/types.js +++ /dev/null @@ -1,2 +0,0 @@ -'use strict' -Object.defineProperty(exports, '__esModule', { value: true }) diff --git a/scripts/cli/build/types/index.js b/scripts/cli/build/types/index.js deleted file mode 100644 index 0a483a32..00000000 --- a/scripts/cli/build/types/index.js +++ /dev/null @@ -1,2 +0,0 @@ -'use strict' -Object.defineProperty(exports, '__esModule', { value: true }) diff --git a/scripts/cli/build/utils/content.js b/scripts/cli/build/utils/content.js deleted file mode 100644 index 88b6e409..00000000 --- a/scripts/cli/build/utils/content.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict' -Object.defineProperty(exports, '__esModule', { value: true }) -exports.deleteText = exports.addBefore = exports.addAfter = void 0 -/** - * 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. - */ -const addAfter = (content, searchText, textToAdd) => { - return content.replace(searchText, searchText + textToAdd) -} -exports.addAfter = addAfter -/** - * 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. - */ -const addBefore = (content, searchText, textToAdd) => { - return content.replace(searchText, textToAdd + searchText) -} -exports.addBefore = addBefore -/** - * 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. - */ -const deleteText = (content, searchText) => { - return content.replace(searchText, '') -} -exports.deleteText = deleteText diff --git a/scripts/cli/build/utils/getDirectoryNames.js b/scripts/cli/build/utils/getDirectoryNames.js deleted file mode 100644 index aa233144..00000000 --- a/scripts/cli/build/utils/getDirectoryNames.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict' -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod } - } -Object.defineProperty(exports, '__esModule', { value: true }) -exports.getDirectoryNames = void 0 -const fs_1 = __importDefault(require('fs')) -const logger_1 = require('./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. - */ -const getDirectoryNames = (directoryPath) => { - try { - if (directoryPath.includes('new-tab')) return [] - // Read the contents of the directory - const contents = fs_1.default.readdirSync(directoryPath) - // Filter out only directories - const folderNames = contents.filter((item) => { - const stat = fs_1.default.statSync(`${directoryPath}/${item}`) - return stat.isDirectory() - }) - return folderNames - } catch (err) { - logger_1.logger.error(`Error reading directory: ${err}`) - return [] - } -} -exports.getDirectoryNames = getDirectoryNames diff --git a/scripts/cli/build/utils/getFolderNames.js b/scripts/cli/build/utils/getFolderNames.js deleted file mode 100644 index 78b9895b..00000000 --- a/scripts/cli/build/utils/getFolderNames.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict' -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod } - } -Object.defineProperty(exports, '__esModule', { value: true }) -exports.getFolderNames = void 0 -const fs_1 = __importDefault(require('fs')) -/** - * Retrieves the names of all folders in the specified directory. - * - * @param directoryPath - The path of the directory to retrieve folder names from. - * @returns An array of folder names. - */ -const getFolderNames = (directoryPath) => { - try { - // Read the contents of the directory - const contents = fs_1.default.readdirSync(directoryPath) - // Filter out only directories - const folderNames = contents.filter((item) => { - const stat = fs_1.default.statSync(`${directoryPath}/${item}`) - return stat.isDirectory() - }) - return folderNames - } catch (err) { - console.error(`Error reading directory: ${err}`) - return [] - } -} -exports.getFolderNames = getFolderNames diff --git a/scripts/cli/build/utils/index.js b/scripts/cli/build/utils/index.js deleted file mode 100644 index 3b11ce9e..00000000 --- a/scripts/cli/build/utils/index.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict' -var __createBinding = - (this && this.__createBinding) || - (Object.create - ? function (o, m, k, k2) { - if (k2 === undefined) k2 = k - var desc = Object.getOwnPropertyDescriptor(m, k) - if (!desc || ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { - enumerable: true, - get: function () { - return m[k] - }, - } - } - Object.defineProperty(o, k2, desc) - } - : function (o, m, k, k2) { - if (k2 === undefined) k2 = k - o[k2] = m[k] - }) -var __exportStar = - (this && this.__exportStar) || - function (m, exports) { - for (var p in m) - if (p !== 'default' && !Object.prototype.hasOwnProperty.call(exports, p)) - __createBinding(exports, m, p) - } -Object.defineProperty(exports, '__esModule', { value: true }) -__exportStar(require('./getDirectoryNames'), exports) -__exportStar(require('./content'), exports) -__exportStar(require('./logger'), exports) diff --git a/scripts/cli/build/utils/logger.js b/scripts/cli/build/utils/logger.js deleted file mode 100644 index e0100c0a..00000000 --- a/scripts/cli/build/utils/logger.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' -Object.defineProperty(exports, '__esModule', { value: true }) -exports.logger = void 0 -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 -exports.logger = { - error: (message, ...optionalParams) => { - console.error(LCERROR, message, ...optionalParams) - }, - warn: (message, ...optionalParams) => { - console.warn(LCWARN, message, ...optionalParams) - }, - info: (message, ...optionalParams) => { - console.info(LCINFO, message, ...optionalParams) - }, - success: (message, ...optionalParams) => { - console.info(LCSUCCESS, message, ...optionalParams) - }, -} diff --git a/scripts/cli/commands/generateScreen.ts b/scripts/cli/commands/generateScreen.ts index b2d70637..0896464d 100644 --- a/scripts/cli/commands/generateScreen.ts +++ b/scripts/cli/commands/generateScreen.ts @@ -160,7 +160,7 @@ export const generateScreen = async () => { const newTabPath = routePath.replace('/new-tab', `/${tabName}`) routePath = newTabPath - console.log({ newTabPath, routePath }) + fs.mkdirSync(newTabPath) } From 500a695e4fcc464a0e9fbc9343c2ff526d7a67a3 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Sun, 10 Mar 2024 18:10:06 +0100 Subject: [PATCH 07/47] chore: update scripts in package json --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index ce2ddaf2..e7d9f793 100644 --- a/package.json +++ b/package.json @@ -45,15 +45,15 @@ "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", - "generate:query": "yarn orval --config ./orval.config.ts", + "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", "generate:env:production": "scripts/generate_dotenv.sh production", "generate:env:qa": "scripts/generate_dotenv.sh qa", "generate:env:staging": "scripts/generate_dotenv.sh staging", - "lint": "eslint src && yarn tsc", + "generate:google-services-config": "./scripts/generate_firebase_config.sh", + "generate:query": "yarn orval --config ./orval.config.ts", "lint:fix": "eslint src --fix", - "login": "expo login", - "logout": "expo logout", + "lint": "eslint src && yarn tsc", "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", From c3d5f39fd264067b6c4ba252f37738659b8cc199 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Sun, 10 Mar 2024 18:15:17 +0100 Subject: [PATCH 08/47] chore: remove not needed ts-expect-error directive --- scripts/cli/commands/generateScreen.ts | 4 +--- templates/screen_template.tsx | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/cli/commands/generateScreen.ts b/scripts/cli/commands/generateScreen.ts index 0896464d..6925fd00 100644 --- a/scripts/cli/commands/generateScreen.ts +++ b/scripts/cli/commands/generateScreen.ts @@ -102,9 +102,7 @@ const validateScreen = (screenName: string) => { */ const createScreenFile = (screenName: string) => { const screenFromFile = fs.readFileSync('./templates/screen_template.tsx', 'utf8') - const screenContent = screenFromFile - .replaceAll('_NAME_', screenName) - .replace("// @ts-expect-error: it's a template and will be removed", '') + const screenContent = screenFromFile.replaceAll('_NAME_', screenName) fs.writeFileSync(`${SCREENS_DIRECTORY}/${screenName}.tsx`, screenContent) } diff --git a/templates/screen_template.tsx b/templates/screen_template.tsx index 113ac472..2ad19f16 100644 --- a/templates/screen_template.tsx +++ b/templates/screen_template.tsx @@ -1,6 +1,6 @@ import { Center, Text } from '@baca/design-system' import { useTranslation } from '@baca/hooks' -// @ts-expect-error: it's a template and will be removed + export const _NAME_ = (): JSX.Element => { const { t } = useTranslation() From aa5fefb38c817dc5404ea65520b2da1e70a81a51 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Mon, 1 Apr 2024 00:23:37 +0200 Subject: [PATCH 09/47] chore: move generate theme to scripts --- scripts/cli/actions/generate.ts | 6 ++ .../commands/generateTheme.ts} | 81 +++++++++++-------- scripts/cli/commands/index.ts | 1 + scripts/cli/tsconfig.cli.json | 2 + 4 files changed, 55 insertions(+), 35 deletions(-) rename scripts/{generate_theme.js => cli/commands/generateTheme.ts} (56%) diff --git a/scripts/cli/actions/generate.ts b/scripts/cli/actions/generate.ts index 66ca715b..cf8798d7 100644 --- a/scripts/cli/actions/generate.ts +++ b/scripts/cli/actions/generate.ts @@ -4,6 +4,7 @@ import selectPrompt from 'select-prompt' import { generateIconTypes } from '../commands/generateIconTypes' import { generateScreen } from '../commands/generateScreen' +import { generateTheme } from '../commands/generateTheme' import { logger } from '../utils' const generatePrompts = [ @@ -19,6 +20,10 @@ const generatePrompts = [ title: 'Icon types', value: 'icon-types', }, + { + title: 'Generate theme', + value: 'generate-theme', + }, ] const commands = { @@ -28,6 +33,7 @@ const commands = { logger.info('Please use the `yarn generate:component` command.') }, 'icon-types': generateIconTypes, + 'generate-theme': generateTheme, } export const generate = () => { 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 index a20c51e2..10afde15 100644 --- a/scripts/cli/commands/index.ts +++ b/scripts/cli/commands/index.ts @@ -1,2 +1,3 @@ export * from './generateIconTypes' export * from './generateScreen' +export * from './generateTheme' diff --git a/scripts/cli/tsconfig.cli.json b/scripts/cli/tsconfig.cli.json index 1382bc7a..9d54d044 100644 --- a/scripts/cli/tsconfig.cli.json +++ b/scripts/cli/tsconfig.cli.json @@ -1,6 +1,8 @@ { + "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { "rootDir": ".", + "noImplicitAny": false, "outDir": "build", "strict": true, "lib": ["ESNext"], From c91692c1e6716823cfdee4d323da98cc73b026e4 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Mon, 1 Apr 2024 00:42:57 +0200 Subject: [PATCH 10/47] chore: add script to generate component --- scripts/cli/actions/generate.ts | 32 +++++----- scripts/cli/commands/generateComponent.ts | 76 +++++++++++++++++++++++ scripts/cli/constants.ts | 3 + 3 files changed, 96 insertions(+), 15 deletions(-) create mode 100644 scripts/cli/commands/generateComponent.ts diff --git a/scripts/cli/actions/generate.ts b/scripts/cli/actions/generate.ts index cf8798d7..845692d9 100644 --- a/scripts/cli/actions/generate.ts +++ b/scripts/cli/actions/generate.ts @@ -2,45 +2,47 @@ // @ts-ignore import selectPrompt from 'select-prompt' +import { generateComponent } from '../commands/generateComponent' import { generateIconTypes } from '../commands/generateIconTypes' import { generateScreen } from '../commands/generateScreen' import { generateTheme } from '../commands/generateTheme' -import { logger } from '../utils' -const generatePrompts = [ +const data = [ { title: 'Screen', value: 'screen', + command: generateScreen, }, { - title: 'Component TBD', + title: 'Component', value: 'component', + command: generateComponent, }, { title: 'Icon types', value: 'icon-types', + command: generateIconTypes, }, { title: 'Generate theme', value: 'generate-theme', + command: generateTheme, }, -] +] as const -const commands = { - screen: generateScreen, - component: () => { - logger.info('Not implemented yet.') - logger.info('Please use the `yarn generate:component` command.') - }, - 'icon-types': generateIconTypes, - 'generate-theme': generateTheme, -} +const generatePrompts = data.map(({ title, value }) => ({ title, value })) export const generate = () => { selectPrompt('What do you want to generate?', generatePrompts).on( 'submit', - async (value: keyof typeof commands) => { - commands[value]() + async (value: string) => { + const command = data.find((item) => item.value === value)?.command + + if (command) { + command() + } else { + console.log('There was some issue while running the script ', value) + } } ) } diff --git a/scripts/cli/commands/generateComponent.ts b/scripts/cli/commands/generateComponent.ts new file mode 100644 index 00000000..d0bc2d4d --- /dev/null +++ b/scripts/cli/commands/generateComponent.ts @@ -0,0 +1,76 @@ +import promptSync from 'prompt-sync' + +import { COMPONENT_TEMPLATE_PATH, COMPONENTS_PATH } from '../constants' +import { logger } from '../utils' + +const prompt = promptSync() + +/* eslint-disable @typescript-eslint/no-var-requires */ +const fs = require('fs') +const selectPrompt = require('select-prompt') + +const paths = { + template: COMPONENT_TEMPLATE_PATH, + componentsIndex: COMPONENTS_PATH, +} + +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') + const componentContent = componentFromFile.replaceAll('_NAME_', name) + + if (type === 'common') { + fs.writeFileSync(newCommonComponentPath, componentContent) + return + } + fs.writeFileSync(newAtomicComponentPath, componentContent) +} + +const addToIndex = (name: string, type: string) => { + const atomicComponentIndexPath = `./src/components/${type}s/index.ts` + const newExport = ` +export * from './${name}'` + if (type === 'common') { + const contents = fs.readFileSync(paths.componentsIndex, 'utf8') + fs.writeFileSync(paths.componentsIndex, contents + newExport) + + return + } + + const contents = fs.readFileSync(atomicComponentIndexPath, 'utf8') + fs.writeFileSync(atomicComponentIndexPath, contents + newExport) +} + +const generateNewComponent = async (name: string, type: string) => { + // Generate Component file + logger.info('Generating component files') + createComponentFile(name, type) + + // Add Component to index + addToIndex(name, type) + + // Finish + logger.success(`Component ${name} created successfully`) +} + +export const generateComponent = async () => { + const componentTypes = [ + { title: 'Molecule', value: 'molecule' }, + { title: 'Organism', value: 'organism' }, + { title: 'Common', value: 'common' }, + ] + + selectPrompt('Select type for new component', componentTypes, { + cursor: 0, + }).on('submit', async (type: string) => { + 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 generateNewComponent(name, type) + }) +} diff --git a/scripts/cli/constants.ts b/scripts/cli/constants.ts index d856b514..7b35d2f6 100644 --- a/scripts/cli/constants.ts +++ b/scripts/cli/constants.ts @@ -8,6 +8,9 @@ export const NAVIGATION_CONFIG_PATH = 'navigation/tabNavigator/navigation-config 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' From 32c544f8e18531d9f9c515a3321bdd5c055d41c3 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Mon, 1 Apr 2024 18:11:39 +0200 Subject: [PATCH 11/47] chore: add scripts to package json --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 003034dc..56cbb445 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,8 @@ "android:dev-client": "IS_DEV=1 npx expo run:android", "baca:bootstrap": "node ./scripts/cli/build bootstrap", "baca:generate": "node ./scripts/cli/build generate", + "generate": "yarn build:baca-cli && node ./scripts/cli/build generate", + "g": "node ./scripts/cli/build g && yarn eslint src --fix && yarn tsc", "build:baca-cli": "npx tsc -p ./scripts/cli/tsconfig.cli.json", "build:production:android": "yarn prepare:production && eas build --platform android --profile production", "build:production:ios": "yarn prepare:production && eas build --platform ios --profile production", From 0fa07e46dcfb17a67d55895fed22c16f79bc1f96 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Fri, 12 Apr 2024 10:37:59 +0200 Subject: [PATCH 12/47] chore: add scripts documentation --- docs/docs/tutorials/SCRIPTS.md | 68 ++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 docs/docs/tutorials/SCRIPTS.md diff --git a/docs/docs/tutorials/SCRIPTS.md b/docs/docs/tutorials/SCRIPTS.md new file mode 100644 index 00000000..07915de4 --- /dev/null +++ b/docs/docs/tutorials/SCRIPTS.md @@ -0,0 +1,68 @@ +--- +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`, 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. From d92adec83643ad4fd0dabaacf90a4ade9331022e Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Fri, 12 Apr 2024 10:38:17 +0200 Subject: [PATCH 13/47] chore: update package json scripts --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 1383c3b0..cb6d12a6 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "android:dev-client": "IS_DEV=1 npx expo run:android", "baca:bootstrap": "node ./scripts/cli/build bootstrap", "baca:generate": "node ./scripts/cli/build generate", - "generate": "yarn build:baca-cli && node ./scripts/cli/build generate", + "b": "node ./scripts/cli/build b && yarn eslint src --fix && yarn tsc", "g": "node ./scripts/cli/build g && yarn eslint src --fix && yarn tsc", "build:baca-cli": "npx tsc -p ./scripts/cli/tsconfig.cli.json", "build:production:android": "yarn prepare:production && eas build --platform android --profile production", @@ -49,7 +49,6 @@ "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", "generate:env:production": "scripts/generate_dotenv.sh production", "generate:env:qa": "scripts/generate_dotenv.sh qa", "generate:env:staging": "scripts/generate_dotenv.sh staging", From 1ab70802149ffa21bf0cd64214f26e940d4d7c48 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Fri, 12 Apr 2024 10:38:40 +0200 Subject: [PATCH 14/47] chore: update bootstrap docs --- docs/docs/bootstrap/BOOTSTRAP_INTRO.mdx | 2 +- docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/bootstrap/BOOTSTRAP_INTRO.mdx b/docs/docs/bootstrap/BOOTSTRAP_INTRO.mdx index 226de4bf..e6d7ff1e 100644 --- a/docs/docs/bootstrap/BOOTSTRAP_INTRO.mdx +++ b/docs/docs/bootstrap/BOOTSTRAP_INTRO.mdx @@ -16,7 +16,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 todo, to start development?** --- diff --git a/docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx b/docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx index 9d0fe62c..74743cd2 100644 --- a/docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx +++ b/docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx @@ -127,7 +127,7 @@ You can generate this data and save it in some notepad or somewhere else. ### **After you will collect all necessary data run this command:** ```sh -yarn bootstrap:new_app +yarn baca:bootstrap ``` ## Potential issues From 1cf6869f2f0242ef40899bfbf60143185fd0dc0e Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Sat, 13 Apr 2024 11:58:50 +0200 Subject: [PATCH 15/47] chore: improve bootstrap script --- .gitignore | 1 + package.json | 9 +- scripts/cli/actions/bootstrap.ts | 220 +++++++++++++++++++++---------- 3 files changed, 154 insertions(+), 76 deletions(-) diff --git a/.gitignore b/.gitignore index ba2e3697..6fdbfa9d 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,4 @@ env.json # baca-cli build scripts/cli/build/ +scripts/cli/temp/ diff --git a/package.json b/package.json index cb6d12a6..bde74f24 100644 --- a/package.json +++ b/package.json @@ -27,10 +27,10 @@ "android:dev-client": "IS_DEV=1 npx expo run:android", "baca:bootstrap": "node ./scripts/cli/build bootstrap", "baca:generate": "node ./scripts/cli/build generate", - "b": "node ./scripts/cli/build b && yarn eslint src --fix && yarn tsc", - "g": "node ./scripts/cli/build g && yarn eslint src --fix && yarn tsc", - "build:baca-cli": "npx tsc -p ./scripts/cli/tsconfig.cli.json", - "build:production:android": "yarn prepare:production && eas build --platform android --profile production", + "b": "yarn build:baca-cli && node ./scripts/cli/build b && yarn eslint src --fix && yarn tsc", + "g": "yarn build:baca-cli && node ./scripts/cli/build g && yarn eslint src --fix && yarn tsc", + "build:baca-cli": "mkdir -p scripts/cli/temp && cp app.config.ts scripts/cli/temp/app.config.ts && 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", @@ -183,6 +183,7 @@ "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", diff --git a/scripts/cli/actions/bootstrap.ts b/scripts/cli/actions/bootstrap.ts index 64a3b09f..a5e3b11b 100644 --- a/scripts/cli/actions/bootstrap.ts +++ b/scripts/cli/actions/bootstrap.ts @@ -1,5 +1,5 @@ +import { prompt } from 'enquirer' import fs from 'fs' -import promptSync from 'prompt-sync' import { README_PATH, @@ -8,9 +8,114 @@ import { PULL_REQUEST_TEMPLATE_PATH, APP_JSON_PATH, } from '../constants' +import { APP_CONFIG } from '../temp/app.config' import { logger, addAfter } from '../utils' -const prompt = promptSync() +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 +} + +// 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 + order: number + } +} + +const questionsObject: QuestionsObject = { + appName: { + type: 'text', + message: 'What is your app name?', + initial: APP_CONFIG.appName, + order: 1, + }, + bundleId: { + type: 'text', + message: 'What is your bundle Id?', + initial: APP_CONFIG.iosBundleIdentifier, + order: 2, + }, + androidPackageName: { + type: 'text', + message: 'What is your android package name?', + initial: APP_CONFIG.androidPackageName, + order: 3, + }, + scheme: { + type: 'text', + message: 'What is your scheme name?', + initial: APP_CONFIG.scheme, + order: 4, + }, + easId: { + type: 'text', + message: 'What is your eas id?', + initial: APP_CONFIG.easProjectId, + order: 5, + }, + organizationOwner: { + type: 'text', + message: 'What is your expo organization owner?', + initial: newAppJson.expo.owner, + order: 6, + }, + androidIconColor: { + type: 'text', + message: 'What is your android icon color?', + initial: APP_CONFIG.adaptiveIconBackgroundColor, + order: 7, + }, + appSlug: { + type: 'text', + message: 'What is your expo app slug?', + initial: newAppJson.expo.slug, + order: 8, + }, +} + +/** + * Creates temp files + * - app.config.ts + * + */ +const createTempFiles = () => { + return new Promise((resolve, reject) => { + try { + const exist = fs.existsSync('scripts/cli/temp/') + + if (!exist) { + fs.mkdirSync('scripts/cli/temp/') + } + + fs.copyFile('app.config.ts', 'scripts/cli/temp/app.config.ts', (err) => { + if (err) { + logger.error('ERROR WHEN CREATING TEMP FILES, please run this script again \n', err) + reject(new Error('ERROR SEE MESSAGE ABOVE')) + } else { + resolve(true) + } + }) + } catch (e) { + reject(e) + } + }) +} /** * Replaces placeholders in the README file with the provided app name and organization owner. @@ -54,7 +159,7 @@ export const APP_CONFIG = { 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 + adaptiveIconBackgroundColor: '${androidIconColor}', // CONFIG: Add your android adaptive icon background color here } as const ` @@ -74,16 +179,15 @@ const replacePullRequestTemplate = () => { } const changeAppJson = (appName: string, appSlug: string, organizationOwner: string) => { - const newAppJson = JSON.parse(fs.readFileSync(APP_JSON_PATH, 'utf8')) newAppJson.expo.slug = appSlug newAppJson.expo.name = appName newAppJson.expo.owner = organizationOwner - newAppJson.version = '1.0.0' + newAppJson.expo.version = '1.0.0' + fs.writeFileSync(APP_JSON_PATH, JSON.stringify(newAppJson, null, 2)) } const changePackageJson = (appName: string, organizationOwner: string) => { - 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' @@ -103,18 +207,18 @@ const removeDocsFolder = () => { fs.rm('./documentation', { recursive: true, force: true }, () => {}) } -const setUpProject = async ( - appName: string, - bundleId: string, - androidPackageName: string, - scheme: string, - easId: string, - organizationOwner: string, - androidIconColor: string, - appSlug: string -) => { +const setUpProject = async ({ + appName, + bundleId, + androidPackageName, + scheme, + easId, + organizationOwner, + androidIconColor, + appSlug, +}: SetupProjectProps) => { // START - logger.success('Start ...') + logger.success('Start bootstrapping ...') // 1. Delete readme -> and create new, with new app name etc. logger.info('Generating new readme file') @@ -148,60 +252,32 @@ const setUpProject = async ( logger.success(`Config your project has been success`) } -export 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 questions = Object.entries(questionsObject) + .map((value) => ({ + name: value[0], + ...value[1], + })) + .sort((a, b) => a.order - b.order) + +export const bootstrap = async () => { + try { + logger.info('Please give me this information to setup your project:') + + // 0. Create temp files + await createTempFiles() + + const answers = (await prompt(questions)) 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" + ) } - - 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') - } - - 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') } From 085c74fe8585158aea065ded19627eea23067951 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Sat, 13 Apr 2024 12:39:52 +0200 Subject: [PATCH 16/47] chore: update package json scripts --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index bde74f24..f84c7e34 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,8 @@ "license": "MIT", "scripts": { "android:dev-client": "IS_DEV=1 npx expo run:android", - "baca:bootstrap": "node ./scripts/cli/build bootstrap", - "baca:generate": "node ./scripts/cli/build generate", + "baca:bootstrap": "yarn build:baca-cli && node ./scripts/cli/build bootstrap", + "baca:generate": "yarn build:baca-cli && node ./scripts/cli/build generate", "b": "yarn build:baca-cli && node ./scripts/cli/build b && yarn eslint src --fix && yarn tsc", "g": "yarn build:baca-cli && node ./scripts/cli/build g && yarn eslint src --fix && yarn tsc", "build:baca-cli": "mkdir -p scripts/cli/temp && cp app.config.ts scripts/cli/temp/app.config.ts && npx tsc -p ./scripts/cli/tsconfig.cli.json", From c0bc5af22a2f3d6e6c9505b54cd5368c3326d842 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Sun, 14 Apr 2024 11:57:51 +0200 Subject: [PATCH 17/47] chore: update readme template --- templates/readme_template.md | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/templates/readme_template.md b/templates/readme_template.md index aaf2a759..468767a0 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,7 +84,9 @@ 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` and pick icon types + + ## Sync up with template: From 6094d50e45560f836b350fe8f3110c6acc0b136d Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Sun, 14 Apr 2024 12:04:45 +0200 Subject: [PATCH 18/47] chore: update bootstrap testing docs --- docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx b/docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx index 74743cd2..45b08e75 100644 --- a/docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx +++ b/docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx @@ -103,6 +103,14 @@ 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 +123,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,7 +135,14 @@ 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 b + +## OR + yarn baca:bootstrap ``` From 33a46d9c8d20e5199a8295f105338494a8e090c5 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Sun, 14 Apr 2024 12:04:59 +0200 Subject: [PATCH 19/47] chore: update bootstrap script --- scripts/cli/actions/bootstrap.ts | 61 ++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/scripts/cli/actions/bootstrap.ts b/scripts/cli/actions/bootstrap.ts index a5e3b11b..b0b4d205 100644 --- a/scripts/cli/actions/bootstrap.ts +++ b/scripts/cli/actions/bootstrap.ts @@ -23,6 +23,7 @@ type SetupProjectProps = { organizationOwner: string androidIconColor: string appSlug: string + appleTeamId: string } // Check types of questions here: @@ -87,6 +88,12 @@ const questionsObject: QuestionsObject = { initial: newAppJson.expo.slug, order: 8, }, + appleTeamId: { + type: 'text', + message: 'What is your apple team id? (Optional)', + initial: newAppJson.expo.slug, + order: 8, + }, } /** @@ -123,7 +130,7 @@ const createTempFiles = () => { * @param appName - The name of the app. * @param organizationOwner - The owner of the organization. */ -const replaceReadme = (appName: string, organizationOwner: string) => { +const replaceReadme = ({ appName, organizationOwner }: SetupProjectProps) => { let contents = fs.readFileSync(README_PATH, 'utf-8') contents = contents.replaceAll('_NAME_', appName) @@ -142,14 +149,14 @@ const replaceReadme = (appName: string, organizationOwner: string) => { * @param easId - The EAS project ID. * @param androidIconColor - The background color for the adaptive icon on Android. */ -const setUpAppConfig = ( - appName: string, - bundleId: string, - androidPackageName: string, - scheme: string, - easId: string, - androidIconColor: string -) => { +const setUpAppConfig = ({ + appName, + bundleId, + androidPackageName, + scheme, + easId, + androidIconColor, +}: SetupProjectProps) => { let contents = fs.readFileSync(APP_CONFIG_PATH, 'utf8') const appConfig = ` @@ -178,7 +185,7 @@ const replacePullRequestTemplate = () => { fs.writeFileSync(PULL_REQUEST_TEMPLATE_PATH, contents) } -const changeAppJson = (appName: string, appSlug: string, organizationOwner: string) => { +const changeAppJson = ({ appName, appSlug, organizationOwner }: SetupProjectProps) => { newAppJson.expo.slug = appSlug newAppJson.expo.name = appName newAppJson.expo.owner = organizationOwner @@ -187,7 +194,7 @@ const changeAppJson = (appName: string, appSlug: string, organizationOwner: stri fs.writeFileSync(APP_JSON_PATH, JSON.stringify(newAppJson, null, 2)) } -const changePackageJson = (appName: string, organizationOwner: string) => { +const changePackageJson = ({ appName, organizationOwner }: SetupProjectProps) => { packageJson.name = `@${organizationOwner}/${appName}` packageJson.description = `App created from expo-template powered by binarapps` packageJson.version = '1.0.0' @@ -204,29 +211,24 @@ const removeIssueTemplates = () => { } const removeDocsFolder = () => { - fs.rm('./documentation', { recursive: true, force: true }, () => {}) + fs.rm('./docs', { recursive: true, force: true }, () => {}) } -const setUpProject = async ({ - appName, - bundleId, - androidPackageName, - scheme, - easId, - organizationOwner, - androidIconColor, - appSlug, -}: SetupProjectProps) => { +// 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(appName, organizationOwner) + 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(appName, bundleId, androidPackageName, scheme, easId, androidIconColor) + setUpAppConfig(config) // 3. Delete exist pull request template -> generate the new logger.info('Generating new pull request template file') @@ -234,11 +236,11 @@ const setUpProject = async ({ // 4. Change app.json file logger.info('Change app.json file') - changeAppJson(appName, appSlug, organizationOwner) + changeAppJson(config) // 5. Change package.json file logger.info('Change package.json file') - changePackageJson(appName, organizationOwner) + changePackageJson(config) // 6. Remove issue templates logger.info('Remove issue templates') @@ -248,6 +250,13 @@ const setUpProject = async ({ logger.info('Remove docs folder') removeDocsFolder() + // 8. Change eas.json + logger.info('Remove docs folder') + changeEasJson(config) + + // 8. Change deeplink files + changeDeeplinkFiles(config) + //Finish logger.success(`Config your project has been success`) } From 39fdfc973e79f0dd28166c0fe516c02f8ee74a3c Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Mon, 15 Apr 2024 18:27:28 +0200 Subject: [PATCH 20/47] chore: revert removing generateIconTypes function - p1 --- scripts/generate_icon_types.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 scripts/generate_icon_types.js diff --git a/scripts/generate_icon_types.js b/scripts/generate_icon_types.js new file mode 100644 index 00000000..e0c23a56 --- /dev/null +++ b/scripts/generate_icon_types.js @@ -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) +} From dcf3d09047d467f995078a7f2fe44c756149d89a Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Mon, 15 Apr 2024 18:27:43 +0200 Subject: [PATCH 21/47] chore: revert removing generateIconTypes function - p2 --- scripts/cli/commands/generateIconTypes.ts | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 scripts/cli/commands/generateIconTypes.ts diff --git a/scripts/cli/commands/generateIconTypes.ts b/scripts/cli/commands/generateIconTypes.ts deleted file mode 100644 index e0c23a56..00000000 --- a/scripts/cli/commands/generateIconTypes.ts +++ /dev/null @@ -1,17 +0,0 @@ -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) -} From 482902c58c05459cfd50f7c73daedd91365600ea Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Mon, 15 Apr 2024 18:28:26 +0200 Subject: [PATCH 22/47] chore: properly move generateIconTypes file --- .../{generate_icon_types.js => cli/commands/generateIconTypes.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename scripts/{generate_icon_types.js => cli/commands/generateIconTypes.ts} (100%) diff --git a/scripts/generate_icon_types.js b/scripts/cli/commands/generateIconTypes.ts similarity index 100% rename from scripts/generate_icon_types.js rename to scripts/cli/commands/generateIconTypes.ts From 3b5a5dbf95e1abc5c280b771910967b12d22f2f2 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Tue, 16 Apr 2024 20:10:47 +0200 Subject: [PATCH 23/47] chore: update package json --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f84c7e34..980634dc 100644 --- a/package.json +++ b/package.json @@ -25,10 +25,11 @@ "license": "MIT", "scripts": { "android:dev-client": "IS_DEV=1 npx expo run:android", + "baca": "yarn build:baca-cli && node ./scripts/cli/build --help ", "baca:bootstrap": "yarn build:baca-cli && node ./scripts/cli/build bootstrap", "baca:generate": "yarn build:baca-cli && node ./scripts/cli/build generate", - "b": "yarn build:baca-cli && node ./scripts/cli/build b && yarn eslint src --fix && yarn tsc", - "g": "yarn build:baca-cli && node ./scripts/cli/build g && yarn eslint src --fix && yarn tsc", + "b": "yarn build:baca-cli && node ./scripts/cli/build b", + "g": "yarn build:baca-cli && node ./scripts/cli/build g", "build:baca-cli": "mkdir -p scripts/cli/temp && cp app.config.ts scripts/cli/temp/app.config.ts && 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", From 2a8fadc8c8f139c1ffe8c284c8dfb92c0b3c4c9c Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Tue, 16 Apr 2024 20:11:06 +0200 Subject: [PATCH 24/47] chore: remove not used script --- scripts/create_new_component.js | 73 --------------------------------- 1 file changed, 73 deletions(-) delete mode 100644 scripts/create_new_component.js diff --git a/scripts/create_new_component.js b/scripts/create_new_component.js deleted file mode 100644 index a650cad8..00000000 --- a/scripts/create_new_component.js +++ /dev/null @@ -1,73 +0,0 @@ -/* 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', -} - -const createComponentFile = (name, type) => { - const newCommonComponentPath = `./src/components/${name}.tsx` - const newAtomicComponentPath = `./src/components/${type}s/${name}.tsx` - const componentFromFile = fs.readFileSync(paths.template, 'utf8') - const componentContent = componentFromFile.replaceAll('_NAME_', name) - - if (type === 'common') { - fs.writeFileSync(newCommonComponentPath, componentContent) - return - } - fs.writeFileSync(newAtomicComponentPath, componentContent) -} - -const addToIndex = (name, type) => { - const atomicComponentIndexPath = `./src/components/${type}s/index.ts` - const newExport = ` -export * from './${name}'` - if (type === 'common') { - const contents = fs.readFileSync(paths.componentsIndex, 'utf8') - fs.writeFileSync(paths.componentsIndex, contents + newExport) - - return - } - - const contents = fs.readFileSync(atomicComponentIndexPath, 'utf8') - fs.writeFileSync(atomicComponentIndexPath, contents + newExport) -} - -const generateComponent = async (name, type) => { - // Generate Component file - logger.info('Generating component files') - createComponentFile(name, type) - - // Add Component to index - addToIndex(name, type) - - // Finish - logger.success(`Component ${name} created successfully`) -} - -const generateNewComponent = async () => { - const componentTypes = [ - { title: 'Atom', value: 'atom' }, - { title: 'Molecule', value: 'molecule' }, - { title: 'Organism', value: 'organism' }, - { title: '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) - }) -} - -generateNewComponent() From a3afde498f14d2920d183af7f83fe8952e4f1e6b Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Wed, 17 Apr 2024 14:24:48 +0200 Subject: [PATCH 25/47] chore: update readme --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fcc08060..4ef639de 100644 --- a/README.md +++ b/README.md @@ -70,13 +70,16 @@ It's great for production project, but if you want to just test it, you can foll ### 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/bootstrap/intro - 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 From cae7e7c7d223e7d4a5ae92eb8760a93516c13887 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Wed, 17 Apr 2024 14:25:08 +0200 Subject: [PATCH 26/47] chore: update scripts in package.json --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 980634dc..b6700165 100644 --- a/package.json +++ b/package.json @@ -25,12 +25,12 @@ "license": "MIT", "scripts": { "android:dev-client": "IS_DEV=1 npx expo run:android", - "baca": "yarn build:baca-cli && node ./scripts/cli/build --help ", - "baca:bootstrap": "yarn build:baca-cli && node ./scripts/cli/build bootstrap", - "baca:generate": "yarn build:baca-cli && node ./scripts/cli/build generate", - "b": "yarn build:baca-cli && node ./scripts/cli/build b", - "g": "yarn build:baca-cli && node ./scripts/cli/build g", - "build:baca-cli": "mkdir -p scripts/cli/temp && cp app.config.ts scripts/cli/temp/app.config.ts && npx tsc -p ./scripts/cli/tsconfig.cli.json", + "baca": "yarn build:baca-cli && node ./scripts/cli/build/scripts/cli", + "baca:bootstrap": "yarn build:baca-cli && yarn baca bootstrap", + "baca:generate": "yarn build:baca-cli && yarn baca generate", + "b": "yarn build:baca-cli && yarn baca b", + "g": "yarn build:baca-cli && 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", From 9c6a0215d72148c0abca8bf01c18d7328505294b Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Wed, 17 Apr 2024 14:25:37 +0200 Subject: [PATCH 27/47] chore: update docs --- docs/docs/bootstrap/BOOTSTRAP_INTRO.mdx | 2 +- docs/docs/tutorials/SCRIPTS.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/docs/bootstrap/BOOTSTRAP_INTRO.mdx b/docs/docs/bootstrap/BOOTSTRAP_INTRO.mdx index e6d7ff1e..cb2402d2 100644 --- a/docs/docs/bootstrap/BOOTSTRAP_INTRO.mdx +++ b/docs/docs/bootstrap/BOOTSTRAP_INTRO.mdx @@ -16,7 +16,7 @@ import Details from '@site/src/components/Details' Bootstrap project structure, that is needed to start development -## **What you need todo, to start development?** +## **What you need to do, to start development** --- diff --git a/docs/docs/tutorials/SCRIPTS.md b/docs/docs/tutorials/SCRIPTS.md index 07915de4..fe794dde 100644 --- a/docs/docs/tutorials/SCRIPTS.md +++ b/docs/docs/tutorials/SCRIPTS.md @@ -66,3 +66,9 @@ BACA - 2.1.0: ``` 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. From c1922da25e0765b0a2e62ff60f5e202ab9bf1649 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Wed, 17 Apr 2024 14:27:53 +0200 Subject: [PATCH 28/47] chore: update readme scripts --- scripts/README.md | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/scripts/README.md b/scripts/README.md index 46544518..b835ebfb 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -44,15 +44,23 @@ yarn prepare:qa yarn prepare:staging ``` -## 4. generators +## 3. generators + +Run this command to see all available commands ```bash -## Create new component -yarn generate:component +yarn baca +``` -## Create new screen -yarn baca:generate +Our custom cli for now contain this screens: -## Bootstrap the app -yarn baca:bootstrap -``` +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` From f6d0ac5b77b6af1d807fb179ae452e7d6239684c Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Wed, 17 Apr 2024 14:32:03 +0200 Subject: [PATCH 29/47] chore: refactor generate screen script to use enquirer instead of promt-sync and select-prompt --- scripts/cli/commands/generateScreen.ts | 107 ++++++++++++++----------- 1 file changed, 62 insertions(+), 45 deletions(-) diff --git a/scripts/cli/commands/generateScreen.ts b/scripts/cli/commands/generateScreen.ts index 6925fd00..4595d8b9 100644 --- a/scripts/cli/commands/generateScreen.ts +++ b/scripts/cli/commands/generateScreen.ts @@ -1,8 +1,6 @@ +import { prompt } from 'enquirer' +// eslint-disable-next-line import/order import fs from 'fs' -import prompt from 'prompt-sync' -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -import selectPrompt from 'select-prompt' import { APP_ROUTER_DIRECTORY, SCREENS_DIRECTORY } from '../constants' import { getDirectoryNames, logger } from '../utils' @@ -16,45 +14,48 @@ const addAfter = (content: string, searchText: string, textToAdd: string) => { * * @param basePath - The path of the current directory. */ -const selectPath = (basePath: string) => - new Promise((resolve) => { - const subDirectories = getDirectoryNames(basePath) - const hasSubDirectories = subDirectories.length > 0 - - // Return the result when there are no subdirectories - if (!hasSubDirectories) { - resolve(basePath) - return - } - - const subDirectoryPrompt = subDirectories.map((directoryName) => ({ - title: directoryName, - value: directoryName, - })) - - if (basePath.includes('tabs')) { - subDirectoryPrompt.unshift({ title: '_New Tab_', value: 'new-tab' }) - } - - if (basePath !== APP_ROUTER_DIRECTORY) { - subDirectoryPrompt.unshift({ title: '.', value: '.' }) - } - - selectPrompt(`Select a directory (${basePath})`, subDirectoryPrompt, { - cursor: 0, - }).on('submit', (subValue: string) => { - // Return the result when user selects current directory - if (subValue === '.') { - resolve(basePath) - return - } - // Recursively execute path selection when subValue is not the current directory - selectPath(`${basePath}/${subValue}`).then((subResult) => { - resolve(subResult) - }) - }) +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 do you want to generate?', + type: 'select', + choices: subDirectoryPrompt, }) + // @ts-expect-error: subValue not found on promptAnswer + const subValue = promptAnswer.subValue + + // 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. @@ -115,8 +116,16 @@ const addToScreensIndex = (screenName: string) => { fs.writeFileSync(indexFilePath, newIndexFile) } -const promptTabName = () => { - const tabName = prompt()('Enter tab name: ') +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') } @@ -148,13 +157,21 @@ const createNewNavTab = (tabName: string) => { * Validates the screen name and path. */ export const generateScreen = async () => { - const routeName = prompt()('Enter screen name: ') + 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(tabName) + createNewNavTab(routeName) const newTabPath = routePath.replace('/new-tab', `/${tabName}`) routePath = newTabPath From 35c121c15469b182452866cfe6b0dbbf8fc181ec Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Wed, 17 Apr 2024 14:33:11 +0200 Subject: [PATCH 30/47] chore: refactor generate component script to use enquirer instead of pr omt-sync --- scripts/cli/commands/generateComponent.ts | 42 +++++++++++++---------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/scripts/cli/commands/generateComponent.ts b/scripts/cli/commands/generateComponent.ts index d0bc2d4d..882a95a2 100644 --- a/scripts/cli/commands/generateComponent.ts +++ b/scripts/cli/commands/generateComponent.ts @@ -1,13 +1,10 @@ -import promptSync from 'prompt-sync' +import { prompt } from 'enquirer' import { COMPONENT_TEMPLATE_PATH, COMPONENTS_PATH } from '../constants' import { logger } from '../utils' -const prompt = promptSync() - /* eslint-disable @typescript-eslint/no-var-requires */ const fs = require('fs') -const selectPrompt = require('select-prompt') const paths = { template: COMPONENT_TEMPLATE_PATH, @@ -56,21 +53,28 @@ const generateNewComponent = async (name: string, type: string) => { export const generateComponent = async () => { const componentTypes = [ - { 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: string) => { - 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 generateNewComponent(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 + + await generateNewComponent(componentName, componentType) } From fc5eb90ca5b9e10032b0f05c82242dc4400bc820 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Wed, 17 Apr 2024 14:33:34 +0200 Subject: [PATCH 31/47] chore: update readme_template --- templates/readme_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/readme_template.md b/templates/readme_template.md index 468767a0..93837346 100644 --- a/templates/readme_template.md +++ b/templates/readme_template.md @@ -84,7 +84,7 @@ 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 g` and pick icon types +- run script generating icon types `yarn g icon-types` and pick icon types From 686360d0d2148cdc504788657a5e21e5608b7430 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Wed, 17 Apr 2024 14:34:08 +0200 Subject: [PATCH 32/47] chore: improve baca cli to work as proffesional cli --- scripts/cli/actions/bootstrap.ts | 35 ++--------------------- scripts/cli/actions/generate.ts | 47 ++++++++++++++++-------------- scripts/cli/actions/index.ts | 19 +------------ scripts/cli/commands/index.ts | 1 + scripts/cli/index.ts | 49 +++++++++++++++++++++++++++++--- scripts/cli/tsconfig.cli.json | 7 +++-- 6 files changed, 79 insertions(+), 79 deletions(-) diff --git a/scripts/cli/actions/bootstrap.ts b/scripts/cli/actions/bootstrap.ts index b0b4d205..3d6bebf8 100644 --- a/scripts/cli/actions/bootstrap.ts +++ b/scripts/cli/actions/bootstrap.ts @@ -1,6 +1,7 @@ import { prompt } from 'enquirer' import fs from 'fs' +import { APP_CONFIG } from '../../../app.config' import { README_PATH, APP_CONFIG_PATH, @@ -8,7 +9,6 @@ import { PULL_REQUEST_TEMPLATE_PATH, APP_JSON_PATH, } from '../constants' -import { APP_CONFIG } from '../temp/app.config' import { logger, addAfter } from '../utils' const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8')) @@ -96,34 +96,6 @@ const questionsObject: QuestionsObject = { }, } -/** - * Creates temp files - * - app.config.ts - * - */ -const createTempFiles = () => { - return new Promise((resolve, reject) => { - try { - const exist = fs.existsSync('scripts/cli/temp/') - - if (!exist) { - fs.mkdirSync('scripts/cli/temp/') - } - - fs.copyFile('app.config.ts', 'scripts/cli/temp/app.config.ts', (err) => { - if (err) { - logger.error('ERROR WHEN CREATING TEMP FILES, please run this script again \n', err) - reject(new Error('ERROR SEE MESSAGE ABOVE')) - } else { - resolve(true) - } - }) - } catch (e) { - reject(e) - } - }) -} - /** * Replaces placeholders in the README file with the provided app name and organization owner. * @@ -268,13 +240,10 @@ const questions = Object.entries(questionsObject) })) .sort((a, b) => a.order - b.order) -export const bootstrap = async () => { +export const bootstrap = async (params?: string) => { try { logger.info('Please give me this information to setup your project:') - // 0. Create temp files - await createTempFiles() - const answers = (await prompt(questions)) as unknown as SetupProjectProps await setUpProject(answers) diff --git a/scripts/cli/actions/generate.ts b/scripts/cli/actions/generate.ts index 845692d9..8ef67d07 100644 --- a/scripts/cli/actions/generate.ts +++ b/scripts/cli/actions/generate.ts @@ -1,48 +1,53 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -import selectPrompt from 'select-prompt' +import { prompt } from 'enquirer' -import { generateComponent } from '../commands/generateComponent' -import { generateIconTypes } from '../commands/generateIconTypes' -import { generateScreen } from '../commands/generateScreen' -import { generateTheme } from '../commands/generateTheme' +import { generateIconTypes, generateScreen, generateTheme, generateComponent } from '../commands' -const data = [ +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 -const generatePrompts = data.map(({ title, value }) => ({ title, value })) +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, + })), + }) -export const generate = () => { - selectPrompt('What do you want to generate?', generatePrompts).on( - 'submit', - async (value: string) => { - const command = data.find((item) => item.value === value)?.command + // @ts-expect-error: generator not found on promptAnswer + const answerValue = promptAnswer?.generator as string - if (command) { - command() - } else { - console.log('There was some issue while running the script ', value) - } - } - ) + 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 index 1fe5edeb..cb6fe6ad 100644 --- a/scripts/cli/actions/index.ts +++ b/scripts/cli/actions/index.ts @@ -1,24 +1,7 @@ import { bootstrap } from './bootstrap' import { generate } from './generate' -import { CLI_ACTIONS } from '../constants' -import { logger } from '../utils' -const actions = { +export const actions = { generate, - g: generate, bootstrap, - b: bootstrap, -} - -/** - * Parses and executes the specified CLI action. - * - * @param action - The CLI action to parse and execute. - */ -export const executeAction = (action: keyof typeof actions) => { - if (!CLI_ACTIONS.includes(action)) { - logger.error(`Action ${action} is not supported.`) - return - } - actions[action]() } diff --git a/scripts/cli/commands/index.ts b/scripts/cli/commands/index.ts index 10afde15..e738d13e 100644 --- a/scripts/cli/commands/index.ts +++ b/scripts/cli/commands/index.ts @@ -1,3 +1,4 @@ +export * from './generateComponent' export * from './generateIconTypes' export * from './generateScreen' export * from './generateTheme' diff --git a/scripts/cli/index.ts b/scripts/cli/index.ts index d4d712d2..6e1a21e6 100644 --- a/scripts/cli/index.ts +++ b/scripts/cli/index.ts @@ -1,11 +1,52 @@ +// #!/usr/bin/env node import { Command } from 'commander' -import { executeAction } from './actions' +import { actions } from './actions' +import { generators } from './actions/generate' -const program = new Command() +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 + } +} -program.argument('').action((action) => { - executeAction(action) +function inpectCommand(command: Command) { + // The option value is stored as property on command because we called .storeOptionsAsProperties() + + console.log(command.helpInformation()) + // console.log(` + // Please run this script with -h argument, to see what options you have + // `) +} + +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') + .description('Bootstrap of new project') + .alias('b') + .action((buildTarget, options, command) => { + actions.bootstrap() + }) + program.parse() diff --git a/scripts/cli/tsconfig.cli.json b/scripts/cli/tsconfig.cli.json index 9d54d044..0aa0ebac 100644 --- a/scripts/cli/tsconfig.cli.json +++ b/scripts/cli/tsconfig.cli.json @@ -1,7 +1,6 @@ { "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { - "rootDir": ".", "noImplicitAny": false, "outDir": "build", "strict": true, @@ -11,6 +10,8 @@ "sourceMap": false, "esModuleInterop": true, "moduleResolution": "node", - "skipLibCheck": true - } + "skipLibCheck": true, + "allowJs": false + }, + "include": ["../../app.config.ts", "**/*"] } From 89acea5749edb061726b3520224f8af161e9422c Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Thu, 18 Apr 2024 10:29:30 +0200 Subject: [PATCH 33/47] chore: update tutorials docs --- docs/docs/tutorials/ICONS.md | 2 +- docs/docs/tutorials/SCRIPTS.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/SCRIPTS.md b/docs/docs/tutorials/SCRIPTS.md index fe794dde..d628819d 100644 --- a/docs/docs/tutorials/SCRIPTS.md +++ b/docs/docs/tutorials/SCRIPTS.md @@ -57,7 +57,7 @@ Check [doppler documentantion](/docs/doppler-config) to see more details ## Generate last update info -This script is automatically executed after running `yarn update:production`, this will return ids of last udpate: +This script is automatically executed after running `yarn update:production` (or staging|qa), this will return ids of last udpate: ``` BACA - 2.1.0: From cc438a80f9eb80e7b201850194846e1e590ba72c Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Thu, 18 Apr 2024 10:30:20 +0200 Subject: [PATCH 34/47] chore: remove not needed script from package json --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index b6700165..5cc7522a 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "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", "ios:dev-client": "IS_DEV=1 npx expo run:ios", From edec8cb27772a0375fccb1883d85b1528cbeddf5 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Fri, 19 Apr 2024 12:41:41 +0200 Subject: [PATCH 35/47] chore: update bootstrap script --- scripts/cli/actions/bootstrap.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/cli/actions/bootstrap.ts b/scripts/cli/actions/bootstrap.ts index 3d6bebf8..cb6cd09a 100644 --- a/scripts/cli/actions/bootstrap.ts +++ b/scripts/cli/actions/bootstrap.ts @@ -223,10 +223,9 @@ const setUpProject = async (config: SetupProjectProps) => { removeDocsFolder() // 8. Change eas.json - logger.info('Remove docs folder') changeEasJson(config) - // 8. Change deeplink files + // 9. Change deeplink files changeDeeplinkFiles(config) //Finish From b1d8b29fcd89946caf32ae8c8e8a1ee018a861a1 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Sat, 20 Apr 2024 16:06:30 +0200 Subject: [PATCH 36/47] chore: change name of the create new app docs --- ...BOOTSTRAP_INTRO.mdx => CREATE_NEW_APP.mdx} | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) rename docs/docs/bootstrap/{BOOTSTRAP_INTRO.mdx => CREATE_NEW_APP.mdx} (80%) diff --git a/docs/docs/bootstrap/BOOTSTRAP_INTRO.mdx b/docs/docs/bootstrap/CREATE_NEW_APP.mdx similarity index 80% rename from docs/docs/bootstrap/BOOTSTRAP_INTRO.mdx rename to docs/docs/bootstrap/CREATE_NEW_APP.mdx index cb2402d2..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 @@ -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) From cfca17ff40ae0a7de12bfb8a9e971a19280acbba Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Sat, 20 Apr 2024 16:08:15 +0200 Subject: [PATCH 37/47] chore: add overview to docs --- docs/docs/OVERVIEW.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 docs/docs/OVERVIEW.md diff --git a/docs/docs/OVERVIEW.md b/docs/docs/OVERVIEW.md new file mode 100644 index 00000000..d87bff3e --- /dev/null +++ b/docs/docs/OVERVIEW.md @@ -0,0 +1,36 @@ +--- +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. + +## 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! From b28d44c7b781f3120db0dfaeb74cc4fe586a1a1f Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Sat, 20 Apr 2024 16:09:31 +0200 Subject: [PATCH 38/47] chore: update navigation in docs pages --- README.md | 6 +++--- docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx | 6 ++++-- docs/docs/bootstrap/_category_.json | 2 +- docs/docs/deploy/_category_.json | 2 +- docs/docs/tutorials/NOTIFICATIONS_SETUP.md | 2 +- docs/docs/tutorials/_category_.json | 2 +- docs/docusaurus.config.ts | 7 ++++++- docs/src/pages/index.tsx | 2 +- 8 files changed, 18 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 4ef639de..a9e3b7ce 100644 --- a/README.md +++ b/README.md @@ -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 @@ -52,7 +52,7 @@ We know there are a lot of project starters for react native, but we have some g ## 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). @@ -79,7 +79,7 @@ It's great for production project, but if you want to just test it, you can foll - eas configuration - update, build, submit - deployment docs - - https://baca-docs.vercel.app/docs/bootstrap/intro + - 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 diff --git a/docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx b/docs/docs/bootstrap/BOOTSTRAP_TESTING.mdx index 45b08e75..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 @@ -104,7 +104,9 @@ If you have some domain, for example: example.com, your bundle id could be: `com
+ > **Optional step** - You can run the bootstrap script later + This can be found in developer apple console. > Image will be added in future @@ -143,7 +145,7 @@ yarn b ## OR -yarn baca:bootstrap +yarn baca bootstrap ``` ## Potential issues 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/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/_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
From 358a790fda67386dd5f542e71c2c90206f0f3821 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Sat, 20 Apr 2024 16:10:00 +0200 Subject: [PATCH 39/47] chore: support short version in bootstrap script --- scripts/cli/actions/bootstrap.ts | 71 +++++++++++++++++++++----------- scripts/cli/index.ts | 6 ++- 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/scripts/cli/actions/bootstrap.ts b/scripts/cli/actions/bootstrap.ts index cb6cd09a..38548722 100644 --- a/scripts/cli/actions/bootstrap.ts +++ b/scripts/cli/actions/bootstrap.ts @@ -35,64 +35,77 @@ type QuestionsObject = { 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: 2, + order: 4, }, androidPackageName: { type: 'text', message: 'What is your android package name?', initial: APP_CONFIG.androidPackageName, - order: 3, + order: 5, }, scheme: { type: 'text', message: 'What is your scheme name?', initial: APP_CONFIG.scheme, - order: 4, + order: 6, }, easId: { type: 'text', message: 'What is your eas id?', initial: APP_CONFIG.easProjectId, - order: 5, - }, - organizationOwner: { - type: 'text', - message: 'What is your expo organization owner?', - initial: newAppJson.expo.owner, - order: 6, + order: 7, }, androidIconColor: { type: 'text', message: 'What is your android icon color?', initial: APP_CONFIG.adaptiveIconBackgroundColor, - order: 7, - }, - appSlug: { - type: 'text', - message: 'What is your expo app slug?', - initial: newAppJson.expo.slug, order: 8, }, appleTeamId: { type: 'text', message: 'What is your apple team id? (Optional)', - initial: newAppJson.expo.slug, - order: 8, + // FIXME: GET IT FROM EAS.JSON + initial: '5764GC687R', + order: 9, }, } @@ -232,18 +245,30 @@ const setUpProject = async (config: SetupProjectProps) => { logger.success(`Config your project has been success`) } -const questions = Object.entries(questionsObject) - .map((value) => ({ +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], })) - .sort((a, b) => a.order - b.order) -export const bootstrap = async (params?: string) => { + 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(questions)) as unknown as SetupProjectProps + const answers = (await prompt(getQuesstions(config))) as unknown as SetupProjectProps await setUpProject(answers) diff --git a/scripts/cli/index.ts b/scripts/cli/index.ts index 6e1a21e6..25a024c7 100644 --- a/scripts/cli/index.ts +++ b/scripts/cli/index.ts @@ -43,10 +43,12 @@ generators.forEach((generator) => { program .command('bootstrap') + .option('-s, --simple', 'Run bootstrap simple with needed values') .description('Bootstrap of new project') .alias('b') - .action((buildTarget, options, command) => { - actions.bootstrap() + .action((buildTarget) => { + const isSimple = buildTarget.simple ?? false + actions.bootstrap({ isSimple }) }) program.parse() From e3e541ad78e917a93df11464e26183b4c506afd4 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Sun, 21 Apr 2024 12:02:17 +0200 Subject: [PATCH 40/47] chore: update readme --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a9e3b7ce..09df6941 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 @@ -48,7 +48,7 @@ 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? @@ -58,15 +58,15 @@ It's great for production project, but if you want to just test it, you can foll ### 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 @@ -159,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 From 765d6794edf7f312baee41c14fff8b5302ea2539 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Sun, 21 Apr 2024 12:02:25 +0200 Subject: [PATCH 41/47] chore: update scripts readme --- src/components/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From f9185779b728ab72869498087e36227c50e59863 Mon Sep 17 00:00:00 2001 From: Mateusz Rostkowski Date: Mon, 22 Apr 2024 08:31:39 +0200 Subject: [PATCH 42/47] chore: update readme template --- templates/readme_template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/readme_template.md b/templates/readme_template.md index 93837346..2e779565 100644 --- a/templates/readme_template.md +++ b/templates/readme_template.md @@ -93,7 +93,7 @@ It is added to the app as a font generated by [icomoon app](https://icomoon.io/a 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) @@ -125,7 +125,7 @@ git commit -m "chore: sync up with template code" ```