Comparing version 0.0.3 to 0.0.4
@@ -12,3 +12,3 @@ /** | ||
constructor(req, res){ | ||
let that = this; | ||
let that = this | ||
this.ctx = { | ||
@@ -21,6 +21,6 @@ req: req, | ||
set body( str ){ | ||
that.__render (str); | ||
that.__render (str) | ||
} | ||
}; | ||
} | ||
} | ||
@@ -31,15 +31,15 @@ | ||
__render (stuff) { | ||
let res = this.ctx.res; | ||
let res = this.ctx.res | ||
// 输出 | ||
if( !res || stuff === null || stuff === undefined ){ | ||
return; | ||
return | ||
} | ||
let contype = 'text/html'; | ||
let contype = 'text/html' | ||
if( typeof stuff == 'object' ){ | ||
stuff = JSON.stringify(stuff); // json 字符串 | ||
contype = 'application/json'; | ||
contype = 'application/json' | ||
} | ||
res.setHeader('Content-Type', contype+'; charset=utf-8') | ||
res.writeHead(200) | ||
res.end( stuff ); | ||
res.end( stuff ) | ||
@@ -46,0 +46,0 @@ } |
@@ -8,3 +8,3 @@ /** | ||
let hascnf = ( appInfo.conf && appInfo.conf.log ) || appInfo.conf.log; | ||
let hascnf = ( appInfo.config && appInfo.config.log ) || appInfo.config.log; | ||
@@ -11,0 +11,0 @@ |
@@ -41,3 +41,7 @@ /** | ||
get app () { | ||
return this.pwd + '/app' | ||
} | ||
} | ||
@@ -44,0 +48,0 @@ |
@@ -30,5 +30,12 @@ /** | ||
// POST 请求 | ||
path(...arg) { | ||
this.request( 'POST', ...arg ) | ||
return this | ||
} | ||
// 请求 | ||
request (method, pathstr, ...middleware_and_control) { | ||
let self = this | ||
method = method.toUpperCase() | ||
@@ -38,2 +45,3 @@ | ||
, append_middlewares = middleware_and_control | ||
, is_static_file_server = (ctrlfunc instanceof app.StaticFileServer) | ||
, is_static_url = ( typeof pathstr == 'string' && pathstr.indexOf('/:') == -1 ) | ||
@@ -52,7 +60,18 @@ , route_item = { | ||
if( is_static_url ){ | ||
this._route_static[ pathstr ] = route_item | ||
if (is_static_file_server) { | ||
if( typeof pathstr == 'string' ){ | ||
ctrlfunc.dropPath( pathstr ) // 去掉前缀 | ||
pathstr = new RegExp(pathstr+'(.+)') | ||
} | ||
regPath() | ||
}else if( is_static_url ){ | ||
this._route_static[ pathstr ] = route_item | ||
}else{ | ||
regPath() | ||
} | ||
function regPath () { | ||
route_item.regexp = pathRegexp(pathstr, route_item.regkeys); | ||
this._route_regexp.push( route_item ) | ||
// app.log(route_item.regexp) | ||
self._route_regexp.push( route_item ) | ||
} | ||
@@ -59,0 +78,0 @@ |
@@ -0,3 +1,35 @@ | ||
const util_object = require('../util/object') | ||
const util_array = require('../util/array') | ||
const util_string = require('../util/string') | ||
const util_fs = require('../util/fs') | ||
const util_util = require('../util/util') | ||
const fs = require('fs') | ||
const path = require('path') | ||
const querystring = require('querystring') | ||
// 压缩模块 | ||
let UglifyJS = require("uglify-js") | ||
let Less = require("less") | ||
// 读取前端模块缓存 | ||
const srcModFileContentCache = {} | ||
const srcOutputStaticPathCache = {} | ||
/** | ||
* 内核: 页面 显示 | ||
* 目录支持两级扫描 | ||
*/ | ||
@@ -8,17 +40,782 @@ | ||
// 页面类 加载 | ||
class ViewLoader | ||
{ | ||
constructor (viewer) { | ||
this.viewer = viewer | ||
this._realconfig // config 缓存 | ||
} | ||
get realConfig () { | ||
if( ! this._realconfig ) { | ||
let inh = this.viewer.inherit | ||
if (inh) { | ||
if (!app.view[inh]) { | ||
throw new Error("not find base view <"+inh+"> to inherit") | ||
} | ||
this.viewer._parent = app.view[inh] | ||
} | ||
// app.view 已经按顺序初始化完成了 | ||
this._realconfig = ViewLoader.mergeInheritConfig ( | ||
this.viewer, | ||
app.view[inh] || null | ||
) | ||
} | ||
return this._realconfig | ||
} | ||
/////////////////////////////////////////////////////// | ||
// 合并继承 config 配置,返回新的配置 | ||
static mergeInheritConfig (self, baseViewer=null) { | ||
let baseConfig = baseViewer | ||
? baseViewer._loader.realConfig | ||
: new Viewer().config | ||
, selfConfig = self.config | ||
// 预定义 | ||
selfConfig.output = selfConfig.output || {} | ||
selfConfig.output.file = selfConfig.output.file || true // 默认为文件名 | ||
// 替换 | ||
ViewLoader.mergeConfigReplace(baseConfig, selfConfig, | ||
[] | ||
) | ||
// 更新最大值 | ||
ViewLoader.mergeConfigNumberMax(baseConfig, selfConfig, | ||
['version'] | ||
) | ||
// 追加 | ||
ViewLoader.mergeConfigAppendArray(baseConfig, selfConfig, | ||
['loader', 'request', 'src', ] | ||
) | ||
ViewLoader.mergeConfigAppendArray(baseConfig.lib, selfConfig.lib, | ||
['css', 'js', ] | ||
) | ||
ViewLoader.mergeConfigAppendArray(baseConfig.output, selfConfig.output, | ||
['plugin', ] | ||
) | ||
// 扩展 | ||
ViewLoader.mergeConfigExtendCoverObject(baseConfig, selfConfig, | ||
['output', 'static', 'data', 'srcdeps', ] | ||
) | ||
// src 依赖处理 | ||
ViewLoader.sortDependentSrc(baseConfig, selfConfig) | ||
// 返回新的 | ||
return baseConfig | ||
} | ||
// 得到处理过依赖的模块列表 | ||
static sortDependentSrc (baseConfig, config) { | ||
let baserealsrc = baseConfig._realsrc || [] | ||
, localsrc = [...(config.src||[])] | ||
, allsrcdeps = baseConfig.srcdeps | ||
, isHandleDeps = {} // 已经处理过依赖的忽略 | ||
// 重复扫描处理依赖 | ||
while (true) { | ||
let hdyilai = 0 | ||
for (let i=0; i<localsrc.length; i++) { | ||
let one = localsrc[i] | ||
, spx = one.split(':') | ||
, realone = spx.length>1 ? spx[1] : spx[0] | ||
, dps = allsrcdeps[realone] | ||
// 查找依赖 | ||
if (dps && dps.length && !isHandleDeps[one]) { | ||
localsrc.splice(i, 0, ...dps) // 插入依赖 | ||
i += dps.length | ||
hdyilai ++ // 统计 | ||
isHandleDeps[one] = true | ||
} | ||
} | ||
if(hdyilai===0 ){ | ||
break | ||
} | ||
} | ||
// 依赖去重 | ||
localsrc = util_array.unique(localsrc) | ||
// 结果 | ||
baseConfig._localsrc = localsrc | ||
baseConfig._realsrc = baserealsrc.concat(localsrc) | ||
//app.log(localsrc) | ||
//app.log(baseConfig._realsrc) | ||
} | ||
// 追加配置 | ||
static mergeConfigAppendArray (base, extd, keys=[]) { | ||
if(base && extd){ | ||
for (let i in keys) { | ||
let k = keys[i] | ||
if ( Array.isArray(base[k]) && Array.isArray(extd[k]) ) { | ||
base[k] = base[k].concat(extd[k]) | ||
} | ||
} | ||
} | ||
} | ||
// 替换配置 | ||
static mergeConfigExtendCoverObject (base, extd, keys=[]) { | ||
if(base && extd){ | ||
for (let i in keys) { | ||
let k = keys[i] | ||
if ( extd[k] !== undefined ) { | ||
util_object.extend(base[k], extd[k], true) // cover | ||
} | ||
} | ||
} | ||
} | ||
// 替换配置 | ||
static mergeConfigReplace (base, extd, keys=[]) { | ||
if(base && extd){ | ||
for (let i in keys) { | ||
let k = keys[i] | ||
if ( extd[k] !== undefined ) { | ||
base[k] = extd[k] | ||
} | ||
} | ||
} | ||
} | ||
// 对比更新配置 | ||
static mergeConfigNumberMax (base, extd, keys=[]) { | ||
if(base && extd){ | ||
for (let i in keys) { | ||
let k = keys[i] | ||
if ( extd[k] !== undefined ) { | ||
if(extd[k] > base[k]){ // 以最大值为准 | ||
base[k] = extd[k] | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
// 页面组装类 | ||
class ViewAssemble | ||
{ | ||
constructor (viewer, loader) { | ||
this.viewer = viewer | ||
this.loader = loader | ||
// 组装好的静态文件 | ||
this.myHtm = '' | ||
this.myJs = '' | ||
this.myCss = '' // 追加的 | ||
this.distHtm = '' // 全部完整的 | ||
this.distJs = '' | ||
this.distCss = '' | ||
this.distMinHtm = '' // 压缩后的 | ||
this.distMinJs = '' // 压缩后的 | ||
this.distMinCss = '' // 压缩后的 | ||
this.distTplFunc = null // tppl func | ||
// 加载数据 | ||
// await this.handleDist() | ||
} | ||
// 处理储存的文件 | ||
handleDist () { | ||
let self = this | ||
let config = this.loader.realConfig | ||
return new Promise(async function( success ){ | ||
let refresh = config.output.refresh | ||
, prepared = config.output.prepared && !refresh | ||
, flph = self.storeStaticFilePath | ||
let htm_cache = await self.loadAllSrc() | ||
if ( ! htm_cache ) { | ||
// 处理压缩 | ||
await self.compressAllDist() | ||
htm_cache = self.distMinHtm || self.distHtm | ||
// 结构框架 | ||
htm_cache = self.handleHtmlStructure( htm_cache ) | ||
htm_cache = self.handleReplaceValue( htm_cache ) | ||
// 存入磁盘 | ||
if( prepared ){ | ||
fs.writeFile(flph.htm, htm_cache, ()=>{}) | ||
} | ||
} | ||
// | ||
if (prepared) { | ||
}else{ | ||
// 输出 css 和 js 文件 | ||
//app.log('fs.writeFile(flph.css, self.distMinCss || self.distCss)') | ||
//app.log(self.distMinJs || self.distJs) | ||
let finalCssCon = self.handleReplaceValue( self.distMinCss || self.distCss ) | ||
, finalJsCon = self.handleReplaceValue( self.distMinJs || self.distJs ) | ||
if (refresh) { | ||
// write js css | ||
fs.writeFile(flph.css, finalCssCon, ()=>{ | ||
fs.writeFile(flph.js, finalJsCon, ()=>{ | ||
// app.log('// write js css ------------'+(self.distMinJs || self.distJs)) | ||
success() | ||
}) | ||
}) | ||
}else{ | ||
// write js css | ||
fs.writeFile(flph.css, finalCssCon, ()=>{}) | ||
fs.writeFile(flph.js, finalJsCon, ()=>{}) | ||
} | ||
} | ||
// tppl func | ||
self.distTplFunc = util_util.tppl( htm_cache.toString() ) | ||
if (!refresh) { | ||
success() | ||
} | ||
}) | ||
} | ||
// 处理 html 结构 | ||
handleHtmlStructure (htmstr) { | ||
let config = this.loader.realConfig | ||
, uph = this.urlStaticFilePath | ||
, head = '</head>' | ||
, body = '</body>' | ||
, static_url = config.static.url || '/static' | ||
, query = config.static.query | ||
, query_str = query ? '?'+querystring.stringify(query) : '' | ||
, lib_css_elms = [] | ||
, lib_js_elms = [] | ||
, newDistHtm = htmstr | ||
// app.log(query_str) | ||
// css lib | ||
for ( let i in config.lib.css ) { | ||
let one = config.lib.css[i] | ||
one = checkIsHttpUrl(one) ? one : path.join(static_url, 'libcss', one) | ||
lib_css_elms.push(`<link href="${one}" rel="stylesheet" type="text/css" />\n` ) | ||
} | ||
// js lib | ||
for ( let i in config.lib.js ) { | ||
let one = config.lib.js[i] | ||
one = checkIsHttpUrl(one) ? one : path.join(static_url, 'libjs', one) | ||
lib_js_elms.push(`<script src="${one}" type="text/javascript"></script>\n`) | ||
} | ||
// css & js | ||
lib_css_elms.push(`<link href="${uph.css}${query_str}" rel="stylesheet" type="text/css" />\n`) | ||
lib_js_elms.push(`<script src="${uph.js}${query_str}" type="text/javascript"></script>\n`) | ||
// 替换 | ||
newDistHtm = newDistHtm.replace(head, lib_css_elms.join('')+head) | ||
newDistHtm = newDistHtm.replace(body, lib_js_elms.join('')+body) | ||
// 检查是否为 http | ||
function checkIsHttpUrl(one){ | ||
return one.substr(0, 4)=='http' | ||
} | ||
// 返回值 | ||
return newDistHtm | ||
} | ||
// 替换变量 | ||
handleReplaceValue( stuff ) { | ||
let config = this.loader.realConfig | ||
for (let i in config.replace) { | ||
if ( i.substr(0, 2) == '__' ){ | ||
continue // 私有属性 | ||
} | ||
let value = config.replace[i] | ||
stuff = util_string.replaceAll(stuff, | ||
config.replace.__prefix + i + config.replace.__suffix, | ||
value) | ||
} | ||
return stuff | ||
} | ||
// 压缩所有文件 | ||
async compressAllDist ( ) { | ||
let self = this | ||
let config = this.loader.realConfig | ||
return new Promise(async function( success ){ | ||
// less 处理 | ||
Less.render(self.distCss, { | ||
compress: config.output.compress // 是否压缩 | ||
}).then(function(output) { | ||
// output.css = string of css | ||
// output.map = string of sourcemap | ||
// output.imports = array of string filenames of the imports referenced | ||
self.distMinCss = output.css | ||
okLess() | ||
}, | ||
function(error) { | ||
okLess() | ||
}); | ||
// okLess | ||
function okLess () { | ||
if ( ! config.output.compress) { | ||
return success() | ||
} | ||
// 如果需要压缩 | ||
// 压缩 html | ||
self.distMinHtm = self.distHtm | ||
.replace(/<!--[\w\W\r\n]*?-->/gmi, '') | ||
.replace(/<!\S*!>/gmi, '') // <!libjs!> | ||
.replace(/[\r\n\t\s]+/gmi, ' ') | ||
.replace(/>\s+</gmi, '><') | ||
.replace(/\s*\[:\s*/gmi, '[:') | ||
.replace(/\s*:\]\s*/gmi, ':]') | ||
.replace(/(^\s*)|(\s*$)/gmi, '') // drop 首尾空格 | ||
// app.log(self.distHtm) | ||
// 压缩 js | ||
UglifyJS = UglifyJS || require("uglify-js") | ||
let resStuff = UglifyJS.minify(self.distJs, { | ||
toplevel: config.output.compress=='topgrade', // 高级压缩 | ||
}) | ||
self.distMinJs = resStuff.code | ||
// 成功 | ||
success() | ||
} | ||
}) | ||
} | ||
// 获取文件地址 | ||
get urlStaticFilePath () { | ||
let cnf = this.loader.realConfig | ||
, base = cnf.static.url || '/static' | ||
, vdir = cnf.version ? '/'+cnf.version : '' | ||
, name = this.viewer._filename | ||
return { | ||
htm: base + '/htm' + vdir + '/' + name + '.htm', | ||
css: base + '/css' + vdir + '/' + name + '.css', | ||
js: base + '/js' + vdir + '/' + name + '.js', | ||
} | ||
} | ||
// 获取保存文件路径 | ||
get storeStaticFilePath () { | ||
let dir = this.storeStaticDir | ||
, name = this.viewer._filename | ||
if ( ! this._ststflpt) { | ||
this._ststflpt = { | ||
htm: dir.htm + '/' + name + '.htm', | ||
css: dir.css + '/' + name + '.css', | ||
js: dir.js + '/' + name + '.js', | ||
} | ||
} | ||
return this._ststflpt | ||
} | ||
// 获取保存路径 | ||
get storeStaticDir () { | ||
if ( ! this._ststpt) { | ||
let cnf = this.loader.realConfig | ||
, base = path.join(app.path.pwd, cnf.output.static || 'static') | ||
, vdir = cnf.version ? '/'+cnf.version : '' | ||
let p = this._ststpt = { | ||
base: base, | ||
htm: base + '/htm' + vdir, | ||
css: base + '/css' + vdir, | ||
js: base + '/js' + vdir, | ||
} | ||
if( ! srcOutputStaticPathCache[base] ){ | ||
srcOutputStaticPathCache[base] = true | ||
// 创建目录 | ||
util_fs.mkdirs(p.js) | ||
util_fs.mkdirs(p.css) | ||
util_fs.mkdirs(p.htm) | ||
} | ||
} | ||
return this._ststpt | ||
} | ||
// 读取加载文件 | ||
async loadAllSrc () { | ||
let self = this | ||
return new Promise(async function( success ){ | ||
let config = self.loader.realConfig | ||
, realsrc = config._realsrc | ||
, mysrc = config._localsrc | ||
, parent = self.viewer._parent | ||
// 如果是快速启动状态,则不读取 js 和 css 模块 | ||
if ( config.output.refresh || !config.output.prepared ) { | ||
// 读取 | ||
let myJsAry = await self.readSrcModFileContents(mysrc, 'js') | ||
, myCssAry = await self.readSrcModFileContents(mysrc, 'less') // less | ||
// css js | ||
self.myCss = myCssAry.map(x=>x[2]).join(' ') | ||
self.myJs = myJsAry .map(x=>x[2]).join(' ') | ||
self.distCss = (parent ? parent._assemble.distCss : '') + ' ' + self.myCss | ||
self.distJs = (parent ? parent._assemble.distJs : '') + ' ' + self.myJs | ||
} | ||
// 如果是快速启动状态,则检查缓存 | ||
if ( config.output.prepared && !config.output.refresh ) { | ||
let htmflph = self.storeStaticFilePath.htm | ||
// 检查文件缓存 | ||
fs.readFile(htmflph, function(err, data){ | ||
if( !err ){ | ||
success(data||' ') // htm 是取自缓存 | ||
}else{ | ||
readHtmContents() | ||
} | ||
}) | ||
}else{ | ||
readHtmContents() | ||
} | ||
// htm | ||
async function readHtmContents () { | ||
let myHtmAry = await self.readSrcModFileContents(mysrc, 'htm') | ||
let distHtm = parent ? parent._assemble.distHtm : '' | ||
for (let i in myHtmAry) { | ||
let li = myHtmAry[i] | ||
, ik = li[1] | ||
, con = li[2] | ||
if(ik){ | ||
let can | ||
, ink = '<!'+ik+'!>' | ||
if( distHtm.indexOf(ink) > -1 ){ | ||
distHtm = distHtm.replace(ink, con+'\n'+ink) // 插入埋点 | ||
can = true | ||
} | ||
if( self.myHtm.indexOf(ink) > -1 ){ | ||
self.myHtm = self.myHtm.replace(ink, con+'\n'+ink) // 插入埋点 | ||
can = true | ||
} | ||
if( ! can ){ | ||
app.log(distHtm) | ||
throw new Error('view tpl htm insert key '+ink+' not find !') | ||
} | ||
}else{ | ||
distHtm += con | ||
self.myHtm += con | ||
} | ||
} | ||
self.distHtm = distHtm | ||
//app.log(this.myHtm) | ||
//app.log(distHtm) | ||
/*app.log(myCssAry) | ||
app.log(myJsAry) | ||
app.log(myHtmAry)*/ | ||
success() | ||
} | ||
}) | ||
} | ||
// 读取模块 | ||
async readSrcModFileContents (srcs, ext) { | ||
let self = this | ||
let config = self.loader.realConfig | ||
return new Promise(async function( success ){ | ||
if(!srcs || !srcs.length){ | ||
return success( [] ) | ||
} | ||
let srcnames = [] // 纯名 | ||
let rescontents = [] | ||
for (let i in srcs) { | ||
let spx = srcs[i].split(':') | ||
, name = spx[0] | ||
, insert = '' | ||
if(spx.length>1){ | ||
insert = spx[0] | ||
name = spx[1] | ||
} | ||
srcnames.push(name) | ||
rescontents.push([name, insert, '']) | ||
} | ||
// 绝对路径 | ||
let files = srcnames.map(x=>{ | ||
let bs = app.path.app + '/src/' + x + '.' | ||
return bs + ext | ||
}) | ||
// 读取文件 | ||
await self.fsReadWithCaches(files, rescontents) | ||
success( rescontents ) | ||
}) | ||
} | ||
// 读取文件 | ||
async fsReadWithCaches (files, rescontents) { | ||
let self = this | ||
let config = self.loader.realConfig | ||
return new Promise(function( success ){ | ||
let total = files.length | ||
, step = 0 | ||
for (let i=0; i<total; i++) { | ||
read(i) | ||
} | ||
function read( k ){ | ||
let file = files[k] | ||
if ( !config.output.refresh){ // 是否缓存 | ||
if(srcModFileContentCache[file]) { // 取缓存 | ||
return getOne(k, srcModFileContentCache[file]) | ||
} | ||
} | ||
fs.readFile(file, 'utf8',function(err, data){ | ||
// app.log( 'fs.readFile : ' + file + ' = '+data ) | ||
data = data || '' | ||
if ( !config.output.refresh){ // 是否缓存 | ||
srcModFileContentCache[file] = data | ||
} | ||
getOne(k, data) // 忽略错误 | ||
}) | ||
} | ||
function getOne(k, con){ | ||
rescontents[k][2] = con // 保存 | ||
step++ | ||
if(step == total){ | ||
success( rescontents ) | ||
} | ||
} | ||
}) | ||
} | ||
} | ||
// 页面输出类 | ||
class ViewRender | ||
{ | ||
constructor (viewer, assemble) { | ||
this.viewer = viewer | ||
this.assemble = assemble | ||
} | ||
// 输出数据 | ||
render (req, res) { | ||
let self = this | ||
// 取数据请求函数 | ||
let realcnf = self.viewer._loader.realConfig | ||
, dataRequests = realcnf.request | ||
, dataBase = util_object.clone(realcnf.data) | ||
// 取出无依赖的先 | ||
let nodpsfunc = {} | ||
, depsfunc = {} // 有依赖的 | ||
, funTotal = dataRequests.length // 总数 | ||
, funDone = 0 // 已完成 | ||
if (0==funTotal) { | ||
return self.__doRenderRes(res, dataBase) | ||
} | ||
for ( let i in dataRequests ) { | ||
let one = dataRequests[i] | ||
, kn = '_$_'+i | ||
if( one.length==1 ){ | ||
nodpsfunc[kn] = one[0] | ||
}else if( one.length==2 ){ | ||
nodpsfunc[one[0]||kn] = one[1] | ||
}else{ | ||
depsfunc[ one[0]||kn ] = { | ||
func: one[1], | ||
deps: one.slice(2), | ||
} | ||
} | ||
} | ||
// app.log( realcnf) | ||
// app.log( dataRequests) | ||
// 数据请求环境 | ||
let context = new app.ViewDataRequester(req, res, self.viewer) | ||
// 开始请求数据 | ||
// app.log(nodpsfunc) | ||
// 从无依赖的开始(必须有一个无依赖的,否则整个调用链条无法开始) | ||
for ( let i in nodpsfunc ) { | ||
let func = nodpsfunc[i] | ||
callDataFunc (i, func) | ||
} | ||
// 调用处理函数 | ||
function callDataFunc (name, func) { | ||
// 调用 | ||
func.call(context, function(data){ | ||
// app.log('call view data ok : '+name) | ||
funDone++ // 完成度 | ||
dataBase[name] = data | ||
scanFuncToCall() // 查找下一个执行 | ||
}, dataBase, name) | ||
} | ||
// 调用函数处理 | ||
function scanFuncToCall () { | ||
// app.log( funDone + '/' + funTotal) | ||
// 检查是否完成 | ||
if( funDone == funTotal ){ | ||
return self.__doRenderRes(res, dataBase) // 已完成 输出 | ||
} | ||
// 扫描有依赖的 | ||
for (let i in depsfunc) { | ||
let one = depsfunc[i] | ||
// 检查依赖是否都具备 | ||
if ( isReady(i, one) ) { | ||
depsfunc[i] = null // 踢出队列 | ||
callDataFunc(i, one.func) // 调用 | ||
} | ||
} | ||
} | ||
// 是否完成依赖 | ||
function isReady (name, one) { | ||
if ( ! one ) { | ||
return false | ||
} | ||
let deps = one.deps | ||
for (let i in deps) { | ||
if ( ! dataBase[ deps[i] ]) { | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
} | ||
///////////////// | ||
// 输出数据 | ||
__doRenderRes (res, tpldata) { | ||
// app.log(tpldata) | ||
let html = this.viewer._assemble.distTplFunc( tpldata ) | ||
res.end(html) | ||
// console.timeEnd('req-time-log') | ||
} | ||
} | ||
//////////////////////////////////////////////////////////////////// | ||
// 页面类 | ||
class Viewer | ||
{ | ||
constructor(){ | ||
this.is_init | ||
// 文件名(loader时会写入) | ||
this._filename | ||
// 真实的、继承合并后的 config 对象 | ||
this._parent = null // 父级 | ||
this._loader // 加载类 | ||
this._assemble // 组装类 | ||
this._render // 输出类 | ||
} | ||
// 配置 | ||
config () { | ||
// 继承自(与文件名一致) | ||
get inherit () { | ||
return null | ||
} | ||
// 获取配置 | ||
get config () { | ||
return { | ||
// 静态替换变量 | ||
version: '', | ||
src: [], | ||
srcdeps: [], | ||
output: { | ||
file: '', | ||
}, | ||
lib: { | ||
css: [], | ||
js: [], | ||
}, | ||
static: { | ||
// url: '', | ||
}, | ||
replace: { | ||
__prefix: '<%', // 默认的前缀 | ||
__suffix: '%>', // 默认的后缀 | ||
}, | ||
////////_realsrc | ||
_realsrc: [], // 展开依赖的真实src | ||
request: [ | ||
// data gain | ||
], | ||
data: { | ||
}, | ||
} | ||
} | ||
///////////////////////////// | ||
// 清理 | ||
__clean () { | ||
// this._parent = null // 父级 | ||
// this._loader = null // 加载类 | ||
// this._assemble = null // 组装类 | ||
} | ||
// 初始化 | ||
__init () { | ||
let self = this | ||
return new Promise(async function( success ){ | ||
// app.log(this._filename+'------init') | ||
if (!self._filename) { | ||
throw new Error('view need set this._filename !') | ||
} | ||
if ( self.is_init ){ | ||
return success() | ||
} | ||
self._loader = new ViewLoader( self ) | ||
self._assemble = new ViewAssemble( self, self._loader ) | ||
await self._assemble.handleDist() // 加载文件 | ||
self._render = new ViewRender( self, self._assemble ) | ||
// 初始化完成 | ||
self.is_init = true | ||
// ok | ||
success() | ||
}) | ||
} | ||
// 输出内容 | ||
async __render (req, res) { | ||
// 初始化 | ||
if( ! this.is_init ){ | ||
await this.__init() | ||
} | ||
// 重复刷新组装模块 | ||
if (this._loader.realConfig.output.refresh) { | ||
// app.log('handleDist refresh') | ||
await this._assemble.handleDist() | ||
} | ||
// 输出内容 | ||
this._render.render(req, res) | ||
} | ||
} | ||
@@ -28,2 +825,8 @@ | ||
app.Viewer = Viewer; | ||
} | ||
} | ||
/// | ||
@@ -23,15 +23,26 @@ /** | ||
// 开始加载配置等等 | ||
let app_context = await lib_loader.base(); | ||
let app = await lib_loader.base(); | ||
// 多核 | ||
let is_continue = lib_cluster.startup( app_context ); | ||
let is_continue = lib_cluster.startup( app ); | ||
if( is_continue ){ | ||
// 初始化 | ||
// 工作线程 | ||
// 启动其它服务 | ||
await lib_loader.run(); | ||
// 没有懒加载view | ||
const cnf_is_lazy_view = app.configRead('init.view.lazyload') | ||
if ( ! cnf_is_lazy_view ) { | ||
// 初始化全部view页面类 | ||
await app.okgo.viewIniter.initAllView(); | ||
} | ||
// 启动http服务 | ||
let hp = await lib_http.run( app_context ); | ||
let hp = await lib_http.run( app ); | ||
app_context.log(123); | ||
// app.log(123); | ||
@@ -41,3 +52,3 @@ } | ||
// ok | ||
resolve( app_context ); | ||
resolve( app ); | ||
}); | ||
@@ -44,0 +55,0 @@ } |
@@ -11,2 +11,4 @@ /** | ||
}; | ||
}; | ||
@@ -15,14 +15,13 @@ | ||
// 是否禁用 | ||
let not_worker = app.confRead( 'port.worker.enabled' )===false; | ||
let use_worker = app.configRead( 'port.worker.enabled' ); | ||
if (cluster.isMaster) { | ||
if( not_worker ){ | ||
if( use_worker ){ | ||
forkWorker(); // 创建子线程 | ||
}else{ | ||
// 主线程监听 继续 | ||
return true; | ||
}else{ | ||
forkWorker(); // 创建子线程 | ||
return true; | ||
} | ||
}else{ | ||
if(not_worker){ | ||
}else{ | ||
if(use_worker){ | ||
willKill(); // 是否子线程自杀 | ||
@@ -38,13 +37,16 @@ // 子线程 继续 | ||
{ | ||
let print = `worker idx:${process.env.worker_index} pid:${process.pid} started`; | ||
// app.log(`worker ${process.pid} started`); | ||
// 定时自杀 | ||
let killsec = app.confRead( 'port.worker.expire' ); | ||
let killsec = parseInt( app.configRead( 'port.worker.expire' ) ) | ||
// app.log(killsec); | ||
if( killsec > 0 ){ | ||
setTimeout(x=>{ | ||
process.exit(); // | ||
setTimeout(function(){ | ||
// app.log(killsec+'============ kill !!!!') | ||
process.exit(1); // | ||
}, killsec * 1000); | ||
print += `, after ${killsec} sec exit`; | ||
} | ||
app.log(print) | ||
@@ -57,25 +59,34 @@ } | ||
// 监听 | ||
let worker_num = app.confRead( 'port.worker.num' ) || numCPUs; | ||
let worker_num = parseInt( app.configRead( 'port.worker.num' ) ) || numCPUs; | ||
let http_port = app.confRead( 'port.listen.http' ) || 9693; | ||
let http_port = app.configRead( 'port.listen.http' ) || 9693; | ||
app.log(`expre cluster ${process.pid} listen on port ${http_port}, ${worker_num} worker starting...`); | ||
app.log(`cluster pid:${process.pid} listen on port ${http_port}, ${worker_num} worker starting...`); | ||
// 衍生工作进程 | ||
for (let i = 0; i < worker_num; i++) { | ||
cluster.fork(); | ||
forkOne(i); | ||
} | ||
// 退出重启 | ||
let delay_time = app.confRead( 'port.worker.delay' ) || 1; | ||
let delay_time = parseInt( app.configRead( 'port.worker.delay' ) ) || 1; | ||
cluster.on('exit', (worker, code, signal) => { | ||
let wkidx = worker.worker_index; | ||
app.log(`worker ${worker.process.pid} exit, after ${delay_time} sec forking...`); | ||
app.log(`worker idx:${wkidx} pid:${worker.process.pid} exit, after ${delay_time} sec forking...`); | ||
setTimeout(x=>{ | ||
cluster.fork(); // 重启一个 | ||
forkOne(wkidx); | ||
}, delay_time * 1000); | ||
}); | ||
// fork one | ||
function forkOne(idx){ | ||
let wkr = cluster.fork({ | ||
'worker_index': idx, | ||
}); // 重启一个 | ||
wkr.worker_index = idx; // worker idx copy | ||
} | ||
} | ||
} |
@@ -25,2 +25,3 @@ /** | ||
{ | ||
// console.time('req-time-log') | ||
@@ -84,10 +85,34 @@ // 解析 | ||
function callCtrl(){ | ||
// app.log(route_obj.control_func) | ||
let kind = typeof route_obj.control_func | ||
// app.log(route_obj) | ||
// 实例化控制器 | ||
let newController = new route_obj.control_class(req, res) | ||
// 执行 | ||
route_obj.control_func.call( newController ); | ||
if ( kind == 'object' ) { | ||
if ( route_obj.control_func instanceof app.StaticFileServer) | ||
{ | ||
// static file | ||
route_obj.control_func.__renderFileContent(req, res) | ||
}else if (route_obj.control_func instanceof app.Viewer) | ||
{ | ||
// view page | ||
route_obj.control_func.__render(req, res) | ||
} | ||
}else if ( kind == 'function' ){ | ||
// controller | ||
// app.log(route_obj) | ||
// 实例化控制器 | ||
let newController = new route_obj.control_class(req, res) | ||
// 执行 | ||
route_obj.control_func.call( newController ); | ||
} | ||
// route_obj.control_func instanceof app.ViewDataRequester | ||
} | ||
@@ -94,0 +119,0 @@ |
@@ -24,3 +24,3 @@ /** | ||
let cnfkey = 'port.listen.http'; | ||
let http_port = app.confRead( cnfkey ); | ||
let http_port = app.configRead( cnfkey ); | ||
if( ! http_port ){ | ||
@@ -32,3 +32,4 @@ http_port = 9693; | ||
// 监听 | ||
let worker_num = app.confRead( 'port.worker.num' ) || numCPUs; | ||
let worker_num = parseInt( app.configRead( 'port.worker.num' ) ) || numCPUs; | ||
let enable_worker = app.configRead( 'port.worker.enabled' ); | ||
@@ -38,3 +39,3 @@ //if (cluster.isMaster) { | ||
app.log(`process ${process.pid} listen port ${http_port}`); | ||
// app.log(`process ${process.pid} listen port ${http_port}`); | ||
@@ -53,6 +54,11 @@ | ||
if ( ! enable_worker ) { | ||
app.log(`process pid:${process.pid} listen on port ${http_port}`); | ||
} | ||
}); | ||
} | ||
@@ -26,3 +26,4 @@ /** | ||
const util_object = require('../util/object.js') | ||
, app = require('./app.js') | ||
, app_mod = require('./app.js') | ||
, app = app_mod.Context | ||
; | ||
@@ -48,8 +49,7 @@ | ||
await loadBoot ( ); // 引导 | ||
await loadConf ( 'conf' ); // 配置 | ||
await loadConfig ( 'config' ); // 配置 | ||
await loadCore ( ); // 核心 | ||
await loadInit ( 'init' ); // 初始化 | ||
// ok | ||
resolve( app.Context ); | ||
resolve( app ); | ||
@@ -66,6 +66,8 @@ }); | ||
// 控制器页面等 | ||
await loadConstant ( 'constant' ); // 常量 | ||
await loadInit ( 'init' ); // 初始化 | ||
await loadLibrary ( 'library' ); // library 代码库 | ||
await loadHelp ( 'help' ); // 帮助 | ||
await loadCommon ( 'common' ); // 通用 | ||
await loadMiddleware ( 'middleware' ); // 中间件 | ||
await loadCommon ( 'common' ); // 通用 | ||
await loadModel ( 'model' ); // model 模块 | ||
@@ -77,3 +79,3 @@ await loadView ( 'view' ); // web页面 | ||
// ok | ||
resolve( app.Context ); | ||
resolve( app ); | ||
@@ -98,6 +100,6 @@ }); | ||
, name = path.basename(f, '.js') | ||
, router = new app.Context.Router() // 默认提供一个 | ||
, router = new app.Router() // 默认提供一个 | ||
, ret_routers = [router] | ||
// 执行获取router | ||
let retrts = mod(app.Context, router); | ||
let retrts = mod(app, router); | ||
if( retrts ){ | ||
@@ -111,7 +113,7 @@ if(retrts[0]){ | ||
for(let i in ret_routers){ | ||
ret_routers[i].__renderForContext(app.Context); // 输出至app上下文 | ||
ret_routers[i].__renderForContext(app); // 输出至app上下文 | ||
} | ||
}); | ||
// app.Context.Log('yangjie'); | ||
// app.Log('yangjie'); | ||
@@ -133,3 +135,3 @@ // ok | ||
app.Context[dir] = app.Context[dir] || {}; | ||
app[dir] = app[dir] || {}; | ||
@@ -140,3 +142,3 @@ // 加载执行 | ||
, name = path.basename(f, '.js') | ||
, _class = mod(app.Context) | ||
, _class = mod(app) | ||
// 反射类成员函数 | ||
@@ -154,6 +156,6 @@ , ctrl_func_names = Object.getOwnPropertyNames(_class.prototype) | ||
// 执行 实例化 | ||
app.Context[dir][name] = funcs; | ||
app[dir][name] = funcs; | ||
}); | ||
// console.log(app.Context[dir]); | ||
// console.log(app[dir]); | ||
@@ -170,14 +172,37 @@ // ok | ||
return new Promise( async function(resolve, reject){ | ||
app[dir] = app[dir] || {}; | ||
// 扫描 js 文件,绝对路径 | ||
let files = await scanFile ( path.join(dir_app, './'+dir), 'js', true ); | ||
loadFiles(files); | ||
// 扫描目录 | ||
let dirs = await scanDir ( path.join(dir_app, './'+dir) ); | ||
for (let i in dirs) { | ||
let childdir = dirs[i] | ||
let childfiles = await scanFile ( path.join(dir_app, './'+dir+'/'+childdir), 'js', true ); | ||
loadFiles(childfiles, childdir); | ||
} | ||
// 加载执行 | ||
files.forEach(function( f ){ | ||
let mod = require(f) | ||
, name = path.basename(f, '.js'); | ||
// 执行 | ||
mod(app.Context); | ||
}); | ||
// 加载 view | ||
function loadFiles(files, basedir=''){ | ||
// 加载执行 | ||
files.forEach(function( f ){ | ||
let mod = require(f) | ||
, name = path.basename(f, '.js'); | ||
if (basedir) { | ||
name = basedir + '_' + name | ||
} | ||
if( app[dir][name] ) { // 不允许重复 | ||
throw new Error('view "'+name+'" path can\'t repeat !') | ||
} | ||
// 执行 | ||
let tarViewClass = mod(app); | ||
// view 类初始化 | ||
let viewer = new tarViewClass(); | ||
viewer._filename = name; // 文件名(斜杠转下划线) | ||
// app.log('---=======---'+name) | ||
app[dir][name] = viewer; | ||
}); | ||
} | ||
// ok | ||
@@ -197,3 +222,3 @@ resolve(); | ||
app.Context[dir] = app.Context[dir] || {}; | ||
app[dir] = app[dir] || {}; | ||
@@ -204,5 +229,5 @@ // 加载执行 | ||
, name = path.basename(f, '.js') | ||
, modelClass = mod(app.Context) | ||
, modelClass = mod(app) | ||
// 执行 | ||
app.Context[dir][name] = new modelClass(); | ||
app[dir][name] = new modelClass(); | ||
}); | ||
@@ -222,3 +247,3 @@ | ||
app.Context[dir] = app.Context[dir] || {}; | ||
app[dir] = app[dir] || {}; | ||
@@ -232,3 +257,3 @@ // 扫描 js 文件,绝对路径 | ||
// 执行 | ||
app.Context[dir][name] = mod; | ||
app[dir][name] = mod; | ||
}); | ||
@@ -245,3 +270,3 @@ | ||
// 执行 | ||
app.Context[dir][name] = mod; | ||
app[dir][name] = mod; | ||
}catch(e){} | ||
@@ -268,3 +293,3 @@ }); | ||
// 执行 | ||
mod(app.Context); | ||
mod(app); | ||
}); | ||
@@ -286,3 +311,3 @@ | ||
app.Context[dir] = app.Context[dir] || {}; | ||
app[dir] = app[dir] || {}; | ||
@@ -294,3 +319,3 @@ // 加载执行 | ||
// 执行 | ||
app.Context[dir][name] = mod(app.Context); | ||
app[dir][name] = mod(app); | ||
}); | ||
@@ -312,3 +337,3 @@ | ||
app.Context[dir] = app.Context[dir] || {}; | ||
app[dir] = app[dir] || {}; | ||
@@ -320,3 +345,3 @@ // 加载执行 | ||
// 执行 | ||
app.Context[dir][name] = mod(app.Context); | ||
app[dir][name] = mod(app); | ||
}); | ||
@@ -338,3 +363,3 @@ | ||
app.Context[dir] = app.Context[dir] || {}; | ||
app[dir] = app[dir] || {}; | ||
@@ -346,3 +371,3 @@ // 加载执行 | ||
// 执行 | ||
app.Context[dir][name] = mod; | ||
app[dir][name] = mod; | ||
}); | ||
@@ -356,4 +381,28 @@ | ||
// 加载常量 | ||
async function loadConstant( dir ) | ||
{ | ||
return new Promise( async function(resolve, reject){ | ||
// 扫描 js 文件,绝对路径 | ||
let files = await scanFile ( path.join(dir_app, './'+dir), 'js', true ); | ||
app[dir] = app[dir] || {}; | ||
// 加载执行 | ||
files.forEach(function( f ){ | ||
let mod = require(f) | ||
, name = path.basename(f, '.js'); | ||
// 执行 | ||
app[dir][name] = mod; | ||
}); | ||
// ok | ||
resolve(); | ||
}); | ||
} | ||
// 加载所有配置文件 | ||
async function loadConf ( dir ) | ||
async function loadConfig ( dir ) | ||
{ | ||
@@ -377,3 +426,3 @@ return new Promise( async function(resolve, reject){ | ||
app.Context[dir] = app.Context[dir] || {}; | ||
app[dir] = app[dir] || {}; | ||
@@ -385,3 +434,3 @@ // 加载配置 | ||
// 数据get | ||
app.Context[dir][name] = mod(app.Context); | ||
app[dir][name] = mod(app); | ||
}); | ||
@@ -395,4 +444,4 @@ | ||
util_object.extend( | ||
( app.Context[dir][name] || {}), | ||
mod(app.Context), | ||
( app[dir][name] || {}), | ||
mod(app), | ||
true // 覆盖 | ||
@@ -420,3 +469,3 @@ ); | ||
// 执行 | ||
mod(app.Context); | ||
mod(app); | ||
}); | ||
@@ -443,3 +492,3 @@ | ||
// 执行 | ||
mod(app.Context); | ||
mod(app); | ||
}); | ||
@@ -446,0 +495,0 @@ |
@@ -7,6 +7,32 @@ /** | ||
var path = require('path'); | ||
var exec = require('child_process').exec | ||
//递归创建目录 异步方法 | ||
var mkdirs = exports.mkdirs = function (dirname, callback=()=>{}) { | ||
fs.exists(dirname, function (exists) { | ||
if (exists) { | ||
callback(); | ||
} else { | ||
//console.log(path.dirname(dirname)); | ||
mkdirs(path.dirname(dirname), function () { | ||
fs.mkdir(dirname, callback); | ||
}); | ||
} | ||
}); | ||
} | ||
// 【小心使用】 限 linux | ||
// 强制删除文件夹 | ||
exports.deleteFolderForce = function ( abs_dir ) { | ||
exec('rm -rf '+abs_dir,function(err,out) { | ||
// console.log(out); err && console.log(err); | ||
}); | ||
} | ||
/** | ||
@@ -20,3 +46,6 @@ * 按文件名数组读取文件并按顺序合并,异步版本 | ||
{ | ||
opts = opts || {}; | ||
opts = opts || { | ||
merge: false, // 合并 | ||
ext: null, // 文件后缀名 | ||
}; | ||
@@ -23,0 +52,0 @@ var leg = nameAry.length |
@@ -13,8 +13,10 @@ | ||
for(var i in get){ | ||
if(get[i] instanceof Object){ | ||
if(get[i] instanceof Array){ | ||
if(override || !tar[i]){ | ||
tar[i] = get[i]; | ||
} | ||
}else if(get[i] instanceof Object){ | ||
tar[i] = tar[i] || {}; | ||
exports.extend(tar[i],get[i],override); | ||
}else if(get[i] instanceof Array){ | ||
if(override || !tar[i]) | ||
tar[i] = get[i]; | ||
}else{ | ||
@@ -21,0 +23,0 @@ if(override || !tar[i]) |
@@ -62,6 +62,7 @@ /** | ||
*/ | ||
exports.replaceAll = function(str, s1, s2){ | ||
return str.replace(new RegExp(s1,"gm"),s2); | ||
exports.replaceAll = function(str, s1, s2){ | ||
return str.split(s1).join(s2); | ||
// return str.replace(new RegExp(s1,"gm"),s2); | ||
} | ||
{ | ||
"name": "okgo", | ||
"version": "0.0.3", | ||
"version": "0.0.4", | ||
"description": "enterprise-level high-performance web app framework", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
95933
36
3107
20
4