Skip to content
Merged
42 changes: 29 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,24 +73,40 @@ npx docs-cache clean

### Options

| Field | Type | Description |
| ---------- | ------- | ---------------------------------------- |
| `cacheDir` | string | Directory for cache, defaults to `.docs` |
| `index` | boolean | Write `index.json` summary file |
| `sources` | array | List of repositories to sync |
| `defaults` | object | Default settings for all sources |
| Field | Type | Description |
| ---------- | ------- | ---------------------------------------------------- |
| `cacheDir` | string | Directory for cache, defaults to `.docs` |
| `sources` | array | List of repositories to sync |
| `defaults` | object | Default settings for all sources |

**Default Options:**

All fields in `defaults` apply to all sources unless overridden per-source:

- `ref`: Branch, tag, or commit (default: `"HEAD"`)
- `mode`: Cache mode (default: `"materialize"`)
- `include`: Glob patterns to copy (default: `["**/*.{md,mdx,markdown,mkd,txt,rst,adoc,asciidoc}"]`)
- `targetMode`: `"symlink"` or `"copy"` (default: `"symlink"` on Unix, `"copy"` on Windows)
- `depth`: Git clone depth (default: `1`)
- `required`: Whether missing sources should fail (default: `true`)
- `maxBytes`: Maximum total bytes to materialize (default: `200000000`)
- `maxFiles`: Maximum total files to materialize (optional)
- `allowHosts`: Allowed Git hosts (default: `["github.com", "gitlab.com"]`)
- `toc`: Generate per-source `TOC.md` listing all documentation files (default: `false`)
Comment thread
fbosch marked this conversation as resolved.
Outdated

**Source Options:**

- `repo`: Git URL
- `ref`: Branch, tag, or commit
- `include`: Glob patterns to copy, defaults to `"**/*.{md,mdx,markdown,mkd,txt,rst,adoc,asciidoc}"`,
- `repo`: Git URL (required)
- `id`: Unique identifier for the source (required)
- `ref`: Branch, tag, or commit (overrides default)
- `include`: Glob patterns to copy (overrides default)
- `exclude`: Glob patterns to skip
- `targetDir`: Optional path where files should be symlinked/copied to, outside `.docs`
- `targetMode`: Defaults to `symlink` on Unix and `copy` on Windows
- `required`: Whether missing sources should fail in offline/strict runs
- `maxBytes`: Maximum total bytes to materialize for the source
- `maxFiles`: Maximum total files to materialize for the source
- `targetMode`: `"symlink"` or `"copy"` (overrides default)
- `required`: Whether missing sources should fail (overrides default)
- `maxBytes`: Maximum total bytes to materialize (overrides default)
- `maxFiles`: Maximum total files to materialize (overrides default)
- `toc`: Generate per-source `TOC.md` listing all documentation files (overrides default)

> **Note**: Sources are always downloaded to `.docs/<id>/`. If you provide a `targetDir`, `docs-cache` will create a symlink or copy pointing from the cache to that target directory. The target should be outside `.docs`.

Expand Down
9 changes: 6 additions & 3 deletions docs.config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
"type": "string",
"enum": ["symlink", "copy"]
},
"index": {
"type": "boolean"
},
"defaults": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -62,6 +59,9 @@
"type": "string",
"minLength": 1
}
},
"toc": {
"type": "boolean"
}
},
"additionalProperties": false
Expand Down Expand Up @@ -144,6 +144,9 @@
},
"required": ["type", "value"],
"additionalProperties": false
},
"toc": {
"type": "boolean"
}
},
"required": ["id", "repo"],
Expand Down
3 changes: 0 additions & 3 deletions src/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,6 @@ export const addSources = async (params: {
if (rawConfig?.cacheDir) {
nextConfig.cacheDir = rawConfig.cacheDir;
}
if (rawConfig?.index !== undefined) {
nextConfig.index = rawConfig.index;
}
if (rawConfig?.defaults) {
nextConfig.defaults = rawConfig.defaults;
}
Expand Down
3 changes: 2 additions & 1 deletion src/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const DefaultsSchema = z
maxBytes: z.number().min(1),
maxFiles: z.number().min(1).optional(),
allowHosts: z.array(z.string().min(1)).min(1),
toc: z.boolean().optional(),
})
.strict();

Expand All @@ -38,6 +39,7 @@ export const SourceSchema = z
maxBytes: z.number().min(1).optional(),
maxFiles: z.number().min(1).optional(),
integrity: IntegritySchema.optional(),
toc: z.boolean().optional(),
})
.strict();

Expand All @@ -46,7 +48,6 @@ export const ConfigSchema = z
$schema: z.string().min(1).optional(),
cacheDir: z.string().min(1).optional(),
targetMode: TargetModeSchema.optional(),
index: z.boolean().optional(),
defaults: DefaultsSchema.partial().optional(),
sources: z.array(SourceSchema),
})
Expand Down
20 changes: 12 additions & 8 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface DocsCacheDefaults {
maxBytes: number;
maxFiles?: number;
allowHosts: string[];
toc?: boolean;
}

export interface DocsCacheSource {
Expand All @@ -39,13 +40,13 @@ export interface DocsCacheSource {
maxBytes?: number;
maxFiles?: number;
integrity?: DocsCacheIntegrity;
toc?: boolean;
}

export interface DocsCacheConfig {
$schema?: string;
cacheDir?: string;
targetMode?: "symlink" | "copy";
index?: boolean;
defaults?: Partial<DocsCacheDefaults>;
sources: DocsCacheSource[];
}
Expand All @@ -64,6 +65,7 @@ export interface DocsCacheResolvedSource {
maxBytes: number;
maxFiles?: number;
integrity?: DocsCacheIntegrity;
toc?: boolean;
}

export const DEFAULT_CONFIG_FILENAME = "docs.config.json";
Expand All @@ -72,7 +74,6 @@ const PACKAGE_JSON_FILENAME = "package.json";
const DEFAULT_TARGET_MODE = process.platform === "win32" ? "copy" : "symlink";
export const DEFAULT_CONFIG: DocsCacheConfig = {
cacheDir: DEFAULT_CACHE_DIR,
index: false,
defaults: {
ref: "HEAD",
mode: "materialize",
Expand All @@ -82,6 +83,7 @@ export const DEFAULT_CONFIG: DocsCacheConfig = {
required: true,
maxBytes: 200000000,
allowHosts: ["github.com", "gitlab.com"],
toc: false,
},
sources: [],
};
Expand Down Expand Up @@ -144,7 +146,6 @@ export const stripDefaultConfigValues = (
const next: DocsCacheConfig = {
$schema: pruned.$schema as DocsCacheConfig["$schema"],
cacheDir: pruned.cacheDir as DocsCacheConfig["cacheDir"],
index: pruned.index as DocsCacheConfig["index"],
targetMode: pruned.targetMode as DocsCacheConfig["targetMode"],
defaults: pruned.defaults as DocsCacheConfig["defaults"],
sources: config.sources,
Expand Down Expand Up @@ -247,10 +248,6 @@ export const validateConfig = (input: unknown): DocsCacheConfig => {
const cacheDir = input.cacheDir
? assertString(input.cacheDir, "cacheDir")
: DEFAULT_CACHE_DIR;
const index =
input.index !== undefined
? assertBoolean(input.index, "index")
: (DEFAULT_CONFIG.index ?? false);

const defaultsInput = input.defaults;
const targetModeOverride =
Expand Down Expand Up @@ -300,6 +297,10 @@ export const validateConfig = (input: unknown): DocsCacheConfig => {
defaultsInput.allowHosts !== undefined
? assertStringArray(defaultsInput.allowHosts, "defaults.allowHosts")
: defaultValues.allowHosts,
toc:
defaultsInput.toc !== undefined
? assertBoolean(defaultsInput.toc, "defaults.toc")
: defaultValues.toc,
};
} else if (targetModeOverride !== undefined) {
defaults = {
Expand Down Expand Up @@ -386,6 +387,9 @@ export const validateConfig = (input: unknown): DocsCacheConfig => {
`sources[${index}].integrity`,
);
}
if (entry.toc !== undefined) {
source.toc = assertBoolean(entry.toc, `sources[${index}].toc`);
}
return source;
});

Expand All @@ -407,7 +411,6 @@ export const validateConfig = (input: unknown): DocsCacheConfig => {
return {
cacheDir,
targetMode: targetModeOverride,
index,
defaults,
sources,
};
Expand All @@ -432,6 +435,7 @@ export const resolveSources = (
maxBytes: source.maxBytes ?? defaults.maxBytes,
maxFiles: source.maxFiles ?? defaults.maxFiles,
integrity: source.integrity,
toc: source.toc ?? defaults.toc,
}));
};

Expand Down
60 changes: 0 additions & 60 deletions src/index.ts

This file was deleted.

18 changes: 9 additions & 9 deletions src/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,12 @@ export const initConfig = async (
throw new Error("Init cancelled.");
}
const cacheDirValue = cacheDirAnswer || DEFAULT_CACHE_DIR;
const indexAnswer = await confirm({
const tocAnswer = await confirm({
message:
"Generate index.json (summary of cached sources + paths for tools)",
"Generate TOC.md (table of contents with links to all documentation)",
initialValue: false,
});
if (isCancel(indexAnswer)) {
if (isCancel(tocAnswer)) {
throw new Error("Init cancelled.");
}
const gitignoreStatus = await getGitignoreStatus(cwd, cacheDirValue);
Expand All @@ -117,12 +117,12 @@ export const initConfig = async (
const answers = {
configPath,
cacheDir: cacheDirAnswer,
index: indexAnswer,
toc: tocAnswer,
gitignore: gitignoreAnswer,
} as {
configPath: string;
cacheDir: string;
index: boolean;
toc: boolean;
gitignore: boolean;
};

Expand All @@ -144,8 +144,8 @@ export const initConfig = async (
if (resolvedCacheDir !== DEFAULT_CACHE_DIR) {
baseConfig.cacheDir = resolvedCacheDir;
}
if (answers.index) {
baseConfig.index = true;
if (answers.toc) {
baseConfig.defaults = { toc: true };
}
pkg["docs-cache"] = stripDefaultConfigValues(baseConfig);
await writeFile(
Expand Down Expand Up @@ -178,8 +178,8 @@ export const initConfig = async (
if (resolvedCacheDir !== DEFAULT_CACHE_DIR) {
config.cacheDir = resolvedCacheDir;
}
if (answers.index) {
config.index = true;
if (answers.toc) {
config.defaults = { toc: true };
}

await writeConfig(resolvedConfigPath, config);
Expand Down
6 changes: 3 additions & 3 deletions src/paths.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from "node:path";

export const DEFAULT_LOCK_FILENAME = "docs.lock";
export const DEFAULT_INDEX_FILENAME = "index.json";
export const DEFAULT_TOC_FILENAME = "TOC.md";

export const toPosixPath = (value: string) => value.replace(/\\/g, "/");

Expand Down Expand Up @@ -40,10 +40,10 @@ export const resolveCacheDir = (
export const getCacheLayout = (cacheDir: string, sourceId: string) => {
const _reposDir = path.join(cacheDir, "repos");
const sourceDir = path.join(cacheDir, sourceId);
const indexPath = path.join(cacheDir, DEFAULT_INDEX_FILENAME);
const tocPath = path.join(cacheDir, DEFAULT_TOC_FILENAME);
return {
cacheDir,
sourceDir,
indexPath,
tocPath,
};
Comment thread
fbosch marked this conversation as resolved.
Outdated
};
3 changes: 0 additions & 3 deletions src/remove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,6 @@ export const removeSources = async (params: {
if (rawConfig?.cacheDir) {
nextConfig.cacheDir = rawConfig.cacheDir;
}
if (rawConfig?.index !== undefined) {
nextConfig.index = rawConfig.index;
}
if (rawConfig?.defaults) {
nextConfig.defaults = rawConfig.defaults;
}
Expand Down
9 changes: 6 additions & 3 deletions src/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import {
} from "./config";
import { fetchSource } from "./git/fetch-source";
import { resolveRemoteCommit } from "./git/resolve-remote";
import { writeIndex } from "./index";
import { readLock, resolveLockPath, writeLock } from "./lock";
import { MANIFEST_FILENAME } from "./manifest";
import { computeManifestHash, materializeSource } from "./materialize";
import { resolveCacheDir, resolveTargetDir } from "./paths";
import { applyTargetDir } from "./targets";
import { writeToc } from "./toc";
import { verifyCache } from "./verify";

type SyncOptions = {
Expand Down Expand Up @@ -482,8 +482,11 @@ export const runSync = async (options: SyncOptions, deps: SyncDeps = {}) => {
`${symbols.info} Completed in ${elapsedMs.toFixed(0)}ms · ${formatBytes(totalBytes)} · ${totalFiles} files${warningCount ? ` · ${warningCount} warning${warningCount === 1 ? "" : "s"}` : ""}`,
);
}
if (plan.config.index) {
await writeIndex({
// Check if any source has TOC enabled
const anySourceHasToc = plan.sources.some((source) => source.toc === true);

if (anySourceHasToc) {
await writeToc({
cacheDir: plan.cacheDir,
configPath: plan.configPath,
lock,
Expand Down
Loading
Loading