chanjs 1.0.46 → 1.1.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 +9 -7
- package/core/config.js +25 -17
- package/core/helper.js +4 -4
- package/core/lib/cache.js +1 -1
- package/core/lib/controller.js +1 -1
- package/core/lib/extend.js +5 -3
- package/core/lib/index.js +11 -10
- package/core/lib/service.js +1 -2
- package/core/lib/view.js +7 -4
- package/index.js +74 -78
- package/package.json +10 -8
package/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
# Chanjs
|
3
3
|
|
4
|
-
Chanjs 是一个基于
|
4
|
+
Chanjs 是一个基于 Express5+ 构建的轻量级 MVC 框架,完全使用 JavaScript 开发。它体现了函数式编程的概念,提供了卓越的性能、清晰的代码和易于遵循的过程,确保了高可维护性。
|
5
5
|
|
6
6
|
## 特点
|
7
7
|
|
@@ -86,14 +86,16 @@ Chanjs 是一个基于 Express 构建的轻量级 MVC 框架,完全使用 Java
|
|
86
86
|
### 运行
|
87
87
|
|
88
88
|
```javascript
|
89
|
-
|
89
|
+
import Chanjs from "chanjs";
|
90
90
|
const chan = new Chanjs();
|
91
|
-
//
|
91
|
+
// 前置钩子
|
92
92
|
chan.beforeStart(fn);
|
93
|
-
//
|
94
|
-
chan.start();
|
95
|
-
|
96
|
-
|
93
|
+
// 开始加载
|
94
|
+
await chan.start();
|
95
|
+
chan.run((port) => {
|
96
|
+
console.log(`ChanCMS is running on ${port}`);
|
97
|
+
});
|
98
|
+
|
97
99
|
```
|
98
100
|
|
99
101
|
该框架专为寻求简单与功能之间平衡的开发者设计,为构建 Web 应用程序提供了一个强大的基础。
|
package/core/config.js
CHANGED
@@ -1,33 +1,40 @@
|
|
1
|
-
|
1
|
+
import path from 'path';
|
2
|
+
import { createRequire } from 'module';
|
3
|
+
import { pathToFileURL } from 'url';
|
2
4
|
|
3
5
|
/**
|
4
|
-
* @description
|
6
|
+
* @description 根目录(ESM 兼容写法)
|
5
7
|
*/
|
8
|
+
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
9
|
+
console.log(__dirname)
|
6
10
|
const ROOT_PATH = process.cwd();
|
7
|
-
|
8
|
-
|
11
|
+
|
12
|
+
// 异步读取 package.json(ESM 标准方式)
|
13
|
+
const packageUrl = pathToFileURL(path.join(ROOT_PATH, 'package.json')).href;
|
14
|
+
const { default: pkg } = await import(packageUrl,{ assert: { type: 'json' } });
|
15
|
+
const { version = '1.0.0', author = '明空' } = pkg;
|
9
16
|
|
10
17
|
/**
|
11
18
|
* @description 程序目录
|
12
19
|
*/
|
13
20
|
const APP_PATH = path.join(ROOT_PATH, 'app');
|
14
21
|
|
15
|
-
|
16
|
-
JSON_LIMIT:"100kb",
|
17
|
-
logger
|
22
|
+
const baseConfig = {
|
23
|
+
JSON_LIMIT: "100kb",
|
24
|
+
logger: {
|
18
25
|
level: 'dev',
|
19
26
|
},
|
20
27
|
version,
|
21
28
|
author,
|
22
|
-
env:'dev',
|
23
|
-
template:'default',
|
24
|
-
views:[],
|
25
|
-
static:[{
|
29
|
+
env: 'dev',
|
30
|
+
template: 'default',
|
31
|
+
views: [],
|
32
|
+
static: [{
|
26
33
|
prefix: "/public/",
|
27
34
|
dir: ["app/public"],
|
28
35
|
maxAge: 0,
|
29
36
|
}],
|
30
|
-
database:{
|
37
|
+
database: {
|
31
38
|
client: "mysql2",
|
32
39
|
host: "localhost",
|
33
40
|
port: "3306",
|
@@ -36,10 +43,11 @@ let config = {
|
|
36
43
|
database: "chancms",
|
37
44
|
charset: "utf8mb4",
|
38
45
|
}
|
39
|
-
}
|
46
|
+
};
|
40
47
|
|
41
|
-
|
48
|
+
// 导出配置对象
|
49
|
+
export default {
|
42
50
|
ROOT_PATH,
|
43
|
-
APP_PATH,
|
44
|
-
...
|
45
|
-
}
|
51
|
+
APP_PATH,
|
52
|
+
...baseConfig
|
53
|
+
};
|
package/core/helper.js
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
|
1
|
+
import knex from 'knex';
|
2
2
|
|
3
3
|
/**
|
4
4
|
* @description 实例化一个类,并将该类的所有方法绑定到一个新的对象上。
|
5
5
|
* @param {Function} className - 需要实例化的类。
|
6
6
|
*@returns {Object} 包含绑定方法的对象。
|
7
7
|
*/
|
8
|
-
|
8
|
+
export const bindClass = function(className) {
|
9
9
|
let obj = {};
|
10
10
|
const cls = new className();
|
11
11
|
Object.getOwnPropertyNames(cls.constructor.prototype).forEach(
|
@@ -22,7 +22,7 @@ exports.bindClass = function(className) {
|
|
22
22
|
}
|
23
23
|
|
24
24
|
|
25
|
-
|
25
|
+
export const createKnex = function(opt) {
|
26
26
|
let config = {
|
27
27
|
host:"localhost",
|
28
28
|
port:"3306",
|
@@ -63,7 +63,7 @@ exports.createKnex = function(opt) {
|
|
63
63
|
* @returns Array
|
64
64
|
* @description 将web模块放到最后加载
|
65
65
|
*/
|
66
|
-
|
66
|
+
export const loadWebToEnd = function(module=[]){
|
67
67
|
const index = module.indexOf('web');
|
68
68
|
if (index !== -1) {
|
69
69
|
const web = module.splice(index, 1);
|
package/core/lib/cache.js
CHANGED
package/core/lib/controller.js
CHANGED
package/core/lib/extend.js
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
import express from "express";
|
2
|
+
export default (app)=>{
|
3
3
|
app.plus = {
|
4
4
|
setStatic:function ({prefix,dir,maxAge}) {
|
5
5
|
app.use(prefix, express.static(dir, { maxAge: maxAge || 0 }));
|
6
6
|
}
|
7
7
|
}
|
8
|
-
}
|
8
|
+
}
|
9
|
+
|
10
|
+
|
package/core/lib/index.js
CHANGED
@@ -1,11 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
import express from "express";
|
2
|
+
import cookieParser from "cookie-parser";
|
3
|
+
import favicon from "serve-favicon";
|
4
|
+
|
5
|
+
import morgan from "morgan";
|
6
|
+
import path from "path";
|
7
|
+
import view from "./view.js";
|
8
|
+
export default async function (app, config) {
|
9
|
+
const { logger, APP_PATH, cookieKey, statics, JSON_LIMIT, appName, version } =
|
9
10
|
config;
|
10
11
|
|
11
12
|
app.use(morgan(logger.level));
|
@@ -14,8 +15,8 @@ module.exports = async function (app, config) {
|
|
14
15
|
app.use(express.json({ limit: JSON_LIMIT }));
|
15
16
|
app.use(express.urlencoded({ extended: false }));
|
16
17
|
view(app, config);
|
17
|
-
if (
|
18
|
-
|
18
|
+
if (statics.length > 0) {
|
19
|
+
statics.forEach((item) => {
|
19
20
|
const { prefix, dir, maxAge } = item;
|
20
21
|
app.use(prefix, express.static(dir, { maxAge: maxAge || 0 }));
|
21
22
|
});
|
package/core/lib/service.js
CHANGED
package/core/lib/view.js
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
import path from "path";
|
2
|
+
import dayjs from 'dayjs';
|
3
|
+
import { createRequire } from 'module';
|
4
|
+
|
5
|
+
const require = createRequire(import.meta.url);
|
4
6
|
|
7
|
+
const template = require("art-template");
|
5
8
|
// 注册 dateFormat 函数
|
6
9
|
template.defaults.imports.dateFormat = function (date, format) {
|
7
10
|
if (!date) {
|
@@ -20,7 +23,7 @@ template.defaults.imports.dateFormat = function (date, format) {
|
|
20
23
|
return date.format(format);
|
21
24
|
};
|
22
25
|
|
23
|
-
|
26
|
+
export default (app, config) => {
|
24
27
|
const { APP_PATH, views, env } = config;
|
25
28
|
//合并插件中的view
|
26
29
|
const all = [...views, 'app/modules/web/view'];
|
package/index.js
CHANGED
@@ -1,14 +1,17 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
import express from "express";
|
2
|
+
import path from "path";
|
3
|
+
import fs from "fs";
|
4
|
+
import cors from "cors";
|
5
|
+
import { pathToFileURL } from 'url'; // 新增顶部导入
|
6
|
+
import config from "./core/config.js";
|
7
|
+
import {bindClass,createKnex,loadWebToEnd} from "./core/helper.js";
|
8
|
+
|
9
|
+
import Controller from "./core/lib/controller.js";
|
10
|
+
|
11
|
+
import Service from "./core/lib/service.js";
|
12
|
+
import cache from './core/lib/cache.js';
|
13
|
+
import core from "./core/lib/index.js";
|
14
|
+
import extend from "./core/lib/extend.js";
|
12
15
|
/**
|
13
16
|
* @description 基于express封装的mvc框架,遵循约定优于配置原则
|
14
17
|
*/
|
@@ -21,45 +24,38 @@ class Chan {
|
|
21
24
|
static Controller = Controller;
|
22
25
|
|
23
26
|
constructor() {
|
24
|
-
this.
|
27
|
+
this.app = express();
|
28
|
+
this.router = express.Router();
|
25
29
|
}
|
26
30
|
|
27
|
-
init() {
|
28
|
-
const startTime = performance.now();
|
29
|
-
this.app = express();
|
31
|
+
async init() {
|
30
32
|
extend(this.app);
|
31
|
-
this.
|
32
|
-
this.
|
33
|
-
this.loadExtends();
|
33
|
+
await this.loadConfig();
|
34
|
+
await this.loadExtends();
|
34
35
|
this.loadCore();
|
35
36
|
this.loadKnex();
|
36
37
|
this.loadCors();
|
37
|
-
// 记录结束时间
|
38
|
-
const endTime = performance.now();
|
39
|
-
console.log(`Chanjs init: ${endTime - startTime} ms`);
|
40
38
|
}
|
41
39
|
|
42
40
|
|
43
|
-
|
44
|
-
loadConfig() {
|
41
|
+
async loadConfig() {
|
45
42
|
const configPath = path.join(Chan.config.APP_PATH, "config/index.js");
|
46
43
|
if (fs.existsSync(configPath)) {
|
47
|
-
const
|
48
|
-
|
44
|
+
const configUrl = pathToFileURL(configPath).href; // 新增转换
|
45
|
+
const configModule = await import(configUrl); // 使用 URL 导入
|
46
|
+
Chan.config = { ...Chan.config, ...configModule.default }; // 注意 default
|
49
47
|
}
|
50
48
|
}
|
51
49
|
|
52
|
-
loadExtends() {
|
50
|
+
async loadExtends() {
|
53
51
|
const extendPath = path.join(Chan.config.APP_PATH, "extend");
|
54
52
|
if (fs.existsSync(extendPath)) {
|
55
|
-
|
56
|
-
|
57
|
-
.
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
const fileName = file.replace(".js", "");
|
62
|
-
Chan.helper[fileName] = helper;
|
53
|
+
const files = fs.readdirSync(extendPath).filter(file => file.endsWith(".js"));
|
54
|
+
for (const file of files) {
|
55
|
+
const filePath = path.join(extendPath, file);
|
56
|
+
const fileUrl = pathToFileURL(filePath).href; // 转换路径
|
57
|
+
const helperModule = await import(fileUrl); // 使用 URL 导入
|
58
|
+
Chan.helper[file.replace(".js", "")] = helperModule?.default || helperModule; // 注意 default
|
63
59
|
}
|
64
60
|
}
|
65
61
|
}
|
@@ -90,14 +86,11 @@ class Chan {
|
|
90
86
|
cb && cb();
|
91
87
|
}
|
92
88
|
//启动
|
93
|
-
start(cb) {
|
94
|
-
|
95
|
-
this.
|
96
|
-
this.
|
97
|
-
this.loadCommonRouter();
|
98
|
-
// 记录结束时间
|
99
|
-
const endTime = performance.now();
|
100
|
-
console.log(`Chanjs load modules: ${endTime - startTime} ms`);
|
89
|
+
async start(cb) {
|
90
|
+
await this.init();
|
91
|
+
await this.loadPlugins();
|
92
|
+
await this.loadModules();
|
93
|
+
await this.loadCommonRouter();
|
101
94
|
cb && cb();
|
102
95
|
}
|
103
96
|
|
@@ -107,63 +100,69 @@ class Chan {
|
|
107
100
|
}
|
108
101
|
|
109
102
|
// 加载插件
|
110
|
-
loadPlugins() {
|
111
|
-
|
103
|
+
async loadPlugins() {
|
104
|
+
await this.loadModules("plugins");
|
112
105
|
}
|
113
106
|
|
114
107
|
/**
|
115
108
|
* @description 模块加载入口(路由&控制器& 服务)
|
116
109
|
*/
|
117
|
-
loadModules(modules = "modules") {
|
110
|
+
async loadModules(modules = "modules") {
|
118
111
|
const configPath = path.join(Chan.config.APP_PATH, modules);
|
112
|
+
console.log('configPath',configPath);
|
119
113
|
if (fs.existsSync(configPath)) {
|
120
|
-
//模块名称
|
121
114
|
const dirs = loadWebToEnd(Chan.config[modules]);
|
115
|
+
console.log('dirs',dirs);
|
122
116
|
Chan[modules] = {};
|
117
|
+
|
123
118
|
// 先加载所有服务
|
124
|
-
|
119
|
+
for (const item of dirs) {
|
125
120
|
Chan[modules][item] = {
|
126
121
|
service: {},
|
127
122
|
controller: {},
|
128
123
|
};
|
129
|
-
this.loadServices(modules, item);
|
130
|
-
}
|
131
|
-
|
124
|
+
await this.loadServices(modules, item); // 确保每个模块的服务加载完成
|
125
|
+
}
|
126
|
+
|
132
127
|
// 加载控制器和路由
|
133
|
-
|
134
|
-
this.loadModule(modules, item);
|
135
|
-
}
|
128
|
+
for (const item of dirs) {
|
129
|
+
await this.loadModule(modules, item); // 确保每个模块的加载完成
|
130
|
+
}
|
136
131
|
}
|
137
132
|
}
|
133
|
+
|
138
134
|
|
139
135
|
/**
|
140
136
|
* @description 加载模块,包括 controller service router
|
141
137
|
* @param {String} moduleName 模块名称
|
142
138
|
*/
|
143
|
-
loadModule(modules, moduleName) {
|
144
|
-
this.loadControllers(modules, moduleName);
|
145
|
-
this.loadRoutes(modules, moduleName);
|
139
|
+
async loadModule(modules, moduleName) {
|
140
|
+
await this.loadControllers(modules, moduleName); // 确保控制器加载完成
|
141
|
+
await this.loadRoutes(modules, moduleName); // 然后加载路由
|
146
142
|
}
|
147
143
|
|
148
|
-
loadFiles(modules, moduleName, type) {
|
144
|
+
async loadFiles(modules, moduleName, type) {
|
149
145
|
const dir = path.join(Chan.config.APP_PATH, modules, moduleName, type);
|
150
146
|
if (fs.existsSync(dir)) {
|
151
147
|
const files = fs.readdirSync(dir).filter(file => file.endsWith(".js"));
|
152
|
-
|
153
|
-
const
|
148
|
+
for (const file of files) { // 使用 for...of 确保异步操作顺序
|
149
|
+
const filePath = path.join(dir, file);
|
150
|
+
const fileUrl = pathToFileURL(filePath).href;
|
151
|
+
const module = await import(fileUrl);
|
154
152
|
const name = file.replace(".js", "");
|
155
|
-
Chan[modules][moduleName][type][name] = { ...bindClass(module) };
|
156
|
-
}
|
153
|
+
Chan[modules][moduleName][type][name] = { ...bindClass(module.default) };
|
154
|
+
}
|
157
155
|
}
|
158
156
|
}
|
157
|
+
|
159
158
|
|
160
159
|
/**
|
161
160
|
* @description 扫描模块下所有service
|
162
161
|
* @param {*} moduleDir 模块路径
|
163
162
|
* @param {*} moduleName 模块名称
|
164
163
|
*/
|
165
|
-
loadServices(modules, moduleName) {
|
166
|
-
|
164
|
+
async loadServices(modules, moduleName) {
|
165
|
+
await this.loadFiles(modules, moduleName, "service");
|
167
166
|
}
|
168
167
|
|
169
168
|
/**
|
@@ -171,8 +170,8 @@ class Chan {
|
|
171
170
|
* @param {*} moduleDir 模块路径
|
172
171
|
* @param {*} moduleName 模块名称
|
173
172
|
*/
|
174
|
-
loadControllers(modules, moduleName) {
|
175
|
-
this.loadFiles(modules, moduleName, "controller");
|
173
|
+
async loadControllers(modules, moduleName) {
|
174
|
+
await this.loadFiles(modules, moduleName, "controller");
|
176
175
|
}
|
177
176
|
|
178
177
|
/**
|
@@ -180,26 +179,23 @@ class Chan {
|
|
180
179
|
* @param {*} moduleDir 模块路径
|
181
180
|
* @param {*} moduleName 模块名称
|
182
181
|
*/
|
183
|
-
loadRoutes(modules, moduleName) {
|
184
|
-
const routersDir = path.join(
|
185
|
-
Chan.config.APP_PATH,
|
186
|
-
modules,
|
187
|
-
moduleName,
|
188
|
-
"router.js"
|
189
|
-
);
|
182
|
+
async loadRoutes(modules, moduleName) {
|
183
|
+
const routersDir = path.join(Chan.config.APP_PATH, modules, moduleName, "router.js");
|
190
184
|
if (fs.existsSync(routersDir)) {
|
191
|
-
const
|
192
|
-
routes
|
185
|
+
const routeUrl = pathToFileURL(routersDir).href; // 转换路径
|
186
|
+
const routes = await import(routeUrl); // 使用 URL 导入
|
187
|
+
routes.default({ router: this.router, modules: Chan[modules], app: this.app });
|
193
188
|
}
|
194
189
|
}
|
195
190
|
|
196
191
|
//通用路由,加载错误处理和500路由和爬虫处理
|
197
|
-
loadCommonRouter() {
|
192
|
+
async loadCommonRouter() {
|
198
193
|
try {
|
199
194
|
const baseRouterPath = path.join(Chan.config.APP_PATH, "router.js");
|
200
195
|
if (fs.existsSync(baseRouterPath)) {
|
201
|
-
const
|
202
|
-
_router
|
196
|
+
const routerUrl = pathToFileURL(baseRouterPath).href; // 转换路径
|
197
|
+
const _router = await import(routerUrl); // 使用 URL 导入
|
198
|
+
_router.default(this.app, this.router, Chan.config); // 注意 default
|
203
199
|
}
|
204
200
|
} catch (error) {
|
205
201
|
console.log(error);
|
@@ -214,4 +210,4 @@ class Chan {
|
|
214
210
|
}
|
215
211
|
}
|
216
212
|
global.Chan = Chan;
|
217
|
-
|
213
|
+
export default Chan;
|
package/package.json
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
{
|
2
|
+
"type": "module",
|
2
3
|
"name": "chanjs",
|
3
|
-
"version": "1.
|
4
|
-
"description": "chanjs基于
|
4
|
+
"version": "1.1.1",
|
5
|
+
"description": "chanjs基于express5 纯js研发的轻量级mvc框架。",
|
5
6
|
"main": "index.js",
|
6
7
|
"module": "index.js",
|
7
8
|
"keywords": [
|
@@ -12,18 +13,19 @@
|
|
12
13
|
"chancms",
|
13
14
|
"nodejs"
|
14
15
|
],
|
16
|
+
"engines": { "node": ">=20.0.0" },
|
15
17
|
"author": "明空",
|
16
18
|
"license": "ISC",
|
17
19
|
"dependencies": {
|
18
|
-
"art-template": "4.13.
|
19
|
-
"body-parser": "
|
20
|
-
"cookie-parser": "1.4.
|
20
|
+
"art-template": "4.13.4",
|
21
|
+
"body-parser": "2.2.0",
|
22
|
+
"cookie-parser": "1.4.7",
|
21
23
|
"cors": "2.8.5",
|
22
|
-
"express": "
|
24
|
+
"express": "5.1.0",
|
23
25
|
"express-art-template": "1.0.1",
|
24
26
|
"knex": "3.1.0",
|
25
27
|
"morgan": "1.10.0",
|
26
|
-
"mysql2": "3.
|
27
|
-
"dayjs": "1.11.
|
28
|
+
"mysql2": "3.14.0",
|
29
|
+
"dayjs": "1.11.13"
|
28
30
|
}
|
29
31
|
}
|