@docusaurus/core
Version:
Easy to Maintain Open Source Documentation Websites
176 lines (156 loc) • 5.55 kB
JavaScript
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// @ts-check
import fs from 'fs-extra';
import path from 'path';
import {createRequire} from 'module';
import execa from 'execa';
import {logger} from '@docusaurus/logger';
import semver from 'semver';
import updateNotifier from 'update-notifier';
import boxen from 'boxen';
import {DOCUSAURUS_VERSION} from '@docusaurus/utils';
const packageJson = /** @type {import("../package.json")} */ (
createRequire(import.meta.url)('../package.json')
);
/** @type {Record<string, any>} */
let sitePkg;
try {
sitePkg = createRequire(path.resolve('package.json'))('./package.json');
} catch {
logger.warn`path=${'package.json'} file not found at CWD: path=${process.cwd()}.`;
logger.info`This is non-critical, but could lead to undesired behavior downstream. Docusaurus assumes that path=${'package.json'} exists at CWD, because it's where the package manager looks up the script at. A common reason is because you have changed directory in the script. Instead of writing code=${'"start": "cd website && docusaurus start"'}, consider using the code=${'[siteDir]'} argument: code=${'"start": "docusaurus start website"'}.`;
sitePkg = {};
}
const {
name,
engines: {node: requiredVersion},
} = packageJson;
/**
* Notify user if `@docusaurus` packages are outdated
*
* Note: this is a 2-step process to avoid delaying cli usage by awaiting a
* response:
* - 1st run: trigger background job to check releases + store result
* - 2nd run: display potential update to users
*
* cache data is stored in `~/.config/configstore/update-notifier-@docusaurus`
*/
export default async function beforeCli() {
const notifier = updateNotifier({
pkg: {
name,
version: DOCUSAURUS_VERSION,
},
// Check is in background so it's fine to use a small value like 1h
// Use 0 for debugging
updateCheckInterval: 1000 * 60 * 60,
});
// Hacky way to ensure we check for updates on first run
// Note: the notification will only happen in the 2nd run
// See https://github.com/yeoman/update-notifier/issues/209
try {
if (
notifier.config &&
// @ts-expect-error: this is an internal API
!notifier.disabled &&
Date.now() - notifier.config.get('lastUpdateCheck') < 50
) {
notifier.config.set('lastUpdateCheck', 0);
notifier.check();
}
} catch (err) {
// Do not stop cli if this fails, see https://github.com/facebook/docusaurus/issues/5400
logger.error(err);
}
/**
* We don't want to display update message for canary releases.
* See https://github.com/facebook/docusaurus/issues/5378
* @param {import('update-notifier').UpdateInfo} update
*/
function ignoreUpdate(update) {
const isCanaryRelease = update?.current?.startsWith('0.0.0');
return isCanaryRelease;
}
if (
notifier.config &&
notifier.update &&
semver.lt(notifier.update.current, notifier.update.latest)
) {
// Because notifier clears cached data after reading it, leading to notifier
// not consistently displaying the update.
// See https://github.com/yeoman/update-notifier/issues/209
notifier.config.set('update', notifier.update);
if (ignoreUpdate(notifier.update)) {
return;
}
const siteDocusaurusPackagesForUpdate = Object.keys({
...sitePkg.dependencies,
...sitePkg.devDependencies,
})
.filter((p) => p.startsWith('@docusaurus'))
.map((p) => p.concat('@latest'))
.join(' ');
const getYarnVersion = async () => {
if (!(await fs.pathExists(path.resolve('yarn.lock')))) {
return undefined;
}
const yarnVersionResult = await execa.command('yarn --version');
if (yarnVersionResult.exitCode === 0) {
const majorVersion = parseInt(
yarnVersionResult.stdout?.trim().split('.')[0] ?? '',
10,
);
if (!Number.isNaN(majorVersion)) {
return majorVersion;
}
}
return undefined;
};
const getUpgradeCommand = async () => {
const yarnVersion = await getYarnVersion();
if (!yarnVersion) {
return `npm i ${siteDocusaurusPackagesForUpdate}`;
}
return yarnVersion >= 2
? `yarn up ${siteDocusaurusPackagesForUpdate}`
: `yarn upgrade ${siteDocusaurusPackagesForUpdate}`;
};
/** @type {import('boxen').Options} */
const boxenOptions = {
padding: 1,
margin: 1,
align: 'center',
borderColor: 'yellow',
borderStyle: {
topLeft: ' ',
topRight: ' ',
bottomLeft: ' ',
bottomRight: ' ',
top: '-',
bottom: '-',
left: ' ',
right: ' ',
},
};
const docusaurusUpdateMessage = boxen(
`Update available ${logger.dim(
`${notifier.update.current}`,
)} → ${logger.green(`${notifier.update.latest}`)}
To upgrade Docusaurus packages with the latest version, run the following command:
${logger.code(await getUpgradeCommand())}`,
boxenOptions,
);
console.log(docusaurusUpdateMessage);
}
// Notify user if node version needs to be updated
if (!semver.satisfies(process.version, requiredVersion)) {
logger.error('Minimum Node.js version not met :(');
logger.info`You are using Node.js number=${process.version}, Requirement: Node.js number=${requiredVersion}.`;
process.exit(1);
}
}