diff --git a/packages/docusaurus/bin/docusaurus.mjs b/packages/docusaurus/bin/docusaurus.mjs index 11ac62cadbfb..5fce33c368df 100755 --- a/packages/docusaurus/bin/docusaurus.mjs +++ b/packages/docusaurus/bin/docusaurus.mjs @@ -20,6 +20,7 @@ import { externalCommand, serve, clear, + upgrade, writeTranslations, writeHeadingIds, } from '../lib/index.js'; @@ -189,6 +190,17 @@ cli .description('Remove build artifacts.') .action(clear); +cli + .command('upgrade [siteDir]') + .description('Upgrade @docusaurus packages.') + .option( + '-t, --tag ', + 'Tag of npm to look for upgrading. This option accepts any of: ', + ) + .action(async (siteDir, {tag = undefined}) => { + upgrade(await resolveDir(siteDir), {tag}); + }); + cli .command('write-translations [siteDir]') .description('Extract required translations of your site.') @@ -239,6 +251,7 @@ function isInternalCommand(command) { 'start', 'build', 'swizzle', + 'upgrade', 'deploy', 'serve', 'clear', diff --git a/packages/docusaurus/src/commands/upgrade.ts b/packages/docusaurus/src/commands/upgrade.ts new file mode 100644 index 000000000000..0909e658ba8b --- /dev/null +++ b/packages/docusaurus/src/commands/upgrade.ts @@ -0,0 +1,59 @@ +/** + * 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. + */ + +import logger from '@docusaurus/logger'; +import fs from 'fs-extra'; +import path from 'path'; +import os from 'os'; +import childProcess from 'child_process'; +import {clear} from './clear'; + +async function hasYarn() { + return fs.pathExists(path.resolve(path.dirname(process.cwd()), 'yarn.lock')); +} + +type CommonNpmTags = 'alpha' | 'beta' | 'latest' | 'next'; + +export async function upgrade( + siteDir: string, + {tag}: {tag: CommonNpmTags}, +): Promise { + const [npmClient, npmCommand] = (await hasYarn()) + ? ['yarn', 'upgrade'] + : ['npm', 'install']; + + const {dependencies = {}, devDependencies = {}} = await fs.readJSON( + path.join(siteDir, 'package.json'), + ); + + const packageNames = Array.from( + new Set( + [...Object.keys(dependencies), ...Object.keys(devDependencies)].filter( + (pkg) => pkg.startsWith('@docusaurus'), + ), + ), + ) + .sort() + .map((name) => (tag ? `${name}@${tag}` : name)); + + if (!packageNames.length) { + logger.error(`Found 0 packages with scope @docusaurus`); + return; + } + + logger.info`Found number=${packageNames.length} to update: name=${packageNames}`; + logger.info`Executing code=${`${npmClient} ${npmCommand} ${packageNames.join( + ' ', + )}`}`; + + childProcess.spawnSync(npmClient, [npmCommand, ...packageNames], { + stdio: 'inherit', + shell: os.platform() === 'win32', + }); + + await clear(siteDir); +} diff --git a/packages/docusaurus/src/index.ts b/packages/docusaurus/src/index.ts index 82ba70af2e93..2b15c085d4e9 100644 --- a/packages/docusaurus/src/index.ts +++ b/packages/docusaurus/src/index.ts @@ -12,5 +12,6 @@ export {externalCommand} from './commands/external'; export {serve} from './commands/serve'; export {start} from './commands/start/start'; export {swizzle} from './commands/swizzle'; +export {upgrade} from './commands/upgrade'; export {writeHeadingIds} from './commands/writeHeadingIds'; export {writeTranslations} from './commands/writeTranslations'; diff --git a/website/package.json b/website/package.json index d5ce9aca8359..4a8008cfb97a 100644 --- a/website/package.json +++ b/website/package.json @@ -10,6 +10,7 @@ "deploy": "docusaurus deploy", "clear": "docusaurus clear && rimraf changelog && rimraf _dogfooding/_swizzle_theme_tests", "serve": "docusaurus serve", + "upgrade": "docusaurus upgrade", "test:css-order": "node testCSSOrder.mjs", "test:swizzle:eject:js": "cross-env SWIZZLE_ACTION='eject' SWIZZLE_TYPESCRIPT='false' node _dogfooding/testSwizzleThemeClassic.mjs", "test:swizzle:eject:ts": "cross-env SWIZZLE_ACTION='eject' SWIZZLE_TYPESCRIPT='true' node _dogfooding/testSwizzleThemeClassic.mjs",