mm_config 1.1.5 → 2.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.
Files changed (4) hide show
  1. package/README.md +122 -30
  2. package/index.js +206 -20
  3. package/package.json +5 -2
  4. package/test.js +130 -19
package/README.md CHANGED
@@ -7,7 +7,11 @@
7
7
  - 支持配置文件的动态读取和修改
8
8
  - 修改配置后自动同步保存到JSON文件
9
9
  - 使用Proxy实现属性监听,操作方便
10
- - 支持无配置文件时的默认配置创建
10
+ - 支持同步和异步API操作
11
+ - 支持配置修改防抖功能,减少频繁保存
12
+ - 内置错误处理机制,提高稳定性
13
+ - 支持JSON5格式配置文件
14
+ - 向后兼容原有API
11
15
 
12
16
  ## 安装
13
17
 
@@ -17,32 +21,61 @@ npm install mm_config
17
21
 
18
22
  ## API说明
19
23
 
20
- ### 配置初始化
24
+ ### 导入模块
21
25
 
22
26
  ```javascript
27
+ // 导入同步配置创建函数(默认)
23
28
  const conf = require('mm_config');
24
29
 
30
+ // 导入异步配置创建函数
31
+ const createConfigAsync = conf.createConfigAsync;
32
+
33
+ // 导入配置管理器类
34
+ const { ConfigManager } = require('mm_config');
35
+ ```
36
+
37
+ ### 配置初始化
38
+
39
+ #### 同步API
40
+
41
+ ```javascript
25
42
  /**
43
+ * 创建同步配置管理器
26
44
  * @param {Object} _config 配置对象,可选,不传则从文件读取
27
45
  * @param {String} _jsonFile 配置文件路径,可选,不传则使用this.filename
46
+ * @param {Object} options 配置选项,可选
47
+ * @param {Number} options.debounceTime 防抖时间(ms),默认0
48
+ * @param {Boolean} options.pretty 是否美化输出,默认true
49
+ * @param {String} options.format 格式(json/json5),默认json
28
50
  * @returns {Proxy} 返回配置代理对象
29
51
  */
30
- const config = conf(_config, _jsonFile);
52
+ const config = conf(_config, _jsonFile, options);
53
+ ```
54
+
55
+ #### 异步API
56
+
57
+ ```javascript
58
+ /**
59
+ * 创建异步配置管理器
60
+ * @param {Object} _config 配置对象,可选,不传则从文件读取
61
+ * @param {String} _jsonFile 配置文件路径,可选,不传则使用this.filename
62
+ * @param {Object} options 配置选项,可选
63
+ * @returns {Promise<Proxy>} 返回配置代理对象的Promise
64
+ */
65
+ const config = await createConfigAsync(_config, _jsonFile, options);
31
66
  ```
32
67
 
33
- ### 参数说明
68
+ #### 代理对象特殊方法
34
69
 
35
- - `_config`: 初始配置对象,可选参数
36
- - 当传入对象时,将使用该对象作为初始配置
37
- - 当不传入时,将从配置文件中读取配置
70
+ 通过代理对象可以访问以下特殊方法:
38
71
 
39
- - `_jsonFile`: 配置文件路径,可选参数
40
- - 当传入路径时,将使用该路径作为配置文件
41
- - 当不传入时,将使用`this.filename`作为配置文件路径
72
+ - `_saveSync()`: 同步保存配置到文件
73
+ - `_saveAsync()`: 异步保存配置到文件
74
+ - `_raw`: 获取原始配置对象
42
75
 
43
76
  ## 使用示例
44
77
 
45
- ### 1. 创建新配置
78
+ ### 1. 基本同步配置(兼容原API)
46
79
 
47
80
  ```javascript
48
81
  // 创建配置并指定保存路径
@@ -54,47 +87,106 @@ const config = conf({
54
87
  // 修改配置会自动保存到文件
55
88
  config.name = "test";
56
89
  config.sort = 2;
90
+
91
+ // 显式调用保存
92
+ config._saveSync();
93
+
94
+ // 读取现有配置
95
+ const existingConfig = conf(null, "./config.json");
96
+ ```
97
+
98
+ ### 2. 使用异步API
99
+
100
+ ```javascript
101
+ async function setupConfig() {
102
+ // 创建异步配置
103
+ const config = await createConfigAsync({
104
+ name: "asyncDemo",
105
+ version: "1.0.0"
106
+ }, "./async_config.json");
107
+
108
+ // 修改配置会异步自动保存
109
+ config.timestamp = new Date().toISOString();
110
+
111
+ // 显式调用异步保存
112
+ await config._saveAsync();
113
+
114
+ return config;
115
+ }
57
116
  ```
58
117
 
59
- ### 2. 读取现有配置
118
+ ### 3. 使用防抖功能
60
119
 
61
120
  ```javascript
62
- // 设置配置文件路径
63
- this.filename = "./config.json";
121
+ // 创建带防抖的配置(500ms延迟保存)
122
+ const debouncedConfig = conf({
123
+ name: "debounceDemo"
124
+ }, "./debounce_config.json", {
125
+ debounceTime: 500 // 500ms防抖时间
126
+ });
127
+
128
+ // 快速多次修改,只会在最后一次修改后500ms保存一次
129
+ for (let i = 0; i < 10; i++) {
130
+ debouncedConfig.counter = i;
131
+ debouncedConfig.timestamp = new Date().toISOString();
132
+ }
133
+ ```
64
134
 
65
- // 读取现有配置文件
66
- const config = conf();
135
+ ### 4. 使用JSON5格式
67
136
 
68
- // 获取配置
69
- console.log(config.name);
137
+ ```javascript
138
+ // 创建支持JSON5格式的配置
139
+ const json5Config = conf(null, "./config.json5", {
140
+ format: 'json5'
141
+ });
70
142
 
71
- // 修改配置
72
- config.age = 26;
143
+ // JSON5格式支持注释、尾随逗号等特性
144
+ json5Config.description = "This supports JSON5 format";
73
145
  ```
74
146
 
75
- ### 3. 使用默认配置
147
+ ### 5. 直接使用ConfigManager类
76
148
 
77
149
  ```javascript
78
- // 使用默认配置,并指定保存路径
79
- const config = conf({
80
- name: "default",
81
- version: "1.0.0"
82
- }, "./config.json");
150
+ const { ConfigManager } = require('mm_config');
151
+
152
+ // 创建配置管理器实例
153
+ const manager = new ConfigManager({
154
+ name: "directDemo"
155
+ }, "./direct_config.json");
156
+
157
+ // 获取代理对象
158
+ const config = manager.getProxy();
83
159
 
84
- // 如果配置文件不存在,将使用默认配置并创建文件
85
- // 如果配置文件存在,将忽略默认配置,使用文件中的配置
160
+ // 使用配置
161
+ config.version = "1.0.0";
162
+
163
+ // 手动调用保存方法
164
+ manager.saveSync();
165
+ await manager.saveAsync();
86
166
  ```
87
167
 
168
+ ## 配置选项
169
+
170
+ 创建配置时可提供以下选项:
171
+
172
+ | 选项 | 类型 | 默认值 | 描述 |
173
+ |------|------|--------|------|
174
+ | debounceTime | Number | 0 | 防抖时间(毫秒),设置后多次修改会延迟保存 |
175
+ | pretty | Boolean | true | 是否美化JSON输出,设为false可生成压缩的JSON |
176
+ | format | String | 'json' | 配置文件格式,可选'json'或'json5' |
177
+
88
178
  ## 注意事项
89
179
 
90
180
  1. 配置文件会自动创建和同步
91
181
  2. 修改配置对象的属性会自动保存到文件
92
182
  3. 确保有文件的写入权限
93
- 4. 配置文件使用UTF-8编码的JSON格式
183
+ 4. 使用防抖功能可以减少频繁的文件IO操作
184
+ 5. 异步API适合在非阻塞环境中使用
185
+ 6. 当使用JSON5格式时,保存仍使用标准JSON格式,但可读取JSON5格式的文件
94
186
 
95
187
  ## 依赖
96
188
 
97
- - json5: 用于解析JSON
189
+ - json5: 用于解析JSON5格式
98
190
  - mm_expand: 提供文件操作扩展
99
191
 
100
192
  ## 许可证
package/index.js CHANGED
@@ -1,24 +1,210 @@
1
- require("mm_expand");
1
+ const JSON5 = require('json5');
2
+ const { writeFileSync, readFileSync, writeFile } = require('fs');
3
+ const { promisify } = require('util');
4
+ const writeFileAsync = promisify(writeFile);
2
5
 
3
6
  /**
4
- * 重够配置,同步保存
7
+ * 配置管理器类,提供同步和异步API
8
+ */
9
+ class ConfigManager {
10
+ /**
11
+ * 构造函数
12
+ * @param {Object} _config 配置对象
13
+ * @param {String} _jsonFile 配置文件路径
14
+ * @param {Object} options 配置选项
15
+ * @param {Number} options.debounceTime 防抖时间(ms)
16
+ * @param {Boolean} options.pretty 是否美化输出
17
+ * @param {String} options.format 格式(json/json5)
18
+ */
19
+ constructor(_config, _jsonFile, options = {}) {
20
+ this.config = _config || {};
21
+ this.filePath = _jsonFile;
22
+ this.options = {
23
+ debounceTime: 0, // 默认不防抖
24
+ pretty: true,
25
+ format: 'json',
26
+ ...options
27
+ };
28
+ this.writeTimer = null;
29
+ this.writePromise = Promise.resolve();
30
+
31
+ // 如果没有提供配置且有文件路径,则从文件加载
32
+ if (!_config && _jsonFile) {
33
+ try {
34
+ const content = readFileSync(_jsonFile, 'utf-8');
35
+ if (content) {
36
+ this.config = this.options.format === 'json5' ? JSON5.parse(content) : JSON.parse(content);
37
+ }
38
+ } catch (error) {
39
+ console.error(`Error loading config from ${_jsonFile}:`, error.message);
40
+ this.config = {};
41
+ }
42
+ }
43
+ }
44
+
45
+ /**
46
+ * 保存配置到文件(同步)
47
+ */
48
+ saveSync() {
49
+ if (!this.filePath) return;
50
+
51
+ try {
52
+ const content = this.options.pretty
53
+ ? JSON.stringify(this.config, null, 4)
54
+ : JSON.stringify(this.config);
55
+ writeFileSync(this.filePath, content, 'utf-8');
56
+ return true;
57
+ } catch (error) {
58
+ console.error(`Error saving config to ${this.filePath}:`, error.message);
59
+ return false;
60
+ }
61
+ }
62
+
63
+ /**
64
+ * 保存配置到文件(异步)
65
+ * @returns {Promise<Boolean>}
66
+ */
67
+ async saveAsync() {
68
+ if (!this.filePath) return Promise.resolve(true);
69
+
70
+ // 确保之前的写操作完成
71
+ await this.writePromise;
72
+
73
+ const content = this.options.pretty
74
+ ? JSON.stringify(this.config, null, 4)
75
+ : JSON.stringify(this.config);
76
+
77
+ this.writePromise = writeFileAsync(this.filePath, content, 'utf-8')
78
+ .then(() => true)
79
+ .catch(error => {
80
+ console.error(`Error saving config to ${this.filePath}:`, error.message);
81
+ return false;
82
+ });
83
+
84
+ return this.writePromise;
85
+ }
86
+
87
+ /**
88
+ * 获取代理对象
89
+ * @returns {Proxy}
90
+ */
91
+ getProxy() {
92
+ const self = this;
93
+
94
+ return new Proxy(this.config, {
95
+ set(obj, prop, value) {
96
+ obj[prop] = value;
97
+
98
+ if (self.options.debounceTime > 0) {
99
+ // 防抖处理
100
+ if (self.writeTimer) {
101
+ clearTimeout(self.writeTimer);
102
+ }
103
+ self.writeTimer = setTimeout(() => {
104
+ self.saveSync();
105
+ }, self.options.debounceTime);
106
+ } else {
107
+ // 无防抖,直接保存
108
+ self.saveSync();
109
+ }
110
+
111
+ return true;
112
+ },
113
+
114
+ get(obj, prop) {
115
+ // 提供一些特殊属性访问
116
+ if (prop === '_saveAsync') {
117
+ return () => self.saveAsync();
118
+ }
119
+ if (prop === '_saveSync') {
120
+ return () => self.saveSync();
121
+ }
122
+ if (prop === '_raw') {
123
+ return obj;
124
+ }
125
+
126
+ return obj[prop];
127
+ }
128
+ });
129
+ }
130
+
131
+ /**
132
+ * 获取异步代理对象
133
+ * @returns {Proxy}
134
+ */
135
+ getAsyncProxy() {
136
+ const self = this;
137
+
138
+ return new Proxy(this.config, {
139
+ set(obj, prop, value) {
140
+ obj[prop] = value;
141
+
142
+ if (self.options.debounceTime > 0) {
143
+ if (self.writeTimer) {
144
+ clearTimeout(self.writeTimer);
145
+ }
146
+ self.writeTimer = setTimeout(() => {
147
+ self.saveAsync();
148
+ }, self.options.debounceTime);
149
+ } else {
150
+ self.saveAsync();
151
+ }
152
+
153
+ return true;
154
+ },
155
+
156
+ get(obj, prop) {
157
+ if (prop === '_saveAsync') {
158
+ return () => self.saveAsync();
159
+ }
160
+ if (prop === '_saveSync') {
161
+ return () => self.saveSync();
162
+ }
163
+ if (prop === '_raw') {
164
+ return obj;
165
+ }
166
+
167
+ return obj[prop];
168
+ }
169
+ });
170
+ }
171
+ }
172
+
173
+ /**
174
+ * 创建配置管理器的工厂函数
175
+ * @param {Object} _config 配置对象
176
+ * @param {String} _jsonFile 配置文件路径
177
+ * @param {Object} options 配置选项
178
+ * @returns {Proxy}
179
+ */
180
+ function createConfig(_config, _jsonFile, options = {}) {
181
+ // 兼容旧的调用方式(this.filename)
182
+ if (!_jsonFile && this && this.filename) {
183
+ _jsonFile = this.filename;
184
+ }
185
+
186
+ const manager = new ConfigManager(_config, _jsonFile, options);
187
+ return manager.getProxy(); // 默认返回同步代理
188
+ }
189
+
190
+ /**
191
+ * 创建异步配置管理器的工厂函数
5
192
  * @param {Object} _config 配置对象
6
- * @param {String} _jsonFile 配置文件
193
+ * @param {String} _jsonFile 配置文件路径
194
+ * @param {Object} options 配置选项
195
+ * @returns {Promise<Proxy>}
7
196
  */
8
- module.exports = function(_config, _jsonFile) {
9
- if (!_jsonFile) {
10
- _jsonFile = this.filename;
11
- }
12
- if (!_config) {
13
- _config = _jsonFile.loadJson() || {};
14
- }
15
- return new Proxy(_config, {
16
- set: function(obj, prop, value) {
17
- obj[prop] = value;
18
- if (_jsonFile) {
19
- _jsonFile.saveJson(obj);
20
- }
21
- return true
22
- }
23
- });
24
- }
197
+ async function createConfigAsync(_config, _jsonFile, options = {}) {
198
+ // 兼容旧的调用方式(this.filename)
199
+ if (!_jsonFile && this && this.filename) {
200
+ _jsonFile = this.filename;
201
+ }
202
+
203
+ const manager = new ConfigManager(_config, _jsonFile, options);
204
+ return manager.getAsyncProxy();
205
+ }
206
+
207
+ // 导出工厂函数和类
208
+ module.exports = createConfig;
209
+ module.exports.createConfigAsync = createConfigAsync;
210
+ module.exports.ConfigManager = ConfigManager;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mm_config",
3
- "version": "1.1.5",
3
+ "version": "2.1.0",
4
4
  "description": "这是超级美眉配置同步器,用于更改配置同步保存为JSON文件",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -14,7 +14,10 @@
14
14
  "config",
15
15
  "JSON",
16
16
  "file",
17
- "sync"
17
+ "sync",
18
+ "async",
19
+ "debounce",
20
+ "json5"
18
21
  ],
19
22
  "author": "邱文武",
20
23
  "license": "ISC",
package/test.js CHANGED
@@ -1,21 +1,132 @@
1
- var conf = require('./index.js');
1
+ const conf = require('./index.js');
2
+ const createConfigAsync = conf.createConfigAsync;
2
3
 
3
- async function test(){
4
- // var config = conf({
5
- // name: "demo",
6
- // state: 1
7
- // }, "./config.json");
8
-
9
- this.filename = "./config.json";
10
- var config = conf({
11
- name: "demo",
12
- state: 2
13
- });
14
- config.name = "test";
15
- config.sort = 2;
16
-
17
- config = conf(null, this.filename);
18
- console.log(config);
19
- config.age = 26;
4
+ // 测试同步API(向后兼容)
5
+ function testSyncConfig() {
6
+ console.log('=== 测试同步配置 API(向后兼容)===');
7
+
8
+ // 方式1:提供配置对象和文件路径
9
+ const config1 = conf({
10
+ name: "demo",
11
+ state: 1
12
+ }, "./config1.json");
13
+
14
+ // 修改配置,会自动保存
15
+ config1.name = "test1";
16
+ config1.sort = 1;
17
+ console.log('配置1:', config1);
18
+
19
+ // 方式2:使用this.filename(兼容旧用法)
20
+ const context = {
21
+ filename: "./config2.json"
22
+ };
23
+
24
+ const config2 = conf.call(context, {
25
+ name: "demo",
26
+ state: 2
27
+ });
28
+
29
+ config2.name = "test2";
30
+ config2.sort = 2;
31
+ console.log('配置2:', config2);
32
+
33
+ // 从文件加载配置
34
+ const config3 = conf(null, "./config1.json");
35
+ console.log('从文件加载的配置:', config3);
36
+
37
+ // 手动保存
38
+ config3.age = 26;
39
+ config3._saveSync(); // 显式调用保存
40
+ console.log('更新后的配置3:', config3);
20
41
  }
21
- test();
42
+
43
+ // 测试异步API
44
+ async function testAsyncConfig() {
45
+ console.log('\n=== 测试异步配置 API ===');
46
+
47
+ try {
48
+ // 创建异步配置
49
+ const configAsync = await createConfigAsync({
50
+ name: "asyncDemo",
51
+ state: 3
52
+ }, "./config_async.json");
53
+
54
+ // 修改配置,会异步自动保存
55
+ configAsync.name = "asyncTest2";
56
+ configAsync.timestamp = new Date().toISOString();
57
+ console.log('异步配置:', configAsync);
58
+
59
+ // 显式调用异步保存
60
+ // await configAsync._saveAsync();
61
+ console.log('异步保存完成');
62
+
63
+ } catch (error) {
64
+ console.error('异步配置测试出错:', error);
65
+ }
66
+ }
67
+
68
+ // 测试高级功能:防抖
69
+ function testDebounce() {
70
+ console.log('\n=== 测试防抖功能 ===');
71
+
72
+ // 创建带防抖的配置(500ms延迟保存)
73
+ const configDebounce = conf({
74
+ name: "debounceDemo"
75
+ }, "./config_debounce.json", {
76
+ debounceTime: 500, // 500ms防抖时间
77
+ pretty: true // 美化JSON输出
78
+ });
79
+
80
+ // 快速多次修改,应该只保存最后一次
81
+ console.log('开始多次快速修改...');
82
+ for (let i = 0; i < 5; i++) {
83
+ configDebounce.value = i;
84
+ configDebounce.timestamp = new Date().toISOString();
85
+ }
86
+
87
+ console.log('修改完成,等待防抖触发保存...');
88
+ console.log('最终配置:', configDebounce);
89
+ }
90
+
91
+ // 测试JSON5格式
92
+ function testJson5() {
93
+ console.log('\n=== 测试JSON5格式支持 ===');
94
+
95
+ // 创建配置时指定JSON5格式
96
+ const configJson5 = conf({
97
+ name: "json5Demo",
98
+ // JSON5支持的一些特性,如注释、尾随逗号等
99
+ // 虽然保存时会转为标准JSON,但读取时可以处理JSON5格式
100
+ }, "./config_json5.json", {
101
+ format: 'json5',
102
+ pretty: true
103
+ });
104
+
105
+ configJson5.description = "This uses JSON5 format";
106
+ console.log('JSON5配置:', configJson5);
107
+ }
108
+
109
+ // 运行所有测试
110
+ async function runAllTests() {
111
+ console.log('开始运行配置管理器测试...');
112
+
113
+ // 运行同步测试
114
+ testSyncConfig();
115
+
116
+ // 运行异步测试
117
+ await testAsyncConfig();
118
+
119
+ // 运行防抖测试
120
+ testDebounce();
121
+
122
+ // 运行JSON5测试
123
+ testJson5();
124
+
125
+ console.log('\n所有测试完成!');
126
+ console.log('请查看生成的配置文件以验证保存结果。');
127
+ }
128
+
129
+ // 执行测试
130
+ runAllTests().catch(err => {
131
+ console.error('测试过程中出现错误:', err);
132
+ });