mirror of
https://github.com/facebook/docusaurus.git
synced 2025-12-26 01:33:02 +00:00
move getGitLastUpdate fn to gitUtils
This commit is contained in:
parent
8af8aab7cb
commit
4fa2e093ac
|
|
@ -1 +0,0 @@
|
|||
# Hoo hoo, if this path tricks you...
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
---
|
||||
id: hello
|
||||
title: Hello, World !
|
||||
slug: /
|
||||
---
|
||||
|
||||
Hello
|
||||
|
|
@ -5,13 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {jest} from '@jest/globals';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import {createTempRepo} from '@testing-utils/git';
|
||||
import execa from 'execa';
|
||||
|
||||
import {getGitLastUpdate, readLastUpdateData} from '../lastUpdateUtils';
|
||||
import {readLastUpdateData} from '../lastUpdateUtils';
|
||||
import {
|
||||
VcsHardcoded,
|
||||
VCS_HARDCODED_UNTRACKED_FILE_PATH,
|
||||
|
|
@ -20,111 +14,6 @@ import {
|
|||
|
||||
import type {FrontMatterLastUpdate} from '../lastUpdateUtils';
|
||||
|
||||
describe('getGitLastUpdate', () => {
|
||||
const {repoDir} = createTempRepo();
|
||||
|
||||
const existingFilePath = path.join(
|
||||
__dirname,
|
||||
'__fixtures__/simple-site/hello.md',
|
||||
);
|
||||
it('existing test file in repository with Git timestamp', async () => {
|
||||
const lastUpdateData = await getGitLastUpdate(existingFilePath);
|
||||
expect(lastUpdateData).not.toBeNull();
|
||||
|
||||
const {lastUpdatedAt, lastUpdatedBy} = lastUpdateData!;
|
||||
expect(lastUpdatedBy).not.toBeNull();
|
||||
expect(typeof lastUpdatedBy).toBe('string');
|
||||
|
||||
expect(lastUpdatedAt).not.toBeNull();
|
||||
expect(typeof lastUpdatedAt).toBe('number');
|
||||
});
|
||||
|
||||
it('existing test file with spaces in path', async () => {
|
||||
const filePathWithSpace = path.join(
|
||||
__dirname,
|
||||
'__fixtures__/simple-site/doc with space.md',
|
||||
);
|
||||
const lastUpdateData = await getGitLastUpdate(filePathWithSpace);
|
||||
expect(lastUpdateData).not.toBeNull();
|
||||
|
||||
const {lastUpdatedBy, lastUpdatedAt} = lastUpdateData!;
|
||||
expect(lastUpdatedBy).not.toBeNull();
|
||||
expect(typeof lastUpdatedBy).toBe('string');
|
||||
|
||||
expect(lastUpdatedAt).not.toBeNull();
|
||||
expect(typeof lastUpdatedAt).toBe('number');
|
||||
});
|
||||
|
||||
it('non-existing file', async () => {
|
||||
const consoleMock = jest
|
||||
.spyOn(console, 'warn')
|
||||
.mockImplementation(() => {});
|
||||
const nonExistingFileName = '.nonExisting';
|
||||
const nonExistingFilePath = path.join(
|
||||
__dirname,
|
||||
'__fixtures__',
|
||||
nonExistingFileName,
|
||||
);
|
||||
await expect(getGitLastUpdate(nonExistingFilePath)).rejects.toThrow(
|
||||
/An error occurred when trying to get the last update date/,
|
||||
);
|
||||
expect(consoleMock).toHaveBeenCalledTimes(0);
|
||||
consoleMock.mockRestore();
|
||||
});
|
||||
|
||||
it('git does not exist', async () => {
|
||||
const mock = jest.spyOn(execa, 'sync').mockImplementationOnce(() => {
|
||||
throw new Error('Git does not exist');
|
||||
});
|
||||
|
||||
const consoleMock = jest
|
||||
.spyOn(console, 'warn')
|
||||
.mockImplementation(() => {});
|
||||
const lastUpdateData = await getGitLastUpdate(existingFilePath);
|
||||
expect(lastUpdateData).toBeNull();
|
||||
expect(consoleMock).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(
|
||||
/.*\[WARNING\].* Sorry, the last update options require Git\..*/,
|
||||
),
|
||||
);
|
||||
|
||||
consoleMock.mockRestore();
|
||||
mock.mockRestore();
|
||||
});
|
||||
|
||||
it('temporary created file that is not tracked by git', async () => {
|
||||
const consoleMock = jest
|
||||
.spyOn(console, 'warn')
|
||||
.mockImplementation(() => {});
|
||||
const tempFilePath = path.join(repoDir, 'file.md');
|
||||
await fs.writeFile(tempFilePath, 'Lorem ipsum :)');
|
||||
await expect(getGitLastUpdate(tempFilePath)).resolves.toBeNull();
|
||||
expect(consoleMock).toHaveBeenCalledTimes(1);
|
||||
expect(consoleMock).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(/not tracked by git./),
|
||||
);
|
||||
await fs.unlink(tempFilePath);
|
||||
});
|
||||
|
||||
it('multiple files not tracked by git', async () => {
|
||||
const consoleMock = jest
|
||||
.spyOn(console, 'warn')
|
||||
.mockImplementation(() => {});
|
||||
const tempFilePath1 = path.join(repoDir, 'file1.md');
|
||||
const tempFilePath2 = path.join(repoDir, 'file2.md');
|
||||
await fs.writeFile(tempFilePath1, 'Lorem ipsum :)');
|
||||
await fs.writeFile(tempFilePath2, 'Lorem ipsum :)');
|
||||
await expect(getGitLastUpdate(tempFilePath1)).resolves.toBeNull();
|
||||
await expect(getGitLastUpdate(tempFilePath2)).resolves.toBeNull();
|
||||
expect(consoleMock).toHaveBeenCalledTimes(1);
|
||||
expect(consoleMock).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(/not tracked by git./),
|
||||
);
|
||||
await fs.unlink(tempFilePath1);
|
||||
await fs.unlink(tempFilePath2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('readLastUpdateData', () => {
|
||||
const testDate = '2021-01-01';
|
||||
const testTimestamp = new Date(testDate).getTime();
|
||||
|
|
|
|||
|
|
@ -123,7 +123,6 @@ export {askPreferredLanguage} from './cliUtils';
|
|||
export {flattenRoutes} from './routeUtils';
|
||||
|
||||
export {
|
||||
getGitLastUpdate,
|
||||
readLastUpdateData,
|
||||
type LastUpdateData,
|
||||
type FrontMatterLastUpdate,
|
||||
|
|
|
|||
|
|
@ -6,12 +6,6 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import logger from '@docusaurus/logger';
|
||||
import {
|
||||
FileNotTrackedError,
|
||||
GitNotFoundError,
|
||||
getFileCommitDate,
|
||||
} from './vcs/gitUtils';
|
||||
import {getDefaultVcsConfig} from './vcs/vcs';
|
||||
|
||||
import type {PluginOptions, VcsConfig} from '@docusaurus/types';
|
||||
|
|
@ -31,48 +25,6 @@ export type LastUpdateData = {
|
|||
lastUpdatedBy: string | undefined | null;
|
||||
};
|
||||
|
||||
let showedGitRequirementError = false;
|
||||
let showedFileNotTrackedError = false;
|
||||
|
||||
export async function getGitLastUpdate(
|
||||
filePath: string,
|
||||
): Promise<LastUpdateData | null> {
|
||||
if (!filePath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Wrap in try/catch in case the shell commands fail
|
||||
// (e.g. project doesn't use Git, etc).
|
||||
try {
|
||||
const result = await getFileCommitDate(filePath, {
|
||||
age: 'newest',
|
||||
includeAuthor: true,
|
||||
});
|
||||
|
||||
return {lastUpdatedAt: result.timestamp, lastUpdatedBy: result.author};
|
||||
} catch (err) {
|
||||
if (err instanceof GitNotFoundError) {
|
||||
if (!showedGitRequirementError) {
|
||||
logger.warn('Sorry, the last update options require Git.');
|
||||
showedGitRequirementError = true;
|
||||
}
|
||||
} else if (err instanceof FileNotTrackedError) {
|
||||
if (!showedFileNotTrackedError) {
|
||||
logger.warn(
|
||||
'Cannot infer the update date for some files, as they are not tracked by git.',
|
||||
);
|
||||
showedFileNotTrackedError = true;
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
`An error occurred when trying to get the last update date`,
|
||||
{cause: err},
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
type LastUpdateOptions = Pick<
|
||||
PluginOptions,
|
||||
'showLastUpdateAuthor' | 'showLastUpdateTime'
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {jest} from '@jest/globals';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import {createTempRepo} from '@testing-utils/git';
|
||||
import {FileNotTrackedError, getFileCommitDate} from '../gitUtils';
|
||||
import execa from 'execa';
|
||||
import {FileNotTrackedError, getFileCommitDate,getGitLastUpdate} from '../gitUtils';
|
||||
|
||||
/* eslint-disable no-restricted-properties */
|
||||
function initializeTempRepo() {
|
||||
|
|
@ -143,3 +145,109 @@ describe('getFileCommitDate', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getGitLastUpdate', () => {
|
||||
const {repoDir} = createTempRepo();
|
||||
|
||||
const existingFilePath = path.join(
|
||||
__dirname,
|
||||
'__fixtures__/simple-site/docs/doc1.md',
|
||||
);
|
||||
|
||||
it('existing test file in repository with Git timestamp', async () => {
|
||||
const lastUpdateData = await getGitLastUpdate(existingFilePath);
|
||||
expect(lastUpdateData).not.toBeNull();
|
||||
|
||||
const {timestamp, author} = lastUpdateData!;
|
||||
expect(author).not.toBeNull();
|
||||
expect(typeof author).toBe('string');
|
||||
|
||||
expect(timestamp).not.toBeNull();
|
||||
expect(typeof timestamp).toBe('number');
|
||||
});
|
||||
|
||||
it('existing test file with spaces in path', async () => {
|
||||
const filePathWithSpace = path.join(
|
||||
__dirname,
|
||||
'__fixtures__/simple-site/docs/doc with space.md',
|
||||
);
|
||||
const lastUpdateData = await getGitLastUpdate(filePathWithSpace);
|
||||
expect(lastUpdateData).not.toBeNull();
|
||||
|
||||
const {timestamp, author} = lastUpdateData!;
|
||||
expect(author).not.toBeNull();
|
||||
expect(typeof author).toBe('string');
|
||||
|
||||
expect(timestamp).not.toBeNull();
|
||||
expect(typeof timestamp).toBe('number');
|
||||
});
|
||||
|
||||
it('non-existing file', async () => {
|
||||
const consoleMock = jest
|
||||
.spyOn(console, 'warn')
|
||||
.mockImplementation(() => {});
|
||||
const nonExistingFileName = '.nonExisting';
|
||||
const nonExistingFilePath = path.join(
|
||||
__dirname,
|
||||
'__fixtures__',
|
||||
nonExistingFileName,
|
||||
);
|
||||
await expect(getGitLastUpdate(nonExistingFilePath)).rejects.toThrow(
|
||||
/An error occurred when trying to get the last update date/,
|
||||
);
|
||||
expect(consoleMock).toHaveBeenCalledTimes(0);
|
||||
consoleMock.mockRestore();
|
||||
});
|
||||
|
||||
it('git does not exist', async () => {
|
||||
const mock = jest.spyOn(execa, 'sync').mockImplementationOnce(() => {
|
||||
throw new Error('Git does not exist');
|
||||
});
|
||||
|
||||
const consoleMock = jest
|
||||
.spyOn(console, 'warn')
|
||||
.mockImplementation(() => {});
|
||||
const lastUpdateData = await getGitLastUpdate(existingFilePath);
|
||||
expect(lastUpdateData).toBeNull();
|
||||
expect(consoleMock).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(
|
||||
/.*\[WARNING\].* Sorry, the last update options require Git\..*/,
|
||||
),
|
||||
);
|
||||
|
||||
consoleMock.mockRestore();
|
||||
mock.mockRestore();
|
||||
});
|
||||
|
||||
it('temporary created file that is not tracked by git', async () => {
|
||||
const consoleMock = jest
|
||||
.spyOn(console, 'warn')
|
||||
.mockImplementation(() => {});
|
||||
const tempFilePath = path.join(repoDir, 'file.md');
|
||||
await fs.writeFile(tempFilePath, 'Lorem ipsum :)');
|
||||
await expect(getGitLastUpdate(tempFilePath)).resolves.toBeNull();
|
||||
expect(consoleMock).toHaveBeenCalledTimes(1);
|
||||
expect(consoleMock).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(/not tracked by git./),
|
||||
);
|
||||
await fs.unlink(tempFilePath);
|
||||
});
|
||||
|
||||
it('multiple files not tracked by git', async () => {
|
||||
const consoleMock = jest
|
||||
.spyOn(console, 'warn')
|
||||
.mockImplementation(() => {});
|
||||
const tempFilePath1 = path.join(repoDir, 'file1.md');
|
||||
const tempFilePath2 = path.join(repoDir, 'file2.md');
|
||||
await fs.writeFile(tempFilePath1, 'Lorem ipsum :)');
|
||||
await fs.writeFile(tempFilePath2, 'Lorem ipsum :)');
|
||||
await expect(getGitLastUpdate(tempFilePath1)).resolves.toBeNull();
|
||||
await expect(getGitLastUpdate(tempFilePath2)).resolves.toBeNull();
|
||||
expect(consoleMock).toHaveBeenCalledTimes(1);
|
||||
expect(consoleMock).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(/not tracked by git./),
|
||||
);
|
||||
await fs.unlink(tempFilePath1);
|
||||
await fs.unlink(tempFilePath2);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import os from 'os';
|
|||
import _ from 'lodash';
|
||||
import execa from 'execa';
|
||||
import PQueue from 'p-queue';
|
||||
import logger from '@docusaurus/logger';
|
||||
|
||||
// Quite high/conservative concurrency value (it was previously "Infinity")
|
||||
// See https://github.com/facebook/docusaurus/pull/10915
|
||||
|
|
@ -204,3 +205,60 @@ export async function getFileCommitDate(
|
|||
}
|
||||
return {date, timestamp};
|
||||
}
|
||||
|
||||
type GitLastUpdateResult = {
|
||||
/**
|
||||
* A timestamp in **milliseconds**
|
||||
* `undefined`: not read
|
||||
* `null`: no value to read (usual for untracked files)
|
||||
*/
|
||||
timestamp: number | undefined | null;
|
||||
/**
|
||||
* The Git author's name
|
||||
* `undefined`: not read
|
||||
* `null`: no value to read (usual for untracked files)
|
||||
*/
|
||||
author: string | undefined | null;
|
||||
};
|
||||
|
||||
let showedGitRequirementError = false;
|
||||
let showedFileNotTrackedError = false;
|
||||
|
||||
export async function getGitLastUpdate(
|
||||
filePath: string,
|
||||
): Promise<GitLastUpdateResult | null> {
|
||||
if (!filePath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Wrap in try/catch in case the shell commands fail
|
||||
// (e.g. project doesn't use Git, etc).
|
||||
try {
|
||||
const result = await getFileCommitDate(filePath, {
|
||||
age: 'newest',
|
||||
includeAuthor: true,
|
||||
});
|
||||
return {timestamp: result.timestamp, author: result.author};
|
||||
} catch (err) {
|
||||
// TODO legacy perf issue: do not use exceptions for control flow!
|
||||
if (err instanceof GitNotFoundError) {
|
||||
if (!showedGitRequirementError) {
|
||||
logger.warn('Sorry, the last update options require Git.');
|
||||
showedGitRequirementError = true;
|
||||
}
|
||||
} else if (err instanceof FileNotTrackedError) {
|
||||
if (!showedFileNotTrackedError) {
|
||||
logger.warn(
|
||||
'Cannot infer the update date for some files, as they are not tracked by git.',
|
||||
);
|
||||
showedFileNotTrackedError = true;
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
`An error occurred when trying to get the last update date`,
|
||||
{cause: err},
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue