import path from 'path'; import * as glob from "glob"; import {COMPONENT_TYPE, PATH_INFO, PATH_MAP, SCAN_TYPE} from "../@types/types"; import ApplicationConfig from '../config/ApplicationConfig'; import TSDocumentSimpleParser from "./TSDocumentSimpleParser"; import BIFlowApplicationContext from "../decorators/BIFlowApplicationContext"; import {logger} from "../logger/Logger"; const config:ApplicationConfig = ApplicationConfig.loadDefault(); export class ComponentScan{ public scanPathMap:PATH_MAP = {}; /** * @param value scan base directory * @desc 파라미터의 하위 모듈들을 scan 한다. */ public scan(value: string | string[]):SCAN_TYPE[] { let components:SCAN_TYPE[] = []; if(value instanceof Array){ value.forEach((value, i)=>components = components.concat(this.scan(value))); }else{ let scanBase = path.normalize(value); let allJS:string[] = glob.sync(scanBase+"/**/*.{js,ts}").filter((file:string)=> { let name = file; return (path.extname(name) !== "" && name.lastIndexOf(".d.ts") === -1); }); allJS.forEach((file:string,index)=>{ if(file.lastIndexOf(".ts")){ const parser:TSDocumentSimpleParser = TSDocumentSimpleParser.with(file); parser.parse(); if(parser.clazz){ parser.clazz.forEach((entry)=>{ entry.decorators?.forEach((decoEntry)=>{ let type = COMPONENT_TYPE.NONE; const name:string | undefined = entry.name; if(entry.name && decoEntry.name == "BIFlowApplicationContext"){ type = COMPONENT_TYPE.APPLICATION_MAIN; }else if(entry.name && decoEntry.name == "TransactionProcessor"){ type = COMPONENT_TYPE.TRANSACTION_PROCESSOR; }else if(entry.name && (decoEntry.name == "Component" )){ type = COMPONENT_TYPE.COMPONENT; }else if(entry.name && (decoEntry.name == "Bean")){ type = COMPONENT_TYPE.BEAN; } if (name && type != COMPONENT_TYPE.NONE && type != COMPONENT_TYPE.APPLICATION_MAIN) { const module = require(file); const obj = (new module.default()); let bean: SCAN_TYPE = { TYPE: type, PATH: this.toBusinessPath(file), CLASS_NAME: name, CLASS: module, BEAN: obj, ENTRY:entry }; components.push(bean); } }); }); } }else{ try{ this.addPathMap(file); require(file); }catch(e){ logger.debug(e); } } }); } return components; } public scanFromModule(value: string | string[]):SCAN_TYPE[] { let components:SCAN_TYPE[] = []; if(value instanceof Array){ value.forEach((value, i)=>components = components.concat(this.scanFromModule(value))); }else{ let scanBase = path.normalize(value); let allJS:string[] = glob.sync(scanBase+"/**/*.{js,ts}").filter((file:string)=> { let name = file; return (path.extname(name) !== "" && name.lastIndexOf("assets/") === -1 && name.lastIndexOf(".d.ts") === -1); }); allJS.forEach((file:string,index)=>{ try{ //모듈을 한번 import 해서 BEAN, TRANSACTION_PROCESSOR 를 활성화 시켜 빈을 등록한다. this.addPathMap(file); require(file); }catch(e){ logger.debug(`${file} 모듈을 import 할 수 없습니다.`); } }); } return components; } public scanFromFile(scanBasePath: string | string[], value: string):SCAN_TYPE|undefined { let component:SCAN_TYPE|undefined; if(scanBasePath instanceof Array){ scanBasePath.forEach((path)=>{ if (!component) { component = this.scanFromFile(path, value); } }); }else{ let scanBase = path.normalize(scanBasePath); let allJS:string[] = glob.sync(`${scanBase}/**/${value}.{js,ts}`).filter((file:string)=> { let name = file; return (path.extname(name) !== "" && name.lastIndexOf(".d.ts") === -1); }); allJS.forEach((file:string,index)=>{ if(!component) { const parser:TSDocumentSimpleParser = TSDocumentSimpleParser.with(file); parser.parse(); if(parser.clazz){ parser.clazz.forEach((entry)=>{ entry.decorators?.forEach((decoEntry)=>{ let type = COMPONENT_TYPE.NONE; const name:string | undefined = entry.name; if(entry.name && decoEntry.name == "BIFlowApplicationContext"){ type = COMPONENT_TYPE.APPLICATION_MAIN; }else if(entry.name && decoEntry.name == "TransactionProcessor"){ type = COMPONENT_TYPE.TRANSACTION_PROCESSOR; }else if(entry.name && (decoEntry.name == "Component" )){ type = COMPONENT_TYPE.COMPONENT; }else if(entry.name && (decoEntry.name == "Bean")){ type = COMPONENT_TYPE.BEAN; } if (name && type != COMPONENT_TYPE.NONE && type != COMPONENT_TYPE.APPLICATION_MAIN) { const module = require(file); const obj = (new module.default()); let bean: SCAN_TYPE = { TYPE: type, PATH: this.toBusinessPath(file), CLASS_NAME: name, CLASS: module, BEAN: obj, ENTRY:entry }; component = bean; } }); }); } } }); } return component; } public getPathInfo(name: string):PATH_INFO { return this.scanPathMap[name]; } public addPathMap(path:string){ let info:PATH_INFO = { FILE_NAME:"",PATH:"",BIZ_PATH:"" }; if(path.lastIndexOf("/") > -1){ info.PATH = path; info.BIZ_PATH = this.toBusinessPath(path); info.FILE_NAME = path.substring(path.lastIndexOf("/")+1, path.lastIndexOf(".")); }else{ info.PATH = path; info.BIZ_PATH = path; info.FILE_NAME = path; } if (!this.scanPathMap[info.FILE_NAME]) { this.scanPathMap[info.FILE_NAME] = info; } } public toBusinessPath(fileName:string){ let ret:string = ""; let businessPath:string = config.getBusinessPath(); if (fileName.indexOf(businessPath) != -1) { //json 서비스 ret = fileName.substring(fileName.indexOf(businessPath) + businessPath.length, fileName.length); if(ret.lastIndexOf(".") != -1){ ret = ret.substring(0, ret.lastIndexOf(".")); } }else if (fileName.indexOf("/assets/business") != -1) { //view 서비스 businessPath = "/assets/business"; ret = fileName.substring(fileName.indexOf(businessPath) + businessPath.length, fileName.length); if(ret.lastIndexOf(".") != -1){ ret = ret.substring(0, ret.lastIndexOf(".")); } } return ret; } } export const beanScanner:ComponentScan = new ComponentScan();