import * as path from 'path' ; import chalk from 'chalk' ; import { IAddonsConfig } from '@materia/interfaces' ; import { App } from './app' ; import { Addon } from './addons/addon' ; import { ConfigType } from './config' ; class classdesc export class Addons { addons : Addon [ ] ; addonsObj : any ; addonsConfig : IAddonsConfig ; constructor ( private app ) { this . addons = [ ] ; this . addonsObj = { } ; this . addonsConfig = { } ; } returns unload ( pkg ) { this . addons . forEach ( ( addon , i ) => { if ( addon . package == pkg ) { this . addons . splice ( i , 1 ) ; } } ) ; } returns isInstalled ( pkg ) { return this . searchInstalledAddons ( ) . then ( ( installedAddons ) => { const installedAddon = installedAddons . find ( addonPkg => addonPkg === pkg ) ; return installedAddon ? true : false ; } ) ; } setupModule ( setup ) { let current = path . resolve ( this . app . path , 'node_modules' ) ; let old ; let shifted = 0 ; const paths = [ ] ; while ( current != old ) { paths . unshift ( current ) ; shifted ++ ; old = current ; current = path . resolve ( current , '..' , '..' , 'node_modules' ) ; } for ( const p of paths ) { module [ 'paths' ] . unshift ( p ) ; } const done = ( ) => { while ( shifted -- > 0 ) { module [ 'paths' ] . shift ( ) ; } } ; let setup_p ; try { setup_p = Promise . resolve ( setup ( require ) ) ; } catch ( e ) { done ( ) ; return Promise . reject ( e ) ; } return setup_p . catch ( ( e ) => { done ( ) ; throw e ; } ) . then ( ( r ) => { done ( ) ; return r ; } ) ; } returns searchInstalledAddons ( ) { let pkg ; try { const packageJsonPath = require . resolve ( path . join ( this . app . path , 'package.json' ) ) ; if ( require . cache [ packageJsonPath ] ) { delete require . cache [ packageJsonPath ] ; } pkg = require ( path . join ( this . app . path , 'package.json' ) ) ; } catch ( e ) { pkg = { dependencies : { } , devDependencies : { } } ; } const addons = [ ] ; const dependencies = Object . assign ( { } , pkg . dependencies || { } , pkg . devDependencies || { } ) ; const links = this . app . config . get < string [ ] > ( this . app . mode , ConfigType . LINKS ) || [ ] ; return this . setupModule ( ( ) => { new Set ( [ ... Object . keys ( dependencies ) , ... links ] ) . forEach ( dep => { try { const dep_pkg = require ( dep + '/package.json' ) ; if ( dep_pkg . materia ) { addons . push ( dep ) ; } } catch ( e ) { try { require ( dep + '/materia.json' ) ; addons . push ( dep ) ; } catch ( e2 ) { } } } ) ; return Promise . resolve ( addons ) ; } ) ; } loadConfig ( ) { this . addonsConfig = this . app . config . get ( this . app . mode , ConfigType . ADDONS ) ; return this . addonsConfig ; } returns loadAddons ( ) { const elapsedTimeAddons = new Date ( ) . getTime ( ) ; this . loadConfig ( ) ; return this . searchInstalledAddons ( ) . then ( addonsPkg => { this . addons = [ ] ; const promises = [ ] ; addonsPkg . forEach ( pkg => { const addon = new Addon ( this . app , pkg ) ; this . addons . push ( addon ) ; promises . push ( addon . loadFromApp ( ) ) ; } ) ; return Promise . all ( promises ) ; } ) . then ( ( ) => { let p = Promise . resolve ( ) ; this . app . logger . log ( ` └─${ this . addons . length == 0 ? '─' : '┬' } Addons: ${ chalk . bold ( this . addons . length . toString ( ) ) }` ) ; this . addons . forEach ( addon => { p = p . then ( ( ) => { this . app . logger . log ( ` │ └── ${ chalk . bold ( addon . package ) }: ${ chalk . bold ( addon . enabled ? 'OK' : chalk . red ( 'ERROR' ) ) }` ) ; if ( this . addonsConfig && this . addonsConfig [ addon . package ] && this . addonsConfig [ addon . package ] . disabled ) { addon . enabled = false ; } if ( addon . obj && typeof addon . obj . load == 'function' && addon . enabled ) { const obj = addon . obj . load ( ) ; if ( this . _isPromise ( obj ) ) { return obj ; } } return Promise . resolve ( ) ; } ) ; } ) ; return p . then ( ( ) => this . app . logger . log ( ` │ └── ${ chalk . green . bold ( 'OK' ) } - Completed in ${ chalk . bold ( ( ( new Date ( ) . getTime ( ) ) - elapsedTimeAddons ) . toString ( ) + 'ms' ) }` ) ) ; } ) ; } setConfig ( pkg , config ) { if ( ! this . addonsConfig ) { this . addonsConfig = { } ; } this . addonsConfig [ pkg ] = config ; this . app . config . set ( this . addonsConfig , this . app . mode , ConfigType . ADDONS ) ; return this . app . config . save ( ) ; } start ( ) { let p = Promise . resolve ( ) ; this . addons . forEach ( addon => { p = p . then ( ( ) => { if ( addon . enabled ) { return addon . start ( ) ; } else { return Promise . resolve ( ) ; } } ) ; } ) ; return p . catch ( e => { this . app . logger . error ( e ) ; } ) ; } returns { Array < object > } findAll ( ) { return this . addons ; } param { string } returns { object } get ( pkg ) { return this . addons . find ( addon => addon . package === pkg ) ; } returns { integer } getLength ( ) { return this . addons . length ; } loadFiles ( ) { let p = Promise . resolve ( ) ; this . addons . forEach ( ( addon ) => { p = p . then ( ( ) => { if ( addon . enabled ) { return this . app . entities . loadFiles ( addon ) ; } return Promise . resolve ( ) ; } ) ; } ) ; return p ; } handleHook ( name ) { let p = Promise . resolve ( ) ; this . addons . forEach ( addon => { p = p . then ( ( ) => { if ( addon . enabled && typeof addon [ name ] == 'function' ) { return addon [ name ] ( ) ; } return Promise . resolve ( ) ; } ) ; } ) ; return p ; } loadEntities ( ) { return this . handleHook ( 'beforeLoadEntities' ) . then ( ( ) => { let p = Promise . resolve ( ) ; this . addons . forEach ( ( addon ) => { if ( addon . enabled ) { p = p . then ( ( ) => this . app . entities . loadEntities ( addon ) ) ; } } ) ; return p ; } ) . then ( ( ) => { return this . handleHook ( 'afterLoadEntities' ) ; } ) ; } loadQueries ( ) { return this . handleHook ( 'beforeLoadQueries' ) . then ( ( ) => { let p = Promise . resolve ( ) ; this . addons . forEach ( ( addon ) => { p = p . then ( ( ) => { if ( addon . enabled ) { return this . app . entities . loadQueries ( addon ) ; } else { return Promise . resolve ( ) ; } } ) ; } ) ; return p ; } ) . then ( ( ) => { return this . handleHook ( 'afterLoadQueries' ) ; } ) ; } loadActions ( ) { return this . handleHook ( 'beforeLoadHooks' ) . then ( ( ) => { let p = Promise . resolve ( ) ; this . addons . forEach ( ( addon ) => { p = p . then ( ( ) => { if ( addon . enabled ) { return this . app . actions . load ( addon ) ; } else { return Promise . resolve ( ) ; } } ) ; } ) ; return p ; } ) . then ( ( ) => { return this . handleHook ( 'afterLoadHooks' ) ; } ) ; } loadAPI ( ) { return this . handleHook ( 'beforeLoadAPI' ) . then ( ( ) => { let p = Promise . resolve ( ) ; this . addons . forEach ( ( addon ) => { p = p . then ( ( ) => { if ( addon . enabled ) { return this . app . api . load ( addon ) ; } else { return Promise . resolve ( ) ; } } ) ; } ) ; return p ; } ) . then ( ( ) => { return this . handleHook ( 'afterLoadAPI' ) ; } ) ; } private _isPromise ( obj ) { return obj && obj . then && obj . catch && typeof obj . then === 'function' && typeof obj . catch === 'function' ; } }