autosnippet 3.2.21 → 3.3.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/dashboard/dist/assets/{icons-C1dUryS-.js → icons-BofcEZ3f.js} +1 -1
- package/dashboard/dist/assets/index-SiN1GChm.js +128 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/bin/cli.d.ts +0 -1
- package/dist/bin/cli.js +0 -133
- package/dist/lib/cli/SetupService.d.ts +46 -2
- package/dist/lib/cli/SetupService.js +2 -27
- package/dist/lib/{platform/ios/spm → core/discovery}/SpmDiscoverer.d.ts +2 -5
- package/dist/lib/{platform/ios/spm → core/discovery}/SpmDiscoverer.js +159 -44
- package/dist/lib/core/discovery/index.d.ts +1 -1
- package/dist/lib/core/discovery/index.js +2 -2
- package/dist/lib/external/mcp/handlers/guard.js +6 -3
- package/dist/lib/http/HttpServer.js +0 -6
- package/dist/lib/http/routes/commands.d.ts +1 -1
- package/dist/lib/http/routes/commands.js +1 -66
- package/dist/lib/http/routes/remote.js +0 -5
- package/dist/lib/injection/ServiceMap.d.ts +0 -9
- package/dist/lib/injection/modules/AppModule.d.ts +2 -3
- package/dist/lib/injection/modules/AppModule.js +3 -30
- package/dist/lib/injection/modules/GuardModule.js +33 -1
- package/dist/lib/service/guard/GuardCheckEngine.d.ts +13 -1
- package/dist/lib/service/guard/GuardCheckEngine.js +44 -2
- package/dist/lib/service/module/ModuleService.js +3 -13
- package/dist/lib/service/search/SearchEngine.js +1 -1
- package/dist/lib/shared/constants.d.ts +0 -15
- package/dist/lib/shared/constants.js +0 -10
- package/dist/lib/shared/schemas/config.d.ts +4 -1
- package/dist/lib/shared/schemas/config.js +8 -1
- package/dist/scripts/release.js +2 -10
- package/package.json +4 -19
- package/dashboard/dist/assets/index-DdvZE4Yd.js +0 -128
- package/dist/lib/http/routes/snippets.d.ts +0 -6
- package/dist/lib/http/routes/snippets.js +0 -49
- package/dist/lib/platform/ClipboardManager.d.ts +0 -24
- package/dist/lib/platform/ClipboardManager.js +0 -142
- package/dist/lib/platform/NativeUi.d.ts +0 -53
- package/dist/lib/platform/NativeUi.js +0 -284
- package/dist/lib/platform/ios/index.d.ts +0 -38
- package/dist/lib/platform/ios/index.js +0 -42
- package/dist/lib/platform/ios/routes/spm.d.ts +0 -9
- package/dist/lib/platform/ios/routes/spm.js +0 -371
- package/dist/lib/platform/ios/snippet/PlaceholderConverter.d.ts +0 -21
- package/dist/lib/platform/ios/snippet/PlaceholderConverter.js +0 -48
- package/dist/lib/platform/ios/snippet/XcodeCodec.d.ts +0 -23
- package/dist/lib/platform/ios/snippet/XcodeCodec.js +0 -96
- package/dist/lib/platform/ios/spm/DependencyGraph.d.ts +0 -56
- package/dist/lib/platform/ios/spm/DependencyGraph.js +0 -195
- package/dist/lib/platform/ios/spm/PackageSwiftParser.d.ts +0 -69
- package/dist/lib/platform/ios/spm/PackageSwiftParser.js +0 -231
- package/dist/lib/platform/ios/spm/PathFinder.d.ts +0 -28
- package/dist/lib/platform/ios/spm/PathFinder.js +0 -117
- package/dist/lib/platform/ios/spm/PolicyEngine.d.ts +0 -44
- package/dist/lib/platform/ios/spm/PolicyEngine.js +0 -79
- package/dist/lib/platform/ios/spm/SpmHelper.d.ts +0 -102
- package/dist/lib/platform/ios/spm/SpmHelper.js +0 -464
- package/dist/lib/platform/ios/xcode/HeaderResolver.d.ts +0 -33
- package/dist/lib/platform/ios/xcode/HeaderResolver.js +0 -90
- package/dist/lib/platform/ios/xcode/SaveEventFilter.d.ts +0 -66
- package/dist/lib/platform/ios/xcode/SaveEventFilter.js +0 -142
- package/dist/lib/platform/ios/xcode/XcodeAutomation.d.ts +0 -71
- package/dist/lib/platform/ios/xcode/XcodeAutomation.js +0 -327
- package/dist/lib/platform/ios/xcode/XcodeImportResolver.d.ts +0 -130
- package/dist/lib/platform/ios/xcode/XcodeImportResolver.js +0 -404
- package/dist/lib/platform/ios/xcode/XcodeIntegration.d.ts +0 -89
- package/dist/lib/platform/ios/xcode/XcodeIntegration.js +0 -588
- package/dist/lib/platform/ios/xcode/XcodeWriteUtils.d.ts +0 -99
- package/dist/lib/platform/ios/xcode/XcodeWriteUtils.js +0 -190
- package/dist/lib/service/automation/ActionPipeline.d.ts +0 -34
- package/dist/lib/service/automation/ActionPipeline.js +0 -53
- package/dist/lib/service/automation/AutomationOrchestrator.d.ts +0 -86
- package/dist/lib/service/automation/AutomationOrchestrator.js +0 -57
- package/dist/lib/service/automation/ContextCollector.d.ts +0 -24
- package/dist/lib/service/automation/ContextCollector.js +0 -35
- package/dist/lib/service/automation/DirectiveDetector.d.ts +0 -51
- package/dist/lib/service/automation/DirectiveDetector.js +0 -112
- package/dist/lib/service/automation/FileWatcher.d.ts +0 -51
- package/dist/lib/service/automation/FileWatcher.js +0 -366
- package/dist/lib/service/automation/TriggerResolver.d.ts +0 -36
- package/dist/lib/service/automation/TriggerResolver.js +0 -62
- package/dist/lib/service/automation/handlers/AlinkHandler.d.ts +0 -7
- package/dist/lib/service/automation/handlers/AlinkHandler.js +0 -80
- package/dist/lib/service/automation/handlers/CreateHandler.d.ts +0 -11
- package/dist/lib/service/automation/handlers/CreateHandler.js +0 -170
- package/dist/lib/service/automation/handlers/GuardHandler.d.ts +0 -17
- package/dist/lib/service/automation/handlers/GuardHandler.js +0 -218
- package/dist/lib/service/automation/handlers/HeaderHandler.d.ts +0 -2
- package/dist/lib/service/automation/handlers/HeaderHandler.js +0 -32
- package/dist/lib/service/automation/handlers/SearchHandler.d.ts +0 -11
- package/dist/lib/service/automation/handlers/SearchHandler.js +0 -278
- package/dist/lib/service/snippet/SnippetFactory.d.ts +0 -101
- package/dist/lib/service/snippet/SnippetFactory.js +0 -145
- package/dist/lib/service/snippet/SnippetInstaller.d.ts +0 -91
- package/dist/lib/service/snippet/SnippetInstaller.js +0 -276
- package/dist/lib/service/snippet/codecs/SnippetCodec.d.ts +0 -44
- package/dist/lib/service/snippet/codecs/SnippetCodec.js +0 -35
- package/dist/lib/service/snippet/codecs/VSCodeCodec.d.ts +0 -27
- package/dist/lib/service/snippet/codecs/VSCodeCodec.js +0 -82
- package/dist/scripts/build-native-ui.d.ts +0 -3
- package/dist/scripts/build-native-ui.js +0 -62
- package/dist/scripts/init-snippets.d.ts +0 -30
- package/dist/scripts/init-snippets.js +0 -298
- package/dist/scripts/install-full.d.ts +0 -7
- package/dist/scripts/install-full.js +0 -38
- package/resources/native-ui/README.md +0 -29
- package/resources/native-ui/combined-window.swift +0 -494
- package/resources/native-ui/main.swift +0 -598
- package/scripts/postinstall-safe.mjs +0 -89
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>AutoSnippet Dashboard</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-SiN1GChm.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/yaml-qRaU8Ldn.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vendor-BZEJEVBn.js">
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/icons-
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/icons-BofcEZ3f.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/axios-42ANG6Sg.js">
|
|
13
13
|
<link rel="modulepreload" crossorigin href="/assets/framer-motion-CZfOSYpP.js">
|
|
14
14
|
<link rel="stylesheet" crossorigin href="/assets/index-D0whuycy.css">
|
package/dist/bin/cli.d.ts
CHANGED
package/dist/bin/cli.js
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
* asd search <query> - 搜索知识库
|
|
11
11
|
* asd guard <file> - Guard 检查
|
|
12
12
|
* asd guard:ci [path] - CI/CD Guard 合规检查
|
|
13
|
-
* asd watch - 文件监控
|
|
14
13
|
* asd server - 启动 API 服务
|
|
15
14
|
* asd ui - 启动 Dashboard UI
|
|
16
15
|
* asd upgrade - 升级 IDE 集成
|
|
@@ -605,53 +604,6 @@ program
|
|
|
605
604
|
}
|
|
606
605
|
});
|
|
607
606
|
// ─────────────────────────────────────────────────────
|
|
608
|
-
// watch 命令
|
|
609
|
-
// ─────────────────────────────────────────────────────
|
|
610
|
-
program
|
|
611
|
-
.command('watch')
|
|
612
|
-
.option('-d, --dir <path>', '监控目录', '.')
|
|
613
|
-
.option('-e, --ext <exts>', '文件扩展名(逗号分隔,留空则自动检测)')
|
|
614
|
-
.option('--guard', '自动运行 Guard 检查', true)
|
|
615
|
-
.action(async (opts) => {
|
|
616
|
-
try {
|
|
617
|
-
const dir = resolve(opts.dir);
|
|
618
|
-
let bootstrap;
|
|
619
|
-
try {
|
|
620
|
-
const result = await initContainer({ projectRoot: dir });
|
|
621
|
-
bootstrap = result.bootstrap;
|
|
622
|
-
}
|
|
623
|
-
catch {
|
|
624
|
-
// ServiceContainer 初始化失败不阻塞 watch(HTTP fallback 仍可用)
|
|
625
|
-
bootstrap = await initBootstrap();
|
|
626
|
-
}
|
|
627
|
-
const Paths = await import('../lib/infrastructure/config/Paths.js');
|
|
628
|
-
const specPath = Paths.getProjectSpecPath(dir);
|
|
629
|
-
// IDE + 扩展名自动检测
|
|
630
|
-
let exts = null;
|
|
631
|
-
if (opts.ext) {
|
|
632
|
-
exts = opts.ext.split(',').map((e) => e.trim());
|
|
633
|
-
}
|
|
634
|
-
// 不指定 --ext 时,FileWatcher 内部根据 IDE 检测结果使用默认模式
|
|
635
|
-
const { FileWatcher } = await import('../lib/service/automation/FileWatcher.js');
|
|
636
|
-
const watcher = new FileWatcher(specPath, dir, {
|
|
637
|
-
quiet: false,
|
|
638
|
-
exts,
|
|
639
|
-
});
|
|
640
|
-
watcher.start();
|
|
641
|
-
// 优雅退出
|
|
642
|
-
process.on('SIGINT', async () => {
|
|
643
|
-
await watcher.stop();
|
|
644
|
-
await bootstrap.shutdown();
|
|
645
|
-
process.exit(0);
|
|
646
|
-
});
|
|
647
|
-
}
|
|
648
|
-
catch (err) {
|
|
649
|
-
cli.error(`Error: ${err.message}`);
|
|
650
|
-
cli.debug(err.stack);
|
|
651
|
-
process.exit(1);
|
|
652
|
-
}
|
|
653
|
-
});
|
|
654
|
-
// ─────────────────────────────────────────────────────
|
|
655
607
|
// server 命令
|
|
656
608
|
// ─────────────────────────────────────────────────────
|
|
657
609
|
program
|
|
@@ -766,91 +718,6 @@ program
|
|
|
766
718
|
cli.warn(`⚠️ SignalCollector failed to start: ${scErr.message}`);
|
|
767
719
|
cli.debug(scErr.stack);
|
|
768
720
|
}
|
|
769
|
-
// 3. 启动文件监听器(仅 iOS/macOS 项目 — Xcode 工作流)
|
|
770
|
-
// VSCode 用户通过 AutoSnippet 扩展原生处理 as:s/as:c/as:a 指令
|
|
771
|
-
const isAppleProject = (() => {
|
|
772
|
-
try {
|
|
773
|
-
const entries = readdirSync(projectRoot, { withFileTypes: true });
|
|
774
|
-
// ── Level 1: 项目配置文件(确定性高)──
|
|
775
|
-
const hasAppleConfig = entries.some((e) => e.name === 'Package.swift' || // SPM
|
|
776
|
-
e.name === 'Podfile' || // CocoaPods
|
|
777
|
-
e.name === 'Cartfile' || // Carthage
|
|
778
|
-
e.name === 'project.yml' || // XcodeGen
|
|
779
|
-
e.name.endsWith('.xcodeproj') || // Xcode project
|
|
780
|
-
e.name.endsWith('.xcworkspace') // Xcode workspace
|
|
781
|
-
);
|
|
782
|
-
if (hasAppleConfig) {
|
|
783
|
-
return true;
|
|
784
|
-
}
|
|
785
|
-
// ── Level 2: 目录结构特征 ──
|
|
786
|
-
const hasAppleDir = entries.some((e) => e.isDirectory() &&
|
|
787
|
-
(e.name === 'Tuist' || // Tuist 项目
|
|
788
|
-
e.name === 'Pods' || // CocoaPods 产物
|
|
789
|
-
e.name === 'Carthage' || // Carthage 产物
|
|
790
|
-
e.name === 'DerivedData') // Xcode 构建产物
|
|
791
|
-
);
|
|
792
|
-
if (hasAppleDir) {
|
|
793
|
-
return true;
|
|
794
|
-
}
|
|
795
|
-
// ── Level 3: 向下扫一层(处理 monorepo 或 Sources/ 下有 .swift 的情况)──
|
|
796
|
-
const APPLE_EXTS = new Set(['.swift', '.m', '.mm', '.h']);
|
|
797
|
-
const SCAN_DIRS = ['Sources', 'Source', 'src', 'App', 'Classes', 'ios', 'iOS'];
|
|
798
|
-
for (const e of entries) {
|
|
799
|
-
// 根目录直接有 .swift/.m 文件
|
|
800
|
-
if (!e.isDirectory() && APPLE_EXTS.has(e.name.slice(e.name.lastIndexOf('.')))) {
|
|
801
|
-
return true;
|
|
802
|
-
}
|
|
803
|
-
// 常见源码目录下有 Apple 文件
|
|
804
|
-
if (e.isDirectory() && SCAN_DIRS.includes(e.name)) {
|
|
805
|
-
try {
|
|
806
|
-
const subEntries = readdirSync(join(projectRoot, e.name));
|
|
807
|
-
if (subEntries.some((f) => APPLE_EXTS.has(f.slice(f.lastIndexOf('.'))))) {
|
|
808
|
-
return true;
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
catch {
|
|
812
|
-
/* 读取失败忽略 */
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
return false;
|
|
817
|
-
}
|
|
818
|
-
catch {
|
|
819
|
-
return false;
|
|
820
|
-
}
|
|
821
|
-
})();
|
|
822
|
-
if (isAppleProject) {
|
|
823
|
-
try {
|
|
824
|
-
const Paths = await import('../lib/infrastructure/config/Paths.js');
|
|
825
|
-
const specPath = Paths.getProjectSpecPath(projectRoot);
|
|
826
|
-
const isDebugMode = process.env.ASD_DEBUG === '1';
|
|
827
|
-
// 设置 Dashboard URL 供 watcher 跳转浏览器使用
|
|
828
|
-
// 生产模式用 API 同端口,开发模式用 vite dev 5173
|
|
829
|
-
const dashDirCheck = DASHBOARD_DIR;
|
|
830
|
-
const isProductionDashboard = existsSync(join(dashDirCheck, 'dist', 'index.html')) &&
|
|
831
|
-
!existsSync(join(dashDirCheck, 'src'));
|
|
832
|
-
if (!opts.apiOnly) {
|
|
833
|
-
process.env.ASD_DASHBOARD_URL = isProductionDashboard
|
|
834
|
-
? `http://127.0.0.1:${port}`
|
|
835
|
-
: `http://localhost:5173`;
|
|
836
|
-
}
|
|
837
|
-
else {
|
|
838
|
-
process.env.ASD_DASHBOARD_URL =
|
|
839
|
-
process.env.ASD_DASHBOARD_URL || `http://${host}:${port}`;
|
|
840
|
-
}
|
|
841
|
-
const { FileWatcher } = await import('../lib/service/automation/FileWatcher.js');
|
|
842
|
-
const watcher = new FileWatcher(specPath, projectRoot, { quiet: !isDebugMode });
|
|
843
|
-
watcher.start();
|
|
844
|
-
if (isDebugMode) {
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
catch (watchErr) {
|
|
848
|
-
cli.warn(`⚠️ File watcher failed to start: ${watchErr.message}`);
|
|
849
|
-
cli.debug(watchErr.stack);
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
else if (process.env.ASD_DEBUG === '1') {
|
|
853
|
-
}
|
|
854
721
|
if (opts.apiOnly) {
|
|
855
722
|
return;
|
|
856
723
|
}
|
|
@@ -98,7 +98,49 @@ export declare class SetupService {
|
|
|
98
98
|
};
|
|
99
99
|
} | {
|
|
100
100
|
label: string;
|
|
101
|
-
fn: () => Promise<
|
|
101
|
+
fn: () => Promise<{
|
|
102
|
+
dbPath: string;
|
|
103
|
+
}>;
|
|
104
|
+
} | {
|
|
105
|
+
label: string;
|
|
106
|
+
fn: () => Promise<{
|
|
107
|
+
skipped: boolean;
|
|
108
|
+
}>;
|
|
109
|
+
} | {
|
|
110
|
+
label: string;
|
|
111
|
+
fn: () => Promise<{
|
|
112
|
+
status: string;
|
|
113
|
+
reason: string;
|
|
114
|
+
hint: string;
|
|
115
|
+
indexed?: undefined;
|
|
116
|
+
skipped?: undefined;
|
|
117
|
+
errors?: undefined;
|
|
118
|
+
error?: undefined;
|
|
119
|
+
} | {
|
|
120
|
+
status: string;
|
|
121
|
+
reason: string;
|
|
122
|
+
hint?: undefined;
|
|
123
|
+
indexed?: undefined;
|
|
124
|
+
skipped?: undefined;
|
|
125
|
+
errors?: undefined;
|
|
126
|
+
error?: undefined;
|
|
127
|
+
} | {
|
|
128
|
+
status: string;
|
|
129
|
+
indexed: number;
|
|
130
|
+
skipped: number;
|
|
131
|
+
errors: number;
|
|
132
|
+
reason?: undefined;
|
|
133
|
+
hint?: undefined;
|
|
134
|
+
error?: undefined;
|
|
135
|
+
} | {
|
|
136
|
+
status: string;
|
|
137
|
+
error: string;
|
|
138
|
+
hint: string;
|
|
139
|
+
reason?: undefined;
|
|
140
|
+
indexed?: undefined;
|
|
141
|
+
skipped?: undefined;
|
|
142
|
+
errors?: undefined;
|
|
143
|
+
}>;
|
|
102
144
|
})[];
|
|
103
145
|
run(): Promise<{
|
|
104
146
|
step: number;
|
|
@@ -139,7 +181,9 @@ export declare class SetupService {
|
|
|
139
181
|
* 委托 KnowledgeSyncService 执行全字段同步(setup 场景跳过违规记录)
|
|
140
182
|
*/
|
|
141
183
|
private _syncRecipesToDB;
|
|
142
|
-
stepPlatform(): Promise<
|
|
184
|
+
stepPlatform(): Promise<{
|
|
185
|
+
skipped: boolean;
|
|
186
|
+
}>;
|
|
143
187
|
/**
|
|
144
188
|
* 在项目根目录创建 .env 文件(从 .env.example 复制)
|
|
145
189
|
* 如果 .env 已存在则跳过并提示用户手动配置。
|
|
@@ -514,34 +514,9 @@ export class SetupService {
|
|
|
514
514
|
if (report.orphaned.length > 0) {
|
|
515
515
|
}
|
|
516
516
|
}
|
|
517
|
-
/* ═══ Step 5: Snippet 初始化 (
|
|
517
|
+
/* ═══ Step 5: Snippet 初始化 (已移除 — AI-first 迁移) ═════ */
|
|
518
518
|
async stepPlatform() {
|
|
519
|
-
|
|
520
|
-
if (!existsSync(initScript)) {
|
|
521
|
-
return { skipped: true };
|
|
522
|
-
}
|
|
523
|
-
try {
|
|
524
|
-
const mod = await import(initScript);
|
|
525
|
-
// 优先使用模块级导出的 initialize 函数(无 this 依赖)
|
|
526
|
-
// 若 mod.default 是对象则在其上调用 initialize 保留 this
|
|
527
|
-
if (typeof mod.initialize === 'function') {
|
|
528
|
-
const result = await mod.initialize(this.projectRoot, 'all');
|
|
529
|
-
return result;
|
|
530
|
-
}
|
|
531
|
-
if (mod.default && typeof mod.default.initialize === 'function') {
|
|
532
|
-
const result = await mod.default.initialize(this.projectRoot, 'all');
|
|
533
|
-
return result;
|
|
534
|
-
}
|
|
535
|
-
if (typeof mod.default === 'function') {
|
|
536
|
-
const result = await mod.default(this.projectRoot, 'all');
|
|
537
|
-
return result;
|
|
538
|
-
}
|
|
539
|
-
return { skipped: true };
|
|
540
|
-
}
|
|
541
|
-
catch (e) {
|
|
542
|
-
console.warn(` ⚠️ Snippet 初始化失败:${e.message}`);
|
|
543
|
-
return { error: e.message };
|
|
544
|
-
}
|
|
519
|
+
return { skipped: true };
|
|
545
520
|
}
|
|
546
521
|
/* ═══ Helpers ════════════════════════════════════════ */
|
|
547
522
|
/**
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
* @module SpmDiscoverer
|
|
3
3
|
* @description SPM 项目发现器,适配 ProjectDiscoverer 接口
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* SpmHelper 仅用于 Xcode 工作流的依赖检查/修复,不在此链路加载。
|
|
5
|
+
* 内置 Package.swift 正则解析,提供模块列表和文件遍历。
|
|
7
6
|
*
|
|
8
7
|
* 检测: 项目根或子目录存在 Package.swift
|
|
9
8
|
*/
|
|
10
|
-
import { ProjectDiscoverer } from '
|
|
9
|
+
import { ProjectDiscoverer } from './ProjectDiscoverer.js';
|
|
11
10
|
export declare class SpmDiscoverer extends ProjectDiscoverer {
|
|
12
11
|
#private;
|
|
13
12
|
get id(): string;
|
|
@@ -50,6 +49,4 @@ export declare class SpmDiscoverer extends ProjectDiscoverer {
|
|
|
50
49
|
type: string;
|
|
51
50
|
}[];
|
|
52
51
|
}>;
|
|
53
|
-
/** @deprecated SpmHelper 不再由 SpmDiscoverer 持有,仅 XcodeIntegration 通过 container 使用 */
|
|
54
|
-
getSpmService(): null;
|
|
55
52
|
}
|
|
@@ -2,20 +2,27 @@
|
|
|
2
2
|
* @module SpmDiscoverer
|
|
3
3
|
* @description SPM 项目发现器,适配 ProjectDiscoverer 接口
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* SpmHelper 仅用于 Xcode 工作流的依赖检查/修复,不在此链路加载。
|
|
5
|
+
* 内置 Package.swift 正则解析,提供模块列表和文件遍历。
|
|
7
6
|
*
|
|
8
7
|
* 检测: 项目根或子目录存在 Package.swift
|
|
9
8
|
*/
|
|
10
|
-
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
9
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
11
10
|
import { basename, dirname, extname, join } from 'node:path';
|
|
12
|
-
import { ProjectDiscoverer } from '#core/discovery/ProjectDiscoverer.js';
|
|
13
11
|
import { LanguageService } from '#shared/LanguageService.js';
|
|
14
|
-
import {
|
|
12
|
+
import { ProjectDiscoverer } from './ProjectDiscoverer.js';
|
|
13
|
+
const SKIP_DIRS = new Set([
|
|
14
|
+
'node_modules',
|
|
15
|
+
'.git',
|
|
16
|
+
'Build',
|
|
17
|
+
'.build',
|
|
18
|
+
'.swiftpm',
|
|
19
|
+
'Pods',
|
|
20
|
+
'DerivedData',
|
|
21
|
+
'Carthage',
|
|
22
|
+
'.cursor',
|
|
23
|
+
]);
|
|
15
24
|
export class SpmDiscoverer extends ProjectDiscoverer {
|
|
16
|
-
#parser = null;
|
|
17
25
|
#projectRoot = null;
|
|
18
|
-
/** >} */
|
|
19
26
|
#parsedPackages = [];
|
|
20
27
|
get id() {
|
|
21
28
|
return 'spm';
|
|
@@ -24,12 +31,10 @@ export class SpmDiscoverer extends ProjectDiscoverer {
|
|
|
24
31
|
return 'Swift Package Manager (SPM)';
|
|
25
32
|
}
|
|
26
33
|
async detect(projectRoot) {
|
|
27
|
-
// 检查项目根是否有 Package.swift
|
|
28
34
|
const hasRoot = existsSync(join(projectRoot, 'Package.swift'));
|
|
29
35
|
if (hasRoot) {
|
|
30
36
|
return { match: true, confidence: 0.95, reason: 'Package.swift found at project root' };
|
|
31
37
|
}
|
|
32
|
-
// 检查子目录是否有 Package.swift(多包项目)
|
|
33
38
|
try {
|
|
34
39
|
const entries = readdirSync(projectRoot, { withFileTypes: true });
|
|
35
40
|
for (const entry of entries) {
|
|
@@ -51,12 +56,11 @@ export class SpmDiscoverer extends ProjectDiscoverer {
|
|
|
51
56
|
}
|
|
52
57
|
async load(projectRoot) {
|
|
53
58
|
this.#projectRoot = projectRoot;
|
|
54
|
-
this.#parser = new PackageSwiftParser(projectRoot);
|
|
55
59
|
this.#parsedPackages = [];
|
|
56
|
-
const allPaths = this.#
|
|
60
|
+
const allPaths = this.#findAllPackageSwifts(projectRoot);
|
|
57
61
|
for (const pkgPath of allPaths) {
|
|
58
62
|
try {
|
|
59
|
-
const parsed = this.#
|
|
63
|
+
const parsed = this.#parsePackageSwift(pkgPath);
|
|
60
64
|
if (parsed) {
|
|
61
65
|
this.#parsedPackages.push({ pkgPath, parsed });
|
|
62
66
|
}
|
|
@@ -67,9 +71,6 @@ export class SpmDiscoverer extends ProjectDiscoverer {
|
|
|
67
71
|
}
|
|
68
72
|
}
|
|
69
73
|
async listTargets() {
|
|
70
|
-
if (!this.#parser) {
|
|
71
|
-
return [];
|
|
72
|
-
}
|
|
73
74
|
const targets = [];
|
|
74
75
|
for (const { pkgPath, parsed } of this.#parsedPackages) {
|
|
75
76
|
const pkgDir = dirname(pkgPath);
|
|
@@ -91,17 +92,12 @@ export class SpmDiscoverer extends ProjectDiscoverer {
|
|
|
91
92
|
return targets;
|
|
92
93
|
}
|
|
93
94
|
async getTargetFiles(target) {
|
|
94
|
-
if (!this.#parser) {
|
|
95
|
-
return [];
|
|
96
|
-
}
|
|
97
95
|
const targetName = typeof target === 'string' ? target : target.name;
|
|
98
|
-
// 找到 target 所在的包目录和自定义 path
|
|
99
96
|
let sourcesDir = null;
|
|
100
97
|
for (const { pkgPath, parsed } of this.#parsedPackages) {
|
|
101
98
|
const matchTarget = parsed.targets?.find((t) => t.name === targetName);
|
|
102
99
|
if (matchTarget) {
|
|
103
100
|
const pkgDir = dirname(pkgPath);
|
|
104
|
-
// 优先使用 target 声明的自定义 path
|
|
105
101
|
const candidates = [];
|
|
106
102
|
if (matchTarget.path) {
|
|
107
103
|
candidates.push(join(pkgDir, matchTarget.path));
|
|
@@ -120,7 +116,6 @@ export class SpmDiscoverer extends ProjectDiscoverer {
|
|
|
120
116
|
}
|
|
121
117
|
}
|
|
122
118
|
if (!sourcesDir) {
|
|
123
|
-
// Fallback: projectRoot/Sources/targetName
|
|
124
119
|
const fallback = join(this.#projectRoot, 'Sources', targetName);
|
|
125
120
|
if (existsSync(fallback)) {
|
|
126
121
|
sourcesDir = fallback;
|
|
@@ -137,7 +132,7 @@ export class SpmDiscoverer extends ProjectDiscoverer {
|
|
|
137
132
|
}));
|
|
138
133
|
}
|
|
139
134
|
async getDependencyGraph() {
|
|
140
|
-
if (!this.#projectRoot
|
|
135
|
+
if (!this.#projectRoot) {
|
|
141
136
|
return { nodes: [], edges: [] };
|
|
142
137
|
}
|
|
143
138
|
if (this.#parsedPackages.length === 0) {
|
|
@@ -147,7 +142,6 @@ export class SpmDiscoverer extends ProjectDiscoverer {
|
|
|
147
142
|
const edges = [];
|
|
148
143
|
const pkgNameSet = new Set();
|
|
149
144
|
const targetToPkg = new Map();
|
|
150
|
-
// ── 第一遍:收集所有 package + target 节点 ──
|
|
151
145
|
const allParsed = [];
|
|
152
146
|
const umbrellaNames = new Set();
|
|
153
147
|
for (const { pkgPath, parsed } of this.#parsedPackages) {
|
|
@@ -156,14 +150,12 @@ export class SpmDiscoverer extends ProjectDiscoverer {
|
|
|
156
150
|
}
|
|
157
151
|
pkgNameSet.add(parsed.name);
|
|
158
152
|
allParsed.push({ ...parsed, _dir: dirname(pkgPath) });
|
|
159
|
-
// 跳过 umbrella 包(无 targets + 无 products)——它只是组织子包的入口
|
|
160
153
|
const hasTargets = parsed.targets && parsed.targets.length > 0;
|
|
161
154
|
const hasProducts = parsed.products && parsed.products.length > 0;
|
|
162
155
|
if (!hasTargets && !hasProducts) {
|
|
163
156
|
umbrellaNames.add(parsed.name);
|
|
164
157
|
continue;
|
|
165
158
|
}
|
|
166
|
-
// package 节点
|
|
167
159
|
nodes.push({
|
|
168
160
|
id: parsed.name,
|
|
169
161
|
label: parsed.name,
|
|
@@ -171,7 +163,6 @@ export class SpmDiscoverer extends ProjectDiscoverer {
|
|
|
171
163
|
fullPath: dirname(pkgPath),
|
|
172
164
|
targetCount: parsed.targets.length,
|
|
173
165
|
});
|
|
174
|
-
// target 节点
|
|
175
166
|
for (const t of parsed.targets) {
|
|
176
167
|
nodes.push({
|
|
177
168
|
id: t.name,
|
|
@@ -182,27 +173,22 @@ export class SpmDiscoverer extends ProjectDiscoverer {
|
|
|
182
173
|
});
|
|
183
174
|
targetToPkg.set(t.name, parsed.name);
|
|
184
175
|
}
|
|
185
|
-
// product name → package(product 名可能和 target 名不同)
|
|
186
176
|
for (const prod of parsed.products || []) {
|
|
187
177
|
if (!targetToPkg.has(prod.name)) {
|
|
188
178
|
targetToPkg.set(prod.name, parsed.name);
|
|
189
179
|
}
|
|
190
180
|
}
|
|
191
181
|
}
|
|
192
|
-
// ── 第二遍:构建 edges ──
|
|
193
182
|
for (const parsed of allParsed) {
|
|
194
|
-
// 跳过 umbrella 包的边
|
|
195
183
|
if (umbrellaNames.has(parsed.name)) {
|
|
196
184
|
continue;
|
|
197
185
|
}
|
|
198
|
-
// 包级 local path 依赖
|
|
199
186
|
for (const dep of parsed.dependencies || []) {
|
|
200
187
|
if (dep.type === 'local' && 'path' in dep && dep.path) {
|
|
201
188
|
const depPkgSwift = join(parsed._dir, dep.path, 'Package.swift');
|
|
202
189
|
if (existsSync(depPkgSwift)) {
|
|
203
190
|
try {
|
|
204
|
-
const depParsed = this.#
|
|
205
|
-
// 跳过指向 umbrella 包的边
|
|
191
|
+
const depParsed = this.#parsePackageSwift(depPkgSwift);
|
|
206
192
|
if (!umbrellaNames.has(depParsed.name)) {
|
|
207
193
|
edges.push({ from: parsed.name, to: depParsed.name, type: 'depends_on' });
|
|
208
194
|
}
|
|
@@ -224,12 +210,9 @@ export class SpmDiscoverer extends ProjectDiscoverer {
|
|
|
224
210
|
edges.push({ from: parsed.name, to: remoteName, type: 'depends_on' });
|
|
225
211
|
}
|
|
226
212
|
}
|
|
227
|
-
// target 级依赖
|
|
228
213
|
for (const t of parsed.targets || []) {
|
|
229
|
-
// target → parent package (contains)
|
|
230
214
|
edges.push({ from: parsed.name, to: t.name, type: 'contains' });
|
|
231
215
|
for (const depName of t.dependencies || []) {
|
|
232
|
-
// target → target 依赖(跳过指向 umbrella 包的)
|
|
233
216
|
if (!umbrellaNames.has(depName)) {
|
|
234
217
|
edges.push({ from: t.name, to: depName, type: 'depends_on' });
|
|
235
218
|
}
|
|
@@ -238,16 +221,148 @@ export class SpmDiscoverer extends ProjectDiscoverer {
|
|
|
238
221
|
}
|
|
239
222
|
return { nodes, edges };
|
|
240
223
|
}
|
|
241
|
-
/** @deprecated SpmHelper 不再由 SpmDiscoverer 持有,仅 XcodeIntegration 通过 container 使用 */
|
|
242
|
-
getSpmService() {
|
|
243
|
-
return null;
|
|
244
|
-
}
|
|
245
224
|
// ─────────────── Private Helpers ───────────────
|
|
246
|
-
/**
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
225
|
+
/** 向下递归扫描所有 Package.swift(支持多 Package 项目) */
|
|
226
|
+
#findAllPackageSwifts(rootDir) {
|
|
227
|
+
const results = [];
|
|
228
|
+
const scan = (dir, depth = 0) => {
|
|
229
|
+
if (depth > 5) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
234
|
+
for (const entry of entries) {
|
|
235
|
+
if (entry.isDirectory()) {
|
|
236
|
+
if (SKIP_DIRS.has(entry.name)) {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
scan(join(dir, entry.name), depth + 1);
|
|
240
|
+
}
|
|
241
|
+
else if (entry.name === 'Package.swift') {
|
|
242
|
+
results.push(join(dir, entry.name));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
// 权限错误等,跳过
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
scan(rootDir);
|
|
251
|
+
return results;
|
|
252
|
+
}
|
|
253
|
+
/** 简易解析 Package.swift(无 Swift 编译器,使用正则) */
|
|
254
|
+
#parsePackageSwift(packagePath) {
|
|
255
|
+
if (!packagePath || !existsSync(packagePath)) {
|
|
256
|
+
throw new Error(`Package.swift not found: ${packagePath}`);
|
|
257
|
+
}
|
|
258
|
+
const content = readFileSync(packagePath, 'utf-8');
|
|
259
|
+
return {
|
|
260
|
+
path: packagePath,
|
|
261
|
+
name: this.#extractName(content),
|
|
262
|
+
version: this.#extractVersion(content),
|
|
263
|
+
targets: this.#extractTargets(content),
|
|
264
|
+
dependencies: this.#extractDependencies(content),
|
|
265
|
+
products: this.#extractProducts(content),
|
|
266
|
+
platforms: this.#extractPlatforms(content),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
#extractName(content) {
|
|
270
|
+
const m = content.match(/name\s*:\s*"([^"]+)"/);
|
|
271
|
+
return m ? m[1] : 'unknown';
|
|
272
|
+
}
|
|
273
|
+
#extractVersion(content) {
|
|
274
|
+
const m = content.match(/version\s*:\s*"([^"]+)"/);
|
|
275
|
+
return m ? m[1] : '0.0.0';
|
|
276
|
+
}
|
|
277
|
+
#extractTargets(content) {
|
|
278
|
+
const targets = [];
|
|
279
|
+
const re = /\.(?:target|testTarget|executableTarget)\s*\(/g;
|
|
280
|
+
let match;
|
|
281
|
+
while ((match = re.exec(content)) !== null) {
|
|
282
|
+
const type = match[0].includes('testTarget')
|
|
283
|
+
? 'testTarget'
|
|
284
|
+
: match[0].includes('executableTarget')
|
|
285
|
+
? 'executableTarget'
|
|
286
|
+
: 'target';
|
|
287
|
+
const startPos = match.index + match[0].length;
|
|
288
|
+
let depth = 1;
|
|
289
|
+
let endPos = startPos;
|
|
290
|
+
while (depth > 0 && endPos < content.length) {
|
|
291
|
+
if (content[endPos] === '(') {
|
|
292
|
+
depth++;
|
|
293
|
+
}
|
|
294
|
+
else if (content[endPos] === ')') {
|
|
295
|
+
depth--;
|
|
296
|
+
}
|
|
297
|
+
endPos++;
|
|
298
|
+
}
|
|
299
|
+
if (depth === 0) {
|
|
300
|
+
const block = content.substring(startPos, endPos - 1);
|
|
301
|
+
const nameMatch = block.match(/name\s*:\s*"([^"]+)"/);
|
|
302
|
+
if (!nameMatch) {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
const pathMatch = block.match(/path\s*:\s*"([^"]+)"/);
|
|
306
|
+
const depsMatch = block.match(/dependencies\s*:\s*\[([^\]]*)\]/s);
|
|
307
|
+
const deps = [];
|
|
308
|
+
if (depsMatch) {
|
|
309
|
+
const depRe = /\.(?:product|target)\s*\(\s*name\s*:\s*"([^"]+)"/g;
|
|
310
|
+
let dm;
|
|
311
|
+
while ((dm = depRe.exec(depsMatch[1])) !== null) {
|
|
312
|
+
deps.push(dm[1]);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
targets.push({
|
|
316
|
+
name: nameMatch[1],
|
|
317
|
+
type,
|
|
318
|
+
path: pathMatch ? pathMatch[1] : null,
|
|
319
|
+
dependencies: deps,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return targets;
|
|
324
|
+
}
|
|
325
|
+
#extractDependencies(content) {
|
|
326
|
+
const deps = [];
|
|
327
|
+
const urlRe = /\.package\s*\(\s*url\s*:\s*"([^"]+)"[^)]*\)/g;
|
|
328
|
+
let m;
|
|
329
|
+
while ((m = urlRe.exec(content)) !== null) {
|
|
330
|
+
const block = m[0];
|
|
331
|
+
const fromMatch = block.match(/from\s*:\s*"([^"]+)"/);
|
|
332
|
+
const exactMatch = block.match(/exact\s*:\s*"([^"]+)"/);
|
|
333
|
+
deps.push({
|
|
334
|
+
url: m[1],
|
|
335
|
+
version: fromMatch ? fromMatch[1] : exactMatch ? exactMatch[1] : null,
|
|
336
|
+
type: 'package',
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
const pathRe = /\.package\s*\(\s*path\s*:\s*"([^"]+)"\s*\)/g;
|
|
340
|
+
while ((m = pathRe.exec(content)) !== null) {
|
|
341
|
+
deps.push({
|
|
342
|
+
path: m[1],
|
|
343
|
+
type: 'local',
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
return deps;
|
|
347
|
+
}
|
|
348
|
+
#extractProducts(content) {
|
|
349
|
+
const products = [];
|
|
350
|
+
const re = /\.(library|executable)\s*\(\s*name\s*:\s*"([^"]+)"/g;
|
|
351
|
+
let m;
|
|
352
|
+
while ((m = re.exec(content)) !== null) {
|
|
353
|
+
products.push({ name: m[2], type: m[1] });
|
|
354
|
+
}
|
|
355
|
+
return products;
|
|
356
|
+
}
|
|
357
|
+
#extractPlatforms(content) {
|
|
358
|
+
const platforms = [];
|
|
359
|
+
const re = /\.(iOS|macOS|tvOS|watchOS|visionOS)\s*\(\s*\.v(\d+(?:_\d+)?)\s*\)/g;
|
|
360
|
+
let m;
|
|
361
|
+
while ((m = re.exec(content)) !== null) {
|
|
362
|
+
platforms.push({ name: m[1], version: m[2].replace(/_/g, '.') });
|
|
363
|
+
}
|
|
364
|
+
return platforms;
|
|
365
|
+
}
|
|
251
366
|
#walkSourceFiles(dir) {
|
|
252
367
|
const CODE_EXTS = new Set(['.swift', '.m', '.h', '.c', '.cpp', '.mm']);
|
|
253
368
|
const SKIP_DIRS = new Set([
|
|
@@ -7,7 +7,6 @@ import { DiscovererRegistry } from './DiscovererRegistry.js';
|
|
|
7
7
|
export declare function getDiscovererRegistry(): DiscovererRegistry;
|
|
8
8
|
/** 重置 Registry(仅用于测试) */
|
|
9
9
|
export declare function resetDiscovererRegistry(): void;
|
|
10
|
-
export { SpmDiscoverer } from '../../platform/ios/spm/SpmDiscoverer.js';
|
|
11
10
|
export { DartDiscoverer } from './DartDiscoverer.js';
|
|
12
11
|
export { DiscovererRegistry } from './DiscovererRegistry.js';
|
|
13
12
|
export { GenericDiscoverer } from './GenericDiscoverer.js';
|
|
@@ -17,3 +16,4 @@ export { NodeDiscoverer } from './NodeDiscoverer.js';
|
|
|
17
16
|
export { ProjectDiscoverer } from './ProjectDiscoverer.js';
|
|
18
17
|
export { PythonDiscoverer } from './PythonDiscoverer.js';
|
|
19
18
|
export { RustDiscoverer } from './RustDiscoverer.js';
|
|
19
|
+
export { SpmDiscoverer } from './SpmDiscoverer.js';
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* @module discovery/index
|
|
3
3
|
* @description ProjectDiscoverer 系统入口 - 初始化 Registry 并注册所有 Discoverer
|
|
4
4
|
*/
|
|
5
|
-
import { SpmDiscoverer } from '../../platform/ios/spm/SpmDiscoverer.js';
|
|
6
5
|
import { DartDiscoverer } from './DartDiscoverer.js';
|
|
7
6
|
import { DiscovererRegistry } from './DiscovererRegistry.js';
|
|
8
7
|
import { GenericDiscoverer } from './GenericDiscoverer.js';
|
|
@@ -11,6 +10,7 @@ import { JvmDiscoverer } from './JvmDiscoverer.js';
|
|
|
11
10
|
import { NodeDiscoverer } from './NodeDiscoverer.js';
|
|
12
11
|
import { PythonDiscoverer } from './PythonDiscoverer.js';
|
|
13
12
|
import { RustDiscoverer } from './RustDiscoverer.js';
|
|
13
|
+
import { SpmDiscoverer } from './SpmDiscoverer.js';
|
|
14
14
|
let _registry = null;
|
|
15
15
|
/** 获取全局 DiscovererRegistry 单例 */
|
|
16
16
|
export function getDiscovererRegistry() {
|
|
@@ -32,7 +32,6 @@ export function getDiscovererRegistry() {
|
|
|
32
32
|
export function resetDiscovererRegistry() {
|
|
33
33
|
_registry = null;
|
|
34
34
|
}
|
|
35
|
-
export { SpmDiscoverer } from '../../platform/ios/spm/SpmDiscoverer.js';
|
|
36
35
|
export { DartDiscoverer } from './DartDiscoverer.js';
|
|
37
36
|
export { DiscovererRegistry } from './DiscovererRegistry.js';
|
|
38
37
|
export { GenericDiscoverer } from './GenericDiscoverer.js';
|
|
@@ -43,3 +42,4 @@ export { NodeDiscoverer } from './NodeDiscoverer.js';
|
|
|
43
42
|
export { ProjectDiscoverer } from './ProjectDiscoverer.js';
|
|
44
43
|
export { PythonDiscoverer } from './PythonDiscoverer.js';
|
|
45
44
|
export { RustDiscoverer } from './RustDiscoverer.js';
|
|
45
|
+
export { SpmDiscoverer } from './SpmDiscoverer.js';
|