@docusaurus/utils 3.9.2-canary-6436 → 3.9.2-canary-6439
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.ts +3 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +8 -5
- package/lib/index.js.map +1 -1
- package/lib/lastUpdateUtils.d.ts +2 -6
- package/lib/lastUpdateUtils.d.ts.map +1 -1
- package/lib/lastUpdateUtils.js +13 -60
- package/lib/lastUpdateUtils.js.map +1 -1
- package/lib/{gitUtils.d.ts → vcs/gitUtils.d.ts} +17 -0
- package/lib/vcs/gitUtils.d.ts.map +1 -0
- package/lib/vcs/gitUtils.js +330 -0
- package/lib/vcs/gitUtils.js.map +1 -0
- package/lib/vcs/vcs.d.ts +19 -0
- package/lib/vcs/vcs.d.ts.map +1 -0
- package/lib/vcs/vcs.js +46 -0
- package/lib/vcs/vcs.js.map +1 -0
- package/lib/vcs/vcsDefaultV1.d.ts +13 -0
- package/lib/vcs/vcsDefaultV1.d.ts.map +1 -0
- package/lib/vcs/vcsDefaultV1.js +33 -0
- package/lib/vcs/vcsDefaultV1.js.map +1 -0
- package/lib/vcs/vcsDefaultV2.d.ts +13 -0
- package/lib/vcs/vcsDefaultV2.d.ts.map +1 -0
- package/lib/vcs/vcsDefaultV2.js +33 -0
- package/lib/vcs/vcsDefaultV2.js.map +1 -0
- package/lib/vcs/vcsDisabled.d.ts +12 -0
- package/lib/vcs/vcsDisabled.d.ts.map +1 -0
- package/lib/vcs/vcsDisabled.js +24 -0
- package/lib/vcs/vcsDisabled.js.map +1 -0
- package/lib/vcs/vcsGitAdHoc.d.ts +16 -0
- package/lib/vcs/vcsGitAdHoc.d.ts.map +1 -0
- package/lib/vcs/vcsGitAdHoc.js +29 -0
- package/lib/vcs/vcsGitAdHoc.js.map +1 -0
- package/lib/vcs/vcsGitEager.d.ts +9 -0
- package/lib/vcs/vcsGitEager.d.ts.map +1 -0
- package/lib/vcs/vcsGitEager.js +68 -0
- package/lib/vcs/vcsGitEager.js.map +1 -0
- package/lib/vcs/vcsHardcoded.d.ts +17 -0
- package/lib/vcs/vcsHardcoded.d.ts.map +1 -0
- package/lib/vcs/vcsHardcoded.js +41 -0
- package/lib/vcs/vcsHardcoded.js.map +1 -0
- package/package.json +6 -6
- package/src/index.ts +5 -4
- package/src/lastUpdateUtils.ts +18 -76
- package/src/vcs/gitUtils.ts +524 -0
- package/src/vcs/vcs.ts +54 -0
- package/src/vcs/vcsDefaultV1.ts +33 -0
- package/src/vcs/vcsDefaultV2.ts +33 -0
- package/src/vcs/vcsDisabled.ts +25 -0
- package/src/vcs/vcsGitAdHoc.ts +30 -0
- package/src/vcs/vcsGitEager.ts +99 -0
- package/src/vcs/vcsHardcoded.ts +45 -0
- package/lib/gitUtils.d.ts.map +0 -1
- package/lib/gitUtils.js +0 -103
- package/lib/gitUtils.js.map +0 -1
- package/src/gitUtils.ts +0 -200
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {resolve, basename} from 'node:path';
|
|
9
|
+
import logger, {PerfLogger} from '@docusaurus/logger';
|
|
10
|
+
import {getGitAllRepoRoots, getGitRepositoryFilesInfo} from './gitUtils';
|
|
11
|
+
import type {GitFileInfo, GitFileInfoMap} from './gitUtils';
|
|
12
|
+
import type {VcsConfig} from '@docusaurus/types';
|
|
13
|
+
|
|
14
|
+
// The Map keys should be absolute file paths, not relative Git paths
|
|
15
|
+
function resolveFileInfoMapPaths(
|
|
16
|
+
repoRoot: string,
|
|
17
|
+
filesInfo: GitFileInfoMap,
|
|
18
|
+
): GitFileInfoMap {
|
|
19
|
+
function transformMapEntry(
|
|
20
|
+
entry: [string, GitFileInfo],
|
|
21
|
+
): [string, GitFileInfo] {
|
|
22
|
+
// We just resolve the Git paths that are relative to the repo root
|
|
23
|
+
return [resolve(repoRoot, entry[0]), entry[1]];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return new Map(Array.from(filesInfo.entries()).map(transformMapEntry));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function mergeFileMaps(fileMaps: GitFileInfoMap[]): GitFileInfoMap {
|
|
30
|
+
return new Map(fileMaps.flatMap((m) => [...m]));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function loadAllGitFilesInfoMap(cwd: string): Promise<GitFileInfoMap> {
|
|
34
|
+
const roots = await PerfLogger.async('Reading Git root dirs', () =>
|
|
35
|
+
getGitAllRepoRoots(cwd),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const allMaps: GitFileInfoMap[] = await Promise.all(
|
|
39
|
+
roots.map(async (root) => {
|
|
40
|
+
const map = await PerfLogger.async(
|
|
41
|
+
`Reading Git history for repo ${logger.path(basename(root))}`,
|
|
42
|
+
() => getGitRepositoryFilesInfo(root),
|
|
43
|
+
);
|
|
44
|
+
return resolveFileInfoMapPaths(root, map);
|
|
45
|
+
}),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return mergeFileMaps(allMaps);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function createGitVcsConfig(): VcsConfig {
|
|
52
|
+
let filesMapPromise: Promise<GitFileInfoMap> | null = null;
|
|
53
|
+
|
|
54
|
+
async function getGitFileInfo(filePath: string): Promise<GitFileInfo | null> {
|
|
55
|
+
const filesMap = await filesMapPromise;
|
|
56
|
+
return filesMap?.get(filePath) ?? null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
initialize: ({siteDir}) => {
|
|
61
|
+
if (filesMapPromise) {
|
|
62
|
+
// We only initialize this VCS once!
|
|
63
|
+
// For i18n sites, this permits reading ahead of time for all locales
|
|
64
|
+
// so that it only slows down the first locale
|
|
65
|
+
// I assume this logic is fine, but we'll see if it causes trouble
|
|
66
|
+
|
|
67
|
+
// Note: we could also only call "initialize()" once from the outside,
|
|
68
|
+
// But maybe it could be useful for custom VCS implementations to be
|
|
69
|
+
// able to initialize once per locale?
|
|
70
|
+
PerfLogger.log(
|
|
71
|
+
'Git Eager VCS strategy already initialized, skipping re-initialization',
|
|
72
|
+
);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
filesMapPromise = PerfLogger.async('Git Eager VCS init', () =>
|
|
77
|
+
loadAllGitFilesInfoMap(siteDir),
|
|
78
|
+
);
|
|
79
|
+
filesMapPromise.catch((error) => {
|
|
80
|
+
console.error(
|
|
81
|
+
'Failed to initialize the Docusaurus Git Eager VCS strategy',
|
|
82
|
+
error,
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
getFileCreationInfo: async (filePath: string) => {
|
|
88
|
+
const fileInfo = await getGitFileInfo(filePath);
|
|
89
|
+
return fileInfo?.creation ?? null;
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
getFileLastUpdateInfo: async (filePath: string) => {
|
|
93
|
+
const fileInfo = await getGitFileInfo(filePath);
|
|
94
|
+
return fileInfo?.lastUpdate ?? null;
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const VscGitEager: VcsConfig = createGitVcsConfig();
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {VcsConfig, VcsChangeInfo} from '@docusaurus/types';
|
|
9
|
+
|
|
10
|
+
export const VCS_HARDCODED_CREATION_INFO: VcsChangeInfo = {
|
|
11
|
+
timestamp: 1490997600000, // 1st Apr 2017
|
|
12
|
+
author: 'Creator',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const VCS_HARDCODED_LAST_UPDATE_INFO: VcsChangeInfo = {
|
|
16
|
+
timestamp: 1539502055000, // 14th Oct 2018
|
|
17
|
+
author: 'Author',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const VCS_HARDCODED_UNTRACKED_FILE_PATH = `file/path/${Math.random()}.mdx`;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* This VCS implementation always returns hardcoded values for testing purposes.
|
|
24
|
+
* It is also useful in dev environments where VCS info is not important.
|
|
25
|
+
* Reading information from the VCS can be slow and is not always necessary.
|
|
26
|
+
*/
|
|
27
|
+
export const VcsHardcoded: VcsConfig = {
|
|
28
|
+
initialize: () => {
|
|
29
|
+
// Noop
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
getFileCreationInfo: async (filePath: string) => {
|
|
33
|
+
if (filePath === VCS_HARDCODED_UNTRACKED_FILE_PATH) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return VCS_HARDCODED_CREATION_INFO;
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
getFileLastUpdateInfo: async (filePath: string) => {
|
|
40
|
+
if (filePath === VCS_HARDCODED_UNTRACKED_FILE_PATH) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return VCS_HARDCODED_LAST_UPDATE_INFO;
|
|
44
|
+
},
|
|
45
|
+
};
|
package/lib/gitUtils.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gitUtils.d.ts","sourceRoot":"","sources":["../src/gitUtils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA6CH,2DAA2D;AAC3D,qBAAa,gBAAiB,SAAQ,KAAK;CAAG;AAE9C,uEAAuE;AACvE,qBAAa,mBAAoB,SAAQ,KAAK;CAAG;AAEjD;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB;AACrC,iCAAiC;AACjC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IACJ;;;OAGG;IACH,GAAG,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B,uEAAuE;IACvE,aAAa,CAAC,EAAE,KAAK,CAAC;CACvB,GACA,OAAO,CAAC;IACT,4BAA4B;IAC5B,IAAI,EAAE,IAAI,CAAC;IACX,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,CAAC;AACH;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB;AACrC,iCAAiC;AACjC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IACJ;;;OAGG;IACH,GAAG,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B,aAAa,EAAE,IAAI,CAAC;CACrB,GACA,OAAO,CAAC;IACT,4BAA4B;IAC5B,IAAI,EAAE,IAAI,CAAC;IACX,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC"}
|
package/lib/gitUtils.js
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.FileNotTrackedError = exports.GitNotFoundError = void 0;
|
|
10
|
-
exports.getFileCommitDate = getFileCommitDate;
|
|
11
|
-
const tslib_1 = require("tslib");
|
|
12
|
-
const path_1 = tslib_1.__importDefault(require("path"));
|
|
13
|
-
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
14
|
-
const os_1 = tslib_1.__importDefault(require("os"));
|
|
15
|
-
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
16
|
-
const execa_1 = tslib_1.__importDefault(require("execa"));
|
|
17
|
-
const p_queue_1 = tslib_1.__importDefault(require("p-queue"));
|
|
18
|
-
// Quite high/conservative concurrency value (it was previously "Infinity")
|
|
19
|
-
// See https://github.com/facebook/docusaurus/pull/10915
|
|
20
|
-
const DefaultGitCommandConcurrency =
|
|
21
|
-
// TODO Docusaurus v4: bump node, availableParallelism() now always exists
|
|
22
|
-
(typeof os_1.default.availableParallelism === 'function'
|
|
23
|
-
? os_1.default.availableParallelism()
|
|
24
|
-
: os_1.default.cpus().length) * 4;
|
|
25
|
-
const GitCommandConcurrencyEnv = process.env.DOCUSAURUS_GIT_COMMAND_CONCURRENCY
|
|
26
|
-
? parseInt(process.env.DOCUSAURUS_GIT_COMMAND_CONCURRENCY, 10)
|
|
27
|
-
: undefined;
|
|
28
|
-
const GitCommandConcurrency = GitCommandConcurrencyEnv && GitCommandConcurrencyEnv > 0
|
|
29
|
-
? GitCommandConcurrencyEnv
|
|
30
|
-
: DefaultGitCommandConcurrency;
|
|
31
|
-
// We use a queue to avoid running too many concurrent Git commands at once
|
|
32
|
-
// See https://github.com/facebook/docusaurus/issues/10348
|
|
33
|
-
const GitCommandQueue = new p_queue_1.default({
|
|
34
|
-
concurrency: GitCommandConcurrency,
|
|
35
|
-
});
|
|
36
|
-
const realHasGitFn = () => {
|
|
37
|
-
try {
|
|
38
|
-
return execa_1.default.sync('git', ['--version']).exitCode === 0;
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
// The hasGit call is synchronous IO so we memoize it
|
|
45
|
-
// The user won't install Git in the middle of a build anyway...
|
|
46
|
-
const hasGit = process.env.NODE_ENV === 'test' ? realHasGitFn : lodash_1.default.memoize(realHasGitFn);
|
|
47
|
-
/** Custom error thrown when git is not found in `PATH`. */
|
|
48
|
-
class GitNotFoundError extends Error {
|
|
49
|
-
}
|
|
50
|
-
exports.GitNotFoundError = GitNotFoundError;
|
|
51
|
-
/** Custom error thrown when the current file is not tracked by git. */
|
|
52
|
-
class FileNotTrackedError extends Error {
|
|
53
|
-
}
|
|
54
|
-
exports.FileNotTrackedError = FileNotTrackedError;
|
|
55
|
-
async function getFileCommitDate(file, { age = 'oldest', includeAuthor = false, }) {
|
|
56
|
-
if (!hasGit()) {
|
|
57
|
-
throw new GitNotFoundError(`Failed to retrieve git history for "${file}" because git is not installed.`);
|
|
58
|
-
}
|
|
59
|
-
if (!(await fs_extra_1.default.pathExists(file))) {
|
|
60
|
-
throw new Error(`Failed to retrieve git history for "${file}" because the file does not exist.`);
|
|
61
|
-
}
|
|
62
|
-
// We add a "RESULT:" prefix to make parsing easier
|
|
63
|
-
// See why: https://github.com/facebook/docusaurus/pull/10022
|
|
64
|
-
const resultFormat = includeAuthor ? 'RESULT:%ct,%an' : 'RESULT:%ct';
|
|
65
|
-
const args = [
|
|
66
|
-
`--format=${resultFormat}`,
|
|
67
|
-
'--max-count=1',
|
|
68
|
-
age === 'oldest' ? '--follow --diff-filter=A' : undefined,
|
|
69
|
-
]
|
|
70
|
-
.filter(Boolean)
|
|
71
|
-
.join(' ');
|
|
72
|
-
const command = `git -c log.showSignature=false log ${args} -- "${path_1.default.basename(file)}"`;
|
|
73
|
-
const result = (await GitCommandQueue.add(() => {
|
|
74
|
-
return (0, execa_1.default)(command, {
|
|
75
|
-
cwd: path_1.default.dirname(file),
|
|
76
|
-
shell: true,
|
|
77
|
-
});
|
|
78
|
-
}));
|
|
79
|
-
if (result.exitCode !== 0) {
|
|
80
|
-
throw new Error(`Failed to retrieve the git history for file "${file}" with exit code ${result.exitCode}: ${result.stderr}`);
|
|
81
|
-
}
|
|
82
|
-
// We only parse the output line starting with our "RESULT:" prefix
|
|
83
|
-
// See why https://github.com/facebook/docusaurus/pull/10022
|
|
84
|
-
const regex = includeAuthor
|
|
85
|
-
? /(?:^|\n)RESULT:(?<timestamp>\d+),(?<author>.+)(?:$|\n)/
|
|
86
|
-
: /(?:^|\n)RESULT:(?<timestamp>\d+)(?:$|\n)/;
|
|
87
|
-
const output = result.stdout.trim();
|
|
88
|
-
if (!output) {
|
|
89
|
-
throw new FileNotTrackedError(`Failed to retrieve the git history for file "${file}" because the file is not tracked by git.`);
|
|
90
|
-
}
|
|
91
|
-
const match = output.match(regex);
|
|
92
|
-
if (!match) {
|
|
93
|
-
throw new Error(`Failed to retrieve the git history for file "${file}" with unexpected output: ${output}`);
|
|
94
|
-
}
|
|
95
|
-
const timestampInSeconds = Number(match.groups.timestamp);
|
|
96
|
-
const timestamp = timestampInSeconds * 1000;
|
|
97
|
-
const date = new Date(timestamp);
|
|
98
|
-
if (includeAuthor) {
|
|
99
|
-
return { date, timestamp, author: match.groups.author };
|
|
100
|
-
}
|
|
101
|
-
return { date, timestamp };
|
|
102
|
-
}
|
|
103
|
-
//# sourceMappingURL=gitUtils.js.map
|
package/lib/gitUtils.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gitUtils.js","sourceRoot":"","sources":["../src/gitUtils.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AA6GH,8CAqFC;;AAhMD,wDAAwB;AACxB,gEAA0B;AAC1B,oDAAoB;AACpB,4DAAuB;AACvB,0DAA0B;AAC1B,8DAA6B;AAE7B,2EAA2E;AAC3E,wDAAwD;AACxD,MAAM,4BAA4B;AAChC,0EAA0E;AAC1E,CAAC,OAAO,YAAE,CAAC,oBAAoB,KAAK,UAAU;IAC5C,CAAC,CAAC,YAAE,CAAC,oBAAoB,EAAE;IAC3B,CAAC,CAAC,YAAE,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAE5B,MAAM,wBAAwB,GAAG,OAAO,CAAC,GAAG,CAAC,kCAAkC;IAC7E,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,kCAAkC,EAAE,EAAE,CAAC;IAC9D,CAAC,CAAC,SAAS,CAAC;AAEd,MAAM,qBAAqB,GACzB,wBAAwB,IAAI,wBAAwB,GAAG,CAAC;IACtD,CAAC,CAAC,wBAAwB;IAC1B,CAAC,CAAC,4BAA4B,CAAC;AAEnC,2EAA2E;AAC3E,0DAA0D;AAC1D,MAAM,eAAe,GAAG,IAAI,iBAAM,CAAC;IACjC,WAAW,EAAE,qBAAqB;CACnC,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,GAAG,EAAE;IACxB,IAAI,CAAC;QACH,OAAO,eAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC;IACzD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAEF,qDAAqD;AACrD,gEAAgE;AAChE,MAAM,MAAM,GACV,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,gBAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;AAE3E,2DAA2D;AAC3D,MAAa,gBAAiB,SAAQ,KAAK;CAAG;AAA9C,4CAA8C;AAE9C,uEAAuE;AACvE,MAAa,mBAAoB,SAAQ,KAAK;CAAG;AAAjD,kDAAiD;AA4D1C,KAAK,UAAU,iBAAiB,CACrC,IAAY,EACZ,EACE,GAAG,GAAG,QAAQ,EACd,aAAa,GAAG,KAAK,GAItB;IAMD,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACd,MAAM,IAAI,gBAAgB,CACxB,uCAAuC,IAAI,iCAAiC,CAC7E,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,CAAC,MAAM,kBAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,uCAAuC,IAAI,oCAAoC,CAChF,CAAC;IACJ,CAAC;IAED,mDAAmD;IACnD,6DAA6D;IAC7D,MAAM,YAAY,GAAG,aAAa,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,YAAY,CAAC;IAErE,MAAM,IAAI,GAAG;QACX,YAAY,YAAY,EAAE;QAC1B,eAAe;QACf,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,SAAS;KAC1D;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,MAAM,OAAO,GAAG,sCAAsC,IAAI,QAAQ,cAAI,CAAC,QAAQ,CAC7E,IAAI,CACL,GAAG,CAAC;IAEL,MAAM,MAAM,GAAG,CAAC,MAAM,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE;QAC7C,OAAO,IAAA,eAAK,EAAC,OAAO,EAAE;YACpB,GAAG,EAAE,cAAI,CAAC,OAAO,CAAC,IAAI,CAAC;YACvB,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC;IACL,CAAC,CAAC,CAAE,CAAC;IAEL,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,gDAAgD,IAAI,oBAAoB,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,MAAM,EAAE,CAC5G,CAAC;IACJ,CAAC;IAED,mEAAmE;IACnE,4DAA4D;IAC5D,MAAM,KAAK,GAAG,aAAa;QACzB,CAAC,CAAC,wDAAwD;QAC1D,CAAC,CAAC,0CAA0C,CAAC;IAE/C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAEpC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,mBAAmB,CAC3B,gDAAgD,IAAI,2CAA2C,CAChG,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAElC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,gDAAgD,IAAI,6BAA6B,MAAM,EAAE,CAC1F,CAAC;IACJ,CAAC;IAED,MAAM,kBAAkB,GAAG,MAAM,CAAC,KAAK,CAAC,MAAO,CAAC,SAAS,CAAC,CAAC;IAC3D,MAAM,SAAS,GAAG,kBAAkB,GAAG,IAAK,CAAC;IAC7C,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;IAEjC,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,EAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,MAAO,CAAC,MAAO,EAAC,CAAC;IAC1D,CAAC;IACD,OAAO,EAAC,IAAI,EAAE,SAAS,EAAC,CAAC;AAC3B,CAAC"}
|
package/src/gitUtils.ts
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import path from 'path';
|
|
9
|
-
import fs from 'fs-extra';
|
|
10
|
-
import os from 'os';
|
|
11
|
-
import _ from 'lodash';
|
|
12
|
-
import execa from 'execa';
|
|
13
|
-
import PQueue from 'p-queue';
|
|
14
|
-
|
|
15
|
-
// Quite high/conservative concurrency value (it was previously "Infinity")
|
|
16
|
-
// See https://github.com/facebook/docusaurus/pull/10915
|
|
17
|
-
const DefaultGitCommandConcurrency =
|
|
18
|
-
// TODO Docusaurus v4: bump node, availableParallelism() now always exists
|
|
19
|
-
(typeof os.availableParallelism === 'function'
|
|
20
|
-
? os.availableParallelism()
|
|
21
|
-
: os.cpus().length) * 4;
|
|
22
|
-
|
|
23
|
-
const GitCommandConcurrencyEnv = process.env.DOCUSAURUS_GIT_COMMAND_CONCURRENCY
|
|
24
|
-
? parseInt(process.env.DOCUSAURUS_GIT_COMMAND_CONCURRENCY, 10)
|
|
25
|
-
: undefined;
|
|
26
|
-
|
|
27
|
-
const GitCommandConcurrency =
|
|
28
|
-
GitCommandConcurrencyEnv && GitCommandConcurrencyEnv > 0
|
|
29
|
-
? GitCommandConcurrencyEnv
|
|
30
|
-
: DefaultGitCommandConcurrency;
|
|
31
|
-
|
|
32
|
-
// We use a queue to avoid running too many concurrent Git commands at once
|
|
33
|
-
// See https://github.com/facebook/docusaurus/issues/10348
|
|
34
|
-
const GitCommandQueue = new PQueue({
|
|
35
|
-
concurrency: GitCommandConcurrency,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
const realHasGitFn = () => {
|
|
39
|
-
try {
|
|
40
|
-
return execa.sync('git', ['--version']).exitCode === 0;
|
|
41
|
-
} catch (error) {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
// The hasGit call is synchronous IO so we memoize it
|
|
47
|
-
// The user won't install Git in the middle of a build anyway...
|
|
48
|
-
const hasGit =
|
|
49
|
-
process.env.NODE_ENV === 'test' ? realHasGitFn : _.memoize(realHasGitFn);
|
|
50
|
-
|
|
51
|
-
/** Custom error thrown when git is not found in `PATH`. */
|
|
52
|
-
export class GitNotFoundError extends Error {}
|
|
53
|
-
|
|
54
|
-
/** Custom error thrown when the current file is not tracked by git. */
|
|
55
|
-
export class FileNotTrackedError extends Error {}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Fetches the git history of a file and returns a relevant commit date.
|
|
59
|
-
* It gets the commit date instead of author date so that amended commits
|
|
60
|
-
* can have their dates updated.
|
|
61
|
-
*
|
|
62
|
-
* @throws {@link GitNotFoundError} If git is not found in `PATH`.
|
|
63
|
-
* @throws {@link FileNotTrackedError} If the current file is not tracked by git.
|
|
64
|
-
* @throws Also throws when `git log` exited with non-zero, or when it outputs
|
|
65
|
-
* unexpected text.
|
|
66
|
-
*/
|
|
67
|
-
export async function getFileCommitDate(
|
|
68
|
-
/** Absolute path to the file. */
|
|
69
|
-
file: string,
|
|
70
|
-
args: {
|
|
71
|
-
/**
|
|
72
|
-
* `"oldest"` is the commit that added the file, following renames;
|
|
73
|
-
* `"newest"` is the last commit that edited the file.
|
|
74
|
-
*/
|
|
75
|
-
age?: 'oldest' | 'newest';
|
|
76
|
-
/** Use `includeAuthor: true` to get the author information as well. */
|
|
77
|
-
includeAuthor?: false;
|
|
78
|
-
},
|
|
79
|
-
): Promise<{
|
|
80
|
-
/** Relevant commit date. */
|
|
81
|
-
date: Date;
|
|
82
|
-
/** Timestamp returned from git, converted to **milliseconds**. */
|
|
83
|
-
timestamp: number;
|
|
84
|
-
}>;
|
|
85
|
-
/**
|
|
86
|
-
* Fetches the git history of a file and returns a relevant commit date.
|
|
87
|
-
* It gets the commit date instead of author date so that amended commits
|
|
88
|
-
* can have their dates updated.
|
|
89
|
-
*
|
|
90
|
-
* @throws {@link GitNotFoundError} If git is not found in `PATH`.
|
|
91
|
-
* @throws {@link FileNotTrackedError} If the current file is not tracked by git.
|
|
92
|
-
* @throws Also throws when `git log` exited with non-zero, or when it outputs
|
|
93
|
-
* unexpected text.
|
|
94
|
-
*/
|
|
95
|
-
export async function getFileCommitDate(
|
|
96
|
-
/** Absolute path to the file. */
|
|
97
|
-
file: string,
|
|
98
|
-
args: {
|
|
99
|
-
/**
|
|
100
|
-
* `"oldest"` is the commit that added the file, following renames;
|
|
101
|
-
* `"newest"` is the last commit that edited the file.
|
|
102
|
-
*/
|
|
103
|
-
age?: 'oldest' | 'newest';
|
|
104
|
-
includeAuthor: true;
|
|
105
|
-
},
|
|
106
|
-
): Promise<{
|
|
107
|
-
/** Relevant commit date. */
|
|
108
|
-
date: Date;
|
|
109
|
-
/** Timestamp returned from git, converted to **milliseconds**. */
|
|
110
|
-
timestamp: number;
|
|
111
|
-
/** The author's name, as returned from git. */
|
|
112
|
-
author: string;
|
|
113
|
-
}>;
|
|
114
|
-
|
|
115
|
-
export async function getFileCommitDate(
|
|
116
|
-
file: string,
|
|
117
|
-
{
|
|
118
|
-
age = 'oldest',
|
|
119
|
-
includeAuthor = false,
|
|
120
|
-
}: {
|
|
121
|
-
age?: 'oldest' | 'newest';
|
|
122
|
-
includeAuthor?: boolean;
|
|
123
|
-
},
|
|
124
|
-
): Promise<{
|
|
125
|
-
date: Date;
|
|
126
|
-
timestamp: number;
|
|
127
|
-
author?: string;
|
|
128
|
-
}> {
|
|
129
|
-
if (!hasGit()) {
|
|
130
|
-
throw new GitNotFoundError(
|
|
131
|
-
`Failed to retrieve git history for "${file}" because git is not installed.`,
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (!(await fs.pathExists(file))) {
|
|
136
|
-
throw new Error(
|
|
137
|
-
`Failed to retrieve git history for "${file}" because the file does not exist.`,
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// We add a "RESULT:" prefix to make parsing easier
|
|
142
|
-
// See why: https://github.com/facebook/docusaurus/pull/10022
|
|
143
|
-
const resultFormat = includeAuthor ? 'RESULT:%ct,%an' : 'RESULT:%ct';
|
|
144
|
-
|
|
145
|
-
const args = [
|
|
146
|
-
`--format=${resultFormat}`,
|
|
147
|
-
'--max-count=1',
|
|
148
|
-
age === 'oldest' ? '--follow --diff-filter=A' : undefined,
|
|
149
|
-
]
|
|
150
|
-
.filter(Boolean)
|
|
151
|
-
.join(' ');
|
|
152
|
-
|
|
153
|
-
const command = `git -c log.showSignature=false log ${args} -- "${path.basename(
|
|
154
|
-
file,
|
|
155
|
-
)}"`;
|
|
156
|
-
|
|
157
|
-
const result = (await GitCommandQueue.add(() => {
|
|
158
|
-
return execa(command, {
|
|
159
|
-
cwd: path.dirname(file),
|
|
160
|
-
shell: true,
|
|
161
|
-
});
|
|
162
|
-
}))!;
|
|
163
|
-
|
|
164
|
-
if (result.exitCode !== 0) {
|
|
165
|
-
throw new Error(
|
|
166
|
-
`Failed to retrieve the git history for file "${file}" with exit code ${result.exitCode}: ${result.stderr}`,
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// We only parse the output line starting with our "RESULT:" prefix
|
|
171
|
-
// See why https://github.com/facebook/docusaurus/pull/10022
|
|
172
|
-
const regex = includeAuthor
|
|
173
|
-
? /(?:^|\n)RESULT:(?<timestamp>\d+),(?<author>.+)(?:$|\n)/
|
|
174
|
-
: /(?:^|\n)RESULT:(?<timestamp>\d+)(?:$|\n)/;
|
|
175
|
-
|
|
176
|
-
const output = result.stdout.trim();
|
|
177
|
-
|
|
178
|
-
if (!output) {
|
|
179
|
-
throw new FileNotTrackedError(
|
|
180
|
-
`Failed to retrieve the git history for file "${file}" because the file is not tracked by git.`,
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const match = output.match(regex);
|
|
185
|
-
|
|
186
|
-
if (!match) {
|
|
187
|
-
throw new Error(
|
|
188
|
-
`Failed to retrieve the git history for file "${file}" with unexpected output: ${output}`,
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const timestampInSeconds = Number(match.groups!.timestamp);
|
|
193
|
-
const timestamp = timestampInSeconds * 1_000;
|
|
194
|
-
const date = new Date(timestamp);
|
|
195
|
-
|
|
196
|
-
if (includeAuthor) {
|
|
197
|
-
return {date, timestamp, author: match.groups!.author!};
|
|
198
|
-
}
|
|
199
|
-
return {date, timestamp};
|
|
200
|
-
}
|