grf-cli 1.0.5 → 1.0.7
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/cli.js.map +1 -1
- package/dist/commands/list/index.d.ts.map +1 -1
- package/dist/commands/list/index.js +9 -1
- package/dist/commands/list/index.js.map +1 -1
- package/dist/core/loading-state.js.map +1 -1
- package/dist/utils/constants.js +1 -1
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/validation.js +1 -1
- package/dist/utils/validation.js.map +1 -1
- package/package.json +1 -1
- package/dist/commands/add.d.ts +0 -12
- package/dist/commands/add.d.ts.map +0 -1
- package/dist/commands/add.js +0 -92
- package/dist/commands/add.js.map +0 -1
- package/dist/commands/clean.d.ts +0 -12
- package/dist/commands/clean.d.ts.map +0 -1
- package/dist/commands/clean.js +0 -154
- package/dist/commands/clean.js.map +0 -1
- package/dist/commands/config.d.ts +0 -12
- package/dist/commands/config.d.ts.map +0 -1
- package/dist/commands/config.js +0 -151
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/list.d.ts +0 -12
- package/dist/commands/list.d.ts.map +0 -1
- package/dist/commands/list.js +0 -118
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/load.d.ts +0 -13
- package/dist/commands/load.d.ts.map +0 -1
- package/dist/commands/load.js +0 -202
- package/dist/commands/load.js.map +0 -1
- package/dist/commands/unload.d.ts +0 -12
- package/dist/commands/unload.d.ts.map +0 -1
- package/dist/commands/unload.js +0 -734
- package/dist/commands/unload.js.map +0 -1
- package/dist/commands/update.d.ts +0 -12
- package/dist/commands/update.d.ts.map +0 -1
- package/dist/commands/update.js +0 -464
- package/dist/commands/update.js.map +0 -1
- package/dist/commands/use.d.ts +0 -8
- package/dist/commands/use.d.ts.map +0 -1
- package/dist/commands/use.js +0 -224
- package/dist/commands/use.js.map +0 -1
package/dist/commands/unload.js
DELETED
|
@@ -1,734 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* unload 命令
|
|
4
|
-
* 从当前项目中移除已加载的参考代码(use 命令的逆操作)
|
|
5
|
-
*/
|
|
6
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
-
if (k2 === undefined) k2 = k;
|
|
8
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
-
}
|
|
12
|
-
Object.defineProperty(o, k2, desc);
|
|
13
|
-
}) : (function(o, m, k, k2) {
|
|
14
|
-
if (k2 === undefined) k2 = k;
|
|
15
|
-
o[k2] = m[k];
|
|
16
|
-
}));
|
|
17
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
-
}) : function(o, v) {
|
|
20
|
-
o["default"] = v;
|
|
21
|
-
});
|
|
22
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
-
var ownKeys = function(o) {
|
|
24
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
-
var ar = [];
|
|
26
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
-
return ar;
|
|
28
|
-
};
|
|
29
|
-
return ownKeys(o);
|
|
30
|
-
};
|
|
31
|
-
return function (mod) {
|
|
32
|
-
if (mod && mod.__esModule) return mod;
|
|
33
|
-
var result = {};
|
|
34
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
-
__setModuleDefault(result, mod);
|
|
36
|
-
return result;
|
|
37
|
-
};
|
|
38
|
-
})();
|
|
39
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
40
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
41
|
-
};
|
|
42
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
-
exports.unloadCommand = void 0;
|
|
44
|
-
exports.registerUnloadCommand = registerUnloadCommand;
|
|
45
|
-
const commander_1 = require("commander");
|
|
46
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
47
|
-
const path_1 = __importDefault(require("path"));
|
|
48
|
-
const prompts_1 = require("@inquirer/prompts");
|
|
49
|
-
const filesystem = __importStar(require("../core/filesystem.js"));
|
|
50
|
-
const loading = __importStar(require("../core/loading.js"));
|
|
51
|
-
const spinner_js_1 = require("../ui/spinner.js");
|
|
52
|
-
const prompt_js_1 = require("../ui/prompt.js");
|
|
53
|
-
const table_js_1 = require("../ui/table.js");
|
|
54
|
-
const error_js_1 = require("../utils/error.js");
|
|
55
|
-
const constants_js_1 = require("../utils/constants.js");
|
|
56
|
-
const index_js_1 = require("./options/index.js");
|
|
57
|
-
/**
|
|
58
|
-
* 注册 unload 命令
|
|
59
|
-
* @param program Commander 程序实例
|
|
60
|
-
*/
|
|
61
|
-
function registerUnloadCommand(program) {
|
|
62
|
-
program.addCommand(exports.unloadCommand);
|
|
63
|
-
}
|
|
64
|
-
/** .gitreference 目录名(使用共享常量) */
|
|
65
|
-
const GITREFERENCE_DIR = constants_js_1.DIR_NAMES.GITREFERENCE;
|
|
66
|
-
/**
|
|
67
|
-
* 从 loading.json 获取所有已加载的仓库条目
|
|
68
|
-
* @returns 加载条目列表
|
|
69
|
-
*/
|
|
70
|
-
async function getLoadedEntries() {
|
|
71
|
-
return await loading.getEntries();
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* 递归扫描 .gitreference 目录以获取空目录列表
|
|
75
|
-
* @param baseDir .gitreference 目录的绝对路径
|
|
76
|
-
* @param currentPath 当前正在扫描的相对路径
|
|
77
|
-
* @param loadedPaths 已加载路径列表(用于排除)
|
|
78
|
-
* @returns 空目录列表
|
|
79
|
-
*/
|
|
80
|
-
async function scanEmptyDirs(baseDir, currentPath = "", loadedPaths = new Set()) {
|
|
81
|
-
const emptyDirs = [];
|
|
82
|
-
const fullCurrentPath = path_1.default.join(baseDir, currentPath);
|
|
83
|
-
try {
|
|
84
|
-
const entries = await filesystem.readDir(fullCurrentPath);
|
|
85
|
-
// 如果目录为空,标记为空目录
|
|
86
|
-
if (entries.length === 0 && currentPath) {
|
|
87
|
-
emptyDirs.push({
|
|
88
|
-
fullPath: currentPath,
|
|
89
|
-
absolutePath: fullCurrentPath,
|
|
90
|
-
});
|
|
91
|
-
return emptyDirs;
|
|
92
|
-
}
|
|
93
|
-
// 检查当前目录是否包含非目录文件
|
|
94
|
-
let hasFiles = false;
|
|
95
|
-
const subdirs = [];
|
|
96
|
-
for (const entry of entries) {
|
|
97
|
-
const entryPath = path_1.default.join(fullCurrentPath, entry);
|
|
98
|
-
try {
|
|
99
|
-
const isDir = await filesystem.isDirectory(entryPath);
|
|
100
|
-
if (isDir) {
|
|
101
|
-
subdirs.push(entry);
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
hasFiles = true;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
catch {
|
|
108
|
-
// 忽略无法访问的条目
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
// 如果当前目录没有文件,继续递归扫描子目录
|
|
112
|
-
if (!hasFiles) {
|
|
113
|
-
for (const subdir of subdirs) {
|
|
114
|
-
const subPath = currentPath ? path_1.default.join(currentPath, subdir) : subdir;
|
|
115
|
-
const subEmptyDirs = await scanEmptyDirs(baseDir, subPath, loadedPaths);
|
|
116
|
-
emptyDirs.push(...subEmptyDirs);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
catch {
|
|
121
|
-
// 目录不存在或无法访问
|
|
122
|
-
}
|
|
123
|
-
return emptyDirs;
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* 匹配仓库条目
|
|
127
|
-
* @param entries 加载条目列表
|
|
128
|
-
* @param name 要匹配的名称(可以是 repoName、targetPath 或部分路径)
|
|
129
|
-
* @returns 匹配的条目列表
|
|
130
|
-
*/
|
|
131
|
-
function matchEntries(entries, name) {
|
|
132
|
-
const normalizedName = name.replace(/\\/g, "/");
|
|
133
|
-
return entries.filter((entry) => {
|
|
134
|
-
const repoName = entry.repoName.replace(/\\/g, "/");
|
|
135
|
-
const targetPath = entry.targetPath.replace(/\\/g, "/");
|
|
136
|
-
// 完整仓库名称匹配: github.com/facebook/react
|
|
137
|
-
if (repoName === normalizedName) {
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
// 完整目标路径匹配
|
|
141
|
-
if (targetPath === normalizedName) {
|
|
142
|
-
return true;
|
|
143
|
-
}
|
|
144
|
-
// 短仓库名称匹配: react -> 匹配所有名为 react 的仓库
|
|
145
|
-
const shortName = path_1.default.basename(repoName);
|
|
146
|
-
if (shortName === normalizedName) {
|
|
147
|
-
return true;
|
|
148
|
-
}
|
|
149
|
-
// 部分路径匹配: facebook/react -> 匹配 */facebook/react
|
|
150
|
-
if (repoName.endsWith("/" + normalizedName)) {
|
|
151
|
-
return true;
|
|
152
|
-
}
|
|
153
|
-
// 目标路径部分匹配
|
|
154
|
-
if (targetPath.endsWith("/" + normalizedName)) {
|
|
155
|
-
return true;
|
|
156
|
-
}
|
|
157
|
-
return false;
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* 递归删除空的父目录
|
|
162
|
-
* @param dirPath 起始目录路径
|
|
163
|
-
* @param stopAt 停止的目录(不会删除此目录)
|
|
164
|
-
* @param verbose 是否输出详细信息
|
|
165
|
-
*/
|
|
166
|
-
async function removeEmptyParents(dirPath, stopAt, verbose = false) {
|
|
167
|
-
let currentDir = path_1.default.dirname(dirPath);
|
|
168
|
-
while (currentDir !== stopAt && currentDir.startsWith(stopAt)) {
|
|
169
|
-
try {
|
|
170
|
-
const entries = await filesystem.readDir(currentDir);
|
|
171
|
-
if (entries.length === 0) {
|
|
172
|
-
if (verbose) {
|
|
173
|
-
console.log(chalk_1.default.gray(` Cleaning empty directory: ${path_1.default.relative(stopAt, currentDir)}`));
|
|
174
|
-
}
|
|
175
|
-
await filesystem.removeDir(currentDir);
|
|
176
|
-
currentDir = path_1.default.dirname(currentDir);
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
break;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
catch {
|
|
183
|
-
break;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* 清理所有空目录
|
|
189
|
-
* @param gitrefDir .gitreference 目录的绝对路径
|
|
190
|
-
* @param emptyDirs 空目录列表
|
|
191
|
-
* @param verbose 是否输出详细信息
|
|
192
|
-
* @returns 清理的目录数量
|
|
193
|
-
*/
|
|
194
|
-
async function cleanEmptyDirectories(gitrefDir, emptyDirs, verbose = false) {
|
|
195
|
-
let cleanedCount = 0;
|
|
196
|
-
// 按路径深度排序,先删除最深的目录
|
|
197
|
-
const sortedDirs = [...emptyDirs].sort((a, b) => {
|
|
198
|
-
const depthA = a.fullPath.split(path_1.default.sep).length;
|
|
199
|
-
const depthB = b.fullPath.split(path_1.default.sep).length;
|
|
200
|
-
return depthB - depthA;
|
|
201
|
-
});
|
|
202
|
-
for (const dir of sortedDirs) {
|
|
203
|
-
try {
|
|
204
|
-
// 检查目录是否仍然存在且为空
|
|
205
|
-
if (await filesystem.exists(dir.absolutePath)) {
|
|
206
|
-
const entries = await filesystem.readDir(dir.absolutePath);
|
|
207
|
-
if (entries.length === 0) {
|
|
208
|
-
if (verbose) {
|
|
209
|
-
console.log(chalk_1.default.gray(` Removing empty directory: ${dir.fullPath.replace(/\\/g, "/")}`));
|
|
210
|
-
}
|
|
211
|
-
await filesystem.removeDir(dir.absolutePath);
|
|
212
|
-
cleanedCount++;
|
|
213
|
-
// 递归清理空的父目录
|
|
214
|
-
await removeEmptyParents(dir.absolutePath, gitrefDir, verbose);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
catch {
|
|
219
|
-
// 忽略删除失败的目录
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return cleanedCount;
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* 交互式仓库条目选择
|
|
226
|
-
* @param matches 匹配的条目列表
|
|
227
|
-
* @param name 用户输入的名称
|
|
228
|
-
* @returns 用户选择的条目,如果取消则返回 null
|
|
229
|
-
*/
|
|
230
|
-
async function selectEntry(matches, name) {
|
|
231
|
-
console.log(chalk_1.default.yellow(`Found ${matches.length} reference code matching '${name}':`));
|
|
232
|
-
console.log();
|
|
233
|
-
try {
|
|
234
|
-
const selected = await (0, prompts_1.select)({
|
|
235
|
-
message: "Select reference code to remove:",
|
|
236
|
-
choices: [
|
|
237
|
-
...matches.map((match) => ({
|
|
238
|
-
name: `${match.repoName} -> ${match.targetPath}`,
|
|
239
|
-
value: match,
|
|
240
|
-
})),
|
|
241
|
-
{
|
|
242
|
-
name: chalk_1.default.gray("Cancel"),
|
|
243
|
-
value: null,
|
|
244
|
-
},
|
|
245
|
-
],
|
|
246
|
-
});
|
|
247
|
-
return selected;
|
|
248
|
-
}
|
|
249
|
-
catch {
|
|
250
|
-
// 用户按 Ctrl+C 取消
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* 在详细模式下删除目录
|
|
256
|
-
* @param dirPath 要删除的目录路径
|
|
257
|
-
* @param displayPath 用于显示的路径
|
|
258
|
-
* @param verbose 是否输出详细信息
|
|
259
|
-
*/
|
|
260
|
-
async function removeDirVerbose(dirPath, displayPath, verbose) {
|
|
261
|
-
if (verbose) {
|
|
262
|
-
console.log(chalk_1.default.gray(` Removing directory: ${displayPath}`));
|
|
263
|
-
}
|
|
264
|
-
await filesystem.removeDir(dirPath);
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* 从 .gitignore 中清理参考代码路径条目
|
|
268
|
-
* @param targetPath 已删除的目标路径(相对于工作目录)
|
|
269
|
-
* @param gitreferenceDirExists .gitreference 目录是否仍然存在
|
|
270
|
-
* @param verbose 是否输出详细信息
|
|
271
|
-
*/
|
|
272
|
-
async function cleanupGitignore(targetPath, gitreferenceDirExists, verbose = false) {
|
|
273
|
-
const cwd = process.cwd();
|
|
274
|
-
// 如果删除的是 .gitreference 目录下的内容
|
|
275
|
-
if (targetPath.startsWith(".gitreference") ||
|
|
276
|
-
targetPath.startsWith(GITREFERENCE_DIR)) {
|
|
277
|
-
// 只有当目录不存在或为空时才从 .gitignore 中移除 .gitreference/ 条目
|
|
278
|
-
if (!gitreferenceDirExists) {
|
|
279
|
-
const removed = await filesystem.removeFromGitignore(cwd, ".gitreference/");
|
|
280
|
-
if (removed && verbose) {
|
|
281
|
-
console.log(chalk_1.default.gray(" Removed .gitreference/ from .gitignore"));
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
else {
|
|
286
|
-
// 如果是自定义路径,直接移除对应的 .gitignore 条目
|
|
287
|
-
const gitignoreEntry = targetPath.endsWith("/")
|
|
288
|
-
? targetPath
|
|
289
|
-
: targetPath + "/";
|
|
290
|
-
const removed = await filesystem.removeFromGitignore(cwd, gitignoreEntry);
|
|
291
|
-
if (removed && verbose) {
|
|
292
|
-
console.log(chalk_1.default.gray(` Removed ${gitignoreEntry} from .gitignore`));
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
exports.unloadCommand = new commander_1.Command("unload")
|
|
297
|
-
.description("Remove reference code from current project")
|
|
298
|
-
.argument("[name]", "Repository name to remove")
|
|
299
|
-
.addOption(index_js_1.allOption)
|
|
300
|
-
.addOption(index_js_1.forceOption)
|
|
301
|
-
.addOption(index_js_1.dryRunOption)
|
|
302
|
-
.addOption(index_js_1.listOption)
|
|
303
|
-
.addOption(index_js_1.keepEmptyOption)
|
|
304
|
-
.addOption(index_js_1.cleanEmptyOption)
|
|
305
|
-
.addOption(index_js_1.verboseOption)
|
|
306
|
-
.action(async (name, options) => {
|
|
307
|
-
const startTime = Date.now();
|
|
308
|
-
try {
|
|
309
|
-
const cwd = process.cwd();
|
|
310
|
-
const gitrefDir = path_1.default.join(cwd, GITREFERENCE_DIR);
|
|
311
|
-
// 获取所有已加载的条目
|
|
312
|
-
const loadedEntries = await getLoadedEntries();
|
|
313
|
-
// 检查 .gitreference 目录是否存在(用于空目录扫描)
|
|
314
|
-
const gitrefDirExists = await filesystem.exists(gitrefDir);
|
|
315
|
-
// 扫描空目录(仅当 .gitreference 目录存在时)
|
|
316
|
-
const loadedPaths = new Set(loadedEntries.map((e) => e.targetPath));
|
|
317
|
-
const emptyDirs = gitrefDirExists
|
|
318
|
-
? await scanEmptyDirs(gitrefDir, "", loadedPaths)
|
|
319
|
-
: [];
|
|
320
|
-
// 情况 0: --clean-empty 选项,清理空目录
|
|
321
|
-
if (options.cleanEmpty) {
|
|
322
|
-
if (emptyDirs.length === 0) {
|
|
323
|
-
console.log(chalk_1.default.green("No empty directories to clean."));
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
console.log(`Found ${chalk_1.default.bold(emptyDirs.length)} empty directories:`);
|
|
327
|
-
for (const dir of emptyDirs) {
|
|
328
|
-
console.log(` - ${dir.fullPath.replace(/\\/g, "/")}`);
|
|
329
|
-
}
|
|
330
|
-
console.log();
|
|
331
|
-
// dry-run 模式
|
|
332
|
-
if (options.dryRun) {
|
|
333
|
-
console.log(chalk_1.default.yellow("(Dry run mode, no actual deletion)"));
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
// 确认删除
|
|
337
|
-
if (!options.force) {
|
|
338
|
-
const confirmed = await (0, prompt_js_1.confirm)("Are you sure you want to clean these empty directories?");
|
|
339
|
-
if (!confirmed) {
|
|
340
|
-
console.log(chalk_1.default.yellow("Operation cancelled."));
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
// 执行清理
|
|
345
|
-
const spinner = (0, spinner_js_1.startSpinner)("Cleaning empty directories...");
|
|
346
|
-
try {
|
|
347
|
-
const cleanedCount = await cleanEmptyDirectories(gitrefDir, emptyDirs, options.verbose);
|
|
348
|
-
// 检查 .gitreference 目录是否为空
|
|
349
|
-
if (!options.keepEmpty) {
|
|
350
|
-
try {
|
|
351
|
-
const remaining = await filesystem.readDir(gitrefDir);
|
|
352
|
-
if (remaining.length === 0) {
|
|
353
|
-
if (options.verbose) {
|
|
354
|
-
console.log(chalk_1.default.gray(` Removing empty .gitreference directory`));
|
|
355
|
-
}
|
|
356
|
-
await filesystem.removeDir(gitrefDir);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
catch {
|
|
360
|
-
// 忽略错误
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
const elapsed = Date.now() - startTime;
|
|
364
|
-
spinner.succeed(chalk_1.default.green(`Cleaned ${cleanedCount} empty directories!`));
|
|
365
|
-
if (options.verbose) {
|
|
366
|
-
console.log(chalk_1.default.gray(` Time elapsed: ${elapsed}ms`));
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
catch (error) {
|
|
370
|
-
spinner.fail(chalk_1.default.red("Cleanup failed"));
|
|
371
|
-
throw error;
|
|
372
|
-
}
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
// 情况 1: --list 选项,列出所有已加载的参考代码
|
|
376
|
-
if (options.list) {
|
|
377
|
-
if (loadedEntries.length === 0 && emptyDirs.length === 0) {
|
|
378
|
-
console.log(chalk_1.default.yellow("No loaded reference code in current project."));
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
if (loadedEntries.length > 0) {
|
|
382
|
-
console.log(chalk_1.default.bold(`📦 Loaded reference code in current project (${loadedEntries.length})`));
|
|
383
|
-
console.log();
|
|
384
|
-
// 表头
|
|
385
|
-
const COL_NAME = 35;
|
|
386
|
-
const COL_PATH = 30;
|
|
387
|
-
const COL_COMMIT = 10;
|
|
388
|
-
console.log(chalk_1.default.gray(" " +
|
|
389
|
-
(0, table_js_1.padEnd)("REPO", COL_NAME) +
|
|
390
|
-
(0, table_js_1.padEnd)("PATH", COL_PATH) +
|
|
391
|
-
(0, table_js_1.padEnd)("COMMIT", COL_COMMIT)));
|
|
392
|
-
// 条目列表
|
|
393
|
-
for (const entry of loadedEntries) {
|
|
394
|
-
const repoName = entry.repoName.replace(/\\/g, "/");
|
|
395
|
-
const targetPath = entry.targetPath.replace(/\\/g, "/");
|
|
396
|
-
const commitShort = entry.commitId
|
|
397
|
-
? entry.commitId.substring(0, 7)
|
|
398
|
-
: "-";
|
|
399
|
-
console.log(" " +
|
|
400
|
-
(0, table_js_1.padEnd)(repoName, COL_NAME) +
|
|
401
|
-
(0, table_js_1.padEnd)(targetPath, COL_PATH) +
|
|
402
|
-
(0, table_js_1.padEnd)(commitShort, COL_COMMIT));
|
|
403
|
-
}
|
|
404
|
-
console.log();
|
|
405
|
-
// 在详细模式下显示更多信息
|
|
406
|
-
if (options.verbose) {
|
|
407
|
-
console.log(chalk_1.default.gray("Details:"));
|
|
408
|
-
for (const entry of loadedEntries) {
|
|
409
|
-
console.log(chalk_1.default.gray(` - ${entry.repoName}`));
|
|
410
|
-
console.log(chalk_1.default.gray(` ID: ${entry.id}`));
|
|
411
|
-
console.log(chalk_1.default.gray(` URL: ${entry.repoUrl}`));
|
|
412
|
-
console.log(chalk_1.default.gray(` Path: ${entry.targetPath}`));
|
|
413
|
-
console.log(chalk_1.default.gray(` Commit: ${entry.commitId}`));
|
|
414
|
-
if (entry.branch) {
|
|
415
|
-
console.log(chalk_1.default.gray(` Branch: ${entry.branch}`));
|
|
416
|
-
}
|
|
417
|
-
if (entry.subdir) {
|
|
418
|
-
console.log(chalk_1.default.gray(` Subdir: ${entry.subdir}`));
|
|
419
|
-
}
|
|
420
|
-
console.log(chalk_1.default.gray(` Loaded at: ${entry.loadedAt}`));
|
|
421
|
-
if (entry.updatedAt) {
|
|
422
|
-
console.log(chalk_1.default.gray(` Updated at: ${entry.updatedAt}`));
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
console.log();
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
else {
|
|
429
|
-
console.log(chalk_1.default.yellow("No loaded reference code in current project."));
|
|
430
|
-
console.log();
|
|
431
|
-
}
|
|
432
|
-
// 显示空目录提示
|
|
433
|
-
if (emptyDirs.length > 0) {
|
|
434
|
-
console.log(chalk_1.default.yellow(`⚠️ Found ${emptyDirs.length} empty directory structures`));
|
|
435
|
-
if (options.verbose) {
|
|
436
|
-
for (const dir of emptyDirs) {
|
|
437
|
-
console.log(chalk_1.default.gray(` - ${dir.fullPath.replace(/\\/g, "/")}`));
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
console.log(chalk_1.default.gray(` Use 'grf unload --clean-empty' to clean empty directories`));
|
|
441
|
-
console.log();
|
|
442
|
-
}
|
|
443
|
-
if (loadedEntries.length > 0) {
|
|
444
|
-
console.log(chalk_1.default.gray(`💡 Use 'grf unload <name>' to remove specific reference code`));
|
|
445
|
-
}
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
// 情况 2: --all 选项,删除所有参考代码
|
|
449
|
-
if (options.all) {
|
|
450
|
-
// 如果没有已加载的条目
|
|
451
|
-
if (loadedEntries.length === 0) {
|
|
452
|
-
console.log(chalk_1.default.yellow("No reference code found to delete."));
|
|
453
|
-
console.log();
|
|
454
|
-
console.log(chalk_1.default.gray("💡 Hint: No loaded reference code recorded in loading.json."));
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
457
|
-
const pathsToDelete = [];
|
|
458
|
-
for (const entry of loadedEntries) {
|
|
459
|
-
const absolutePath = path_1.default.resolve(cwd, entry.targetPath);
|
|
460
|
-
const exists = await filesystem.exists(absolutePath);
|
|
461
|
-
pathsToDelete.push({
|
|
462
|
-
entry,
|
|
463
|
-
absolutePath,
|
|
464
|
-
exists,
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
|
-
// 显示将要删除的内容
|
|
468
|
-
console.log(`Will delete ${chalk_1.default.bold(loadedEntries.length)} reference code:`);
|
|
469
|
-
for (const pathInfo of pathsToDelete) {
|
|
470
|
-
const status = pathInfo.exists
|
|
471
|
-
? ""
|
|
472
|
-
: chalk_1.default.gray(" (path does not exist)");
|
|
473
|
-
console.log(` - ${pathInfo.entry.repoName} -> ${pathInfo.entry.targetPath}${status}`);
|
|
474
|
-
}
|
|
475
|
-
console.log();
|
|
476
|
-
// dry-run 模式
|
|
477
|
-
if (options.dryRun) {
|
|
478
|
-
console.log(chalk_1.default.yellow("(Dry run mode, no actual deletion)"));
|
|
479
|
-
return;
|
|
480
|
-
}
|
|
481
|
-
// 确认删除
|
|
482
|
-
if (!options.force) {
|
|
483
|
-
const confirmed = await (0, prompt_js_1.confirm)("Are you sure you want to delete?");
|
|
484
|
-
if (!confirmed) {
|
|
485
|
-
console.log(chalk_1.default.yellow("Operation cancelled."));
|
|
486
|
-
return;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
// 执行删除
|
|
490
|
-
const spinner = (0, spinner_js_1.startSpinner)("Removing reference code...");
|
|
491
|
-
try {
|
|
492
|
-
let deletedCount = 0;
|
|
493
|
-
for (const pathInfo of pathsToDelete) {
|
|
494
|
-
if (options.verbose) {
|
|
495
|
-
spinner.stop();
|
|
496
|
-
console.log(chalk_1.default.gray(` Removing: ${pathInfo.entry.targetPath}`));
|
|
497
|
-
spinner.start();
|
|
498
|
-
}
|
|
499
|
-
// 删除实际文件/目录(如果存在)
|
|
500
|
-
if (pathInfo.exists) {
|
|
501
|
-
await filesystem.removeDir(pathInfo.absolutePath);
|
|
502
|
-
// 递归删除空的父目录
|
|
503
|
-
if (pathInfo.entry.targetPath.startsWith(".gitreference/") ||
|
|
504
|
-
pathInfo.entry.targetPath.startsWith(GITREFERENCE_DIR + "/")) {
|
|
505
|
-
// 在 .gitreference 下,清理到 gitrefDir 为止
|
|
506
|
-
if (options.verbose) {
|
|
507
|
-
spinner.stop();
|
|
508
|
-
}
|
|
509
|
-
await removeEmptyParents(pathInfo.absolutePath, gitrefDir, options.verbose);
|
|
510
|
-
if (options.verbose) {
|
|
511
|
-
spinner.start();
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
else {
|
|
515
|
-
// 自定义路径,清理到工作目录为止
|
|
516
|
-
if (options.verbose) {
|
|
517
|
-
spinner.stop();
|
|
518
|
-
}
|
|
519
|
-
await removeEmptyParents(pathInfo.absolutePath, cwd, options.verbose);
|
|
520
|
-
if (options.verbose) {
|
|
521
|
-
spinner.start();
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
// 从 .gitignore 中清理对应条目
|
|
526
|
-
const gitignoreEntry = pathInfo.entry.targetPath.endsWith("/")
|
|
527
|
-
? pathInfo.entry.targetPath
|
|
528
|
-
: pathInfo.entry.targetPath + "/";
|
|
529
|
-
await filesystem.removeFromGitignore(cwd, gitignoreEntry);
|
|
530
|
-
deletedCount++;
|
|
531
|
-
}
|
|
532
|
-
// 清空 loading.json
|
|
533
|
-
await loading.clearAllEntries();
|
|
534
|
-
// 检查 .gitreference 目录是否为空
|
|
535
|
-
if (!options.keepEmpty && gitrefDirExists) {
|
|
536
|
-
try {
|
|
537
|
-
const remaining = await filesystem.readDir(gitrefDir);
|
|
538
|
-
// 当只剩 loading.json 或为空时删除整个目录
|
|
539
|
-
if (remaining.length === 0 ||
|
|
540
|
-
(remaining.length === 1 && remaining[0] === "loading.json")) {
|
|
541
|
-
if (options.verbose) {
|
|
542
|
-
spinner.stop();
|
|
543
|
-
console.log(chalk_1.default.gray(` Removing .gitreference directory`));
|
|
544
|
-
spinner.start();
|
|
545
|
-
}
|
|
546
|
-
await filesystem.removeDir(gitrefDir);
|
|
547
|
-
// 从 .gitignore 中移除 .gitreference/ 条目
|
|
548
|
-
await filesystem.removeFromGitignore(cwd, ".gitreference/");
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
catch {
|
|
552
|
-
// 忽略错误
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
const elapsed = Date.now() - startTime;
|
|
556
|
-
spinner.succeed(chalk_1.default.green(`Deleted ${deletedCount} reference code!`));
|
|
557
|
-
if (options.verbose) {
|
|
558
|
-
console.log(chalk_1.default.gray(` Time elapsed: ${elapsed}ms`));
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
catch (error) {
|
|
562
|
-
spinner.fail(chalk_1.default.red("Deletion failed"));
|
|
563
|
-
throw error;
|
|
564
|
-
}
|
|
565
|
-
return;
|
|
566
|
-
}
|
|
567
|
-
// 情况 3: 指定了仓库名称,删除特定参考代码
|
|
568
|
-
if (name) {
|
|
569
|
-
// 匹配条目
|
|
570
|
-
const matches = matchEntries(loadedEntries, name);
|
|
571
|
-
if (matches.length === 0) {
|
|
572
|
-
console.error(chalk_1.default.red(`${chalk_1.default.bold("✗")} No matching reference code found: ${name}`));
|
|
573
|
-
console.log();
|
|
574
|
-
console.log(`Use '${chalk_1.default.cyan("grf unload --list")}' to see all loaded reference code.`);
|
|
575
|
-
process.exit(1);
|
|
576
|
-
}
|
|
577
|
-
let targetEntry;
|
|
578
|
-
if (matches.length > 1) {
|
|
579
|
-
// 如果使用了 --force 选项,仍然需要精确指定
|
|
580
|
-
if (options.force) {
|
|
581
|
-
console.error(chalk_1.default.red(`${chalk_1.default.bold("✗")} Found multiple matching reference code:`));
|
|
582
|
-
console.log();
|
|
583
|
-
for (const match of matches) {
|
|
584
|
-
console.log(` - ${match.repoName} -> ${match.targetPath}`);
|
|
585
|
-
}
|
|
586
|
-
console.log();
|
|
587
|
-
console.log(`Please use full path to specify exactly which reference code to delete.`);
|
|
588
|
-
process.exit(1);
|
|
589
|
-
}
|
|
590
|
-
// 交互式选择
|
|
591
|
-
const selected = await selectEntry(matches, name);
|
|
592
|
-
if (!selected) {
|
|
593
|
-
console.log(chalk_1.default.yellow("Operation cancelled."));
|
|
594
|
-
return;
|
|
595
|
-
}
|
|
596
|
-
targetEntry = selected;
|
|
597
|
-
console.log();
|
|
598
|
-
}
|
|
599
|
-
else {
|
|
600
|
-
targetEntry = matches[0];
|
|
601
|
-
}
|
|
602
|
-
const displayName = targetEntry.repoName.replace(/\\/g, "/");
|
|
603
|
-
const displayPath = targetEntry.targetPath.replace(/\\/g, "/");
|
|
604
|
-
const absolutePath = path_1.default.resolve(cwd, targetEntry.targetPath);
|
|
605
|
-
const pathExists = await filesystem.exists(absolutePath);
|
|
606
|
-
// 显示将要删除的内容
|
|
607
|
-
console.log(`Will delete: ${chalk_1.default.cyan(displayName)}`);
|
|
608
|
-
console.log(` Target path: ${chalk_1.default.gray(displayPath)}`);
|
|
609
|
-
if (!pathExists) {
|
|
610
|
-
console.log(chalk_1.default.yellow(` (Note: Target path does not exist, will only remove record from loading.json)`));
|
|
611
|
-
}
|
|
612
|
-
if (options.verbose) {
|
|
613
|
-
console.log(` Absolute path: ${chalk_1.default.gray(absolutePath)}`);
|
|
614
|
-
console.log(` Commit: ${chalk_1.default.gray(targetEntry.commitId)}`);
|
|
615
|
-
if (targetEntry.branch) {
|
|
616
|
-
console.log(` Branch: ${chalk_1.default.gray(targetEntry.branch)}`);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
console.log();
|
|
620
|
-
// dry-run 模式
|
|
621
|
-
if (options.dryRun) {
|
|
622
|
-
console.log(chalk_1.default.yellow("(Dry run mode, no actual deletion)"));
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
// 确认删除
|
|
626
|
-
if (!options.force) {
|
|
627
|
-
const confirmed = await (0, prompt_js_1.confirm)(`Are you sure you want to delete '${displayName}'?`);
|
|
628
|
-
if (!confirmed) {
|
|
629
|
-
console.log(chalk_1.default.yellow("Operation cancelled."));
|
|
630
|
-
return;
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
// 执行删除
|
|
634
|
-
const spinner = (0, spinner_js_1.startSpinner)("Removing reference code...");
|
|
635
|
-
try {
|
|
636
|
-
// 删除实际文件/目录(如果存在)
|
|
637
|
-
if (pathExists) {
|
|
638
|
-
if (options.verbose) {
|
|
639
|
-
spinner.stop();
|
|
640
|
-
console.log(chalk_1.default.gray(` Removing directory: ${displayPath}`));
|
|
641
|
-
spinner.start();
|
|
642
|
-
}
|
|
643
|
-
await filesystem.removeDir(absolutePath);
|
|
644
|
-
// 递归删除空的父目录
|
|
645
|
-
if (targetEntry.targetPath.startsWith(".gitreference/") ||
|
|
646
|
-
targetEntry.targetPath.startsWith(GITREFERENCE_DIR + "/")) {
|
|
647
|
-
// 在 .gitreference 下,清理到 gitrefDir 为止
|
|
648
|
-
if (options.verbose) {
|
|
649
|
-
spinner.stop();
|
|
650
|
-
}
|
|
651
|
-
await removeEmptyParents(absolutePath, gitrefDir, options.verbose);
|
|
652
|
-
if (options.verbose) {
|
|
653
|
-
spinner.start();
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
else {
|
|
657
|
-
// 自定义路径,清理到工作目录为止
|
|
658
|
-
if (options.verbose) {
|
|
659
|
-
spinner.stop();
|
|
660
|
-
}
|
|
661
|
-
await removeEmptyParents(absolutePath, cwd, options.verbose);
|
|
662
|
-
if (options.verbose) {
|
|
663
|
-
spinner.start();
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
// 从 loading.json 中移除条目
|
|
668
|
-
await loading.removeEntry(targetEntry.id);
|
|
669
|
-
// 从 .gitignore 中清理对应条目
|
|
670
|
-
const gitignoreEntry = targetEntry.targetPath.endsWith("/")
|
|
671
|
-
? targetEntry.targetPath
|
|
672
|
-
: targetEntry.targetPath + "/";
|
|
673
|
-
const gitignoreRemoved = await filesystem.removeFromGitignore(cwd, gitignoreEntry);
|
|
674
|
-
if (gitignoreRemoved && options.verbose) {
|
|
675
|
-
spinner.stop();
|
|
676
|
-
console.log(chalk_1.default.gray(` Removed ${gitignoreEntry} from .gitignore`));
|
|
677
|
-
spinner.start();
|
|
678
|
-
}
|
|
679
|
-
// 检查 .gitreference 目录是否为空
|
|
680
|
-
if (!options.keepEmpty && gitrefDirExists) {
|
|
681
|
-
try {
|
|
682
|
-
const remaining = await filesystem.readDir(gitrefDir);
|
|
683
|
-
// 当只剩 loading.json 或为空时删除整个目录
|
|
684
|
-
if (remaining.length === 0 ||
|
|
685
|
-
(remaining.length === 1 && remaining[0] === "loading.json")) {
|
|
686
|
-
// 检查 loading.json 是否还有其他条目
|
|
687
|
-
const remainingEntries = await loading.getEntries();
|
|
688
|
-
if (remainingEntries.length === 0) {
|
|
689
|
-
if (options.verbose) {
|
|
690
|
-
spinner.stop();
|
|
691
|
-
console.log(chalk_1.default.gray(` Removing empty .gitreference directory`));
|
|
692
|
-
spinner.start();
|
|
693
|
-
}
|
|
694
|
-
await filesystem.removeDir(gitrefDir);
|
|
695
|
-
// 从 .gitignore 中移除 .gitreference/ 条目
|
|
696
|
-
await filesystem.removeFromGitignore(cwd, ".gitreference/");
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
catch {
|
|
701
|
-
// 忽略错误
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
const elapsed = Date.now() - startTime;
|
|
705
|
-
spinner.succeed(chalk_1.default.green("Reference code removed!"));
|
|
706
|
-
console.log();
|
|
707
|
-
console.log(` ${chalk_1.default.gray("Repository:")} ${displayName}`);
|
|
708
|
-
console.log(` ${chalk_1.default.gray("Path:")} ${displayPath}`);
|
|
709
|
-
if (options.verbose) {
|
|
710
|
-
console.log(chalk_1.default.gray(` Time elapsed: ${elapsed}ms`));
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
catch (error) {
|
|
714
|
-
spinner.fail(chalk_1.default.red("Deletion failed"));
|
|
715
|
-
throw error;
|
|
716
|
-
}
|
|
717
|
-
return;
|
|
718
|
-
}
|
|
719
|
-
// 情况 4: 未指定名称、--all 或 --list,显示用法
|
|
720
|
-
console.log(chalk_1.default.yellow("No reference code specified to remove."));
|
|
721
|
-
console.log();
|
|
722
|
-
console.log("Usage:");
|
|
723
|
-
console.log(` ${chalk_1.default.cyan("grf unload <name>")} Remove specific reference code`);
|
|
724
|
-
console.log(` ${chalk_1.default.cyan("grf unload --all")} Remove all reference code`);
|
|
725
|
-
console.log(` ${chalk_1.default.cyan("grf unload --list")} List all loaded reference code`);
|
|
726
|
-
console.log(` ${chalk_1.default.cyan("grf unload --clean-empty")} Clean empty directory structures`);
|
|
727
|
-
console.log();
|
|
728
|
-
console.log(`Use '${chalk_1.default.cyan("grf unload --list")}' to see all loaded reference code.`);
|
|
729
|
-
}
|
|
730
|
-
catch (error) {
|
|
731
|
-
(0, error_js_1.handleError)(error, { exit: true });
|
|
732
|
-
}
|
|
733
|
-
});
|
|
734
|
-
//# sourceMappingURL=unload.js.map
|