@zwa73/dev-utils 1.0.18 → 1.0.20
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/dist/Command/MapPath.js
CHANGED
@@ -12,7 +12,7 @@ const DupMethodList = ["no-rename", "overwrite", "move"];
|
|
12
12
|
const CmdMapPath = (program) => program
|
13
13
|
.command("mappath")
|
14
14
|
.description("根据正则表达式对文件名进行映射")
|
15
|
-
.argument("<regex>", "
|
15
|
+
.argument("<regex>", "要匹配的正则表达式, posix风格路径")
|
16
16
|
.argument("<replacement>", "替换字符串")
|
17
17
|
.option("-e, --exclude <regex>", "排除文件的正则表达式")
|
18
18
|
.option(`-d, --duplicate-handling <${DupMethodList.join('|')}>`, "处理重名文件的方式", "no-rename")
|
@@ -26,10 +26,10 @@ const CmdMapPath = (program) => program
|
|
26
26
|
if (!DupMethodList.includes(options.duplicateHandling))
|
27
27
|
(0, utils_1.throwError)(`${options.duplicateHandling} 不是有效的 duplicate-handling`);
|
28
28
|
const duplicateHandling = options.duplicateHandling;
|
29
|
-
const basePath = process.cwd();
|
29
|
+
const basePath = process.cwd().replaceAll('\\', '/');
|
30
30
|
// 遍历当前目录下的所有文件
|
31
31
|
const filePaths = utils_1.UtilFT.fileSearchRegex(basePath, regex.source, { relative: options.recursive, normalize: "posix" })
|
32
|
-
.map((filePath) => path_1.default.relative(basePath, filePath))
|
32
|
+
.map((filePath) => path_1.default.posix.relative(basePath, filePath))
|
33
33
|
.filter((filePath) => excludeRegex ? (!excludeRegex.test(filePath)) : true);
|
34
34
|
//对单个路径映射
|
35
35
|
const mapPath = async (source, target) => {
|
@@ -51,7 +51,7 @@ const CmdMapPath = (program) => program
|
|
51
51
|
//如果文件已存在
|
52
52
|
if (await utils_1.UtilFT.pathExists(newFilePath)) {
|
53
53
|
await (0, utils_1.matchProc)(duplicateHandling, {
|
54
|
-
'move': () => mapPath(filePath, path_1.default.join(options.duplicateOutput, newFilePath)),
|
54
|
+
'move': () => mapPath(filePath, path_1.default.posix.join(options.duplicateOutput, newFilePath)),
|
55
55
|
'no-rename': () => utils_1.SLogger.info(`重名文件存在,跳过:${newFilePath}`),
|
56
56
|
'overwrite': () => mapPath(filePath, newFilePath),
|
57
57
|
});
|
package/dist/Command/Route.js
CHANGED
@@ -6,11 +6,13 @@ const Init_1 = require("./Init");
|
|
6
6
|
const Node_1 = require("./Node");
|
7
7
|
const Release_1 = require("./Release");
|
8
8
|
const MapPath_1 = require("./MapPath");
|
9
|
+
const ScanDups_1 = require("./ScanDups");
|
9
10
|
async function cliRoute() {
|
10
11
|
(0, Init_1.CmdInit)(commander_1.program);
|
11
12
|
(0, Node_1.CmdNode)(commander_1.program);
|
12
13
|
(0, Release_1.CmdRelease)(commander_1.program);
|
13
14
|
(0, MapPath_1.CmdMapPath)(commander_1.program);
|
15
|
+
(0, ScanDups_1.CmdScanDups)(commander_1.program);
|
14
16
|
commander_1.program.parse(process.argv);
|
15
17
|
}
|
16
18
|
exports.cliRoute = cliRoute;
|
@@ -0,0 +1,49 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.CmdScanDups = void 0;
|
7
|
+
const utils_1 = require("@zwa73/utils");
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
9
|
+
const crypto_1 = __importDefault(require("crypto"));
|
10
|
+
async function calculateHash(filePath) {
|
11
|
+
return new Promise((resolve, reject) => {
|
12
|
+
const hash = crypto_1.default.createHash('md5');
|
13
|
+
const stream = fs_1.default.createReadStream(filePath);
|
14
|
+
stream.on('data', (data) => hash.update(data));
|
15
|
+
stream.on('end', () => resolve(hash.digest('hex')));
|
16
|
+
stream.on('error', reject);
|
17
|
+
});
|
18
|
+
}
|
19
|
+
/**重命名文件或路径 scan_duplicates */
|
20
|
+
const CmdScanDups = (program) => program
|
21
|
+
.command("scandups")
|
22
|
+
.description("扫描当前目录下hash重复的文件")
|
23
|
+
.option("-re, --regex <regex>", "文件的正则表达式, 使用posix路径", ".*")
|
24
|
+
.option("-o, --out <dir|console>", "输出的json文件路径, 默认 scandupsOut.json, 为 \"console\" 时无文件输出", "scandupsOut")
|
25
|
+
.option("-r, --recursive", "是否处理子目录, 默认 true", true)
|
26
|
+
.action(async (options) => {
|
27
|
+
const regex = new RegExp(options.regex);
|
28
|
+
const basePath = process.cwd().replaceAll("\\", "/");
|
29
|
+
const pList = utils_1.UtilFT.fileSearchRegex(basePath, regex.source, {
|
30
|
+
relative: options.recursive,
|
31
|
+
normalize: "posix",
|
32
|
+
}).map(async (filePath) => ({ filePath, hash: await calculateHash(filePath) }));
|
33
|
+
const hashMap = (await Promise.all(pList)).reduce((obj, cur) => {
|
34
|
+
obj[cur.hash] = (obj[cur.hash] ?? []).concat(cur.filePath);
|
35
|
+
return obj;
|
36
|
+
}, {});
|
37
|
+
const out = {};
|
38
|
+
for (const hash in hashMap) {
|
39
|
+
const duplicateFiles = hashMap[hash];
|
40
|
+
if (duplicateFiles.length <= 1)
|
41
|
+
continue;
|
42
|
+
out[hash] = duplicateFiles;
|
43
|
+
}
|
44
|
+
if (options.out === "console")
|
45
|
+
utils_1.SLogger.info(out);
|
46
|
+
else
|
47
|
+
await utils_1.UtilFT.writeJSONFile(options.out, out);
|
48
|
+
});
|
49
|
+
exports.CmdScanDups = CmdScanDups;
|
package/package.json
CHANGED
package/src/Command/MapPath.ts
CHANGED
@@ -10,7 +10,7 @@ type DupMethod = typeof DupMethodList[number];
|
|
10
10
|
export const CmdMapPath = (program: Command) => program
|
11
11
|
.command("mappath")
|
12
12
|
.description("根据正则表达式对文件名进行映射")
|
13
|
-
.argument("<regex>", "
|
13
|
+
.argument("<regex>", "要匹配的正则表达式, posix风格路径")
|
14
14
|
.argument("<replacement>", "替换字符串")
|
15
15
|
.option("-e, --exclude <regex>", "排除文件的正则表达式")
|
16
16
|
.option(`-d, --duplicate-handling <${DupMethodList.join('|')}>`, "处理重名文件的方式", "no-rename")
|
@@ -24,10 +24,10 @@ export const CmdMapPath = (program: Command) => program
|
|
24
24
|
if(!DupMethodList.includes(options.duplicateHandling))
|
25
25
|
throwError(`${options.duplicateHandling} 不是有效的 duplicate-handling`);
|
26
26
|
const duplicateHandling = options.duplicateHandling as DupMethod;
|
27
|
-
const basePath = process.cwd();
|
27
|
+
const basePath = process.cwd().replaceAll('\\','/');
|
28
28
|
// 遍历当前目录下的所有文件
|
29
29
|
const filePaths = UtilFT.fileSearchRegex(basePath, regex.source,{relative:options.recursive,normalize:"posix"})
|
30
|
-
.map((filePath)=>path.relative(basePath, filePath))
|
30
|
+
.map((filePath)=>path.posix.relative(basePath, filePath))
|
31
31
|
.filter((filePath)=>excludeRegex ? (!excludeRegex.test(filePath)) : true);
|
32
32
|
|
33
33
|
//对单个路径映射
|
@@ -49,7 +49,7 @@ export const CmdMapPath = (program: Command) => program
|
|
49
49
|
//如果文件已存在
|
50
50
|
if(await UtilFT.pathExists(newFilePath)){
|
51
51
|
await matchProc(duplicateHandling,{
|
52
|
-
'move': ()=> mapPath(filePath, path.join(options.duplicateOutput, newFilePath)),
|
52
|
+
'move': ()=> mapPath(filePath, path.posix.join(options.duplicateOutput, newFilePath)),
|
53
53
|
'no-rename': ()=> SLogger.info(`重名文件存在,跳过:${newFilePath}`),
|
54
54
|
'overwrite': ()=> mapPath(filePath,newFilePath),
|
55
55
|
})
|
package/src/Command/Route.ts
CHANGED
@@ -4,11 +4,13 @@ import { CmdInit } from './Init';
|
|
4
4
|
import { CmdNode } from './Node';
|
5
5
|
import { CmdRelease } from './Release';
|
6
6
|
import { CmdMapPath } from './MapPath';
|
7
|
+
import { CmdScanDups } from './ScanDups';
|
7
8
|
|
8
9
|
export async function cliRoute(){
|
9
10
|
CmdInit(program);
|
10
11
|
CmdNode(program);
|
11
12
|
CmdRelease(program);
|
12
13
|
CmdMapPath(program);
|
14
|
+
CmdScanDups(program);
|
13
15
|
program.parse(process.argv);
|
14
16
|
}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import { SLogger, UtilFT } from "@zwa73/utils";
|
2
|
+
import { Command } from "commander";
|
3
|
+
import fs from "fs";
|
4
|
+
import crypto from 'crypto';
|
5
|
+
|
6
|
+
async function calculateHash(filePath:string) {
|
7
|
+
return new Promise<string>((resolve, reject) => {
|
8
|
+
const hash = crypto.createHash('md5');
|
9
|
+
const stream = fs.createReadStream(filePath);
|
10
|
+
stream.on('data', (data) => hash.update(data));
|
11
|
+
stream.on('end', () => resolve(hash.digest('hex')));
|
12
|
+
stream.on('error', reject);
|
13
|
+
});
|
14
|
+
}
|
15
|
+
|
16
|
+
|
17
|
+
/**重命名文件或路径 scan_duplicates */
|
18
|
+
export const CmdScanDups = (program: Command) => program
|
19
|
+
.command("scandups")
|
20
|
+
.description("扫描当前目录下hash重复的文件")
|
21
|
+
.option("-re, --regex <regex>", "文件的正则表达式, 使用posix路径",".*")
|
22
|
+
.option("-o, --out <dir|console>", "输出的json文件路径, 默认 scandupsOut.json, 为 \"console\" 时无文件输出", "scandupsOut")
|
23
|
+
.option("-r, --recursive", "是否处理子目录, 默认 true", true)
|
24
|
+
.action(async (options) => {
|
25
|
+
const regex = new RegExp(options.regex);
|
26
|
+
const basePath = process.cwd().replaceAll("\\", "/")
|
27
|
+
const pList = UtilFT.fileSearchRegex(basePath, regex.source, {
|
28
|
+
relative: options.recursive,
|
29
|
+
normalize: "posix",
|
30
|
+
}).map(async (filePath) => ({ filePath, hash: await calculateHash(filePath) }));
|
31
|
+
|
32
|
+
const hashMap = (await Promise.all(pList)).reduce((obj,cur)=>{
|
33
|
+
obj[cur.hash] = (obj[cur.hash]??[]).concat(cur.filePath);
|
34
|
+
return obj;
|
35
|
+
},{} as Record<string,string[]>);
|
36
|
+
const out:Record<string,string[]> = {};
|
37
|
+
for (const hash in hashMap) {
|
38
|
+
const duplicateFiles = hashMap[hash];
|
39
|
+
if(duplicateFiles.length<=1) continue;
|
40
|
+
out[hash] = duplicateFiles;
|
41
|
+
}
|
42
|
+
if(options.out==="console")
|
43
|
+
SLogger.info(out);
|
44
|
+
else
|
45
|
+
await UtilFT.writeJSONFile(options.out,out);
|
46
|
+
});
|