chanjs 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- package/README.MD +52 -0
- package/chan.js +162 -0
- package/lib/base.js +8 -0
- package/lib/config/config.js +18 -0
- package/lib/controller/Base.js +9 -0
- package/lib/extend/controller.js +217 -0
- package/lib/extend/fsExtend.js +135 -0
- package/lib/extend/router.js +17 -0
- package/package.json +14 -0
package/README.MD
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# <center>Chan.js mvc框架</center>
|
2
|
+
|
3
|
+
Chan.js 基于express 纯js研发的轻量级mvc框架。基于函数式编程思想,性能优越,代码清晰,流程易读,可持续维护高。
|
4
|
+
|
5
|
+
## 特性
|
6
|
+
|
7
|
+
- 基于express
|
8
|
+
- 支持es6语法
|
9
|
+
- 模块化
|
10
|
+
- 支持多模块路由
|
11
|
+
- 支持多模块视图
|
12
|
+
- 支持多模块控制器
|
13
|
+
- 支持多模块服务
|
14
|
+
- 插件化
|
15
|
+
- 轻量级(核心代码300行内)
|
16
|
+
- 长期维护
|
17
|
+
|
18
|
+
## 规范 遵循约定优于配置
|
19
|
+
|
20
|
+
```code
|
21
|
+
|- app
|
22
|
+
|- config 配置
|
23
|
+
|- module 模块1
|
24
|
+
|- module1 模块1
|
25
|
+
|- controller 控制器
|
26
|
+
|- service 服务模型
|
27
|
+
|- view 视图模板
|
28
|
+
|- router.js 路由
|
29
|
+
|- module2 模块2
|
30
|
+
|- controller 控制器
|
31
|
+
|- service 服务模型
|
32
|
+
|- view 视图模板
|
33
|
+
|- router.js路由
|
34
|
+
|- extend 扩展
|
35
|
+
|- middleware 中间件
|
36
|
+
|- plugin 插件
|
37
|
+
|- public 静态文件
|
38
|
+
|- index.js
|
39
|
+
```
|
40
|
+
|
41
|
+
### 初始化流程
|
42
|
+
|
43
|
+
- 初始化
|
44
|
+
- 加载配置
|
45
|
+
- 加载模块
|
46
|
+
- 加载service
|
47
|
+
- 加载controller
|
48
|
+
- 加载router
|
49
|
+
- 加载extend
|
50
|
+
- 加载plugin
|
51
|
+
- beforeStart() 挂在从数据库获取的配置合并到配置文件中
|
52
|
+
- run() 启动服务
|
package/chan.js
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
const express = require("express");
|
2
|
+
const config = require("./lib/config/config.js");
|
3
|
+
const path = require("path");
|
4
|
+
const fs = require("fs");
|
5
|
+
|
6
|
+
/**
|
7
|
+
* @description chanjs
|
8
|
+
*/
|
9
|
+
class Chan {
|
10
|
+
constructor(options={}) {
|
11
|
+
|
12
|
+
//加载全局对象
|
13
|
+
this.config = Object.assign(config, options);
|
14
|
+
//配置
|
15
|
+
Chan.config = this.config;
|
16
|
+
//模块
|
17
|
+
Chan.modules = this.modules = {};
|
18
|
+
//帮助文档
|
19
|
+
Chan.helper = this.helper = {};
|
20
|
+
|
21
|
+
this.app = express();
|
22
|
+
this.router = express.Router();
|
23
|
+
|
24
|
+
this.loadConfig();
|
25
|
+
this.beforeStart();
|
26
|
+
this.init();
|
27
|
+
}
|
28
|
+
|
29
|
+
// 加载配置
|
30
|
+
loadConfig() {
|
31
|
+
const configPath = path.join(config.APP_PATH, "config/index.js");
|
32
|
+
if (fs.existsSync(configPath)) {
|
33
|
+
const config = require(configPath);
|
34
|
+
Object.assign(this.config, config);
|
35
|
+
}
|
36
|
+
|
37
|
+
console.log("config——>配置文件加载完成");
|
38
|
+
}
|
39
|
+
|
40
|
+
//启动加载
|
41
|
+
beforeStart(cb) {
|
42
|
+
// 初始化一些配置
|
43
|
+
console.log("beforeStart--->初始化一些配置");
|
44
|
+
cb && cb();
|
45
|
+
}
|
46
|
+
|
47
|
+
init() {
|
48
|
+
this.loadModules();
|
49
|
+
this.loadExtends();
|
50
|
+
this.loadPlusins();
|
51
|
+
|
52
|
+
}
|
53
|
+
|
54
|
+
loadModules() {
|
55
|
+
const configPath = path.join(config.APP_PATH, "module");
|
56
|
+
if (fs.existsSync(configPath)) {
|
57
|
+
const dirs = fs
|
58
|
+
.readdirSync(configPath, { withFileTypes: true })
|
59
|
+
.filter((dirent) => dirent.isDirectory())
|
60
|
+
.map((dirent) => dirent.name)
|
61
|
+
.forEach((item, index) => {
|
62
|
+
this.modules[item] = {
|
63
|
+
controller: {},
|
64
|
+
service: {},
|
65
|
+
};
|
66
|
+
this.loadModule(item);
|
67
|
+
});
|
68
|
+
//执行路由
|
69
|
+
this.app.use(this.router);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
// 加载插件
|
74
|
+
loadPlugins() {
|
75
|
+
const configPath = path.join(config.APP_PATH, "plugin");
|
76
|
+
if (fs.existsSync(configPath)) {
|
77
|
+
const dirs = fs
|
78
|
+
.readdirSync(configPath, { withFileTypes: true })
|
79
|
+
.filter((dirent) => dirent.isDirectory())
|
80
|
+
.map((dirent) => dirent.name);
|
81
|
+
this.plugins = dirs;
|
82
|
+
} else {
|
83
|
+
this.plugins = [];
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
loadModule(moduleName) {
|
88
|
+
const moduleDir = path.join(config.APP_PATH, "module", moduleName);
|
89
|
+
this.loadServices(moduleDir,moduleName);
|
90
|
+
this.loadControllers(moduleDir,moduleName);
|
91
|
+
this.loadRoutes(moduleDir,moduleName);
|
92
|
+
}
|
93
|
+
|
94
|
+
// 加载服务
|
95
|
+
loadServices(moduleDir,moduleName) {
|
96
|
+
const servicesDir = path.join(moduleDir, "service");
|
97
|
+
if (fs.existsSync(servicesDir)) {
|
98
|
+
let services = fs
|
99
|
+
.readdirSync(servicesDir)
|
100
|
+
.filter((file) => file.endsWith(".js"));
|
101
|
+
for (let i = 0, file = services[i]; i < services.length; i++) {
|
102
|
+
const Service = require(path.join(servicesDir, file));
|
103
|
+
const serviceName = file.replace(".js", "");
|
104
|
+
this.modules[moduleName].service[serviceName] = {};
|
105
|
+
this.modules[moduleName].service[serviceName] = Service;
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
// 加载控制器
|
111
|
+
loadControllers(moduleDir,moduleName) {
|
112
|
+
const controllersDir = path.join(moduleDir, "controller");
|
113
|
+
if (fs.existsSync(controllersDir)) {
|
114
|
+
let controllers = fs
|
115
|
+
.readdirSync(controllersDir)
|
116
|
+
.filter((file) => file.endsWith(".js"));
|
117
|
+
for (let i = 0, file = controllers[i]; i < controllers.length; i++) {
|
118
|
+
const controller = require(path.join(controllersDir, file));
|
119
|
+
const controllerName = file.replace(".js", "");
|
120
|
+
this.modules[moduleName].controller[controllerName] = {};
|
121
|
+
this.modules[moduleName].controller[controllerName] = controller;
|
122
|
+
}
|
123
|
+
}
|
124
|
+
}
|
125
|
+
|
126
|
+
// 加载路由
|
127
|
+
loadRoutes(moduleDir,moduleName) {
|
128
|
+
const routersDir = path.join(moduleDir, "router.js");
|
129
|
+
if (fs.existsSync(routersDir)) {
|
130
|
+
const routes = require(routersDir);
|
131
|
+
routes({router:this.router,controller:this.modules[moduleName].controller});
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
// 加载扩展
|
136
|
+
loadExtends() {
|
137
|
+
const extendPath = path.join(config.APP_PATH, "extend");
|
138
|
+
if (fs.existsSync(extendPath)) {
|
139
|
+
let controllers = fs
|
140
|
+
.readdirSync(extendPath)
|
141
|
+
.filter((file) => file.endsWith(".js"));
|
142
|
+
for (let i = 0, file = controllers[i]; i < controllers.length; i++) {
|
143
|
+
const helper = require(path.join(extendPath, file));
|
144
|
+
const fileName = file.replace(".js", "");
|
145
|
+
this.helper[fileName] = helper;
|
146
|
+
}
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
loadPlusins() {
|
151
|
+
// 加载插件
|
152
|
+
}
|
153
|
+
|
154
|
+
run(port) {
|
155
|
+
this.app.listen(port, () => {
|
156
|
+
console.log(`Server is running on port ${port}`);
|
157
|
+
});
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
|
162
|
+
module.exports = Chan;
|
package/lib/base.js
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
const path = require('path');
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @description 根目录
|
5
|
+
*/
|
6
|
+
const ROOT_PATH = process.cwd();
|
7
|
+
|
8
|
+
/**
|
9
|
+
* @description 程序目录
|
10
|
+
*/
|
11
|
+
const APP_PATH = path.join(ROOT_PATH, 'app');
|
12
|
+
|
13
|
+
module.exports = {
|
14
|
+
ROOT_PATH,
|
15
|
+
APP_PATH,
|
16
|
+
}
|
17
|
+
|
18
|
+
|
@@ -0,0 +1,217 @@
|
|
1
|
+
|
2
|
+
/**
|
3
|
+
* extend controller
|
4
|
+
*/
|
5
|
+
module.exports = {
|
6
|
+
/**
|
7
|
+
* body getter
|
8
|
+
*/
|
9
|
+
get body() {
|
10
|
+
|
11
|
+
},
|
12
|
+
/**
|
13
|
+
* body setter
|
14
|
+
*/
|
15
|
+
set body(value) {
|
16
|
+
|
17
|
+
},
|
18
|
+
/**
|
19
|
+
* get client ip
|
20
|
+
*/
|
21
|
+
get ip() {
|
22
|
+
|
23
|
+
},
|
24
|
+
/**
|
25
|
+
* get client ips
|
26
|
+
*/
|
27
|
+
get ips() {
|
28
|
+
|
29
|
+
},
|
30
|
+
/**
|
31
|
+
* get status code
|
32
|
+
*/
|
33
|
+
get status() {
|
34
|
+
|
35
|
+
},
|
36
|
+
/**
|
37
|
+
* set status code
|
38
|
+
*/
|
39
|
+
set status(status) {
|
40
|
+
|
41
|
+
},
|
42
|
+
/**
|
43
|
+
* get content type
|
44
|
+
*/
|
45
|
+
get type() {
|
46
|
+
|
47
|
+
},
|
48
|
+
/**
|
49
|
+
* set content type
|
50
|
+
*/
|
51
|
+
set type(contentType) {
|
52
|
+
|
53
|
+
},
|
54
|
+
/**
|
55
|
+
* get userAgent header
|
56
|
+
*/
|
57
|
+
get userAgent() {
|
58
|
+
|
59
|
+
},
|
60
|
+
/**
|
61
|
+
* get request method
|
62
|
+
*/
|
63
|
+
get method() {
|
64
|
+
|
65
|
+
},
|
66
|
+
/**
|
67
|
+
* is get method
|
68
|
+
*/
|
69
|
+
get isGet() {
|
70
|
+
|
71
|
+
},
|
72
|
+
/**
|
73
|
+
* is post method
|
74
|
+
*/
|
75
|
+
get isPost() {
|
76
|
+
|
77
|
+
},
|
78
|
+
/**
|
79
|
+
* is command line invoke
|
80
|
+
*/
|
81
|
+
get isCli() {
|
82
|
+
|
83
|
+
},
|
84
|
+
/**
|
85
|
+
* get or set config
|
86
|
+
* @param {String} name
|
87
|
+
* @param {Mix} value
|
88
|
+
* @param {String} m
|
89
|
+
*/
|
90
|
+
config(name, value, m ) {
|
91
|
+
|
92
|
+
},
|
93
|
+
/**
|
94
|
+
* is method
|
95
|
+
* @param {String} method
|
96
|
+
*/
|
97
|
+
isMethod(method) {
|
98
|
+
|
99
|
+
},
|
100
|
+
/**
|
101
|
+
* check if is ajax request
|
102
|
+
* @param {String} method
|
103
|
+
*/
|
104
|
+
isAjax(method) {
|
105
|
+
|
106
|
+
},
|
107
|
+
/**
|
108
|
+
* is jsonp request
|
109
|
+
* @param {String} callback
|
110
|
+
*/
|
111
|
+
isJsonp(callbackField) {
|
112
|
+
|
113
|
+
},
|
114
|
+
/**
|
115
|
+
* send jsonp data
|
116
|
+
*/
|
117
|
+
jsonp(data, callbackField) {
|
118
|
+
|
119
|
+
},
|
120
|
+
/**
|
121
|
+
* send json data
|
122
|
+
*/
|
123
|
+
json(data) {
|
124
|
+
|
125
|
+
},
|
126
|
+
/**
|
127
|
+
* send success data
|
128
|
+
*/
|
129
|
+
success(data, message) {
|
130
|
+
|
131
|
+
},
|
132
|
+
/**
|
133
|
+
* send fail data
|
134
|
+
*/
|
135
|
+
fail(errno, errmsg, data) {
|
136
|
+
|
137
|
+
},
|
138
|
+
/**
|
139
|
+
* set expires header
|
140
|
+
* @param {Number} time
|
141
|
+
*/
|
142
|
+
expires(time) {
|
143
|
+
|
144
|
+
},
|
145
|
+
/**
|
146
|
+
* get query data
|
147
|
+
* @param {String} name
|
148
|
+
* @param {Mixed} value
|
149
|
+
*/
|
150
|
+
get(name, value) {
|
151
|
+
|
152
|
+
},
|
153
|
+
/**
|
154
|
+
* get query data
|
155
|
+
* @param {String} name
|
156
|
+
* @param {Mixed} value
|
157
|
+
*/
|
158
|
+
query(name, value) {
|
159
|
+
|
160
|
+
},
|
161
|
+
/**
|
162
|
+
* get or set post data
|
163
|
+
* @param {String} name
|
164
|
+
* @param {Mixed} value
|
165
|
+
*/
|
166
|
+
post(name, value) {
|
167
|
+
|
168
|
+
},
|
169
|
+
/**
|
170
|
+
* get or set file data
|
171
|
+
* @param {String} name
|
172
|
+
* @param {Mixed} value
|
173
|
+
*/
|
174
|
+
file(name, value) {
|
175
|
+
|
176
|
+
},
|
177
|
+
/**
|
178
|
+
* get or set cookies
|
179
|
+
* @param {String} name
|
180
|
+
* @param {String} value
|
181
|
+
* @param {Object} options
|
182
|
+
*/
|
183
|
+
cookie(name, value, options) {
|
184
|
+
|
185
|
+
},
|
186
|
+
|
187
|
+
/**
|
188
|
+
* get referer header
|
189
|
+
*/
|
190
|
+
referrer(onlyHost) {
|
191
|
+
|
192
|
+
},
|
193
|
+
/**
|
194
|
+
* get referer header
|
195
|
+
*/
|
196
|
+
referer(onlyHost) {
|
197
|
+
|
198
|
+
},
|
199
|
+
/**
|
200
|
+
* Perform a 302 redirect to `url`.
|
201
|
+
* @param {String} url
|
202
|
+
* @param {String} alt
|
203
|
+
*/
|
204
|
+
redirect(url, alt) {
|
205
|
+
|
206
|
+
},
|
207
|
+
|
208
|
+
|
209
|
+
/**
|
210
|
+
* download
|
211
|
+
* @param {String} filepath
|
212
|
+
* @param {String} filename
|
213
|
+
*/
|
214
|
+
download(filepath, filename) {
|
215
|
+
|
216
|
+
}
|
217
|
+
};
|
@@ -0,0 +1,135 @@
|
|
1
|
+
const fs = require("fs");
|
2
|
+
const path = require("path");
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @description 是否存在
|
6
|
+
* @param {*} dir 目录
|
7
|
+
* @returns {Boolean} 返回true false
|
8
|
+
*/
|
9
|
+
function isExist(dir) {
|
10
|
+
let formatDir = path.normalize(dir);
|
11
|
+
try {
|
12
|
+
fs.accessSync(formatDir, fs.R_OK);
|
13
|
+
return true;
|
14
|
+
} catch (e) {
|
15
|
+
return false;
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
exports.isExist = isExist;
|
20
|
+
|
21
|
+
/**
|
22
|
+
* @description 是否是文件
|
23
|
+
* @param {*} filePath
|
24
|
+
* @returns {Boolean} true 是文件 false 不是文件
|
25
|
+
*/
|
26
|
+
function isFile(filePath) {
|
27
|
+
if (!isExist(filePath)) return false;
|
28
|
+
try {
|
29
|
+
const stat = fs.statSync(filePath);
|
30
|
+
return stat.isFile();
|
31
|
+
} catch (e) {
|
32
|
+
return false;
|
33
|
+
}
|
34
|
+
}
|
35
|
+
exports.isFile = isFile;
|
36
|
+
|
37
|
+
/**
|
38
|
+
* @description 是否是目录
|
39
|
+
* @param {*} filePath
|
40
|
+
* @returns {Boolean} 返回true false
|
41
|
+
*/
|
42
|
+
function isDirectory(filePath) {
|
43
|
+
if (!isExist(filePath)) return false;
|
44
|
+
try {
|
45
|
+
const stat = fs.statSync(filePath);
|
46
|
+
return stat.isDirectory();
|
47
|
+
} catch (e) {
|
48
|
+
return false;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
exports.isDirectory = isDirectory;
|
52
|
+
|
53
|
+
/**
|
54
|
+
* @description 修改指定路径 `p` 的权限模式为给定的 `mode`。这是一个同步版本的文件权限更改方法
|
55
|
+
* 如果成功修改权限,则返回 `true`,否则在发生错误时捕获异常并返回 `false`。
|
56
|
+
* @param {String} p - 要更改权限的文件或目录的绝对路径。
|
57
|
+
* @param {String|Number} mode - 指定的新权限模式,可以是八进制数字表示(如0o755)或字符串形式(如"755")
|
58
|
+
* @return {Boolean} - 根据操作是否成功,返回 `true` 表示成功,`false` 表示失败。
|
59
|
+
*/
|
60
|
+
function chmod(p, mode) {
|
61
|
+
try {
|
62
|
+
fs.chmodSync(p, mode);
|
63
|
+
return true;
|
64
|
+
} catch (e) {
|
65
|
+
return false;
|
66
|
+
}
|
67
|
+
}
|
68
|
+
exports.chmod = chmod;
|
69
|
+
|
70
|
+
/**
|
71
|
+
* @function mkdir
|
72
|
+
* @description 递归创建目录
|
73
|
+
* @param {string} dir - 要创建的目录路径。
|
74
|
+
* @param {string|number} - 默认为 0o777。可以是八进制数字表示或字符串形式。
|
75
|
+
* @returns {boolean} - 返回 `true` 表示成功,`false` 表示失败。
|
76
|
+
*/
|
77
|
+
function mkdir(dir, mode) {
|
78
|
+
if (isExist(dir)) {
|
79
|
+
if (mode) return chmod(dir, mode);
|
80
|
+
return true;
|
81
|
+
}
|
82
|
+
const pp = path.dirname(dir);
|
83
|
+
if (isExist(pp)) {
|
84
|
+
try {
|
85
|
+
fs.mkdirSync(dir, mode);
|
86
|
+
return true;
|
87
|
+
} catch (e) {
|
88
|
+
return false;
|
89
|
+
}
|
90
|
+
}
|
91
|
+
if (mkdir(pp, mode)) return mkdir(dir, mode);
|
92
|
+
return false;
|
93
|
+
}
|
94
|
+
exports.mkdir = mkdir;
|
95
|
+
|
96
|
+
function getdirFiles(dir, prefix = "") {
|
97
|
+
dir = path.normalize(dir);
|
98
|
+
if (!fs.existsSync(dir)) return [];
|
99
|
+
const files = fs.readdirSync(dir);
|
100
|
+
let result = [];
|
101
|
+
files.forEach((item) => {
|
102
|
+
const currentDir = path.join(dir, item);
|
103
|
+
const stat = fs.statSync(currentDir);
|
104
|
+
if (stat.isFile()) {
|
105
|
+
result.push(path.join(prefix, item));
|
106
|
+
} else if (stat.isDirectory()) {
|
107
|
+
const cFiles = getdirFiles(currentDir, path.join(prefix, item));
|
108
|
+
result = result.concat(cFiles);
|
109
|
+
}
|
110
|
+
});
|
111
|
+
return result;
|
112
|
+
}
|
113
|
+
|
114
|
+
exports.getdirFiles = getdirFiles;
|
115
|
+
|
116
|
+
/**
|
117
|
+
* remove dir aync
|
118
|
+
* @param {String} p [path]
|
119
|
+
* @param {Boolean} reserve []
|
120
|
+
* @return {Promise} []
|
121
|
+
*/
|
122
|
+
function rmdir(p, reserve) {
|
123
|
+
if (!isDirectory(p)) return Promise.resolve();
|
124
|
+
return fsReaddir(p).then((files) => {
|
125
|
+
const promises = files.map((item) => {
|
126
|
+
const filepath = path.join(p, item);
|
127
|
+
if (isDirectory(filepath)) return rmdir(filepath, false);
|
128
|
+
return fsUnlink(filepath);
|
129
|
+
});
|
130
|
+
return Promise.all(promises).then(() => {
|
131
|
+
if (!reserve) return fsRmdir(p);
|
132
|
+
});
|
133
|
+
});
|
134
|
+
}
|
135
|
+
exports.rmdir = rmdir;
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module.exports = {
|
4
|
+
/**
|
5
|
+
* @description 给路由加前缀
|
6
|
+
* @returns viod 0
|
7
|
+
*/
|
8
|
+
addRoutePrefix:()=>{
|
9
|
+
return function (req, res, next) {
|
10
|
+
req.baseUrl = `${prefix}${req.baseUrl || ''}`;
|
11
|
+
// 如果有 query 参数,则保留原有参数
|
12
|
+
req.url = `${req.baseUrl}${req.url.includes('?') ? req.url.split('?')[1] : ''}`;
|
13
|
+
next();
|
14
|
+
};
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
package/package.json
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"name": "chanjs",
|
3
|
+
"version": "0.0.1",
|
4
|
+
"description": "Chan.js 基于express 纯js研发的轻量级mvc框架。基于函数式编程思想,性能优越,代码清晰,流程易读,可持续维护高。",
|
5
|
+
"main": "chan.js",
|
6
|
+
"directories": {
|
7
|
+
"lib": "lib"
|
8
|
+
},
|
9
|
+
"scripts": {
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
11
|
+
},
|
12
|
+
"author": "明空",
|
13
|
+
"license": "ISC"
|
14
|
+
}
|