Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/cache/lock.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { isRecord } from "#core/is-record";

export interface DocsCacheLockSource {
repo: string;
Expand All @@ -19,9 +20,6 @@ export interface DocsCacheLock {

export const DEFAULT_LOCK_FILENAME = "docs-lock.json";

const isRecord = (value: unknown): value is Record<string, unknown> =>
typeof value === "object" && value !== null && !Array.isArray(value);

const assertString = (value: unknown, label: string): string => {
if (typeof value !== "string" || value.length === 0) {
throw new Error(`${label} must be a non-empty string.`);
Expand Down
71 changes: 47 additions & 24 deletions src/commands/sync.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createHash } from "node:crypto";
import { access, mkdir, readFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import pc from "picocolors";
import type { DocsCacheLock, DocsCacheLockSource } from "#cache/lock";
import { readLock, resolveLockPath, writeLock } from "#cache/lock";
Expand All @@ -18,6 +19,7 @@ import {
type DocsCacheResolvedSource,
loadConfig,
} from "#config";
import { isRecord } from "#core/is-record";
import { resolveCacheDir, resolveTargetDir } from "#core/paths";
import { fetchSource } from "#git/fetch-source";
import { resolveRemoteCommit } from "#git/resolve-remote";
Expand Down Expand Up @@ -187,35 +189,56 @@ export const getSyncPlan = async (
};
};

const loadToolVersion = async () => {
const cwdPath = path.resolve(process.cwd(), "package.json");
const TOOL_PACKAGE_NAME = "docs-cache";

const readToolVersionFromPackageFile = async (packagePath: string) => {
try {
const raw = await readFile(cwdPath, "utf8");
const pkg = JSON.parse(raw.toString());
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
const raw = await readFile(packagePath, "utf8");
const parsed: unknown = JSON.parse(raw);
if (!isRecord(parsed)) {
return null;
}
const pkgName = parsed.name;
const pkgVersion = parsed.version;
if (pkgName !== TOOL_PACKAGE_NAME) {
return null;
}
if (typeof pkgVersion !== "string" || pkgVersion.length === 0) {
return null;
}
return pkgVersion;
} catch {
// fallback to bundle-relative location
return null;
}
try {
const raw = await readFile(
new URL("../package.json", import.meta.url),
"utf8",
);
const pkg = JSON.parse(raw.toString());
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
} catch {
// fallback to dist/chunks relative location
};

const findToolVersionFrom = async (startDir: string) => {
let currentDir = startDir;
while (true) {
const packagePath = path.join(currentDir, "package.json");
const version = await readToolVersionFromPackageFile(packagePath);
if (version) {
return version;
}
const parentDir = path.dirname(currentDir);
if (parentDir === currentDir) {
return null;
}
currentDir = parentDir;
}
try {
const raw = await readFile(
new URL("../../package.json", import.meta.url),
"utf8",
);
const pkg = JSON.parse(raw.toString());
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
} catch {
return "0.0.0";
};

const loadToolVersion = async () => {
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
const moduleVersion = await findToolVersionFrom(moduleDir);
if (moduleVersion) {
return moduleVersion;
}
const cwdVersion = await findToolVersionFrom(process.cwd());
if (cwdVersion) {
return cwdVersion;
}
return "0.0.0";
};

const buildLockSource = (
Expand Down
2 changes: 2 additions & 0 deletions src/is-record.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const isRecord = (value: unknown): value is Record<string, unknown> =>
typeof value === "object" && value !== null && !Array.isArray(value);
32 changes: 23 additions & 9 deletions tests/sync-tool-version.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,37 @@ test("sync writes lock toolVersion from package.json", async () => {
),
"utf8",
);
await writeFile(
path.join(tmpRoot, "package.json"),
JSON.stringify({
name: "not-docs-cache",
version: "9.9.9",
}),
"utf8",
);

await runSync({
configPath,
cacheDirOverride: cacheDir,
json: true,
lockOnly: true,
offline: true,
failOnMiss: false,
});
const originalCwd = process.cwd();
try {
process.chdir(tmpRoot);
await runSync({
configPath,
cacheDirOverride: cacheDir,
json: true,
lockOnly: true,
offline: true,
failOnMiss: false,
});
} finally {
process.chdir(originalCwd);
}

const lockRaw = await readFile(
path.join(tmpRoot, DEFAULT_LOCK_FILENAME),
"utf8",
);
const lock = JSON.parse(lockRaw);
const pkgRaw = await readFile(
path.resolve(process.cwd(), "package.json"),
new URL("../package.json", import.meta.url),
"utf8",
);
const pkg = JSON.parse(pkgRaw);
Expand Down
Loading