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,11 @@
|
|
|
1
|
+
type VerifyOptions = {
|
|
2
|
+
configPath?: string;
|
|
3
|
+
cacheDirOverride?: string;
|
|
4
|
+
json: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare const verifyCache: (options: VerifyOptions) => Promise<{
|
|
7
|
+
cacheDir: any;
|
|
8
|
+
results: any;
|
|
9
|
+
}>;
|
|
10
|
+
export declare const printVerify: (report: Awaited<ReturnType<typeof verifyCache>>) => void;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { access, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { streamManifestEntries } from "#cache/manifest";
|
|
4
|
+
import { symbols, ui } from "#cli/ui";
|
|
5
|
+
import { DEFAULT_CACHE_DIR, loadConfig } from "#config";
|
|
6
|
+
import { getErrnoCode } from "#core/errors";
|
|
7
|
+
import { resolveCacheDir, resolveTargetDir } from "#core/paths";
|
|
8
|
+
const exists = async (target) => {
|
|
9
|
+
try {
|
|
10
|
+
await access(target);
|
|
11
|
+
return true;
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
export const verifyCache = async (options) => {
|
|
17
|
+
const { config, resolvedPath, sources } = await loadConfig(
|
|
18
|
+
options.configPath
|
|
19
|
+
);
|
|
20
|
+
const cacheDir = resolveCacheDir(
|
|
21
|
+
resolvedPath,
|
|
22
|
+
config.cacheDir ?? DEFAULT_CACHE_DIR,
|
|
23
|
+
options.cacheDirOverride
|
|
24
|
+
);
|
|
25
|
+
const verifyDir = async (directory, label) => {
|
|
26
|
+
if (!await exists(directory)) {
|
|
27
|
+
return {
|
|
28
|
+
ok: false,
|
|
29
|
+
issues: [
|
|
30
|
+
label === "source" ? "missing source directory" : "missing target directory"
|
|
31
|
+
]
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
let missingCount = 0;
|
|
36
|
+
let sizeMismatchCount = 0;
|
|
37
|
+
for await (const entry of streamManifestEntries(directory)) {
|
|
38
|
+
const filePath = path.join(directory, entry.path);
|
|
39
|
+
try {
|
|
40
|
+
const info = await stat(filePath);
|
|
41
|
+
if (info.size !== entry.size) {
|
|
42
|
+
sizeMismatchCount += 1;
|
|
43
|
+
}
|
|
44
|
+
} catch (error) {
|
|
45
|
+
const code = getErrnoCode(error);
|
|
46
|
+
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
47
|
+
missingCount += 1;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const issues = [];
|
|
54
|
+
if (missingCount > 0) {
|
|
55
|
+
issues.push(
|
|
56
|
+
label === "source" ? `missing files: ${missingCount}` : `target missing files: ${missingCount}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
if (sizeMismatchCount > 0) {
|
|
60
|
+
issues.push(
|
|
61
|
+
label === "source" ? `size mismatch: ${sizeMismatchCount}` : `target size mismatch: ${sizeMismatchCount}`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
ok: issues.length === 0,
|
|
66
|
+
issues
|
|
67
|
+
};
|
|
68
|
+
} catch (error) {
|
|
69
|
+
const code = getErrnoCode(error);
|
|
70
|
+
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
71
|
+
return {
|
|
72
|
+
ok: false,
|
|
73
|
+
issues: [
|
|
74
|
+
label === "source" ? "missing manifest" : "missing target manifest"
|
|
75
|
+
]
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const results = await Promise.all(
|
|
82
|
+
sources.map(async (source) => {
|
|
83
|
+
const sourceDir = path.join(cacheDir, source.id);
|
|
84
|
+
const sourceReport = await verifyDir(sourceDir, "source");
|
|
85
|
+
const issues = [...sourceReport.issues];
|
|
86
|
+
if (source.targetDir && source.targetMode === "copy") {
|
|
87
|
+
const targetDir = resolveTargetDir(resolvedPath, source.targetDir);
|
|
88
|
+
const targetReport = await verifyDir(targetDir, "target");
|
|
89
|
+
issues.push(...targetReport.issues);
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
id: source.id,
|
|
93
|
+
ok: issues.length === 0,
|
|
94
|
+
issues
|
|
95
|
+
};
|
|
96
|
+
})
|
|
97
|
+
);
|
|
98
|
+
return {
|
|
99
|
+
cacheDir,
|
|
100
|
+
results
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
export const printVerify = (report) => {
|
|
104
|
+
const okCount = report.results.filter((r) => r.ok).length;
|
|
105
|
+
const failCount = report.results.length - okCount;
|
|
106
|
+
if (report.results.length === 0) {
|
|
107
|
+
ui.line(`${symbols.warn} No sources to verify.`);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
ui.line(
|
|
111
|
+
`${symbols.info} Verified ${report.results.length} sources (${okCount} ok, ${failCount} failed)`
|
|
112
|
+
);
|
|
113
|
+
for (const result of report.results) {
|
|
114
|
+
if (result.ok) {
|
|
115
|
+
ui.item(symbols.success, result.id);
|
|
116
|
+
} else {
|
|
117
|
+
ui.item(symbols.warn, result.id, result.issues.join(", "));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CacheMode, DocsCacheConfig, DocsCacheDefaults, DocsCacheIntegrity, DocsCacheResolvedSource, DocsCacheSource, TocFormat } from "#config/schema";
|
|
2
|
+
export type { CacheMode, DocsCacheConfig, DocsCacheDefaults, DocsCacheIntegrity, DocsCacheResolvedSource, DocsCacheSource, TocFormat, };
|
|
3
|
+
export declare const DEFAULT_CONFIG_FILENAME = "docs.config.json";
|
|
4
|
+
export declare const DEFAULT_CACHE_DIR = ".docs";
|
|
5
|
+
export declare const DEFAULT_CONFIG: DocsCacheConfig;
|
|
6
|
+
export declare const stripDefaultConfigValues: (config: DocsCacheConfig) => DocsCacheConfig;
|
|
7
|
+
export declare const validateConfig: (input: unknown) => DocsCacheConfig;
|
|
8
|
+
export declare const resolveSources: (config: DocsCacheConfig) => DocsCacheResolvedSource[];
|
|
9
|
+
export declare const resolveConfigPath: (configPath?: string) => any;
|
|
10
|
+
export declare const writeConfig: (configPath: string, config: DocsCacheConfig) => Promise<void>;
|
|
11
|
+
export declare const loadConfig: (configPath?: string) => Promise<{
|
|
12
|
+
config: DocsCacheConfig;
|
|
13
|
+
resolvedPath: string;
|
|
14
|
+
sources: DocsCacheResolvedSource[];
|
|
15
|
+
}>;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { access, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ConfigSchema } from "#config/schema";
|
|
4
|
+
import { resolveTargetDir } from "#core/paths";
|
|
5
|
+
export const DEFAULT_CONFIG_FILENAME = "docs.config.json";
|
|
6
|
+
export const DEFAULT_CACHE_DIR = ".docs";
|
|
7
|
+
const PACKAGE_JSON_FILENAME = "package.json";
|
|
8
|
+
const DEFAULT_TARGET_MODE = process.platform === "win32" ? "copy" : "symlink";
|
|
9
|
+
export const DEFAULT_CONFIG = {
|
|
10
|
+
cacheDir: DEFAULT_CACHE_DIR,
|
|
11
|
+
defaults: {
|
|
12
|
+
ref: "HEAD",
|
|
13
|
+
mode: "materialize",
|
|
14
|
+
include: ["**/*.{md,mdx,markdown,mkd,txt,rst,adoc,asciidoc}"],
|
|
15
|
+
exclude: [],
|
|
16
|
+
targetMode: DEFAULT_TARGET_MODE,
|
|
17
|
+
required: true,
|
|
18
|
+
maxBytes: 2e8,
|
|
19
|
+
ignoreHidden: false,
|
|
20
|
+
allowHosts: ["github.com", "gitlab.com", "visualstudio.com"],
|
|
21
|
+
toc: true,
|
|
22
|
+
unwrapSingleRootDir: false
|
|
23
|
+
},
|
|
24
|
+
sources: []
|
|
25
|
+
};
|
|
26
|
+
const isEqualStringArray = (left, right) => {
|
|
27
|
+
if (!left || !right) {
|
|
28
|
+
return left === right;
|
|
29
|
+
}
|
|
30
|
+
if (left.length !== right.length) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return left.every((value, index) => value === right[index]);
|
|
34
|
+
};
|
|
35
|
+
const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36
|
+
const pruneDefaults = (value, baseline) => {
|
|
37
|
+
const result = {};
|
|
38
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
39
|
+
const base = baseline[key];
|
|
40
|
+
if (Array.isArray(entry) && Array.isArray(base)) {
|
|
41
|
+
if (!isEqualStringArray(entry, base)) {
|
|
42
|
+
result[key] = entry;
|
|
43
|
+
}
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (isObject(entry) && isObject(base)) {
|
|
47
|
+
const pruned = pruneDefaults(entry, base);
|
|
48
|
+
if (Object.keys(pruned).length > 0) {
|
|
49
|
+
result[key] = pruned;
|
|
50
|
+
}
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (entry !== base) {
|
|
54
|
+
result[key] = entry;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
};
|
|
59
|
+
export const stripDefaultConfigValues = (config) => {
|
|
60
|
+
const baseline = {
|
|
61
|
+
...DEFAULT_CONFIG,
|
|
62
|
+
$schema: config.$schema,
|
|
63
|
+
defaults: {
|
|
64
|
+
...DEFAULT_CONFIG.defaults,
|
|
65
|
+
...config.targetMode ? { targetMode: config.targetMode } : void 0
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const pruned = pruneDefaults(
|
|
69
|
+
config,
|
|
70
|
+
baseline
|
|
71
|
+
);
|
|
72
|
+
const next = {
|
|
73
|
+
$schema: pruned.$schema,
|
|
74
|
+
cacheDir: pruned.cacheDir,
|
|
75
|
+
targetMode: pruned.targetMode,
|
|
76
|
+
defaults: pruned.defaults,
|
|
77
|
+
sources: config.sources
|
|
78
|
+
};
|
|
79
|
+
if (!next.defaults || Object.keys(next.defaults).length === 0) {
|
|
80
|
+
delete next.defaults;
|
|
81
|
+
}
|
|
82
|
+
return next;
|
|
83
|
+
};
|
|
84
|
+
export const validateConfig = (input) => {
|
|
85
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) {
|
|
86
|
+
throw new Error("Config must be a JSON object.");
|
|
87
|
+
}
|
|
88
|
+
const parsed = ConfigSchema.safeParse(input);
|
|
89
|
+
if (!parsed.success) {
|
|
90
|
+
const details = parsed.error.issues.map((issue) => `${issue.path.join(".") || "config"} ${issue.message}`).join("; ");
|
|
91
|
+
throw new Error(`Config does not match schema: ${details}.`);
|
|
92
|
+
}
|
|
93
|
+
const configInput = parsed.data;
|
|
94
|
+
const cacheDir = configInput.cacheDir ?? DEFAULT_CACHE_DIR;
|
|
95
|
+
const defaultValues = DEFAULT_CONFIG.defaults;
|
|
96
|
+
const targetModeOverride = configInput.targetMode;
|
|
97
|
+
const defaultsInput = configInput.defaults;
|
|
98
|
+
const defaults = {
|
|
99
|
+
...defaultValues,
|
|
100
|
+
...defaultsInput ?? {},
|
|
101
|
+
targetMode: defaultsInput?.targetMode ?? targetModeOverride ?? defaultValues.targetMode
|
|
102
|
+
};
|
|
103
|
+
return {
|
|
104
|
+
cacheDir,
|
|
105
|
+
targetMode: targetModeOverride,
|
|
106
|
+
defaults,
|
|
107
|
+
sources: configInput.sources
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
export const resolveSources = (config) => {
|
|
111
|
+
const defaults = config.defaults ?? DEFAULT_CONFIG.defaults;
|
|
112
|
+
const { allowHosts: _allowHosts, ...defaultValues } = defaults;
|
|
113
|
+
return config.sources.map((source) => ({
|
|
114
|
+
...defaultValues,
|
|
115
|
+
...source,
|
|
116
|
+
targetMode: source.targetMode ?? defaultValues.targetMode,
|
|
117
|
+
include: source.include ?? defaultValues.include,
|
|
118
|
+
exclude: source.exclude ?? defaultValues.exclude,
|
|
119
|
+
maxFiles: source.maxFiles ?? defaultValues.maxFiles,
|
|
120
|
+
toc: source.toc ?? defaultValues.toc,
|
|
121
|
+
unwrapSingleRootDir: source.unwrapSingleRootDir ?? defaultValues.unwrapSingleRootDir
|
|
122
|
+
}));
|
|
123
|
+
};
|
|
124
|
+
export const resolveConfigPath = (configPath) => configPath ? path.resolve(configPath) : path.resolve(process.cwd(), DEFAULT_CONFIG_FILENAME);
|
|
125
|
+
const resolvePackagePath = () => path.resolve(process.cwd(), PACKAGE_JSON_FILENAME);
|
|
126
|
+
const exists = async (target) => {
|
|
127
|
+
try {
|
|
128
|
+
await access(target);
|
|
129
|
+
return true;
|
|
130
|
+
} catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
const loadConfigFromFile = async (filePath, mode) => {
|
|
135
|
+
let raw;
|
|
136
|
+
try {
|
|
137
|
+
raw = await readFile(filePath, "utf8");
|
|
138
|
+
} catch (error) {
|
|
139
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
140
|
+
throw new Error(`Failed to read config at ${filePath}: ${message}`);
|
|
141
|
+
}
|
|
142
|
+
let parsed;
|
|
143
|
+
try {
|
|
144
|
+
parsed = JSON.parse(raw);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
147
|
+
throw new Error(`Invalid JSON in ${filePath}: ${message}`);
|
|
148
|
+
}
|
|
149
|
+
const configInput = mode === "package" ? parsed?.["docs-cache"] : parsed;
|
|
150
|
+
if (mode === "package" && configInput === void 0) {
|
|
151
|
+
throw new Error(`Missing docs-cache config in ${filePath}.`);
|
|
152
|
+
}
|
|
153
|
+
const config = validateConfig(configInput);
|
|
154
|
+
for (const source of config.sources) {
|
|
155
|
+
if (source.targetDir) {
|
|
156
|
+
resolveTargetDir(filePath, source.targetDir);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
config,
|
|
161
|
+
resolvedPath: filePath,
|
|
162
|
+
sources: resolveSources(config)
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
export const writeConfig = async (configPath, config) => {
|
|
166
|
+
const data = `${JSON.stringify(config, null, 2)}
|
|
167
|
+
`;
|
|
168
|
+
await writeFile(configPath, data, "utf8");
|
|
169
|
+
};
|
|
170
|
+
export const loadConfig = async (configPath) => {
|
|
171
|
+
const resolvedPath = resolveConfigPath(configPath);
|
|
172
|
+
const isPackageConfig = path.basename(resolvedPath) === PACKAGE_JSON_FILENAME;
|
|
173
|
+
if (configPath) {
|
|
174
|
+
return loadConfigFromFile(
|
|
175
|
+
resolvedPath,
|
|
176
|
+
isPackageConfig ? "package" : "config"
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
if (await exists(resolvedPath)) {
|
|
180
|
+
return loadConfigFromFile(resolvedPath, "config");
|
|
181
|
+
}
|
|
182
|
+
const packagePath = resolvePackagePath();
|
|
183
|
+
if (await exists(packagePath)) {
|
|
184
|
+
try {
|
|
185
|
+
return await loadConfigFromFile(packagePath, "package");
|
|
186
|
+
} catch (error) {
|
|
187
|
+
if (error instanceof Error && error.message.includes("Missing docs-cache config")) {
|
|
188
|
+
} else {
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
throw new Error(
|
|
194
|
+
`No docs.config.json found at ${resolvedPath} and no docs-cache config in ${packagePath}.`
|
|
195
|
+
);
|
|
196
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type DocsCacheConfig } from "#config";
|
|
2
|
+
export type ConfigTarget = {
|
|
3
|
+
resolvedPath: string;
|
|
4
|
+
mode: "package" | "config";
|
|
5
|
+
};
|
|
6
|
+
export declare const loadPackageConfig: (configPath: string) => Promise<{
|
|
7
|
+
parsed: Record<string, unknown>;
|
|
8
|
+
config: any;
|
|
9
|
+
}>;
|
|
10
|
+
export declare const resolveConfigTarget: (configPath?: string) => Promise<ConfigTarget>;
|
|
11
|
+
export declare const readConfigAtPath: (target: ConfigTarget, options?: {
|
|
12
|
+
allowMissing?: boolean;
|
|
13
|
+
}) => Promise<{
|
|
14
|
+
config: any;
|
|
15
|
+
rawConfig: any;
|
|
16
|
+
rawPackage: Record<string, unknown>;
|
|
17
|
+
hadDocsCacheConfig: boolean;
|
|
18
|
+
} | {
|
|
19
|
+
config: any;
|
|
20
|
+
rawConfig: any;
|
|
21
|
+
rawPackage: null;
|
|
22
|
+
hadDocsCacheConfig: boolean;
|
|
23
|
+
}>;
|
|
24
|
+
export declare const mergeConfigBase: (config: DocsCacheConfig, sources: DocsCacheConfig["sources"]) => DocsCacheConfig;
|
|
25
|
+
export declare const writeConfigFile: (params: {
|
|
26
|
+
mode: "package" | "config";
|
|
27
|
+
resolvedPath: string;
|
|
28
|
+
config: DocsCacheConfig;
|
|
29
|
+
rawPackage: Record<string, unknown> | null;
|
|
30
|
+
}) => Promise<void>;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { access, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_CONFIG,
|
|
5
|
+
resolveConfigPath,
|
|
6
|
+
stripDefaultConfigValues,
|
|
7
|
+
validateConfig,
|
|
8
|
+
writeConfig
|
|
9
|
+
} from "#config";
|
|
10
|
+
const PACKAGE_JSON = "package.json";
|
|
11
|
+
const SCHEMA_URL = "https://raw.githubusercontent.com/fbosch/docs-cache/main/docs.config.schema.json";
|
|
12
|
+
const exists = async (target) => {
|
|
13
|
+
try {
|
|
14
|
+
await access(target);
|
|
15
|
+
return true;
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
export const loadPackageConfig = async (configPath) => {
|
|
21
|
+
const raw = await readFile(configPath, "utf8");
|
|
22
|
+
const parsed = JSON.parse(raw);
|
|
23
|
+
const config = parsed["docs-cache"];
|
|
24
|
+
if (!config) {
|
|
25
|
+
return { parsed, config: null };
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
parsed,
|
|
29
|
+
config: validateConfig(config)
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
export const resolveConfigTarget = async (configPath) => {
|
|
33
|
+
if (configPath) {
|
|
34
|
+
const resolvedPath = resolveConfigPath(configPath);
|
|
35
|
+
return {
|
|
36
|
+
resolvedPath,
|
|
37
|
+
mode: path.basename(resolvedPath) === PACKAGE_JSON ? "package" : "config"
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const defaultPath = resolveConfigPath();
|
|
41
|
+
if (await exists(defaultPath)) {
|
|
42
|
+
return { resolvedPath: defaultPath, mode: "config" };
|
|
43
|
+
}
|
|
44
|
+
const packagePath = path.resolve(process.cwd(), PACKAGE_JSON);
|
|
45
|
+
if (await exists(packagePath)) {
|
|
46
|
+
const pkg = await loadPackageConfig(packagePath);
|
|
47
|
+
if (pkg.config) {
|
|
48
|
+
return { resolvedPath: packagePath, mode: "package" };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { resolvedPath: defaultPath, mode: "config" };
|
|
52
|
+
};
|
|
53
|
+
export const readConfigAtPath = async (target, options) => {
|
|
54
|
+
if (!await exists(target.resolvedPath)) {
|
|
55
|
+
if (!options?.allowMissing) {
|
|
56
|
+
throw new Error(`Config not found at ${target.resolvedPath}.`);
|
|
57
|
+
}
|
|
58
|
+
if (target.mode === "package") {
|
|
59
|
+
throw new Error(`package.json not found at ${target.resolvedPath}.`);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
config: DEFAULT_CONFIG,
|
|
63
|
+
rawConfig: null,
|
|
64
|
+
rawPackage: null,
|
|
65
|
+
hadDocsCacheConfig: false
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (target.mode === "package") {
|
|
69
|
+
const pkg = await loadPackageConfig(target.resolvedPath);
|
|
70
|
+
return {
|
|
71
|
+
config: pkg.config ?? DEFAULT_CONFIG,
|
|
72
|
+
rawConfig: pkg.config,
|
|
73
|
+
rawPackage: pkg.parsed,
|
|
74
|
+
hadDocsCacheConfig: Boolean(pkg.config)
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const raw = await readFile(target.resolvedPath, "utf8");
|
|
78
|
+
const rawConfig = JSON.parse(raw.toString());
|
|
79
|
+
return {
|
|
80
|
+
config: validateConfig(rawConfig),
|
|
81
|
+
rawConfig,
|
|
82
|
+
rawPackage: null,
|
|
83
|
+
hadDocsCacheConfig: true
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
export const mergeConfigBase = (config, sources) => {
|
|
87
|
+
const nextConfig = {
|
|
88
|
+
$schema: config.$schema ?? SCHEMA_URL,
|
|
89
|
+
sources
|
|
90
|
+
};
|
|
91
|
+
if (config.cacheDir) {
|
|
92
|
+
nextConfig.cacheDir = config.cacheDir;
|
|
93
|
+
}
|
|
94
|
+
if (config.defaults) {
|
|
95
|
+
nextConfig.defaults = config.defaults;
|
|
96
|
+
}
|
|
97
|
+
if (config.targetMode) {
|
|
98
|
+
nextConfig.targetMode = config.targetMode;
|
|
99
|
+
}
|
|
100
|
+
return nextConfig;
|
|
101
|
+
};
|
|
102
|
+
export const writeConfigFile = async (params) => {
|
|
103
|
+
const { mode, resolvedPath, config, rawPackage } = params;
|
|
104
|
+
if (mode === "package") {
|
|
105
|
+
const pkg = rawPackage ?? {};
|
|
106
|
+
pkg["docs-cache"] = stripDefaultConfigValues(config);
|
|
107
|
+
await writeFile(resolvedPath, `${JSON.stringify(pkg, null, 2)}
|
|
108
|
+
`, "utf8");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
await writeConfig(resolvedPath, config);
|
|
112
|
+
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
export declare const TargetModeSchema: z.ZodEnum<{
|
|
3
|
+
symlink: "symlink";
|
|
4
|
+
copy: "copy";
|
|
5
|
+
}>;
|
|
6
|
+
export declare const CacheModeSchema: z.ZodEnum<{
|
|
7
|
+
materialize: "materialize";
|
|
8
|
+
}>;
|
|
9
|
+
export declare const TocFormatSchema: z.ZodEnum<{
|
|
10
|
+
compressed: "compressed";
|
|
11
|
+
tree: "tree";
|
|
12
|
+
}>;
|
|
13
|
+
export declare const IntegritySchema: z.ZodObject<{
|
|
14
|
+
type: z.ZodEnum<{
|
|
15
|
+
commit: "commit";
|
|
16
|
+
manifest: "manifest";
|
|
17
|
+
}>;
|
|
18
|
+
value: z.ZodNullable<z.ZodString>;
|
|
19
|
+
}, z.z.core.$strict>;
|
|
20
|
+
export declare const DefaultsSchema: z.ZodObject<{
|
|
21
|
+
ref: z.ZodString;
|
|
22
|
+
mode: z.ZodEnum<{
|
|
23
|
+
materialize: "materialize";
|
|
24
|
+
}>;
|
|
25
|
+
include: z.ZodArray<z.ZodString>;
|
|
26
|
+
exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
27
|
+
targetMode: z.ZodOptional<z.ZodEnum<{
|
|
28
|
+
symlink: "symlink";
|
|
29
|
+
copy: "copy";
|
|
30
|
+
}>>;
|
|
31
|
+
required: z.ZodBoolean;
|
|
32
|
+
maxBytes: z.ZodNumber;
|
|
33
|
+
maxFiles: z.ZodOptional<z.ZodNumber>;
|
|
34
|
+
ignoreHidden: z.ZodBoolean;
|
|
35
|
+
toc: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodEnum<{
|
|
36
|
+
compressed: "compressed";
|
|
37
|
+
tree: "tree";
|
|
38
|
+
}>]>>;
|
|
39
|
+
unwrapSingleRootDir: z.ZodOptional<z.ZodBoolean>;
|
|
40
|
+
allowHosts: z.ZodArray<z.ZodString>;
|
|
41
|
+
}, z.z.core.$strict>;
|
|
42
|
+
export declare const SourceSchema: z.ZodObject<{
|
|
43
|
+
ref: z.ZodOptional<z.ZodString>;
|
|
44
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
45
|
+
materialize: "materialize";
|
|
46
|
+
}>>;
|
|
47
|
+
exclude: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
48
|
+
targetMode: z.ZodOptional<z.ZodOptional<z.ZodEnum<{
|
|
49
|
+
symlink: "symlink";
|
|
50
|
+
copy: "copy";
|
|
51
|
+
}>>>;
|
|
52
|
+
required: z.ZodOptional<z.ZodBoolean>;
|
|
53
|
+
maxBytes: z.ZodOptional<z.ZodNumber>;
|
|
54
|
+
maxFiles: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
55
|
+
ignoreHidden: z.ZodOptional<z.ZodBoolean>;
|
|
56
|
+
toc: z.ZodOptional<z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodEnum<{
|
|
57
|
+
compressed: "compressed";
|
|
58
|
+
tree: "tree";
|
|
59
|
+
}>]>>>;
|
|
60
|
+
unwrapSingleRootDir: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
|
|
61
|
+
id: z.ZodString;
|
|
62
|
+
repo: z.ZodString;
|
|
63
|
+
targetDir: z.ZodOptional<z.ZodString>;
|
|
64
|
+
integrity: z.ZodOptional<z.ZodObject<{
|
|
65
|
+
type: z.ZodEnum<{
|
|
66
|
+
commit: "commit";
|
|
67
|
+
manifest: "manifest";
|
|
68
|
+
}>;
|
|
69
|
+
value: z.ZodNullable<z.ZodString>;
|
|
70
|
+
}, z.z.core.$strict>>;
|
|
71
|
+
include: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
72
|
+
}, z.z.core.$strict>;
|
|
73
|
+
export declare const ResolvedSourceSchema: z.ZodObject<{
|
|
74
|
+
id: z.ZodString;
|
|
75
|
+
repo: z.ZodString;
|
|
76
|
+
targetDir: z.ZodOptional<z.ZodString>;
|
|
77
|
+
integrity: z.ZodOptional<z.ZodObject<{
|
|
78
|
+
type: z.ZodEnum<{
|
|
79
|
+
commit: "commit";
|
|
80
|
+
manifest: "manifest";
|
|
81
|
+
}>;
|
|
82
|
+
value: z.ZodNullable<z.ZodString>;
|
|
83
|
+
}, z.z.core.$strict>>;
|
|
84
|
+
ref: z.ZodString;
|
|
85
|
+
mode: z.ZodEnum<{
|
|
86
|
+
materialize: "materialize";
|
|
87
|
+
}>;
|
|
88
|
+
include: z.ZodArray<z.ZodString>;
|
|
89
|
+
exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
90
|
+
targetMode: z.ZodOptional<z.ZodEnum<{
|
|
91
|
+
symlink: "symlink";
|
|
92
|
+
copy: "copy";
|
|
93
|
+
}>>;
|
|
94
|
+
required: z.ZodBoolean;
|
|
95
|
+
maxBytes: z.ZodNumber;
|
|
96
|
+
maxFiles: z.ZodOptional<z.ZodNumber>;
|
|
97
|
+
ignoreHidden: z.ZodBoolean;
|
|
98
|
+
toc: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodEnum<{
|
|
99
|
+
compressed: "compressed";
|
|
100
|
+
tree: "tree";
|
|
101
|
+
}>]>>;
|
|
102
|
+
unwrapSingleRootDir: z.ZodOptional<z.ZodBoolean>;
|
|
103
|
+
}, z.z.core.$strict>;
|
|
104
|
+
export declare const ConfigSchema: z.ZodObject<{
|
|
105
|
+
$schema: z.ZodOptional<z.ZodString>;
|
|
106
|
+
cacheDir: z.ZodOptional<z.ZodString>;
|
|
107
|
+
targetMode: z.ZodOptional<z.ZodEnum<{
|
|
108
|
+
symlink: "symlink";
|
|
109
|
+
copy: "copy";
|
|
110
|
+
}>>;
|
|
111
|
+
defaults: z.ZodOptional<z.ZodObject<{
|
|
112
|
+
ref: z.ZodOptional<z.ZodString>;
|
|
113
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
114
|
+
materialize: "materialize";
|
|
115
|
+
}>>;
|
|
116
|
+
include: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
117
|
+
exclude: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
118
|
+
targetMode: z.ZodOptional<z.ZodOptional<z.ZodEnum<{
|
|
119
|
+
symlink: "symlink";
|
|
120
|
+
copy: "copy";
|
|
121
|
+
}>>>;
|
|
122
|
+
required: z.ZodOptional<z.ZodBoolean>;
|
|
123
|
+
maxBytes: z.ZodOptional<z.ZodNumber>;
|
|
124
|
+
maxFiles: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
125
|
+
ignoreHidden: z.ZodOptional<z.ZodBoolean>;
|
|
126
|
+
toc: z.ZodOptional<z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodEnum<{
|
|
127
|
+
compressed: "compressed";
|
|
128
|
+
tree: "tree";
|
|
129
|
+
}>]>>>;
|
|
130
|
+
unwrapSingleRootDir: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
|
|
131
|
+
allowHosts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
132
|
+
}, z.z.core.$strict>>;
|
|
133
|
+
sources: z.ZodArray<z.ZodObject<{
|
|
134
|
+
ref: z.ZodOptional<z.ZodString>;
|
|
135
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
136
|
+
materialize: "materialize";
|
|
137
|
+
}>>;
|
|
138
|
+
exclude: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
139
|
+
targetMode: z.ZodOptional<z.ZodOptional<z.ZodEnum<{
|
|
140
|
+
symlink: "symlink";
|
|
141
|
+
copy: "copy";
|
|
142
|
+
}>>>;
|
|
143
|
+
required: z.ZodOptional<z.ZodBoolean>;
|
|
144
|
+
maxBytes: z.ZodOptional<z.ZodNumber>;
|
|
145
|
+
maxFiles: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
146
|
+
ignoreHidden: z.ZodOptional<z.ZodBoolean>;
|
|
147
|
+
toc: z.ZodOptional<z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodEnum<{
|
|
148
|
+
compressed: "compressed";
|
|
149
|
+
tree: "tree";
|
|
150
|
+
}>]>>>;
|
|
151
|
+
unwrapSingleRootDir: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
|
|
152
|
+
id: z.ZodString;
|
|
153
|
+
repo: z.ZodString;
|
|
154
|
+
targetDir: z.ZodOptional<z.ZodString>;
|
|
155
|
+
integrity: z.ZodOptional<z.ZodObject<{
|
|
156
|
+
type: z.ZodEnum<{
|
|
157
|
+
commit: "commit";
|
|
158
|
+
manifest: "manifest";
|
|
159
|
+
}>;
|
|
160
|
+
value: z.ZodNullable<z.ZodString>;
|
|
161
|
+
}, z.z.core.$strict>>;
|
|
162
|
+
include: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
163
|
+
}, z.z.core.$strict>>;
|
|
164
|
+
}, z.z.core.$strict>;
|
|
165
|
+
export type DocsCacheDefaults = z.infer<typeof DefaultsSchema>;
|
|
166
|
+
export type DocsCacheSource = z.infer<typeof SourceSchema>;
|
|
167
|
+
export type DocsCacheResolvedSource = z.infer<typeof ResolvedSourceSchema>;
|
|
168
|
+
export type DocsCacheConfig = z.infer<typeof ConfigSchema>;
|
|
169
|
+
export type DocsCacheIntegrity = z.infer<typeof IntegritySchema>;
|
|
170
|
+
export type CacheMode = z.infer<typeof CacheModeSchema>;
|
|
171
|
+
export type TocFormat = z.infer<typeof TocFormatSchema>;
|