This commit is contained in:
2024-03-22 03:47:51 +05:30
parent 8bcf3d211e
commit 89819f6fe2
28440 changed files with 3211033 additions and 2 deletions

13
node_modules/@docusaurus/core/lib/commands/build.d.ts generated vendored Normal file
View File

@@ -0,0 +1,13 @@
/**
* 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 { type LoadContextOptions } from '../server';
export type BuildCLIOptions = Pick<LoadContextOptions, 'config' | 'locale' | 'outDir'> & {
bundleAnalyzer?: boolean;
minify?: boolean;
dev?: boolean;
};
export declare function build(siteDirParam?: string, cliOptions?: Partial<BuildCLIOptions>, forceTerminate?: boolean): Promise<string>;

206
node_modules/@docusaurus/core/lib/commands/build.js generated vendored Normal file
View File

@@ -0,0 +1,206 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.build = void 0;
const tslib_1 = require("tslib");
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const path_1 = tslib_1.__importDefault(require("path"));
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const utils_1 = require("@docusaurus/utils");
const copy_webpack_plugin_1 = tslib_1.__importDefault(require("copy-webpack-plugin"));
const react_loadable_ssr_addon_v5_slorber_1 = tslib_1.__importDefault(require("react-loadable-ssr-addon-v5-slorber"));
const webpack_bundle_analyzer_1 = require("webpack-bundle-analyzer");
const webpack_merge_1 = tslib_1.__importDefault(require("webpack-merge"));
const server_1 = require("../server");
const brokenLinks_1 = require("../server/brokenLinks");
const client_1 = tslib_1.__importDefault(require("../webpack/client"));
const server_2 = tslib_1.__importDefault(require("../webpack/server"));
const utils_2 = require("../webpack/utils");
const CleanWebpackPlugin_1 = tslib_1.__importDefault(require("../webpack/plugins/CleanWebpackPlugin"));
const i18n_1 = require("../server/i18n");
async function build(siteDirParam = '.', cliOptions = {},
// When running build, we force terminate the process to prevent async
// operations from never returning. However, if run as part of docusaurus
// deploy, we have to let deploy finish.
// See https://github.com/facebook/docusaurus/pull/2496
forceTerminate = true) {
process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production';
process.env.DOCUSAURUS_CURRENT_LOCALE = cliOptions.locale;
if (cliOptions.dev) {
logger_1.default.info `Building in dev mode`;
process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';
}
const siteDir = await fs_extra_1.default.realpath(siteDirParam);
['SIGINT', 'SIGTERM'].forEach((sig) => {
process.on(sig, () => process.exit());
});
async function tryToBuildLocale({ locale, isLastLocale, }) {
try {
return await buildLocale({
siteDir,
locale,
cliOptions,
forceTerminate,
isLastLocale,
});
}
catch (err) {
throw new Error(logger_1.default.interpolate `Unable to build website for locale name=${locale}.`, {
cause: err,
});
}
}
const context = await (0, server_1.loadContext)({
siteDir,
outDir: cliOptions.outDir,
config: cliOptions.config,
locale: cliOptions.locale,
localizePath: cliOptions.locale ? false : undefined,
});
const i18n = await (0, i18n_1.loadI18n)(context.siteConfig, {
locale: cliOptions.locale,
});
if (cliOptions.locale) {
return tryToBuildLocale({ locale: cliOptions.locale, isLastLocale: true });
}
if (i18n.locales.length > 1) {
logger_1.default.info `Website will be built for all these locales: ${i18n.locales}`;
}
// We need the default locale to always be the 1st in the list. If we build it
// last, it would "erase" the localized sites built in sub-folders
const orderedLocales = [
i18n.defaultLocale,
...i18n.locales.filter((locale) => locale !== i18n.defaultLocale),
];
const results = await (0, utils_1.mapAsyncSequential)(orderedLocales, (locale) => {
const isLastLocale = orderedLocales.indexOf(locale) === orderedLocales.length - 1;
return tryToBuildLocale({ locale, isLastLocale });
});
return results[0];
}
exports.build = build;
async function buildLocale({ siteDir, locale, cliOptions, forceTerminate, isLastLocale, }) {
// Temporary workaround to unlock the ability to translate the site config
// We'll remove it if a better official API can be designed
// See https://github.com/facebook/docusaurus/issues/4542
process.env.DOCUSAURUS_CURRENT_LOCALE = locale;
logger_1.default.info `name=${`[${locale}]`} Creating an optimized production build...`;
const props = await (0, server_1.load)({
siteDir,
outDir: cliOptions.outDir,
config: cliOptions.config,
locale,
localizePath: cliOptions.locale ? false : undefined,
});
// Apply user webpack config.
const { outDir, generatedFilesDir, plugins, siteConfig: { onBrokenLinks, onBrokenAnchors, staticDirectories: staticDirectoriesOption, }, routes, } = props;
const clientManifestPath = path_1.default.join(generatedFilesDir, 'client-manifest.json');
let clientConfig = (0, webpack_merge_1.default)(await (0, client_1.default)(props, cliOptions.minify, true), {
plugins: [
// Remove/clean build folders before building bundles.
new CleanWebpackPlugin_1.default({ verbose: false }),
// Visualize size of webpack output files with an interactive zoomable
// tree map.
cliOptions.bundleAnalyzer && new webpack_bundle_analyzer_1.BundleAnalyzerPlugin(),
// Generate client manifests file that will be used for server bundle.
new react_loadable_ssr_addon_v5_slorber_1.default({
filename: clientManifestPath,
}),
].filter((x) => Boolean(x)),
});
const collectedLinks = {};
const headTags = {};
let serverConfig = await (0, server_2.default)({
props,
onLinksCollected: ({ staticPagePath, links, anchors }) => {
collectedLinks[staticPagePath] = { links, anchors };
},
onHeadTagsCollected: (staticPagePath, tags) => {
headTags[staticPagePath] = tags;
},
});
// The staticDirectories option can contain empty directories, or non-existent
// directories (e.g. user deleted `static`). Instead of issuing an error, we
// just silently filter them out, because user could have never configured it
// in the first place (the default option should always "work").
const staticDirectories = (await Promise.all(staticDirectoriesOption.map(async (dir) => {
const staticDir = path_1.default.resolve(siteDir, dir);
if ((await fs_extra_1.default.pathExists(staticDir)) &&
(await fs_extra_1.default.readdir(staticDir)).length > 0) {
return staticDir;
}
return '';
}))).filter(Boolean);
if (staticDirectories.length > 0) {
serverConfig = (0, webpack_merge_1.default)(serverConfig, {
plugins: [
new copy_webpack_plugin_1.default({
patterns: staticDirectories.map((dir) => ({
from: dir,
to: outDir,
toType: 'dir',
})),
}),
],
});
}
// Plugin Lifecycle - configureWebpack and configurePostCss.
plugins.forEach((plugin) => {
const { configureWebpack, configurePostCss } = plugin;
if (configurePostCss) {
clientConfig = (0, utils_2.applyConfigurePostCss)(configurePostCss.bind(plugin), clientConfig);
}
if (configureWebpack) {
clientConfig = (0, utils_2.applyConfigureWebpack)(configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
clientConfig, false, props.siteConfig.webpack?.jsLoader, plugin.content);
serverConfig = (0, utils_2.applyConfigureWebpack)(configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
serverConfig, true, props.siteConfig.webpack?.jsLoader, plugin.content);
}
});
// Make sure generated client-manifest is cleaned first so we don't reuse
// the one from previous builds.
if (await fs_extra_1.default.pathExists(clientManifestPath)) {
await fs_extra_1.default.unlink(clientManifestPath);
}
// Run webpack to build JS bundle (client) and static html files (server).
await (0, utils_2.compile)([clientConfig, serverConfig]);
// Remove server.bundle.js because it is not needed.
if (typeof serverConfig.output?.filename === 'string') {
const serverBundle = path_1.default.join(outDir, serverConfig.output.filename);
if (await fs_extra_1.default.pathExists(serverBundle)) {
await fs_extra_1.default.unlink(serverBundle);
}
}
// Plugin Lifecycle - postBuild.
await Promise.all(plugins.map(async (plugin) => {
if (!plugin.postBuild) {
return;
}
await plugin.postBuild({
...props,
head: headTags,
content: plugin.content,
});
}));
await (0, brokenLinks_1.handleBrokenLinks)({
collectedLinks,
routes,
onBrokenLinks,
onBrokenAnchors,
});
logger_1.default.success `Generated static files in path=${path_1.default.relative(process.cwd(), outDir)}.`;
if (isLastLocale) {
logger_1.default.info `Use code=${'npm run serve'} command to test your build locally.`;
}
if (forceTerminate && isLastLocale && !cliOptions.bundleAnalyzer) {
process.exit(0);
}
return outDir;
}

View File

@@ -0,0 +1,7 @@
/**
* 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.
*/
export declare function clear(siteDirParam?: string): Promise<void>;

45
node_modules/@docusaurus/core/lib/commands/clear.js generated vendored Normal file
View File

@@ -0,0 +1,45 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.clear = void 0;
const tslib_1 = require("tslib");
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const path_1 = tslib_1.__importDefault(require("path"));
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const utils_1 = require("@docusaurus/utils");
async function removePath(entry) {
if (!(await fs_extra_1.default.pathExists(entry.path))) {
return;
}
try {
await fs_extra_1.default.remove(entry.path);
logger_1.default.success `Removed the ${entry.description} at path=${entry.path}.`;
}
catch (err) {
logger_1.default.error `Could not remove the ${entry.description} at path=${entry.path}.`;
logger_1.default.error(err);
}
}
async function clear(siteDirParam = '.') {
const siteDir = await fs_extra_1.default.realpath(siteDirParam);
const generatedFolder = {
path: path_1.default.join(siteDir, utils_1.GENERATED_FILES_DIR_NAME),
description: 'generated folder',
};
const buildFolder = {
path: path_1.default.join(siteDir, utils_1.DEFAULT_BUILD_DIR_NAME),
description: 'build output folder',
};
// In Yarn PnP, cache is stored in `.yarn/.cache` because n_m doesn't exist
const cacheFolders = ['node_modules', '.yarn'].map((p) => ({
path: path_1.default.join(siteDir, p, '.cache'),
description: 'Webpack persistent cache folder',
}));
await Promise.all([generatedFolder, buildFolder, ...cacheFolders].map(removePath));
}
exports.clear = clear;

11
node_modules/@docusaurus/core/lib/commands/deploy.d.ts generated vendored Normal file
View File

@@ -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.
*/
import { type LoadContextOptions } from '../server';
export type DeployCLIOptions = Pick<LoadContextOptions, 'config' | 'locale' | 'outDir'> & {
skipBuild?: boolean;
};
export declare function deploy(siteDirParam?: string, cliOptions?: Partial<DeployCLIOptions>): Promise<void>;

199
node_modules/@docusaurus/core/lib/commands/deploy.js generated vendored Normal file
View File

@@ -0,0 +1,199 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.deploy = void 0;
const tslib_1 = require("tslib");
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const path_1 = tslib_1.__importDefault(require("path"));
const os_1 = tslib_1.__importDefault(require("os"));
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const shelljs_1 = tslib_1.__importDefault(require("shelljs"));
const utils_1 = require("@docusaurus/utils");
const server_1 = require("../server");
const build_1 = require("./build");
// GIT_PASS env variable should not appear in logs
function obfuscateGitPass(str) {
const gitPass = process.env.GIT_PASS;
return gitPass ? str.replace(gitPass, 'GIT_PASS') : str;
}
// Log executed commands so that user can figure out mistakes on his own
// for example: https://github.com/facebook/docusaurus/issues/3875
function shellExecLog(cmd) {
try {
const result = shelljs_1.default.exec(cmd);
logger_1.default.info `code=${obfuscateGitPass(cmd)} subdue=${`code: ${result.code}`}`;
return result;
}
catch (err) {
logger_1.default.error `code=${obfuscateGitPass(cmd)}`;
throw err;
}
}
async function deploy(siteDirParam = '.', cliOptions = {}) {
const siteDir = await fs_extra_1.default.realpath(siteDirParam);
const { outDir, siteConfig, siteConfigPath } = await (0, server_1.loadContext)({
siteDir,
config: cliOptions.config,
outDir: cliOptions.outDir,
});
if (typeof siteConfig.trailingSlash === 'undefined') {
logger_1.default.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.
`);
}
logger_1.default.info('Deploy command invoked...');
if (!shelljs_1.default.which('git')) {
throw new Error('Git not installed or on the PATH!');
}
// Source repo is the repo from where the command is invoked
const sourceRepoUrl = shelljs_1.default
.exec('git remote get-url origin', { silent: true })
.stdout.trim();
// The source branch; defaults to the currently checked out branch
const sourceBranch = process.env.CURRENT_BRANCH ??
shelljs_1.default.exec('git rev-parse --abbrev-ref HEAD', { silent: true }).stdout.trim();
const gitUser = process.env.GIT_USER;
let useSSH = process.env.USE_SSH !== undefined &&
process.env.USE_SSH.toLowerCase() === 'true';
if (!gitUser && !useSSH) {
// If USE_SSH is unspecified: try inferring from repo URL
if (process.env.USE_SSH === undefined && (0, utils_1.hasSSHProtocol)(sourceRepoUrl)) {
useSSH = true;
}
else {
throw new Error('Please set the GIT_USER environment variable, or explicitly specify USE_SSH instead!');
}
}
const organizationName = process.env.ORGANIZATION_NAME ??
process.env.CIRCLE_PROJECT_USERNAME ??
siteConfig.organizationName;
if (!organizationName) {
throw new Error(`Missing project organization name. Did you forget to define "organizationName" in ${siteConfigPath}? You may also export it via the ORGANIZATION_NAME environment variable.`);
}
logger_1.default.info `organizationName: name=${organizationName}`;
const projectName = process.env.PROJECT_NAME ??
process.env.CIRCLE_PROJECT_REPONAME ??
siteConfig.projectName;
if (!projectName) {
throw new Error(`Missing project name. Did you forget to define "projectName" in ${siteConfigPath}? You may also export it via the PROJECT_NAME environment variable.`);
}
logger_1.default.info `projectName: name=${projectName}`;
// We never deploy on pull request.
const isPullRequest = process.env.CI_PULL_REQUEST ?? process.env.CIRCLE_PULL_REQUEST;
if (isPullRequest) {
shelljs_1.default.echo('Skipping deploy on a pull request.');
shelljs_1.default.exit(0);
}
// github.io indicates organization repos that deploy via default branch. All
// others use gh-pages (either case can be configured actually, but we can
// make educated guesses). Organization deploys look like:
// - Git repo: https://github.com/<organization>/<organization>.github.io
// - Site url: https://<organization>.github.io
const isGitHubPagesOrganizationDeploy = projectName.includes('.github.io');
if (isGitHubPagesOrganizationDeploy &&
!process.env.DEPLOYMENT_BRANCH &&
!siteConfig.deploymentBranch) {
throw new Error(`For GitHub pages organization deployments, 'docusaurus deploy' does not assume anymore that 'master' is your default Git branch.
Please provide the branch name to deploy to as an environment variable, for example DEPLOYMENT_BRANCH=main or DEPLOYMENT_BRANCH=master .
You can also set the deploymentBranch property in docusaurus.config.js .`);
}
const deploymentBranch = process.env.DEPLOYMENT_BRANCH ?? siteConfig.deploymentBranch ?? 'gh-pages';
logger_1.default.info `deploymentBranch: name=${deploymentBranch}`;
const githubHost = process.env.GITHUB_HOST ?? siteConfig.githubHost ?? 'github.com';
const githubPort = process.env.GITHUB_PORT ?? siteConfig.githubPort;
let deploymentRepoURL;
if (useSSH) {
deploymentRepoURL = (0, utils_1.buildSshUrl)(githubHost, organizationName, projectName, githubPort);
}
else {
const gitPass = process.env.GIT_PASS;
const gitCredentials = gitPass ? `${gitUser}:${gitPass}` : gitUser;
deploymentRepoURL = (0, utils_1.buildHttpsUrl)(gitCredentials, githubHost, organizationName, projectName, githubPort);
}
logger_1.default.info `Remote repo URL: name=${obfuscateGitPass(deploymentRepoURL)}`;
// Check if this is a cross-repo publish.
const crossRepoPublish = !sourceRepoUrl.endsWith(`${organizationName}/${projectName}.git`);
// We don't allow deploying to the same branch unless it's a cross publish.
if (sourceBranch === deploymentBranch && !crossRepoPublish) {
throw new Error(`You cannot deploy from this branch (${sourceBranch}).` +
'\nYou will need to checkout to a different branch!');
}
// Save the commit hash that triggers publish-gh-pages before checking
// out to deployment branch.
const currentCommit = shellExecLog('git rev-parse HEAD').stdout.trim();
const runDeploy = async (outputDirectory) => {
const fromPath = outputDirectory;
const toPath = await fs_extra_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), `${projectName}-${deploymentBranch}`));
shelljs_1.default.cd(toPath);
// Check out deployment branch when cloning repository, and then remove all
// the files in the directory. If the 'clone' command fails, assume that
// the deployment branch doesn't exist, and initialize git in an empty
// directory, check out a clean deployment branch and add remote.
if (shellExecLog(`git clone --depth 1 --branch ${deploymentBranch} ${deploymentRepoURL} "${toPath}"`).code === 0) {
shellExecLog('git rm -rf .');
}
else {
shellExecLog('git init');
shellExecLog(`git checkout -b ${deploymentBranch}`);
shellExecLog(`git remote add origin ${deploymentRepoURL}`);
}
try {
await fs_extra_1.default.copy(fromPath, toPath);
}
catch (err) {
logger_1.default.error `Copying build assets from path=${fromPath} to path=${toPath} failed.`;
throw err;
}
shellExecLog('git add --all');
const gitUserName = process.env.GIT_USER_NAME;
if (gitUserName) {
shellExecLog(`git config user.name "${gitUserName}"`);
}
const gitUserEmail = process.env.GIT_USER_EMAIL;
if (gitUserEmail) {
shellExecLog(`git config user.email "${gitUserEmail}"`);
}
const commitMessage = process.env.CUSTOM_COMMIT_MESSAGE ??
`Deploy website - based on ${currentCommit}`;
const commitResults = shellExecLog(`git commit -m "${commitMessage}"`);
if (shellExecLog(`git push --force origin ${deploymentBranch}`).code !== 0) {
throw new Error('Running "git push" command failed. Does the GitHub user account you are using have push access to the repository?');
}
else if (commitResults.code === 0) {
// The commit might return a non-zero value when site is up to date.
let websiteURL = '';
if (githubHost === 'github.com') {
websiteURL = projectName.includes('.github.io')
? `https://${organizationName}.github.io/`
: `https://${organizationName}.github.io/${projectName}/`;
}
else {
// GitHub enterprise hosting.
websiteURL = `https://${githubHost}/pages/${organizationName}/${projectName}/`;
}
shelljs_1.default.echo(`Website is live at "${websiteURL}".`);
shelljs_1.default.exit(0);
}
};
if (!cliOptions.skipBuild) {
// Build site, then push to deploymentBranch branch of specified repo.
try {
await (0, build_1.build)(siteDir, cliOptions, false).then(runDeploy);
}
catch (err) {
logger_1.default.error('Deployment of the build output failed.');
throw err;
}
}
else {
// Push current build to deploymentBranch branch of specified repo.
await runDeploy(outDir);
}
}
exports.deploy = deploy;

View File

@@ -0,0 +1,8 @@
/**
* 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 type { CommanderStatic } from 'commander';
export declare function externalCommand(cli: CommanderStatic): Promise<void>;

23
node_modules/@docusaurus/core/lib/commands/external.js generated vendored Normal file
View File

@@ -0,0 +1,23 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.externalCommand = void 0;
const tslib_1 = require("tslib");
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const server_1 = require("../server");
const init_1 = require("../server/plugins/init");
async function externalCommand(cli) {
const siteDir = await fs_extra_1.default.realpath('.');
const context = await (0, server_1.loadContext)({ siteDir });
const plugins = await (0, init_1.initPlugins)(context);
// Plugin Lifecycle - extendCli.
plugins.forEach((plugin) => {
plugin.extendCli?.(cli);
});
}
exports.externalCommand = externalCommand;

14
node_modules/@docusaurus/core/lib/commands/serve.d.ts generated vendored Normal file
View File

@@ -0,0 +1,14 @@
/**
* 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 { type HostPortOptions } from '../server/getHostPort';
import type { LoadContextOptions } from '../server';
export type ServeCLIOptions = HostPortOptions & Pick<LoadContextOptions, 'config'> & {
dir?: string;
build?: boolean;
open?: boolean;
};
export declare function serve(siteDirParam?: string, cliOptions?: Partial<ServeCLIOptions>): Promise<void>;

66
node_modules/@docusaurus/core/lib/commands/serve.js generated vendored Normal file
View File

@@ -0,0 +1,66 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.serve = void 0;
const tslib_1 = require("tslib");
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const http_1 = tslib_1.__importDefault(require("http"));
const path_1 = tslib_1.__importDefault(require("path"));
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const utils_1 = require("@docusaurus/utils");
const serve_handler_1 = tslib_1.__importDefault(require("serve-handler"));
const openBrowser_1 = tslib_1.__importDefault(require("react-dev-utils/openBrowser"));
const config_1 = require("../server/config");
const build_1 = require("./build");
const getHostPort_1 = require("../server/getHostPort");
async function serve(siteDirParam = '.', cliOptions = {}) {
const siteDir = await fs_extra_1.default.realpath(siteDirParam);
const buildDir = cliOptions.dir ?? utils_1.DEFAULT_BUILD_DIR_NAME;
let dir = path_1.default.resolve(siteDir, buildDir);
if (cliOptions.build) {
dir = await (0, build_1.build)(siteDir, {
config: cliOptions.config,
outDir: dir,
}, false);
}
const { host, port } = await (0, getHostPort_1.getHostPort)(cliOptions);
if (port === null) {
process.exit();
}
const { siteConfig: { baseUrl, trailingSlash }, } = await (0, config_1.loadSiteConfig)({
siteDir,
customConfigFilePath: cliOptions.config,
});
const servingUrl = `http://${host}:${port}`;
const server = http_1.default.createServer((req, res) => {
// Automatically redirect requests to /baseUrl/
if (!req.url?.startsWith(baseUrl)) {
res.writeHead(302, {
Location: baseUrl,
});
res.end();
return;
}
// Remove baseUrl before calling serveHandler, because /baseUrl/ should
// serve /build/index.html, not /build/baseUrl/index.html (does not exist)
req.url = req.url.replace(baseUrl, '/');
(0, serve_handler_1.default)(req, res, {
cleanUrls: true,
public: dir,
trailingSlash,
directoryListing: false,
});
});
const url = servingUrl + baseUrl;
logger_1.default.success `Serving path=${buildDir} directory at: url=${url}`;
server.listen(port);
if (cliOptions.open && !process.env.CI) {
(0, openBrowser_1.default)(url);
}
}
exports.serve = serve;

15
node_modules/@docusaurus/core/lib/commands/start.d.ts generated vendored Normal file
View File

@@ -0,0 +1,15 @@
/**
* 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 { type LoadContextOptions } from '../server';
import { type HostPortOptions } from '../server/getHostPort';
export type StartCLIOptions = HostPortOptions & Pick<LoadContextOptions, 'locale' | 'config'> & {
hotOnly?: boolean;
open?: boolean;
poll?: boolean | number;
minify?: boolean;
};
export declare function start(siteDirParam?: string, cliOptions?: Partial<StartCLIOptions>): Promise<void>;

212
node_modules/@docusaurus/core/lib/commands/start.js generated vendored Normal file
View File

@@ -0,0 +1,212 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.start = void 0;
const tslib_1 = require("tslib");
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const path_1 = tslib_1.__importDefault(require("path"));
const lodash_1 = tslib_1.__importDefault(require("lodash"));
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const utils_1 = require("@docusaurus/utils");
const chokidar_1 = tslib_1.__importDefault(require("chokidar"));
const html_webpack_plugin_1 = tslib_1.__importDefault(require("html-webpack-plugin"));
const openBrowser_1 = tslib_1.__importDefault(require("react-dev-utils/openBrowser"));
const WebpackDevServerUtils_1 = require("react-dev-utils/WebpackDevServerUtils");
const evalSourceMapMiddleware_1 = tslib_1.__importDefault(require("react-dev-utils/evalSourceMapMiddleware"));
const webpack_1 = tslib_1.__importDefault(require("webpack"));
const webpack_dev_server_1 = tslib_1.__importDefault(require("webpack-dev-server"));
const webpack_merge_1 = tslib_1.__importDefault(require("webpack-merge"));
const server_1 = require("../server");
const client_1 = tslib_1.__importDefault(require("../webpack/client"));
const utils_2 = require("../webpack/utils");
const getHostPort_1 = require("../server/getHostPort");
async function start(siteDirParam = '.', cliOptions = {}) {
// Temporary workaround to unlock the ability to translate the site config
// We'll remove it if a better official API can be designed
// See https://github.com/facebook/docusaurus/issues/4542
process.env.DOCUSAURUS_CURRENT_LOCALE = cliOptions.locale;
const siteDir = await fs_extra_1.default.realpath(siteDirParam);
logger_1.default.info('Starting the development server...');
function loadSite() {
return (0, server_1.load)({
siteDir,
config: cliOptions.config,
locale: cliOptions.locale,
localizePath: undefined, // Should this be configurable?
});
}
// Process all related files as a prop.
const props = await loadSite();
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const { host, port } = await (0, getHostPort_1.getHostPort)(cliOptions);
if (port === null) {
process.exit();
}
const { baseUrl, headTags, preBodyTags, postBodyTags } = props;
const urls = (0, WebpackDevServerUtils_1.prepareUrls)(protocol, host, port);
const openUrl = (0, utils_1.normalizeUrl)([urls.localUrlForBrowser, baseUrl]);
logger_1.default.success `Docusaurus website is running at: url=${openUrl}`;
// Reload files processing.
const reload = lodash_1.default.debounce(() => {
loadSite()
.then(({ baseUrl: newBaseUrl }) => {
const newOpenUrl = (0, utils_1.normalizeUrl)([urls.localUrlForBrowser, newBaseUrl]);
if (newOpenUrl !== openUrl) {
logger_1.default.success `Docusaurus website is running at: url=${newOpenUrl}`;
}
})
.catch((err) => {
logger_1.default.error(err.stack);
});
}, 500);
const { siteConfig, plugins, localizationDir } = props;
const normalizeToSiteDir = (filepath) => {
if (filepath && path_1.default.isAbsolute(filepath)) {
return (0, utils_1.posixPath)(path_1.default.relative(siteDir, filepath));
}
return (0, utils_1.posixPath)(filepath);
};
const pluginPaths = plugins
.flatMap((plugin) => plugin.getPathsToWatch?.() ?? [])
.filter(Boolean)
.map(normalizeToSiteDir);
const pathsToWatch = [...pluginPaths, props.siteConfigPath, localizationDir];
const pollingOptions = {
usePolling: !!cliOptions.poll,
interval: Number.isInteger(cliOptions.poll)
? cliOptions.poll
: undefined,
};
const httpsConfig = await (0, utils_2.getHttpsConfig)();
const fsWatcher = chokidar_1.default.watch(pathsToWatch, {
cwd: siteDir,
ignoreInitial: true,
...{ pollingOptions },
});
['add', 'change', 'unlink', 'addDir', 'unlinkDir'].forEach((event) => fsWatcher.on(event, reload));
let config = (0, webpack_merge_1.default)(await (0, client_1.default)(props, cliOptions.minify, false), {
watchOptions: {
ignored: /node_modules\/(?!@docusaurus)/,
poll: cliOptions.poll,
},
infrastructureLogging: {
// Reduce log verbosity, see https://github.com/facebook/docusaurus/pull/5420#issuecomment-906613105
level: 'warn',
},
plugins: [
// Generates an `index.html` file with the <script> injected.
new html_webpack_plugin_1.default({
template: path_1.default.join(__dirname, '../webpack/templates/index.html.template.ejs'),
// So we can define the position where the scripts are injected.
inject: false,
filename: 'index.html',
title: siteConfig.title,
headTags,
preBodyTags,
postBodyTags,
}),
],
});
// Plugin Lifecycle - configureWebpack and configurePostCss.
plugins.forEach((plugin) => {
const { configureWebpack, configurePostCss } = plugin;
if (configurePostCss) {
config = (0, utils_2.applyConfigurePostCss)(configurePostCss.bind(plugin), config);
}
if (configureWebpack) {
config = (0, utils_2.applyConfigureWebpack)(configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
config, false, props.siteConfig.webpack?.jsLoader, plugin.content);
}
});
const compiler = (0, webpack_1.default)(config);
compiler.hooks.done.tap('done', (stats) => {
const errorsWarnings = stats.toJson('errors-warnings');
const statsErrorMessage = (0, utils_2.formatStatsErrorMessage)(errorsWarnings);
if (statsErrorMessage) {
console.error(statsErrorMessage);
}
(0, utils_2.printStatsWarnings)(errorsWarnings);
if (process.env.E2E_TEST) {
if (stats.hasErrors()) {
logger_1.default.error('E2E_TEST: Project has compiler errors.');
process.exit(1);
}
logger_1.default.success('E2E_TEST: Project can compile.');
process.exit(0);
}
});
// https://webpack.js.org/configuration/dev-server
const defaultDevServerConfig = {
hot: cliOptions.hotOnly ? 'only' : true,
liveReload: false,
client: {
progress: true,
overlay: {
warnings: false,
errors: true,
},
webSocketURL: {
hostname: '0.0.0.0',
port: 0,
},
},
headers: {
'access-control-allow-origin': '*',
},
devMiddleware: {
publicPath: baseUrl,
// Reduce log verbosity, see https://github.com/facebook/docusaurus/pull/5420#issuecomment-906613105
stats: 'summary',
},
static: siteConfig.staticDirectories.map((dir) => ({
publicPath: baseUrl,
directory: path_1.default.resolve(siteDir, dir),
watch: {
// Useful options for our own monorepo using symlinks!
// See https://github.com/webpack/webpack/issues/11612#issuecomment-879259806
followSymlinks: true,
ignored: /node_modules\/(?!@docusaurus)/,
...{ pollingOptions },
},
})),
...(httpsConfig && {
server: typeof httpsConfig === 'object'
? {
type: 'https',
options: httpsConfig,
}
: 'https',
}),
historyApiFallback: {
rewrites: [{ from: /\/*/, to: baseUrl }],
},
allowedHosts: 'all',
host,
port,
setupMiddlewares: (middlewares, devServer) => {
// This lets us fetch source contents from webpack for the error overlay.
middlewares.unshift((0, evalSourceMapMiddleware_1.default)(devServer));
return middlewares;
},
};
// Allow plugin authors to customize/override devServer config
const devServerConfig = (0, webpack_merge_1.default)([defaultDevServerConfig, config.devServer].filter(Boolean));
const devServer = new webpack_dev_server_1.default(devServerConfig, compiler);
devServer.startCallback(() => {
if (cliOptions.open) {
(0, openBrowser_1.default)(openUrl);
}
});
['SIGINT', 'SIGTERM'].forEach((sig) => {
process.on(sig, () => {
devServer.stop();
process.exit();
});
});
}
exports.start = start;

View File

@@ -0,0 +1,23 @@
/**
* 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 type { SwizzleAction, SwizzleComponentConfig } from '@docusaurus/types';
import type { SwizzleCLIOptions } from './common';
export declare const SwizzleActions: SwizzleAction[];
export declare function getAction(componentConfig: SwizzleComponentConfig, options: Pick<SwizzleCLIOptions, 'wrap' | 'eject'>): Promise<SwizzleAction>;
export type ActionParams = {
siteDir: string;
themePath: string;
componentName: string;
typescript: boolean;
};
export type ActionResult = {
createdFiles: string[];
};
export declare function eject({ siteDir, themePath, componentName, typescript, }: ActionParams): Promise<ActionResult>;
export declare function wrap({ siteDir, themePath, componentName: themeComponentName, typescript, importType, }: ActionParams & {
importType?: 'original' | 'init';
}): Promise<ActionResult>;

View File

@@ -0,0 +1,107 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.wrap = exports.eject = exports.getAction = exports.SwizzleActions = void 0;
const tslib_1 = require("tslib");
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const path_1 = tslib_1.__importDefault(require("path"));
const lodash_1 = tslib_1.__importDefault(require("lodash"));
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const utils_1 = require("@docusaurus/utils");
const prompts_1 = require("./prompts");
exports.SwizzleActions = ['wrap', 'eject'];
async function getAction(componentConfig, options) {
if (options.wrap) {
return 'wrap';
}
if (options.eject) {
return 'eject';
}
return (0, prompts_1.askSwizzleAction)(componentConfig);
}
exports.getAction = getAction;
async function isDir(dirPath) {
return ((await fs_extra_1.default.pathExists(dirPath)) && (await fs_extra_1.default.stat(dirPath)).isDirectory());
}
async function eject({ siteDir, themePath, componentName, typescript, }) {
const fromPath = path_1.default.join(themePath, componentName);
const isDirectory = await isDir(fromPath);
const globPattern = isDirectory
? // Do we really want to copy all components?
path_1.default.join(fromPath, '**/*')
: `${fromPath}.*`;
const globPatternPosix = (0, utils_1.posixPath)(globPattern);
const filesToCopy = await (0, utils_1.Globby)(globPatternPosix, {
ignore: lodash_1.default.compact([
'**/*.{story,stories,test,tests}.{js,jsx,ts,tsx}',
// When ejecting JS components, we want to avoid emitting TS files
// In particular the .d.ts files that theme build output contains
typescript ? null : '**/*.{d.ts,ts,tsx}',
'**/{__fixtures__,__tests__}/*',
]),
});
if (filesToCopy.length === 0) {
// This should never happen
throw new Error(logger_1.default.interpolate `No files to copy from path=${fromPath} with glob code=${globPatternPosix}`);
}
const toPath = path_1.default.join(siteDir, utils_1.THEME_PATH);
await fs_extra_1.default.ensureDir(toPath);
const createdFiles = await Promise.all(filesToCopy.map(async (sourceFile) => {
const targetFile = path_1.default.join(toPath, path_1.default.relative(themePath, sourceFile));
try {
const fileContents = await fs_extra_1.default.readFile(sourceFile, 'utf-8');
await fs_extra_1.default.outputFile(targetFile, fileContents.trimStart().replace(/^\/\*.+?\*\/\s*/ms, ''));
}
catch (err) {
logger_1.default.error `Could not copy file from path=${sourceFile} to path=${targetFile}`;
throw err;
}
return targetFile;
}));
return { createdFiles };
}
exports.eject = eject;
async function wrap({ siteDir, themePath, componentName: themeComponentName, typescript, importType = 'original', }) {
const isDirectory = await isDir(path_1.default.join(themePath, themeComponentName));
// Top/Parent/ComponentName => ComponentName
const componentName = lodash_1.default.last(themeComponentName.split('/'));
const wrapperComponentName = `${componentName}Wrapper`;
const wrapperFileName = `${themeComponentName}${isDirectory ? '/index' : ''}${typescript ? '.tsx' : '.js'}`;
await fs_extra_1.default.ensureDir(path_1.default.resolve(siteDir, utils_1.THEME_PATH));
const toPath = path_1.default.resolve(siteDir, utils_1.THEME_PATH, wrapperFileName);
const content = typescript
? `import React from 'react';
import ${componentName} from '@theme-${importType}/${themeComponentName}';
import type ${componentName}Type from '@theme/${themeComponentName}';
import type {WrapperProps} from '@docusaurus/types';
type Props = WrapperProps<typeof ${componentName}Type>;
export default function ${wrapperComponentName}(props: Props): JSX.Element {
return (
<>
<${componentName} {...props} />
</>
);
}
`
: `import React from 'react';
import ${componentName} from '@theme-${importType}/${themeComponentName}';
export default function ${wrapperComponentName}(props) {
return (
<>
<${componentName} {...props} />
</>
);
}
`;
await fs_extra_1.default.outputFile(toPath, content);
return { createdFiles: [toPath] };
}
exports.wrap = wrap;

View File

@@ -0,0 +1,34 @@
/**
* 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 type { NormalizedPluginConfig } from '../../server/plugins/configs';
import type { InitializedPlugin, SwizzleAction, SwizzleActionStatus } from '@docusaurus/types';
export declare const SwizzleActions: SwizzleAction[];
export declare const SwizzleActionsStatuses: SwizzleActionStatus[];
export declare const PartiallySafeHint: string;
export declare function actionStatusLabel(status: SwizzleActionStatus): string;
export declare function actionStatusColor(status: SwizzleActionStatus, str: string): string;
export declare function actionStatusSuffix(status: SwizzleActionStatus, options?: {
partiallySafe?: boolean;
}): string;
export type SwizzlePlugin = {
instance: InitializedPlugin;
plugin: NormalizedPluginConfig;
};
export type SwizzleContext = {
plugins: SwizzlePlugin[];
};
export type SwizzleCLIOptions = {
typescript: boolean;
danger: boolean;
list: boolean;
wrap: boolean;
eject: boolean;
config?: string;
};
export declare function normalizeOptions(options: Partial<SwizzleCLIOptions>): SwizzleCLIOptions;
export declare function findStringIgnoringCase(str: string, values: string[]): string | undefined;
export declare function findClosestValue(str: string, values: string[], maxLevenshtein?: number): string | undefined;

View File

@@ -0,0 +1,57 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.findClosestValue = exports.findStringIgnoringCase = exports.normalizeOptions = exports.actionStatusSuffix = exports.actionStatusColor = exports.actionStatusLabel = exports.PartiallySafeHint = exports.SwizzleActionsStatuses = exports.SwizzleActions = void 0;
const tslib_1 = require("tslib");
const lodash_1 = tslib_1.__importDefault(require("lodash"));
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const leven_1 = tslib_1.__importDefault(require("leven"));
exports.SwizzleActions = ['wrap', 'eject'];
exports.SwizzleActionsStatuses = [
'safe',
'unsafe',
'forbidden',
];
exports.PartiallySafeHint = logger_1.default.red('*');
function actionStatusLabel(status) {
return lodash_1.default.capitalize(status);
}
exports.actionStatusLabel = actionStatusLabel;
const SwizzleActionStatusColors = {
safe: logger_1.default.green,
unsafe: logger_1.default.yellow,
forbidden: logger_1.default.red,
};
function actionStatusColor(status, str) {
const colorFn = SwizzleActionStatusColors[status];
return colorFn(str);
}
exports.actionStatusColor = actionStatusColor;
function actionStatusSuffix(status, options = {}) {
return ` (${actionStatusColor(status, actionStatusLabel(status))}${options.partiallySafe ? exports.PartiallySafeHint : ''})`;
}
exports.actionStatusSuffix = actionStatusSuffix;
function normalizeOptions(options) {
return {
typescript: options.typescript ?? false,
danger: options.danger ?? false,
list: options.list ?? false,
wrap: options.wrap ?? false,
eject: options.eject ?? false,
config: options.config ?? undefined,
};
}
exports.normalizeOptions = normalizeOptions;
function findStringIgnoringCase(str, values) {
return values.find((v) => v.toLowerCase() === str.toLowerCase());
}
exports.findStringIgnoringCase = findStringIgnoringCase;
function findClosestValue(str, values, maxLevenshtein = 3) {
return values.find((v) => (0, leven_1.default)(v, str) <= maxLevenshtein);
}
exports.findClosestValue = findClosestValue;

View File

@@ -0,0 +1,38 @@
/**
* 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 type { SwizzleAction, SwizzleActionStatus, SwizzleComponentConfig, SwizzleConfig } from '@docusaurus/types';
export type ThemeComponents = {
themeName: string;
all: string[];
getConfig: (component: string) => SwizzleComponentConfig;
getDescription: (component: string) => string;
getActionStatus: (component: string, action: SwizzleAction) => SwizzleActionStatus;
isSafeAction: (component: string, action: SwizzleAction) => boolean;
hasAnySafeAction: (component: string) => boolean;
hasAllSafeAction: (component: string) => boolean;
};
/**
* Expand a list of components to include and return parent folders.
* If a folder is not directly a component (no Folder/index.tsx file),
* we still want to be able to swizzle --eject that folder.
* See https://github.com/facebook/docusaurus/pull/7175#issuecomment-1103757218
*
* @param componentNames the original list of component names
*/
export declare function getMissingIntermediateComponentFolderNames(componentNames: string[]): string[];
export declare function readComponentNames(themePath: string): Promise<string[]>;
export declare function listComponentNames(themeComponents: ThemeComponents): string;
export declare function getThemeComponents({ themeName, themePath, swizzleConfig, }: {
themeName: string;
themePath: string;
swizzleConfig: SwizzleConfig;
}): Promise<ThemeComponents>;
export declare function getComponentName({ componentNameParam, themeComponents, list, }: {
componentNameParam: string | undefined;
themeComponents: ThemeComponents;
list: boolean | undefined;
}): Promise<string>;

View File

@@ -0,0 +1,201 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getComponentName = exports.getThemeComponents = exports.listComponentNames = exports.readComponentNames = exports.getMissingIntermediateComponentFolderNames = void 0;
const tslib_1 = require("tslib");
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const path_1 = tslib_1.__importDefault(require("path"));
const lodash_1 = tslib_1.__importDefault(require("lodash"));
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const utils_1 = require("@docusaurus/utils");
const prompts_1 = require("./prompts");
const common_1 = require("./common");
const tables_1 = require("./tables");
const actions_1 = require("./actions");
const formatComponentName = (componentName) => componentName.replace(/[/\\]index\.[jt]sx?/, '').replace(/\.[jt]sx?/, '');
function sortComponentNames(componentNames) {
return componentNames.sort(); // Algo may change?
}
/**
* Expand a list of components to include and return parent folders.
* If a folder is not directly a component (no Folder/index.tsx file),
* we still want to be able to swizzle --eject that folder.
* See https://github.com/facebook/docusaurus/pull/7175#issuecomment-1103757218
*
* @param componentNames the original list of component names
*/
function getMissingIntermediateComponentFolderNames(componentNames) {
function getAllIntermediatePaths(componentName) {
const paths = componentName.split('/');
return lodash_1.default.range(1, paths.length + 1).map((i) => paths.slice(0, i).join('/'));
}
const expandedComponentNames = lodash_1.default.uniq(componentNames.flatMap((componentName) => getAllIntermediatePaths(componentName)));
return lodash_1.default.difference(expandedComponentNames, componentNames);
}
exports.getMissingIntermediateComponentFolderNames = getMissingIntermediateComponentFolderNames;
const skipReadDirNames = ['__test__', '__tests__', '__mocks__', '__fixtures__'];
async function readComponentNames(themePath) {
if (!(await fs_extra_1.default.pathExists(themePath))) {
return [];
}
async function walk(dir) {
const files = await Promise.all((await fs_extra_1.default.readdir(dir)).flatMap(async (file) => {
const fullPath = path_1.default.join(dir, file);
const stat = await fs_extra_1.default.stat(fullPath);
const isDir = stat.isDirectory();
return { file, fullPath, isDir };
}));
return (await Promise.all(files.map(async (file) => {
if (file.isDir) {
if (skipReadDirNames.includes(file.file)) {
return [];
}
return walk(file.fullPath);
}
else if (
// TODO can probably be refactored
/(?<!\.d)\.[jt]sx?$/.test(file.fullPath) &&
!/(?<!\.d)\.(?:test|tests|story|stories)\.[jt]sx?$/.test(file.fullPath)) {
const componentName = formatComponentName((0, utils_1.posixPath)(path_1.default.relative(themePath, file.fullPath)));
return [{ ...file, componentName }];
}
return [];
}))).flat();
}
const componentFiles = await walk(themePath);
const componentNames = componentFiles.map((f) => f.componentName);
return sortComponentNames(componentNames);
}
exports.readComponentNames = readComponentNames;
function listComponentNames(themeComponents) {
if (themeComponents.all.length === 0) {
return 'No component to swizzle.';
}
return `${(0, tables_1.themeComponentsTable)(themeComponents)}
${(0, tables_1.helpTables)()}
`;
}
exports.listComponentNames = listComponentNames;
async function getThemeComponents({ themeName, themePath, swizzleConfig, }) {
const FallbackSwizzleActionStatus = 'unsafe';
const FallbackSwizzleComponentDescription = 'N/A';
const FallbackSwizzleComponentConfig = {
actions: {
wrap: FallbackSwizzleActionStatus,
eject: FallbackSwizzleActionStatus,
},
description: FallbackSwizzleComponentDescription,
};
const FallbackIntermediateFolderSwizzleComponentConfig = {
actions: {
// It doesn't make sense to wrap an intermediate folder
// because it has not any index component
wrap: 'forbidden',
eject: FallbackSwizzleActionStatus,
},
description: FallbackSwizzleComponentDescription,
};
const allInitialComponents = await readComponentNames(themePath);
const missingIntermediateComponentFolderNames = getMissingIntermediateComponentFolderNames(allInitialComponents);
const allComponents = sortComponentNames(allInitialComponents.concat(missingIntermediateComponentFolderNames));
function getConfig(component) {
if (!allComponents.includes(component)) {
throw new Error(`Can't get component config: component doesn't exist: ${component}`);
}
const config = swizzleConfig.components[component];
if (config) {
return config;
}
const isIntermediateFolder = missingIntermediateComponentFolderNames.includes(component);
if (isIntermediateFolder) {
return FallbackIntermediateFolderSwizzleComponentConfig;
}
return (swizzleConfig.components[component] ?? FallbackSwizzleComponentConfig);
}
function getDescription(component) {
return (getConfig(component).description ?? FallbackSwizzleComponentDescription);
}
function getActionStatus(component, action) {
return getConfig(component).actions[action] ?? FallbackSwizzleActionStatus;
}
function isSafeAction(component, action) {
return getActionStatus(component, action) === 'safe';
}
function hasAllSafeAction(component) {
return actions_1.SwizzleActions.every((action) => isSafeAction(component, action));
}
function hasAnySafeAction(component) {
return actions_1.SwizzleActions.some((action) => isSafeAction(component, action));
}
// Present the safest components first
const orderedComponents = lodash_1.default.orderBy(allComponents, [
hasAllSafeAction,
(component) => isSafeAction(component, 'wrap'),
(component) => isSafeAction(component, 'eject'),
(component) => component,
], ['desc', 'desc', 'desc', 'asc']);
return {
themeName,
all: orderedComponents,
getConfig,
getDescription,
getActionStatus,
isSafeAction,
hasAnySafeAction,
hasAllSafeAction,
};
}
exports.getThemeComponents = getThemeComponents;
// Returns a valid value if recovering is possible
function handleInvalidComponentNameParam({ componentNameParam, themeComponents, }) {
// Trying to recover invalid value
// We look for potential matches that only differ in casing.
const differentCaseMatch = (0, common_1.findStringIgnoringCase)(componentNameParam, themeComponents.all);
if (differentCaseMatch) {
logger_1.default.warn `Component name=${componentNameParam} doesn't exist.`;
logger_1.default.info `name=${differentCaseMatch} will be used instead of name=${componentNameParam}.`;
return differentCaseMatch;
}
// No recovery value is possible: print error
logger_1.default.error `Component name=${componentNameParam} not found.`;
const suggestion = (0, common_1.findClosestValue)(componentNameParam, themeComponents.all);
if (suggestion) {
logger_1.default.info `Did you mean name=${suggestion}? ${themeComponents.hasAnySafeAction(suggestion)
? `Note: this component is an unsafe internal component and can only be swizzled with code=${'--danger'} or explicit confirmation.`
: ''}`;
}
else {
logger_1.default.info(listComponentNames(themeComponents));
}
return process.exit(1);
}
async function handleComponentNameParam({ componentNameParam, themeComponents, }) {
const isValidName = themeComponents.all.includes(componentNameParam);
if (!isValidName) {
return handleInvalidComponentNameParam({
componentNameParam,
themeComponents,
});
}
return componentNameParam;
}
async function getComponentName({ componentNameParam, themeComponents, list, }) {
if (list) {
logger_1.default.info(listComponentNames(themeComponents));
return process.exit(0);
}
const componentName = componentNameParam
? await handleComponentNameParam({
componentNameParam,
themeComponents,
})
: await (0, prompts_1.askComponentName)(themeComponents);
return componentName;
}
exports.getComponentName = getComponentName;

View File

@@ -0,0 +1,10 @@
/**
* 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 type { SwizzleConfig } from '@docusaurus/types';
import type { SwizzlePlugin } from './common';
export declare function normalizeSwizzleConfig(unsafeSwizzleConfig: unknown): SwizzleConfig;
export declare function getThemeSwizzleConfig(themeName: string, plugins: SwizzlePlugin[]): SwizzleConfig;

View File

@@ -0,0 +1,84 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getThemeSwizzleConfig = exports.normalizeSwizzleConfig = void 0;
const tslib_1 = require("tslib");
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const utils_validation_1 = require("@docusaurus/utils-validation");
const common_1 = require("./common");
const themes_1 = require("./themes");
function getModuleSwizzleConfig(swizzlePlugin) {
const getSwizzleConfig = swizzlePlugin.plugin.plugin.getSwizzleConfig ??
swizzlePlugin.plugin.pluginModule?.module.getSwizzleConfig;
if (getSwizzleConfig) {
return getSwizzleConfig();
}
// TODO deprecate getSwizzleComponentList later
const getSwizzleComponentList = swizzlePlugin.plugin.plugin.getSwizzleComponentList ??
swizzlePlugin.plugin.pluginModule?.module.getSwizzleComponentList;
if (getSwizzleComponentList) {
const safeComponents = getSwizzleComponentList() ?? [];
const safeComponentConfig = {
actions: {
eject: 'safe',
wrap: 'safe',
},
description: undefined,
};
return {
components: Object.fromEntries(safeComponents.map((comp) => [comp, safeComponentConfig])),
};
}
return undefined;
}
const SwizzleConfigSchema = utils_validation_1.Joi.object({
components: utils_validation_1.Joi.object()
.pattern(utils_validation_1.Joi.string(), utils_validation_1.Joi.object({
actions: utils_validation_1.Joi.object().pattern(utils_validation_1.Joi.string().valid(...common_1.SwizzleActions), utils_validation_1.Joi.string().valid(...common_1.SwizzleActionsStatuses)),
description: utils_validation_1.Joi.string(),
}))
.required(),
});
function validateSwizzleConfig(unsafeSwizzleConfig) {
const result = SwizzleConfigSchema.validate(unsafeSwizzleConfig);
if (result.error) {
throw new Error(`Swizzle config does not match expected schema: ${result.error.message}`);
}
return result.value;
}
function normalizeSwizzleConfig(unsafeSwizzleConfig) {
const swizzleConfig = validateSwizzleConfig(unsafeSwizzleConfig);
// Ensure all components always declare all actions
Object.values(swizzleConfig.components).forEach((componentConfig) => {
common_1.SwizzleActions.forEach((action) => {
if (!componentConfig.actions[action]) {
componentConfig.actions[action] = 'unsafe';
}
});
});
return swizzleConfig;
}
exports.normalizeSwizzleConfig = normalizeSwizzleConfig;
const FallbackSwizzleConfig = {
components: {},
};
function getThemeSwizzleConfig(themeName, plugins) {
const plugin = (0, themes_1.getPluginByThemeName)(plugins, themeName);
const config = getModuleSwizzleConfig(plugin);
if (config) {
try {
return normalizeSwizzleConfig(config);
}
catch (err) {
logger_1.default.error `Invalid Swizzle config for theme name=${themeName}.`;
throw err;
}
}
return FallbackSwizzleConfig;
}
exports.getThemeSwizzleConfig = getThemeSwizzleConfig;

View File

@@ -0,0 +1,8 @@
/**
* 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 type { SwizzleCLIOptions, SwizzleContext } from './common';
export declare function initSwizzleContext(siteDir: string, options: SwizzleCLIOptions): Promise<SwizzleContext>;

View File

@@ -0,0 +1,24 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.initSwizzleContext = void 0;
const server_1 = require("../../server");
const init_1 = require("../../server/plugins/init");
const configs_1 = require("../../server/plugins/configs");
async function initSwizzleContext(siteDir, options) {
const context = await (0, server_1.loadContext)({ siteDir, config: options.config });
const plugins = await (0, init_1.initPlugins)(context);
const pluginConfigs = await (0, configs_1.loadPluginConfigs)(context);
return {
plugins: plugins.map((plugin, pluginIndex) => ({
plugin: pluginConfigs[pluginIndex],
instance: plugin,
})),
};
}
exports.initSwizzleContext = initSwizzleContext;

View File

@@ -0,0 +1,8 @@
/**
* 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 type { SwizzleCLIOptions } from './common';
export declare function swizzle(themeNameParam?: string | undefined, componentNameParam?: string | undefined, siteDirParam?: string, optionsParam?: Partial<SwizzleCLIOptions>): Promise<void>;

View File

@@ -0,0 +1,119 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.swizzle = void 0;
const tslib_1 = require("tslib");
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const themes_1 = require("./themes");
const components_1 = require("./components");
const tables_1 = require("./tables");
const common_1 = require("./common");
const actions_1 = require("./actions");
const config_1 = require("./config");
const prompts_1 = require("./prompts");
const context_1 = require("./context");
async function listAllThemeComponents({ themeNames, plugins, typescript, }) {
const themeComponentsTables = (await Promise.all(themeNames.map(async (themeName) => {
const themePath = (0, themes_1.getThemePath)({ themeName, plugins, typescript });
const swizzleConfig = (0, config_1.getThemeSwizzleConfig)(themeName, plugins);
const themeComponents = await (0, components_1.getThemeComponents)({
themeName,
themePath,
swizzleConfig,
});
return (0, tables_1.themeComponentsTable)(themeComponents);
}))).join('\n\n');
logger_1.default.info(`All theme components available to swizzle:
${themeComponentsTables}
${(0, tables_1.helpTables)()}
`);
return process.exit(0);
}
async function ensureActionSafety({ componentName, componentConfig, action, danger, }) {
const actionStatus = componentConfig.actions[action];
if (actionStatus === 'forbidden') {
logger_1.default.error `
Swizzle action name=${action} is forbidden for component name=${componentName}
`;
return process.exit(1);
}
if (actionStatus === 'unsafe' && !danger) {
logger_1.default.warn `
Swizzle action name=${action} is unsafe to perform on name=${componentName}.
It is more likely to be affected by breaking changes in the future
If you want to swizzle it, use the code=${'--danger'} flag, or confirm that you understand the risks.
`;
const swizzleDangerousComponent = await (0, prompts_1.askSwizzleDangerousComponent)();
if (!swizzleDangerousComponent) {
return process.exit(1);
}
}
return undefined;
}
async function swizzle(themeNameParam = undefined, componentNameParam = undefined, siteDirParam = '.', optionsParam = {}) {
const siteDir = await fs_extra_1.default.realpath(siteDirParam);
const options = (0, common_1.normalizeOptions)(optionsParam);
const { list, danger, typescript } = options;
const { plugins } = await (0, context_1.initSwizzleContext)(siteDir, options);
const themeNames = (0, themes_1.getThemeNames)(plugins);
if (list && !themeNameParam) {
await listAllThemeComponents({ themeNames, plugins, typescript });
}
const themeName = await (0, themes_1.getThemeName)({ themeNameParam, themeNames, list });
const themePath = (0, themes_1.getThemePath)({ themeName, plugins, typescript });
const swizzleConfig = (0, config_1.getThemeSwizzleConfig)(themeName, plugins);
const themeComponents = await (0, components_1.getThemeComponents)({
themeName,
themePath,
swizzleConfig,
});
const componentName = await (0, components_1.getComponentName)({
componentNameParam,
themeComponents,
list,
});
const componentConfig = themeComponents.getConfig(componentName);
const action = await (0, actions_1.getAction)(componentConfig, options);
await ensureActionSafety({ componentName, componentConfig, action, danger });
async function executeAction() {
switch (action) {
case 'wrap': {
const result = await (0, actions_1.wrap)({
siteDir,
themePath,
componentName,
typescript,
});
logger_1.default.success `
Created wrapper of name=${componentName} from name=${themeName} in path=${result.createdFiles}
`;
return result;
}
case 'eject': {
const result = await (0, actions_1.eject)({
siteDir,
themePath,
componentName,
typescript,
});
logger_1.default.success `
Ejected name=${componentName} from name=${themeName} to path=${result.createdFiles}
`;
return result;
}
default:
throw new Error(`Unexpected action ${action}`);
}
}
await executeAction();
return process.exit(0);
}
exports.swizzle = swizzle;

View File

@@ -0,0 +1,12 @@
/**
* 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 type { ThemeComponents } from './components';
import type { SwizzleAction, SwizzleComponentConfig } from '@docusaurus/types';
export declare function askThemeName(themeNames: string[]): Promise<string>;
export declare function askComponentName(themeComponents: ThemeComponents): Promise<string>;
export declare function askSwizzleDangerousComponent(): Promise<boolean>;
export declare function askSwizzleAction(componentConfig: SwizzleComponentConfig): Promise<SwizzleAction>;

View File

@@ -0,0 +1,110 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.askSwizzleAction = exports.askSwizzleDangerousComponent = exports.askComponentName = exports.askThemeName = void 0;
const tslib_1 = require("tslib");
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const prompts_1 = tslib_1.__importDefault(require("prompts"));
const common_1 = require("./common");
const ExitTitle = logger_1.default.yellow('[Exit]');
async function askThemeName(themeNames) {
const { themeName } = (await (0, prompts_1.default)({
type: 'select',
name: 'themeName',
message: 'Select a theme to swizzle:',
choices: themeNames
.map((theme) => ({ title: theme, value: theme }))
.concat({ title: ExitTitle, value: '[Exit]' }),
}));
if (!themeName || themeName === '[Exit]') {
process.exit(0);
}
return themeName;
}
exports.askThemeName = askThemeName;
async function askComponentName(themeComponents) {
function formatComponentName(componentName) {
const anySafe = themeComponents.hasAnySafeAction(componentName);
const allSafe = themeComponents.hasAllSafeAction(componentName);
const safestStatus = anySafe ? 'safe' : 'unsafe'; // Not 100% accurate but good enough for now.
const partiallySafe = anySafe && !allSafe;
return `${componentName}${(0, common_1.actionStatusSuffix)(safestStatus, {
partiallySafe,
})}`;
}
const { componentName } = (await (0, prompts_1.default)({
type: 'autocomplete',
name: 'componentName',
message: `
Select or type the component to swizzle.
${common_1.PartiallySafeHint} = not safe for all swizzle actions
`,
// This doesn't work well in small-height terminals (like IDE)
// limit: 30,
// This does not work well and messes up with terminal scroll position
// limit: Number.POSITIVE_INFINITY,
choices: themeComponents.all
.map((compName) => ({
title: formatComponentName(compName),
value: compName,
}))
.concat({ title: ExitTitle, value: '[Exit]' }),
async suggest(input, choices) {
return choices.filter((choice) => choice.title.toLowerCase().includes(input.toLowerCase()));
},
}));
logger_1.default.newLine();
if (!componentName || componentName === '[Exit]') {
return process.exit(0);
}
return componentName;
}
exports.askComponentName = askComponentName;
async function askSwizzleDangerousComponent() {
const { switchToDanger } = (await (0, prompts_1.default)({
type: 'select',
name: 'switchToDanger',
message: `Do you really want to swizzle this unsafe internal component?`,
choices: [
{ title: logger_1.default.green('NO: cancel and stay safe'), value: false },
{
title: logger_1.default.red('YES: I know what I am doing!'),
value: true,
},
{ title: ExitTitle, value: '[Exit]' },
],
}));
if (typeof switchToDanger === 'undefined' || switchToDanger === '[Exit]') {
return process.exit(0);
}
return !!switchToDanger;
}
exports.askSwizzleDangerousComponent = askSwizzleDangerousComponent;
async function askSwizzleAction(componentConfig) {
const { action } = (await (0, prompts_1.default)({
type: 'select',
name: 'action',
message: `Which swizzle action do you want to do?`,
choices: [
{
title: `${logger_1.default.bold('Wrap')}${(0, common_1.actionStatusSuffix)(componentConfig.actions.wrap)}`,
value: 'wrap',
},
{
title: `${logger_1.default.bold('Eject')}${(0, common_1.actionStatusSuffix)(componentConfig.actions.eject)}`,
value: 'eject',
},
{ title: ExitTitle, value: '[Exit]' },
],
}));
if (typeof action === 'undefined' || action === '[Exit]') {
return process.exit(0);
}
return action;
}
exports.askSwizzleAction = askSwizzleAction;

View File

@@ -0,0 +1,9 @@
/**
* 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 type { ThemeComponents } from './components';
export declare function helpTables(): string;
export declare function themeComponentsTable(themeComponents: ThemeComponents): string;

View File

@@ -0,0 +1,113 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.themeComponentsTable = exports.helpTables = void 0;
const tslib_1 = require("tslib");
const lodash_1 = tslib_1.__importDefault(require("lodash"));
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const cli_table3_1 = tslib_1.__importDefault(require("cli-table3"));
const actions_1 = require("./actions");
const common_1 = require("./common");
function tableStatusLabel(status) {
return (0, common_1.actionStatusColor)(status, (0, common_1.actionStatusLabel)(status));
}
function getStatusLabel(status) {
return (0, common_1.actionStatusColor)(status, (0, common_1.actionStatusLabel)(status));
}
function statusTable() {
const table = new cli_table3_1.default({
head: ['Status', 'CLI option', 'Description'],
});
table.push({
[tableStatusLabel('safe')]: [
'',
`
This component is safe to swizzle and was designed for this purpose.
The swizzled component is retro-compatible with minor version upgrades.
`,
],
}, {
[tableStatusLabel('unsafe')]: [
logger_1.default.code('--danger'),
`
This component is unsafe to swizzle, but you can still do it!
Warning: we may release breaking changes within minor version upgrades.
You will have to upgrade your component manually and maintain it over time.
${logger_1.default.green('Tip')}: your customization can't be done in a ${tableStatusLabel('safe')} way?
Report it here: https://github.com/facebook/docusaurus/discussions/5468
`,
],
}, {
[tableStatusLabel('forbidden')]: [
'',
`
This component is not meant to be swizzled.
`,
],
});
return table.toString();
}
function actionsTable() {
const table = new cli_table3_1.default({
head: ['Actions', 'CLI option', 'Description'],
});
table.push({
[logger_1.default.bold('Wrap')]: [
logger_1.default.code('--wrap'),
`
Creates a wrapper around the original theme component.
Allows rendering other components before/after the original theme component.
${logger_1.default.green('Tip')}: prefer ${logger_1.default.code('--wrap')} whenever possible to reduce the amount of code to maintain.
`,
],
}, {
[logger_1.default.bold('Eject')]: [
logger_1.default.code('--eject'),
`
Ejects the full source code of the original theme component.
Allows overriding of the original component entirely with your own UI and logic.
${logger_1.default.green('Tip')}: ${logger_1.default.code('--eject')} can be useful for completely redesigning a component.
`,
],
});
return table.toString();
}
function helpTables() {
return `${logger_1.default.bold('Swizzle actions')}:
${actionsTable()}
${logger_1.default.bold('Swizzle safety statuses')}:
${statusTable()}
${logger_1.default.bold('Swizzle guide')}: https://docusaurus.io/docs/swizzling`;
}
exports.helpTables = helpTables;
function themeComponentsTable(themeComponents) {
const table = new cli_table3_1.default({
head: [
'Component name',
...actions_1.SwizzleActions.map((action) => lodash_1.default.capitalize(action)),
'Description',
],
});
themeComponents.all.forEach((component) => {
table.push({
[component]: [
...actions_1.SwizzleActions.map((action) => getStatusLabel(themeComponents.getActionStatus(component, action))),
themeComponents.getDescription(component),
],
});
});
return `${logger_1.default.bold(`Components available for swizzle in ${logger_1.default.name(themeComponents.themeName)}`)}:
${table.toString()}
`;
}
exports.themeComponentsTable = themeComponentsTable;

View File

@@ -0,0 +1,20 @@
/**
* 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 { type SwizzlePlugin } from './common';
export declare function pluginToThemeName(plugin: SwizzlePlugin): string | undefined;
export declare function getPluginByThemeName(plugins: SwizzlePlugin[], themeName: string): SwizzlePlugin;
export declare function getThemeNames(plugins: SwizzlePlugin[]): string[];
export declare function getThemeName({ themeNameParam, themeNames, list, }: {
themeNameParam: string | undefined;
themeNames: string[];
list: boolean | undefined;
}): Promise<string>;
export declare function getThemePath({ plugins, themeName, typescript, }: {
plugins: SwizzlePlugin[];
themeName: string;
typescript: boolean | undefined;
}): string;

View File

@@ -0,0 +1,106 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getThemePath = exports.getThemeName = exports.getThemeNames = exports.getPluginByThemeName = exports.pluginToThemeName = void 0;
const tslib_1 = require("tslib");
const path_1 = tslib_1.__importDefault(require("path"));
const lodash_1 = tslib_1.__importDefault(require("lodash"));
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const leven_1 = tslib_1.__importDefault(require("leven"));
const prompts_1 = require("./prompts");
const common_1 = require("./common");
function pluginToThemeName(plugin) {
if (plugin.instance.getThemePath) {
return (plugin.instance.version.name ?? plugin.instance.name);
}
return undefined;
}
exports.pluginToThemeName = pluginToThemeName;
function getPluginByThemeName(plugins, themeName) {
const plugin = plugins.find((p) => pluginToThemeName(p) === themeName);
if (!plugin) {
throw new Error(`Theme ${themeName} not found`);
}
return plugin;
}
exports.getPluginByThemeName = getPluginByThemeName;
function getThemeNames(plugins) {
const themeNames = lodash_1.default.uniq(
// The fact that getThemePath is attached to the plugin instance makes
// this code impossible to optimize. If this is a static method, we don't
// need to initialize all plugins just to filter which are themes
// Benchmark: loadContext-58ms; initPlugins-323ms
plugins.map((plugin) => pluginToThemeName(plugin)).filter(Boolean));
// Opinionated ordering: user is most likely to swizzle:
// - the classic theme
// - official themes
// - official plugins
return lodash_1.default.orderBy(themeNames, [
(t) => t === '@docusaurus/theme-classic',
(t) => t.includes('@docusaurus/theme'),
(t) => t.includes('@docusaurus'),
], ['desc', 'desc', 'desc']);
}
exports.getThemeNames = getThemeNames;
// Returns a valid value if recovering is possible
function handleInvalidThemeName({ themeNameParam, themeNames, }) {
// Trying to recover invalid value
// We look for potential matches that only differ in casing.
const differentCaseMatch = (0, common_1.findStringIgnoringCase)(themeNameParam, themeNames);
if (differentCaseMatch) {
logger_1.default.warn `Theme name=${themeNameParam} doesn't exist.`;
logger_1.default.info `name=${differentCaseMatch} will be used instead of name=${themeNameParam}.`;
return differentCaseMatch;
}
// TODO recover from short theme-names here: "classic" => "@docusaurus/theme-classic"
// No recovery value is possible: print error
const suggestion = themeNames.find((name) => (0, leven_1.default)(name, themeNameParam) < 4);
logger_1.default.error `Theme name=${themeNameParam} not found. ${suggestion
? logger_1.default.interpolate `Did you mean name=${suggestion}?`
: logger_1.default.interpolate `Themes available for swizzle: ${themeNames}`}`;
return process.exit(1);
}
function validateThemeName({ themeNameParam, themeNames, }) {
const isValidName = themeNames.includes(themeNameParam);
if (!isValidName) {
return handleInvalidThemeName({
themeNameParam,
themeNames,
});
}
return themeNameParam;
}
async function getThemeName({ themeNameParam, themeNames, list, }) {
if (list && !themeNameParam) {
logger_1.default.info `Themes available for swizzle: name=${themeNames}`;
return process.exit(0);
}
return themeNameParam
? validateThemeName({ themeNameParam, themeNames })
: (0, prompts_1.askThemeName)(themeNames);
}
exports.getThemeName = getThemeName;
function getThemePath({ plugins, themeName, typescript, }) {
const pluginInstance = getPluginByThemeName(plugins, themeName);
const themePath = typescript
? pluginInstance.instance.getTypeScriptThemePath &&
path_1.default.resolve(pluginInstance.instance.path, pluginInstance.instance.getTypeScriptThemePath())
: pluginInstance.instance.getThemePath &&
path_1.default.resolve(pluginInstance.instance.path, pluginInstance.instance.getThemePath());
if (!themePath) {
logger_1.default.warn(typescript
? logger_1.default.interpolate `name=${themeName} does not provide TypeScript theme code via ${'getTypeScriptThemePath()'}.`
: // This is... technically possible to happen, e.g. returning undefined
// from getThemePath. Plugins may intentionally or unintentionally
// disguise as themes?
logger_1.default.interpolate `name=${themeName} does not provide any theme code.`);
return process.exit(1);
}
return themePath;
}
exports.getThemePath = getThemePath;

View File

@@ -0,0 +1,8 @@
/**
* 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 { type WriteHeadingIDOptions } from '@docusaurus/utils';
export declare function writeHeadingIds(siteDirParam?: string, files?: string[], options?: WriteHeadingIDOptions): Promise<void>;

View File

@@ -0,0 +1,51 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.writeHeadingIds = void 0;
const tslib_1 = require("tslib");
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const utils_1 = require("@docusaurus/utils");
const server_1 = require("../server");
const init_1 = require("../server/plugins/init");
const utils_2 = require("../server/utils");
async function transformMarkdownFile(filepath, options) {
const content = await fs_extra_1.default.readFile(filepath, 'utf8');
const updatedContent = (0, utils_1.writeMarkdownHeadingId)(content, options);
if (content !== updatedContent) {
await fs_extra_1.default.writeFile(filepath, updatedContent);
return filepath;
}
return undefined;
}
/**
* We only handle the "paths to watch" because these are the paths where the
* markdown files are. Also we don't want to transform the site md docs that do
* not belong to a content plugin. For example ./README.md should not be
* transformed
*/
async function getPathsToWatch(siteDir) {
const context = await (0, server_1.loadContext)({ siteDir });
const plugins = await (0, init_1.initPlugins)(context);
return plugins.flatMap((plugin) => plugin.getPathsToWatch?.() ?? []);
}
async function writeHeadingIds(siteDirParam = '.', files = [], options = {}) {
const siteDir = await fs_extra_1.default.realpath(siteDirParam);
const markdownFiles = await (0, utils_2.safeGlobby)(files ?? (await getPathsToWatch(siteDir)), {
expandDirectories: ['**/*.{md,mdx}'],
});
const result = await Promise.all(markdownFiles.map((p) => transformMarkdownFile(p, options)));
const pathsModified = result.filter(Boolean);
if (pathsModified.length) {
logger_1.default.success `Heading ids added to Markdown files (number=${`${pathsModified.length}/${markdownFiles.length}`} files): ${pathsModified}`;
}
else {
logger_1.default.warn `number=${markdownFiles.length} Markdown files already have explicit heading IDs. If you intend to overwrite the existing heading IDs, use the code=${'--overwrite'} option.`;
}
}
exports.writeHeadingIds = writeHeadingIds;

View File

@@ -0,0 +1,10 @@
/**
* 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 { type LoadContextOptions } from '../server';
import { type WriteTranslationsOptions } from '../server/translations/translations';
export type WriteTranslationsCLIOptions = Pick<LoadContextOptions, 'config' | 'locale'> & WriteTranslationsOptions;
export declare function writeTranslations(siteDirParam?: string, options?: Partial<WriteTranslationsCLIOptions>): Promise<void>;

View File

@@ -0,0 +1,85 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.writeTranslations = void 0;
const tslib_1 = require("tslib");
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const path_1 = tslib_1.__importDefault(require("path"));
const server_1 = require("../server");
const init_1 = require("../server/plugins/init");
const translations_1 = require("../server/translations/translations");
const translationsExtractor_1 = require("../server/translations/translationsExtractor");
const utils_1 = require("../webpack/utils");
function resolveThemeCommonLibDir() {
try {
return path_1.default.dirname(require.resolve('@docusaurus/theme-common'));
}
catch {
return undefined;
}
}
/**
* This is a hack, so that @docusaurus/theme-common translations are extracted!
* A theme doesn't have a way to express that one of its dependency (like
* @docusaurus/theme-common) also has translations to extract.
* Instead of introducing a new lifecycle (like `getThemeTranslationPaths()`?)
* We just make an exception and assume that user is using an official theme
*/
async function getExtraSourceCodeFilePaths() {
const themeCommonLibDir = resolveThemeCommonLibDir();
if (!themeCommonLibDir) {
return []; // User may not use a Docusaurus official theme? Quite unlikely...
}
return (0, translationsExtractor_1.globSourceCodeFilePaths)([themeCommonLibDir]);
}
async function writePluginTranslationFiles({ localizationDir, plugin, options, }) {
if (plugin.getTranslationFiles) {
const content = await plugin.loadContent?.();
const translationFiles = await plugin.getTranslationFiles({
content,
});
await Promise.all(translationFiles.map(async (translationFile) => {
await (0, translations_1.writePluginTranslations)({
localizationDir,
plugin,
translationFile,
options,
});
}));
}
}
async function writeTranslations(siteDirParam = '.', options = {}) {
const siteDir = await fs_extra_1.default.realpath(siteDirParam);
const context = await (0, server_1.loadContext)({
siteDir,
config: options.config,
locale: options.locale,
});
const { localizationDir } = context;
const plugins = await (0, init_1.initPlugins)(context);
const locale = options.locale ?? context.i18n.defaultLocale;
if (!context.i18n.locales.includes(locale)) {
throw new Error(`Can't write-translation for locale "${locale}" that is not in the locale configuration file.
Available locales are: ${context.i18n.locales.join(',')}.`);
}
const babelOptions = (0, utils_1.getBabelOptions)({
isServer: true,
babelOptions: await (0, utils_1.getCustomBabelConfigFilePath)(siteDir),
});
const extractedCodeTranslations = await (0, translationsExtractor_1.extractSiteSourceCodeTranslations)(siteDir, plugins, babelOptions, await getExtraSourceCodeFilePaths());
const defaultCodeMessages = await (0, translations_1.getPluginsDefaultCodeTranslationMessages)(plugins);
const codeTranslations = (0, translations_1.applyDefaultCodeTranslations)({
extractedCodeTranslations,
defaultCodeMessages,
});
await (0, translations_1.writeCodeTranslations)({ localizationDir }, codeTranslations, options);
await Promise.all(plugins.map(async (plugin) => {
await writePluginTranslationFiles({ localizationDir, plugin, options });
}));
}
exports.writeTranslations = writeTranslations;