schema-dsl 1.2.2 → 1.2.3

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/index.js CHANGED
@@ -110,12 +110,83 @@ dsl.error = {
110
110
  assert: (condition, code, paramsOrLocale, statusCode, locale) => I18nError.assert(condition, code, paramsOrLocale, statusCode, locale)
111
111
  };
112
112
 
113
+ /**
114
+ * 从目录递归加载语言包(内部函数)
115
+ *
116
+ * 支持子目录递归扫描,子目录名作为模块组织层(不影响语言 key)。
117
+ * 同名 key 冲突时:strict 模式抛错,默认模式打 WARN 日志。
118
+ *
119
+ * @param {string} dirPath - 目录绝对路径
120
+ * @param {Object} options - 配置选项
121
+ * @param {boolean} [options.strict=false] - 严格模式:同名 key 冲突时抛出 Error
122
+ * @private
123
+ */
124
+ function loadLocalesFromDir(dirPath, options = {}) {
125
+ const fs = require('fs');
126
+ const path = require('path');
127
+
128
+ if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory()) {
129
+ console.warn('[schema-dsl] i18n path does not exist or is not a directory:', dirPath);
130
+ return;
131
+ }
132
+
133
+ // 语言代码格式校验:zh-CN / en-US / zh / en 等
134
+ const LOCALE_NAME_RE = /^[a-z]{2,3}(-[A-Z]{2,4})?$/;
135
+
136
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
137
+
138
+ entries.forEach(entry => {
139
+ const fullPath = path.join(dirPath, entry.name);
140
+
141
+ if (entry.isDirectory()) {
142
+ // 递归进入子目录(子目录名仅作模块组织层,不影响语言 key)
143
+ loadLocalesFromDir(fullPath, options);
144
+ } else if (entry.isFile()) {
145
+ const ext = path.extname(entry.name);
146
+ if (ext !== '.js' && ext !== '.json') return;
147
+
148
+ const localeName = path.basename(entry.name, ext);
149
+
150
+ // 文件名格式校验:只加载符合语言代码格式的文件
151
+ if (!LOCALE_NAME_RE.test(localeName)) {
152
+ // 仅 debug 级别,不影响正常运行
153
+ return;
154
+ }
155
+
156
+ let messages;
157
+ try {
158
+ messages = require(path.resolve(fullPath));
159
+ } catch (e) {
160
+ console.warn('[schema-dsl] Failed to load locale file:', fullPath, e.message);
161
+ return;
162
+ }
163
+
164
+ // 冲突检测:检查 messages 的每个 key 是否已存在于当前语言包
165
+ const existing = Locale.locales[localeName] || {};
166
+ const conflictKeys = Object.keys(messages).filter(k => Object.prototype.hasOwnProperty.call(existing, k));
167
+
168
+ if (conflictKeys.length > 0) {
169
+ const keyList = conflictKeys.join(', ');
170
+ const msg = `[schema-dsl] i18n key 冲突 in locale '${localeName}'\n 冲突 key: ${keyList}\n 来源文件: ${fullPath}`;
171
+ if (options.strict) {
172
+ throw new Error(msg);
173
+ } else {
174
+ console.warn(msg);
175
+ }
176
+ }
177
+
178
+ Locale.addLocale(localeName, messages);
179
+ }
180
+ });
181
+ }
182
+
113
183
  /**
114
184
  * 全局配置
115
185
  * @param {Object} options - 配置选项
116
186
  * @param {Object} options.patterns - 验证规则扩展 (phone, idCard, creditCard)
117
- * @param {string|Object} options.i18n - 多语言配置(目录路径或语言包对象)
187
+ * @param {string|Object} options.i18n - 多语言配置(目录路径、对象语言包、或含 localesPath 的对象)
118
188
  * @param {Object} options.cache - 缓存配置
189
+ * @param {boolean} [options.strict=false] - 严格模式:i18n 目录扫描时 key 冲突直接抛错(默认 false)
119
190
  */
120
191
  dsl.config = function (options = {}) {
121
192
  const patterns = require('./lib/config/patterns');
@@ -127,31 +198,24 @@ dsl.config = function (options = {}) {
127
198
  if (options.patterns.creditCard) Object.assign(patterns.creditCard, options.patterns.creditCard);
128
199
  }
129
200
 
130
- // 多语言支持 (v1.0.1 优化)
201
+ // 多语言支持 (v1.0.1 优化;v1.2.3 增强:递归子目录 + 冲突检测)
131
202
  if (options.i18n) {
132
- // 方式 1: 传入目录路径(字符串)
203
+ // 方式 1: 传入目录路径(字符串)→ 递归扫描
133
204
  if (typeof options.i18n === 'string') {
134
- const fs = require('fs');
135
- const path = require('path');
136
-
137
- if (fs.existsSync(options.i18n) && fs.statSync(options.i18n).isDirectory()) {
138
- const files = fs.readdirSync(options.i18n);
139
- files.forEach(file => {
140
- if (file.endsWith('.js') || file.endsWith('.json')) {
141
- const localeName = path.basename(file, path.extname(file));
142
- const messages = require(path.resolve(options.i18n, file));
143
- Locale.addLocale(localeName, messages);
144
- }
145
- });
146
- } else {
147
- console.warn('[schema-dsl] i18n path does not exist:', options.i18n);
148
- }
205
+ loadLocalesFromDir(options.i18n, options);
149
206
  }
150
- // 方式 2: 直接传入对象
207
+ // 方式 2: 传入对象
151
208
  else if (typeof options.i18n === 'object') {
152
- Object.keys(options.i18n).forEach(locale => {
153
- Locale.addLocale(locale, options.i18n[locale]);
154
- });
209
+ // 方式 2a: 含 localesPath 字段 → 走目录扫描(兼容文档记载用法)
210
+ if (options.i18n.localesPath) {
211
+ loadLocalesFromDir(options.i18n.localesPath, options);
212
+ }
213
+ // 方式 2b: 直接传语言包对象 { 'zh-CN': {...}, 'en-US': {...} }(原有逻辑,不变)
214
+ else {
215
+ Object.keys(options.i18n).forEach(locale => {
216
+ Locale.addLocale(locale, options.i18n[locale]);
217
+ });
218
+ }
155
219
  }
156
220
  }
157
221
 
@@ -388,7 +452,7 @@ module.exports = {
388
452
  CONSTANTS,
389
453
 
390
454
  // 版本信息
391
- VERSION: '1.0.4'
455
+ VERSION: '1.2.3'
392
456
  };
393
457
 
394
458
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "schema-dsl",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "description": "简洁强大的JSON Schema验证库 - DSL语法 + String扩展 + 便捷validate",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",