Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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.toString());
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