mm_expand 2.4.2 → 2.4.4
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/lib/base.js +3 -3
- package/lib/errors.js +1 -1
- package/lib/event.js +7 -7
- package/lib/eventer.js +2294 -2422
- package/lib/file.js +797 -797
- package/lib/global.js +2 -1
- package/lib/lang.js +6 -6
- package/lib/logger.js +2 -2
- package/lib/string.js +11 -10
- package/package.json +2 -2
package/lib/file.js
CHANGED
|
@@ -1,797 +1,797 @@
|
|
|
1
|
-
const {
|
|
2
|
-
writeFileSync,
|
|
3
|
-
existsSync,
|
|
4
|
-
readFileSync,
|
|
5
|
-
copyFileSync,
|
|
6
|
-
statSync,
|
|
7
|
-
readdirSync,
|
|
8
|
-
mkdirSync,
|
|
9
|
-
unlinkSync,
|
|
10
|
-
promises: fsp
|
|
11
|
-
} = require('fs');
|
|
12
|
-
const {
|
|
13
|
-
join,
|
|
14
|
-
dirname,
|
|
15
|
-
basename,
|
|
16
|
-
extname,
|
|
17
|
-
resolve,
|
|
18
|
-
isAbsolute
|
|
19
|
-
} = require('path');
|
|
20
|
-
const os = require('os');
|
|
21
|
-
const {
|
|
22
|
-
rimrafSync
|
|
23
|
-
} = require('rimraf');
|
|
24
|
-
var ncp = require('ncp').ncp;
|
|
25
|
-
const { Base } = require('./base.js');
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 判断是否为文件
|
|
29
|
-
* @param {string} file_or_dir 文件或目录
|
|
30
|
-
* @returns {boolean} 是文件返回true,否则返回false
|
|
31
|
-
*/
|
|
32
|
-
function isFile(file_or_dir) {
|
|
33
|
-
return statSync(file_or_dir).isDirectory() !== true;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* 处理根路径
|
|
38
|
-
* @param {string} file 文件路径
|
|
39
|
-
* @returns {string} 完整路径
|
|
40
|
-
*/
|
|
41
|
-
function handleRootPath(file) {
|
|
42
|
-
if ($.run_path.indexOf(file) === 0 || file.indexOf($.run_path) === 0) {
|
|
43
|
-
return file;
|
|
44
|
-
}
|
|
45
|
-
return resolve($.run_path || process.cwd(), file.substring(1));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* 处理用户主目录路径
|
|
50
|
-
* @param {string} file 文件路径
|
|
51
|
-
* @returns {string} 完整路径
|
|
52
|
-
*/
|
|
53
|
-
function handleHomePath(file) {
|
|
54
|
-
return resolve(os.homedir(), file.substring(2));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* 处理项目根路径
|
|
59
|
-
* @param {string} file 文件路径
|
|
60
|
-
* @returns {string} 完整路径
|
|
61
|
-
*/
|
|
62
|
-
function handleAtPath(file) {
|
|
63
|
-
return resolve($.root_path, file.substring(2));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* 处理相对路径
|
|
68
|
-
* @param {string} file 文件路径
|
|
69
|
-
* @param {string} dir 当前目录
|
|
70
|
-
* @returns {string} 完整路径
|
|
71
|
-
*/
|
|
72
|
-
function handleRelativePath(file, dir) {
|
|
73
|
-
var base_dir = dir && typeof dir === 'string' ? dir : $.run_path;
|
|
74
|
-
return resolve(fullname(base_dir), file);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* 验证文件路径
|
|
79
|
-
* @param {string} file 文件路径
|
|
80
|
-
* @returns {string | null} 验证结果或null
|
|
81
|
-
*/
|
|
82
|
-
function validateFilePath(file) {
|
|
83
|
-
if (typeof file !== 'string') {
|
|
84
|
-
throw new Error('file参数必须是字符串');
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// 如果是空字符串,返回当前运行路径
|
|
88
|
-
if (!file) {
|
|
89
|
-
return $.run_path || process.cwd();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* 补全文件路径
|
|
97
|
-
* @param {string} file 文件路径
|
|
98
|
-
* @param {string} dir 当前目录
|
|
99
|
-
* @returns {string} 完整路径
|
|
100
|
-
*/
|
|
101
|
-
function fullname(file, dir) {
|
|
102
|
-
var result = validateFilePath(file);
|
|
103
|
-
if (result) {
|
|
104
|
-
return result;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// 处理以 / 开头的路径(从项目启动位置)
|
|
108
|
-
if (file.startsWith('/') && !file.startsWith('//') && file !== '/') {
|
|
109
|
-
return handleRootPath(file);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// 处理以 ~/ 开头的路径(从用户主目录开始)
|
|
113
|
-
if (file.startsWith('~/')) {
|
|
114
|
-
return handleHomePath(file);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// 处理以 @/ 开头的路径(从用户主目录开始)
|
|
118
|
-
if (file.startsWith('@/')) {
|
|
119
|
-
return handleAtPath(file);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// 处理相对路径
|
|
123
|
-
if (file.startsWith('./') || file.startsWith('../')
|
|
124
|
-
|
|
125
|
-
|| (!isAbsolute(file) && !file.startsWith('/'))) {
|
|
126
|
-
return handleRelativePath(file, dir);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// 如果已经是绝对路径,直接返回
|
|
130
|
-
return file;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* 文件类函数
|
|
135
|
-
*/
|
|
136
|
-
class File extends Base {
|
|
137
|
-
/**
|
|
138
|
-
* 构造函数,用于初始化文件类
|
|
139
|
-
* @param {object} config 文件配置对象
|
|
140
|
-
*/
|
|
141
|
-
constructor(config) {
|
|
142
|
-
super(config);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* 补全文件路径
|
|
148
|
-
* @param {string} file 文件路径
|
|
149
|
-
* @param {string} dir 当前目录
|
|
150
|
-
* @returns {string} 完整路径
|
|
151
|
-
*/
|
|
152
|
-
File.prototype.fullname = fullname;
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* 搜索目录下所有文件
|
|
156
|
-
* @param {string} dir 目录地址
|
|
157
|
-
* @param {string} keyword 搜索关键词
|
|
158
|
-
* @param {string} keyword_dir 目录搜素关键词
|
|
159
|
-
* @returns {Array} 文件路径数组
|
|
160
|
-
*/
|
|
161
|
-
File.prototype.getAll = function (dir, keyword, keyword_dir) {
|
|
162
|
-
// 文件数组
|
|
163
|
-
var list = [];
|
|
164
|
-
|
|
165
|
-
// 目录数组
|
|
166
|
-
var dirs = [];
|
|
167
|
-
// 获取相关目录
|
|
168
|
-
eachDir(dirs, dir.fullname(), keyword_dir);
|
|
169
|
-
var len = dirs.length;
|
|
170
|
-
for (var i = 0; i < len; i++) {
|
|
171
|
-
getFile(list, dirs[i], keyword);
|
|
172
|
-
}
|
|
173
|
-
return list;
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* 搜索目录下所有文件(异步并行极速版)
|
|
178
|
-
* @param {string} dir 目录地址
|
|
179
|
-
* @param {string} keyword 搜索关键词
|
|
180
|
-
* @param {string} keyword_dir 目录搜素关键词
|
|
181
|
-
* @returns {Array} 文件路径数组
|
|
182
|
-
*/
|
|
183
|
-
File.prototype.getAllAsync = async function (dir, keyword, keyword_dir) {
|
|
184
|
-
var root_dir = dir.fullname();
|
|
185
|
-
var
|
|
186
|
-
|
|
187
|
-
// 使用独立 eachDirAsync 并行收集匹配的目录
|
|
188
|
-
await eachDirAsync(
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* 异步获取目录下的匹配文件
|
|
192
|
-
* @param {string} directory 目录路径
|
|
193
|
-
* @returns {Array} 文件路径数组
|
|
194
|
-
*/
|
|
195
|
-
async function getFileAsync(directory) {
|
|
196
|
-
var items;
|
|
197
|
-
try {
|
|
198
|
-
items = await fsp.readdir(directory, { withFileTypes: true });
|
|
199
|
-
} catch {
|
|
200
|
-
return [];
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
var files = [];
|
|
204
|
-
for (var i = 0; i < items.length; i++) {
|
|
205
|
-
var item = items[i];
|
|
206
|
-
if (item.isFile() && (!keyword || item.name.has(keyword))) {
|
|
207
|
-
files.push(join(directory, item.name));
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
return files;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// 没有匹配的子目录时,直接搜索根目录
|
|
214
|
-
if (
|
|
215
|
-
return await getFileAsync(root_dir);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// 并行处理所有匹配目录的文件收集
|
|
219
|
-
var file_lists = await Promise.all(
|
|
220
|
-
return file_lists.flat();
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* 获取当前目录下所有文件
|
|
225
|
-
* @param {string} dir 目录地址
|
|
226
|
-
* @param {string} keyword 搜索关键词
|
|
227
|
-
* @returns {Array} 文件路径数组
|
|
228
|
-
*/
|
|
229
|
-
File.prototype.get = function (dir, keyword) {
|
|
230
|
-
var list = [];
|
|
231
|
-
getFile(list, dir, keyword);
|
|
232
|
-
return list;
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* 加载文件
|
|
237
|
-
* @param {string} file 文件路径
|
|
238
|
-
* @param {string} encode 编码方式
|
|
239
|
-
* @returns {string} 加载的文本
|
|
240
|
-
*/
|
|
241
|
-
File.prototype.load = function (file, encode) {
|
|
242
|
-
if (!file || typeof file !== 'string') {
|
|
243
|
-
throw new Error('file参数必须是非空字符串');
|
|
244
|
-
}
|
|
245
|
-
try {
|
|
246
|
-
if (existsSync(file) && isFile(file)) {
|
|
247
|
-
return readFileSync(file, encode);
|
|
248
|
-
}
|
|
249
|
-
return null;
|
|
250
|
-
} catch (error) {
|
|
251
|
-
this.log('error', '加载文件失败:', error);
|
|
252
|
-
return null;
|
|
253
|
-
}
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* 保存文件
|
|
258
|
-
* @param {string} file 文件路径
|
|
259
|
-
* @param {string} data 数据内容
|
|
260
|
-
* @param {string} encode 编码方式
|
|
261
|
-
* @returns {boolean} 保存成功返回true,否则返回false
|
|
262
|
-
*/
|
|
263
|
-
File.prototype.save = function (file, data, encode) {
|
|
264
|
-
if (!file || typeof file !== 'string') {
|
|
265
|
-
throw new Error('file参数必须是非空字符串');
|
|
266
|
-
}
|
|
267
|
-
if (data === undefined || data === null) {
|
|
268
|
-
throw new Error('data参数不能为空');
|
|
269
|
-
}
|
|
270
|
-
var enc = encode || 'utf-8';
|
|
271
|
-
try {
|
|
272
|
-
writeFileSync(file, data, enc);
|
|
273
|
-
return true;
|
|
274
|
-
} catch (e) {
|
|
275
|
-
this.log('error', '保存文件失败:', e);
|
|
276
|
-
return false;
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* 复制文件
|
|
282
|
-
* @param {string} source 文件原路径
|
|
283
|
-
* @param {string} target 目标路径
|
|
284
|
-
* @returns {boolean} 复制成功返回true,否则返回false
|
|
285
|
-
*/
|
|
286
|
-
File.prototype.copy = function (source, target) {
|
|
287
|
-
if (!source || typeof source !== 'string') {
|
|
288
|
-
throw new Error('source参数必须是非空字符串');
|
|
289
|
-
}
|
|
290
|
-
if (!target || typeof target !== 'string') {
|
|
291
|
-
throw new Error('target参数必须是非空字符串');
|
|
292
|
-
}
|
|
293
|
-
try {
|
|
294
|
-
copyFileSync(source.fullname(), target.fullname());
|
|
295
|
-
return true;
|
|
296
|
-
} catch (err) {
|
|
297
|
-
this.log('error', err);
|
|
298
|
-
return false;
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* 删除文件
|
|
304
|
-
* @param {string} file 文件路径
|
|
305
|
-
* @returns {boolean} 删除成功返回true,否则返回false
|
|
306
|
-
*/
|
|
307
|
-
File.prototype.del = function (file) {
|
|
308
|
-
if (!file || typeof file !== 'string') {
|
|
309
|
-
throw new Error('file参数必须是非空字符串');
|
|
310
|
-
}
|
|
311
|
-
try {
|
|
312
|
-
unlinkSync(file.fullname());
|
|
313
|
-
return true;
|
|
314
|
-
} catch (err) {
|
|
315
|
-
this.log('error', err);
|
|
316
|
-
return false;
|
|
317
|
-
}
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* 检查文件是否存在
|
|
322
|
-
* @param {string} file 文件路径
|
|
323
|
-
* @returns {boolean} 存在返回true,否则返回false
|
|
324
|
-
*/
|
|
325
|
-
File.prototype.exists = function (file) {
|
|
326
|
-
if (!file || typeof file !== 'string') {
|
|
327
|
-
throw new Error('file参数必须是非空字符串');
|
|
328
|
-
}
|
|
329
|
-
return existsSync(file);
|
|
330
|
-
};
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* 读取文件内容
|
|
334
|
-
* @param {string} file 文件路径
|
|
335
|
-
* @param {string} encode 编码方式
|
|
336
|
-
* @returns {string} 文件内容
|
|
337
|
-
*/
|
|
338
|
-
File.prototype.read = function (file, encode) {
|
|
339
|
-
var enc = encode || 'utf-8';
|
|
340
|
-
return this.load(file, enc);
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* 写入文件内容
|
|
345
|
-
* @param {string} file 文件路径
|
|
346
|
-
* @param {string} data 数据内容
|
|
347
|
-
* @param {string} encode 编码方式
|
|
348
|
-
* @returns {boolean} 写入成功返回true,否则返回false
|
|
349
|
-
*/
|
|
350
|
-
File.prototype.write = function (file, data, encode) {
|
|
351
|
-
if (!data) {
|
|
352
|
-
throw new Error('data参数不能为空');
|
|
353
|
-
}
|
|
354
|
-
return this.save(file, data, encode);
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* 搜索目录下所有目录
|
|
359
|
-
* @param {Array} list 结果路径数组
|
|
360
|
-
* @param {string} dir 目录地址
|
|
361
|
-
* @param {string} keyword 搜索关键词
|
|
362
|
-
*/
|
|
363
|
-
function eachDir(list, dir, keyword) {
|
|
364
|
-
var items = readdirSync(dir);
|
|
365
|
-
var len = items.length;
|
|
366
|
-
|
|
367
|
-
for (var i = 0; i < len; i++) {
|
|
368
|
-
var name = items[i];
|
|
369
|
-
var f = join(dir, name);
|
|
370
|
-
|
|
371
|
-
// 识别是否为目录
|
|
372
|
-
if (!isFile(f)) {
|
|
373
|
-
// 判断是否包含关键词
|
|
374
|
-
if (!keyword || name.has(keyword)) {
|
|
375
|
-
list.push(f);
|
|
376
|
-
}
|
|
377
|
-
eachDir(list, f, keyword);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* 异步并行搜索目录下所有目录
|
|
383
|
-
* @param {Array} list 结果路径数组
|
|
384
|
-
* @param {string} dir 目录地址
|
|
385
|
-
* @param {string} keyword 搜索关键词
|
|
386
|
-
*/
|
|
387
|
-
async function eachDirAsync(list, dir, keyword) {
|
|
388
|
-
var items;
|
|
389
|
-
try {
|
|
390
|
-
items = await fsp.readdir(dir, { withFileTypes: true });
|
|
391
|
-
} catch {
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
var subDirs = [];
|
|
396
|
-
for (var i = 0; i < items.length; i++) {
|
|
397
|
-
var item = items[i];
|
|
398
|
-
if (item.isDirectory()) {
|
|
399
|
-
var full_path = join(dir, item.name);
|
|
400
|
-
if (!keyword || item.name.has(keyword)) {
|
|
401
|
-
list.push(full_path);
|
|
402
|
-
}
|
|
403
|
-
subDirs.push(full_path);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// 并行遍历所有子目录
|
|
408
|
-
if (subDirs.length > 0) {
|
|
409
|
-
await Promise.all(subDirs.map((d) => {
|
|
410
|
-
return eachDirAsync(list, d, keyword);
|
|
411
|
-
}));
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* 获取当前目录下所有目录
|
|
417
|
-
* @param {Array} list 结果路径数组
|
|
418
|
-
* @param {string} dir 目录地址
|
|
419
|
-
* @param {string} keyword 搜索关键词
|
|
420
|
-
*/
|
|
421
|
-
function getDir(list, dir, keyword) {
|
|
422
|
-
var items = readdirSync(dir);
|
|
423
|
-
var len = items.length;
|
|
424
|
-
|
|
425
|
-
for (var i = 0; i < len; i++) {
|
|
426
|
-
var name = items[i];
|
|
427
|
-
var f = join(dir, name);
|
|
428
|
-
|
|
429
|
-
// 识别是否为目录
|
|
430
|
-
if (!isFile(f)) {
|
|
431
|
-
// 判断关键词是否相等
|
|
432
|
-
if (!keyword || name.has(keyword)) {
|
|
433
|
-
list.push(f);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
/**
|
|
440
|
-
* 添加文件路径
|
|
441
|
-
* @param {Array} list 结果路径数组
|
|
442
|
-
* @param {string} dir 目录地址
|
|
443
|
-
* @param {string} keyword 搜索关键词
|
|
444
|
-
*/
|
|
445
|
-
function getFile(list, dir, keyword) {
|
|
446
|
-
var items = readdirSync(dir);
|
|
447
|
-
var len = items.length;
|
|
448
|
-
|
|
449
|
-
for (var i = 0; i < len; i++) {
|
|
450
|
-
var name = items[i];
|
|
451
|
-
var f = join(dir, name);
|
|
452
|
-
|
|
453
|
-
// 识别是否非目录
|
|
454
|
-
if (isFile(f)) {
|
|
455
|
-
// 判断关键词是否相等
|
|
456
|
-
if (!keyword || name.has(keyword)) {
|
|
457
|
-
list.push(f);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
/**
|
|
463
|
-
* 目录类函数
|
|
464
|
-
*/
|
|
465
|
-
class Dir extends Base {
|
|
466
|
-
/**
|
|
467
|
-
* 构造函数
|
|
468
|
-
* @param {object} config 目录配置对象
|
|
469
|
-
*/
|
|
470
|
-
constructor(config) {
|
|
471
|
-
super(config);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
/**
|
|
476
|
-
* 补全目录路径
|
|
477
|
-
* @param {string} dir 目录路径
|
|
478
|
-
* @param {string} base_dir 基础目录
|
|
479
|
-
* @returns {string} 完整路径
|
|
480
|
-
*/
|
|
481
|
-
Dir.prototype.fullname = fullname;
|
|
482
|
-
|
|
483
|
-
/**
|
|
484
|
-
* 搜索目录下所有目录
|
|
485
|
-
* @param {string} dir 目录地址
|
|
486
|
-
* @param {string} keyword 搜索关键词
|
|
487
|
-
* @returns {Array} 目录路径数组
|
|
488
|
-
*/
|
|
489
|
-
Dir.prototype.getAll = function (dir, keyword) {
|
|
490
|
-
var list = [];
|
|
491
|
-
eachDir(list, this.fullname(dir), keyword);
|
|
492
|
-
return list;
|
|
493
|
-
};
|
|
494
|
-
|
|
495
|
-
/**
|
|
496
|
-
* 搜索目录下所有目录(异步并行极速版)
|
|
497
|
-
* @param {string} dir 目录地址
|
|
498
|
-
* @param {string} keyword 搜索关键词
|
|
499
|
-
* @returns {Array} 目录路径数组
|
|
500
|
-
*/
|
|
501
|
-
Dir.prototype.getAllAsync = async function (dir, keyword) {
|
|
502
|
-
var list = [];
|
|
503
|
-
await eachDirAsync(list, this.fullname(dir), keyword);
|
|
504
|
-
return list;
|
|
505
|
-
};
|
|
506
|
-
|
|
507
|
-
/**
|
|
508
|
-
* 搜索当前目录下所有目录
|
|
509
|
-
* @param {string} dir 目录地址
|
|
510
|
-
* @param {string} keyword 搜索关键词
|
|
511
|
-
* @returns {Array} 目录路径数组
|
|
512
|
-
*/
|
|
513
|
-
Dir.prototype.get = function (dir, keyword) {
|
|
514
|
-
var list = [];
|
|
515
|
-
getDir(list, this.fullname(dir), keyword);
|
|
516
|
-
return list;
|
|
517
|
-
};
|
|
518
|
-
|
|
519
|
-
/**
|
|
520
|
-
* 复制目录
|
|
521
|
-
* @param {string} source 源路径
|
|
522
|
-
* @param {string} target 目标路径
|
|
523
|
-
* @param {Function} func 回调函数
|
|
524
|
-
*/
|
|
525
|
-
Dir.prototype.copy = function (source, target, func) {
|
|
526
|
-
ncp(source.fullname(), target.fullname(), func);
|
|
527
|
-
};
|
|
528
|
-
|
|
529
|
-
/**
|
|
530
|
-
* 删除目录
|
|
531
|
-
* @param {string} dir 目录路径
|
|
532
|
-
* @param {Function} func 回调函数
|
|
533
|
-
*/
|
|
534
|
-
Dir.prototype.del = function (dir, func) {
|
|
535
|
-
rimrafSync(dir.fullname(), func);
|
|
536
|
-
};
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* 判断目录是否存在
|
|
540
|
-
* @param {string} dir 目录路径
|
|
541
|
-
* @returns {boolean} 存在返回true, 否则返回false
|
|
542
|
-
*/
|
|
543
|
-
Dir.prototype.exists = function (dir) {
|
|
544
|
-
return existsSync(dir.fullname());
|
|
545
|
-
};
|
|
546
|
-
|
|
547
|
-
/** === 文件类函数 === */
|
|
548
|
-
/**
|
|
549
|
-
* 补全路径
|
|
550
|
-
* @param {string} dir 文件路径
|
|
551
|
-
* @returns {string} 全路径
|
|
552
|
-
*/
|
|
553
|
-
String.prototype.fullname = function (dir) {
|
|
554
|
-
return fullname(this + '', dir);
|
|
555
|
-
};
|
|
556
|
-
|
|
557
|
-
/**
|
|
558
|
-
* 获取当前目录下所有文件
|
|
559
|
-
* @param {string} keyword 搜索关键词
|
|
560
|
-
* @returns {Array} 文件路径数组
|
|
561
|
-
*/
|
|
562
|
-
String.prototype.getFile = function (keyword) {
|
|
563
|
-
return $.file.get(this + '', keyword);
|
|
564
|
-
};
|
|
565
|
-
|
|
566
|
-
/**
|
|
567
|
-
* 搜索当前目录下所有目录
|
|
568
|
-
* @param {string} keyword 搜索关键词
|
|
569
|
-
* @returns {Array} 目录路径数组
|
|
570
|
-
*/
|
|
571
|
-
String.prototype.getDir = function (keyword) {
|
|
572
|
-
return $.dir.get(this + '', keyword);
|
|
573
|
-
};
|
|
574
|
-
|
|
575
|
-
/**
|
|
576
|
-
* 复制文件
|
|
577
|
-
* @param {string} file 保存路径
|
|
578
|
-
* @returns {boolean} 复制成功返回true, 失败返回false
|
|
579
|
-
*/
|
|
580
|
-
String.prototype.copyFile = function (file) {
|
|
581
|
-
return copyFileSync(this.fullname(), file.fullname());
|
|
582
|
-
};
|
|
583
|
-
|
|
584
|
-
/**
|
|
585
|
-
* 复制目录
|
|
586
|
-
* @param {string} file 保存路径
|
|
587
|
-
* @param {Function} func 回调函数
|
|
588
|
-
* @returns {boolean} 复制成功返回true, 失败返回false
|
|
589
|
-
*/
|
|
590
|
-
String.prototype.copyDir = function (file, func) {
|
|
591
|
-
return ncp(this.fullname(), file.fullname(), func);
|
|
592
|
-
};
|
|
593
|
-
|
|
594
|
-
/**
|
|
595
|
-
* 是否目录
|
|
596
|
-
* @returns {boolean} 是目录返回true, 否则返回false
|
|
597
|
-
*/
|
|
598
|
-
String.prototype.isDir = function () {
|
|
599
|
-
var bl = false;
|
|
600
|
-
try {
|
|
601
|
-
bl = statSync(this + '').isDirectory();
|
|
602
|
-
} catch {
|
|
603
|
-
// $.log.error("非路径");
|
|
604
|
-
}
|
|
605
|
-
return bl;
|
|
606
|
-
};
|
|
607
|
-
|
|
608
|
-
/**
|
|
609
|
-
* 是否文件
|
|
610
|
-
* @returns {boolean} 是目录返回true, 否则返回false
|
|
611
|
-
*/
|
|
612
|
-
String.prototype.isFile = function () {
|
|
613
|
-
var bl = false;
|
|
614
|
-
try {
|
|
615
|
-
bl = statSync(this + '').isFile();
|
|
616
|
-
} catch {
|
|
617
|
-
// $.log.error("非路径");
|
|
618
|
-
}
|
|
619
|
-
return bl;
|
|
620
|
-
};
|
|
621
|
-
|
|
622
|
-
/**
|
|
623
|
-
* 取路径
|
|
624
|
-
* @returns {string} 字符串
|
|
625
|
-
*/
|
|
626
|
-
String.prototype.dirname = function () {
|
|
627
|
-
return dirname(this + '');
|
|
628
|
-
};
|
|
629
|
-
/**
|
|
630
|
-
* 取基础名
|
|
631
|
-
* @returns {string} 字符串
|
|
632
|
-
*/
|
|
633
|
-
String.prototype.basename = function () {
|
|
634
|
-
return basename(this + '');
|
|
635
|
-
};
|
|
636
|
-
/**
|
|
637
|
-
* 取拓展名
|
|
638
|
-
* @returns {string} 字符串
|
|
639
|
-
*/
|
|
640
|
-
String.prototype.extname = function () {
|
|
641
|
-
return extname(this + '');
|
|
642
|
-
};
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
/**
|
|
646
|
-
* 保存文件
|
|
647
|
-
* @param {string} text 被保存的文本
|
|
648
|
-
* @returns {boolean} 保存成功返回true, 失败返回false
|
|
649
|
-
*/
|
|
650
|
-
String.prototype.saveText = function (text) {
|
|
651
|
-
try {
|
|
652
|
-
writeFileSync(this.fullname(), text);
|
|
653
|
-
return true;
|
|
654
|
-
} catch (e) {
|
|
655
|
-
$.log.error('保存文件失败:', e);
|
|
656
|
-
return false;
|
|
657
|
-
}
|
|
658
|
-
};
|
|
659
|
-
|
|
660
|
-
/**
|
|
661
|
-
* 保存为JSON格式文件
|
|
662
|
-
* @param {object} obj 被保存的内容
|
|
663
|
-
* @param {boolean} format 是否格式化
|
|
664
|
-
* @param {number} space 格式化缩进空格数
|
|
665
|
-
* @returns {boolean} 保存成功返回true, 失败返回false
|
|
666
|
-
*/
|
|
667
|
-
String.prototype.saveJson = function (obj, format = true, space = 2) {
|
|
668
|
-
try {
|
|
669
|
-
var text = '';
|
|
670
|
-
if (format) {
|
|
671
|
-
text = JSON.stringify(obj, null, space);
|
|
672
|
-
} else {
|
|
673
|
-
text = JSON.stringify(obj);
|
|
674
|
-
}
|
|
675
|
-
writeFileSync(this.fullname(), text);
|
|
676
|
-
return true;
|
|
677
|
-
} catch (e) {
|
|
678
|
-
$.log.error('保存JSON文件失败:', e);
|
|
679
|
-
return false;
|
|
680
|
-
}
|
|
681
|
-
};
|
|
682
|
-
|
|
683
|
-
/**
|
|
684
|
-
* 加载文件
|
|
685
|
-
* @param {string} encode 编码方式
|
|
686
|
-
* @returns {string} 加载的文本
|
|
687
|
-
*/
|
|
688
|
-
String.prototype.loadText = function (encode) {
|
|
689
|
-
var file = this.fullname();
|
|
690
|
-
if (existsSync(file)) {
|
|
691
|
-
var enc = encode || 'utf-8';
|
|
692
|
-
return readFileSync(file, enc);
|
|
693
|
-
} else {
|
|
694
|
-
return undefined;
|
|
695
|
-
}
|
|
696
|
-
};
|
|
697
|
-
|
|
698
|
-
/**
|
|
699
|
-
* 加载xml
|
|
700
|
-
* @param {string} file 当前路径
|
|
701
|
-
* @returns {string} 加载的文本
|
|
702
|
-
*/
|
|
703
|
-
String.prototype.loadXml = function (file) {
|
|
704
|
-
var f = this.fullname(file);
|
|
705
|
-
if (existsSync(f)) {
|
|
706
|
-
var text = readFileSync(f, 'utf-8');
|
|
707
|
-
if (text) {
|
|
708
|
-
var ret = text.toXml();
|
|
709
|
-
if (ret === null) {
|
|
710
|
-
$.log.error('xml反序列化错误!', f);
|
|
711
|
-
}
|
|
712
|
-
return ret;
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
return undefined;
|
|
716
|
-
};
|
|
717
|
-
|
|
718
|
-
/**
|
|
719
|
-
* 加载json
|
|
720
|
-
* @param {string} file 当前路径
|
|
721
|
-
* @returns {object} 加载后转换的对象
|
|
722
|
-
*/
|
|
723
|
-
String.prototype.loadJson = function (file) {
|
|
724
|
-
var f = this.fullname(file);
|
|
725
|
-
if (existsSync(f)) {
|
|
726
|
-
var text = readFileSync(f, 'utf-8');
|
|
727
|
-
if (text) {
|
|
728
|
-
var ret = text.toJson();
|
|
729
|
-
if (ret === null) {
|
|
730
|
-
$.log.error('json反序列化错误!', f);
|
|
731
|
-
}
|
|
732
|
-
return ret;
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
return undefined;
|
|
736
|
-
};
|
|
737
|
-
|
|
738
|
-
/**
|
|
739
|
-
* 判断文件是否存在
|
|
740
|
-
* @param {string} file 当前路径
|
|
741
|
-
* @returns {boolean} 存在返回true, 不存在返回false
|
|
742
|
-
*/
|
|
743
|
-
String.prototype.hasFile = function (file) {
|
|
744
|
-
return existsSync(this.fullname(file));
|
|
745
|
-
};
|
|
746
|
-
|
|
747
|
-
/**
|
|
748
|
-
* 删除文件
|
|
749
|
-
* @param {string} file 当前路径
|
|
750
|
-
*/
|
|
751
|
-
String.prototype.delFile = function (file) {
|
|
752
|
-
unlinkSync(this.fullname(file), (e) => { });
|
|
753
|
-
};
|
|
754
|
-
|
|
755
|
-
/**
|
|
756
|
-
* 判断目录是否存在
|
|
757
|
-
* @param {string} dir 当前路径
|
|
758
|
-
* @returns {boolean} 存在返回true, 不存在返回false
|
|
759
|
-
*/
|
|
760
|
-
String.prototype.hasDir = function (dir) {
|
|
761
|
-
return existsSync(this.fullname(dir));
|
|
762
|
-
};
|
|
763
|
-
|
|
764
|
-
/**
|
|
765
|
-
* 删除目录
|
|
766
|
-
* @param {string} dir 当前路径
|
|
767
|
-
* @param {Function} func 回调函数
|
|
768
|
-
*/
|
|
769
|
-
String.prototype.delDir = function (dir, func) {
|
|
770
|
-
rimrafSync(this.fullname(dir), func);
|
|
771
|
-
};
|
|
772
|
-
|
|
773
|
-
/**
|
|
774
|
-
* 创建路径
|
|
775
|
-
* @param {string} dirbase 目录基础路径
|
|
776
|
-
*/
|
|
777
|
-
String.prototype.addDir = function (dirbase) {
|
|
778
|
-
var d = (this + '').fullname(dirbase);
|
|
779
|
-
|
|
780
|
-
if (d.indexOf('.') !== -1) {
|
|
781
|
-
d = d.dirname();
|
|
782
|
-
}
|
|
783
|
-
var l = $.slash;
|
|
784
|
-
var arr = d.split(l);
|
|
785
|
-
var dir = '';
|
|
786
|
-
for (var i = 0; i < arr.length; i++) {
|
|
787
|
-
dir += arr[i] + l;
|
|
788
|
-
if (!existsSync(dir)) {
|
|
789
|
-
mkdirSync(dir);
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
};
|
|
793
|
-
|
|
794
|
-
module.exports = {
|
|
795
|
-
File,
|
|
796
|
-
Dir
|
|
797
|
-
};
|
|
1
|
+
const {
|
|
2
|
+
writeFileSync,
|
|
3
|
+
existsSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
copyFileSync,
|
|
6
|
+
statSync,
|
|
7
|
+
readdirSync,
|
|
8
|
+
mkdirSync,
|
|
9
|
+
unlinkSync,
|
|
10
|
+
promises: fsp
|
|
11
|
+
} = require('fs');
|
|
12
|
+
const {
|
|
13
|
+
join,
|
|
14
|
+
dirname,
|
|
15
|
+
basename,
|
|
16
|
+
extname,
|
|
17
|
+
resolve,
|
|
18
|
+
isAbsolute
|
|
19
|
+
} = require('path');
|
|
20
|
+
const os = require('os');
|
|
21
|
+
const {
|
|
22
|
+
rimrafSync
|
|
23
|
+
} = require('rimraf');
|
|
24
|
+
var ncp = require('ncp').ncp;
|
|
25
|
+
const { Base } = require('./base.js');
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 判断是否为文件
|
|
29
|
+
* @param {string} file_or_dir 文件或目录
|
|
30
|
+
* @returns {boolean} 是文件返回true,否则返回false
|
|
31
|
+
*/
|
|
32
|
+
function isFile(file_or_dir) {
|
|
33
|
+
return statSync(file_or_dir).isDirectory() !== true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 处理根路径
|
|
38
|
+
* @param {string} file 文件路径
|
|
39
|
+
* @returns {string} 完整路径
|
|
40
|
+
*/
|
|
41
|
+
function handleRootPath(file) {
|
|
42
|
+
if ($.run_path.indexOf(file) === 0 || file.indexOf($.run_path) === 0) {
|
|
43
|
+
return file;
|
|
44
|
+
}
|
|
45
|
+
return resolve($.run_path || process.cwd(), file.substring(1));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 处理用户主目录路径
|
|
50
|
+
* @param {string} file 文件路径
|
|
51
|
+
* @returns {string} 完整路径
|
|
52
|
+
*/
|
|
53
|
+
function handleHomePath(file) {
|
|
54
|
+
return resolve(os.homedir(), file.substring(2));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 处理项目根路径
|
|
59
|
+
* @param {string} file 文件路径
|
|
60
|
+
* @returns {string} 完整路径
|
|
61
|
+
*/
|
|
62
|
+
function handleAtPath(file) {
|
|
63
|
+
return resolve($.root_path, file.substring(2));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 处理相对路径
|
|
68
|
+
* @param {string} file 文件路径
|
|
69
|
+
* @param {string} dir 当前目录
|
|
70
|
+
* @returns {string} 完整路径
|
|
71
|
+
*/
|
|
72
|
+
function handleRelativePath(file, dir) {
|
|
73
|
+
var base_dir = dir && typeof dir === 'string' ? dir : $.run_path;
|
|
74
|
+
return resolve(fullname(base_dir), file);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 验证文件路径
|
|
79
|
+
* @param {string} file 文件路径
|
|
80
|
+
* @returns {string | null} 验证结果或null
|
|
81
|
+
*/
|
|
82
|
+
function validateFilePath(file) {
|
|
83
|
+
if (typeof file !== 'string') {
|
|
84
|
+
throw new Error('file参数必须是字符串');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 如果是空字符串,返回当前运行路径
|
|
88
|
+
if (!file) {
|
|
89
|
+
return $.run_path || process.cwd();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 补全文件路径
|
|
97
|
+
* @param {string} file 文件路径
|
|
98
|
+
* @param {string} dir 当前目录
|
|
99
|
+
* @returns {string} 完整路径
|
|
100
|
+
*/
|
|
101
|
+
function fullname(file, dir) {
|
|
102
|
+
var result = validateFilePath(file);
|
|
103
|
+
if (result) {
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 处理以 / 开头的路径(从项目启动位置)
|
|
108
|
+
if (file.startsWith('/') && !file.startsWith('//') && file !== '/') {
|
|
109
|
+
return handleRootPath(file);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 处理以 ~/ 开头的路径(从用户主目录开始)
|
|
113
|
+
if (file.startsWith('~/')) {
|
|
114
|
+
return handleHomePath(file);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 处理以 @/ 开头的路径(从用户主目录开始)
|
|
118
|
+
if (file.startsWith('@/')) {
|
|
119
|
+
return handleAtPath(file);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 处理相对路径
|
|
123
|
+
if (file.startsWith('./') || file.startsWith('../')
|
|
124
|
+
|
|
125
|
+
|| (!isAbsolute(file) && !file.startsWith('/'))) {
|
|
126
|
+
return handleRelativePath(file, dir);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 如果已经是绝对路径,直接返回
|
|
130
|
+
return file;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 文件类函数
|
|
135
|
+
*/
|
|
136
|
+
class File extends Base {
|
|
137
|
+
/**
|
|
138
|
+
* 构造函数,用于初始化文件类
|
|
139
|
+
* @param {object} config 文件配置对象
|
|
140
|
+
*/
|
|
141
|
+
constructor(config) {
|
|
142
|
+
super(config);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 补全文件路径
|
|
148
|
+
* @param {string} file 文件路径
|
|
149
|
+
* @param {string} dir 当前目录
|
|
150
|
+
* @returns {string} 完整路径
|
|
151
|
+
*/
|
|
152
|
+
File.prototype.fullname = fullname;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 搜索目录下所有文件
|
|
156
|
+
* @param {string} dir 目录地址
|
|
157
|
+
* @param {string} keyword 搜索关键词
|
|
158
|
+
* @param {string} keyword_dir 目录搜素关键词
|
|
159
|
+
* @returns {Array} 文件路径数组
|
|
160
|
+
*/
|
|
161
|
+
File.prototype.getAll = function (dir, keyword, keyword_dir) {
|
|
162
|
+
// 文件数组
|
|
163
|
+
var list = [];
|
|
164
|
+
|
|
165
|
+
// 目录数组
|
|
166
|
+
var dirs = [];
|
|
167
|
+
// 获取相关目录
|
|
168
|
+
eachDir(dirs, dir.fullname(), keyword_dir);
|
|
169
|
+
var len = dirs.length;
|
|
170
|
+
for (var i = 0; i < len; i++) {
|
|
171
|
+
getFile(list, dirs[i], keyword);
|
|
172
|
+
}
|
|
173
|
+
return list;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 搜索目录下所有文件(异步并行极速版)
|
|
178
|
+
* @param {string} dir 目录地址
|
|
179
|
+
* @param {string} keyword 搜索关键词
|
|
180
|
+
* @param {string} keyword_dir 目录搜素关键词
|
|
181
|
+
* @returns {Array} 文件路径数组
|
|
182
|
+
*/
|
|
183
|
+
File.prototype.getAllAsync = async function (dir, keyword, keyword_dir) {
|
|
184
|
+
var root_dir = dir.fullname();
|
|
185
|
+
var matched_dirs = [];
|
|
186
|
+
|
|
187
|
+
// 使用独立 eachDirAsync 并行收集匹配的目录
|
|
188
|
+
await eachDirAsync(matched_dirs, root_dir, keyword_dir);
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* 异步获取目录下的匹配文件
|
|
192
|
+
* @param {string} directory 目录路径
|
|
193
|
+
* @returns {Array} 文件路径数组
|
|
194
|
+
*/
|
|
195
|
+
async function getFileAsync(directory) {
|
|
196
|
+
var items;
|
|
197
|
+
try {
|
|
198
|
+
items = await fsp.readdir(directory, { withFileTypes: true });
|
|
199
|
+
} catch {
|
|
200
|
+
return [];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
var files = [];
|
|
204
|
+
for (var i = 0; i < items.length; i++) {
|
|
205
|
+
var item = items[i];
|
|
206
|
+
if (item.isFile() && (!keyword || item.name.has(keyword))) {
|
|
207
|
+
files.push(join(directory, item.name));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return files;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// 没有匹配的子目录时,直接搜索根目录
|
|
214
|
+
if (matched_dirs.length === 0) {
|
|
215
|
+
return await getFileAsync(root_dir);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 并行处理所有匹配目录的文件收集
|
|
219
|
+
var file_lists = await Promise.all(matched_dirs.map(getFileAsync));
|
|
220
|
+
return file_lists.flat();
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* 获取当前目录下所有文件
|
|
225
|
+
* @param {string} dir 目录地址
|
|
226
|
+
* @param {string} keyword 搜索关键词
|
|
227
|
+
* @returns {Array} 文件路径数组
|
|
228
|
+
*/
|
|
229
|
+
File.prototype.get = function (dir, keyword) {
|
|
230
|
+
var list = [];
|
|
231
|
+
getFile(list, dir, keyword);
|
|
232
|
+
return list;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* 加载文件
|
|
237
|
+
* @param {string} file 文件路径
|
|
238
|
+
* @param {string} encode 编码方式
|
|
239
|
+
* @returns {string} 加载的文本
|
|
240
|
+
*/
|
|
241
|
+
File.prototype.load = function (file, encode) {
|
|
242
|
+
if (!file || typeof file !== 'string') {
|
|
243
|
+
throw new Error('file参数必须是非空字符串');
|
|
244
|
+
}
|
|
245
|
+
try {
|
|
246
|
+
if (existsSync(file) && isFile(file)) {
|
|
247
|
+
return readFileSync(file, encode);
|
|
248
|
+
}
|
|
249
|
+
return null;
|
|
250
|
+
} catch (error) {
|
|
251
|
+
this.log('error', '加载文件失败:', error);
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* 保存文件
|
|
258
|
+
* @param {string} file 文件路径
|
|
259
|
+
* @param {string} data 数据内容
|
|
260
|
+
* @param {string} encode 编码方式
|
|
261
|
+
* @returns {boolean} 保存成功返回true,否则返回false
|
|
262
|
+
*/
|
|
263
|
+
File.prototype.save = function (file, data, encode) {
|
|
264
|
+
if (!file || typeof file !== 'string') {
|
|
265
|
+
throw new Error('file参数必须是非空字符串');
|
|
266
|
+
}
|
|
267
|
+
if (data === undefined || data === null) {
|
|
268
|
+
throw new Error('data参数不能为空');
|
|
269
|
+
}
|
|
270
|
+
var enc = encode || 'utf-8';
|
|
271
|
+
try {
|
|
272
|
+
writeFileSync(file, data, enc);
|
|
273
|
+
return true;
|
|
274
|
+
} catch (e) {
|
|
275
|
+
this.log('error', '保存文件失败:', e);
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* 复制文件
|
|
282
|
+
* @param {string} source 文件原路径
|
|
283
|
+
* @param {string} target 目标路径
|
|
284
|
+
* @returns {boolean} 复制成功返回true,否则返回false
|
|
285
|
+
*/
|
|
286
|
+
File.prototype.copy = function (source, target) {
|
|
287
|
+
if (!source || typeof source !== 'string') {
|
|
288
|
+
throw new Error('source参数必须是非空字符串');
|
|
289
|
+
}
|
|
290
|
+
if (!target || typeof target !== 'string') {
|
|
291
|
+
throw new Error('target参数必须是非空字符串');
|
|
292
|
+
}
|
|
293
|
+
try {
|
|
294
|
+
copyFileSync(source.fullname(), target.fullname());
|
|
295
|
+
return true;
|
|
296
|
+
} catch (err) {
|
|
297
|
+
this.log('error', err);
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* 删除文件
|
|
304
|
+
* @param {string} file 文件路径
|
|
305
|
+
* @returns {boolean} 删除成功返回true,否则返回false
|
|
306
|
+
*/
|
|
307
|
+
File.prototype.del = function (file) {
|
|
308
|
+
if (!file || typeof file !== 'string') {
|
|
309
|
+
throw new Error('file参数必须是非空字符串');
|
|
310
|
+
}
|
|
311
|
+
try {
|
|
312
|
+
unlinkSync(file.fullname());
|
|
313
|
+
return true;
|
|
314
|
+
} catch (err) {
|
|
315
|
+
this.log('error', err);
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* 检查文件是否存在
|
|
322
|
+
* @param {string} file 文件路径
|
|
323
|
+
* @returns {boolean} 存在返回true,否则返回false
|
|
324
|
+
*/
|
|
325
|
+
File.prototype.exists = function (file) {
|
|
326
|
+
if (!file || typeof file !== 'string') {
|
|
327
|
+
throw new Error('file参数必须是非空字符串');
|
|
328
|
+
}
|
|
329
|
+
return existsSync(file);
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* 读取文件内容
|
|
334
|
+
* @param {string} file 文件路径
|
|
335
|
+
* @param {string} encode 编码方式
|
|
336
|
+
* @returns {string} 文件内容
|
|
337
|
+
*/
|
|
338
|
+
File.prototype.read = function (file, encode) {
|
|
339
|
+
var enc = encode || 'utf-8';
|
|
340
|
+
return this.load(file, enc);
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* 写入文件内容
|
|
345
|
+
* @param {string} file 文件路径
|
|
346
|
+
* @param {string} data 数据内容
|
|
347
|
+
* @param {string} encode 编码方式
|
|
348
|
+
* @returns {boolean} 写入成功返回true,否则返回false
|
|
349
|
+
*/
|
|
350
|
+
File.prototype.write = function (file, data, encode) {
|
|
351
|
+
if (!data) {
|
|
352
|
+
throw new Error('data参数不能为空');
|
|
353
|
+
}
|
|
354
|
+
return this.save(file, data, encode);
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* 搜索目录下所有目录
|
|
359
|
+
* @param {Array} list 结果路径数组
|
|
360
|
+
* @param {string} dir 目录地址
|
|
361
|
+
* @param {string} keyword 搜索关键词
|
|
362
|
+
*/
|
|
363
|
+
function eachDir(list, dir, keyword) {
|
|
364
|
+
var items = readdirSync(dir);
|
|
365
|
+
var len = items.length;
|
|
366
|
+
|
|
367
|
+
for (var i = 0; i < len; i++) {
|
|
368
|
+
var name = items[i];
|
|
369
|
+
var f = join(dir, name);
|
|
370
|
+
|
|
371
|
+
// 识别是否为目录
|
|
372
|
+
if (!isFile(f)) {
|
|
373
|
+
// 判断是否包含关键词
|
|
374
|
+
if (!keyword || name.has(keyword)) {
|
|
375
|
+
list.push(f);
|
|
376
|
+
}
|
|
377
|
+
eachDir(list, f, keyword);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* 异步并行搜索目录下所有目录
|
|
383
|
+
* @param {Array} list 结果路径数组
|
|
384
|
+
* @param {string} dir 目录地址
|
|
385
|
+
* @param {string} keyword 搜索关键词
|
|
386
|
+
*/
|
|
387
|
+
async function eachDirAsync(list, dir, keyword) {
|
|
388
|
+
var items;
|
|
389
|
+
try {
|
|
390
|
+
items = await fsp.readdir(dir, { withFileTypes: true });
|
|
391
|
+
} catch {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
var subDirs = [];
|
|
396
|
+
for (var i = 0; i < items.length; i++) {
|
|
397
|
+
var item = items[i];
|
|
398
|
+
if (item.isDirectory()) {
|
|
399
|
+
var full_path = join(dir, item.name);
|
|
400
|
+
if (!keyword || item.name.has(keyword)) {
|
|
401
|
+
list.push(full_path);
|
|
402
|
+
}
|
|
403
|
+
subDirs.push(full_path);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// 并行遍历所有子目录
|
|
408
|
+
if (subDirs.length > 0) {
|
|
409
|
+
await Promise.all(subDirs.map((d) => {
|
|
410
|
+
return eachDirAsync(list, d, keyword);
|
|
411
|
+
}));
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* 获取当前目录下所有目录
|
|
417
|
+
* @param {Array} list 结果路径数组
|
|
418
|
+
* @param {string} dir 目录地址
|
|
419
|
+
* @param {string} keyword 搜索关键词
|
|
420
|
+
*/
|
|
421
|
+
function getDir(list, dir, keyword) {
|
|
422
|
+
var items = readdirSync(dir);
|
|
423
|
+
var len = items.length;
|
|
424
|
+
|
|
425
|
+
for (var i = 0; i < len; i++) {
|
|
426
|
+
var name = items[i];
|
|
427
|
+
var f = join(dir, name);
|
|
428
|
+
|
|
429
|
+
// 识别是否为目录
|
|
430
|
+
if (!isFile(f)) {
|
|
431
|
+
// 判断关键词是否相等
|
|
432
|
+
if (!keyword || name.has(keyword)) {
|
|
433
|
+
list.push(f);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* 添加文件路径
|
|
441
|
+
* @param {Array} list 结果路径数组
|
|
442
|
+
* @param {string} dir 目录地址
|
|
443
|
+
* @param {string} keyword 搜索关键词
|
|
444
|
+
*/
|
|
445
|
+
function getFile(list, dir, keyword) {
|
|
446
|
+
var items = readdirSync(dir);
|
|
447
|
+
var len = items.length;
|
|
448
|
+
|
|
449
|
+
for (var i = 0; i < len; i++) {
|
|
450
|
+
var name = items[i];
|
|
451
|
+
var f = join(dir, name);
|
|
452
|
+
|
|
453
|
+
// 识别是否非目录
|
|
454
|
+
if (isFile(f)) {
|
|
455
|
+
// 判断关键词是否相等
|
|
456
|
+
if (!keyword || name.has(keyword)) {
|
|
457
|
+
list.push(f);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* 目录类函数
|
|
464
|
+
*/
|
|
465
|
+
class Dir extends Base {
|
|
466
|
+
/**
|
|
467
|
+
* 构造函数
|
|
468
|
+
* @param {object} config 目录配置对象
|
|
469
|
+
*/
|
|
470
|
+
constructor(config) {
|
|
471
|
+
super(config);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* 补全目录路径
|
|
477
|
+
* @param {string} dir 目录路径
|
|
478
|
+
* @param {string} base_dir 基础目录
|
|
479
|
+
* @returns {string} 完整路径
|
|
480
|
+
*/
|
|
481
|
+
Dir.prototype.fullname = fullname;
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* 搜索目录下所有目录
|
|
485
|
+
* @param {string} dir 目录地址
|
|
486
|
+
* @param {string} keyword 搜索关键词
|
|
487
|
+
* @returns {Array} 目录路径数组
|
|
488
|
+
*/
|
|
489
|
+
Dir.prototype.getAll = function (dir, keyword) {
|
|
490
|
+
var list = [];
|
|
491
|
+
eachDir(list, this.fullname(dir), keyword);
|
|
492
|
+
return list;
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* 搜索目录下所有目录(异步并行极速版)
|
|
497
|
+
* @param {string} dir 目录地址
|
|
498
|
+
* @param {string} keyword 搜索关键词
|
|
499
|
+
* @returns {Array} 目录路径数组
|
|
500
|
+
*/
|
|
501
|
+
Dir.prototype.getAllAsync = async function (dir, keyword) {
|
|
502
|
+
var list = [];
|
|
503
|
+
await eachDirAsync(list, this.fullname(dir), keyword);
|
|
504
|
+
return list;
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* 搜索当前目录下所有目录
|
|
509
|
+
* @param {string} dir 目录地址
|
|
510
|
+
* @param {string} keyword 搜索关键词
|
|
511
|
+
* @returns {Array} 目录路径数组
|
|
512
|
+
*/
|
|
513
|
+
Dir.prototype.get = function (dir, keyword) {
|
|
514
|
+
var list = [];
|
|
515
|
+
getDir(list, this.fullname(dir), keyword);
|
|
516
|
+
return list;
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* 复制目录
|
|
521
|
+
* @param {string} source 源路径
|
|
522
|
+
* @param {string} target 目标路径
|
|
523
|
+
* @param {Function} func 回调函数
|
|
524
|
+
*/
|
|
525
|
+
Dir.prototype.copy = function (source, target, func) {
|
|
526
|
+
ncp(source.fullname(), target.fullname(), func);
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* 删除目录
|
|
531
|
+
* @param {string} dir 目录路径
|
|
532
|
+
* @param {Function} func 回调函数
|
|
533
|
+
*/
|
|
534
|
+
Dir.prototype.del = function (dir, func) {
|
|
535
|
+
rimrafSync(dir.fullname(), func);
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* 判断目录是否存在
|
|
540
|
+
* @param {string} dir 目录路径
|
|
541
|
+
* @returns {boolean} 存在返回true, 否则返回false
|
|
542
|
+
*/
|
|
543
|
+
Dir.prototype.exists = function (dir) {
|
|
544
|
+
return existsSync(dir.fullname());
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
/** === 文件类函数 === */
|
|
548
|
+
/**
|
|
549
|
+
* 补全路径
|
|
550
|
+
* @param {string} dir 文件路径
|
|
551
|
+
* @returns {string} 全路径
|
|
552
|
+
*/
|
|
553
|
+
String.prototype.fullname = function (dir) {
|
|
554
|
+
return fullname(this + '', dir);
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* 获取当前目录下所有文件
|
|
559
|
+
* @param {string} keyword 搜索关键词
|
|
560
|
+
* @returns {Array} 文件路径数组
|
|
561
|
+
*/
|
|
562
|
+
String.prototype.getFile = function (keyword) {
|
|
563
|
+
return $.file.get(this + '', keyword);
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* 搜索当前目录下所有目录
|
|
568
|
+
* @param {string} keyword 搜索关键词
|
|
569
|
+
* @returns {Array} 目录路径数组
|
|
570
|
+
*/
|
|
571
|
+
String.prototype.getDir = function (keyword) {
|
|
572
|
+
return $.dir.get(this + '', keyword);
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* 复制文件
|
|
577
|
+
* @param {string} file 保存路径
|
|
578
|
+
* @returns {boolean} 复制成功返回true, 失败返回false
|
|
579
|
+
*/
|
|
580
|
+
String.prototype.copyFile = function (file) {
|
|
581
|
+
return copyFileSync(this.fullname(), file.fullname());
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* 复制目录
|
|
586
|
+
* @param {string} file 保存路径
|
|
587
|
+
* @param {Function} func 回调函数
|
|
588
|
+
* @returns {boolean} 复制成功返回true, 失败返回false
|
|
589
|
+
*/
|
|
590
|
+
String.prototype.copyDir = function (file, func) {
|
|
591
|
+
return ncp(this.fullname(), file.fullname(), func);
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* 是否目录
|
|
596
|
+
* @returns {boolean} 是目录返回true, 否则返回false
|
|
597
|
+
*/
|
|
598
|
+
String.prototype.isDir = function () {
|
|
599
|
+
var bl = false;
|
|
600
|
+
try {
|
|
601
|
+
bl = statSync(this + '').isDirectory();
|
|
602
|
+
} catch {
|
|
603
|
+
// $.log.error("非路径");
|
|
604
|
+
}
|
|
605
|
+
return bl;
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* 是否文件
|
|
610
|
+
* @returns {boolean} 是目录返回true, 否则返回false
|
|
611
|
+
*/
|
|
612
|
+
String.prototype.isFile = function () {
|
|
613
|
+
var bl = false;
|
|
614
|
+
try {
|
|
615
|
+
bl = statSync(this + '').isFile();
|
|
616
|
+
} catch {
|
|
617
|
+
// $.log.error("非路径");
|
|
618
|
+
}
|
|
619
|
+
return bl;
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* 取路径
|
|
624
|
+
* @returns {string} 字符串
|
|
625
|
+
*/
|
|
626
|
+
String.prototype.dirname = function () {
|
|
627
|
+
return dirname(this + '');
|
|
628
|
+
};
|
|
629
|
+
/**
|
|
630
|
+
* 取基础名
|
|
631
|
+
* @returns {string} 字符串
|
|
632
|
+
*/
|
|
633
|
+
String.prototype.basename = function () {
|
|
634
|
+
return basename(this + '');
|
|
635
|
+
};
|
|
636
|
+
/**
|
|
637
|
+
* 取拓展名
|
|
638
|
+
* @returns {string} 字符串
|
|
639
|
+
*/
|
|
640
|
+
String.prototype.extname = function () {
|
|
641
|
+
return extname(this + '');
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* 保存文件
|
|
647
|
+
* @param {string} text 被保存的文本
|
|
648
|
+
* @returns {boolean} 保存成功返回true, 失败返回false
|
|
649
|
+
*/
|
|
650
|
+
String.prototype.saveText = function (text) {
|
|
651
|
+
try {
|
|
652
|
+
writeFileSync(this.fullname(), text);
|
|
653
|
+
return true;
|
|
654
|
+
} catch (e) {
|
|
655
|
+
$.log.error('保存文件失败:', e);
|
|
656
|
+
return false;
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* 保存为JSON格式文件
|
|
662
|
+
* @param {object} obj 被保存的内容
|
|
663
|
+
* @param {boolean} format 是否格式化
|
|
664
|
+
* @param {number} space 格式化缩进空格数
|
|
665
|
+
* @returns {boolean} 保存成功返回true, 失败返回false
|
|
666
|
+
*/
|
|
667
|
+
String.prototype.saveJson = function (obj, format = true, space = 2) {
|
|
668
|
+
try {
|
|
669
|
+
var text = '';
|
|
670
|
+
if (format) {
|
|
671
|
+
text = JSON.stringify(obj, null, space);
|
|
672
|
+
} else {
|
|
673
|
+
text = JSON.stringify(obj);
|
|
674
|
+
}
|
|
675
|
+
writeFileSync(this.fullname(), text);
|
|
676
|
+
return true;
|
|
677
|
+
} catch (e) {
|
|
678
|
+
$.log.error('保存JSON文件失败:', e);
|
|
679
|
+
return false;
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* 加载文件
|
|
685
|
+
* @param {string} encode 编码方式
|
|
686
|
+
* @returns {string} 加载的文本
|
|
687
|
+
*/
|
|
688
|
+
String.prototype.loadText = function (encode) {
|
|
689
|
+
var file = this.fullname();
|
|
690
|
+
if (existsSync(file)) {
|
|
691
|
+
var enc = encode || 'utf-8';
|
|
692
|
+
return readFileSync(file, enc);
|
|
693
|
+
} else {
|
|
694
|
+
return undefined;
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* 加载xml
|
|
700
|
+
* @param {string} file 当前路径
|
|
701
|
+
* @returns {string} 加载的文本
|
|
702
|
+
*/
|
|
703
|
+
String.prototype.loadXml = function (file) {
|
|
704
|
+
var f = this.fullname(file);
|
|
705
|
+
if (existsSync(f)) {
|
|
706
|
+
var text = readFileSync(f, 'utf-8');
|
|
707
|
+
if (text) {
|
|
708
|
+
var ret = text.toXml();
|
|
709
|
+
if (ret === null) {
|
|
710
|
+
$.log.error('xml反序列化错误!', f);
|
|
711
|
+
}
|
|
712
|
+
return ret;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return undefined;
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* 加载json
|
|
720
|
+
* @param {string} file 当前路径
|
|
721
|
+
* @returns {object} 加载后转换的对象
|
|
722
|
+
*/
|
|
723
|
+
String.prototype.loadJson = function (file) {
|
|
724
|
+
var f = this.fullname(file);
|
|
725
|
+
if (existsSync(f)) {
|
|
726
|
+
var text = readFileSync(f, 'utf-8');
|
|
727
|
+
if (text) {
|
|
728
|
+
var ret = text.toJson();
|
|
729
|
+
if (ret === null) {
|
|
730
|
+
$.log.error('json反序列化错误!', f);
|
|
731
|
+
}
|
|
732
|
+
return ret;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return undefined;
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* 判断文件是否存在
|
|
740
|
+
* @param {string} file 当前路径
|
|
741
|
+
* @returns {boolean} 存在返回true, 不存在返回false
|
|
742
|
+
*/
|
|
743
|
+
String.prototype.hasFile = function (file) {
|
|
744
|
+
return existsSync(this.fullname(file));
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* 删除文件
|
|
749
|
+
* @param {string} file 当前路径
|
|
750
|
+
*/
|
|
751
|
+
String.prototype.delFile = function (file) {
|
|
752
|
+
unlinkSync(this.fullname(file), (e) => { });
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* 判断目录是否存在
|
|
757
|
+
* @param {string} dir 当前路径
|
|
758
|
+
* @returns {boolean} 存在返回true, 不存在返回false
|
|
759
|
+
*/
|
|
760
|
+
String.prototype.hasDir = function (dir) {
|
|
761
|
+
return existsSync(this.fullname(dir));
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* 删除目录
|
|
766
|
+
* @param {string} dir 当前路径
|
|
767
|
+
* @param {Function} func 回调函数
|
|
768
|
+
*/
|
|
769
|
+
String.prototype.delDir = function (dir, func) {
|
|
770
|
+
rimrafSync(this.fullname(dir), func);
|
|
771
|
+
};
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* 创建路径
|
|
775
|
+
* @param {string} dirbase 目录基础路径
|
|
776
|
+
*/
|
|
777
|
+
String.prototype.addDir = function (dirbase) {
|
|
778
|
+
var d = (this + '').fullname(dirbase);
|
|
779
|
+
|
|
780
|
+
if (d.indexOf('.') !== -1) {
|
|
781
|
+
d = d.dirname();
|
|
782
|
+
}
|
|
783
|
+
var l = $.slash;
|
|
784
|
+
var arr = d.split(l);
|
|
785
|
+
var dir = '';
|
|
786
|
+
for (var i = 0; i < arr.length; i++) {
|
|
787
|
+
dir += arr[i] + l;
|
|
788
|
+
if (!existsSync(dir)) {
|
|
789
|
+
mkdirSync(dir);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
module.exports = {
|
|
795
|
+
File,
|
|
796
|
+
Dir
|
|
797
|
+
};
|