diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index ab97846..18e59be 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -10,6 +10,7 @@ import { createPathResolver } from './files/paths.js'; import { LunariaGitInstance } from './status/git.js'; import { getDictionaryCompletion, isFileLocalizable } from './status/status.js'; import type { LunariaStatus, StatusLocalizationEntry } from './status/types.js'; +import { useCache } from './status/cache.js'; // Additional data to ensure we can force rebuild the cache. // Bump this whenever there are breaking changes to the status output. @@ -130,6 +131,9 @@ export class Lunaria { ); } + // Save the existing git data into the cache for next builds. + useCache(this.#config.cacheDir, 'git').write(this.#git.cache); + return status; } diff --git a/packages/core/src/status/cache.ts b/packages/core/src/status/cache.ts new file mode 100644 index 0000000..3219a2b --- /dev/null +++ b/packages/core/src/status/cache.ts @@ -0,0 +1,22 @@ +import { existsSync, mkdirSync, writeFileSync } from 'node:fs'; +import { join, resolve } from 'node:path'; +import { jsonLoader } from '../files/loaders.js'; + +export function useCache(cacheDir: string, entry: string) { + const resolvedCacheDir = resolve(cacheDir); + const file = `${entry}.json`; + + if (!existsSync(resolvedCacheDir)) { + mkdirSync(resolvedCacheDir, { recursive: true }); + } + + const cachePath = join(resolvedCacheDir, file); + + return { + contents: existsSync(cachePath) ? jsonLoader(cachePath) : undefined, + write(contents: unknown) { + // TODO: Test with writeFile instead. + return writeFileSync(cachePath, JSON.stringify(contents)); + }, + }; +} diff --git a/packages/core/src/status/git.ts b/packages/core/src/status/git.ts index 804f7e7..be381d8 100644 --- a/packages/core/src/status/git.ts +++ b/packages/core/src/status/git.ts @@ -4,8 +4,10 @@ import type { ConsolaInstance } from 'consola'; import picomatch from 'picomatch'; import { type DefaultLogFields, type ListLogLine, simpleGit } from 'simple-git'; import type { LunariaConfig } from '../config/types.js'; -import { FileCommitsNotFound, UncommittedFileFound } from '../errors/errors.js'; +import { UncommittedFileFound } from '../errors/errors.js'; import type { RegExpGroups } from '../utils/types.js'; +import { useCache } from './cache.js'; +import type { FileGitData } from './types.js'; export class LunariaGitInstance { #git = simpleGit({ @@ -13,31 +15,36 @@ export class LunariaGitInstance { }); #config: LunariaConfig; #logger: ConsolaInstance; + cache: Record; constructor(config: LunariaConfig, logger: ConsolaInstance) { this.#logger = logger; this.#config = config; + this.cache = useCache(this.#config.cacheDir, 'git').contents ?? {}; } - // TODO: Try to cache the latest changes hash for each file so that you don't have to fetch the entire history every run, only new ones. - async #getFileHistory(path: string) { - try { - const log = await this.#git.log({ - file: resolve(path), - strictDate: true, - }); - - return log; - } catch (e) { - this.#logger.error(FileCommitsNotFound.message(path)); - throw e; + async getFileLatestChanges(path: string) { + const latestFileCommit = await this.#git.log({ + file: resolve(path), + strictDate: true, + maxCount: 1, + }); + + console.log(latestFileCommit); + + // The cache will keep the latest changes data, that means it will be able + // to completely skip the find for the latest changes if there are no new changes. + if (this.cache[path] && latestFileCommit?.latest?.hash === this.cache[path].latestChange.hash) { + return this.cache[path]; } - } - async getFileLatestChanges(path: string) { - const logHistory = await this.#getFileHistory(path); + const log = await this.#git.log({ + file: resolve(path), + strictDate: true, + from: this.cache[path]?.latestTrackedChange?.hash, + }); - const latestChange = logHistory.latest; + const latestChange = log.latest; /** * Edge case: it might be possible all the changes for a file have * been purposefully ignored in Lunaria, therefore we need to define @@ -45,14 +52,14 @@ export class LunariaGitInstance { * TODO: Check if this is not an stupid assumption. */ const latestTrackedChange = - findLatestTrackedCommit(this.#config.tracking, path, logHistory.all) ?? latestChange; + findLatestTrackedCommit(this.#config.tracking, path, log.all) ?? latestChange; if (!latestChange || !latestTrackedChange) { this.#logger.error(UncommittedFileFound.message(path)); process.exit(1); } - return { + const latestChanges = { latestChange: { date: latestChange.date, message: latestChange.message, @@ -64,6 +71,10 @@ export class LunariaGitInstance { hash: latestTrackedChange.hash, }, }; + + this.cache[path] = latestChanges; + + return latestChanges; } } diff --git a/packages/core/src/status/types.ts b/packages/core/src/status/types.ts index b6d0931..284e65b 100644 --- a/packages/core/src/status/types.ts +++ b/packages/core/src/status/types.ts @@ -4,7 +4,7 @@ export type Dictionary = { [k: string]: string | Dictionary; }; -type FileGitData = { +export type FileGitData = { latestChange: { message: string; date: string;