perf: optimize getFileCommitDate, make it async (#9890)

This commit is contained in:
Sébastien Lorber 2024-02-24 23:32:18 +01:00 committed by GitHub
parent 0279c329ad
commit f159bb2472
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 54 additions and 34 deletions

View file

@ -437,7 +437,7 @@ describe('blog plugin', () => {
const noDateSource = path.posix.join('@site', PluginPath, 'no date.md'); const noDateSource = path.posix.join('@site', PluginPath, 'no date.md');
const noDateSourceFile = path.posix.join(siteDir, PluginPath, 'no date.md'); const noDateSourceFile = path.posix.join(siteDir, PluginPath, 'no date.md');
// We know the file exists and we know we have git // We know the file exists and we know we have git
const result = getFileCommitDate(noDateSourceFile, {age: 'oldest'}); const result = await getFileCommitDate(noDateSourceFile, {age: 'oldest'});
const noDateSourceTime = result.date; const noDateSourceTime = result.date;
expect({ expect({

View file

@ -258,10 +258,11 @@ async function processBlogSourceFile(
} }
try { try {
const result = getFileCommitDate(blogSourceAbsolute, { const result = await getFileCommitDate(blogSourceAbsolute, {
age: 'oldest', age: 'oldest',
includeAuthor: false, includeAuthor: false,
}); });
return result.date; return result.date;
} catch (err) { } catch (err) {
logger.warn(err); logger.warn(err);

View file

@ -25,10 +25,11 @@ export async function getFileLastUpdate(
// Wrap in try/catch in case the shell commands fail // Wrap in try/catch in case the shell commands fail
// (e.g. project doesn't use Git, etc). // (e.g. project doesn't use Git, etc).
try { try {
const result = getFileCommitDate(filePath, { const result = await getFileCommitDate(filePath, {
age: 'newest', age: 'newest',
includeAuthor: true, includeAuthor: true,
}); });
return {timestamp: result.timestamp, author: result.author}; return {timestamp: result.timestamp, author: result.author};
} catch (err) { } catch (err) {
if (err instanceof GitNotFoundError) { if (err instanceof GitNotFoundError) {

View file

@ -47,88 +47,92 @@ function initializeTempRepo() {
describe('getFileCommitDate', () => { describe('getFileCommitDate', () => {
const repoDir = initializeTempRepo(); const repoDir = initializeTempRepo();
it('returns earliest commit date', async () => { it('returns earliest commit date', async () => {
expect(getFileCommitDate(path.join(repoDir, 'test.txt'), {})).toEqual({ await expect(
getFileCommitDate(path.join(repoDir, 'test.txt'), {}),
).resolves.toEqual({
date: new Date('2020-06-19'), date: new Date('2020-06-19'),
timestamp: new Date('2020-06-19').getTime() / 1000, timestamp: new Date('2020-06-19').getTime() / 1000,
}); });
expect(getFileCommitDate(path.join(repoDir, 'dest.txt'), {})).toEqual({ await expect(
getFileCommitDate(path.join(repoDir, 'dest.txt'), {}),
).resolves.toEqual({
date: new Date('2020-09-13'), date: new Date('2020-09-13'),
timestamp: new Date('2020-09-13').getTime() / 1000, timestamp: new Date('2020-09-13').getTime() / 1000,
}); });
}); });
it('returns latest commit date', async () => { it('returns latest commit date', async () => {
expect( await expect(
getFileCommitDate(path.join(repoDir, 'test.txt'), {age: 'newest'}), getFileCommitDate(path.join(repoDir, 'test.txt'), {age: 'newest'}),
).toEqual({ ).resolves.toEqual({
date: new Date('2020-09-13'), date: new Date('2020-09-13'),
timestamp: new Date('2020-09-13').getTime() / 1000, timestamp: new Date('2020-09-13').getTime() / 1000,
}); });
expect( await expect(
getFileCommitDate(path.join(repoDir, 'dest.txt'), {age: 'newest'}), getFileCommitDate(path.join(repoDir, 'dest.txt'), {age: 'newest'}),
).toEqual({ ).resolves.toEqual({
date: new Date('2020-11-13'), date: new Date('2020-11-13'),
timestamp: new Date('2020-11-13').getTime() / 1000, timestamp: new Date('2020-11-13').getTime() / 1000,
}); });
}); });
it('returns latest commit date with author', async () => { it('returns latest commit date with author', async () => {
expect( await expect(
getFileCommitDate(path.join(repoDir, 'test.txt'), { getFileCommitDate(path.join(repoDir, 'test.txt'), {
age: 'oldest', age: 'oldest',
includeAuthor: true, includeAuthor: true,
}), }),
).toEqual({ ).resolves.toEqual({
date: new Date('2020-06-19'), date: new Date('2020-06-19'),
timestamp: new Date('2020-06-19').getTime() / 1000, timestamp: new Date('2020-06-19').getTime() / 1000,
author: 'Caroline', author: 'Caroline',
}); });
expect( await expect(
getFileCommitDate(path.join(repoDir, 'dest.txt'), { getFileCommitDate(path.join(repoDir, 'dest.txt'), {
age: 'oldest', age: 'oldest',
includeAuthor: true, includeAuthor: true,
}), }),
).toEqual({ ).resolves.toEqual({
date: new Date('2020-09-13'), date: new Date('2020-09-13'),
timestamp: new Date('2020-09-13').getTime() / 1000, timestamp: new Date('2020-09-13').getTime() / 1000,
author: 'Caroline', author: 'Caroline',
}); });
}); });
it('returns earliest commit date with author', async () => { it('returns earliest commit date with author', async () => {
expect( await expect(
getFileCommitDate(path.join(repoDir, 'test.txt'), { getFileCommitDate(path.join(repoDir, 'test.txt'), {
age: 'newest', age: 'newest',
includeAuthor: true, includeAuthor: true,
}), }),
).toEqual({ ).resolves.toEqual({
date: new Date('2020-09-13'), date: new Date('2020-09-13'),
timestamp: new Date('2020-09-13').getTime() / 1000, timestamp: new Date('2020-09-13').getTime() / 1000,
author: 'Caroline', author: 'Caroline',
}); });
expect( await expect(
getFileCommitDate(path.join(repoDir, 'dest.txt'), { getFileCommitDate(path.join(repoDir, 'dest.txt'), {
age: 'newest', age: 'newest',
includeAuthor: true, includeAuthor: true,
}), }),
).toEqual({ ).resolves.toEqual({
date: new Date('2020-11-13'), date: new Date('2020-11-13'),
timestamp: new Date('2020-11-13').getTime() / 1000, timestamp: new Date('2020-11-13').getTime() / 1000,
author: 'Josh-Cena', author: 'Josh-Cena',
}); });
}); });
it('throws custom error when file is not tracked', async () => { it('throws custom error when file is not tracked', async () => {
expect(() => await expect(() =>
getFileCommitDate(path.join(repoDir, 'untracked.txt'), { getFileCommitDate(path.join(repoDir, 'untracked.txt'), {
age: 'newest', age: 'newest',
includeAuthor: true, includeAuthor: true,
}), }),
).toThrow(FileNotTrackedError); ).rejects.toThrow(FileNotTrackedError);
}); });
it('throws when file not found', async () => { it('throws when file not found', async () => {
expect(() => await expect(() =>
getFileCommitDate(path.join(repoDir, 'nonexistent.txt'), { getFileCommitDate(path.join(repoDir, 'nonexistent.txt'), {
age: 'newest', age: 'newest',
includeAuthor: true, includeAuthor: true,
}), }),
).toThrow( ).rejects.toThrow(
/Failed to retrieve git history for ".*nonexistent.txt" because the file does not exist./, /Failed to retrieve git history for ".*nonexistent.txt" because the file does not exist./,
); );
}); });

View file

@ -24,7 +24,7 @@ export class FileNotTrackedError extends Error {}
* @throws Also throws when `git log` exited with non-zero, or when it outputs * @throws Also throws when `git log` exited with non-zero, or when it outputs
* unexpected text. * unexpected text.
*/ */
export function getFileCommitDate( export async function getFileCommitDate(
/** Absolute path to the file. */ /** Absolute path to the file. */
file: string, file: string,
args: { args: {
@ -36,12 +36,12 @@ export function getFileCommitDate(
/** Use `includeAuthor: true` to get the author information as well. */ /** Use `includeAuthor: true` to get the author information as well. */
includeAuthor?: false; includeAuthor?: false;
}, },
): { ): Promise<{
/** Relevant commit date. */ /** Relevant commit date. */
date: Date; date: Date;
/** Timestamp in **seconds**, as returned from git. */ /** Timestamp in **seconds**, as returned from git. */
timestamp: number; timestamp: number;
}; }>;
/** /**
* Fetches the git history of a file and returns a relevant commit date. * Fetches the git history of a file and returns a relevant commit date.
* It gets the commit date instead of author date so that amended commits * It gets the commit date instead of author date so that amended commits
@ -52,7 +52,7 @@ export function getFileCommitDate(
* @throws Also throws when `git log` exited with non-zero, or when it outputs * @throws Also throws when `git log` exited with non-zero, or when it outputs
* unexpected text. * unexpected text.
*/ */
export function getFileCommitDate( export async function getFileCommitDate(
/** Absolute path to the file. */ /** Absolute path to the file. */
file: string, file: string,
args: { args: {
@ -63,15 +63,16 @@ export function getFileCommitDate(
age?: 'oldest' | 'newest'; age?: 'oldest' | 'newest';
includeAuthor: true; includeAuthor: true;
}, },
): { ): Promise<{
/** Relevant commit date. */ /** Relevant commit date. */
date: Date; date: Date;
/** Timestamp in **seconds**, as returned from git. */ /** Timestamp in **seconds**, as returned from git. */
timestamp: number; timestamp: number;
/** The author's name, as returned from git. */ /** The author's name, as returned from git. */
author: string; author: string;
}; }>;
export function getFileCommitDate(
export async function getFileCommitDate(
file: string, file: string,
{ {
age = 'oldest', age = 'oldest',
@ -80,11 +81,11 @@ export function getFileCommitDate(
age?: 'oldest' | 'newest'; age?: 'oldest' | 'newest';
includeAuthor?: boolean; includeAuthor?: boolean;
}, },
): { ): Promise<{
date: Date; date: Date;
timestamp: number; timestamp: number;
author?: string; author?: string;
} { }> {
if (!shell.which('git')) { if (!shell.which('git')) {
throw new GitNotFoundError( throw new GitNotFoundError(
`Failed to retrieve git history for "${file}" because git is not installed.`, `Failed to retrieve git history for "${file}" because git is not installed.`,
@ -105,11 +106,24 @@ export function getFileCommitDate(
.filter(Boolean) .filter(Boolean)
.join(' '); .join(' ');
const result = shell.exec(`git log ${args} -- "${path.basename(file)}"`, { const result = await new Promise<{
// Setting cwd is important, see: https://github.com/facebook/docusaurus/pull/5048 code: number;
cwd: path.dirname(file), stdout: string;
silent: true, stderr: string;
}>((resolve) => {
shell.exec(
`git log ${args} -- "${path.basename(file)}"`,
{
// Setting cwd is important, see: https://github.com/facebook/docusaurus/pull/5048
cwd: path.dirname(file),
silent: true,
},
(code, stdout, stderr) => {
resolve({code, stdout, stderr});
},
);
}); });
if (result.code !== 0) { if (result.code !== 0) {
throw new Error( throw new Error(
`Failed to retrieve the git history for file "${file}" with exit code ${result.code}: ${result.stderr}`, `Failed to retrieve the git history for file "${file}" with exit code ${result.code}: ${result.stderr}`,