chanjs 1.0.45 → 1.1.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/core/config.js CHANGED
@@ -1,33 +1,40 @@
1
- const path = require('path');
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
- // 读取package.json文件
8
- const {version='1.0.0',author='明空'} = require(path.join(ROOT_PATH, 'package.json'));
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
- let config = {
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
- module.exports = {
48
+ // 导出配置对象
49
+ export default {
42
50
  ROOT_PATH,
43
- APP_PATH,
44
- ...config
45
- }
51
+ APP_PATH,
52
+ ...baseConfig
53
+ };
package/core/helper.js CHANGED
@@ -1,11 +1,11 @@
1
- const knex = require('knex');
1
+ import knex from 'knex';
2
2
 
3
3
  /**
4
4
  * @description 实例化一个类,并将该类的所有方法绑定到一个新的对象上。
5
5
  * @param {Function} className - 需要实例化的类。
6
6
  *@returns {Object} 包含绑定方法的对象。
7
7
  */
8
- exports.bindClass = function(className) {
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
- exports.createKnex = function(opt) {
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
- exports.loadWebToEnd = function(module=[]){
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
@@ -104,7 +104,7 @@ class Cache {
104
104
  }
105
105
  }
106
106
 
107
- module.exports = new Cache();
107
+ export default new Cache();
108
108
 
109
109
  // // 示例用法
110
110
  // const cache = new SimpleCache(100); // 设置缓存最大容量为 100
@@ -65,4 +65,4 @@ class Controller {
65
65
  }
66
66
  }
67
67
 
68
- module.exports = Controller;
68
+ export default Controller;
@@ -1,8 +1,10 @@
1
- const express = require("express");
2
- module.exports = (app)=>{
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
- const express = require("express");
2
- const cookieParser = require("cookie-parser");
3
- const favicon = require("serve-favicon");
4
- const morgan = require("morgan");
5
- const path = require("path");
6
- const view = require("./view.js");
7
- module.exports = async function (app, config) {
8
- const { logger, APP_PATH, cookieKey, static, JSON_LIMIT, appName, version } =
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 (static.length > 0) {
18
- static.forEach((item) => {
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
  });
@@ -105,6 +105,43 @@ async update({query, params} = {}) {
105
105
  }
106
106
 
107
107
 
108
+ /**
109
+ * @description 批量更新多条记录(基于事务保证原子性)
110
+ * @param {Array<{query: Object, params: Object}>} updates - 更新操作数组,每个元素包含查询条件和更新内容
111
+ * @returns {Promise<{ affectedRows: number[] }>} 返回每个操作影响的行数数组
112
+ */
113
+ async updateMany(updates = []) {
114
+ // 参数合法性校验
115
+ if (!Array.isArray(updates)) {
116
+ throw new Error('参数必须为数组格式');
117
+ }
118
+
119
+ // 获取事务对象
120
+ const trx = await this.knex.transaction();
121
+
122
+ try {
123
+ const affectedRows = [];
124
+ // 循环处理每个更新操作
125
+ for (const { query, params } of updates) {
126
+ // 执行单个更新操作,使用事务对象替换原有knex实例
127
+ const result = await trx(this.model)
128
+ .where(query)
129
+ .update(params);
130
+ affectedRows.push(result);
131
+ }
132
+ // 提交事务
133
+ await trx.commit();
134
+ // 返回影响行数数组(与入参顺序一致)
135
+ return { affectedRows };
136
+ } catch (err) {
137
+ // 回滚事务
138
+ await trx.rollback();
139
+ // 错误向上抛出,由调用者处理
140
+ throw err;
141
+ }
142
+ }
143
+
144
+
108
145
 
109
146
 
110
147
  /**
@@ -159,5 +196,4 @@ async update({query, params} = {}) {
159
196
  }
160
197
 
161
198
  }
162
-
163
- module.exports = BaseService;
199
+ export default BaseService;
package/core/lib/view.js CHANGED
@@ -1,7 +1,10 @@
1
- const path = require("path");
2
- const template = require("art-template");
3
- const dayjs = require("dayjs");
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
- module.exports = (app, config) => {
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
- const express = require("express");
2
- const path = require("path");
3
- const fs = require("fs");
4
- const cors = require("cors");
5
- const config = require("./core/config.js");
6
- const {bindClass,createKnex,loadWebToEnd} = require("./core/helper.js");
7
- const Controller = require("./core/lib/controller.js");
8
- const Service = require("./core/lib/service.js");
9
- const cache = require('./core/lib/cache.js');
10
- const core = require("./core/lib/index.js");
11
- const extend = require("./core/lib/extend.js");
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.init();
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.router = express.Router();
32
- this.loadConfig();
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 config = require(configPath);
48
- Chan.config = { ...Chan.config, ...config };
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
- let controllers = fs
56
- .readdirSync(extendPath)
57
- .filter((file) => file.endsWith(".js"));
58
- for (let i = 0, file; i < controllers.length; i++) {
59
- file = controllers[i];
60
- const helper = require(path.join(extendPath, file));
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,10 @@ class Chan {
90
86
  cb && cb();
91
87
  }
92
88
  //启动
93
- start(cb) {
94
- const startTime = performance.now();
95
- this.loadModules();
96
- this.loadPlugins();
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.loadPlugins();
91
+ await this.loadModules();
92
+ await this.loadCommonRouter();
101
93
  cb && cb();
102
94
  }
103
95
 
@@ -107,63 +99,69 @@ class Chan {
107
99
  }
108
100
 
109
101
  // 加载插件
110
- loadPlugins() {
111
- this.loadModules("plugins");
102
+ async loadPlugins() {
103
+ await this.loadModules("plugins");
112
104
  }
113
105
 
114
106
  /**
115
107
  * @description 模块加载入口(路由&控制器& 服务)
116
108
  */
117
- loadModules(modules = "modules") {
109
+ async loadModules(modules = "modules") {
118
110
  const configPath = path.join(Chan.config.APP_PATH, modules);
111
+ console.log('configPath',configPath);
119
112
  if (fs.existsSync(configPath)) {
120
- //模块名称
121
113
  const dirs = loadWebToEnd(Chan.config[modules]);
114
+ console.log('dirs',dirs);
122
115
  Chan[modules] = {};
116
+
123
117
  // 先加载所有服务
124
- dirs.forEach((item) => {
118
+ for (const item of dirs) {
125
119
  Chan[modules][item] = {
126
120
  service: {},
127
121
  controller: {},
128
122
  };
129
- this.loadServices(modules, item);
130
- });
131
-
123
+ await this.loadServices(modules, item); // 确保每个模块的服务加载完成
124
+ }
125
+
132
126
  // 加载控制器和路由
133
- dirs.forEach((item) => {
134
- this.loadModule(modules, item);
135
- });
127
+ for (const item of dirs) {
128
+ await this.loadModule(modules, item); // 确保每个模块的加载完成
129
+ }
136
130
  }
137
131
  }
132
+
138
133
 
139
134
  /**
140
135
  * @description 加载模块,包括 controller service router
141
136
  * @param {String} moduleName 模块名称
142
137
  */
143
- loadModule(modules, moduleName) {
144
- this.loadControllers(modules, moduleName);
145
- this.loadRoutes(modules, moduleName);
138
+ async loadModule(modules, moduleName) {
139
+ await this.loadControllers(modules, moduleName); // 确保控制器加载完成
140
+ await this.loadRoutes(modules, moduleName); // 然后加载路由
146
141
  }
147
142
 
148
- loadFiles(modules, moduleName, type) {
143
+ async loadFiles(modules, moduleName, type) {
149
144
  const dir = path.join(Chan.config.APP_PATH, modules, moduleName, type);
150
145
  if (fs.existsSync(dir)) {
151
146
  const files = fs.readdirSync(dir).filter(file => file.endsWith(".js"));
152
- files.forEach(file => {
153
- const module = require(path.join(dir, file));
147
+ for (const file of files) { // 使用 for...of 确保异步操作顺序
148
+ const filePath = path.join(dir, file);
149
+ const fileUrl = pathToFileURL(filePath).href;
150
+ const module = await import(fileUrl);
154
151
  const name = file.replace(".js", "");
155
- Chan[modules][moduleName][type][name] = { ...bindClass(module) };
156
- });
152
+ Chan[modules][moduleName][type][name] = { ...bindClass(module.default) };
153
+ }
157
154
  }
158
155
  }
156
+
159
157
 
160
158
  /**
161
159
  * @description 扫描模块下所有service
162
160
  * @param {*} moduleDir 模块路径
163
161
  * @param {*} moduleName 模块名称
164
162
  */
165
- loadServices(modules, moduleName) {
166
- this.loadFiles(modules, moduleName, "service");
163
+ async loadServices(modules, moduleName) {
164
+ await this.loadFiles(modules, moduleName, "service");
167
165
  }
168
166
 
169
167
  /**
@@ -171,8 +169,8 @@ class Chan {
171
169
  * @param {*} moduleDir 模块路径
172
170
  * @param {*} moduleName 模块名称
173
171
  */
174
- loadControllers(modules, moduleName) {
175
- this.loadFiles(modules, moduleName, "controller");
172
+ async loadControllers(modules, moduleName) {
173
+ await this.loadFiles(modules, moduleName, "controller");
176
174
  }
177
175
 
178
176
  /**
@@ -180,26 +178,23 @@ class Chan {
180
178
  * @param {*} moduleDir 模块路径
181
179
  * @param {*} moduleName 模块名称
182
180
  */
183
- loadRoutes(modules, moduleName) {
184
- const routersDir = path.join(
185
- Chan.config.APP_PATH,
186
- modules,
187
- moduleName,
188
- "router.js"
189
- );
181
+ async loadRoutes(modules, moduleName) {
182
+ const routersDir = path.join(Chan.config.APP_PATH, modules, moduleName, "router.js");
190
183
  if (fs.existsSync(routersDir)) {
191
- const routes = require(routersDir);
192
- routes({ router: this.router, modules: Chan[modules], app: this.app });
184
+ const routeUrl = pathToFileURL(routersDir).href; // 转换路径
185
+ const routes = await import(routeUrl); // 使用 URL 导入
186
+ routes.default({ router: this.router, modules: Chan[modules], app: this.app });
193
187
  }
194
188
  }
195
189
 
196
190
  //通用路由,加载错误处理和500路由和爬虫处理
197
- loadCommonRouter() {
191
+ async loadCommonRouter() {
198
192
  try {
199
193
  const baseRouterPath = path.join(Chan.config.APP_PATH, "router.js");
200
194
  if (fs.existsSync(baseRouterPath)) {
201
- const _router = require(baseRouterPath);
202
- _router(this.app, this.router, Chan.config);
195
+ const routerUrl = pathToFileURL(baseRouterPath).href; // 转换路径
196
+ const _router = await import(routerUrl); // 使用 URL 导入
197
+ _router.default(this.app, this.router, Chan.config); // 注意 default
203
198
  }
204
199
  } catch (error) {
205
200
  console.log(error);
@@ -214,4 +209,4 @@ class Chan {
214
209
  }
215
210
  }
216
211
  global.Chan = Chan;
217
- module.exports = Chan;
212
+ export default Chan;
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
+ "type": "module",
2
3
  "name": "chanjs",
3
- "version": "1.0.45",
4
+ "version": "1.1.0",
4
5
  "description": "chanjs基于express 纯js研发的轻量级mvc框架。",
5
6
  "main": "index.js",
6
7
  "module": "index.js",
@@ -12,6 +13,7 @@
12
13
  "chancms",
13
14
  "nodejs"
14
15
  ],
16
+ "engines": { "node": ">=20.0.0" },
15
17
  "author": "明空",
16
18
  "license": "ISC",
17
19
  "dependencies": {