mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-10 15:47:23 +02:00
refactor(core): improve dev perf, fine-grained site reloads - part 3 (#9975)
This commit is contained in:
parent
06e70a4f9a
commit
efbe474e9c
22 changed files with 359 additions and 286 deletions
1
packages/docusaurus-types/src/context.d.ts
vendored
1
packages/docusaurus-types/src/context.d.ts
vendored
|
@ -31,6 +31,7 @@ export type GlobalData = {[pluginName: string]: {[pluginId: string]: unknown}};
|
||||||
|
|
||||||
export type LoadContext = {
|
export type LoadContext = {
|
||||||
siteDir: string;
|
siteDir: string;
|
||||||
|
siteVersion: string | undefined;
|
||||||
generatedFilesDir: string;
|
generatedFilesDir: string;
|
||||||
siteConfig: DocusaurusConfig;
|
siteConfig: DocusaurusConfig;
|
||||||
siteConfigPath: string;
|
siteConfigPath: string;
|
||||||
|
|
3
packages/docusaurus-types/src/plugin.d.ts
vendored
3
packages/docusaurus-types/src/plugin.d.ts
vendored
|
@ -5,7 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {TranslationFile} from './i18n';
|
import type {CodeTranslations, TranslationFile} from './i18n';
|
||||||
import type {RuleSetRule, Configuration as WebpackConfiguration} from 'webpack';
|
import type {RuleSetRule, Configuration as WebpackConfiguration} from 'webpack';
|
||||||
import type {CustomizeRuleString} from 'webpack-merge/dist/types';
|
import type {CustomizeRuleString} from 'webpack-merge/dist/types';
|
||||||
import type {CommanderStatic} from 'commander';
|
import type {CommanderStatic} from 'commander';
|
||||||
|
@ -185,6 +185,7 @@ export type LoadedPlugin = InitializedPlugin & {
|
||||||
readonly content: unknown;
|
readonly content: unknown;
|
||||||
readonly globalData: unknown;
|
readonly globalData: unknown;
|
||||||
readonly routes: RouteConfig[];
|
readonly routes: RouteConfig[];
|
||||||
|
readonly defaultCodeTranslations: CodeTranslations;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PluginModule<Content = unknown> = {
|
export type PluginModule<Content = unknown> = {
|
||||||
|
|
|
@ -12,6 +12,10 @@ import {findAsyncSequential} from './jsUtils';
|
||||||
|
|
||||||
const fileHash = new Map<string, string>();
|
const fileHash = new Map<string, string>();
|
||||||
|
|
||||||
|
const hashContent = (content: string): string => {
|
||||||
|
return createHash('md5').update(content).digest('hex');
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Outputs a file to the generated files directory. Only writes files if content
|
* Outputs a file to the generated files directory. Only writes files if content
|
||||||
* differs from cache (for hot reload performance).
|
* differs from cache (for hot reload performance).
|
||||||
|
@ -38,7 +42,7 @@ export async function generate(
|
||||||
// first "A" remains in cache. But if the file never existed in cache, no
|
// first "A" remains in cache. But if the file never existed in cache, no
|
||||||
// need to register it.
|
// need to register it.
|
||||||
if (fileHash.get(filepath)) {
|
if (fileHash.get(filepath)) {
|
||||||
fileHash.set(filepath, createHash('md5').update(content).digest('hex'));
|
fileHash.set(filepath, hashContent(content));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -50,11 +54,11 @@ export async function generate(
|
||||||
// overwriting and we can reuse old file.
|
// overwriting and we can reuse old file.
|
||||||
if (!lastHash && (await fs.pathExists(filepath))) {
|
if (!lastHash && (await fs.pathExists(filepath))) {
|
||||||
const lastContent = await fs.readFile(filepath, 'utf8');
|
const lastContent = await fs.readFile(filepath, 'utf8');
|
||||||
lastHash = createHash('md5').update(lastContent).digest('hex');
|
lastHash = hashContent(lastContent);
|
||||||
fileHash.set(filepath, lastHash);
|
fileHash.set(filepath, lastHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentHash = createHash('md5').update(content).digest('hex');
|
const currentHash = hashContent(content);
|
||||||
|
|
||||||
if (lastHash !== currentHash) {
|
if (lastHash !== currentHash) {
|
||||||
await fs.outputFile(filepath, content);
|
await fs.outputFile(filepath, content);
|
||||||
|
|
|
@ -6,7 +6,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import shell from 'shelljs';
|
import fs from 'fs-extra';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import shell from 'shelljs'; // TODO replace with async-first version
|
||||||
|
|
||||||
|
const realHasGitFn = () => !!shell.which('git');
|
||||||
|
|
||||||
|
// The hasGit call is synchronous IO so we memoize it
|
||||||
|
// The user won't install Git in the middle of a build anyway...
|
||||||
|
const hasGit =
|
||||||
|
process.env.NODE_ENV === 'test' ? realHasGitFn : _.memoize(realHasGitFn);
|
||||||
|
|
||||||
/** Custom error thrown when git is not found in `PATH`. */
|
/** Custom error thrown when git is not found in `PATH`. */
|
||||||
export class GitNotFoundError extends Error {}
|
export class GitNotFoundError extends Error {}
|
||||||
|
@ -86,13 +95,13 @@ export async function getFileCommitDate(
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
author?: string;
|
author?: string;
|
||||||
}> {
|
}> {
|
||||||
if (!shell.which('git')) {
|
if (!hasGit()) {
|
||||||
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.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!shell.test('-f', file)) {
|
if (!(await fs.pathExists(file))) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to retrieve git history for "${file}" because the file does not exist.`,
|
`Failed to retrieve git history for "${file}" because the file does not exist.`,
|
||||||
);
|
);
|
||||||
|
|
|
@ -64,23 +64,15 @@ export async function build(
|
||||||
process.on(sig, () => process.exit());
|
process.on(sig, () => process.exit());
|
||||||
});
|
});
|
||||||
|
|
||||||
async function tryToBuildLocale({
|
async function tryToBuildLocale({locale}: {locale: string}) {
|
||||||
locale,
|
|
||||||
isLastLocale,
|
|
||||||
}: {
|
|
||||||
locale: string;
|
|
||||||
isLastLocale: boolean;
|
|
||||||
}) {
|
|
||||||
try {
|
try {
|
||||||
PerfLogger.start(`Building site for locale ${locale}`);
|
await PerfLogger.async(`${logger.name(locale)}`, () =>
|
||||||
await buildLocale({
|
buildLocale({
|
||||||
siteDir,
|
siteDir,
|
||||||
locale,
|
locale,
|
||||||
cliOptions,
|
cliOptions,
|
||||||
forceTerminate,
|
}),
|
||||||
isLastLocale,
|
);
|
||||||
});
|
|
||||||
PerfLogger.end(`Building site for locale ${locale}`);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
logger.interpolate`Unable to build website for locale name=${locale}.`,
|
logger.interpolate`Unable to build website for locale name=${locale}.`,
|
||||||
|
@ -91,20 +83,28 @@ export async function build(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PerfLogger.start(`Get locales to build`);
|
const locales = await PerfLogger.async('Get locales to build', () =>
|
||||||
const locales = await getLocalesToBuild({siteDir, cliOptions});
|
getLocalesToBuild({siteDir, cliOptions}),
|
||||||
PerfLogger.end(`Get locales to build`);
|
);
|
||||||
|
|
||||||
if (locales.length > 1) {
|
if (locales.length > 1) {
|
||||||
logger.info`Website will be built for all these locales: ${locales}`;
|
logger.info`Website will be built for all these locales: ${locales}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
PerfLogger.start(`Building ${locales.length} locales`);
|
await PerfLogger.async(`Build`, () =>
|
||||||
await mapAsyncSequential(locales, (locale) => {
|
mapAsyncSequential(locales, async (locale) => {
|
||||||
const isLastLocale = locales.indexOf(locale) === locales.length - 1;
|
const isLastLocale = locales.indexOf(locale) === locales.length - 1;
|
||||||
return tryToBuildLocale({locale, isLastLocale});
|
await tryToBuildLocale({locale});
|
||||||
});
|
if (isLastLocale) {
|
||||||
PerfLogger.end(`Building ${locales.length} locales`);
|
logger.info`Use code=${'npm run serve'} command to test your build locally.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO do we really need this historical forceTerminate exit???
|
||||||
|
if (forceTerminate && isLastLocale && !cliOptions.bundleAnalyzer) {
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getLocalesToBuild({
|
async function getLocalesToBuild({
|
||||||
|
@ -144,14 +144,10 @@ async function buildLocale({
|
||||||
siteDir,
|
siteDir,
|
||||||
locale,
|
locale,
|
||||||
cliOptions,
|
cliOptions,
|
||||||
forceTerminate,
|
|
||||||
isLastLocale,
|
|
||||||
}: {
|
}: {
|
||||||
siteDir: string;
|
siteDir: string;
|
||||||
locale: string;
|
locale: string;
|
||||||
cliOptions: Partial<BuildCLIOptions>;
|
cliOptions: Partial<BuildCLIOptions>;
|
||||||
forceTerminate: boolean;
|
|
||||||
isLastLocale: boolean;
|
|
||||||
}): Promise<string> {
|
}): Promise<string> {
|
||||||
// Temporary workaround to unlock the ability to translate the site config
|
// Temporary workaround to unlock the ability to translate the site config
|
||||||
// We'll remove it if a better official API can be designed
|
// We'll remove it if a better official API can be designed
|
||||||
|
@ -160,81 +156,66 @@ async function buildLocale({
|
||||||
|
|
||||||
logger.info`name=${`[${locale}]`} Creating an optimized production build...`;
|
logger.info`name=${`[${locale}]`} Creating an optimized production build...`;
|
||||||
|
|
||||||
PerfLogger.start('Loading site');
|
const site = await PerfLogger.async('Load site', () =>
|
||||||
const site = await loadSite({
|
loadSite({
|
||||||
siteDir,
|
siteDir,
|
||||||
outDir: cliOptions.outDir,
|
outDir: cliOptions.outDir,
|
||||||
config: cliOptions.config,
|
config: cliOptions.config,
|
||||||
locale,
|
locale,
|
||||||
localizePath: cliOptions.locale ? false : undefined,
|
localizePath: cliOptions.locale ? false : undefined,
|
||||||
});
|
}),
|
||||||
PerfLogger.end('Loading site');
|
);
|
||||||
|
|
||||||
const {props} = site;
|
const {props} = site;
|
||||||
const {outDir, plugins} = props;
|
const {outDir, plugins} = props;
|
||||||
|
|
||||||
// We can build the 2 configs in parallel
|
// We can build the 2 configs in parallel
|
||||||
PerfLogger.start('Creating webpack configs');
|
|
||||||
const [{clientConfig, clientManifestPath}, {serverConfig, serverBundlePath}] =
|
const [{clientConfig, clientManifestPath}, {serverConfig, serverBundlePath}] =
|
||||||
await Promise.all([
|
await PerfLogger.async('Creating webpack configs', () =>
|
||||||
getBuildClientConfig({
|
Promise.all([
|
||||||
props,
|
getBuildClientConfig({
|
||||||
cliOptions,
|
props,
|
||||||
}),
|
cliOptions,
|
||||||
getBuildServerConfig({
|
}),
|
||||||
props,
|
getBuildServerConfig({
|
||||||
}),
|
props,
|
||||||
]);
|
}),
|
||||||
PerfLogger.end('Creating webpack configs');
|
]),
|
||||||
|
);
|
||||||
// Make sure generated client-manifest is cleaned first, so we don't reuse
|
|
||||||
// the one from previous builds.
|
|
||||||
// TODO do we really need this? .docusaurus folder is cleaned between builds
|
|
||||||
PerfLogger.start('Deleting previous client manifest');
|
|
||||||
await ensureUnlink(clientManifestPath);
|
|
||||||
PerfLogger.end('Deleting previous client manifest');
|
|
||||||
|
|
||||||
// Run webpack to build JS bundle (client) and static html files (server).
|
// Run webpack to build JS bundle (client) and static html files (server).
|
||||||
PerfLogger.start('Bundling');
|
await PerfLogger.async('Bundling with Webpack', () =>
|
||||||
await compile([clientConfig, serverConfig]);
|
compile([clientConfig, serverConfig]),
|
||||||
PerfLogger.end('Bundling');
|
);
|
||||||
|
|
||||||
PerfLogger.start('Executing static site generation');
|
const {collectedData} = await PerfLogger.async('SSG', () =>
|
||||||
const {collectedData} = await executeSSG({
|
executeSSG({
|
||||||
props,
|
props,
|
||||||
serverBundlePath,
|
serverBundlePath,
|
||||||
clientManifestPath,
|
clientManifestPath,
|
||||||
});
|
}),
|
||||||
PerfLogger.end('Executing static site generation');
|
);
|
||||||
|
|
||||||
// Remove server.bundle.js because it is not needed.
|
// Remove server.bundle.js because it is not needed.
|
||||||
PerfLogger.start('Deleting server bundle');
|
await PerfLogger.async('Deleting server bundle', () =>
|
||||||
await ensureUnlink(serverBundlePath);
|
ensureUnlink(serverBundlePath),
|
||||||
PerfLogger.end('Deleting server bundle');
|
);
|
||||||
|
|
||||||
// Plugin Lifecycle - postBuild.
|
// Plugin Lifecycle - postBuild.
|
||||||
PerfLogger.start('Executing postBuild()');
|
await PerfLogger.async('postBuild()', () =>
|
||||||
await executePluginsPostBuild({plugins, props, collectedData});
|
executePluginsPostBuild({plugins, props, collectedData}),
|
||||||
PerfLogger.end('Executing postBuild()');
|
);
|
||||||
|
|
||||||
// TODO execute this in parallel to postBuild?
|
// TODO execute this in parallel to postBuild?
|
||||||
PerfLogger.start('Executing broken links checker');
|
await PerfLogger.async('Broken links checker', () =>
|
||||||
await executeBrokenLinksCheck({props, collectedData});
|
executeBrokenLinksCheck({props, collectedData}),
|
||||||
PerfLogger.end('Executing broken links checker');
|
);
|
||||||
|
|
||||||
logger.success`Generated static files in path=${path.relative(
|
logger.success`Generated static files in path=${path.relative(
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
outDir,
|
outDir,
|
||||||
)}.`;
|
)}.`;
|
||||||
|
|
||||||
if (isLastLocale) {
|
|
||||||
logger.info`Use code=${'npm run serve'} command to test your build locally.`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forceTerminate && isLastLocale && !cliOptions.bundleAnalyzer) {
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return outDir;
|
return outDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,40 +228,39 @@ async function executeSSG({
|
||||||
serverBundlePath: string;
|
serverBundlePath: string;
|
||||||
clientManifestPath: string;
|
clientManifestPath: string;
|
||||||
}) {
|
}) {
|
||||||
PerfLogger.start('Reading client manifest');
|
const manifest: Manifest = await PerfLogger.async(
|
||||||
const manifest: Manifest = await fs.readJSON(clientManifestPath, 'utf-8');
|
'Read client manifest',
|
||||||
PerfLogger.end('Reading client manifest');
|
() => fs.readJSON(clientManifestPath, 'utf-8'),
|
||||||
|
|
||||||
PerfLogger.start('Compiling SSR template');
|
|
||||||
const ssrTemplate = await compileSSRTemplate(
|
|
||||||
props.siteConfig.ssrTemplate ?? defaultSSRTemplate,
|
|
||||||
);
|
);
|
||||||
PerfLogger.end('Compiling SSR template');
|
|
||||||
|
|
||||||
PerfLogger.start('Loading App renderer');
|
const ssrTemplate = await PerfLogger.async('Compile SSR template', () =>
|
||||||
const renderer = await loadAppRenderer({
|
compileSSRTemplate(props.siteConfig.ssrTemplate ?? defaultSSRTemplate),
|
||||||
serverBundlePath,
|
);
|
||||||
});
|
|
||||||
PerfLogger.end('Loading App renderer');
|
|
||||||
|
|
||||||
PerfLogger.start('Generate static files');
|
const renderer = await PerfLogger.async('Load App renderer', () =>
|
||||||
const ssgResult = await generateStaticFiles({
|
loadAppRenderer({
|
||||||
pathnames: props.routesPaths,
|
serverBundlePath,
|
||||||
renderer,
|
}),
|
||||||
params: {
|
);
|
||||||
trailingSlash: props.siteConfig.trailingSlash,
|
|
||||||
outDir: props.outDir,
|
const ssgResult = await PerfLogger.async('Generate static files', () =>
|
||||||
baseUrl: props.baseUrl,
|
generateStaticFiles({
|
||||||
manifest,
|
pathnames: props.routesPaths,
|
||||||
headTags: props.headTags,
|
renderer,
|
||||||
preBodyTags: props.preBodyTags,
|
params: {
|
||||||
postBodyTags: props.postBodyTags,
|
trailingSlash: props.siteConfig.trailingSlash,
|
||||||
ssrTemplate,
|
outDir: props.outDir,
|
||||||
noIndex: props.siteConfig.noIndex,
|
baseUrl: props.baseUrl,
|
||||||
DOCUSAURUS_VERSION,
|
manifest,
|
||||||
},
|
headTags: props.headTags,
|
||||||
});
|
preBodyTags: props.preBodyTags,
|
||||||
PerfLogger.end('Generate static files');
|
postBodyTags: props.postBodyTags,
|
||||||
|
ssrTemplate,
|
||||||
|
noIndex: props.siteConfig.noIndex,
|
||||||
|
DOCUSAURUS_VERSION,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
return ssgResult;
|
return ssgResult;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
reloadSite,
|
reloadSite,
|
||||||
reloadSitePlugin,
|
reloadSitePlugin,
|
||||||
} from '../../server/site';
|
} from '../../server/site';
|
||||||
|
import {formatPluginName} from '../../server/plugins/pluginsUtils';
|
||||||
import type {StartCLIOptions} from './start';
|
import type {StartCLIOptions} from './start';
|
||||||
import type {LoadedPlugin} from '@docusaurus/types';
|
import type {LoadedPlugin} from '@docusaurus/types';
|
||||||
|
|
||||||
|
@ -69,10 +70,13 @@ async function createLoadSiteParams({
|
||||||
export async function createReloadableSite(startParams: StartParams) {
|
export async function createReloadableSite(startParams: StartParams) {
|
||||||
const openUrlContext = await createOpenUrlContext(startParams);
|
const openUrlContext = await createOpenUrlContext(startParams);
|
||||||
|
|
||||||
let site = await PerfLogger.async('Loading site', async () => {
|
const loadSiteParams = await PerfLogger.async('createLoadSiteParams', () =>
|
||||||
const params = await createLoadSiteParams(startParams);
|
createLoadSiteParams(startParams),
|
||||||
return loadSite(params);
|
);
|
||||||
});
|
|
||||||
|
let site = await PerfLogger.async('Load site', () =>
|
||||||
|
loadSite(loadSiteParams),
|
||||||
|
);
|
||||||
|
|
||||||
const get = () => site;
|
const get = () => site;
|
||||||
|
|
||||||
|
@ -89,7 +93,7 @@ export async function createReloadableSite(startParams: StartParams) {
|
||||||
const reloadBase = async () => {
|
const reloadBase = async () => {
|
||||||
try {
|
try {
|
||||||
const oldSite = site;
|
const oldSite = site;
|
||||||
site = await PerfLogger.async('Reloading site', () => reloadSite(site));
|
site = await PerfLogger.async('Reload site', () => reloadSite(site));
|
||||||
if (oldSite.props.baseUrl !== site.props.baseUrl) {
|
if (oldSite.props.baseUrl !== site.props.baseUrl) {
|
||||||
printOpenUrlMessage();
|
printOpenUrlMessage();
|
||||||
}
|
}
|
||||||
|
@ -108,7 +112,7 @@ export async function createReloadableSite(startParams: StartParams) {
|
||||||
const reloadPlugin = async (plugin: LoadedPlugin) => {
|
const reloadPlugin = async (plugin: LoadedPlugin) => {
|
||||||
try {
|
try {
|
||||||
site = await PerfLogger.async(
|
site = await PerfLogger.async(
|
||||||
`Reloading site plugin ${plugin.name}@${plugin.options.id}`,
|
`Reload site plugin ${formatPluginName(plugin)}`,
|
||||||
() => {
|
() => {
|
||||||
const pluginIdentifier = {name: plugin.name, id: plugin.options.id};
|
const pluginIdentifier = {name: plugin.name, id: plugin.options.id};
|
||||||
return reloadSitePlugin(site, pluginIdentifier);
|
return reloadSitePlugin(site, pluginIdentifier);
|
||||||
|
@ -116,7 +120,7 @@ export async function createReloadableSite(startParams: StartParams) {
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`Site plugin reload failure - Plugin ${plugin.name}@${plugin.options.id}`,
|
`Site plugin reload failure - Plugin ${formatPluginName(plugin)}`,
|
||||||
);
|
);
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
writePluginTranslations,
|
writePluginTranslations,
|
||||||
writeCodeTranslations,
|
writeCodeTranslations,
|
||||||
type WriteTranslationsOptions,
|
type WriteTranslationsOptions,
|
||||||
getPluginsDefaultCodeTranslationMessages,
|
loadPluginsDefaultCodeTranslationMessages,
|
||||||
applyDefaultCodeTranslations,
|
applyDefaultCodeTranslations,
|
||||||
} from '../server/translations/translations';
|
} from '../server/translations/translations';
|
||||||
import {
|
import {
|
||||||
|
@ -114,7 +114,7 @@ Available locales are: ${context.i18n.locales.join(',')}.`,
|
||||||
await getExtraSourceCodeFilePaths(),
|
await getExtraSourceCodeFilePaths(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const defaultCodeMessages = await getPluginsDefaultCodeTranslationMessages(
|
const defaultCodeMessages = await loadPluginsDefaultCodeTranslationMessages(
|
||||||
plugins,
|
plugins,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ exports[`load loads props for site with custom i18n path 1`] = `
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
"content": undefined,
|
"content": undefined,
|
||||||
|
"defaultCodeTranslations": {},
|
||||||
"getClientModules": [Function],
|
"getClientModules": [Function],
|
||||||
"globalData": undefined,
|
"globalData": undefined,
|
||||||
"injectHtmlTags": [Function],
|
"injectHtmlTags": [Function],
|
||||||
|
@ -52,6 +53,7 @@ exports[`load loads props for site with custom i18n path 1`] = `
|
||||||
{
|
{
|
||||||
"configureWebpack": [Function],
|
"configureWebpack": [Function],
|
||||||
"content": undefined,
|
"content": undefined,
|
||||||
|
"defaultCodeTranslations": {},
|
||||||
"globalData": undefined,
|
"globalData": undefined,
|
||||||
"name": "docusaurus-mdx-fallback-plugin",
|
"name": "docusaurus-mdx-fallback-plugin",
|
||||||
"options": {
|
"options": {
|
||||||
|
@ -132,5 +134,6 @@ exports[`load loads props for site with custom i18n path 1`] = `
|
||||||
"pluginVersions": {},
|
"pluginVersions": {},
|
||||||
"siteVersion": undefined,
|
"siteVersion": undefined,
|
||||||
},
|
},
|
||||||
|
"siteVersion": undefined,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {loadClientModules} from '../clientModules';
|
import {getAllClientModules} from '../clientModules';
|
||||||
import type {LoadedPlugin} from '@docusaurus/types';
|
import type {LoadedPlugin} from '@docusaurus/types';
|
||||||
|
|
||||||
const pluginEmpty = {
|
const pluginEmpty = {
|
||||||
|
@ -33,14 +33,14 @@ const pluginHelloWorld = {
|
||||||
},
|
},
|
||||||
} as unknown as LoadedPlugin;
|
} as unknown as LoadedPlugin;
|
||||||
|
|
||||||
describe('loadClientModules', () => {
|
describe('getAllClientModules', () => {
|
||||||
it('loads an empty plugin', () => {
|
it('loads an empty plugin', () => {
|
||||||
const clientModules = loadClientModules([pluginEmpty]);
|
const clientModules = getAllClientModules([pluginEmpty]);
|
||||||
expect(clientModules).toMatchInlineSnapshot(`[]`);
|
expect(clientModules).toMatchInlineSnapshot(`[]`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads a non-empty plugin', () => {
|
it('loads a non-empty plugin', () => {
|
||||||
const clientModules = loadClientModules([pluginFooBar]);
|
const clientModules = getAllClientModules([pluginFooBar]);
|
||||||
expect(clientModules).toMatchInlineSnapshot(`
|
expect(clientModules).toMatchInlineSnapshot(`
|
||||||
[
|
[
|
||||||
"<PROJECT_ROOT>/packages/docusaurus/src/server/__tests__/foo",
|
"<PROJECT_ROOT>/packages/docusaurus/src/server/__tests__/foo",
|
||||||
|
@ -50,7 +50,7 @@ describe('loadClientModules', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads multiple non-empty plugins', () => {
|
it('loads multiple non-empty plugins', () => {
|
||||||
const clientModules = loadClientModules([pluginFooBar, pluginHelloWorld]);
|
const clientModules = getAllClientModules([pluginFooBar, pluginHelloWorld]);
|
||||||
expect(clientModules).toMatchInlineSnapshot(`
|
expect(clientModules).toMatchInlineSnapshot(`
|
||||||
[
|
[
|
||||||
"<PROJECT_ROOT>/packages/docusaurus/src/server/__tests__/foo",
|
"<PROJECT_ROOT>/packages/docusaurus/src/server/__tests__/foo",
|
||||||
|
@ -62,7 +62,7 @@ describe('loadClientModules', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads multiple non-empty plugins in different order', () => {
|
it('loads multiple non-empty plugins in different order', () => {
|
||||||
const clientModules = loadClientModules([pluginHelloWorld, pluginFooBar]);
|
const clientModules = getAllClientModules([pluginHelloWorld, pluginFooBar]);
|
||||||
expect(clientModules).toMatchInlineSnapshot(`
|
expect(clientModules).toMatchInlineSnapshot(`
|
||||||
[
|
[
|
||||||
"/hello",
|
"/hello",
|
||||||
|
@ -74,7 +74,7 @@ describe('loadClientModules', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads both empty and non-empty plugins', () => {
|
it('loads both empty and non-empty plugins', () => {
|
||||||
const clientModules = loadClientModules([
|
const clientModules = getAllClientModules([
|
||||||
pluginHelloWorld,
|
pluginHelloWorld,
|
||||||
pluginEmpty,
|
pluginEmpty,
|
||||||
pluginFooBar,
|
pluginFooBar,
|
||||||
|
@ -90,7 +90,7 @@ describe('loadClientModules', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads empty and non-empty in a different order', () => {
|
it('loads empty and non-empty in a different order', () => {
|
||||||
const clientModules = loadClientModules([
|
const clientModules = getAllClientModules([
|
||||||
pluginHelloWorld,
|
pluginHelloWorld,
|
||||||
pluginFooBar,
|
pluginFooBar,
|
||||||
pluginEmpty,
|
pluginEmpty,
|
||||||
|
|
|
@ -7,13 +7,13 @@
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {DOCUSAURUS_VERSION} from '@docusaurus/utils';
|
import {DOCUSAURUS_VERSION} from '@docusaurus/utils';
|
||||||
import {getPluginVersion, loadSiteMetadata} from '../siteMetadata';
|
import {loadPluginVersion, createSiteMetadata} from '../siteMetadata';
|
||||||
import type {LoadedPlugin} from '@docusaurus/types';
|
import type {LoadedPlugin} from '@docusaurus/types';
|
||||||
|
|
||||||
describe('getPluginVersion', () => {
|
describe('loadPluginVersion', () => {
|
||||||
it('detects external packages plugins versions', async () => {
|
it('detects external packages plugins versions', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
getPluginVersion(
|
loadPluginVersion(
|
||||||
path.join(__dirname, '__fixtures__/siteMetadata/dummy-plugin.js'),
|
path.join(__dirname, '__fixtures__/siteMetadata/dummy-plugin.js'),
|
||||||
// Make the plugin appear external.
|
// Make the plugin appear external.
|
||||||
path.join(__dirname, '..', '..', '..', '..', '..', '..', 'website'),
|
path.join(__dirname, '..', '..', '..', '..', '..', '..', 'website'),
|
||||||
|
@ -23,7 +23,7 @@ describe('getPluginVersion', () => {
|
||||||
|
|
||||||
it('detects project plugins versions', async () => {
|
it('detects project plugins versions', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
getPluginVersion(
|
loadPluginVersion(
|
||||||
path.join(__dirname, '__fixtures__/siteMetadata/dummy-plugin.js'),
|
path.join(__dirname, '__fixtures__/siteMetadata/dummy-plugin.js'),
|
||||||
// Make the plugin appear project local.
|
// Make the plugin appear project local.
|
||||||
path.join(__dirname, '__fixtures__/siteMetadata'),
|
path.join(__dirname, '__fixtures__/siteMetadata'),
|
||||||
|
@ -32,14 +32,14 @@ describe('getPluginVersion', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('detects local packages versions', async () => {
|
it('detects local packages versions', async () => {
|
||||||
await expect(getPluginVersion('/', '/')).resolves.toEqual({type: 'local'});
|
await expect(loadPluginVersion('/', '/')).resolves.toEqual({type: 'local'});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('loadSiteMetadata', () => {
|
describe('createSiteMetadata', () => {
|
||||||
it('throws if plugin versions mismatch', async () => {
|
it('throws if plugin versions mismatch', () => {
|
||||||
await expect(
|
expect(() =>
|
||||||
loadSiteMetadata({
|
createSiteMetadata({
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
name: 'docusaurus-plugin-content-docs',
|
name: 'docusaurus-plugin-content-docs',
|
||||||
|
@ -50,10 +50,9 @@ describe('loadSiteMetadata', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
] as LoadedPlugin[],
|
] as LoadedPlugin[],
|
||||||
siteDir: path.join(__dirname, '__fixtures__/siteMetadata'),
|
siteVersion: 'some-random-version',
|
||||||
}),
|
}),
|
||||||
).rejects
|
).toThrow(`Invalid name=docusaurus-plugin-content-docs version number=1.0.0.
|
||||||
.toThrow(`Invalid name=docusaurus-plugin-content-docs version number=1.0.0.
|
|
||||||
All official @docusaurus/* packages should have the exact same version as @docusaurus/core (number=${DOCUSAURUS_VERSION}).
|
All official @docusaurus/* packages should have the exact same version as @docusaurus/core (number=${DOCUSAURUS_VERSION}).
|
||||||
Maybe you want to check, or regenerate your yarn.lock or package-lock.json file?`);
|
Maybe you want to check, or regenerate your yarn.lock or package-lock.json file?`);
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,7 +12,7 @@ import type {LoadedPlugin} from '@docusaurus/types';
|
||||||
* Runs the `getClientModules` lifecycle. The returned file paths are all
|
* Runs the `getClientModules` lifecycle. The returned file paths are all
|
||||||
* absolute.
|
* absolute.
|
||||||
*/
|
*/
|
||||||
export function loadClientModules(plugins: LoadedPlugin[]): string[] {
|
export function getAllClientModules(plugins: LoadedPlugin[]): string[] {
|
||||||
return plugins.flatMap(
|
return plugins.flatMap(
|
||||||
(plugin) =>
|
(plugin) =>
|
||||||
plugin.getClientModules?.().map((p) => path.resolve(plugin.path, p)) ??
|
plugin.getClientModules?.().map((p) => path.resolve(plugin.path, p)) ??
|
||||||
|
|
|
@ -48,6 +48,7 @@ export async function createPluginActionsUtils({
|
||||||
dataDir,
|
dataDir,
|
||||||
`${docuHash('pluginRouteContextModule')}.json`,
|
`${docuHash('pluginRouteContextModule')}.json`,
|
||||||
);
|
);
|
||||||
|
// TODO not ideal place to generate that file
|
||||||
await generate(
|
await generate(
|
||||||
'/',
|
'/',
|
||||||
pluginRouteContextModulePath,
|
pluginRouteContextModulePath,
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {
|
||||||
normalizePluginOptions,
|
normalizePluginOptions,
|
||||||
normalizeThemeConfig,
|
normalizeThemeConfig,
|
||||||
} from '@docusaurus/utils-validation';
|
} from '@docusaurus/utils-validation';
|
||||||
import {getPluginVersion} from '../siteMetadata';
|
import {loadPluginVersion} from '../siteMetadata';
|
||||||
import {ensureUniquePluginInstanceIds} from './pluginIds';
|
import {ensureUniquePluginInstanceIds} from './pluginIds';
|
||||||
import {loadPluginConfigs, type NormalizedPluginConfig} from './configs';
|
import {loadPluginConfigs, type NormalizedPluginConfig} from './configs';
|
||||||
import type {
|
import type {
|
||||||
|
@ -61,14 +61,14 @@ export async function initPlugins(
|
||||||
const pluginRequire = createRequire(context.siteConfigPath);
|
const pluginRequire = createRequire(context.siteConfigPath);
|
||||||
const pluginConfigs = await loadPluginConfigs(context);
|
const pluginConfigs = await loadPluginConfigs(context);
|
||||||
|
|
||||||
async function doGetPluginVersion(
|
async function doLoadPluginVersion(
|
||||||
normalizedPluginConfig: NormalizedPluginConfig,
|
normalizedPluginConfig: NormalizedPluginConfig,
|
||||||
): Promise<PluginVersionInformation> {
|
): Promise<PluginVersionInformation> {
|
||||||
if (normalizedPluginConfig.pluginModule?.path) {
|
if (normalizedPluginConfig.pluginModule?.path) {
|
||||||
const pluginPath = pluginRequire.resolve(
|
const pluginPath = pluginRequire.resolve(
|
||||||
normalizedPluginConfig.pluginModule.path,
|
normalizedPluginConfig.pluginModule.path,
|
||||||
);
|
);
|
||||||
return getPluginVersion(pluginPath, context.siteDir);
|
return loadPluginVersion(pluginPath, context.siteDir);
|
||||||
}
|
}
|
||||||
return {type: 'local'};
|
return {type: 'local'};
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ export async function initPlugins(
|
||||||
async function initializePlugin(
|
async function initializePlugin(
|
||||||
normalizedPluginConfig: NormalizedPluginConfig,
|
normalizedPluginConfig: NormalizedPluginConfig,
|
||||||
): Promise<InitializedPlugin> {
|
): Promise<InitializedPlugin> {
|
||||||
const pluginVersion: PluginVersionInformation = await doGetPluginVersion(
|
const pluginVersion: PluginVersionInformation = await doLoadPluginVersion(
|
||||||
normalizedPluginConfig,
|
normalizedPluginConfig,
|
||||||
);
|
);
|
||||||
const pluginOptions = doValidatePluginOptions(normalizedPluginConfig);
|
const pluginOptions = doValidatePluginOptions(normalizedPluginConfig);
|
||||||
|
|
|
@ -15,6 +15,7 @@ import {
|
||||||
aggregateAllContent,
|
aggregateAllContent,
|
||||||
aggregateGlobalData,
|
aggregateGlobalData,
|
||||||
aggregateRoutes,
|
aggregateRoutes,
|
||||||
|
formatPluginName,
|
||||||
getPluginByIdentifier,
|
getPluginByIdentifier,
|
||||||
mergeGlobalData,
|
mergeGlobalData,
|
||||||
} from './pluginsUtils';
|
} from './pluginsUtils';
|
||||||
|
@ -73,46 +74,57 @@ async function executePluginContentLoading({
|
||||||
plugin: InitializedPlugin;
|
plugin: InitializedPlugin;
|
||||||
context: LoadContext;
|
context: LoadContext;
|
||||||
}): Promise<LoadedPlugin> {
|
}): Promise<LoadedPlugin> {
|
||||||
return PerfLogger.async(
|
return PerfLogger.async(`Load ${formatPluginName(plugin)}`, async () => {
|
||||||
`Plugins - single plugin content loading - ${plugin.name}@${plugin.options.id}`,
|
let content = await PerfLogger.async('loadContent()', () =>
|
||||||
async () => {
|
plugin.loadContent?.(),
|
||||||
let content = await plugin.loadContent?.();
|
);
|
||||||
|
|
||||||
content = await translatePluginContent({
|
content = await PerfLogger.async('translatePluginContent()', () =>
|
||||||
|
translatePluginContent({
|
||||||
plugin,
|
plugin,
|
||||||
content,
|
content,
|
||||||
context,
|
context,
|
||||||
});
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
if (!plugin.contentLoaded) {
|
const defaultCodeTranslations =
|
||||||
return {
|
(await PerfLogger.async('getDefaultCodeTranslationMessages()', () =>
|
||||||
...plugin,
|
plugin.getDefaultCodeTranslationMessages?.(),
|
||||||
content,
|
)) ?? {};
|
||||||
routes: [],
|
|
||||||
globalData: undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const pluginActionsUtils = await createPluginActionsUtils({
|
|
||||||
plugin,
|
|
||||||
generatedFilesDir: context.generatedFilesDir,
|
|
||||||
baseUrl: context.siteConfig.baseUrl,
|
|
||||||
trailingSlash: context.siteConfig.trailingSlash,
|
|
||||||
});
|
|
||||||
|
|
||||||
await plugin.contentLoaded({
|
|
||||||
content,
|
|
||||||
actions: pluginActionsUtils.getActions(),
|
|
||||||
});
|
|
||||||
|
|
||||||
|
if (!plugin.contentLoaded) {
|
||||||
return {
|
return {
|
||||||
...plugin,
|
...plugin,
|
||||||
content,
|
content,
|
||||||
routes: pluginActionsUtils.getRoutes(),
|
defaultCodeTranslations,
|
||||||
globalData: pluginActionsUtils.getGlobalData(),
|
routes: [],
|
||||||
|
globalData: undefined,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
);
|
|
||||||
|
const pluginActionsUtils = await createPluginActionsUtils({
|
||||||
|
plugin,
|
||||||
|
generatedFilesDir: context.generatedFilesDir,
|
||||||
|
baseUrl: context.siteConfig.baseUrl,
|
||||||
|
trailingSlash: context.siteConfig.trailingSlash,
|
||||||
|
});
|
||||||
|
|
||||||
|
await PerfLogger.async('contentLoaded()', () =>
|
||||||
|
// @ts-expect-error: should autofix with TS 5.4
|
||||||
|
plugin.contentLoaded({
|
||||||
|
content,
|
||||||
|
actions: pluginActionsUtils.getActions(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...plugin,
|
||||||
|
content,
|
||||||
|
defaultCodeTranslations,
|
||||||
|
routes: pluginActionsUtils.getRoutes(),
|
||||||
|
globalData: pluginActionsUtils.getGlobalData(),
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function executeAllPluginsContentLoading({
|
async function executeAllPluginsContentLoading({
|
||||||
|
@ -122,7 +134,7 @@ async function executeAllPluginsContentLoading({
|
||||||
plugins: InitializedPlugin[];
|
plugins: InitializedPlugin[];
|
||||||
context: LoadContext;
|
context: LoadContext;
|
||||||
}): Promise<LoadedPlugin[]> {
|
}): Promise<LoadedPlugin[]> {
|
||||||
return PerfLogger.async(`Plugins - all plugins content loading`, () => {
|
return PerfLogger.async(`Load plugins content`, () => {
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
plugins.map((plugin) => executePluginContentLoading({plugin, context})),
|
plugins.map((plugin) => executePluginContentLoading({plugin, context})),
|
||||||
);
|
);
|
||||||
|
@ -139,7 +151,7 @@ async function executePluginAllContentLoaded({
|
||||||
allContent: AllContent;
|
allContent: AllContent;
|
||||||
}): Promise<{routes: RouteConfig[]; globalData: unknown}> {
|
}): Promise<{routes: RouteConfig[]; globalData: unknown}> {
|
||||||
return PerfLogger.async(
|
return PerfLogger.async(
|
||||||
`Plugins - allContentLoaded - ${plugin.name}@${plugin.options.id}`,
|
`allContentLoaded() - ${formatPluginName(plugin)}`,
|
||||||
async () => {
|
async () => {
|
||||||
if (!plugin.allContentLoaded) {
|
if (!plugin.allContentLoaded) {
|
||||||
return {routes: [], globalData: undefined};
|
return {routes: [], globalData: undefined};
|
||||||
|
@ -171,7 +183,7 @@ async function executeAllPluginsAllContentLoaded({
|
||||||
plugins: LoadedPlugin[];
|
plugins: LoadedPlugin[];
|
||||||
context: LoadContext;
|
context: LoadContext;
|
||||||
}): Promise<AllContentLoadedResult> {
|
}): Promise<AllContentLoadedResult> {
|
||||||
return PerfLogger.async(`Plugins - allContentLoaded`, async () => {
|
return PerfLogger.async(`allContentLoaded()`, async () => {
|
||||||
const allContent = aggregateAllContent(plugins);
|
const allContent = aggregateAllContent(plugins);
|
||||||
|
|
||||||
const routes: RouteConfig[] = [];
|
const routes: RouteConfig[] = [];
|
||||||
|
@ -199,6 +211,9 @@ async function executeAllPluginsAllContentLoaded({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This merges plugins routes and global data created from both lifecycles:
|
||||||
|
// - contentLoaded()
|
||||||
|
// - allContentLoaded()
|
||||||
function mergeResults({
|
function mergeResults({
|
||||||
plugins,
|
plugins,
|
||||||
allContentLoadedResult,
|
allContentLoadedResult,
|
||||||
|
@ -232,9 +247,9 @@ export type LoadPluginsResult = {
|
||||||
export async function loadPlugins(
|
export async function loadPlugins(
|
||||||
context: LoadContext,
|
context: LoadContext,
|
||||||
): Promise<LoadPluginsResult> {
|
): Promise<LoadPluginsResult> {
|
||||||
return PerfLogger.async('Plugins - loadPlugins', async () => {
|
return PerfLogger.async('Load plugins', async () => {
|
||||||
const initializedPlugins: InitializedPlugin[] = await PerfLogger.async(
|
const initializedPlugins: InitializedPlugin[] = await PerfLogger.async(
|
||||||
'Plugins - initPlugins',
|
'Init plugins',
|
||||||
() => initPlugins(context),
|
() => initPlugins(context),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -272,36 +287,39 @@ export async function reloadPlugin({
|
||||||
plugins: LoadedPlugin[];
|
plugins: LoadedPlugin[];
|
||||||
context: LoadContext;
|
context: LoadContext;
|
||||||
}): Promise<LoadPluginsResult> {
|
}): Promise<LoadPluginsResult> {
|
||||||
return PerfLogger.async('Plugins - reloadPlugin', async () => {
|
return PerfLogger.async(
|
||||||
const previousPlugin = getPluginByIdentifier({
|
`Reload plugin ${formatPluginName(pluginIdentifier)}`,
|
||||||
plugins: previousPlugins,
|
async () => {
|
||||||
pluginIdentifier,
|
const previousPlugin = getPluginByIdentifier({
|
||||||
});
|
plugins: previousPlugins,
|
||||||
const plugin = await executePluginContentLoading({
|
pluginIdentifier,
|
||||||
plugin: previousPlugin,
|
});
|
||||||
context,
|
const plugin = await executePluginContentLoading({
|
||||||
});
|
plugin: previousPlugin,
|
||||||
|
context,
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// TODO Docusaurus v4 - upgrade to Node 20, use array.with()
|
// TODO Docusaurus v4 - upgrade to Node 20, use array.with()
|
||||||
const plugins = previousPlugins.with(
|
const plugins = previousPlugins.with(
|
||||||
previousPlugins.indexOf(previousPlugin),
|
previousPlugins.indexOf(previousPlugin),
|
||||||
plugin,
|
plugin,
|
||||||
);
|
);
|
||||||
*/
|
*/
|
||||||
const plugins = [...previousPlugins];
|
const plugins = [...previousPlugins];
|
||||||
plugins[previousPlugins.indexOf(previousPlugin)] = plugin;
|
plugins[previousPlugins.indexOf(previousPlugin)] = plugin;
|
||||||
|
|
||||||
const allContentLoadedResult = await executeAllPluginsAllContentLoaded({
|
const allContentLoadedResult = await executeAllPluginsAllContentLoaded({
|
||||||
plugins,
|
plugins,
|
||||||
context,
|
context,
|
||||||
});
|
});
|
||||||
|
|
||||||
const {routes, globalData} = mergeResults({
|
const {routes, globalData} = mergeResults({
|
||||||
plugins,
|
plugins,
|
||||||
allContentLoadedResult,
|
allContentLoadedResult,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {plugins, routes, globalData};
|
return {plugins, routes, globalData};
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,9 @@ export function getPluginByIdentifier<P extends InitializedPlugin>({
|
||||||
);
|
);
|
||||||
if (!plugin) {
|
if (!plugin) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
logger.interpolate`Plugin not found for identifier ${pluginIdentifier.name}@${pluginIdentifier.id}`,
|
logger.interpolate`Plugin not found for identifier ${formatPluginName(
|
||||||
|
pluginIdentifier,
|
||||||
|
)}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return plugin;
|
return plugin;
|
||||||
|
@ -85,3 +87,22 @@ export function mergeGlobalData(...globalDataList: GlobalData[]): GlobalData {
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is primarily useful for colored logging purpose
|
||||||
|
// Do not rely on this for logic
|
||||||
|
export function formatPluginName(
|
||||||
|
plugin: InitializedPlugin | PluginIdentifier,
|
||||||
|
): string {
|
||||||
|
let formattedName = plugin.name;
|
||||||
|
// Hacky way to reduce string size for logging purpose
|
||||||
|
formattedName = formattedName.replace('docusaurus-plugin-content-', '');
|
||||||
|
formattedName = formattedName.replace('docusaurus-plugin-', '');
|
||||||
|
formattedName = formattedName.replace('docusaurus-theme-', '');
|
||||||
|
formattedName = formattedName.replace('-plugin', '');
|
||||||
|
formattedName = logger.name(formattedName);
|
||||||
|
|
||||||
|
const id = 'id' in plugin ? plugin.id : plugin.options.id;
|
||||||
|
const formattedId = logger.subdue(id);
|
||||||
|
|
||||||
|
return `${formattedName}@${formattedId}`;
|
||||||
|
}
|
||||||
|
|
|
@ -13,14 +13,14 @@ import {
|
||||||
} from '@docusaurus/utils';
|
} from '@docusaurus/utils';
|
||||||
import combinePromises from 'combine-promises';
|
import combinePromises from 'combine-promises';
|
||||||
import {loadSiteConfig} from './config';
|
import {loadSiteConfig} from './config';
|
||||||
import {loadClientModules} from './clientModules';
|
import {getAllClientModules} from './clientModules';
|
||||||
import {loadPlugins, reloadPlugin} from './plugins/plugins';
|
import {loadPlugins, reloadPlugin} from './plugins/plugins';
|
||||||
import {loadHtmlTags} from './htmlTags';
|
import {loadHtmlTags} from './htmlTags';
|
||||||
import {loadSiteMetadata} from './siteMetadata';
|
import {createSiteMetadata, loadSiteVersion} from './siteMetadata';
|
||||||
import {loadI18n} from './i18n';
|
import {loadI18n} from './i18n';
|
||||||
import {
|
import {
|
||||||
loadSiteCodeTranslations,
|
loadSiteCodeTranslations,
|
||||||
getPluginsDefaultCodeTranslationMessages,
|
getPluginsDefaultCodeTranslations,
|
||||||
} from './translations/translations';
|
} from './translations/translations';
|
||||||
import {PerfLogger} from '../utils';
|
import {PerfLogger} from '../utils';
|
||||||
import {generateSiteFiles} from './codegen/codegen';
|
import {generateSiteFiles} from './codegen/codegen';
|
||||||
|
@ -76,9 +76,15 @@ export async function loadContext(
|
||||||
} = params;
|
} = params;
|
||||||
const generatedFilesDir = path.resolve(siteDir, GENERATED_FILES_DIR_NAME);
|
const generatedFilesDir = path.resolve(siteDir, GENERATED_FILES_DIR_NAME);
|
||||||
|
|
||||||
const {siteConfig: initialSiteConfig, siteConfigPath} = await loadSiteConfig({
|
const {
|
||||||
siteDir,
|
siteVersion,
|
||||||
customConfigFilePath,
|
loadSiteConfig: {siteConfig: initialSiteConfig, siteConfigPath},
|
||||||
|
} = await combinePromises({
|
||||||
|
siteVersion: loadSiteVersion(siteDir),
|
||||||
|
loadSiteConfig: loadSiteConfig({
|
||||||
|
siteDir,
|
||||||
|
customConfigFilePath,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const i18n = await loadI18n(initialSiteConfig, {locale});
|
const i18n = await loadI18n(initialSiteConfig, {locale});
|
||||||
|
@ -107,6 +113,7 @@ export async function loadContext(
|
||||||
|
|
||||||
return {
|
return {
|
||||||
siteDir,
|
siteDir,
|
||||||
|
siteVersion,
|
||||||
generatedFilesDir,
|
generatedFilesDir,
|
||||||
localizationDir,
|
localizationDir,
|
||||||
siteConfig,
|
siteConfig,
|
||||||
|
@ -118,13 +125,14 @@ export async function loadContext(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createSiteProps(
|
function createSiteProps(
|
||||||
params: LoadPluginsResult & {context: LoadContext},
|
params: LoadPluginsResult & {context: LoadContext},
|
||||||
): Promise<Props> {
|
): Props {
|
||||||
const {plugins, routes, context} = params;
|
const {plugins, routes, context} = params;
|
||||||
const {
|
const {
|
||||||
generatedFilesDir,
|
generatedFilesDir,
|
||||||
siteDir,
|
siteDir,
|
||||||
|
siteVersion,
|
||||||
siteConfig,
|
siteConfig,
|
||||||
siteConfigPath,
|
siteConfigPath,
|
||||||
outDir,
|
outDir,
|
||||||
|
@ -136,19 +144,12 @@ async function createSiteProps(
|
||||||
|
|
||||||
const {headTags, preBodyTags, postBodyTags} = loadHtmlTags(plugins);
|
const {headTags, preBodyTags, postBodyTags} = loadHtmlTags(plugins);
|
||||||
|
|
||||||
const {codeTranslations, siteMetadata} = await combinePromises({
|
const siteMetadata = createSiteMetadata({plugins, siteVersion});
|
||||||
// TODO code translations should be loaded as part of LoadedPlugin?
|
|
||||||
codeTranslations: PerfLogger.async(
|
const codeTranslations = {
|
||||||
'Load - loadCodeTranslations',
|
...getPluginsDefaultCodeTranslations({plugins}),
|
||||||
async () => ({
|
...siteCodeTranslations,
|
||||||
...(await getPluginsDefaultCodeTranslationMessages(plugins)),
|
};
|
||||||
...siteCodeTranslations,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
siteMetadata: PerfLogger.async('Load - loadSiteMetadata', () =>
|
|
||||||
loadSiteMetadata({plugins, siteDir}),
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
handleDuplicateRoutes(routes, siteConfig.onDuplicateRoutes);
|
handleDuplicateRoutes(routes, siteConfig.onDuplicateRoutes);
|
||||||
const routesPaths = getRoutesPaths(routes, baseUrl);
|
const routesPaths = getRoutesPaths(routes, baseUrl);
|
||||||
|
@ -157,6 +158,7 @@ async function createSiteProps(
|
||||||
siteConfig,
|
siteConfig,
|
||||||
siteConfigPath,
|
siteConfigPath,
|
||||||
siteMetadata,
|
siteMetadata,
|
||||||
|
siteVersion,
|
||||||
siteDir,
|
siteDir,
|
||||||
outDir,
|
outDir,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
|
@ -181,7 +183,7 @@ async function createSiteFiles({
|
||||||
site: Site;
|
site: Site;
|
||||||
globalData: GlobalData;
|
globalData: GlobalData;
|
||||||
}) {
|
}) {
|
||||||
return PerfLogger.async('Load - createSiteFiles', async () => {
|
return PerfLogger.async('Create site files', async () => {
|
||||||
const {
|
const {
|
||||||
props: {
|
props: {
|
||||||
plugins,
|
plugins,
|
||||||
|
@ -194,7 +196,7 @@ async function createSiteFiles({
|
||||||
baseUrl,
|
baseUrl,
|
||||||
},
|
},
|
||||||
} = site;
|
} = site;
|
||||||
const clientModules = loadClientModules(plugins);
|
const clientModules = getAllClientModules(plugins);
|
||||||
await generateSiteFiles({
|
await generateSiteFiles({
|
||||||
generatedFilesDir,
|
generatedFilesDir,
|
||||||
clientModules,
|
clientModules,
|
||||||
|
@ -216,13 +218,11 @@ async function createSiteFiles({
|
||||||
* it generates temp files in the `.docusaurus` folder for the bundler.
|
* it generates temp files in the `.docusaurus` folder for the bundler.
|
||||||
*/
|
*/
|
||||||
export async function loadSite(params: LoadContextParams): Promise<Site> {
|
export async function loadSite(params: LoadContextParams): Promise<Site> {
|
||||||
PerfLogger.start('Load - loadContext');
|
const context = await PerfLogger.async('Load context', () =>
|
||||||
const context = await loadContext(params);
|
loadContext(params),
|
||||||
PerfLogger.end('Load - loadContext');
|
);
|
||||||
|
|
||||||
PerfLogger.start('Load - loadPlugins');
|
|
||||||
const {plugins, routes, globalData} = await loadPlugins(context);
|
const {plugins, routes, globalData} = await loadPlugins(context);
|
||||||
PerfLogger.end('Load - loadPlugins');
|
|
||||||
|
|
||||||
const props = await createSiteProps({plugins, routes, globalData, context});
|
const props = await createSiteProps({plugins, routes, globalData, context});
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import type {
|
||||||
SiteMetadata,
|
SiteMetadata,
|
||||||
} from '@docusaurus/types';
|
} from '@docusaurus/types';
|
||||||
|
|
||||||
async function getPackageJsonVersion(
|
async function loadPackageJsonVersion(
|
||||||
packageJsonPath: string,
|
packageJsonPath: string,
|
||||||
): Promise<string | undefined> {
|
): Promise<string | undefined> {
|
||||||
if (await fs.pathExists(packageJsonPath)) {
|
if (await fs.pathExists(packageJsonPath)) {
|
||||||
|
@ -24,14 +24,20 @@ async function getPackageJsonVersion(
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPackageJsonName(
|
async function loadPackageJsonName(
|
||||||
packageJsonPath: string,
|
packageJsonPath: string,
|
||||||
): Promise<string | undefined> {
|
): Promise<string | undefined> {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-dynamic-require, global-require
|
// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-dynamic-require, global-require
|
||||||
return (require(packageJsonPath) as {name?: string}).name;
|
return (require(packageJsonPath) as {name?: string}).name;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPluginVersion(
|
export async function loadSiteVersion(
|
||||||
|
siteDir: string,
|
||||||
|
): Promise<string | undefined> {
|
||||||
|
return loadPackageJsonVersion(path.join(siteDir, 'package.json'));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadPluginVersion(
|
||||||
pluginPath: string,
|
pluginPath: string,
|
||||||
siteDir: string,
|
siteDir: string,
|
||||||
): Promise<PluginVersionInformation> {
|
): Promise<PluginVersionInformation> {
|
||||||
|
@ -52,8 +58,8 @@ export async function getPluginVersion(
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'package',
|
type: 'package',
|
||||||
name: await getPackageJsonName(packageJsonPath),
|
name: await loadPackageJsonName(packageJsonPath),
|
||||||
version: await getPackageJsonVersion(packageJsonPath),
|
version: await loadPackageJsonVersion(packageJsonPath),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
potentialPluginPackageJsonDirectory = path.dirname(
|
potentialPluginPackageJsonDirectory = path.dirname(
|
||||||
|
@ -89,18 +95,16 @@ Maybe you want to check, or regenerate your yarn.lock or package-lock.json file?
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadSiteMetadata({
|
export function createSiteMetadata({
|
||||||
|
siteVersion,
|
||||||
plugins,
|
plugins,
|
||||||
siteDir,
|
|
||||||
}: {
|
}: {
|
||||||
|
siteVersion: string | undefined;
|
||||||
plugins: LoadedPlugin[];
|
plugins: LoadedPlugin[];
|
||||||
siteDir: string;
|
}): SiteMetadata {
|
||||||
}): Promise<SiteMetadata> {
|
|
||||||
const siteMetadata: SiteMetadata = {
|
const siteMetadata: SiteMetadata = {
|
||||||
docusaurusVersion: DOCUSAURUS_VERSION,
|
docusaurusVersion: DOCUSAURUS_VERSION,
|
||||||
siteVersion: await getPackageJsonVersion(
|
siteVersion,
|
||||||
path.join(siteDir, 'package.json'),
|
|
||||||
),
|
|
||||||
pluginVersions: Object.fromEntries(
|
pluginVersions: Object.fromEntries(
|
||||||
plugins
|
plugins
|
||||||
.filter(({version: {type}}) => type !== 'synthetic')
|
.filter(({version: {type}}) => type !== 'synthetic')
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {
|
||||||
readCodeTranslationFileContent,
|
readCodeTranslationFileContent,
|
||||||
type WriteTranslationsOptions,
|
type WriteTranslationsOptions,
|
||||||
localizePluginTranslationFile,
|
localizePluginTranslationFile,
|
||||||
getPluginsDefaultCodeTranslationMessages,
|
loadPluginsDefaultCodeTranslationMessages,
|
||||||
applyDefaultCodeTranslations,
|
applyDefaultCodeTranslations,
|
||||||
} from '../translations';
|
} from '../translations';
|
||||||
import type {
|
import type {
|
||||||
|
@ -537,7 +537,7 @@ describe('readCodeTranslationFileContent', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getPluginsDefaultCodeTranslationMessages', () => {
|
describe('loadPluginsDefaultCodeTranslationMessages', () => {
|
||||||
function createTestPlugin(
|
function createTestPlugin(
|
||||||
fn: InitializedPlugin['getDefaultCodeTranslationMessages'],
|
fn: InitializedPlugin['getDefaultCodeTranslationMessages'],
|
||||||
): InitializedPlugin {
|
): InitializedPlugin {
|
||||||
|
@ -547,14 +547,14 @@ describe('getPluginsDefaultCodeTranslationMessages', () => {
|
||||||
it('works for empty plugins', async () => {
|
it('works for empty plugins', async () => {
|
||||||
const plugins: InitializedPlugin[] = [];
|
const plugins: InitializedPlugin[] = [];
|
||||||
await expect(
|
await expect(
|
||||||
getPluginsDefaultCodeTranslationMessages(plugins),
|
loadPluginsDefaultCodeTranslationMessages(plugins),
|
||||||
).resolves.toEqual({});
|
).resolves.toEqual({});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('works for 1 plugin without lifecycle', async () => {
|
it('works for 1 plugin without lifecycle', async () => {
|
||||||
const plugins: InitializedPlugin[] = [createTestPlugin(undefined)];
|
const plugins: InitializedPlugin[] = [createTestPlugin(undefined)];
|
||||||
await expect(
|
await expect(
|
||||||
getPluginsDefaultCodeTranslationMessages(plugins),
|
loadPluginsDefaultCodeTranslationMessages(plugins),
|
||||||
).resolves.toEqual({});
|
).resolves.toEqual({});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -566,7 +566,7 @@ describe('getPluginsDefaultCodeTranslationMessages', () => {
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
await expect(
|
await expect(
|
||||||
getPluginsDefaultCodeTranslationMessages(plugins),
|
loadPluginsDefaultCodeTranslationMessages(plugins),
|
||||||
).resolves.toEqual({
|
).resolves.toEqual({
|
||||||
a: '1',
|
a: '1',
|
||||||
b: '2',
|
b: '2',
|
||||||
|
@ -585,7 +585,7 @@ describe('getPluginsDefaultCodeTranslationMessages', () => {
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
await expect(
|
await expect(
|
||||||
getPluginsDefaultCodeTranslationMessages(plugins),
|
loadPluginsDefaultCodeTranslationMessages(plugins),
|
||||||
).resolves.toEqual({
|
).resolves.toEqual({
|
||||||
a: '1',
|
a: '1',
|
||||||
b: '2',
|
b: '2',
|
||||||
|
@ -613,7 +613,7 @@ describe('getPluginsDefaultCodeTranslationMessages', () => {
|
||||||
createTestPlugin(undefined),
|
createTestPlugin(undefined),
|
||||||
];
|
];
|
||||||
await expect(
|
await expect(
|
||||||
getPluginsDefaultCodeTranslationMessages(plugins),
|
loadPluginsDefaultCodeTranslationMessages(plugins),
|
||||||
).resolves.toEqual({
|
).resolves.toEqual({
|
||||||
// merge, last plugin wins
|
// merge, last plugin wins
|
||||||
b: '2',
|
b: '2',
|
||||||
|
|
|
@ -20,6 +20,7 @@ import type {
|
||||||
TranslationFile,
|
TranslationFile,
|
||||||
CodeTranslations,
|
CodeTranslations,
|
||||||
InitializedPlugin,
|
InitializedPlugin,
|
||||||
|
LoadedPlugin,
|
||||||
} from '@docusaurus/types';
|
} from '@docusaurus/types';
|
||||||
|
|
||||||
export type WriteTranslationsOptions = {
|
export type WriteTranslationsOptions = {
|
||||||
|
@ -242,17 +243,33 @@ export async function localizePluginTranslationFile({
|
||||||
return translationFile;
|
return translationFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPluginsDefaultCodeTranslationMessages(
|
export function mergeCodeTranslations(
|
||||||
|
codeTranslations: CodeTranslations[],
|
||||||
|
): CodeTranslations {
|
||||||
|
return codeTranslations.reduce(
|
||||||
|
(allCodeTranslations, current) => ({
|
||||||
|
...allCodeTranslations,
|
||||||
|
...current,
|
||||||
|
}),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadPluginsDefaultCodeTranslationMessages(
|
||||||
plugins: InitializedPlugin[],
|
plugins: InitializedPlugin[],
|
||||||
): Promise<CodeTranslations> {
|
): Promise<CodeTranslations> {
|
||||||
const pluginsMessages = await Promise.all(
|
const pluginsMessages = await Promise.all(
|
||||||
plugins.map((plugin) => plugin.getDefaultCodeTranslationMessages?.() ?? {}),
|
plugins.map((plugin) => plugin.getDefaultCodeTranslationMessages?.() ?? {}),
|
||||||
);
|
);
|
||||||
|
return mergeCodeTranslations(pluginsMessages);
|
||||||
|
}
|
||||||
|
|
||||||
return pluginsMessages.reduce(
|
export function getPluginsDefaultCodeTranslations({
|
||||||
(allMessages, pluginMessages) => ({...allMessages, ...pluginMessages}),
|
plugins,
|
||||||
{},
|
}: {
|
||||||
);
|
plugins: LoadedPlugin[];
|
||||||
|
}): CodeTranslations {
|
||||||
|
return mergeCodeTranslations(plugins.map((p) => p.defaultCodeTranslations));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyDefaultCodeTranslations({
|
export function applyDefaultCodeTranslations({
|
||||||
|
|
|
@ -47,12 +47,11 @@ export async function loadAppRenderer({
|
||||||
}: {
|
}: {
|
||||||
serverBundlePath: string;
|
serverBundlePath: string;
|
||||||
}): Promise<AppRenderer> {
|
}): Promise<AppRenderer> {
|
||||||
console.log(`SSG - Load server bundle`);
|
const source = await PerfLogger.async(`Load server bundle`, () =>
|
||||||
PerfLogger.start(`SSG - Load server bundle`);
|
fs.readFile(serverBundlePath),
|
||||||
const source = await fs.readFile(serverBundlePath);
|
);
|
||||||
PerfLogger.end(`SSG - Load server bundle`);
|
|
||||||
PerfLogger.log(
|
PerfLogger.log(
|
||||||
`SSG - Server bundle size = ${(source.length / 1024000).toFixed(3)} MB`,
|
`Server bundle size = ${(source.length / 1024000).toFixed(3)} MB`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const filename = path.basename(serverBundlePath);
|
const filename = path.basename(serverBundlePath);
|
||||||
|
@ -69,14 +68,16 @@ export async function loadAppRenderer({
|
||||||
require: createRequire(serverBundlePath),
|
require: createRequire(serverBundlePath),
|
||||||
};
|
};
|
||||||
|
|
||||||
PerfLogger.start(`SSG - Evaluate server bundle`);
|
const serverEntry = await PerfLogger.async(
|
||||||
const serverEntry = evaluate(
|
`Evaluate server bundle`,
|
||||||
source,
|
() =>
|
||||||
/* filename: */ filename,
|
evaluate(
|
||||||
/* scope: */ globals,
|
source,
|
||||||
/* includeGlobals: */ true,
|
/* filename: */ filename,
|
||||||
) as {default?: AppRenderer};
|
/* scope: */ globals,
|
||||||
PerfLogger.end(`SSG - Evaluate server bundle`);
|
/* includeGlobals: */ true,
|
||||||
|
) as {default?: AppRenderer},
|
||||||
|
);
|
||||||
|
|
||||||
if (!serverEntry?.default || typeof serverEntry.default !== 'function') {
|
if (!serverEntry?.default || typeof serverEntry.default !== 'function') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* This source code is licensed under the MIT license found in the
|
* This source code is licensed under the MIT license found in the
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
import {AsyncLocalStorage} from 'async_hooks';
|
||||||
import logger from '@docusaurus/logger';
|
import logger from '@docusaurus/logger';
|
||||||
|
|
||||||
// For now this is a private env variable we use internally
|
// For now this is a private env variable we use internally
|
||||||
|
@ -17,6 +18,16 @@ const Thresholds = {
|
||||||
red: 1000,
|
red: 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PerfPrefix = logger.yellow(`[PERF] `);
|
||||||
|
|
||||||
|
// This is what enables to "see the parent stack" for each log
|
||||||
|
// Parent1 > Parent2 > Parent3 > child trace
|
||||||
|
const ParentPrefix = new AsyncLocalStorage<string>();
|
||||||
|
function applyParentPrefix(label: string) {
|
||||||
|
const parentPrefix = ParentPrefix.getStore();
|
||||||
|
return parentPrefix ? `${parentPrefix} > ${label}` : label;
|
||||||
|
}
|
||||||
|
|
||||||
type PerfLoggerAPI = {
|
type PerfLoggerAPI = {
|
||||||
start: (label: string) => void;
|
start: (label: string) => void;
|
||||||
end: (label: string) => void;
|
end: (label: string) => void;
|
||||||
|
@ -38,8 +49,6 @@ function createPerfLogger(): PerfLoggerAPI {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const prefix = logger.yellow(`[PERF] `);
|
|
||||||
|
|
||||||
const formatDuration = (duration: number): string => {
|
const formatDuration = (duration: number): string => {
|
||||||
if (duration > Thresholds.red) {
|
if (duration > Thresholds.red) {
|
||||||
return logger.red(`${(duration / 1000).toFixed(2)} seconds!`);
|
return logger.red(`${(duration / 1000).toFixed(2)} seconds!`);
|
||||||
|
@ -54,7 +63,7 @@ function createPerfLogger(): PerfLoggerAPI {
|
||||||
if (duration < Thresholds.min) {
|
if (duration < Thresholds.min) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(`${prefix + label} - ${formatDuration(duration)}`);
|
console.log(`${PerfPrefix + label} - ${formatDuration(duration)}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const start: PerfLoggerAPI['start'] = (label) => performance.mark(label);
|
const start: PerfLoggerAPI['start'] = (label) => performance.mark(label);
|
||||||
|
@ -62,18 +71,18 @@ function createPerfLogger(): PerfLoggerAPI {
|
||||||
const end: PerfLoggerAPI['end'] = (label) => {
|
const end: PerfLoggerAPI['end'] = (label) => {
|
||||||
const {duration} = performance.measure(label);
|
const {duration} = performance.measure(label);
|
||||||
performance.clearMarks(label);
|
performance.clearMarks(label);
|
||||||
logDuration(label, duration);
|
logDuration(applyParentPrefix(label), duration);
|
||||||
};
|
};
|
||||||
|
|
||||||
const log: PerfLoggerAPI['log'] = (label: string) =>
|
const log: PerfLoggerAPI['log'] = (label: string) =>
|
||||||
console.log(prefix + label);
|
console.log(PerfPrefix + applyParentPrefix(label));
|
||||||
|
|
||||||
const async: PerfLoggerAPI['async'] = async (label, asyncFn) => {
|
const async: PerfLoggerAPI['async'] = async (label, asyncFn) => {
|
||||||
start(label);
|
const finalLabel = applyParentPrefix(label);
|
||||||
const before = performance.now();
|
const before = performance.now();
|
||||||
const result = await asyncFn();
|
const result = await ParentPrefix.run(finalLabel, () => asyncFn());
|
||||||
const duration = performance.now() - before;
|
const duration = performance.now() - before;
|
||||||
logDuration(label, duration);
|
logDuration(finalLabel, duration);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ architecting
|
||||||
Astro
|
Astro
|
||||||
atrule
|
atrule
|
||||||
Autoconverted
|
Autoconverted
|
||||||
|
autofix
|
||||||
Autogen
|
Autogen
|
||||||
autogen
|
autogen
|
||||||
autogenerating
|
autogenerating
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue