From c1bad8dede54fd7fe809229f8fdd420e38567e38 Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Tue, 23 Nov 2021 17:13:17 +0800 Subject: [PATCH 01/33] refactor: migrate from chalk to picocolors --- packages/create-docusaurus/bin/index.js | 10 ++-- packages/create-docusaurus/package.json | 2 +- packages/create-docusaurus/src/index.ts | 42 +++++++-------- packages/docusaurus-mdx-loader/package.json | 2 +- packages/docusaurus-mdx-loader/src/index.ts | 4 +- packages/docusaurus-migrate/bin/index.js | 8 +-- packages/docusaurus-migrate/package.json | 2 +- packages/docusaurus-migrate/src/index.ts | 54 +++++++++---------- .../package.json | 2 +- .../src/collectRedirects.ts | 6 +-- .../package.json | 2 +- .../src/authors.ts | 6 +-- .../src/blogUtils.ts | 6 +-- .../package.json | 2 +- .../src/docs.ts | 4 +- .../src/index.ts | 6 +-- .../src/options.ts | 6 +-- .../src/sidebars/generator.ts | 6 +-- .../docusaurus-theme-classic/package.json | 1 - .../package.json | 4 +- .../docusaurus-theme-translations/update.js | 18 +++---- .../docusaurus-utils-validation/package.json | 2 +- .../src/validationUtils.ts | 12 ++--- packages/docusaurus-utils/package.json | 2 +- packages/docusaurus-utils/src/index.ts | 8 +-- .../docusaurus-utils/src/markdownParser.ts | 4 +- packages/docusaurus/bin/beforeCli.js | 12 ++--- packages/docusaurus/bin/docusaurus.js | 6 +-- packages/docusaurus/package.json | 2 +- packages/docusaurus/src/choosePort.ts | 16 +++--- packages/docusaurus/src/client/serverEntry.js | 8 +-- packages/docusaurus/src/commands/build.ts | 12 ++--- packages/docusaurus/src/commands/clear.ts | 4 +- packages/docusaurus/src/commands/deploy.ts | 16 +++--- packages/docusaurus/src/commands/serve.ts | 4 +- packages/docusaurus/src/commands/start.ts | 14 ++--- packages/docusaurus/src/commands/swizzle.ts | 34 ++++++------ .../src/commands/writeHeadingIds.ts | 8 +-- packages/docusaurus/src/server/i18n.ts | 6 +-- packages/docusaurus/src/server/index.ts | 4 +- .../docusaurus/src/server/plugins/index.ts | 4 +- .../src/server/translations/translations.ts | 6 +-- .../translations/translationsExtractor.ts | 4 +- packages/docusaurus/src/webpack/client.ts | 4 +- packages/docusaurus/src/webpack/utils.ts | 16 +++--- 45 files changed, 196 insertions(+), 205 deletions(-) diff --git a/packages/create-docusaurus/bin/index.js b/packages/create-docusaurus/bin/index.js index 5598fd748696..4a7d129c34e8 100755 --- a/packages/create-docusaurus/bin/index.js +++ b/packages/create-docusaurus/bin/index.js @@ -6,7 +6,7 @@ * LICENSE file in the root directory of this source tree. */ -const chalk = require('chalk'); +const pico = require('picocolors'); const semver = require('semver'); const path = require('path'); const program = require('commander'); @@ -15,8 +15,8 @@ const requiredVersion = require('../package.json').engines.node; if (!semver.satisfies(process.version, requiredVersion)) { console.log( - chalk.red(`\nMinimum Node.js version not met :)`) + - chalk.yellow( + pico.red(`\nMinimum Node.js version not met :)`) + + pico.yellow( `\nYou are using Node.js ${process.version}, Requirement: Node.js ${requiredVersion}.\n`, ), ); @@ -26,7 +26,7 @@ if (!semver.satisfies(process.version, requiredVersion)) { function wrapCommand(fn) { return (...args) => fn(...args).catch((err) => { - console.error(chalk.red(err.stack)); + console.error(pico.red(err.stack)); process.exitCode = 1; }); } @@ -58,7 +58,7 @@ program program.arguments('').action((cmd) => { program.outputHelp(); - console.log(` ${chalk.red(`\n Unknown command ${chalk.yellow(cmd)}.`)}`); + console.log(` ${pico.red(`\n Unknown command ${pico.yellow(cmd)}.`)}`); console.log(); }); diff --git a/packages/create-docusaurus/package.json b/packages/create-docusaurus/package.json index 26f2373694f5..6b7c5723482c 100755 --- a/packages/create-docusaurus/package.json +++ b/packages/create-docusaurus/package.json @@ -23,10 +23,10 @@ }, "license": "MIT", "dependencies": { - "chalk": "^4.1.2", "commander": "^5.1.0", "fs-extra": "^10.0.0", "lodash": "^4.17.20", + "picocolors": "^1.0.0", "prompts": "^2.4.1", "semver": "^7.3.4", "shelljs": "^0.8.4", diff --git a/packages/create-docusaurus/src/index.ts b/packages/create-docusaurus/src/index.ts index 55da5f84c383..3dcdf0dda5bb 100755 --- a/packages/create-docusaurus/src/index.ts +++ b/packages/create-docusaurus/src/index.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import chalk from 'chalk'; +import pico from 'picocolors'; import fs from 'fs-extra'; import {execSync} from 'child_process'; import prompts, {Choice} from 'prompts'; @@ -131,7 +131,7 @@ export default async function init( } if (!name) { - throw new Error(chalk.red('A website name is required.')); + throw new Error(pico.red('A website name is required.')); } const dest = path.resolve(rootDir, name); @@ -171,7 +171,7 @@ export default async function init( if (url && isValidGitRepoUrl(url)) { return true; } - return chalk.red(`Invalid repository URL`); + return pico.red(`Invalid repository URL`); }, message: 'Enter a repository URL from GitHub, Bitbucket, GitLab, or any other public repo.\n(e.g: https://github.com/ownerName/repoName.git)', @@ -187,11 +187,9 @@ export default async function init( if (fs.existsSync(fullDir)) { return true; } - return chalk.red( - `The path ${chalk.magenta(fullDir)} does not exist.`, - ); + return pico.red(`The path ${pico.magenta(fullDir)} does not exist.`); } - return chalk.red('Please enter a valid path.'); + return pico.red('Please enter a valid path.'); }, message: 'Enter a local folder path, relative to the current working directory.', @@ -204,16 +202,16 @@ export default async function init( } console.log(` -${chalk.cyan('Creating new Docusaurus project...')} +${pico.cyan('Creating new Docusaurus project...')} `); if (isValidGitRepoUrl(template)) { - console.log(`Cloning Git template ${chalk.cyan(template)}...`); + console.log(`Cloning Git template ${pico.cyan(template)}...`); if ( shell.exec(`git clone --recursive ${template} ${dest}`, {silent: true}) .code !== 0 ) { - throw new Error(chalk.red(`Cloning Git template ${template} failed!`)); + throw new Error(pico.red(`Cloning Git template ${template} failed!`)); } } else if (templates.includes(template)) { // Docusaurus templates. @@ -228,9 +226,7 @@ ${chalk.cyan('Creating new Docusaurus project...')} try { await copyTemplate(templatesDir, template, dest); } catch (err) { - console.log( - `Copying Docusaurus template ${chalk.cyan(template)} failed!`, - ); + console.log(`Copying Docusaurus template ${pico.cyan(template)} failed!`); throw err; } } else if (fs.existsSync(path.resolve(process.cwd(), template))) { @@ -253,7 +249,7 @@ ${chalk.cyan('Creating new Docusaurus project...')} private: true, }); } catch (err) { - console.log(chalk.red('Failed to update package.json.')); + console.log(pico.red('Failed to update package.json.')); throw err; } @@ -270,7 +266,7 @@ ${chalk.cyan('Creating new Docusaurus project...')} const pkgManager = useYarn ? 'yarn' : 'npm'; if (!cliOptions.skipInstall) { - console.log(`Installing dependencies with ${chalk.cyan(pkgManager)}...`); + console.log(`Installing dependencies with ${pico.cyan(pkgManager)}...`); try { // Use force coloring the output, since the command is invoked by shelljs, which is not the interactive shell @@ -284,7 +280,7 @@ ${chalk.cyan('Creating new Docusaurus project...')} }, ); } catch (err) { - console.log(chalk.red('Installation failed.')); + console.log(pico.red('Installation failed.')); throw err; } } @@ -297,25 +293,25 @@ ${chalk.cyan('Creating new Docusaurus project...')} : path.relative(process.cwd(), name); console.log(` -Successfully created "${chalk.cyan(cdpath)}". +Successfully created "${pico.cyan(cdpath)}". Inside that directory, you can run several commands: - ${chalk.cyan(`${pkgManager} start`)} + ${pico.cyan(`${pkgManager} start`)} Starts the development server. - ${chalk.cyan(`${pkgManager} ${useYarn ? '' : 'run '}build`)} + ${pico.cyan(`${pkgManager} ${useYarn ? '' : 'run '}build`)} Bundles your website into static files for production. - ${chalk.cyan(`${pkgManager} ${useYarn ? '' : 'run '}serve`)} + ${pico.cyan(`${pkgManager} ${useYarn ? '' : 'run '}serve`)} Serves the built website locally. - ${chalk.cyan(`${pkgManager} deploy`)} + ${pico.cyan(`${pkgManager} deploy`)} Publishes the website to GitHub pages. We recommend that you begin by typing: - ${chalk.cyan('cd')} ${cdpath} - ${chalk.cyan(`${pkgManager} start`)} + ${pico.cyan('cd')} ${cdpath} + ${pico.cyan(`${pkgManager} start`)} Happy building awesome websites! `); diff --git a/packages/docusaurus-mdx-loader/package.json b/packages/docusaurus-mdx-loader/package.json index 98046eae1650..f1db41a66423 100644 --- a/packages/docusaurus-mdx-loader/package.json +++ b/packages/docusaurus-mdx-loader/package.json @@ -24,13 +24,13 @@ "@docusaurus/utils": "2.0.0-beta.9", "@mdx-js/mdx": "^1.6.21", "@mdx-js/react": "^1.6.21", - "chalk": "^4.1.2", "escape-html": "^1.0.3", "file-loader": "^6.2.0", "fs-extra": "^10.0.0", "github-slugger": "^1.4.0", "gray-matter": "^4.0.3", "mdast-util-to-string": "^2.0.0", + "picocolors": "^1.0.0", "remark-emoji": "^2.1.0", "stringify-object": "^3.3.0", "unist-util-visit": "^2.0.2", diff --git a/packages/docusaurus-mdx-loader/src/index.ts b/packages/docusaurus-mdx-loader/src/index.ts index 2ffb0e415d3c..96e13603c185 100644 --- a/packages/docusaurus-mdx-loader/src/index.ts +++ b/packages/docusaurus-mdx-loader/src/index.ts @@ -7,7 +7,7 @@ import {readFile} from 'fs-extra'; import mdx from '@mdx-js/mdx'; -import chalk from 'chalk'; +import pico from 'picocolors'; import emoji from 'remark-emoji'; import { parseFrontMatter, @@ -164,7 +164,7 @@ ${JSON.stringify(frontMatter, null, 2)}`; if (shouldError) { return callback(new Error(errorMessage)); } else { - console.warn(chalk.yellow(errorMessage)); + console.warn(pico.yellow(errorMessage)); } } } diff --git a/packages/docusaurus-migrate/bin/index.js b/packages/docusaurus-migrate/bin/index.js index 79145ede74aa..180731820d11 100755 --- a/packages/docusaurus-migrate/bin/index.js +++ b/packages/docusaurus-migrate/bin/index.js @@ -6,7 +6,7 @@ * LICENSE file in the root directory of this source tree. */ -const chalk = require('chalk'); +const pico = require('picocolors'); const semver = require('semver'); const cli = require('commander'); const path = require('path'); @@ -18,15 +18,15 @@ const {migrateDocusaurusProject, migrateMDToMDX} = require('../lib'); function wrapCommand(fn) { return (...args) => fn(...args).catch((err) => { - console.error(chalk.red(err.stack)); + console.error(pico.red(err.stack)); process.exitCode = 1; }); } if (!semver.satisfies(process.version, requiredVersion)) { console.log( - chalk.red(`\nMinimum Node.js version not met :(`) + - chalk.yellow( + pico.red(`\nMinimum Node.js version not met :(`) + + pico.yellow( `\n\nYou are using Node ${process.version}. We require Node.js ${requiredVersion} or up!\n`, ), ); diff --git a/packages/docusaurus-migrate/package.json b/packages/docusaurus-migrate/package.json index f71e609aef51..3aab2b126632 100644 --- a/packages/docusaurus-migrate/package.json +++ b/packages/docusaurus-migrate/package.json @@ -25,7 +25,6 @@ "dependencies": { "@babel/preset-env": "^7.15.6", "@mapbox/hast-util-to-jsx": "^1.0.0", - "chalk": "^4.1.2", "color": "^4.0.1", "commander": "^5.1.0", "fs-extra": "^10.0.0", @@ -35,6 +34,7 @@ "html-tags": "^3.1.0", "import-fresh": "^3.2.2", "jscodeshift": "^0.13.0", + "picocolors": "^1.0.0", "rehype-parse": "^7.0.1", "remark-parse": "^8.0.2", "remark-stringify": "^8.1.0", diff --git a/packages/docusaurus-migrate/src/index.ts b/packages/docusaurus-migrate/src/index.ts index 08375170c795..ae321e5f0848 100644 --- a/packages/docusaurus-migrate/src/index.ts +++ b/packages/docusaurus-migrate/src/index.ts @@ -7,7 +7,7 @@ import * as fs from 'fs-extra'; import importFresh from 'import-fresh'; -import chalk from 'chalk'; +import pico from 'picocolors'; import glob from 'glob'; import Color from 'color'; @@ -112,24 +112,24 @@ export async function migrateDocusaurusProject( try { createClientRedirects(siteConfig, deps, config); console.log( - chalk.green('Successfully created client redirect for non clean URL'), + pico.green('Successfully created client redirect for non clean URL'), ); } catch (errorInClientRedirect) { console.log( - chalk.red(`Error while creating redirects: ${errorInClientRedirect}`), + pico.red(`Error while creating redirects: ${errorInClientRedirect}`), ); } if (shouldMigratePages) { try { createPages(newDir, siteDir); console.log( - chalk.green( + pico.green( 'Successfully created pages (check migration page for more details)', ), ); } catch (errorInMigratingPages) { console.log( - chalk.red( + pico.red( `Error occurred while creating pages: ${errorInMigratingPages}`, ), ); @@ -138,13 +138,13 @@ export async function migrateDocusaurusProject( try { createDefaultLandingPage(newDir); console.log( - chalk.green( + pico.green( 'Successfully created landing page (check migration page for more details)', ), ); } catch (errorInLandingPage) { console.log( - chalk.red( + pico.red( `Error occurred while creating landing page: ${errorInLandingPage}`, ), ); @@ -153,17 +153,17 @@ export async function migrateDocusaurusProject( try { migrateStaticFiles(siteDir, newDir); - console.log(chalk.green('Successfully migrated static folder')); + console.log(pico.green('Successfully migrated static folder')); } catch (errorInStatic) { console.log( - chalk.red(`Error occurred while copying static folder: ${errorInStatic}`), + pico.red(`Error occurred while copying static folder: ${errorInStatic}`), ); } try { migrateBlogFiles(siteDir, newDir, classicPreset, shouldMigrateMdFiles); } catch (errorInMigratingBlogs) { console.log( - chalk.red( + pico.red( `Error occurred while migrating blogs: ${errorInMigratingBlogs}`, ), ); @@ -172,7 +172,7 @@ export async function migrateDocusaurusProject( handleVersioning(siteDir, siteConfig, newDir, config, shouldMigrateMdFiles); } catch (errorInVersion) { console.log( - chalk.red( + pico.red( `Error occurred while migrating versioned docs: ${errorInVersion}`, ), ); @@ -181,13 +181,13 @@ export async function migrateDocusaurusProject( try { migrateLatestDocs(siteDir, newDir, shouldMigrateMdFiles, classicPreset); } catch (errorInDoc) { - chalk.red(`Error occurred while migrating docs: ${errorInDoc}`); + pico.red(`Error occurred while migrating docs: ${errorInDoc}`); } try { migrateLatestSidebar(siteDir, newDir, classicPreset, siteConfig); } catch (error) { - console.log(chalk.red(`Error occurred while migrating sidebar: ${error}`)); + console.log(pico.red(`Error occurred while migrating sidebar: ${error}`)); } try { @@ -196,20 +196,20 @@ export async function migrateDocusaurusProject( `module.exports=${JSON.stringify(config, null, 2)}`, ); console.log( - chalk.green( + pico.green( `Successfully created a new config file with new navbar and footer config`, ), ); } catch (error) { console.log( - chalk.red(`Error occurred while creating config file: ${error}`), + pico.red(`Error occurred while creating config file: ${error}`), ); } try { migratePackageFile(siteDir, deps, newDir); } catch (error) { console.log( - chalk.red( + pico.red( `Error occurred while creating package.json file for project: ${error}`, ), ); @@ -274,9 +274,9 @@ export function createConfigFile({ } }); console.log( - `${chalk.yellow( + `${pico.yellow( 'Following Fields from siteConfig.js will be added to docusaurus.config.js in `customFields`', - )}\n${chalk.yellow(Object.keys(customConfigFields).join('\n'))}`, + )}\n${pico.yellow(Object.keys(customConfigFields).join('\n'))}`, ); let v2DocsPath: string | undefined; @@ -414,7 +414,7 @@ function createPages(newDir: string, siteDir: string): void { fs.writeFileSync(filePath, migratePage(content)); }); } catch (error) { - console.log(chalk.red(`Unable to migrate Pages : ${error}`)); + console.log(pico.red(`Unable to migrate Pages : ${error}`)); createDefaultLandingPage(newDir); } } else { @@ -457,12 +457,12 @@ function migrateBlogFiles( }); classicPreset.blog.path = 'blog'; console.log( - chalk.green( + pico.green( `Successfully migrated blogs to version 2 with change in frontmatter`, ), ); } else { - console.log(chalk.yellow(`Blog not found. Skipping migration for blog`)); + console.log(pico.yellow(`Blog not found. Skipping migration for blog`)); } } @@ -494,7 +494,7 @@ function handleVersioning( migrateMDFiles, ); console.log( - chalk.green( + pico.green( `Successfully migrated version docs and sidebar. The following doc versions have been created: \n${loadedVersions.join( '\n', )}`, @@ -502,7 +502,7 @@ function handleVersioning( ); } else { console.log( - chalk.yellow( + pico.yellow( 'Versioned docs not found. Skipping migration for versioned docs', ), ); @@ -694,7 +694,7 @@ function migrateLatestSidebar( ); } catch { console.log( - chalk.yellow(`Sidebar not found. Skipping migration for sidebar`), + pico.yellow(`Sidebar not found. Skipping migration for sidebar`), ); } if (siteConfig.colors) { @@ -736,10 +736,10 @@ function migrateLatestDocs( const content = String(fs.readFileSync(file)); fs.writeFileSync(file, sanitizedFileContent(content, migrateMDFiles)); }); - console.log(chalk.green(`Successfully migrated docs to version 2`)); + console.log(pico.green(`Successfully migrated docs to version 2`)); } else { console.log( - chalk.yellow(`Docs folder not found. Skipping migration for docs`), + pico.yellow(`Docs folder not found. Skipping migration for docs`), ); } } @@ -778,7 +778,7 @@ function migratePackageFile( path.join(newDir, 'package.json'), JSON.stringify(packageFile, null, 2), ); - console.log(chalk.green(`Successfully migrated package.json file`)); + console.log(pico.green(`Successfully migrated package.json file`)); } export async function migrateMDToMDX( diff --git a/packages/docusaurus-plugin-client-redirects/package.json b/packages/docusaurus-plugin-client-redirects/package.json index a86b71664ef4..4419957b1ecc 100644 --- a/packages/docusaurus-plugin-client-redirects/package.json +++ b/packages/docusaurus-plugin-client-redirects/package.json @@ -23,10 +23,10 @@ "@docusaurus/utils": "2.0.0-beta.9", "@docusaurus/utils-common": "2.0.0-beta.9", "@docusaurus/utils-validation": "2.0.0-beta.9", - "chalk": "^4.1.2", "eta": "^1.12.3", "fs-extra": "^10.0.0", "lodash": "^4.17.20", + "picocolors": "^1.0.0", "tslib": "^2.3.1" }, "peerDependencies": { diff --git a/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts b/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts index 327d608bd945..ff5c6d5899cc 100644 --- a/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts +++ b/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts @@ -22,7 +22,7 @@ import { ApplyTrailingSlashParams, } from '@docusaurus/utils-common'; -import chalk from 'chalk'; +import pico from 'picocolors'; export default function collectRedirects( pluginContext: PluginContext, @@ -100,7 +100,7 @@ function filterUnwantedRedirects( ([from, groupedFromRedirects]) => { if (groupedFromRedirects.length > 1) { console.error( - chalk.red( + pico.red( `@docusaurus/plugin-client-redirects: multiple redirects are created with the same "from" pathname=${from} It is not possible to redirect the same pathname to multiple destinations: - ${groupedFromRedirects.map((r) => JSON.stringify(r)).join('\n- ')} @@ -118,7 +118,7 @@ It is not possible to redirect the same pathname to multiple destinations: ); if (redirectsOverridingExistingPath.length > 0) { console.error( - chalk.red( + pico.red( `@docusaurus/plugin-client-redirects: some redirects would override existing paths, and will be ignored: - ${redirectsOverridingExistingPath.map((r) => JSON.stringify(r)).join('\n- ')} `, diff --git a/packages/docusaurus-plugin-content-blog/package.json b/packages/docusaurus-plugin-content-blog/package.json index 53f9202093da..6bfb6b22c608 100644 --- a/packages/docusaurus-plugin-content-blog/package.json +++ b/packages/docusaurus-plugin-content-blog/package.json @@ -23,7 +23,6 @@ "@docusaurus/types": "2.0.0-beta.9", "@docusaurus/utils": "2.0.0-beta.9", "@docusaurus/utils-validation": "2.0.0-beta.9", - "chalk": "^4.1.2", "escape-string-regexp": "^4.0.0", "feed": "^4.2.2", "fs-extra": "^10.0.0", @@ -31,6 +30,7 @@ "js-yaml": "^4.0.0", "loader-utils": "^2.0.0", "lodash": "^4.17.20", + "picocolors": "^1.0.0", "reading-time": "^1.5.0", "remark-admonitions": "^1.2.1", "tslib": "^2.3.1", diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index ab77371e3d3f..7e59f9b5a87e 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -6,7 +6,7 @@ */ import fs from 'fs-extra'; -import chalk from 'chalk'; +import pico from 'picocolors'; import path from 'path'; import {Author, BlogContentPaths} from './types'; import {findFolderContainingFile} from '@docusaurus/utils'; @@ -48,7 +48,7 @@ export async function readAuthorsMapFile( return validateAuthorsMapFile(unsafeContent); } catch (e) { // TODO replace later by error cause: see https://v8.dev/features/error-cause - console.error(chalk.red('The author list file looks invalid!')); + console.error(pico.red('The author list file looks invalid!')); throw e; } } @@ -89,7 +89,7 @@ export async function getAuthorsMap( } catch (e) { // TODO replace later by error cause, see https://v8.dev/features/error-cause console.error( - chalk.red(`Couldn't read blog authors map at path ${filePath}`), + pico.red(`Couldn't read blog authors map at path ${filePath}`), ); throw e; } diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index d7372ad9a77d..67d6a2808e0f 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -6,7 +6,7 @@ */ import fs from 'fs-extra'; -import chalk from 'chalk'; +import pico from 'picocolors'; import path from 'path'; import readingTime from 'reading-time'; import {keyBy, mapValues} from 'lodash'; @@ -152,7 +152,7 @@ async function processBlogSourceFile( if (frontMatter.id) { console.warn( - chalk.yellow( + pico.yellow( `"id" header option is deprecated in ${blogSourceRelative} file. Please use "slug" option instead.`, ), ); @@ -277,7 +277,7 @@ export async function generateBlogPosts( ); } catch (e) { console.error( - chalk.red( + pico.red( `Processing of blog source file failed for path "${blogSourceFile}"`, ), ); diff --git a/packages/docusaurus-plugin-content-docs/package.json b/packages/docusaurus-plugin-content-docs/package.json index ee791cbb155f..d3a07b88cd5e 100644 --- a/packages/docusaurus-plugin-content-docs/package.json +++ b/packages/docusaurus-plugin-content-docs/package.json @@ -31,7 +31,6 @@ "@docusaurus/types": "2.0.0-beta.9", "@docusaurus/utils": "2.0.0-beta.9", "@docusaurus/utils-validation": "2.0.0-beta.9", - "chalk": "^4.1.2", "combine-promises": "^1.1.0", "escape-string-regexp": "^4.0.0", "fs-extra": "^10.0.0", @@ -40,6 +39,7 @@ "js-yaml": "^4.0.0", "loader-utils": "^2.0.0", "lodash": "^4.17.20", + "picocolors": "^1.0.0", "remark-admonitions": "^1.2.1", "shelljs": "^0.8.4", "tslib": "^2.3.1", diff --git a/packages/docusaurus-plugin-content-docs/src/docs.ts b/packages/docusaurus-plugin-content-docs/src/docs.ts index 8fb324535be7..cbeb9776d917 100644 --- a/packages/docusaurus-plugin-content-docs/src/docs.ts +++ b/packages/docusaurus-plugin-content-docs/src/docs.ts @@ -7,7 +7,7 @@ import path from 'path'; import fs from 'fs-extra'; -import chalk from 'chalk'; +import pico from 'picocolors'; import {keyBy} from 'lodash'; import { aliasedSitePath, @@ -283,7 +283,7 @@ export function processDocMetadata(args: { return doProcessDocMetadata(args); } catch (e) { console.error( - chalk.red( + pico.red( `Can't process doc metadata for doc at path "${args.docFile.filePath}" in version "${args.versionMetadata.versionName}"`, ), ); diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index b239e3d991d6..0893dec97bf2 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -46,7 +46,7 @@ import { translateLoadedContent, getLoadedContentTranslationFiles, } from './translations'; -import chalk from 'chalk'; +import pico from 'picocolors'; import {getVersionTags} from './tags'; import type {PropTagsListPage} from '@docusaurus/plugin-content-docs'; @@ -187,7 +187,7 @@ export default function pluginContentDocs( return await doLoadVersion(versionMetadata); } catch (e) { console.error( - chalk.red( + pico.red( `Loading of version failed for version "${versionMetadata.versionName}"`, ), ); @@ -330,7 +330,7 @@ export default function pluginContentDocs( return await doCreateVersionRoutes(loadedVersion); } catch (e) { console.error( - chalk.red( + pico.red( `Can't create version routes for version "${loadedVersion.versionName}"`, ), ); diff --git a/packages/docusaurus-plugin-content-docs/src/options.ts b/packages/docusaurus-plugin-content-docs/src/options.ts index d465b18e656a..9255e3dba12d 100644 --- a/packages/docusaurus-plugin-content-docs/src/options.ts +++ b/packages/docusaurus-plugin-content-docs/src/options.ts @@ -18,7 +18,7 @@ import type { OptionValidationContext, ValidationResult, } from '@docusaurus/types'; -import chalk from 'chalk'; +import pico from 'picocolors'; import admonitions from 'remark-admonitions'; import {DefaultSidebarItemsGenerator} from './sidebars/generator'; import { @@ -150,7 +150,7 @@ export function validateOptions({ } if (options.sidebarCollapsed) { console.warn( - chalk.yellow( + pico.yellow( 'The docs plugin config is inconsistent. It does not make sense to use sidebarCollapsible=false and sidebarCollapsed=true at the same time. sidebarCollapsed=false will be ignored.', ), ); @@ -165,7 +165,7 @@ export function validateOptions({ // "slug: /" is better because the home doc can be different across versions if (options.homePageId) { console.log( - chalk.red( + pico.red( `The docs plugin option homePageId=${options.homePageId} is deprecated. To make a doc the "home", prefer frontmatter: "slug: /"`, ), ); diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts index a936980a8387..1719eb7df94e 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts @@ -15,7 +15,7 @@ import type { import {keyBy, sortBy} from 'lodash'; import {addTrailingSlash, posixPath} from '@docusaurus/utils'; import {Joi} from '@docusaurus/utils-validation'; -import chalk from 'chalk'; +import pico from 'picocolors'; import path from 'path'; import fs from 'fs-extra'; import Yaml from 'js-yaml'; @@ -72,7 +72,7 @@ async function readCategoryMetadataFile( return Joi.attempt(unsafeContent, CategoryMetadataFileSchema); } catch (e) { console.error( - chalk.red( + pico.red( `The docs sidebar category metadata file looks invalid!\nPath: ${filePath}`, ), ); @@ -119,7 +119,7 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({ if (docs.length === 0) { console.warn( - chalk.yellow( + pico.yellow( `No docs found in dir ${autogenDir}: can't auto-generate a sidebar.`, ), ); diff --git a/packages/docusaurus-theme-classic/package.json b/packages/docusaurus-theme-classic/package.json index 6645c006da51..f9e152435301 100644 --- a/packages/docusaurus-theme-classic/package.json +++ b/packages/docusaurus-theme-classic/package.json @@ -33,7 +33,6 @@ "@docusaurus/utils-validation": "2.0.0-beta.9", "@mdx-js/mdx": "^1.6.21", "@mdx-js/react": "^1.6.21", - "chalk": "^4.1.2", "clsx": "^1.1.1", "copy-text-to-clipboard": "^3.0.1", "globby": "^11.0.2", diff --git a/packages/docusaurus-theme-translations/package.json b/packages/docusaurus-theme-translations/package.json index b832a6e249f7..0bcedd845c1b 100644 --- a/packages/docusaurus-theme-translations/package.json +++ b/packages/docusaurus-theme-translations/package.json @@ -23,9 +23,9 @@ }, "dependencies": { "@docusaurus/core": "2.0.0-beta.9", - "chalk": "^4.1.2", "fs-extra": "^10.0.0", - "lodash": "^4.17.20" + "lodash": "^4.17.20", + "picocolors": "^1.0.0" }, "engines": { "node": ">=14" diff --git a/packages/docusaurus-theme-translations/update.js b/packages/docusaurus-theme-translations/update.js index 74b5b2086619..760c17d3c86f 100644 --- a/packages/docusaurus-theme-translations/update.js +++ b/packages/docusaurus-theme-translations/update.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -const chalk = require('chalk'); +const pico = require('picocolors'); const path = require('path'); const fs = require('fs-extra'); const {mapValues, pickBy, difference, orderBy} = require('lodash'); @@ -71,7 +71,7 @@ function logSection(title) { console.log(``); console.log(``); console.log(`##############################`); - console.log(`## ${chalk.blue(title)}`); + console.log(`## ${pico.blue(title)}`); } function logKeys(keys) { @@ -165,7 +165,7 @@ async function updateBaseFile(baseFile, targetDirs) { if (unknownMessages.length) { console.log( - chalk.red(`Some messages exist in base locale but were not found by the code extractor! + pico.red(`Some messages exist in base locale but were not found by the code extractor! They won't be removed automatically, so do the cleanup manually if necessary! ${logKeys(unknownMessages)}`), ); @@ -209,7 +209,7 @@ async function updateLocaleCodeTranslations(localeFile, baseFileMessages) { if (unknownMessages.length) { console.log( - chalk.red(`Some localized messages do not exist in base.json! + pico.red(`Some localized messages do not exist in base.json! You may want to delete these! ${logKeys(unknownMessages)}`), ); @@ -226,7 +226,7 @@ ${logKeys(unknownMessages)}`), if (untranslatedKeys.length) { console.warn( - chalk.yellow(`Some messages do not seem to be translated! + pico.yellow(`Some messages do not seem to be translated! ${logKeys(untranslatedKeys)}`), ); } @@ -247,13 +247,13 @@ async function updateCodeTranslations() { if (!fs.existsSync(newLocalePath)) { await writeMessagesFile(newLocalePath, baseFileMessages); console.error( - chalk.green( + pico.green( `Locale file ${path.basename(newLocalePath)} have been created.`, ), ); } else { console.error( - chalk.red( + pico.red( `Locale file ${path.basename(newLocalePath)} was already created!`, ), ); @@ -276,12 +276,12 @@ function run() { updateCodeTranslations().then( () => { console.log(''); - console.log(chalk.green('updateCodeTranslations end')); + console.log(pico.green('updateCodeTranslations end')); console.log(''); }, (e) => { console.log(''); - console.error(chalk.red(`updateCodeTranslations failure: ${e.message}`)); + console.error(pico.red(`updateCodeTranslations failure: ${e.message}`)); console.log(''); console.error(e.stack); console.log(''); diff --git a/packages/docusaurus-utils-validation/package.json b/packages/docusaurus-utils-validation/package.json index 92b40fa560f7..050ff2782448 100644 --- a/packages/docusaurus-utils-validation/package.json +++ b/packages/docusaurus-utils-validation/package.json @@ -19,8 +19,8 @@ "license": "MIT", "dependencies": { "@docusaurus/utils": "2.0.0-beta.9", - "chalk": "^4.1.2", "joi": "^17.4.2", + "picocolors": "^1.0.0", "tslib": "^2.3.1" }, "engines": { diff --git a/packages/docusaurus-utils-validation/src/validationUtils.ts b/packages/docusaurus-utils-validation/src/validationUtils.ts index d530b165a2df..c9ecca789217 100644 --- a/packages/docusaurus-utils-validation/src/validationUtils.ts +++ b/packages/docusaurus-utils-validation/src/validationUtils.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ import Joi from './Joi'; -import chalk from 'chalk'; +import pico from 'picocolors'; import {PluginIdSchema} from './validationSchemas'; // TODO temporary escape hatch for alpha-60: to be removed soon @@ -19,7 +19,7 @@ export const isValidationDisabledEscapeHatch = if (isValidationDisabledEscapeHatch) { console.error( - chalk.red( + pico.red( 'You should avoid using DISABLE_DOCUSAURUS_VALIDATION escape hatch, this will be removed.', ), ); @@ -27,7 +27,7 @@ if (isValidationDisabledEscapeHatch) { export const logValidationBugReportHint = (): void => { console.log( - `\n${chalk.red('A validation error occurred.')}${chalk.cyanBright( + `\n${pico.red('A validation error occurred.')}${pico.cyan( '\nThe validation system was added recently to Docusaurus as an attempt to avoid user configuration errors.' + '\nWe may have made some mistakes.' + '\nIf you think your configuration is valid and should keep working, please open a bug report.', @@ -40,7 +40,7 @@ export function printWarning(warning?: Joi.ValidationError): void { const warningMessages = warning.details .map(({message}) => message) .join('\n'); - console.log(chalk.yellow(warningMessages)); + console.log(pico.yellow(warningMessages)); } } @@ -122,8 +122,8 @@ export function validateFrontMatter( logValidationBugReportHint(); console.error( - chalk.red( - `The following frontmatter:\n${chalk.yellow( + pico.red( + `The following frontmatter:\n${pico.yellow( frontMatterString, )}\ncontains invalid values for field(s): ${invalidFields}.\n${errorMessages}\n`, ), diff --git a/packages/docusaurus-utils/package.json b/packages/docusaurus-utils/package.json index 2f311bd33bd8..9dba213e0563 100644 --- a/packages/docusaurus-utils/package.json +++ b/packages/docusaurus-utils/package.json @@ -20,13 +20,13 @@ "dependencies": { "@docusaurus/types": "2.0.0-beta.9", "@mdx-js/runtime": "^1.6.22", - "chalk": "^4.1.2", "escape-string-regexp": "^4.0.0", "fs-extra": "^10.0.0", "globby": "^11.0.4", "gray-matter": "^4.0.3", "lodash": "^4.17.20", "micromatch": "^4.0.4", + "picocolors": "^1.0.0", "remark-mdx-remove-exports": "^1.6.22", "remark-mdx-remove-imports": "^1.6.22", "resolve-pathname": "^3.0.0", diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index 3bacc56b805a..bf62f3043b48 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import chalk from 'chalk'; +import pico from 'picocolors'; import path from 'path'; import {createHash} from 'crypto'; import {camelCase, mapValues} from 'lodash'; @@ -368,13 +368,13 @@ export function reportMessage( case 'ignore': break; case 'log': - console.log(chalk.bold.blue('info ') + chalk.blue(message)); + console.log(pico.bold(pico.blue('info ')) + pico.blue(message)); break; case 'warn': - console.warn(chalk.bold.yellow('warn ') + chalk.yellow(message)); + console.warn(pico.bold(pico.yellow('warn ')) + pico.yellow(message)); break; case 'error': - console.error(chalk.bold.red('error ') + chalk.red(message)); + console.error(pico.bold(pico.red('error ')) + pico.red(message)); break; case 'throw': throw new Error(message); diff --git a/packages/docusaurus-utils/src/markdownParser.ts b/packages/docusaurus-utils/src/markdownParser.ts index 3649b123e836..aefdda272ea8 100644 --- a/packages/docusaurus-utils/src/markdownParser.ts +++ b/packages/docusaurus-utils/src/markdownParser.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import chalk from 'chalk'; +import pico from 'picocolors'; import fs from 'fs-extra'; import matter from 'gray-matter'; @@ -167,7 +167,7 @@ export function parseMarkdownString( }; } catch (e) { console.error( - chalk.red(`Error while parsing Markdown frontmatter. + pico.red(`Error while parsing Markdown frontmatter. This can happen if you use special characters in frontmatter values (try using double quotes around that value).`), ); throw e; diff --git a/packages/docusaurus/bin/beforeCli.js b/packages/docusaurus/bin/beforeCli.js index 91a265a4c3c6..ba9eb82b8694 100644 --- a/packages/docusaurus/bin/beforeCli.js +++ b/packages/docusaurus/bin/beforeCli.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -const chalk = require('chalk'); +const pico = require('picocolors'); const fs = require('fs-extra'); const semver = require('semver'); const path = require('path'); @@ -100,11 +100,11 @@ if ( }; const docusaurusUpdateMessage = boxen( - `Update available ${chalk.dim(`${notifier.update.current}`)}${chalk.reset( + `Update available ${pico.dim(`${notifier.update.current}`)}${pico.reset( ' → ', - )}${chalk.green( + )}${pico.green( `${notifier.update.latest}`, - )}\n\nTo upgrade Docusaurus packages with the latest version, run the following command:\n${chalk.cyan( + )}\n\nTo upgrade Docusaurus packages with the latest version, run the following command:\n${pico.cyan( `${upgradeCommand}`, )}`, boxenOptions, @@ -116,8 +116,8 @@ if ( // notify user if node version needs to be updated if (!semver.satisfies(process.version, requiredVersion)) { console.log( - chalk.red(`\nMinimum Node version not met :(`) + - chalk.yellow( + pico.red(`\nMinimum Node version not met :(`) + + pico.yellow( `\n\nYou are using Node ${process.version}. We require Node ${requiredVersion} or up!\n`, ), ); diff --git a/packages/docusaurus/bin/docusaurus.js b/packages/docusaurus/bin/docusaurus.js index 279623514af1..c67558b32652 100755 --- a/packages/docusaurus/bin/docusaurus.js +++ b/packages/docusaurus/bin/docusaurus.js @@ -6,7 +6,7 @@ * LICENSE file in the root directory of this source tree. */ -const chalk = require('chalk'); +const pico = require('picocolors'); const fs = require('fs'); const cli = require('commander'); const { @@ -219,7 +219,7 @@ cli cli.arguments('').action((cmd) => { cli.outputHelp(); - console.log(` ${chalk.red(`\n Unknown command ${chalk.yellow(cmd)}.`)}.`); + console.log(` ${pico.red(`\n Unknown command ${pico.yellow(cmd)}.`)}.`); console.log(); }); @@ -251,6 +251,6 @@ async function run() { run(); process.on('unhandledRejection', (err) => { - console.error(chalk.red(err.stack)); + console.error(pico.red(err.stack)); process.exit(1); }); diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index a8c6f575f00a..af645dd5851a 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -67,7 +67,6 @@ "babel-loader": "^8.2.2", "babel-plugin-dynamic-import-node": "2.3.0", "boxen": "^5.0.1", - "chalk": "^4.1.2", "chokidar": "^3.5.2", "clean-css": "^5.1.5", "commander": "^5.1.0", @@ -93,6 +92,7 @@ "lodash": "^4.17.20", "mini-css-extract-plugin": "^1.6.0", "nprogress": "^0.2.0", + "picocolors": "^1.0.0", "postcss": "^8.3.7", "postcss-loader": "^6.1.1", "prompts": "^2.4.1", diff --git a/packages/docusaurus/src/choosePort.ts b/packages/docusaurus/src/choosePort.ts index 7e430d9909dc..fe26a956713a 100644 --- a/packages/docusaurus/src/choosePort.ts +++ b/packages/docusaurus/src/choosePort.ts @@ -13,7 +13,7 @@ import {execSync} from 'child_process'; import detect from 'detect-port'; import isRoot from 'is-root'; -import chalk from 'chalk'; +import pico from 'picocolors'; import prompts from 'prompts'; const isInteractive = process.stdout.isTTY; @@ -69,10 +69,10 @@ function getProcessForPort(port: number): string | null { const directory = getDirectoryOfProcessById(processId); const command = getProcessCommand(processId); return ( - chalk.cyan(command) + - chalk.grey(` (pid ${processId})\n`) + - chalk.blue(' in ') + - chalk.cyan(directory) + pico.cyan(command) + + pico.gray(` (pid ${processId})\n`) + + pico.blue(' in ') + + pico.cyan(directory) ); } catch (e) { return null; @@ -104,7 +104,7 @@ export default async function choosePort( const question: prompts.PromptObject = { type: 'confirm', name: 'shouldChangePort', - message: `${chalk.yellow( + message: `${pico.yellow( `${message}${ existingProcess ? ` Probably:\n ${existingProcess}` : '' }`, @@ -119,13 +119,13 @@ export default async function choosePort( } }); } else { - console.log(chalk.red(message)); + console.log(pico.red(message)); resolve(null); } }), (err) => { throw new Error( - `${chalk.red(`Could not find an open port at ${chalk.bold(host)}.`)}\n${ + `${pico.red(`Could not find an open port at ${pico.bold(host)}.`)}\n${ `Network error message: "${err.message}".` || err }\n`, ); diff --git a/packages/docusaurus/src/client/serverEntry.js b/packages/docusaurus/src/client/serverEntry.js index 6954b0250c67..923766c9f609 100644 --- a/packages/docusaurus/src/client/serverEntry.js +++ b/packages/docusaurus/src/client/serverEntry.js @@ -24,7 +24,7 @@ import { createStatefulLinksCollector, ProvideLinksCollector, } from './LinksCollector'; -import chalk from 'chalk'; +import pico from 'picocolors'; // eslint-disable-next-line no-restricted-imports import {memoize} from 'lodash'; @@ -44,7 +44,7 @@ export default async function render(locals) { return await doRender(locals); } catch (e) { console.error( - chalk.red( + pico.red( `Docusaurus Node/SSR could not render static page with path "${locals.path}" because of following error:\n\n${e.stack}\n`, ), ); @@ -54,7 +54,7 @@ export default async function render(locals) { if (isNotDefinedErrorRegex.test(e.message)) { console.error( - chalk.green( + pico.green( 'Pro tip: It looks like you are using code that should run on the client-side only.\nTo get around it, try using (https://docusaurus.io/docs/docusaurus-core/#browseronly) or ExecutionEnvironment (https://docusaurus.io/docs/docusaurus-core/#executionenvironment).\nIt might also require to wrap your client code in useEffect hook and/or import a third-party library dynamically (if any).', ), ); @@ -143,7 +143,7 @@ async function doRender(locals) { }); } catch (e) { console.error( - chalk.red( + pico.red( `Minification page with path "${locals.path}" failed because of following error:\n\n${e.stack}\n`, ), ); diff --git a/packages/docusaurus/src/commands/build.ts b/packages/docusaurus/src/commands/build.ts index cd5830dc469a..9b4093da3448 100644 --- a/packages/docusaurus/src/commands/build.ts +++ b/packages/docusaurus/src/commands/build.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import chalk from 'chalk'; +import pico from 'picocolors'; import CopyWebpackPlugin from 'copy-webpack-plugin'; import fs from 'fs-extra'; import path from 'path'; @@ -47,7 +47,7 @@ export default async function build( isLastLocale: boolean; }) { try { - // console.log(chalk.green(`Site successfully built in locale=${locale}`)); + // console.log(pico.green(`Site successfully built in locale=${locale}`)); return await buildLocale({ siteDir, locale, @@ -74,7 +74,7 @@ export default async function build( } else { if (i18n.locales.length > 1) { console.log( - chalk.yellow( + pico.yellow( `\nWebsite will be built for all these locales: - ${i18n.locales.join('\n- ')}`, ), @@ -113,7 +113,7 @@ async function buildLocale({ process.env.BABEL_ENV = 'production'; process.env.NODE_ENV = 'production'; console.log( - chalk.blue(`\n[${locale}] Creating an optimized production build...`), + pico.blue(`\n[${locale}] Creating an optimized production build...`), ); const props: Props = await load(siteDir, { @@ -239,14 +239,14 @@ async function buildLocale({ }); console.log( - `${chalk.green(`Success!`)} Generated static files in "${chalk.cyan( + `${pico.green(`Success!`)} Generated static files in "${pico.cyan( path.relative(process.cwd(), outDir), )}".`, ); if (isLastLocale) { console.log( - `\nUse ${chalk.greenBright( + `\nUse ${pico.green( '`npm run serve`', )} command to test your build locally.\n`, ); diff --git a/packages/docusaurus/src/commands/clear.ts b/packages/docusaurus/src/commands/clear.ts index 4486ac9b414a..32b6f44f4983 100644 --- a/packages/docusaurus/src/commands/clear.ts +++ b/packages/docusaurus/src/commands/clear.ts @@ -6,14 +6,14 @@ */ import fs from 'fs-extra'; import path from 'path'; -import chalk = require('chalk'); +import pico from 'picocolors'; import {DEFAULT_BUILD_DIR_NAME, GENERATED_FILES_DIR_NAME} from '../constants'; function removePath(fsPath: string) { return fs .remove(path.join(fsPath)) .then(() => { - console.log(chalk.green(`Successfully removed "${fsPath}" directory.`)); + console.log(pico.green(`Successfully removed "${fsPath}" directory.`)); }) .catch((err) => { console.error(`Could not remove ${fsPath} directory.`); diff --git a/packages/docusaurus/src/commands/deploy.ts b/packages/docusaurus/src/commands/deploy.ts index e98f01bdec5e..cdd048b207f9 100644 --- a/packages/docusaurus/src/commands/deploy.ts +++ b/packages/docusaurus/src/commands/deploy.ts @@ -7,7 +7,7 @@ import fs from 'fs-extra'; import shell from 'shelljs'; -import chalk from 'chalk'; +import pico from 'picocolors'; import {loadContext} from '../server'; import build from './build'; import {BuildCLIOptions} from '@docusaurus/types'; @@ -26,13 +26,13 @@ function shellExecLog(cmd: string) { try { const result = shell.exec(cmd); console.log( - `${chalk.cyan('CMD:')} ${obfuscateGitPass(cmd)} ${chalk.cyan( + `${pico.cyan('CMD:')} ${obfuscateGitPass(cmd)} ${pico.cyan( `(code: ${result.code})`, )}`, ); return result; } catch (e) { - console.log(`${chalk.red('CMD:')} ${obfuscateGitPass(cmd)}`); + console.log(`${pico.red('CMD:')} ${obfuscateGitPass(cmd)}`); throw e; } } @@ -85,7 +85,7 @@ export default async function deploy( if (typeof siteConfig.trailingSlash === 'undefined') { console.warn( - chalk.yellow(` + pico.yellow(` Docusaurus recommendation: When deploying to GitHub Pages, it is better to use an explicit "trailingSlash" site config. Otherwise, GitHub Pages will add an extra trailing slash to your site urls only on direct-access (not when navigation) with a server redirect. @@ -135,7 +135,7 @@ This behavior can have SEO impacts and create relative link issues. `Missing project organization name. Did you forget to define "organizationName" in ${siteConfigPath}? You may also export it via the ORGANIZATION_NAME environment variable.`, ); } - console.log(`${chalk.cyan('organizationName:')} ${organizationName}`); + console.log(`${pico.cyan('organizationName:')} ${organizationName}`); const projectName = process.env.PROJECT_NAME || @@ -146,7 +146,7 @@ This behavior can have SEO impacts and create relative link issues. `Missing project name. Did you forget to define "projectName" in ${siteConfigPath}? You may also export it via the PROJECT_NAME environment variable.`, ); } - console.log(`${chalk.cyan('projectName:')} ${projectName}`); + console.log(`${pico.cyan('projectName:')} ${projectName}`); // We never deploy on pull request. const isPullRequest = @@ -173,7 +173,7 @@ You can also set the deploymentBranch property in docusaurus.config.js .`); const deploymentBranch = process.env.DEPLOYMENT_BRANCH || siteConfig.deploymentBranch || 'gh-pages'; - console.log(`${chalk.cyan('deploymentBranch:')} ${deploymentBranch}`); + console.log(`${pico.cyan('deploymentBranch:')} ${deploymentBranch}`); const githubHost = process.env.GITHUB_HOST || siteConfig.githubHost || 'github.com'; @@ -200,7 +200,7 @@ You can also set the deploymentBranch property in docusaurus.config.js .`); } console.log( - `${chalk.cyan('Remote repo URL:')} ${obfuscateGitPass(deploymentRepoURL)}`, + `${pico.cyan('Remote repo URL:')} ${obfuscateGitPass(deploymentRepoURL)}`, ); // Check if this is a cross-repo publish. diff --git a/packages/docusaurus/src/commands/serve.ts b/packages/docusaurus/src/commands/serve.ts index 037b1899ac7b..cf9ff1f53fd0 100644 --- a/packages/docusaurus/src/commands/serve.ts +++ b/packages/docusaurus/src/commands/serve.ts @@ -8,7 +8,7 @@ import http from 'http'; import serveHandler from 'serve-handler'; import boxen from 'boxen'; -import chalk from 'chalk'; +import pico from 'picocolors'; import path from 'path'; import {loadSiteConfig} from '../server'; import build from './build'; @@ -73,7 +73,7 @@ export default async function serve( console.log( boxen( - chalk.green( + pico.green( `Serving "${cliOptions.dir}" directory at "${servingUrl + baseUrl}".`, ), { diff --git a/packages/docusaurus/src/commands/start.ts b/packages/docusaurus/src/commands/start.ts index a8ac0ed6d11b..a251e1d47389 100644 --- a/packages/docusaurus/src/commands/start.ts +++ b/packages/docusaurus/src/commands/start.ts @@ -6,7 +6,7 @@ */ import {normalizeUrl, posixPath} from '@docusaurus/utils'; -import chalk = require('chalk'); +import pico from 'picocolors'; import chokidar from 'chokidar'; import HtmlWebpackPlugin from 'html-webpack-plugin'; @@ -35,7 +35,7 @@ export default async function start( ): Promise { process.env.NODE_ENV = 'development'; process.env.BABEL_ENV = 'development'; - console.log(chalk.blue('Starting the development server...')); + console.log(pico.blue('Starting the development server...')); function loadSite() { return load(siteDir, { @@ -61,9 +61,7 @@ export default async function start( const urls = prepareUrls(protocol, host, port); const openUrl = normalizeUrl([urls.localUrlForBrowser, baseUrl]); - console.log( - chalk.cyanBright(`Docusaurus website is running at "${openUrl}".`), - ); + console.log(pico.cyan(`Docusaurus website is running at "${openUrl}".`)); // Reload files processing. const reload = debounce(() => { @@ -72,14 +70,12 @@ export default async function start( const newOpenUrl = normalizeUrl([urls.localUrlForBrowser, newBaseUrl]); if (newOpenUrl !== openUrl) { console.log( - chalk.cyanBright( - `Docusaurus website is running at "${newOpenUrl}".`, - ), + pico.cyan(`Docusaurus website is running at "${newOpenUrl}".`), ); } }) .catch((err) => { - console.error(chalk.red(err.stack)); + console.error(pico.red(err.stack)); }); }, 500); const {siteConfig, plugins = []} = props; diff --git a/packages/docusaurus/src/commands/swizzle.ts b/packages/docusaurus/src/commands/swizzle.ts index 49b1bd719564..ecd442f7ff80 100644 --- a/packages/docusaurus/src/commands/swizzle.ts +++ b/packages/docusaurus/src/commands/swizzle.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import chalk = require('chalk'); +import pico from 'picocolors'; import fs from 'fs-extra'; import importFresh from 'import-fresh'; import path from 'path'; @@ -100,14 +100,14 @@ function themeComponents( const components = colorCode(themePath, plugin); if (components.length === 0) { - return `${chalk.red('No component to swizzle.')}`; + return `${pico.red('No component to swizzle.')}`; } return ` -${chalk.cyan('Theme components available for swizzle.')} +${pico.cyan('Theme components available for swizzle.')} -${chalk.green('green =>')} safe: lower breaking change risk -${chalk.red('red =>')} unsafe: higher breaking change risk +${pico.green('green =>')} safe: lower breaking change risk +${pico.red('red =>')} unsafe: higher breaking change risk ${components.join('\n')} `; @@ -135,8 +135,8 @@ function colorCode( ); return [ - ...greenComponents.map((component) => chalk.green(`safe: ${component}`)), - ...redComponents.map((component) => chalk.red(`unsafe: ${component}`)), + ...greenComponents.map((component) => pico.green(`safe: ${component}`)), + ...redComponents.map((component) => pico.red(`unsafe: ${component}`)), ]; } @@ -175,7 +175,7 @@ export default async function swizzle( suggestion = name; } }); - chalk.red( + pico.red( `Theme ${themeName} not found. ${ suggestion ? `Did you mean "${suggestion}" ?` @@ -219,7 +219,7 @@ export default async function swizzle( if (!themePath) { console.warn( - chalk.yellow( + pico.yellow( typescript ? `${themeName} does not provide TypeScript theme code via "getTypeScriptThemePath()".` : `${themeName} does not provide any theme code.`, @@ -257,8 +257,8 @@ export default async function swizzle( if (mostSuitableMatch !== componentName) { mostSuitableComponent = mostSuitableMatch; console.log( - chalk.red(`Component "${componentName}" doesn't exist.`), - chalk.yellow( + pico.red(`Component "${componentName}" doesn't exist.`), + pico.yellow( `"${mostSuitableComponent}" is swizzled instead of "${componentName}".`, ), ); @@ -283,7 +283,7 @@ export default async function swizzle( suggestion = name; } }); - console.warn(chalk.red(`Component ${mostSuitableComponent} not found.`)); + console.warn(pico.red(`Component ${mostSuitableComponent} not found.`)); console.warn( suggestion ? `Did you mean "${suggestion}"?` @@ -295,7 +295,7 @@ export default async function swizzle( if (!components.includes(mostSuitableComponent) && !danger) { console.warn( - chalk.red( + pico.red( `${mostSuitableComponent} is an internal component and has a higher breaking change probability. If you want to swizzle it, use the "--danger" flag.`, ), ); @@ -305,12 +305,12 @@ export default async function swizzle( await fs.copy(fromPath, toPath); const relativeDir = path.relative(process.cwd(), toPath); - const fromMsg = chalk.blue( + const fromMsg = pico.blue( mostSuitableComponent - ? `${themeName} ${chalk.yellow(mostSuitableComponent)}` + ? `${themeName} ${pico.yellow(mostSuitableComponent)}` : themeName, ); - const toMsg = chalk.cyan(relativeDir); + const toMsg = pico.cyan(relativeDir); - console.log(`\n${chalk.green('Success!')} Copied ${fromMsg} to ${toMsg}.\n`); + console.log(`\n${pico.green('Success!')} Copied ${fromMsg} to ${toMsg}.\n`); } diff --git a/packages/docusaurus/src/commands/writeHeadingIds.ts b/packages/docusaurus/src/commands/writeHeadingIds.ts index dddc9ee870d4..078a35ef60d2 100644 --- a/packages/docusaurus/src/commands/writeHeadingIds.ts +++ b/packages/docusaurus/src/commands/writeHeadingIds.ts @@ -7,7 +7,7 @@ import fs from 'fs-extra'; import GithubSlugger from 'github-slugger'; -import chalk from 'chalk'; +import pico from 'picocolors'; import {loadContext, loadPluginConfigs} from '../server'; import initPlugins from '../server/plugins/init'; @@ -148,17 +148,17 @@ export default async function writeHeadingIds( if (pathsModified.length) { console.log( - chalk.green(`Heading ids added to Markdown files (${ + pico.green(`Heading ids added to Markdown files (${ pathsModified.length }/${markdownFiles.length} files): - ${pathsModified.join('\n- ')}`), ); } else { console.log( - chalk.yellow( + pico.yellow( `${ markdownFiles.length - } Markdown files already have explicit heading IDs. If you intend to overwrite the existing heading IDs, use the ${chalk.cyan( + } Markdown files already have explicit heading IDs. If you intend to overwrite the existing heading IDs, use the ${pico.cyan( '--overwrite', )} option.`, ), diff --git a/packages/docusaurus/src/server/i18n.ts b/packages/docusaurus/src/server/i18n.ts index b8ee4dca07d8..9e6ae0178c6c 100644 --- a/packages/docusaurus/src/server/i18n.ts +++ b/packages/docusaurus/src/server/i18n.ts @@ -9,7 +9,7 @@ import path from 'path'; import {normalizeUrl} from '@docusaurus/utils'; import {getLangDir} from 'rtl-detect'; import {NODE_MAJOR_VERSION} from '../constants'; -import chalk from 'chalk'; +import pico from 'picocolors'; function getDefaultLocaleLabel(locale: string) { // Intl.DisplayNames is ES2021 - Node14+ @@ -46,7 +46,7 @@ export async function loadI18n( if (!i18nConfig.locales.includes(currentLocale)) { console.warn( - chalk.yellow( + pico.yellow( `The locale "${currentLocale}" was not found in your site configuration: Available locales are: ${i18nConfig.locales.join( ',', )}. @@ -61,7 +61,7 @@ Note: Docusaurus only support running one locale at a time.`, if (shouldWarnAboutNodeVersion(NODE_MAJOR_VERSION, locales)) { console.warn( - chalk.yellow( + pico.yellow( `To use Docusaurus i18n, it is strongly advised to use Node.js 14 or later (instead of ${NODE_MAJOR_VERSION}).`, ), ); diff --git a/packages/docusaurus/src/server/index.ts b/packages/docusaurus/src/server/index.ts index f48c3b89d85b..be56c25bb37a 100644 --- a/packages/docusaurus/src/server/index.ts +++ b/packages/docusaurus/src/server/index.ts @@ -7,7 +7,7 @@ import {generate} from '@docusaurus/utils'; import path, {join} from 'path'; -import chalk from 'chalk'; +import pico from 'picocolors'; import ssrDefaultTemplate from '../client/templates/ssr.html.template'; import { DEFAULT_BUILD_DIR_NAME, @@ -419,7 +419,7 @@ function checkDocusaurusPackagesVersion(siteMetadata: DocusaurusSiteMetadata) { // should we throw instead? // It still could work with different versions console.warn( - chalk.red( + pico.red( `Invalid ${plugin} version ${versionInfo.version}.\nAll official @docusaurus/* packages should have the exact same version as @docusaurus/core (${docusaurusVersion}).\nMaybe you want to check, or regenerate your yarn.lock or package-lock.json file?`, ), ); diff --git a/packages/docusaurus/src/server/plugins/index.ts b/packages/docusaurus/src/server/plugins/index.ts index 751900e8f1dd..162acff8924c 100644 --- a/packages/docusaurus/src/server/plugins/index.ts +++ b/packages/docusaurus/src/server/plugins/index.ts @@ -20,7 +20,7 @@ import { InitializedPlugin, } from '@docusaurus/types'; import initPlugins from './init'; -import chalk from 'chalk'; +import pico from 'picocolors'; import {DEFAULT_PLUGIN_ID} from '../../constants'; import {chain} from 'lodash'; import {localizePluginTranslationFile} from '../translations/translations'; @@ -213,7 +213,7 @@ export async function loadPlugins({ // deprecated since alpha-60 // TODO, 1 user reported usage of this lifecycle! https://github.com/facebook/docusaurus/issues/3918 console.error( - chalk.red( + pico.red( 'Plugin routesLoaded lifecycle is deprecated. If you think we should keep this lifecycle, please report here: https://github.com/facebook/docusaurus/issues/3918', ), ); diff --git a/packages/docusaurus/src/server/translations/translations.ts b/packages/docusaurus/src/server/translations/translations.ts index 5b71db74d951..4fb971b07594 100644 --- a/packages/docusaurus/src/server/translations/translations.ts +++ b/packages/docusaurus/src/server/translations/translations.ts @@ -15,7 +15,7 @@ import { } from '@docusaurus/types'; import {getPluginI18nPath, toMessageRelativeFilePath} from '@docusaurus/utils'; import {Joi} from '@docusaurus/utils-validation'; -import chalk from 'chalk'; +import pico from 'picocolors'; export type WriteTranslationsOptions = { override?: boolean; @@ -115,7 +115,7 @@ export async function writeTranslationFileContent({ ); if (unknownKeys.length > 0) { console.warn( - chalk.yellow(`Some translation keys looks unknown to us in file ${filePath} + pico.yellow(`Some translation keys looks unknown to us in file ${filePath} Maybe you should remove them? - ${unknownKeys.join('\n- ')}`), ); @@ -290,7 +290,7 @@ export function applyDefaultCodeTranslations({ ); if (unusedDefaultCodeMessages.length > 0) { console.warn( - chalk.yellow(`Unused default message codes found. + pico.yellow(`Unused default message codes found. Please report this Docusaurus issue. - ${unusedDefaultCodeMessages.join('\n- ')} `), diff --git a/packages/docusaurus/src/server/translations/translationsExtractor.ts b/packages/docusaurus/src/server/translations/translationsExtractor.ts index a0eb5f730350..c450b01a1716 100644 --- a/packages/docusaurus/src/server/translations/translationsExtractor.ts +++ b/packages/docusaurus/src/server/translations/translationsExtractor.ts @@ -7,7 +7,7 @@ import fs from 'fs-extra'; import traverse, {Node} from '@babel/traverse'; import generate from '@babel/generator'; -import chalk from 'chalk'; +import pico from 'picocolors'; import {parse, types as t, NodePath, TransformOptions} from '@babel/core'; import { InitializedPlugin, @@ -115,7 +115,7 @@ function logSourceCodeFileTranslationsWarnings( sourceCodeFilesTranslations.forEach(({sourceCodeFilePath, warnings}) => { if (warnings.length > 0) { console.warn( - `Translation extraction warnings for file path=${sourceCodeFilePath}:\n- ${chalk.yellow( + `Translation extraction warnings for file path=${sourceCodeFilePath}:\n- ${pico.yellow( warnings.join('\n\n- '), )}`, ); diff --git a/packages/docusaurus/src/webpack/client.ts b/packages/docusaurus/src/webpack/client.ts index 40fb507c2255..b5aa201fa75f 100644 --- a/packages/docusaurus/src/webpack/client.ts +++ b/packages/docusaurus/src/webpack/client.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import chalk from 'chalk'; +import pico from 'picocolors'; import path from 'path'; import {Configuration} from 'webpack'; import merge from 'webpack-merge'; @@ -46,7 +46,7 @@ export default function createClientConfig( compiler.hooks.done.tap('client:done', (stats) => { if (stats.hasErrors()) { console.log( - chalk.red( + pico.red( 'Client bundle compiled with errors therefore further build is impossible.', ), ); diff --git a/packages/docusaurus/src/webpack/utils.ts b/packages/docusaurus/src/webpack/utils.ts index 8eb3dd85ec3a..8f161e4528bb 100644 --- a/packages/docusaurus/src/webpack/utils.ts +++ b/packages/docusaurus/src/webpack/utils.ts @@ -21,7 +21,7 @@ import TerserPlugin from 'terser-webpack-plugin'; import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'; import path from 'path'; import crypto from 'crypto'; -import chalk from 'chalk'; +import pico from 'picocolors'; import {TransformOptions} from '@babel/core'; import { ConfigureWebpackFn, @@ -166,7 +166,7 @@ export const getCustomizableJSLoader = // TODO remove this before end of 2021? const warnBabelLoaderOnce = memoize(() => { console.warn( - chalk.yellow( + pico.yellow( 'Docusaurus plans to support multiple JS loader strategies (Babel, esbuild...): "getBabelLoader(isServer)" is now deprecated in favor of "getJSLoader({isServer})".', ), ); @@ -182,7 +182,7 @@ const getBabelLoaderDeprecated = function getBabelLoaderDeprecated( // TODO remove this before end of 2021 ? const warnCacheLoaderOnce = memoize(() => { console.warn( - chalk.yellow( + pico.yellow( 'Docusaurus uses Webpack 5 and getCacheLoader() usage is now deprecated.', ), ); @@ -296,7 +296,7 @@ export function compile(config: Configuration[]): Promise { compiler.close((errClose) => { if (errClose) { console.error( - chalk.red('Error while closing Webpack compiler:', errClose), + pico.red('Error while closing Webpack compiler:', errClose), ); reject(errClose); } else { @@ -445,7 +445,7 @@ function validateKeyAndCerts({ encrypted = crypto.publicEncrypt(cert, Buffer.from('test')); } catch (err) { throw new Error( - `The certificate "${chalk.yellow(crtFile)}" is invalid.\n${ + `The certificate "${pico.yellow(crtFile)}" is invalid.\n${ (err as Error).message }`, ); @@ -456,7 +456,7 @@ function validateKeyAndCerts({ crypto.privateDecrypt(key, encrypted); } catch (err) { throw new Error( - `The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${ + `The certificate key "${pico.yellow(keyFile)}" is invalid.\n${ (err as Error).message }`, ); @@ -467,9 +467,9 @@ function validateKeyAndCerts({ function readEnvFile(file: string, type: string) { if (!fs.existsSync(file)) { throw new Error( - `You specified ${chalk.cyan( + `You specified ${pico.cyan( type, - )} in your env, but the file "${chalk.yellow(file)}" can't be found.`, + )} in your env, but the file "${pico.yellow(file)}" can't be found.`, ); } return fs.readFileSync(file); From 4660e46ef703ad3a5ece1b1f4773f137bd4ac119 Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Tue, 23 Nov 2021 17:18:47 +0800 Subject: [PATCH 02/33] Fix --- packages/docusaurus/src/webpack/utils.ts | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/docusaurus/src/webpack/utils.ts b/packages/docusaurus/src/webpack/utils.ts index 8f161e4528bb..c8a2ddcbd03b 100644 --- a/packages/docusaurus/src/webpack/utils.ts +++ b/packages/docusaurus/src/webpack/utils.ts @@ -296,7 +296,7 @@ export function compile(config: Configuration[]): Promise { compiler.close((errClose) => { if (errClose) { console.error( - pico.red('Error while closing Webpack compiler:', errClose), + pico.red(`Error while closing Webpack compiler: ${errClose}`), ); reject(errClose); } else { diff --git a/yarn.lock b/yarn.lock index fe9afb453cf1..fde37066af9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6571,7 +6571,7 @@ chalk@^3.0.0, chalk@^3.0.0-beta.2: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== From bc6645c508b356ce49be97d7ff8868172850c9e0 Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 08:44:55 +0800 Subject: [PATCH 03/33] Migrate some packages to logger --- packages/create-docusaurus/bin/index.js | 14 +- packages/create-docusaurus/package.json | 1 + packages/create-docusaurus/src/index.ts | 53 +++--- packages/docusaurus-logger/package.json | 31 ++++ packages/docusaurus-logger/src/index.ts | 36 ++++ packages/docusaurus-logger/tsconfig.json | 9 + packages/docusaurus-mdx-loader/package.json | 2 +- packages/docusaurus-mdx-loader/src/index.ts | 4 +- packages/docusaurus-migrate/bin/index.js | 10 +- packages/docusaurus-migrate/package.json | 2 +- .../index.d.ts => src/deps.d.ts} | 2 - packages/docusaurus-migrate/src/index.ts | 167 ++++++++---------- .../src/collectRedirects.ts | 20 +-- packages/docusaurus/bin/beforeCli.js | 9 +- packages/docusaurus/bin/docusaurus.js | 6 +- packages/docusaurus/package.json | 1 + packages/docusaurus/src/choosePort.ts | 20 +-- packages/docusaurus/src/client/serverEntry.js | 32 ++-- packages/docusaurus/src/commands/build.ts | 28 ++- packages/docusaurus/src/commands/clear.ts | 20 +-- packages/docusaurus/src/commands/deploy.ts | 32 ++-- packages/docusaurus/src/commands/serve.ts | 19 +- packages/docusaurus/src/commands/start.ts | 18 +- packages/docusaurus/src/commands/swizzle.ts | 79 +++++---- .../src/commands/writeHeadingIds.ts | 26 ++- packages/docusaurus/src/server/i18n.ts | 23 +-- packages/docusaurus/src/server/index.ts | 10 +- .../docusaurus/src/server/plugins/index.ts | 8 +- .../src/server/translations/translations.ts | 16 +- .../translations/translationsExtractor.ts | 14 +- packages/docusaurus/src/webpack/client.ts | 9 +- packages/docusaurus/src/webpack/utils.ts | 30 ++-- 32 files changed, 373 insertions(+), 378 deletions(-) create mode 100644 packages/docusaurus-logger/package.json create mode 100644 packages/docusaurus-logger/src/index.ts create mode 100644 packages/docusaurus-logger/tsconfig.json rename packages/docusaurus-migrate/{external_types/index.d.ts => src/deps.d.ts} (91%) diff --git a/packages/create-docusaurus/bin/index.js b/packages/create-docusaurus/bin/index.js index 4a7d129c34e8..d04533589c83 100755 --- a/packages/create-docusaurus/bin/index.js +++ b/packages/create-docusaurus/bin/index.js @@ -6,7 +6,7 @@ * LICENSE file in the root directory of this source tree. */ -const pico = require('picocolors'); +const logger = require('@docusaurus/logger'); const semver = require('semver'); const path = require('path'); const program = require('commander'); @@ -14,11 +14,9 @@ const {default: init} = require('../lib'); const requiredVersion = require('../package.json').engines.node; if (!semver.satisfies(process.version, requiredVersion)) { - console.log( - pico.red(`\nMinimum Node.js version not met :)`) + - pico.yellow( - `\nYou are using Node.js ${process.version}, Requirement: Node.js ${requiredVersion}.\n`, - ), + logger.error('Minimum Node.js version not met :('); + logger.error( + `You are using Node.js ${process.version}, Requirement: Node.js ${requiredVersion}.`, ); process.exit(1); } @@ -26,7 +24,7 @@ if (!semver.satisfies(process.version, requiredVersion)) { function wrapCommand(fn) { return (...args) => fn(...args).catch((err) => { - console.error(pico.red(err.stack)); + logger.error(err.stack); process.exitCode = 1; }); } @@ -58,7 +56,7 @@ program program.arguments('').action((cmd) => { program.outputHelp(); - console.log(` ${pico.red(`\n Unknown command ${pico.yellow(cmd)}.`)}`); + logger.error(` Unknown command ${logger.idC(cmd)}.`); console.log(); }); diff --git a/packages/create-docusaurus/package.json b/packages/create-docusaurus/package.json index 6b7c5723482c..6abc269faa02 100755 --- a/packages/create-docusaurus/package.json +++ b/packages/create-docusaurus/package.json @@ -23,6 +23,7 @@ }, "license": "MIT", "dependencies": { + "@docusaurus/logger": "2.0.0-beta.9", "commander": "^5.1.0", "fs-extra": "^10.0.0", "lodash": "^4.17.20", diff --git a/packages/create-docusaurus/src/index.ts b/packages/create-docusaurus/src/index.ts index 3dcdf0dda5bb..3e877c95b4f2 100755 --- a/packages/create-docusaurus/src/index.ts +++ b/packages/create-docusaurus/src/index.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import fs from 'fs-extra'; import {execSync} from 'child_process'; import prompts, {Choice} from 'prompts'; @@ -131,7 +131,7 @@ export default async function init( } if (!name) { - throw new Error(pico.red('A website name is required.')); + throw new Error('A website name is required.'); } const dest = path.resolve(rootDir, name); @@ -171,7 +171,7 @@ export default async function init( if (url && isValidGitRepoUrl(url)) { return true; } - return pico.red(`Invalid repository URL`); + return logger.errorC('Invalid repository URL'); }, message: 'Enter a repository URL from GitHub, Bitbucket, GitLab, or any other public repo.\n(e.g: https://github.com/ownerName/repoName.git)', @@ -187,9 +187,11 @@ export default async function init( if (fs.existsSync(fullDir)) { return true; } - return pico.red(`The path ${pico.magenta(fullDir)} does not exist.`); + return logger.errorC( + `The path ${logger.pathC(fullDir)} does not exist.`, + ); } - return pico.red('Please enter a valid path.'); + return logger.errorC('Please enter a valid path.'); }, message: 'Enter a local folder path, relative to the current working directory.', @@ -201,24 +203,24 @@ export default async function init( throw new Error('Template should not be empty'); } - console.log(` -${pico.cyan('Creating new Docusaurus project...')} -`); + logger.log('Creating new Docusaurus project...'); if (isValidGitRepoUrl(template)) { - console.log(`Cloning Git template ${pico.cyan(template)}...`); + logger.log(`Cloning Git template ${logger.idC(template)}...`); if ( shell.exec(`git clone --recursive ${template} ${dest}`, {silent: true}) .code !== 0 ) { - throw new Error(pico.red(`Cloning Git template ${template} failed!`)); + throw new Error(`Cloning Git template ${logger.idC(template)} failed!`); } } else if (templates.includes(template)) { // Docusaurus templates. if (useTS) { if (!hasTS(template)) { throw new Error( - `Template ${template} doesn't provide the Typescript variant.`, + `Template ${logger.idC( + template, + )} doesn't provide the Typescript variant.`, ); } template = `${template}${TypeScriptTemplateSuffix}`; @@ -226,7 +228,9 @@ ${pico.cyan('Creating new Docusaurus project...')} try { await copyTemplate(templatesDir, template, dest); } catch (err) { - console.log(`Copying Docusaurus template ${pico.cyan(template)} failed!`); + logger.error( + `Copying Docusaurus template ${logger.idC(template)} failed!`, + ); throw err; } } else if (fs.existsSync(path.resolve(process.cwd(), template))) { @@ -234,7 +238,7 @@ ${pico.cyan('Creating new Docusaurus project...')} try { await fs.copy(templateDir, dest); } catch (err) { - console.log(`Copying local template ${templateDir} failed!`); + logger.error(`Copying local template ${logger.pathC(template)} failed!`); throw err; } } else { @@ -249,7 +253,7 @@ ${pico.cyan('Creating new Docusaurus project...')} private: true, }); } catch (err) { - console.log(pico.red('Failed to update package.json.')); + logger.error('Failed to update package.json.'); throw err; } @@ -266,7 +270,7 @@ ${pico.cyan('Creating new Docusaurus project...')} const pkgManager = useYarn ? 'yarn' : 'npm'; if (!cliOptions.skipInstall) { - console.log(`Installing dependencies with ${pico.cyan(pkgManager)}...`); + logger.log(`Installing dependencies with ${logger.idC(pkgManager)}...`); try { // Use force coloring the output, since the command is invoked by shelljs, which is not the interactive shell @@ -280,11 +284,10 @@ ${pico.cyan('Creating new Docusaurus project...')} }, ); } catch (err) { - console.log(pico.red('Installation failed.')); + logger.error('Installation failed.'); throw err; } } - console.log(); // Display the most elegant way to cd. const cdpath = @@ -292,26 +295,26 @@ ${pico.cyan('Creating new Docusaurus project...')} ? name : path.relative(process.cwd(), name); - console.log(` -Successfully created "${pico.cyan(cdpath)}". + logger.info(` +Successfully created "${logger.pathC(cdpath)}". Inside that directory, you can run several commands: - ${pico.cyan(`${pkgManager} start`)} + ${logger.codeC(`${pkgManager} start`)} Starts the development server. - ${pico.cyan(`${pkgManager} ${useYarn ? '' : 'run '}build`)} + ${logger.codeC(`${pkgManager} ${useYarn ? '' : 'run '}build`)} Bundles your website into static files for production. - ${pico.cyan(`${pkgManager} ${useYarn ? '' : 'run '}serve`)} + ${logger.codeC(`${pkgManager} ${useYarn ? '' : 'run '}serve`)} Serves the built website locally. - ${pico.cyan(`${pkgManager} deploy`)} + ${logger.codeC(`${pkgManager} deploy`)} Publishes the website to GitHub pages. We recommend that you begin by typing: - ${pico.cyan('cd')} ${cdpath} - ${pico.cyan(`${pkgManager} start`)} + ${logger.codeC('cd')} ${cdpath} + ${logger.codeC(`${pkgManager} start`)} Happy building awesome websites! `); diff --git a/packages/docusaurus-logger/package.json b/packages/docusaurus-logger/package.json new file mode 100644 index 000000000000..2aca731a19c7 --- /dev/null +++ b/packages/docusaurus-logger/package.json @@ -0,0 +1,31 @@ +{ + "name": "@docusaurus/logger", + "version": "2.0.0-beta.9", + "description": "An encapsulated logger for different levels of messages", + "main": "./lib/index.js", + "repository": { + "type": "git", + "url": "https://github.com/facebook/docusaurus.git", + "directory": "packages/docusaurus-logger" + }, + "bugs": { + "url": "https://github.com/facebook/docusaurus/issues" + }, + "scripts": { + "build": "tsc", + "watch": "tsc --watch" + }, + "publishConfig": { + "access": "public" + }, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=14" + }, + "devDependencies": { + "@types/supports-color": "^8.1.1" + } +} diff --git a/packages/docusaurus-logger/src/index.ts b/packages/docusaurus-logger/src/index.ts new file mode 100644 index 000000000000..83c92583d3d0 --- /dev/null +++ b/packages/docusaurus-logger/src/index.ts @@ -0,0 +1,36 @@ +/** + * 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 pico from 'picocolors'; + +export const pathC = pico.magenta; +export const idC = pico.blue; +export const errorC = pico.red; +export const codeC = pico.blue; +export const subdueC = pico.gray; +export const warnC = pico.yellow; +export const successC = pico.green; + +export function log(msg: string): void { + console.info(`${pico.bold('[LOG] ')}${msg}`); +} + +export function info(msg: string): void { + console.info(`${pico.cyan(pico.bold('[INFO] '))}${msg}`); +} + +export function warn(msg: string): void { + console.info(`${pico.yellow(pico.bold('[WARNING] '))}${msg}`); +} + +export function error(msg: string): void { + console.info(`${pico.red(pico.bold('[ERROR] '))}${msg}`); +} + +export function success(msg: string): void { + console.info(`${pico.green(pico.bold('[SUCCESS] '))}${msg}`); +} diff --git a/packages/docusaurus-logger/tsconfig.json b/packages/docusaurus-logger/tsconfig.json new file mode 100644 index 000000000000..f5902ba1089b --- /dev/null +++ b/packages/docusaurus-logger/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "incremental": true, + "tsBuildInfoFile": "./lib/.tsbuildinfo", + "rootDir": "src", + "outDir": "lib" + } +} diff --git a/packages/docusaurus-mdx-loader/package.json b/packages/docusaurus-mdx-loader/package.json index f1db41a66423..bcddbd1bc5b7 100644 --- a/packages/docusaurus-mdx-loader/package.json +++ b/packages/docusaurus-mdx-loader/package.json @@ -21,6 +21,7 @@ "@babel/parser": "^7.12.16", "@babel/traverse": "^7.12.13", "@docusaurus/core": "2.0.0-beta.9", + "@docusaurus/logger": "2.0.0-beta.9", "@docusaurus/utils": "2.0.0-beta.9", "@mdx-js/mdx": "^1.6.21", "@mdx-js/react": "^1.6.21", @@ -30,7 +31,6 @@ "github-slugger": "^1.4.0", "gray-matter": "^4.0.3", "mdast-util-to-string": "^2.0.0", - "picocolors": "^1.0.0", "remark-emoji": "^2.1.0", "stringify-object": "^3.3.0", "unist-util-visit": "^2.0.2", diff --git a/packages/docusaurus-mdx-loader/src/index.ts b/packages/docusaurus-mdx-loader/src/index.ts index 96e13603c185..50e1a5042c3d 100644 --- a/packages/docusaurus-mdx-loader/src/index.ts +++ b/packages/docusaurus-mdx-loader/src/index.ts @@ -7,7 +7,7 @@ import {readFile} from 'fs-extra'; import mdx from '@mdx-js/mdx'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import emoji from 'remark-emoji'; import { parseFrontMatter, @@ -164,7 +164,7 @@ ${JSON.stringify(frontMatter, null, 2)}`; if (shouldError) { return callback(new Error(errorMessage)); } else { - console.warn(pico.yellow(errorMessage)); + logger.warn(errorMessage); } } } diff --git a/packages/docusaurus-migrate/bin/index.js b/packages/docusaurus-migrate/bin/index.js index 180731820d11..22fc82343e46 100755 --- a/packages/docusaurus-migrate/bin/index.js +++ b/packages/docusaurus-migrate/bin/index.js @@ -6,7 +6,7 @@ * LICENSE file in the root directory of this source tree. */ -const pico = require('picocolors'); +const logger = require('@docusaurus/logger'); const semver = require('semver'); const cli = require('commander'); const path = require('path'); @@ -24,11 +24,9 @@ function wrapCommand(fn) { } if (!semver.satisfies(process.version, requiredVersion)) { - console.log( - pico.red(`\nMinimum Node.js version not met :(`) + - pico.yellow( - `\n\nYou are using Node ${process.version}. We require Node.js ${requiredVersion} or up!\n`, - ), + logger.error('Minimum Node.js version not met :('); + logger.error( + `You are using Node.js ${process.version}, Requirement: Node.js ${requiredVersion}.`, ); process.exit(1); } diff --git a/packages/docusaurus-migrate/package.json b/packages/docusaurus-migrate/package.json index 3aab2b126632..ccce419cc418 100644 --- a/packages/docusaurus-migrate/package.json +++ b/packages/docusaurus-migrate/package.json @@ -24,6 +24,7 @@ }, "dependencies": { "@babel/preset-env": "^7.15.6", + "@docusaurus/logger": "2.0.0-beta.9", "@mapbox/hast-util-to-jsx": "^1.0.0", "color": "^4.0.1", "commander": "^5.1.0", @@ -34,7 +35,6 @@ "html-tags": "^3.1.0", "import-fresh": "^3.2.2", "jscodeshift": "^0.13.0", - "picocolors": "^1.0.0", "rehype-parse": "^7.0.1", "remark-parse": "^8.0.2", "remark-stringify": "^8.1.0", diff --git a/packages/docusaurus-migrate/external_types/index.d.ts b/packages/docusaurus-migrate/src/deps.d.ts similarity index 91% rename from packages/docusaurus-migrate/external_types/index.d.ts rename to packages/docusaurus-migrate/src/deps.d.ts index 7554a9b68ea7..ca55197f76d4 100644 --- a/packages/docusaurus-migrate/external_types/index.d.ts +++ b/packages/docusaurus-migrate/src/deps.d.ts @@ -8,5 +8,3 @@ declare module '@mapbox/hast-util-to-jsx'; declare module 'hast-util-to-string'; - -declare module 'color'; diff --git a/packages/docusaurus-migrate/src/index.ts b/packages/docusaurus-migrate/src/index.ts index ae321e5f0848..9e26f43c2da9 100644 --- a/packages/docusaurus-migrate/src/index.ts +++ b/packages/docusaurus-migrate/src/index.ts @@ -5,9 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -import * as fs from 'fs-extra'; +import fs from 'fs-extra'; import importFresh from 'import-fresh'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import glob from 'glob'; import Color from 'color'; @@ -109,85 +109,68 @@ export async function migrateDocusaurusProject( react: '^17.0.1', 'react-dom': '^17.0.1', }; + let errorCount = 0; try { createClientRedirects(siteConfig, deps, config); - console.log( - pico.green('Successfully created client redirect for non clean URL'), - ); - } catch (errorInClientRedirect) { - console.log( - pico.red(`Error while creating redirects: ${errorInClientRedirect}`), - ); + logger.success('Created client redirect for non clean URL'); + } catch (e) { + logger.error(`Failed to creating redirects: ${e}`); + errorCount += 1; } if (shouldMigratePages) { try { createPages(newDir, siteDir); - console.log( - pico.green( - 'Successfully created pages (check migration page for more details)', - ), - ); - } catch (errorInMigratingPages) { - console.log( - pico.red( - `Error occurred while creating pages: ${errorInMigratingPages}`, - ), + logger.success( + 'Created new doc pages (check migration page for more details)', ); + } catch (e) { + logger.error(`Failed to create new doc pages: ${e}`); + errorCount += 1; } } else { try { createDefaultLandingPage(newDir); - console.log( - pico.green( - 'Successfully created landing page (check migration page for more details)', - ), - ); - } catch (errorInLandingPage) { - console.log( - pico.red( - `Error occurred while creating landing page: ${errorInLandingPage}`, - ), + logger.success( + 'Created landing page (check migration page for more details)', ); + } catch (e) { + logger.error(`Failed to create landing page: ${e}`); + errorCount += 1; } } try { migrateStaticFiles(siteDir, newDir); - console.log(pico.green('Successfully migrated static folder')); - } catch (errorInStatic) { - console.log( - pico.red(`Error occurred while copying static folder: ${errorInStatic}`), - ); + logger.success('Migrated static folder'); + } catch (e) { + logger.error(`Failed to copy static folder: ${e}`); + errorCount += 1; } try { migrateBlogFiles(siteDir, newDir, classicPreset, shouldMigrateMdFiles); - } catch (errorInMigratingBlogs) { - console.log( - pico.red( - `Error occurred while migrating blogs: ${errorInMigratingBlogs}`, - ), - ); + } catch (e) { + logger.error(`Failed to migrate blogs: ${e}`); + errorCount += 1; } try { handleVersioning(siteDir, siteConfig, newDir, config, shouldMigrateMdFiles); - } catch (errorInVersion) { - console.log( - pico.red( - `Error occurred while migrating versioned docs: ${errorInVersion}`, - ), - ); + } catch (e) { + logger.error(`Failed to migrate versioned docs: ${e}`); + errorCount += 1; } try { migrateLatestDocs(siteDir, newDir, shouldMigrateMdFiles, classicPreset); - } catch (errorInDoc) { - pico.red(`Error occurred while migrating docs: ${errorInDoc}`); + } catch (e) { + logger.error(`Failed to migrate docs: ${e}`); + errorCount += 1; } try { migrateLatestSidebar(siteDir, newDir, classicPreset, siteConfig); - } catch (error) { - console.log(pico.red(`Error occurred while migrating sidebar: ${error}`)); + } catch (e) { + logger.error(`Failed to migrate sidebar: ${e}`); + errorCount += 1; } try { @@ -195,26 +178,28 @@ export async function migrateDocusaurusProject( path.join(newDir, 'docusaurus.config.js'), `module.exports=${JSON.stringify(config, null, 2)}`, ); - console.log( - pico.green( - `Successfully created a new config file with new navbar and footer config`, - ), - ); - } catch (error) { - console.log( - pico.red(`Error occurred while creating config file: ${error}`), + logger.success( + `Created a new config file with new navbar and footer config`, ); + } catch (e) { + logger.error(`Failed to create config file: ${e}`); + errorCount += 1; } try { migratePackageFile(siteDir, deps, newDir); - } catch (error) { - console.log( - pico.red( - `Error occurred while creating package.json file for project: ${error}`, - ), + } catch (e) { + logger.error( + `Error occurred while creating package.json file for project: ${e}`, ); + errorCount += 1; + } + if (errorCount) { + logger.warn( + `Migration from v1 to v2 failed with ${errorCount} errors: please check the log above`, + ); + } else { + logger.success('Completed migration from v1 to v2'); } - console.log('Completed migration from v1 to v2'); } export function createConfigFile({ @@ -273,10 +258,13 @@ export function createConfigFile({ customConfigFields[key] = value; } }); - console.log( - `${pico.yellow( - 'Following Fields from siteConfig.js will be added to docusaurus.config.js in `customFields`', - )}\n${pico.yellow(Object.keys(customConfigFields).join('\n'))}`, + logger.info( + `Following Fields from ${logger.pathC( + 'siteConfig.js', + )} will be added to ${logger.pathC( + 'docusaurus.config.js', + )} in ${logger.codeC('customFields')}: +- ${Object.keys(customConfigFields).join('\n- ')}`, ); let v2DocsPath: string | undefined; @@ -413,8 +401,8 @@ function createPages(newDir: string, siteDir: string): void { const content = String(fs.readFileSync(filePath)); fs.writeFileSync(filePath, migratePage(content)); }); - } catch (error) { - console.log(pico.red(`Unable to migrate Pages : ${error}`)); + } catch (e) { + logger.error(`Unable to migrate Pages: ${e}`); createDefaultLandingPage(newDir); } } else { @@ -456,13 +444,9 @@ function migrateBlogFiles( fs.writeFileSync(file, sanitizedFileContent(content, migrateMDFiles)); }); classicPreset.blog.path = 'blog'; - console.log( - pico.green( - `Successfully migrated blogs to version 2 with change in frontmatter`, - ), - ); + logger.success('Migrated blogs to version 2 with change in front matter'); } else { - console.log(pico.yellow(`Blog not found. Skipping migration for blog`)); + logger.warn('Blog not found. Skipping migration for blog'); } } @@ -493,18 +477,11 @@ function handleVersioning( versionRegex, migrateMDFiles, ); - console.log( - pico.green( - `Successfully migrated version docs and sidebar. The following doc versions have been created: \n${loadedVersions.join( - '\n', - )}`, - ), - ); + logger.success(`Migrated version docs and sidebar. The following doc versions have been created: +- ${loadedVersions.join('\n- ')}`); } else { - console.log( - pico.yellow( - 'Versioned docs not found. Skipping migration for versioned docs', - ), + logger.warn( + 'Versioned docs not found. Skipping migration for versioned docs', ); } } @@ -693,9 +670,7 @@ function migrateLatestSidebar( 'sidebars.json', ); } catch { - console.log( - pico.yellow(`Sidebar not found. Skipping migration for sidebar`), - ); + logger.warn('Sidebar not found. Skipping migration for sidebar'); } if (siteConfig.colors) { const primaryColor = Color(siteConfig.colors.primaryColor); @@ -736,11 +711,9 @@ function migrateLatestDocs( const content = String(fs.readFileSync(file)); fs.writeFileSync(file, sanitizedFileContent(content, migrateMDFiles)); }); - console.log(pico.green(`Successfully migrated docs to version 2`)); + logger.success('Migrated docs to version 2'); } else { - console.log( - pico.yellow(`Docs folder not found. Skipping migration for docs`), - ); + logger.warn('Docs folder not found. Skipping migration for docs'); } } @@ -778,7 +751,7 @@ function migratePackageFile( path.join(newDir, 'package.json'), JSON.stringify(packageFile, null, 2), ); - console.log(pico.green(`Successfully migrated package.json file`)); + logger.success('Migrated package.json file'); } export async function migrateMDToMDX( @@ -794,5 +767,7 @@ export async function migrateMDToMDX( sanitizedFileContent(String(fs.readFileSync(file)), true), ); }); - console.log(`Successfully migrated ${siteDir} to ${newDir}`); + logger.success( + `Successfully migrated ${logger.pathC(siteDir)} to ${logger.pathC(newDir)}`, + ); } diff --git a/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts b/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts index ff5c6d5899cc..0bfcb4975468 100644 --- a/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts +++ b/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts @@ -22,7 +22,7 @@ import { ApplyTrailingSlashParams, } from '@docusaurus/utils-common'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; export default function collectRedirects( pluginContext: PluginContext, @@ -99,14 +99,12 @@ function filterUnwantedRedirects( Object.entries(groupBy(redirects, (redirect) => redirect.from)).forEach( ([from, groupedFromRedirects]) => { if (groupedFromRedirects.length > 1) { - console.error( - pico.red( - `@docusaurus/plugin-client-redirects: multiple redirects are created with the same "from" pathname=${from} + logger.error(`@docusaurus/plugin-client-redirects: multiple redirects are created with the same "from" pathname: ${logger.pathC( + from, + )} It is not possible to redirect the same pathname to multiple destinations: - ${groupedFromRedirects.map((r) => JSON.stringify(r)).join('\n- ')} -`, - ), - ); +`); } }, ); @@ -117,13 +115,9 @@ It is not possible to redirect the same pathname to multiple destinations: (redirect) => pluginContext.relativeRoutesPaths.includes(redirect.from), ); if (redirectsOverridingExistingPath.length > 0) { - console.error( - pico.red( - `@docusaurus/plugin-client-redirects: some redirects would override existing paths, and will be ignored: + logger.error(`@docusaurus/plugin-client-redirects: some redirects would override existing paths, and will be ignored: - ${redirectsOverridingExistingPath.map((r) => JSON.stringify(r)).join('\n- ')} -`, - ), - ); +`); } return collectedRedirects.filter( (redirect) => !pluginContext.relativeRoutesPaths.includes(redirect.from), diff --git a/packages/docusaurus/bin/beforeCli.js b/packages/docusaurus/bin/beforeCli.js index ba9eb82b8694..7f97c6d6152a 100644 --- a/packages/docusaurus/bin/beforeCli.js +++ b/packages/docusaurus/bin/beforeCli.js @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +const logger = require('@docusaurus/logger'); const pico = require('picocolors'); const fs = require('fs-extra'); const semver = require('semver'); @@ -115,11 +116,9 @@ if ( // notify user if node version needs to be updated if (!semver.satisfies(process.version, requiredVersion)) { - console.log( - pico.red(`\nMinimum Node version not met :(`) + - pico.yellow( - `\n\nYou are using Node ${process.version}. We require Node ${requiredVersion} or up!\n`, - ), + logger.error('Minimum Node.js version not met :('); + logger.error( + `You are using Node.js ${process.version}, Requirement: Node.js ${requiredVersion}.`, ); process.exit(1); } diff --git a/packages/docusaurus/bin/docusaurus.js b/packages/docusaurus/bin/docusaurus.js index c67558b32652..9ed1781805d6 100755 --- a/packages/docusaurus/bin/docusaurus.js +++ b/packages/docusaurus/bin/docusaurus.js @@ -6,7 +6,7 @@ * LICENSE file in the root directory of this source tree. */ -const pico = require('picocolors'); +const logger = require('@docusaurus/logger'); const fs = require('fs'); const cli = require('commander'); const { @@ -219,7 +219,7 @@ cli cli.arguments('').action((cmd) => { cli.outputHelp(); - console.log(` ${pico.red(`\n Unknown command ${pico.yellow(cmd)}.`)}.`); + logger.error(` Unknown command ${logger.idC(cmd)}.`); console.log(); }); @@ -251,6 +251,6 @@ async function run() { run(); process.on('unhandledRejection', (err) => { - console.error(pico.red(err.stack)); + logger.error(err.stack); process.exit(1); }); diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index af645dd5851a..5cb04c96d9db 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -57,6 +57,7 @@ "@babel/runtime-corejs3": "^7.15.4", "@babel/traverse": "^7.12.13", "@docusaurus/cssnano-preset": "2.0.0-beta.9", + "@docusaurus/logger": "2.0.0-beta.9", "@docusaurus/react-loadable": "5.5.2", "@docusaurus/utils": "2.0.0-beta.9", "@docusaurus/utils-common": "2.0.0-beta.9", diff --git a/packages/docusaurus/src/choosePort.ts b/packages/docusaurus/src/choosePort.ts index fe26a956713a..14069bda9d86 100644 --- a/packages/docusaurus/src/choosePort.ts +++ b/packages/docusaurus/src/choosePort.ts @@ -13,7 +13,7 @@ import {execSync} from 'child_process'; import detect from 'detect-port'; import isRoot from 'is-root'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import prompts from 'prompts'; const isInteractive = process.stdout.isTTY; @@ -68,12 +68,9 @@ function getProcessForPort(port: number): string | null { const processId = getProcessIdOnPort(port); const directory = getDirectoryOfProcessById(processId); const command = getProcessCommand(processId); - return ( - pico.cyan(command) + - pico.gray(` (pid ${processId})\n`) + - pico.blue(' in ') + - pico.cyan(directory) - ); + return `${logger.codeC(command)} ${logger.subdueC( + `(pid ${processId})`, + )} in ${logger.pathC(directory)}`; } catch (e) { return null; } @@ -104,7 +101,7 @@ export default async function choosePort( const question: prompts.PromptObject = { type: 'confirm', name: 'shouldChangePort', - message: `${pico.yellow( + message: `${logger.warnC( `${message}${ existingProcess ? ` Probably:\n ${existingProcess}` : '' }`, @@ -119,15 +116,14 @@ export default async function choosePort( } }); } else { - console.log(pico.red(message)); + logger.error(message); resolve(null); } }), (err) => { throw new Error( - `${pico.red(`Could not find an open port at ${pico.bold(host)}.`)}\n${ - `Network error message: "${err.message}".` || err - }\n`, + `Could not find an open port at ${logger.idC(host)}. +${`Network error message: "${err.message || err}".`}`, ); }, ); diff --git a/packages/docusaurus/src/client/serverEntry.js b/packages/docusaurus/src/client/serverEntry.js index 923766c9f609..7c28d05d57c3 100644 --- a/packages/docusaurus/src/client/serverEntry.js +++ b/packages/docusaurus/src/client/serverEntry.js @@ -24,7 +24,7 @@ import { createStatefulLinksCollector, ProvideLinksCollector, } from './LinksCollector'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; // eslint-disable-next-line no-restricted-imports import {memoize} from 'lodash'; @@ -43,21 +43,22 @@ export default async function render(locals) { try { return await doRender(locals); } catch (e) { - console.error( - pico.red( - `Docusaurus Node/SSR could not render static page with path "${locals.path}" because of following error:\n\n${e.stack}\n`, - ), - ); + logger.error(`Docusaurus Node/SSR could not render static page with path "${logger.pathC( + locals.path, + )}" because of following error: +${e.stack}`); const isNotDefinedErrorRegex = /(window|document|localStorage|navigator|alert|location|buffer|self) is not defined/i; if (isNotDefinedErrorRegex.test(e.message)) { - console.error( - pico.green( - 'Pro tip: It looks like you are using code that should run on the client-side only.\nTo get around it, try using (https://docusaurus.io/docs/docusaurus-core/#browseronly) or ExecutionEnvironment (https://docusaurus.io/docs/docusaurus-core/#executionenvironment).\nIt might also require to wrap your client code in useEffect hook and/or import a third-party library dynamically (if any).', - ), - ); + logger.tip(`It looks like you are using code that should run on the client-side only. +To get around it, try using ${logger.idC( + '', + )} (https://docusaurus.io/docs/docusaurus-core/#browseronly) or ${logger.idC( + 'ExecutionEnvironment', + )} (https://docusaurus.io/docs/docusaurus-core/#executionenvironment). +It might also require to wrap your client code in useEffect hook and/or import a third-party library dynamically (if any).`); } throw new Error('Server-side rendering fails due to the error above.'); @@ -142,11 +143,10 @@ async function doRender(locals) { minifyJS: true, }); } catch (e) { - console.error( - pico.red( - `Minification page with path "${locals.path}" failed because of following error:\n\n${e.stack}\n`, - ), - ); + logger.error(`Minification page with path "${logger.pathC( + locals.path, + )}" failed because of following error: +${e.stack}`); throw e; } } diff --git a/packages/docusaurus/src/commands/build.ts b/packages/docusaurus/src/commands/build.ts index 9b4093da3448..3473abd6d785 100644 --- a/packages/docusaurus/src/commands/build.ts +++ b/packages/docusaurus/src/commands/build.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import CopyWebpackPlugin from 'copy-webpack-plugin'; import fs from 'fs-extra'; import path from 'path'; @@ -73,12 +73,8 @@ export default async function build( return tryToBuildLocale({locale: cliOptions.locale, isLastLocale: true}); } else { if (i18n.locales.length > 1) { - console.log( - pico.yellow( - `\nWebsite will be built for all these locales: -- ${i18n.locales.join('\n- ')}`, - ), - ); + logger.info(`Website will be built for all these locales: +- ${i18n.locales.join('\n- ')}`); } // We need the default locale to always be the 1st in the list @@ -112,9 +108,7 @@ async function buildLocale({ }): Promise { process.env.BABEL_ENV = 'production'; process.env.NODE_ENV = 'production'; - console.log( - pico.blue(`\n[${locale}] Creating an optimized production build...`), - ); + logger.log(`[${locale}] Creating an optimized production build...`); const props: Props = await load(siteDir, { customOutDir: cliOptions.outDir, @@ -238,17 +232,17 @@ async function buildLocale({ baseUrl, }); - console.log( - `${pico.green(`Success!`)} Generated static files in "${pico.cyan( + logger.success( + `Generated static files in ${logger.pathC( path.relative(process.cwd(), outDir), - )}".`, + )}.`, ); if (isLastLocale) { - console.log( - `\nUse ${pico.green( - '`npm run serve`', - )} command to test your build locally.\n`, + logger.info( + `Use ${logger.codeC( + 'npm run serve', + )} command to test your build locally.`, ); } diff --git a/packages/docusaurus/src/commands/clear.ts b/packages/docusaurus/src/commands/clear.ts index 32b6f44f4983..f10fba218c7d 100644 --- a/packages/docusaurus/src/commands/clear.ts +++ b/packages/docusaurus/src/commands/clear.ts @@ -6,19 +6,17 @@ */ import fs from 'fs-extra'; import path from 'path'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import {DEFAULT_BUILD_DIR_NAME, GENERATED_FILES_DIR_NAME} from '../constants'; -function removePath(fsPath: string) { - return fs - .remove(path.join(fsPath)) - .then(() => { - console.log(pico.green(`Successfully removed "${fsPath}" directory.`)); - }) - .catch((err) => { - console.error(`Could not remove ${fsPath} directory.`); - console.error(err); - }); +async function removePath(fsPath: string) { + try { + fs.remove(path.join(fsPath)); + logger.success(`Removed the "${logger.pathC(fsPath)}" directory.`); + } catch (e) { + logger.error(`Could not remove ${logger.pathC(fsPath)} directory.`); + logger.error((e as Error).message); + } } export default async function clear(siteDir: string): Promise { diff --git a/packages/docusaurus/src/commands/deploy.ts b/packages/docusaurus/src/commands/deploy.ts index cdd048b207f9..0b1c61194cb3 100644 --- a/packages/docusaurus/src/commands/deploy.ts +++ b/packages/docusaurus/src/commands/deploy.ts @@ -7,7 +7,7 @@ import fs from 'fs-extra'; import shell from 'shelljs'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import {loadContext} from '../server'; import build from './build'; import {BuildCLIOptions} from '@docusaurus/types'; @@ -25,14 +25,12 @@ function obfuscateGitPass(str: string) { function shellExecLog(cmd: string) { try { const result = shell.exec(cmd); - console.log( - `${pico.cyan('CMD:')} ${obfuscateGitPass(cmd)} ${pico.cyan( - `(code: ${result.code})`, - )}`, + logger.log( + `${obfuscateGitPass(cmd)} ${logger.subdueC(`(code: ${result.code})`)}`, ); return result; } catch (e) { - console.log(`${pico.red('CMD:')} ${obfuscateGitPass(cmd)}`); + logger.error(`${obfuscateGitPass(cmd)}`); throw e; } } @@ -84,17 +82,13 @@ export default async function deploy( }); if (typeof siteConfig.trailingSlash === 'undefined') { - console.warn( - pico.yellow(` -Docusaurus recommendation: -When deploying to GitHub Pages, it is better to use an explicit "trailingSlash" site config. + logger.warn(`When deploying to GitHub Pages, it is better to use an explicit "trailingSlash" site config. Otherwise, GitHub Pages will add an extra trailing slash to your site urls only on direct-access (not when navigation) with a server redirect. This behavior can have SEO impacts and create relative link issues. -`), - ); +`); } - console.log('Deploy command invoked...'); + logger.log('Deploy command invoked...'); if (!shell.which('git')) { throw new Error('Git not installed or on the PATH!'); } @@ -135,7 +129,7 @@ This behavior can have SEO impacts and create relative link issues. `Missing project organization name. Did you forget to define "organizationName" in ${siteConfigPath}? You may also export it via the ORGANIZATION_NAME environment variable.`, ); } - console.log(`${pico.cyan('organizationName:')} ${organizationName}`); + logger.info(`organizationName: ${logger.idC(organizationName)}`); const projectName = process.env.PROJECT_NAME || @@ -146,7 +140,7 @@ This behavior can have SEO impacts and create relative link issues. `Missing project name. Did you forget to define "projectName" in ${siteConfigPath}? You may also export it via the PROJECT_NAME environment variable.`, ); } - console.log(`${pico.cyan('projectName:')} ${projectName}`); + logger.info(`projectName: ${logger.idC(projectName)}`); // We never deploy on pull request. const isPullRequest = @@ -173,7 +167,7 @@ You can also set the deploymentBranch property in docusaurus.config.js .`); const deploymentBranch = process.env.DEPLOYMENT_BRANCH || siteConfig.deploymentBranch || 'gh-pages'; - console.log(`${pico.cyan('deploymentBranch:')} ${deploymentBranch}`); + logger.info(`deploymentBranch: ${logger.idC(deploymentBranch)}`); const githubHost = process.env.GITHUB_HOST || siteConfig.githubHost || 'github.com'; @@ -199,8 +193,8 @@ You can also set the deploymentBranch property in docusaurus.config.js .`); ); } - console.log( - `${pico.cyan('Remote repo URL:')} ${obfuscateGitPass(deploymentRepoURL)}`, + logger.info( + `Remote repo URL: ${logger.idC(obfuscateGitPass(deploymentRepoURL))}`, ); // Check if this is a cross-repo publish. @@ -283,7 +277,7 @@ You can also set the deploymentBranch property in docusaurus.config.js .`); try { await runDeploy(await build(siteDir, cliOptions, false)); } catch (buildError) { - console.error(buildError); + logger.error((buildError as Error).message); process.exit(1); } } else { diff --git a/packages/docusaurus/src/commands/serve.ts b/packages/docusaurus/src/commands/serve.ts index cf9ff1f53fd0..e724be7a5b1b 100644 --- a/packages/docusaurus/src/commands/serve.ts +++ b/packages/docusaurus/src/commands/serve.ts @@ -7,8 +7,7 @@ import http from 'http'; import serveHandler from 'serve-handler'; -import boxen from 'boxen'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import path from 'path'; import {loadSiteConfig} from '../server'; import build from './build'; @@ -71,18 +70,10 @@ export default async function serve( }); }); - console.log( - boxen( - pico.green( - `Serving "${cliOptions.dir}" directory at "${servingUrl + baseUrl}".`, - ), - { - borderColor: 'green', - padding: 1, - margin: 1, - align: 'center', - }, - ), + logger.success( + `Serving "${logger.pathC(cliOptions.dir)}" directory at "${logger.pathC( + servingUrl + baseUrl, + )}".`, ); server.listen(port); } diff --git a/packages/docusaurus/src/commands/start.ts b/packages/docusaurus/src/commands/start.ts index a251e1d47389..a4effc4d81e6 100644 --- a/packages/docusaurus/src/commands/start.ts +++ b/packages/docusaurus/src/commands/start.ts @@ -6,7 +6,7 @@ */ import {normalizeUrl, posixPath} from '@docusaurus/utils'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import chokidar from 'chokidar'; import HtmlWebpackPlugin from 'html-webpack-plugin'; @@ -35,7 +35,7 @@ export default async function start( ): Promise { process.env.NODE_ENV = 'development'; process.env.BABEL_ENV = 'development'; - console.log(pico.blue('Starting the development server...')); + logger.log('Starting the development server...'); function loadSite() { return load(siteDir, { @@ -61,7 +61,9 @@ export default async function start( const urls = prepareUrls(protocol, host, port); const openUrl = normalizeUrl([urls.localUrlForBrowser, baseUrl]); - console.log(pico.cyan(`Docusaurus website is running at "${openUrl}".`)); + logger.success( + `Docusaurus website is running at "${logger.pathC(openUrl)}".`, + ); // Reload files processing. const reload = debounce(() => { @@ -69,13 +71,13 @@ export default async function start( .then(({baseUrl: newBaseUrl}) => { const newOpenUrl = normalizeUrl([urls.localUrlForBrowser, newBaseUrl]); if (newOpenUrl !== openUrl) { - console.log( - pico.cyan(`Docusaurus website is running at "${newOpenUrl}".`), + logger.success( + `Docusaurus website is running at "${logger.pathC(newOpenUrl)}".`, ); } }) .catch((err) => { - console.error(pico.red(err.stack)); + logger.error(err.stack); }); }, 500); const {siteConfig, plugins = []} = props; @@ -214,10 +216,10 @@ export default async function start( if (process.env.E2E_TEST) { compiler.hooks.done.tap('done', (stats) => { if (stats.hasErrors()) { - console.log('E2E_TEST: Project has compiler errors.'); + logger.error('E2E_TEST: Project has compiler errors.'); process.exit(1); } - console.log('E2E_TEST: Project can compile.'); + logger.success('E2E_TEST: Project can compile.'); process.exit(0); }); } diff --git a/packages/docusaurus/src/commands/swizzle.ts b/packages/docusaurus/src/commands/swizzle.ts index ecd442f7ff80..fa1ea1d836ac 100644 --- a/packages/docusaurus/src/commands/swizzle.ts +++ b/packages/docusaurus/src/commands/swizzle.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import fs from 'fs-extra'; import importFresh from 'import-fresh'; import path from 'path'; @@ -100,14 +100,14 @@ function themeComponents( const components = colorCode(themePath, plugin); if (components.length === 0) { - return `${pico.red('No component to swizzle.')}`; + return 'No component to swizzle.'; } return ` -${pico.cyan('Theme components available for swizzle.')} +'Theme components available for swizzle.' -${pico.green('green =>')} safe: lower breaking change risk -${pico.red('red =>')} unsafe: higher breaking change risk +${logger.successC('green =>')} safe: lower breaking change risk +${logger.errorC('red =>')} unsafe: higher breaking change risk ${components.join('\n')} `; @@ -135,8 +135,10 @@ function colorCode( ); return [ - ...greenComponents.map((component) => pico.green(`safe: ${component}`)), - ...redComponents.map((component) => pico.red(`unsafe: ${component}`)), + ...greenComponents.map((component) => + logger.successC(`safe: ${component}`), + ), + ...redComponents.map((component) => logger.errorC(`unsafe: ${component}`)), ]; } @@ -161,7 +163,7 @@ export default async function swizzle( ); if (!themeName) { - console.log(formattedThemeNames(themeNames)); + logger.error(formattedThemeNames(themeNames)); process.exit(1); } @@ -175,10 +177,10 @@ export default async function swizzle( suggestion = name; } }); - pico.red( - `Theme ${themeName} not found. ${ + logger.error( + `Theme ${logger.idC(themeName)} not found. ${ suggestion - ? `Did you mean "${suggestion}" ?` + ? `Did you mean "${logger.idC(suggestion)}" ?` : formattedThemeNames(themeNames) }`, ); @@ -218,12 +220,10 @@ export default async function swizzle( : pluginInstance.getThemePath?.(); if (!themePath) { - console.warn( - pico.yellow( - typescript - ? `${themeName} does not provide TypeScript theme code via "getTypeScriptThemePath()".` - : `${themeName} does not provide any theme code.`, - ), + logger.warn( + typescript + ? `${themeName} does not provide TypeScript theme code via "getTypeScriptThemePath()".` + : `${themeName} does not provide any theme code.`, ); process.exit(1); } @@ -256,11 +256,11 @@ export default async function swizzle( if (mostSuitableMatch !== componentName) { mostSuitableComponent = mostSuitableMatch; - console.log( - pico.red(`Component "${componentName}" doesn't exist.`), - pico.yellow( - `"${mostSuitableComponent}" is swizzled instead of "${componentName}".`, - ), + logger.error(`Component "${logger.idC(componentName)}" doesn't exist.`); + logger.info( + `"${logger.idC( + mostSuitableComponent, + )}" is swizzled instead of "${logger.idC(componentName)}".`, ); } } @@ -283,34 +283,35 @@ export default async function swizzle( suggestion = name; } }); - console.warn(pico.red(`Component ${mostSuitableComponent} not found.`)); - console.warn( - suggestion - ? `Did you mean "${suggestion}"?` - : `${themeComponents(themePath, pluginModule)}`, + logger.error( + `Component ${logger.idC(mostSuitableComponent)} not found. ${ + suggestion + ? `Did you mean "${logger.idC(suggestion)}"?` + : `${themeComponents(themePath, pluginModule)}` + }`, ); process.exit(1); } } if (!components.includes(mostSuitableComponent) && !danger) { - console.warn( - pico.red( - `${mostSuitableComponent} is an internal component and has a higher breaking change probability. If you want to swizzle it, use the "--danger" flag.`, - ), + logger.error( + `${logger.idC( + mostSuitableComponent, + )} is an internal component and has a higher breaking change probability. If you want to swizzle it, use the ${logger.codeC( + '--danger', + )} flag.`, ); process.exit(1); } await fs.copy(fromPath, toPath); - const relativeDir = path.relative(process.cwd(), toPath); - const fromMsg = pico.blue( - mostSuitableComponent - ? `${themeName} ${pico.yellow(mostSuitableComponent)}` - : themeName, + logger.success( + `Copied ${logger.codeC( + mostSuitableComponent + ? `${themeName} ${mostSuitableComponent}` + : themeName, + )} to ${logger.pathC(path.relative(process.cwd(), toPath))}.`, ); - const toMsg = pico.cyan(relativeDir); - - console.log(`\n${pico.green('Success!')} Copied ${fromMsg} to ${toMsg}.\n`); } diff --git a/packages/docusaurus/src/commands/writeHeadingIds.ts b/packages/docusaurus/src/commands/writeHeadingIds.ts index 078a35ef60d2..eca86a5a3016 100644 --- a/packages/docusaurus/src/commands/writeHeadingIds.ts +++ b/packages/docusaurus/src/commands/writeHeadingIds.ts @@ -7,7 +7,7 @@ import fs from 'fs-extra'; import GithubSlugger from 'github-slugger'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import {loadContext, loadPluginConfigs} from '../server'; import initPlugins from '../server/plugins/init'; @@ -147,21 +147,17 @@ export default async function writeHeadingIds( const pathsModified = result.filter(Boolean) as string[]; if (pathsModified.length) { - console.log( - pico.green(`Heading ids added to Markdown files (${ - pathsModified.length - }/${markdownFiles.length} files): -- ${pathsModified.join('\n- ')}`), - ); + logger.success(`Heading ids added to Markdown files (${ + pathsModified.length + }/${markdownFiles.length} files): +- ${pathsModified.join('\n- ')}`); } else { - console.log( - pico.yellow( - `${ - markdownFiles.length - } Markdown files already have explicit heading IDs. If you intend to overwrite the existing heading IDs, use the ${pico.cyan( - '--overwrite', - )} option.`, - ), + logger.warn( + `${ + markdownFiles.length + } Markdown files already have explicit heading IDs. If you intend to overwrite the existing heading IDs, use the ${logger.codeC( + '--overwrite', + )} option.`, ); } } diff --git a/packages/docusaurus/src/server/i18n.ts b/packages/docusaurus/src/server/i18n.ts index 9e6ae0178c6c..b64e0e505123 100644 --- a/packages/docusaurus/src/server/i18n.ts +++ b/packages/docusaurus/src/server/i18n.ts @@ -8,8 +8,7 @@ import {I18n, DocusaurusConfig, I18nLocaleConfig} from '@docusaurus/types'; import path from 'path'; import {normalizeUrl} from '@docusaurus/utils'; import {getLangDir} from 'rtl-detect'; -import {NODE_MAJOR_VERSION} from '../constants'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; function getDefaultLocaleLabel(locale: string) { // Intl.DisplayNames is ES2021 - Node14+ @@ -45,13 +44,13 @@ export async function loadI18n( const currentLocale = options.locale ?? i18nConfig.defaultLocale; if (!i18nConfig.locales.includes(currentLocale)) { - console.warn( - pico.yellow( - `The locale "${currentLocale}" was not found in your site configuration: Available locales are: ${i18nConfig.locales.join( - ',', - )}. + logger.warn( + `The locale "${logger.idC( + currentLocale, + )}" was not found in your site configuration: Available locales are: ${i18nConfig.locales.join( + ',', + )}. Note: Docusaurus only support running one locale at a time.`, - ), ); } @@ -59,14 +58,6 @@ Note: Docusaurus only support running one locale at a time.`, ? i18nConfig.locales : (i18nConfig.locales.concat(currentLocale) as [string, ...string[]]); - if (shouldWarnAboutNodeVersion(NODE_MAJOR_VERSION, locales)) { - console.warn( - pico.yellow( - `To use Docusaurus i18n, it is strongly advised to use Node.js 14 or later (instead of ${NODE_MAJOR_VERSION}).`, - ), - ); - } - function getLocaleConfig(locale: string): I18nLocaleConfig { return { ...getDefaultLocaleConfig(locale), diff --git a/packages/docusaurus/src/server/index.ts b/packages/docusaurus/src/server/index.ts index be56c25bb37a..7e78ad3ce476 100644 --- a/packages/docusaurus/src/server/index.ts +++ b/packages/docusaurus/src/server/index.ts @@ -7,7 +7,7 @@ import {generate} from '@docusaurus/utils'; import path, {join} from 'path'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import ssrDefaultTemplate from '../client/templates/ssr.html.template'; import { DEFAULT_BUILD_DIR_NAME, @@ -418,10 +418,10 @@ function checkDocusaurusPackagesVersion(siteMetadata: DocusaurusSiteMetadata) { ) { // should we throw instead? // It still could work with different versions - console.warn( - pico.red( - `Invalid ${plugin} version ${versionInfo.version}.\nAll official @docusaurus/* packages should have the exact same version as @docusaurus/core (${docusaurusVersion}).\nMaybe you want to check, or regenerate your yarn.lock or package-lock.json file?`, - ), + logger.error( + `Invalid ${plugin} version ${versionInfo.version}. +All official @docusaurus/* packages should have the exact same version as @docusaurus/core (${docusaurusVersion}). +Maybe you want to check, or regenerate your yarn.lock or package-lock.json file?`, ); } }, diff --git a/packages/docusaurus/src/server/plugins/index.ts b/packages/docusaurus/src/server/plugins/index.ts index 162acff8924c..b824a9d47828 100644 --- a/packages/docusaurus/src/server/plugins/index.ts +++ b/packages/docusaurus/src/server/plugins/index.ts @@ -20,7 +20,7 @@ import { InitializedPlugin, } from '@docusaurus/types'; import initPlugins from './init'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import {DEFAULT_PLUGIN_ID} from '../../constants'; import {chain} from 'lodash'; import {localizePluginTranslationFile} from '../translations/translations'; @@ -212,10 +212,8 @@ export async function loadPlugins({ // TODO remove this deprecated lifecycle soon // deprecated since alpha-60 // TODO, 1 user reported usage of this lifecycle! https://github.com/facebook/docusaurus/issues/3918 - console.error( - pico.red( - 'Plugin routesLoaded lifecycle is deprecated. If you think we should keep this lifecycle, please report here: https://github.com/facebook/docusaurus/issues/3918', - ), + logger.error( + 'Plugin routesLoaded lifecycle is deprecated. If you think we should keep this lifecycle, please report here: https://github.com/facebook/docusaurus/issues/3918', ); return plugin.routesLoaded(pluginsRouteConfigs); diff --git a/packages/docusaurus/src/server/translations/translations.ts b/packages/docusaurus/src/server/translations/translations.ts index 4fb971b07594..857352edc29b 100644 --- a/packages/docusaurus/src/server/translations/translations.ts +++ b/packages/docusaurus/src/server/translations/translations.ts @@ -15,7 +15,7 @@ import { } from '@docusaurus/types'; import {getPluginI18nPath, toMessageRelativeFilePath} from '@docusaurus/utils'; import {Joi} from '@docusaurus/utils-validation'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; export type WriteTranslationsOptions = { override?: boolean; @@ -114,11 +114,11 @@ export async function writeTranslationFileContent({ Object.keys(newContent), ); if (unknownKeys.length > 0) { - console.warn( - pico.yellow(`Some translation keys looks unknown to us in file ${filePath} + logger.warn(`Some translation keys looks unknown to us in file ${logger.pathC( + filePath, + )} Maybe you should remove them? -- ${unknownKeys.join('\n- ')}`), - ); +- ${unknownKeys.join('\n- ')}`); } const mergedContent = mergeTranslationFileContent({ @@ -289,12 +289,10 @@ export function applyDefaultCodeTranslations({ Object.keys(extractedCodeTranslations), ); if (unusedDefaultCodeMessages.length > 0) { - console.warn( - pico.yellow(`Unused default message codes found. + logger.warn(`Unused default message codes found. Please report this Docusaurus issue. - ${unusedDefaultCodeMessages.join('\n- ')} -`), - ); +`); } return mapValues( diff --git a/packages/docusaurus/src/server/translations/translationsExtractor.ts b/packages/docusaurus/src/server/translations/translationsExtractor.ts index c450b01a1716..e6870c00b99d 100644 --- a/packages/docusaurus/src/server/translations/translationsExtractor.ts +++ b/packages/docusaurus/src/server/translations/translationsExtractor.ts @@ -7,7 +7,7 @@ import fs from 'fs-extra'; import traverse, {Node} from '@babel/traverse'; import generate from '@babel/generator'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import {parse, types as t, NodePath, TransformOptions} from '@babel/core'; import { InitializedPlugin, @@ -114,10 +114,11 @@ function logSourceCodeFileTranslationsWarnings( ) { sourceCodeFilesTranslations.forEach(({sourceCodeFilePath, warnings}) => { if (warnings.length > 0) { - console.warn( - `Translation extraction warnings for file path=${sourceCodeFilePath}:\n- ${pico.yellow( - warnings.join('\n\n- '), - )}`, + logger.warn( + `Translation extraction warnings for file ${logger.pathC( + sourceCodeFilePath, + )}: +- ${logger.warnC(warnings.join('\n\n- '))}`, ); } }); @@ -301,7 +302,6 @@ function extractSourceCodeAstTranslations( return; } - // console.log('CallExpression', path.node); const args = path.get('arguments'); if (args.length === 1 || args.length === 2) { const firstArgPath = args[0]; @@ -309,8 +309,6 @@ function extractSourceCodeAstTranslations( // evaluation allows translate("x" + "y"); to be considered as translate("xy"); const firstArgEvaluated = firstArgPath.evaluate(); - // console.log('firstArgEvaluated', firstArgEvaluated); - if ( firstArgEvaluated.confident && typeof firstArgEvaluated.value === 'object' diff --git a/packages/docusaurus/src/webpack/client.ts b/packages/docusaurus/src/webpack/client.ts index b5aa201fa75f..6c1913368076 100644 --- a/packages/docusaurus/src/webpack/client.ts +++ b/packages/docusaurus/src/webpack/client.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import path from 'path'; import {Configuration} from 'webpack'; import merge from 'webpack-merge'; @@ -45,12 +45,9 @@ export default function createClientConfig( apply: (compiler) => { compiler.hooks.done.tap('client:done', (stats) => { if (stats.hasErrors()) { - console.log( - pico.red( - 'Client bundle compiled with errors therefore further build is impossible.', - ), + logger.error( + 'Client bundle compiled with errors therefore further build is impossible.', ); - process.exit(1); } }); diff --git a/packages/docusaurus/src/webpack/utils.ts b/packages/docusaurus/src/webpack/utils.ts index c8a2ddcbd03b..1abb1f534384 100644 --- a/packages/docusaurus/src/webpack/utils.ts +++ b/packages/docusaurus/src/webpack/utils.ts @@ -21,7 +21,7 @@ import TerserPlugin from 'terser-webpack-plugin'; import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'; import path from 'path'; import crypto from 'crypto'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import {TransformOptions} from '@babel/core'; import { ConfigureWebpackFn, @@ -165,10 +165,10 @@ export const getCustomizableJSLoader = // TODO remove this before end of 2021? const warnBabelLoaderOnce = memoize(() => { - console.warn( - pico.yellow( - 'Docusaurus plans to support multiple JS loader strategies (Babel, esbuild...): "getBabelLoader(isServer)" is now deprecated in favor of "getJSLoader({isServer})".', - ), + logger.warn( + `Docusaurus plans to support multiple JS loader strategies (Babel, esbuild...): ${logger.codeC( + 'getBabelLoader(isServer)', + )} is now deprecated in favor of ${logger.codeC('getJSLoader(isServer)')}.`, ); }); const getBabelLoaderDeprecated = function getBabelLoaderDeprecated( @@ -181,10 +181,10 @@ const getBabelLoaderDeprecated = function getBabelLoaderDeprecated( // TODO remove this before end of 2021 ? const warnCacheLoaderOnce = memoize(() => { - console.warn( - pico.yellow( - 'Docusaurus uses Webpack 5 and getCacheLoader() usage is now deprecated.', - ), + logger.warn( + `Docusaurus uses Webpack 5 and ${logger.codeC( + 'getCacheLoader()', + )} usage is now deprecated.`, ); }); function getCacheLoaderDeprecated() { @@ -295,9 +295,7 @@ export function compile(config: Configuration[]): Promise { // See https://github.com/webpack/webpack.js.org/pull/4775 compiler.close((errClose) => { if (errClose) { - console.error( - pico.red(`Error while closing Webpack compiler: ${errClose}`), - ); + logger.error(`Error while closing Webpack compiler: ${errClose}`); reject(errClose); } else { resolve(); @@ -445,7 +443,7 @@ function validateKeyAndCerts({ encrypted = crypto.publicEncrypt(cert, Buffer.from('test')); } catch (err) { throw new Error( - `The certificate "${pico.yellow(crtFile)}" is invalid.\n${ + `The certificate "${logger.pathC(crtFile)}" is invalid.\n${ (err as Error).message }`, ); @@ -456,7 +454,7 @@ function validateKeyAndCerts({ crypto.privateDecrypt(key, encrypted); } catch (err) { throw new Error( - `The certificate key "${pico.yellow(keyFile)}" is invalid.\n${ + `The certificate key "${logger.pathC(keyFile)}" is invalid.\n${ (err as Error).message }`, ); @@ -467,9 +465,9 @@ function validateKeyAndCerts({ function readEnvFile(file: string, type: string) { if (!fs.existsSync(file)) { throw new Error( - `You specified ${pico.cyan( + `You specified ${logger.codeC( type, - )} in your env, but the file "${pico.yellow(file)}" can't be found.`, + )} in your env, but the file "${logger.pathC(file)}" can't be found.`, ); } return fs.readFileSync(file); From 77784f38208caaa9bece23de7137fe72c7439c97 Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 08:46:01 +0800 Subject: [PATCH 04/33] Fix package --- packages/docusaurus-plugin-client-redirects/package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/docusaurus-plugin-client-redirects/package.json b/packages/docusaurus-plugin-client-redirects/package.json index 4419957b1ecc..a86d402a75c1 100644 --- a/packages/docusaurus-plugin-client-redirects/package.json +++ b/packages/docusaurus-plugin-client-redirects/package.json @@ -19,7 +19,7 @@ "license": "MIT", "dependencies": { "@docusaurus/core": "2.0.0-beta.9", - "@docusaurus/types": "2.0.0-beta.9", + "@docusaurus/logger": "2.0.0-beta.9", "@docusaurus/utils": "2.0.0-beta.9", "@docusaurus/utils-common": "2.0.0-beta.9", "@docusaurus/utils-validation": "2.0.0-beta.9", @@ -29,6 +29,9 @@ "picocolors": "^1.0.0", "tslib": "^2.3.1" }, + "devDependencies": { + "@docusaurus/types": "2.0.0-beta.9" + }, "peerDependencies": { "react": "^16.8.4 || ^17.0.0", "react-dom": "^16.8.4 || ^17.0.0" From b111968b5f41e5def1454963591736e4a6c53bb9 Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 10:12:49 +0800 Subject: [PATCH 05/33] Interpolate messages --- packages/create-docusaurus/bin/index.js | 7 ++- packages/create-docusaurus/src/index.ts | 16 ++--- packages/docusaurus-logger/src/index.ts | 80 +++++++++++++++++++++---- 3 files changed, 78 insertions(+), 25 deletions(-) diff --git a/packages/create-docusaurus/bin/index.js b/packages/create-docusaurus/bin/index.js index d04533589c83..1b161bc37cc6 100755 --- a/packages/create-docusaurus/bin/index.js +++ b/packages/create-docusaurus/bin/index.js @@ -16,7 +16,9 @@ const requiredVersion = require('../package.json').engines.node; if (!semver.satisfies(process.version, requiredVersion)) { logger.error('Minimum Node.js version not met :('); logger.error( - `You are using Node.js ${process.version}, Requirement: Node.js ${requiredVersion}.`, + 'You are using Node.js %n, Requirement: Node.js %n.', + process.version, + requiredVersion, ); process.exit(1); } @@ -56,8 +58,7 @@ program program.arguments('').action((cmd) => { program.outputHelp(); - logger.error(` Unknown command ${logger.idC(cmd)}.`); - console.log(); + logger.error('Unknown command %c.', cmd); }); program.parse(process.argv); diff --git a/packages/create-docusaurus/src/index.ts b/packages/create-docusaurus/src/index.ts index 3e877c95b4f2..a3f11e14ed4a 100755 --- a/packages/create-docusaurus/src/index.ts +++ b/packages/create-docusaurus/src/index.ts @@ -206,21 +206,19 @@ export default async function init( logger.log('Creating new Docusaurus project...'); if (isValidGitRepoUrl(template)) { - logger.log(`Cloning Git template ${logger.idC(template)}...`); + logger.log('Cloning Git template %p...', template); if ( shell.exec(`git clone --recursive ${template} ${dest}`, {silent: true}) .code !== 0 ) { - throw new Error(`Cloning Git template ${logger.idC(template)} failed!`); + throw new Error(`Cloning Git template ${template} failed!`); } } else if (templates.includes(template)) { // Docusaurus templates. if (useTS) { if (!hasTS(template)) { throw new Error( - `Template ${logger.idC( - template, - )} doesn't provide the Typescript variant.`, + `Template ${template} doesn't provide the Typescript variant.`, ); } template = `${template}${TypeScriptTemplateSuffix}`; @@ -228,9 +226,7 @@ export default async function init( try { await copyTemplate(templatesDir, template, dest); } catch (err) { - logger.error( - `Copying Docusaurus template ${logger.idC(template)} failed!`, - ); + logger.error('Copying Docusaurus template %i failed!', template); throw err; } } else if (fs.existsSync(path.resolve(process.cwd(), template))) { @@ -238,7 +234,7 @@ export default async function init( try { await fs.copy(templateDir, dest); } catch (err) { - logger.error(`Copying local template ${logger.pathC(template)} failed!`); + logger.error('Copying local template %p failed!', template); throw err; } } else { @@ -270,7 +266,7 @@ export default async function init( const pkgManager = useYarn ? 'yarn' : 'npm'; if (!cliOptions.skipInstall) { - logger.log(`Installing dependencies with ${logger.idC(pkgManager)}...`); + logger.log('Installing dependencies with %i...', pkgManager); try { // Use force coloring the output, since the command is invoked by shelljs, which is not the interactive shell diff --git a/packages/docusaurus-logger/src/index.ts b/packages/docusaurus-logger/src/index.ts index 83c92583d3d0..27dc1b3577c8 100644 --- a/packages/docusaurus-logger/src/index.ts +++ b/packages/docusaurus-logger/src/index.ts @@ -8,29 +8,85 @@ import pico from 'picocolors'; export const pathC = pico.magenta; -export const idC = pico.blue; +export const idC = pico.cyan; export const errorC = pico.red; -export const codeC = pico.blue; +export const codeC = pico.cyan; export const subdueC = pico.gray; export const warnC = pico.yellow; export const successC = pico.green; +export const numC = pico.yellow; -export function log(msg: string): void { - console.info(`${pico.bold('[LOG] ')}${msg}`); +type InterpolatableValue = string | number | string[]; + +function assertIsArray(value: InterpolatableValue): asserts value is string[] { + if (!Array.isArray(value)) { + throw new Error( + 'Bad Docusaurus logging message. This is likely an internal bug, please report it', + ); + } +} + +function assertIsStrNum( + value: InterpolatableValue, +): asserts value is string | number { + if (typeof value !== 'string' && typeof value !== 'number') { + throw new Error( + 'Bad Docusaurus logging message. This is likely an internal bug, please report it', + ); + } +} + +function interpolate(msg: string, values: InterpolatableValue[]) { + let index = 0; + function replacer(match: string) { + if (index >= values.length) { + return match; + } + const newStr = values[index]; + index += 1; + if (match === '%a') { + assertIsArray(newStr); + return newStr.join('\n- '); + } + assertIsStrNum(newStr); + switch (match) { + case '%p': + return pathC(newStr); + case '%c': + return codeC(`\`${newStr}\``); + case '%i': + return idC(newStr); + case '%n': + return numC(newStr); + default: + throw new Error( + 'Bad Docusaurus logging message. This is likely an internal bug, please report it', + ); + } + } + return msg.replace(/%[A-Za-z]/g, replacer); +} + +export function log(msg: string, ...values: InterpolatableValue[]): void { + console.info(`${pico.bold('[LOG] ')}${interpolate(msg, values)}`); } -export function info(msg: string): void { - console.info(`${pico.cyan(pico.bold('[INFO] '))}${msg}`); +export function info(msg: string, ...values: InterpolatableValue[]): void { + console.info(`${pico.cyan(pico.bold('[INFO] '))}${interpolate(msg, values)}`); } -export function warn(msg: string): void { - console.info(`${pico.yellow(pico.bold('[WARNING] '))}${msg}`); +export function warn(msg: string, ...values: InterpolatableValue[]): void { + console.info( + `${pico.yellow(pico.bold('[WARNING] '))}${interpolate(msg, values)}`, + ); } -export function error(msg: string): void { - console.info(`${pico.red(pico.bold('[ERROR] '))}${msg}`); +export function error(msg: string, ...values: InterpolatableValue[]): void { + console.info(`${pico.red(pico.bold('[ERROR] '))}${interpolate(msg, values)}`); } -export function success(msg: string): void { - console.info(`${pico.green(pico.bold('[SUCCESS] '))}${msg}`); +export function success(msg: string, ...values: InterpolatableValue[]): void { + console.info( + `${pico.green(pico.bold('[SUCCESS] '))}${interpolate(msg, values)}`, + ); } From f0c50ee31723b55166f2923f82d936f024f30cb4 Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 10:17:11 +0800 Subject: [PATCH 06/33] Fix TS --- packages/docusaurus/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/docusaurus/tsconfig.json b/packages/docusaurus/tsconfig.json index 105a516d4232..af81dee45121 100644 --- a/packages/docusaurus/tsconfig.json +++ b/packages/docusaurus/tsconfig.json @@ -2,7 +2,6 @@ "extends": "../../tsconfig.json", "compilerOptions": { "incremental": true, - "lib": ["DOM", "ES2019"], "tsBuildInfoFile": "./lib/.tsbuildinfo", "rootDir": "src", "outDir": "lib", From 2139b667a31c0fcc77faeaab9a94871a3bb7c490 Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 13:09:16 +0800 Subject: [PATCH 07/33] Fixes --- packages/create-docusaurus/src/index.ts | 33 +++-- packages/docusaurus-logger/src/index.ts | 140 ++++++++++-------- packages/docusaurus-migrate/src/index.ts | 15 +- .../src/collectRedirects.ts | 19 ++- packages/docusaurus/bin/docusaurus.js | 2 +- packages/docusaurus/src/choosePort.ts | 2 +- packages/docusaurus/src/client/serverEntry.js | 27 ++-- packages/docusaurus/src/commands/build.ts | 13 +- packages/docusaurus/src/commands/clear.ts | 6 +- packages/docusaurus/src/commands/deploy.ts | 14 +- packages/docusaurus/src/commands/serve.ts | 6 +- packages/docusaurus/src/commands/start.ts | 10 +- packages/docusaurus/src/commands/swizzle.ts | 67 ++++----- .../src/commands/writeHeadingIds.ts | 17 +-- .../src/server/__tests__/i18n.test.ts | 2 +- packages/docusaurus/src/server/i18n.ts | 8 +- .../src/server/translations/translations.ts | 9 +- .../translations/translationsExtractor.ts | 7 +- packages/docusaurus/src/webpack/utils.ts | 25 ++-- packages/docusaurus/tsconfig.json | 3 +- 20 files changed, 218 insertions(+), 207 deletions(-) diff --git a/packages/create-docusaurus/src/index.ts b/packages/create-docusaurus/src/index.ts index a3f11e14ed4a..339e628b94ed 100755 --- a/packages/create-docusaurus/src/index.ts +++ b/packages/create-docusaurus/src/index.ts @@ -203,10 +203,10 @@ export default async function init( throw new Error('Template should not be empty'); } - logger.log('Creating new Docusaurus project...'); + logger.info('Creating new Docusaurus project...'); if (isValidGitRepoUrl(template)) { - logger.log('Cloning Git template %p...', template); + logger.info('Cloning Git template %p...', template); if ( shell.exec(`git clone --recursive ${template} ${dest}`, {silent: true}) .code !== 0 @@ -266,7 +266,7 @@ export default async function init( const pkgManager = useYarn ? 'yarn' : 'npm'; if (!cliOptions.skipInstall) { - logger.log('Installing dependencies with %i...', pkgManager); + logger.info('Installing dependencies with %i...', pkgManager); try { // Use force coloring the output, since the command is invoked by shelljs, which is not the interactive shell @@ -291,27 +291,36 @@ export default async function init( ? name : path.relative(process.cwd(), name); - logger.info(` -Successfully created "${logger.pathC(cdpath)}". + logger.info( + ` +Successfully created %p. Inside that directory, you can run several commands: - ${logger.codeC(`${pkgManager} start`)} + %c Starts the development server. - ${logger.codeC(`${pkgManager} ${useYarn ? '' : 'run '}build`)} + %c Bundles your website into static files for production. - ${logger.codeC(`${pkgManager} ${useYarn ? '' : 'run '}serve`)} + %c Serves the built website locally. - ${logger.codeC(`${pkgManager} deploy`)} + %c Publishes the website to GitHub pages. We recommend that you begin by typing: - ${logger.codeC('cd')} ${cdpath} - ${logger.codeC(`${pkgManager} start`)} + %c + %c Happy building awesome websites! -`); +`, + cdpath, + `${pkgManager} start`, + `${pkgManager} ${useYarn ? '' : 'run '}build`, + `${pkgManager} ${useYarn ? '' : 'run '}serve`, + `${pkgManager} deploy`, + `cd ${cdpath}`, + `${pkgManager} start`, + ); } diff --git a/packages/docusaurus-logger/src/index.ts b/packages/docusaurus-logger/src/index.ts index 27dc1b3577c8..c8875b6696be 100644 --- a/packages/docusaurus-logger/src/index.ts +++ b/packages/docusaurus-logger/src/index.ts @@ -7,15 +7,6 @@ import pico from 'picocolors'; -export const pathC = pico.magenta; -export const idC = pico.cyan; -export const errorC = pico.red; -export const codeC = pico.cyan; -export const subdueC = pico.gray; -export const warnC = pico.yellow; -export const successC = pico.green; -export const numC = pico.yellow; - type InterpolatableValue = string | number | string[]; function assertIsArray(value: InterpolatableValue): asserts value is string[] { @@ -36,57 +27,84 @@ function assertIsStrNum( } } -function interpolate(msg: string, values: InterpolatableValue[]) { - let index = 0; - function replacer(match: string) { - if (index >= values.length) { - return match; - } - const newStr = values[index]; - index += 1; - if (match === '%a') { - assertIsArray(newStr); - return newStr.join('\n- '); - } - assertIsStrNum(newStr); - switch (match) { - case '%p': - return pathC(newStr); - case '%c': - return codeC(`\`${newStr}\``); - case '%i': - return idC(newStr); - case '%n': - return numC(newStr); - default: - throw new Error( - 'Bad Docusaurus logging message. This is likely an internal bug, please report it', - ); - } - } - return msg.replace(/%[A-Za-z]/g, replacer); -} - -export function log(msg: string, ...values: InterpolatableValue[]): void { - console.info(`${pico.bold('[LOG] ')}${interpolate(msg, values)}`); -} - -export function info(msg: string, ...values: InterpolatableValue[]): void { - console.info(`${pico.cyan(pico.bold('[INFO] '))}${interpolate(msg, values)}`); -} +const pathC: import('picocolors/types').Formatter = (msg) => + pico.cyan(pico.underline(msg)); +const idC: import('picocolors/types').Formatter = (msg) => + pico.blue(pico.bold(msg)); +const errorC = pico.red; +const codeC: import('picocolors/types').Formatter = (msg) => + pico.cyan(`\`${msg}\``); +const subdueC = pico.gray; +const warnC = pico.yellow; +const successC = pico.green; +const numC = pico.yellow; -export function warn(msg: string, ...values: InterpolatableValue[]): void { - console.info( - `${pico.yellow(pico.bold('[WARNING] '))}${interpolate(msg, values)}`, - ); -} - -export function error(msg: string, ...values: InterpolatableValue[]): void { - console.info(`${pico.red(pico.bold('[ERROR] '))}${interpolate(msg, values)}`); -} +const logger = { + ...pico, + pathC, + idC, + errorC, + codeC, + subdueC, + warnC, + successC, + numC, + interpolate(msg: string, ...values: InterpolatableValue[]): string { + let index = 0; + function replacer(match: string) { + if (index >= values.length) { + return match; + } + const newStr = values[index]; + index += 1; + if (match === '%a') { + assertIsArray(newStr); + return `\n- ${newStr.join('\n- ')}\n`; + } + assertIsStrNum(newStr); + switch (match) { + case '%p': + return pathC(newStr); + case '%c': + return codeC(newStr); + case '%i': + return idC(newStr); + case '%n': + return numC(newStr); + default: + throw new Error( + 'Bad Docusaurus logging message. This is likely an internal bug, please report it', + ); + } + } + return msg.replace(/%[A-Za-z]/g, replacer); + }, + info(msg: string, ...values: InterpolatableValue[]): void { + console.info( + `${pico.cyan(pico.bold('[INFO] '))}${this.interpolate(msg, ...values)}`, + ); + }, + warn(msg: string, ...values: InterpolatableValue[]): void { + console.warn( + `${pico.yellow(pico.bold('[WARNING] '))}${this.interpolate( + msg, + ...values, + )}`, + ); + }, + error(msg: string, ...values: InterpolatableValue[]): void { + console.error( + `${pico.red(pico.bold('[ERROR] '))}${this.interpolate(msg, ...values)}`, + ); + }, + success(msg: string, ...values: InterpolatableValue[]): void { + console.log( + `${pico.green(pico.bold('[SUCCESS] '))}${this.interpolate( + msg, + ...values, + )}`, + ); + }, +}; -export function success(msg: string, ...values: InterpolatableValue[]): void { - console.info( - `${pico.green(pico.bold('[SUCCESS] '))}${interpolate(msg, values)}`, - ); -} +export default logger; diff --git a/packages/docusaurus-migrate/src/index.ts b/packages/docusaurus-migrate/src/index.ts index 9e26f43c2da9..b478c88f561b 100644 --- a/packages/docusaurus-migrate/src/index.ts +++ b/packages/docusaurus-migrate/src/index.ts @@ -259,12 +259,11 @@ export function createConfigFile({ } }); logger.info( - `Following Fields from ${logger.pathC( - 'siteConfig.js', - )} will be added to ${logger.pathC( - 'docusaurus.config.js', - )} in ${logger.codeC('customFields')}: -- ${Object.keys(customConfigFields).join('\n- ')}`, + `Following Fields from %p will be added to %p in %c:%a`, + 'siteConfig.js', + 'docusaurus.config.js', + 'customFields', + Object.keys(customConfigFields), ); let v2DocsPath: string | undefined; @@ -767,7 +766,5 @@ export async function migrateMDToMDX( sanitizedFileContent(String(fs.readFileSync(file)), true), ); }); - logger.success( - `Successfully migrated ${logger.pathC(siteDir)} to ${logger.pathC(newDir)}`, - ); + logger.success(`Successfully migrated %p to %p`, siteDir, newDir); } diff --git a/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts b/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts index 0bfcb4975468..2e067d064abc 100644 --- a/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts +++ b/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts @@ -99,12 +99,13 @@ function filterUnwantedRedirects( Object.entries(groupBy(redirects, (redirect) => redirect.from)).forEach( ([from, groupedFromRedirects]) => { if (groupedFromRedirects.length > 1) { - logger.error(`@docusaurus/plugin-client-redirects: multiple redirects are created with the same "from" pathname: ${logger.pathC( + logger.error( + `%i: multiple redirects are created with the same "from" pathname: %p +It is not possible to redirect the same pathname to multiple destinations:%a`, + '@docusaurus/plugin-client-redirects', from, - )} -It is not possible to redirect the same pathname to multiple destinations: -- ${groupedFromRedirects.map((r) => JSON.stringify(r)).join('\n- ')} -`); + groupedFromRedirects.map((r) => JSON.stringify(r)), + ); } }, ); @@ -115,9 +116,11 @@ It is not possible to redirect the same pathname to multiple destinations: (redirect) => pluginContext.relativeRoutesPaths.includes(redirect.from), ); if (redirectsOverridingExistingPath.length > 0) { - logger.error(`@docusaurus/plugin-client-redirects: some redirects would override existing paths, and will be ignored: -- ${redirectsOverridingExistingPath.map((r) => JSON.stringify(r)).join('\n- ')} -`); + logger.error( + `%i: some redirects would override existing paths, and will be ignored:%a`, + '@docusaurus/plugin-client-redirects', + redirectsOverridingExistingPath.map((r) => JSON.stringify(r)), + ); } return collectedRedirects.filter( (redirect) => !pluginContext.relativeRoutesPaths.includes(redirect.from), diff --git a/packages/docusaurus/bin/docusaurus.js b/packages/docusaurus/bin/docusaurus.js index 9ed1781805d6..8ba7e866a662 100755 --- a/packages/docusaurus/bin/docusaurus.js +++ b/packages/docusaurus/bin/docusaurus.js @@ -219,7 +219,7 @@ cli cli.arguments('').action((cmd) => { cli.outputHelp(); - logger.error(` Unknown command ${logger.idC(cmd)}.`); + logger.error(' Unknown command %i.', cmd); console.log(); }); diff --git a/packages/docusaurus/src/choosePort.ts b/packages/docusaurus/src/choosePort.ts index 14069bda9d86..2a5faf5f2fb5 100644 --- a/packages/docusaurus/src/choosePort.ts +++ b/packages/docusaurus/src/choosePort.ts @@ -122,7 +122,7 @@ export default async function choosePort( }), (err) => { throw new Error( - `Could not find an open port at ${logger.idC(host)}. + `Could not find an open port at ${host}. ${`Network error message: "${err.message || err}".`}`, ); }, diff --git a/packages/docusaurus/src/client/serverEntry.js b/packages/docusaurus/src/client/serverEntry.js index 7c28d05d57c3..fee844698600 100644 --- a/packages/docusaurus/src/client/serverEntry.js +++ b/packages/docusaurus/src/client/serverEntry.js @@ -43,22 +43,26 @@ export default async function render(locals) { try { return await doRender(locals); } catch (e) { - logger.error(`Docusaurus Node/SSR could not render static page with path "${logger.pathC( + logger.error( + `Docusaurus Node/SSR could not render static page with path %p because of following error: +${e.stack}`, locals.path, - )}" because of following error: -${e.stack}`); + ); const isNotDefinedErrorRegex = /(window|document|localStorage|navigator|alert|location|buffer|self) is not defined/i; if (isNotDefinedErrorRegex.test(e.message)) { - logger.tip(`It looks like you are using code that should run on the client-side only. -To get around it, try using ${logger.idC( + logger.tip( + `It looks like you are using code that should run on the client-side only. +To get around it, try using %c (%p) or %c (%p). +It might also require to wrap your client code in %c hook and/or import a third-party library dynamically (if any).`, '', - )} (https://docusaurus.io/docs/docusaurus-core/#browseronly) or ${logger.idC( + 'https://docusaurus.io/docs/docusaurus-core/#browseronly', 'ExecutionEnvironment', - )} (https://docusaurus.io/docs/docusaurus-core/#executionenvironment). -It might also require to wrap your client code in useEffect hook and/or import a third-party library dynamically (if any).`); + 'https://docusaurus.io/docs/docusaurus-core/#executionenvironment', + 'useEffect', + ); } throw new Error('Server-side rendering fails due to the error above.'); @@ -143,10 +147,11 @@ async function doRender(locals) { minifyJS: true, }); } catch (e) { - logger.error(`Minification page with path "${logger.pathC( + logger.error( + `Minification page with path %p failed because of following error: +${e.stack}`, locals.path, - )}" failed because of following error: -${e.stack}`); + ); throw e; } } diff --git a/packages/docusaurus/src/commands/build.ts b/packages/docusaurus/src/commands/build.ts index 3473abd6d785..314d05abcdc9 100644 --- a/packages/docusaurus/src/commands/build.ts +++ b/packages/docusaurus/src/commands/build.ts @@ -108,7 +108,7 @@ async function buildLocale({ }): Promise { process.env.BABEL_ENV = 'production'; process.env.NODE_ENV = 'production'; - logger.log(`[${locale}] Creating an optimized production build...`); + logger.info('[%i] Creating an optimized production build...', locale); const props: Props = await load(siteDir, { customOutDir: cliOptions.outDir, @@ -233,17 +233,12 @@ async function buildLocale({ }); logger.success( - `Generated static files in ${logger.pathC( - path.relative(process.cwd(), outDir), - )}.`, + 'Generated static files in %p.', + path.relative(process.cwd(), outDir), ); if (isLastLocale) { - logger.info( - `Use ${logger.codeC( - 'npm run serve', - )} command to test your build locally.`, - ); + logger.info('Use %c command to test your build locally.', 'npm run serve'); } if (forceTerminate && isLastLocale && !cliOptions.bundleAnalyzer) { diff --git a/packages/docusaurus/src/commands/clear.ts b/packages/docusaurus/src/commands/clear.ts index f10fba218c7d..d192296c8295 100644 --- a/packages/docusaurus/src/commands/clear.ts +++ b/packages/docusaurus/src/commands/clear.ts @@ -12,10 +12,10 @@ import {DEFAULT_BUILD_DIR_NAME, GENERATED_FILES_DIR_NAME} from '../constants'; async function removePath(fsPath: string) { try { fs.remove(path.join(fsPath)); - logger.success(`Removed the "${logger.pathC(fsPath)}" directory.`); + logger.success('Removed the %p directory.', fsPath); } catch (e) { - logger.error(`Could not remove ${logger.pathC(fsPath)} directory.`); - logger.error((e as Error).message); + logger.error('Could not remove %p directory.', fsPath); + logger.error(e as string); } } diff --git a/packages/docusaurus/src/commands/deploy.ts b/packages/docusaurus/src/commands/deploy.ts index 0b1c61194cb3..a5971813d7ec 100644 --- a/packages/docusaurus/src/commands/deploy.ts +++ b/packages/docusaurus/src/commands/deploy.ts @@ -25,7 +25,7 @@ function obfuscateGitPass(str: string) { function shellExecLog(cmd: string) { try { const result = shell.exec(cmd); - logger.log( + logger.info( `${obfuscateGitPass(cmd)} ${logger.subdueC(`(code: ${result.code})`)}`, ); return result; @@ -88,7 +88,7 @@ This behavior can have SEO impacts and create relative link issues. `); } - logger.log('Deploy command invoked...'); + logger.info('Deploy command invoked...'); if (!shell.which('git')) { throw new Error('Git not installed or on the PATH!'); } @@ -129,7 +129,7 @@ This behavior can have SEO impacts and create relative link issues. `Missing project organization name. Did you forget to define "organizationName" in ${siteConfigPath}? You may also export it via the ORGANIZATION_NAME environment variable.`, ); } - logger.info(`organizationName: ${logger.idC(organizationName)}`); + logger.info('organizationName: %i', organizationName); const projectName = process.env.PROJECT_NAME || @@ -140,7 +140,7 @@ This behavior can have SEO impacts and create relative link issues. `Missing project name. Did you forget to define "projectName" in ${siteConfigPath}? You may also export it via the PROJECT_NAME environment variable.`, ); } - logger.info(`projectName: ${logger.idC(projectName)}`); + logger.info('projectName: %i', projectName); // We never deploy on pull request. const isPullRequest = @@ -167,7 +167,7 @@ You can also set the deploymentBranch property in docusaurus.config.js .`); const deploymentBranch = process.env.DEPLOYMENT_BRANCH || siteConfig.deploymentBranch || 'gh-pages'; - logger.info(`deploymentBranch: ${logger.idC(deploymentBranch)}`); + logger.info('deploymentBranch: %i', deploymentBranch); const githubHost = process.env.GITHUB_HOST || siteConfig.githubHost || 'github.com'; @@ -193,9 +193,7 @@ You can also set the deploymentBranch property in docusaurus.config.js .`); ); } - logger.info( - `Remote repo URL: ${logger.idC(obfuscateGitPass(deploymentRepoURL))}`, - ); + logger.info('Remote repo URL: %i', obfuscateGitPass(deploymentRepoURL)); // Check if this is a cross-repo publish. const crossRepoPublish = !sourceRepoUrl.endsWith( diff --git a/packages/docusaurus/src/commands/serve.ts b/packages/docusaurus/src/commands/serve.ts index e724be7a5b1b..bdf3ba3c4f54 100644 --- a/packages/docusaurus/src/commands/serve.ts +++ b/packages/docusaurus/src/commands/serve.ts @@ -71,9 +71,9 @@ export default async function serve( }); logger.success( - `Serving "${logger.pathC(cliOptions.dir)}" directory at "${logger.pathC( - servingUrl + baseUrl, - )}".`, + 'Serving %p directory at %p.', + cliOptions.dir, + servingUrl + baseUrl, ); server.listen(port); } diff --git a/packages/docusaurus/src/commands/start.ts b/packages/docusaurus/src/commands/start.ts index 57e89839ee15..0efdfcda9567 100644 --- a/packages/docusaurus/src/commands/start.ts +++ b/packages/docusaurus/src/commands/start.ts @@ -34,7 +34,7 @@ export default async function start( ): Promise { process.env.NODE_ENV = 'development'; process.env.BABEL_ENV = 'development'; - logger.log('Starting the development server...'); + logger.info('Starting the development server...'); function loadSite() { return load(siteDir, { @@ -60,9 +60,7 @@ export default async function start( const urls = prepareUrls(protocol, host, port); const openUrl = normalizeUrl([urls.localUrlForBrowser, baseUrl]); - logger.success( - `Docusaurus website is running at "${logger.pathC(openUrl)}".`, - ); + logger.success('Docusaurus website is running at %p.', openUrl); // Reload files processing. const reload = debounce(() => { @@ -70,9 +68,7 @@ export default async function start( .then(({baseUrl: newBaseUrl}) => { const newOpenUrl = normalizeUrl([urls.localUrlForBrowser, newBaseUrl]); if (newOpenUrl !== openUrl) { - logger.success( - `Docusaurus website is running at "${logger.pathC(newOpenUrl)}".`, - ); + logger.success('Docusaurus website is running at %p.', newOpenUrl); } }) .catch((err) => { diff --git a/packages/docusaurus/src/commands/swizzle.ts b/packages/docusaurus/src/commands/swizzle.ts index fa1ea1d836ac..bade6744abd7 100644 --- a/packages/docusaurus/src/commands/swizzle.ts +++ b/packages/docusaurus/src/commands/swizzle.ts @@ -104,7 +104,7 @@ function themeComponents( } return ` -'Theme components available for swizzle.' +Theme components available for swizzle. ${logger.successC('green =>')} safe: lower breaking change risk ${logger.errorC('red =>')} unsafe: higher breaking change risk @@ -113,10 +113,6 @@ ${components.join('\n')} `; } -function formattedThemeNames(themeNames: string[]): string { - return `Themes available for swizzle:\n- ${themeNames.join('\n- ')}`; -} - function colorCode( themePath: string, plugin: ImportedPluginModule, @@ -135,10 +131,12 @@ function colorCode( ); return [ - ...greenComponents.map((component) => - logger.successC(`safe: ${component}`), + ...greenComponents.map( + (component) => `${logger.successC('safe:')} ${component}`, + ), + ...redComponents.map( + (component) => `${logger.errorC('unsafe:')} ${component}`, ), - ...redComponents.map((component) => logger.errorC(`unsafe: ${component}`)), ]; } @@ -163,26 +161,26 @@ export default async function swizzle( ); if (!themeName) { - logger.error(formattedThemeNames(themeNames)); - process.exit(1); + logger.info('Themes available for swizzle:\n%a', themeNames); + return; } let pluginModule: ImportedPluginModule; try { pluginModule = importFresh(themeName); } catch { - let suggestion; + let suggestion: string | undefined; themeNames.forEach((name) => { if (leven(name, themeName) < 4) { suggestion = name; } }); logger.error( - `Theme ${logger.idC(themeName)} not found. ${ - suggestion - ? `Did you mean "${logger.idC(suggestion)}" ?` - : formattedThemeNames(themeNames) + `Theme %i not found. ${ + suggestion ? `Did you mean %i ?` : 'Themes available for swizzle:%a' }`, + themeName, + suggestion ?? themeNames, ); process.exit(1); } @@ -229,8 +227,8 @@ export default async function swizzle( } if (!componentName) { - console.warn(themeComponents(themePath, pluginModule)); - process.exit(1); + logger.info(themeComponents(themePath, pluginModule)); + return; } const components = getComponentName(themePath, pluginModule, Boolean(danger)); @@ -256,11 +254,11 @@ export default async function swizzle( if (mostSuitableMatch !== componentName) { mostSuitableComponent = mostSuitableMatch; - logger.error(`Component "${logger.idC(componentName)}" doesn't exist.`); + logger.error("Component %i doesn't exist.", componentName); logger.info( - `"${logger.idC( - mostSuitableComponent, - )}" is swizzled instead of "${logger.idC(componentName)}".`, + `%i is swizzled instead of %i.`, + mostSuitableComponent, + componentName, ); } } @@ -277,18 +275,21 @@ export default async function swizzle( } else if (fs.existsSync(`${fromPath}.js`)) { [fromPath, toPath] = [`${fromPath}.js`, `${toPath}.js`]; } else { - let suggestion; + let suggestion: string | undefined; components.forEach((name) => { if (leven(name, mostSuitableComponent) < 3) { suggestion = name; } }); logger.error( - `Component ${logger.idC(mostSuitableComponent)} not found. ${ + `Component %i not found. ${ suggestion - ? `Did you mean "${logger.idC(suggestion)}"?` - : `${themeComponents(themePath, pluginModule)}` + ? `Did you mean %i ?` + : themeComponents(themePath, pluginModule) + } }`, + mostSuitableComponent, + suggestion!, ); process.exit(1); } @@ -296,11 +297,9 @@ export default async function swizzle( if (!components.includes(mostSuitableComponent) && !danger) { logger.error( - `${logger.idC( - mostSuitableComponent, - )} is an internal component and has a higher breaking change probability. If you want to swizzle it, use the ${logger.codeC( - '--danger', - )} flag.`, + '%i is an internal component and has a higher breaking change probability. If you want to swizzle it, use the %c flag.', + mostSuitableComponent, + '--danger', ); process.exit(1); } @@ -308,10 +307,8 @@ export default async function swizzle( await fs.copy(fromPath, toPath); logger.success( - `Copied ${logger.codeC( - mostSuitableComponent - ? `${themeName} ${mostSuitableComponent}` - : themeName, - )} to ${logger.pathC(path.relative(process.cwd(), toPath))}.`, + `Copied %c to %p.`, + mostSuitableComponent ? `${themeName} ${mostSuitableComponent}` : themeName, + path.relative(process.cwd(), toPath), ); } diff --git a/packages/docusaurus/src/commands/writeHeadingIds.ts b/packages/docusaurus/src/commands/writeHeadingIds.ts index eca86a5a3016..8e26bb75a7f4 100644 --- a/packages/docusaurus/src/commands/writeHeadingIds.ts +++ b/packages/docusaurus/src/commands/writeHeadingIds.ts @@ -147,17 +147,16 @@ export default async function writeHeadingIds( const pathsModified = result.filter(Boolean) as string[]; if (pathsModified.length) { - logger.success(`Heading ids added to Markdown files (${ - pathsModified.length - }/${markdownFiles.length} files): -- ${pathsModified.join('\n- ')}`); + logger.success( + `Heading ids added to Markdown files (%n files):%a`, + `${pathsModified.length}/${markdownFiles.length}`, + pathsModified, + ); } else { logger.warn( - `${ - markdownFiles.length - } Markdown files already have explicit heading IDs. If you intend to overwrite the existing heading IDs, use the ${logger.codeC( - '--overwrite', - )} option.`, + `%n Markdown files already have explicit heading IDs. If you intend to overwrite the existing heading IDs, use the %c option.`, + markdownFiles.length, + '--overwrite', ); } } diff --git a/packages/docusaurus/src/server/__tests__/i18n.test.ts b/packages/docusaurus/src/server/__tests__/i18n.test.ts index 6c2a30e5856a..bec9e039b1a5 100644 --- a/packages/docusaurus/src/server/__tests__/i18n.test.ts +++ b/packages/docusaurus/src/server/__tests__/i18n.test.ts @@ -174,7 +174,7 @@ describe('loadI18n', () => { 'it', ); expect(consoleSpy.mock.calls[0][0]).toMatch( - /The locale "it" was not found in your site configuration/, + /The locale .*it.* was not found in your site configuration/, ); }); }); diff --git a/packages/docusaurus/src/server/i18n.ts b/packages/docusaurus/src/server/i18n.ts index b64e0e505123..12167bd593b2 100644 --- a/packages/docusaurus/src/server/i18n.ts +++ b/packages/docusaurus/src/server/i18n.ts @@ -45,12 +45,10 @@ export async function loadI18n( if (!i18nConfig.locales.includes(currentLocale)) { logger.warn( - `The locale "${logger.idC( - currentLocale, - )}" was not found in your site configuration: Available locales are: ${i18nConfig.locales.join( - ',', - )}. + `The locale %i was not found in your site configuration: Available locales are: %a. Note: Docusaurus only support running one locale at a time.`, + currentLocale, + i18nConfig.locales, ); } diff --git a/packages/docusaurus/src/server/translations/translations.ts b/packages/docusaurus/src/server/translations/translations.ts index 857352edc29b..3535c34554e9 100644 --- a/packages/docusaurus/src/server/translations/translations.ts +++ b/packages/docusaurus/src/server/translations/translations.ts @@ -114,11 +114,12 @@ export async function writeTranslationFileContent({ Object.keys(newContent), ); if (unknownKeys.length > 0) { - logger.warn(`Some translation keys looks unknown to us in file ${logger.pathC( + logger.warn( + `Some translation keys looks unknown to us in file %p. +Maybe you should remove them? %a`, filePath, - )} -Maybe you should remove them? -- ${unknownKeys.join('\n- ')}`); + unknownKeys, + ); } const mergedContent = mergeTranslationFileContent({ diff --git a/packages/docusaurus/src/server/translations/translationsExtractor.ts b/packages/docusaurus/src/server/translations/translationsExtractor.ts index e6870c00b99d..7ec2c48549eb 100644 --- a/packages/docusaurus/src/server/translations/translationsExtractor.ts +++ b/packages/docusaurus/src/server/translations/translationsExtractor.ts @@ -115,10 +115,9 @@ function logSourceCodeFileTranslationsWarnings( sourceCodeFilesTranslations.forEach(({sourceCodeFilePath, warnings}) => { if (warnings.length > 0) { logger.warn( - `Translation extraction warnings for file ${logger.pathC( - sourceCodeFilePath, - )}: -- ${logger.warnC(warnings.join('\n\n- '))}`, + `Translation extraction warnings for file %p:%a`, + sourceCodeFilePath, + warnings, ); } }); diff --git a/packages/docusaurus/src/webpack/utils.ts b/packages/docusaurus/src/webpack/utils.ts index 1abb1f534384..e3f5af893550 100644 --- a/packages/docusaurus/src/webpack/utils.ts +++ b/packages/docusaurus/src/webpack/utils.ts @@ -166,9 +166,9 @@ export const getCustomizableJSLoader = // TODO remove this before end of 2021? const warnBabelLoaderOnce = memoize(() => { logger.warn( - `Docusaurus plans to support multiple JS loader strategies (Babel, esbuild...): ${logger.codeC( - 'getBabelLoader(isServer)', - )} is now deprecated in favor of ${logger.codeC('getJSLoader(isServer)')}.`, + `Docusaurus plans to support multiple JS loader strategies (Babel, esbuild...): %c is now deprecated in favor of %c.`, + 'getBabelLoader(isServer)', + 'getJSLoader(isServer)', ); }); const getBabelLoaderDeprecated = function getBabelLoaderDeprecated( @@ -182,9 +182,8 @@ const getBabelLoaderDeprecated = function getBabelLoaderDeprecated( // TODO remove this before end of 2021 ? const warnCacheLoaderOnce = memoize(() => { logger.warn( - `Docusaurus uses Webpack 5 and ${logger.codeC( - 'getCacheLoader()', - )} usage is now deprecated.`, + `Docusaurus uses Webpack 5 and %c usage is now deprecated.`, + 'getCacheLoader()', ); }); function getCacheLoaderDeprecated() { @@ -443,9 +442,8 @@ function validateKeyAndCerts({ encrypted = crypto.publicEncrypt(cert, Buffer.from('test')); } catch (err) { throw new Error( - `The certificate "${logger.pathC(crtFile)}" is invalid.\n${ - (err as Error).message - }`, + `The certificate ${crtFile} is invalid. +${err}`, ); } @@ -454,9 +452,8 @@ function validateKeyAndCerts({ crypto.privateDecrypt(key, encrypted); } catch (err) { throw new Error( - `The certificate key "${logger.pathC(keyFile)}" is invalid.\n${ - (err as Error).message - }`, + `The certificate key ${keyFile} is invalid. +${err}`, ); } } @@ -465,9 +462,7 @@ function validateKeyAndCerts({ function readEnvFile(file: string, type: string) { if (!fs.existsSync(file)) { throw new Error( - `You specified ${logger.codeC( - type, - )} in your env, but the file "${logger.pathC(file)}" can't be found.`, + `You specified ${type} in your env, but the file "${file}" can't be found.`, ); } return fs.readFileSync(file); diff --git a/packages/docusaurus/tsconfig.json b/packages/docusaurus/tsconfig.json index af81dee45121..817c06354103 100644 --- a/packages/docusaurus/tsconfig.json +++ b/packages/docusaurus/tsconfig.json @@ -6,7 +6,8 @@ "rootDir": "src", "outDir": "lib", "jsx": "react", - "allowJs": true + "allowJs": true, + "allowSyntheticDefaultImports": true, }, "include": ["src"], "exclude": ["**/__tests__/**/*", "src/client"] From cfe77000b2e8fd13fddb86bfab91c4648b861084 Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 13:15:32 +0800 Subject: [PATCH 08/33] Better API --- packages/create-docusaurus/src/index.ts | 8 ++--- packages/docusaurus-logger/src/index.ts | 34 +++++++++------------ packages/docusaurus/src/choosePort.ts | 14 ++++----- packages/docusaurus/src/commands/swizzle.ts | 8 ++--- 4 files changed, 28 insertions(+), 36 deletions(-) diff --git a/packages/create-docusaurus/src/index.ts b/packages/create-docusaurus/src/index.ts index 339e628b94ed..44cf1d7f57a5 100755 --- a/packages/create-docusaurus/src/index.ts +++ b/packages/create-docusaurus/src/index.ts @@ -171,7 +171,7 @@ export default async function init( if (url && isValidGitRepoUrl(url)) { return true; } - return logger.errorC('Invalid repository URL'); + return logger.red('Invalid repository URL'); }, message: 'Enter a repository URL from GitHub, Bitbucket, GitLab, or any other public repo.\n(e.g: https://github.com/ownerName/repoName.git)', @@ -187,11 +187,9 @@ export default async function init( if (fs.existsSync(fullDir)) { return true; } - return logger.errorC( - `The path ${logger.pathC(fullDir)} does not exist.`, - ); + return logger.red(`The path ${logger.path(fullDir)} does not exist.`); } - return logger.errorC('Please enter a valid path.'); + return logger.red('Please enter a valid path.'); }, message: 'Enter a local folder path, relative to the current working directory.', diff --git a/packages/docusaurus-logger/src/index.ts b/packages/docusaurus-logger/src/index.ts index c8875b6696be..b9cc92a937a5 100644 --- a/packages/docusaurus-logger/src/index.ts +++ b/packages/docusaurus-logger/src/index.ts @@ -27,28 +27,22 @@ function assertIsStrNum( } } -const pathC: import('picocolors/types').Formatter = (msg) => +const path: import('picocolors/types').Formatter = (msg) => pico.cyan(pico.underline(msg)); -const idC: import('picocolors/types').Formatter = (msg) => +const id: import('picocolors/types').Formatter = (msg) => pico.blue(pico.bold(msg)); -const errorC = pico.red; -const codeC: import('picocolors/types').Formatter = (msg) => +const code: import('picocolors/types').Formatter = (msg) => pico.cyan(`\`${msg}\``); -const subdueC = pico.gray; -const warnC = pico.yellow; -const successC = pico.green; -const numC = pico.yellow; +const subdue = pico.gray; +const num = pico.yellow; const logger = { ...pico, - pathC, - idC, - errorC, - codeC, - subdueC, - warnC, - successC, - numC, + path, + id, + code, + subdue, + num, interpolate(msg: string, ...values: InterpolatableValue[]): string { let index = 0; function replacer(match: string) { @@ -64,13 +58,13 @@ const logger = { assertIsStrNum(newStr); switch (match) { case '%p': - return pathC(newStr); + return path(newStr); case '%c': - return codeC(newStr); + return code(newStr); case '%i': - return idC(newStr); + return id(newStr); case '%n': - return numC(newStr); + return num(newStr); default: throw new Error( 'Bad Docusaurus logging message. This is likely an internal bug, please report it', diff --git a/packages/docusaurus/src/choosePort.ts b/packages/docusaurus/src/choosePort.ts index 2a5faf5f2fb5..09618c49258d 100644 --- a/packages/docusaurus/src/choosePort.ts +++ b/packages/docusaurus/src/choosePort.ts @@ -68,9 +68,9 @@ function getProcessForPort(port: number): string | null { const processId = getProcessIdOnPort(port); const directory = getDirectoryOfProcessById(processId); const command = getProcessCommand(processId); - return `${logger.codeC(command)} ${logger.subdueC( + return `${logger.code(command)} ${logger.subdue( `(pid ${processId})`, - )} in ${logger.pathC(directory)}`; + )} in ${logger.path(directory)}`; } catch (e) { return null; } @@ -101,11 +101,11 @@ export default async function choosePort( const question: prompts.PromptObject = { type: 'confirm', name: 'shouldChangePort', - message: `${logger.warnC( - `${message}${ - existingProcess ? ` Probably:\n ${existingProcess}` : '' - }`, - )}\n\nWould you like to run the app on another port instead?`, + message: `${logger.yellow(logger.bold('[WARNING]'))} ${message}${ + existingProcess ? ` Probably:\n ${existingProcess}` : '' + } + +Would you like to run the app on another port instead?`, initial: true, }; prompts(question).then((answer) => { diff --git a/packages/docusaurus/src/commands/swizzle.ts b/packages/docusaurus/src/commands/swizzle.ts index bade6744abd7..91364663a000 100644 --- a/packages/docusaurus/src/commands/swizzle.ts +++ b/packages/docusaurus/src/commands/swizzle.ts @@ -106,8 +106,8 @@ function themeComponents( return ` Theme components available for swizzle. -${logger.successC('green =>')} safe: lower breaking change risk -${logger.errorC('red =>')} unsafe: higher breaking change risk +${logger.green(logger.bold('green =>'))} safe: lower breaking change risk +${logger.red(logger.bold('red =>'))} unsafe: higher breaking change risk ${components.join('\n')} `; @@ -132,10 +132,10 @@ function colorCode( return [ ...greenComponents.map( - (component) => `${logger.successC('safe:')} ${component}`, + (component) => `${logger.green(logger.bold('safe:'))} ${component}`, ), ...redComponents.map( - (component) => `${logger.errorC('unsafe:')} ${component}`, + (component) => `${logger.red(logger.bold('unsafe:'))} ${component}`, ), ]; } From 9ff817dd6c1c3e2d46cd2aa26524379926543e26 Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 13:19:33 +0800 Subject: [PATCH 09/33] Fix --- packages/docusaurus/src/commands/deploy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus/src/commands/deploy.ts b/packages/docusaurus/src/commands/deploy.ts index a5971813d7ec..b4ac6b2200ad 100644 --- a/packages/docusaurus/src/commands/deploy.ts +++ b/packages/docusaurus/src/commands/deploy.ts @@ -26,7 +26,7 @@ function shellExecLog(cmd: string) { try { const result = shell.exec(cmd); logger.info( - `${obfuscateGitPass(cmd)} ${logger.subdueC(`(code: ${result.code})`)}`, + `${obfuscateGitPass(cmd)} ${logger.subdue(`(code: ${result.code})`)}`, ); return result; } catch (e) { From dd8f609806134c9712e242d7b0f662151e99f3f3 Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 13:43:56 +0800 Subject: [PATCH 10/33] More migration --- packages/create-docusaurus/package.json | 1 - packages/docusaurus-logger/src/index.ts | 12 ++++++++---- packages/docusaurus-migrate/bin/index.js | 2 +- packages/docusaurus-migrate/src/index.ts | 4 ++-- .../package.json | 1 - .../package.json | 2 +- .../src/authors.ts | 8 +++----- .../src/blogUtils.ts | 18 +++++++++--------- packages/docusaurus/bin/beforeCli.js | 16 +++++++--------- packages/docusaurus/bin/docusaurus.js | 1 - packages/docusaurus/package.json | 1 - packages/docusaurus/src/commands/build.ts | 2 +- .../src/server/__tests__/i18n.test.ts | 1 - .../docusaurus/src/server/configValidation.ts | 3 ++- .../src/server/translations/translations.ts | 13 ++++--------- packages/docusaurus/src/webpack/utils.ts | 6 +++--- packages/docusaurus/tsconfig.json | 3 +-- 17 files changed, 42 insertions(+), 52 deletions(-) diff --git a/packages/create-docusaurus/package.json b/packages/create-docusaurus/package.json index 6abc269faa02..151dec1b3d27 100755 --- a/packages/create-docusaurus/package.json +++ b/packages/create-docusaurus/package.json @@ -27,7 +27,6 @@ "commander": "^5.1.0", "fs-extra": "^10.0.0", "lodash": "^4.17.20", - "picocolors": "^1.0.0", "prompts": "^2.4.1", "semver": "^7.3.4", "shelljs": "^0.8.4", diff --git a/packages/docusaurus-logger/src/index.ts b/packages/docusaurus-logger/src/index.ts index b9cc92a937a5..eeacdedbbecc 100644 --- a/packages/docusaurus-logger/src/index.ts +++ b/packages/docusaurus-logger/src/index.ts @@ -86,10 +86,14 @@ const logger = { )}`, ); }, - error(msg: string, ...values: InterpolatableValue[]): void { - console.error( - `${pico.red(pico.bold('[ERROR] '))}${this.interpolate(msg, ...values)}`, - ); + error(msg: string | Error, ...values: InterpolatableValue[]): void { + if (msg instanceof Error) { + console.error(msg); + } else { + console.error( + `${pico.red(pico.bold('[ERROR] '))}${this.interpolate(msg, ...values)}`, + ); + } }, success(msg: string, ...values: InterpolatableValue[]): void { console.log( diff --git a/packages/docusaurus-migrate/bin/index.js b/packages/docusaurus-migrate/bin/index.js index 22fc82343e46..65018004e619 100755 --- a/packages/docusaurus-migrate/bin/index.js +++ b/packages/docusaurus-migrate/bin/index.js @@ -18,7 +18,7 @@ const {migrateDocusaurusProject, migrateMDToMDX} = require('../lib'); function wrapCommand(fn) { return (...args) => fn(...args).catch((err) => { - console.error(pico.red(err.stack)); + logger.error(err.stack); process.exitCode = 1; }); } diff --git a/packages/docusaurus-migrate/src/index.ts b/packages/docusaurus-migrate/src/index.ts index b478c88f561b..afa564898ba9 100644 --- a/packages/docusaurus-migrate/src/index.ts +++ b/packages/docusaurus-migrate/src/index.ts @@ -79,7 +79,7 @@ export async function migrateDocusaurusProject( ): Promise { function createMigrationContext(): MigrationContext { const v1Config = importFresh(`${siteDir}/siteConfig`) as VersionOneConfig; - console.log('Starting migration from v1 to v2...'); + logger.info('Starting migration from v1 to v2...'); const partialMigrationContext = { siteDir, newDir, @@ -405,7 +405,7 @@ function createPages(newDir: string, siteDir: string): void { createDefaultLandingPage(newDir); } } else { - console.log('Ignoring Pages'); + logger.info('Ignoring Pages'); } } diff --git a/packages/docusaurus-plugin-client-redirects/package.json b/packages/docusaurus-plugin-client-redirects/package.json index a86d402a75c1..e08703784fa6 100644 --- a/packages/docusaurus-plugin-client-redirects/package.json +++ b/packages/docusaurus-plugin-client-redirects/package.json @@ -26,7 +26,6 @@ "eta": "^1.12.3", "fs-extra": "^10.0.0", "lodash": "^4.17.20", - "picocolors": "^1.0.0", "tslib": "^2.3.1" }, "devDependencies": { diff --git a/packages/docusaurus-plugin-content-blog/package.json b/packages/docusaurus-plugin-content-blog/package.json index 6bfb6b22c608..748edca7c0e3 100644 --- a/packages/docusaurus-plugin-content-blog/package.json +++ b/packages/docusaurus-plugin-content-blog/package.json @@ -19,6 +19,7 @@ "license": "MIT", "dependencies": { "@docusaurus/core": "2.0.0-beta.9", + "@docusaurus/logger": "2.0.0-beta.9", "@docusaurus/mdx-loader": "2.0.0-beta.9", "@docusaurus/types": "2.0.0-beta.9", "@docusaurus/utils": "2.0.0-beta.9", @@ -30,7 +31,6 @@ "js-yaml": "^4.0.0", "loader-utils": "^2.0.0", "lodash": "^4.17.20", - "picocolors": "^1.0.0", "reading-time": "^1.5.0", "remark-admonitions": "^1.2.1", "tslib": "^2.3.1", diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index 7e59f9b5a87e..ef5f60413f46 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -6,7 +6,7 @@ */ import fs from 'fs-extra'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import path from 'path'; import {Author, BlogContentPaths} from './types'; import {findFolderContainingFile} from '@docusaurus/utils'; @@ -48,7 +48,7 @@ export async function readAuthorsMapFile( return validateAuthorsMapFile(unsafeContent); } catch (e) { // TODO replace later by error cause: see https://v8.dev/features/error-cause - console.error(pico.red('The author list file looks invalid!')); + logger.error('The author list file looks invalid!'); throw e; } } @@ -88,9 +88,7 @@ export async function getAuthorsMap( return await readAuthorsMapFile(filePath); } catch (e) { // TODO replace later by error cause, see https://v8.dev/features/error-cause - console.error( - pico.red(`Couldn't read blog authors map at path ${filePath}`), - ); + logger.error(`Couldn't read blog authors map at path %p`, filePath); throw e; } } diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index 67d6a2808e0f..126e2df15585 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -6,7 +6,6 @@ */ import fs from 'fs-extra'; -import pico from 'picocolors'; import path from 'path'; import readingTime from 'reading-time'; import {keyBy, mapValues} from 'lodash'; @@ -33,6 +32,7 @@ import { import {LoadContext} from '@docusaurus/types'; import {validateBlogPostFrontMatter} from './blogFrontMatter'; import {AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors'; +import logger from '@docusaurus/logger'; export function truncate(fileString: string, truncateMarker: RegExp): string { return fileString.split(truncateMarker, 1).shift()!; @@ -151,10 +151,11 @@ async function processBlogSourceFile( } if (frontMatter.id) { - console.warn( - pico.yellow( - `"id" header option is deprecated in ${blogSourceRelative} file. Please use "slug" option instead.`, - ), + logger.warn( + `%i header option is deprecated in %p file. Please use %i option instead.`, + 'id', + blogSourceRelative, + 'slug', ); } @@ -276,10 +277,9 @@ export async function generateBlogPosts( authorsMap, ); } catch (e) { - console.error( - pico.red( - `Processing of blog source file failed for path "${blogSourceFile}"`, - ), + logger.error( + `Processing of blog source file failed for path %p`, + blogSourceFile, ); throw e; } diff --git a/packages/docusaurus/bin/beforeCli.js b/packages/docusaurus/bin/beforeCli.js index 7f97c6d6152a..a55273095e61 100644 --- a/packages/docusaurus/bin/beforeCli.js +++ b/packages/docusaurus/bin/beforeCli.js @@ -6,7 +6,6 @@ */ const logger = require('@docusaurus/logger'); -const pico = require('picocolors'); const fs = require('fs-extra'); const semver = require('semver'); const path = require('path'); @@ -54,7 +53,7 @@ try { } } catch (e) { // Do not stop cli if this fails, see https://github.com/facebook/docusaurus/issues/5400 - console.error(e); + logger.error(e); } // We don't want to display update message for canary releases @@ -101,13 +100,12 @@ if ( }; const docusaurusUpdateMessage = boxen( - `Update available ${pico.dim(`${notifier.update.current}`)}${pico.reset( - ' → ', - )}${pico.green( - `${notifier.update.latest}`, - )}\n\nTo upgrade Docusaurus packages with the latest version, run the following command:\n${pico.cyan( - `${upgradeCommand}`, - )}`, + `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(upgradeCommand)}`, boxenOptions, ); diff --git a/packages/docusaurus/bin/docusaurus.js b/packages/docusaurus/bin/docusaurus.js index 8ba7e866a662..87d8b02bc2c3 100755 --- a/packages/docusaurus/bin/docusaurus.js +++ b/packages/docusaurus/bin/docusaurus.js @@ -220,7 +220,6 @@ cli cli.arguments('').action((cmd) => { cli.outputHelp(); logger.error(' Unknown command %i.', cmd); - console.log(); }); function isInternalCommand(command) { diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index 19bdf932bc45..8908e1792bcb 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -93,7 +93,6 @@ "lodash": "^4.17.20", "mini-css-extract-plugin": "^1.6.0", "nprogress": "^0.2.0", - "picocolors": "^1.0.0", "postcss": "^8.3.7", "postcss-loader": "^6.1.1", "prompts": "^2.4.1", diff --git a/packages/docusaurus/src/commands/build.ts b/packages/docusaurus/src/commands/build.ts index 314d05abcdc9..efad11651683 100644 --- a/packages/docusaurus/src/commands/build.ts +++ b/packages/docusaurus/src/commands/build.ts @@ -56,7 +56,7 @@ export default async function build( isLastLocale, }); } catch (e) { - console.error(`Unable to build website for locale "${locale}".`); + logger.error(`Unable to build website for locale %i.`, locale); throw e; } } diff --git a/packages/docusaurus/src/server/__tests__/i18n.test.ts b/packages/docusaurus/src/server/__tests__/i18n.test.ts index bec9e039b1a5..d013dd6cd97d 100644 --- a/packages/docusaurus/src/server/__tests__/i18n.test.ts +++ b/packages/docusaurus/src/server/__tests__/i18n.test.ts @@ -34,7 +34,6 @@ function loadI18nTest(i18nConfig: I18nConfig, locale?: string) { } describe('defaultLocaleConfig', () => { - // @ts-expect-error: wait for TS support of ES2021 feature const canComputeLabel = typeof Intl.DisplayNames !== 'undefined'; test('returns correct labels', () => { diff --git a/packages/docusaurus/src/server/configValidation.ts b/packages/docusaurus/src/server/configValidation.ts index d873c497777c..92de8cd0a1d9 100644 --- a/packages/docusaurus/src/server/configValidation.ts +++ b/packages/docusaurus/src/server/configValidation.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +import logger from '@docusaurus/logger'; import {DocusaurusConfig, I18nConfig} from '@docusaurus/types'; import {DEFAULT_CONFIG_FILE_NAME, STATIC_DIR_NAME} from '../constants'; import { @@ -200,7 +201,7 @@ export function validateConfig( if (error) { logValidationBugReportHint(); if (isValidationDisabledEscapeHatch) { - console.error(error); + logger.error(error.message); return config as DocusaurusConfig; } diff --git a/packages/docusaurus/src/server/translations/translations.ts b/packages/docusaurus/src/server/translations/translations.ts index 3535c34554e9..5e5ca86d016f 100644 --- a/packages/docusaurus/src/server/translations/translations.ts +++ b/packages/docusaurus/src/server/translations/translations.ts @@ -130,15 +130,10 @@ Maybe you should remove them? %a`, // Avoid creating empty translation files if (Object.keys(mergedContent).length > 0) { - console.log( - `${Object.keys(mergedContent) - .length.toString() - .padStart( - 3, - ' ', - )} translations will be written at "${toMessageRelativeFilePath( - filePath, - )}".`, + logger.info( + `%n translations will be written at %p.`, + Object.keys(mergedContent).length, + toMessageRelativeFilePath(filePath), ); await fs.ensureDir(path.dirname(filePath)); await fs.writeFile(filePath, JSON.stringify(mergedContent, null, 2)); diff --git a/packages/docusaurus/src/webpack/utils.ts b/packages/docusaurus/src/webpack/utils.ts index e3f5af893550..481148574456 100644 --- a/packages/docusaurus/src/webpack/utils.ts +++ b/packages/docusaurus/src/webpack/utils.ts @@ -272,11 +272,11 @@ export function compile(config: Configuration[]): Promise { const compiler = webpack(config); compiler.run((err, stats) => { if (err) { - console.error(err.stack || err); + logger.error(err.stack || err); // @ts-expect-error: see https://webpack.js.org/api/node/#error-handling if (err.details) { // @ts-expect-error: see https://webpack.js.org/api/node/#error-handling - console.error(err.details); + logger.error(err.details); } reject(err); } @@ -287,7 +287,7 @@ export function compile(config: Configuration[]): Promise { } if (errorsWarnings && stats?.hasWarnings()) { errorsWarnings.warnings?.forEach((warning) => { - console.warn(warning); + logger.warn(`${warning}`); }); } // Webpack 5 requires calling close() so that persistent caching works diff --git a/packages/docusaurus/tsconfig.json b/packages/docusaurus/tsconfig.json index 817c06354103..af81dee45121 100644 --- a/packages/docusaurus/tsconfig.json +++ b/packages/docusaurus/tsconfig.json @@ -6,8 +6,7 @@ "rootDir": "src", "outDir": "lib", "jsx": "react", - "allowJs": true, - "allowSyntheticDefaultImports": true, + "allowJs": true }, "include": ["src"], "exclude": ["**/__tests__/**/*", "src/client"] From 250860960302a9e32a8d794ee80b58ef011736e8 Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 14:31:33 +0800 Subject: [PATCH 11/33] Tweak --- packages/create-docusaurus/bin/index.js | 2 +- packages/docusaurus-migrate/bin/index.js | 6 ++++-- packages/docusaurus/bin/beforeCli.js | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/create-docusaurus/bin/index.js b/packages/create-docusaurus/bin/index.js index 1b161bc37cc6..3c58eb19ca7d 100755 --- a/packages/create-docusaurus/bin/index.js +++ b/packages/create-docusaurus/bin/index.js @@ -15,7 +15,7 @@ const requiredVersion = require('../package.json').engines.node; if (!semver.satisfies(process.version, requiredVersion)) { logger.error('Minimum Node.js version not met :('); - logger.error( + logger.info( 'You are using Node.js %n, Requirement: Node.js %n.', process.version, requiredVersion, diff --git a/packages/docusaurus-migrate/bin/index.js b/packages/docusaurus-migrate/bin/index.js index 65018004e619..41309e1e4901 100755 --- a/packages/docusaurus-migrate/bin/index.js +++ b/packages/docusaurus-migrate/bin/index.js @@ -25,8 +25,10 @@ function wrapCommand(fn) { if (!semver.satisfies(process.version, requiredVersion)) { logger.error('Minimum Node.js version not met :('); - logger.error( - `You are using Node.js ${process.version}, Requirement: Node.js ${requiredVersion}.`, + logger.info( + `You are using Node.js %n, Requirement: Node.js %n.`, + process.version, + requiredVersion, ); process.exit(1); } diff --git a/packages/docusaurus/bin/beforeCli.js b/packages/docusaurus/bin/beforeCli.js index a55273095e61..172df9c8e8b4 100644 --- a/packages/docusaurus/bin/beforeCli.js +++ b/packages/docusaurus/bin/beforeCli.js @@ -115,8 +115,10 @@ ${logger.code(upgradeCommand)}`, // notify user if node version needs to be updated if (!semver.satisfies(process.version, requiredVersion)) { logger.error('Minimum Node.js version not met :('); - logger.error( - `You are using Node.js ${process.version}, Requirement: Node.js ${requiredVersion}.`, + logger.info( + `You are using Node.js %n, Requirement: Node.js %n.`, + process.version, + requiredVersion, ); process.exit(1); } From 130952008b41b3f64a4d6e5471196118b7216a9d Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 16:03:15 +0800 Subject: [PATCH 12/33] Template tags! --- .eslintrc.js | 2 +- packages/create-docusaurus/bin/index.js | 12 +- packages/create-docusaurus/src/index.ts | 39 ++-- packages/docusaurus-logger/src/index.ts | 199 +++++++++++------- packages/docusaurus-migrate/bin/index.js | 10 +- packages/docusaurus-migrate/src/index.ts | 12 +- .../src/collectRedirects.ts | 19 +- .../src/authors.ts | 2 +- .../src/blogUtils.ts | 12 +- packages/docusaurus/bin/beforeCli.js | 12 +- packages/docusaurus/bin/docusaurus.js | 8 +- packages/docusaurus/src/choosePort.ts | 4 +- packages/docusaurus/src/client/serverEntry.js | 29 +-- packages/docusaurus/src/commands/build.ts | 17 +- packages/docusaurus/src/commands/clear.ts | 7 +- packages/docusaurus/src/commands/deploy.ts | 8 +- packages/docusaurus/src/commands/serve.ts | 8 +- packages/docusaurus/src/commands/start.ts | 4 +- packages/docusaurus/src/commands/swizzle.ts | 55 ++--- .../src/commands/writeHeadingIds.ts | 14 +- packages/docusaurus/src/server/i18n.ts | 8 +- packages/docusaurus/src/server/index.ts | 9 +- .../docusaurus/src/server/plugins/index.ts | 4 +- .../src/server/translations/translations.ts | 24 +-- .../translations/translationsExtractor.ts | 6 +- packages/docusaurus/src/webpack/utils.ts | 11 +- 26 files changed, 244 insertions(+), 291 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index b61b8fb46ef4..6573ef9c4f5b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -129,7 +129,7 @@ module.exports = { 'array-callback-return': WARNING, camelcase: WARNING, 'no-restricted-syntax': WARNING, - 'no-unused-expressions': WARNING, + 'no-unused-expressions': [WARNING, {allowTaggedTemplates: true}], 'global-require': WARNING, 'prefer-destructuring': WARNING, yoda: WARNING, diff --git a/packages/create-docusaurus/bin/index.js b/packages/create-docusaurus/bin/index.js index 3c58eb19ca7d..64ab5e00847d 100755 --- a/packages/create-docusaurus/bin/index.js +++ b/packages/create-docusaurus/bin/index.js @@ -6,7 +6,9 @@ * LICENSE file in the root directory of this source tree. */ -const logger = require('@docusaurus/logger'); +// @ts-check + +const logger = require('@docusaurus/logger').default; const semver = require('semver'); const path = require('path'); const program = require('commander'); @@ -15,11 +17,7 @@ const requiredVersion = require('../package.json').engines.node; if (!semver.satisfies(process.version, requiredVersion)) { logger.error('Minimum Node.js version not met :('); - logger.info( - 'You are using Node.js %n, Requirement: Node.js %n.', - process.version, - requiredVersion, - ); + logger.info`You are using Node.js %n${process.version}, Requirement: Node.js %n${requiredVersion}.`; process.exit(1); } @@ -58,7 +56,7 @@ program program.arguments('').action((cmd) => { program.outputHelp(); - logger.error('Unknown command %c.', cmd); + logger.error`Unknown command %c${cmd}.`; }); program.parse(process.argv); diff --git a/packages/create-docusaurus/src/index.ts b/packages/create-docusaurus/src/index.ts index 44cf1d7f57a5..1dab44ac1516 100755 --- a/packages/create-docusaurus/src/index.ts +++ b/packages/create-docusaurus/src/index.ts @@ -173,8 +173,8 @@ export default async function init( } return logger.red('Invalid repository URL'); }, - message: - 'Enter a repository URL from GitHub, Bitbucket, GitLab, or any other public repo.\n(e.g: https://github.com/ownerName/repoName.git)', + message: `Enter a repository URL from GitHub, Bitbucket, GitLab, or any other public repo. +(e.g: %p${'https://github.com/ownerName/repoName.git'})`, }); template = repoPrompt.gitRepoUrl; } else if (template === 'Local template') { @@ -204,7 +204,7 @@ export default async function init( logger.info('Creating new Docusaurus project...'); if (isValidGitRepoUrl(template)) { - logger.info('Cloning Git template %p...', template); + logger.info`Cloning Git template %p${template}...`; if ( shell.exec(`git clone --recursive ${template} ${dest}`, {silent: true}) .code !== 0 @@ -224,7 +224,7 @@ export default async function init( try { await copyTemplate(templatesDir, template, dest); } catch (err) { - logger.error('Copying Docusaurus template %i failed!', template); + logger.error`Copying Docusaurus template %i${template} failed!`; throw err; } } else if (fs.existsSync(path.resolve(process.cwd(), template))) { @@ -232,7 +232,7 @@ export default async function init( try { await fs.copy(templateDir, dest); } catch (err) { - logger.error('Copying local template %p failed!', template); + logger.error`Copying local template %p${template} failed!`; throw err; } } else { @@ -264,7 +264,7 @@ export default async function init( const pkgManager = useYarn ? 'yarn' : 'npm'; if (!cliOptions.skipInstall) { - logger.info('Installing dependencies with %i...', pkgManager); + logger.info`Installing dependencies with %i${pkgManager}...`; try { // Use force coloring the output, since the command is invoked by shelljs, which is not the interactive shell @@ -289,36 +289,27 @@ export default async function init( ? name : path.relative(process.cwd(), name); - logger.info( - ` -Successfully created %p. + logger.info` +Successfully created %p${cdpath}. Inside that directory, you can run several commands: - %c + %c${`${pkgManager} start`} Starts the development server. - %c + %c${`${pkgManager} ${useYarn ? '' : 'run '}build`} Bundles your website into static files for production. - %c + %c${`${pkgManager} ${useYarn ? '' : 'run '}serve`} Serves the built website locally. - %c + %c${`${pkgManager} deploy`} Publishes the website to GitHub pages. We recommend that you begin by typing: - %c - %c + %c${`cd ${cdpath}`} + %c${`${pkgManager} start`} Happy building awesome websites! -`, - cdpath, - `${pkgManager} start`, - `${pkgManager} ${useYarn ? '' : 'run '}build`, - `${pkgManager} ${useYarn ? '' : 'run '}serve`, - `${pkgManager} deploy`, - `cd ${cdpath}`, - `${pkgManager} start`, - ); +`; } diff --git a/packages/docusaurus-logger/src/index.ts b/packages/docusaurus-logger/src/index.ts index eeacdedbbecc..2059be05c2e3 100644 --- a/packages/docusaurus-logger/src/index.ts +++ b/packages/docusaurus-logger/src/index.ts @@ -7,25 +7,7 @@ import pico from 'picocolors'; -type InterpolatableValue = string | number | string[]; - -function assertIsArray(value: InterpolatableValue): asserts value is string[] { - if (!Array.isArray(value)) { - throw new Error( - 'Bad Docusaurus logging message. This is likely an internal bug, please report it', - ); - } -} - -function assertIsStrNum( - value: InterpolatableValue, -): asserts value is string | number { - if (typeof value !== 'string' && typeof value !== 'number') { - throw new Error( - 'Bad Docusaurus logging message. This is likely an internal bug, please report it', - ); - } -} +type InterpolatableValue = string | number | string[] | number[]; const path: import('picocolors/types').Formatter = (msg) => pico.cyan(pico.underline(msg)); @@ -36,73 +18,132 @@ const code: import('picocolors/types').Formatter = (msg) => const subdue = pico.gray; const num = pico.yellow; +function interpolate( + msgs: TemplateStringsArray, + ...values: InterpolatableValue[] +): string { + let res = ''; + values.forEach((value, idx) => { + const flag = msgs[idx].match(/%[a-z]+$/); + const cleanValue = Array.isArray(value) + ? `\n- ${value.join('\n- ')}\n` + : String(value); + res += msgs[idx].replace(/%[a-z]+$/, ''); + if (!flag) { + res += cleanValue; + return; + } + switch (flag[0]) { + case '%p': + res += path(cleanValue); + break; + case '%n': + res += num(cleanValue); + break; + case '%i': + res += id(cleanValue); + break; + case '%s': + res += subdue(cleanValue); + break; + case '%c': + res += code(cleanValue); + break; + default: + throw new Error( + 'Bad Docusaurus logging message. This is likely an internal bug, please report it', + ); + } + }); + res += msgs.slice(-1)[0]; + return res; +} + +function info(msg: string): void; +function info( + msg: TemplateStringsArray, + ...values: InterpolatableValue[] +): void; +function info( + msg: TemplateStringsArray | string, + ...values: InterpolatableValue[] +): void { + if (typeof msg === 'string') { + console.info(`${pico.cyan(pico.bold('[INFO]'))} ${msg}`); + return; + } + console.info( + `${pico.cyan(pico.bold('[INFO]'))} ${interpolate(msg, ...values)}`, + ); +} +function warn(msg: string): void; +function warn( + msg: TemplateStringsArray, + ...values: InterpolatableValue[] +): void; +function warn( + msg: TemplateStringsArray | string, + ...values: InterpolatableValue[] +): void { + if (typeof msg === 'string') { + console.warn(pico.yellow(`${pico.bold('[WARNING]')} ${msg}`)); + return; + } + console.warn( + pico.yellow(`${pico.bold('[WARNING]')} ${interpolate(msg, ...values)}`), + ); +} +function error(msg: string | Error): void; +function error( + msg: TemplateStringsArray, + ...values: InterpolatableValue[] +): void; +function error( + msg: TemplateStringsArray | Error | string, + ...values: InterpolatableValue[] +): void { + if (msg instanceof Error) { + console.error(msg); + return; + } + if (typeof msg === 'string') { + console.error(pico.red(`${pico.bold('[ERROR]')} ${msg}`)); + return; + } + console.error( + pico.red(`${pico.bold('[ERROR]')} ${interpolate(msg, ...values)}`), + ); +} +function success(msg: string): void; +function success( + msg: TemplateStringsArray, + ...values: InterpolatableValue[] +): void; +function success( + msg: TemplateStringsArray | string, + ...values: InterpolatableValue[] +): void { + if (typeof msg === 'string') { + console.log(`${pico.green(pico.bold('[SUCCESS]'))} ${msg}`); + return; + } + console.log( + `${pico.green(pico.bold('[SUCCESS]'))} ${interpolate(msg, ...values)}`, + ); +} + const logger = { ...pico, path, id, code, subdue, + interpolate, num, - interpolate(msg: string, ...values: InterpolatableValue[]): string { - let index = 0; - function replacer(match: string) { - if (index >= values.length) { - return match; - } - const newStr = values[index]; - index += 1; - if (match === '%a') { - assertIsArray(newStr); - return `\n- ${newStr.join('\n- ')}\n`; - } - assertIsStrNum(newStr); - switch (match) { - case '%p': - return path(newStr); - case '%c': - return code(newStr); - case '%i': - return id(newStr); - case '%n': - return num(newStr); - default: - throw new Error( - 'Bad Docusaurus logging message. This is likely an internal bug, please report it', - ); - } - } - return msg.replace(/%[A-Za-z]/g, replacer); - }, - info(msg: string, ...values: InterpolatableValue[]): void { - console.info( - `${pico.cyan(pico.bold('[INFO] '))}${this.interpolate(msg, ...values)}`, - ); - }, - warn(msg: string, ...values: InterpolatableValue[]): void { - console.warn( - `${pico.yellow(pico.bold('[WARNING] '))}${this.interpolate( - msg, - ...values, - )}`, - ); - }, - error(msg: string | Error, ...values: InterpolatableValue[]): void { - if (msg instanceof Error) { - console.error(msg); - } else { - console.error( - `${pico.red(pico.bold('[ERROR] '))}${this.interpolate(msg, ...values)}`, - ); - } - }, - success(msg: string, ...values: InterpolatableValue[]): void { - console.log( - `${pico.green(pico.bold('[SUCCESS] '))}${this.interpolate( - msg, - ...values, - )}`, - ); - }, + info, + warn, + error, + success, }; export default logger; diff --git a/packages/docusaurus-migrate/bin/index.js b/packages/docusaurus-migrate/bin/index.js index 41309e1e4901..477a5d8453c4 100755 --- a/packages/docusaurus-migrate/bin/index.js +++ b/packages/docusaurus-migrate/bin/index.js @@ -6,7 +6,9 @@ * LICENSE file in the root directory of this source tree. */ -const logger = require('@docusaurus/logger'); +// @ts-check + +const logger = require('@docusaurus/logger').default; const semver = require('semver'); const cli = require('commander'); const path = require('path'); @@ -25,11 +27,7 @@ function wrapCommand(fn) { if (!semver.satisfies(process.version, requiredVersion)) { logger.error('Minimum Node.js version not met :('); - logger.info( - `You are using Node.js %n, Requirement: Node.js %n.`, - process.version, - requiredVersion, - ); + logger.info`You are using Node.js %n${process.version}, Requirement: Node.js %n${requiredVersion}.`; process.exit(1); } diff --git a/packages/docusaurus-migrate/src/index.ts b/packages/docusaurus-migrate/src/index.ts index afa564898ba9..f49d4a327599 100644 --- a/packages/docusaurus-migrate/src/index.ts +++ b/packages/docusaurus-migrate/src/index.ts @@ -258,13 +258,9 @@ export function createConfigFile({ customConfigFields[key] = value; } }); - logger.info( - `Following Fields from %p will be added to %p in %c:%a`, - 'siteConfig.js', - 'docusaurus.config.js', - 'customFields', - Object.keys(customConfigFields), - ); + logger.info`Following Fields from %p${'siteConfig.js'} will be added to %p${'docusaurus.config.js'} in %c${'customFields'}: ${Object.keys( + customConfigFields, + )}`; let v2DocsPath: string | undefined; if (siteConfig.customDocsPath) { @@ -766,5 +762,5 @@ export async function migrateMDToMDX( sanitizedFileContent(String(fs.readFileSync(file)), true), ); }); - logger.success(`Successfully migrated %p to %p`, siteDir, newDir); + logger.success`Successfully migrated %p${siteDir} to %p${newDir}`; } diff --git a/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts b/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts index 2e067d064abc..e5e80199b935 100644 --- a/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts +++ b/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts @@ -99,13 +99,10 @@ function filterUnwantedRedirects( Object.entries(groupBy(redirects, (redirect) => redirect.from)).forEach( ([from, groupedFromRedirects]) => { if (groupedFromRedirects.length > 1) { - logger.error( - `%i: multiple redirects are created with the same "from" pathname: %p -It is not possible to redirect the same pathname to multiple destinations:%a`, - '@docusaurus/plugin-client-redirects', - from, - groupedFromRedirects.map((r) => JSON.stringify(r)), - ); + logger.error`%i${'@docusaurus/plugin-client-redirects'}: multiple redirects are created with the same "from" pathname: %p${from} +It is not possible to redirect the same pathname to multiple destinations: ${groupedFromRedirects.map( + (r) => JSON.stringify(r), + )}`; } }, ); @@ -116,11 +113,9 @@ It is not possible to redirect the same pathname to multiple destinations:%a`, (redirect) => pluginContext.relativeRoutesPaths.includes(redirect.from), ); if (redirectsOverridingExistingPath.length > 0) { - logger.error( - `%i: some redirects would override existing paths, and will be ignored:%a`, - '@docusaurus/plugin-client-redirects', - redirectsOverridingExistingPath.map((r) => JSON.stringify(r)), - ); + logger.error`%i${'@docusaurus/plugin-client-redirects'}: some redirects would override existing paths, and will be ignored: ${redirectsOverridingExistingPath.map( + (r) => JSON.stringify(r), + )}`; } return collectedRedirects.filter( (redirect) => !pluginContext.relativeRoutesPaths.includes(redirect.from), diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index ef5f60413f46..0e558d6c020c 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -88,7 +88,7 @@ export async function getAuthorsMap( return await readAuthorsMapFile(filePath); } catch (e) { // TODO replace later by error cause, see https://v8.dev/features/error-cause - logger.error(`Couldn't read blog authors map at path %p`, filePath); + logger.error`Couldn't read blog authors map at path %p${filePath}`; throw e; } } diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index 126e2df15585..9dbb76a3a80b 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -151,12 +151,7 @@ async function processBlogSourceFile( } if (frontMatter.id) { - logger.warn( - `%i header option is deprecated in %p file. Please use %i option instead.`, - 'id', - blogSourceRelative, - 'slug', - ); + logger.warn`%i${'id'} header option is deprecated in %p${blogSourceRelative} file. Please use %i${'slug'} option instead.`; } const parsedBlogFileName = parseBlogFileName(blogSourceRelative); @@ -277,10 +272,7 @@ export async function generateBlogPosts( authorsMap, ); } catch (e) { - logger.error( - `Processing of blog source file failed for path %p`, - blogSourceFile, - ); + logger.error`Processing of blog source file failed for path %p${blogSourceFile}.`; throw e; } }), diff --git a/packages/docusaurus/bin/beforeCli.js b/packages/docusaurus/bin/beforeCli.js index 172df9c8e8b4..6ef2af7915e8 100644 --- a/packages/docusaurus/bin/beforeCli.js +++ b/packages/docusaurus/bin/beforeCli.js @@ -5,7 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -const logger = require('@docusaurus/logger'); +// @ts-check + +const logger = require('@docusaurus/logger').default; const fs = require('fs-extra'); const semver = require('semver'); const path = require('path'); @@ -74,6 +76,7 @@ if ( notifier.config.set('update', notifier.update); if (ignoreUpdate(notifier.update)) { + // @ts-expect-error: it works return; } @@ -91,6 +94,7 @@ if ( ? `yarn upgrade ${siteDocusaurusPackagesForUpdate}` : `npm i ${siteDocusaurusPackagesForUpdate}`; + /** @type {import('boxen').Options} */ const boxenOptions = { padding: 1, margin: 1, @@ -115,10 +119,6 @@ ${logger.code(upgradeCommand)}`, // 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 %n, Requirement: Node.js %n.`, - process.version, - requiredVersion, - ); + logger.info`You are using Node.js %n${process.version}, Requirement: Node.js %n${requiredVersion}.`; process.exit(1); } diff --git a/packages/docusaurus/bin/docusaurus.js b/packages/docusaurus/bin/docusaurus.js index 87d8b02bc2c3..a136851398df 100755 --- a/packages/docusaurus/bin/docusaurus.js +++ b/packages/docusaurus/bin/docusaurus.js @@ -6,7 +6,9 @@ * LICENSE file in the root directory of this source tree. */ -const logger = require('@docusaurus/logger'); +// @ts-check + +const logger = require('@docusaurus/logger').default; const fs = require('fs'); const cli = require('commander'); const { @@ -219,7 +221,7 @@ cli cli.arguments('').action((cmd) => { cli.outputHelp(); - logger.error(' Unknown command %i.', cmd); + logger.error` Unknown command %i${cmd}.`; }); function isInternalCommand(command) { @@ -237,6 +239,7 @@ function isInternalCommand(command) { async function run() { if (!isInternalCommand(process.argv.slice(2)[0])) { + // @ts-expect-error: Hmmm await externalCommand(cli, resolveDir('.')); } @@ -250,6 +253,7 @@ async function run() { run(); process.on('unhandledRejection', (err) => { + // @ts-expect-error: Hmmm logger.error(err.stack); process.exit(1); }); diff --git a/packages/docusaurus/src/choosePort.ts b/packages/docusaurus/src/choosePort.ts index 09618c49258d..8a9a21b2db2d 100644 --- a/packages/docusaurus/src/choosePort.ts +++ b/packages/docusaurus/src/choosePort.ts @@ -68,9 +68,7 @@ function getProcessForPort(port: number): string | null { const processId = getProcessIdOnPort(port); const directory = getDirectoryOfProcessById(processId); const command = getProcessCommand(processId); - return `${logger.code(command)} ${logger.subdue( - `(pid ${processId})`, - )} in ${logger.path(directory)}`; + return logger.interpolate`%c${command} %s${`(pid ${processId})`} in %p${directory}`; } catch (e) { return null; } diff --git a/packages/docusaurus/src/client/serverEntry.js b/packages/docusaurus/src/client/serverEntry.js index fee844698600..4da543c05215 100644 --- a/packages/docusaurus/src/client/serverEntry.js +++ b/packages/docusaurus/src/client/serverEntry.js @@ -5,6 +5,8 @@ * LICENSE file in the root directory of this source tree. */ +// @ts-check + import * as eta from 'eta'; import React from 'react'; import {StaticRouter} from 'react-router-dom'; @@ -43,26 +45,16 @@ export default async function render(locals) { try { return await doRender(locals); } catch (e) { - logger.error( - `Docusaurus Node/SSR could not render static page with path %p because of following error: -${e.stack}`, - locals.path, - ); + logger.error`Docusaurus Node/SSR could not render static page with path %p${locals.path} because of following error: +${e.stack}`; const isNotDefinedErrorRegex = /(window|document|localStorage|navigator|alert|location|buffer|self) is not defined/i; if (isNotDefinedErrorRegex.test(e.message)) { - logger.tip( - `It looks like you are using code that should run on the client-side only. -To get around it, try using %c (%p) or %c (%p). -It might also require to wrap your client code in %c hook and/or import a third-party library dynamically (if any).`, - '', - 'https://docusaurus.io/docs/docusaurus-core/#browseronly', - 'ExecutionEnvironment', - 'https://docusaurus.io/docs/docusaurus-core/#executionenvironment', - 'useEffect', - ); + logger.info`It looks like you are using code that should run on the client-side only. +To get around it, try using %c${''} (%p${'https://docusaurus.io/docs/docusaurus-core/#browseronly'}) or %c${'ExecutionEnvironment'} (%p${'https://docusaurus.io/docs/docusaurus-core/#executionenvironment'}). +It might also require to wrap your client code in %c${'useEffect'} hook and/or import a third-party library dynamically (if any).`; } throw new Error('Server-side rendering fails due to the error above.'); @@ -147,11 +139,8 @@ async function doRender(locals) { minifyJS: true, }); } catch (e) { - logger.error( - `Minification page with path %p failed because of following error: -${e.stack}`, - locals.path, - ); + logger.error`Minification page with path %p${locals.path} failed because of following error: +${e.stack}`; throw e; } } diff --git a/packages/docusaurus/src/commands/build.ts b/packages/docusaurus/src/commands/build.ts index efad11651683..3c2750fcb19c 100644 --- a/packages/docusaurus/src/commands/build.ts +++ b/packages/docusaurus/src/commands/build.ts @@ -56,7 +56,7 @@ export default async function build( isLastLocale, }); } catch (e) { - logger.error(`Unable to build website for locale %i.`, locale); + logger.error`Unable to build website for locale %i${locale}.`; throw e; } } @@ -73,8 +73,7 @@ export default async function build( return tryToBuildLocale({locale: cliOptions.locale, isLastLocale: true}); } else { if (i18n.locales.length > 1) { - logger.info(`Website will be built for all these locales: -- ${i18n.locales.join('\n- ')}`); + logger.info`Website will be built for all these locales: ${i18n.locales}`; } // We need the default locale to always be the 1st in the list @@ -108,7 +107,7 @@ async function buildLocale({ }): Promise { process.env.BABEL_ENV = 'production'; process.env.NODE_ENV = 'production'; - logger.info('[%i] Creating an optimized production build...', locale); + logger.info`%i${`[${locale}]`} Creating an optimized production build...`; const props: Props = await load(siteDir, { customOutDir: cliOptions.outDir, @@ -232,13 +231,13 @@ async function buildLocale({ baseUrl, }); - logger.success( - 'Generated static files in %p.', - path.relative(process.cwd(), outDir), - ); + logger.success`Generated static files in %p${path.relative( + process.cwd(), + outDir, + )}.`; if (isLastLocale) { - logger.info('Use %c command to test your build locally.', 'npm run serve'); + logger.info`Use %c${'npm run serve'} command to test your build locally.`; } if (forceTerminate && isLastLocale && !cliOptions.bundleAnalyzer) { diff --git a/packages/docusaurus/src/commands/clear.ts b/packages/docusaurus/src/commands/clear.ts index d192296c8295..0426d8b0bf4c 100644 --- a/packages/docusaurus/src/commands/clear.ts +++ b/packages/docusaurus/src/commands/clear.ts @@ -4,6 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ + import fs from 'fs-extra'; import path from 'path'; import logger from '@docusaurus/logger'; @@ -12,10 +13,10 @@ import {DEFAULT_BUILD_DIR_NAME, GENERATED_FILES_DIR_NAME} from '../constants'; async function removePath(fsPath: string) { try { fs.remove(path.join(fsPath)); - logger.success('Removed the %p directory.', fsPath); + logger.success`Removed the %p${fsPath} directory.`; } catch (e) { - logger.error('Could not remove %p directory.', fsPath); - logger.error(e as string); + logger.error`Could not remove %p${fsPath} directory. +${e as string}`; } } diff --git a/packages/docusaurus/src/commands/deploy.ts b/packages/docusaurus/src/commands/deploy.ts index b4ac6b2200ad..dca6f1bbb1f0 100644 --- a/packages/docusaurus/src/commands/deploy.ts +++ b/packages/docusaurus/src/commands/deploy.ts @@ -129,7 +129,7 @@ This behavior can have SEO impacts and create relative link issues. `Missing project organization name. Did you forget to define "organizationName" in ${siteConfigPath}? You may also export it via the ORGANIZATION_NAME environment variable.`, ); } - logger.info('organizationName: %i', organizationName); + logger.info`organizationName: %i${organizationName}`; const projectName = process.env.PROJECT_NAME || @@ -140,7 +140,7 @@ This behavior can have SEO impacts and create relative link issues. `Missing project name. Did you forget to define "projectName" in ${siteConfigPath}? You may also export it via the PROJECT_NAME environment variable.`, ); } - logger.info('projectName: %i', projectName); + logger.info`projectName: %i${projectName}`; // We never deploy on pull request. const isPullRequest = @@ -167,7 +167,7 @@ You can also set the deploymentBranch property in docusaurus.config.js .`); const deploymentBranch = process.env.DEPLOYMENT_BRANCH || siteConfig.deploymentBranch || 'gh-pages'; - logger.info('deploymentBranch: %i', deploymentBranch); + logger.info`deploymentBranch: %i${deploymentBranch}`; const githubHost = process.env.GITHUB_HOST || siteConfig.githubHost || 'github.com'; @@ -193,7 +193,7 @@ You can also set the deploymentBranch property in docusaurus.config.js .`); ); } - logger.info('Remote repo URL: %i', obfuscateGitPass(deploymentRepoURL)); + logger.info`Remote repo URL: %i${obfuscateGitPass(deploymentRepoURL)}`; // Check if this is a cross-repo publish. const crossRepoPublish = !sourceRepoUrl.endsWith( diff --git a/packages/docusaurus/src/commands/serve.ts b/packages/docusaurus/src/commands/serve.ts index bdf3ba3c4f54..420b83d45e9c 100644 --- a/packages/docusaurus/src/commands/serve.ts +++ b/packages/docusaurus/src/commands/serve.ts @@ -70,10 +70,8 @@ export default async function serve( }); }); - logger.success( - 'Serving %p directory at %p.', - cliOptions.dir, - servingUrl + baseUrl, - ); + logger.success`Serving %p${cliOptions.dir} directory at %p${ + servingUrl + baseUrl + }.`; server.listen(port); } diff --git a/packages/docusaurus/src/commands/start.ts b/packages/docusaurus/src/commands/start.ts index 0efdfcda9567..d6a9a0bc3991 100644 --- a/packages/docusaurus/src/commands/start.ts +++ b/packages/docusaurus/src/commands/start.ts @@ -60,7 +60,7 @@ export default async function start( const urls = prepareUrls(protocol, host, port); const openUrl = normalizeUrl([urls.localUrlForBrowser, baseUrl]); - logger.success('Docusaurus website is running at %p.', openUrl); + logger.success`Docusaurus website is running at %p${openUrl}.`; // Reload files processing. const reload = debounce(() => { @@ -68,7 +68,7 @@ export default async function start( .then(({baseUrl: newBaseUrl}) => { const newOpenUrl = normalizeUrl([urls.localUrlForBrowser, newBaseUrl]); if (newOpenUrl !== openUrl) { - logger.success('Docusaurus website is running at %p.', newOpenUrl); + logger.success`Docusaurus website is running at %p${newOpenUrl}.`; } }) .catch((err) => { diff --git a/packages/docusaurus/src/commands/swizzle.ts b/packages/docusaurus/src/commands/swizzle.ts index 91364663a000..cecc5d9142f0 100644 --- a/packages/docusaurus/src/commands/swizzle.ts +++ b/packages/docusaurus/src/commands/swizzle.ts @@ -161,7 +161,7 @@ export default async function swizzle( ); if (!themeName) { - logger.info('Themes available for swizzle:\n%a', themeNames); + logger.info`Themes available for swizzle: %i${themeNames}`; return; } @@ -175,13 +175,11 @@ export default async function swizzle( suggestion = name; } }); - logger.error( - `Theme %i not found. ${ - suggestion ? `Did you mean %i ?` : 'Themes available for swizzle:%a' - }`, - themeName, - suggestion ?? themeNames, - ); + logger.error`Theme %i${themeName} not found. ${ + suggestion + ? logger.interpolate`Did you mean %i${suggestion}?` + : logger.interpolate`Themes available for swizzle: ${themeNames}` + }`; process.exit(1); } @@ -220,8 +218,8 @@ export default async function swizzle( if (!themePath) { logger.warn( typescript - ? `${themeName} does not provide TypeScript theme code via "getTypeScriptThemePath()".` - : `${themeName} does not provide any theme code.`, + ? logger.interpolate`%i${themeName} does not provide TypeScript theme code via ${'getTypeScriptThemePath()'}.` + : logger.interpolate`%i${themeName} does not provide any theme code.`, ); process.exit(1); } @@ -254,12 +252,8 @@ export default async function swizzle( if (mostSuitableMatch !== componentName) { mostSuitableComponent = mostSuitableMatch; - logger.error("Component %i doesn't exist.", componentName); - logger.info( - `%i is swizzled instead of %i.`, - mostSuitableComponent, - componentName, - ); + logger.error`Component %i${componentName} doesn't exist.`; + logger.info`%i${mostSuitableComponent} is swizzled instead of %i${componentName}.`; } } @@ -281,34 +275,23 @@ export default async function swizzle( suggestion = name; } }); - logger.error( - `Component %i not found. ${ - suggestion - ? `Did you mean %i ?` - : themeComponents(themePath, pluginModule) - } - }`, - mostSuitableComponent, - suggestion!, - ); + logger.error`Component %i${mostSuitableComponent} not found. ${ + suggestion + ? logger.interpolate`Did you mean %i${suggestion} ?` + : themeComponents(themePath, pluginModule) + }`; process.exit(1); } } if (!components.includes(mostSuitableComponent) && !danger) { - logger.error( - '%i is an internal component and has a higher breaking change probability. If you want to swizzle it, use the %c flag.', - mostSuitableComponent, - '--danger', - ); + logger.error`%i${mostSuitableComponent} is an internal component and has a higher breaking change probability. If you want to swizzle it, use the %c${'--danger'} flag.`; process.exit(1); } await fs.copy(fromPath, toPath); - logger.success( - `Copied %c to %p.`, - mostSuitableComponent ? `${themeName} ${mostSuitableComponent}` : themeName, - path.relative(process.cwd(), toPath), - ); + logger.success`Copied %c${ + mostSuitableComponent ? `${themeName} ${mostSuitableComponent}` : themeName + } to %p${path.relative(process.cwd(), toPath)}.`; } diff --git a/packages/docusaurus/src/commands/writeHeadingIds.ts b/packages/docusaurus/src/commands/writeHeadingIds.ts index 8e26bb75a7f4..74cc84ab03fc 100644 --- a/packages/docusaurus/src/commands/writeHeadingIds.ts +++ b/packages/docusaurus/src/commands/writeHeadingIds.ts @@ -147,16 +147,10 @@ export default async function writeHeadingIds( const pathsModified = result.filter(Boolean) as string[]; if (pathsModified.length) { - logger.success( - `Heading ids added to Markdown files (%n files):%a`, - `${pathsModified.length}/${markdownFiles.length}`, - pathsModified, - ); + logger.success`Heading ids added to Markdown files (%n${`${pathsModified.length}/${markdownFiles.length}`} files): ${pathsModified}`; } else { - logger.warn( - `%n Markdown files already have explicit heading IDs. If you intend to overwrite the existing heading IDs, use the %c option.`, - markdownFiles.length, - '--overwrite', - ); + logger.warn`%n${ + markdownFiles.length + } Markdown files already have explicit heading IDs. If you intend to overwrite the existing heading IDs, use the %c${'--overwrite'} option.`; } } diff --git a/packages/docusaurus/src/server/i18n.ts b/packages/docusaurus/src/server/i18n.ts index 12167bd593b2..91ee45e7e5ef 100644 --- a/packages/docusaurus/src/server/i18n.ts +++ b/packages/docusaurus/src/server/i18n.ts @@ -44,12 +44,8 @@ export async function loadI18n( const currentLocale = options.locale ?? i18nConfig.defaultLocale; if (!i18nConfig.locales.includes(currentLocale)) { - logger.warn( - `The locale %i was not found in your site configuration: Available locales are: %a. -Note: Docusaurus only support running one locale at a time.`, - currentLocale, - i18nConfig.locales, - ); + logger.warn`The locale %i${currentLocale} was not found in your site configuration: Available locales are: ${i18nConfig.locales} +Note: Docusaurus only support running one locale at a time.`; } const locales = i18nConfig.locales.includes(currentLocale) diff --git a/packages/docusaurus/src/server/index.ts b/packages/docusaurus/src/server/index.ts index 7e78ad3ce476..d0e77d925337 100644 --- a/packages/docusaurus/src/server/index.ts +++ b/packages/docusaurus/src/server/index.ts @@ -414,15 +414,14 @@ function checkDocusaurusPackagesVersion(siteMetadata: DocusaurusSiteMetadata) { if ( versionInfo.type === 'package' && versionInfo.name?.startsWith('@docusaurus/') && + versionInfo.version && versionInfo.version !== docusaurusVersion ) { // should we throw instead? // It still could work with different versions - logger.error( - `Invalid ${plugin} version ${versionInfo.version}. -All official @docusaurus/* packages should have the exact same version as @docusaurus/core (${docusaurusVersion}). -Maybe you want to check, or regenerate your yarn.lock or package-lock.json file?`, - ); + logger.error`Invalid %i${plugin} version %n${versionInfo.version}. +All official @docusaurus/* packages should have the exact same version as @docusaurus/core (%n${docusaurusVersion}). +Maybe you want to check, or regenerate your yarn.lock or package-lock.json file?`; } }, ); diff --git a/packages/docusaurus/src/server/plugins/index.ts b/packages/docusaurus/src/server/plugins/index.ts index b824a9d47828..fc806a2921c4 100644 --- a/packages/docusaurus/src/server/plugins/index.ts +++ b/packages/docusaurus/src/server/plugins/index.ts @@ -212,9 +212,7 @@ export async function loadPlugins({ // TODO remove this deprecated lifecycle soon // deprecated since alpha-60 // TODO, 1 user reported usage of this lifecycle! https://github.com/facebook/docusaurus/issues/3918 - logger.error( - 'Plugin routesLoaded lifecycle is deprecated. If you think we should keep this lifecycle, please report here: https://github.com/facebook/docusaurus/issues/3918', - ); + logger.error`Plugin %c${'routesLoaded'} lifecycle is deprecated. If you think we should keep this lifecycle, please report here: %p${'https://github.com/facebook/docusaurus/issues/3918'}`; return plugin.routesLoaded(pluginsRouteConfigs); }), diff --git a/packages/docusaurus/src/server/translations/translations.ts b/packages/docusaurus/src/server/translations/translations.ts index 5e5ca86d016f..2f8bca795e88 100644 --- a/packages/docusaurus/src/server/translations/translations.ts +++ b/packages/docusaurus/src/server/translations/translations.ts @@ -114,12 +114,8 @@ export async function writeTranslationFileContent({ Object.keys(newContent), ); if (unknownKeys.length > 0) { - logger.warn( - `Some translation keys looks unknown to us in file %p. -Maybe you should remove them? %a`, - filePath, - unknownKeys, - ); + logger.warn`Some translation keys looks unknown to us in file %p${filePath}. +Maybe you should remove them? ${unknownKeys}`; } const mergedContent = mergeTranslationFileContent({ @@ -130,11 +126,11 @@ Maybe you should remove them? %a`, // Avoid creating empty translation files if (Object.keys(mergedContent).length > 0) { - logger.info( - `%n translations will be written at %p.`, - Object.keys(mergedContent).length, - toMessageRelativeFilePath(filePath), - ); + logger.info`%n${ + Object.keys(mergedContent).length + } translations will be written at %p${toMessageRelativeFilePath( + filePath, + )}.`; await fs.ensureDir(path.dirname(filePath)); await fs.writeFile(filePath, JSON.stringify(mergedContent, null, 2)); } @@ -285,10 +281,8 @@ export function applyDefaultCodeTranslations({ Object.keys(extractedCodeTranslations), ); if (unusedDefaultCodeMessages.length > 0) { - logger.warn(`Unused default message codes found. -Please report this Docusaurus issue. -- ${unusedDefaultCodeMessages.join('\n- ')} -`); + logger.warn`Unused default message codes found. +Please report this Docusaurus issue. %i${unusedDefaultCodeMessages}`; } return mapValues( diff --git a/packages/docusaurus/src/server/translations/translationsExtractor.ts b/packages/docusaurus/src/server/translations/translationsExtractor.ts index 7ec2c48549eb..5845cc58df47 100644 --- a/packages/docusaurus/src/server/translations/translationsExtractor.ts +++ b/packages/docusaurus/src/server/translations/translationsExtractor.ts @@ -114,11 +114,7 @@ function logSourceCodeFileTranslationsWarnings( ) { sourceCodeFilesTranslations.forEach(({sourceCodeFilePath, warnings}) => { if (warnings.length > 0) { - logger.warn( - `Translation extraction warnings for file %p:%a`, - sourceCodeFilePath, - warnings, - ); + logger.warn`Translation extraction warnings for file %p${sourceCodeFilePath}: ${warnings}`; } }); } diff --git a/packages/docusaurus/src/webpack/utils.ts b/packages/docusaurus/src/webpack/utils.ts index 481148574456..e487543d7ba4 100644 --- a/packages/docusaurus/src/webpack/utils.ts +++ b/packages/docusaurus/src/webpack/utils.ts @@ -165,11 +165,7 @@ export const getCustomizableJSLoader = // TODO remove this before end of 2021? const warnBabelLoaderOnce = memoize(() => { - logger.warn( - `Docusaurus plans to support multiple JS loader strategies (Babel, esbuild...): %c is now deprecated in favor of %c.`, - 'getBabelLoader(isServer)', - 'getJSLoader(isServer)', - ); + logger.warn`Docusaurus plans to support multiple JS loader strategies (Babel, esbuild...): %c${'getBabelLoader(isServer)'} is now deprecated in favor of %c${'getJSLoader(isServer)'}.`; }); const getBabelLoaderDeprecated = function getBabelLoaderDeprecated( isServer: boolean, @@ -181,10 +177,7 @@ const getBabelLoaderDeprecated = function getBabelLoaderDeprecated( // TODO remove this before end of 2021 ? const warnCacheLoaderOnce = memoize(() => { - logger.warn( - `Docusaurus uses Webpack 5 and %c usage is now deprecated.`, - 'getCacheLoader()', - ); + logger.warn`Docusaurus uses Webpack 5 and %c${'getCacheLoader()'} usage is now deprecated.`; }); function getCacheLoaderDeprecated() { warnCacheLoaderOnce(); From feaa03e90603286285a848e7e8fac6480bdd75ad Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 16:18:08 +0800 Subject: [PATCH 13/33] Complete migration --- packages/create-docusaurus/src/index.ts | 3 +- .../package.json | 2 +- .../src/docs.ts | 8 +--- .../src/index.ts | 14 ++----- .../src/options.ts | 14 ++----- .../src/sidebars/generator.ts | 14 ++----- .../docusaurus-utils-validation/package.json | 1 + .../src/validationUtils.ts | 37 +++++++------------ packages/docusaurus-utils/package.json | 10 ++--- packages/docusaurus-utils/src/index.ts | 8 ++-- .../docusaurus-utils/src/markdownParser.ts | 8 ++-- packages/docusaurus/src/commands/build.ts | 1 - 12 files changed, 39 insertions(+), 81 deletions(-) diff --git a/packages/create-docusaurus/src/index.ts b/packages/create-docusaurus/src/index.ts index 1dab44ac1516..e72a709b0286 100755 --- a/packages/create-docusaurus/src/index.ts +++ b/packages/create-docusaurus/src/index.ts @@ -289,8 +289,7 @@ export default async function init( ? name : path.relative(process.cwd(), name); - logger.info` -Successfully created %p${cdpath}. + logger.info`Successfully created %p${cdpath}. Inside that directory, you can run several commands: %c${`${pkgManager} start`} diff --git a/packages/docusaurus-plugin-content-docs/package.json b/packages/docusaurus-plugin-content-docs/package.json index d3a07b88cd5e..bc4ba10a9797 100644 --- a/packages/docusaurus-plugin-content-docs/package.json +++ b/packages/docusaurus-plugin-content-docs/package.json @@ -27,6 +27,7 @@ }, "dependencies": { "@docusaurus/core": "2.0.0-beta.9", + "@docusaurus/logger": "2.0.0-beta.9", "@docusaurus/mdx-loader": "2.0.0-beta.9", "@docusaurus/types": "2.0.0-beta.9", "@docusaurus/utils": "2.0.0-beta.9", @@ -39,7 +40,6 @@ "js-yaml": "^4.0.0", "loader-utils": "^2.0.0", "lodash": "^4.17.20", - "picocolors": "^1.0.0", "remark-admonitions": "^1.2.1", "shelljs": "^0.8.4", "tslib": "^2.3.1", diff --git a/packages/docusaurus-plugin-content-docs/src/docs.ts b/packages/docusaurus-plugin-content-docs/src/docs.ts index cbeb9776d917..b4642e3280de 100644 --- a/packages/docusaurus-plugin-content-docs/src/docs.ts +++ b/packages/docusaurus-plugin-content-docs/src/docs.ts @@ -7,7 +7,7 @@ import path from 'path'; import fs from 'fs-extra'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import {keyBy} from 'lodash'; import { aliasedSitePath, @@ -282,11 +282,7 @@ export function processDocMetadata(args: { try { return doProcessDocMetadata(args); } catch (e) { - console.error( - pico.red( - `Can't process doc metadata for doc at path "${args.docFile.filePath}" in version "${args.versionMetadata.versionName}"`, - ), - ); + logger.error`Can't process doc metadata for doc at path %p${args.docFile.filePath} in version %i${args.versionMetadata.versionName}`; throw e; } } diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 0893dec97bf2..632167252239 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -46,7 +46,7 @@ import { translateLoadedContent, getLoadedContentTranslationFiles, } from './translations'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import {getVersionTags} from './tags'; import type {PropTagsListPage} from '@docusaurus/plugin-content-docs'; @@ -186,11 +186,7 @@ export default function pluginContentDocs( try { return await doLoadVersion(versionMetadata); } catch (e) { - console.error( - pico.red( - `Loading of version failed for version "${versionMetadata.versionName}"`, - ), - ); + logger.error`Loading of version failed for version %i${versionMetadata.versionName}`; throw e; } } @@ -329,11 +325,7 @@ export default function pluginContentDocs( try { return await doCreateVersionRoutes(loadedVersion); } catch (e) { - console.error( - pico.red( - `Can't create version routes for version "${loadedVersion.versionName}"`, - ), - ); + logger.error`Can't create version routes for version %i${loadedVersion.versionName}`; throw e; } } diff --git a/packages/docusaurus-plugin-content-docs/src/options.ts b/packages/docusaurus-plugin-content-docs/src/options.ts index 9255e3dba12d..3e5030884fb6 100644 --- a/packages/docusaurus-plugin-content-docs/src/options.ts +++ b/packages/docusaurus-plugin-content-docs/src/options.ts @@ -18,7 +18,7 @@ import type { OptionValidationContext, ValidationResult, } from '@docusaurus/types'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import admonitions from 'remark-admonitions'; import {DefaultSidebarItemsGenerator} from './sidebars/generator'; import { @@ -149,11 +149,7 @@ export function validateOptions({ }; } if (options.sidebarCollapsed) { - console.warn( - pico.yellow( - 'The docs plugin config is inconsistent. It does not make sense to use sidebarCollapsible=false and sidebarCollapsed=true at the same time. sidebarCollapsed=false will be ignored.', - ), - ); + logger.warn`The docs plugin config is inconsistent. It does not make sense to use %c${'sidebarCollapsible: false'} and %c${'sidebarCollapsed: true'} at the same time. %c${'sidebarCollapsed: true'} will be ignored.`; options = { ...options, sidebarCollapsed: false, @@ -164,11 +160,7 @@ export function validateOptions({ // TODO remove homePageId before end of 2020 // "slug: /" is better because the home doc can be different across versions if (options.homePageId) { - console.log( - pico.red( - `The docs plugin option homePageId=${options.homePageId} is deprecated. To make a doc the "home", prefer frontmatter: "slug: /"`, - ), - ); + logger.warn`The docs plugin option %c${`homePageId: ${options.homePageId}`} is deprecated. To make a doc the "home", prefer frontmatter: %c${'slug: /'}`; } const normalizedOptions = validate(OptionsSchema, options); diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts index 1719eb7df94e..c739641096cd 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts @@ -15,7 +15,7 @@ import type { import {keyBy, sortBy} from 'lodash'; import {addTrailingSlash, posixPath} from '@docusaurus/utils'; import {Joi} from '@docusaurus/utils-validation'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import path from 'path'; import fs from 'fs-extra'; import Yaml from 'js-yaml'; @@ -71,11 +71,7 @@ async function readCategoryMetadataFile( try { return Joi.attempt(unsafeContent, CategoryMetadataFileSchema); } catch (e) { - console.error( - pico.red( - `The docs sidebar category metadata file looks invalid!\nPath: ${filePath}`, - ), - ); + logger.error`The docs sidebar category metadata file %p${filePath} looks invalid!`; throw e; } } @@ -118,11 +114,7 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({ const docs = allDocs.filter(isInAutogeneratedDir); if (docs.length === 0) { - console.warn( - pico.yellow( - `No docs found in dir ${autogenDir}: can't auto-generate a sidebar.`, - ), - ); + logger.warn`No docs found in dir %p${autogenDir}: can't auto-generate a sidebar.`; } return docs; } diff --git a/packages/docusaurus-utils-validation/package.json b/packages/docusaurus-utils-validation/package.json index 050ff2782448..9df50a0800ec 100644 --- a/packages/docusaurus-utils-validation/package.json +++ b/packages/docusaurus-utils-validation/package.json @@ -18,6 +18,7 @@ }, "license": "MIT", "dependencies": { + "@docusaurus/logger": "2.0.0-beta.9", "@docusaurus/utils": "2.0.0-beta.9", "joi": "^17.4.2", "picocolors": "^1.0.0", diff --git a/packages/docusaurus-utils-validation/src/validationUtils.ts b/packages/docusaurus-utils-validation/src/validationUtils.ts index c9ecca789217..ae2bbe946174 100644 --- a/packages/docusaurus-utils-validation/src/validationUtils.ts +++ b/packages/docusaurus-utils-validation/src/validationUtils.ts @@ -4,8 +4,9 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ + import Joi from './Joi'; -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import {PluginIdSchema} from './validationSchemas'; // TODO temporary escape hatch for alpha-60: to be removed soon @@ -18,21 +19,14 @@ export const isValidationDisabledEscapeHatch = process.env.DISABLE_DOCUSAURUS_VALIDATION === 'true'; if (isValidationDisabledEscapeHatch) { - console.error( - pico.red( - 'You should avoid using DISABLE_DOCUSAURUS_VALIDATION escape hatch, this will be removed.', - ), - ); + logger.error`You should avoid using %c${'DISABLE_DOCUSAURUS_VALIDATION'} escape hatch, this will be removed.`; } export const logValidationBugReportHint = (): void => { - console.log( - `\n${pico.red('A validation error occurred.')}${pico.cyan( - '\nThe validation system was added recently to Docusaurus as an attempt to avoid user configuration errors.' + - '\nWe may have made some mistakes.' + - '\nIf you think your configuration is valid and should keep working, please open a bug report.', - )}\n`, - ); + logger.error('A validation error occurred.'); + logger.info(`The validation system was added recently to Docusaurus as an attempt to avoid user configuration errors. +We may have made some mistakes. +If you think your configuration is valid and should keep working, please open a bug report.`); }; export function printWarning(warning?: Joi.ValidationError): void { @@ -40,7 +34,7 @@ export function printWarning(warning?: Joi.ValidationError): void { const warningMessages = warning.details .map(({message}) => message) .join('\n'); - console.log(pico.yellow(warningMessages)); + logger.warn(warningMessages); } } @@ -115,19 +109,14 @@ export function validateFrontMatter( const frontMatterString = JSON.stringify(frontMatter, null, 2); const errorDetails = error.details; const invalidFields = errorDetails.map(({path}) => path).join(', '); - const errorMessages = errorDetails - .map(({message}) => ` - ${message}`) - .join('\n'); logValidationBugReportHint(); - console.error( - pico.red( - `The following frontmatter:\n${pico.yellow( - frontMatterString, - )}\ncontains invalid values for field(s): ${invalidFields}.\n${errorMessages}\n`, - ), - ); + logger.error`The following frontmatter: +${logger.yellow(frontMatterString)} +contains invalid values for field(s): ${invalidFields}. +${errorDetails.map(({message}) => message)} +`; throw error; } diff --git a/packages/docusaurus-utils/package.json b/packages/docusaurus-utils/package.json index 9dba213e0563..95131b109758 100644 --- a/packages/docusaurus-utils/package.json +++ b/packages/docusaurus-utils/package.json @@ -18,7 +18,7 @@ }, "license": "MIT", "dependencies": { - "@docusaurus/types": "2.0.0-beta.9", + "@docusaurus/logger": "2.0.0-beta.9", "@mdx-js/runtime": "^1.6.22", "escape-string-regexp": "^4.0.0", "fs-extra": "^10.0.0", @@ -26,20 +26,20 @@ "gray-matter": "^4.0.3", "lodash": "^4.17.20", "micromatch": "^4.0.4", - "picocolors": "^1.0.0", "remark-mdx-remove-exports": "^1.6.22", "remark-mdx-remove-imports": "^1.6.22", - "resolve-pathname": "^3.0.0", - "tslib": "^2.3.1" + "resolve-pathname": "^3.0.0" }, "engines": { "node": ">=14" }, "devDependencies": { + "@docusaurus/types": "2.0.0-beta.9", "@types/dedent": "^0.7.0", "@types/micromatch": "^4.0.2", "@types/react-dom": "^17.0.1", - "dedent": "^0.7.0" + "dedent": "^0.7.0", + "tslib": "^2.3.1" }, "peerDependencies": { "react": "*", diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index bf62f3043b48..47792fdee265 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import path from 'path'; import {createHash} from 'crypto'; import {camelCase, mapValues} from 'lodash'; @@ -368,13 +368,13 @@ export function reportMessage( case 'ignore': break; case 'log': - console.log(pico.bold(pico.blue('info ')) + pico.blue(message)); + logger.info(message); break; case 'warn': - console.warn(pico.bold(pico.yellow('warn ')) + pico.yellow(message)); + logger.warn(message); break; case 'error': - console.error(pico.bold(pico.red('error ')) + pico.red(message)); + logger.error(message); break; case 'throw': throw new Error(message); diff --git a/packages/docusaurus-utils/src/markdownParser.ts b/packages/docusaurus-utils/src/markdownParser.ts index aefdda272ea8..a0577f2b8409 100644 --- a/packages/docusaurus-utils/src/markdownParser.ts +++ b/packages/docusaurus-utils/src/markdownParser.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import pico from 'picocolors'; +import logger from '@docusaurus/logger'; import fs from 'fs-extra'; import matter from 'gray-matter'; @@ -166,10 +166,8 @@ export function parseMarkdownString( excerpt, }; } catch (e) { - console.error( - pico.red(`Error while parsing Markdown frontmatter. -This can happen if you use special characters in frontmatter values (try using double quotes around that value).`), - ); + logger.error(`Error while parsing Markdown frontmatter. +This can happen if you use special characters in frontmatter values (try using double quotes around that value).`); throw e; } } diff --git a/packages/docusaurus/src/commands/build.ts b/packages/docusaurus/src/commands/build.ts index 3c2750fcb19c..502cb1ad2006 100644 --- a/packages/docusaurus/src/commands/build.ts +++ b/packages/docusaurus/src/commands/build.ts @@ -47,7 +47,6 @@ export default async function build( isLastLocale: boolean; }) { try { - // console.log(pico.green(`Site successfully built in locale=${locale}`)); return await buildLocale({ siteDir, locale, From f18d0124a051e0601697eba406fa1a4fc2143b32 Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 16:36:31 +0800 Subject: [PATCH 14/33] Write docs --- packages/docusaurus-logger/README.md | 43 ++++++++++++++++++++++++ packages/docusaurus-logger/demo.png | Bin 0 -> 24967 bytes packages/docusaurus-logger/package.json | 2 +- packages/docusaurus-logger/src/index.ts | 8 +++++ 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 packages/docusaurus-logger/README.md create mode 100644 packages/docusaurus-logger/demo.png diff --git a/packages/docusaurus-logger/README.md b/packages/docusaurus-logger/README.md new file mode 100644 index 000000000000..7e02eebea2f7 --- /dev/null +++ b/packages/docusaurus-logger/README.md @@ -0,0 +1,43 @@ +# `@docusaurus/logger` + +An encapsulated logger for semantically formatting console messages. + +## APIs + +It exports a single object as default export: `logger`. `logger` has the following properties: + +- All fields of `picocolors`. This includes `yellow`, `createColors`, `isColorSupported`, etc. +- Formatters. These functions have the same signature as the formatters of `picocolors`. Note that their implementations are not guaranteed. You should only care about their semantics. + - `path`: formats a file path or URL. + - `id`: formats an identifier. + - `code`: formats a code snippet. + - `subdue`: subdues the text. + - `num`: formats a number. +- The `interpolate` function. It is a template literal tag. +- Logging functions. All logging functions can both be used as functions (in which it has the same usage as `console.log`) or template literal tags. + - `info`: prints information. + - `warn`: prints a warning that should be payed attention to. + - `error`: prints an error (not necessarily halting the program) that signals significant problems. + - `success`: prints a success message. + +### Using the template literal tag + +The template literal tag evaluates the template and expressions embedded. `interpolate` returns a new string, while other logging functions prints it. Below is a typical usage: + +```js +logger.info`Hello %i${name}! You have %n${money} dollars. Here are the ${ + items.length > 1 ? 'items' : 'item' +} on the shelf: ${items}`; +``` + +An embedded expression is optionally preceded by a flag in the form `%[a-z]+` (a percentage sign followed by a few lowercase letters). The expression is first casted to a string—if it's an array, it's formatted by `` `\n- ${array.join('\n- ')}\n` `` (note it automatically gets line ends on both ends). If it's not preceded by any flag, it's printed out as-is. Otherwise, it's formatted with one of the formatters: + +- `%p`: `path` +- `%i`: `id` +- `%c`: `code` +- `%s`: `subdue` +- `%n`: `num` + +So you would see the above message printed as: + +![demo](./demo.png) diff --git a/packages/docusaurus-logger/demo.png b/packages/docusaurus-logger/demo.png new file mode 100644 index 0000000000000000000000000000000000000000..6cbde6f861e008f92a6fbaa07fd00c488024fe2f GIT binary patch literal 24967 zcmb5V1ymi&5-yCp69OT)LvVsSY&5|M?(XjH7Tnz}!JXg)cXzko?*32CJ@?#m-+%6U z>+Q8>ch9s=b#+yB)i-wqsS!1qE65VcSLPrKkOA7K`zV!pY|oUN94{ zbxAVl;Qu@ZSQ@YC zuNApn*!?bGMEXRdzlK}_(FkF|w1?uvzk(6bTqVYZksS1Lzq?dFrVR5L3 zqN2`|QC1VTA^G4Y3?=-6$hJj2c-PaS(VutzPEVOhn+!%^3Vu@{GkPdk1-)58cLNH# z+;1Lb@e@(dIhfInbNUbOtucblT|4{WO6Cf1IYg`hGU#RMS7XzchBt5fVLj(N1hFJZ zF|qEua90Pj(8@X#Lz;UQs%*p5gr5Yvm1pt6;z{Dg3aO6e^fSYe4`Hu}op$Vn^N87k zI5afS-sepk?}q&3MC)4dlYWb38%xaQE0I&o4y8zmg=iw&LX|(8ylXCr*M|^H z=*FY+po}a<4xFIFk>u5IAK2Mn4Ru00CN^BfC?V_F#%l2ef=NB2(Sb5^bmbexgh zKH-H>5?+M7Y($k@d@If$t{bfzx~{P+7iat-ZhJP5Nv4pWtD%eT550y00V6hSZ>!hX z+|&%7?J`I8+O|r8)FO=bJZs&=*$#+%>;Jy(6QxxmNps*?^DkOwu^GH zJ*iiKqO1XTrY(e-`B7L0fz}s)xDVe2(i;uwr@)&K$N?hAaQ`oFe|pkG$8~&Ud>`+N zPzhBEW!?c(`OX5ixr1*U4#gjH9jOtH-v4l&ewjC8oqP}cRDjTrh%6vz99_5@sY(ibPi&DK^A_NL8GkM0C6VqXCM%FRkz_QChT`0fQYHr*FF0V$4#%a6Y1|zlRW0 zeAq!QMKLlLgkdi2M_ehQKEm47;jqvOa<-j*yAGLuM+oa||%?cUoz}_@J zJ9dHeLTC@;?{+_{fWqxeUKc+jwSn6Y(D+Fv^e!j1GQq~j1~;=n`X@I@OtDNY`9nud zvfwbOG&wz4Iq3-n0cAZVA*Q4PH>~Rb++ykC(EW_x?ePTZk zW^3|lSHm(-778Tb485=ngMq9s7v`*B&7b`+J)j-NzD)AS`2*!xy z$a8Aa$L6|zH8wR1y7F11GP5#&HTF8Uy8T6~Mcc)|MZ`L1ldL(fa^cCU3HYgv!{M2Q zQhWJbCj310$pr@jPZUqY=Z~!lNyX+RLlYxYW#cOaONC2$MR~j5lD@O6R;ZnZXa(~X zTIY>TFpO)Lw#&Q*y%gzEbg6Vfb-hI@A#^gFclY0b>4bssxAR|tnM3A#$Bfj4bcvjV z%Z9tk{xbqE93!0KT^T!<eL$=cK;ekgvi_XHToFQm=BOlA;<>ooe*O*s(9wVx;!1T17RV`SWV;EX9aX zrc~aDy*i|Nq-CgO#xXBGs2R7}{08J+=-Kwn^*Dr5?4RU6p0l_SOM0+(Ol7CB zJAVSQxp2hbp5a(=DCDY1|C(`_PS~{9+}#-8^s%W0SC^H#{|yrOoBh2M$qV^i^wpWzbT~bRi1W~JZ z_Z%H0KJP23vq_`Tld&3^DoOi!Lf$K4UQx|6Clj`?-n|>jOz2x2aKqZfB|+vyyU0B5 z%N*q+2n!SqNDQtI+z%)SD1&!LvqH~&uZ6vexq-#Ry6Hkj%*LnJ%o#H{j$6__RFLtk*t=gRLyp z{5b_`=r#>Cj@y&iigO}s{e&W+mF~gbLobp!8T&nji=vM0+EdMBvAQ<$3VnZSiopoS zFoZdkSt8|IiaLwCSa5_EXOn&STyixNd+%ao2G7kCN1iS8R%G}b-WYy6|JUe*#5&SL z7WPQ>uH%tk1{EVG8&$3)E)4gbPgqa%f<8Ja>#EWMqt)rVKfr$s**6CW{*=^h2%%FW zb<4m`X&^rJ^wM~#e|qt&^;-!v3sen+?cv(=IR6G;h)E%1lEfi7NQRI3Oi`XbPa2VE zl|WowRb5D;c_+6<(-8Mc=B@vG8-T2tv&1*fTf>Tc?A2J7v) z3_AZzf@!?Okk#mAR)a1dpZ3RgOW!`f8N{~4%OM8EMkPfh53PQ6mkr~FtP-yi?f9Bv z%c|SW>rWw{x<6UbBC9odrrcD=8b&2WsjXJ}Rpom0Xn*Ld;L+Y!{h^CxnBeh6?91mb zQ)VSIpOn0{NHy$Rx$@m^)wb#oXWO*s8f%qSF(7nmTD=}ci+5GsN<`v;|_PGEf+rAcGTqVJ8Eh9;GnF~ zYX4lg^3eR@#)tQUm*K|fxV4JGO?$}e*UN);nYX69k5_gJxM}j__!O1Jk;$Nm89$C= zWx+!GeFt%p@DfTIVL4x}v%$IC;KUEH$EXCnLtc8$RZo)J?ybagI%k=}tQIGl4aKGX zy{X;NfGkeeM8})?A3pZMV~-j9F21fg=ef5%K}r>~knOI|PT3>PWgD?$1%*RxNoh-B z^@Ob$4m;g5<0Z!Bw&j{F1n#ZRqJt6Y?0w5ejXmwxPfnf3bIxI>S1TEgQ8pD%ZL`mV zu)U~eUJb9F&cV)oF~K>g>V)LHO^?a9cRQF1abp?wS(BMcd@SxtkIT>Z8|7!^Lkyzr zdmhxT3v-$`!8e5F+6%1@*JJDa>h6>7T-u+sZ`)PxX3vi6+j(Bi_9IR`&&tm<*B&oW zg9z)r4ZL_BsgI{>+9a|hvP#>UEWm)#L4t7| zEm7qqw66OQ4Zb*n`Mw4uJa78AK+2-FuVFTA%#BEa-B|b@V{gv|w3_iC`xt2ZxVa!9 zC-mF#EGK9v&KIE(T-~+@^1e6a1k+Buy1kvby7gW_@(*m?vaPCy{Q`%6gY>?u%>diT z-u^mjiF1q|ty^t6uLH_X^0t_tJ6b$OmI?_P;`xgydzuWvPg^UdXg9Yw>A97r>A^u$(3L_ix z-+8bApbSh04XU>l#?=8PdC0+WhVZjK_rw$XXgY=n}bDT3Fe0x$u(wQG*M}|9;FsLi9%! z2XkH$6)9OFVQV`>A~t$PdPWjH1R^3L9yRCHF z@RE@H9_X*%zvgM^V)~zvtnB}ME#L(iewQ#X(K9mq)i=OJGa~zW=P(pX2_$@Xv-k z48LFfZ>0Dao&R_W5SkBxhvBb9<3rdbGQtGb5ziDPqX?t`m;Lb*0{*Fh>vsxV?7{wz z)0{x6AOR9obOArogiU@UviM?TjQJ^=i1RBHc?jGS-d+p{gbnI;@j)PY2lB;4hl1Su z*6ynS5)_bSgz)pj0BwMf_>1vmhU zZHJ#L0^+x`GE2p~n+EW%GiXsCbRDQsV<;|%J%Kn1sa#I`U*k=Vhoi<+jVBavEw`)g zZ;CuW9M4zz6S!`p$D!>ODKFJqns$ev*V=B0s@0icW$<~XE?co9<8eekT8+>mzgXsKN2X{QkKur_-JtDQD@4janZ+J%nr%ylQ;y{*C^nwLxhi?d#HV2T z2mT^qf(QpAMSBl7U8bHCg!E46QzC6ntNU%QXinoBCVwzY_+L^JKkSWh*{#yOs$?I% zpf*Uz`T4VY!`}%9qY@@+c^E+9_)?LsU$r^nG7Qe_99fd+M2O?qk^gvfW5tN(>xYz4 zqMz_}pmFiyop_03tJzrx2JC=i{5@_5Ts$Ats_QO(SX5NeY5QxY(QvZd1I=EgXap`R zMk)`smJ9I@4SHTIG&oaJ)2n&?D0n*PXNWAIvA{ICjoTVm1okz8J-7w2PN;Zg0tu`U z6b|?wOY&y|K_vT4p8UH~S?V~SxQq-cT3G8la8aT}w12hzhCQIJ>+4lGM`cE+!sg9qIB}wO(9OjZ^7qlm{0L+igin60S5&kNqJ{0r*y{Th!fi*^`U=ZJPeucBja@TGOc@ ztM-+4D}ZrGRSHqaq_?0YPd#p&$@66cQEm^=wvS#DM_-ymoG&G|(R@VC2NOO_Ij?z& zl_*z4YV_{PNz*3<^JJ0;TPT&Be9WA=f(-m5A&~=*NddQI`$4G`4dwV6+6 zwX@{_2nZM6@#Dcu{PXAhi9E5d#`BAH=KDoiUI-czZFZ}~Hsv?wCCFI&4}q7wnyqe* z2eW1GO}QDvEY0P1yKYVCF;OJ-0x(>+OYyf7tGnEO6%h0D#we2`h-suUuZ^jcS) zsuUgbE6b8!T{Qg3Z95JXX72EffXuoFf@Z!=)mfq7d%VY*LKF9HPjJ8e6PoMCtoZ%Q zfjkw+S;&<1ewPc3e*b_~f2x=2c3F)wm~Fd0g^m3<#l~B)!Dky{SCkiJm}}0agO9oK zrWFr&5@NsR70nF-!qK)lJnn1`=Bx7He0hga@wv9Kaov@=VKb0?CH(t-G z!}I4It+u97$Yfev&8|pq?Zbo4x`pY#bK0cLdWY5L$k*I%8&1R+I)B$f=XF*t7O8{r zF5XQ!-$LN}@nXATxoa?6s;b!RWH~r6kdq@Eycml6@dy%?M<7Y_*9yta^_+H*R2o}K zP@j@EL+cer0-d_jds;V>>0-I~m>6ALwzs@%UXPZN;^Ns^-Y+RHFSqS|dgystg&)fM z=jvU!KPAw(O6)Au#O+VXVEQMTuXo@zZ3N+c*ILO_ZFV+ITncqMjm!%sq%X;UrNBz> zMO4E{!#*d;V(EL7ep>fO?M{qli#tO+`D4Ac!RP&R&)W>=^%233Xnjwq`+Xr{skphh zO~A)yZXul}T&};IveWjDv#+|4n(!WPVtiz3!?A@rs$cuDSWyP?9$f=bDotDVGOo53EncCfO;`EFfgc1hd+l| zE7gt^;I8GFVx@apkzk$N3tqq7rH^m`^VOx;Aj7DPP#j966JgsU+oKijgr(!NAbx(h zX3?rYwwkZZ(P=j5J}jT=Cpq3x!;4w}m^n-tD~2Y1-Rp_Z_^I~!WdR>Ti(wxT=;tlH zhBy|DA{t2H_2s!s>FN{xkAO^b2(k82`2D36V#7R zb`pCC#GKN>L71yE&#Z@iQvI%q8E+LcoR$Q-+%;d+1UGoPvuN5$~nAWoWfjdGC@)`!g%1SmgdRna+Y1p z{1M}U3ZwgLu7XtGs-y3Gc89=gH9TarXS+*YhBxD=gxV8F-ak%d6%Pe^Xr&#CmO*t5i-nbTq1U!*fY z(zm1)1W_yIRyi;v&-ak4sCm#`{HDX?%L?Z41E*_ygZZ3vyYXCwwhTl2lifD=WEN*4 zRg3`j4%Ky}3W9y|Nff zRpF$@NU~AZz+9{=%m99V)-_P8>+cK?(nt?xv*HnERDk?(|Es#zyM5_!{3WAz3KMP% z9ur;{iX|vkkAye~msC<@13ZcI%*mEXCeSl4&}Zf5!|@vLM0r-+Ir_)2)5a~7iNHeX z4BRix6y%@Ca86?LC!!QQO4|3@R57h6#`ZCr)H9RO;R99Q#k00m7!Av*PgvQS3U^jC z?;VxOF)#S^-Dx??PDTh{9TIWa?T)>%Qa%ofzv$T5TqXO*dTHm%fFGvL_pMhiVf zQuxc_HQ^hfe`2H1x~eyyb10 zRa5AAk@N8)`2}VQwYT%CYmw~p!^2^PX;czNrM8Uzc{%sBtcoxf6< z8kP-Pk!p?6beJK61dgW&f9<6_h2YetBM5`fi6JJ#oA8-Zy4#eY5zjKke@;u9h-AYL5yA9Wt-=$D zsxZIsttr04Y38RWel3ttnL4)2RAE5~2}cBW>622$#==6qrMl(4s8o(T-zqG3p}<*`b4KX;^C&#m4F5pC;<+(05MV3j^0zNO7(F&-_R z=zh3VxB4MMn)6-m{$dAyC|5e2L$KqzW{i(?nRFu71`S^Nl)uP@Yh(<*cVdqrrdWG- zG{bR(ixx6|+gVL5Rb}#hHgukUZyxMT+Z3NuQ%$`)v zItgZt0zb}u+qxejTu|%zYRg{kS2#o_|&5*kJLdI_jAV}^Ia#13$hj_ z%!1i_>oBe7D|M%9jAisOenvzMQ!5nSwX{e<2JNZh#ehLZU~wT8gbi9Dm?gSWPVVi6 zRVmu2O${kQaWg8mTY{4-n&#g9AWKFm44se0A<#jZLC2^{jWxl`jp4zZD7OUgnll5#rZ*J zHg}Ylk2^`)-LPOzHjIO5)@}Q@HcCmJI9*-HLa5&h4#d7$l(e+)DEJqJu-k2mH$5Db z*h(_yhen}OZ#1qMsyRg|g#@&D+JE#<(5lvbesD?Un)&pGjKXa%PG;1E@TG-`n-%R|9nOpVYG7o0p%gq-V36E1=Aai^l2GA>Y)!&jHBVZOccraIsyhC3Gj=( zn57%D7^E{Vt0SG-DGl(C@PtjqsK+XHgMmh(!YuhXQ*>3&!}Z~m z{TOeNnE!-hjpqFo*jpLBOmQk6ujI||E)lT5PUl5AYX}jzV`PnH)!hg>==4D{S#<9) zU!4fa9Zt%GQ=gy6Xr>eOk|pv(ppbBfRrHbOx7S5k>*v2IQ2H5qQtLSB2I8>vOwusH zcoE^|ccQ#g;#E67Gs8<)k0|x^~D5z@#HS2$>Au3r+-#>L5?zIyDH`NlgN7vx*+ zG&{<6=?i>tCI_G0nAo^fbAs7of5z9vLa?MFI0XYW0-VXB6%|3`7$nba zbzhD1PjAI`Xa`i2|Gu*Q=jxD9owvmWp%*2Jp2)I9k?)n3&W$vf@8Qq2P} z{q2TNgdx!3bJjQWy=W5elzQATmJ}tybwiyAGrQ2{HGbwplTD6c7mc~rMN&1)y!cf* zk;mWIt4UKHTQz8zxfC4}96t=~dfI{@>Ql-U2h0h;^w~LQA#Ht5F{qastcw5}Oor=v z%k>l4Heh@=r8P`helS=q0_?qP`Tl0M?NT@Y3uQVCu%edQAdSUxWVERIL(nW$g<1XC z_)8_Zf!=AZulZ=MG1aTK`LP81LlFni~f@z>2l}$E_{ZKW2I6!@37a$S(>Dpd$ zzrfibMD{r=v%r*K=c@sw6iwbnf}-2EoJa*u=EDghJG<35%1KI;-^7 z0(Cp?+k+9=A=q*vRwkUFKm#}!Je;FVT!6QP_UA~Lb}GoSn)`btUKPWNFyYVqo!*Bq zTuD&xmE84rJtK6&#w-uw3<=GasZ;ug<&n*|Jsf->d==+XB$_#viwDBWq^Qt_-auj8(C1N&28ySdAwe$omQ6yh&M5^Vp~`wob1!#U~7yoXPgjopAfb~*!}!}y^KIKcg&&+Ekdr^d72-X;`LW#X z%we$OtlyVCc8cq-9`rP+^M;C$Yyd`55wUPkh+es7v(g%a3LQJlFC7miAJ}FZW04s~ z4(G{@vfeH=5YD!L@`&g+RDQczIQ+R%) z#H-X`eQCdT@TR_5cT^ zig(b`5Nv;Ek|^a&NX4$?tajL!Z;kAf7O19Gtp-7LcI*?r-USmpB`9m5ANEaX&AY3# zG6)X(oCmMBb#gg~bDTQIWc%}f0R22#c$eA=UZEM%d@4P0#NBPqMj>r}Lm}=?-BfJ7 zcCpy;6yBC$&=HLYs3jB6jK?Z&V3Q8$7K=z@os+9bGne~^U z1MFcIb15z1vXZ!#<2TGiZX9Db!=?!yE^>^Ywyz7FEO_^Un7cN&NhY&0&%YR(ZzCE9 zZeucTg+Crr4r#}N{H7ixXlD#VRmLK~@e$mkCf!$LOB*ug~(_YtM? zBN)pQ8c#eiBLR#s2imZQ9?bOTbO)DweR2XA$veStmZqNykC2Z@Z*ggAzfYjK@@n+y zfP}8OS980~q^?T6yd}EW7}TAW57<%$>zX8-hjw5@BcPysl!Ruzl}vAtKX&PPKk1ld zPl%Ik7raUbzg^vv;^#{_h+NIxO~|N^h@>MdXpW$iS0K_b&kNr#u`>A~umtVr%LrpI zF3l)Obxf}f7DIudQ?ixmescZ|;iKuQ+acWstgvm3@z~7|6j-Cm-zU?mHxReqT3%Dx zENOneoOPiJVTy@DEs!SBW*@UzEmJXsMo|UsK0lfj4D!C)2>u56 zpMJup`!>)Vzy?Z^1x!}VT-jRf+?$4Pk!7yDP+!LH)I4Z_!!OsFE2HDzVyqf7;shEB zR*}%epzYt5;q&CVwu|Mq12RM!R>Nr(b)qz@Wll0y+(wQ6-b&foCH5BjNWOy)8mgN< zk;mgCZ+V7X1Ze6Ej+VaS#W=G5^%G^N*D7lXNhtZIjIx$%LI3P$Xaa15=Zr)k=q=0y5x>DM|T5Qq0flz#Z(>UHRB+;QI?Em}FhVnQ{W;7cztH|cJK7)Em?3denUqS!1> z7FBhYtTOa;dZ`b@T2n`7tJ)>XS+lc4ag|Bq-{PE2{ij@{4}3Z9bb5lZYXvJmg~+Kj zSd~@mDE}cxm{32A$>`%Xmi5{%@*8F{6V9OcwHP^ddxDJw-npE*D+hJj!v{bl#ul6N z?Ocl!d?6v8JXPm*5#92NGVl%+eKz2?=UT76^a?S0HsJmAC(rcB^V8n@C&$1Lot|eZ z4g>;i*N;w%j@07&gBaR90bm2b>#uso_1=$(S zCJwHW8dL}a!fjbDSH~z?mO|q9ygA=p0W`7!)(~2_hzmMz;_HKIh6@U$u;f zKf-ps9)n@}#gvs$|6V8ijRBsKcz#eilK&%w!Ov7y^L*v*DCy7un`LjMMJWi3$R`6e zrO22QhM4lKFyATgq<<-gL$yDY17J)X5p=wOgD~JLqo7GJ^hC)z_~plvWdArOJs=*u z{T~0z>p1`4tLfyFdCS~}`5vCxsxfL8DP}#?USVOO#4#^~1}z5zen3Hs*!j8FEw=X< z|G7TcLl4Sz@@8*iCJtCUMN%8aSSZK0MVt9uqzoU<8T%6;X^`xg?mi;G80g^pTZS5k zV~&^6A!a}2wjRvb)CHB#AQ5R%weUdS*Lpj_K3BVy5pidrjJB$^2gOyFS?3LEh8dMa zmEy^8wp-o6L)sA)n94|6jmN~A)lP@PzuW5$N{Ro`#odfAO(IxG7@7w#H?VeuZx1Y5 zOVi9k28xM^y5@PgTsL&yzQ4r>Ww6G$mV6=^4uM+6?0_RdmK<>F8my`HCYeIqeWstP zb*~j7TlR8~tZxqPMWbSNb3pLC&2Ua!+_@M}q$>(c^ecQWU+_A^fS2B)UYO_lGN+ju zlf;NiRj2U#4C`e{o1L&*#Dub}v>CX_KjSg{P@(!mkv|$vHs*AuB#A@MoE@$o3e6aL zzNw37d|Yk-*5s?t9()nUAF6)9sm9NkJER>>hC>zHP^h9~D`@)2x7VX)?OACeb|rlvN7l&a5zk>D!`!sxaFaDU0+ z<1{NcTa16OJO`oZL=x~y&a=;bR|!o5LJR(|?n%)C8Si`T!%3{~uC5$-OPRvIIFEFe z>`g0In6e>wBzgYW4Vo&{J6szemN`zhRW#HqAWs8@MEPcWEXlQspE2+IQHg6_z8*S(a05i}t}Zp3o$f`H2k
    mYSH{XTR&((znYTAtB|d@~e6^ zgZ%I{(r0D#-qFMm9a2RpWil|ACg}fZ!}Um}gdyq|Jx3?T*>PFkZVsMYxm7wvIX#!Q z6h&M+<;H+DYfNVIPIhrfFXA#4TK8#IYb`UW=>n4$Hgs0mu(-$>b1$E(DIH6@{NTv?3$v4 zg?{h~gkO(B0|gT)4G@SI%@cEX9)s(HH80aW9MCMxt&iAb8(h7l-g&Cp8l%I3mE8;! z!KD#xhTK_NDMNnv3RA`z?3J5$y80>8509DN+=rUx> zea908SZPR?jnQqtASs|qA)Z^dJ%|@-UZmq0Fb4@@d*6+OzXk#Zp2{BvUi7b2)*@EY ziq+|zQDo){Wvzi}qvF<@2~-|gQFZRqjBhC9nV*+NtK4r-xFia!YN)58)#PnTKtfc{ zYK3%BfWeO}_FBtwmf(os5Ky)Dq5KS^&gd3#^BcEehviH^=}$GRB9IWr2|u?>5Gj92 zGLgogqQKXg!fD;Nm>arSciNs3G8Q-sB(BiM=)htZUB{^erHNNwc*HcSa0XqZCpm_H zjL|82_VNAAC(a*|x#2NOSMoIZDNmg#L4J$MzPgR8Y2=b*jo+uU<5jKI%r0B#%&Bf8 zj6w4PAPD-z6u!0Ka3Aer5X7W-CbO9O>Nr%9Ao(GaWyjrPn~$zujJSaA%sV`u+DbiiNIcnHQ7A4_;AJ7a^7YLRAR3V(SSg0nK z%HaCKJ{ss5XxPS#+AJnP_6Ga3yU>{Ga&0Kg?+50$*>Rcv3=OSY|3b;m1%)@-N;ODD z{pT_SIi(qd`+Sa~=1@W&7`hTrA&4f-jS4hh$x(<{^NXFZeu|JyfB*7T=Uy8DW`q0( z%mPh}&jp9&?fz7edE%>jD2?T6i|Wx0crH{kem}l|4O1!zB9T>`)RX8!#|Og`JF$?d zc`jE3QO@fLP?L&f(C;|r-xhB)tr==5MSlDCWAS-^MK?#G2x7$M``k-cy`|M+?Le~z zG5eedp?7*QCA*tLef?3FIps!tFLe}NictIWui07}yIM_EI}wi3?t|Hp=2AgX2V9xn z2KVlp^!Zs7lGFDlVLmI?Yi+HW>5^m}Q>N{+m`|782bWV4EjCv&+>qMw=(hs$^0DJw zkDFf^pafxfe8~^OE_L$lX))o8wTo}FGH zdz?5W(F{vdf#v;&QwxaNvBc<1l9pn?pLmyQ$qg#>&NY(5et-}jB_o>X0WM43QkpaD^5lYQxw(pG;Z5oO0>TIh^R zvT4uJ2R<_5yX0$iZj9%8DJCD!yyYMa0Tg0+ELI1`Gc*JqZ2&I`>&mGu%O)ybY?rd+DC3#`mE}U-e5d;d55Koz9V1`LUKZ zNAo7MNmTjupQ{1@k&`KEy}@RsaO@y}vH$Q*{cWn!=X0dmJRd+Y06y>i0CBkM`&`Af z$!}A;Ev0!Y;7tWWSeX6NkDPQxaZU_{^MVZAmbWHzV}tICD(9C$;@N{U#Yb_UBCgpT zcP(F%?XdYjlz&?Ur|Bf3r%1?&-&MXtTCXN}_c@b)@~PgN_HKvY;bwrT@IJ*&sTeen zM{v7<4zYny!%TI1oK&n+a-SGtrbaG7R$NozTQBBuzmpVLrxrh8f`de8rm}@tCwJ{H zl}PBm;02s%Hz}WoBj>d+%!DctQi+Zn}7lwm9abo7%Twqa;A=1 z9#mzYsaQmpVD9;$K;vj-Y#E1LLESI<<(@_DF^f?P!^*kGO|kBFDqglAX3WuqGT+*@ z%A^Qp$Bf*vFNhiY`3vng*_P|68rb@&0c08Zmr#;2?5*lxq!G9+i4mNQgjFc3K-@qA1WTbCp zXtdrR=C*YRh7f-P3fie~#D&7|S4c0xo z?2jSs$<6nQBeek(4>5yf|Fa1^7Qut&huGkY_SNDt=-W}ax9WK+DB15=zm}}Ui%&_P zJvpwyEp9V!x0S}hZCN>Sk~>`BvIYFc4wcmnBeTHB)NYq=f=>!8*&&)IpUHQB zg$SpDVXY#=s_U7m{DrK92`Q)YD~V-Pm#(N@k@&@Lal0)$k_{&!?@0l+cJ0N7n+xaC z3BYU#%@i(T1ct@C8=zvP^XVXM?nX{I1%xnY$8FXcBGWrVU>V?{yTXvrJz-Gw5J)fL zhg%)amIby#nGoV}JJC|0n=Ln1Ra*OGC{#L7V%#qOuxqEJaH}noxMcOKmR(vQ@kJrH zG9$;DipWu$qMm9wDz1vG5+}nM>bLqkD(^oSy$%`xJEtN- zBhEpA;pfwp^q-R7xXM58B(WgsL!qM76&@>;49FxzAe|6K#dJT9_9^XhvHS~0_;Q6Aho(c~`w0FY7~WAV*Gz@ zodfkKASsC;mCWd?L62RgQsvk7`tpb|91-__0vP{I0s`M;GK?TpLTP+%r#E$3A5Agv zxg7dYL&XSO1Awp^C@?V4VNTQeUeo!&SCD@)5Rb#gr@E#lgF77_d# ziUjiB&2TnZs5X?2(KrTk}#XA=PsOb8h16mdqdpVI$K zbOFKaX$<(0H1?vCm1c#mAY^K4YKeK(I{=mlSC7C$5Cs7)2cQP_V?WWOdjY7e?B4*I z%i7wHi>>YrGV?n_NmCnL!Jbd+;!;ut^_ENWBdM(W>z)3!{{c1(elrSt0+5bktY*{W zmrJ##m@96FWhC>7bn1I^+TLICLhR?g8+Q`bivirOVw;ES;NV~`;6n_#@VZ$rj!#QF z#17{DJ3as6U>xvJfp5V2A-@;pb-#&syyDaw=P?|7L&&_oV4Q^&;4jkwhB7a6_jEaS zT>=je|Iz-}=M{lGFG246^FG3FB46MB92%a_U9lq}Zdj6%;k(Z__4*y;8)9hZ(ecvn zi!7kmYzjFv^5%9rBEqRM(Eaz;`S0045Tjj!ewM1%d^VnCN-o#Mw+`r^=s z?+oW{LhuC4KyMd%xI5{Cvh-aRr+v^#%cV3v=J2?-(}Lm8jP&%Nsz>Tbzu8nsU2Gv@xW&T!iw)otd0_G67h#mP_t!|11l30QspVQc+Wc+3Rd;WjjkwP%Zkl`R& z{7MAEpH?21Bkb@7VB!IQ>5XZ$K#XI+f4gk|69!`0es{D0pcYgSpI)CRM4}+AN~HiA z%sho1fWLNlxH63)=!Zrcl%oidB;%kfO4XK@Mu{NcO^qbvr=PT*Eq%v(-i=v!Puvjz zk9kGMTD7dV(&R{z`2;y3UuX6OfHYL(V-o^+;=jqNzoy6!C5aUSwgdx!!Z8xMt0A_I znj|rP?@9nUy#*VBTiAF^c-nMGe=t=PCEi7Qq#(mf{t**RfE*(|QDNkC#d)ou;8W5= zJR!)h_B#7lu)z4b$z*;g96x}j;Fm(o3YhQwXTUO)KnF$)qxXx4-$Tn6-^CWiyarq` zWOn^tB(sI;f-|)i7be|t>o-V2Ov0!!xbTc0sUnbt9(68%jr5va^}e@;b9@;d{*23E zlfCB14f1Om))~(h1c0deXc_Z=7bQg7cB0xArjEX90mZb29n>@ zS*@teg~=~Rf55R|IA$wL;#k& zvw4V3)y0|pg#;>&^udRetzv} zc=OrPn9lqDe_qYI-^li}uU~!wt{tf2YvEQxh)1}}IGs1NQyoV%Q0qrF2tpeC&-BV%!qK8@%^i8vwZ6uRd z-GxrOO=BKIwxVSB00X*)9H&x$yun%tZkNYmqk4mt z-Vn&;1VCjKeooo6$43Dq-2zERHp-F;kmC&g_h|5E4r4{&XBxsPsfk7%W21E4i$P z{_x6Y0{}G?FqKT0Xs)vm^yX+BYzfYc^Eb4Xaj*a9KM|+{4oZlKBJ{UdRwz*_1EoM( zZc#Zi3J~Sx2;+Q)l9SC5g3faDS5&Fe6OnC(4e{x`_5p{4yMd`x6o|!WG$O-TpA)H3 zX9Of(RwpkUv;`}t(J&4a`Ve^{y(Z1*PS%v&ECBAH9qPDAqI!R^*zVmv?X+URnw z=NAD>O~hSU3#kS#D)@x_o(=98af{XIe;9-`5KO=!6B=o!MXx7JPZXGr*NRd)9nID8WI3hM01`rIQLJaPej&ouxku7EnJCsMVSfe-FVYt(^7^{O272M+q3q z0hl9j`7;16vuXr@TfR31;4Qy3nQol!dWY|Hkqn_6go&08x8&P@)Jmd6%fQzxXjY(4 z9K4SA5Y<0NF}@?i&U5={26Y9aCfP`Exc*Z+-%E6G3XIVzCF|+pn0d8}pNI@s3%1}; zLI60Eks0_V=YNcrO$Hd+Ib^aCvZ)C)UvdHFhClYdy1Ihf8C>{pqxsLf^f6$1Hhc|y za~c0kgUV3glO#6qt^fLn=s#U`+=m0@yY`3nEB`|;r~Uq#2a{3%4A`S`Jz-e;OV;fL zQG5^mE}TZomW#DHz)|SyMa$Vw#BMe$y+YGQqJL0e=?ZY&crlEQ2a4ad+uOj2>5}Z6k8+>NdU){s#*G3J3gerFbhh)&yV5HB_xr2I45E04{WN*3iJCQ?LJwv|3=??)AKF z+8yv>MoD;>(TAPMkt{uhLmB9ZD=t<$d-~V zTasOtq!gJ%O43jmWJwx3g%m1e-xIPgBU@v8&%A&9eqX-V`~KtN`dmKCd7g95ec$Jt zr~CmKx*XarU`8IEnxV~dX?ZU|i)_!$4okl5Lbri_XoTd;zBUMLS4YWKanhV>Yk6%s zmOXag6CLBN{2YxhSf-VnLoXUlwq`tjDSzn$-756nHh8MChy~LzupS$vc-pOv6s-qqy^U&{Wu3(oVK&HE zS~9oS!ML62Yfwd_Af?iD{Q{KT-nGwvT-Vq>M3~K z78kIm;h&2N%ZoB15*gVbz{y9AnnW|8W^^L=*_=&KcnhLX7tCrs7r?Q89!tEllaH!a zek#xa3>3AFa(Wqln$&?Q^PKJPJLx?m_54J>YSapLq^qVkO?5R@K1WD5z) zQ%77M5$SY(_pMAPjGUE}g!WGe-IpwR>3S90p_R;~#uukLU_4`R*{t$b;6Md5aJ8U6 zovs+Y$}YYzJ20@+@9&oT3;HzjZ+ut&d?Ckz-?$6d&6qDQ&tKT;E*ooq)BG*uWoi|B z-8E*9q*#gZY+oE%Se!7>$yV^;P$ueeEgIZW3SlRkMEY{H=qH0)So$LF1Q3w&j~l%= z8sk`bNRN6U?gwSjh6v6ksVMtZ3os;WMhhrXfSvlvGAydz<3s)8Meaj685JlC<8V9p zc~qJR^l{L8+moAd6Z9nQPfKy!*RiJq?tZZf&l6pBjlZEb9rnh!pKHH~4->_wm8MqE0JPCwA+pd?C`^ zW5xIv2` zYloOU*u_Z(*|qLDT73gUeVaGX`k(KNi^Mq_pt*Tu?&)WdG=mF!Jx*tYgjsrZee&vY zQ8LW00TwjiPd`S5#8H0hjP4%Z*Q_IH&t3Cf$X&Jmtdbc&Va#B^*VT1INSJ$N-R&U+ z1<3izV(HCEJ8F7LKZ{}dxwA=jdk&tN8)SqzwLjXG9AB1IUI=oEOKxJHUN%(T$AiRi z{Ap5_V!u39xw%pC=Fh=%iDTu`F=)Ouy6s=l>JyU4z!r^OGHvB*)Dm}b4mvtI{?rz! z-b16-Suj1aH*i;_ojTrKG+QUMq}$q~PJ;Y#hu{k3*@n13L$CjAxvHSLM@ik8(Yw>4 z1g=?SuBCA$RRv6x$A0gPzoR`Bt22{#9iN7KaELz$->u-<+iCx_iNGEg(AZK;!GyMJ zG|N4dZ&Yca@x8o^&Rii>S&G+m_b2k?{%b|Gz+=BfB0kxgs{bea(gLv)8FLOIQ@DnP zDjx^xNt;|_`fFxx>eBRNA2n#>ul*?qYis$AAxsjNjAq$Jgz?RC_n`jkD`tkUivwk6 zCD$(S-m)j_$NTv^)Un9HtlWxuVu-cMl8!ISQjeS3Knm3;*X{&9nk}wHU#)M!qAn#F zA*zpk8h2pekLNU)CxTaFUKjDXDKGMjR{6<=APibDwp!F`IP+4~pGGy2fv?B6B${9h zg7?c|0zR%WX)g|!rFMisOlH(0ljeUvchmOWQT3Nyjs^iL5_XA4im3J5Bk|Ry8iF22 zL$yhdl@@6PDg;=`RfRx2j6!Lpdgw?7b2Bi|n&1MVm+z!XI(Mk*Nz-awyKf0`Extlw zTXQl#klD!yDcZlNmTi-jJC{&lAi%Y8pK8LP4r-H@HTi8c2r zhsE0PmGI_krV>&r(*-R7#Sk8nYz?FZE;p5WyA23$fK)^S4Wn6<)vwDcmXcup>D8%s z(T$zNtIQgnz4Idt?=Lz|#{=1-9d zu}^34Zio9ZoNm)Or8D^;&`~p&tRdv6@~8_!H|E4g=5OD*UOqY?jKgbKpoL>q-ricL zs(K>D16$)aeS|}NIKn^?Xq+`hOL}!X9uib=z{mE=b)AB3cAS}e!PzfK(WTp=Ii^_H zmgK0^cI|R~?BV`*SPdc1viXA!X7Z{e7F^mhk+rI|tq#IP+RcEHA`pdGC+NBKcV*ND znnuf}Gjjuc7>VE-lIM}sQ3t$|B7XbE@2_?HaIBkRp6B)*CBhpwL0vDeCkTZ|4L1xM zzT*@o{TWj>12R}mPRT?jyRxh~Qhk{i76B+?u7pR(U|u)Q;FWgQUUTQc2CA3_+T6gv zpkNwWliwb@QmZ*Bvn_14T7-n|6b}=>P3fg`T$|9V{4DIhk|UQDase33AR-VUeUDE< zxixTE4c7J=#I+Pj|ML=K7>IgPrS)s+nS>3LY!pb zLU$!rcuQCYkQ|b^2T8!+Y;WD>)xnted?ZRp*vXp{ra(E~07|j)8Dyrl39$c41>LQi zhQPU(=llI7~hy#ZN(+Y*2yQ1Td*O807nfA4Ub}UIThub&KU2VS&y*WI*n4COG)_t&jOAy)0KAv+nb)bh5TIx~yvtY; zCwc)tyBmWPJzzt>)y_mZW+@0&{j9Jx^?dwO1mbz%Gpi?9#T+Um2~ z%Fo*w#@VGaCCz*r=}CU|s?@PptwS5rQG?HRq6v6QmCfm8-SsV2!<^+M6=1NwA0TQ$ zBu4!?P0|VVJ1E{zbbZMA@mWR6H>fof0O~%(nQ>vVO@e^W#uuAy-Bcm$e3D-@EQ*?d8$IJL=RbFHTs#YSa^U9r^3p{6@AthI}D?c!*#4Jbf|@f%rA0 zgK$cQLC{seU}qQWe?_Zpu-<*LVob!%s&{mHwwzt>gj&hpQd%M9v`{W$Btv#F^k4Mv zAf5nw?YqW#P1Fz6B(`j-e7Rq>A(&jgzSN@_urjyedJ6VxHYnMj$c_@``C1ZQ7LXFA zX&@sQ`${`XFEMB!aJlPBG;tQr_-s-hS`iW~EdK^RJ%``5HWvObUKCE{wOhd?AS1yziVn z_E~7?<9_B#KkDJ0Zcx~>d}}$9k=uD^8;;`oB1!w0zHB>N{FMzr>2Kks$ep{m7pZp3Hb)g zuTyeYVGCx;N^e$yWhNUaYA_u8&gFgEg^I&HT#@x0;-rri4M$5?0!e%yU?}tKUk-AQ zYw6`;nI8;ne|^Y~Ewx80Rxao2Td}idDBTOyG3?;COkWOV0j#;u!U0n#$sM0Q)uD_lTepz4z)ZMb4fC90rq8(;}*k~rpU%<5^?ZTRMNS^o6c#W;SPY0&!OH1NfxRnQ~vEEAg6GbM|NwE?)uXM4d^o16MM zN$ac2N{d1U291ct+IxJ9k)1$0rvc(dxh*YcB4hjxjIC!DM)--;Jn)ltaHu62yh{1@ zx1G;2n0cGqB{H!q5{R-RK-|jJYGMx?IuCz&RAG^8g?_H!7$Zv~pTKHT-f$*dt5#y- zURbm!=A@BKOC{B|*B=FweMVd$hXE|#C{qys;?soBEJdx zfy$PNxBnrQ(9rq;8pUw>>KGXaM5$AwaLJ-k_tZH=KjeZHIYz`zyz($XL&Z1wNt^s2 z)HB;I*Pd_3gmTj-0VMjAQ~3SlWG75f%=W}0~r$^|3j;G1{f^WiQ7ldQ{mPhrxmCNq)REMonrkhH>DDI+h#Ciap1iVrM~N=_eVv zGDJ;R{<<&ioc_XVzU7<>Mbpq^-8uCQP!Mw?rN6cYjOHU?-TMqIgZr2-Dt2CCoV`Dg zZLU;leY3Aa;fa(Zn`m|MU?CV?RR4Ai)@%HSgc1Mx3}_XqW$J=bQUqi)_{g`rfttM8 zW=)$2Uk8#_cO*i9Jw=EW#PRt69?Fe(M@G%9$=C8Zw`Gyn|4WwMw8QCJlZ*+ioqFkB#b&h*`CuwF-YS2E zz!G>QUYC6S8AOc7Y3Ez>5PjG3Yi$G(Dqhz!yZJtJk zvQ0R0i&V(nTJw3?EE=zwFi5Q%$Zw5TiNCqcm8KY&_v4zfqxt1)E1j=dRL0CYjs@2< z^I80CVWa9jQ8V6+!!J@q`Rb#HOn0T~5py+r^jAE{YJ;Z(azgGpcj(QW+FbkN0s(#T zTU{8Tn^eXIO%<x-viu;VI z=3Fp07zt+N$OAAz9Z`r&@sq4M{e?nwfj2K&_;K0=`q-AuaXYvlo}7@R=^TLrfK37p z?y}ZhGdLGUOYr2hT)QmU^i3bUu0nnTLAMk zRkZhv*^FT^lR^#V>R{EK{>P#LQ5_|r+#`n*jvE@AhDY<{6WVy2U#l$0lrwa|1`b9aX;hW!GgjG7>c(C0U-fgt!S`VbZdjknNVyKm{0cRTNIm@kbcgC^_TQ`+aaATVyqJY}qUvM0RAou0tqUc0=H9F1H7dxFD(G#y1gEo-rJh z;v}XWIX5ya^6g-F@s(WuX>kG#_mIG%3kB;&=!h^|U79X8!4_RuF@BL-LCw_X7J1rO(sE$PoARvy;A#=Ld76PWwYo&s&SlPo(GXI!EfL2`PQAZ=KD1N3+~!z?ca?r zUs77<+v6!!Q(GUy3`U>n!LMD3>C z(uALZO%GWF2OqU4gxPf0dRQg2Y_kTJr zk$Y~K;Py`;x-+wi^URbEe&3cf|KGv8l<^)f(+!j2EH9oj~} zk-?acJAtdHm+x+s4%tR?!TV7zo>1v(R%)wp?{U1y^o&Ep- literal 0 HcmV?d00001 diff --git a/packages/docusaurus-logger/package.json b/packages/docusaurus-logger/package.json index 2aca731a19c7..85b3c9e103d5 100644 --- a/packages/docusaurus-logger/package.json +++ b/packages/docusaurus-logger/package.json @@ -1,7 +1,7 @@ { "name": "@docusaurus/logger", "version": "2.0.0-beta.9", - "description": "An encapsulated logger for different levels of messages", + "description": "An encapsulated logger for semantically formatting console messages.", "main": "./lib/index.js", "repository": { "type": "git", diff --git a/packages/docusaurus-logger/src/index.ts b/packages/docusaurus-logger/src/index.ts index 2059be05c2e3..f3df5ba8e606 100644 --- a/packages/docusaurus-logger/src/index.ts +++ b/packages/docusaurus-logger/src/index.ts @@ -146,4 +146,12 @@ const logger = { success, }; +const name = 'Josh'; +const money = 100; +const items = ['Hotdogs', 'Macbooks', 'Vinyl discs']; + +logger.info`Hello %i${name}! You have %n${money} dollars. Here are the ${ + items.length > 1 ? 'items' : 'item' +} on the shelf: ${items}`; + export default logger; From c25b60e17d642e89d1a583e12e510e31a25acb80 Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 16:40:24 +0800 Subject: [PATCH 15/33] Fix docs --- packages/docusaurus-logger/README.md | 5 +++-- packages/docusaurus-logger/demo.png | Bin 24967 -> 94456 bytes packages/docusaurus-logger/src/index.ts | 10 +--------- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/packages/docusaurus-logger/README.md b/packages/docusaurus-logger/README.md index 7e02eebea2f7..c1e6b05ebbec 100644 --- a/packages/docusaurus-logger/README.md +++ b/packages/docusaurus-logger/README.md @@ -27,10 +27,11 @@ The template literal tag evaluates the template and expressions embedded. `inter ```js logger.info`Hello %i${name}! You have %n${money} dollars. Here are the ${ items.length > 1 ? 'items' : 'item' -} on the shelf: ${items}`; +} on the shelf: ${items} +To buy anything, enter %c${'buy x'} where %c${'x'} is the item's name; to quit, press %c${'Ctrl + C'}.`; ``` -An embedded expression is optionally preceded by a flag in the form `%[a-z]+` (a percentage sign followed by a few lowercase letters). The expression is first casted to a string—if it's an array, it's formatted by `` `\n- ${array.join('\n- ')}\n` `` (note it automatically gets line ends on both ends). If it's not preceded by any flag, it's printed out as-is. Otherwise, it's formatted with one of the formatters: +An embedded expression is optionally preceded by a flag in the form `%[a-z]+` (a percentage sign followed by a few lowercase letters). The expression is first casted to a string—if it's an array, it's formatted by `` `\n- ${array.join('\n- ')}\n` `` (note it automatically gets a leading line end). If it's not preceded by any flag, it's printed out as-is. Otherwise, it's formatted with one of the formatters: - `%p`: `path` - `%i`: `id` diff --git a/packages/docusaurus-logger/demo.png b/packages/docusaurus-logger/demo.png index 6cbde6f861e008f92a6fbaa07fd00c488024fe2f..f3877552104f4c8c3500610950cc0c3de15e2012 100644 GIT binary patch delta 91399 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%0DURn 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 E537KsbN~PV delta 21363 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�AuUiA?0pLV`ZTc^YBir3mPu|JsNga|sM((T)98 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 { const flag = msgs[idx].match(/%[a-z]+$/); const cleanValue = Array.isArray(value) - ? `\n- ${value.join('\n- ')}\n` + ? `\n- ${value.join('\n- ')}` : String(value); res += msgs[idx].replace(/%[a-z]+$/, ''); if (!flag) { @@ -146,12 +146,4 @@ const logger = { success, }; -const name = 'Josh'; -const money = 100; -const items = ['Hotdogs', 'Macbooks', 'Vinyl discs']; - -logger.info`Hello %i${name}! You have %n${money} dollars. Here are the ${ - items.length > 1 ? 'items' : 'item' -} on the shelf: ${items}`; - export default logger; From 46e6c9d03ce144ef9522f2044da238a1ba80dcbb Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 16:47:50 +0800 Subject: [PATCH 16/33] Migrate the rest --- packages/docusaurus-plugin-content-docs/src/cli.ts | 3 ++- packages/docusaurus-plugin-content-docs/src/lastUpdate.ts | 7 ++++--- packages/docusaurus-theme-search-algolia/package.json | 1 + packages/docusaurus-theme-search-algolia/src/index.ts | 7 ++++--- .../docusaurus-utils-validation/src/validationUtils.ts | 4 ++-- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/cli.ts b/packages/docusaurus-plugin-content-docs/src/cli.ts index 1e5c87257f44..ecf346bfb361 100644 --- a/packages/docusaurus-plugin-content-docs/src/cli.ts +++ b/packages/docusaurus-plugin-content-docs/src/cli.ts @@ -17,6 +17,7 @@ import {transformSidebarItems} from './sidebars/utils'; import type {SidebarItem, NormalizedSidebars, Sidebar} from './sidebars/types'; import {loadUnprocessedSidebars, resolveSidebarPathOption} from './sidebars'; import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; +import logger from '@docusaurus/logger'; function createVersionedSidebarFile({ siteDir, @@ -163,5 +164,5 @@ export function cliDocsVersionCommand( fs.ensureDirSync(path.dirname(versionsJSONFile)); fs.writeFileSync(versionsJSONFile, `${JSON.stringify(versions, null, 2)}\n`); - console.log(`${pluginIdLogPrefix}: version ${version} created!`); + logger.success`%i${pluginIdLogPrefix}: version %i${version} created!`; } diff --git a/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts b/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts index c36d99efb10a..5ef1be2e90a2 100644 --- a/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts +++ b/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts @@ -6,6 +6,7 @@ */ import shell from 'shelljs'; +import logger from '@docusaurus/logger'; type FileLastUpdateData = {timestamp?: number; author?: string}; @@ -36,7 +37,7 @@ export async function getFileLastUpdate( if (!shell.which('git')) { if (!showedGitRequirementError) { showedGitRequirementError = true; - console.warn('Sorry, the docs plugin last update options require Git.'); + logger.warn('Sorry, the docs plugin last update options require Git.'); } return null; @@ -51,8 +52,8 @@ export async function getFileLastUpdate( ); } return getTimestampAndAuthor(result.stdout.trim()); - } catch (error) { - console.error(error); + } catch (e) { + logger.error(e as Error); } return null; diff --git a/packages/docusaurus-theme-search-algolia/package.json b/packages/docusaurus-theme-search-algolia/package.json index f3d3ddf936b3..829105ea3573 100644 --- a/packages/docusaurus-theme-search-algolia/package.json +++ b/packages/docusaurus-theme-search-algolia/package.json @@ -22,6 +22,7 @@ "dependencies": { "@docsearch/react": "^3.0.0-alpha.39", "@docusaurus/core": "2.0.0-beta.9", + "@docusaurus/logger": "2.0.0-beta.9", "@docusaurus/theme-common": "2.0.0-beta.9", "@docusaurus/theme-translations": "2.0.0-beta.9", "@docusaurus/utils": "2.0.0-beta.9", diff --git a/packages/docusaurus-theme-search-algolia/src/index.ts b/packages/docusaurus-theme-search-algolia/src/index.ts index ad1e5a49c056..0b1be918ff4d 100644 --- a/packages/docusaurus-theme-search-algolia/src/index.ts +++ b/packages/docusaurus-theme-search-algolia/src/index.ts @@ -10,6 +10,7 @@ import fs from 'fs'; import {defaultConfig, compile} from 'eta'; import {normalizeUrl, getSwizzledComponent} from '@docusaurus/utils'; import {readDefaultCodeTranslationMessages} from '@docusaurus/theme-translations'; +import logger from '@docusaurus/logger'; import openSearchTemplate from './templates/opensearch'; import {memoize} from 'lodash'; @@ -83,9 +84,9 @@ export default function theme( favicon: favicon ? normalizeUrl([url, baseUrl, favicon]) : null, }), ); - } catch (err) { - console.error(err); - throw new Error(`Generating OpenSearch file failed: ${err}`); + } catch (e) { + logger.error('Generating OpenSearch file failed.'); + throw e; } }, diff --git a/packages/docusaurus-utils-validation/src/validationUtils.ts b/packages/docusaurus-utils-validation/src/validationUtils.ts index ae2bbe946174..bf907eee6107 100644 --- a/packages/docusaurus-utils-validation/src/validationUtils.ts +++ b/packages/docusaurus-utils-validation/src/validationUtils.ts @@ -56,7 +56,7 @@ export function normalizePluginOptions( if (error) { logValidationBugReportHint(); if (isValidationDisabledEscapeHatch) { - console.error(error); + logger.error(error); return options as T; } else { throw error; @@ -84,7 +84,7 @@ export function normalizeThemeConfig( if (error) { logValidationBugReportHint(); if (isValidationDisabledEscapeHatch) { - console.error(error); + logger.error(error); return themeConfig as T; } else { throw error; From 7b1cc11d17e03bab6101b9e35125003ef257187c Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 16:57:08 +0800 Subject: [PATCH 17/33] Fix snapshots --- .../src/__tests__/cli.test.ts | 16 +++++++++++++--- .../src/__tests__/lastUpdate.test.ts | 4 +++- .../src/sidebars/__tests__/generator.test.ts | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts index ea004a7302b1..1628acf62958 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts @@ -221,7 +221,11 @@ describe('docsVersion', () => { getVersionsFilePath(simpleSiteDir, DEFAULT_PLUGIN_ID), ); expect(versions).toEqual(['1.0.0']); - expect(consoleMock).toHaveBeenCalledWith('[docs]: version 1.0.0 created!'); + expect(consoleMock).toHaveBeenCalledWith( + expect.stringMatching( + /.*\[SUCCESS\].* .*\[docs\].*: version .*1\.0\.0.* created!.*/, + ), + ); copyMock.mockRestore(); writeMock.mockRestore(); @@ -274,7 +278,11 @@ describe('docsVersion', () => { getVersionsFilePath(versionedSiteDir, DEFAULT_PLUGIN_ID), ); expect(versions).toEqual(['2.0.0', '1.0.1', '1.0.0', 'withSlugs']); - expect(consoleMock).toHaveBeenCalledWith('[docs]: version 2.0.0 created!'); + expect(consoleMock).toHaveBeenCalledWith( + expect.stringMatching( + /.*\[SUCCESS\].* .*\[docs\].*: version .*2\.0\.0.* created!.*/, + ), + ); copyMock.mockRestore(); writeMock.mockRestore(); @@ -326,7 +334,9 @@ describe('docsVersion', () => { ); expect(versions).toEqual(['2.0.0', '1.0.0']); expect(consoleMock).toHaveBeenCalledWith( - '[community]: version 2.0.0 created!', + expect.stringMatching( + /.*\[SUCCESS\].* .*\[community\].*: version .*2.0.0.* created!.*/, + ), ); copyMock.mockRestore(); diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts index 36324d16c025..3c305100b398 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts @@ -60,7 +60,9 @@ describe('lastUpdate', () => { const lastUpdateData = await getFileLastUpdate(existingFilePath); expect(lastUpdateData).toBeNull(); expect(consoleMock).toHaveBeenLastCalledWith( - 'Sorry, the docs plugin last update options require Git.', + expect.stringMatching( + /.*\[WARNING\].* Sorry, the docs plugin last update options require Git\..*/, + ), ); consoleMock.mockRestore(); diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/generator.test.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/generator.test.ts index 3fde5b32eb4b..b5503b43c4eb 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/generator.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/generator.test.ts @@ -57,7 +57,7 @@ describe('DefaultSidebarItemsGenerator', () => { expect(sidebarSlice).toEqual([]); expect(consoleWarn).toHaveBeenCalledWith( expect.stringMatching( - /No docs found in dir .: can't auto-generate a sidebar/, + /.*\[WARNING\].* No docs found in dir .*\..*: can't auto-generate a sidebar\..*/, ), ); }); From 9e2812c935fc6788ef900b8f2041cfabad24a73e Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 17:06:56 +0800 Subject: [PATCH 18/33] tweaks --- packages/create-docusaurus/src/index.ts | 20 ++++++++++++-------- packages/docusaurus/src/commands/swizzle.ts | 3 +-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/create-docusaurus/src/index.ts b/packages/create-docusaurus/src/index.ts index e72a709b0286..f7059a6de9c5 100755 --- a/packages/create-docusaurus/src/index.ts +++ b/packages/create-docusaurus/src/index.ts @@ -131,12 +131,14 @@ export default async function init( } if (!name) { - throw new Error('A website name is required.'); + logger.error('A website name is required.'); + process.exit(1); } const dest = path.resolve(rootDir, name); if (fs.existsSync(dest)) { - throw new Error(`Directory already exists at "${dest}"!`); + logger.error`Directory already exists at %p${dest}!`; + process.exit(1); } let template = reqTemplate; @@ -198,7 +200,8 @@ export default async function init( } if (!template) { - throw new Error('Template should not be empty'); + logger.error('Template should not be empty'); + process.exit(1); } logger.info('Creating new Docusaurus project...'); @@ -209,15 +212,15 @@ export default async function init( shell.exec(`git clone --recursive ${template} ${dest}`, {silent: true}) .code !== 0 ) { - throw new Error(`Cloning Git template ${template} failed!`); + logger.error`Cloning Git template %i${template} failed!`; + process.exit(1); } } else if (templates.includes(template)) { // Docusaurus templates. if (useTS) { if (!hasTS(template)) { - throw new Error( - `Template ${template} doesn't provide the Typescript variant.`, - ); + logger.error`Template %i${template} doesn't provide the Typescript variant.`; + process.exit(1); } template = `${template}${TypeScriptTemplateSuffix}`; } @@ -236,7 +239,8 @@ export default async function init( throw err; } } else { - throw new Error('Invalid template.'); + logger.error('Invalid template.'); + process.exit(1); } // Update package.json info. diff --git a/packages/docusaurus/src/commands/swizzle.ts b/packages/docusaurus/src/commands/swizzle.ts index cecc5d9142f0..04d319a6006e 100644 --- a/packages/docusaurus/src/commands/swizzle.ts +++ b/packages/docusaurus/src/commands/swizzle.ts @@ -103,8 +103,7 @@ function themeComponents( return 'No component to swizzle.'; } - return ` -Theme components available for swizzle. + return `Theme components available for swizzle. ${logger.green(logger.bold('green =>'))} safe: lower breaking change risk ${logger.red(logger.bold('red =>'))} unsafe: higher breaking change risk From bb9bebcc9cb88d614073d764f38881e9fabc3864 Mon Sep 17 00:00:00 2001 From: Josh-Cena Date: Sun, 28 Nov 2021 17:17:01 +0800 Subject: [PATCH 19/33] Tweak --- packages/docusaurus-logger/README.md | 4 +- packages/docusaurus-logger/src/index.ts | 52 ++++++++++++------------- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/packages/docusaurus-logger/README.md b/packages/docusaurus-logger/README.md index c1e6b05ebbec..153cbc2b0b1b 100644 --- a/packages/docusaurus-logger/README.md +++ b/packages/docusaurus-logger/README.md @@ -31,7 +31,7 @@ logger.info`Hello %i${name}! You have %n${money} dollars. Here are the ${ To buy anything, enter %c${'buy x'} where %c${'x'} is the item's name; to quit, press %c${'Ctrl + C'}.`; ``` -An embedded expression is optionally preceded by a flag in the form `%[a-z]+` (a percentage sign followed by a few lowercase letters). The expression is first casted to a string—if it's an array, it's formatted by `` `\n- ${array.join('\n- ')}\n` `` (note it automatically gets a leading line end). If it's not preceded by any flag, it's printed out as-is. Otherwise, it's formatted with one of the formatters: +An embedded expression is optionally preceded by a flag in the form `%[a-z]+` (a percentage sign followed by a few lowercase letters). If it's not preceded by any flag, it's printed out as-is. Otherwise, it's formatted with one of the formatters: - `%p`: `path` - `%i`: `id` @@ -39,6 +39,6 @@ An embedded expression is optionally preceded by a flag in the form `%[a-z]+` (a - `%s`: `subdue` - `%n`: `num` -So you would see the above message printed as: +If the expression is an array, it's formatted by `` `\n- ${array.join('\n- ')}\n` `` (note it automatically gets a leading line end). Each member is formatted by itself and the bullet is not formatted. So you would see the above message printed as: ![demo](./demo.png) diff --git a/packages/docusaurus-logger/src/index.ts b/packages/docusaurus-logger/src/index.ts index 1d4e1cfa5d89..6b7d5afeee93 100644 --- a/packages/docusaurus-logger/src/index.ts +++ b/packages/docusaurus-logger/src/index.ts @@ -25,35 +25,31 @@ function interpolate( let res = ''; values.forEach((value, idx) => { const flag = msgs[idx].match(/%[a-z]+$/); - const cleanValue = Array.isArray(value) - ? `\n- ${value.join('\n- ')}` - : String(value); res += msgs[idx].replace(/%[a-z]+$/, ''); - if (!flag) { - res += cleanValue; - return; - } - switch (flag[0]) { - case '%p': - res += path(cleanValue); - break; - case '%n': - res += num(cleanValue); - break; - case '%i': - res += id(cleanValue); - break; - case '%s': - res += subdue(cleanValue); - break; - case '%c': - res += code(cleanValue); - break; - default: - throw new Error( - 'Bad Docusaurus logging message. This is likely an internal bug, please report it', - ); - } + const format = (function () { + if (!flag) { + return (a: string | number) => a; + } + switch (flag[0]) { + case '%p': + return path; + case '%n': + return num; + case '%i': + return id; + case '%s': + return subdue; + case '%c': + return code; + default: + throw new Error( + 'Bad Docusaurus logging message. This is likely an internal bug, please report it.', + ); + } + })(); + res += Array.isArray(value) + ? `\n- ${value.map((v) => format(v)).join('\n- ')}` + : format(value); }); res += msgs.slice(-1)[0]; return res; From a0113a5d96ef2cf7cd925a67e61085423cbe2448 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Mon, 29 Nov 2021 12:50:00 +0800 Subject: [PATCH 20/33] Add tests --- .../src/__tests__/index.test.ts | 73 +++++++++++++++ packages/docusaurus-logger/src/index.ts | 89 ++++++++----------- packages/docusaurus/bin/docusaurus.js | 1 - 3 files changed, 112 insertions(+), 51 deletions(-) create mode 100644 packages/docusaurus-logger/src/__tests__/index.test.ts diff --git a/packages/docusaurus-logger/src/__tests__/index.test.ts b/packages/docusaurus-logger/src/__tests__/index.test.ts new file mode 100644 index 000000000000..b2f7b9120ffd --- /dev/null +++ b/packages/docusaurus-logger/src/__tests__/index.test.ts @@ -0,0 +1,73 @@ +/** + * 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 '../index'; + +describe('formatters', () => { + test('path', () => { + expect(logger.path('hey')).toMatchInlineSnapshot(`"hey"`); + }); + test('id', () => { + expect(logger.id('hey')).toMatchInlineSnapshot(`"hey"`); + }); + test('code', () => { + expect(logger.code('hey')).toMatchInlineSnapshot(`"\`hey\`"`); + }); + test('subdue', () => { + expect(logger.subdue('hey')).toMatchInlineSnapshot(`"hey"`); + }); +}); + +describe('interpolate', () => { + test('should format text with variables & arrays', () => { + const name = 'Josh'; + const items = [1, 'hi', 'Hmmm']; + expect(logger.interpolate`Hello ${name}! Here are your goodies:${items}`) + .toMatchInlineSnapshot(` + "Hello Josh! Here are your goodies: + - 1 + - hi + - Hmmm" + `); + }); + test('should recognize valid flags', () => { + expect( + logger.interpolate`The package at %p${'packages/docusaurus'} has %n${10} files. %i${'Babel'} is exported here %s${'(as a preset)'} that you can with %c${"require.resolve('@docusaurus/core/lib/babel/preset')"}`, + ).toMatchInlineSnapshot( + `"The package at packages/docusaurus has 10 files. Babel is exported here (as a preset) that you can with \`require.resolve('@docusaurus/core/lib/babel/preset')\`"`, + ); + }); + test('should interpolate arrays with flags', () => { + expect( + logger.interpolate`The following commands are available:%c${[ + 'docusaurus start', + 'docusaurus build', + 'docusaurus deploy', + ]}`, + ).toMatchInlineSnapshot(` + "The following commands are available: + - \`docusaurus start\` + - \`docusaurus build\` + - \`docusaurus deploy\`" + `); + }); + test('should print detached flags as-is', () => { + expect( + logger.interpolate`You can use placeholders like %c ${'and it will'} be replaced with the succeeding arguments`, + ).toMatchInlineSnapshot( + `"You can use placeholders like %c and it will be replaced with the succeeding arguments"`, + ); + }); + test('should throw with bad flags', () => { + expect( + () => + logger.interpolate`I mistyped this: %a${'this code'} and I will be damned`, + ).toThrowErrorMatchingInlineSnapshot( + `"Bad Docusaurus logging message. This is likely an internal bug, please report it."`, + ); + }); +}); diff --git a/packages/docusaurus-logger/src/index.ts b/packages/docusaurus-logger/src/index.ts index 6b7d5afeee93..bc9c82441608 100644 --- a/packages/docusaurus-logger/src/index.ts +++ b/packages/docusaurus-logger/src/index.ts @@ -4,10 +4,11 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ +/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types */ import pico from 'picocolors'; -type InterpolatableValue = string | number | string[] | number[]; +type InterpolatableValue = string | number | (string | number)[]; const path: import('picocolors/types').Formatter = (msg) => pico.cyan(pico.underline(msg)); @@ -55,76 +56,64 @@ function interpolate( return res; } -function info(msg: string): void; +function info(msg: any): void; function info( msg: TemplateStringsArray, - ...values: InterpolatableValue[] + ...values: [InterpolatableValue, ...InterpolatableValue[]] ): void; -function info( - msg: TemplateStringsArray | string, - ...values: InterpolatableValue[] -): void { - if (typeof msg === 'string') { - console.info(`${pico.cyan(pico.bold('[INFO]'))} ${msg}`); - return; - } +function info(msg: any, ...values: InterpolatableValue[]): void { console.info( - `${pico.cyan(pico.bold('[INFO]'))} ${interpolate(msg, ...values)}`, + `${pico.cyan(pico.bold('[INFO]'))} ${ + values.length === 0 + ? msg + : interpolate(msg as TemplateStringsArray, ...values) + }`, ); } -function warn(msg: string): void; +function warn(msg: any): void; function warn( msg: TemplateStringsArray, - ...values: InterpolatableValue[] + ...values: [InterpolatableValue, ...InterpolatableValue[]] ): void; -function warn( - msg: TemplateStringsArray | string, - ...values: InterpolatableValue[] -): void { - if (typeof msg === 'string') { - console.warn(pico.yellow(`${pico.bold('[WARNING]')} ${msg}`)); - return; - } +function warn(msg: any, ...values: InterpolatableValue[]): void { console.warn( - pico.yellow(`${pico.bold('[WARNING]')} ${interpolate(msg, ...values)}`), + pico.yellow( + `${pico.bold('[WARNING]')} ${ + values.length === 0 + ? msg + : interpolate(msg as TemplateStringsArray, ...values) + }`, + ), ); } -function error(msg: string | Error): void; +function error(msg: any): void; function error( msg: TemplateStringsArray, - ...values: InterpolatableValue[] + ...values: [InterpolatableValue, ...InterpolatableValue[]] ): void; -function error( - msg: TemplateStringsArray | Error | string, - ...values: InterpolatableValue[] -): void { - if (msg instanceof Error) { - console.error(msg); - return; - } - if (typeof msg === 'string') { - console.error(pico.red(`${pico.bold('[ERROR]')} ${msg}`)); - return; - } +function error(msg: any, ...values: InterpolatableValue[]): void { console.error( - pico.red(`${pico.bold('[ERROR]')} ${interpolate(msg, ...values)}`), + pico.red( + `${pico.bold('[ERROR]')} ${ + values.length === 0 + ? msg + : interpolate(msg as TemplateStringsArray, ...values) + }`, + ), ); } -function success(msg: string): void; +function success(msg: any): void; function success( msg: TemplateStringsArray, - ...values: InterpolatableValue[] + ...values: [InterpolatableValue, ...InterpolatableValue[]] ): void; -function success( - msg: TemplateStringsArray | string, - ...values: InterpolatableValue[] -): void { - if (typeof msg === 'string') { - console.log(`${pico.green(pico.bold('[SUCCESS]'))} ${msg}`); - return; - } +function success(msg: any, ...values: InterpolatableValue[]): void { console.log( - `${pico.green(pico.bold('[SUCCESS]'))} ${interpolate(msg, ...values)}`, + `${pico.green(pico.bold('[SUCCESS]'))} ${ + values.length === 0 + ? msg + : interpolate(msg as TemplateStringsArray, ...values) + }`, ); } @@ -134,8 +123,8 @@ const logger = { id, code, subdue, - interpolate, num, + interpolate, info, warn, error, diff --git a/packages/docusaurus/bin/docusaurus.js b/packages/docusaurus/bin/docusaurus.js index a136851398df..2723613b5f11 100755 --- a/packages/docusaurus/bin/docusaurus.js +++ b/packages/docusaurus/bin/docusaurus.js @@ -253,7 +253,6 @@ async function run() { run(); process.on('unhandledRejection', (err) => { - // @ts-expect-error: Hmmm logger.error(err.stack); process.exit(1); }); From c52228b6fb1adcd6a195f7d94ea22840054af6df Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Mon, 29 Nov 2021 13:41:12 +0800 Subject: [PATCH 21/33] Fix test --- .../src/__tests__/lastUpdate.test.ts | 7 +++---- packages/docusaurus-plugin-content-docs/src/lastUpdate.ts | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts index 3c305100b398..53ffc3a27f5f 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts @@ -29,8 +29,7 @@ describe('lastUpdate', () => { }); test('non-existing file', async () => { - const consoleMock = jest.spyOn(console, 'error'); - consoleMock.mockImplementation(); + const consoleMock = jest.spyOn(console, 'error').mockImplementation(); const nonExistingFileName = '.nonExisting'; const nonExistingFilePath = path.join( __dirname, @@ -39,8 +38,8 @@ describe('lastUpdate', () => { ); expect(await getFileLastUpdate(nonExistingFilePath)).toBeNull(); expect(consoleMock).toHaveBeenCalledTimes(1); - expect(consoleMock.mock.calls[0][0].message).toContain( - ' with exit code 128', + expect(consoleMock).toHaveBeenLastCalledWith( + expect.stringMatching(/with exit code 128/), ); expect(await getFileLastUpdate(null)).toBeNull(); expect(await getFileLastUpdate(undefined)).toBeNull(); diff --git a/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts b/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts index 5ef1be2e90a2..c37e55bdb6f4 100644 --- a/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts +++ b/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts @@ -53,7 +53,7 @@ export async function getFileLastUpdate( } return getTimestampAndAuthor(result.stdout.trim()); } catch (e) { - logger.error(e as Error); + logger.error(e); } return null; From 446aa2cba3f9072934be52e2a67e3de0a619ec33 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Mon, 29 Nov 2021 13:49:45 +0800 Subject: [PATCH 22/33] Fix --- packages/create-docusaurus/src/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/create-docusaurus/src/index.ts b/packages/create-docusaurus/src/index.ts index f7059a6de9c5..d8140df7bd21 100755 --- a/packages/create-docusaurus/src/index.ts +++ b/packages/create-docusaurus/src/index.ts @@ -175,7 +175,7 @@ export default async function init( } return logger.red('Invalid repository URL'); }, - message: `Enter a repository URL from GitHub, Bitbucket, GitLab, or any other public repo. + message: logger.interpolate`Enter a repository URL from GitHub, Bitbucket, GitLab, or any other public repo. (e.g: %p${'https://github.com/ownerName/repoName.git'})`, }); template = repoPrompt.gitRepoUrl; @@ -189,7 +189,9 @@ export default async function init( if (fs.existsSync(fullDir)) { return true; } - return logger.red(`The path ${logger.path(fullDir)} does not exist.`); + return logger.red( + logger.interpolate`The path %p${fullDir} does not exist.`, + ); } return logger.red('Please enter a valid path.'); }, From a7a99fc0b9e8cec78931c772ef346cd7435b80f2 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Mon, 29 Nov 2021 14:09:51 +0800 Subject: [PATCH 23/33] Tweaks --- packages/docusaurus-migrate/src/index.ts | 7 ++----- .../docusaurus-utils-validation/src/validationUtils.ts | 2 +- packages/docusaurus/src/commands/deploy.ts | 6 ++---- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/docusaurus-migrate/src/index.ts b/packages/docusaurus-migrate/src/index.ts index f49d4a327599..e690233e3e62 100644 --- a/packages/docusaurus-migrate/src/index.ts +++ b/packages/docusaurus-migrate/src/index.ts @@ -194,9 +194,7 @@ export async function migrateDocusaurusProject( errorCount += 1; } if (errorCount) { - logger.warn( - `Migration from v1 to v2 failed with ${errorCount} errors: please check the log above`, - ); + logger.warn`Migration from v1 to v2 failed with %n${errorCount} errors: please check the log above`; } else { logger.success('Completed migration from v1 to v2'); } @@ -472,8 +470,7 @@ function handleVersioning( versionRegex, migrateMDFiles, ); - logger.success(`Migrated version docs and sidebar. The following doc versions have been created: -- ${loadedVersions.join('\n- ')}`); + logger.success`Migrated version docs and sidebar. The following doc versions have been created:%i${loadedVersions}`; } else { logger.warn( 'Versioned docs not found. Skipping migration for versioned docs', diff --git a/packages/docusaurus-utils-validation/src/validationUtils.ts b/packages/docusaurus-utils-validation/src/validationUtils.ts index bf907eee6107..b30c299760e1 100644 --- a/packages/docusaurus-utils-validation/src/validationUtils.ts +++ b/packages/docusaurus-utils-validation/src/validationUtils.ts @@ -114,7 +114,7 @@ export function validateFrontMatter( logger.error`The following frontmatter: ${logger.yellow(frontMatterString)} -contains invalid values for field(s): ${invalidFields}. +contains invalid values for field(s): ${logger.yellow(invalidFields)}. ${errorDetails.map(({message}) => message)} `; throw error; diff --git a/packages/docusaurus/src/commands/deploy.ts b/packages/docusaurus/src/commands/deploy.ts index dca6f1bbb1f0..26fb7fc5ae49 100644 --- a/packages/docusaurus/src/commands/deploy.ts +++ b/packages/docusaurus/src/commands/deploy.ts @@ -25,12 +25,10 @@ function obfuscateGitPass(str: string) { function shellExecLog(cmd: string) { try { const result = shell.exec(cmd); - logger.info( - `${obfuscateGitPass(cmd)} ${logger.subdue(`(code: ${result.code})`)}`, - ); + logger.info`%c${obfuscateGitPass(cmd)} %s${`code: ${result.code}`}`; return result; } catch (e) { - logger.error(`${obfuscateGitPass(cmd)}`); + logger.error`%c${obfuscateGitPass(cmd)}`; throw e; } } From e7570745ce30df992578872c810d9bec28bb23d1 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 3 Dec 2021 21:09:44 +0800 Subject: [PATCH 24/33] More friendly syntax --- packages/create-docusaurus/bin/index.js | 4 +-- packages/create-docusaurus/src/index.ts | 32 +++++++++---------- packages/docusaurus-logger/README.md | 14 ++++---- .../src/__tests__/index.test.ts | 10 +++--- packages/docusaurus-logger/src/index.ts | 14 ++++---- packages/docusaurus-migrate/bin/index.js | 2 +- packages/docusaurus-migrate/src/index.ts | 8 ++--- .../src/collectRedirects.ts | 4 +-- .../src/authors.ts | 2 +- .../src/blogUtils.ts | 4 +-- .../docusaurus-plugin-content-docs/src/cli.ts | 2 +- .../src/docs.ts | 2 +- .../src/index.ts | 4 +-- .../src/options.ts | 4 +-- .../src/sidebars/generator.ts | 4 +-- .../src/validationUtils.ts | 2 +- packages/docusaurus/bin/beforeCli.js | 2 +- packages/docusaurus/bin/docusaurus.js | 2 +- packages/docusaurus/src/choosePort.ts | 2 +- packages/docusaurus/src/client/serverEntry.js | 8 ++--- packages/docusaurus/src/commands/build.ts | 8 ++--- packages/docusaurus/src/commands/clear.ts | 4 +-- packages/docusaurus/src/commands/deploy.ts | 12 +++---- packages/docusaurus/src/commands/serve.ts | 2 +- packages/docusaurus/src/commands/start.ts | 4 +-- packages/docusaurus/src/commands/swizzle.ts | 24 +++++++------- .../src/commands/writeHeadingIds.ts | 6 ++-- .../server/__tests__/configValidation.test.ts | 2 +- packages/docusaurus/src/server/i18n.ts | 2 +- packages/docusaurus/src/server/index.ts | 4 +-- .../docusaurus/src/server/plugins/index.ts | 2 +- .../src/server/translations/translations.ts | 8 ++--- .../translations/translationsExtractor.ts | 2 +- packages/docusaurus/src/webpack/utils.ts | 4 +-- 34 files changed, 105 insertions(+), 105 deletions(-) diff --git a/packages/create-docusaurus/bin/index.js b/packages/create-docusaurus/bin/index.js index 64ab5e00847d..bd608096f061 100755 --- a/packages/create-docusaurus/bin/index.js +++ b/packages/create-docusaurus/bin/index.js @@ -17,7 +17,7 @@ const requiredVersion = require('../package.json').engines.node; if (!semver.satisfies(process.version, requiredVersion)) { logger.error('Minimum Node.js version not met :('); - logger.info`You are using Node.js %n${process.version}, Requirement: Node.js %n${requiredVersion}.`; + logger.info`You are using Node.js number=${process.version}, Requirement: Node.js number=${requiredVersion}.`; process.exit(1); } @@ -56,7 +56,7 @@ program program.arguments('').action((cmd) => { program.outputHelp(); - logger.error`Unknown command %c${cmd}.`; + logger.error`Unknown command code=${cmd}.`; }); program.parse(process.argv); diff --git a/packages/create-docusaurus/src/index.ts b/packages/create-docusaurus/src/index.ts index d8140df7bd21..8b3c48956638 100755 --- a/packages/create-docusaurus/src/index.ts +++ b/packages/create-docusaurus/src/index.ts @@ -137,7 +137,7 @@ export default async function init( const dest = path.resolve(rootDir, name); if (fs.existsSync(dest)) { - logger.error`Directory already exists at %p${dest}!`; + logger.error`Directory already exists at path=${dest}!`; process.exit(1); } @@ -176,7 +176,7 @@ export default async function init( return logger.red('Invalid repository URL'); }, message: logger.interpolate`Enter a repository URL from GitHub, Bitbucket, GitLab, or any other public repo. -(e.g: %p${'https://github.com/ownerName/repoName.git'})`, +(e.g: path=${'https://github.com/ownerName/repoName.git'})`, }); template = repoPrompt.gitRepoUrl; } else if (template === 'Local template') { @@ -190,7 +190,7 @@ export default async function init( return true; } return logger.red( - logger.interpolate`The path %p${fullDir} does not exist.`, + logger.interpolate`path=${fullDir} does not exist.`, ); } return logger.red('Please enter a valid path.'); @@ -209,19 +209,19 @@ export default async function init( logger.info('Creating new Docusaurus project...'); if (isValidGitRepoUrl(template)) { - logger.info`Cloning Git template %p${template}...`; + logger.info`Cloning Git template path=${template}...`; if ( shell.exec(`git clone --recursive ${template} ${dest}`, {silent: true}) .code !== 0 ) { - logger.error`Cloning Git template %i${template} failed!`; + logger.error`Cloning Git template name=${template} failed!`; process.exit(1); } } else if (templates.includes(template)) { // Docusaurus templates. if (useTS) { if (!hasTS(template)) { - logger.error`Template %i${template} doesn't provide the Typescript variant.`; + logger.error`Template name=${template} doesn't provide the Typescript variant.`; process.exit(1); } template = `${template}${TypeScriptTemplateSuffix}`; @@ -229,7 +229,7 @@ export default async function init( try { await copyTemplate(templatesDir, template, dest); } catch (err) { - logger.error`Copying Docusaurus template %i${template} failed!`; + logger.error`Copying Docusaurus template name=${template} failed!`; throw err; } } else if (fs.existsSync(path.resolve(process.cwd(), template))) { @@ -237,7 +237,7 @@ export default async function init( try { await fs.copy(templateDir, dest); } catch (err) { - logger.error`Copying local template %p${template} failed!`; + logger.error`Copying local template path=${template} failed!`; throw err; } } else { @@ -270,7 +270,7 @@ export default async function init( const pkgManager = useYarn ? 'yarn' : 'npm'; if (!cliOptions.skipInstall) { - logger.info`Installing dependencies with %i${pkgManager}...`; + logger.info`Installing dependencies with name=${pkgManager}...`; try { // Use force coloring the output, since the command is invoked by shelljs, which is not the interactive shell @@ -295,25 +295,25 @@ export default async function init( ? name : path.relative(process.cwd(), name); - logger.info`Successfully created %p${cdpath}. + logger.info`Successfully created path=${cdpath}. Inside that directory, you can run several commands: - %c${`${pkgManager} start`} + code=${`${pkgManager} start`} Starts the development server. - %c${`${pkgManager} ${useYarn ? '' : 'run '}build`} + code=${`${pkgManager} ${useYarn ? '' : 'run '}build`} Bundles your website into static files for production. - %c${`${pkgManager} ${useYarn ? '' : 'run '}serve`} + code=${`${pkgManager} ${useYarn ? '' : 'run '}serve`} Serves the built website locally. - %c${`${pkgManager} deploy`} + code=${`${pkgManager} deploy`} Publishes the website to GitHub pages. We recommend that you begin by typing: - %c${`cd ${cdpath}`} - %c${`${pkgManager} start`} + code=${`cd ${cdpath}`} + code=${`${pkgManager} start`} Happy building awesome websites! `; diff --git a/packages/docusaurus-logger/README.md b/packages/docusaurus-logger/README.md index 153cbc2b0b1b..43d920f41e09 100644 --- a/packages/docusaurus-logger/README.md +++ b/packages/docusaurus-logger/README.md @@ -25,19 +25,19 @@ It exports a single object as default export: `logger`. `logger` has the followi The template literal tag evaluates the template and expressions embedded. `interpolate` returns a new string, while other logging functions prints it. Below is a typical usage: ```js -logger.info`Hello %i${name}! You have %n${money} dollars. Here are the ${ +logger.info`Hello name=${name}! You have number=${money} dollars. Here are the ${ items.length > 1 ? 'items' : 'item' } on the shelf: ${items} -To buy anything, enter %c${'buy x'} where %c${'x'} is the item's name; to quit, press %c${'Ctrl + C'}.`; +To buy anything, enter code=${'buy x'} where code=${'x'} is the item's name; to quit, press code=${'Ctrl + C'}.`; ``` An embedded expression is optionally preceded by a flag in the form `%[a-z]+` (a percentage sign followed by a few lowercase letters). If it's not preceded by any flag, it's printed out as-is. Otherwise, it's formatted with one of the formatters: -- `%p`: `path` -- `%i`: `id` -- `%c`: `code` -- `%s`: `subdue` -- `%n`: `num` +- `path=`: `path` +- `name=`: `id` +- `code=`: `code` +- `subdue=`: `subdue` +- `number=`: `num` If the expression is an array, it's formatted by `` `\n- ${array.join('\n- ')}\n` `` (note it automatically gets a leading line end). Each member is formatted by itself and the bullet is not formatted. So you would see the above message printed as: diff --git a/packages/docusaurus-logger/src/__tests__/index.test.ts b/packages/docusaurus-logger/src/__tests__/index.test.ts index b2f7b9120ffd..7873405dcae1 100644 --- a/packages/docusaurus-logger/src/__tests__/index.test.ts +++ b/packages/docusaurus-logger/src/__tests__/index.test.ts @@ -36,14 +36,14 @@ describe('interpolate', () => { }); test('should recognize valid flags', () => { expect( - logger.interpolate`The package at %p${'packages/docusaurus'} has %n${10} files. %i${'Babel'} is exported here %s${'(as a preset)'} that you can with %c${"require.resolve('@docusaurus/core/lib/babel/preset')"}`, + logger.interpolate`The package at path=${'packages/docusaurus'} has number=${10} files. name=${'Babel'} is exported here subdue=${'(as a preset)'} that you can with code=${"require.resolve('@docusaurus/core/lib/babel/preset')"}`, ).toMatchInlineSnapshot( `"The package at packages/docusaurus has 10 files. Babel is exported here (as a preset) that you can with \`require.resolve('@docusaurus/core/lib/babel/preset')\`"`, ); }); test('should interpolate arrays with flags', () => { expect( - logger.interpolate`The following commands are available:%c${[ + logger.interpolate`The following commands are available:code=${[ 'docusaurus start', 'docusaurus build', 'docusaurus deploy', @@ -57,15 +57,15 @@ describe('interpolate', () => { }); test('should print detached flags as-is', () => { expect( - logger.interpolate`You can use placeholders like %c ${'and it will'} be replaced with the succeeding arguments`, + logger.interpolate`You can use placeholders like code= ${'and it will'} be replaced with the succeeding arguments`, ).toMatchInlineSnapshot( - `"You can use placeholders like %c and it will be replaced with the succeeding arguments"`, + `"You can use placeholders like code= and it will be replaced with the succeeding arguments"`, ); }); test('should throw with bad flags', () => { expect( () => - logger.interpolate`I mistyped this: %a${'this code'} and I will be damned`, + logger.interpolate`I mistyped this: cde=${'this code'} and I will be damned`, ).toThrowErrorMatchingInlineSnapshot( `"Bad Docusaurus logging message. This is likely an internal bug, please report it."`, ); diff --git a/packages/docusaurus-logger/src/index.ts b/packages/docusaurus-logger/src/index.ts index bc9c82441608..3d030f726e91 100644 --- a/packages/docusaurus-logger/src/index.ts +++ b/packages/docusaurus-logger/src/index.ts @@ -25,22 +25,22 @@ function interpolate( ): string { let res = ''; values.forEach((value, idx) => { - const flag = msgs[idx].match(/%[a-z]+$/); - res += msgs[idx].replace(/%[a-z]+$/, ''); + const flag = msgs[idx].match(/[a-z]+=$/); + res += msgs[idx].replace(/[a-z]+=$/, ''); const format = (function () { if (!flag) { return (a: string | number) => a; } switch (flag[0]) { - case '%p': + case 'path=': return path; - case '%n': + case 'number=': return num; - case '%i': + case 'name=': return id; - case '%s': + case 'subdue=': return subdue; - case '%c': + case 'code=': return code; default: throw new Error( diff --git a/packages/docusaurus-migrate/bin/index.js b/packages/docusaurus-migrate/bin/index.js index 477a5d8453c4..2b79c4d52116 100755 --- a/packages/docusaurus-migrate/bin/index.js +++ b/packages/docusaurus-migrate/bin/index.js @@ -27,7 +27,7 @@ function wrapCommand(fn) { if (!semver.satisfies(process.version, requiredVersion)) { logger.error('Minimum Node.js version not met :('); - logger.info`You are using Node.js %n${process.version}, Requirement: Node.js %n${requiredVersion}.`; + logger.info`You are using Node.js number=${process.version}, Requirement: Node.js number=${requiredVersion}.`; process.exit(1); } diff --git a/packages/docusaurus-migrate/src/index.ts b/packages/docusaurus-migrate/src/index.ts index e690233e3e62..550f955c7dfe 100644 --- a/packages/docusaurus-migrate/src/index.ts +++ b/packages/docusaurus-migrate/src/index.ts @@ -194,7 +194,7 @@ export async function migrateDocusaurusProject( errorCount += 1; } if (errorCount) { - logger.warn`Migration from v1 to v2 failed with %n${errorCount} errors: please check the log above`; + logger.warn`Migration from v1 to v2 failed with number=${errorCount} errors: please check the log above`; } else { logger.success('Completed migration from v1 to v2'); } @@ -256,7 +256,7 @@ export function createConfigFile({ customConfigFields[key] = value; } }); - logger.info`Following Fields from %p${'siteConfig.js'} will be added to %p${'docusaurus.config.js'} in %c${'customFields'}: ${Object.keys( + logger.info`Following Fields from path=${'siteConfig.js'} will be added to path=${'docusaurus.config.js'} in code=${'customFields'}: ${Object.keys( customConfigFields, )}`; @@ -470,7 +470,7 @@ function handleVersioning( versionRegex, migrateMDFiles, ); - logger.success`Migrated version docs and sidebar. The following doc versions have been created:%i${loadedVersions}`; + logger.success`Migrated version docs and sidebar. The following doc versions have been created:name=${loadedVersions}`; } else { logger.warn( 'Versioned docs not found. Skipping migration for versioned docs', @@ -759,5 +759,5 @@ export async function migrateMDToMDX( sanitizedFileContent(String(fs.readFileSync(file)), true), ); }); - logger.success`Successfully migrated %p${siteDir} to %p${newDir}`; + logger.success`Successfully migrated path=${siteDir} to path=${newDir}`; } diff --git a/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts b/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts index e5e80199b935..e650705d51c3 100644 --- a/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts +++ b/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts @@ -99,7 +99,7 @@ function filterUnwantedRedirects( Object.entries(groupBy(redirects, (redirect) => redirect.from)).forEach( ([from, groupedFromRedirects]) => { if (groupedFromRedirects.length > 1) { - logger.error`%i${'@docusaurus/plugin-client-redirects'}: multiple redirects are created with the same "from" pathname: %p${from} + logger.error`name=${'@docusaurus/plugin-client-redirects'}: multiple redirects are created with the same "from" pathname: path=${from} It is not possible to redirect the same pathname to multiple destinations: ${groupedFromRedirects.map( (r) => JSON.stringify(r), )}`; @@ -113,7 +113,7 @@ It is not possible to redirect the same pathname to multiple destinations: ${gro (redirect) => pluginContext.relativeRoutesPaths.includes(redirect.from), ); if (redirectsOverridingExistingPath.length > 0) { - logger.error`%i${'@docusaurus/plugin-client-redirects'}: some redirects would override existing paths, and will be ignored: ${redirectsOverridingExistingPath.map( + logger.error`name=${'@docusaurus/plugin-client-redirects'}: some redirects would override existing paths, and will be ignored: ${redirectsOverridingExistingPath.map( (r) => JSON.stringify(r), )}`; } diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index 0e558d6c020c..dddd98f288e3 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -88,7 +88,7 @@ export async function getAuthorsMap( return await readAuthorsMapFile(filePath); } catch (e) { // TODO replace later by error cause, see https://v8.dev/features/error-cause - logger.error`Couldn't read blog authors map at path %p${filePath}`; + logger.error`Couldn't read blog authors map at path=${filePath}`; throw e; } } diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index 9dbb76a3a80b..9769a1ad62cc 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -151,7 +151,7 @@ async function processBlogSourceFile( } if (frontMatter.id) { - logger.warn`%i${'id'} header option is deprecated in %p${blogSourceRelative} file. Please use %i${'slug'} option instead.`; + logger.warn`name=${'id'} header option is deprecated in path=${blogSourceRelative} file. Please use name=${'slug'} option instead.`; } const parsedBlogFileName = parseBlogFileName(blogSourceRelative); @@ -272,7 +272,7 @@ export async function generateBlogPosts( authorsMap, ); } catch (e) { - logger.error`Processing of blog source file failed for path %p${blogSourceFile}.`; + logger.error`Processing of blog source file failed for path path=${blogSourceFile}.`; throw e; } }), diff --git a/packages/docusaurus-plugin-content-docs/src/cli.ts b/packages/docusaurus-plugin-content-docs/src/cli.ts index ecf346bfb361..a5848064cd48 100644 --- a/packages/docusaurus-plugin-content-docs/src/cli.ts +++ b/packages/docusaurus-plugin-content-docs/src/cli.ts @@ -164,5 +164,5 @@ export function cliDocsVersionCommand( fs.ensureDirSync(path.dirname(versionsJSONFile)); fs.writeFileSync(versionsJSONFile, `${JSON.stringify(versions, null, 2)}\n`); - logger.success`%i${pluginIdLogPrefix}: version %i${version} created!`; + logger.success`name=${pluginIdLogPrefix}: version name=${version} created!`; } diff --git a/packages/docusaurus-plugin-content-docs/src/docs.ts b/packages/docusaurus-plugin-content-docs/src/docs.ts index b4642e3280de..4c63e5a58908 100644 --- a/packages/docusaurus-plugin-content-docs/src/docs.ts +++ b/packages/docusaurus-plugin-content-docs/src/docs.ts @@ -282,7 +282,7 @@ export function processDocMetadata(args: { try { return doProcessDocMetadata(args); } catch (e) { - logger.error`Can't process doc metadata for doc at path %p${args.docFile.filePath} in version %i${args.versionMetadata.versionName}`; + logger.error`Can't process doc metadata for doc at path path=${args.docFile.filePath} in version name=${args.versionMetadata.versionName}`; throw e; } } diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 632167252239..44028d7ba459 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -186,7 +186,7 @@ export default function pluginContentDocs( try { return await doLoadVersion(versionMetadata); } catch (e) { - logger.error`Loading of version failed for version %i${versionMetadata.versionName}`; + logger.error`Loading of version failed for version name=${versionMetadata.versionName}`; throw e; } } @@ -325,7 +325,7 @@ export default function pluginContentDocs( try { return await doCreateVersionRoutes(loadedVersion); } catch (e) { - logger.error`Can't create version routes for version %i${loadedVersion.versionName}`; + logger.error`Can't create version routes for version name=${loadedVersion.versionName}`; throw e; } } diff --git a/packages/docusaurus-plugin-content-docs/src/options.ts b/packages/docusaurus-plugin-content-docs/src/options.ts index 3e5030884fb6..0fa4c8ecef90 100644 --- a/packages/docusaurus-plugin-content-docs/src/options.ts +++ b/packages/docusaurus-plugin-content-docs/src/options.ts @@ -149,7 +149,7 @@ export function validateOptions({ }; } if (options.sidebarCollapsed) { - logger.warn`The docs plugin config is inconsistent. It does not make sense to use %c${'sidebarCollapsible: false'} and %c${'sidebarCollapsed: true'} at the same time. %c${'sidebarCollapsed: true'} will be ignored.`; + logger.warn`The docs plugin config is inconsistent. It does not make sense to use code=${'sidebarCollapsible: false'} and code=${'sidebarCollapsed: true'} at the same time. code=${'sidebarCollapsed: true'} will be ignored.`; options = { ...options, sidebarCollapsed: false, @@ -160,7 +160,7 @@ export function validateOptions({ // TODO remove homePageId before end of 2020 // "slug: /" is better because the home doc can be different across versions if (options.homePageId) { - logger.warn`The docs plugin option %c${`homePageId: ${options.homePageId}`} is deprecated. To make a doc the "home", prefer frontmatter: %c${'slug: /'}`; + logger.warn`The docs plugin option code=${`homePageId: ${options.homePageId}`} is deprecated. To make a doc the "home", prefer frontmatter: code=${'slug: /'}`; } const normalizedOptions = validate(OptionsSchema, options); diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts index c739641096cd..efeddd998d6e 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts @@ -71,7 +71,7 @@ async function readCategoryMetadataFile( try { return Joi.attempt(unsafeContent, CategoryMetadataFileSchema); } catch (e) { - logger.error`The docs sidebar category metadata file %p${filePath} looks invalid!`; + logger.error`The docs sidebar category metadata file path=${filePath} looks invalid!`; throw e; } } @@ -114,7 +114,7 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({ const docs = allDocs.filter(isInAutogeneratedDir); if (docs.length === 0) { - logger.warn`No docs found in dir %p${autogenDir}: can't auto-generate a sidebar.`; + logger.warn`No docs found in path=${autogenDir}: can't auto-generate a sidebar.`; } return docs; } diff --git a/packages/docusaurus-utils-validation/src/validationUtils.ts b/packages/docusaurus-utils-validation/src/validationUtils.ts index b30c299760e1..0666a3a7a9da 100644 --- a/packages/docusaurus-utils-validation/src/validationUtils.ts +++ b/packages/docusaurus-utils-validation/src/validationUtils.ts @@ -19,7 +19,7 @@ export const isValidationDisabledEscapeHatch = process.env.DISABLE_DOCUSAURUS_VALIDATION === 'true'; if (isValidationDisabledEscapeHatch) { - logger.error`You should avoid using %c${'DISABLE_DOCUSAURUS_VALIDATION'} escape hatch, this will be removed.`; + logger.error`You should avoid using code=${'DISABLE_DOCUSAURUS_VALIDATION'} escape hatch, this will be removed.`; } export const logValidationBugReportHint = (): void => { diff --git a/packages/docusaurus/bin/beforeCli.js b/packages/docusaurus/bin/beforeCli.js index 6ef2af7915e8..4c0cf280551d 100644 --- a/packages/docusaurus/bin/beforeCli.js +++ b/packages/docusaurus/bin/beforeCli.js @@ -119,6 +119,6 @@ ${logger.code(upgradeCommand)}`, // 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 %n${process.version}, Requirement: Node.js %n${requiredVersion}.`; + logger.info`You are using Node.js number=${process.version}, Requirement: Node.js number=${requiredVersion}.`; process.exit(1); } diff --git a/packages/docusaurus/bin/docusaurus.js b/packages/docusaurus/bin/docusaurus.js index 2723613b5f11..ffe7bf5837e5 100755 --- a/packages/docusaurus/bin/docusaurus.js +++ b/packages/docusaurus/bin/docusaurus.js @@ -221,7 +221,7 @@ cli cli.arguments('').action((cmd) => { cli.outputHelp(); - logger.error` Unknown command %i${cmd}.`; + logger.error` Unknown command name=${cmd}.`; }); function isInternalCommand(command) { diff --git a/packages/docusaurus/src/choosePort.ts b/packages/docusaurus/src/choosePort.ts index 8a9a21b2db2d..7507ede31e26 100644 --- a/packages/docusaurus/src/choosePort.ts +++ b/packages/docusaurus/src/choosePort.ts @@ -68,7 +68,7 @@ function getProcessForPort(port: number): string | null { const processId = getProcessIdOnPort(port); const directory = getDirectoryOfProcessById(processId); const command = getProcessCommand(processId); - return logger.interpolate`%c${command} %s${`(pid ${processId})`} in %p${directory}`; + return logger.interpolate`code=${command} subdue=${`(pid ${processId})`} in path=${directory}`; } catch (e) { return null; } diff --git a/packages/docusaurus/src/client/serverEntry.js b/packages/docusaurus/src/client/serverEntry.js index 4da543c05215..3f2d893323a3 100644 --- a/packages/docusaurus/src/client/serverEntry.js +++ b/packages/docusaurus/src/client/serverEntry.js @@ -45,7 +45,7 @@ export default async function render(locals) { try { return await doRender(locals); } catch (e) { - logger.error`Docusaurus Node/SSR could not render static page with path %p${locals.path} because of following error: + logger.error`Docusaurus Node/SSR could not render static page with path path=${locals.path} because of following error: ${e.stack}`; const isNotDefinedErrorRegex = @@ -53,8 +53,8 @@ ${e.stack}`; if (isNotDefinedErrorRegex.test(e.message)) { logger.info`It looks like you are using code that should run on the client-side only. -To get around it, try using %c${''} (%p${'https://docusaurus.io/docs/docusaurus-core/#browseronly'}) or %c${'ExecutionEnvironment'} (%p${'https://docusaurus.io/docs/docusaurus-core/#executionenvironment'}). -It might also require to wrap your client code in %c${'useEffect'} hook and/or import a third-party library dynamically (if any).`; +To get around it, try using code=${''} (path=${'https://docusaurus.io/docs/docusaurus-core/#browseronly'}) or code=${'ExecutionEnvironment'} (path=${'https://docusaurus.io/docs/docusaurus-core/#executionenvironment'}). +It might also require to wrap your client code in code=${'useEffect'} hook and/or import a third-party library dynamically (if any).`; } throw new Error('Server-side rendering fails due to the error above.'); @@ -139,7 +139,7 @@ async function doRender(locals) { minifyJS: true, }); } catch (e) { - logger.error`Minification page with path %p${locals.path} failed because of following error: + logger.error`Minification of page path=${locals.path} failed because of following error: ${e.stack}`; throw e; } diff --git a/packages/docusaurus/src/commands/build.ts b/packages/docusaurus/src/commands/build.ts index 502cb1ad2006..6604975f5e04 100644 --- a/packages/docusaurus/src/commands/build.ts +++ b/packages/docusaurus/src/commands/build.ts @@ -55,7 +55,7 @@ export default async function build( isLastLocale, }); } catch (e) { - logger.error`Unable to build website for locale %i${locale}.`; + logger.error`Unable to build website for locale name=${locale}.`; throw e; } } @@ -106,7 +106,7 @@ async function buildLocale({ }): Promise { process.env.BABEL_ENV = 'production'; process.env.NODE_ENV = 'production'; - logger.info`%i${`[${locale}]`} Creating an optimized production build...`; + logger.info`name=${`[${locale}]`} Creating an optimized production build...`; const props: Props = await load(siteDir, { customOutDir: cliOptions.outDir, @@ -230,13 +230,13 @@ async function buildLocale({ baseUrl, }); - logger.success`Generated static files in %p${path.relative( + logger.success`Generated static files in path=${path.relative( process.cwd(), outDir, )}.`; if (isLastLocale) { - logger.info`Use %c${'npm run serve'} command to test your build locally.`; + logger.info`Use code=${'npm run serve'} command to test your build locally.`; } if (forceTerminate && isLastLocale && !cliOptions.bundleAnalyzer) { diff --git a/packages/docusaurus/src/commands/clear.ts b/packages/docusaurus/src/commands/clear.ts index 0426d8b0bf4c..7c54554c8ec5 100644 --- a/packages/docusaurus/src/commands/clear.ts +++ b/packages/docusaurus/src/commands/clear.ts @@ -13,9 +13,9 @@ import {DEFAULT_BUILD_DIR_NAME, GENERATED_FILES_DIR_NAME} from '../constants'; async function removePath(fsPath: string) { try { fs.remove(path.join(fsPath)); - logger.success`Removed the %p${fsPath} directory.`; + logger.success`Removed the path=${fsPath} directory.`; } catch (e) { - logger.error`Could not remove %p${fsPath} directory. + logger.error`Could not remove path=${fsPath} directory. ${e as string}`; } } diff --git a/packages/docusaurus/src/commands/deploy.ts b/packages/docusaurus/src/commands/deploy.ts index 26fb7fc5ae49..70c1f5ecfdf7 100644 --- a/packages/docusaurus/src/commands/deploy.ts +++ b/packages/docusaurus/src/commands/deploy.ts @@ -25,10 +25,10 @@ function obfuscateGitPass(str: string) { function shellExecLog(cmd: string) { try { const result = shell.exec(cmd); - logger.info`%c${obfuscateGitPass(cmd)} %s${`code: ${result.code}`}`; + logger.info`code=${obfuscateGitPass(cmd)} subdue=${`code: ${result.code}`}`; return result; } catch (e) { - logger.error`%c${obfuscateGitPass(cmd)}`; + logger.error`code=${obfuscateGitPass(cmd)}`; throw e; } } @@ -127,7 +127,7 @@ This behavior can have SEO impacts and create relative link issues. `Missing project organization name. Did you forget to define "organizationName" in ${siteConfigPath}? You may also export it via the ORGANIZATION_NAME environment variable.`, ); } - logger.info`organizationName: %i${organizationName}`; + logger.info`organizationName: name=${organizationName}`; const projectName = process.env.PROJECT_NAME || @@ -138,7 +138,7 @@ This behavior can have SEO impacts and create relative link issues. `Missing project name. Did you forget to define "projectName" in ${siteConfigPath}? You may also export it via the PROJECT_NAME environment variable.`, ); } - logger.info`projectName: %i${projectName}`; + logger.info`projectName: name=${projectName}`; // We never deploy on pull request. const isPullRequest = @@ -165,7 +165,7 @@ You can also set the deploymentBranch property in docusaurus.config.js .`); const deploymentBranch = process.env.DEPLOYMENT_BRANCH || siteConfig.deploymentBranch || 'gh-pages'; - logger.info`deploymentBranch: %i${deploymentBranch}`; + logger.info`deploymentBranch: name=${deploymentBranch}`; const githubHost = process.env.GITHUB_HOST || siteConfig.githubHost || 'github.com'; @@ -191,7 +191,7 @@ You can also set the deploymentBranch property in docusaurus.config.js .`); ); } - logger.info`Remote repo URL: %i${obfuscateGitPass(deploymentRepoURL)}`; + logger.info`Remote repo URL: name=${obfuscateGitPass(deploymentRepoURL)}`; // Check if this is a cross-repo publish. const crossRepoPublish = !sourceRepoUrl.endsWith( diff --git a/packages/docusaurus/src/commands/serve.ts b/packages/docusaurus/src/commands/serve.ts index 420b83d45e9c..f366c4a67589 100644 --- a/packages/docusaurus/src/commands/serve.ts +++ b/packages/docusaurus/src/commands/serve.ts @@ -70,7 +70,7 @@ export default async function serve( }); }); - logger.success`Serving %p${cliOptions.dir} directory at %p${ + logger.success`Serving path=${cliOptions.dir} directory at path=${ servingUrl + baseUrl }.`; server.listen(port); diff --git a/packages/docusaurus/src/commands/start.ts b/packages/docusaurus/src/commands/start.ts index d6a9a0bc3991..6d05abdcbaff 100644 --- a/packages/docusaurus/src/commands/start.ts +++ b/packages/docusaurus/src/commands/start.ts @@ -60,7 +60,7 @@ export default async function start( const urls = prepareUrls(protocol, host, port); const openUrl = normalizeUrl([urls.localUrlForBrowser, baseUrl]); - logger.success`Docusaurus website is running at %p${openUrl}.`; + logger.success`Docusaurus website is running at path=${openUrl}.`; // Reload files processing. const reload = debounce(() => { @@ -68,7 +68,7 @@ export default async function start( .then(({baseUrl: newBaseUrl}) => { const newOpenUrl = normalizeUrl([urls.localUrlForBrowser, newBaseUrl]); if (newOpenUrl !== openUrl) { - logger.success`Docusaurus website is running at %p${newOpenUrl}.`; + logger.success`Docusaurus website is running at path=${newOpenUrl}.`; } }) .catch((err) => { diff --git a/packages/docusaurus/src/commands/swizzle.ts b/packages/docusaurus/src/commands/swizzle.ts index 04d319a6006e..5d8f1763a776 100644 --- a/packages/docusaurus/src/commands/swizzle.ts +++ b/packages/docusaurus/src/commands/swizzle.ts @@ -160,7 +160,7 @@ export default async function swizzle( ); if (!themeName) { - logger.info`Themes available for swizzle: %i${themeNames}`; + logger.info`Themes available for swizzle: name=${themeNames}`; return; } @@ -174,9 +174,9 @@ export default async function swizzle( suggestion = name; } }); - logger.error`Theme %i${themeName} not found. ${ + logger.error`Theme name=${themeName} not found. ${ suggestion - ? logger.interpolate`Did you mean %i${suggestion}?` + ? logger.interpolate`Did you mean name=${suggestion}?` : logger.interpolate`Themes available for swizzle: ${themeNames}` }`; process.exit(1); @@ -217,8 +217,8 @@ export default async function swizzle( if (!themePath) { logger.warn( typescript - ? logger.interpolate`%i${themeName} does not provide TypeScript theme code via ${'getTypeScriptThemePath()'}.` - : logger.interpolate`%i${themeName} does not provide any theme code.`, + ? logger.interpolate`name=${themeName} does not provide TypeScript theme code via ${'getTypeScriptThemePath()'}.` + : logger.interpolate`name=${themeName} does not provide any theme code.`, ); process.exit(1); } @@ -251,8 +251,8 @@ export default async function swizzle( if (mostSuitableMatch !== componentName) { mostSuitableComponent = mostSuitableMatch; - logger.error`Component %i${componentName} doesn't exist.`; - logger.info`%i${mostSuitableComponent} is swizzled instead of %i${componentName}.`; + logger.error`Component name=${componentName} doesn't exist.`; + logger.info`name=${mostSuitableComponent} is swizzled instead of name=${componentName}.`; } } @@ -274,9 +274,9 @@ export default async function swizzle( suggestion = name; } }); - logger.error`Component %i${mostSuitableComponent} not found. ${ + logger.error`Component name=${mostSuitableComponent} not found. ${ suggestion - ? logger.interpolate`Did you mean %i${suggestion} ?` + ? logger.interpolate`Did you mean name=${suggestion} ?` : themeComponents(themePath, pluginModule) }`; process.exit(1); @@ -284,13 +284,13 @@ export default async function swizzle( } if (!components.includes(mostSuitableComponent) && !danger) { - logger.error`%i${mostSuitableComponent} is an internal component and has a higher breaking change probability. If you want to swizzle it, use the %c${'--danger'} flag.`; + logger.error`name=${mostSuitableComponent} is an internal component and has a higher breaking change probability. If you want to swizzle it, use the code=${'--danger'} flag.`; process.exit(1); } await fs.copy(fromPath, toPath); - logger.success`Copied %c${ + logger.success`Copied code=${ mostSuitableComponent ? `${themeName} ${mostSuitableComponent}` : themeName - } to %p${path.relative(process.cwd(), toPath)}.`; + } to path=${path.relative(process.cwd(), toPath)}.`; } diff --git a/packages/docusaurus/src/commands/writeHeadingIds.ts b/packages/docusaurus/src/commands/writeHeadingIds.ts index 74cc84ab03fc..374d58d5c78c 100644 --- a/packages/docusaurus/src/commands/writeHeadingIds.ts +++ b/packages/docusaurus/src/commands/writeHeadingIds.ts @@ -147,10 +147,10 @@ export default async function writeHeadingIds( const pathsModified = result.filter(Boolean) as string[]; if (pathsModified.length) { - logger.success`Heading ids added to Markdown files (%n${`${pathsModified.length}/${markdownFiles.length}`} files): ${pathsModified}`; + logger.success`Heading ids added to Markdown files (number=${`${pathsModified.length}/${markdownFiles.length}`} files): ${pathsModified}`; } else { - logger.warn`%n${ + logger.warn`number=${ markdownFiles.length - } Markdown files already have explicit heading IDs. If you intend to overwrite the existing heading IDs, use the %c${'--overwrite'} option.`; + } Markdown files already have explicit heading IDs. If you intend to overwrite the existing heading IDs, use the code=${'--overwrite'} option.`; } } diff --git a/packages/docusaurus/src/server/__tests__/configValidation.test.ts b/packages/docusaurus/src/server/__tests__/configValidation.test.ts index 17973023f630..9a00ad358739 100644 --- a/packages/docusaurus/src/server/__tests__/configValidation.test.ts +++ b/packages/docusaurus/src/server/__tests__/configValidation.test.ts @@ -147,7 +147,7 @@ describe('normalizeConfig', () => { 'should accept [function, object] for plugin', [[function (_context, _options) {}, {it: 'should work'}]], ], - ])(`%s for the input of: %p`, (_message, plugins) => { + ])(`subdue= for the input of: path=`, (_message, plugins) => { expect(() => { normalizeConfig({ plugins, diff --git a/packages/docusaurus/src/server/i18n.ts b/packages/docusaurus/src/server/i18n.ts index 91ee45e7e5ef..673a30a3f881 100644 --- a/packages/docusaurus/src/server/i18n.ts +++ b/packages/docusaurus/src/server/i18n.ts @@ -44,7 +44,7 @@ export async function loadI18n( const currentLocale = options.locale ?? i18nConfig.defaultLocale; if (!i18nConfig.locales.includes(currentLocale)) { - logger.warn`The locale %i${currentLocale} was not found in your site configuration: Available locales are: ${i18nConfig.locales} + logger.warn`The locale name=${currentLocale} was not found in your site configuration: Available locales are: ${i18nConfig.locales} Note: Docusaurus only support running one locale at a time.`; } diff --git a/packages/docusaurus/src/server/index.ts b/packages/docusaurus/src/server/index.ts index d0e77d925337..d3ca3a63a66f 100644 --- a/packages/docusaurus/src/server/index.ts +++ b/packages/docusaurus/src/server/index.ts @@ -419,8 +419,8 @@ function checkDocusaurusPackagesVersion(siteMetadata: DocusaurusSiteMetadata) { ) { // should we throw instead? // It still could work with different versions - logger.error`Invalid %i${plugin} version %n${versionInfo.version}. -All official @docusaurus/* packages should have the exact same version as @docusaurus/core (%n${docusaurusVersion}). + logger.error`Invalid name=${plugin} version number=${versionInfo.version}. +All official @docusaurus/* packages should have the exact same version as @docusaurus/core (number=${docusaurusVersion}). Maybe you want to check, or regenerate your yarn.lock or package-lock.json file?`; } }, diff --git a/packages/docusaurus/src/server/plugins/index.ts b/packages/docusaurus/src/server/plugins/index.ts index fc806a2921c4..5757a5fb9b1f 100644 --- a/packages/docusaurus/src/server/plugins/index.ts +++ b/packages/docusaurus/src/server/plugins/index.ts @@ -212,7 +212,7 @@ export async function loadPlugins({ // TODO remove this deprecated lifecycle soon // deprecated since alpha-60 // TODO, 1 user reported usage of this lifecycle! https://github.com/facebook/docusaurus/issues/3918 - logger.error`Plugin %c${'routesLoaded'} lifecycle is deprecated. If you think we should keep this lifecycle, please report here: %p${'https://github.com/facebook/docusaurus/issues/3918'}`; + logger.error`Plugin code=${'routesLoaded'} lifecycle is deprecated. If you think we should keep this lifecycle, please report here: path=${'https://github.com/facebook/docusaurus/issues/3918'}`; return plugin.routesLoaded(pluginsRouteConfigs); }), diff --git a/packages/docusaurus/src/server/translations/translations.ts b/packages/docusaurus/src/server/translations/translations.ts index 2f8bca795e88..4998addf15fd 100644 --- a/packages/docusaurus/src/server/translations/translations.ts +++ b/packages/docusaurus/src/server/translations/translations.ts @@ -114,7 +114,7 @@ export async function writeTranslationFileContent({ Object.keys(newContent), ); if (unknownKeys.length > 0) { - logger.warn`Some translation keys looks unknown to us in file %p${filePath}. + logger.warn`Some translation keys looks unknown to us in file path=${filePath}. Maybe you should remove them? ${unknownKeys}`; } @@ -126,9 +126,9 @@ Maybe you should remove them? ${unknownKeys}`; // Avoid creating empty translation files if (Object.keys(mergedContent).length > 0) { - logger.info`%n${ + logger.info`number=${ Object.keys(mergedContent).length - } translations will be written at %p${toMessageRelativeFilePath( + } translations will be written at path=${toMessageRelativeFilePath( filePath, )}.`; await fs.ensureDir(path.dirname(filePath)); @@ -282,7 +282,7 @@ export function applyDefaultCodeTranslations({ ); if (unusedDefaultCodeMessages.length > 0) { logger.warn`Unused default message codes found. -Please report this Docusaurus issue. %i${unusedDefaultCodeMessages}`; +Please report this Docusaurus issue. name=${unusedDefaultCodeMessages}`; } return mapValues( diff --git a/packages/docusaurus/src/server/translations/translationsExtractor.ts b/packages/docusaurus/src/server/translations/translationsExtractor.ts index 5845cc58df47..ddb493b2bdcf 100644 --- a/packages/docusaurus/src/server/translations/translationsExtractor.ts +++ b/packages/docusaurus/src/server/translations/translationsExtractor.ts @@ -114,7 +114,7 @@ function logSourceCodeFileTranslationsWarnings( ) { sourceCodeFilesTranslations.forEach(({sourceCodeFilePath, warnings}) => { if (warnings.length > 0) { - logger.warn`Translation extraction warnings for file %p${sourceCodeFilePath}: ${warnings}`; + logger.warn`Translation extraction warnings for file path=${sourceCodeFilePath}: ${warnings}`; } }); } diff --git a/packages/docusaurus/src/webpack/utils.ts b/packages/docusaurus/src/webpack/utils.ts index e487543d7ba4..7ff5684dad2c 100644 --- a/packages/docusaurus/src/webpack/utils.ts +++ b/packages/docusaurus/src/webpack/utils.ts @@ -165,7 +165,7 @@ export const getCustomizableJSLoader = // TODO remove this before end of 2021? const warnBabelLoaderOnce = memoize(() => { - logger.warn`Docusaurus plans to support multiple JS loader strategies (Babel, esbuild...): %c${'getBabelLoader(isServer)'} is now deprecated in favor of %c${'getJSLoader(isServer)'}.`; + logger.warn`Docusaurus plans to support multiple JS loader strategies (Babel, esbuild...): code=${'getBabelLoader(isServer)'} is now deprecated in favor of code=${'getJSLoader(isServer)'}.`; }); const getBabelLoaderDeprecated = function getBabelLoaderDeprecated( isServer: boolean, @@ -177,7 +177,7 @@ const getBabelLoaderDeprecated = function getBabelLoaderDeprecated( // TODO remove this before end of 2021 ? const warnCacheLoaderOnce = memoize(() => { - logger.warn`Docusaurus uses Webpack 5 and %c${'getCacheLoader()'} usage is now deprecated.`; + logger.warn`Docusaurus uses Webpack 5 and code=${'getCacheLoader()'} usage is now deprecated.`; }); function getCacheLoaderDeprecated() { warnCacheLoaderOnce(); From 8322a3d7da41bf0d4d8a390498183c76e873be2c Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 3 Dec 2021 21:10:50 +0800 Subject: [PATCH 25/33] id => name --- packages/docusaurus-logger/src/__tests__/index.test.ts | 2 +- packages/docusaurus-logger/src/index.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/docusaurus-logger/src/__tests__/index.test.ts b/packages/docusaurus-logger/src/__tests__/index.test.ts index 7873405dcae1..8436de916c31 100644 --- a/packages/docusaurus-logger/src/__tests__/index.test.ts +++ b/packages/docusaurus-logger/src/__tests__/index.test.ts @@ -12,7 +12,7 @@ describe('formatters', () => { expect(logger.path('hey')).toMatchInlineSnapshot(`"hey"`); }); test('id', () => { - expect(logger.id('hey')).toMatchInlineSnapshot(`"hey"`); + expect(logger.name('hey')).toMatchInlineSnapshot(`"hey"`); }); test('code', () => { expect(logger.code('hey')).toMatchInlineSnapshot(`"\`hey\`"`); diff --git a/packages/docusaurus-logger/src/index.ts b/packages/docusaurus-logger/src/index.ts index 3d030f726e91..2c1f88c30435 100644 --- a/packages/docusaurus-logger/src/index.ts +++ b/packages/docusaurus-logger/src/index.ts @@ -12,7 +12,7 @@ type InterpolatableValue = string | number | (string | number)[]; const path: import('picocolors/types').Formatter = (msg) => pico.cyan(pico.underline(msg)); -const id: import('picocolors/types').Formatter = (msg) => +const name: import('picocolors/types').Formatter = (msg) => pico.blue(pico.bold(msg)); const code: import('picocolors/types').Formatter = (msg) => pico.cyan(`\`${msg}\``); @@ -37,7 +37,7 @@ function interpolate( case 'number=': return num; case 'name=': - return id; + return name; case 'subdue=': return subdue; case 'code=': @@ -120,7 +120,7 @@ function success(msg: any, ...values: InterpolatableValue[]): void { const logger = { ...pico, path, - id, + name, code, subdue, num, From fc4b0ea797a72fe2bc41b2634faebb46009441e6 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 3 Dec 2021 21:38:34 +0800 Subject: [PATCH 26/33] Refactor theme-translations --- .../src/sidebars/__tests__/generator.test.ts | 2 +- .../package.json | 8 +- .../docusaurus-theme-translations/update.js | 94 ++++++++----------- 3 files changed, 44 insertions(+), 60 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/generator.test.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/generator.test.ts index b5503b43c4eb..d6fe1ab9b185 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/generator.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/generator.test.ts @@ -57,7 +57,7 @@ describe('DefaultSidebarItemsGenerator', () => { expect(sidebarSlice).toEqual([]); expect(consoleWarn).toHaveBeenCalledWith( expect.stringMatching( - /.*\[WARNING\].* No docs found in dir .*\..*: can't auto-generate a sidebar\..*/, + /.*\[WARNING\].* No docs found in .*\..*: can't auto-generate a sidebar\..*/, ), ); }); diff --git a/packages/docusaurus-theme-translations/package.json b/packages/docusaurus-theme-translations/package.json index 0bcedd845c1b..6dc747c33fe1 100644 --- a/packages/docusaurus-theme-translations/package.json +++ b/packages/docusaurus-theme-translations/package.json @@ -22,10 +22,12 @@ "update": "node -e 'require(\"./update.js\").run()'" }, "dependencies": { + "fs-extra": "^10.0.0" + }, + "devDependencies": { "@docusaurus/core": "2.0.0-beta.9", - "fs-extra": "^10.0.0", - "lodash": "^4.17.20", - "picocolors": "^1.0.0" + "@docusaurus/logger": "2.0.0-beta.9", + "lodash": "^4.17.20" }, "engines": { "node": ">=14" diff --git a/packages/docusaurus-theme-translations/update.js b/packages/docusaurus-theme-translations/update.js index 760c17d3c86f..78f3f0d01880 100644 --- a/packages/docusaurus-theme-translations/update.js +++ b/packages/docusaurus-theme-translations/update.js @@ -5,7 +5,10 @@ * LICENSE file in the root directory of this source tree. */ -const pico = require('picocolors'); +// @ts-check +/* eslint-disable import/no-extraneous-dependencies */ + +const logger = require('@docusaurus/logger').default; const path = require('path'); const fs = require('fs-extra'); const {mapValues, pickBy, difference, orderBy} = require('lodash'); @@ -67,17 +70,10 @@ function sortObjectKeys(obj) { }, {}); } -function logSection(title) { - console.log(``); - console.log(``); - console.log(`##############################`); - console.log(`## ${pico.blue(title)}`); -} - -function logKeys(keys) { - return `Keys:\n- ${keys.join('\n- ')}`; -} - +/** + * @param {string[]} targetDirs + * @returns {Promise} + */ async function extractThemeCodeMessages(targetDirs = AllThemesSrcDirs) { // Unsafe import, should we create a package for the translationsExtractor ? const { @@ -120,7 +116,7 @@ ${warning} } async function readMessagesFile(filePath) { - return JSON.parse(await fs.readFile(filePath)); + return JSON.parse((await fs.readFile(filePath)).toString()); } async function writeMessagesFile(filePath, messages) { @@ -128,11 +124,11 @@ async function writeMessagesFile(filePath, messages) { const content = `${JSON.stringify(sortedMessages, null, 2)}\n`; // \n makes prettier happy await fs.outputFile(filePath, content); - console.log( - `${path.basename(filePath)} updated (${ - Object.keys(sortedMessages).length - } messages)`, - ); + logger.info`path=${path.basename( + filePath, + )} updated subdue=${logger.interpolate`(number=${ + Object.keys(sortedMessages).length + } messages)`}\n`; } async function getCodeTranslationFiles(themeName) { @@ -164,11 +160,8 @@ async function updateBaseFile(baseFile, targetDirs) { ); if (unknownMessages.length) { - console.log( - pico.red(`Some messages exist in base locale but were not found by the code extractor! -They won't be removed automatically, so do the cleanup manually if necessary! -${logKeys(unknownMessages)}`), - ); + logger.error`Some messages exist in base locale but were not found by the code extractor! +They won't be removed automatically, so do the cleanup manually if necessary! code=${unknownMessages}`; } const newBaseMessages = { @@ -208,11 +201,8 @@ async function updateLocaleCodeTranslations(localeFile, baseFileMessages) { ); if (unknownMessages.length) { - console.log( - pico.red(`Some localized messages do not exist in base.json! -You may want to delete these! -${logKeys(unknownMessages)}`), - ); + logger.error`Some localized messages do not exist in base.json! +You may want to delete these! code=${unknownMessages}`; } const newLocaleFileMessages = { @@ -225,19 +215,17 @@ ${logKeys(unknownMessages)}`), .map(([key]) => key); if (untranslatedKeys.length) { - console.warn( - pico.yellow(`Some messages do not seem to be translated! -${logKeys(untranslatedKeys)}`), - ); + logger.warn`Some messages do not seem to be translated! code=${untranslatedKeys}`; } await writeMessagesFile(localeFile, newLocaleFileMessages); } async function updateCodeTranslations() { + // eslint-disable-next-line no-restricted-syntax for (const theme of Themes) { const {baseFile, localesFiles} = await getCodeTranslationFiles(theme.name); - logSection(`Will update base file for ${theme.name}`); + logger.info`Will update base file for name=${theme.name}\n`; const baseFileMessages = await updateBaseFile(baseFile, theme.src); const [, newLocale] = process.argv; @@ -246,25 +234,23 @@ async function updateCodeTranslations() { if (!fs.existsSync(newLocalePath)) { await writeMessagesFile(newLocalePath, baseFileMessages); - console.error( - pico.green( - `Locale file ${path.basename(newLocalePath)} have been created.`, - ), - ); + logger.success`Locale file path=${path.basename( + newLocalePath, + )} have been created.`; } else { - console.error( - pico.red( - `Locale file ${path.basename(newLocalePath)} was already created!`, - ), - ); + logger.warn`Locale file path=${path.basename( + newLocalePath, + )} was already created!`; } } else { + // eslint-disable-next-line no-restricted-syntax for (const localeFile of localesFiles) { - logSection( - `Will update ${path.basename( - path.dirname(localeFile), - )} locale in ${path.basename(localeFile, path.extname(localeFile))}`, - ); + logger.info`Will update name=${path.basename( + path.dirname(localeFile), + )} locale in name=${path.basename( + localeFile, + path.extname(localeFile), + )}`; await updateLocaleCodeTranslations(localeFile, baseFileMessages); } @@ -275,16 +261,12 @@ async function updateCodeTranslations() { function run() { updateCodeTranslations().then( () => { - console.log(''); - console.log(pico.green('updateCodeTranslations end')); - console.log(''); + logger.success('updateCodeTranslations end\n'); }, (e) => { - console.log(''); - console.error(pico.red(`updateCodeTranslations failure: ${e.message}`)); - console.log(''); - console.error(e.stack); - console.log(''); + logger.error( + `\nupdateCodeTranslations failure: ${e.message}\n${e.stack}\n`, + ); process.exit(1); }, ); From 577713ce6d0b040ad131948bd5527626550b9e42 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 3 Dec 2021 21:42:41 +0800 Subject: [PATCH 27/33] Fix lock --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 3ce471a333a7..a36e6cebba8a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6133,7 +6133,7 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== From 5e54822975e42f8cd6443c3e4a1b04554343505e Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Sat, 4 Dec 2021 08:39:52 +0800 Subject: [PATCH 28/33] Hail chalk --- packages/docusaurus-logger/README.md | 2 +- packages/docusaurus-logger/package.json | 2 +- packages/docusaurus-logger/src/index.ts | 50 ++++++++++++------------- packages/docusaurus/src/choosePort.ts | 4 +- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/packages/docusaurus-logger/README.md b/packages/docusaurus-logger/README.md index 43d920f41e09..2c4cea3bf5fd 100644 --- a/packages/docusaurus-logger/README.md +++ b/packages/docusaurus-logger/README.md @@ -6,7 +6,7 @@ An encapsulated logger for semantically formatting console messages. It exports a single object as default export: `logger`. `logger` has the following properties: -- All fields of `picocolors`. This includes `yellow`, `createColors`, `isColorSupported`, etc. +- Some useful colors. - Formatters. These functions have the same signature as the formatters of `picocolors`. Note that their implementations are not guaranteed. You should only care about their semantics. - `path`: formats a file path or URL. - `id`: formats an identifier. diff --git a/packages/docusaurus-logger/package.json b/packages/docusaurus-logger/package.json index 85b3c9e103d5..ae9b201a20cb 100644 --- a/packages/docusaurus-logger/package.json +++ b/packages/docusaurus-logger/package.json @@ -20,7 +20,7 @@ }, "license": "MIT", "dependencies": { - "picocolors": "^1.0.0" + "chalk": "^4.1.2" }, "engines": { "node": ">=14" diff --git a/packages/docusaurus-logger/src/index.ts b/packages/docusaurus-logger/src/index.ts index 2c1f88c30435..65273995cead 100644 --- a/packages/docusaurus-logger/src/index.ts +++ b/packages/docusaurus-logger/src/index.ts @@ -4,20 +4,16 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types */ -import pico from 'picocolors'; +import chalk, {Chalk} from 'chalk'; type InterpolatableValue = string | number | (string | number)[]; -const path: import('picocolors/types').Formatter = (msg) => - pico.cyan(pico.underline(msg)); -const name: import('picocolors/types').Formatter = (msg) => - pico.blue(pico.bold(msg)); -const code: import('picocolors/types').Formatter = (msg) => - pico.cyan(`\`${msg}\``); -const subdue = pico.gray; -const num = pico.yellow; +const path = (msg: unknown): string => chalk.cyan(chalk.underline(msg)); +const name = (msg: unknown): string => chalk.blue(chalk.bold(msg)); +const code = (msg: unknown): string => chalk.cyan(`\`${msg}\``); +const subdue: Chalk = chalk.gray; +const num: Chalk = chalk.yellow; function interpolate( msgs: TemplateStringsArray, @@ -56,29 +52,29 @@ function interpolate( return res; } -function info(msg: any): void; +function info(msg: unknown): void; function info( msg: TemplateStringsArray, ...values: [InterpolatableValue, ...InterpolatableValue[]] ): void; -function info(msg: any, ...values: InterpolatableValue[]): void { +function info(msg: unknown, ...values: InterpolatableValue[]): void { console.info( - `${pico.cyan(pico.bold('[INFO]'))} ${ + `${chalk.cyan(chalk.bold('[INFO]'))} ${ values.length === 0 ? msg : interpolate(msg as TemplateStringsArray, ...values) }`, ); } -function warn(msg: any): void; +function warn(msg: unknown): void; function warn( msg: TemplateStringsArray, ...values: [InterpolatableValue, ...InterpolatableValue[]] ): void; -function warn(msg: any, ...values: InterpolatableValue[]): void { +function warn(msg: unknown, ...values: InterpolatableValue[]): void { console.warn( - pico.yellow( - `${pico.bold('[WARNING]')} ${ + chalk.yellow( + `${chalk.bold('[WARNING]')} ${ values.length === 0 ? msg : interpolate(msg as TemplateStringsArray, ...values) @@ -86,15 +82,15 @@ function warn(msg: any, ...values: InterpolatableValue[]): void { ), ); } -function error(msg: any): void; +function error(msg: unknown): void; function error( msg: TemplateStringsArray, ...values: [InterpolatableValue, ...InterpolatableValue[]] ): void; -function error(msg: any, ...values: InterpolatableValue[]): void { +function error(msg: unknown, ...values: InterpolatableValue[]): void { console.error( - pico.red( - `${pico.bold('[ERROR]')} ${ + chalk.red( + `${chalk.bold('[ERROR]')} ${ values.length === 0 ? msg : interpolate(msg as TemplateStringsArray, ...values) @@ -102,14 +98,14 @@ function error(msg: any, ...values: InterpolatableValue[]): void { ), ); } -function success(msg: any): void; +function success(msg: unknown): void; function success( msg: TemplateStringsArray, ...values: [InterpolatableValue, ...InterpolatableValue[]] ): void; -function success(msg: any, ...values: InterpolatableValue[]): void { +function success(msg: unknown, ...values: InterpolatableValue[]): void { console.log( - `${pico.green(pico.bold('[SUCCESS]'))} ${ + `${chalk.green(chalk.bold('[SUCCESS]'))} ${ values.length === 0 ? msg : interpolate(msg as TemplateStringsArray, ...values) @@ -118,7 +114,11 @@ function success(msg: any, ...values: InterpolatableValue[]): void { } const logger = { - ...pico, + red: chalk.red, + yellow: chalk.yellow, + green: chalk.green, + bold: chalk.bold, + dim: chalk.dim, path, name, code, diff --git a/packages/docusaurus/src/choosePort.ts b/packages/docusaurus/src/choosePort.ts index 7507ede31e26..b18f87551788 100644 --- a/packages/docusaurus/src/choosePort.ts +++ b/packages/docusaurus/src/choosePort.ts @@ -99,11 +99,11 @@ export default async function choosePort( const question: prompts.PromptObject = { type: 'confirm', name: 'shouldChangePort', - message: `${logger.yellow(logger.bold('[WARNING]'))} ${message}${ + message: logger.yellow(`${logger.bold('[WARNING]')} ${message}${ existingProcess ? ` Probably:\n ${existingProcess}` : '' } -Would you like to run the app on another port instead?`, +Would you like to run the app on another port instead?`), initial: true, }; prompts(question).then((answer) => { From 496245f530eb1bd61e2b1479bd36f1390ecc856e Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Sat, 4 Dec 2021 08:43:42 +0800 Subject: [PATCH 29/33] Fix --- packages/create-docusaurus/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-docusaurus/src/index.ts b/packages/create-docusaurus/src/index.ts index 8b3c48956638..f879e883ecef 100755 --- a/packages/create-docusaurus/src/index.ts +++ b/packages/create-docusaurus/src/index.ts @@ -237,7 +237,7 @@ export default async function init( try { await fs.copy(templateDir, dest); } catch (err) { - logger.error`Copying local template path=${template} failed!`; + logger.error`Copying local template path=${templateDir} failed!`; throw err; } } else { From 326962a93e89f0a39666f42bfcc668d16bc75243 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Sat, 18 Dec 2021 11:19:24 +0800 Subject: [PATCH 30/33] Fix test --- packages/docusaurus-logger/src/__tests__/index.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/docusaurus-logger/src/__tests__/index.test.ts b/packages/docusaurus-logger/src/__tests__/index.test.ts index 8436de916c31..192ea18a27e8 100644 --- a/packages/docusaurus-logger/src/__tests__/index.test.ts +++ b/packages/docusaurus-logger/src/__tests__/index.test.ts @@ -7,6 +7,9 @@ import logger from '../index'; +// Test with colors on +process.env.FORCE_COLOR = '1'; + describe('formatters', () => { test('path', () => { expect(logger.path('hey')).toMatchInlineSnapshot(`"hey"`); From 55aded0c042a5df5675599e13be975649af91030 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Sat, 18 Dec 2021 11:43:32 +0800 Subject: [PATCH 31/33] Fix tests --- .../src/__tests__/__mocks__/chalk.js | 11 +++++++++++ .../docusaurus-logger/src/__tests__/index.test.ts | 3 --- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 packages/docusaurus-logger/src/__tests__/__mocks__/chalk.js diff --git a/packages/docusaurus-logger/src/__tests__/__mocks__/chalk.js b/packages/docusaurus-logger/src/__tests__/__mocks__/chalk.js new file mode 100644 index 000000000000..361c56faf9dd --- /dev/null +++ b/packages/docusaurus-logger/src/__tests__/__mocks__/chalk.js @@ -0,0 +1,11 @@ +/** + * 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. + */ + +const chalk = require('chalk'); + +// Force coloring the output even in CI +module.exports = new chalk.Instance({level: 3}); diff --git a/packages/docusaurus-logger/src/__tests__/index.test.ts b/packages/docusaurus-logger/src/__tests__/index.test.ts index 192ea18a27e8..8436de916c31 100644 --- a/packages/docusaurus-logger/src/__tests__/index.test.ts +++ b/packages/docusaurus-logger/src/__tests__/index.test.ts @@ -7,9 +7,6 @@ import logger from '../index'; -// Test with colors on -process.env.FORCE_COLOR = '1'; - describe('formatters', () => { test('path', () => { expect(logger.path('hey')).toMatchInlineSnapshot(`"hey"`); From 47e25e71bd254741882167c5b8d97ec23b6fa623 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Sun, 19 Dec 2021 10:46:18 +0800 Subject: [PATCH 32/33] Fixes --- packages/docusaurus-logger/package.json | 3 ++- .../docusaurus-logger/src/{__tests__ => }/__mocks__/chalk.js | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename packages/docusaurus-logger/src/{__tests__ => }/__mocks__/chalk.js (100%) diff --git a/packages/docusaurus-logger/package.json b/packages/docusaurus-logger/package.json index 07ed3a5366d3..51907b08ac3e 100644 --- a/packages/docusaurus-logger/package.json +++ b/packages/docusaurus-logger/package.json @@ -20,7 +20,8 @@ }, "license": "MIT", "dependencies": { - "chalk": "^4.1.2" + "chalk": "^4.1.2", + "tslib": "^2.3.1" }, "engines": { "node": ">=14" diff --git a/packages/docusaurus-logger/src/__tests__/__mocks__/chalk.js b/packages/docusaurus-logger/src/__mocks__/chalk.js similarity index 100% rename from packages/docusaurus-logger/src/__tests__/__mocks__/chalk.js rename to packages/docusaurus-logger/src/__mocks__/chalk.js From ac3f1af61ffca8b660b9520534e1f2e772dd0d7b Mon Sep 17 00:00:00 2001 From: sebastienlorber Date: Mon, 20 Dec 2021 16:49:24 +0100 Subject: [PATCH 33/33] remove picocolors deps --- packages/docusaurus-utils-validation/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/docusaurus-utils-validation/package.json b/packages/docusaurus-utils-validation/package.json index c4f409b066f5..160b4917490a 100644 --- a/packages/docusaurus-utils-validation/package.json +++ b/packages/docusaurus-utils-validation/package.json @@ -21,7 +21,6 @@ "@docusaurus/logger": "2.0.0-beta.13", "@docusaurus/utils": "2.0.0-beta.13", "joi": "^17.4.2", - "picocolors": "^1.0.0", "tslib": "^2.3.1" }, "peerDependencies": {