mirror of
https://github.com/facebook/docusaurus.git
synced 2025-08-02 08:19:07 +02:00
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:
parent
b8ccb869f1
commit
aa446b7a9c
167 changed files with 1157 additions and 960 deletions
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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>({
|
||||
|
|
|
@ -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})}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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});
|
||||
}),
|
||||
);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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, {
|
||||
|
|
|
@ -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`];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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}".`);
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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}`;
|
||||
|
|
|
@ -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'},
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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'};
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}),
|
||||
],
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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: ''},
|
||||
}),
|
||||
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue