import 'reflect-metadata' ; import * as fs from 'fs-extra' ; import { join , sep } from 'path' ; import { find } from 'fs-jetpack' ; import { watch } from 'chokidar' ; import * as mongoose from 'mongoose' ; import * as prettier from 'prettier' ; import { Application , IBoot } from 'egg' ; export default class FooBoot implements IBoot { private readonly app : Application ; constructor ( app ) { this . app = app ; } async configWillLoad ( ) { await this . connectDB ( this . app ) ; await this . customLoadModel ( ) ; if ( this . app . config . env !== 'unittest' ) { await this . app . graphql . init ( ) ; } } configDidLoad ( ) { } async didLoad ( ) { } async willReady ( ) { } async didReady ( ) { } async serverDidReady ( ) { } async beforeClose ( ) { } public async connectDB ( app ) { const { url , options } = app . config . mongoose ; if ( url ) { const connection = await mongoose . connect ( url , options ) ; app . context . connection = connection ; } } /**
    * 手动挂载model
    */ private async customLoadModel ( ) { this . watchModel ( this . app ) ; await this . loadModel ( this . app ) ; } private capitalizeFirstLetter ( str ) { return str . charAt ( 0 ) . toUpperCase ( ) + str . slice ( 1 ) ; } private getModelName ( file ) { const filename = file . split ( sep ) . pop ( ) || '' ; const name = this . capitalizeFirstLetter ( filename . replace ( /\.ts$|\.js$/g , '' ) ) ; return name ; } private async loadModel ( app ) { const { baseDir } = app ; const modelDir = join ( baseDir , 'app' , 'model' ) ; if ( ! fs . existsSync ( modelDir ) ) return ; const matching = app . config . env === 'local' || app . config . env === 'unittest' ? '*.ts' : '*.js' ; const files = find ( modelDir , { matching } ) ; app . model = { } ; const modelWhitelist = app . config . modelWhitelist ; try { for ( const file of files ) { const modelPath = join ( baseDir , file ) ; const Model = require ( modelPath ) . default ; const name = this . getModelName ( file ) ; if ( modelWhitelist . indexOf ( name ) === - 1 ) { try { app . model [ name ] = new Model ( ) . getModelForClass ( Model ) ; } catch ( e ) { console . log ( '如果要挂载到model请将export default' ) ; } } } } catch ( e ) { console . log ( e ) ; } } private watchModel ( app ) { const { baseDir } = app ; const modelDir = join ( baseDir , 'app' , 'model' ) ; const typingsDir = join ( baseDir , 'typings' ) ; if ( ! fs . existsSync ( modelDir ) ) return ; fs . ensureDirSync ( typingsDir ) ; watch ( modelDir ) . on ( 'all' , ( eventType ) => { if ( [ 'add' , 'change' ] . includes ( eventType ) ) { this . createTyingFile ( app ) ; } if ( [ 'unlink' ] . includes ( eventType ) ) { this . createTyingFile ( app ) ; } } ) ; } private createTyingFile ( app ) { const { baseDir } = app ; const modelDir = join ( baseDir , 'app' , 'model' ) ; const files = find ( modelDir , { matching : '*.ts' } ) ; const typingPath = join ( baseDir , 'typings' , 'typegoose.d.ts' ) ; const pathArr = this . formatPaths ( files ) ; const importText = pathArr . map ( i => `import ${ i . name } from '${ i . importPath }'` ) . join ( '\n' ) ; const repoText = pathArr . map ( i => `${ i . name }: ModelType<InstanceType<${ i . name }>>` ) . join ( '\n' ) ; const text = this . getTypingText ( importText , repoText ) ; this . writeTyping ( typingPath , text ) ; } private getTypingText ( importText , modelText ) { const tpl = `
  import 'egg'
  import { InstanceType, ModelType } from 'typegoose'
  import * as mongoose from 'mongoose'
  
  ${ importText }
  
  declare module 'egg' {
    interface Context {
      connection: mongoose.Collection
      model: {
        ${ modelText }
      }
    }
  }
  ` ; return tpl ; } private writeTyping ( path , text ) { fs . writeFileSync ( path , this . formatCode ( text ) , { encoding : 'utf8' } ) ; } private formatCode ( text ) { return prettier . format ( text , { semi : false , tabWidth : 2 , singleQuote : true , parser : 'typescript' , trailingComma : 'all' , } ) ; } private formatPaths ( files ) { return files . map ( file => { const name = this . getModelName ( file ) ; file = file . split ( sep ) . join ( '/' ) ; const importPath = `../${ file }` . replace ( /\.ts$|\.js$/g , '' ) ; return { name , importPath , } ; } ) ; } }