UNPKG

@redux-multipurpose/angular-cli

Version:
991 lines (895 loc) 54.6 kB
const glob = require('glob-promise'); const fs = require('fs'); const { cwd } = require('process'); const { camelCase, pascalCase, kebabCase } = require('change-case'); const STORE_SEARCH_PATHS = [ './src/store/store.module.ts', './apps/**/*/src/store/store.module.ts', './**/*/store/store.module.ts', ]; var SRC_DIR = 'src'; function setSrcDir(dir) { SRC_DIR = dir; } function getSrcFileRelativePath(file) { var dirs = [ ...glob.sync(SRC_DIR + '/' + file), ...glob.sync(SRC_DIR + '/**/*/' + file) ]; for (var i = 0; i < dirs.length; ++i) if (dirs[i].indexOf(file) >= 0) return dirs[i]; return null; } function getSrcFileAbsolutePath(file) { let relative = getSrcFileRelativePath(file); if (relative) return cwd() + '/' + relative; else return null; } function getStoreDirectory(absolute) { var filePath = SRC_DIR + '/store/'; if (absolute) filePath = cwd() + '/' + filePath; return filePath; } //If a unique store is found, it returns its relative path (src dir + /store/store.module.ts) function verifyStoreModule() { for (let i = 0; i < STORE_SEARCH_PATHS.length; ++i) { var dirs = glob.sync(STORE_SEARCH_PATHS[i]); if (dirs && dirs.length) break; } if (dirs.length == 1) return dirs[0]; return null; } //Verify if StoreModule is added to app.module yet. //If no app.module is recognized, it quits the app. function verifyInAppModuleImport(pathFile) { if (pathFile) { var appModule = fs.readFileSync(pathFile).toString(); return appModule.indexOf("StoreModule") >= 0; } else { console.log("Cannot recognize app.module. Exit..."); process.exit(-1); return false; } } function trimLines(array) { for (var i = 0; i < array.length; ++i) array[i] = array[i].trim(); return array; } function appModuleImportsOnNewLines(file) { var fileContent = fs.readFileSync(file).toString(); //console.log(fileContent); const fileLines = fileContent.split(/\r?\n/); //console.log(fileLines); for (var i = 0; i < fileLines.length; ++i) { if (fileLines[i].indexOf('imports') >= 0) { //console.log("Line index: ", i); //console.log("Line: ", fileLines[i]); var line = fileLines[i]; var openSquareBracketCount = 0, closeSquareBracketCount = 0; for (var j = 0; j < line.length; ++j) { const char = line.charAt(j); if (char == '[') ++openSquareBracketCount; else if (char == ']') ++closeSquareBracketCount; } //console.log("openBrackets: ", openSquareBracketCount); //console.log("closeBrackets: ", closeSquareBracketCount); if (openSquareBracketCount >= 0 && openSquareBracketCount == closeSquareBracketCount) { line = trimLines(line.split('[')).join('[\n\t\t'); line = trimLines(line.split(']')).join('\n\t]'); line = trimLines(line.split(',')).join(',\n\t\t').trim(); //console.log("line: ", line); fileLines[i] = line; } } } fs.writeFileSync(file, fileLines.join('\n')); } function verifyIfStringInFileExists(value, pathFile) { var file = fs.readFileSync(pathFile).toString(); return file.indexOf(value) >= 0; } function verifyIfWholeWordInFileExists(value, pathFile) { var file = fs.readFileSync(pathFile).toString(); const ret = file.match(new RegExp(/\b/.source + new RegExp(value).source + /\b/.source, 'gi')); return ret; } function matchRegex(regex, pathFile) { var file = fs.readFileSync(pathFile).toString(); const ret = file.match(regex); //console.log("Regex matched: ", ret); return ret && ret.length > 0; } module.exports = function (plop) { //Verify if store module exists and it's contained under src/store path plop.addHelper('cwd', (p) => process.cwd()); var storeModulePath = verifyStoreModule(); if (!storeModulePath) { console.log('\nNo store.module found. Let\'s initialize a new one\n'); plop.setGenerator('createStore', { prompts: [{ type: 'input', name: 'storeDir', message: 'In which directory do you want to create the store folder?', default: SRC_DIR }, { type: 'checkbox', name: 'configurations', message: 'Choose which features activate on store configuration\n', choices: [ { value: 'epics', name: 'Epics skeleton (redux-observables)' }, { value: 'sagas', name: 'Sagas skeleton (redux-saga)' }, { value: 'persistence', name: 'Persistence (redux-persist)' }, { value: 'responsiveness', name: 'Responsiveness (redux-responsive)' }, { value: 'logger', name: 'Logger (redux-logger)' } ] }, { type: 'input', name: 'routerKey', message: 'Do you want to enable angular router reducer? Leave it blank if you don\'t need it; otherwise digit a key name to the reducer (e.g. router)' }, { type: 'input', name: 'breakpoints', when: function(response) { return response.configurations.indexOf('responsiveness') >= 0; }, message: 'Do you want to customize responsiveness breakpoints? (Insert them separated by commas, or leave blank to use defaults)\ndefault are 480, 768, 992, 1200: ' }], actions: function(data) { var actions = []; if (data.configurations.indexOf('epics') >= 0) data.enableEpics = true; if (data.configurations.indexOf('sagas') >= 0) data.enableSagas = true; if (data.configurations.indexOf('persistence') >= 0) data.enablePersistence = true; if (data.configurations.indexOf('responsiveness') >= 0) data.enableResponsiveness = true; if (data.configurations.indexOf('logger') >= 0) data.enableLogger = true; setSrcDir(data.storeDir); actions.push({ type: 'add', path: '{{cwd}}/' + data.storeDir + '/store/store.module.ts', templateFile: 'templates/store.module.tpl' }); actions.push({ type: 'add', path: '{{cwd}}/' + data.storeDir + '/store/store.reducer.ts', templateFile: 'templates/store.reducer.tpl' }); actions.push({ type: 'add', path: '{{cwd}}/' + data.storeDir + '/store/index.ts', templateFile: 'templates/index.tpl', skipIfExists: true }); if (data.enableEpics) actions.push({ type: 'add', path: '{{cwd}}/' + data.storeDir + '/store/epics.ts', templateFile: 'templates/epics.tpl', skipIfExists: true }); if (data.enableSagas) actions.push({ type: 'add', path: '{{cwd}}/' + data.storeDir + '/store/sagas.ts', templateFile: 'templates/sagas.tpl', skipIfExists: true }); if (data.breakpoints) { var breaks = data.breakpoints.split(','); for (var i = 0; i < breaks.length; ++i) { breaks[i] = breaks[i].trim(); if (breaks[i] === '') breaks.splice(i, 1); } if (breaks.length >= 5) { data.breakpoint1 = breaks[0]; data.breakpoint2 = breaks[1]; data.breakpoint3 = breaks[2]; data.breakpoint4 = breaks[3]; data.breakpoint5 = breaks[4]; actions.push({ type: 'modify', path: '{{cwd}}/' + data.storeDir + '/store/store.module.ts', pattern: /(enableResponsiveness\s*:\s*)(true)/, templateFile: 'templates/store.responsiveness-configuration.tpl' }); } else console.log("Please provide at least five breakpoints to customize responsiveness"); } let appModuleFile = getSrcFileAbsolutePath('app.module.ts'); if (!verifyInAppModuleImport(appModuleFile)) { appModuleImportsOnNewLines(appModuleFile); actions.push({ type: 'modify', path: appModuleFile, pattern: /((imports)\s*\t*\n*:\s*\t*\n*\[)/gi, template: '$1\n\t\tStoreModule,' }); actions.push({ type: 'modify', path: appModuleFile, pattern: /(\n*@NgModule\s*\t*\n*\(\s*\t*\n*\{)/gi, template: '\n\nimport { StoreModule } from \'' + data.storeDir + '/store/store.module\';$1' }); } return actions; } }); } else { let srcPath = storeModulePath.substring(0, storeModulePath.indexOf('/store/store.module.ts')); srcPath = srcPath.substring(2); setSrcDir(srcPath); plop.setGenerator('create', { prompts: [{ type: 'list', name: 'operation', message: 'Select an operation', choices: [ { value: 'substate', name: 'Create substate' }, { value: 'persist', name: 'Persist a substate' }, { value: 'epic', name: 'Create epic' }, { value: 'saga', name: 'Create saga' } ] }, { when: function(response) { return response.operation === 'substate'; }, type: 'list', name: 'substateFunction', message: 'What type of substate?', choices: [ { value: 'ws', name: 'Web service wrapper substate' }, { value: 'generic', name: 'Generic substate' } ] }, { when: function(response) { return response.operation === 'substate' && response.substateFunction === 'generic'; }, type: 'input', name: 'substateNoWsName', message: 'Name?' }, { when: function(response) { return response.operation === 'substate' && response.substateFunction === 'ws'; }, type: 'input', name: 'substateWsName', message: 'Data name?' }, { when: function(response) { return response.operation === 'substate' && response.substateFunction === 'generic'; }, type: 'list', name: 'substateType', message: 'Type?', choices: [ 'boolean', 'number', 'string', 'object' ] }, { when: function(response) { return response.operation === 'substate' && response.substateFunction === 'generic'; }, type: 'input', name: 'substateNoWsActions', message: 'Do you want to add some actions?\n(Please insert them separated by commas; or leave it blank to skip this step)' }, { when: function(response) { return response.operation === 'substate' && response.substateFunction === 'ws'; }, type: 'confirm', name: 'substateWsUseAdapter', message: 'Do you want to use an adapter for your data? (Useful when you want data indexing and auto data ordering on retrieve)' }, { when: function(response) { return response.operation === 'substate' && response.substateFunction === 'ws'; }, type: 'input', name: 'substateWsAction', message: 'What is the name of data retrieve action?' }, { when: function(response) { return response.operation === 'substate' && response.substateFunction === 'ws'; }, type: 'input', name: 'substateWsProvider', message: 'Insert a provider name. Please provide the same name if you want to add another call to the same provider.' }, { when: function(response) { return response.operation === 'substate' && response.substateFunction === 'generic'; }, type: 'confirm', name: 'substateNoWsStatic', message: 'Do you want to add the reducer statically?\n(alternatively you can add it dynamically everywhere in your code)' }, { when: function(response) { return response.operation === 'persist'; }, type: 'input', name: 'persistSubstate', message: 'What existent substate do you want to persist?' }, { when: function(response) { return response.operation === 'persist'; }, type: 'confirm', name: 'persistSecure', message: 'Do you want to persist it securely?' }, { when: function(response) { return response.operation === 'epic'; }, type: 'input', name: 'epicSubstate', message: 'Which state do you want to add the epic to?' }, { when: function(response) { return response.operation === 'epic'; }, type: 'input', name: 'epicOnTriggerAction', message: 'What is the launch existent action to trigger?' }, { when: function(response) { return response.operation === 'epic'; }, type: 'input', name: 'epicName', message: 'Give a name to the epic method:' }, { when: function(response) { return response.operation === 'epic'; }, type: 'confirm', name: 'epicStatic', message: 'Do you want to add the epic statically?\n(alternatively you can add it dynamically everywhere in your code)' }, { when: function(response) { return response.operation === 'saga'; }, type: 'input', name: 'sagaOnTriggerAction', message: 'Please provide the full trigger action type:' }, { when: function(response) { return response.operation === 'saga'; }, type: 'input', name: 'sagaName', message: 'Give a name to the saga method:' },], actions: function(data) { var actions = []; var storeDirectory = getStoreDirectory(true); if (data.operation === 'substate') { //Generic substate creation logics if (data.substateFunction === 'generic') { if (data.substateNoWsName && data.substateType) { if (data.substateType === 'object') data.substateObject = true; else { data.substateNotObject = true; if (data.substateType === 'boolean') data.substateInitalValue = 'false'; else if (data.substateType === 'number') data.substateInitalValue = '0'; else if (data.substateType === 'string') data.substateInitalValue = 'null'; } //Setting data to add actions in substate slice if (data.substateNoWsActions && data.substateNoWsActions.length) { var actionArray = data.substateNoWsActions.split(','); for (var i = 0; i < actionArray.length; ++i) { actionArray[i] = actionArray[i].trim(); if (actionArray[i] === '') actionArray.splice(i, 1); } data.actionArray = actionArray; plop.setPartial('stateType', pascalCase(data.substateNoWsName) + 'State'); } //Create generic substate model actions.push({ type: 'add', path: storeDirectory + '{{ dashCase substateNoWsName }}/{{ dashCase substateNoWsName }}.model.ts', templateFile: 'templates/substate/substate.model.tpl' }); //Create generic substate slice actions.push({ type: 'add', path: storeDirectory + '{{ dashCase substateNoWsName }}/{{ dashCase substateNoWsName }}.slice.ts', templateFile: 'templates/substate/substate.slice.tpl' }); //Static generic substate reducer add logics if (data.substateNoWsStatic) { //Create generic substate selectors and dispatchers file actions.push({ type: 'add', path: storeDirectory + '{{ dashCase substateNoWsName }}/{{ dashCase substateNoWsName }}.selectors-dispatchers.ts', templateFile: 'templates/substate/substate.selectors-dispatchers.tpl' }); if (matchRegex(/(export\s*\{\s*\}\s*;)/gi, getSrcFileAbsolutePath("store/index.ts"))) { //Remove default export {}; from index actions.push({ type: 'modify', pattern: /(export\s*\{\s*\}\s*;)/gi, path: storeDirectory + 'index.ts', template: '' }); } //Append actions class to store index actions.push({ type: 'append', path: storeDirectory + 'index.ts', template: 'export { {{ pascalCase substateNoWsName}}Actions } from \'./{{ dashCase substateNoWsName }}/{{ dashCase substateNoWsName }}.selectors-dispatchers\';' }); //Add actions class import to store module if (verifyIfStringInFileExists("//Actions imports:", getSrcFileAbsolutePath('store.module.ts'))) actions.push({ type: 'modify', path: storeDirectory + 'store.module.ts', pattern: /(\/\/Actions imports: PLEASE DON'T DELETE OR MODIFY THIS PLACEHOLDER)/gi, template: '{{ pascalCase substateNoWsName}}Actions' }); else actions.push({ type: 'modify', path: storeDirectory + 'store.module.ts', pattern: /(\n\s*\}\s*from\s*\'.\/index\'\s*;)/gi, template: ',\n\t{{ pascalCase substateNoWsName}}Actions$1' }); //Add actions class to store module provide if (verifyIfStringInFileExists("//Actions:", getSrcFileAbsolutePath('store.module.ts'))) actions.push({ type: 'modify', path: storeDirectory + 'store.module.ts', pattern: /(\/\/Actions: PLEASE DON'T DELETE OR MODIFY THIS PLACEHOLDER)/gi, template: '{{ pascalCase substateNoWsName}}Actions' }); else actions.push({ type: 'modify', path: storeDirectory + 'store.module.ts', pattern: /(const\s*ACTIONS\s*=\s*\[(\s*\n*\w*Actions\b\,*)*)/gi, template: '$1,\n\t{{ pascalCase substateNoWsName}}Actions' }); //Append generic substate reducer import to store actions.push({ type: 'modify', path: storeDirectory + 'store.reducer.ts', pattern: /(\nexport function rootReducer)/gi, template: 'import { {{ camelCase substateNoWsName }}Reducer } from \'./{{ dashCase substateNoWsName }}/{{ dashCase substateNoWsName }}.slice\';\n$1' }); //Append generic substate reducer if (verifyIfStringInFileExists("//Reducers:", getSrcFileAbsolutePath('store.reducer.ts'))) actions.push({ type: 'modify', path: storeDirectory + 'store.reducer.ts', pattern: /(\/\/Reducers: PLEASE DON'T DELETE OR MODIFY THIS PLACEHOLDER)/gi, template: '{{ camelCase substateNoWsName }}: {{ camelCase substateNoWsName }}Reducer' }); else actions.push({ type: 'modify', path: storeDirectory + 'store.reducer.ts', pattern: /(return\s*\n*\{(\s*\n*\w*\s*\:\s*\w*Reducer\b\,*)*)/, template: '$1,\n\t\t{{ camelCase substateNoWsName }}: {{ camelCase substateNoWsName }}Reducer' }); } } } else if (data.substateFunction === 'ws') //Ws substate creation logics { //Set a flag before ws substate creation to detect first initialization var wsCreation = false; if (!getSrcFileRelativePath('ws.selectors-dispatchers.ts')) wsCreation = true; //Add ws model file actions.push({ type: 'add', path: storeDirectory + 'ws/ws.model.ts', templateFile: 'templates/ws-substate/ws.model.tpl', abortOnFail: false }); //Add ws selectors and dispatchers file actions.push({ type: 'add', path: storeDirectory + 'ws/ws.selectors-dispatchers.ts', templateFile: 'templates/ws-substate/ws.selectors-dispatchers.tpl', abortOnFail: false }); //Add ws slices file actions.push({ type: 'add', path: storeDirectory + 'ws/ws.slice.ts', templateFile: 'templates/ws-substate/ws.slice.tpl', abortOnFail: false }); //If ws files are just created if (wsCreation) { //Append ws reducer import to store actions.push({ type: 'modify', path: storeDirectory + 'store.reducer.ts', pattern: /(\nexport function rootReducer)/gi, template: 'import { wsReducer } from \'./ws/ws.slice\';\n$1' }); //Append ws reducer if (verifyIfStringInFileExists("//Reducers:", getSrcFileAbsolutePath('store.reducer.ts'))) actions.push({ type: 'modify', path: storeDirectory + 'store.reducer.ts', pattern: /(\/\/Reducers: PLEASE DON'T DELETE OR MODIFY THIS PLACEHOLDER)/gi, template: 'ws: wsReducer' }); else actions.push({ type: 'modify', path: storeDirectory + 'store.reducer.ts', pattern: /(return\s*\n*\{(\s*\n*\w*\s*\:\s*\w*Reducer\b\,*)*)/, template: '$1,\n\t\tws: wsReducer' }); if (matchRegex(/(export\s*\{\s*\}\s*;)/gi, getSrcFileAbsolutePath("store/index.ts"))) { //Remove default export {}; from index actions.push({ type: 'modify', pattern: /(export\s*\{\s*\}\s*;)/gi, path: storeDirectory + 'index.ts', template: '' }); } //Append Ws actions class to store index actions.push({ type: 'append', path: storeDirectory + 'index.ts', template: 'export { WsActions } from \'./ws/ws.selectors-dispatchers\';' }); //Append Ws actions class import to store module if (verifyIfStringInFileExists("//Actions imports:", getSrcFileAbsolutePath('store.module.ts'))) actions.push({ type: 'modify', path: storeDirectory + 'store.module.ts', pattern: /(\/\/Actions imports: PLEASE DON'T DELETE OR MODIFY THIS PLACEHOLDER)/gi, template: 'WsActions' }); else actions.push({ type: 'modify', path: storeDirectory + 'store.module.ts', pattern: /(\n\s*\}\s*from\s*\'.\/index\'\s*;)/gi, template: ',\n\tWsActions$1' }); //Append Ws actions class provide to store module if (verifyIfStringInFileExists("//Actions:", getSrcFileAbsolutePath('store.module.ts'))) actions.push({ type: 'modify', path: storeDirectory + 'store.module.ts', pattern: /(\/\/Actions: PLEASE DON'T DELETE OR MODIFY THIS PLACEHOLDER)/gi, template: 'WsActions' }); else actions.push({ type: 'modify', path: storeDirectory + 'store.module.ts', pattern: /(const\s*ACTIONS\s*=\s*\[(\s*\n*\w*Actions\b\,*)*)/gi, template: '$1,\n\tWsActions' }); } //Create new provider class if doesn't exist yet actions.push({ type: 'add', path: '{{cwd}}/' + SRC_DIR + '/providers/{{ dashCase substateWsProvider }}.ts', templateFile: 'templates/ws-substate/ws.provider.tpl', abortOnFail: false }); //Create provider index file if doesn't exist yet actions.push({ type: 'add', path: '{{cwd}}/' + SRC_DIR + '/providers/index.ts', template: 'export { {{ pascalCase substateWsProvider }}Provider } from \'./{{ dashCase substateWsProvider }}\';', abortOnFail: false }); //Add provider call to provider actions.push({ type: 'modify', path: '{{cwd}}/' + SRC_DIR + '/providers/{{ dashCase substateWsProvider }}.ts', pattern: /(export\s*class\s*\w*Provider\b\s*\n*\{)/, templateFile: 'templates/ws-substate/ws.provider-call.tpl' }); //Append provider to index var indexPath = getSrcFileAbsolutePath("providers/index.ts"); if (indexPath && !verifyIfStringInFileExists(pascalCase(data.substateWsProvider) + "Provider", indexPath)) actions.push({ type: 'append', path: '{{cwd}}/' + SRC_DIR + '/providers/index.ts', template: 'export { {{ pascalCase substateWsProvider }}Provider } from \'./{{ dashCase substateWsProvider }}\';' }); var slicePath = getSrcFileAbsolutePath("ws.slice.ts"); if (slicePath && !verifyIfStringInFileExists(pascalCase(data.substateWsProvider) + "Provider", slicePath)) { //Add the new provider import actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.slice.ts', pattern: /(\n\s*}\s*from\s*\'..\/..\/providers)/, template: ',\n\t{{ pascalCase substateWsProvider }}Provider$1' }); //Append provider provide to ws slice actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.slice.ts', pattern: /(Injector\s*\.\s*create\s*\(\s*\{\s*providers\s*\:\s*\[(\s*\n*\{\s*provide\s*\:\s*\w*Provider\b\s*\}\,*)*)/gi, template: '$1,\n\t{ provide: {{ pascalCase substateWsProvider }}Provider }' }); //Append provider instance to ws slice actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.slice.ts', pattern: /(\]\s*\n*\}\s*\n*\)\s*\n*\;\n(\s*\n*const\s*\w*Provider\b\s*\=\s*wsProvidersInjector\s*\.\s*get\s*\(\s*\w*Provider\b\)\s*\;)*)/gi, template: '$1\nconst {{camelCase substateWsProvider}}Provider = wsProvidersInjector.get({{pascalCase substateWsProvider}}Provider);' }); } //Append new thunk to ws slice if (slicePath && !verifyIfStringInFileExists(camelCase(data.substateWsAction) + "Thunk", slicePath)) actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.slice.ts', pattern: /(export\s*const\s*\w*Thunk\b)/, template: 'export const {{ camelCase substateWsAction }}Thunk = prepareThunk(\'ws\', \'{{ camelCase substateWsAction }}\', {{camelCase substateWsProvider}}Provider.{{ camelCase substateWsAction }});\n$1' }); var selectorsDispatchersPath = getSrcFileAbsolutePath("ws.selectors-dispatchers.ts"); if (selectorsDispatchersPath && !verifyIfStringInFileExists(camelCase(data.substateWsAction) + "Thunk", selectorsDispatchersPath)) { //Append thunk import in ws selectors dispatchers file actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.selectors-dispatchers.ts', pattern: /(\n\s*\}\s*from\s*\'\.\/ws\.slice\'\s*\;)/, template: ',\n\t{{ camelCase substateWsAction }}Thunk$1' }); //Append new action to trigger thunk execution actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.selectors-dispatchers.ts', pattern: /(export\s*class\s*WsActions\s*\n*\{)/, templateFile: 'templates/ws-substate/ws.action.tpl' }); } var modelPath = getSrcFileAbsolutePath("ws.model.ts"); //Ws substate data adapter logics data.substateWsNotUseAdapter = !data.substateWsUseAdapter; if (data.substateWsUseAdapter) { //Create ws substate data dto actions.push({ type: 'add', path: '{{cwd}}/' + SRC_DIR + '/entities/dto/{{ camelCase substateWsName }}DTO.ts', templateFile: 'templates/ws-substate/ws.dto.tpl' }); //Add dto import to ws substate model if (modelPath && !verifyIfStringInFileExists(pascalCase(data.substateWsName) + "DTO", modelPath)) { if (matchRegex(/(\w*DTO\b)/, modelPath)) actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.model.ts', pattern: /(\'\@redux\-multipurpose\/core'\s*\;\n*)/, template: '$1import { {{ pascalCase substateWsName }}DTO } from \'../../entities/dto/{{ camelCase substateWsName }}DTO\';\n' }); else actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.model.ts', pattern: /(\'\@redux\-multipurpose\/core'\s*\;\n*)/, template: '$1import { {{ pascalCase substateWsName }}DTO } from \'../../entities/dto/{{ camelCase substateWsName }}DTO\';\n\n' }); } //Add new data adapter to ws model actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.model.ts', pattern: /(export\s*\n*const\s*\n*INITIAL_STATE_WEB_SERVICES)/gi, templateFile: 'templates/ws-substate/ws.adapter.tpl' }); //Append new data to ws model if (modelPath && !verifyIfStringInFileExists(camelCase(data.substateWsName) + "Adapter", modelPath)) actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.model.ts', pattern: /(INITIAL_STATE_WEB_SERVICES\s*=\s*createWsInitialState\s*\(\s*\[\s*)/gi, template: '$1{ \'{{ camelCase substateWsName }}\': { data: {{ camelCase substateWsName }}Adapter.getInitialState({ available: null }) }},\n\t' }); actions.push({ type: 'modify', path: storeDirectory + 'index.ts', pattern: /(\{\s*WsActions\s*\})/gi, template: '{\n\tWsActions\n}' }); actions.push({ type: 'modify', path: storeDirectory + 'index.ts', pattern: /(WsActions)/gi, template: '{{ camelCase substateWsName }}Object, {{ camelCase substateWsName }}Array, {{ camelCase substateWsName }}Count,\n\t$1' }); //Append adapter import to ws slice actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.slice.ts', pattern: /(INITIAL_STATE_WEB_SERVICES\s*\n*}\s*\n*from\s*\n*\'\.\/ws.model\')/gi, template: '{{ camelCase substateWsName }}Adapter,\n\t$1' }); //Append substate reducer to ws slice if (slicePath && !verifyIfStringInFileExists(camelCase(data.substateWsAction) + "Thunk", slicePath)) actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.slice.ts', pattern: /(extraReducers\s*\n*\:\s*\n*prepareThunkActionReducers\s*\n*\(\s*\n*\[\s*\n*)/gi, template: '$1{ thunk: {{ camelCase substateWsAction }}Thunk, substate: \'{{ camelCase substateWsName }}\', adapter: {{ camelCase substateWsName }}Adapter },\n\t\t' }); //Append adapter import to ws selectors dispatchers if (selectorsDispatchersPath && !verifyIfStringInFileExists(camelCase("substateWsName") + "Adapter", selectorsDispatchersPath)) { if (!verifyIfStringInFileExists("'./ws.model'", selectorsDispatchersPath)) actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.selectors-dispatchers.ts', pattern: /(from\s*'.\/ws.slice';*)/, template: '$1\n\nimport {\n\t{{ camelCase substateWsName }}Adapter\n} from \'./ws.model\';' }); else actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.selectors-dispatchers.ts', pattern: /(import\s*\{(\s*\w*Adapter\b,*)+)/, template: '$1,\n\t{{ camelCase substateWsName }}Adapter' }); } //Append default with adapter selectors actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.selectors-dispatchers.ts', pattern: /(\@Injectable\(\))/gi, templateFile: 'templates/ws-substate/ws.selectors.tpl' }); } else //Not using ws substate data adapter logics { //Append new data to ws model if (modelPath && !verifyIfStringInFileExists("'" + camelCase(data.substateWsName) + "'", modelPath)) actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.model.ts', pattern: /(INITIAL_STATE_WEB_SERVICES\s*=\s*createWsInitialState\s*\(\s*\[\s*)/gi, template: '$1\'{{ camelCase substateWsName }}\',\n\t' }); //Append substate reducer to ws slice if (slicePath && !verifyIfStringInFileExists(camelCase(data.substateWsAction) + "Thunk", slicePath)) actions.push({ type: 'modify', path: storeDirectory + 'ws/ws.slice.ts', pattern: /(extraReducers\s*\n*\:\s*\n*prepareThunkActionReducers\s*\n*\(\s*\n*\[\s*\n*)/gi, template: '$1{ thunk: {{ camelCase substateWsAction }}Thunk, substate: \'{{ camelCase substateWsName }}\', adapter: null },\n\t\t' }); } } } else if (data.operation === 'persist') { if (data.persistSubstate && data.persistSubstate.length && verifyIfWholeWordInFileExists(camelCase(data.persistSubstate), getSrcFileAbsolutePath("store.reducer.ts"))) { const regex = new RegExp(camelCase(data.persistSubstate) + /\s*\:\s*/.source + camelCase(data.persistSubstate) + "Reducer"); if (data.persistSecure) { //Replace reducer with secure persisted reducer actions.push({ type: 'modify', path: storeDirectory + 'store.reducer.ts', pattern: regex, template: '{{camelCase persistSubstate}}: {{camelCase persistSubstate}}SecurePersistedReducer' }); const key = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); //Add secure persisted reducer creation actions.push({ type: 'modify', path: storeDirectory + 'store.reducer.ts', pattern: /(export\s*function\s*rootReducer\s*\(.+?(?=\))\)\s*\{\s*)/gi, template: '$1const {{camelCase persistSubstate}}SecurePersistedReducer = createSecureStoredReducer(\'{{camelCase persistSubstate}}\', \'' + key + '\', storage, {{camelCase persistSubstate}}Reducer);\n\n\t' }); } else { //Replace reducer with persisted reducer actions.push({ type: 'modify', path: storeDirectory + 'store.reducer.ts', pattern: regex, template: '{{camelCase persistSubstate}}: {{camelCase persistSubstate}}PersistedReducer' }); //Add persisted reducer creation actions.push({ type: 'modify', path: storeDirectory + 'store.reducer.ts', pattern: /(export\s*function\s*rootReducer\s*\(.+?(?=\))\)\s*\{\s*)/gi, // .+?(?=abc) Match any characters as few as possible until a "abc" is found, without counting the "abc". template: '$1const {{camelCase persistSubstate}}PersistedReducer = createStoredReducer(\'{{camelCase persistSubstate}}\', storage, {{camelCase persistSubstate}}Reducer);\n\n\t' }); } } else console.log("Substate not found"); } else if (data.operation === 'epic') { if (data.epicSubstate && data.epicSubstate.length && getSrcFileRelativePath(kebabCase(data.epicSubstate))) { if (data.epicName && data.epicName.length) { if (data.epicOnTriggerAction && data.epicOnTriggerAction.length && verifyIfWholeWordInFileExists(camelCase(data.epicOnTriggerAction), getSrcFileAbsolutePath(kebabCase(data.epicSubstate) + '.slice.ts'))) { //Create epic file actions.push({ type: 'add', path: storeDirectory + '{{ dashCase epicSubstate }}/{{ dashCase epicSubstate }}.epics.ts', templateFile: 'templates/substate/substate.epics.tpl', skipIfExists: true }); if (!getSrcFileAbsolutePath(kebabCase(data.epicSubstate) + '.epics.ts')) actions.push({ type: 'modify', path: storeDirectory + '{{ dashCase epicSubstate }}/{{ dashCase epicSubstate }}.epics.ts', pattern: /(\'redux-observable-es6-compat\'\s*;\s*)/gi, template: '$1import {\n\t{{ camelCase epicOnTriggerAction }}\n} from \'./{{ dashCase epicSubstate }}.slice\';' }); else if (!verifyIfStringInFileExists(camelCase(data.epicOnTriggerAction), getSrcFileAbsolutePath(kebabCase(data.epicSubstate) + '.epics.ts'))) //If the trigger action wasn't added yet