jj.js 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +243 -173
- package/jj.js +14 -14
- package/lib/app.js +53 -15
- package/lib/cache.js +96 -30
- package/lib/config.js +21 -6
- package/lib/context.js +18 -22
- package/lib/controller.js +36 -5
- package/lib/cookie.js +11 -15
- package/lib/ctx.js +28 -28
- package/lib/db/mongodb.js +564 -0
- package/lib/db/mysql.js +294 -0
- package/lib/db/sql.js +116 -0
- package/lib/db/sqlite.js +292 -0
- package/lib/db.js +261 -331
- package/lib/loader.js +79 -44
- package/lib/logger.js +124 -126
- package/lib/middleware.js +39 -6
- package/lib/model.js +23 -9
- package/lib/{run.js → mvc.js} +29 -22
- package/lib/pagination.js +68 -14
- package/lib/request.js +6 -3
- package/lib/response.js +70 -6
- package/lib/router.js +4 -3
- package/lib/types.js +363 -196
- package/lib/upload.js +15 -15
- package/lib/url.js +10 -2
- package/lib/utils/error.js +9 -5
- package/lib/utils/utils.js +2 -0
- package/lib/utils/validate.js +13 -0
- package/lib/view.js +54 -8
- package/logo.png +0 -0
- package/package.json +10 -3
- package/types.js +181 -84
- package/CHANGELOG.md +0 -53
- package/jsdoc.conf.js +0 -17
package/lib/upload.js
CHANGED
|
@@ -61,11 +61,11 @@ class Upload extends Context
|
|
|
61
61
|
/**
|
|
62
62
|
* 保存文件到目录
|
|
63
63
|
* @public
|
|
64
|
-
* @param {string} [dir]
|
|
64
|
+
* @param {string} [dir] - 保存目录,默认为当前目录
|
|
65
65
|
* @returns {Promise<(boolean|UploadData)>}
|
|
66
66
|
*/
|
|
67
|
-
async save(dir) {
|
|
68
|
-
if(!this.check()) {
|
|
67
|
+
async save(dir = './') {
|
|
68
|
+
if(!this.check() || !this._file) {
|
|
69
69
|
return false;
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -76,7 +76,7 @@ class Upload extends Context
|
|
|
76
76
|
savename = utils.date.format('YYYY/mmdd/') + utils.md5(Date.now() + Math.random().toString(36).substr(2));
|
|
77
77
|
}
|
|
78
78
|
if(!pt.extname(savename)) {
|
|
79
|
-
savename += pt.extname(this._file.originalFilename);
|
|
79
|
+
savename += pt.extname(this._file.originalFilename || '');
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
try {
|
|
@@ -98,7 +98,7 @@ class Upload extends Context
|
|
|
98
98
|
extname: pt.extname(savename).slice(1),
|
|
99
99
|
savename,
|
|
100
100
|
filepath: filePath,
|
|
101
|
-
name: this._file.originalFilename,
|
|
101
|
+
name: this._file.originalFilename || '',
|
|
102
102
|
size: this._file.size,
|
|
103
103
|
mimetype: this._file.mimetype,
|
|
104
104
|
hash: this._file.hash
|
|
@@ -113,10 +113,10 @@ class Upload extends Context
|
|
|
113
113
|
* 获取上传文件
|
|
114
114
|
* @public
|
|
115
115
|
* @param {string} [name] - 为空时,获取所有上传文件
|
|
116
|
-
* @returns {(File|File[]|Files)}
|
|
116
|
+
* @returns {(File|File[]|Files|undefined)}
|
|
117
117
|
*/
|
|
118
118
|
getFile(name) {
|
|
119
|
-
return typeof name == 'string' ? this.ctx.request.files[name] : this.ctx.request.files;
|
|
119
|
+
return typeof name == 'string' ? this.ctx.request.files && this.ctx.request.files[name] : this.ctx.request.files;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
/**
|
|
@@ -125,7 +125,7 @@ class Upload extends Context
|
|
|
125
125
|
* @returns {string}
|
|
126
126
|
*/
|
|
127
127
|
getError() {
|
|
128
|
-
return this._error;
|
|
128
|
+
return this._error || '';
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
/**
|
|
@@ -135,7 +135,7 @@ class Upload extends Context
|
|
|
135
135
|
*/
|
|
136
136
|
check() {
|
|
137
137
|
if(!this._file) {
|
|
138
|
-
this._error = '
|
|
138
|
+
this._error = '上传文件未找到!';
|
|
139
139
|
return false;
|
|
140
140
|
}
|
|
141
141
|
|
|
@@ -167,14 +167,14 @@ class Upload extends Context
|
|
|
167
167
|
/**
|
|
168
168
|
* 检查上传文件后缀
|
|
169
169
|
* @public
|
|
170
|
-
* @param {(string|
|
|
170
|
+
* @param {(string|string[])} ext
|
|
171
171
|
* @returns {boolean}
|
|
172
172
|
*/
|
|
173
173
|
checkExt(ext) {
|
|
174
174
|
if(typeof ext == 'string') {
|
|
175
175
|
ext = ext.split(',');
|
|
176
176
|
}
|
|
177
|
-
if(!~ext.indexOf(pt.extname(this._file.originalFilename).slice(1).toLowerCase())) {
|
|
177
|
+
if(!~ext.indexOf(pt.extname(this._file && this._file.originalFilename || '').slice(1).toLowerCase())) {
|
|
178
178
|
return false;
|
|
179
179
|
}
|
|
180
180
|
return true;
|
|
@@ -183,14 +183,14 @@ class Upload extends Context
|
|
|
183
183
|
/**
|
|
184
184
|
* 检查上传文件mimetype
|
|
185
185
|
* @public
|
|
186
|
-
* @param {(string|
|
|
186
|
+
* @param {(string|string[])} type
|
|
187
187
|
* @returns {boolean}
|
|
188
188
|
*/
|
|
189
189
|
checkType(type) {
|
|
190
190
|
if(typeof type == 'string') {
|
|
191
191
|
type = type.split(',');
|
|
192
192
|
}
|
|
193
|
-
if(!~type.indexOf(this._file.mimetype.toLowerCase())) {
|
|
193
|
+
if(!~type.indexOf((this._file && this._file.mimetype || '').toLowerCase())) {
|
|
194
194
|
return false;
|
|
195
195
|
}
|
|
196
196
|
return true;
|
|
@@ -202,8 +202,8 @@ class Upload extends Context
|
|
|
202
202
|
* @returns {boolean}
|
|
203
203
|
*/
|
|
204
204
|
checkImg() {
|
|
205
|
-
const fileExt = pt.extname(this._file.originalFilename).slice(1).toLowerCase();
|
|
206
|
-
const fileType = this._file.mimetype.toLowerCase().split('/');
|
|
205
|
+
const fileExt = pt.extname(this._file && this._file.originalFilename || '').slice(1).toLowerCase();
|
|
206
|
+
const fileType = (this._file && this._file.mimetype || '').toLowerCase().split('/');
|
|
207
207
|
const exts = ['gif', 'png', 'jpeg', 'webp', 'ico', 'bmp', 'svg', 'jpg', 'tiff'];
|
|
208
208
|
const mimes = ['gif', 'png', 'jpeg', 'webp', 'x-icon', 'bmp', 'svg+xml', 'tiff'];
|
|
209
209
|
if(~exts.indexOf(fileExt) && (fileType[0] != 'image' || !~mimes.indexOf(fileType[1]))) {
|
package/lib/url.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
const {app: cfg_app, routes: cfg_routes} = require('./config');
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {import('../types').RouteConfigItem} RouteConfigItem
|
|
4
|
+
* @type {Object.<string, RouteConfigItem>}
|
|
5
|
+
*/
|
|
2
6
|
const routes = (cfg_routes || []).reverse().reduce((routes, route) => {
|
|
3
|
-
if(route.name){
|
|
7
|
+
if(route.name) {
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
if(routes[route.name]) throw new Error(`RouteError: 重复的路由名字:${route.name}`);
|
|
10
|
+
// @ts-ignore
|
|
4
11
|
routes[route.name] = route;
|
|
5
12
|
}
|
|
6
13
|
return routes;
|
|
@@ -30,7 +37,7 @@ class Url extends Context
|
|
|
30
37
|
if(typeof vars != 'object') {
|
|
31
38
|
[vars, ext, domain] = [{}, vars, ext];
|
|
32
39
|
}
|
|
33
|
-
if(ext
|
|
40
|
+
if(typeof ext == 'boolean' || ext.slice(0, 4) == 'http') {
|
|
34
41
|
[ext, domain] = ['', ext];
|
|
35
42
|
}
|
|
36
43
|
if(domain === true) {
|
|
@@ -83,6 +90,7 @@ class Url extends Context
|
|
|
83
90
|
url = routes[url].url;
|
|
84
91
|
|
|
85
92
|
if(~url.indexOf(':')) {
|
|
93
|
+
// @ts-ignore
|
|
86
94
|
url = url.replace(/\:([^/]+\(.*\)|[^/]+)/g, (match, key) => {
|
|
87
95
|
key = key.split(/[(.]/);
|
|
88
96
|
const k = key[0];
|
package/lib/utils/error.js
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
const fs = require('./fs.js');
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {import('../../types').ExceptionData} ExceptionData
|
|
5
|
+
*/
|
|
6
|
+
|
|
3
7
|
/**
|
|
4
8
|
* @function - 解析Error数据
|
|
5
9
|
* @param {Error} err - 错误对象
|
|
6
|
-
* @returns {
|
|
10
|
+
* @returns {ExceptionData} - 解析后数据
|
|
7
11
|
*/
|
|
8
12
|
function parseError(err) {
|
|
9
13
|
let msg = err.message;
|
|
10
|
-
let stack = err.stack.replace(/(\r\n)/g, "\n").split("\n");
|
|
14
|
+
let stack = (err.stack || '').replace(/(\r\n)/g, "\n").split("\n");
|
|
11
15
|
|
|
12
16
|
if(err.name == 'TemplateError') {
|
|
13
17
|
msg = 'TemplateError: ' + stack.pop();
|
|
14
|
-
stack = [stack.shift().replace('TemplateError:', "at")];
|
|
18
|
+
stack = [(stack.shift() || '').replace('TemplateError:', "at")];
|
|
15
19
|
} else {
|
|
16
20
|
stack = stack.filter(text => {return /^ at /.test(text);});
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
const file_info = stack[0].split(' ').slice(-1)[0].replace(/(\()|(\))/g, '').split(':');
|
|
20
|
-
const column = parseInt(file_info.pop());
|
|
21
|
-
const row = parseInt(file_info.pop());
|
|
24
|
+
const column = parseInt(file_info.pop() || '0');
|
|
25
|
+
const row = parseInt(file_info.pop() || '0');
|
|
22
26
|
const file_path = file_info.join(':');
|
|
23
27
|
let begin = row - 10;
|
|
24
28
|
let end = row + 10;
|
package/lib/utils/utils.js
CHANGED
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
module.exports = new Proxy({}, {
|
|
8
8
|
get: (target, prop) => {
|
|
9
9
|
if(prop in target || typeof prop == 'symbol' || prop == 'inspect') {
|
|
10
|
+
// @ts-ignore
|
|
10
11
|
return target[prop];
|
|
11
12
|
}
|
|
12
13
|
if(prop == 'type') {
|
|
14
|
+
// @ts-ignore
|
|
13
15
|
return para => Object.prototype.toString.call(para).slice(8, -1);
|
|
14
16
|
}
|
|
15
17
|
return require('./' + prop);
|
package/lib/view.js
CHANGED
|
@@ -5,19 +5,36 @@ const {app: cfg_app, view: cfg_view} = require('./config');
|
|
|
5
5
|
const {toLine} = require('./utils/str');
|
|
6
6
|
const Context = require('./context');
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {import('../types').KoaCtx} KoaCtx
|
|
10
|
+
* @typedef {import('../types').ViewEngine} ViewEngine
|
|
11
|
+
* @typedef {import('../types').ViewFilter} ViewFilter
|
|
12
|
+
* @typedef {import('../types').UrlInstance} UrlInstance
|
|
13
|
+
*/
|
|
14
|
+
|
|
8
15
|
/**
|
|
9
16
|
* @extends Context
|
|
10
17
|
*/
|
|
11
18
|
class View extends Context
|
|
12
19
|
{
|
|
20
|
+
/**
|
|
21
|
+
* @type {Object.<string, any>}
|
|
22
|
+
*/
|
|
23
|
+
_data = {};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @type {ViewEngine}
|
|
27
|
+
*/
|
|
28
|
+
// @ts-ignore
|
|
29
|
+
_engine = null;
|
|
30
|
+
|
|
13
31
|
/**
|
|
14
32
|
* Initialize a new `View`
|
|
15
33
|
* @public
|
|
16
|
-
* @param {
|
|
34
|
+
* @param {KoaCtx} ctx
|
|
17
35
|
*/
|
|
18
36
|
constructor(ctx) {
|
|
19
37
|
super(ctx);
|
|
20
|
-
this._data = {};
|
|
21
38
|
this.setEngine(cfg_view.view_engine);
|
|
22
39
|
}
|
|
23
40
|
|
|
@@ -61,7 +78,8 @@ class View extends Context
|
|
|
61
78
|
* @returns {this}
|
|
62
79
|
*/
|
|
63
80
|
assign(name, value) {
|
|
64
|
-
|
|
81
|
+
|
|
82
|
+
if(typeof name == 'string') {console.log('assign', this, Object.keys(this));
|
|
65
83
|
this._data[name] = value;
|
|
66
84
|
} else {
|
|
67
85
|
this._data = name;
|
|
@@ -88,11 +106,11 @@ class View extends Context
|
|
|
88
106
|
* @param {string} [view_depr] - 模板文件分隔符
|
|
89
107
|
* @returns {string}
|
|
90
108
|
*/
|
|
91
|
-
parsePath(template, view_folder, view_depr) {
|
|
109
|
+
parsePath(template = '', view_folder, view_depr) {
|
|
92
110
|
template = template || this.ctx.ACTION
|
|
93
111
|
!view_folder && (view_folder = cfg_view.view_folder);
|
|
94
112
|
!view_depr && (view_depr = cfg_view.view_depr);
|
|
95
|
-
let view_file = template;
|
|
113
|
+
let view_file = template || '';
|
|
96
114
|
|
|
97
115
|
if(view_file.indexOf('/') !== 0) {
|
|
98
116
|
const temp = view_file.replace(/^\/|\/$/g, '').split('/').reverse().map(u => toLine(u));
|
|
@@ -110,7 +128,7 @@ class View extends Context
|
|
|
110
128
|
* @param {string} [template] - 默认当前请求方法名,支持智能解析
|
|
111
129
|
* @returns {string}
|
|
112
130
|
*/
|
|
113
|
-
parseTplName(template) {
|
|
131
|
+
parseTplName(template = '') {
|
|
114
132
|
let file_name = this.parsePath(template, this._folder, this._depr);
|
|
115
133
|
let is_file = isFileSync(file_name);
|
|
116
134
|
if(!is_file && (this._folder || this._depr)) {
|
|
@@ -133,8 +151,9 @@ class View extends Context
|
|
|
133
151
|
setEngine(engine) {
|
|
134
152
|
this._engine = typeof engine == 'string' ? require(engine) : engine;
|
|
135
153
|
this._engine.defaults.debug = cfg_app.app_debug;
|
|
154
|
+
// @ts-ignore
|
|
136
155
|
this.setFilter('url', (...args) => {
|
|
137
|
-
return this
|
|
156
|
+
return this._$url.build(...args);
|
|
138
157
|
});
|
|
139
158
|
this.setFilter(cfg_view.view_filter);
|
|
140
159
|
return this;
|
|
@@ -143,7 +162,7 @@ class View extends Context
|
|
|
143
162
|
/**
|
|
144
163
|
* 设置模版函数
|
|
145
164
|
* @public
|
|
146
|
-
* @param {(string|
|
|
165
|
+
* @param {(string|ViewFilter)} fun_obj - 函数名字或者包含多个函数的对象
|
|
147
166
|
* @param {*} [fun] - 函数
|
|
148
167
|
* @returns {this}
|
|
149
168
|
*/
|
|
@@ -179,6 +198,33 @@ class View extends Context
|
|
|
179
198
|
this._depr = view_depr;
|
|
180
199
|
return this;
|
|
181
200
|
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* @type {UrlInstance} Url实例
|
|
204
|
+
* @private
|
|
205
|
+
*/
|
|
206
|
+
// @ts-ignore
|
|
207
|
+
__url = null;
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @type {UrlInstance} Url实例
|
|
211
|
+
*/
|
|
212
|
+
get _$url() {
|
|
213
|
+
if(this.__url === null) {
|
|
214
|
+
if(this.$url && this.$url.__ISCLASS__) {
|
|
215
|
+
this.__url = this.$url;
|
|
216
|
+
} else if(this.$ && this.$.url) {
|
|
217
|
+
this.__url = this.$.url;
|
|
218
|
+
} else {
|
|
219
|
+
this.__url = new (require('./url'))(this.ctx);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return this.__url;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
set _$url(url) {
|
|
226
|
+
this.__url = url;
|
|
227
|
+
}
|
|
182
228
|
}
|
|
183
229
|
|
|
184
230
|
module.exports = View;
|
package/logo.png
ADDED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jj.js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "A super simple lightweight NodeJS MVC framework(一个超级简单轻量的NodeJS MVC框架)",
|
|
5
5
|
"main": "jj.js",
|
|
6
|
-
"scripts": {
|
|
6
|
+
"scripts": {
|
|
7
|
+
"demo": "node ./tests/_demo/server.js",
|
|
8
|
+
"test": "node --test"
|
|
9
|
+
},
|
|
7
10
|
"repository": {
|
|
8
11
|
"type": "git",
|
|
9
12
|
"url": "git+https://github.com/yafoo/jj.js.git"
|
|
@@ -27,10 +30,14 @@
|
|
|
27
30
|
"koa": "^2.16.0",
|
|
28
31
|
"koa-body": "^6.0.1",
|
|
29
32
|
"koa-static": "^5.0.0",
|
|
33
|
+
"mongodb": "^6.0.0",
|
|
30
34
|
"mysql": "^2.18.1",
|
|
31
|
-
"
|
|
35
|
+
"sqlite3": "^5.1.7"
|
|
32
36
|
},
|
|
33
37
|
"engines": {
|
|
34
38
|
"node": ">= 18"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"supertest": "^7.2.2"
|
|
35
42
|
}
|
|
36
43
|
}
|