chanjs 0.0.1
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 +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
|
+
}
|