@tq1086/urpf-cli 2.0.0 → 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.
- package/dist/index.js +2763 -426
- package/package.json +16 -2
- package/readme.md +664 -423
package/dist/index.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/pack.ts
|
|
7
|
-
import
|
|
8
|
-
import
|
|
7
|
+
import path3 from "path";
|
|
8
|
+
import fs4 from "fs/promises";
|
|
9
9
|
|
|
10
10
|
// src/core/scanner/file-scanner.ts
|
|
11
11
|
import * as fs from "fs/promises";
|
|
@@ -554,8 +554,8 @@ var IgnoreRuleParser = class {
|
|
|
554
554
|
* @returns 忽略规则列表
|
|
555
555
|
*/
|
|
556
556
|
async parseFileFromPath(filePath) {
|
|
557
|
-
const
|
|
558
|
-
const content = await
|
|
557
|
+
const fs15 = await import("fs/promises");
|
|
558
|
+
const content = await fs15.readFile(filePath, "utf-8");
|
|
559
559
|
return this.parseFile(content, filePath);
|
|
560
560
|
}
|
|
561
561
|
/**
|
|
@@ -582,13 +582,13 @@ var IgnoreRuleParser = class {
|
|
|
582
582
|
* @returns 忽略规则列表
|
|
583
583
|
*/
|
|
584
584
|
async findAndParseIgnoreFiles(rootPath, ignoreFileNames = [".gitignore", ".iflowignore", ".npmignore"]) {
|
|
585
|
-
const
|
|
586
|
-
const
|
|
585
|
+
const fs15 = await import("fs/promises");
|
|
586
|
+
const path8 = await import("path");
|
|
587
587
|
const rules = [];
|
|
588
588
|
for (const fileName of ignoreFileNames) {
|
|
589
|
-
const filePath =
|
|
589
|
+
const filePath = path8.join(rootPath, fileName);
|
|
590
590
|
try {
|
|
591
|
-
await
|
|
591
|
+
await fs15.access(filePath);
|
|
592
592
|
const fileRules = await this.parseFileFromPath(filePath);
|
|
593
593
|
rules.push(...fileRules);
|
|
594
594
|
} catch {
|
|
@@ -602,11 +602,11 @@ var IgnoreRuleParser = class {
|
|
|
602
602
|
* @returns 白名单规则列表,如果文件不存在则返回空数组
|
|
603
603
|
*/
|
|
604
604
|
async parseWhiteList(rootPath) {
|
|
605
|
-
const
|
|
606
|
-
const
|
|
607
|
-
const whiteListPath =
|
|
605
|
+
const fs15 = await import("fs/promises");
|
|
606
|
+
const path8 = await import("path");
|
|
607
|
+
const whiteListPath = path8.join(rootPath, ".urpfallow");
|
|
608
608
|
try {
|
|
609
|
-
await
|
|
609
|
+
await fs15.access(whiteListPath);
|
|
610
610
|
return await this.parseFileFromPath(whiteListPath);
|
|
611
611
|
} catch {
|
|
612
612
|
return [];
|
|
@@ -618,11 +618,11 @@ var IgnoreRuleParser = class {
|
|
|
618
618
|
* @returns 黑名单规则列表,如果文件不存在则返回空数组
|
|
619
619
|
*/
|
|
620
620
|
async parseBlackList(rootPath) {
|
|
621
|
-
const
|
|
622
|
-
const
|
|
623
|
-
const blackListPath =
|
|
621
|
+
const fs15 = await import("fs/promises");
|
|
622
|
+
const path8 = await import("path");
|
|
623
|
+
const blackListPath = path8.join(rootPath, ".urpfforbid");
|
|
624
624
|
try {
|
|
625
|
-
await
|
|
625
|
+
await fs15.access(blackListPath);
|
|
626
626
|
return await this.parseFileFromPath(blackListPath);
|
|
627
627
|
} catch {
|
|
628
628
|
return [];
|
|
@@ -634,11 +634,11 @@ var IgnoreRuleParser = class {
|
|
|
634
634
|
* @returns 是否存在白名单文件
|
|
635
635
|
*/
|
|
636
636
|
async hasWhiteList(rootPath) {
|
|
637
|
-
const
|
|
638
|
-
const
|
|
639
|
-
const whiteListPath =
|
|
637
|
+
const fs15 = await import("fs/promises");
|
|
638
|
+
const path8 = await import("path");
|
|
639
|
+
const whiteListPath = path8.join(rootPath, ".urpfallow");
|
|
640
640
|
try {
|
|
641
|
-
await
|
|
641
|
+
await fs15.access(whiteListPath);
|
|
642
642
|
return true;
|
|
643
643
|
} catch {
|
|
644
644
|
return false;
|
|
@@ -689,14 +689,14 @@ var URPFGenerator = class {
|
|
|
689
689
|
/**
|
|
690
690
|
* 生成属性部分
|
|
691
691
|
*/
|
|
692
|
-
generatePropertySection(
|
|
692
|
+
generatePropertySection(properties2) {
|
|
693
693
|
const newline = this.getNewline();
|
|
694
694
|
const lines = [];
|
|
695
|
-
for (const prop of
|
|
695
|
+
for (const prop of properties2) {
|
|
696
696
|
if (prop.name.includes(" ")) {
|
|
697
|
-
lines.push(
|
|
697
|
+
lines.push(`${prop.name}=${prop.value}`);
|
|
698
698
|
} else {
|
|
699
|
-
lines.push(
|
|
699
|
+
lines.push(`${prop.name}=${prop.value}`);
|
|
700
700
|
}
|
|
701
701
|
}
|
|
702
702
|
return lines.join(newline);
|
|
@@ -705,7 +705,19 @@ var URPFGenerator = class {
|
|
|
705
705
|
* 生成资源头部
|
|
706
706
|
*/
|
|
707
707
|
generateResourceHeader(resource) {
|
|
708
|
-
const
|
|
708
|
+
const udrsRef = resource.header.udrsReference;
|
|
709
|
+
let udrsRefString;
|
|
710
|
+
if (typeof udrsRef === "string") {
|
|
711
|
+
udrsRefString = udrsRef;
|
|
712
|
+
} else if (udrsRef.protocol && udrsRef.uri) {
|
|
713
|
+
udrsRefString = `${udrsRef.protocol}://${udrsRef.uri}`;
|
|
714
|
+
if (udrsRef.fragment) {
|
|
715
|
+
udrsRefString += `#${udrsRef.fragment}`;
|
|
716
|
+
}
|
|
717
|
+
} else {
|
|
718
|
+
throw new Error(`Invalid UDRS reference: ${JSON.stringify(udrsRef)}`);
|
|
719
|
+
}
|
|
720
|
+
const header = `@${udrsRefString} ${resource.header.encoding} ${resource.header.permissions} ${resource.header.newlineType}`;
|
|
709
721
|
return header;
|
|
710
722
|
}
|
|
711
723
|
/**
|
|
@@ -727,9 +739,9 @@ var URPFGenerator = class {
|
|
|
727
739
|
if (this.state.includeBoundaries) {
|
|
728
740
|
parts.push(this.generateStartBoundary());
|
|
729
741
|
}
|
|
730
|
-
const
|
|
731
|
-
if (
|
|
732
|
-
parts.push(this.generatePropertySection(
|
|
742
|
+
const properties2 = "propertySection" in document ? document.propertySection?.properties : "properties" in document ? document.properties : void 0;
|
|
743
|
+
if (properties2 && properties2.length > 0) {
|
|
744
|
+
parts.push(this.generatePropertySection(properties2));
|
|
733
745
|
parts.push(this.generateDelimiter());
|
|
734
746
|
}
|
|
735
747
|
const resources = "resourceSections" in document ? document.resourceSections : "resources" in document ? document.resources : [];
|
|
@@ -745,10 +757,11 @@ var URPFGenerator = class {
|
|
|
745
757
|
}
|
|
746
758
|
const content = parts.join(newline);
|
|
747
759
|
const duration = Date.now() - startTime;
|
|
760
|
+
const resourcesForCount = "resourceSections" in document ? document.resourceSections : "resources" in document ? document.resources : [];
|
|
748
761
|
return {
|
|
749
762
|
content,
|
|
750
763
|
size: Buffer.byteLength(content, "utf-8"),
|
|
751
|
-
resourceCount:
|
|
764
|
+
resourceCount: resourcesForCount.length,
|
|
752
765
|
duration
|
|
753
766
|
};
|
|
754
767
|
}
|
|
@@ -796,12 +809,7 @@ var ResourceSerializer = class {
|
|
|
796
809
|
Buffer.from(buffer.toString("utf-8"), "utf-8");
|
|
797
810
|
return "utf-8";
|
|
798
811
|
} catch {
|
|
799
|
-
|
|
800
|
-
Buffer.from(buffer.toString("gbk"), "gbk");
|
|
801
|
-
return "gbk";
|
|
802
|
-
} catch {
|
|
803
|
-
return "binary";
|
|
804
|
-
}
|
|
812
|
+
return "binary";
|
|
805
813
|
}
|
|
806
814
|
}
|
|
807
815
|
/**
|
|
@@ -837,6 +845,36 @@ var ResourceSerializer = class {
|
|
|
837
845
|
const permissions = mode & 511;
|
|
838
846
|
return permissions.toString(8).padStart(3, "0");
|
|
839
847
|
}
|
|
848
|
+
/**
|
|
849
|
+
* 解析 UDRS 引用字符串为 UDRSReference 对象
|
|
850
|
+
*/
|
|
851
|
+
parseUDRSReference(udrsRefString) {
|
|
852
|
+
const match = udrsRefString.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):\/\/(.*)$/);
|
|
853
|
+
if (!match) {
|
|
854
|
+
throw new Error(`\u65E0\u6548\u7684 UDRS \u5F15\u7528\u683C\u5F0F: ${udrsRefString}`);
|
|
855
|
+
}
|
|
856
|
+
const protocol = match[1];
|
|
857
|
+
const uriAndFragment = match[2];
|
|
858
|
+
const fragmentIndex = uriAndFragment.indexOf("#");
|
|
859
|
+
const uri = fragmentIndex !== -1 ? uriAndFragment.substring(0, fragmentIndex) : uriAndFragment;
|
|
860
|
+
const fragment = fragmentIndex !== -1 ? uriAndFragment.substring(fragmentIndex + 1) : void 0;
|
|
861
|
+
let fragmentType;
|
|
862
|
+
if (fragment) {
|
|
863
|
+
if (fragment.startsWith("::line=")) {
|
|
864
|
+
fragmentType = "raw-line";
|
|
865
|
+
} else if (fragment.startsWith("::byte=")) {
|
|
866
|
+
fragmentType = "raw-byte";
|
|
867
|
+
} else {
|
|
868
|
+
fragmentType = "structured";
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
return {
|
|
872
|
+
protocol,
|
|
873
|
+
uri,
|
|
874
|
+
fragment,
|
|
875
|
+
fragmentType
|
|
876
|
+
};
|
|
877
|
+
}
|
|
840
878
|
/**
|
|
841
879
|
* 读取文件内容
|
|
842
880
|
*/
|
|
@@ -864,8 +902,9 @@ var ResourceSerializer = class {
|
|
|
864
902
|
}
|
|
865
903
|
const content = await this.readFile(filePath, encoding);
|
|
866
904
|
const permissions = this.convertPermissions(metadata.permissions);
|
|
905
|
+
const udrsRef = this.parseUDRSReference(udrsReference);
|
|
867
906
|
const header = {
|
|
868
|
-
udrsReference,
|
|
907
|
+
udrsReference: udrsRef,
|
|
869
908
|
encoding,
|
|
870
909
|
permissions,
|
|
871
910
|
newlineType
|
|
@@ -1175,6 +1214,9 @@ var LineOperations = class {
|
|
|
1175
1214
|
*/
|
|
1176
1215
|
static applyCLILineRange(content, range) {
|
|
1177
1216
|
const normalizedContent = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1217
|
+
if (normalizedContent === "") {
|
|
1218
|
+
throw new Error(`\u5185\u5BB9\u4E3A\u7A7A\uFF0C\u65E0\u6CD5\u63D0\u53D6\u884C`);
|
|
1219
|
+
}
|
|
1178
1220
|
const lines = normalizedContent.split("\n");
|
|
1179
1221
|
const totalLines = lines.length;
|
|
1180
1222
|
const { start, end } = range;
|
|
@@ -1385,184 +1427,1527 @@ var LineOperations = class {
|
|
|
1385
1427
|
}
|
|
1386
1428
|
};
|
|
1387
1429
|
|
|
1388
|
-
// src/
|
|
1389
|
-
import
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1430
|
+
// src/core/engine/key-extractor.ts
|
|
1431
|
+
import * as fs3 from "fs";
|
|
1432
|
+
import * as path2 from "path";
|
|
1433
|
+
import * as yaml from "js-yaml";
|
|
1434
|
+
import * as toml from "toml";
|
|
1435
|
+
import * as xml2js from "xml2js";
|
|
1436
|
+
import * as ini from "ini";
|
|
1437
|
+
var KeyExtractor = class {
|
|
1438
|
+
constructor(options = {}) {
|
|
1439
|
+
this.options = {
|
|
1440
|
+
preserveFormat: options.preserveFormat ?? true,
|
|
1441
|
+
onError: options.onError ?? ((error) => {
|
|
1442
|
+
throw error;
|
|
1443
|
+
})
|
|
1444
|
+
};
|
|
1395
1445
|
}
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1446
|
+
/**
|
|
1447
|
+
* 从文件中提取指定 key 的值
|
|
1448
|
+
*
|
|
1449
|
+
* @param filePath - 文件路径
|
|
1450
|
+
* @param keyPath - key 路径(如 "config.server.port")
|
|
1451
|
+
* @returns 提取结果
|
|
1452
|
+
*/
|
|
1453
|
+
async extractFromFile(filePath, keyPath) {
|
|
1454
|
+
if (!fs3.existsSync(filePath)) {
|
|
1455
|
+
const error = new Error(`File not found: ${filePath}`);
|
|
1456
|
+
this.options.onError(error);
|
|
1457
|
+
throw error;
|
|
1458
|
+
}
|
|
1459
|
+
const format = this.detectFormat(filePath);
|
|
1460
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
1461
|
+
let value;
|
|
1462
|
+
switch (format) {
|
|
1463
|
+
case "json" /* JSON */:
|
|
1464
|
+
value = await this.extractFromJSON(content, keyPath);
|
|
1465
|
+
break;
|
|
1466
|
+
case "yaml" /* YAML */:
|
|
1467
|
+
value = await this.extractFromYAML(content, keyPath);
|
|
1468
|
+
break;
|
|
1469
|
+
case "toml" /* TOML */:
|
|
1470
|
+
value = await this.extractFromTOML(content, keyPath);
|
|
1471
|
+
break;
|
|
1472
|
+
case "xml" /* XML */:
|
|
1473
|
+
value = await this.extractFromXML(content, keyPath);
|
|
1474
|
+
break;
|
|
1475
|
+
case "properties" /* PROPERTIES */:
|
|
1476
|
+
value = await this.extractFromProperties(content, keyPath);
|
|
1477
|
+
break;
|
|
1478
|
+
case "ini" /* INI */:
|
|
1479
|
+
value = await this.extractFromINI(content, keyPath);
|
|
1480
|
+
break;
|
|
1481
|
+
default: {
|
|
1482
|
+
const error = new Error(`Unsupported file format: ${format}`);
|
|
1483
|
+
this.options.onError(error);
|
|
1484
|
+
throw error;
|
|
1485
|
+
}
|
|
1404
1486
|
}
|
|
1405
|
-
|
|
1487
|
+
return {
|
|
1488
|
+
value,
|
|
1489
|
+
type: this.getValueType(value),
|
|
1490
|
+
format,
|
|
1491
|
+
keyPath
|
|
1492
|
+
};
|
|
1406
1493
|
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1494
|
+
/**
|
|
1495
|
+
* 从 JSON 内容中提取指定 key 的值
|
|
1496
|
+
*
|
|
1497
|
+
* @param content - JSON 内容
|
|
1498
|
+
* @param keyPath - key 路径
|
|
1499
|
+
* @returns 提取的值
|
|
1500
|
+
*/
|
|
1501
|
+
async extractFromJSON(content, keyPath) {
|
|
1502
|
+
const data = this.parseJSON(content);
|
|
1503
|
+
return this.extractByPath(data, keyPath);
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* 从 YAML 内容中提取指定 key 的值
|
|
1507
|
+
*
|
|
1508
|
+
* @param content - YAML 内容
|
|
1509
|
+
* @param keyPath - key 路径
|
|
1510
|
+
* @returns 提取的值
|
|
1511
|
+
*/
|
|
1512
|
+
async extractFromYAML(content, keyPath) {
|
|
1513
|
+
const data = this.parseYAML(content);
|
|
1514
|
+
return this.extractByPath(data, keyPath);
|
|
1515
|
+
}
|
|
1516
|
+
/**
|
|
1517
|
+
* 从 TOML 内容中提取指定 key 的值
|
|
1518
|
+
*
|
|
1519
|
+
* @param content - TOML 内容
|
|
1520
|
+
* @param keyPath - key 路径
|
|
1521
|
+
* @returns 提取的值
|
|
1522
|
+
*/
|
|
1523
|
+
async extractFromTOML(content, keyPath) {
|
|
1524
|
+
const data = this.parseTOML(content);
|
|
1525
|
+
return this.extractByPath(data, keyPath);
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* 从 XML 内容中提取指定 key 的值
|
|
1529
|
+
*
|
|
1530
|
+
* @param content - XML 内容
|
|
1531
|
+
* @param keyPath - key 路径
|
|
1532
|
+
* @returns 提取的值
|
|
1533
|
+
*/
|
|
1534
|
+
async extractFromXML(content, keyPath) {
|
|
1535
|
+
const data = await this.parseXML(content);
|
|
1536
|
+
return this.extractByPath(data, keyPath);
|
|
1537
|
+
}
|
|
1538
|
+
/**
|
|
1539
|
+
* 从 Properties 内容中提取指定 key 的值
|
|
1540
|
+
*
|
|
1541
|
+
* @param content - Properties 内容
|
|
1542
|
+
* @param keyPath - key 路径
|
|
1543
|
+
* @returns 提取的值
|
|
1544
|
+
*/
|
|
1545
|
+
async extractFromProperties(content, keyPath) {
|
|
1546
|
+
const data = this.parseProperties(content);
|
|
1547
|
+
return this.extractByPath(data, keyPath);
|
|
1548
|
+
}
|
|
1549
|
+
/**
|
|
1550
|
+
* 从 INI 内容中提取指定 key 的值
|
|
1551
|
+
*
|
|
1552
|
+
* @param content - INI 内容
|
|
1553
|
+
* @param keyPath - key 路径
|
|
1554
|
+
* @returns 提取的值
|
|
1555
|
+
*/
|
|
1556
|
+
async extractFromINI(content, keyPath) {
|
|
1557
|
+
const data = this.parseINI(content);
|
|
1558
|
+
return this.extractByPath(data, keyPath);
|
|
1559
|
+
}
|
|
1560
|
+
/**
|
|
1561
|
+
* 解析 JSON 内容
|
|
1562
|
+
*
|
|
1563
|
+
* @param content - JSON 内容
|
|
1564
|
+
* @returns 解析后的对象
|
|
1565
|
+
*/
|
|
1566
|
+
parseJSON(content) {
|
|
1409
1567
|
try {
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1568
|
+
return JSON.parse(content);
|
|
1569
|
+
} catch (error) {
|
|
1570
|
+
const parsedError = new Error(`Failed to parse JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
1571
|
+
this.options.onError(parsedError);
|
|
1572
|
+
throw parsedError;
|
|
1415
1573
|
}
|
|
1416
|
-
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* 解析 YAML 内容
|
|
1577
|
+
*
|
|
1578
|
+
* @param content - YAML 内容
|
|
1579
|
+
* @returns 解析后的对象
|
|
1580
|
+
*/
|
|
1581
|
+
parseYAML(content) {
|
|
1417
1582
|
try {
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1583
|
+
return yaml.load(content);
|
|
1584
|
+
} catch (error) {
|
|
1585
|
+
const parsedError = new Error(`Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
|
|
1586
|
+
this.options.onError(parsedError);
|
|
1587
|
+
throw parsedError;
|
|
1423
1588
|
}
|
|
1424
1589
|
}
|
|
1425
|
-
return null;
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
// src/commands/pack.ts
|
|
1429
|
-
var PackCommand = class {
|
|
1430
1590
|
/**
|
|
1431
|
-
*
|
|
1591
|
+
* 解析 TOML 内容
|
|
1592
|
+
*
|
|
1593
|
+
* @param content - TOML 内容
|
|
1594
|
+
* @returns 解析后的对象
|
|
1432
1595
|
*/
|
|
1433
|
-
|
|
1434
|
-
const startTime = Date.now();
|
|
1596
|
+
parseTOML(content) {
|
|
1435
1597
|
try {
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
const urpfDocument = await this.generateURPFDocument(scanResult, resolvedPath, options);
|
|
1442
|
-
const urpfResult = await this.saveURPFDocument(urpfDocument, outputPath, options);
|
|
1443
|
-
const duration = Date.now() - startTime;
|
|
1444
|
-
await this.handleOutputOptions(urpfResult.content, outputPath, options);
|
|
1445
|
-
if (options.verbose) {
|
|
1446
|
-
this.logVerbose(scanResult, urpfResult, outputPath, duration);
|
|
1447
|
-
}
|
|
1448
|
-
return {
|
|
1449
|
-
success: true,
|
|
1450
|
-
outputPath,
|
|
1451
|
-
fileCount: scanResult.files.length,
|
|
1452
|
-
directoryCount: scanResult.directories.length,
|
|
1453
|
-
skippedCount: scanResult.skippedFiles.length,
|
|
1454
|
-
urpfSize: urpfResult.size,
|
|
1455
|
-
duration
|
|
1456
|
-
};
|
|
1457
|
-
} catch (err) {
|
|
1458
|
-
const duration = Date.now() - startTime;
|
|
1459
|
-
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1460
|
-
if (options.verbose) {
|
|
1461
|
-
console.error(`\u9519\u8BEF: ${errorMessage}`);
|
|
1462
|
-
}
|
|
1463
|
-
return {
|
|
1464
|
-
success: false,
|
|
1465
|
-
outputPath: "",
|
|
1466
|
-
fileCount: 0,
|
|
1467
|
-
directoryCount: 0,
|
|
1468
|
-
skippedCount: 0,
|
|
1469
|
-
urpfSize: 0,
|
|
1470
|
-
duration,
|
|
1471
|
-
error: errorMessage
|
|
1472
|
-
};
|
|
1598
|
+
return toml.parse(content);
|
|
1599
|
+
} catch (error) {
|
|
1600
|
+
const parsedError = new Error(`Failed to parse TOML: ${error instanceof Error ? error.message : String(error)}`);
|
|
1601
|
+
this.options.onError(parsedError);
|
|
1602
|
+
throw parsedError;
|
|
1473
1603
|
}
|
|
1474
1604
|
}
|
|
1475
1605
|
/**
|
|
1476
|
-
*
|
|
1606
|
+
* 解析 XML 内容
|
|
1607
|
+
*
|
|
1608
|
+
* @param content - XML 内容
|
|
1609
|
+
* @returns 解析后的对象
|
|
1477
1610
|
*/
|
|
1478
|
-
async
|
|
1611
|
+
async parseXML(content) {
|
|
1479
1612
|
try {
|
|
1480
|
-
const
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
throw
|
|
1613
|
+
const parser = new xml2js.Parser();
|
|
1614
|
+
return await parser.parseStringPromise(content);
|
|
1615
|
+
} catch (error) {
|
|
1616
|
+
const parsedError = new Error(`Failed to parse XML: ${error instanceof Error ? error.message : String(error)}`);
|
|
1617
|
+
this.options.onError(parsedError);
|
|
1618
|
+
throw parsedError;
|
|
1486
1619
|
}
|
|
1487
1620
|
}
|
|
1488
1621
|
/**
|
|
1489
|
-
*
|
|
1622
|
+
* 解析 Properties 内容
|
|
1623
|
+
*
|
|
1624
|
+
* @param content - Properties 内容
|
|
1625
|
+
* @returns 解析后的对象(包含 section)
|
|
1490
1626
|
*/
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1627
|
+
parseProperties(content) {
|
|
1628
|
+
try {
|
|
1629
|
+
const result = {};
|
|
1630
|
+
const lines = content.split("\n");
|
|
1631
|
+
for (const line of lines) {
|
|
1632
|
+
const trimmedLine = line.trim();
|
|
1633
|
+
if (!trimmedLine || trimmedLine.startsWith("#") || trimmedLine.startsWith("!")) {
|
|
1634
|
+
continue;
|
|
1635
|
+
}
|
|
1636
|
+
const processedLine = trimmedLine.replace(/\\:/g, ":").replace(/\\=/g, "=").replace(/\\#/g, "#").replace(/\\!/g, "!");
|
|
1637
|
+
let separatorIndex = -1;
|
|
1638
|
+
for (let i = 0; i < processedLine.length; i++) {
|
|
1639
|
+
if (processedLine[i] === "=" || processedLine[i] === ":") {
|
|
1640
|
+
separatorIndex = i;
|
|
1641
|
+
break;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
if (separatorIndex === -1) {
|
|
1645
|
+
result[processedLine] = "";
|
|
1646
|
+
continue;
|
|
1647
|
+
}
|
|
1648
|
+
const key = processedLine.substring(0, separatorIndex).trim();
|
|
1649
|
+
let value = processedLine.substring(separatorIndex + 1).trim();
|
|
1650
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1651
|
+
value = value.substring(1, value.length - 1);
|
|
1652
|
+
}
|
|
1653
|
+
result[key] = value;
|
|
1654
|
+
}
|
|
1655
|
+
const nestedResult = {};
|
|
1656
|
+
for (const [key, value] of Object.entries(result)) {
|
|
1657
|
+
const parts = key.split(".");
|
|
1658
|
+
if (parts.length > 1) {
|
|
1659
|
+
const section = parts[0];
|
|
1660
|
+
const sectionKey = parts.slice(1).join(".");
|
|
1661
|
+
if (!(nestedResult[section] && typeof nestedResult[section] === "object")) {
|
|
1662
|
+
nestedResult[section] = {};
|
|
1663
|
+
}
|
|
1664
|
+
const sectionObj = nestedResult[section];
|
|
1665
|
+
sectionObj[sectionKey] = value;
|
|
1666
|
+
} else {
|
|
1667
|
+
nestedResult[key] = value;
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
return nestedResult;
|
|
1671
|
+
} catch (error) {
|
|
1672
|
+
const parsedError = new Error(`Failed to parse Properties: ${error instanceof Error ? error.message : String(error)}`);
|
|
1673
|
+
this.options.onError(parsedError);
|
|
1674
|
+
throw parsedError;
|
|
1500
1675
|
}
|
|
1501
1676
|
}
|
|
1502
1677
|
/**
|
|
1503
|
-
*
|
|
1678
|
+
* 解析 INI 内容
|
|
1679
|
+
*
|
|
1680
|
+
* @param content - INI 内容
|
|
1681
|
+
* @returns 解析后的对象(包含 section)
|
|
1504
1682
|
*/
|
|
1505
|
-
|
|
1506
|
-
|
|
1683
|
+
parseINI(content) {
|
|
1684
|
+
try {
|
|
1685
|
+
return ini.parse(content);
|
|
1686
|
+
} catch (error) {
|
|
1687
|
+
const parsedError = new Error(`Failed to parse INI: ${error instanceof Error ? error.message : String(error)}`);
|
|
1688
|
+
this.options.onError(parsedError);
|
|
1689
|
+
throw parsedError;
|
|
1690
|
+
}
|
|
1507
1691
|
}
|
|
1508
1692
|
/**
|
|
1509
|
-
*
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1693
|
+
* 根据路径从对象中提取值
|
|
1694
|
+
*
|
|
1695
|
+
* @param data - 数据对象
|
|
1696
|
+
* @param keyPath - key 路径
|
|
1697
|
+
* @returns 提取的值
|
|
1698
|
+
*/
|
|
1699
|
+
extractByPath(data, keyPath) {
|
|
1700
|
+
const pathSegments = this.parseKeyPath(keyPath);
|
|
1701
|
+
let current = data;
|
|
1702
|
+
for (const segment of pathSegments) {
|
|
1703
|
+
if (current == null) {
|
|
1704
|
+
const error = new Error(`Key path "${keyPath}" not found: cannot access property "${segment}" of ${current}`);
|
|
1705
|
+
this.options.onError(error);
|
|
1706
|
+
throw error;
|
|
1707
|
+
}
|
|
1708
|
+
if (typeof segment === "number") {
|
|
1709
|
+
if (Array.isArray(current)) {
|
|
1710
|
+
if (segment >= current.length) {
|
|
1711
|
+
const error = new Error(`Array index ${segment} out of bounds (length: ${current.length})`);
|
|
1712
|
+
this.options.onError(error);
|
|
1713
|
+
throw error;
|
|
1714
|
+
}
|
|
1715
|
+
current = current[segment];
|
|
1716
|
+
} else {
|
|
1717
|
+
const error = new Error(`Cannot access array index ${segment} on non-array value`);
|
|
1718
|
+
this.options.onError(error);
|
|
1719
|
+
throw error;
|
|
1523
1720
|
}
|
|
1524
|
-
}
|
|
1721
|
+
} else {
|
|
1722
|
+
if (typeof current === "object" && current !== null) {
|
|
1723
|
+
if (!(segment in current)) {
|
|
1724
|
+
const error = new Error(`Key "${segment}" not found in path "${keyPath}"`);
|
|
1725
|
+
this.options.onError(error);
|
|
1726
|
+
throw error;
|
|
1727
|
+
}
|
|
1728
|
+
current = current[segment];
|
|
1729
|
+
} else {
|
|
1730
|
+
const error = new Error(`Cannot access property "${segment}" on non-object value`);
|
|
1731
|
+
this.options.onError(error);
|
|
1732
|
+
throw error;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1525
1735
|
}
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1736
|
+
return current;
|
|
1737
|
+
}
|
|
1738
|
+
/**
|
|
1739
|
+
* 解析 key 路径,支持点分隔符和数组索引
|
|
1740
|
+
*
|
|
1741
|
+
* @param keyPath - key 路径(如 "config.server.port" 或 "items[0].name")
|
|
1742
|
+
* @returns 解析后的路径段数组
|
|
1743
|
+
*/
|
|
1744
|
+
parseKeyPath(keyPath) {
|
|
1745
|
+
const segments = [];
|
|
1746
|
+
let current = "";
|
|
1747
|
+
let inBrackets = false;
|
|
1748
|
+
let bracketContent = "";
|
|
1749
|
+
for (let i = 0; i < keyPath.length; i++) {
|
|
1750
|
+
const char = keyPath[i];
|
|
1751
|
+
if (char === "[") {
|
|
1752
|
+
if (inBrackets) {
|
|
1753
|
+
const error = new Error(`Invalid key path: nested brackets at position ${i}`);
|
|
1754
|
+
this.options.onError(error);
|
|
1755
|
+
throw error;
|
|
1756
|
+
}
|
|
1757
|
+
if (current.trim()) {
|
|
1758
|
+
segments.push(current.trim());
|
|
1759
|
+
}
|
|
1760
|
+
inBrackets = true;
|
|
1761
|
+
bracketContent = "";
|
|
1762
|
+
current = "";
|
|
1763
|
+
} else if (char === "]") {
|
|
1764
|
+
if (!inBrackets) {
|
|
1765
|
+
const error = new Error(`Invalid key path: closing bracket without opening bracket at position ${i}`);
|
|
1766
|
+
this.options.onError(error);
|
|
1767
|
+
throw error;
|
|
1768
|
+
}
|
|
1769
|
+
inBrackets = false;
|
|
1770
|
+
const index = parseInt(bracketContent.trim(), 10);
|
|
1771
|
+
if (isNaN(index)) {
|
|
1772
|
+
const error = new Error(`Invalid array index: "${bracketContent}" is not a number`);
|
|
1773
|
+
this.options.onError(error);
|
|
1774
|
+
throw error;
|
|
1775
|
+
}
|
|
1776
|
+
segments.push(index);
|
|
1777
|
+
current = "";
|
|
1778
|
+
} else if (char === "." && !inBrackets) {
|
|
1779
|
+
if (current.trim()) {
|
|
1780
|
+
const trimmed = current.trim();
|
|
1781
|
+
const numIndex = parseInt(trimmed, 10);
|
|
1782
|
+
if (!isNaN(numIndex) && trimmed === numIndex.toString()) {
|
|
1783
|
+
segments.push(numIndex);
|
|
1784
|
+
} else {
|
|
1785
|
+
segments.push(trimmed);
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
current = "";
|
|
1789
|
+
} else {
|
|
1790
|
+
if (inBrackets) {
|
|
1791
|
+
bracketContent += char;
|
|
1792
|
+
} else {
|
|
1793
|
+
current += char;
|
|
1542
1794
|
}
|
|
1543
1795
|
}
|
|
1544
1796
|
}
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1797
|
+
if (inBrackets) {
|
|
1798
|
+
const error = new Error("Invalid key path: unclosed bracket");
|
|
1799
|
+
this.options.onError(error);
|
|
1800
|
+
throw error;
|
|
1801
|
+
}
|
|
1802
|
+
if (current.trim()) {
|
|
1803
|
+
const trimmed = current.trim();
|
|
1804
|
+
const numIndex = parseInt(trimmed, 10);
|
|
1805
|
+
if (!isNaN(numIndex) && trimmed === numIndex.toString()) {
|
|
1806
|
+
segments.push(numIndex);
|
|
1807
|
+
} else {
|
|
1808
|
+
segments.push(trimmed);
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
return segments;
|
|
1552
1812
|
}
|
|
1553
1813
|
/**
|
|
1554
|
-
*
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1814
|
+
* 根据文件扩展名检测文件格式
|
|
1815
|
+
*
|
|
1816
|
+
* @param filePath - 文件路径
|
|
1817
|
+
* @returns 文件格式
|
|
1818
|
+
*/
|
|
1819
|
+
detectFormat(filePath) {
|
|
1820
|
+
const ext = path2.extname(filePath).toLowerCase();
|
|
1821
|
+
switch (ext) {
|
|
1822
|
+
case ".json":
|
|
1823
|
+
return "json" /* JSON */;
|
|
1824
|
+
case ".yaml":
|
|
1825
|
+
case ".yml":
|
|
1826
|
+
return "yaml" /* YAML */;
|
|
1827
|
+
case ".toml":
|
|
1828
|
+
return "toml" /* TOML */;
|
|
1829
|
+
case ".xml":
|
|
1830
|
+
return "xml" /* XML */;
|
|
1831
|
+
case ".properties":
|
|
1832
|
+
return "properties" /* PROPERTIES */;
|
|
1833
|
+
case ".ini":
|
|
1834
|
+
return "ini" /* INI */;
|
|
1835
|
+
default: {
|
|
1836
|
+
const error = new Error(`Unsupported file extension: ${ext}`);
|
|
1837
|
+
this.options.onError(error);
|
|
1838
|
+
throw error;
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
/**
|
|
1843
|
+
* 获取值的类型
|
|
1844
|
+
*
|
|
1845
|
+
* @param value - 值
|
|
1846
|
+
* @returns 类型字符串
|
|
1847
|
+
*/
|
|
1848
|
+
getValueType(value) {
|
|
1849
|
+
if (value === null) return "null";
|
|
1850
|
+
if (Array.isArray(value)) return "array";
|
|
1851
|
+
return typeof value;
|
|
1852
|
+
}
|
|
1853
|
+
};
|
|
1854
|
+
|
|
1855
|
+
// src/core/engine/wildcard-matcher.ts
|
|
1856
|
+
var WildcardMatcher = class {
|
|
1857
|
+
constructor(options = {}) {
|
|
1858
|
+
this.options = {
|
|
1859
|
+
caseSensitive: options.caseSensitive ?? false
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
/**
|
|
1863
|
+
* 检查单个 key 是否匹配通配符模式
|
|
1864
|
+
*
|
|
1865
|
+
* @param pattern - 通配符模式(如 "config.*.port" 或 "data.**.value")
|
|
1866
|
+
* @param key - 要检查的 key 路径
|
|
1867
|
+
* @returns 是否匹配
|
|
1868
|
+
*/
|
|
1869
|
+
matches(pattern, key) {
|
|
1870
|
+
const patternSegments = this.splitPattern(pattern);
|
|
1871
|
+
const keySegments = this.splitKeyPath(key);
|
|
1872
|
+
return this.matchSegments(patternSegments, keySegments, 0, 0);
|
|
1873
|
+
}
|
|
1874
|
+
/**
|
|
1875
|
+
* 分割通配符模式为段数组
|
|
1876
|
+
*/
|
|
1877
|
+
splitPattern(pattern) {
|
|
1878
|
+
const segments = [];
|
|
1879
|
+
let current = "";
|
|
1880
|
+
let i = 0;
|
|
1881
|
+
let inBrackets = false;
|
|
1882
|
+
while (i < pattern.length) {
|
|
1883
|
+
const char = pattern[i];
|
|
1884
|
+
if (char === "[") {
|
|
1885
|
+
if (current) {
|
|
1886
|
+
segments.push(current);
|
|
1887
|
+
current = "";
|
|
1888
|
+
}
|
|
1889
|
+
inBrackets = true;
|
|
1890
|
+
current = "[";
|
|
1891
|
+
i += 1;
|
|
1892
|
+
} else if (char === "]") {
|
|
1893
|
+
current += char;
|
|
1894
|
+
inBrackets = false;
|
|
1895
|
+
segments.push(current);
|
|
1896
|
+
current = "";
|
|
1897
|
+
i += 1;
|
|
1898
|
+
} else if (char === "*" && pattern[i + 1] === "*" && !inBrackets) {
|
|
1899
|
+
if (current) {
|
|
1900
|
+
segments.push(current);
|
|
1901
|
+
current = "";
|
|
1902
|
+
}
|
|
1903
|
+
segments.push("**");
|
|
1904
|
+
i += 2;
|
|
1905
|
+
} else if (char === "*" && !inBrackets) {
|
|
1906
|
+
current += char;
|
|
1907
|
+
i += 1;
|
|
1908
|
+
} else if (char === "." && !inBrackets) {
|
|
1909
|
+
if (current) {
|
|
1910
|
+
segments.push(current);
|
|
1911
|
+
current = "";
|
|
1912
|
+
}
|
|
1913
|
+
i += 1;
|
|
1914
|
+
} else {
|
|
1915
|
+
current += char;
|
|
1916
|
+
i += 1;
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
if (current) {
|
|
1920
|
+
segments.push(current);
|
|
1921
|
+
}
|
|
1922
|
+
return segments;
|
|
1923
|
+
}
|
|
1924
|
+
/**
|
|
1925
|
+
* 分割 key 路径为段数组
|
|
1926
|
+
*/
|
|
1927
|
+
splitKeyPath(key) {
|
|
1928
|
+
const segments = [];
|
|
1929
|
+
let current = "";
|
|
1930
|
+
let inBrackets = false;
|
|
1931
|
+
for (let i = 0; i < key.length; i++) {
|
|
1932
|
+
const char = key[i];
|
|
1933
|
+
if (char === "[") {
|
|
1934
|
+
if (current) {
|
|
1935
|
+
segments.push(current);
|
|
1936
|
+
current = "";
|
|
1937
|
+
}
|
|
1938
|
+
inBrackets = true;
|
|
1939
|
+
current = "[";
|
|
1940
|
+
} else if (char === "]") {
|
|
1941
|
+
current += char;
|
|
1942
|
+
inBrackets = false;
|
|
1943
|
+
segments.push(current);
|
|
1944
|
+
current = "";
|
|
1945
|
+
} else if (char === "." && !inBrackets) {
|
|
1946
|
+
if (current) {
|
|
1947
|
+
segments.push(current);
|
|
1948
|
+
current = "";
|
|
1949
|
+
}
|
|
1950
|
+
} else {
|
|
1951
|
+
current += char;
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
if (current) {
|
|
1955
|
+
segments.push(current);
|
|
1956
|
+
}
|
|
1957
|
+
return segments;
|
|
1958
|
+
}
|
|
1959
|
+
/**
|
|
1960
|
+
* 递归匹配段
|
|
1961
|
+
*/
|
|
1962
|
+
matchSegments(patternSegments, keySegments, patternIndex, keyIndex) {
|
|
1963
|
+
if (patternIndex >= patternSegments.length && keyIndex >= keySegments.length) {
|
|
1964
|
+
return true;
|
|
1965
|
+
}
|
|
1966
|
+
if (patternIndex >= patternSegments.length) {
|
|
1967
|
+
return false;
|
|
1968
|
+
}
|
|
1969
|
+
const patternSegment = patternSegments[patternIndex];
|
|
1970
|
+
if (patternSegment === "**") {
|
|
1971
|
+
if (this.matchSegments(patternSegments, keySegments, patternIndex + 1, keyIndex)) {
|
|
1972
|
+
return true;
|
|
1973
|
+
}
|
|
1974
|
+
if (keyIndex < keySegments.length) {
|
|
1975
|
+
if (this.matchSegments(patternSegments, keySegments, patternIndex, keyIndex + 1)) {
|
|
1976
|
+
return true;
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
return false;
|
|
1980
|
+
}
|
|
1981
|
+
if (keyIndex >= keySegments.length) {
|
|
1982
|
+
for (let i = patternIndex; i < patternSegments.length; i++) {
|
|
1983
|
+
if (patternSegments[i] !== "**") {
|
|
1984
|
+
return false;
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
return true;
|
|
1988
|
+
}
|
|
1989
|
+
const keySegment = keySegments[keyIndex];
|
|
1990
|
+
if (patternSegment === "*") {
|
|
1991
|
+
return this.matchSegments(patternSegments, keySegments, patternIndex + 1, keyIndex + 1);
|
|
1992
|
+
}
|
|
1993
|
+
if (this.matchPatternSegment(patternSegment, keySegment)) {
|
|
1994
|
+
return this.matchSegments(patternSegments, keySegments, patternIndex + 1, keyIndex + 1);
|
|
1995
|
+
}
|
|
1996
|
+
return false;
|
|
1997
|
+
}
|
|
1998
|
+
/**
|
|
1999
|
+
* 匹配单个模式段和 key 段
|
|
2000
|
+
*/
|
|
2001
|
+
matchPatternSegment(patternSegment, keySegment) {
|
|
2002
|
+
if (patternSegment.includes("*") || patternSegment.includes("?")) {
|
|
2003
|
+
return this.matchWildcardPattern(patternSegment, keySegment);
|
|
2004
|
+
}
|
|
2005
|
+
if (this.options.caseSensitive) {
|
|
2006
|
+
return patternSegment === keySegment;
|
|
2007
|
+
} else {
|
|
2008
|
+
return patternSegment.toLowerCase() === keySegment.toLowerCase();
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
/**
|
|
2012
|
+
* 匹配通配符模式(不包含 globstar)
|
|
2013
|
+
*/
|
|
2014
|
+
matchWildcardPattern(pattern, text) {
|
|
2015
|
+
const pLen = pattern.length;
|
|
2016
|
+
const tLen = text.length;
|
|
2017
|
+
let pIndex = 0;
|
|
2018
|
+
let tIndex = 0;
|
|
2019
|
+
let starIndex = -1;
|
|
2020
|
+
let matchIndex = -1;
|
|
2021
|
+
while (tIndex < tLen) {
|
|
2022
|
+
if (pIndex < pLen && pattern[pIndex] === "*") {
|
|
2023
|
+
starIndex = pIndex;
|
|
2024
|
+
matchIndex = tIndex;
|
|
2025
|
+
pIndex++;
|
|
2026
|
+
} else if (pIndex < pLen && this.matchChar(pattern[pIndex], text[tIndex])) {
|
|
2027
|
+
pIndex++;
|
|
2028
|
+
tIndex++;
|
|
2029
|
+
} else if (starIndex !== -1) {
|
|
2030
|
+
pIndex = starIndex + 1;
|
|
2031
|
+
matchIndex++;
|
|
2032
|
+
tIndex = matchIndex;
|
|
2033
|
+
} else {
|
|
2034
|
+
return false;
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
while (pIndex < pLen && pattern[pIndex] === "*") {
|
|
2038
|
+
pIndex++;
|
|
2039
|
+
}
|
|
2040
|
+
return pIndex === pLen;
|
|
2041
|
+
}
|
|
2042
|
+
/**
|
|
2043
|
+
* 匹配单个字符
|
|
2044
|
+
*/
|
|
2045
|
+
matchChar(patternChar, textChar) {
|
|
2046
|
+
if (patternChar === "?") {
|
|
2047
|
+
return true;
|
|
2048
|
+
}
|
|
2049
|
+
if (this.options.caseSensitive) {
|
|
2050
|
+
return patternChar === textChar;
|
|
2051
|
+
} else {
|
|
2052
|
+
return patternChar.toLowerCase() === textChar.toLowerCase();
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
/**
|
|
2056
|
+
* 从 key 列表中找出所有匹配通配符模式的 key
|
|
2057
|
+
*
|
|
2058
|
+
* @param pattern - 通配符模式
|
|
2059
|
+
* @param keys - key 列表
|
|
2060
|
+
* @returns 匹配结果
|
|
2061
|
+
*/
|
|
2062
|
+
matchKeys(pattern, keys) {
|
|
2063
|
+
const matches = [];
|
|
2064
|
+
for (const key of keys) {
|
|
2065
|
+
if (this.matches(pattern, key)) {
|
|
2066
|
+
matches.push(key);
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
return {
|
|
2070
|
+
matched: matches.length > 0,
|
|
2071
|
+
matches,
|
|
2072
|
+
stats: {
|
|
2073
|
+
total: keys.length,
|
|
2074
|
+
matched: matches.length
|
|
2075
|
+
}
|
|
2076
|
+
};
|
|
2077
|
+
}
|
|
2078
|
+
/**
|
|
2079
|
+
* 从对象中提取所有匹配通配符模式的 key 路径
|
|
2080
|
+
*
|
|
2081
|
+
* @param pattern - 通配符模式
|
|
2082
|
+
* @param obj - 要搜索的对象
|
|
2083
|
+
* @param prefix - 当前路径前缀(内部使用)
|
|
2084
|
+
* @returns 匹配结果
|
|
2085
|
+
*/
|
|
2086
|
+
extractMatchingKeys(pattern, obj, prefix = "") {
|
|
2087
|
+
const matches = [];
|
|
2088
|
+
const allKeys = [];
|
|
2089
|
+
this.collectKeys(obj, prefix, allKeys);
|
|
2090
|
+
for (const key of allKeys) {
|
|
2091
|
+
if (this.matches(pattern, key)) {
|
|
2092
|
+
matches.push(key);
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
return {
|
|
2096
|
+
matched: matches.length > 0,
|
|
2097
|
+
matches,
|
|
2098
|
+
stats: {
|
|
2099
|
+
total: allKeys.length,
|
|
2100
|
+
matched: matches.length
|
|
2101
|
+
}
|
|
2102
|
+
};
|
|
2103
|
+
}
|
|
2104
|
+
/**
|
|
2105
|
+
* 从对象中提取匹配通配符模式的 key 值对
|
|
2106
|
+
*
|
|
2107
|
+
* @param pattern - 通配符模式
|
|
2108
|
+
* @param obj - 要搜索的对象
|
|
2109
|
+
* @returns 匹配的 key 值对
|
|
2110
|
+
*/
|
|
2111
|
+
extractMatchingValues(pattern, obj) {
|
|
2112
|
+
const result = {};
|
|
2113
|
+
const matchResult = this.extractMatchingKeys(pattern, obj);
|
|
2114
|
+
for (const key of matchResult.matches) {
|
|
2115
|
+
try {
|
|
2116
|
+
const value = this.getValueByPath(obj, key);
|
|
2117
|
+
result[key] = value;
|
|
2118
|
+
} catch {
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
return result;
|
|
2122
|
+
}
|
|
2123
|
+
/**
|
|
2124
|
+
* 递归收集对象中的所有 key 路径
|
|
2125
|
+
*
|
|
2126
|
+
* @param obj - 对象
|
|
2127
|
+
* @param prefix - 路径前缀
|
|
2128
|
+
* @param keys - 收集的 key 列表
|
|
2129
|
+
*/
|
|
2130
|
+
collectKeys(obj, prefix, keys) {
|
|
2131
|
+
if (obj == null || typeof obj !== "object") {
|
|
2132
|
+
return;
|
|
2133
|
+
}
|
|
2134
|
+
if (Array.isArray(obj)) {
|
|
2135
|
+
for (let i = 0; i < obj.length; i++) {
|
|
2136
|
+
const key = prefix ? `${prefix}[${i}]` : `[${i}]`;
|
|
2137
|
+
keys.push(key);
|
|
2138
|
+
this.collectKeys(obj[i], key, keys);
|
|
2139
|
+
}
|
|
2140
|
+
} else {
|
|
2141
|
+
for (const [prop, value] of Object.entries(obj)) {
|
|
2142
|
+
const key = prefix ? `${prefix}.${prop}` : prop;
|
|
2143
|
+
keys.push(key);
|
|
2144
|
+
this.collectKeys(value, key, keys);
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
/**
|
|
2149
|
+
* 根据路径从对象中获取值
|
|
2150
|
+
*
|
|
2151
|
+
* @param obj - 对象
|
|
2152
|
+
* @param path - 路径
|
|
2153
|
+
* @returns 值
|
|
2154
|
+
*/
|
|
2155
|
+
getValueByPath(obj, path8) {
|
|
2156
|
+
const segments = [];
|
|
2157
|
+
let current = "";
|
|
2158
|
+
let inBrackets = false;
|
|
2159
|
+
let bracketContent = "";
|
|
2160
|
+
for (let i = 0; i < path8.length; i++) {
|
|
2161
|
+
const char = path8[i];
|
|
2162
|
+
if (char === "[") {
|
|
2163
|
+
if (inBrackets) {
|
|
2164
|
+
throw new Error(`Invalid path: nested brackets at position ${i}`);
|
|
2165
|
+
}
|
|
2166
|
+
if (current.trim()) {
|
|
2167
|
+
segments.push(current.trim());
|
|
2168
|
+
}
|
|
2169
|
+
inBrackets = true;
|
|
2170
|
+
bracketContent = "";
|
|
2171
|
+
current = "";
|
|
2172
|
+
} else if (char === "]") {
|
|
2173
|
+
if (!inBrackets) {
|
|
2174
|
+
throw new Error(`Invalid path: closing bracket without opening bracket at position ${i}`);
|
|
2175
|
+
}
|
|
2176
|
+
inBrackets = false;
|
|
2177
|
+
const index = parseInt(bracketContent.trim(), 10);
|
|
2178
|
+
if (isNaN(index)) {
|
|
2179
|
+
throw new Error(`Invalid array index: "${bracketContent}" is not a number`);
|
|
2180
|
+
}
|
|
2181
|
+
segments.push(index);
|
|
2182
|
+
current = "";
|
|
2183
|
+
} else if (char === "." && !inBrackets) {
|
|
2184
|
+
if (current.trim()) {
|
|
2185
|
+
segments.push(current.trim());
|
|
2186
|
+
}
|
|
2187
|
+
current = "";
|
|
2188
|
+
} else {
|
|
2189
|
+
if (inBrackets) {
|
|
2190
|
+
bracketContent += char;
|
|
2191
|
+
} else {
|
|
2192
|
+
current += char;
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
if (inBrackets) {
|
|
2197
|
+
throw new Error("Invalid path: unclosed bracket");
|
|
2198
|
+
}
|
|
2199
|
+
if (current.trim()) {
|
|
2200
|
+
segments.push(current.trim());
|
|
2201
|
+
}
|
|
2202
|
+
let value = obj;
|
|
2203
|
+
for (const segment of segments) {
|
|
2204
|
+
if (value == null) {
|
|
2205
|
+
throw new Error(`Path "${path8}" not found`);
|
|
2206
|
+
}
|
|
2207
|
+
if (typeof segment === "number") {
|
|
2208
|
+
if (Array.isArray(value)) {
|
|
2209
|
+
if (segment >= value.length) {
|
|
2210
|
+
throw new Error(`Array index ${segment} out of bounds`);
|
|
2211
|
+
}
|
|
2212
|
+
value = value[segment];
|
|
2213
|
+
} else {
|
|
2214
|
+
throw new Error(`Cannot access array index ${segment} on non-array value`);
|
|
2215
|
+
}
|
|
2216
|
+
} else {
|
|
2217
|
+
if (typeof value === "object" && value !== null) {
|
|
2218
|
+
if (!(segment in value)) {
|
|
2219
|
+
throw new Error(`Key "${segment}" not found`);
|
|
2220
|
+
}
|
|
2221
|
+
value = value[segment];
|
|
2222
|
+
} else {
|
|
2223
|
+
throw new Error(`Cannot access property "${segment}" on non-object value`);
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
return value;
|
|
2228
|
+
}
|
|
2229
|
+
/**
|
|
2230
|
+
* 检查模式是否包含通配符
|
|
2231
|
+
*
|
|
2232
|
+
* @param pattern - 模式
|
|
2233
|
+
* @returns 是否包含通配符
|
|
2234
|
+
*/
|
|
2235
|
+
hasWildcard(pattern) {
|
|
2236
|
+
return /[*?]/.test(pattern);
|
|
2237
|
+
}
|
|
2238
|
+
/**
|
|
2239
|
+
* 检查模式是否包含多层级通配符
|
|
2240
|
+
*
|
|
2241
|
+
* @param pattern - 模式
|
|
2242
|
+
* @returns 是否包含多层级通配符
|
|
2243
|
+
*/
|
|
2244
|
+
hasGlobstar(pattern) {
|
|
2245
|
+
return pattern.includes("**");
|
|
2246
|
+
}
|
|
2247
|
+
};
|
|
2248
|
+
var defaultWildcardMatcher = new WildcardMatcher({
|
|
2249
|
+
caseSensitive: false
|
|
2250
|
+
});
|
|
2251
|
+
|
|
2252
|
+
// src/core/engine/batch-key-extractor.ts
|
|
2253
|
+
var BatchKeyExtractor = class {
|
|
2254
|
+
constructor(options = {}) {
|
|
2255
|
+
this.options = {
|
|
2256
|
+
preserveFormat: options.preserveFormat ?? true,
|
|
2257
|
+
onError: options.onError ?? ((error) => {
|
|
2258
|
+
throw error;
|
|
2259
|
+
}),
|
|
2260
|
+
enableWildcard: options.enableWildcard ?? true,
|
|
2261
|
+
warnOnNoMatch: options.warnOnNoMatch ?? true,
|
|
2262
|
+
outputFormat: options.outputFormat ?? "array"
|
|
2263
|
+
};
|
|
2264
|
+
this.keyExtractor = new KeyExtractor({
|
|
2265
|
+
preserveFormat: this.options.preserveFormat,
|
|
2266
|
+
onError: this.options.onError
|
|
2267
|
+
});
|
|
2268
|
+
this.wildcardMatcher = new WildcardMatcher({
|
|
2269
|
+
caseSensitive: false
|
|
2270
|
+
});
|
|
2271
|
+
}
|
|
2272
|
+
/**
|
|
2273
|
+
* 从文件中批量提取多个 key 的值
|
|
2274
|
+
*
|
|
2275
|
+
* @param filePath - 文件路径
|
|
2276
|
+
* @param keyPaths - key 路径列表(支持逗号分隔)
|
|
2277
|
+
* @returns 批量提取结果
|
|
2278
|
+
*/
|
|
2279
|
+
async extractFromFile(filePath, keyPaths) {
|
|
2280
|
+
const keys = this.parseKeyPaths(keyPaths);
|
|
2281
|
+
const results = [];
|
|
2282
|
+
const errors = [];
|
|
2283
|
+
for (const keyPath of keys) {
|
|
2284
|
+
try {
|
|
2285
|
+
if (this.options.enableWildcard && this.wildcardMatcher.hasWildcard(keyPath)) {
|
|
2286
|
+
const wildcardResults = await this.extractWithWildcard(filePath, keyPath);
|
|
2287
|
+
results.push(...wildcardResults.results);
|
|
2288
|
+
if (wildcardResults.errors.length > 0) {
|
|
2289
|
+
errors.push(...wildcardResults.errors);
|
|
2290
|
+
}
|
|
2291
|
+
} else {
|
|
2292
|
+
const result = await this.keyExtractor.extractFromFile(filePath, keyPath);
|
|
2293
|
+
results.push(result);
|
|
2294
|
+
}
|
|
2295
|
+
} catch (error) {
|
|
2296
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2297
|
+
errors.push({ key: keyPath, error: errorMessage });
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
return {
|
|
2301
|
+
results,
|
|
2302
|
+
success: errors.length === 0,
|
|
2303
|
+
successCount: results.length,
|
|
2304
|
+
failureCount: errors.length,
|
|
2305
|
+
errors
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
/**
|
|
2309
|
+
* 使用通配符模式提取 key
|
|
2310
|
+
*
|
|
2311
|
+
* @param filePath - 文件路径
|
|
2312
|
+
* @param pattern - 通配符模式
|
|
2313
|
+
* @returns 批量提取结果
|
|
2314
|
+
*/
|
|
2315
|
+
async extractWithWildcard(filePath, pattern) {
|
|
2316
|
+
const fs15 = await import("fs");
|
|
2317
|
+
const content = fs15.readFileSync(filePath, "utf-8");
|
|
2318
|
+
let obj;
|
|
2319
|
+
const format = this.keyExtractor.detectFormat(filePath);
|
|
2320
|
+
switch (format) {
|
|
2321
|
+
case "json":
|
|
2322
|
+
obj = this.keyExtractor.parseJSON(content);
|
|
2323
|
+
break;
|
|
2324
|
+
case "yaml":
|
|
2325
|
+
obj = this.keyExtractor.parseYAML(content);
|
|
2326
|
+
break;
|
|
2327
|
+
case "toml":
|
|
2328
|
+
obj = this.keyExtractor.parseTOML(content);
|
|
2329
|
+
break;
|
|
2330
|
+
case "xml":
|
|
2331
|
+
obj = await this.keyExtractor.parseXML(content);
|
|
2332
|
+
break;
|
|
2333
|
+
case "properties":
|
|
2334
|
+
obj = this.keyExtractor.parseProperties(content);
|
|
2335
|
+
break;
|
|
2336
|
+
case "ini":
|
|
2337
|
+
obj = this.keyExtractor.parseINI(content);
|
|
2338
|
+
break;
|
|
2339
|
+
default:
|
|
2340
|
+
throw new Error(`Unsupported file format: ${format}`);
|
|
2341
|
+
}
|
|
2342
|
+
const matchedValues = this.wildcardMatcher.extractMatchingValues(pattern, obj);
|
|
2343
|
+
const results = [];
|
|
2344
|
+
const errors = [];
|
|
2345
|
+
for (const [key, value] of Object.entries(matchedValues)) {
|
|
2346
|
+
results.push({
|
|
2347
|
+
value,
|
|
2348
|
+
type: this.getValueType(value),
|
|
2349
|
+
format,
|
|
2350
|
+
keyPath: key
|
|
2351
|
+
});
|
|
2352
|
+
}
|
|
2353
|
+
if (results.length === 0 && this.options.warnOnNoMatch) {
|
|
2354
|
+
console.warn(`\u26A0\uFE0F \u8B66\u544A: \u901A\u914D\u7B26\u6A21\u5F0F "${pattern}" \u672A\u5339\u914D\u5230\u4EFB\u4F55 key`);
|
|
2355
|
+
}
|
|
2356
|
+
return {
|
|
2357
|
+
results,
|
|
2358
|
+
success: errors.length === 0,
|
|
2359
|
+
successCount: results.length,
|
|
2360
|
+
failureCount: errors.length,
|
|
2361
|
+
errors
|
|
2362
|
+
};
|
|
2363
|
+
}
|
|
2364
|
+
/**
|
|
2365
|
+
* 解析 key 路径列表(支持逗号分隔)
|
|
2366
|
+
*
|
|
2367
|
+
* @param keyPaths - key 路径(字符串或字符串数组)
|
|
2368
|
+
* @returns 解析后的 key 路径数组
|
|
2369
|
+
*/
|
|
2370
|
+
parseKeyPaths(keyPaths) {
|
|
2371
|
+
if (Array.isArray(keyPaths)) {
|
|
2372
|
+
const result = [];
|
|
2373
|
+
for (const kp of keyPaths) {
|
|
2374
|
+
const parts = kp.split(",").map((s) => s.trim()).filter((s) => s);
|
|
2375
|
+
result.push(...parts);
|
|
2376
|
+
}
|
|
2377
|
+
return result;
|
|
2378
|
+
} else {
|
|
2379
|
+
return keyPaths.split(",").map((s) => s.trim()).filter((s) => s);
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
/**
|
|
2383
|
+
* 获取值的类型
|
|
2384
|
+
*
|
|
2385
|
+
* @param value - 值
|
|
2386
|
+
* @returns 类型字符串
|
|
2387
|
+
*/
|
|
2388
|
+
getValueType(value) {
|
|
2389
|
+
if (value === null) return "null";
|
|
2390
|
+
if (Array.isArray(value)) return "array";
|
|
2391
|
+
return typeof value;
|
|
2392
|
+
}
|
|
2393
|
+
/**
|
|
2394
|
+
* 格式化输出结果
|
|
2395
|
+
*
|
|
2396
|
+
* @param result - 批量提取结果
|
|
2397
|
+
* @returns 格式化后的输出
|
|
2398
|
+
*/
|
|
2399
|
+
formatOutput(result) {
|
|
2400
|
+
if (this.options.outputFormat === "object") {
|
|
2401
|
+
const obj = {};
|
|
2402
|
+
for (const r of result.results) {
|
|
2403
|
+
obj[r.keyPath] = r.value;
|
|
2404
|
+
}
|
|
2405
|
+
return JSON.stringify(obj, null, 2);
|
|
2406
|
+
} else {
|
|
2407
|
+
return JSON.stringify(result.results.map((r) => ({
|
|
2408
|
+
key: r.keyPath,
|
|
2409
|
+
value: r.value,
|
|
2410
|
+
type: r.type
|
|
2411
|
+
})), null, 2);
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
/**
|
|
2415
|
+
* 打印批量提取结果的摘要
|
|
2416
|
+
*
|
|
2417
|
+
* @param result - 批量提取结果
|
|
2418
|
+
*/
|
|
2419
|
+
printSummary(result) {
|
|
2420
|
+
console.log(`\u2705 \u6210\u529F\u63D0\u53D6: ${result.successCount} \u4E2A key`);
|
|
2421
|
+
if (result.failureCount > 0) {
|
|
2422
|
+
console.log(`\u274C \u5931\u8D25: ${result.failureCount} \u4E2A key`);
|
|
2423
|
+
for (const err of result.errors) {
|
|
2424
|
+
console.log(` - ${err.key}: ${err.error}`);
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
};
|
|
2429
|
+
var defaultBatchKeyExtractor = new BatchKeyExtractor();
|
|
2430
|
+
|
|
2431
|
+
// src/core/formatter/output-formatter.ts
|
|
2432
|
+
import yaml2 from "js-yaml";
|
|
2433
|
+
import TOML from "toml";
|
|
2434
|
+
import xml2js2 from "xml2js";
|
|
2435
|
+
var OutputFormatter = class {
|
|
2436
|
+
/**
|
|
2437
|
+
* 格式化数据为指定格式
|
|
2438
|
+
* @param data 要格式化的数据
|
|
2439
|
+
* @param options 格式化选项
|
|
2440
|
+
* @returns 格式化后的字符串
|
|
2441
|
+
*/
|
|
2442
|
+
format(data, options) {
|
|
2443
|
+
const { format } = options;
|
|
2444
|
+
switch (format) {
|
|
2445
|
+
case "json":
|
|
2446
|
+
return this.formatJSON(data, options);
|
|
2447
|
+
case "yaml":
|
|
2448
|
+
case "yml":
|
|
2449
|
+
return this.formatYAML(data, options);
|
|
2450
|
+
case "toml":
|
|
2451
|
+
return this.formatTOML(data, options);
|
|
2452
|
+
case "xml":
|
|
2453
|
+
return this.formatXML(data, options);
|
|
2454
|
+
case "properties":
|
|
2455
|
+
return this.formatProperties(data, options);
|
|
2456
|
+
case "ini":
|
|
2457
|
+
return this.formatINI(data, options);
|
|
2458
|
+
case "raw":
|
|
2459
|
+
return this.formatRaw(data, options);
|
|
2460
|
+
default:
|
|
2461
|
+
throw new Error(`Unsupported format: ${format}`);
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
/**
|
|
2465
|
+
* 格式化为 JSON
|
|
2466
|
+
*/
|
|
2467
|
+
formatJSON(data, options) {
|
|
2468
|
+
const indent = options.indent ?? 2;
|
|
2469
|
+
return JSON.stringify(data, null, indent);
|
|
2470
|
+
}
|
|
2471
|
+
/**
|
|
2472
|
+
* 格式化为 YAML
|
|
2473
|
+
*/
|
|
2474
|
+
formatYAML(data, options) {
|
|
2475
|
+
const indent = options.indent ?? 2;
|
|
2476
|
+
return yaml2.dump(data, {
|
|
2477
|
+
indent,
|
|
2478
|
+
lineWidth: -1,
|
|
2479
|
+
// 不限制行宽
|
|
2480
|
+
noRefs: true,
|
|
2481
|
+
// 不支持引用
|
|
2482
|
+
sortKeys: false
|
|
2483
|
+
// 不排序键
|
|
2484
|
+
});
|
|
2485
|
+
}
|
|
2486
|
+
/**
|
|
2487
|
+
* 格式化为 TOML
|
|
2488
|
+
*/
|
|
2489
|
+
formatTOML(data, _options) {
|
|
2490
|
+
return TOML.stringify(data);
|
|
2491
|
+
}
|
|
2492
|
+
/**
|
|
2493
|
+
* 格式化为 XML
|
|
2494
|
+
*/
|
|
2495
|
+
formatXML(data, options) {
|
|
2496
|
+
const rootName = options.xmlRootName ?? "root";
|
|
2497
|
+
const indent = options.indent ?? 2;
|
|
2498
|
+
const builder = new xml2js2.Builder({
|
|
2499
|
+
rootName,
|
|
2500
|
+
xmldec: { version: "1.0", encoding: "UTF-8" },
|
|
2501
|
+
renderOpts: { pretty: true, indent: " ".repeat(indent) }
|
|
2502
|
+
});
|
|
2503
|
+
return builder.buildObject(data);
|
|
2504
|
+
}
|
|
2505
|
+
/**
|
|
2506
|
+
* 格式化为 Properties 格式
|
|
2507
|
+
*/
|
|
2508
|
+
formatProperties(data, options) {
|
|
2509
|
+
const prefix = options.keyPrefix ?? "";
|
|
2510
|
+
const lines = [];
|
|
2511
|
+
const flatten = (obj, currentPrefix) => {
|
|
2512
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
2513
|
+
const fullKey = currentPrefix ? `${currentPrefix}.${key}` : key;
|
|
2514
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
2515
|
+
flatten(value, fullKey);
|
|
2516
|
+
} else {
|
|
2517
|
+
const stringValue = this.escapePropertiesValue(String(value));
|
|
2518
|
+
lines.push(`${prefix}${fullKey}=${stringValue}`);
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
};
|
|
2522
|
+
if (typeof data === "object" && data !== null) {
|
|
2523
|
+
flatten(data, "");
|
|
2524
|
+
} else {
|
|
2525
|
+
const stringValue = this.escapePropertiesValue(String(data));
|
|
2526
|
+
lines.push(`${prefix}value=${stringValue}`);
|
|
2527
|
+
}
|
|
2528
|
+
return lines.join("\n");
|
|
2529
|
+
}
|
|
2530
|
+
/**
|
|
2531
|
+
* 转义 Properties 值中的特殊字符
|
|
2532
|
+
*/
|
|
2533
|
+
escapePropertiesValue(value) {
|
|
2534
|
+
return value.replace(/\\/g, "").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t").replace(/\f/g, "\\f");
|
|
2535
|
+
}
|
|
2536
|
+
/**
|
|
2537
|
+
* 格式化为 INI 格式
|
|
2538
|
+
*/
|
|
2539
|
+
formatINI(data, _options) {
|
|
2540
|
+
const lines = [];
|
|
2541
|
+
const flatten = (obj, section) => {
|
|
2542
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
2543
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
2544
|
+
flatten(value, key);
|
|
2545
|
+
} else {
|
|
2546
|
+
const stringValue = this.escapeINIValue(String(value));
|
|
2547
|
+
if (section) {
|
|
2548
|
+
lines.push(`[${section}]`);
|
|
2549
|
+
lines.push(`${key}=${stringValue}`);
|
|
2550
|
+
section = null;
|
|
2551
|
+
} else {
|
|
2552
|
+
lines.push(`${key}=${stringValue}`);
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
};
|
|
2557
|
+
if (typeof data === "object" && data !== null) {
|
|
2558
|
+
flatten(data, null);
|
|
2559
|
+
} else {
|
|
2560
|
+
const stringValue = this.escapeINIValue(String(data));
|
|
2561
|
+
lines.push(`value=${stringValue}`);
|
|
2562
|
+
}
|
|
2563
|
+
return lines.join("\n");
|
|
2564
|
+
}
|
|
2565
|
+
/**
|
|
2566
|
+
* 转义 INI 值中的特殊字符
|
|
2567
|
+
*/
|
|
2568
|
+
escapeINIValue(value) {
|
|
2569
|
+
return value.replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
2570
|
+
}
|
|
2571
|
+
/**
|
|
2572
|
+
* 格式化为原始格式(直接输出值)
|
|
2573
|
+
*/
|
|
2574
|
+
formatRaw(data, _options) {
|
|
2575
|
+
if (typeof data === "string") {
|
|
2576
|
+
return data;
|
|
2577
|
+
}
|
|
2578
|
+
if (typeof data === "number" || typeof data === "boolean") {
|
|
2579
|
+
return String(data);
|
|
2580
|
+
}
|
|
2581
|
+
if (data === null) {
|
|
2582
|
+
return "";
|
|
2583
|
+
}
|
|
2584
|
+
if (typeof data === "object") {
|
|
2585
|
+
return JSON.stringify(data);
|
|
2586
|
+
}
|
|
2587
|
+
return String(data);
|
|
2588
|
+
}
|
|
2589
|
+
/**
|
|
2590
|
+
* 验证格式字符串是否有效
|
|
2591
|
+
*/
|
|
2592
|
+
static isValidFormat(format) {
|
|
2593
|
+
const validFormats = ["json", "yaml", "yml", "toml", "xml", "properties", "ini", "raw"];
|
|
2594
|
+
return validFormats.includes(format);
|
|
2595
|
+
}
|
|
2596
|
+
/**
|
|
2597
|
+
* 获取所有支持的格式列表
|
|
2598
|
+
*/
|
|
2599
|
+
static getSupportedFormats() {
|
|
2600
|
+
return ["json", "yaml", "yml", "toml", "xml", "properties", "ini", "raw"];
|
|
2601
|
+
}
|
|
2602
|
+
};
|
|
2603
|
+
|
|
2604
|
+
// src/core/validator/key-validator.ts
|
|
2605
|
+
var KeyValidator = class {
|
|
2606
|
+
constructor() {
|
|
2607
|
+
this.keyExtractor = new KeyExtractor();
|
|
2608
|
+
this.batchKeyExtractor = new BatchKeyExtractor();
|
|
2609
|
+
}
|
|
2610
|
+
/**
|
|
2611
|
+
* 验证单个 key 是否存在
|
|
2612
|
+
* @param data 配置数据对象
|
|
2613
|
+
* @param keyPath key 路径
|
|
2614
|
+
* @returns 验证结果
|
|
2615
|
+
*/
|
|
2616
|
+
validate(data, keyPath) {
|
|
2617
|
+
try {
|
|
2618
|
+
const value = this.extractValueFromData(data, keyPath);
|
|
2619
|
+
if (value !== void 0) {
|
|
2620
|
+
return {
|
|
2621
|
+
key: keyPath,
|
|
2622
|
+
exists: true,
|
|
2623
|
+
value
|
|
2624
|
+
};
|
|
2625
|
+
} else {
|
|
2626
|
+
return {
|
|
2627
|
+
key: keyPath,
|
|
2628
|
+
exists: false
|
|
2629
|
+
};
|
|
2630
|
+
}
|
|
2631
|
+
} catch (error) {
|
|
2632
|
+
return {
|
|
2633
|
+
key: keyPath,
|
|
2634
|
+
exists: false,
|
|
2635
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2636
|
+
};
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
/**
|
|
2640
|
+
* 从数据对象中提取值(辅助方法)
|
|
2641
|
+
*/
|
|
2642
|
+
extractValueFromData(data, keyPath) {
|
|
2643
|
+
if (!keyPath) {
|
|
2644
|
+
throw new Error("Key path cannot be empty");
|
|
2645
|
+
}
|
|
2646
|
+
const parts = keyPath.split(".");
|
|
2647
|
+
let current = data;
|
|
2648
|
+
for (const part of parts) {
|
|
2649
|
+
if (current === null || current === void 0) {
|
|
2650
|
+
return void 0;
|
|
2651
|
+
}
|
|
2652
|
+
if (typeof current !== "object") {
|
|
2653
|
+
return void 0;
|
|
2654
|
+
}
|
|
2655
|
+
if (part.includes("[") && part.includes("]")) {
|
|
2656
|
+
const [arrayKey, indexStr] = part.split("[");
|
|
2657
|
+
const index = parseInt(indexStr.replace("]", ""), 10);
|
|
2658
|
+
const obj = current;
|
|
2659
|
+
current = obj[arrayKey];
|
|
2660
|
+
if (Array.isArray(current)) {
|
|
2661
|
+
current = current[index];
|
|
2662
|
+
}
|
|
2663
|
+
} else {
|
|
2664
|
+
const obj = current;
|
|
2665
|
+
current = obj[part];
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
return current;
|
|
2669
|
+
}
|
|
2670
|
+
/**
|
|
2671
|
+
* 批量验证多个 key 是否存在
|
|
2672
|
+
* @param data 配置数据对象
|
|
2673
|
+
* @param keyPaths key 路径数组(可能包含逗号分隔的字符串)
|
|
2674
|
+
* @returns 批量验证结果
|
|
2675
|
+
*/
|
|
2676
|
+
validateBatch(data, keyPaths) {
|
|
2677
|
+
const results = [];
|
|
2678
|
+
const allKeys = [];
|
|
2679
|
+
for (const kp of keyPaths) {
|
|
2680
|
+
const parts = kp.split(",").map((s) => s.trim()).filter((s) => s);
|
|
2681
|
+
allKeys.push(...parts);
|
|
2682
|
+
}
|
|
2683
|
+
for (const keyPath of allKeys) {
|
|
2684
|
+
const result = this.validate(data, keyPath);
|
|
2685
|
+
results.push(result);
|
|
2686
|
+
}
|
|
2687
|
+
const existsCount = results.filter((r) => r.exists).length;
|
|
2688
|
+
const notExistsCount = results.length - existsCount;
|
|
2689
|
+
return {
|
|
2690
|
+
allExists: notExistsCount === 0,
|
|
2691
|
+
results,
|
|
2692
|
+
existsCount,
|
|
2693
|
+
notExistsCount
|
|
2694
|
+
};
|
|
2695
|
+
}
|
|
2696
|
+
/**
|
|
2697
|
+
* 格式化验证结果为人类可读的字符串
|
|
2698
|
+
* @param result 验证结果
|
|
2699
|
+
* @returns 格式化后的字符串
|
|
2700
|
+
*/
|
|
2701
|
+
formatResult(result) {
|
|
2702
|
+
if (result.exists) {
|
|
2703
|
+
const valueStr = this.formatValue(result.value);
|
|
2704
|
+
return `\u2705 Key '${result.key}' exists${valueStr ? `: ${valueStr}` : ""}`;
|
|
2705
|
+
} else {
|
|
2706
|
+
if (result.error) {
|
|
2707
|
+
return `\u274C Key '${result.key}' error: ${result.error}`;
|
|
2708
|
+
}
|
|
2709
|
+
return `\u274C Key '${result.key}' not found`;
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
/**
|
|
2713
|
+
* 格式化批量验证结果为人类可读的字符串
|
|
2714
|
+
* @param result 批量验证结果
|
|
2715
|
+
* @returns 格式化后的字符串
|
|
2716
|
+
*/
|
|
2717
|
+
formatBatchResult(result) {
|
|
2718
|
+
const lines = [];
|
|
2719
|
+
for (const r of result.results) {
|
|
2720
|
+
lines.push(this.formatResult(r));
|
|
2721
|
+
}
|
|
2722
|
+
lines.push("");
|
|
2723
|
+
lines.push(`\u{1F4CA} Summary:`);
|
|
2724
|
+
lines.push(` \u2705 Exists: ${result.existsCount}`);
|
|
2725
|
+
lines.push(` \u274C Not found: ${result.notExistsCount}`);
|
|
2726
|
+
lines.push(` \u{1F4C8} Total: ${result.results.length}`);
|
|
2727
|
+
return lines.join("\n");
|
|
2728
|
+
}
|
|
2729
|
+
/**
|
|
2730
|
+
* 格式化值为字符串
|
|
2731
|
+
*/
|
|
2732
|
+
formatValue(value) {
|
|
2733
|
+
if (value === null) {
|
|
2734
|
+
return "null";
|
|
2735
|
+
}
|
|
2736
|
+
if (typeof value === "string") {
|
|
2737
|
+
return `"${value}"`;
|
|
2738
|
+
}
|
|
2739
|
+
if (typeof value === "boolean" || typeof value === "number") {
|
|
2740
|
+
return String(value);
|
|
2741
|
+
}
|
|
2742
|
+
if (typeof value === "object") {
|
|
2743
|
+
return JSON.stringify(value);
|
|
2744
|
+
}
|
|
2745
|
+
return String(value);
|
|
2746
|
+
}
|
|
2747
|
+
/**
|
|
2748
|
+
* 根据验证结果确定退出码
|
|
2749
|
+
* @param result 验证结果
|
|
2750
|
+
* @returns 退出码(0 表示全部存在,1 表示有不存在或错误)
|
|
2751
|
+
*/
|
|
2752
|
+
getExitCode(result) {
|
|
2753
|
+
if ("allExists" in result) {
|
|
2754
|
+
return result.allExists ? 0 : 1;
|
|
2755
|
+
} else {
|
|
2756
|
+
return result.exists ? 0 : 1;
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
};
|
|
2760
|
+
|
|
2761
|
+
// src/commands/pack.ts
|
|
2762
|
+
import clipboardy from "clipboardy";
|
|
2763
|
+
|
|
2764
|
+
// src/commands/types.ts
|
|
2765
|
+
function normalizeURPFPath(filePath) {
|
|
2766
|
+
if (filePath.endsWith(".urpf.txt") || filePath.endsWith(".urpf")) {
|
|
2767
|
+
return filePath;
|
|
2768
|
+
}
|
|
2769
|
+
return `${filePath}.urpf.txt`;
|
|
2770
|
+
}
|
|
2771
|
+
async function findURPFFile(filePath) {
|
|
2772
|
+
const fs15 = await import("fs/promises");
|
|
2773
|
+
try {
|
|
2774
|
+
const stats = await fs15.stat(filePath);
|
|
2775
|
+
if (stats.isFile()) {
|
|
2776
|
+
return filePath;
|
|
2777
|
+
}
|
|
2778
|
+
} catch {
|
|
2779
|
+
}
|
|
2780
|
+
if (!filePath.endsWith(".urpf.txt") && !filePath.endsWith(".urpf")) {
|
|
2781
|
+
const newPath = `${filePath}.urpf.txt`;
|
|
2782
|
+
try {
|
|
2783
|
+
const stats = await fs15.stat(newPath);
|
|
2784
|
+
if (stats.isFile()) {
|
|
2785
|
+
return newPath;
|
|
2786
|
+
}
|
|
2787
|
+
} catch {
|
|
2788
|
+
}
|
|
2789
|
+
const oldPath = `${filePath}.urpf`;
|
|
2790
|
+
try {
|
|
2791
|
+
const stats = await fs15.stat(oldPath);
|
|
2792
|
+
if (stats.isFile()) {
|
|
2793
|
+
return oldPath;
|
|
2794
|
+
}
|
|
2795
|
+
} catch {
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
return null;
|
|
2799
|
+
}
|
|
2800
|
+
|
|
2801
|
+
// src/commands/pack.ts
|
|
2802
|
+
var PackCommand = class {
|
|
2803
|
+
/**
|
|
2804
|
+
* 执行 pack 命令
|
|
2805
|
+
*/
|
|
2806
|
+
async execute(inputPath, options = {}) {
|
|
2807
|
+
const startTime = Date.now();
|
|
2808
|
+
try {
|
|
2809
|
+
const resolvedPath = path3.resolve(inputPath);
|
|
2810
|
+
await this.validatePath(resolvedPath);
|
|
2811
|
+
this.validateSliceOptions(options);
|
|
2812
|
+
const outputPath = options.output || this.generateOutputPath(resolvedPath);
|
|
2813
|
+
const scanResult = await this.scanFiles(resolvedPath, options);
|
|
2814
|
+
const urpfDocument = await this.generateURPFDocument(scanResult, resolvedPath, options);
|
|
2815
|
+
const urpfResult = await this.saveURPFDocument(urpfDocument, outputPath, options);
|
|
2816
|
+
const duration = Date.now() - startTime;
|
|
2817
|
+
await this.handleOutputOptions(urpfResult.content, outputPath, options);
|
|
2818
|
+
if (options.verbose) {
|
|
2819
|
+
this.logVerbose(scanResult, urpfResult, outputPath, duration);
|
|
2820
|
+
}
|
|
2821
|
+
return {
|
|
2822
|
+
success: true,
|
|
2823
|
+
outputPath,
|
|
2824
|
+
fileCount: scanResult.files.length,
|
|
2825
|
+
directoryCount: scanResult.directories.length,
|
|
2826
|
+
skippedCount: scanResult.skippedFiles.length,
|
|
2827
|
+
urpfSize: urpfResult.size,
|
|
2828
|
+
duration
|
|
2829
|
+
};
|
|
2830
|
+
} catch (err) {
|
|
2831
|
+
const duration = Date.now() - startTime;
|
|
2832
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
2833
|
+
if (options.verbose) {
|
|
2834
|
+
console.error(`\u9519\u8BEF: ${errorMessage}`);
|
|
2835
|
+
}
|
|
2836
|
+
return {
|
|
2837
|
+
success: false,
|
|
2838
|
+
outputPath: "",
|
|
2839
|
+
fileCount: 0,
|
|
2840
|
+
directoryCount: 0,
|
|
2841
|
+
skippedCount: 0,
|
|
2842
|
+
urpfSize: 0,
|
|
2843
|
+
duration,
|
|
2844
|
+
error: errorMessage
|
|
2845
|
+
};
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
/**
|
|
2849
|
+
* 验证输入路径
|
|
2850
|
+
*/
|
|
2851
|
+
async validatePath(filePath) {
|
|
2852
|
+
try {
|
|
2853
|
+
const stats = await fs4.stat(filePath);
|
|
2854
|
+
if (!stats.isFile() && !stats.isDirectory()) {
|
|
2855
|
+
throw new Error(`\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6\u6216\u76EE\u5F55: ${filePath}`);
|
|
2856
|
+
}
|
|
2857
|
+
} catch {
|
|
2858
|
+
throw new Error(`\u65E0\u6CD5\u8BBF\u95EE\u8DEF\u5F84: ${filePath}`);
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
/**
|
|
2862
|
+
* 验证切片选项
|
|
2863
|
+
*/
|
|
2864
|
+
validateSliceOptions(options) {
|
|
2865
|
+
if ((options.byteStart !== void 0 || options.byteEnd !== void 0) && (options.lineStart !== void 0 || options.lineEnd !== void 0)) {
|
|
2866
|
+
throw new Error("\u4E0D\u80FD\u540C\u65F6\u4F7F\u7528 --byte \u548C --line \u5207\u7247\u9009\u9879");
|
|
2867
|
+
}
|
|
2868
|
+
if (options.byteEnd !== void 0 && options.byteStart === void 0) {
|
|
2869
|
+
throw new Error("--byte \u9009\u9879\u5FC5\u987B\u6307\u5B9A\u8D77\u59CB\u4F4D\u7F6E");
|
|
2870
|
+
}
|
|
2871
|
+
if (options.lineEnd !== void 0 && options.lineStart === void 0) {
|
|
2872
|
+
throw new Error("--line \u9009\u9879\u5FC5\u987B\u6307\u5B9A\u8D77\u59CB\u884C\u53F7");
|
|
2873
|
+
}
|
|
2874
|
+
if (options.key !== void 0 && (options.byteStart !== void 0 || options.byteEnd !== void 0)) {
|
|
2875
|
+
throw new Error("\u4E0D\u80FD\u540C\u65F6\u4F7F\u7528 --key \u548C --byte \u9009\u9879");
|
|
2876
|
+
}
|
|
2877
|
+
if (options.key !== void 0 && (options.lineStart !== void 0 || options.lineEnd !== void 0)) {
|
|
2878
|
+
throw new Error("\u4E0D\u80FD\u540C\u65F6\u4F7F\u7528 --key \u548C --line \u9009\u9879");
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
/**
|
|
2882
|
+
* 生成输出路径
|
|
2883
|
+
*/
|
|
2884
|
+
generateOutputPath(inputPath) {
|
|
2885
|
+
return normalizeURPFPath(inputPath);
|
|
2886
|
+
}
|
|
2887
|
+
/**
|
|
2888
|
+
* 扫描文件
|
|
2889
|
+
*/
|
|
2890
|
+
async scanFiles(rootPath, options) {
|
|
2891
|
+
const stats = await fs4.stat(rootPath);
|
|
2892
|
+
if (stats.isFile()) {
|
|
2893
|
+
const basePath = path3.dirname(rootPath);
|
|
2894
|
+
const metadata = await this.collectFileMetadata(rootPath, basePath);
|
|
2895
|
+
return {
|
|
2896
|
+
rootPath: basePath,
|
|
2897
|
+
// 设置正确的 rootPath
|
|
2898
|
+
files: [metadata],
|
|
2899
|
+
directories: [],
|
|
2900
|
+
skippedFiles: [],
|
|
2901
|
+
startTime: Date.now(),
|
|
2902
|
+
endTime: Date.now(),
|
|
2903
|
+
duration: 0
|
|
2904
|
+
};
|
|
2905
|
+
}
|
|
2906
|
+
let ignoreRules = [];
|
|
2907
|
+
if (options.ignoreFile) {
|
|
2908
|
+
const parser = new IgnoreRuleParser();
|
|
2909
|
+
const content = await fs4.readFile(options.ignoreFile, "utf-8");
|
|
2910
|
+
ignoreRules = parser.parseFile(content);
|
|
2911
|
+
} else {
|
|
2912
|
+
const defaultIgnoreFiles = [".gitignore", ".iflowignore"];
|
|
2913
|
+
for (const ignoreFile of defaultIgnoreFiles) {
|
|
2914
|
+
const ignorePath = path3.join(rootPath, ignoreFile);
|
|
2915
|
+
try {
|
|
2916
|
+
await fs4.access(ignorePath);
|
|
2917
|
+
const parser = new IgnoreRuleParser();
|
|
2918
|
+
const content = await fs4.readFile(ignorePath, "utf-8");
|
|
2919
|
+
ignoreRules = [...ignoreRules, ...parser.parseFile(content)];
|
|
2920
|
+
break;
|
|
2921
|
+
} catch {
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
const effectiveMaxDepth = options.noRecurse ? 1 : options.maxDepth;
|
|
2926
|
+
return await FileScanner.scanDirectory(rootPath, {
|
|
2927
|
+
ignoreRules,
|
|
2928
|
+
followSymlinks: options.followSymlinks || false,
|
|
2929
|
+
maxDepth: effectiveMaxDepth
|
|
2930
|
+
// undefined 或 1
|
|
2931
|
+
});
|
|
2932
|
+
}
|
|
2933
|
+
/**
|
|
2934
|
+
* 收集文件元数据
|
|
2935
|
+
*/
|
|
2936
|
+
async collectFileMetadata(filePath, rootPath) {
|
|
2937
|
+
const stats = await fs4.stat(filePath);
|
|
2938
|
+
const relativePath = path3.relative(rootPath, filePath);
|
|
2939
|
+
return {
|
|
2940
|
+
path: relativePath,
|
|
2941
|
+
name: path3.basename(filePath),
|
|
2942
|
+
isDirectory: false,
|
|
2943
|
+
isSymlink: false,
|
|
1561
2944
|
size: stats.size,
|
|
1562
|
-
permissions: stats.mode,
|
|
1563
|
-
mtime: stats.mtime,
|
|
2945
|
+
permissions: stats.mode.toString(8).padStart(3, "0"),
|
|
2946
|
+
mtime: stats.mtime.getTime(),
|
|
2947
|
+
ctime: stats.ctime.getTime(),
|
|
1564
2948
|
encoding: "utf-8",
|
|
1565
|
-
newlineType: "lf"
|
|
2949
|
+
newlineType: "lf",
|
|
2950
|
+
isBinary: false
|
|
1566
2951
|
};
|
|
1567
2952
|
}
|
|
1568
2953
|
/**
|
|
@@ -1571,22 +2956,28 @@ var PackCommand = class {
|
|
|
1571
2956
|
async generateURPFDocument(scanResult, rootPath, options) {
|
|
1572
2957
|
const serializer = new ResourceSerializer();
|
|
1573
2958
|
const files = scanResult.files.map((file) => {
|
|
1574
|
-
const
|
|
1575
|
-
const
|
|
1576
|
-
const
|
|
2959
|
+
const relativePath = file.path;
|
|
2960
|
+
const basePath = scanResult.rootPath || rootPath;
|
|
2961
|
+
const filePath = path3.join(basePath, relativePath);
|
|
2962
|
+
const udrsRef = `file://${relativePath}`;
|
|
1577
2963
|
return {
|
|
1578
2964
|
filePath,
|
|
1579
2965
|
metadata: file,
|
|
1580
|
-
udrsReference:
|
|
2966
|
+
udrsReference: udrsRef
|
|
1581
2967
|
};
|
|
1582
2968
|
});
|
|
1583
2969
|
const resources = await serializer.serializeFiles(files);
|
|
1584
|
-
|
|
1585
|
-
|
|
2970
|
+
let processedResources = resources;
|
|
2971
|
+
if (options.key !== void 0) {
|
|
2972
|
+
processedResources = await this.handleKeyExtraction(resources, files, options);
|
|
2973
|
+
} else {
|
|
2974
|
+
processedResources = await this.applySliceToResources(resources, options);
|
|
2975
|
+
}
|
|
2976
|
+
const properties2 = this.buildProperties(options);
|
|
1586
2977
|
return {
|
|
1587
2978
|
boundaryToken: "",
|
|
1588
2979
|
// 将由生成器自动生成
|
|
1589
|
-
properties,
|
|
2980
|
+
properties: properties2,
|
|
1590
2981
|
resources: processedResources
|
|
1591
2982
|
};
|
|
1592
2983
|
}
|
|
@@ -1599,7 +2990,6 @@ var PackCommand = class {
|
|
|
1599
2990
|
}
|
|
1600
2991
|
if (options.byteStart !== void 0) {
|
|
1601
2992
|
return resources.map((resource) => {
|
|
1602
|
-
const { start, end } = options;
|
|
1603
2993
|
const byteRange = { start: options.byteStart, end: options.byteEnd };
|
|
1604
2994
|
let content;
|
|
1605
2995
|
if (Buffer.isBuffer(resource.content)) {
|
|
@@ -1636,22 +3026,150 @@ var PackCommand = class {
|
|
|
1636
3026
|
}
|
|
1637
3027
|
return resources;
|
|
1638
3028
|
}
|
|
3029
|
+
/**
|
|
3030
|
+
* 处理 Key 提取逻辑
|
|
3031
|
+
*/
|
|
3032
|
+
async handleKeyExtraction(resources, files, options) {
|
|
3033
|
+
if (!options.key) {
|
|
3034
|
+
return resources;
|
|
3035
|
+
}
|
|
3036
|
+
if (options.validate) {
|
|
3037
|
+
return this.handleKeyValidation(resources, files, options);
|
|
3038
|
+
}
|
|
3039
|
+
const isBatch = Array.isArray(options.key) || typeof options.key === "string" && options.key.includes(",");
|
|
3040
|
+
const processedResources = [];
|
|
3041
|
+
const formatter = options.format ? new OutputFormatter() : null;
|
|
3042
|
+
if (isBatch) {
|
|
3043
|
+
const batchExtractor = new BatchKeyExtractor({
|
|
3044
|
+
outputFormat: "array",
|
|
3045
|
+
enableWildcard: true
|
|
3046
|
+
});
|
|
3047
|
+
for (let i = 0; i < resources.length; i++) {
|
|
3048
|
+
const resource = resources[i];
|
|
3049
|
+
const file = files[i];
|
|
3050
|
+
const absolutePath = file.filePath;
|
|
3051
|
+
try {
|
|
3052
|
+
const result = await batchExtractor.extractFromFile(absolutePath, options.key);
|
|
3053
|
+
const extractedData = result.results.map((r) => r.value);
|
|
3054
|
+
let extractedContent;
|
|
3055
|
+
if (formatter && options.format) {
|
|
3056
|
+
extractedContent = formatter.format(extractedData, { format: options.format });
|
|
3057
|
+
} else {
|
|
3058
|
+
extractedContent = batchExtractor.formatOutput(result);
|
|
3059
|
+
}
|
|
3060
|
+
processedResources.push({
|
|
3061
|
+
...resource,
|
|
3062
|
+
content: extractedContent
|
|
3063
|
+
});
|
|
3064
|
+
if (options.verbose) {
|
|
3065
|
+
batchExtractor.printSummary(result);
|
|
3066
|
+
console.log(` \u2713 \u4ECE ${absolutePath} \u6279\u91CF\u63D0\u53D6 ${result.successCount} \u4E2A key`);
|
|
3067
|
+
}
|
|
3068
|
+
if (result.errors.length > 0) {
|
|
3069
|
+
throw new Error(`\u6279\u91CF\u63D0\u53D6\u5931\u8D25: ${result.errors[0].error}`);
|
|
3070
|
+
}
|
|
3071
|
+
} catch (error) {
|
|
3072
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3073
|
+
throw new Error(`\u65E0\u6CD5\u4ECE\u6587\u4EF6 ${absolutePath} \u6279\u91CF\u63D0\u53D6 key: ${errorMessage}`);
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
} else {
|
|
3077
|
+
const keyExtractor = new KeyExtractor({
|
|
3078
|
+
onError: (error) => {
|
|
3079
|
+
throw error;
|
|
3080
|
+
}
|
|
3081
|
+
});
|
|
3082
|
+
for (let i = 0; i < resources.length; i++) {
|
|
3083
|
+
const resource = resources[i];
|
|
3084
|
+
const file = files[i];
|
|
3085
|
+
const absolutePath = file.filePath;
|
|
3086
|
+
try {
|
|
3087
|
+
const result = await keyExtractor.extractFromFile(absolutePath, options.key);
|
|
3088
|
+
const extractedValue = result.value;
|
|
3089
|
+
let extractedContent;
|
|
3090
|
+
if (formatter && options.format) {
|
|
3091
|
+
extractedContent = formatter.format(extractedValue, { format: options.format });
|
|
3092
|
+
} else if (typeof extractedValue === "object" && extractedValue !== null) {
|
|
3093
|
+
extractedContent = JSON.stringify(extractedValue, null, 2);
|
|
3094
|
+
} else if (typeof extractedValue === "string") {
|
|
3095
|
+
extractedContent = extractedValue;
|
|
3096
|
+
} else if (extractedValue === null) {
|
|
3097
|
+
extractedContent = "null";
|
|
3098
|
+
} else if (extractedValue === void 0) {
|
|
3099
|
+
extractedContent = "undefined";
|
|
3100
|
+
} else {
|
|
3101
|
+
extractedContent = String(extractedValue);
|
|
3102
|
+
}
|
|
3103
|
+
processedResources.push({
|
|
3104
|
+
...resource,
|
|
3105
|
+
content: extractedContent
|
|
3106
|
+
});
|
|
3107
|
+
if (options.verbose) {
|
|
3108
|
+
console.log(` \u2713 \u4ECE ${absolutePath} \u63D0\u53D6 key "${options.key}": ${result.type} \u7C7B\u578B`);
|
|
3109
|
+
}
|
|
3110
|
+
} catch (error) {
|
|
3111
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3112
|
+
throw new Error(`\u65E0\u6CD5\u4ECE\u6587\u4EF6 ${absolutePath} \u63D0\u53D6 key "${options.key}": ${errorMessage}`);
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
return processedResources;
|
|
3117
|
+
}
|
|
3118
|
+
/**
|
|
3119
|
+
* 处理 Key 验证逻辑
|
|
3120
|
+
*/
|
|
3121
|
+
async handleKeyValidation(resources, files, options) {
|
|
3122
|
+
const validator = new KeyValidator();
|
|
3123
|
+
for (let i = 0; i < resources.length; i++) {
|
|
3124
|
+
const file = files[i];
|
|
3125
|
+
const absolutePath = file.filePath;
|
|
3126
|
+
try {
|
|
3127
|
+
const fileContent = await fs4.readFile(absolutePath, "utf-8");
|
|
3128
|
+
const data = JSON.parse(fileContent);
|
|
3129
|
+
const keyPaths = Array.isArray(options.key) ? options.key : [options.key];
|
|
3130
|
+
const isBatch = keyPaths.length > 1 || typeof options.key === "string" && options.key.includes(",");
|
|
3131
|
+
let result;
|
|
3132
|
+
if (isBatch) {
|
|
3133
|
+
const batchResult = validator.validateBatch(data, keyPaths);
|
|
3134
|
+
console.log(validator.formatBatchResult(batchResult));
|
|
3135
|
+
result = { allExists: batchResult.allExists };
|
|
3136
|
+
} else {
|
|
3137
|
+
const singleResult = validator.validate(data, options.key);
|
|
3138
|
+
console.log(validator.formatResult(singleResult));
|
|
3139
|
+
result = { allExists: singleResult.exists };
|
|
3140
|
+
}
|
|
3141
|
+
if (!result.allExists) {
|
|
3142
|
+
process.exit(1);
|
|
3143
|
+
}
|
|
3144
|
+
return [];
|
|
3145
|
+
} catch (error) {
|
|
3146
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3147
|
+
console.error(`\u274C \u9A8C\u8BC1\u5931\u8D25: ${errorMessage}`);
|
|
3148
|
+
process.exit(1);
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
return [];
|
|
3152
|
+
}
|
|
1639
3153
|
/**
|
|
1640
3154
|
* 构建属性列表(包括环境变量)
|
|
1641
3155
|
*/
|
|
1642
3156
|
buildProperties(options) {
|
|
1643
|
-
const
|
|
3157
|
+
const properties2 = [];
|
|
3158
|
+
properties2.push({
|
|
3159
|
+
name: "version",
|
|
3160
|
+
value: "1.0"
|
|
3161
|
+
});
|
|
1644
3162
|
if (options.envVars && Object.keys(options.envVars).length > 0) {
|
|
1645
3163
|
for (const [name, value] of Object.entries(options.envVars)) {
|
|
1646
3164
|
const envValue = value !== void 0 ? value : process.env[name] || "";
|
|
1647
|
-
|
|
3165
|
+
properties2.push({
|
|
1648
3166
|
name,
|
|
1649
3167
|
value: envValue,
|
|
1650
3168
|
isExtended: true
|
|
1651
3169
|
});
|
|
1652
3170
|
}
|
|
1653
3171
|
}
|
|
1654
|
-
return
|
|
3172
|
+
return properties2;
|
|
1655
3173
|
}
|
|
1656
3174
|
/**
|
|
1657
3175
|
* 保存 URPF 文件
|
|
@@ -1659,10 +3177,10 @@ var PackCommand = class {
|
|
|
1659
3177
|
async saveURPFDocument(document, outputPath, options) {
|
|
1660
3178
|
const generator = new URPFGenerator();
|
|
1661
3179
|
const result = await generator.generate(document);
|
|
1662
|
-
const outputDir =
|
|
1663
|
-
await
|
|
3180
|
+
const outputDir = path3.dirname(outputPath);
|
|
3181
|
+
await fs4.mkdir(outputDir, { recursive: true });
|
|
1664
3182
|
try {
|
|
1665
|
-
await
|
|
3183
|
+
await fs4.access(outputPath);
|
|
1666
3184
|
if (!options.force) {
|
|
1667
3185
|
throw new Error(`\u8F93\u51FA\u6587\u4EF6\u5DF2\u5B58\u5728: ${outputPath}\u3002\u8BF7\u4F7F\u7528 --force \u9009\u9879\u5F3A\u5236\u8986\u76D6\uFF0C\u6216\u6307\u5B9A\u4E0D\u540C\u7684\u8F93\u51FA\u8DEF\u5F84\u3002`);
|
|
1668
3186
|
}
|
|
@@ -1671,7 +3189,7 @@ var PackCommand = class {
|
|
|
1671
3189
|
throw error;
|
|
1672
3190
|
}
|
|
1673
3191
|
}
|
|
1674
|
-
await
|
|
3192
|
+
await fs4.writeFile(outputPath, result.content, "utf-8");
|
|
1675
3193
|
return result;
|
|
1676
3194
|
}
|
|
1677
3195
|
/**
|
|
@@ -1690,7 +3208,7 @@ var PackCommand = class {
|
|
|
1690
3208
|
/**
|
|
1691
3209
|
* 处理输出选项(stdout 和 clipboard)
|
|
1692
3210
|
*/
|
|
1693
|
-
async handleOutputOptions(content,
|
|
3211
|
+
async handleOutputOptions(content, _outputPath, options) {
|
|
1694
3212
|
if (options.stdout) {
|
|
1695
3213
|
console.log(content);
|
|
1696
3214
|
}
|
|
@@ -1708,9 +3226,9 @@ var PackCommand = class {
|
|
|
1708
3226
|
}
|
|
1709
3227
|
};
|
|
1710
3228
|
|
|
1711
|
-
// src/commands/unpack.ts
|
|
1712
|
-
import
|
|
1713
|
-
import
|
|
3229
|
+
// src/commands/unpack.ts
|
|
3230
|
+
import path4 from "path";
|
|
3231
|
+
import fs5 from "fs/promises";
|
|
1714
3232
|
|
|
1715
3233
|
// src/core/parser/types.ts
|
|
1716
3234
|
var URPFParseError = class extends Error {
|
|
@@ -1766,12 +3284,11 @@ var BoundaryParser = class _BoundaryParser {
|
|
|
1766
3284
|
return _BoundaryParser.BOUNDARY_REGEX.test(line);
|
|
1767
3285
|
}
|
|
1768
3286
|
/**
|
|
1769
|
-
* 生成边界行
|
|
1770
3287
|
* @param token 边界令牌
|
|
1771
3288
|
* @param type 边界类型
|
|
1772
3289
|
* @returns 边界行字符串
|
|
1773
3290
|
*/
|
|
1774
|
-
static generateBoundaryLine(token,
|
|
3291
|
+
static generateBoundaryLine(token, _type) {
|
|
1775
3292
|
return `${_BoundaryParser.BOUNDARY_PREFIX}${token}${_BoundaryParser.BOUNDARY_SUFFIX}`;
|
|
1776
3293
|
}
|
|
1777
3294
|
/**
|
|
@@ -1873,11 +3390,11 @@ var BoundaryParser = class _BoundaryParser {
|
|
|
1873
3390
|
var PropertyParser = class _PropertyParser {
|
|
1874
3391
|
static {
|
|
1875
3392
|
/** 简单变量名正则表达式($name 或 $env:name) */
|
|
1876
|
-
this.SIMPLE_NAME_REGEX = /^\$(?:([a-zA-Z0-9_]+)|(env:)([a-zA-Z0-9_]+))$/;
|
|
3393
|
+
this.SIMPLE_NAME_REGEX = /^\$(?:([a-zA-Z0-9_-]+)|(env:)([a-zA-Z0-9_-]+))$/;
|
|
1877
3394
|
}
|
|
1878
3395
|
static {
|
|
1879
3396
|
/** 带空格变量名正则表达式(${full name} 或 ${env:full name}) */
|
|
1880
|
-
this.EXTENDED_NAME_REGEX = /^\$\{(?:([a-zA-Z0-9_ ]+)|(env:)([a-zA-Z0-9_ ]+))\}$/;
|
|
3397
|
+
this.EXTENDED_NAME_REGEX = /^\$\{(?:([a-zA-Z0-9_ -]+)|(env:)([a-zA-Z0-9_ -]+))\}$/;
|
|
1881
3398
|
}
|
|
1882
3399
|
static {
|
|
1883
3400
|
/** 属性行正则表达式 */
|
|
@@ -1943,7 +3460,7 @@ var PropertyParser = class _PropertyParser {
|
|
|
1943
3460
|
*/
|
|
1944
3461
|
static parsePropertySection(content, startLineNo = 1) {
|
|
1945
3462
|
const lines = content.split("\n");
|
|
1946
|
-
const
|
|
3463
|
+
const properties2 = [];
|
|
1947
3464
|
const nameSet = /* @__PURE__ */ new Set();
|
|
1948
3465
|
lines.forEach((line, index) => {
|
|
1949
3466
|
const lineNo = startLineNo + index;
|
|
@@ -1959,9 +3476,9 @@ var PropertyParser = class _PropertyParser {
|
|
|
1959
3476
|
);
|
|
1960
3477
|
}
|
|
1961
3478
|
nameSet.add(property.name);
|
|
1962
|
-
|
|
3479
|
+
properties2.push(property);
|
|
1963
3480
|
});
|
|
1964
|
-
return { properties };
|
|
3481
|
+
return { properties: properties2 };
|
|
1965
3482
|
}
|
|
1966
3483
|
/**
|
|
1967
3484
|
* 检查内容是否为属性部分
|
|
@@ -2212,9 +3729,9 @@ var VariableReplacer = class _VariableReplacer {
|
|
|
2212
3729
|
* @param properties 属性列表
|
|
2213
3730
|
* @returns 变量上下文
|
|
2214
3731
|
*/
|
|
2215
|
-
static createContext(
|
|
3732
|
+
static createContext(properties2) {
|
|
2216
3733
|
const map = /* @__PURE__ */ new Map();
|
|
2217
|
-
|
|
3734
|
+
properties2.forEach((p) => map.set(p.name, p.value));
|
|
2218
3735
|
return {
|
|
2219
3736
|
get(name) {
|
|
2220
3737
|
return map.get(name);
|
|
@@ -2394,8 +3911,8 @@ var URPFParser = class _URPFParser {
|
|
|
2394
3911
|
* @throws {URPFParseError} 如果解析失败
|
|
2395
3912
|
*/
|
|
2396
3913
|
static async parseFile(filePath, options = {}) {
|
|
2397
|
-
const
|
|
2398
|
-
const content = await
|
|
3914
|
+
const fs15 = await import("fs/promises");
|
|
3915
|
+
const content = await fs15.readFile(filePath, "utf-8");
|
|
2399
3916
|
return _URPFParser.parse(content, options);
|
|
2400
3917
|
}
|
|
2401
3918
|
/**
|
|
@@ -2463,7 +3980,7 @@ function matchesAnyPattern(filePath, patterns) {
|
|
|
2463
3980
|
if (filePath.startsWith(pattern + "/")) {
|
|
2464
3981
|
return true;
|
|
2465
3982
|
}
|
|
2466
|
-
const dirMatch = regex.test(
|
|
3983
|
+
const dirMatch = regex.test(path4.dirname(filePath));
|
|
2467
3984
|
if (dirMatch) {
|
|
2468
3985
|
return true;
|
|
2469
3986
|
}
|
|
@@ -2508,14 +4025,14 @@ var UnpackCommand = class {
|
|
|
2508
4025
|
async execute(urpfFilePath, options = {}) {
|
|
2509
4026
|
const startTime = Date.now();
|
|
2510
4027
|
try {
|
|
2511
|
-
const resolvedPath =
|
|
4028
|
+
const resolvedPath = path4.resolve(urpfFilePath);
|
|
2512
4029
|
await this.validateURPFFile(resolvedPath);
|
|
2513
4030
|
const actualPath = await findURPFFile(resolvedPath);
|
|
2514
4031
|
if (actualPath === null) {
|
|
2515
4032
|
throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${resolvedPath}`);
|
|
2516
4033
|
}
|
|
2517
|
-
const outputDir = options.output ?
|
|
2518
|
-
await
|
|
4034
|
+
const outputDir = options.output ? path4.resolve(options.output) : process.cwd();
|
|
4035
|
+
await fs5.mkdir(outputDir, { recursive: true });
|
|
2519
4036
|
const parseResult = await URPFParser.parseFile(actualPath);
|
|
2520
4037
|
const envVars = parseResult.document.propertySection ? extractEnvironmentVariables(parseResult.document.propertySection) : [];
|
|
2521
4038
|
const vars = parseResult.document.propertySection ? extractVariables(parseResult.document.propertySection) : [];
|
|
@@ -2574,7 +4091,7 @@ var UnpackCommand = class {
|
|
|
2574
4091
|
}
|
|
2575
4092
|
return {
|
|
2576
4093
|
success: false,
|
|
2577
|
-
outputDir: options.output ?
|
|
4094
|
+
outputDir: options.output ? path4.resolve(options.output) : process.cwd(),
|
|
2578
4095
|
files: [],
|
|
2579
4096
|
createdCount: 0,
|
|
2580
4097
|
overwrittenCount: 0,
|
|
@@ -2674,7 +4191,7 @@ var UnpackCommand = class {
|
|
|
2674
4191
|
}
|
|
2675
4192
|
throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`);
|
|
2676
4193
|
}
|
|
2677
|
-
const stats = await
|
|
4194
|
+
const stats = await fs5.stat(actualPath);
|
|
2678
4195
|
if (!stats.isFile()) {
|
|
2679
4196
|
throw new Error(`\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6: ${actualPath}`);
|
|
2680
4197
|
}
|
|
@@ -2685,15 +4202,16 @@ var UnpackCommand = class {
|
|
|
2685
4202
|
async extractResource(resource, outputDir, options, variableContext) {
|
|
2686
4203
|
try {
|
|
2687
4204
|
const filePath = this.extractFilePath(resource.header.udrsReference.uri);
|
|
2688
|
-
const
|
|
2689
|
-
const
|
|
2690
|
-
|
|
4205
|
+
const actualFilePath = filePath || `resource-${Date.now()}.txt`;
|
|
4206
|
+
const fullPath = path4.join(outputDir, actualFilePath);
|
|
4207
|
+
const parentDir = path4.dirname(fullPath);
|
|
4208
|
+
await fs5.mkdir(parentDir, { recursive: true });
|
|
2691
4209
|
const fileExists = await this.fileExists(fullPath);
|
|
2692
4210
|
if (fileExists) {
|
|
2693
4211
|
if (options.force) {
|
|
2694
4212
|
await this.writeFile(resource, fullPath, options, variableContext);
|
|
2695
4213
|
return {
|
|
2696
|
-
path:
|
|
4214
|
+
path: actualFilePath,
|
|
2697
4215
|
success: true,
|
|
2698
4216
|
action: "overwritten"
|
|
2699
4217
|
};
|
|
@@ -2703,27 +4221,27 @@ var UnpackCommand = class {
|
|
|
2703
4221
|
if (sourceMtime > targetMtime) {
|
|
2704
4222
|
await this.writeFile(resource, fullPath, options, variableContext);
|
|
2705
4223
|
return {
|
|
2706
|
-
path:
|
|
4224
|
+
path: actualFilePath,
|
|
2707
4225
|
success: true,
|
|
2708
4226
|
action: "overwritten"
|
|
2709
4227
|
};
|
|
2710
4228
|
} else {
|
|
2711
4229
|
if (options.verbose) {
|
|
2712
|
-
console.log(`\u8DF3\u8FC7\uFF08\u76EE\u6807\u6587\u4EF6\u66F4\u65B0\uFF09: ${
|
|
4230
|
+
console.log(`\u8DF3\u8FC7\uFF08\u76EE\u6807\u6587\u4EF6\u66F4\u65B0\uFF09: ${actualFilePath}`);
|
|
2713
4231
|
}
|
|
2714
4232
|
return {
|
|
2715
|
-
path:
|
|
4233
|
+
path: actualFilePath,
|
|
2716
4234
|
success: true,
|
|
2717
4235
|
action: "skipped"
|
|
2718
4236
|
};
|
|
2719
4237
|
}
|
|
2720
4238
|
} else {
|
|
2721
|
-
throw new Error(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${
|
|
4239
|
+
throw new Error(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${actualFilePath}\u3002\u8BF7\u4F7F\u7528 --force \u9009\u9879\u5F3A\u5236\u8986\u76D6\uFF0C\u6216\u4F7F\u7528 --force-when-newer \u9009\u9879\u4EC5\u8986\u76D6\u66F4\u65B0\u7684\u6587\u4EF6\u3002`);
|
|
2722
4240
|
}
|
|
2723
4241
|
} else {
|
|
2724
4242
|
await this.writeFile(resource, fullPath, options, variableContext);
|
|
2725
4243
|
return {
|
|
2726
|
-
path:
|
|
4244
|
+
path: actualFilePath,
|
|
2727
4245
|
success: true,
|
|
2728
4246
|
action: "created"
|
|
2729
4247
|
};
|
|
@@ -2731,7 +4249,7 @@ var UnpackCommand = class {
|
|
|
2731
4249
|
} catch (error) {
|
|
2732
4250
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2733
4251
|
return {
|
|
2734
|
-
path: this.extractFilePath(resource.header.udrsReference.uri)
|
|
4252
|
+
path: this.extractFilePath(resource.header.udrsReference.uri) || `resource-${Date.now()}.txt`,
|
|
2735
4253
|
success: false,
|
|
2736
4254
|
action: "error",
|
|
2737
4255
|
error: errorMessage
|
|
@@ -2747,7 +4265,6 @@ var UnpackCommand = class {
|
|
|
2747
4265
|
filePath = filePath.substring(1);
|
|
2748
4266
|
}
|
|
2749
4267
|
if (/^[A-Za-z]:/.test(filePath)) {
|
|
2750
|
-
filePath = filePath;
|
|
2751
4268
|
}
|
|
2752
4269
|
return filePath;
|
|
2753
4270
|
}
|
|
@@ -2756,7 +4273,7 @@ var UnpackCommand = class {
|
|
|
2756
4273
|
*/
|
|
2757
4274
|
async fileExists(filePath) {
|
|
2758
4275
|
try {
|
|
2759
|
-
await
|
|
4276
|
+
await fs5.access(filePath);
|
|
2760
4277
|
return true;
|
|
2761
4278
|
} catch {
|
|
2762
4279
|
return false;
|
|
@@ -2766,7 +4283,7 @@ var UnpackCommand = class {
|
|
|
2766
4283
|
* 获取文件修改时间
|
|
2767
4284
|
*/
|
|
2768
4285
|
async getFileMtime(filePath) {
|
|
2769
|
-
const stats = await
|
|
4286
|
+
const stats = await fs5.stat(filePath);
|
|
2770
4287
|
return stats.mtime;
|
|
2771
4288
|
}
|
|
2772
4289
|
/**
|
|
@@ -2774,7 +4291,7 @@ var UnpackCommand = class {
|
|
|
2774
4291
|
* 注意:URPF 规范中不直接存储 mtime,这里我们使用一个合理的时间戳
|
|
2775
4292
|
* 在实际应用中,可能需要从 URPF 文件的属性部分读取
|
|
2776
4293
|
*/
|
|
2777
|
-
extractMtime(
|
|
4294
|
+
extractMtime(_resource) {
|
|
2778
4295
|
return new Date(Date.now() - 36e5);
|
|
2779
4296
|
}
|
|
2780
4297
|
/**
|
|
@@ -2795,7 +4312,21 @@ var UnpackCommand = class {
|
|
|
2795
4312
|
content = resource.content;
|
|
2796
4313
|
} else {
|
|
2797
4314
|
content = resource.content;
|
|
2798
|
-
if (options.
|
|
4315
|
+
if (options.key) {
|
|
4316
|
+
try {
|
|
4317
|
+
const extractedContent = await this.applyKeyExtraction(
|
|
4318
|
+
content,
|
|
4319
|
+
filePath,
|
|
4320
|
+
options.key,
|
|
4321
|
+
options.verbose ?? false
|
|
4322
|
+
);
|
|
4323
|
+
content = extractedContent;
|
|
4324
|
+
} catch (error) {
|
|
4325
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4326
|
+
throw new Error(`\u65E0\u6CD5\u4ECE\u6587\u4EF6 ${filePath} \u63D0\u53D6 key "${options.key}": ${errorMessage}`);
|
|
4327
|
+
}
|
|
4328
|
+
}
|
|
4329
|
+
if (options.applyVariables === true && variableContext) {
|
|
2799
4330
|
try {
|
|
2800
4331
|
const originalContent = content;
|
|
2801
4332
|
content = VariableReplacer.replace(originalContent, variableContext);
|
|
@@ -2813,24 +4344,176 @@ var UnpackCommand = class {
|
|
|
2813
4344
|
}
|
|
2814
4345
|
}
|
|
2815
4346
|
if (Buffer.isBuffer(content)) {
|
|
2816
|
-
await
|
|
4347
|
+
await fs5.writeFile(filePath, content);
|
|
2817
4348
|
} else {
|
|
2818
|
-
await
|
|
4349
|
+
await fs5.writeFile(filePath, content, {
|
|
2819
4350
|
encoding: resource.header.encoding
|
|
2820
4351
|
});
|
|
2821
4352
|
}
|
|
2822
4353
|
if (resource.header.permissions) {
|
|
2823
4354
|
const permissions = parseInt(resource.header.permissions, 8);
|
|
2824
|
-
await
|
|
4355
|
+
await fs5.chmod(filePath, permissions);
|
|
2825
4356
|
}
|
|
2826
4357
|
if (options.preserveMtime) {
|
|
2827
4358
|
const mtime = this.extractMtime(resource);
|
|
2828
|
-
await
|
|
4359
|
+
await fs5.utimes(filePath, mtime, mtime);
|
|
2829
4360
|
}
|
|
2830
4361
|
if (options.verbose) {
|
|
2831
4362
|
console.log(`\u5DF2${await this.fileExists(filePath) ? "\u8986\u76D6" : "\u521B\u5EFA"}: ${filePath}`);
|
|
2832
4363
|
}
|
|
2833
4364
|
}
|
|
4365
|
+
/**
|
|
4366
|
+
* 应用 key 提取到文件内容
|
|
4367
|
+
* @param content - 文件内容
|
|
4368
|
+
* @param filePath - 文件路径
|
|
4369
|
+
* @param keyPath - key 路径(可以是字符串或字符串数组,支持通配符和逗号分隔)
|
|
4370
|
+
* @param verbose - 是否显示详细日志
|
|
4371
|
+
* @param format - 输出格式(可选)
|
|
4372
|
+
* @param validate - 是否仅验证(可选)
|
|
4373
|
+
* @returns 提取后的内容
|
|
4374
|
+
*/
|
|
4375
|
+
async applyKeyExtraction(content, filePath, keyPath, verbose, format, validate) {
|
|
4376
|
+
let fileFormat = this.detectFileFormat(filePath);
|
|
4377
|
+
if (!fileFormat) {
|
|
4378
|
+
fileFormat = this.detectFormatFromContent(content);
|
|
4379
|
+
}
|
|
4380
|
+
if (!fileFormat) {
|
|
4381
|
+
console.warn(`\u26A0\uFE0F \u8B66\u544A: \u6587\u4EF6 ${filePath} \u4E0D\u662F\u652F\u6301\u7684\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\uFF0C\u8DF3\u8FC7 key \u63D0\u53D6`);
|
|
4382
|
+
return content;
|
|
4383
|
+
}
|
|
4384
|
+
const tempDir = path4.join(process.cwd(), ".urpf-temp");
|
|
4385
|
+
await fs5.mkdir(tempDir, { recursive: true });
|
|
4386
|
+
const tempFilePath = path4.join(tempDir, `temp.${fileFormat}`);
|
|
4387
|
+
try {
|
|
4388
|
+
await fs5.writeFile(tempFilePath, content, "utf-8");
|
|
4389
|
+
if (validate) {
|
|
4390
|
+
return this.applyKeyValidation(tempFilePath, filePath, keyPath, verbose);
|
|
4391
|
+
}
|
|
4392
|
+
const formatter = format ? new OutputFormatter() : null;
|
|
4393
|
+
const isBatch = Array.isArray(keyPath) || typeof keyPath === "string" && keyPath.includes(",");
|
|
4394
|
+
let extractedContent;
|
|
4395
|
+
if (isBatch) {
|
|
4396
|
+
const batchExtractor = new BatchKeyExtractor({
|
|
4397
|
+
outputFormat: "array",
|
|
4398
|
+
enableWildcard: true
|
|
4399
|
+
});
|
|
4400
|
+
const result = await batchExtractor.extractFromFile(tempFilePath, keyPath);
|
|
4401
|
+
const extractedData = result.results.map((r) => r.value);
|
|
4402
|
+
if (formatter && format) {
|
|
4403
|
+
extractedContent = formatter.format(extractedData, { format });
|
|
4404
|
+
} else {
|
|
4405
|
+
extractedContent = batchExtractor.formatOutput(result);
|
|
4406
|
+
}
|
|
4407
|
+
if (verbose) {
|
|
4408
|
+
batchExtractor.printSummary(result);
|
|
4409
|
+
console.log(` \u2713 \u4ECE ${filePath} \u6279\u91CF\u63D0\u53D6 ${result.successCount} \u4E2A key`);
|
|
4410
|
+
}
|
|
4411
|
+
if (result.errors.length > 0) {
|
|
4412
|
+
throw new Error(`\u6279\u91CF\u63D0\u53D6\u5931\u8D25: ${result.errors[0].error}`);
|
|
4413
|
+
}
|
|
4414
|
+
} else {
|
|
4415
|
+
const keyExtractor = new KeyExtractor({
|
|
4416
|
+
onError: (error) => {
|
|
4417
|
+
throw error;
|
|
4418
|
+
}
|
|
4419
|
+
});
|
|
4420
|
+
const result = await keyExtractor.extractFromFile(tempFilePath, keyPath);
|
|
4421
|
+
const extractedValue = result.value;
|
|
4422
|
+
if (formatter && format) {
|
|
4423
|
+
extractedContent = formatter.format(extractedValue, { format });
|
|
4424
|
+
} else if (typeof extractedValue === "object" && extractedValue !== null) {
|
|
4425
|
+
extractedContent = JSON.stringify(extractedValue, null, 2);
|
|
4426
|
+
} else if (typeof extractedValue === "string") {
|
|
4427
|
+
extractedContent = extractedValue;
|
|
4428
|
+
} else if (extractedValue === null) {
|
|
4429
|
+
extractedContent = "null";
|
|
4430
|
+
} else if (extractedValue === void 0) {
|
|
4431
|
+
extractedContent = "undefined";
|
|
4432
|
+
} else {
|
|
4433
|
+
extractedContent = String(extractedValue);
|
|
4434
|
+
}
|
|
4435
|
+
if (verbose) {
|
|
4436
|
+
console.log(` \u2713 \u4ECE ${filePath} \u63D0\u53D6 key "${keyPath}": ${result.type} \u7C7B\u578B`);
|
|
4437
|
+
}
|
|
4438
|
+
}
|
|
4439
|
+
return extractedContent;
|
|
4440
|
+
} finally {
|
|
4441
|
+
try {
|
|
4442
|
+
await fs5.unlink(tempFilePath);
|
|
4443
|
+
} catch {
|
|
4444
|
+
}
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4447
|
+
/**
|
|
4448
|
+
* 应用 Key 验证逻辑
|
|
4449
|
+
*/
|
|
4450
|
+
async applyKeyValidation(tempFilePath, _filePath, keyPath, _verbose) {
|
|
4451
|
+
const validator = new KeyValidator();
|
|
4452
|
+
try {
|
|
4453
|
+
const fileContent = await fs5.readFile(tempFilePath, "utf-8");
|
|
4454
|
+
const data = JSON.parse(fileContent);
|
|
4455
|
+
const keyPaths = Array.isArray(keyPath) ? keyPath : [keyPath];
|
|
4456
|
+
const isBatch = keyPaths.length > 1 || typeof keyPath === "string" && keyPath.includes(",");
|
|
4457
|
+
if (isBatch) {
|
|
4458
|
+
const batchResult = validator.validateBatch(data, keyPaths);
|
|
4459
|
+
console.log(validator.formatBatchResult(batchResult));
|
|
4460
|
+
} else {
|
|
4461
|
+
const singleResult = validator.validate(data, keyPath);
|
|
4462
|
+
console.log(validator.formatResult(singleResult));
|
|
4463
|
+
}
|
|
4464
|
+
return fileContent;
|
|
4465
|
+
} catch (error) {
|
|
4466
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4467
|
+
console.error(`\u274C \u9A8C\u8BC1\u5931\u8D25: ${errorMessage}`);
|
|
4468
|
+
throw error;
|
|
4469
|
+
}
|
|
4470
|
+
}
|
|
4471
|
+
/**
|
|
4472
|
+
* 检测文件格式
|
|
4473
|
+
* @param filePath - 文件路径
|
|
4474
|
+
* @returns 文件格式,如果不支持则返回 null
|
|
4475
|
+
*/
|
|
4476
|
+
detectFileFormat(filePath) {
|
|
4477
|
+
const ext = path4.extname(filePath).toLowerCase();
|
|
4478
|
+
const supportedFormats = [
|
|
4479
|
+
".json",
|
|
4480
|
+
".yaml",
|
|
4481
|
+
".yml",
|
|
4482
|
+
".toml",
|
|
4483
|
+
".xml",
|
|
4484
|
+
".properties",
|
|
4485
|
+
".ini"
|
|
4486
|
+
];
|
|
4487
|
+
if (supportedFormats.includes(ext)) {
|
|
4488
|
+
return ext.substring(1);
|
|
4489
|
+
}
|
|
4490
|
+
return null;
|
|
4491
|
+
}
|
|
4492
|
+
/**
|
|
4493
|
+
* 从内容检测文件格式
|
|
4494
|
+
* @param content - 文件内容
|
|
4495
|
+
* @returns 文件格式,如果不支持则返回 null
|
|
4496
|
+
*/
|
|
4497
|
+
detectFormatFromContent(content) {
|
|
4498
|
+
const trimmedContent = content.trim();
|
|
4499
|
+
if (trimmedContent.startsWith("{") || trimmedContent.startsWith("[")) {
|
|
4500
|
+
try {
|
|
4501
|
+
JSON.parse(trimmedContent);
|
|
4502
|
+
return "json";
|
|
4503
|
+
} catch {
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4506
|
+
if (/^[a-zA-Z_][a-zA-Z0-9_]*\s*:/.test(trimmedContent)) {
|
|
4507
|
+
return "yaml";
|
|
4508
|
+
}
|
|
4509
|
+
if (trimmedContent.startsWith("<?xml") || trimmedContent.startsWith("<")) {
|
|
4510
|
+
return "xml";
|
|
4511
|
+
}
|
|
4512
|
+
if (/^[a-zA-Z0-9_-]+\s*=\s*/.test(trimmedContent)) {
|
|
4513
|
+
return "properties";
|
|
4514
|
+
}
|
|
4515
|
+
return null;
|
|
4516
|
+
}
|
|
2834
4517
|
/**
|
|
2835
4518
|
* 输出详细日志
|
|
2836
4519
|
*/
|
|
@@ -2859,8 +4542,8 @@ var UnpackCommand = class {
|
|
|
2859
4542
|
};
|
|
2860
4543
|
|
|
2861
4544
|
// src/commands/pack-add.ts
|
|
2862
|
-
import
|
|
2863
|
-
import
|
|
4545
|
+
import path5 from "path";
|
|
4546
|
+
import fs6 from "fs/promises";
|
|
2864
4547
|
var PackAddCommand = class {
|
|
2865
4548
|
/**
|
|
2866
4549
|
* 执行 pack add 命令(支持多文件/目录添加)
|
|
@@ -2871,7 +4554,7 @@ var PackAddCommand = class {
|
|
|
2871
4554
|
async execute(urpfFilePath, targetPaths, options = {}) {
|
|
2872
4555
|
const startTime = Date.now();
|
|
2873
4556
|
try {
|
|
2874
|
-
const resolvedUrpfPath =
|
|
4557
|
+
const resolvedUrpfPath = path5.resolve(urpfFilePath);
|
|
2875
4558
|
await this.validateUrpfFile(resolvedUrpfPath);
|
|
2876
4559
|
const actualUrpfPath = await findURPFFile(resolvedUrpfPath);
|
|
2877
4560
|
if (actualUrpfPath === null) {
|
|
@@ -2881,7 +4564,7 @@ var PackAddCommand = class {
|
|
|
2881
4564
|
if (paths.length === 0) {
|
|
2882
4565
|
throw new Error("\u81F3\u5C11\u9700\u8981\u6307\u5B9A\u4E00\u4E2A\u6587\u4EF6\u6216\u76EE\u5F55\u8DEF\u5F84");
|
|
2883
4566
|
}
|
|
2884
|
-
const urpfContent = await
|
|
4567
|
+
const urpfContent = await fs6.readFile(actualUrpfPath, "utf-8");
|
|
2885
4568
|
const parseResult = URPFParser.parse(urpfContent, { strict: false });
|
|
2886
4569
|
const document = parseResult.document;
|
|
2887
4570
|
const variableContext = document.propertySection ? VariableReplacer.createContext(document.propertySection.properties) : void 0;
|
|
@@ -2889,14 +4572,14 @@ var PackAddCommand = class {
|
|
|
2889
4572
|
const allSkippedFiles = [];
|
|
2890
4573
|
const allDirectories = [];
|
|
2891
4574
|
for (const targetPath of paths) {
|
|
2892
|
-
const resolvedTargetPath =
|
|
4575
|
+
const resolvedTargetPath = path5.resolve(targetPath);
|
|
2893
4576
|
await this.validatePath(resolvedTargetPath);
|
|
2894
4577
|
const scanResult = await this.scanFiles(resolvedTargetPath, options, variableContext);
|
|
2895
4578
|
const filesWithBasePath = scanResult.files.map((file) => {
|
|
2896
|
-
const isAbsolutePath =
|
|
4579
|
+
const isAbsolutePath = path5.isAbsolute(file.path);
|
|
2897
4580
|
return {
|
|
2898
4581
|
...file,
|
|
2899
|
-
basePath: isAbsolutePath ?
|
|
4582
|
+
basePath: isAbsolutePath ? path5.dirname(file.path) : resolvedTargetPath,
|
|
2900
4583
|
isAbsolutePath
|
|
2901
4584
|
};
|
|
2902
4585
|
});
|
|
@@ -2919,44 +4602,33 @@ var PackAddCommand = class {
|
|
|
2919
4602
|
}
|
|
2920
4603
|
const serializer = new ResourceSerializer();
|
|
2921
4604
|
const filesToAdd = allFiles.map((file) => {
|
|
4605
|
+
const fileWithBase = file;
|
|
2922
4606
|
let absoluteFilePath;
|
|
2923
|
-
if (
|
|
2924
|
-
absoluteFilePath =
|
|
4607
|
+
if (fileWithBase.isAbsolutePath) {
|
|
4608
|
+
absoluteFilePath = fileWithBase.path;
|
|
2925
4609
|
} else {
|
|
2926
|
-
absoluteFilePath =
|
|
4610
|
+
absoluteFilePath = path5.join(fileWithBase.basePath, fileWithBase.path);
|
|
2927
4611
|
}
|
|
2928
|
-
const relativePath =
|
|
4612
|
+
const relativePath = path5.relative(process.cwd(), absoluteFilePath);
|
|
2929
4613
|
return {
|
|
2930
4614
|
filePath: absoluteFilePath,
|
|
2931
|
-
metadata:
|
|
4615
|
+
metadata: fileWithBase,
|
|
2932
4616
|
udrsReference: `file://${relativePath}`
|
|
2933
4617
|
};
|
|
2934
4618
|
});
|
|
2935
4619
|
const newResources = await serializer.serializeFiles(filesToAdd);
|
|
2936
|
-
const
|
|
4620
|
+
const updatedDocument = {
|
|
2937
4621
|
boundaryToken: document.boundaryToken,
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
value: p.value,
|
|
2941
|
-
isExtended: p.isExtended
|
|
2942
|
-
})),
|
|
2943
|
-
resources: document.resourceSections.map((rs) => ({
|
|
2944
|
-
header: {
|
|
2945
|
-
udrsReference: rs.header.udrsReference.protocol + "://" + rs.header.udrsReference.uri + (rs.header.udrsReference.fragment ? "#" + rs.header.udrsReference.fragment : ""),
|
|
2946
|
-
encoding: rs.header.encoding,
|
|
2947
|
-
permissions: rs.header.permissions,
|
|
2948
|
-
newlineType: rs.header.newlineType
|
|
2949
|
-
},
|
|
2950
|
-
content: rs.content
|
|
2951
|
-
}))
|
|
4622
|
+
propertySection: document.propertySection,
|
|
4623
|
+
resourceSections: [...document.resourceSections]
|
|
2952
4624
|
};
|
|
2953
|
-
|
|
4625
|
+
updatedDocument.resourceSections = [...updatedDocument.resourceSections, ...newResources];
|
|
2954
4626
|
const generator = new URPFGenerator({
|
|
2955
|
-
boundaryToken:
|
|
4627
|
+
boundaryToken: document.boundaryToken,
|
|
2956
4628
|
includeBoundaries: true
|
|
2957
4629
|
});
|
|
2958
|
-
const result = await generator.generate(
|
|
2959
|
-
await
|
|
4630
|
+
const result = await generator.generate(updatedDocument);
|
|
4631
|
+
await fs6.writeFile(actualUrpfPath, result.content, "utf-8");
|
|
2960
4632
|
const duration = Date.now() - startTime;
|
|
2961
4633
|
const totalBytes = allFiles.reduce((sum, f) => sum + f.size, 0);
|
|
2962
4634
|
if (options.verbose) {
|
|
@@ -2982,7 +4654,7 @@ var PackAddCommand = class {
|
|
|
2982
4654
|
}
|
|
2983
4655
|
return {
|
|
2984
4656
|
success: false,
|
|
2985
|
-
urpfFilePath:
|
|
4657
|
+
urpfFilePath: path5.resolve(urpfFilePath),
|
|
2986
4658
|
addedCount: 0,
|
|
2987
4659
|
skippedCount: 0,
|
|
2988
4660
|
totalBytes: 0,
|
|
@@ -2996,7 +4668,7 @@ var PackAddCommand = class {
|
|
|
2996
4668
|
*/
|
|
2997
4669
|
async validatePath(filePath) {
|
|
2998
4670
|
try {
|
|
2999
|
-
const stats = await
|
|
4671
|
+
const stats = await fs6.stat(filePath);
|
|
3000
4672
|
if (!stats.isFile() && !stats.isDirectory()) {
|
|
3001
4673
|
throw new Error(`\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6\u6216\u76EE\u5F55: ${filePath}`);
|
|
3002
4674
|
}
|
|
@@ -3018,7 +4690,7 @@ var PackAddCommand = class {
|
|
|
3018
4690
|
}
|
|
3019
4691
|
throw new Error(`\u65E0\u6CD5\u8BBF\u95EE URPF \u6587\u4EF6: ${filePath}`);
|
|
3020
4692
|
}
|
|
3021
|
-
const stats = await
|
|
4693
|
+
const stats = await fs6.stat(actualPath);
|
|
3022
4694
|
if (!stats.isFile()) {
|
|
3023
4695
|
throw new Error(`\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6: ${actualPath}`);
|
|
3024
4696
|
}
|
|
@@ -3027,33 +4699,32 @@ var PackAddCommand = class {
|
|
|
3027
4699
|
* 扫描文件
|
|
3028
4700
|
*/
|
|
3029
4701
|
async scanFiles(rootPath, options, _variableContext) {
|
|
3030
|
-
const stats = await
|
|
4702
|
+
const stats = await fs6.stat(rootPath);
|
|
3031
4703
|
if (stats.isFile()) {
|
|
3032
4704
|
const metadata = await this.collectFileMetadata(rootPath);
|
|
3033
4705
|
return {
|
|
4706
|
+
rootPath: path5.dirname(rootPath),
|
|
3034
4707
|
files: [metadata],
|
|
3035
4708
|
directories: [],
|
|
3036
4709
|
skippedFiles: [],
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
skippedFiles: 0
|
|
3041
|
-
}
|
|
4710
|
+
startTime: Date.now(),
|
|
4711
|
+
endTime: Date.now(),
|
|
4712
|
+
duration: 0
|
|
3042
4713
|
};
|
|
3043
4714
|
}
|
|
3044
4715
|
let ignoreRules = [];
|
|
3045
4716
|
if (options.ignoreFile) {
|
|
3046
4717
|
const parser = new IgnoreRuleParser();
|
|
3047
|
-
const content = await
|
|
4718
|
+
const content = await fs6.readFile(options.ignoreFile, "utf-8");
|
|
3048
4719
|
ignoreRules = parser.parseFile(content);
|
|
3049
4720
|
} else {
|
|
3050
4721
|
const defaultIgnoreFiles = [".gitignore", ".iflowignore"];
|
|
3051
4722
|
for (const ignoreFile of defaultIgnoreFiles) {
|
|
3052
|
-
const ignorePath =
|
|
4723
|
+
const ignorePath = path5.join(rootPath, ignoreFile);
|
|
3053
4724
|
try {
|
|
3054
|
-
await
|
|
4725
|
+
await fs6.access(ignorePath);
|
|
3055
4726
|
const parser = new IgnoreRuleParser();
|
|
3056
|
-
const content = await
|
|
4727
|
+
const content = await fs6.readFile(ignorePath, "utf-8");
|
|
3057
4728
|
ignoreRules = [...ignoreRules, ...parser.parseFile(content)];
|
|
3058
4729
|
break;
|
|
3059
4730
|
} catch {
|
|
@@ -3071,22 +4742,26 @@ var PackAddCommand = class {
|
|
|
3071
4742
|
* 收集文件元数据
|
|
3072
4743
|
*/
|
|
3073
4744
|
async collectFileMetadata(filePath) {
|
|
3074
|
-
const stats = await
|
|
4745
|
+
const stats = await fs6.stat(filePath);
|
|
3075
4746
|
return {
|
|
3076
4747
|
path: filePath,
|
|
3077
|
-
name:
|
|
4748
|
+
name: path5.basename(filePath),
|
|
4749
|
+
isDirectory: false,
|
|
4750
|
+
isSymlink: false,
|
|
3078
4751
|
size: stats.size,
|
|
3079
|
-
permissions: stats.mode,
|
|
3080
|
-
mtime: stats.mtime,
|
|
4752
|
+
permissions: stats.mode.toString(8).padStart(3, "0"),
|
|
4753
|
+
mtime: stats.mtime.getTime(),
|
|
4754
|
+
ctime: stats.ctime.getTime(),
|
|
3081
4755
|
encoding: "utf-8",
|
|
3082
|
-
newlineType: "lf"
|
|
4756
|
+
newlineType: "lf",
|
|
4757
|
+
isBinary: false
|
|
3083
4758
|
};
|
|
3084
4759
|
}
|
|
3085
4760
|
};
|
|
3086
4761
|
|
|
3087
4762
|
// src/commands/pack-remove.ts
|
|
3088
|
-
import
|
|
3089
|
-
import
|
|
4763
|
+
import path6 from "path";
|
|
4764
|
+
import fs7 from "fs/promises";
|
|
3090
4765
|
var PackRemoveCommand = class {
|
|
3091
4766
|
/**
|
|
3092
4767
|
* 执行 pack remove 命令(支持多文件移除)
|
|
@@ -3097,7 +4772,7 @@ var PackRemoveCommand = class {
|
|
|
3097
4772
|
async execute(urpfFilePath, filenamePatterns, options = {}) {
|
|
3098
4773
|
const startTime = Date.now();
|
|
3099
4774
|
try {
|
|
3100
|
-
const resolvedUrpfPath =
|
|
4775
|
+
const resolvedUrpfPath = path6.resolve(urpfFilePath);
|
|
3101
4776
|
await this.validateUrpfFile(resolvedUrpfPath);
|
|
3102
4777
|
const actualUrpfPath = await findURPFFile(resolvedUrpfPath);
|
|
3103
4778
|
if (actualUrpfPath === null) {
|
|
@@ -3107,26 +4782,14 @@ var PackRemoveCommand = class {
|
|
|
3107
4782
|
if (patterns.length === 0) {
|
|
3108
4783
|
throw new Error("\u81F3\u5C11\u9700\u8981\u6307\u5B9A\u4E00\u4E2A\u6587\u4EF6\u540D\u6216\u6A21\u5F0F");
|
|
3109
4784
|
}
|
|
3110
|
-
const urpfContent = await
|
|
4785
|
+
const urpfContent = await fs7.readFile(actualUrpfPath, "utf-8");
|
|
3111
4786
|
const parseResult = URPFParser.parse(urpfContent, { strict: false });
|
|
3112
4787
|
const document = parseResult.document;
|
|
3113
4788
|
const variableContext = document.propertySection ? VariableReplacer.createContext(document.propertySection.properties) : void 0;
|
|
3114
|
-
const
|
|
4789
|
+
const updatedDocument = {
|
|
3115
4790
|
boundaryToken: document.boundaryToken,
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
value: p.value,
|
|
3119
|
-
isExtended: p.isExtended
|
|
3120
|
-
})),
|
|
3121
|
-
resources: document.resourceSections.map((rs) => ({
|
|
3122
|
-
header: {
|
|
3123
|
-
udrsReference: rs.header.udrsReference.protocol + "://" + rs.header.udrsReference.uri + (rs.header.udrsReference.fragment ? "#" + rs.header.udrsReference.fragment : ""),
|
|
3124
|
-
encoding: rs.header.encoding,
|
|
3125
|
-
permissions: rs.header.permissions,
|
|
3126
|
-
newlineType: rs.header.newlineType
|
|
3127
|
-
},
|
|
3128
|
-
content: rs.content
|
|
3129
|
-
}))
|
|
4791
|
+
propertySection: document.propertySection,
|
|
4792
|
+
resourceSections: [...document.resourceSections]
|
|
3130
4793
|
};
|
|
3131
4794
|
const removedResources = [];
|
|
3132
4795
|
let notFoundCount = 0;
|
|
@@ -3145,7 +4808,7 @@ var PackRemoveCommand = class {
|
|
|
3145
4808
|
throw error;
|
|
3146
4809
|
}
|
|
3147
4810
|
}
|
|
3148
|
-
const { removedResources: removed, notFoundCount: notFound } = this.removeResources(
|
|
4811
|
+
const { removedResources: removed, notFoundCount: notFound } = this.removeResources(updatedDocument, processedPattern);
|
|
3149
4812
|
removedResources.push(...removed);
|
|
3150
4813
|
notFoundCount += notFound;
|
|
3151
4814
|
if (options.verbose) {
|
|
@@ -3162,11 +4825,11 @@ var PackRemoveCommand = class {
|
|
|
3162
4825
|
};
|
|
3163
4826
|
}
|
|
3164
4827
|
const generator = new URPFGenerator({
|
|
3165
|
-
boundaryToken:
|
|
4828
|
+
boundaryToken: document.boundaryToken,
|
|
3166
4829
|
includeBoundaries: true
|
|
3167
4830
|
});
|
|
3168
|
-
const result = await generator.generate(
|
|
3169
|
-
await
|
|
4831
|
+
const result = await generator.generate(updatedDocument);
|
|
4832
|
+
await fs7.writeFile(actualUrpfPath, result.content, "utf-8");
|
|
3170
4833
|
const duration = Date.now() - startTime;
|
|
3171
4834
|
if (options.verbose) {
|
|
3172
4835
|
console.log(`Successfully removed ${removedResources.length} files: ${actualUrpfPath}`);
|
|
@@ -3192,7 +4855,7 @@ var PackRemoveCommand = class {
|
|
|
3192
4855
|
}
|
|
3193
4856
|
return {
|
|
3194
4857
|
success: false,
|
|
3195
|
-
urpfFilePath:
|
|
4858
|
+
urpfFilePath: path6.resolve(urpfFilePath),
|
|
3196
4859
|
removedCount: 0,
|
|
3197
4860
|
notFoundCount: 0,
|
|
3198
4861
|
duration,
|
|
@@ -3214,25 +4877,25 @@ var PackRemoveCommand = class {
|
|
|
3214
4877
|
}
|
|
3215
4878
|
throw new Error(`\u65E0\u6CD5\u8BBF\u95EE URPF \u6587\u4EF6: ${filePath}`);
|
|
3216
4879
|
}
|
|
3217
|
-
const stats = await
|
|
4880
|
+
const stats = await fs7.stat(actualPath);
|
|
3218
4881
|
if (!stats.isFile()) {
|
|
3219
4882
|
throw new Error(`\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6: ${actualPath}`);
|
|
3220
4883
|
}
|
|
3221
4884
|
}
|
|
3222
4885
|
/**
|
|
3223
|
-
|
|
3224
|
-
|
|
4886
|
+
* 从文档中移除匹配的资源
|
|
4887
|
+
*/
|
|
3225
4888
|
removeResources(document, pattern) {
|
|
3226
4889
|
const removedResources = [];
|
|
3227
4890
|
let notFoundCount = 0;
|
|
3228
4891
|
const normalizedPattern = pattern.startsWith("/") || pattern.startsWith("\\") ? pattern.slice(1) : pattern;
|
|
3229
4892
|
const normalizedPatternWithSlash = normalizedPattern.replace(/\\/g, "/");
|
|
3230
|
-
for (let i = document.
|
|
3231
|
-
const resource = document.
|
|
3232
|
-
const udrsRefString = resource.header.udrsReference;
|
|
4893
|
+
for (let i = document.resourceSections.length - 1; i >= 0; i--) {
|
|
4894
|
+
const resource = document.resourceSections[i];
|
|
4895
|
+
const udrsRefString = resource.header.udrsReference.protocol + "://" + resource.header.udrsReference.uri + (resource.header.udrsReference.fragment ? "#" + resource.header.udrsReference.fragment : "");
|
|
3233
4896
|
if (this.matchResource(udrsRefString, normalizedPatternWithSlash)) {
|
|
3234
4897
|
removedResources.push(resource);
|
|
3235
|
-
document.
|
|
4898
|
+
document.resourceSections.splice(i, 1);
|
|
3236
4899
|
}
|
|
3237
4900
|
}
|
|
3238
4901
|
if (removedResources.length === 0) {
|
|
@@ -3260,7 +4923,7 @@ var PackRemoveCommand = class {
|
|
|
3260
4923
|
};
|
|
3261
4924
|
|
|
3262
4925
|
// src/commands/list.ts
|
|
3263
|
-
import
|
|
4926
|
+
import fs8 from "fs/promises";
|
|
3264
4927
|
function wildcardToRegex2(pattern) {
|
|
3265
4928
|
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
3266
4929
|
const regexPattern = "^" + escaped.replace(/\*/g, ".*").replace(/\?/g, ".") + "$";
|
|
@@ -3334,7 +4997,7 @@ var ListCommand = class {
|
|
|
3334
4997
|
error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`
|
|
3335
4998
|
};
|
|
3336
4999
|
}
|
|
3337
|
-
const content = await
|
|
5000
|
+
const content = await fs8.readFile(actualFilePath, "utf-8");
|
|
3338
5001
|
let parseResult;
|
|
3339
5002
|
try {
|
|
3340
5003
|
parseResult = URPFParser.parse(content, { strict: false });
|
|
@@ -3443,7 +5106,7 @@ var ListCommand = class {
|
|
|
3443
5106
|
};
|
|
3444
5107
|
|
|
3445
5108
|
// src/commands/info.ts
|
|
3446
|
-
import
|
|
5109
|
+
import fs9 from "fs/promises";
|
|
3447
5110
|
function formatFileSize2(bytes) {
|
|
3448
5111
|
if (bytes === 0) return "0 B";
|
|
3449
5112
|
const k = 1024;
|
|
@@ -3513,7 +5176,7 @@ var InfoCommand = class {
|
|
|
3513
5176
|
error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`
|
|
3514
5177
|
};
|
|
3515
5178
|
}
|
|
3516
|
-
const content = await
|
|
5179
|
+
const content = await fs9.readFile(actualFilePath, "utf-8");
|
|
3517
5180
|
let parseResult;
|
|
3518
5181
|
try {
|
|
3519
5182
|
parseResult = URPFParser.parse(content, { strict: false });
|
|
@@ -3534,21 +5197,23 @@ var InfoCommand = class {
|
|
|
3534
5197
|
error: `\u89E3\u6790\u5931\u8D25: ${errorMessage}`
|
|
3535
5198
|
};
|
|
3536
5199
|
}
|
|
5200
|
+
const versionProp = parseResult.document.propertySection?.properties?.find((p) => p.name === "version");
|
|
5201
|
+
const version = versionProp ? versionProp.value : "1.0";
|
|
3537
5202
|
const info = {
|
|
3538
5203
|
fileCount: parseResult.document.resourceSections.length,
|
|
3539
5204
|
totalSize: parseResult.document.resourceSections.reduce((sum, section) => sum + section.content.length, 0),
|
|
3540
|
-
version
|
|
5205
|
+
version,
|
|
3541
5206
|
boundaryToken: parseResult.document.boundaryToken,
|
|
3542
|
-
propertyCount: parseResult.document.propertySection?.properties
|
|
5207
|
+
propertyCount: parseResult.document.propertySection?.properties?.length || 0,
|
|
3543
5208
|
resourceCount: parseResult.document.resourceSections.length
|
|
3544
5209
|
};
|
|
3545
5210
|
if (options.verbose) {
|
|
3546
5211
|
if (parseResult.document.propertySection?.properties) {
|
|
3547
|
-
const
|
|
5212
|
+
const properties2 = {};
|
|
3548
5213
|
for (const prop of parseResult.document.propertySection.properties) {
|
|
3549
|
-
|
|
5214
|
+
properties2[prop.name] = prop.value;
|
|
3550
5215
|
}
|
|
3551
|
-
info.properties =
|
|
5216
|
+
info.properties = properties2;
|
|
3552
5217
|
}
|
|
3553
5218
|
const textFiles = parseResult.document.resourceSections.filter((s) => s.header.encoding !== "binary").length;
|
|
3554
5219
|
const binaryFiles = parseResult.document.resourceSections.filter((s) => s.header.encoding === "binary").length;
|
|
@@ -3608,7 +5273,7 @@ var InfoCommand = class {
|
|
|
3608
5273
|
};
|
|
3609
5274
|
|
|
3610
5275
|
// src/commands/verify.ts
|
|
3611
|
-
import
|
|
5276
|
+
import fs10 from "fs/promises";
|
|
3612
5277
|
var URPFVerifier = class {
|
|
3613
5278
|
constructor() {
|
|
3614
5279
|
this.issues = [];
|
|
@@ -3697,7 +5362,7 @@ var URPFVerifier = class {
|
|
|
3697
5362
|
/**
|
|
3698
5363
|
* 验证属性部分
|
|
3699
5364
|
*/
|
|
3700
|
-
verifyProperties(content,
|
|
5365
|
+
verifyProperties(content, _boundaryToken) {
|
|
3701
5366
|
try {
|
|
3702
5367
|
const boundaries = BoundaryParser.findBoundaries(content);
|
|
3703
5368
|
const sections = BoundaryParser.splitByBoundaries(content, boundaries);
|
|
@@ -3716,13 +5381,12 @@ var URPFVerifier = class {
|
|
|
3716
5381
|
try {
|
|
3717
5382
|
const propertySection = PropertyParser.parsePropertySection(sections[0], boundaries[0].line + 1);
|
|
3718
5383
|
const propertyNames = /* @__PURE__ */ new Set();
|
|
3719
|
-
for (const property of
|
|
5384
|
+
for (const property of properties) {
|
|
3720
5385
|
if (propertyNames.has(property.name)) {
|
|
3721
5386
|
this.addIssue({
|
|
3722
5387
|
type: "property_duplicate",
|
|
3723
5388
|
message: `\u91CD\u590D\u7684\u5C5E\u6027\u540D: ${property.name}`,
|
|
3724
|
-
severity: "error"
|
|
3725
|
-
line: property.line
|
|
5389
|
+
severity: "error"
|
|
3726
5390
|
}, false);
|
|
3727
5391
|
}
|
|
3728
5392
|
propertyNames.add(property.name);
|
|
@@ -3782,18 +5446,6 @@ var URPFVerifier = class {
|
|
|
3782
5446
|
line: boundaries[i].line + 1
|
|
3783
5447
|
});
|
|
3784
5448
|
}
|
|
3785
|
-
const expectedSize = parseInt(resourceSection.header.udrsReference.attributes.size || "0", 10);
|
|
3786
|
-
const actualSize = resourceSection.content.length;
|
|
3787
|
-
if (expectedSize > 0 && actualSize !== expectedSize) {
|
|
3788
|
-
this.addIssue({
|
|
3789
|
-
type: "resource_size_mismatch",
|
|
3790
|
-
message: `\u6587\u4EF6\u5927\u5C0F\u4E0D\u5339\u914D: ${resourceSection.header.udrsReference.uri}`,
|
|
3791
|
-
severity: "warning",
|
|
3792
|
-
filePath: resourceSection.header.udrsReference.uri,
|
|
3793
|
-
expected: expectedSize.toString(),
|
|
3794
|
-
actual: actualSize.toString()
|
|
3795
|
-
}, true);
|
|
3796
|
-
}
|
|
3797
5449
|
const encoding = resourceSection.header.encoding;
|
|
3798
5450
|
if (!encoding) {
|
|
3799
5451
|
this.addIssue({
|
|
@@ -3824,38 +5476,8 @@ var URPFVerifier = class {
|
|
|
3824
5476
|
/**
|
|
3825
5477
|
* 尝试修复问题
|
|
3826
5478
|
*/
|
|
3827
|
-
async tryFix(content,
|
|
3828
|
-
|
|
3829
|
-
for (const issue of issues) {
|
|
3830
|
-
if (!issue.fixable) {
|
|
3831
|
-
continue;
|
|
3832
|
-
}
|
|
3833
|
-
if (issue.type === "resource_size_mismatch" && issue.filePath) {
|
|
3834
|
-
try {
|
|
3835
|
-
const boundaries = BoundaryParser.findBoundaries(fixedContent);
|
|
3836
|
-
const sections = BoundaryParser.splitByBoundaries(fixedContent, boundaries);
|
|
3837
|
-
let startSectionIndex = 0;
|
|
3838
|
-
if (sections.length > 0 && PropertyParser.isPropertySection(sections[0])) {
|
|
3839
|
-
startSectionIndex = 1;
|
|
3840
|
-
}
|
|
3841
|
-
for (let i = startSectionIndex; i < sections.length; i++) {
|
|
3842
|
-
try {
|
|
3843
|
-
const resourceSection = ResourceParser.parseResourceSection(sections[i], boundaries[i].line + 1);
|
|
3844
|
-
if (resourceSection.header.udrsReference.uri === issue.filePath) {
|
|
3845
|
-
const actualSize = resourceSection.content.length;
|
|
3846
|
-
const sizePattern = new RegExp(`(size=${issue.expected})`, "g");
|
|
3847
|
-
fixedContent = fixedContent.replace(sizePattern, `size=${actualSize}`);
|
|
3848
|
-
this.fixedCount++;
|
|
3849
|
-
break;
|
|
3850
|
-
}
|
|
3851
|
-
} catch {
|
|
3852
|
-
}
|
|
3853
|
-
}
|
|
3854
|
-
} catch {
|
|
3855
|
-
}
|
|
3856
|
-
}
|
|
3857
|
-
}
|
|
3858
|
-
return fixedContent;
|
|
5479
|
+
async tryFix(content, _issues) {
|
|
5480
|
+
return content;
|
|
3859
5481
|
}
|
|
3860
5482
|
/**
|
|
3861
5483
|
* 执行验证
|
|
@@ -3967,11 +5589,11 @@ var VerifyCommand = class {
|
|
|
3967
5589
|
error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`
|
|
3968
5590
|
};
|
|
3969
5591
|
}
|
|
3970
|
-
|
|
5592
|
+
const content = await fs10.readFile(actualFilePath, "utf-8");
|
|
3971
5593
|
const verifier = new URPFVerifier();
|
|
3972
5594
|
const verifyResult = await verifier.verify(content, options);
|
|
3973
5595
|
if (options.fix && verifyResult.fixedContent && verifyResult.fixedCount > 0) {
|
|
3974
|
-
await
|
|
5596
|
+
await fs10.writeFile(actualFilePath, verifyResult.fixedContent, "utf-8");
|
|
3975
5597
|
}
|
|
3976
5598
|
let totalFiles = 0;
|
|
3977
5599
|
let validFiles = 0;
|
|
@@ -4031,13 +5653,13 @@ var VerifyCommand = class {
|
|
|
4031
5653
|
};
|
|
4032
5654
|
|
|
4033
5655
|
// src/commands/diff.ts
|
|
4034
|
-
import
|
|
5656
|
+
import fs11 from "fs/promises";
|
|
4035
5657
|
async function extractFileInfo(filePath) {
|
|
4036
|
-
const content = await
|
|
5658
|
+
const content = await fs11.readFile(filePath, "utf-8");
|
|
4037
5659
|
let parseResult;
|
|
4038
5660
|
try {
|
|
4039
5661
|
parseResult = URPFParser.parse(content, { strict: false });
|
|
4040
|
-
} catch
|
|
5662
|
+
} catch {
|
|
4041
5663
|
return [];
|
|
4042
5664
|
}
|
|
4043
5665
|
if (!parseResult.document.resourceSections || parseResult.document.resourceSections.length === 0) {
|
|
@@ -4068,19 +5690,19 @@ function compareFileLists(files1, files2) {
|
|
|
4068
5690
|
const removed = [];
|
|
4069
5691
|
const modified = [];
|
|
4070
5692
|
const unchanged = [];
|
|
4071
|
-
for (const
|
|
4072
|
-
const file1 = fileMap1.get(
|
|
4073
|
-
const file2 = fileMap2.get(
|
|
5693
|
+
for (const path8 of allPaths) {
|
|
5694
|
+
const file1 = fileMap1.get(path8);
|
|
5695
|
+
const file2 = fileMap2.get(path8);
|
|
4074
5696
|
if (!file1 && file2) {
|
|
4075
5697
|
added.push({
|
|
4076
|
-
path:
|
|
5698
|
+
path: path8,
|
|
4077
5699
|
changeType: "added",
|
|
4078
5700
|
size2: file2.size,
|
|
4079
5701
|
encoding2: file2.encoding
|
|
4080
5702
|
});
|
|
4081
5703
|
} else if (file1 && !file2) {
|
|
4082
5704
|
removed.push({
|
|
4083
|
-
path:
|
|
5705
|
+
path: path8,
|
|
4084
5706
|
changeType: "removed",
|
|
4085
5707
|
size1: file1.size,
|
|
4086
5708
|
encoding1: file1.encoding
|
|
@@ -4088,7 +5710,7 @@ function compareFileLists(files1, files2) {
|
|
|
4088
5710
|
} else if (file1 && file2) {
|
|
4089
5711
|
if (file1.size !== file2.size || file1.encoding !== file2.encoding) {
|
|
4090
5712
|
modified.push({
|
|
4091
|
-
path:
|
|
5713
|
+
path: path8,
|
|
4092
5714
|
changeType: "modified",
|
|
4093
5715
|
size1: file1.size,
|
|
4094
5716
|
size2: file2.size,
|
|
@@ -4098,7 +5720,7 @@ function compareFileLists(files1, files2) {
|
|
|
4098
5720
|
});
|
|
4099
5721
|
} else {
|
|
4100
5722
|
unchanged.push({
|
|
4101
|
-
path:
|
|
5723
|
+
path: path8,
|
|
4102
5724
|
changeType: "unchanged",
|
|
4103
5725
|
size1: file1.size,
|
|
4104
5726
|
size2: file2.size,
|
|
@@ -4280,8 +5902,8 @@ var DiffCommand = class {
|
|
|
4280
5902
|
};
|
|
4281
5903
|
|
|
4282
5904
|
// src/commands/merge.ts
|
|
4283
|
-
import
|
|
4284
|
-
import
|
|
5905
|
+
import path7 from "path";
|
|
5906
|
+
import fs12 from "fs/promises";
|
|
4285
5907
|
var MergeCommand = class {
|
|
4286
5908
|
/**
|
|
4287
5909
|
* 执行 merge 命令
|
|
@@ -4302,10 +5924,10 @@ var MergeCommand = class {
|
|
|
4302
5924
|
parsedDocuments,
|
|
4303
5925
|
options.verbose
|
|
4304
5926
|
);
|
|
4305
|
-
const resolvedOutputPath =
|
|
4306
|
-
await
|
|
4307
|
-
await
|
|
4308
|
-
const outputStats = await
|
|
5927
|
+
const resolvedOutputPath = path7.resolve(options.output);
|
|
5928
|
+
await fs12.mkdir(path7.dirname(resolvedOutputPath), { recursive: true });
|
|
5929
|
+
await fs12.writeFile(resolvedOutputPath, outputContent, "utf-8");
|
|
5930
|
+
const outputStats = await fs12.stat(resolvedOutputPath);
|
|
4309
5931
|
const duration = Date.now() - startTime;
|
|
4310
5932
|
const result = {
|
|
4311
5933
|
success: true,
|
|
@@ -4364,7 +5986,7 @@ var MergeCommand = class {
|
|
|
4364
5986
|
async resolveInputFiles(inputPaths) {
|
|
4365
5987
|
const resolvedFiles = [];
|
|
4366
5988
|
for (const inputPath of inputPaths) {
|
|
4367
|
-
const resolvedPath =
|
|
5989
|
+
const resolvedPath = path7.resolve(inputPath);
|
|
4368
5990
|
const actualPath = await findURPFFile(resolvedPath);
|
|
4369
5991
|
if (!actualPath) {
|
|
4370
5992
|
throw new Error(`\u627E\u4E0D\u5230 URPF \u6587\u4EF6: ${inputPath}`);
|
|
@@ -4380,7 +6002,7 @@ var MergeCommand = class {
|
|
|
4380
6002
|
const documents = [];
|
|
4381
6003
|
for (const filePath of filePaths) {
|
|
4382
6004
|
try {
|
|
4383
|
-
const content = await
|
|
6005
|
+
const content = await fs12.readFile(filePath, "utf-8");
|
|
4384
6006
|
const parseResult = URPFParser.parse(content);
|
|
4385
6007
|
documents.push({
|
|
4386
6008
|
filePath,
|
|
@@ -4412,7 +6034,7 @@ var MergeCommand = class {
|
|
|
4412
6034
|
const resourceSections = document.resourceSections || [];
|
|
4413
6035
|
if (verbose) {
|
|
4414
6036
|
console.log(`
|
|
4415
|
-
\u5904\u7406\u6587\u4EF6: ${
|
|
6037
|
+
\u5904\u7406\u6587\u4EF6: ${path7.basename(filePath)} (${resourceSections.length} \u4E2A\u8D44\u6E90)`);
|
|
4416
6038
|
}
|
|
4417
6039
|
for (const section of resourceSections) {
|
|
4418
6040
|
const udrsRef = section.header.udrsReference;
|
|
@@ -4463,9 +6085,9 @@ var MergeCommand = class {
|
|
|
4463
6085
|
let counter = 1;
|
|
4464
6086
|
let newPath;
|
|
4465
6087
|
do {
|
|
4466
|
-
const ext =
|
|
4467
|
-
const baseName =
|
|
4468
|
-
const dirName =
|
|
6088
|
+
const ext = path7.extname(udrsReference);
|
|
6089
|
+
const baseName = path7.basename(udrsReference, ext);
|
|
6090
|
+
const dirName = path7.dirname(udrsReference);
|
|
4469
6091
|
const newBaseName = `${baseName}-${counter}`;
|
|
4470
6092
|
newPath = dirName === "." ? `${newBaseName}${ext}` : `${dirName}/${newBaseName}${ext}`;
|
|
4471
6093
|
counter++;
|
|
@@ -4496,11 +6118,14 @@ var MergeCommand = class {
|
|
|
4496
6118
|
*/
|
|
4497
6119
|
async generateMergedURPF(mergedResources, sourceDocuments, verbose) {
|
|
4498
6120
|
const propertySection = sourceDocuments[0]?.document?.propertySection;
|
|
4499
|
-
const
|
|
6121
|
+
const properties2 = propertySection?.properties || [];
|
|
4500
6122
|
const generatorResources = mergedResources.map((resource) => {
|
|
4501
6123
|
return {
|
|
4502
6124
|
header: {
|
|
4503
|
-
udrsReference:
|
|
6125
|
+
udrsReference: {
|
|
6126
|
+
protocol: "file",
|
|
6127
|
+
uri: resource.udrsReference
|
|
6128
|
+
},
|
|
4504
6129
|
encoding: resource.encoding,
|
|
4505
6130
|
permissions: resource.permissions,
|
|
4506
6131
|
newlineType: resource.newlineType
|
|
@@ -4510,18 +6135,23 @@ var MergeCommand = class {
|
|
|
4510
6135
|
});
|
|
4511
6136
|
const generator = new URPFGenerator();
|
|
4512
6137
|
const document = {
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
6138
|
+
boundaryToken: "",
|
|
6139
|
+
// 将由生成器自动生成
|
|
6140
|
+
propertySection: {
|
|
6141
|
+
properties: properties2.map((prop) => ({
|
|
6142
|
+
name: prop.name,
|
|
6143
|
+
value: prop.value,
|
|
6144
|
+
isExtended: false
|
|
6145
|
+
}))
|
|
6146
|
+
},
|
|
6147
|
+
resourceSections: generatorResources
|
|
4518
6148
|
};
|
|
4519
6149
|
const result = await generator.generate(document);
|
|
4520
6150
|
if (verbose) {
|
|
4521
6151
|
console.log(`
|
|
4522
6152
|
\u751F\u6210\u5B8C\u6210:`);
|
|
4523
6153
|
console.log(` \u8FB9\u754C\u4EE4\u724C: ${generator.getBoundaryToken()}`);
|
|
4524
|
-
console.log(` \u5C5E\u6027\u6570\u91CF: ${
|
|
6154
|
+
console.log(` \u5C5E\u6027\u6570\u91CF: ${properties2.length}`);
|
|
4525
6155
|
console.log(` \u8D44\u6E90\u6570\u91CF: ${generatorResources.length}`);
|
|
4526
6156
|
}
|
|
4527
6157
|
return result.content;
|
|
@@ -4544,17 +6174,17 @@ var MergeCommand = class {
|
|
|
4544
6174
|
};
|
|
4545
6175
|
|
|
4546
6176
|
// src/commands/var.ts
|
|
4547
|
-
import * as
|
|
6177
|
+
import * as fs13 from "fs/promises";
|
|
4548
6178
|
function parseResultToDocument(parseResult) {
|
|
4549
|
-
const
|
|
6179
|
+
const properties2 = {};
|
|
4550
6180
|
if (parseResult.document.propertySection) {
|
|
4551
6181
|
for (const prop of parseResult.document.propertySection.properties) {
|
|
4552
|
-
|
|
6182
|
+
properties2[prop.name] = prop.value;
|
|
4553
6183
|
}
|
|
4554
6184
|
}
|
|
4555
6185
|
return {
|
|
4556
6186
|
boundaryToken: parseResult.document.boundaryToken,
|
|
4557
|
-
properties,
|
|
6187
|
+
properties: properties2,
|
|
4558
6188
|
resources: parseResult.document.resourceSections
|
|
4559
6189
|
};
|
|
4560
6190
|
}
|
|
@@ -4585,7 +6215,7 @@ async function executeVarCommand(options) {
|
|
|
4585
6215
|
error: `URPF \u6587\u4EF6\u4E0D\u5B58\u5728: ${options.filePath}`
|
|
4586
6216
|
};
|
|
4587
6217
|
}
|
|
4588
|
-
const urpfContent = await
|
|
6218
|
+
const urpfContent = await fs13.readFile(filePath, "utf-8");
|
|
4589
6219
|
const parseResult = URPFParser.parse(urpfContent);
|
|
4590
6220
|
const urpf = parseResultToDocument(parseResult);
|
|
4591
6221
|
let result;
|
|
@@ -4664,7 +6294,7 @@ async function varAdd(urpf, filePath, options) {
|
|
|
4664
6294
|
const urpfDocument = documentToURPFDocument(urpf);
|
|
4665
6295
|
const generateResult = await generator.generate(urpfDocument);
|
|
4666
6296
|
const newURPFContent = generateResult.content;
|
|
4667
|
-
await
|
|
6297
|
+
await fs13.writeFile(filePath, newURPFContent, "utf-8");
|
|
4668
6298
|
return {
|
|
4669
6299
|
success: true,
|
|
4670
6300
|
action: "add",
|
|
@@ -4677,8 +6307,9 @@ async function varAdd(urpf, filePath, options) {
|
|
|
4677
6307
|
async function varList(urpf, filePath, _options) {
|
|
4678
6308
|
const startTime = Date.now();
|
|
4679
6309
|
const variables = {};
|
|
6310
|
+
const systemProperties = ["version", "boundary", "encoding", "line-ending"];
|
|
4680
6311
|
for (const [key, value] of Object.entries(urpf.properties)) {
|
|
4681
|
-
if (!key.startsWith("env:")) {
|
|
6312
|
+
if (!key.startsWith("env:") && !systemProperties.includes(key)) {
|
|
4682
6313
|
variables[key] = value;
|
|
4683
6314
|
}
|
|
4684
6315
|
}
|
|
@@ -4716,7 +6347,7 @@ async function varRemove(urpf, filePath, options) {
|
|
|
4716
6347
|
const urpfDocument = documentToURPFDocument(urpf);
|
|
4717
6348
|
const generateResult = await generator.generate(urpfDocument);
|
|
4718
6349
|
const newURPFContent = generateResult.content;
|
|
4719
|
-
await
|
|
6350
|
+
await fs13.writeFile(filePath, newURPFContent, "utf-8");
|
|
4720
6351
|
return {
|
|
4721
6352
|
success: true,
|
|
4722
6353
|
action: "remove",
|
|
@@ -4747,7 +6378,7 @@ async function varClear(urpf, filePath, _options) {
|
|
|
4747
6378
|
const urpfDocument = documentToURPFDocument(urpf);
|
|
4748
6379
|
const generateResult = await generator.generate(urpfDocument);
|
|
4749
6380
|
const newURPFContent = generateResult.content;
|
|
4750
|
-
await
|
|
6381
|
+
await fs13.writeFile(filePath, newURPFContent, "utf-8");
|
|
4751
6382
|
return {
|
|
4752
6383
|
success: true,
|
|
4753
6384
|
action: "clear",
|
|
@@ -4758,17 +6389,17 @@ async function varClear(urpf, filePath, _options) {
|
|
|
4758
6389
|
}
|
|
4759
6390
|
|
|
4760
6391
|
// src/commands/env.ts
|
|
4761
|
-
import * as
|
|
6392
|
+
import * as fs14 from "fs/promises";
|
|
4762
6393
|
function parseResultToDocument2(parseResult) {
|
|
4763
|
-
const
|
|
6394
|
+
const properties2 = {};
|
|
4764
6395
|
if (parseResult.document.propertySection) {
|
|
4765
6396
|
for (const prop of parseResult.document.propertySection.properties) {
|
|
4766
|
-
|
|
6397
|
+
properties2[prop.name] = prop.value;
|
|
4767
6398
|
}
|
|
4768
6399
|
}
|
|
4769
6400
|
return {
|
|
4770
6401
|
boundaryToken: parseResult.document.boundaryToken,
|
|
4771
|
-
properties,
|
|
6402
|
+
properties: properties2,
|
|
4772
6403
|
resources: parseResult.document.resourceSections
|
|
4773
6404
|
};
|
|
4774
6405
|
}
|
|
@@ -4799,7 +6430,7 @@ async function executeEnvCommand(options) {
|
|
|
4799
6430
|
error: `URPF \u6587\u4EF6\u4E0D\u5B58\u5728: ${options.filePath}`
|
|
4800
6431
|
};
|
|
4801
6432
|
}
|
|
4802
|
-
const urpfContent = await
|
|
6433
|
+
const urpfContent = await fs14.readFile(filePath, "utf-8");
|
|
4803
6434
|
const parseResult = URPFParser.parse(urpfContent);
|
|
4804
6435
|
const urpf = parseResultToDocument2(parseResult);
|
|
4805
6436
|
let result;
|
|
@@ -4873,7 +6504,7 @@ async function envAdd(urpf, filePath, options) {
|
|
|
4873
6504
|
const urpfDocument = documentToURPFDocument2(urpf);
|
|
4874
6505
|
const generateResult = await generator.generate(urpfDocument);
|
|
4875
6506
|
const newURPFContent = generateResult.content;
|
|
4876
|
-
await
|
|
6507
|
+
await fs14.writeFile(filePath, newURPFContent, "utf-8");
|
|
4877
6508
|
return {
|
|
4878
6509
|
success: true,
|
|
4879
6510
|
action: "add",
|
|
@@ -4928,7 +6559,7 @@ async function envRemove(urpf, filePath, options) {
|
|
|
4928
6559
|
const urpfDocument = documentToURPFDocument2(urpf);
|
|
4929
6560
|
const generateResult = await generator.generate(urpfDocument);
|
|
4930
6561
|
const newURPFContent = generateResult.content;
|
|
4931
|
-
await
|
|
6562
|
+
await fs14.writeFile(filePath, newURPFContent, "utf-8");
|
|
4932
6563
|
return {
|
|
4933
6564
|
success: true,
|
|
4934
6565
|
action: "remove",
|
|
@@ -4959,7 +6590,7 @@ async function envClear(urpf, filePath, _options) {
|
|
|
4959
6590
|
const urpfDocument = documentToURPFDocument2(urpf);
|
|
4960
6591
|
const generateResult = await generator.generate(urpfDocument);
|
|
4961
6592
|
const newURPFContent = generateResult.content;
|
|
4962
|
-
await
|
|
6593
|
+
await fs14.writeFile(filePath, newURPFContent, "utf-8");
|
|
4963
6594
|
return {
|
|
4964
6595
|
success: true,
|
|
4965
6596
|
action: "clear",
|
|
@@ -4969,6 +6600,682 @@ async function envClear(urpf, filePath, _options) {
|
|
|
4969
6600
|
};
|
|
4970
6601
|
}
|
|
4971
6602
|
|
|
6603
|
+
// src/commands/completion.ts
|
|
6604
|
+
function generateBashCompletion() {
|
|
6605
|
+
const script = `#!/usr/bin/env bash
|
|
6606
|
+
|
|
6607
|
+
###-begin-urpf-cli-completions-###
|
|
6608
|
+
#
|
|
6609
|
+
# urpf-cli command completion script
|
|
6610
|
+
#
|
|
6611
|
+
# Installation: urpf-cli completion bash >> ~/.bashrc
|
|
6612
|
+
# or: urpf-cli completion bash >> ~/.bash_profile on macOS
|
|
6613
|
+
#
|
|
6614
|
+
|
|
6615
|
+
_urpf_cli_completion() {
|
|
6616
|
+
local cur words cword
|
|
6617
|
+
_init_completion || return
|
|
6618
|
+
|
|
6619
|
+
# urpf-cli \u4E3B\u547D\u4EE4
|
|
6620
|
+
if [[ $cword -eq 1 ]]; then
|
|
6621
|
+
COMPREPLY=($(compgen -W "pack unpack list info verify diff merge var env help --help --version" -- "$cur"))
|
|
6622
|
+
return
|
|
6623
|
+
fi
|
|
6624
|
+
|
|
6625
|
+
# pack \u5B50\u547D\u4EE4
|
|
6626
|
+
if [[ "$words[1]" == "pack" ]]; then
|
|
6627
|
+
if [[ $cword -eq 2 ]]; then
|
|
6628
|
+
COMPREPLY=($(compgen -W "create add remove help" -- "$cur"))
|
|
6629
|
+
return
|
|
6630
|
+
fi
|
|
6631
|
+
|
|
6632
|
+
if [[ "$words[2]" == "create" ]]; then
|
|
6633
|
+
# pack create \u5B50\u547D\u4EE4
|
|
6634
|
+
if [[ $cword -eq 3 ]]; then
|
|
6635
|
+
_filedir
|
|
6636
|
+
return
|
|
6637
|
+
fi
|
|
6638
|
+
# pack create \u9009\u9879\u8865\u5168
|
|
6639
|
+
COMPREPLY=($(compgen -W "-o --output -v --verbose -i --ignore -f --follow-symlinks -d --max-depth --no-recurse --force --byte --line -s --stdout -c --clipboard -e --env --key --format --validate --help" -- "$cur"))
|
|
6640
|
+
return
|
|
6641
|
+
fi
|
|
6642
|
+
|
|
6643
|
+
if [[ "$words[2]" == "add" ]]; then
|
|
6644
|
+
# pack add \u5B50\u547D\u4EE4
|
|
6645
|
+
if [[ $cword -eq 3 ]]; then
|
|
6646
|
+
_filedir
|
|
6647
|
+
return
|
|
6648
|
+
fi
|
|
6649
|
+
if [[ $cword -ge 4 ]]; then
|
|
6650
|
+
_filedir
|
|
6651
|
+
return
|
|
6652
|
+
fi
|
|
6653
|
+
# pack add \u9009\u9879\u8865\u5168
|
|
6654
|
+
COMPREPLY=($(compgen -W "-v --verbose -i --ignore -f --follow-symlinks -d --max-depth --no-recurse --help" -- "$cur"))
|
|
6655
|
+
return
|
|
6656
|
+
fi
|
|
6657
|
+
|
|
6658
|
+
if [[ "$words[2]" == "remove" ]]; then
|
|
6659
|
+
# pack remove \u5B50\u547D\u4EE4
|
|
6660
|
+
if [[ $cword -eq 3 ]]; then
|
|
6661
|
+
_filedir
|
|
6662
|
+
return
|
|
6663
|
+
fi
|
|
6664
|
+
if [[ $cword -ge 4 ]]; then
|
|
6665
|
+
_filedir
|
|
6666
|
+
return
|
|
6667
|
+
fi
|
|
6668
|
+
# pack remove \u9009\u9879\u8865\u5168
|
|
6669
|
+
COMPREPLY=($(compgen -W "-v --verbose --no-apply-variables --help" -- "$cur"))
|
|
6670
|
+
return
|
|
6671
|
+
fi
|
|
6672
|
+
fi
|
|
6673
|
+
|
|
6674
|
+
# unpack \u5B50\u547D\u4EE4
|
|
6675
|
+
if [[ "$words[1]" == "unpack" ]]; then
|
|
6676
|
+
if [[ $cword -eq 2 ]]; then
|
|
6677
|
+
_filedir
|
|
6678
|
+
return
|
|
6679
|
+
fi
|
|
6680
|
+
# unpack \u9009\u9879\u8865\u5168
|
|
6681
|
+
COMPREPLY=($(compgen -W "-o --output -v --verbose --force --force-when-newer --preserve-mtime --unpack-env --apply-variables --packed-path --key --format --validate --help" -- "$cur"))
|
|
6682
|
+
return
|
|
6683
|
+
fi
|
|
6684
|
+
|
|
6685
|
+
# list \u5B50\u547D\u4EE4
|
|
6686
|
+
if [[ "$words[1]" == "list" ]]; then
|
|
6687
|
+
if [[ $cword -eq 2 ]]; then
|
|
6688
|
+
_filedir
|
|
6689
|
+
return
|
|
6690
|
+
fi
|
|
6691
|
+
COMPREPLY=($(compgen -W "-v --verbose -p --pattern -j --json --help" -- "$cur"))
|
|
6692
|
+
return
|
|
6693
|
+
fi
|
|
6694
|
+
|
|
6695
|
+
# info \u5B50\u547D\u4EE4
|
|
6696
|
+
if [[ "$words[1]" == "info" ]]; then
|
|
6697
|
+
if [[ $cword -eq 2 ]]; then
|
|
6698
|
+
_filedir
|
|
6699
|
+
return
|
|
6700
|
+
fi
|
|
6701
|
+
COMPREPLY=($(compgen -W "-v --verbose -j --json --help" -- "$cur"))
|
|
6702
|
+
return
|
|
6703
|
+
fi
|
|
6704
|
+
|
|
6705
|
+
# verify \u5B50\u547D\u4EE4
|
|
6706
|
+
if [[ "$words[1]" == "verify" ]]; then
|
|
6707
|
+
if [[ $cword -eq 2 ]]; then
|
|
6708
|
+
_filedir
|
|
6709
|
+
return
|
|
6710
|
+
fi
|
|
6711
|
+
COMPREPLY=($(compgen -W "-f --fix -v --verbose -j --json --help" -- "$cur"))
|
|
6712
|
+
return
|
|
6713
|
+
fi
|
|
6714
|
+
|
|
6715
|
+
# diff \u5B50\u547D\u4EE4
|
|
6716
|
+
if [[ "$words[1]" == "diff" ]]; then
|
|
6717
|
+
if [[ $cword -eq 2 || $cword -eq 3 ]]; then
|
|
6718
|
+
_filedir
|
|
6719
|
+
return
|
|
6720
|
+
fi
|
|
6721
|
+
COMPREPLY=($(compgen -W "-v --verbose -j --json --help" -- "$cur"))
|
|
6722
|
+
return
|
|
6723
|
+
fi
|
|
6724
|
+
|
|
6725
|
+
# merge \u5B50\u547D\u4EE4
|
|
6726
|
+
if [[ "$words[1]" == "merge" ]]; then
|
|
6727
|
+
if [[ $cword -eq 2 ]]; then
|
|
6728
|
+
_filedir
|
|
6729
|
+
return
|
|
6730
|
+
fi
|
|
6731
|
+
if [[ $cword -ge 3 ]]; then
|
|
6732
|
+
_filedir
|
|
6733
|
+
return
|
|
6734
|
+
fi
|
|
6735
|
+
COMPREPLY=($(compgen -W "-s --strategy -v --verbose --help" -- "$cur"))
|
|
6736
|
+
return
|
|
6737
|
+
fi
|
|
6738
|
+
|
|
6739
|
+
# var \u5B50\u547D\u4EE4
|
|
6740
|
+
if [[ "$words[1]" == "var" ]]; then
|
|
6741
|
+
if [[ $cword -eq 2 ]]; then
|
|
6742
|
+
COMPREPLY=($(compgen -W "add list remove clear help" -- "$cur"))
|
|
6743
|
+
return
|
|
6744
|
+
fi
|
|
6745
|
+
|
|
6746
|
+
if [[ "$words[2]" == "add" || "$words[2]" == "remove" ]]; then
|
|
6747
|
+
if [[ $cword -eq 3 ]]; then
|
|
6748
|
+
_filedir
|
|
6749
|
+
return
|
|
6750
|
+
fi
|
|
6751
|
+
COMPREPLY=($(compgen -W "-v --verbose --force --help" -- "$cur"))
|
|
6752
|
+
return
|
|
6753
|
+
fi
|
|
6754
|
+
|
|
6755
|
+
if [[ "$words[2]" == "list" || "$words[2]" == "clear" ]]; then
|
|
6756
|
+
if [[ $cword -eq 3 ]]; then
|
|
6757
|
+
_filedir
|
|
6758
|
+
return
|
|
6759
|
+
fi
|
|
6760
|
+
COMPREPLY=($(compgen -W "-v --verbose --help" -- "$cur"))
|
|
6761
|
+
return
|
|
6762
|
+
fi
|
|
6763
|
+
fi
|
|
6764
|
+
|
|
6765
|
+
# env \u5B50\u547D\u4EE4
|
|
6766
|
+
if [[ "$words[1]" == "env" ]]; then
|
|
6767
|
+
if [[ $cword -eq 2 ]]; then
|
|
6768
|
+
COMPREPLY=($(compgen -W "add list remove clear help" -- "$cur"))
|
|
6769
|
+
return
|
|
6770
|
+
fi
|
|
6771
|
+
|
|
6772
|
+
if [[ "$words[2]" == "add" || "$words[2]" == "remove" ]]; then
|
|
6773
|
+
if [[ $cword -eq 3 ]]; then
|
|
6774
|
+
_filedir
|
|
6775
|
+
return
|
|
6776
|
+
fi
|
|
6777
|
+
COMPREPLY=($(compgen -W "-v --verbose --force --help" -- "$cur"))
|
|
6778
|
+
return
|
|
6779
|
+
fi
|
|
6780
|
+
|
|
6781
|
+
if [[ "$words[2]" == "list" || "$words[2]" == "clear" ]]; then
|
|
6782
|
+
if [[ $cword -eq 3 ]]; then
|
|
6783
|
+
_filedir
|
|
6784
|
+
return
|
|
6785
|
+
fi
|
|
6786
|
+
COMPREPLY=($(compgen -W "-v --verbose --help" -- "$cur"))
|
|
6787
|
+
return
|
|
6788
|
+
fi
|
|
6789
|
+
fi
|
|
6790
|
+
}
|
|
6791
|
+
|
|
6792
|
+
complete -F _urpf_cli_completion urpf-cli
|
|
6793
|
+
###-end-urpf-cli-completions-###
|
|
6794
|
+
`;
|
|
6795
|
+
return script;
|
|
6796
|
+
}
|
|
6797
|
+
function generateZshCompletion() {
|
|
6798
|
+
const script = `#compdef urpf-cli
|
|
6799
|
+
|
|
6800
|
+
# urpf-cli zsh completion script
|
|
6801
|
+
# Installation: urpf-cli completion zsh >> ~/.zshrc
|
|
6802
|
+
|
|
6803
|
+
_urpf_cli() {
|
|
6804
|
+
local -a commands subcommands
|
|
6805
|
+
|
|
6806
|
+
commands=(
|
|
6807
|
+
'pack:URPF \u5305\u7BA1\u7406\u547D\u4EE4'
|
|
6808
|
+
'unpack:\u89E3\u5305 URPF \u6587\u4EF6\u5230\u6307\u5B9A\u76EE\u5F55'
|
|
6809
|
+
'list:\u5217\u51FA URPF \u5305\u4E2D\u7684\u6240\u6709\u6587\u4EF6\u53CA\u5176\u5143\u6570\u636E'
|
|
6810
|
+
'info:\u663E\u793A URPF \u5305\u7684\u6458\u8981\u4FE1\u606F'
|
|
6811
|
+
'verify:\u9A8C\u8BC1 URPF \u5305\u7684\u6587\u4EF6\u5B8C\u6574\u6027'
|
|
6812
|
+
'diff:\u6BD4\u8F83\u4E24\u4E2A URPF \u5305\u4E4B\u95F4\u7684\u5DEE\u5F02'
|
|
6813
|
+
'merge:\u5408\u5E76\u591A\u4E2A URPF \u5305\u5230\u4E00\u4E2A\u8F93\u51FA\u6587\u4EF6'
|
|
6814
|
+
'var:URPF \u5305\u53D8\u91CF\u7BA1\u7406\u547D\u4EE4'
|
|
6815
|
+
'env:URPF \u5305\u73AF\u5883\u53D8\u91CF\u7BA1\u7406\u547D\u4EE4'
|
|
6816
|
+
'help:\u663E\u793A\u5E2E\u52A9\u4FE1\u606F'
|
|
6817
|
+
)
|
|
6818
|
+
|
|
6819
|
+
if (( CURRENT == 2 )); then
|
|
6820
|
+
_describe 'command' commands
|
|
6821
|
+
else
|
|
6822
|
+
case $words[2] in
|
|
6823
|
+
pack)
|
|
6824
|
+
subcommands=(
|
|
6825
|
+
'create:\u521B\u5EFA\u65B0\u7684 URPF \u5305'
|
|
6826
|
+
'add:\u5411\u5DF2\u6709 URPF \u5305\u6DFB\u52A0\u591A\u4E2A\u6587\u4EF6\u6216\u76EE\u5F55'
|
|
6827
|
+
'remove:\u4ECE URPF \u5305\u4E2D\u79FB\u9664\u591A\u4E2A\u6587\u4EF6'
|
|
6828
|
+
)
|
|
6829
|
+
if (( CURRENT == 3 )); then
|
|
6830
|
+
_describe 'subcommand' subcommands
|
|
6831
|
+
else
|
|
6832
|
+
case $words[3] in
|
|
6833
|
+
create)
|
|
6834
|
+
_arguments -S \\
|
|
6835
|
+
'(-o --output)'{-o,--output}'[\u8F93\u51FA\u6587\u4EF6\u8DEF\u5F84]' \\
|
|
6836
|
+
'(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
|
|
6837
|
+
'(-i --ignore)'{-i,--ignore}'[\u6307\u5B9A\u5FFD\u7565\u89C4\u5219\u6587\u4EF6\u8DEF\u5F84]' \\
|
|
6838
|
+
'(-f --follow-symlinks)'{-f,--follow-symlinks}'[\u8DDF\u968F\u7B26\u53F7\u94FE\u63A5]' \\
|
|
6839
|
+
'(-d --max-depth)'{-d,--max-depth}'[\u6700\u5927\u626B\u63CF\u6DF1\u5EA6]' \\
|
|
6840
|
+
'(--no-recurse)--no-recurse[\u4E0D\u9012\u5F52\u626B\u63CF\u5B50\u76EE\u5F55]' \\
|
|
6841
|
+
'(--force)--force[\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u8F93\u51FA\u6587\u4EF6]' \\
|
|
6842
|
+
'(--byte)--byte[\u6309\u5B57\u8282\u504F\u79FB\u91CF\u5207\u7247]' \\
|
|
6843
|
+
'(--line)--line[\u6309\u884C\u53F7\u5207\u7247]' \\
|
|
6844
|
+
'(-s --stdout)'{-s,--stdout}'[\u8F93\u51FA URPF \u5185\u5BB9\u5230\u5C4F\u5E55]' \\
|
|
6845
|
+
'(-c --clipboard)'{-c,--clipboard}'[\u590D\u5236 URPF \u5185\u5BB9\u5230\u526A\u8D34\u677F]' \\
|
|
6846
|
+
'(-e --env)'{-e,--env}'[\u6253\u5305\u73AF\u5883\u53D8\u91CF]' \\
|
|
6847
|
+
'(--key)--key[\u4ECE\u914D\u7F6E\u6587\u4EF6\u4E2D\u63D0\u53D6\u6307\u5B9A\u7684\u952E\u503C]' \\
|
|
6848
|
+
'(--format)--format[\u8F93\u51FA\u683C\u5F0F(json/yaml/toml/xml/properties/ini/raw)]' \\
|
|
6849
|
+
'(--validate)--validate[\u9A8C\u8BC1\u6A21\u5F0F(\u4EC5\u9A8C\u8BC1 key \u662F\u5426\u5B58\u5728)]' \\
|
|
6850
|
+
'(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
|
|
6851
|
+
;;
|
|
6852
|
+
add)
|
|
6853
|
+
_arguments -S \\
|
|
6854
|
+
'(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
|
|
6855
|
+
'(-i --ignore)'{-i,--ignore}'[\u6307\u5B9A\u5FFD\u7565\u89C4\u5219\u6587\u4EF6\u8DEF\u5F84]' \\
|
|
6856
|
+
'(-f --follow-symlinks)'{-f,--follow-symlinks}'[\u8DDF\u968F\u7B26\u53F7\u94FE\u63A5]' \\
|
|
6857
|
+
'(-d --max-depth)'{-d,--max-depth}'[\u6700\u5927\u626B\u63CF\u6DF1\u5EA6]' \\
|
|
6858
|
+
'(--no-recurse)--no-recurse[\u4E0D\u9012\u5F52\u626B\u63CF\u5B50\u76EE\u5F55]' \\
|
|
6859
|
+
'(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
|
|
6860
|
+
;;
|
|
6861
|
+
remove)
|
|
6862
|
+
_arguments -S \\
|
|
6863
|
+
'(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
|
|
6864
|
+
'(--no-apply-variables)--no-apply-variables[\u4E0D\u5E94\u7528\u53D8\u91CF\u66FF\u6362]' \\
|
|
6865
|
+
'(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
|
|
6866
|
+
;;
|
|
6867
|
+
esac
|
|
6868
|
+
fi
|
|
6869
|
+
;;
|
|
6870
|
+
unpack)
|
|
6871
|
+
_arguments -S \\
|
|
6872
|
+
'(-o --output)'{-o,--output}'[\u8F93\u51FA\u76EE\u5F55\u8DEF\u5F84]' \\
|
|
6873
|
+
'(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
|
|
6874
|
+
'(--force)--force[\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u6587\u4EF6]' \\
|
|
6875
|
+
'(--force-when-newer)--force-when-newer[\u4EC5\u5F53\u6E90\u6587\u4EF6\u6BD4\u76EE\u6807\u6587\u4EF6\u66F4\u65B0\u65F6\u624D\u8986\u76D6]' \\
|
|
6876
|
+
'(--preserve-mtime)--preserve-mtime[\u4FDD\u7559\u539F\u59CB\u4FEE\u6539\u65F6\u95F4]' \\
|
|
6877
|
+
'(--unpack-env)--unpack-env[\u81EA\u52A8\u786E\u8BA4\u73AF\u5883\u53D8\u91CF\u8BBE\u7F6E]' \\
|
|
6878
|
+
'(--apply-variables)--apply-variables[\u5E94\u7528\u53D8\u91CF\u66FF\u6362\u5230\u6587\u4EF6\u5185\u5BB9]' \\
|
|
6879
|
+
'(--packed-path)--packed-path[\u53EA\u89E3\u538B\u6307\u5B9A\u7684\u6587\u4EF6\u6216\u76EE\u5F55]' \\
|
|
6880
|
+
'(--key)--key[\u4ECE\u914D\u7F6E\u6587\u4EF6\u4E2D\u63D0\u53D6\u6307\u5B9A\u7684\u952E\u503C]' \\
|
|
6881
|
+
'(--format)--format[\u8F93\u51FA\u683C\u5F0F(json/yaml/toml/xml/properties/ini/raw)]' \\
|
|
6882
|
+
'(--validate)--validate[\u9A8C\u8BC1\u6A21\u5F0F(\u4EC5\u9A8C\u8BC1 key \u662F\u5426\u5B58\u5728)]' \\
|
|
6883
|
+
'(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
|
|
6884
|
+
;;
|
|
6885
|
+
list)
|
|
6886
|
+
_arguments -S \\
|
|
6887
|
+
'(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
|
|
6888
|
+
'(-p --pattern)'{-p,--pattern}'[\u6309\u6A21\u5F0F\u8FC7\u6EE4\u6587\u4EF6]' \\
|
|
6889
|
+
'(-j --json)'{-j,--json}'[\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA]' \\
|
|
6890
|
+
'(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
|
|
6891
|
+
;;
|
|
6892
|
+
info)
|
|
6893
|
+
_arguments -S \\
|
|
6894
|
+
'(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u4FE1\u606F]' \\
|
|
6895
|
+
'(-j --json)'{-j,--json}'[\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA]' \\
|
|
6896
|
+
'(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
|
|
6897
|
+
;;
|
|
6898
|
+
verify)
|
|
6899
|
+
_arguments -S \\
|
|
6900
|
+
'(-f --fix)'{-f,--fix}'[\u5C1D\u8BD5\u4FEE\u590D\u5E38\u89C1\u95EE\u9898]' \\
|
|
6901
|
+
'(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
|
|
6902
|
+
'(-j --json)'{-j,--json}'[\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA]' \\
|
|
6903
|
+
'(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
|
|
6904
|
+
;;
|
|
6905
|
+
diff)
|
|
6906
|
+
_arguments -S \\
|
|
6907
|
+
'(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
|
|
6908
|
+
'(-j --json)'{-j,--json}'[\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA]' \\
|
|
6909
|
+
'(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
|
|
6910
|
+
;;
|
|
6911
|
+
merge)
|
|
6912
|
+
_arguments -S \\
|
|
6913
|
+
'(-s --strategy)'{-s,--strategy}'[\u5408\u5E76\u7B56\u7565(skip/overwrite/rename)]' \\
|
|
6914
|
+
'(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
|
|
6915
|
+
'(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
|
|
6916
|
+
;;
|
|
6917
|
+
var)
|
|
6918
|
+
subcommands=(
|
|
6919
|
+
'add:\u6DFB\u52A0\u53D8\u91CF\u5230 URPF \u5305'
|
|
6920
|
+
'list:\u5217\u51FA URPF \u5305\u4E2D\u7684\u6240\u6709\u53D8\u91CF'
|
|
6921
|
+
'remove:\u4ECE URPF \u5305\u4E2D\u5220\u9664\u53D8\u91CF'
|
|
6922
|
+
'clear:\u6E05\u9664 URPF \u5305\u4E2D\u7684\u6240\u6709\u53D8\u91CF'
|
|
6923
|
+
)
|
|
6924
|
+
if (( CURRENT == 3 )); then
|
|
6925
|
+
_describe 'subcommand' subcommands
|
|
6926
|
+
else
|
|
6927
|
+
case $words[3] in
|
|
6928
|
+
add|remove)
|
|
6929
|
+
_arguments -S \\
|
|
6930
|
+
'(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
|
|
6931
|
+
'(--force)--force[\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u53D8\u91CF]' \\
|
|
6932
|
+
'(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
|
|
6933
|
+
;;
|
|
6934
|
+
list|clear)
|
|
6935
|
+
_arguments -S \\
|
|
6936
|
+
'(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
|
|
6937
|
+
'(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
|
|
6938
|
+
;;
|
|
6939
|
+
esac
|
|
6940
|
+
fi
|
|
6941
|
+
;;
|
|
6942
|
+
env)
|
|
6943
|
+
subcommands=(
|
|
6944
|
+
'add:\u6DFB\u52A0\u73AF\u5883\u53D8\u91CF\u5230 URPF \u5305'
|
|
6945
|
+
'list:\u5217\u51FA URPF \u5305\u4E2D\u7684\u6240\u6709\u73AF\u5883\u53D8\u91CF'
|
|
6946
|
+
'remove:\u4ECE URPF \u5305\u4E2D\u5220\u9664\u73AF\u5883\u53D8\u91CF'
|
|
6947
|
+
'clear:\u6E05\u9664 URPF \u5305\u4E2D\u7684\u6240\u6709\u73AF\u5883\u53D8\u91CF'
|
|
6948
|
+
)
|
|
6949
|
+
if (( CURRENT == 3 )); then
|
|
6950
|
+
_describe 'subcommand' subcommands
|
|
6951
|
+
else
|
|
6952
|
+
case $words[3] in
|
|
6953
|
+
add|remove)
|
|
6954
|
+
_arguments -S \\
|
|
6955
|
+
'(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
|
|
6956
|
+
'(--force)--force[\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u73AF\u5883\u53D8\u91CF]' \\
|
|
6957
|
+
'(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
|
|
6958
|
+
;;
|
|
6959
|
+
list|clear)
|
|
6960
|
+
_arguments -S \\
|
|
6961
|
+
'(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
|
|
6962
|
+
'(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
|
|
6963
|
+
;;
|
|
6964
|
+
esac
|
|
6965
|
+
fi
|
|
6966
|
+
;;
|
|
6967
|
+
esac
|
|
6968
|
+
fi
|
|
6969
|
+
}
|
|
6970
|
+
|
|
6971
|
+
_urpf_cli
|
|
6972
|
+
`;
|
|
6973
|
+
return script;
|
|
6974
|
+
}
|
|
6975
|
+
function generateFishCompletion() {
|
|
6976
|
+
const script = `#!/usr/bin/env fish
|
|
6977
|
+
|
|
6978
|
+
# urpf-cli fish completion script
|
|
6979
|
+
# Installation: urpf-cli completion fish > ~/.config/fish/completions/urpf-cli.fish
|
|
6980
|
+
|
|
6981
|
+
# Main commands
|
|
6982
|
+
complete -c urpf-cli -f -n "__fish_use_subcommand" -a pack -d 'URPF \u5305\u7BA1\u7406\u547D\u4EE4'
|
|
6983
|
+
complete -c urpf-cli -f -n "__fish_use_subcommand" -a unpack -d '\u89E3\u5305 URPF \u6587\u4EF6\u5230\u6307\u5B9A\u76EE\u5F55'
|
|
6984
|
+
complete -c urpf-cli -f -n "__fish_use_subcommand" -a list -d '\u5217\u51FA URPF \u5305\u4E2D\u7684\u6240\u6709\u6587\u4EF6\u53CA\u5176\u5143\u6570\u636E'
|
|
6985
|
+
complete -c urpf-cli -f -n "__fish_use_subcommand" -a info -d '\u663E\u793A URPF \u5305\u7684\u6458\u8981\u4FE1\u606F'
|
|
6986
|
+
complete -c urpf-cli -f -n "__fish_use_subcommand" -a verify -d '\u9A8C\u8BC1 URPF \u5305\u7684\u6587\u4EF6\u5B8C\u6574\u6027'
|
|
6987
|
+
complete -c urpf-cli -f -n "__fish_use_subcommand" -a diff -d '\u6BD4\u8F83\u4E24\u4E2A URPF \u5305\u4E4B\u95F4\u7684\u5DEE\u5F02'
|
|
6988
|
+
complete -c urpf-cli -f -n "__fish_use_subcommand" -a merge -d '\u5408\u5E76\u591A\u4E2A URPF \u5305\u5230\u4E00\u4E2A\u8F93\u51FA\u6587\u4EF6'
|
|
6989
|
+
complete -c urpf-cli -f -n "__fish_use_subcommand" -a var -d 'URPF \u5305\u53D8\u91CF\u7BA1\u7406\u547D\u4EE4'
|
|
6990
|
+
complete -c urpf-cli -f -n "__fish_use_subcommand" -a env -d 'URPF \u5305\u73AF\u5883\u53D8\u91CF\u7BA1\u7406\u547D\u4EE4'
|
|
6991
|
+
complete -c urpf-cli -f -n "__fish_use_subcommand" -a help -d '\u663E\u793A\u5E2E\u52A9\u4FE1\u606F'
|
|
6992
|
+
|
|
6993
|
+
# pack subcommands
|
|
6994
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack" -a create -d '\u521B\u5EFA\u65B0\u7684 URPF \u5305'
|
|
6995
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack" -a add -d '\u5411\u5DF2\u6709 URPF \u5305\u6DFB\u52A0\u591A\u4E2A\u6587\u4EF6\u6216\u76EE\u5F55'
|
|
6996
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack" -a remove -d '\u4ECE URPF \u5305\u4E2D\u79FB\u9664\u591A\u4E2A\u6587\u4EF6'
|
|
6997
|
+
|
|
6998
|
+
# pack create options
|
|
6999
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l output -s o -d '\u8F93\u51FA\u6587\u4EF6\u8DEF\u5F84'
|
|
7000
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
|
|
7001
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l ignore -s i -d '\u6307\u5B9A\u5FFD\u7565\u89C4\u5219\u6587\u4EF6\u8DEF\u5F84'
|
|
7002
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l follow-symlinks -s f -d '\u8DDF\u968F\u7B26\u53F7\u94FE\u63A5'
|
|
7003
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l max-depth -s d -d '\u6700\u5927\u626B\u63CF\u6DF1\u5EA6'
|
|
7004
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l no-recurse -d '\u4E0D\u9012\u5F52\u626B\u63CF\u5B50\u76EE\u5F55'
|
|
7005
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l force -d '\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u8F93\u51FA\u6587\u4EF6'
|
|
7006
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l byte -d '\u6309\u5B57\u8282\u504F\u79FB\u91CF\u5207\u7247'
|
|
7007
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l line -d '\u6309\u884C\u53F7\u5207\u7247'
|
|
7008
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l stdout -s s -d '\u8F93\u51FA URPF \u5185\u5BB9\u5230\u5C4F\u5E55'
|
|
7009
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l clipboard -s c -d '\u590D\u5236 URPF \u5185\u5BB9\u5230\u526A\u8D34\u677F'
|
|
7010
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l env -s e -d '\u6253\u5305\u73AF\u5883\u53D8\u91CF'
|
|
7011
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l key -d '\u4ECE\u914D\u7F6E\u6587\u4EF6\u4E2D\u63D0\u53D6\u6307\u5B9A\u7684\u952E\u503C'
|
|
7012
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l format -d '\u8F93\u51FA\u683C\u5F0F(json/yaml/toml/xml/properties/ini/raw)'
|
|
7013
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l validate -d '\u9A8C\u8BC1\u6A21\u5F0F(\u4EC5\u9A8C\u8BC1 key \u662F\u5426\u5B58\u5728)'
|
|
7014
|
+
|
|
7015
|
+
# pack add options
|
|
7016
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from add" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
|
|
7017
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from add" -l ignore -s i -d '\u6307\u5B9A\u5FFD\u7565\u89C4\u5219\u6587\u4EF6\u8DEF\u5F84'
|
|
7018
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from add" -l follow-symlinks -s f -d '\u8DDF\u968F\u7B26\u53F7\u94FE\u63A5'
|
|
7019
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from add" -l max-depth -s d -d '\u6700\u5927\u626B\u63CF\u6DF1\u5EA6'
|
|
7020
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from add" -l no-recurse -d '\u4E0D\u9012\u5F52\u626B\u63CF\u5B50\u76EE\u5F55'
|
|
7021
|
+
|
|
7022
|
+
# pack remove options
|
|
7023
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from remove" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
|
|
7024
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from remove" -l no-apply-variables -d '\u4E0D\u5E94\u7528\u53D8\u91CF\u66FF\u6362'
|
|
7025
|
+
|
|
7026
|
+
# unpack options
|
|
7027
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l output -s o -d '\u8F93\u51FA\u76EE\u5F55\u8DEF\u5F84'
|
|
7028
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
|
|
7029
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l force -d '\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u6587\u4EF6'
|
|
7030
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l force-when-newer -d '\u4EC5\u5F53\u6E90\u6587\u4EF6\u6BD4\u76EE\u6807\u6587\u4EF6\u66F4\u65B0\u65F6\u624D\u8986\u76D6'
|
|
7031
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l preserve-mtime -d '\u4FDD\u7559\u539F\u59CB\u4FEE\u6539\u65F6\u95F4'
|
|
7032
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l unpack-env -d '\u81EA\u52A8\u786E\u8BA4\u73AF\u5883\u53D8\u91CF\u8BBE\u7F6E'
|
|
7033
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l apply-variables -d '\u5E94\u7528\u53D8\u91CF\u66FF\u6362\u5230\u6587\u4EF6\u5185\u5BB9'
|
|
7034
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l packed-path -d '\u53EA\u89E3\u538B\u6307\u5B9A\u7684\u6587\u4EF6\u6216\u76EE\u5F55'
|
|
7035
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l key -d '\u4ECE\u914D\u7F6E\u6587\u4EF6\u4E2D\u63D0\u53D6\u6307\u5B9A\u7684\u952E\u503C'
|
|
7036
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l format -d '\u8F93\u51FA\u683C\u5F0F(json/yaml/toml/xml/properties/ini/raw)'
|
|
7037
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l validate -d '\u9A8C\u8BC1\u6A21\u5F0F(\u4EC5\u9A8C\u8BC1 key \u662F\u5426\u5B58\u5728)'
|
|
7038
|
+
|
|
7039
|
+
# list options
|
|
7040
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from list" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
|
|
7041
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from list" -l pattern -s p -d '\u6309\u6A21\u5F0F\u8FC7\u6EE4\u6587\u4EF6'
|
|
7042
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from list" -l json -s j -d '\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA'
|
|
7043
|
+
|
|
7044
|
+
# info options
|
|
7045
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from info" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u4FE1\u606F'
|
|
7046
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from info" -l json -s j -d '\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA'
|
|
7047
|
+
|
|
7048
|
+
# verify options
|
|
7049
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from verify" -l fix -s f -d '\u5C1D\u8BD5\u4FEE\u590D\u5E38\u89C1\u95EE\u9898'
|
|
7050
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from verify" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
|
|
7051
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from verify" -l json -s j -d '\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA'
|
|
7052
|
+
|
|
7053
|
+
# diff options
|
|
7054
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from diff" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
|
|
7055
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from diff" -l json -s j -d '\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA'
|
|
7056
|
+
|
|
7057
|
+
# merge options
|
|
7058
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from merge" -l strategy -s s -d '\u5408\u5E76\u7B56\u7565(skip/overwrite/rename)'
|
|
7059
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from merge" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
|
|
7060
|
+
|
|
7061
|
+
# var subcommands
|
|
7062
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from var" -a add -d '\u6DFB\u52A0\u53D8\u91CF\u5230 URPF \u5305'
|
|
7063
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from var" -a list -d '\u5217\u51FA URPF \u5305\u4E2D\u7684\u6240\u6709\u53D8\u91CF'
|
|
7064
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from var" -a remove -d '\u4ECE URPF \u5305\u4E2D\u5220\u9664\u53D8\u91CF'
|
|
7065
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from var" -a clear -d '\u6E05\u9664 URPF \u5305\u4E2D\u7684\u6240\u6709\u53D8\u91CF'
|
|
7066
|
+
|
|
7067
|
+
# var add/remove options
|
|
7068
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from var; and __fish_seen_subcommand_from add" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
|
|
7069
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from var; and __fish_seen_subcommand_from add" -l force -d '\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u53D8\u91CF'
|
|
7070
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from var; and __fish_seen_subcommand_from remove" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
|
|
7071
|
+
|
|
7072
|
+
# var list/clear options
|
|
7073
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from var; and __fish_seen_subcommand_from list" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
|
|
7074
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from var; and __fish_seen_subcommand_from clear" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
|
|
7075
|
+
|
|
7076
|
+
# env subcommands
|
|
7077
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from env" -a add -d '\u6DFB\u52A0\u73AF\u5883\u53D8\u91CF\u5230 URPF \u5305'
|
|
7078
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from env" -a list -d '\u5217\u51FA URPF \u5305\u4E2D\u7684\u6240\u6709\u73AF\u5883\u53D8\u91CF'
|
|
7079
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from env" -a remove -d '\u4ECE URPF \u5305\u4E2D\u5220\u9664\u73AF\u5883\u53D8\u91CF'
|
|
7080
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from env" -a clear -d '\u6E05\u9664 URPF \u5305\u4E2D\u7684\u6240\u6709\u73AF\u5883\u53D8\u91CF'
|
|
7081
|
+
|
|
7082
|
+
# env add/remove options
|
|
7083
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from env; and __fish_seen_subcommand_from add" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
|
|
7084
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from env; and __fish_seen_subcommand_from add" -l force -d '\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u73AF\u5883\u53D8\u91CF'
|
|
7085
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from env; and __fish_seen_subcommand_from remove" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
|
|
7086
|
+
|
|
7087
|
+
# env list/clear options
|
|
7088
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from env; and __fish_seen_subcommand_from list" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
|
|
7089
|
+
complete -c urpf-cli -f -n "__fish_seen_subcommand_from env; and __fish_seen_subcommand_from clear" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
|
|
7090
|
+
`;
|
|
7091
|
+
return script;
|
|
7092
|
+
}
|
|
7093
|
+
function generatePowerShellCompletion() {
|
|
7094
|
+
const script = `# urpf-cli PowerShell completion script
|
|
7095
|
+
# Installation: urpf-cli completion powershell >> $PROFILE
|
|
7096
|
+
|
|
7097
|
+
Register-ArgumentCompleter -Native -CommandName urpf-cli -ScriptBlock {
|
|
7098
|
+
param($wordToComplete, $commandAst, $cursorPosition)
|
|
7099
|
+
|
|
7100
|
+
$commands = @('pack', 'unpack', 'list', 'info', 'verify', 'diff', 'merge', 'var', 'env', 'help')
|
|
7101
|
+
|
|
7102
|
+
# Get the current command
|
|
7103
|
+
$commandElements = $commandAst.CommandElements
|
|
7104
|
+
$currentCommand = if ($commandElements.Count -gt 1) { $commandElements[1].Value } else { '' }
|
|
7105
|
+
|
|
7106
|
+
# Main command completion
|
|
7107
|
+
if ($commandElements.Count -eq 2) {
|
|
7108
|
+
$commands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7109
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7110
|
+
}
|
|
7111
|
+
return
|
|
7112
|
+
}
|
|
7113
|
+
|
|
7114
|
+
# pack subcommand completion
|
|
7115
|
+
if ($currentCommand -eq 'pack' -and $commandElements.Count -eq 3) {
|
|
7116
|
+
$subcommands = @('create', 'add', 'remove')
|
|
7117
|
+
$subcommands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7118
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7119
|
+
}
|
|
7120
|
+
return
|
|
7121
|
+
}
|
|
7122
|
+
|
|
7123
|
+
# pack create options
|
|
7124
|
+
if ($currentCommand -eq 'pack' -and $commandElements.Count -gt 3 -and $commandElements[2].Value -eq 'create') {
|
|
7125
|
+
$options = @('-o', '--output', '-v', '--verbose', '-i', '--ignore', '-f', '--follow-symlinks', '-d', '--max-depth', '--no-recurse', '--force', '--byte', '--line', '-s', '--stdout', '-c', '--clipboard', '-e', '--env', '--key', '--format', '--validate')
|
|
7126
|
+
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7127
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7128
|
+
}
|
|
7129
|
+
return
|
|
7130
|
+
}
|
|
7131
|
+
|
|
7132
|
+
# pack add options
|
|
7133
|
+
if ($currentCommand -eq 'pack' -and $commandElements.Count -gt 3 -and $commandElements[2].Value -eq 'add') {
|
|
7134
|
+
$options = @('-v', '--verbose', '-i', '--ignore', '-f', '--follow-symlinks', '-d', '--max-depth', '--no-recurse')
|
|
7135
|
+
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7136
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7137
|
+
}
|
|
7138
|
+
return
|
|
7139
|
+
}
|
|
7140
|
+
|
|
7141
|
+
# pack remove options
|
|
7142
|
+
if ($currentCommand -eq 'pack' -and $commandElements.Count -gt 3 -and $commandElements[2].Value -eq 'remove') {
|
|
7143
|
+
$options = @('-v', '--verbose', '--no-apply-variables')
|
|
7144
|
+
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7145
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7146
|
+
}
|
|
7147
|
+
return
|
|
7148
|
+
}
|
|
7149
|
+
|
|
7150
|
+
# unpack options
|
|
7151
|
+
if ($currentCommand -eq 'unpack' -and $commandElements.Count -gt 2) {
|
|
7152
|
+
$options = @('-o', '--output', '-v', '--verbose', '--force', '--force-when-newer', '--preserve-mtime', '--unpack-env', '--apply-variables', '--packed-path', '--key', '--format', '--validate')
|
|
7153
|
+
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7154
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7155
|
+
}
|
|
7156
|
+
return
|
|
7157
|
+
}
|
|
7158
|
+
|
|
7159
|
+
# list options
|
|
7160
|
+
if ($currentCommand -eq 'list' -and $commandElements.Count -gt 2) {
|
|
7161
|
+
$options = @('-v', '--verbose', '-p', '--pattern', '-j', '--json')
|
|
7162
|
+
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7163
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7164
|
+
}
|
|
7165
|
+
return
|
|
7166
|
+
}
|
|
7167
|
+
|
|
7168
|
+
# info options
|
|
7169
|
+
if ($currentCommand -eq 'info' -and $commandElements.Count -gt 2) {
|
|
7170
|
+
$options = @('-v', '--verbose', '-j', '--json')
|
|
7171
|
+
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7172
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7173
|
+
}
|
|
7174
|
+
return
|
|
7175
|
+
}
|
|
7176
|
+
|
|
7177
|
+
# verify options
|
|
7178
|
+
if ($currentCommand -eq 'verify' -and $commandElements.Count -gt 2) {
|
|
7179
|
+
$options = @('-f', '--fix', '-v', '--verbose', '-j', '--json')
|
|
7180
|
+
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7181
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7182
|
+
}
|
|
7183
|
+
return
|
|
7184
|
+
}
|
|
7185
|
+
|
|
7186
|
+
# diff options
|
|
7187
|
+
if ($currentCommand -eq 'diff' -and $commandElements.Count -gt 2) {
|
|
7188
|
+
$options = @('-v', '--verbose', '-j', '--json')
|
|
7189
|
+
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7190
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7191
|
+
}
|
|
7192
|
+
return
|
|
7193
|
+
}
|
|
7194
|
+
|
|
7195
|
+
# merge options
|
|
7196
|
+
if ($currentCommand -eq 'merge' -and $commandElements.Count -gt 2) {
|
|
7197
|
+
$options = @('-s', '--strategy', '-v', '--verbose')
|
|
7198
|
+
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7199
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7200
|
+
}
|
|
7201
|
+
return
|
|
7202
|
+
}
|
|
7203
|
+
|
|
7204
|
+
# var subcommand completion
|
|
7205
|
+
if ($currentCommand -eq 'var' -and $commandElements.Count -eq 3) {
|
|
7206
|
+
$subcommands = @('add', 'list', 'remove', 'clear')
|
|
7207
|
+
$subcommands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7208
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7209
|
+
}
|
|
7210
|
+
return
|
|
7211
|
+
}
|
|
7212
|
+
|
|
7213
|
+
# var add/remove options
|
|
7214
|
+
if ($currentCommand -eq 'var' -and $commandElements.Count -gt 3 -and ($commandElements[2].Value -eq 'add' -or $commandElements[2].Value -eq 'remove')) {
|
|
7215
|
+
$options = @('-v', '--verbose', '--force')
|
|
7216
|
+
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7217
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7218
|
+
}
|
|
7219
|
+
return
|
|
7220
|
+
}
|
|
7221
|
+
|
|
7222
|
+
# var list/clear options
|
|
7223
|
+
if ($currentCommand -eq 'var' -and $commandElements.Count -gt 3 -and ($commandElements[2].Value -eq 'list' -or $commandElements[2].Value -eq 'clear')) {
|
|
7224
|
+
$options = @('-v', '--verbose')
|
|
7225
|
+
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7226
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7227
|
+
}
|
|
7228
|
+
return
|
|
7229
|
+
}
|
|
7230
|
+
|
|
7231
|
+
# env subcommand completion
|
|
7232
|
+
if ($currentCommand -eq 'env' -and $commandElements.Count -eq 3) {
|
|
7233
|
+
$subcommands = @('add', 'list', 'remove', 'clear')
|
|
7234
|
+
$subcommands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7235
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7236
|
+
}
|
|
7237
|
+
return
|
|
7238
|
+
}
|
|
7239
|
+
|
|
7240
|
+
# env add/remove options
|
|
7241
|
+
if ($currentCommand -eq 'env' -and $commandElements.Count -gt 3 -and ($commandElements[2].Value -eq 'add' -or $commandElements[2].Value -eq 'remove')) {
|
|
7242
|
+
$options = @('-v', '--verbose', '--force')
|
|
7243
|
+
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7244
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7245
|
+
}
|
|
7246
|
+
return
|
|
7247
|
+
}
|
|
7248
|
+
|
|
7249
|
+
# env list/clear options
|
|
7250
|
+
if ($currentCommand -eq 'env' -and $commandElements.Count -gt 3 -and ($commandElements[2].Value -eq 'list' -or $commandElements[2].Value -eq 'clear')) {
|
|
7251
|
+
$options = @('-v', '--verbose')
|
|
7252
|
+
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
7253
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
7254
|
+
}
|
|
7255
|
+
return
|
|
7256
|
+
}
|
|
7257
|
+
}
|
|
7258
|
+
`;
|
|
7259
|
+
return script;
|
|
7260
|
+
}
|
|
7261
|
+
function generateCompletion(shell) {
|
|
7262
|
+
switch (shell) {
|
|
7263
|
+
case "bash":
|
|
7264
|
+
return generateBashCompletion();
|
|
7265
|
+
case "zsh":
|
|
7266
|
+
return generateZshCompletion();
|
|
7267
|
+
case "fish":
|
|
7268
|
+
return generateFishCompletion();
|
|
7269
|
+
case "powershell":
|
|
7270
|
+
return generatePowerShellCompletion();
|
|
7271
|
+
default:
|
|
7272
|
+
throw new Error(`Unsupported shell: ${shell}`);
|
|
7273
|
+
}
|
|
7274
|
+
}
|
|
7275
|
+
function getSupportedShells() {
|
|
7276
|
+
return ["bash", "zsh", "fish", "powershell"];
|
|
7277
|
+
}
|
|
7278
|
+
|
|
4972
7279
|
// src/index.ts
|
|
4973
7280
|
var program = new Command();
|
|
4974
7281
|
program.name("urpf-cli").description("URPF CLI \u5DE5\u5177 - \u57FA\u4E8E URPF \u89C4\u8303\u7684\u547D\u4EE4\u884C\u6253\u5305\u5DE5\u5177").version("0.1.0");
|
|
@@ -4985,7 +7292,10 @@ packCommand.command("create").description("\u5C06\u6587\u4EF6\u6216\u76EE\u5F55\
|
|
|
4985
7292
|
previous[name] = void 0;
|
|
4986
7293
|
}
|
|
4987
7294
|
return previous;
|
|
4988
|
-
}, {}).
|
|
7295
|
+
}, {}).option("--key <key-path>", '\u4ECE\u914D\u7F6E\u6587\u4EF6\u4E2D\u63D0\u53D6\u6307\u5B9A\u7684\u952E\u503C\uFF08\u652F\u6301\u5D4C\u5957\u548C\u6570\u7EC4\u8BBF\u95EE\uFF0C\u5982 "config.server.port" \u6216 "items[0].name"\uFF1B\u652F\u6301\u901A\u914D\u7B26 * \u548C **\uFF1B\u652F\u6301\u9017\u53F7\u5206\u9694\u591A\u4E2A key\uFF1B\u53EF\u591A\u6B21\u6307\u5B9A\uFF09', (value, previous = []) => {
|
|
7296
|
+
previous.push(value);
|
|
7297
|
+
return previous;
|
|
7298
|
+
}).option("--format <format>", "\u8F93\u51FA\u683C\u5F0F\uFF08json\u3001yaml\u3001toml\u3001xml\u3001properties\u3001ini\u3001raw\uFF09", "json").option("--validate", "\u9A8C\u8BC1\u6A21\u5F0F\uFF08\u4EC5\u9A8C\u8BC1 key \u662F\u5426\u5B58\u5728\uFF0C\u4E0D\u6267\u884C\u5B9E\u9645\u63D0\u53D6\uFF09").action(async (file, options) => {
|
|
4989
7299
|
try {
|
|
4990
7300
|
const envVars = options.env;
|
|
4991
7301
|
const packOptions = {
|
|
@@ -5006,7 +7316,12 @@ packCommand.command("create").description("\u5C06\u6587\u4EF6\u6216\u76EE\u5F55\
|
|
|
5006
7316
|
stdout: options.stdout,
|
|
5007
7317
|
clipboard: options.clipboard,
|
|
5008
7318
|
// 环境变量
|
|
5009
|
-
envVars: Object.keys(envVars).length > 0 ? envVars : void 0
|
|
7319
|
+
envVars: Object.keys(envVars).length > 0 ? envVars : void 0,
|
|
7320
|
+
// key 提取
|
|
7321
|
+
key: options.key,
|
|
7322
|
+
// 格式化和验证
|
|
7323
|
+
format: options.format,
|
|
7324
|
+
validate: options.validate
|
|
5010
7325
|
};
|
|
5011
7326
|
const command = new PackCommand();
|
|
5012
7327
|
const result = await command.execute(file, packOptions);
|
|
@@ -5074,15 +7389,20 @@ packCommand.command("remove").description("\u4ECE URPF \u5305\u4E2D\u79FB\u9664\
|
|
|
5074
7389
|
process.exit(1);
|
|
5075
7390
|
}
|
|
5076
7391
|
});
|
|
5077
|
-
program.command("unpack").description(
|
|
7392
|
+
program.command("unpack").description('\u89E3\u5305 URPF \u6587\u4EF6\u5230\u6307\u5B9A\u76EE\u5F55\n\n\u73AF\u5883\u53D8\u91CF\u652F\u6301:\n \u5982\u679C URPF \u5305\u5305\u542B\u73AF\u5883\u53D8\u91CF\uFF0C\u89E3\u5305\u65F6\u4F1A\u63D0\u793A\u7528\u6237\u662F\u5426\u8BBE\u7F6E\n \u4F7F\u7528 --unpack-env \u9009\u9879\u53EF\u81EA\u52A8\u786E\u8BA4\u73AF\u5883\u53D8\u91CF\u8BBE\u7F6E\uFF08\u65E0\u9700\u7528\u6237\u4EA4\u4E92\uFF09\n \u4F7F\u7528 --apply-variables \u9009\u9879\u53EF\u5E94\u7528\u53D8\u91CF\u66FF\u6362\u5230\u6587\u4EF6\u5185\u5BB9\n\nKey \u63D0\u53D6\u652F\u6301:\n \u4F7F\u7528 --key \u9009\u9879\u4ECE\u89E3\u5305\u540E\u7684\u914D\u7F6E\u6587\u4EF6\u4E2D\u63D0\u53D6\u6307\u5B9A\u7684\u952E\u503C\n \u652F\u6301\u5D4C\u5957\u952E\u8BBF\u95EE\uFF08\u5982 "config.server.port"\uFF09\n \u652F\u6301\u6570\u7EC4\u7D22\u5F15\u8BBF\u95EE\uFF08\u5982 "items[0].name"\uFF09').argument("<file.urpf>", "\u8981\u89E3\u5305\u7684 URPF \u6587\u4EF6\u8DEF\u5F84").option("-o, --output <path>", "\u8F93\u51FA\u76EE\u5F55\u8DEF\u5F84\uFF08\u9ED8\u8BA4\uFF1A\u5F53\u524D\u76EE\u5F55\uFF09").option("-v, --verbose", "\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7").option("--force", "\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u6587\u4EF6").option("--force-when-newer", "\u4EC5\u5F53\u6E90\u6587\u4EF6\u6BD4\u76EE\u6807\u6587\u4EF6\u66F4\u65B0\u65F6\u624D\u8986\u76D6").option("--preserve-mtime", "\u4FDD\u7559\u539F\u59CB\u4FEE\u6539\u65F6\u95F4").option("--unpack-env", "\u81EA\u52A8\u786E\u8BA4\u73AF\u5883\u53D8\u91CF\u8BBE\u7F6E\uFF08\u65E0\u9700\u7528\u6237\u4EA4\u4E92\uFF09").option("--apply-variables", "\u5E94\u7528\u53D8\u91CF\u66FF\u6362\u5230\u6587\u4EF6\u5185\u5BB9").option("--packed-path <path>", "\u53EA\u89E3\u538B\u6307\u5B9A\u7684\u6587\u4EF6\u6216\u76EE\u5F55\uFF08\u652F\u6301\u901A\u914D\u7B26 * \u548C ?\uFF0C\u53EF\u591A\u6B21\u4F7F\u7528\uFF09", (value, previous = []) => {
|
|
7393
|
+
previous.push(value);
|
|
7394
|
+
return previous;
|
|
7395
|
+
}).option("--key <key-path>", '\u4ECE\u914D\u7F6E\u6587\u4EF6\u4E2D\u63D0\u53D6\u6307\u5B9A\u7684\u952E\u503C\uFF08\u652F\u6301\u5D4C\u5957\u548C\u6570\u7EC4\u8BBF\u95EE\uFF0C\u5982 "config.server.port" \u6216 "items[0].name"\uFF1B\u652F\u6301\u901A\u914D\u7B26 * \u548C **\uFF1B\u652F\u6301\u9017\u53F7\u5206\u9694\u591A\u4E2A key\uFF1B\u53EF\u591A\u6B21\u6307\u5B9A\uFF09', (value, previous = []) => {
|
|
5078
7396
|
previous.push(value);
|
|
5079
7397
|
return previous;
|
|
5080
|
-
}).action(async (file, options) => {
|
|
7398
|
+
}).option("--format <format>", "\u8F93\u51FA\u683C\u5F0F\uFF08json\u3001yaml\u3001toml\u3001xml\u3001properties\u3001ini\u3001raw\uFF09", "json").option("--validate", "\u9A8C\u8BC1\u6A21\u5F0F\uFF08\u4EC5\u9A8C\u8BC1 key \u662F\u5426\u5B58\u5728\uFF0C\u4E0D\u6267\u884C\u5B9E\u9645\u63D0\u53D6\uFF09").action(async (file, options) => {
|
|
5081
7399
|
try {
|
|
5082
7400
|
const unpackCommand = new UnpackCommand();
|
|
5083
7401
|
const result = await unpackCommand.execute(file, {
|
|
5084
7402
|
...options,
|
|
5085
|
-
packedPaths: options.packedPath
|
|
7403
|
+
packedPaths: options.packedPath,
|
|
7404
|
+
format: options.format,
|
|
7405
|
+
validate: options.validate
|
|
5086
7406
|
});
|
|
5087
7407
|
if (result.success) {
|
|
5088
7408
|
console.log(`\u2705 URPF \u6587\u4EF6\u5DF2\u89E3\u5305\u5230: ${result.outputDir}`);
|
|
@@ -5426,4 +7746,21 @@ envCommand.command("clear").description("\u6E05\u9664 URPF \u5305\u4E2D\u7684\u6
|
|
|
5426
7746
|
process.exit(1);
|
|
5427
7747
|
}
|
|
5428
7748
|
});
|
|
7749
|
+
program.command("completion").description("\u751F\u6210 Shell \u81EA\u52A8\u8865\u5168\u811A\u672C\n\n\u652F\u6301\u7684 Shell:\n bash - Bash shell\n zsh - Zsh shell\n fish - Fish shell\n powershell - PowerShell\n\n\u4F7F\u7528\u793A\u4F8B:\n urpf-cli completion bash >> ~/.bashrc\n urpf-cli completion zsh >> ~/.zshrc\n urpf-cli completion fish > ~/.config/fish/completions/urpf-cli.fish\n urpf-cli completion powershell >> $PROFILE").argument("<shell>", "Shell \u7C7B\u578B\uFF08bash\u3001zsh\u3001fish\u3001powershell\uFF09").action(async (shell) => {
|
|
7750
|
+
try {
|
|
7751
|
+
const shellType = shell.toLowerCase();
|
|
7752
|
+
const supportedShells = getSupportedShells();
|
|
7753
|
+
if (!supportedShells.includes(shellType)) {
|
|
7754
|
+
console.error(`\u274C \u4E0D\u652F\u6301\u7684 Shell \u7C7B\u578B: ${shell}`);
|
|
7755
|
+
console.error(`\u652F\u6301\u7684 Shell \u7C7B\u578B: ${supportedShells.join(", ")}`);
|
|
7756
|
+
process.exit(1);
|
|
7757
|
+
}
|
|
7758
|
+
const completionScript = generateCompletion(shellType);
|
|
7759
|
+
console.log(completionScript);
|
|
7760
|
+
} catch (error) {
|
|
7761
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
7762
|
+
console.error(`\u274C \u751F\u6210\u8865\u5168\u811A\u672C\u5931\u8D25: ${errorMessage}`);
|
|
7763
|
+
process.exit(1);
|
|
7764
|
+
}
|
|
7765
|
+
});
|
|
5429
7766
|
program.parse();
|