docs-cache 0.4.3 → 0.5.1
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.mjs +13 -13
- package/dist/esm/api.d.ts +14 -0
- package/dist/esm/api.mjs +14 -0
- package/dist/esm/cache/cache-layout.d.ts +1 -0
- package/dist/esm/cache/cache-layout.mjs +12 -0
- package/dist/esm/cache/lock.d.ts +21 -0
- package/dist/esm/cache/lock.mjs +91 -0
- package/dist/esm/cache/manifest.d.ts +11 -0
- package/dist/esm/cache/manifest.mjs +68 -0
- package/dist/esm/cache/materialize.d.ts +26 -0
- package/dist/esm/cache/materialize.mjs +442 -0
- package/dist/esm/cache/targets.d.ts +19 -0
- package/dist/esm/cache/targets.mjs +66 -0
- package/dist/esm/cache/toc.d.ts +12 -0
- package/dist/esm/cache/toc.mjs +167 -0
- package/dist/esm/cli/exit-code.d.ts +11 -0
- package/dist/esm/cli/exit-code.mjs +5 -0
- package/dist/esm/cli/index.d.ts +5 -0
- package/dist/esm/cli/index.mjs +345 -0
- package/dist/esm/cli/live-output.d.ts +12 -0
- package/dist/esm/cli/live-output.mjs +30 -0
- package/dist/esm/cli/parse-args.d.ts +13 -0
- package/dist/esm/cli/parse-args.mjs +295 -0
- package/dist/esm/cli/run.d.ts +1 -0
- package/dist/esm/cli/run.mjs +2 -0
- package/dist/esm/cli/task-reporter.d.ts +32 -0
- package/dist/esm/cli/task-reporter.mjs +122 -0
- package/dist/esm/cli/types.d.ts +51 -0
- package/dist/esm/cli/types.mjs +0 -0
- package/dist/esm/cli/ui.d.ts +21 -0
- package/dist/esm/cli/ui.mjs +64 -0
- package/dist/esm/commands/add.d.ts +20 -0
- package/dist/esm/commands/add.mjs +81 -0
- package/dist/esm/commands/clean-git-cache.d.ts +10 -0
- package/dist/esm/commands/clean-git-cache.mjs +48 -0
- package/dist/esm/commands/clean.d.ts +10 -0
- package/dist/esm/commands/clean.mjs +27 -0
- package/dist/esm/commands/init.d.ts +19 -0
- package/dist/esm/commands/init.mjs +179 -0
- package/dist/esm/commands/prune.d.ts +11 -0
- package/dist/esm/commands/prune.mjs +52 -0
- package/dist/esm/commands/remove.d.ts +12 -0
- package/dist/esm/commands/remove.mjs +87 -0
- package/dist/esm/commands/status.d.ts +16 -0
- package/dist/esm/commands/status.mjs +78 -0
- package/dist/esm/commands/sync.d.ts +33 -0
- package/dist/esm/commands/sync.mjs +730 -0
- package/dist/esm/commands/verify.d.ts +11 -0
- package/dist/esm/commands/verify.mjs +120 -0
- package/dist/esm/config/index.d.ts +15 -0
- package/dist/esm/config/index.mjs +196 -0
- package/dist/esm/config/io.d.ts +30 -0
- package/dist/esm/config/io.mjs +112 -0
- package/dist/esm/config/schema.d.ts +171 -0
- package/dist/esm/config/schema.mjs +69 -0
- package/dist/esm/errors.d.ts +3 -0
- package/dist/esm/errors.mjs +2 -0
- package/dist/esm/git/cache-dir.d.ts +16 -0
- package/dist/esm/git/cache-dir.mjs +23 -0
- package/dist/esm/git/fetch-source.d.ts +19 -0
- package/dist/esm/git/fetch-source.mjs +477 -0
- package/dist/esm/git/redact.d.ts +1 -0
- package/dist/esm/git/redact.mjs +4 -0
- package/dist/esm/git/resolve-remote.d.ts +15 -0
- package/dist/esm/git/resolve-remote.mjs +87 -0
- package/dist/esm/git/resolve-repo.d.ts +5 -0
- package/dist/esm/git/resolve-repo.mjs +52 -0
- package/dist/esm/gitignore.d.ts +18 -0
- package/dist/esm/gitignore.mjs +80 -0
- package/dist/esm/paths.d.ts +8 -0
- package/dist/esm/paths.mjs +34 -0
- package/dist/esm/source-id.d.ts +1 -0
- package/dist/esm/source-id.mjs +29 -0
- package/dist/esm/types/sync.d.ts +25 -0
- package/dist/esm/types/sync.mjs +0 -0
- package/package.json +138 -91
- package/dist/chunks/add.mjs +0 -3
- package/dist/chunks/clean-git-cache.mjs +0 -2
- package/dist/chunks/clean.mjs +0 -2
- package/dist/chunks/init.mjs +0 -3
- package/dist/chunks/prune.mjs +0 -2
- package/dist/chunks/remove.mjs +0 -3
- package/dist/chunks/status.mjs +0 -2
- package/dist/chunks/sync.mjs +0 -9
- package/dist/chunks/verify.mjs +0 -2
- package/dist/shared/docs-cache.BOr9BnyP.mjs +0 -5
- package/dist/shared/docs-cache.BSvQNKuf.mjs +0 -2
- package/dist/shared/docs-cache.CQiaFDb_.mjs +0 -7
- package/dist/shared/docs-cache.CaOcl4OS.mjs +0 -3
- package/dist/shared/docs-cache.kK1DPQIQ.mjs +0 -2
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type CleanGitCacheResult = {
|
|
2
|
+
removed: boolean;
|
|
3
|
+
cacheDir: string;
|
|
4
|
+
repoCount?: number;
|
|
5
|
+
bytesFreed?: number;
|
|
6
|
+
};
|
|
7
|
+
export type CleanGitCacheOptions = {
|
|
8
|
+
json?: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare const cleanGitCache: () => Promise<CleanGitCacheResult>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { readdir, rm, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { exists, resolveGitCacheDir } from "#git/cache-dir";
|
|
4
|
+
const getDirSize = async (dirPath) => {
|
|
5
|
+
try {
|
|
6
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
7
|
+
let totalSize = 0;
|
|
8
|
+
for (const entry of entries) {
|
|
9
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
10
|
+
if (entry.isDirectory()) {
|
|
11
|
+
totalSize += await getDirSize(fullPath);
|
|
12
|
+
} else {
|
|
13
|
+
const stats = await stat(fullPath);
|
|
14
|
+
totalSize += stats.size;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return totalSize;
|
|
18
|
+
} catch {
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const countCachedRepos = async (cacheDir) => {
|
|
23
|
+
try {
|
|
24
|
+
const entries = await readdir(cacheDir);
|
|
25
|
+
return entries.length;
|
|
26
|
+
} catch {
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
export const cleanGitCache = async () => {
|
|
31
|
+
const cacheDir = resolveGitCacheDir();
|
|
32
|
+
const cacheExists = await exists(cacheDir);
|
|
33
|
+
if (!cacheExists) {
|
|
34
|
+
return {
|
|
35
|
+
removed: false,
|
|
36
|
+
cacheDir
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const repoCount = await countCachedRepos(cacheDir);
|
|
40
|
+
const bytesFreed = await getDirSize(cacheDir);
|
|
41
|
+
await rm(cacheDir, { recursive: true, force: true });
|
|
42
|
+
return {
|
|
43
|
+
removed: true,
|
|
44
|
+
cacheDir,
|
|
45
|
+
repoCount,
|
|
46
|
+
bytesFreed
|
|
47
|
+
};
|
|
48
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { access, rm } from "node:fs/promises";
|
|
2
|
+
import { DEFAULT_CACHE_DIR, loadConfig } from "#config";
|
|
3
|
+
import { resolveCacheDir } from "#core/paths";
|
|
4
|
+
const exists = async (target) => {
|
|
5
|
+
try {
|
|
6
|
+
await access(target);
|
|
7
|
+
return true;
|
|
8
|
+
} catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
export const cleanCache = async (options) => {
|
|
13
|
+
const { config, resolvedPath } = await loadConfig(options.configPath);
|
|
14
|
+
const cacheDir = resolveCacheDir(
|
|
15
|
+
resolvedPath,
|
|
16
|
+
config.cacheDir ?? DEFAULT_CACHE_DIR,
|
|
17
|
+
options.cacheDirOverride
|
|
18
|
+
);
|
|
19
|
+
const cacheDirExists = await exists(cacheDir);
|
|
20
|
+
if (cacheDirExists) {
|
|
21
|
+
await rm(cacheDir, { recursive: true, force: true });
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
cacheDir,
|
|
25
|
+
removed: cacheDirExists
|
|
26
|
+
};
|
|
27
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { confirm as clackConfirm, isCancel as clackIsCancel, select as clackSelect, text as clackText } from "@clack/prompts";
|
|
2
|
+
type InitOptions = {
|
|
3
|
+
cacheDirOverride?: string;
|
|
4
|
+
json: boolean;
|
|
5
|
+
cwd?: string;
|
|
6
|
+
};
|
|
7
|
+
type PromptDeps = {
|
|
8
|
+
confirm?: typeof clackConfirm;
|
|
9
|
+
isCancel?: typeof clackIsCancel;
|
|
10
|
+
select?: typeof clackSelect;
|
|
11
|
+
text?: typeof clackText;
|
|
12
|
+
};
|
|
13
|
+
export declare const initConfig: (options: InitOptions, deps?: PromptDeps) => Promise<{
|
|
14
|
+
configPath: string;
|
|
15
|
+
created: boolean;
|
|
16
|
+
gitignoreUpdated: any;
|
|
17
|
+
gitignorePath: any;
|
|
18
|
+
}>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { access, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
confirm as clackConfirm,
|
|
5
|
+
isCancel as clackIsCancel,
|
|
6
|
+
select as clackSelect,
|
|
7
|
+
text as clackText
|
|
8
|
+
} from "@clack/prompts";
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_CACHE_DIR,
|
|
11
|
+
DEFAULT_CONFIG_FILENAME,
|
|
12
|
+
stripDefaultConfigValues,
|
|
13
|
+
writeConfig
|
|
14
|
+
} from "#config";
|
|
15
|
+
import { ensureGitignoreEntry, getGitignoreStatus } from "#core/gitignore";
|
|
16
|
+
const exists = async (target) => {
|
|
17
|
+
try {
|
|
18
|
+
await access(target);
|
|
19
|
+
return true;
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const readJson = async (filePath) => {
|
|
25
|
+
const raw = await readFile(filePath, "utf8");
|
|
26
|
+
return JSON.parse(raw);
|
|
27
|
+
};
|
|
28
|
+
const findExistingConfigPaths = async (cwd) => {
|
|
29
|
+
const defaultConfigPath = path.resolve(cwd, DEFAULT_CONFIG_FILENAME);
|
|
30
|
+
const packagePath = path.resolve(cwd, "package.json");
|
|
31
|
+
const existingConfigPaths = [];
|
|
32
|
+
if (await exists(defaultConfigPath)) {
|
|
33
|
+
existingConfigPaths.push(defaultConfigPath);
|
|
34
|
+
}
|
|
35
|
+
if (await exists(packagePath)) {
|
|
36
|
+
const parsed = await readJson(packagePath);
|
|
37
|
+
if (parsed["docs-cache"]) {
|
|
38
|
+
existingConfigPaths.push(packagePath);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { existingConfigPaths, defaultConfigPath, packagePath };
|
|
42
|
+
};
|
|
43
|
+
const selectConfigPath = async (packagePath, defaultConfigPath, select, isCancel) => {
|
|
44
|
+
if (!await exists(packagePath)) {
|
|
45
|
+
return defaultConfigPath;
|
|
46
|
+
}
|
|
47
|
+
const parsed = await readJson(packagePath);
|
|
48
|
+
if (parsed["docs-cache"]) {
|
|
49
|
+
return defaultConfigPath;
|
|
50
|
+
}
|
|
51
|
+
const locationAnswer = await select({
|
|
52
|
+
message: "Config location",
|
|
53
|
+
options: [
|
|
54
|
+
{ value: "config", label: "docs.config.json" },
|
|
55
|
+
{ value: "package", label: "package.json" }
|
|
56
|
+
],
|
|
57
|
+
initialValue: "config"
|
|
58
|
+
});
|
|
59
|
+
if (isCancel(locationAnswer)) {
|
|
60
|
+
throw new Error("Init cancelled.");
|
|
61
|
+
}
|
|
62
|
+
return locationAnswer === "package" ? packagePath : defaultConfigPath;
|
|
63
|
+
};
|
|
64
|
+
const promptInitAnswers = async (cacheDir, cwd, confirm, text, isCancel) => {
|
|
65
|
+
const cacheDirAnswer = await text({
|
|
66
|
+
message: "Cache directory",
|
|
67
|
+
initialValue: cacheDir
|
|
68
|
+
});
|
|
69
|
+
if (isCancel(cacheDirAnswer)) {
|
|
70
|
+
throw new Error("Init cancelled.");
|
|
71
|
+
}
|
|
72
|
+
const cacheDirValue = cacheDirAnswer || DEFAULT_CACHE_DIR;
|
|
73
|
+
const tocAnswer = await confirm({
|
|
74
|
+
message: "Generate TOC.md (table of contents with links to all documentation)",
|
|
75
|
+
initialValue: true
|
|
76
|
+
});
|
|
77
|
+
if (isCancel(tocAnswer)) {
|
|
78
|
+
throw new Error("Init cancelled.");
|
|
79
|
+
}
|
|
80
|
+
const gitignoreStatus = await getGitignoreStatus(cwd, cacheDirValue);
|
|
81
|
+
let gitignoreAnswer = false;
|
|
82
|
+
if (gitignoreStatus.entry && !gitignoreStatus.hasEntry) {
|
|
83
|
+
const reply = await confirm({
|
|
84
|
+
message: "Add cache directory to .gitignore",
|
|
85
|
+
initialValue: true
|
|
86
|
+
});
|
|
87
|
+
if (isCancel(reply)) {
|
|
88
|
+
throw new Error("Init cancelled.");
|
|
89
|
+
}
|
|
90
|
+
gitignoreAnswer = reply;
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
cacheDir: cacheDirValue,
|
|
94
|
+
toc: tocAnswer,
|
|
95
|
+
gitignore: gitignoreAnswer
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
const buildBaseConfig = (cacheDir, toc) => {
|
|
99
|
+
const config = {
|
|
100
|
+
$schema: "https://raw.githubusercontent.com/fbosch/docs-cache/main/docs.config.schema.json",
|
|
101
|
+
sources: []
|
|
102
|
+
};
|
|
103
|
+
if (cacheDir !== DEFAULT_CACHE_DIR) {
|
|
104
|
+
config.cacheDir = cacheDir;
|
|
105
|
+
}
|
|
106
|
+
if (!toc) {
|
|
107
|
+
config.defaults = { toc: false };
|
|
108
|
+
}
|
|
109
|
+
return config;
|
|
110
|
+
};
|
|
111
|
+
const writePackageConfig = async (configPath, config, gitignore) => {
|
|
112
|
+
const raw = await readFile(configPath, "utf8");
|
|
113
|
+
const pkg = JSON.parse(raw);
|
|
114
|
+
if (pkg["docs-cache"]) {
|
|
115
|
+
throw new Error(`docs-cache config already exists in ${configPath}.`);
|
|
116
|
+
}
|
|
117
|
+
pkg["docs-cache"] = stripDefaultConfigValues(config);
|
|
118
|
+
await writeFile(configPath, `${JSON.stringify(pkg, null, 2)}
|
|
119
|
+
`, "utf8");
|
|
120
|
+
const gitignoreResult = gitignore ? await ensureGitignoreEntry(
|
|
121
|
+
path.dirname(configPath),
|
|
122
|
+
config.cacheDir ?? DEFAULT_CACHE_DIR
|
|
123
|
+
) : null;
|
|
124
|
+
return {
|
|
125
|
+
configPath,
|
|
126
|
+
created: true,
|
|
127
|
+
gitignoreUpdated: gitignoreResult?.updated ?? false,
|
|
128
|
+
gitignorePath: gitignoreResult?.gitignorePath ?? null
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
const writeStandaloneConfig = async (configPath, config, gitignore) => {
|
|
132
|
+
await writeConfig(configPath, config);
|
|
133
|
+
const gitignoreResult = gitignore ? await ensureGitignoreEntry(
|
|
134
|
+
path.dirname(configPath),
|
|
135
|
+
config.cacheDir ?? DEFAULT_CACHE_DIR
|
|
136
|
+
) : null;
|
|
137
|
+
return {
|
|
138
|
+
configPath,
|
|
139
|
+
created: true,
|
|
140
|
+
gitignoreUpdated: gitignoreResult?.updated ?? false,
|
|
141
|
+
gitignorePath: gitignoreResult?.gitignorePath ?? null
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
export const initConfig = async (options, deps = {}) => {
|
|
145
|
+
const confirm = deps.confirm ?? clackConfirm;
|
|
146
|
+
const isCancel = deps.isCancel ?? clackIsCancel;
|
|
147
|
+
const select = deps.select ?? clackSelect;
|
|
148
|
+
const text = deps.text ?? clackText;
|
|
149
|
+
const cwd = options.cwd ?? process.cwd();
|
|
150
|
+
const { existingConfigPaths, defaultConfigPath, packagePath } = await findExistingConfigPaths(cwd);
|
|
151
|
+
if (existingConfigPaths.length > 0) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
`Config already exists at ${existingConfigPaths.join(", ")}. Init aborted.`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
const configPath = await selectConfigPath(
|
|
157
|
+
packagePath,
|
|
158
|
+
defaultConfigPath,
|
|
159
|
+
select,
|
|
160
|
+
isCancel
|
|
161
|
+
);
|
|
162
|
+
const cacheDir = options.cacheDirOverride ?? DEFAULT_CACHE_DIR;
|
|
163
|
+
const answers = await promptInitAnswers(
|
|
164
|
+
cacheDir,
|
|
165
|
+
cwd,
|
|
166
|
+
confirm,
|
|
167
|
+
text,
|
|
168
|
+
isCancel
|
|
169
|
+
);
|
|
170
|
+
const resolvedConfigPath = path.resolve(cwd, configPath);
|
|
171
|
+
const config = buildBaseConfig(answers.cacheDir, answers.toc);
|
|
172
|
+
if (path.basename(resolvedConfigPath) === "package.json") {
|
|
173
|
+
return writePackageConfig(resolvedConfigPath, config, answers.gitignore);
|
|
174
|
+
}
|
|
175
|
+
if (await exists(resolvedConfigPath)) {
|
|
176
|
+
throw new Error(`Config already exists at ${resolvedConfigPath}.`);
|
|
177
|
+
}
|
|
178
|
+
return writeStandaloneConfig(resolvedConfigPath, config, answers.gitignore);
|
|
179
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { access, readdir, rm } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { DEFAULT_CACHE_DIR, loadConfig } from "#config";
|
|
4
|
+
import { resolveCacheDir } from "#core/paths";
|
|
5
|
+
const exists = async (target) => {
|
|
6
|
+
try {
|
|
7
|
+
await access(target);
|
|
8
|
+
return true;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
export const pruneCache = async (options) => {
|
|
14
|
+
const { config, resolvedPath, sources } = await loadConfig(
|
|
15
|
+
options.configPath
|
|
16
|
+
);
|
|
17
|
+
const cacheDir = resolveCacheDir(
|
|
18
|
+
resolvedPath,
|
|
19
|
+
config.cacheDir ?? DEFAULT_CACHE_DIR,
|
|
20
|
+
options.cacheDirOverride
|
|
21
|
+
);
|
|
22
|
+
const cacheDirExists = await exists(cacheDir);
|
|
23
|
+
if (!cacheDirExists) {
|
|
24
|
+
return {
|
|
25
|
+
cacheDir,
|
|
26
|
+
removed: [],
|
|
27
|
+
kept: sources.map((source) => source.id)
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const keepIds = new Set(sources.map((source) => source.id));
|
|
31
|
+
const entries = await readdir(cacheDir, { withFileTypes: true });
|
|
32
|
+
const removed = [];
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
if (!entry.isDirectory()) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const name = entry.name;
|
|
38
|
+
if (keepIds.has(name)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (name.startsWith(".tmp-")) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
await rm(path.join(cacheDir, name), { recursive: true, force: true });
|
|
45
|
+
removed.push(name);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
cacheDir,
|
|
49
|
+
removed,
|
|
50
|
+
kept: sources.map((source) => source.id)
|
|
51
|
+
};
|
|
52
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { rm } from "node:fs/promises";
|
|
2
|
+
import {
|
|
3
|
+
mergeConfigBase,
|
|
4
|
+
readConfigAtPath,
|
|
5
|
+
resolveConfigTarget,
|
|
6
|
+
writeConfigFile
|
|
7
|
+
} from "#config/io";
|
|
8
|
+
import { resolveTargetDir } from "#core/paths";
|
|
9
|
+
import { resolveRepoInput } from "#git/resolve-repo";
|
|
10
|
+
const resolveIdsToRemove = (ids, config) => {
|
|
11
|
+
const sourcesById = new Map(
|
|
12
|
+
config.sources.map((source) => [source.id, source])
|
|
13
|
+
);
|
|
14
|
+
const sourcesByRepo = new Map(
|
|
15
|
+
config.sources.map((source) => [source.repo, source])
|
|
16
|
+
);
|
|
17
|
+
const idsToRemove = /* @__PURE__ */ new Set();
|
|
18
|
+
const missing = [];
|
|
19
|
+
for (const token of ids) {
|
|
20
|
+
if (sourcesById.has(token)) {
|
|
21
|
+
idsToRemove.add(token);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const resolved = resolveRepoInput(token);
|
|
25
|
+
if (resolved.repoUrl && sourcesByRepo.has(resolved.repoUrl)) {
|
|
26
|
+
const source = sourcesByRepo.get(resolved.repoUrl);
|
|
27
|
+
if (source) {
|
|
28
|
+
idsToRemove.add(source.id);
|
|
29
|
+
}
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (resolved.inferredId && sourcesById.has(resolved.inferredId)) {
|
|
33
|
+
idsToRemove.add(resolved.inferredId);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
missing.push(token);
|
|
37
|
+
}
|
|
38
|
+
return { idsToRemove, missing };
|
|
39
|
+
};
|
|
40
|
+
const removeTargets = async (resolvedPath, removedSources) => {
|
|
41
|
+
const targetRemovals = [];
|
|
42
|
+
for (const source of removedSources) {
|
|
43
|
+
if (!source.targetDir) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const targetDir = resolveTargetDir(resolvedPath, source.targetDir);
|
|
47
|
+
await rm(targetDir, { recursive: true, force: true });
|
|
48
|
+
targetRemovals.push({ id: source.id, targetDir });
|
|
49
|
+
}
|
|
50
|
+
return targetRemovals;
|
|
51
|
+
};
|
|
52
|
+
export const removeSources = async (params) => {
|
|
53
|
+
if (params.ids.length === 0) {
|
|
54
|
+
throw new Error("No sources specified to remove.");
|
|
55
|
+
}
|
|
56
|
+
const target = await resolveConfigTarget(params.configPath);
|
|
57
|
+
const resolvedPath = target.resolvedPath;
|
|
58
|
+
const { config, rawConfig, rawPackage } = await readConfigAtPath(target);
|
|
59
|
+
if (target.mode === "package" && !rawConfig) {
|
|
60
|
+
throw new Error(`Missing docs-cache config in ${resolvedPath}.`);
|
|
61
|
+
}
|
|
62
|
+
const { idsToRemove, missing } = resolveIdsToRemove(params.ids, config);
|
|
63
|
+
const remaining = config.sources.filter(
|
|
64
|
+
(source) => !idsToRemove.has(source.id)
|
|
65
|
+
);
|
|
66
|
+
const removed = config.sources.filter((source) => idsToRemove.has(source.id)).map((source) => source.id);
|
|
67
|
+
const removedSources = config.sources.filter(
|
|
68
|
+
(source) => idsToRemove.has(source.id)
|
|
69
|
+
);
|
|
70
|
+
if (removed.length === 0) {
|
|
71
|
+
throw new Error("No matching sources found to remove.");
|
|
72
|
+
}
|
|
73
|
+
const nextConfig = mergeConfigBase(rawConfig ?? config, remaining);
|
|
74
|
+
await writeConfigFile({
|
|
75
|
+
mode: target.mode,
|
|
76
|
+
resolvedPath,
|
|
77
|
+
config: nextConfig,
|
|
78
|
+
rawPackage
|
|
79
|
+
});
|
|
80
|
+
const targetRemovals = await removeTargets(resolvedPath, removedSources);
|
|
81
|
+
return {
|
|
82
|
+
configPath: resolvedPath,
|
|
83
|
+
removed,
|
|
84
|
+
missing,
|
|
85
|
+
targetsRemoved: targetRemovals
|
|
86
|
+
};
|
|
87
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type StatusOptions = {
|
|
2
|
+
configPath?: string;
|
|
3
|
+
cacheDirOverride?: string;
|
|
4
|
+
json: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare const getStatus: (options: StatusOptions) => Promise<{
|
|
7
|
+
configPath: any;
|
|
8
|
+
cacheDir: any;
|
|
9
|
+
cacheDirExists: boolean;
|
|
10
|
+
lockPath: any;
|
|
11
|
+
lockExists: boolean;
|
|
12
|
+
lockValid: boolean;
|
|
13
|
+
sources: any;
|
|
14
|
+
}>;
|
|
15
|
+
export declare const printStatus: (status: Awaited<ReturnType<typeof getStatus>>) => void;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
import pc from "picocolors";
|
|
3
|
+
import { DEFAULT_LOCK_FILENAME, readLock, resolveLockPath } from "#cache/lock";
|
|
4
|
+
import { symbols, ui } from "#cli/ui";
|
|
5
|
+
import { DEFAULT_CACHE_DIR, loadConfig } from "#config";
|
|
6
|
+
import { getCacheLayout, resolveCacheDir } from "#core/paths";
|
|
7
|
+
const exists = async (target) => {
|
|
8
|
+
try {
|
|
9
|
+
await access(target);
|
|
10
|
+
return true;
|
|
11
|
+
} catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
export const getStatus = async (options) => {
|
|
16
|
+
const { config, resolvedPath, sources } = await loadConfig(
|
|
17
|
+
options.configPath
|
|
18
|
+
);
|
|
19
|
+
const resolvedCacheDir = resolveCacheDir(
|
|
20
|
+
resolvedPath,
|
|
21
|
+
config.cacheDir ?? DEFAULT_CACHE_DIR,
|
|
22
|
+
options.cacheDirOverride
|
|
23
|
+
);
|
|
24
|
+
const cacheDirExists = await exists(resolvedCacheDir);
|
|
25
|
+
const lockPath = resolveLockPath(resolvedPath);
|
|
26
|
+
const lockExists = await exists(lockPath);
|
|
27
|
+
let lockValid = false;
|
|
28
|
+
let lockData = null;
|
|
29
|
+
if (lockExists) {
|
|
30
|
+
try {
|
|
31
|
+
lockData = await readLock(lockPath);
|
|
32
|
+
lockValid = true;
|
|
33
|
+
} catch {
|
|
34
|
+
lockValid = false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const sourceStatus = await Promise.all(
|
|
38
|
+
sources.map(async (source) => {
|
|
39
|
+
const layout = getCacheLayout(resolvedCacheDir, source.id);
|
|
40
|
+
const docsExists = await exists(layout.sourceDir);
|
|
41
|
+
const lockEntry = lockData?.sources?.[source.id] ?? null;
|
|
42
|
+
return {
|
|
43
|
+
id: source.id,
|
|
44
|
+
docsPath: layout.sourceDir,
|
|
45
|
+
docsExists,
|
|
46
|
+
lockEntry
|
|
47
|
+
};
|
|
48
|
+
})
|
|
49
|
+
);
|
|
50
|
+
return {
|
|
51
|
+
configPath: resolvedPath,
|
|
52
|
+
cacheDir: resolvedCacheDir,
|
|
53
|
+
cacheDirExists,
|
|
54
|
+
lockPath,
|
|
55
|
+
lockExists,
|
|
56
|
+
lockValid,
|
|
57
|
+
sources: sourceStatus
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
export const printStatus = (status) => {
|
|
61
|
+
const relCache = ui.path(status.cacheDir);
|
|
62
|
+
const cacheState = status.cacheDirExists ? pc.green("present") : pc.red("missing");
|
|
63
|
+
const lockState = status.lockExists ? status.lockValid ? pc.green("valid") : pc.red("invalid") : pc.yellow("missing");
|
|
64
|
+
ui.header("Cache", `${relCache} (${cacheState})`);
|
|
65
|
+
ui.header("Lock", `${DEFAULT_LOCK_FILENAME} (${lockState})`);
|
|
66
|
+
if (status.sources.length === 0) {
|
|
67
|
+
ui.line();
|
|
68
|
+
ui.line(`${symbols.warn} No sources configured.`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
ui.line();
|
|
72
|
+
for (const source of status.sources) {
|
|
73
|
+
const icon = source.docsExists ? symbols.success : symbols.error;
|
|
74
|
+
const lockLabel = source.lockEntry ? pc.green("locked") : pc.yellow("new");
|
|
75
|
+
const shortHash = ui.hash(source.lockEntry?.resolvedCommit);
|
|
76
|
+
ui.item(icon, source.id.padEnd(20), `${lockLabel.padEnd(10)} ${shortHash}`);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { materializeSource } from "#cache/materialize";
|
|
2
|
+
import { fetchSource } from "#git/fetch-source";
|
|
3
|
+
import { resolveRemoteCommit } from "#git/resolve-remote";
|
|
4
|
+
import type { SyncOptions } from "#types/sync";
|
|
5
|
+
type SyncDeps = {
|
|
6
|
+
resolveRemoteCommit?: typeof resolveRemoteCommit;
|
|
7
|
+
fetchSource?: typeof fetchSource;
|
|
8
|
+
materializeSource?: typeof materializeSource;
|
|
9
|
+
};
|
|
10
|
+
export declare const getSyncPlan: (options: SyncOptions, deps?: SyncDeps) => Promise<{
|
|
11
|
+
config: any;
|
|
12
|
+
configPath: any;
|
|
13
|
+
cacheDir: any;
|
|
14
|
+
lockPath: any;
|
|
15
|
+
lockExists: boolean;
|
|
16
|
+
lockData: any;
|
|
17
|
+
results: SyncResult[];
|
|
18
|
+
sources: any;
|
|
19
|
+
defaults: DocsCacheDefaults;
|
|
20
|
+
}>;
|
|
21
|
+
export declare const runSync: (options: SyncOptions, deps?: SyncDeps) => Promise<{
|
|
22
|
+
config: any;
|
|
23
|
+
configPath: any;
|
|
24
|
+
cacheDir: any;
|
|
25
|
+
lockPath: any;
|
|
26
|
+
lockExists: boolean;
|
|
27
|
+
lockData: any;
|
|
28
|
+
results: SyncResult[];
|
|
29
|
+
sources: any;
|
|
30
|
+
defaults: DocsCacheDefaults;
|
|
31
|
+
}>;
|
|
32
|
+
export declare const printSyncPlan: (plan: Awaited<ReturnType<typeof getSyncPlan>>) => void;
|
|
33
|
+
export {};
|