mirror of
https://github.com/facebook/docusaurus.git
synced 2025-04-29 10:17:55 +02:00
perf(v2): skip runtime fileHash cache in prod & get timestamp asynchronously (#1951)
* skip hash calculation in prod when generating file * perf: convert lastUpdated to be async process * changelog
This commit is contained in:
parent
e04c8f140f
commit
639d8d3eac
8 changed files with 107 additions and 42 deletions
|
@ -3,6 +3,8 @@
|
|||
## Unreleased
|
||||
- More efficient hot reload & consistent generated file.
|
||||
- Set babel `compact` options to `true` which removes "superfluous whitespace characters and line terminators.
|
||||
- Skip fileHash caching on production calculation to improve build performance. The runtime cache is only useful in development.
|
||||
- Obtain git timestamp for `showLastUpdateAuthor`/ `showLastUpdateTime` asynchronously instead of synchronously. This improves build performance greatly.
|
||||
|
||||
## 2.0.0-alpha.33
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
"dependencies": {
|
||||
"@docusaurus/mdx-loader": "^2.0.0-alpha.33",
|
||||
"@docusaurus/utils": "^2.0.0-alpha.33",
|
||||
"cross-spawn": "^7.0.1",
|
||||
"execa": "^3.2.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"globby": "^10.0.1",
|
||||
"import-fresh": "^3.1.0",
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import shell from 'shelljs';
|
||||
import spawn from 'cross-spawn';
|
||||
|
||||
import lastUpdate from '../lastUpdate';
|
||||
|
||||
|
@ -17,8 +16,8 @@ describe('lastUpdate', () => {
|
|||
__dirname,
|
||||
'__fixtures__/simple-site/docs/hello.md',
|
||||
);
|
||||
test('existing test file in repository with Git timestamp', () => {
|
||||
const lastUpdateData = lastUpdate(existingFilePath);
|
||||
test('existing test file in repository with Git timestamp', async () => {
|
||||
const lastUpdateData = await lastUpdate(existingFilePath);
|
||||
expect(lastUpdateData).not.toBeNull();
|
||||
|
||||
const {author, timestamp} = lastUpdateData;
|
||||
|
@ -29,28 +28,37 @@ describe('lastUpdate', () => {
|
|||
expect(typeof timestamp).toBe('number');
|
||||
});
|
||||
|
||||
test('non-existing file', () => {
|
||||
test('non-existing file', async () => {
|
||||
const consoleMock = jest.spyOn(console, 'error');
|
||||
consoleMock.mockImplementation();
|
||||
const nonExistingFilePath = path.join(
|
||||
__dirname,
|
||||
'__fixtures__',
|
||||
'.nonExisting',
|
||||
);
|
||||
expect(lastUpdate(null)).toBeNull();
|
||||
expect(lastUpdate(undefined)).toBeNull();
|
||||
expect(lastUpdate(nonExistingFilePath)).toBeNull();
|
||||
expect(await lastUpdate(nonExistingFilePath)).toBeNull();
|
||||
expect(consoleMock).toHaveBeenCalledTimes(1);
|
||||
expect(consoleMock).toHaveBeenCalledWith(
|
||||
new Error(
|
||||
`Command failed with exit code 128: git log -1 --format=%ct, %an ${nonExistingFilePath}`,
|
||||
),
|
||||
);
|
||||
expect(await lastUpdate(null)).toBeNull();
|
||||
expect(await lastUpdate(undefined)).toBeNull();
|
||||
consoleMock.mockRestore();
|
||||
});
|
||||
|
||||
test('temporary created file that has no git timestamp', () => {
|
||||
test('temporary created file that has no git timestamp', async () => {
|
||||
const tempFilePath = path.join(__dirname, '__fixtures__', '.temp');
|
||||
fs.writeFileSync(tempFilePath, 'Lorem ipsum :)');
|
||||
expect(lastUpdate(tempFilePath)).toBeNull();
|
||||
expect(await lastUpdate(tempFilePath)).toBeNull();
|
||||
fs.unlinkSync(tempFilePath);
|
||||
});
|
||||
|
||||
test('Git does not exist', () => {
|
||||
test('Git does not exist', async () => {
|
||||
const mock = jest.spyOn(shell, 'which').mockImplementationOnce(() => null);
|
||||
const consoleMock = jest.spyOn(console, 'warn').mockImplementation();
|
||||
const lastUpdateData = lastUpdate(existingFilePath);
|
||||
const lastUpdateData = await lastUpdate(existingFilePath);
|
||||
expect(lastUpdateData).toBeNull();
|
||||
expect(consoleMock).toHaveBeenLastCalledWith(
|
||||
'Sorry, the docs plugin last update options require Git.',
|
||||
|
@ -59,17 +67,4 @@ describe('lastUpdate', () => {
|
|||
consoleMock.mockRestore();
|
||||
mock.mockRestore();
|
||||
});
|
||||
|
||||
test('Error', () => {
|
||||
const mock = jest.spyOn(spawn, 'sync').mockImplementationOnce(() => {
|
||||
throw new Error('PERMISSION Error');
|
||||
});
|
||||
const consoleMock = jest.spyOn(console, 'error').mockImplementation();
|
||||
const lastUpdateData = lastUpdate('/fake/path/');
|
||||
expect(lastUpdateData).toBeNull();
|
||||
expect(consoleMock).toHaveBeenLastCalledWith(new Error('PERMISSION Error'));
|
||||
|
||||
consoleMock.mockRestore();
|
||||
mock.mockRestore();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import shell from 'shelljs';
|
||||
import spawn from 'cross-spawn';
|
||||
import execa from 'execa';
|
||||
|
||||
type FileLastUpdateData = {timestamp?: number; author?: string};
|
||||
|
||||
|
@ -14,9 +14,12 @@ const GIT_COMMIT_TIMESTAMP_AUTHOR_REGEX = /^(\d+), (.+)$/;
|
|||
|
||||
let showedGitRequirementError = false;
|
||||
|
||||
export default function getFileLastUpdate(
|
||||
filePath: string,
|
||||
): FileLastUpdateData | null {
|
||||
export default async function getFileLastUpdate(
|
||||
filePath?: string,
|
||||
): Promise<FileLastUpdateData | null> {
|
||||
if (!filePath) {
|
||||
return null;
|
||||
}
|
||||
function getTimestampAndAuthor(str: string): FileLastUpdateData | null {
|
||||
if (!str) {
|
||||
return null;
|
||||
|
@ -39,12 +42,13 @@ export default function getFileLastUpdate(
|
|||
return null;
|
||||
}
|
||||
|
||||
const result = spawn
|
||||
.sync('git', ['log', '-1', '--format=%ct, %an', filePath])
|
||||
.stdout.toString()
|
||||
.trim();
|
||||
|
||||
return getTimestampAndAuthor(result);
|
||||
const {stdout} = await execa('git', [
|
||||
'log',
|
||||
'-1',
|
||||
'--format=%ct, %an',
|
||||
filePath,
|
||||
]);
|
||||
return getTimestampAndAuthor(stdout);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
|
|
@ -112,7 +112,7 @@ export default async function processMetadata({
|
|||
// Use fake data in dev for faster development
|
||||
const fileLastUpdateData =
|
||||
process.env.NODE_ENV === 'production'
|
||||
? lastUpdate(filePath)
|
||||
? await lastUpdate(filePath)
|
||||
: {
|
||||
author: 'Author',
|
||||
timestamp: '1539502055',
|
||||
|
|
|
@ -17,8 +17,16 @@ export async function generate(
|
|||
generatedFilesDir: string,
|
||||
file: string,
|
||||
content: any,
|
||||
skipCache: boolean = process.env.NODE_ENV === 'production',
|
||||
): Promise<void> {
|
||||
const filepath = path.join(generatedFilesDir, file);
|
||||
|
||||
if (skipCache) {
|
||||
await fs.ensureDir(path.dirname(filepath));
|
||||
await fs.writeFile(filepath, content);
|
||||
return;
|
||||
}
|
||||
|
||||
const lastHash = fileHash.get(filepath);
|
||||
const currentHash = createHash('md5')
|
||||
.update(content)
|
||||
|
@ -110,7 +118,7 @@ export function genChunkName(
|
|||
modulePath: string,
|
||||
prefix?: string,
|
||||
preferredName?: string,
|
||||
shortId?: boolean,
|
||||
shortId: boolean = process.env.NODE_ENV === 'production',
|
||||
): string {
|
||||
let chunkName: string | undefined = chunkNameCache.get(modulePath);
|
||||
if (!chunkName) {
|
||||
|
|
|
@ -24,7 +24,6 @@ function getModulePath(target: Module): string {
|
|||
}
|
||||
|
||||
export async function loadRoutes(pluginsRouteConfigs: RouteConfig[]) {
|
||||
const isProd = process.env.NODE_ENV === 'production';
|
||||
const routesImports = [
|
||||
`import React from 'react';`,
|
||||
`import ComponentCreator from '@docusaurus/ComponentCreator';`,
|
||||
|
@ -83,7 +82,7 @@ export async function loadRoutes(pluginsRouteConfigs: RouteConfig[]) {
|
|||
}
|
||||
|
||||
const modulePath = getModulePath(value as Module);
|
||||
const chunkName = genChunkName(modulePath, prefix, name, isProd);
|
||||
const chunkName = genChunkName(modulePath, prefix, name);
|
||||
const loader = `() => import(/* webpackChunkName: '${chunkName}' */ ${JSON.stringify(
|
||||
modulePath,
|
||||
)})`;
|
||||
|
|
63
yarn.lock
63
yarn.lock
|
@ -5288,7 +5288,7 @@ cross-spawn@^5.0.1, cross-spawn@^5.1.0:
|
|||
shebang-command "^1.2.0"
|
||||
which "^1.2.9"
|
||||
|
||||
cross-spawn@^7.0.1:
|
||||
cross-spawn@^7.0.0:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14"
|
||||
integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==
|
||||
|
@ -6722,6 +6722,22 @@ execa@^1.0.0:
|
|||
signal-exit "^3.0.0"
|
||||
strip-eof "^1.0.0"
|
||||
|
||||
execa@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/execa/-/execa-3.2.0.tgz#18326b79c7ab7fbd6610fd900c1b9e95fa48f90a"
|
||||
integrity sha512-kJJfVbI/lZE1PZYDI5VPxp8zXPO9rtxOkhpZ0jMKha56AI9y2gGVC6bkukStQf0ka5Rh15BA5m7cCCH4jmHqkw==
|
||||
dependencies:
|
||||
cross-spawn "^7.0.0"
|
||||
get-stream "^5.0.0"
|
||||
human-signals "^1.1.1"
|
||||
is-stream "^2.0.0"
|
||||
merge-stream "^2.0.0"
|
||||
npm-run-path "^4.0.0"
|
||||
onetime "^5.1.0"
|
||||
p-finally "^2.0.0"
|
||||
signal-exit "^3.0.2"
|
||||
strip-final-newline "^2.0.0"
|
||||
|
||||
executable@^4.1.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c"
|
||||
|
@ -7519,6 +7535,13 @@ get-stream@^4.0.0, get-stream@^4.1.0:
|
|||
dependencies:
|
||||
pump "^3.0.0"
|
||||
|
||||
get-stream@^5.0.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9"
|
||||
integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==
|
||||
dependencies:
|
||||
pump "^3.0.0"
|
||||
|
||||
get-value@^2.0.3, get-value@^2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
|
||||
|
@ -8291,6 +8314,11 @@ https-proxy-agent@^2.2.1:
|
|||
agent-base "^4.1.0"
|
||||
debug "^3.1.0"
|
||||
|
||||
human-signals@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
|
||||
integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
|
||||
|
||||
humanize-ms@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
|
||||
|
@ -9037,6 +9065,11 @@ is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0:
|
|||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
|
||||
|
||||
is-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
|
||||
integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==
|
||||
|
||||
is-string@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.4.tgz#cc3a9b69857d621e963725a24caeec873b826e64"
|
||||
|
@ -10694,7 +10727,7 @@ mimic-fn@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
|
||||
integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
|
||||
|
||||
mimic-fn@^2.0.0:
|
||||
mimic-fn@^2.0.0, mimic-fn@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||
|
@ -11336,6 +11369,13 @@ npm-run-path@^2.0.0:
|
|||
dependencies:
|
||||
path-key "^2.0.0"
|
||||
|
||||
npm-run-path@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.0.tgz#d644ec1bd0569187d2a52909971023a0a58e8438"
|
||||
integrity sha512-8eyAOAH+bYXFPSnNnKr3J+yoybe8O87Is5rtAQ8qRczJz1ajcsjg8l2oZqP+Ppx15Ii3S1vUTjQN2h4YO2tWWQ==
|
||||
dependencies:
|
||||
path-key "^3.0.0"
|
||||
|
||||
npm-which@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/npm-which/-/npm-which-3.0.1.tgz#9225f26ec3a285c209cae67c3b11a6b4ab7140aa"
|
||||
|
@ -11527,6 +11567,13 @@ onetime@^2.0.0:
|
|||
dependencies:
|
||||
mimic-fn "^1.0.0"
|
||||
|
||||
onetime@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5"
|
||||
integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==
|
||||
dependencies:
|
||||
mimic-fn "^2.1.0"
|
||||
|
||||
open@^6.3.0:
|
||||
version "6.4.0"
|
||||
resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9"
|
||||
|
@ -11678,6 +11725,11 @@ p-finally@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
|
||||
integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
|
||||
|
||||
p-finally@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561"
|
||||
integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==
|
||||
|
||||
p-is-promise@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e"
|
||||
|
@ -11994,7 +12046,7 @@ path-key@^2.0.0, path-key@^2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
|
||||
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
|
||||
|
||||
path-key@^3.1.0:
|
||||
path-key@^3.0.0, path-key@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.0.tgz#99a10d870a803bdd5ee6f0470e58dfcd2f9a54d3"
|
||||
integrity sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg==
|
||||
|
@ -15093,6 +15145,11 @@ strip-eof@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
|
||||
integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
|
||||
|
||||
strip-final-newline@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
|
||||
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
|
||||
|
||||
strip-indent@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2"
|
||||
|
|
Loading…
Add table
Reference in a new issue