diff --git a/README.md b/README.md
index 87cb5ca..e7887bc 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
Works on Windows, Linux and MacOS
-
+Obs: On Linux, sudo command is required
# CAUTION
DO NOT use this project in a production project due to it still being under development
@@ -20,8 +20,8 @@ You can install via npm:
# Features
Create React Native files.
-1. Create component/screen files using JavaScript.
-2. Create component/screen files using TypeScript.
+1. Create component/screen/route files using JavaScript.
+2. Create component/screen/route files using TypeScript.
3. Create a config file to store your preferences.
# Project folders structure
@@ -37,6 +37,9 @@ Create React Native files.
+-- ScreenName
+-- index.js
+-- styles.js
+ +-- routes
+ +-- index.js
+ +-- route-name.routes.js
+-- rn-files-creator.json
```
@@ -44,35 +47,52 @@ Create React Native files.
# Usage
The basic use of rn-file-creator is :
```bash
- rn -command componentName [otherComponentName]
+ rn -command [ComponentName] [otherComponentName]
```
## Creating a Component
Type the following command to create a component
```bash
- rn -c ComponentName
+ rn -c [ComponentName]
```
You can use more than one arg per time to create more than one component
```bash
- rn -c ComponentName1 -c ComponentName2 -c ComponentName3 -c ComponentName4
+ rn -c [ComponentName1] -c [ComponentName2] -c [ComponentName3] -c [ComponentName4]
```
## Creating a Screen
Type the following command to create a screen
```bash
- rn -s ScreenName
+ rn -s [ScreenName]
```
You can use more than one arg per time to create more than one screen
```bash
- rn -s ScreenName1 -s ScreenName2 -s ScreenName3 -s ScreenName4
+ rn -s [ScreenName1] -s [ScreenName2] -s [ScreenName3] -s [ScreenName4]
```
+## Creating a route
+Type the following command to create a route
+
+ ```bash
+ rn -r [RouteName]
+ ```
+
+You can specify a navigator type (stack, bottomTab or drawer) passing -t param.
+
+ ```bash
+ rn -r [RouteName] -t [NavigatorType]
+ ```
+
+Obs: If you don't specify the navigator type it will be asked to you.
+
+Each created route is included on main route (index file). This file is generated automaticaly.
+
## Creating guide
You can follow the guide to create a component if you do not want to type the entire command.
@@ -89,6 +109,7 @@ After typing enter just follow the guide.
| ----------------- | ------------------------------------------------------------- |
| -c, --component | Create one or more components |
| -h, --help | Show possible commands |
+| -r, --route | Create one route |
| -s, --screen | Create one or more screens |
# Inspiration
diff --git a/dev/args.ts b/dev/args.ts
index 3ee3693..2579bb6 100644
--- a/dev/args.ts
+++ b/dev/args.ts
@@ -5,16 +5,18 @@ export const defaultArgsPrompt = [
const commands = {
empty: "rn",
- createComponent: "rn -c Component1 -i Login -h",
+ createComponent: "rn -c Component1 -c Component2",
createComponentShort: "rn --screen A4 --component A4",
- createNavigator: "rn -n Login -n Logged -t stack -i MainNavigator -aaps",
- // createNavigatorShort: "rn -c Component1",
+ createRoute: "rn -r RecipeRoutes -t bottomTab",
+ // createRoute: "rn -r Login -r Logged -t stack -i MainRoute -aaps",
+ // createRoute: "rn -r Login -r Logged -t stack -i MainRoute -aaps",
+ // createRouteShort: "rn -c Component1",
}
-const argCommand = commands.createComponentShort.split(' ');
+const argCommand = commands.createRoute.split(' ');
// const argCommand = commands.createComponent.split(' ');
// const argCommand = commands.empty.split(' ');
-// const argCommand = commands.createNavigator.split(' ');
+// const argCommand = commands.createRoute.split(' ');
argCommand.shift();
export const args = [...defaultArgsPrompt, ...argCommand];
\ No newline at end of file
diff --git a/package.json b/package.json
index c2831d0..42bdc51 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@bachmateus/rn-files-creator",
- "version": "2.0.4",
+ "version": "2.1.1",
"description": "rn-files-creator make easier to create new React Native components files. It provides a basic file structure for your components with StyleSheet or StyledComponent.",
"main": "src/main.js",
"bin": {
@@ -10,18 +10,22 @@
"publishConfig": {
"access": "public"
},
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/bachmateus/rn-files-creator.git"
+ },
"scripts": {
- "start": "SET NODE_ENVIRONMENT=development & ts-node dev/index.ts",
- "start:dev": "SET NODE_ENVIRONMENT=development & npx nodemon dev/index.ts",
- "build": "npx tsc -p tsconfig-build.json & npm run copy:files",
- "build:dev": "npm run build & npm run copy:files & npm link",
- "test": "SET NODE_ENVIRONMENT=development & jest --runInBand",
- "test:watch": "SET NODE_ENVIRONMENT=development & jest --runInBand --watch",
- "test:coverage": "SET NODE_ENVIRONMENT=development & jest --coverage --runInBand",
- "copy:files": "npm run copy:template-files & npm run copy:assets-files",
- "copy:template-files": "cp -R ./src/builder/templates/ ./build/builder/",
+ "start": "env NODE_ENVIRONMENT=development ts-node dev/index.ts",
+ "start:dev": "env NODE_ENVIRONMENT=development npx nodemon dev/index.ts",
+ "build": "npx tsc -p tsconfig-build.json && npm run copy:files",
+ "build:dev": "npm run build && npm link",
+ "test": "env NODE_ENVIRONMENT=development jest --runInBand",
+ "test:watch": "env NODE_ENVIRONMENT=development jest --runInBand --watch",
+ "test:coverage": "env NODE_ENVIRONMENT=development jest --coverage --runInBand",
+ "copy:files": "npm run copy:template-files && npm run copy:assets-files",
+ "copy:template-files": "cp -R ./src/builder/templates/ ./build/builder/templates/",
"copy:assets-files": "cp -R ./src/cli/assets/ ./build/cli/assets/",
- "deploy": "npm run build & npm publish"
+ "deploy:npm": "npm run build && npm publish"
},
"keywords": [
"cli",
diff --git a/src/builder/builder.module.ts b/src/builder/builder.module.ts
index a92fb32..084bfd8 100644
--- a/src/builder/builder.module.ts
+++ b/src/builder/builder.module.ts
@@ -1,6 +1,8 @@
import { filesManagerService } from "../manager/manager.module";
import { ComponentBuilderService } from "./service/component-builder.service";
import { ScreenBuilderService } from "./service/screen-builder.service";
+import { RouteBuilderService } from "./service/route-builder.service";
export const componentBuilderService = new ComponentBuilderService(filesManagerService);
-export const screenBuilderService = new ScreenBuilderService(filesManagerService);
\ No newline at end of file
+export const screenBuilderService = new ScreenBuilderService(filesManagerService);
+export const routeBuilderService = new RouteBuilderService(filesManagerService);
\ No newline at end of file
diff --git a/src/builder/data/component-files-to-copy.ts b/src/builder/data/component-files-to-copy.ts
index f56776f..67e7cdf 100644
--- a/src/builder/data/component-files-to-copy.ts
+++ b/src/builder/data/component-files-to-copy.ts
@@ -11,11 +11,11 @@ export const componentFilesToCopy = {
},
TypeScript: {
StyleSheet: [
- { templateFileName: 'index.StyleSheet.ts', fileName: 'index.ts', shallRename: true },
+ { templateFileName: 'index.StyleSheet.ts', fileName: 'index.tsx', shallRename: true },
{ templateFileName: 'styles.StyleSheet.js', fileName: 'styles.ts', shallRename: false },
],
'styled-component': [
- { templateFileName: 'index.StyledComponent.ts', fileName: 'index.ts', shallRename: true },
+ { templateFileName: 'index.StyledComponent.ts', fileName: 'index.tsx', shallRename: true },
{ templateFileName: 'styles.StyledComponent.js', fileName: 'styles.ts', shallRename: false },
]
},
diff --git a/src/builder/data/route-constants.spec.ts b/src/builder/data/route-constants.spec.ts
new file mode 100644
index 0000000..36c373e
--- /dev/null
+++ b/src/builder/data/route-constants.spec.ts
@@ -0,0 +1,34 @@
+import { generateRouteNameFile, getRouteNameByFileName } from './route-constants';
+
+describe('route-words-rename', () => {
+ describe('generateRouteNameFile', () => {
+ it('success to generate component file name composed by one word', () => {
+ const componentName = 'LoggedRoutes';
+ const fileName = 'logged.routes';
+ expect(generateRouteNameFile(componentName)).toEqual(fileName);
+ });
+ it('success to generate component file name composed by more than one word', () => {
+ const componentName = 'UserLoggedRoutes';
+ const fileName = 'user-logged.routes';
+ expect(generateRouteNameFile(componentName)).toEqual(fileName);
+ });
+ it('success to generate component file name composed by lowercase words', () => {
+ const componentName = 'user-logged-routes';
+ const fileName = 'user-logged.routes';
+ expect(generateRouteNameFile(componentName)).toEqual(fileName);
+ });
+ })
+
+ describe('getRouteNameByFileName', () => {
+ it('success to get component name composed by one word', () => {
+ const fileName = 'logged.routes.js';
+ const componentName = 'LoggedRoutes';
+ expect(getRouteNameByFileName(fileName)).toEqual(componentName);
+ });
+ it('success to get component name composed by more than one word', () => {
+ const fileName = 'user-logged.routes.js';
+ const componentName = 'UserLoggedRoutes';
+ expect(getRouteNameByFileName(fileName)).toEqual(componentName);
+ });
+ })
+});
\ No newline at end of file
diff --git a/src/builder/data/route-constants.ts b/src/builder/data/route-constants.ts
new file mode 100644
index 0000000..d6a02c1
--- /dev/null
+++ b/src/builder/data/route-constants.ts
@@ -0,0 +1,68 @@
+export const routeWordsToRename = {
+ // TODO: change interface to use routesTypesEnum
+ routeFile: {
+ routeNameToRename: 'MyRoutes',
+ library: {
+ stack: [
+ { find: 'ROUTE_CREATOR', replaceTo: 'createNativeStackNavigator' },
+ { find: 'ROUTE_LIB', replaceTo: '@react-navigation/native-stack' },
+ ],
+ bottomTab: [
+ { find: 'ROUTE_CREATOR', replaceTo: 'createBottomTabNavigator' },
+ { find: 'ROUTE_LIB', replaceTo: '@react-navigation/bottom-tabs' },
+ ],
+ drawer: [
+ { find: 'ROUTE_CREATOR', replaceTo: 'createDrawerNavigator' },
+ { find: 'ROUTE_LIB', replaceTo: '@react-navigation/drawer' },
+ ],
+ },
+ }
+}
+
+export const routeFileToCopy = {
+ JavaScript:(routeName:string) => ({
+ routeName, templateFileName: 'routes.js', fileName: `${generateRouteNameFile(routeName)}.js`
+ }),
+ TypeScript:(routeName:string) => ({
+ routeName, templateFileName: 'routes.js', fileName: `${generateRouteNameFile(routeName)}.tsx`
+ }),
+}
+
+export const mainRouteFileName = {
+ JavaScript: { templateFileName: 'main.routes.js', fileName: 'index.js'},
+ TypeScript: { templateFileName: 'main.routes.js', fileName: 'index.tsx'},
+};
+
+export const generateRouteNameFile = (componentRouteName:string) => {
+ const routeName = componentRouteName.replace('Routes', '').replace('-routes', '');
+ if (routeName.length === 0) return componentRouteName.toLocaleLowerCase();
+ const lowercaseName = routeName
+ .split('')
+ .map(char => {
+ if(char === char.toUpperCase()) return `-${char.toLocaleLowerCase()}`;
+ return char;
+ })
+ .join('')
+ .replace('-', '');
+ return `${lowercaseName}.routes`.toLocaleLowerCase();
+}
+
+export const getRouteNameByFileName = (fileName:string): string => {
+ const fileNameWithouExtension = fileName.replace('.tsx', '').replace('.js', '');
+ return fileNameWithouExtension
+ .split('-')
+ .map(word=>word.charAt(0).toUpperCase() + word.slice(1))
+ .join('')
+ .replace('.routes', 'Routes');
+}
+
+export const generateImportCodeLine = (routeName: string): string => `import ${routeName.split('.')[0]} from './${generateRouteNameFile(routeName)}'`;
+
+export const generateJsxCodeLine = (routeName: string, isMainRoute: boolean): string => isMainRoute ? ` <${routeName} />` : ` ` ;
+
+export const getMainRoutePathAndTagReference = (mainRouteName: string) => {
+ return {
+ importTermToFind: 'import',
+ nestedRouteTagReference: (!mainRouteName) ? ' {
+ // TODO: if fail remove dir
+ const { fileName, templateFileName} = routeFileToCopy[this.projectConfig.language](this.routeCliParams.route);
+ if (await this.checkIfRouteExists(fileName)) return false;
+ return await this.createRouteFile(this.routeCliParams.route, this.routeCliParams.routeType, fileName, templateFileName);
+ }
+
+ private async checkIfRouteExists(fileName: string): Promise {
+ const routeName = this.routeCliParams.route;
+ const doesExist = await this.filesManagerService.checkIfPathExists(`${userProjectDirectory.route}\/${fileName}`);
+ if (doesExist) new ComponentAlreadyExistsLogger(routeName);
+ const doesPathExist = await this.filesManagerService.checkIfPathExists(`${userProjectDirectory.route}\/`);
+ if (!doesPathExist) await this.filesManagerService.createDirectory(userProjectDirectory.route)
+ return doesExist;
+ }
+
+ private async createRouteFile(routeName: string, routeType: routesTypesEnum, fileName: string, templateFileName: string): Promise {
+ const fileData = await this.filesManagerService.readFile(`${cliTemplatePath.route}\/${templateFileName}`);
+ const newContent = this.replaceWordsFromTemplateFile(fileData!, routeName, routeType);
+ return await this.filesManagerService.writeFile(userProjectDirectory.route, fileName, newContent)
+ }
+
+ private replaceWordsFromTemplateFile(content: string, routeName: string, routeType: routesTypesEnum): string {
+ const libraryWords = routeWordsToRename.routeFile.library[routeType];
+ let newContent = content;
+ newContent = NativeStringFunction.replaceAll(newContent, routeWordsToRename.routeFile.routeNameToRename, routeName);
+ libraryWords.forEach(item => newContent = NativeStringFunction.replaceAll(newContent, item.find, item.replaceTo));
+ return newContent;
+ }
+
+ private async createMainRouteFile(language: configFileLanguageType): Promise {
+ const mainFileExists = await this.filesManagerService.checkIfPathExists(userProjectDirectory.route + mainRouteFileName[language].fileName);
+ if(mainFileExists) return false;
+ return await this.filesManagerService.copyFile(
+ `${cliTemplatePath.route}\/${mainRouteFileName[language].templateFileName}`,
+ userProjectDirectory.route,
+ mainRouteFileName[language].fileName
+ );
+ }
+
+ private async includeRouteOnMainRoute(routeName: string, nestedRouteName: string, language: configFileLanguageType): Promise {
+ const { nestedRouteTagReference, importTermToFind } = getMainRoutePathAndTagReference(nestedRouteName);
+ const routeFileName = (!nestedRouteName) ? mainRouteFileName[language].fileName : routeFileToCopy[this.projectConfig.language](nestedRouteName).fileName;
+ if(!nestedRouteName) await this.createMainRouteFile(language);
+
+ const nestedRouteData = await this.filesManagerService.readFile(userProjectDirectory.route + routeFileName);
+ if(!nestedRouteData) return false;
+ const codeLines = nestedRouteData?.split('\n') as string[];
+ // TODO: convert app.route.tsx to AppRoute
+ const lineOfLastImport = this.getLineNumberFromLastOcorrency(codeLines, importTermToFind)
+ codeLines?.splice(lineOfLastImport+1, 0, generateImportCodeLine(routeName));
+
+ // TODO: get space number from the file
+ const lineOfNavigatorContentTag = this.getLineNumberFromLastOcorrency(codeLines, nestedRouteTagReference)
+ codeLines?.splice(lineOfNavigatorContentTag+1, 0, generateJsxCodeLine(routeName, !nestedRouteName));
+
+ return await this.filesManagerService.updateFile(userProjectDirectory.route, routeFileName, codeLines?.join('\n'))
+ }
+
+ private getLineNumberFromLastOcorrency(codeLines: string[], textToFind: string): number {
+ return codeLines?.reduce(
+ (previousValue, codeLine: string, index) => {
+ const found = codeLine.indexOf(textToFind);
+ if (found > -1) return index;
+ return previousValue;
+ },
+ -1
+ ) as number;
+ }
+}
\ No newline at end of file
diff --git a/src/builder/templates/routes/main.routes.js b/src/builder/templates/routes/main.routes.js
new file mode 100644
index 0000000..9b59d97
--- /dev/null
+++ b/src/builder/templates/routes/main.routes.js
@@ -0,0 +1,9 @@
+import React from 'react';
+import { NavigationContainer } from '@react-navigation/native';
+
+export default function Routes() {
+ return (
+
+
+ )
+}
diff --git a/src/builder/templates/routes/routes.js b/src/builder/templates/routes/routes.js
new file mode 100644
index 0000000..30656aa
--- /dev/null
+++ b/src/builder/templates/routes/routes.js
@@ -0,0 +1,11 @@
+import React from 'react';
+import { ROUTE_CREATOR } from 'ROUTE_LIB';
+
+const { Navigator, Screen } = ROUTE_CREATOR();
+
+export default function MyRoutes() {
+ return (
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/cli/cli.module.ts b/src/cli/cli.module.ts
index a6d657c..08b8e36 100644
--- a/src/cli/cli.module.ts
+++ b/src/cli/cli.module.ts
@@ -1,13 +1,15 @@
import { RnFilesCreatorConfigFileService } from "./service/rn-files-creator-config-file-cli.service";
import { RnFilesCreatorConfigFileView } from "./view/rn-files-creator-config-file-cli.view";
import { filesManagerService } from "../manager/manager.module";
-import { componentBuilderService, screenBuilderService } from "../builder/builder.module";
+import { componentBuilderService, routeBuilderService, screenBuilderService } from "../builder/builder.module";
import { ComponentCliService } from "./service/component-cli.service";
import { ComponentCliView } from "./view/component-cli.view";
import { ScreenCliService } from "./service/screen-cli.service";
import { ScreenCliView } from "./view/screen-cli.view";
import { HelpCliView } from "./view/help-cli-view";
import { HelpCliService } from "./service/help-cli.service";
+import { RouteCliView } from "./view/route-cli.view";
+import { RouteCliService } from "./service/route-cli.service";
import { UserPromptedArgsCliService } from "./service/user-prompted-args-cli.service";
const rnFilesCreatorConfigFileView = new RnFilesCreatorConfigFileView();
@@ -22,6 +24,9 @@ const screenCliService = new ScreenCliService(screenCliView, screenBuilderServic
const helpCliView = new HelpCliView();
const helpCliService = new HelpCliService(filesManagerService, helpCliView)
+const routeCliView = new RouteCliView();
+const routeCliService = new RouteCliService(routeCliView, routeBuilderService)
+
const userPromptedArgsCliService = new UserPromptedArgsCliService();
export {
@@ -29,5 +34,6 @@ export {
componentCliService,
screenCliService,
helpCliService,
- userPromptedArgsCliService
+ userPromptedArgsCliService,
+ routeCliService
}
\ No newline at end of file
diff --git a/src/cli/config/argOptions.ts b/src/cli/config/argOptions.ts
index 20ca924..f2a39cc 100644
--- a/src/cli/config/argOptions.ts
+++ b/src/cli/config/argOptions.ts
@@ -7,13 +7,13 @@ export const argOptions: arg.Spec = {
"-h": Boolean,
"-c": [String],
"-s": [String],
- // "-n": String,
- // "-t": String,
+ "-r": String,
+ "-t": String,
// "-i": [String],
'--help': '-h',
'--component': '-c',
'--screen': '-s',
- // '--navigator': '-n',
- // '--navigator-type': '-t',
+ '--route': '-r',
+ '--route-type': '-t',
// '--include': '-i',
}
\ No newline at end of file
diff --git a/src/cli/data/args-cli-options.ts b/src/cli/data/args-cli-options.ts
index 733dbf5..888e660 100644
--- a/src/cli/data/args-cli-options.ts
+++ b/src/cli/data/args-cli-options.ts
@@ -5,15 +5,15 @@ export interface IArgsCliOptions {
isHelp: boolean
component: string[] | undefined
screen: string[] | undefined
- navigator: string[] | undefined
- navigatorsType: navigatorsTypesEnum | undefined
+ route: string | undefined
+ routeType: routesTypesEnum | undefined
includeOn: string[] | undefined
}
/**
- * enum with the availables navigator
+ * enum with the availables route types
*/
-export enum navigatorsTypesEnum {
+export enum routesTypesEnum {
stack = "stack",
bottomTab = "bottomTab",
drawer = "drawer",
diff --git a/src/cli/data/creator-list.ts b/src/cli/data/creator-list.ts
index b089c08..7e05ad9 100644
--- a/src/cli/data/creator-list.ts
+++ b/src/cli/data/creator-list.ts
@@ -1,4 +1,4 @@
-export const creatorList = ['component', 'screen'] as const;
+export const creatorList = ['component', 'screen', 'route'] as const;
type ElementType < T extends ReadonlyArray < unknown > > = T extends ReadonlyArray<
infer ElementType
diff --git a/src/cli/data/creator-params.ts b/src/cli/data/creator-params.ts
index c714769..6c5eee7 100644
--- a/src/cli/data/creator-params.ts
+++ b/src/cli/data/creator-params.ts
@@ -1,7 +1,14 @@
+import { routesTypesEnum } from "./args-cli-options"
+
export interface ComponentCliParams {
components: string[]
}
export interface ScreenCliParams {
screens: string[]
+}
+
+export interface RouteCliParams {
+ route: string
+ routeType: routesTypesEnum
}
\ No newline at end of file
diff --git a/src/cli/logger/invalid-component-name.logger.ts b/src/cli/logger/invalid-component-name.logger.ts
index c921ab5..c6028bc 100644
--- a/src/cli/logger/invalid-component-name.logger.ts
+++ b/src/cli/logger/invalid-component-name.logger.ts
@@ -2,6 +2,7 @@ import { loggerType, PromptLoggerProps } from "../../common/data/prompt-logger-p
import { PromptLogger } from "../../common/logger/prompt-logger";
export class InvalidComponentNameLogger {
+ // TODO: indicate from which cli it is comming
constructor(componentsName: string[]){
const promptLoggerProps: PromptLoggerProps = {
message: `Invalid component name: ${componentsName.toString()}.`,
diff --git a/src/cli/service/route-cli.service.ts b/src/cli/service/route-cli.service.ts
new file mode 100644
index 0000000..9a1b3ae
--- /dev/null
+++ b/src/cli/service/route-cli.service.ts
@@ -0,0 +1,40 @@
+import { RnFilesCreatorConfigFile } from "../data/rn-files-creator-config-file";
+import { RouteCliView } from "../view/route-cli.view";
+import { RouteCliParams } from '../data/creator-params';
+import { formatComponentName, validateComponentName } from "../../common/functions/validation";
+import { InvalidComponentNameLogger } from "../logger/invalid-component-name.logger";
+import { routesTypesEnum } from '../data/args-cli-options';
+import { RouteBuilderService } from "../../builder/service/route-builder.service";
+
+export class RouteCliService {
+ private routeCliParams: RouteCliParams = {} as RouteCliParams
+
+ constructor(
+ private readonly routeCliView: RouteCliView,
+ private readonly routeBuilderService: RouteBuilderService,
+ ){}
+
+ async handler(routeCliParams: RouteCliParams, projectConfig: RnFilesCreatorConfigFile): Promise {
+ this.routeCliParams = routeCliParams;
+ this.validateRouteType()
+ this.routeCliParams = await this.routeCliView.askForMissingParams(this.routeCliParams);
+ this.validateRouteName();
+
+ await this.routeBuilderService.handle(this.routeCliParams, projectConfig);
+ return true;
+ }
+
+ private validateRouteType() {
+ if (!Object.values(routesTypesEnum).includes(this.routeCliParams.routeType))
+ this.routeCliParams.routeType = '' as routesTypesEnum;
+ }
+
+ private validateRouteName() {
+ const validComponentsName = validateComponentName(this.routeCliParams.route);
+ if (!validComponentsName) new InvalidComponentNameLogger([this.routeCliParams.route]);
+ this.routeCliParams.route = formatComponentName(this.routeCliParams.route);
+ if (this.routeCliParams.route.indexOf('Route') === -1) this.routeCliParams.route = `${this.routeCliParams.route}Routes`;
+ // TODO: create an specific message for this case
+ if (this.routeCliParams.route==='Route'||this.routeCliParams.route==='Routes') new InvalidComponentNameLogger([this.routeCliParams.route]);
+ }
+}
\ No newline at end of file
diff --git a/src/cli/service/user-prompted-args-cli.service.ts b/src/cli/service/user-prompted-args-cli.service.ts
index 87163c5..4b95809 100644
--- a/src/cli/service/user-prompted-args-cli.service.ts
+++ b/src/cli/service/user-prompted-args-cli.service.ts
@@ -40,8 +40,8 @@ export class UserPromptedArgsCliService {
isHelp: args['-h'] || false,
component: args['-c'] || undefined,
screen: args['-s'] || undefined,
- navigator: args['-n'] || undefined,
- navigatorsType: args['-t'] || undefined,
+ route: args['-r'] || undefined,
+ routeType: args['-t'] || undefined,
includeOn: args['-i'] || undefined,
}
}
@@ -51,13 +51,14 @@ export class UserPromptedArgsCliService {
* @returns {IArgsCliOptions} an object with the arg that the user typed
*/
async validateUsersArg(usersArgs: IArgsCliOptions): Promise {
- const {isHelp, component, screen, navigator} = usersArgs;
- const haveACreator = [component, screen, navigator].some(item=>item)
+ const {isHelp, component, screen, route} = usersArgs;
+ const haveACreator = [component, screen, route].some(item=>item)
if (haveACreator) return usersArgs;
if (isHelp) return usersArgs;
const userPromptedArgsCliView = new UserPromptedArgsCliView();
const creator = await userPromptedArgsCliView.askForMissingParams() as CreatorType;
- usersArgs[creator] = [];
+ if (creator === 'component' || creator === 'screen') usersArgs[creator] = [];
+ else usersArgs[creator] = "";
return usersArgs
}
diff --git a/src/cli/view/route-cli.view.ts b/src/cli/view/route-cli.view.ts
new file mode 100644
index 0000000..b8bad53
--- /dev/null
+++ b/src/cli/view/route-cli.view.ts
@@ -0,0 +1,37 @@
+import inquirer, { Answers } from "inquirer";
+import { RouteCliParams } from "../data/creator-params";
+import { validateComponentName } from '../../common/functions/validation';
+import { routesTypesEnum } from "../data/args-cli-options";
+
+export class RouteCliView {
+ async askForMissingParams(routeCliParams: RouteCliParams): Promise {
+ const questions: Answers = [];
+ if(routeCliParams.route.length===0) questions.push(this.routeNameQuestion())
+ if(routeCliParams.routeType.length===0) questions.push(this.routeTypeQuestion())
+ if(questions.length === 0) return routeCliParams;
+ const userResponse = await inquirer.prompt(questions)
+ return {
+ route: (routeCliParams.route.length===0) ? userResponse.route : routeCliParams.route,
+ routeType: (routeCliParams.routeType.length===0) ? userResponse.routeType : routeCliParams.routeType,
+ }
+ }
+
+ private routeNameQuestion(): Answers {
+ return {
+ type: 'input',
+ name: 'route',
+ message: `What's the route name?\n->`,
+ validate: validateComponentName
+ }
+ }
+
+ private routeTypeQuestion(): Answers {
+ return {
+ type: 'list',
+ name: 'routeType',
+ message: 'What kind of route navigator do you want to create?',
+ choices: Object.values(routesTypesEnum),
+ default: routesTypesEnum.stack
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/common/functions/validation.ts b/src/common/functions/validation.ts
index 8155ea7..ff23ef8 100644
--- a/src/common/functions/validation.ts
+++ b/src/common/functions/validation.ts
@@ -8,10 +8,12 @@ export const validateComponentName = (userInput:any) => {
return true
}
+export const formatComponentName = (componentName:string): string => {
+ const splitedName = componentName.split('-');
+ const compositeName = splitedName.reduce( (response, name) => response + name.substring(0,1).toUpperCase() + name.substring(1), '');
+ return compositeName
+}
+
export const formatComponentsNames = (componentsNames: string[]): string[] => {
- return componentsNames.map( componentName => {
- const splitedName = componentName.split('-');
- const compositeName = splitedName.reduce( (response, name) => response + name.substring(0,1).toUpperCase() + name.substring(1), '');
- return compositeName
- })
+ return componentsNames.map(formatComponentName)
}
\ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
index a0a66d9..09ded8f 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,9 +1,11 @@
-import { componentCliService, rnFilesCreatorConfigFileService, screenCliService, userPromptedArgsCliService } from "./cli/cli.module";
+import { componentCliService, routeCliService, rnFilesCreatorConfigFileService, screenCliService, userPromptedArgsCliService } from "./cli/cli.module";
import { helpCliService } from "./cli/cli.module";
+import { routesTypesEnum } from "./cli/data/args-cli-options";
+import { packagesManagerService } from "./manager/manager.module";
export async function cli(args:string[]) {
- // TODO: to be created
- // const isReactNativeProject = libManagerService.isReactNativeProject();
+ await packagesManagerService.isReactNativeProject();
+
const userPromptedArgs = await userPromptedArgsCliService.handleGetUserPromptedArgs(args);
if (!userPromptedArgs) return;
@@ -21,9 +23,11 @@ export async function cli(args:string[]) {
if (userPromptedArgs.screen)
await screenCliService.handler({screens: userPromptedArgs.screen}, projectConfig)
- // TODO: create navigator flow
- // if (userPromptedArgs.screen)
- // await componentCliService.handler({components: userPromptedArgs.component}, projectConfig)
-
+ // TODO: create route flow
+ if (userPromptedArgs.route || userPromptedArgs.route === ''){
+ const route = userPromptedArgs.route;
+ const routeType= (userPromptedArgs.routeType) ? userPromptedArgs.routeType : "" as routesTypesEnum;
+ await routeCliService.handler({route, routeType}, projectConfig)
+ }
return true
}
\ No newline at end of file
diff --git a/src/manager/constants/paths.ts b/src/manager/constants/paths.ts
index 767babb..6aec473 100644
--- a/src/manager/constants/paths.ts
+++ b/src/manager/constants/paths.ts
@@ -2,19 +2,28 @@ import path from "path";
export const userProjectPath = process.env.NODE_ENVIRONMENT ? `${process.cwd()}\/test\/target-dir` : process.cwd()
-export const cliPath = path.resolve(
- __dirname.substring(__dirname.indexOf('\/') + 1),
- process.env.NODE_ENVIRONMENT ? '..\/..\/..\/src' : '..\/..\/..\/build'
-).replace(userProjectPath, '');
+const getPlatformCliPath = (OS: string) => {
+ const cliPath = path.resolve(
+ __dirname.substring(__dirname.indexOf('\/') + 1),
+ process.env.NODE_ENVIRONMENT ? '..\/..\/..\/src' : '..\/..\/..\/build'
+ );
+ if (OS === 'win32') return cliPath;
+ return cliPath.replace(process.cwd(),'');
+}
+
+export const cliPath = getPlatformCliPath(process.platform);
export const userProjectDirectory = {
component: `${userProjectPath}\/src\/components\/`,
- screen: `${userProjectPath}\/src\/screens\/`
+ screen: `${userProjectPath}\/src\/screens\/`,
+ route: `${userProjectPath}\/src\/routes\/`,
+ packageJson: `${userProjectPath}\/package.json`
}
export const cliTemplatePath = {
component: `${cliPath}\/builder\/templates\/component`,
- screen: `${cliPath}\/builder\/templates\/component`
+ screen: `${cliPath}\/builder\/templates\/component`,
+ route: `${cliPath}\/builder\/templates\/routes`,
}
-export const txtHelpFile = `${cliPath}\/cli\/assets\/help-text.txt`
\ No newline at end of file
+export const txtHelpFile = `${cliPath}\/cli\/assets\/help-text.txt`;
\ No newline at end of file
diff --git a/src/manager/logger/error-not-react-native-project.logger.ts b/src/manager/logger/error-not-react-native-project.logger.ts
new file mode 100644
index 0000000..eb13868
--- /dev/null
+++ b/src/manager/logger/error-not-react-native-project.logger.ts
@@ -0,0 +1,14 @@
+import { loggerType, PromptLoggerProps } from "../../common/data/prompt-logger-props";
+import { PromptLogger } from "../../common/logger/prompt-logger";
+
+export class ErrorNotReactNativeProjectLogger {
+ constructor(){
+ const promptLoggerProps: PromptLoggerProps = {
+ message: 'The CLI must be run in the root of a React Native project',
+ loggerType: loggerType.ERROR,
+ interruptProcess: true
+ }
+
+ new PromptLogger(promptLoggerProps);
+ }
+}
\ No newline at end of file
diff --git a/src/manager/logger/success-file-updated.logger.ts b/src/manager/logger/success-file-updated.logger.ts
new file mode 100644
index 0000000..c47efc0
--- /dev/null
+++ b/src/manager/logger/success-file-updated.logger.ts
@@ -0,0 +1,14 @@
+import { loggerType, PromptLoggerProps } from "../../common/data/prompt-logger-props";
+import { PromptLogger } from "../../common/logger/prompt-logger";
+
+export class SuccessFileUpdated {
+ constructor(filePath:string){
+ const promptLoggerProps: PromptLoggerProps = {
+ message: filePath,
+ loggerType: loggerType.UPDATE,
+ interruptProcess: false
+ }
+
+ new PromptLogger(promptLoggerProps);
+ }
+}
\ No newline at end of file
diff --git a/src/manager/manager.module.ts b/src/manager/manager.module.ts
index 0b7ed38..60ad557 100644
--- a/src/manager/manager.module.ts
+++ b/src/manager/manager.module.ts
@@ -1,3 +1,5 @@
import { FilesManagerService } from "./service/files-manager.service";
+import { PackagesManagerService } from "./service/packages-manager.service";
-export const filesManagerService = new FilesManagerService();
\ No newline at end of file
+export const filesManagerService = new FilesManagerService();
+export const packagesManagerService = new PackagesManagerService(filesManagerService);
\ No newline at end of file
diff --git a/src/manager/service/files-manager.service.ts b/src/manager/service/files-manager.service.ts
index 7c10eab..5935c24 100644
--- a/src/manager/service/files-manager.service.ts
+++ b/src/manager/service/files-manager.service.ts
@@ -3,6 +3,7 @@ import { ErrorFileCreated } from '../logger/error-file-created.logger';
import { FileNotFoundException } from '../logger/file-not-found.logger';
import { FileNotWrittenException } from '../logger/file-not-written.logger';
import { SuccessFileCreated } from '../logger/success-file-created.logger';
+import { SuccessFileUpdated } from '../logger/success-file-updated.logger';
const { mkdir, stat, copyFile, readFile, writeFile, rm } = fs;
export class FilesManagerService {
@@ -47,9 +48,24 @@ export class FilesManagerService {
}
}
+ async saveOrUpdateFile(directoryPath: string, filePath:string, newContent:string) {
+ await writeFile(directoryPath + filePath, newContent);
+ }
+
+ async updateFile(directoryPath: string, filePath:string, newContent:string): Promise {
+ try {
+ await this.saveOrUpdateFile(directoryPath, filePath, newContent);
+ new SuccessFileUpdated(filePath)
+ return true
+ } catch (error) {
+ new FileNotWrittenException(filePath)
+ return false
+ }
+ }
+
async writeFile(directoryPath: string, filePath:string, newContent:string): Promise {
try {
- await writeFile(directoryPath + filePath, newContent);
+ await this.saveOrUpdateFile(directoryPath, filePath, newContent);
new SuccessFileCreated(filePath)
return true
} catch (error) {
diff --git a/src/manager/service/packages-manager.service.ts b/src/manager/service/packages-manager.service.ts
new file mode 100644
index 0000000..cc4d983
--- /dev/null
+++ b/src/manager/service/packages-manager.service.ts
@@ -0,0 +1,25 @@
+import { FilesManagerService } from "../../../src/manager/service/files-manager.service";
+import { userProjectDirectory } from "../constants/paths";
+import { ErrorNotReactNativeProjectLogger } from "../logger/error-not-react-native-project.logger";
+
+export class PackagesManagerService {
+ constructor(
+ private filesManagerService: FilesManagerService
+ ){}
+
+ async isReactNativeProject(): Promise {
+ await this.checkIfPackageJsonExists();
+ await this.checkIfPackageJsonBelongsToRNProject();
+ return true;
+ }
+
+ async checkIfPackageJsonExists(): Promise {
+ const packageJsonExists = await this.filesManagerService.checkIfPathExists(userProjectDirectory.packageJson);
+ if (!packageJsonExists) new ErrorNotReactNativeProjectLogger();
+ }
+
+ async checkIfPackageJsonBelongsToRNProject(): Promise {
+ const packageJsonContent = await this.filesManagerService.readFile(userProjectDirectory.packageJson);
+ if (packageJsonContent?.indexOf('react-native') === -1) new ErrorNotReactNativeProjectLogger();
+ }
+}
\ No newline at end of file
diff --git a/test/integration/builder/service/route-builder.service.spec.ts b/test/integration/builder/service/route-builder.service.spec.ts
new file mode 100644
index 0000000..56c3468
--- /dev/null
+++ b/test/integration/builder/service/route-builder.service.spec.ts
@@ -0,0 +1,56 @@
+import { routeBuilderService } from "../../../../src/builder/builder.module";
+import { routeFileToCopy, routeWordsToRename } from "../../../../src/builder/data/route-constants";
+import { routesTypesEnum } from "../../../../src/cli/data/args-cli-options";
+import { RnFilesCreatorConfigFile } from "../../../../src/cli/data/rn-files-creator-config-file";
+import { cliTemplatePath, userProjectDirectory } from "../../../../src/manager/constants/paths";
+import { filesManagerService } from "../../../../src/manager/manager.module";
+import { removeTestDir } from "../../../util/manager-test-folder";
+
+const javascriptConfigFile:RnFilesCreatorConfigFile = { language:'TypeScript', styleType: 'StyleSheet' }
+
+describe('Route builder', () => {
+ beforeAll(async() => {
+ await removeTestDir()
+ });
+ afterAll(async() => {
+ await removeTestDir()
+ });
+ beforeEach(() => {
+ jest.clearAllMocks();
+ jest.restoreAllMocks()
+ });
+
+ it('should replace words from template file', async() => {
+ const routeType = routesTypesEnum.stack;
+ const routeName = 'LoggedRoutes';
+ const libraryWords = routeWordsToRename.routeFile.library[routeType];
+ const fileData = await filesManagerService.readFile(`${cliTemplatePath.route}\/routes.js`) as string;
+ const resp = routeBuilderService['replaceWordsFromTemplateFile'](fileData, routeName, routeType);
+
+ expect(resp.split(libraryWords[0].replaceTo).length).toEqual(3);
+ expect(resp).toContain(libraryWords[1].replaceTo);
+ expect(resp).toContain(routeName);
+ });
+
+ it('should create a route file', async()=> {
+ const routeName = 'LoggedRoutes';
+ const routeType = routesTypesEnum.stack;
+ const { fileName, templateFileName } = routeFileToCopy[javascriptConfigFile.language](routeName);
+ await filesManagerService.createDirectory(userProjectDirectory.route)
+ await routeBuilderService['createRouteFile'](routeName, routeType, fileName, templateFileName);
+
+ const resp = await filesManagerService.readFile(`${userProjectDirectory.route}\/${fileName}`) as string;
+ expect(resp).toContain(routeName);
+ });
+
+ it('should include route import on nested one', async() => {
+ const nestedRoute = 'HomeRoutes';
+ const route = 'AppRoutes';
+ await routeBuilderService['setConfigVars']({route, routeType: routesTypesEnum.stack}, javascriptConfigFile);
+ await routeBuilderService['createRoute']()
+ await routeBuilderService['setConfigVars']({route: nestedRoute, routeType: routesTypesEnum.bottomTab}, javascriptConfigFile);
+ await routeBuilderService['createRoute']()
+ await routeBuilderService['createMainRouteFile'](javascriptConfigFile.language)
+ expect(await routeBuilderService['includeRouteOnMainRoute'](nestedRoute, 'AppRoutes', javascriptConfigFile.language)).toBeTruthy();
+ })
+})
\ No newline at end of file
diff --git a/test/integration/create-component-flow.spec.ts b/test/integration/create-component-flow.spec.ts
index c5c7680..87df536 100644
--- a/test/integration/create-component-flow.spec.ts
+++ b/test/integration/create-component-flow.spec.ts
@@ -4,7 +4,7 @@ import { PromptLogger } from '../../src/common/logger/prompt-logger';
import { createCommandsArgs } from "../util/create-commands-args";
import { rnFilesCreatorConfigFileService } from "../../src/cli/cli.module";
import { removeTestDir } from "../util/manager-test-folder";
-import { filesManagerService } from "../../src/manager/manager.module";
+import { filesManagerService, packagesManagerService } from "../../src/manager/manager.module";
jest.mock('../../src/common/logger/prompt-logger');
@@ -20,6 +20,7 @@ describe('Create Component Flow', () => {
jest.clearAllMocks();
jest.restoreAllMocks()
jest.spyOn(rnFilesCreatorConfigFileService, 'handleGetUserRnConfigFile').mockReturnValue(Promise.resolve({language: 'JavaScript', styleType: 'StyleSheet'}))
+ jest.spyOn(packagesManagerService, 'isReactNativeProject').mockReturnValue(Promise.resolve(true))
})
it('should SUCCESS to create a component', async() => {
diff --git a/test/integration/create-routes-flow.spec.ts b/test/integration/create-routes-flow.spec.ts
new file mode 100644
index 0000000..97faf9e
--- /dev/null
+++ b/test/integration/create-routes-flow.spec.ts
@@ -0,0 +1,70 @@
+import { cli } from "../../src/main";
+import { createCommandsArgs } from "../util/create-commands-args";
+import { rnFilesCreatorConfigFileService } from "../../src/cli/cli.module";
+import { removeTestDir } from "../util/manager-test-folder";
+import { filesManagerService, packagesManagerService } from "../../src/manager/manager.module";
+import { userProjectDirectory } from '../../src/manager/constants/paths';
+import { generateImportCodeLine, generateJsxCodeLine, generateRouteNameFile } from '../../src/builder/data/route-constants';
+import { LoggerColor } from '../../src/common/data/prompt-logger-props';
+
+const mainRouteFilePath = userProjectDirectory.route+'index.js';
+describe('Create Routes Flow', () => {
+ beforeAll(async() => {
+ await removeTestDir()
+ });
+
+ afterEach(async() => {
+ await removeTestDir()
+ });
+ beforeEach(() => {
+ jest.clearAllMocks();
+ jest.restoreAllMocks()
+ jest.spyOn(rnFilesCreatorConfigFileService, 'handleGetUserRnConfigFile').mockReturnValue(Promise.resolve({language: 'JavaScript', styleType: 'StyleSheet'}))
+ jest.spyOn(packagesManagerService, 'isReactNativeProject').mockReturnValue(Promise.resolve(true))
+ jest.spyOn(process, 'exit').mockImplementation();
+ })
+
+ it('should SUCCESS to create a route and include it in main route', async() => {
+ const consoleLogSpy = jest.spyOn(console,'log');
+ const routeName = 'TestRoute';
+ const routeFileName = generateRouteNameFile(routeName) + '.js';
+ const args = createCommandsArgs(`rn -r ${routeName} -t bottomTab`);
+ const resp = await cli(args);
+ expect(resp).toBeTruthy()
+ expect(consoleLogSpy).toBeCalledTimes(3);
+ expect(consoleLogSpy).toHaveBeenNthCalledWith(1, LoggerColor.CREATE, routeFileName);
+ expect(consoleLogSpy).toHaveBeenNthCalledWith(2, LoggerColor.CREATE, "index.js");
+ expect(consoleLogSpy).toHaveBeenNthCalledWith(3, LoggerColor.UPDATE, "index.js");
+ const routeContent = await filesManagerService.readFile(userProjectDirectory.route+routeFileName);
+ const mainRouteContent = await filesManagerService.readFile(mainRouteFilePath)
+
+ expect(routeContent).toContain(routeName)
+ expect(mainRouteContent).toContain(generateImportCodeLine(routeName))
+ expect(mainRouteContent).toContain(generateJsxCodeLine(routeName, true));
+ });
+
+ it('should SUCCESS to create two routes and include them in main route', async() => {
+ const consoleLogSpy = jest.spyOn(console,'log');
+
+ const route1 = 'TestRoute1';
+ const route1FileName = generateRouteNameFile(route1) + '.js';
+ const args1 = createCommandsArgs(`rn -r ${route1} -t bottomTab`);
+ expect(await cli(args1)).toBeTruthy();
+ expect(consoleLogSpy).toHaveBeenNthCalledWith(1, LoggerColor.CREATE, route1FileName);
+ expect(consoleLogSpy).toHaveBeenNthCalledWith(2, LoggerColor.CREATE, "index.js");
+ expect(consoleLogSpy).toHaveBeenNthCalledWith(3, LoggerColor.UPDATE, "index.js");
+
+ const route2 = 'TestRoute2';
+ const route2FileName = generateRouteNameFile(route2) + '.js';
+ const args2 = createCommandsArgs(`rn -r ${route2} -t bottomTab`);
+ expect(await cli(args2)).toBeTruthy();
+ expect(consoleLogSpy).toHaveBeenNthCalledWith(4, LoggerColor.CREATE, route2FileName);
+ expect(consoleLogSpy).toHaveBeenNthCalledWith(5, LoggerColor.UPDATE, "index.js");
+
+ const mainRouteContent = await filesManagerService.readFile(mainRouteFilePath)
+ expect(mainRouteContent).toContain(generateImportCodeLine(route1))
+ expect(mainRouteContent).toContain(generateJsxCodeLine(route1, true));
+ expect(mainRouteContent).toContain(generateImportCodeLine(route2))
+ expect(mainRouteContent).toContain(generateJsxCodeLine(route2, true));
+ });
+})
\ No newline at end of file
diff --git a/test/integration/create-screen-flow.spec.ts b/test/integration/create-screen-flow.spec.ts
index d117434..985a98b 100644
--- a/test/integration/create-screen-flow.spec.ts
+++ b/test/integration/create-screen-flow.spec.ts
@@ -4,7 +4,7 @@ import { PromptLogger } from '../../src/common/logger/prompt-logger';
import { createCommandsArgs } from "../util/create-commands-args";
import { rnFilesCreatorConfigFileService } from "../../src/cli/cli.module";
import { removeTestDir } from "../util/manager-test-folder";
-import { filesManagerService } from "../../src/manager/manager.module";
+import { filesManagerService, packagesManagerService } from "../../src/manager/manager.module";
jest.mock('../../src/common/logger/prompt-logger');
@@ -20,6 +20,7 @@ describe('Create Screen Flow', () => {
jest.clearAllMocks();
jest.restoreAllMocks()
jest.spyOn(rnFilesCreatorConfigFileService, 'handleGetUserRnConfigFile').mockReturnValue(Promise.resolve({language: 'JavaScript', styleType: 'StyleSheet'}))
+ jest.spyOn(packagesManagerService, 'isReactNativeProject').mockReturnValue(Promise.resolve(true))
})
it('should SUCCESS to create a screen', async() => {
diff --git a/test/integration/help-flow.spec.ts b/test/integration/help-flow.spec.ts
index 0558339..af0cc52 100644
--- a/test/integration/help-flow.spec.ts
+++ b/test/integration/help-flow.spec.ts
@@ -1,6 +1,6 @@
import { cli } from "../../src/main";
import { createCommandsArgs } from "../util/create-commands-args";
-import { filesManagerService } from "../../src/manager/manager.module";
+import { filesManagerService, packagesManagerService } from "../../src/manager/manager.module";
import { txtHelpFile } from "../../src/manager/constants/paths";
import { componentCliService, rnFilesCreatorConfigFileService, screenCliService } from "../../src/cli/cli.module";
@@ -9,6 +9,7 @@ describe('Help Flow', () => {
jest.clearAllMocks();
jest.restoreAllMocks();
jest.spyOn(console, 'log').mockImplementation()
+ jest.spyOn(packagesManagerService, 'isReactNativeProject').mockReturnValue(Promise.resolve(true))
})
it('should SUCCESS to show help commands', async() => {
diff --git a/test/integration/manager/service/packages-manager.service.spec.ts b/test/integration/manager/service/packages-manager.service.spec.ts
new file mode 100644
index 0000000..87858df
--- /dev/null
+++ b/test/integration/manager/service/packages-manager.service.spec.ts
@@ -0,0 +1,37 @@
+import { removeTestDir } from '../../../util/manager-test-folder';
+import { PromptLogger } from '../../../../src/common/logger/prompt-logger';
+import { filesManagerService, packagesManagerService } from '../../../../src/manager/manager.module';
+
+jest.mock('../../../../src/common/logger/prompt-logger');
+const testTargetDirectory = process.cwd()+'\/test\/target-dir';
+const promptLoggerArgs = {'interruptProcess': true, 'loggerType': 'ERROR', 'message': 'The CLI must be run in the root of a React Native project'};
+
+describe('test PackagesManagerService', () => {
+ beforeAll(async() => {
+ await removeTestDir()
+ await filesManagerService.createDirectory(testTargetDirectory)
+ });
+ afterAll(async() => {
+ await removeTestDir()
+ });
+ beforeEach(() => {
+ jest.clearAllMocks();
+ jest.restoreAllMocks()
+ })
+
+ it('should stop flow if package.json was not found', async() => {
+ await packagesManagerService.checkIfPackageJsonExists()
+ expect(PromptLogger).toHaveBeenCalledWith(promptLoggerArgs);
+ })
+ it('should stop flow if package.json does not belong to a React Native project', async() => {
+ await filesManagerService.writeFile(testTargetDirectory, '\/package.json', '{}');
+ await packagesManagerService.checkIfPackageJsonBelongsToRNProject()
+ expect(PromptLogger).toHaveBeenNthCalledWith(2, promptLoggerArgs);
+ })
+ it('should return true it is a React Native project', async() => {
+ // TODO: consume an example json file
+ await filesManagerService.writeFile(testTargetDirectory, '\/package.json', JSON.stringify({dependencies: {"react-native": "0.68.2"}}));
+ expect(await packagesManagerService.isReactNativeProject()).toBeTruthy()
+ expect(PromptLogger).not.toHaveBeenCalledWith(promptLoggerArgs);
+ })
+})
\ No newline at end of file
diff --git a/test/util/data/package-json-rn-example.json b/test/util/data/package-json-rn-example.json
new file mode 100644
index 0000000..be4a8d6
--- /dev/null
+++ b/test/util/data/package-json-rn-example.json
@@ -0,0 +1,24 @@
+{
+ "name": "test_project",
+ "version": "1.0.0",
+ "main": "node_modules/expo/AppEntry.js",
+ "scripts": {
+ "start": "expo start",
+ "android": "expo start --android",
+ "ios": "expo start --ios",
+ "web": "expo start --web",
+ "eject": "expo eject"
+ },
+ "dependencies": {
+ "expo": "~45.0.0",
+ "expo-status-bar": "~1.3.0",
+ "react": "17.0.2",
+ "react-dom": "17.0.2",
+ "react-native": "0.68.2",
+ "react-native-safe-area-context": "4.2.4",
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.9"
+ },
+ "private": true
+}
diff --git a/test/util/manager-test-folder.ts b/test/util/manager-test-folder.ts
index b997081..0162fb1 100644
--- a/test/util/manager-test-folder.ts
+++ b/test/util/manager-test-folder.ts
@@ -1,7 +1,13 @@
import { FilesManagerService } from "../../src/manager/service/files-manager.service";
+const testTargetDirectory = process.cwd()+'\/test\/target-dir';
+
+export const createTestDir = async () => {
+ const filesManagerService = new FilesManagerService();
+ await filesManagerService.createDirectory(testTargetDirectory)
+}
+
export const removeTestDir = async () => {
- const testTargetDirectory = process.cwd()+'\\test\\target-dir';
const filesManagerService = new FilesManagerService();
if (await filesManagerService.checkIfPathExists(testTargetDirectory))
await filesManagerService.deleteDirectory(testTargetDirectory)