/** * 框架核心 * @author yutent * @date 2020/09/28 10:01:47 */ import 'es.shim' // 加载拓展方法 import http from 'node:http' import { parse, join } from 'node:path' import fs from 'iofs' import Request from '@gm5/request' import Response from '@gm5/response' // import { sessionPackage, sessionConnect } from '@gm5/session' // import { jwtPackage, jwtConnect } from '@gm5/jwt' import { noop, readonlyProp } from './lib.js' import config from './config/index.js' import { createRouter } from './middleware/router.js' import { createCors } from './middleware/cors.js' process.on('uncaughtException', err => { console.error('UncaughtException: ', err) }) class Five { #config = config #modules = {} #middlewares = [createCors()] #server = null constructor() { readonlyProp(this, 'state', Object.create(null)) } #loadBuildIn() { // let { domain, website, session, jwt } = this.#config // domain = domain || website // session.domain = session.domain || domain // this.set({ domain, session }) // 将jwt & session中间件提到最前 // 以便用户自定义的中间件可以直接操作session // if (session.enabled) { // this.install(sessionPackage) // this.#middlewares.unshift(sessionConnect) // } // // 开启jwt // if (jwt) { // this.install(jwtPackage) // this.#middlewares.unshift(jwtConnect) // } // 路由中间件要在最后 // this.use(createRouter()) } async #loop(req, res, idx = 0) { let fn = this.#middlewares[idx] if (fn) { await fn.call(this, req, res, _ => { idx++ this.#loop(req, res, idx) }) } } /*------------------------------------------------------------------------*/ get server() { return this.#server } /*------------------------------------------------------------------------*/ // 注册属性到全局Five对象 set(obj) { for (let i in obj) { if (typeof obj[i] === 'object' && !Array.isArray(obj[i])) { if (!this.#config[i]) { this.#config[i] = obj[i] } else { try { Object.assign(this.#config[i], obj[i]) } catch (err) { console.error(err) } } } else { this.#config[i] = obj[i] } } return this } // 获取全局配置 get(key) { return this.#config[key] } // 加载中间件 // 与别的中间件用法有些不一样, 回调的传入参数中的req和res, // 并非原生的request对象和response对象, // 而是框架内部封装过的,可通过origin属性访问原生的对象 use(fn = noop) { if (typeof fn === 'function') { this.#middlewares.push(fn) return this } throw TypeError('argument must be a function') } // 注入实例化对象到实例池中 // 与use方法不同的是, 这个会在server创建之前就已经执行 install({ name, install }, args) { this['$$' + name] = install.call(this, args) return this } // 预加载应用, 缓存以提高性能 preload(dir) { let list = fs.ls(dir) if (list) { list.forEach(item => { let { name } = parse(item) if (name.startsWith('.')) { return } // 如果是目录,则默认加载index.js, 其他文件不加载 // 交由index.js自行处理, 用于复杂的应用 if (fs.isdir(item)) { item = join(item, './index.js') } this.#modules[name] = import(item) .then(r => r.default) .catch(err => { console.error(err) return null }) }) } return this } $load(name) { return this.#modules[name] } // 启动http服务 listen(port) { this.set({ port }) return this } run() { this.#server = http.createServer() // this.#loadBuildIn() // 路由中间件要在最后 this.use(createRouter()) this.#server .on('request', (req, res) => { let request = new Request(req, res) let response = new Response(req, res) if (response.ended) { return } response.set('X-Powered-By', this.get('X-Powered-By') || 'Five.js') this.#loop(request, response) }) .on('listening', _ => { if (this.get('debug')) { console.log('Server successfully started ...') console.log('%s://%s:%d\n', 'http', '127.0.0.1', this.get('port')) } }) .on('error', err => { console.error(err) }) .listen(this.get('port')) return this } } export function createApp(conf = {}) { let app = new Five() app.set(conf) return app }