chore: clean up ESLint config, enable a few rules (#6514)

* chore: clean up ESLint config, enable a few rules

* enable max-len for comments

* fix build
This commit is contained in:
Joshua Chen 2022-01-31 10:31:24 +08:00 committed by GitHub
parent b8ccb869f1
commit aa446b7a9c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
167 changed files with 1157 additions and 960 deletions

View file

@ -13,7 +13,8 @@ function getTransformOptions(isServer: boolean): TransformOptions {
require.resolve(`@babel/runtime/package.json`),
);
return {
// All optional newlines and whitespace will be omitted when generating code in compact mode
// All optional newlines and whitespace will be omitted when generating code
// in compact mode
compact: true,
presets: [
isServer
@ -48,9 +49,9 @@ function getTransformOptions(isServer: boolean): TransformOptions {
{
corejs: false,
helpers: true,
// By default, it assumes @babel/runtime@7.0.0. Since we use >7.0.0, better to
// explicitly specify the version so that it can reuse the helper better
// See https://github.com/babel/babel/issues/10261
// By default, it assumes @babel/runtime@7.0.0. Since we use >7.0.0,
// better to explicitly specify the version so that it can reuse the
// helper better. See https://github.com/babel/babel/issues/10261
// eslint-disable-next-line @typescript-eslint/no-var-requires, global-require
version: require('@babel/runtime/package.json').version,
regenerator: true,

View file

@ -49,8 +49,8 @@ class PendingNavigation extends React.Component<Props, State> {
const routeDidChange = nextProps.location !== this.props.location;
const {routes, delay} = this.props;
// If `routeDidChange` is true, means the router is trying to navigate to a new
// route. We will preload the new route.
// If `routeDidChange` is true, means the router is trying to navigate to a
// new route. We will preload the new route.
if (routeDidChange) {
const nextLocation = normalizeLocation(nextProps.location);
this.startProgressBar(delay);
@ -99,14 +99,14 @@ class PendingNavigation extends React.Component<Props, State> {
return true;
}
clearProgressBarTimeout() {
private clearProgressBarTimeout() {
if (this.progressBarTimeout) {
clearTimeout(this.progressBarTimeout);
this.progressBarTimeout = null;
}
}
startProgressBar(delay: number) {
private startProgressBar(delay: number) {
this.clearProgressBarTimeout();
this.progressBarTimeout = setTimeout(() => {
clientLifecyclesDispatcher.onRouteUpdateDelayed({
@ -116,7 +116,7 @@ class PendingNavigation extends React.Component<Props, State> {
}, delay);
}
stopProgressBar() {
private stopProgressBar() {
this.clearProgressBarTimeout();
nprogress.done();
}

View file

@ -101,11 +101,14 @@ function BaseUrlIssueBannerEnabled() {
);
}
// We want to help the users with a bad baseUrl configuration (very common error)
// Help message is inlined, and hidden if JS or CSS is able to load
// Note: it might create false positives (ie network failures): not a big deal
// Note: we only inline this for the homepage to avoid polluting all the site's pages
// See https://github.com/facebook/docusaurus/pull/3621
/**
* We want to help the users with a bad baseUrl configuration (very common
* error) Help message is inlined, and hidden if JS or CSS is able to load
* Note: it might create false positives (ie network failures): not a big deal
* Note: we only inline this for the homepage to avoid polluting all the site's
* pages
* @see https://github.com/facebook/docusaurus/pull/3621
*/
export default function BaseUrlIssueBanner(): JSX.Element | null {
const {
siteConfig: {baseUrl, baseUrlIssueBanner},

View file

@ -21,12 +21,14 @@ declare global {
}
}
// Client-side render (e.g: running in browser) to become single-page application (SPA).
// Client-side render (e.g: running in browser) to become single-page
// application (SPA).
if (ExecutionEnvironment.canUseDOM) {
window.docusaurus = docusaurus;
// For production, attempt to hydrate existing markup for performant first-load experience.
// For production, attempt to hydrate existing markup for performant
// first-load experience.
// For development, there is no existing markup so we had to render it.
// Note that we also preload async component to avoid first-load loading screen.
// We also preload async component to avoid first-load loading screen.
const renderMethod = process.env.NODE_ENV === 'production' ? hydrate : render;
preload(routes, window.location.pathname).then(() => {
renderMethod(

View file

@ -16,7 +16,7 @@ const fetched: Record<string, boolean> = {};
const loaded: Record<string, boolean> = {};
declare global {
// eslint-disable-next-line camelcase
// eslint-disable-next-line camelcase, no-underscore-dangle
const __webpack_require__: {gca: (name: string) => string};
interface Navigator {
connection: {effectiveType: string; saveData: boolean};
@ -66,12 +66,14 @@ const docusaurus = {
// Prefetch all webpack chunk assets file needed.
chunkNamesNeeded.forEach((chunkName) => {
// "__webpack_require__.gca" is a custom function provided by ChunkAssetPlugin.
// Pass it the chunkName or chunkId you want to load and it will return the URL for that chunk.
// "__webpack_require__.gca" is a custom function provided by
// ChunkAssetPlugin. Pass it the chunkName or chunkId you want to load and
// it will return the URL for that chunk.
// eslint-disable-next-line camelcase
const chunkAsset = __webpack_require__.gca(chunkName);
// In some cases, webpack might decide to optimize further & hence the chunk assets are merged to another chunk/previous chunk.
// In some cases, webpack might decide to optimize further & hence the
// chunk assets are merged to another chunk/previous chunk.
// Hence, we can safely filter it out/don't need to load it.
if (chunkAsset && !/undefined/.test(chunkAsset)) {
prefetchHelper(chunkAsset);

View file

@ -41,7 +41,10 @@ function ComponentCreator(
content.foo: () => import('./doc1.md'),
}
- optsModules: ['./Pages.js', './doc1.md']
- optsWebpack: [require.resolveWeak('./Pages.js'), require.resolveWeak('./doc1.md')]
- optsWebpack: [
require.resolveWeak('./Pages.js'),
require.resolveWeak('./doc1.md'),
]
*/
const flatChunkNames = flat(chunkNames);
Object.keys(flatChunkNames).forEach((key) => {

View file

@ -21,7 +21,7 @@ More details here: https://github.com/facebook/docusaurus/pull/4295
const ValueRegexp = /{\w+}/g;
const ValueFoundMarker = '{}'; // does not care much
// TS function overload: if all the values are plain strings, then interpolate returns a simple string
// If all the values are plain strings, then interpolate returns a simple string
export function interpolate<Str extends string>(
text: Str,
values?: InterpolateValues<Str, string | number>,
@ -55,9 +55,8 @@ export function interpolate<Str extends string, Value extends ReactNode>(
String(value);
elements.push(element);
return ValueFoundMarker;
} else {
return match; // no match? add warning?
}
return match; // no match? add warning?
});
// No interpolation to be done: just return the text
@ -65,7 +64,7 @@ export function interpolate<Str extends string, Value extends ReactNode>(
return text;
}
// Basic string interpolation: returns interpolated string
else if (elements.every((el) => typeof el === 'string')) {
if (elements.every((el) => typeof el === 'string')) {
return processedText
.split(ValueFoundMarker)
.reduce<string>(
@ -75,18 +74,16 @@ export function interpolate<Str extends string, Value extends ReactNode>(
);
}
// JSX interpolation: returns ReactNode
else {
return processedText.split(ValueFoundMarker).reduce<ReactNode[]>(
(array, value, index) => [
...array,
<React.Fragment key={index}>
{value}
{elements[index]}
</React.Fragment>,
],
[],
);
}
return processedText.split(ValueFoundMarker).reduce<ReactNode[]>(
(array, value, index) => [
...array,
<React.Fragment key={index}>
{value}
{elements[index]}
</React.Fragment>,
],
[],
);
}
export default function Interpolate<Str extends string>({

View file

@ -95,7 +95,7 @@ function Link({
ioRef.current = new window.IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (el === entry.target) {
// If element is in viewport, stop listening/observing and run callback.
// If element is in viewport, stop observing and run callback.
// https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
if (entry.isIntersecting || entry.intersectionRatio > 0) {
ioRef.current!.unobserve(el);
@ -112,7 +112,7 @@ function Link({
const handleRef = (ref: HTMLAnchorElement | null) => {
if (IOSupported && ref && isInternal) {
// If IO supported and element reference found, setup Observer functionality.
// If IO supported and element reference found, set up Observer.
handleIntersection(ref, () => {
if (targetLink != null) {
window.docusaurus.prefetch(targetLink);
@ -165,7 +165,8 @@ function Link({
onMouseEnter={onMouseEnter}
innerRef={handleRef}
to={targetLink || ''}
// avoid "React does not recognize the `activeClassName` prop on a DOM element"
// avoid "React does not recognize the `activeClassName` prop on a DOM
// element"
{...(isNavLink && {isActive, activeClassName})}
/>
);

View file

@ -40,7 +40,8 @@ export function translate<Str extends string>(
}
// Maybe we'll want to improve this component with additional features
// Like toggling a translation mode that adds a little translation button near the text?
// Like toggling a translation mode that adds a little translation button near
// the text?
export default function Translate<Str extends string>({
children,
id,

View file

@ -13,7 +13,8 @@ import React, {type ReactNode, useEffect, useState} from 'react';
// isBrowser is set to true only after a successful hydration
// Note, isBrowser is not part of useDocusaurusContext() for perf reasons
// Using useDocusaurusContext() (much more common need) should not trigger re-rendering after a successful hydration
// Using useDocusaurusContext() (much more common need) should not trigger
// re-rendering after a successful hydration
export const Context = React.createContext<boolean>(false);

View file

@ -9,7 +9,8 @@ import {matchRoutes, type RouteConfig} from 'react-router-config';
/**
* Helper function to make sure all async components for that particular route
* is preloaded before rendering. This is especially useful to avoid loading screens.
* is preloaded before rendering. This is especially useful to avoid loading
* screens.
*
* @param routes react-router-config
* @param pathname the route pathname, example: /docs/installation

View file

@ -66,7 +66,7 @@ It might also require to wrap your client code in code=${'useEffect'} hook and/o
}
}
// Renderer for static-site-generator-webpack-plugin (async rendering via promises).
// Renderer for static-site-generator-webpack-plugin (async rendering).
async function doRender(locals: Locals & {path: string}) {
const {
routesLocation,

View file

@ -32,11 +32,13 @@ function ErrorDisplay({error, tryAgain}: Props): JSX.Element {
}
function Error({error, tryAgain}: Props): JSX.Element {
// We wrap the error in its own error boundary because the layout can actually throw too...
// Only the ErrorDisplay component is simple enough to be considered safe to never throw
// We wrap the error in its own error boundary because the layout can actually
// throw too... Only the ErrorDisplay component is simple enough to be
// considered safe to never throw
return (
<ErrorBoundary
// Note: we display the original error here, not the error that we captured in this extra error boundary
// Note: we display the original error here, not the error that we
// captured in this extra error boundary
fallback={() => <ErrorDisplay error={error} tryAgain={tryAgain} />}>
<Layout title="Page Error">
<ErrorDisplay error={error} tryAgain={tryAgain} />

View file

@ -72,25 +72,24 @@ export default async function build(
});
if (cliOptions.locale) {
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}`;
}
// 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: string[] = [
i18n.defaultLocale,
...i18n.locales.filter((locale) => locale !== i18n.defaultLocale),
];
const results = await mapAsyncSequential(orderedLocales, (locale) => {
const isLastLocale =
orderedLocales.indexOf(locale) === orderedLocales.length - 1;
return tryToBuildLocale({locale, isLastLocale});
});
return results[0];
}
if (i18n.locales.length > 1) {
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. If we build it
// last, it would "erase" the localized sites built in sub-folders
const orderedLocales: string[] = [
i18n.defaultLocale,
...i18n.locales.filter((locale) => locale !== i18n.defaultLocale),
];
const results = await mapAsyncSequential(orderedLocales, (locale) => {
const isLastLocale =
orderedLocales.indexOf(locale) === orderedLocales.length - 1;
return tryToBuildLocale({locale, isLastLocale});
});
return results[0];
}
async function buildLocale({
@ -136,7 +135,8 @@ async function buildLocale({
plugins: [
// Remove/clean build folders before building bundles.
new CleanWebpackPlugin({verbose: false}),
// Visualize size of webpack output files with an interactive zoomable tree map.
// Visualize size of webpack output files with an interactive zoomable
// tree map.
cliOptions.bundleAnalyzer && new BundleAnalyzerPlugin(),
// Generate client manifests file that will be used for server bundle.
new ReactLoadableSSRAddon({
@ -220,7 +220,6 @@ async function buildLocale({
if (!plugin.postBuild) {
return;
}
// The plugin may reference `this`. We manually bind it again to prevent any bugs.
await plugin.postBuild({...props, content: plugin.content});
}),
);

View file

@ -148,8 +148,8 @@ This behavior can have SEO impacts and create relative link issues.
shell.exit(0);
}
// github.io indicates organization repos that deploy via default branch. All others use gh-pages.
// Organization deploys looks like:
// github.io indicates organization repos that deploy via default branch.
// All others use gh-pages. Organization deploys looks like:
// - Git repo: https://github.com/<organization>/<organization>.github.io
// - Site url: https://<organization>.github.io
const isGitHubPagesOrganizationDeploy = projectName.includes('.github.io');
@ -269,7 +269,7 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
};
if (!cliOptions.skipBuild) {
// Build static html files, then push to deploymentBranch branch of specified repo.
// Build site, then push to deploymentBranch branch of specified repo.
try {
await runDeploy(await build(siteDir, cliOptions, false));
} catch (buildError) {

View file

@ -59,8 +59,8 @@ export default async function serve(
return;
}
// Remove baseUrl before calling serveHandler
// Reason: /baseUrl/ should serve /build/index.html, not /build/baseUrl/index.html (does not exist)
// 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, '/');
serveHandler(req, res, {

View file

@ -240,7 +240,8 @@ export default async function swizzle(
let score = formattedComponentName.length;
components.forEach((component) => {
if (component.toLowerCase() === formattedComponentName.toLowerCase()) {
// may be components with same lowercase key, try to match closest component
// may be components with same lowercase key, try to match closest
// component
const currentScore = leven(formattedComponentName, component);
if (currentScore < score) {
score = currentScore;
@ -259,7 +260,8 @@ export default async function swizzle(
let fromPath = path.join(themePath, mostSuitableComponent);
let toPath = path.resolve(siteDir, THEME_PATH, mostSuitableComponent);
// Handle single TypeScript/JavaScript file only.
// E.g: if <fromPath> does not exist, we try to swizzle <fromPath>.(ts|tsx|js) instead
// E.g: if <fromPath> does not exist, we try to swizzle
// <fromPath>.(ts|tsx|js) instead
if (!fs.existsSync(fromPath)) {
if (fs.existsSync(`${fromPath}.ts`)) {
[fromPath, toPath] = [`${fromPath}.ts`, `${toPath}.ts`];

View file

@ -73,9 +73,8 @@ function transformMarkdownLine(
// Ignore h1 headings on purpose, as we don't create anchor links for those
if (line.startsWith('##')) {
return transformMarkdownHeadingLine(line, slugger, options);
} else {
return line;
}
return line;
}
function transformMarkdownLines(lines: string[], options?: Options): string[] {
@ -86,12 +85,11 @@ function transformMarkdownLines(lines: string[], options?: Options): string[] {
if (line.startsWith('```')) {
inCode = !inCode;
return line;
} else {
if (inCode) {
return line;
}
return transformMarkdownLine(line, slugger, options);
}
if (inCode) {
return line;
}
return transformMarkdownLine(line, slugger, options);
});
}
@ -118,9 +116,12 @@ async function transformMarkdownFile(
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
/**
* 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: string): Promise<string[]> {
const context = await loadContext(siteDir);
const pluginConfigs = loadPluginConfigs(context);

View file

@ -23,10 +23,13 @@ import {
} from '../server/translations/translationsExtractor';
import {getCustomBabelConfigFilePath, getBabelOptions} from '../webpack/utils';
// 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 plugin.getThemeTranslationPaths() ?)
// We just make an exception and assume that Docusaurus user is using an official theme
/**
* 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(): Promise<string[]> {
try {
const themeCommonSourceDir = path.dirname(

View file

@ -168,10 +168,13 @@ describe('normalizeConfig', () => {
['this/should/work', {too: 'yes'}],
],
],
['should accept function for plugin', [function (_context, _options) {}]],
[
'should accept function for plugin',
[function plugin(_context, _options) {}],
],
[
'should accept [function, object] for plugin',
[[function (_context, _options) {}, {it: 'should work'}]],
[[(_context, _options) => {}, {it: 'should work'}]],
],
])(`%s for the input of: %p`, (_message, plugins) => {
expect(() => {
@ -206,10 +209,13 @@ describe('normalizeConfig', () => {
['this/should/work', {too: 'yes'}],
],
],
['should accept function for theme', [function (_context, _options) {}]],
[
'should accept function for theme',
[function theme(_context, _options) {}],
],
[
'should accept [function, object] for theme',
[[function (_context, _options) {}, {it: 'should work'}]],
[[function theme(_context, _options) {}, {it: 'should work'}]],
],
])(`%s for the input of: %p`, (_message, themes) => {
expect(() => {

View file

@ -64,10 +64,12 @@ function getPageBrokenLinks({
return pageLinks.map(resolveLink).filter((l) => isBrokenLink(l.resolvedLink));
}
// The route defs can be recursive, and have a parent match-all route
// We don't want to match broken links like /docs/brokenLink against /docs/*
// For this reason, we only consider the "final routes", that do not have subroutes
// We also need to remove the match all 404 route
/**
* The route defs can be recursive, and have a parent match-all route. We don't
* want to match broken links like /docs/brokenLink against /docs/*. For this
* reason, we only consider the "final routes", that do not have subroutes.
* We also need to remove the match all 404 route
*/
function filterIntermediateRoutes(routesInput: RouteConfig[]): RouteConfig[] {
const routesWithout404 = routesInput.filter((route) => route.path !== '*');
return getAllFinalRoutes(routesWithout404);
@ -113,9 +115,11 @@ export function getBrokenLinksErrorMessage(
.join('\n -> linking to ')}`;
}
// If there's a broken link appearing very often, it is probably a broken link on the layout!
// Add an additional message in such case to help user figure this out.
// see https://github.com/facebook/docusaurus/issues/3567#issuecomment-706973805
/**
* If there's a broken link appearing very often, it is probably a broken link
* on the layout. Add an additional message in such case to help user figure
* this out. See https://github.com/facebook/docusaurus/issues/3567#issuecomment-706973805
*/
function getLayoutBrokenLinksHelpMessage() {
const flatList = Object.entries(allBrokenLinks).flatMap(
([pagePage, brokenLinks]) =>
@ -215,8 +219,9 @@ export async function handleBrokenLinks({
return;
}
// If we link to a file like /myFile.zip, and the file actually exist for the file system
// it is not a broken link, it may simply be a link to an existing static file...
// If we link to a file like /myFile.zip, and the file actually exist for the
// file system. It is not a broken link, it may simply be a link to an
// existing static file...
const allCollectedLinksFiltered = await filterExistingFileLinks({
allCollectedLinks,
baseUrl,

View file

@ -19,10 +19,9 @@ export function getAllDuplicateRoutes(
return allRoutes.filter((route) => {
if (Object.prototype.hasOwnProperty.call(seenRoutes, route)) {
return true;
} else {
seenRoutes[route] = true;
return false;
}
seenRoutes[route] = true;
return false;
});
}

View file

@ -91,20 +91,17 @@ export function localizePath({
i18n.currentLocale !== i18n.defaultLocale
: options.localizePath;
if (shouldLocalizePath) {
// FS paths need special care, for Windows support
if (pathType === 'fs') {
return path.join(originalPath, path.sep, i18n.currentLocale, path.sep);
}
// Url paths
else if (pathType === 'url') {
return normalizeUrl([originalPath, '/', i18n.currentLocale, '/']);
}
// should never happen
else {
throw new Error(`Unhandled path type "${pathType}".`);
}
} else {
if (!shouldLocalizePath) {
return originalPath;
}
// FS paths need special care, for Windows support
if (pathType === 'fs') {
return path.join(originalPath, path.sep, i18n.currentLocale, path.sep);
}
// Url paths
if (pathType === 'url') {
return normalizeUrl([originalPath, '/', i18n.currentLocale, '/']);
}
// should never happen
throw new Error(`Unhandled path type "${pathType}".`);
}

View file

@ -221,10 +221,12 @@ function createBootstrapPlugin({
};
}
// Configure Webpack fallback mdx loader for md/mdx files out of content-plugin folders
// Adds a "fallback" mdx loader for mdx files that are not processed by content plugins
// This allows to do things such as importing repo/README.md as a partial from another doc
// Not ideal solution though, but good enough for now
/**
* Configure Webpack fallback mdx loader for md/mdx files out of content-plugin
* folders. Adds a "fallback" mdx loader for mdx files that are not processed by
* content plugins. This allows to do things such as importing repo/README.md as
* a partial from another doc. Not ideal solution, but good enough for now
*/
function createMDXFallbackPlugin({
siteDir,
siteConfig,
@ -238,9 +240,9 @@ function createMDXFallbackPlugin({
options: {},
version: {type: 'synthetic'},
configureWebpack(config, isServer, {getJSLoader}) {
// We need the mdx fallback loader to exclude files that were already processed by content plugins mdx loaders
// This works, but a bit hacky...
// Not sure there's a way to handle that differently in webpack :s
// We need the mdx fallback loader to exclude files that were already
// processed by content plugins mdx loaders. This works, but a bit
// hacky... Not sure there's a way to handle that differently in webpack
function getMDXFallbackExcludedPaths(): string[] {
const rules: RuleSetRule[] = config?.module?.rules as RuleSetRule[];
return rules.flatMap((rule) => {

View file

@ -84,9 +84,9 @@ export async function loadPlugins({
});
// 2. Plugin Lifecycle - loadContent.
// Currently plugins run lifecycle methods in parallel and are not order-dependent.
// We could change this in future if there are plugins which need to
// run in certain order or depend on others for data.
// Currently plugins run lifecycle methods in parallel and are not
// order-dependent. We could change this in future if there are plugins which
// need to run in certain order or depend on others for data.
const loadedPlugins: LoadedPlugin[] = await Promise.all(
plugins.map(async (plugin) => {
const content = plugin.loadContent ? await plugin.loadContent() : null;
@ -199,9 +199,9 @@ export async function loadPlugins({
);
// 4. Plugin Lifecycle - routesLoaded.
// Currently plugins run lifecycle methods in parallel and are not order-dependent.
// We could change this in future if there are plugins which need to
// run in certain order or depend on others for data.
// Currently plugins run lifecycle methods in parallel and are not
// order-dependent. We could change this in future if there are plugins which
// need to run in certain order or depend on others for data.
await Promise.all(
contentLoadedTranslatedPlugins.map(async (plugin) => {
if (!plugin.routesLoaded) {

View file

@ -105,9 +105,8 @@ function getOptionValidationFunction(
normalizedPluginConfig.pluginModule.module?.default?.validateOptions ??
normalizedPluginConfig.pluginModule.module?.validateOptions
);
} else {
return normalizedPluginConfig.plugin.validateOptions;
}
return normalizedPluginConfig.plugin.validateOptions;
}
function getThemeValidationFunction(
@ -119,9 +118,8 @@ function getThemeValidationFunction(
normalizedPluginConfig.pluginModule.module.default?.validateThemeConfig ??
normalizedPluginConfig.pluginModule.module.validateThemeConfig
);
} else {
return normalizedPluginConfig.plugin.validateThemeConfig;
}
return normalizedPluginConfig.plugin.validateThemeConfig;
}
export default async function initPlugins({
@ -131,8 +129,8 @@ export default async function initPlugins({
pluginConfigs: PluginConfig[];
context: LoadContext;
}): Promise<InitializedPlugin[]> {
// We need to resolve plugins from the perspective of the siteDir, since the siteDir's package.json
// declares the dependency on these plugins.
// We need to resolve plugins from the perspective of the siteDir, since the
// siteDir's package.json declares the dependency on these plugins.
const pluginRequire = createRequire(context.siteConfigPath);
function doGetPluginVersion(
@ -144,9 +142,8 @@ export default async function initPlugins({
normalizedPluginConfig.pluginModule?.path,
);
return getPluginVersion(pluginPath, context.siteDir);
} else {
return {type: 'local'};
}
return {type: 'local'};
}
function doValidateThemeConfig(
@ -160,9 +157,8 @@ export default async function initPlugins({
validate: normalizeThemeConfig,
themeConfig: context.siteConfig.themeConfig,
});
} else {
return context.siteConfig.themeConfig;
}
return context.siteConfig.themeConfig;
}
function doValidatePluginOptions(
@ -174,14 +170,13 @@ export default async function initPlugins({
validate: normalizePluginOptions,
options: normalizedPluginConfig.options,
});
} else {
// Important to ensure all plugins have an id
// as we don't go through the Joi schema that adds it
return {
...normalizedPluginConfig.options,
id: normalizedPluginConfig.options.id ?? DEFAULT_PLUGIN_ID,
};
}
// Important to ensure all plugins have an id
// as we don't go through the Joi schema that adds it
return {
...normalizedPluginConfig.options,
id: normalizedPluginConfig.options.id ?? DEFAULT_PLUGIN_ID,
};
}
async function initializePlugin(

View file

@ -19,8 +19,8 @@ export default function loadPresets(context: LoadContext): {
plugins: PluginConfig[];
themes: PluginConfig[];
} {
// We need to resolve presets from the perspective of the siteDir, since the siteDir's package.json
// declares the dependency on these presets.
// We need to resolve presets from the perspective of the siteDir, since the
// siteDir's package.json declares the dependency on these presets.
const presetRequire = createRequire(context.siteConfigPath);
const presets: PresetConfig[] = context.siteConfig.presets || [];
@ -28,7 +28,7 @@ export default function loadPresets(context: LoadContext): {
const unflatThemes: PluginConfig[][] = [];
presets.forEach((presetItem) => {
let presetModuleImport;
let presetModuleImport: string;
let presetOptions = {};
if (typeof presetItem === 'string') {
presetModuleImport = presetItem;

View file

@ -147,7 +147,8 @@ export default async function loadRoutes(
// Collect all page paths for injecting it later in the plugin lifecycle
// This is useful for plugins like sitemaps, redirects etc...
// If a route has subroutes, it is not necessarily a valid page path (more likely to be a wrapper)
// If a route has subroutes, it is not necessarily a valid page path (more
// likely to be a wrapper)
if (!subroutes) {
routesPaths.push(routePath);
}

View file

@ -21,8 +21,9 @@ export function loadThemeAliases(
themePaths.forEach((themePath) => {
const themeAliases = themeAlias(themePath, true);
Object.keys(themeAliases).forEach((aliasKey) => {
// If this alias shadows a previous one, use @theme-init to preserve the initial one.
// @theme-init is only applied once: to the initial theme that provided this component
// If this alias shadows a previous one, use @theme-init to preserve the
// initial one. @theme-init is only applied once: to the initial theme
// that provided this component
if (aliasKey in aliases) {
const componentName = aliasKey.substring(aliasKey.indexOf('/') + 1);
const initAlias = `@theme-init/${componentName}`;

View file

@ -459,8 +459,9 @@ describe('localizePluginTranslationFile', () => {
expect(localizedTranslationFile).toEqual({
path: translationFile.path,
content: {
// We only append/override localized messages, but never delete the data of the unlocalized translation file
// This ensures that all required keys are present when trying to read the translations files
// We only append/override localized messages, but never delete the data
// of the unlocalized translation file. This ensures that all required
// keys are present when trying to read the translations files
key1: {message: 'key1 message'},
key2: {message: 'key2 message localized'},
key3: {message: 'key3 message'},

View file

@ -86,7 +86,7 @@ function mergeTranslationFileContent({
Object.entries(newContentTransformed).forEach(
([key, {message, description}]) => {
result[key] = {
// If the messages already exist, we don't override them (unless requested)
// If messages already exist, we don't override them (unless requested)
message: options.override
? message
: existingContent[key]?.message ?? message,
@ -252,9 +252,8 @@ export async function localizePluginTranslationFile({
...localizedContent,
},
};
} else {
return translationFile;
}
return translationFile;
}
export async function getPluginsDefaultCodeTranslationMessages(

View file

@ -45,8 +45,9 @@ function getSiteSourceCodeFilePaths(siteDir: string): string[] {
function getPluginSourceCodeFilePaths(plugin: InitializedPlugin): string[] {
// The getPathsToWatch() generally returns the js/jsx/ts/tsx/md/mdx file paths
// We can use this method as well to know which folders we should try to extract translations from
// Hacky/implicit, but do we want to introduce a new lifecycle method just for that???
// We can use this method as well to know which folders we should try to
// extract translations from. Hacky/implicit, but do we want to introduce a
// new lifecycle method just for that???
const codePaths: string[] = plugin.getPathsToWatch?.() ?? [];
// We also include theme code
@ -72,8 +73,9 @@ async function getSourceCodeFilePaths(
const sitePaths = getSiteSourceCodeFilePaths(siteDir);
// The getPathsToWatch() generally returns the js/jsx/ts/tsx/md/mdx file paths
// We can use this method as well to know which folders we should try to extract translations from
// Hacky/implicit, but do we want to introduce a new lifecycle method for that???
// We can use this method as well to know which folders we should try to
// extract translations from. Hacky/implicit, but do we want to introduce a
// new lifecycle method for that???
const pluginsPaths = plugins.flatMap(getPluginSourceCodeFilePaths);
const allPaths = [...sitePaths, ...pluginsPaths];
@ -87,7 +89,8 @@ export async function extractSiteSourceCodeTranslations(
babelOptions: TransformOptions,
extraSourceCodeFilePaths: string[] = [],
): Promise<TranslationFileContent> {
// Should we warn here if the same translation "key" is found in multiple source code files?
// Should we warn here if the same translation "key" is found in multiple
// source code files?
function toTranslationFileContent(
sourceCodeFileTranslations: SourceCodeFileTranslations[],
): TranslationFileContent {
@ -152,8 +155,9 @@ export async function extractSourceCodeFileTranslations(
const ast = parse(code, {
...babelOptions,
ast: true,
// filename is important, because babel does not process the same files according to their js/ts extensions
// see see https://twitter.com/NicoloRibaudo/status/1321130735605002243
// filename is important, because babel does not process the same files
// according to their js/ts extensions.
// See https://twitter.com/NicoloRibaudo/status/1321130735605002243
filename: sourceCodeFilePath,
}) as Node;
@ -260,14 +264,13 @@ Full code: ${generate(node).code}`;
typeof attributeValueEvaluated.value === 'string'
) {
return attributeValueEvaluated.value;
} else {
warnings.push(
`<Translate> prop=${propName} should be a statically evaluable object.
}
warnings.push(
`<Translate> prop=${propName} should be a statically evaluable object.
Example: <Translate id="optional id" description="optional description">Message</Translate>
Dynamically constructed values are not allowed, because they prevent translations to be extracted.
${sourceWarningPart(path.node)}`,
);
}
);
}
return undefined;
@ -275,7 +278,7 @@ ${sourceWarningPart(path.node)}`,
const id = evaluateJSXProp('id');
const description = evaluateJSXProp('description');
let message;
let message: string;
const childrenPath = path.get('children');
// Handle empty content
@ -286,7 +289,7 @@ Example: <Translate id="my-id" />
${sourceWarningPart(path.node)}`);
} else {
translations[id] = {
message: message ?? id,
message: id,
...(description && {description}),
};
}
@ -296,8 +299,9 @@ ${sourceWarningPart(path.node)}`);
// Handle single non-empty content
const singleChildren = childrenPath
// Remove empty/useless text nodes that might be around our translation!
// Makes the translation system more reliable to JSX formatting issues
// Remove empty/useless text nodes that might be around our
// translation! Makes the translation system more reliable to JSX
// formatting issues
.filter(
(children) =>
!(
@ -340,7 +344,7 @@ ${sourceWarningPart(path.node)}`,
if (args.length === 1 || args.length === 2) {
const firstArgPath = args[0];
// evaluation allows translate("x" + "y"); to be considered as translate("xy");
// translate("x" + "y"); => translate("xy");
const firstArgEvaluated = firstArgPath.evaluate();
if (

View file

@ -43,7 +43,8 @@ export function getPluginVersion(
);
if (existsSync(packageJsonPath) && lstatSync(packageJsonPath).isFile()) {
if (potentialPluginPackageJsonDirectory === siteDir) {
// If the plugin belongs to the same docusaurus project, we classify it as local plugin.
// If the plugin belongs to the same docusaurus project, we classify it
// as local plugin.
return {type: 'project'};
}
return {
@ -56,6 +57,7 @@ export function getPluginVersion(
potentialPluginPackageJsonDirectory,
);
}
// In rare cases where a plugin is a path where no parent directory contains package.json, we can only classify it as local.
// In the case where a plugin is a path where no parent directory contains
// package.json (e.g. inline plugin), we can only classify it as local.
return {type: 'local'};
}

View file

@ -104,16 +104,19 @@ export function createBaseConfig(
// When version string changes, cache is evicted
version: [
siteMetadata.docusaurusVersion,
// Webpack does not evict the cache correctly on alias/swizzle change, so we force eviction.
// Webpack does not evict the cache correctly on alias/swizzle change,
// so we force eviction.
// See https://github.com/webpack/webpack/issues/13627
md5Hash(JSON.stringify(themeAliases)),
].join('-'),
// When one of those modules/dependencies change (including transitive deps), cache is invalidated
// When one of those modules/dependencies change (including transitive
// deps), cache is invalidated
buildDependencies: {
config: [
__filename,
path.join(__dirname, isServer ? 'server.js' : 'client.js'),
// Docusaurus config changes can affect MDX/JSX compilation, so we'd rather evict the cache.
// Docusaurus config changes can affect MDX/JSX compilation, so we'd
// rather evict the cache.
// See https://github.com/questdb/questdb.io/issues/493
siteConfigPath,
],
@ -158,10 +161,11 @@ export function createBaseConfig(
...getDocusaurusAliases(),
...themeAliases,
},
// This allows you to set a fallback for where Webpack should look for modules.
// We want `@docusaurus/core` own dependencies/`node_modules` to "win" if there is conflict
// Example: if there is core-js@3 in user's own node_modules, but core depends on
// core-js@2, we should use core-js@2.
// This allows you to set a fallback for where Webpack should look for
// modules. We want `@docusaurus/core` own dependencies/`node_modules` to
// "win" if there is conflict. Example: if there is core-js@3 in user's
// own node_modules, but core depends on core-js@2, we should use
// core-js@2.
modules: [
path.resolve(__dirname, '..', '..', 'node_modules'),
'node_modules',
@ -173,7 +177,8 @@ export function createBaseConfig(
},
optimization: {
removeAvailableModules: false,
// Only minimize client bundle in production because server bundle is only used for static site generation
// Only minimize client bundle in production because server bundle is only
// used for static site generation
minimize: minimizeEnabled,
minimizer: minimizeEnabled
? getMinimizer(useSimpleCssMinifier)
@ -181,7 +186,9 @@ export function createBaseConfig(
splitChunks: isServer
? false
: {
// Since the chunk name includes all origin chunk names it's recommended for production builds with long term caching to NOT include [name] in the filenames
// Since the chunk name includes all origin chunk names it's
// recommended for production builds with long term caching to NOT
// include [name] in the filenames
name: false,
cacheGroups: {
// disable the built-in cacheGroups
@ -255,8 +262,9 @@ export function createBaseConfig(
chunkFilename: isProd
? 'assets/css/[name].[contenthash:8].css'
: '[name].css',
// remove css order warnings if css imports are not sorted alphabetically
// see https://github.com/webpack-contrib/mini-css-extract-plugin/pull/422 for more reasoning
// remove css order warnings if css imports are not sorted
// alphabetically. See https://github.com/webpack-contrib/mini-css-extract-plugin/pull/422
// for more reasoning
ignoreOrder: true,
}),
],

View file

@ -23,7 +23,9 @@ export default function createClientConfig(
const config = createBaseConfig(props, false, minify);
const clientConfig = merge(config, {
// target: 'browserslist', // useless, disabled on purpose (errors on existing sites with no browserslist cfg)
// useless, disabled on purpose (errors on existing sites with no
// browserslist config)
// target: 'browserslist',
entry: path.resolve(__dirname, '../client/clientEntry.js'),
optimization: {
// Keep the runtime chunk separated to enable long term caching
@ -39,7 +41,8 @@ export default function createClientConfig(
],
});
// When building include the plugin to force terminate building if errors happened in the client bundle.
// When building, include the plugin to force terminate building if errors
// happened in the client bundle.
if (isBuilding) {
clientConfig.plugins?.push({
apply: (compiler) => {

View file

@ -9,16 +9,20 @@ import {Template, type Compiler} from 'webpack';
const pluginName = 'chunk-asset-plugin';
/**
* We modify webpack runtime to add an extra function called
* "__webpack_require__.gca" that will allow us to get the corresponding chunk
* asset for a webpack chunk. Pass it the chunkName or chunkId you want to load.
* For example: if you have a chunk named "my-chunk-name" that will map to
* "/publicPath/0a84b5e7.c8e35c7a.js" as its corresponding output path
* __webpack_require__.gca("my-chunk-name") will return
* "/publicPath/0a84b5e7.c8e35c7a.js"
*
* "gca" stands for "get chunk asset"
*/
class ChunkAssetPlugin {
apply(compiler: Compiler): void {
compiler.hooks.thisCompilation.tap(pluginName, ({mainTemplate}) => {
/* We modify webpack runtime to add an extra function called "__webpack_require__.gca"
that will allow us to get the corresponding chunk asset for a webpack chunk.
Pass it the chunkName or chunkId you want to load.
For example: if you have a chunk named "my-chunk-name" that will map to "/publicPath/0a84b5e7.c8e35c7a.js" as its corresponding output path
__webpack_require__.gca("my-chunk-name") will return "/publicPath/0a84b5e7.c8e35c7a.js"
"gca" stands for "get chunk asset"
*/
mainTemplate.hooks.requireExtensions.tap(pluginName, (source, chunk) => {
const chunkIdToName = chunk.getChunkMaps(false).name;
const chunkNameToId = Object.create(null);
@ -31,10 +35,13 @@ class ChunkAssetPlugin {
buf.push(
// If chunkName is passed, we convert it to chunk asset url
// .p => public path url ("/" or "/baseUrl/")
// .u(chunkId) => chunk asset url ("assets/js/x63b64xd.contentHash.js")
// not sure where this is documented, but this link was helpful: https://programmer.help/blogs/5d68849083e1a.html
// .u(chunkId) =>
// chunk asset url ("assets/js/x63b64xd.contentHash.js")
// not sure where this is documented, but this link was helpful:
// https://programmer.help/blogs/5d68849083e1a.html
//
// Note: __webpack_require__.gca() is called in docusaurus.ts for prefetching
// Note: __webpack_require__.gca() is called in docusaurus.ts for
// prefetching
// Note: we previously used jsonpScriptSrc (Webpack 4)
`__webpack_require__.gca = function(chunkId) { chunkId = ${JSON.stringify(
chunkNameToId,

View file

@ -14,8 +14,8 @@
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -144,7 +144,8 @@ class CleanWebpackPlugin {
*
* Only happens once.
*
* Warning: It is recommended to initially clean your build directory outside of webpack to minimize unexpected behavior.
* Warning: It is recommended to initially clean your build directory outside
* of webpack to minimize unexpected behavior.
*/
handleInitial(): void {
if (this.initialClean) {

View file

@ -79,9 +79,10 @@ export default function createServerConfig({
paths: ssgPaths,
preferFoldersOutput: trailingSlash,
// When using "new URL('file.js', import.meta.url)", Webpack will emit __filename, and this plugin will throw
// not sure the __filename value has any importance for this plugin, just using an empty string to avoid the error
// See https://github.com/facebook/docusaurus/issues/4922
// When using "new URL('file.js', import.meta.url)", Webpack will emit
// __filename, and this plugin will throw. not sure the __filename value
// has any importance for this plugin, just using an empty string to
// avoid the error. See https://github.com/facebook/docusaurus/issues/4922
globals: {__filename: ''},
}),

View file

@ -126,16 +126,13 @@ export function getBabelOptions({
configFile: babelOptions,
caller: {name: isServer ? 'server' : 'client'},
};
} else {
return Object.assign(
babelOptions ?? {presets: [require.resolve('../babel/preset')]},
{
babelrc: false,
configFile: false,
caller: {name: isServer ? 'server' : 'client'},
},
);
}
return {
...(babelOptions ?? {presets: [require.resolve('../babel/preset')]}),
babelrc: false,
configFile: false,
caller: {name: isServer ? 'server' : 'client'},
};
}
// Name is generic on purpose
@ -405,8 +402,8 @@ export function getMinimizer(
output: {
ecma: 5,
comments: false,
// Turned on because emoji and regex is not minified properly using default
// https://github.com/facebook/create-react-app/issues/2488
// Turned on because emoji and regex is not minified properly using
// default. See https://github.com/facebook/create-react-app/issues/2488
ascii_only: true,
},
},