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 -![](logo.png) +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)