mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-16 17:52:29 +02:00
chore: upgrade TypeScript & other ESLint related deps (#5963)
* chore: upgrade ESLint related deps * Upgrade TS * Fix lock * Bump Babel * Update config
This commit is contained in:
parent
2f7d6fea1e
commit
0374426ce3
104 changed files with 2662 additions and 2487 deletions
23
.eslintrc.js
23
.eslintrc.js
|
@ -31,7 +31,6 @@ module.exports = {
|
||||||
'plugin:react-hooks/recommended',
|
'plugin:react-hooks/recommended',
|
||||||
'airbnb',
|
'airbnb',
|
||||||
'prettier',
|
'prettier',
|
||||||
'prettier/react',
|
|
||||||
],
|
],
|
||||||
settings: {
|
settings: {
|
||||||
'import/resolver': {
|
'import/resolver': {
|
||||||
|
@ -61,6 +60,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'import/extensions': OFF,
|
'import/extensions': OFF,
|
||||||
|
'no-restricted-exports': OFF,
|
||||||
'header/header': [
|
'header/header': [
|
||||||
ERROR,
|
ERROR,
|
||||||
'block',
|
'block',
|
||||||
|
@ -90,6 +90,14 @@ module.exports = {
|
||||||
'react/prefer-stateless-function': WARNING,
|
'react/prefer-stateless-function': WARNING,
|
||||||
'react/jsx-props-no-spreading': OFF,
|
'react/jsx-props-no-spreading': OFF,
|
||||||
'react/require-default-props': [ERROR, {ignoreFunctionalComponents: true}],
|
'react/require-default-props': [ERROR, {ignoreFunctionalComponents: true}],
|
||||||
|
'react/function-component-definition': [
|
||||||
|
WARNING,
|
||||||
|
{
|
||||||
|
namedComponents: 'function-declaration',
|
||||||
|
unnamedComponents: 'arrow-function',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'react/no-unstable-nested-components': [WARNING, {allowAsProps: true}],
|
||||||
'@typescript-eslint/no-inferrable-types': OFF,
|
'@typescript-eslint/no-inferrable-types': OFF,
|
||||||
'import/first': OFF,
|
'import/first': OFF,
|
||||||
'import/order': OFF,
|
'import/order': OFF,
|
||||||
|
@ -104,19 +112,16 @@ module.exports = {
|
||||||
'no-unused-vars': OFF,
|
'no-unused-vars': OFF,
|
||||||
'no-nested-ternary': WARNING,
|
'no-nested-ternary': WARNING,
|
||||||
'@typescript-eslint/no-empty-function': OFF,
|
'@typescript-eslint/no-empty-function': OFF,
|
||||||
'@typescript-eslint/no-non-null-assertion': OFF, // Have to use type assertion anyways
|
'@typescript-eslint/no-non-null-assertion': OFF,
|
||||||
'@typescript-eslint/no-unused-vars': [
|
'@typescript-eslint/no-unused-vars': [
|
||||||
ERROR,
|
ERROR,
|
||||||
{argsIgnorePattern: '^_', ignoreRestSiblings: true},
|
{argsIgnorePattern: '^_', ignoreRestSiblings: true},
|
||||||
],
|
],
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': WARNING,
|
||||||
'@typescript-eslint/ban-ts-comment': [
|
'@typescript-eslint/ban-ts-comment': [
|
||||||
ERROR,
|
ERROR,
|
||||||
{'ts-expect-error': 'allow-with-description'},
|
{'ts-expect-error': 'allow-with-description'},
|
||||||
],
|
],
|
||||||
|
|
||||||
// TODO re-enable some these as errors
|
|
||||||
// context: https://github.com/facebook/docusaurus/pull/2949
|
|
||||||
'@typescript-eslint/ban-types': WARNING,
|
|
||||||
'import/no-extraneous-dependencies': ERROR,
|
'import/no-extraneous-dependencies': ERROR,
|
||||||
'no-useless-escape': WARNING,
|
'no-useless-escape': WARNING,
|
||||||
'prefer-template': WARNING,
|
'prefer-template': WARNING,
|
||||||
|
@ -189,6 +194,12 @@ module.exports = {
|
||||||
'import/no-duplicates': OFF,
|
'import/no-duplicates': OFF,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
files: ['*.ts', '*.tsx'],
|
||||||
|
rules: {
|
||||||
|
'import/no-import-module-exports': OFF,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
files: ['*.js'],
|
files: ['*.js'],
|
||||||
rules: {
|
rules: {
|
||||||
|
|
|
@ -22,12 +22,10 @@ async function getPackagesJsonFiles(): Promise<PackageJsonFile[]> {
|
||||||
const files = await glob('packages/*/package.json');
|
const files = await glob('packages/*/package.json');
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
files.map(async (file) => {
|
files.map(async (file) => ({
|
||||||
return {
|
|
||||||
file,
|
file,
|
||||||
content: JSON.parse(await readFile(file, 'utf8')),
|
content: JSON.parse(await readFile(file, 'utf8')),
|
||||||
};
|
})),
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ export function createPlaygroundResponse(
|
||||||
// Inspired by https://stackoverflow.com/a/3409200/82609
|
// Inspired by https://stackoverflow.com/a/3409200/82609
|
||||||
function parseCookieString(cookieString: string): Record<string, string> {
|
function parseCookieString(cookieString: string): Record<string, string> {
|
||||||
const result: Record<string, string> = {};
|
const result: Record<string, string> = {};
|
||||||
cookieString.split(';').forEach(function (cookie) {
|
cookieString.split(';').forEach((cookie) => {
|
||||||
const [name, value] = cookie.split('=');
|
const [name, value] = cookie.split('=');
|
||||||
result[name.trim()] = decodeURI(value);
|
result[name.trim()] = decodeURI(value);
|
||||||
});
|
});
|
||||||
|
|
36
package.json
36
package.json
|
@ -58,13 +58,13 @@
|
||||||
"lock:update": "npx yarn-deduplicate"
|
"lock:update": "npx yarn-deduplicate"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.15.7",
|
"@babel/cli": "^7.16.0",
|
||||||
"@babel/core": "^7.12.16",
|
"@babel/core": "^7.16.0",
|
||||||
"@babel/eslint-parser": "^7.15.7",
|
"@babel/eslint-parser": "^7.16.3",
|
||||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.13",
|
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0",
|
||||||
"@babel/plugin-proposal-optional-chaining": "^7.12.16",
|
"@babel/plugin-proposal-optional-chaining": "^7.16.0",
|
||||||
"@babel/plugin-transform-modules-commonjs": "^7.12.13",
|
"@babel/plugin-transform-modules-commonjs": "^7.16.0",
|
||||||
"@babel/preset-typescript": "^7.12.16",
|
"@babel/preset-typescript": "^7.16.0",
|
||||||
"@crowdin/cli": "^3.7.1",
|
"@crowdin/cli": "^3.7.1",
|
||||||
"@formatjs/intl-datetimeformat": "^3.2.12",
|
"@formatjs/intl-datetimeformat": "^3.2.12",
|
||||||
"@formatjs/intl-numberformat": "^6.2.2",
|
"@formatjs/intl-numberformat": "^6.2.2",
|
||||||
|
@ -87,18 +87,18 @@
|
||||||
"@types/shelljs": "^0.8.6",
|
"@types/shelljs": "^0.8.6",
|
||||||
"@types/wait-on": "^5.2.0",
|
"@types/wait-on": "^5.2.0",
|
||||||
"@types/webpack-dev-server": "^4.1.0",
|
"@types/webpack-dev-server": "^4.1.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.18.0",
|
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||||
"@typescript-eslint/parser": "^4.18.0",
|
"@typescript-eslint/parser": "^5.4.0",
|
||||||
"concurrently": "^6.2.1",
|
"concurrently": "^6.2.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^7.20.0",
|
"eslint": "^8.2.0",
|
||||||
"eslint-config-airbnb": "^18.2.1",
|
"eslint-config-airbnb": "^19.0.0",
|
||||||
"eslint-config-prettier": "^6.15.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-plugin-header": "^3.0.0",
|
"eslint-plugin-header": "^3.1.1",
|
||||||
"eslint-plugin-import": "^2.22.1",
|
"eslint-plugin-import": "^2.25.3",
|
||||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||||
"eslint-plugin-react": "^7.21.5",
|
"eslint-plugin-react": "^7.27.0",
|
||||||
"eslint-plugin-react-hooks": "^4.2.0",
|
"eslint-plugin-react-hooks": "^4.3.0",
|
||||||
"glob": "^7.1.6",
|
"glob": "^7.1.6",
|
||||||
"husky": "^5.0.9",
|
"husky": "^5.0.9",
|
||||||
"is-ci": "^3.0.0",
|
"is-ci": "^3.0.0",
|
||||||
|
@ -117,7 +117,7 @@
|
||||||
"serve": "^12.0.1",
|
"serve": "^12.0.1",
|
||||||
"stylelint": "^13.10.0",
|
"stylelint": "^13.10.0",
|
||||||
"tslib": "^2.3.1",
|
"tslib": "^2.3.1",
|
||||||
"typescript": "^4.1.5"
|
"typescript": "^4.5.2"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,jsx,ts,tsx}": [
|
"*.{js,jsx,ts,tsx}": [
|
||||||
|
|
|
@ -42,7 +42,12 @@ program
|
||||||
.option('--typescript')
|
.option('--typescript')
|
||||||
.description('Initialize website.')
|
.description('Initialize website.')
|
||||||
.action(
|
.action(
|
||||||
(siteName, template, rootDir = '.', {useNpm, skipInstall, typescript}) => {
|
(
|
||||||
|
siteName,
|
||||||
|
template,
|
||||||
|
rootDir = '.',
|
||||||
|
{useNpm, skipInstall, typescript} = {},
|
||||||
|
) => {
|
||||||
wrapCommand(init)(path.resolve(rootDir), siteName, template, {
|
wrapCommand(init)(path.resolve(rootDir), siteName, template, {
|
||||||
useNpm,
|
useNpm,
|
||||||
skipInstall,
|
skipInstall,
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/module-type-aliases": "2.0.0-beta.9",
|
"@docusaurus/module-type-aliases": "2.0.0-beta.9",
|
||||||
"@tsconfig/docusaurus": "^1.0.4",
|
"@tsconfig/docusaurus": "^1.0.4",
|
||||||
"typescript": "^4.3.5"
|
"typescript": "^4.5.2"
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
|
|
|
@ -23,7 +23,7 @@ module.exports = {
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
allowImportExportEverywhere: true,
|
allowImportExportEverywhere: true,
|
||||||
},
|
},
|
||||||
extends: ['airbnb', 'prettier', 'prettier/react'],
|
extends: ['airbnb', 'prettier'],
|
||||||
plugins: ['react-hooks', 'header'],
|
plugins: ['react-hooks', 'header'],
|
||||||
rules: {
|
rules: {
|
||||||
// Ignore certain webpack alias because it can't be resolved
|
// Ignore certain webpack alias because it can't be resolved
|
||||||
|
@ -56,5 +56,12 @@ module.exports = {
|
||||||
'react/jsx-filename-extension': OFF,
|
'react/jsx-filename-extension': OFF,
|
||||||
'react-hooks/rules-of-hooks': ERROR,
|
'react-hooks/rules-of-hooks': ERROR,
|
||||||
'react/prop-types': OFF, // PropTypes aren't used much these days.
|
'react/prop-types': OFF, // PropTypes aren't used much these days.
|
||||||
|
'react/function-component-definition': [
|
||||||
|
WARNING,
|
||||||
|
{
|
||||||
|
namedComponents: 'function-declaration',
|
||||||
|
unnamedComponents: 'arrow-function',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -30,15 +30,15 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/eslint-parser": "^7.15.7",
|
"@babel/eslint-parser": "^7.15.7",
|
||||||
"eslint": "^7.20.0",
|
"eslint": "^8.2.0",
|
||||||
"eslint-config-airbnb": "^18.2.1",
|
"eslint-config-airbnb": "^19.0.0",
|
||||||
"eslint-config-prettier": "^6.15.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-plugin-header": "^3.0.0",
|
"eslint-plugin-header": "^3.1.1",
|
||||||
"eslint-plugin-import": "^2.22.1",
|
"eslint-plugin-import": "^2.25.3",
|
||||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||||
"eslint-plugin-react": "^7.21.5",
|
"eslint-plugin-react": "^7.27.0",
|
||||||
"eslint-plugin-react-hooks": "^4.2.0",
|
"eslint-plugin-react-hooks": "^4.3.0",
|
||||||
"prettier": "^2.2.1",
|
"prettier": "^2.4.1",
|
||||||
"stylelint": "^13.2.1"
|
"stylelint": "^13.2.1"
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
|
|
|
@ -38,7 +38,7 @@ cli
|
||||||
.option('--mdx', 'try to migrate MD to MDX too')
|
.option('--mdx', 'try to migrate MD to MDX too')
|
||||||
.option('--page', 'try to migrate pages too')
|
.option('--page', 'try to migrate pages too')
|
||||||
.description('Migrate between versions of Docusaurus website.')
|
.description('Migrate between versions of Docusaurus website.')
|
||||||
.action((siteDir = '.', newDir = '.', {mdx, page}) => {
|
.action((siteDir = '.', newDir = '.', {mdx, page} = {}) => {
|
||||||
const sitePath = path.resolve(siteDir);
|
const sitePath = path.resolve(siteDir);
|
||||||
const newSitePath = path.resolve(newDir);
|
const newSitePath = path.resolve(newDir);
|
||||||
wrapCommand(migrateDocusaurusProject)(sitePath, newSitePath, mdx, page);
|
wrapCommand(migrateDocusaurusProject)(sitePath, newSitePath, mdx, page);
|
||||||
|
|
|
@ -47,11 +47,10 @@ function sanitizedFileContent(
|
||||||
): string {
|
): string {
|
||||||
const extractedData = extractMetadata(content);
|
const extractedData = extractMetadata(content);
|
||||||
const extractedMetaData = Object.entries(extractedData.metadata).reduce(
|
const extractedMetaData = Object.entries(extractedData.metadata).reduce(
|
||||||
(metaData, [key, value]) => {
|
(metaData, [key, value]) =>
|
||||||
return `${metaData}\n${key}: ${
|
`${metaData}\n${key}: ${
|
||||||
shouldQuotifyFrontMatter([key, value]) ? `"${value}"` : value
|
shouldQuotifyFrontMatter([key, value]) ? `"${value}"` : value
|
||||||
}`;
|
}`,
|
||||||
},
|
|
||||||
'',
|
'',
|
||||||
);
|
);
|
||||||
const sanitizedData = `---${extractedMetaData}\n---\n${
|
const sanitizedData = `---${extractedMetaData}\n---\n${
|
||||||
|
@ -618,8 +617,7 @@ function migrateVersionedSidebar(
|
||||||
const newSidebar = Object.entries(sidebar.entries).reduce(
|
const newSidebar = Object.entries(sidebar.entries).reduce(
|
||||||
(acc: SidebarEntries, val) => {
|
(acc: SidebarEntries, val) => {
|
||||||
const key = `version-${sidebar.version}/${val[0]}`;
|
const key = `version-${sidebar.version}/${val[0]}`;
|
||||||
acc[key] = Object.entries(val[1]).map((value) => {
|
acc[key] = Object.entries(val[1]).map((value) => ({
|
||||||
return {
|
|
||||||
type: 'category',
|
type: 'category',
|
||||||
label: value[0],
|
label: value[0],
|
||||||
items: (value[1] as Array<SidebarEntry>).map((sidebarItem) => {
|
items: (value[1] as Array<SidebarEntry>).map((sidebarItem) => {
|
||||||
|
@ -638,8 +636,7 @@ function migrateVersionedSidebar(
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
};
|
}));
|
||||||
});
|
|
||||||
return acc;
|
return acc;
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
|
|
|
@ -131,10 +131,8 @@ export default function transformer(file: string): string {
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.filter(function (p) {
|
.filter((p) => p.parentPath.parentPath.name === 'body')
|
||||||
return p.parentPath.parentPath.name === 'body';
|
.forEach((p) => {
|
||||||
})
|
|
||||||
.forEach(function (p) {
|
|
||||||
const exportDecl = jscodeshift.exportDeclaration(
|
const exportDecl = jscodeshift.exportDeclaration(
|
||||||
true,
|
true,
|
||||||
jscodeshift.arrowFunctionExpression(
|
jscodeshift.arrowFunctionExpression(
|
||||||
|
@ -177,9 +175,7 @@ function getDefaultImportDeclarators(rootAst: Collection) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.filter((variableDeclarator) => {
|
.filter((variableDeclarator) => !!variableDeclarator.value);
|
||||||
return !!variableDeclarator.value;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNamedImportDeclarators(rootAst: Collection) {
|
function getNamedImportDeclarators(rootAst: Collection) {
|
||||||
|
|
|
@ -220,12 +220,10 @@ describe('collectRedirects', () => {
|
||||||
collectRedirects(
|
collectRedirects(
|
||||||
createTestPluginContext(
|
createTestPluginContext(
|
||||||
{
|
{
|
||||||
createRedirects: (routePath) => {
|
createRedirects: (routePath) => [
|
||||||
return [
|
|
||||||
`${removeTrailingSlash(routePath)}/some/path/suffix1`,
|
`${removeTrailingSlash(routePath)}/some/path/suffix1`,
|
||||||
`${removeTrailingSlash(routePath)}/some/other/path/suffix2`,
|
`${removeTrailingSlash(routePath)}/some/other/path/suffix2`,
|
||||||
];
|
],
|
||||||
},
|
|
||||||
},
|
},
|
||||||
['/', '/testpath', '/otherPath.html'],
|
['/', '/testpath', '/otherPath.html'],
|
||||||
),
|
),
|
||||||
|
|
|
@ -28,9 +28,7 @@ describe('normalizePluginOptions', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should override all default options with valid user options', () => {
|
test('should override all default options with valid user options', () => {
|
||||||
const createRedirects: CreateRedirectsFnOption = (_routePath: string) => {
|
const createRedirects: CreateRedirectsFnOption = (_routePath: string) => [];
|
||||||
return [];
|
|
||||||
};
|
|
||||||
expect(
|
expect(
|
||||||
normalizePluginOptions({
|
normalizePluginOptions({
|
||||||
fromExtensions: ['exe', 'zip'],
|
fromExtensions: ['exe', 'zip'],
|
||||||
|
|
|
@ -47,12 +47,10 @@ function applyRedirectsTrailingSlash(
|
||||||
redirects: RedirectMetadata[],
|
redirects: RedirectMetadata[],
|
||||||
params: ApplyTrailingSlashParams,
|
params: ApplyTrailingSlashParams,
|
||||||
) {
|
) {
|
||||||
return redirects.map((redirect) => {
|
return redirects.map((redirect) => ({
|
||||||
return {
|
|
||||||
...redirect,
|
...redirect,
|
||||||
to: applyTrailingSlash(redirect.to, params),
|
to: applyTrailingSlash(redirect.to, params),
|
||||||
};
|
}));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateCollectedRedirects(
|
function validateCollectedRedirects(
|
||||||
|
@ -181,12 +179,10 @@ function createCreateRedirectsOptionRedirects(
|
||||||
const froms: string[] =
|
const froms: string[] =
|
||||||
typeof fromsMixed === 'string' ? [fromsMixed] : fromsMixed;
|
typeof fromsMixed === 'string' ? [fromsMixed] : fromsMixed;
|
||||||
|
|
||||||
return froms.map((from) => {
|
return froms.map((from) => ({
|
||||||
return {
|
|
||||||
from,
|
from,
|
||||||
to: path,
|
to: path,
|
||||||
};
|
}));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return paths.flatMap(createPathRedirects);
|
return paths.flatMap(createPathRedirects);
|
||||||
|
|
|
@ -13,9 +13,9 @@ type CreateRedirectPageOptions = {
|
||||||
toUrl: string;
|
toUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCompiledRedirectPageTemplate = memoize(() => {
|
const getCompiledRedirectPageTemplate = memoize(() =>
|
||||||
return eta.compile(redirectPageTemplate.trim());
|
eta.compile(redirectPageTemplate.trim()),
|
||||||
});
|
);
|
||||||
|
|
||||||
function renderRedirectPageTemplate(data: Record<string, unknown>) {
|
function renderRedirectPageTemplate(data: Record<string, unknown>) {
|
||||||
const compiled = getCompiledRedirectPageTemplate();
|
const compiled = getCompiledRedirectPageTemplate();
|
||||||
|
|
|
@ -60,9 +60,9 @@ export function toRedirectFilesMetadata(
|
||||||
// Perf: avoid rendering the template twice with the exact same "props"
|
// Perf: avoid rendering the template twice with the exact same "props"
|
||||||
// We might create multiple redirect pages for the same destination url
|
// We might create multiple redirect pages for the same destination url
|
||||||
// note: the first fn arg is the cache key!
|
// note: the first fn arg is the cache key!
|
||||||
const createPageContentMemoized = memoize((toUrl: string) => {
|
const createPageContentMemoized = memoize((toUrl: string) =>
|
||||||
return createRedirectPageContent({toUrl});
|
createRedirectPageContent({toUrl}),
|
||||||
});
|
);
|
||||||
|
|
||||||
const createFileMetadata = (redirect: RedirectMetadata) => {
|
const createFileMetadata = (redirect: RedirectMetadata) => {
|
||||||
const fileRelativePath = getRedirectFilePath(redirect.from, trailingSlash);
|
const fileRelativePath = getRedirectFilePath(redirect.from, trailingSlash);
|
||||||
|
|
|
@ -39,7 +39,6 @@ const BlogPostFrontMatterAuthorSchema = Joi.object({
|
||||||
.rename('image_url', 'imageURL', {alias: true});
|
.rename('image_url', 'imageURL', {alias: true});
|
||||||
|
|
||||||
export type BlogPostFrontMatter = {
|
export type BlogPostFrontMatter = {
|
||||||
/* eslint-disable camelcase */
|
|
||||||
id?: string;
|
id?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
@ -68,7 +67,6 @@ export type BlogPostFrontMatter = {
|
||||||
hide_table_of_contents?: boolean;
|
hide_table_of_contents?: boolean;
|
||||||
toc_min_heading_level?: number;
|
toc_min_heading_level?: number;
|
||||||
toc_max_heading_level?: number;
|
toc_max_heading_level?: number;
|
||||||
/* eslint-enable camelcase */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const FrontMatterAuthorErrorMessage =
|
const FrontMatterAuthorErrorMessage =
|
||||||
|
|
|
@ -52,13 +52,11 @@ export function getBlogTags(blogPosts: BlogPost[]): BlogTags {
|
||||||
blogPosts,
|
blogPosts,
|
||||||
(blogPost) => blogPost.metadata.tags,
|
(blogPost) => blogPost.metadata.tags,
|
||||||
);
|
);
|
||||||
return mapValues(groups, (group) => {
|
return mapValues(groups, (group) => ({
|
||||||
return {
|
|
||||||
name: group.tag.label,
|
name: group.tag.label,
|
||||||
items: group.items.map((item) => item.id),
|
items: group.items.map((item) => item.id),
|
||||||
permalink: group.tag.permalink,
|
permalink: group.tag.permalink,
|
||||||
};
|
}));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const DATE_FILENAME_REGEX =
|
const DATE_FILENAME_REGEX =
|
||||||
|
@ -112,9 +110,8 @@ async function parseBlogPostMarkdownFile(blogSourceAbsolute: string) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultReadingTime: ReadingTimeFunction = ({content, options}) => {
|
const defaultReadingTime: ReadingTimeFunction = ({content, options}) =>
|
||||||
return readingTime(content, options).minutes;
|
readingTime(content, options).minutes;
|
||||||
};
|
|
||||||
|
|
||||||
async function processBlogSourceFile(
|
async function processBlogSourceFile(
|
||||||
blogSourceRelative: string,
|
blogSourceRelative: string,
|
||||||
|
|
|
@ -118,7 +118,7 @@ export async function createBlogFeedFiles({
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
feedTypes.map(async function (feedType) {
|
feedTypes.map(async (feedType) => {
|
||||||
await createBlogFeedFile({
|
await createBlogFeedFile({
|
||||||
feed,
|
feed,
|
||||||
feedType,
|
feedType,
|
||||||
|
|
|
@ -322,9 +322,9 @@ export default function pluginContentBlog(
|
||||||
exact: true,
|
exact: true,
|
||||||
modules: {
|
modules: {
|
||||||
sidebar: aliasedSource(sidebarProp),
|
sidebar: aliasedSource(sidebarProp),
|
||||||
items: items.map((postID) => {
|
items: items.map((postID) =>
|
||||||
// To tell routes.js this is an import and not a nested object to recurse.
|
// To tell routes.js this is an import and not a nested object to recurse.
|
||||||
return {
|
({
|
||||||
content: {
|
content: {
|
||||||
__import: true,
|
__import: true,
|
||||||
path: blogItemsToMetadata[postID].source,
|
path: blogItemsToMetadata[postID].source,
|
||||||
|
@ -332,8 +332,8 @@ export default function pluginContentBlog(
|
||||||
truncated: true,
|
truncated: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
|
||||||
}),
|
}),
|
||||||
|
),
|
||||||
metadata: aliasedSource(pageMetadataPath),
|
metadata: aliasedSource(pageMetadataPath),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -490,14 +490,12 @@ export default function pluginContentBlog(
|
||||||
}: {
|
}: {
|
||||||
frontMatter: BlogPostFrontMatter;
|
frontMatter: BlogPostFrontMatter;
|
||||||
metadata: MetaData;
|
metadata: MetaData;
|
||||||
}): Assets => {
|
}): Assets => ({
|
||||||
return {
|
|
||||||
image: frontMatter.image,
|
image: frontMatter.image,
|
||||||
authorsImageUrls: metadata.authors.map(
|
authorsImageUrls: metadata.authors.map(
|
||||||
(author) => author.imageURL,
|
(author) => author.imageURL,
|
||||||
),
|
),
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -600,11 +600,9 @@ describe('versioned site, pluginId=default', () => {
|
||||||
test('readVersionsMetadata versioned site with invalid versions.json file', async () => {
|
test('readVersionsMetadata versioned site with invalid versions.json file', async () => {
|
||||||
const {defaultOptions, defaultContext} = await loadSite();
|
const {defaultOptions, defaultContext} = await loadSite();
|
||||||
|
|
||||||
const mock = jest.spyOn(JSON, 'parse').mockImplementationOnce(() => {
|
const mock = jest.spyOn(JSON, 'parse').mockImplementationOnce(() => ({
|
||||||
return {
|
|
||||||
invalid: 'json',
|
invalid: 'json',
|
||||||
};
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
readVersionsMetadata({
|
readVersionsMetadata({
|
||||||
|
|
|
@ -31,13 +31,12 @@ export function getActivePlugin(
|
||||||
options: GetActivePluginOptions = {},
|
options: GetActivePluginOptions = {},
|
||||||
): ActivePlugin | undefined {
|
): ActivePlugin | undefined {
|
||||||
const activeEntry = Object.entries(allPluginDatas).find(
|
const activeEntry = Object.entries(allPluginDatas).find(
|
||||||
([_id, pluginData]) => {
|
([_id, pluginData]) =>
|
||||||
return !!matchPath(pathname, {
|
!!matchPath(pathname, {
|
||||||
path: pluginData.path,
|
path: pluginData.path,
|
||||||
exact: false,
|
exact: false,
|
||||||
strict: false,
|
strict: false,
|
||||||
});
|
}),
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const activePlugin: ActivePlugin | undefined = activeEntry
|
const activePlugin: ActivePlugin | undefined = activeEntry
|
||||||
|
@ -63,9 +62,8 @@ export type ActiveDocContext = {
|
||||||
alternateDocVersions: Record<string, Doc>;
|
alternateDocVersions: Record<string, Doc>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLatestVersion = (data: GlobalPluginData): Version => {
|
export const getLatestVersion = (data: GlobalPluginData): Version =>
|
||||||
return data.versions.find((version) => version.isLast)!;
|
data.versions.find((version) => version.isLast)!;
|
||||||
};
|
|
||||||
|
|
||||||
// Note: return undefined on doc-unrelated pages,
|
// Note: return undefined on doc-unrelated pages,
|
||||||
// because there's no version currently considered as active
|
// because there's no version currently considered as active
|
||||||
|
@ -80,13 +78,14 @@ export const getActiveVersion = (
|
||||||
...data.versions.filter((version) => version !== lastVersion),
|
...data.versions.filter((version) => version !== lastVersion),
|
||||||
lastVersion,
|
lastVersion,
|
||||||
];
|
];
|
||||||
return orderedVersionsMetadata.find((version) => {
|
return orderedVersionsMetadata.find(
|
||||||
return !!matchPath(pathname, {
|
(version) =>
|
||||||
|
!!matchPath(pathname, {
|
||||||
path: version.path,
|
path: version.path,
|
||||||
exact: false,
|
exact: false,
|
||||||
strict: false,
|
strict: false,
|
||||||
});
|
}),
|
||||||
});
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getActiveDocContext = (
|
export const getActiveDocContext = (
|
||||||
|
|
|
@ -70,12 +70,10 @@ declare module '@theme/DocItem' {
|
||||||
readonly title: string;
|
readonly title: string;
|
||||||
readonly image?: string;
|
readonly image?: string;
|
||||||
readonly keywords?: readonly string[];
|
readonly keywords?: readonly string[];
|
||||||
/* eslint-disable camelcase */
|
|
||||||
readonly hide_title?: boolean;
|
readonly hide_title?: boolean;
|
||||||
readonly hide_table_of_contents?: boolean;
|
readonly hide_table_of_contents?: boolean;
|
||||||
readonly toc_min_heading_level?: number;
|
readonly toc_min_heading_level?: number;
|
||||||
readonly toc_max_heading_level?: number;
|
readonly toc_max_heading_level?: number;
|
||||||
/* eslint-enable camelcase */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Metadata = {
|
export type Metadata = {
|
||||||
|
|
|
@ -36,14 +36,16 @@ describe('DefaultSidebarItemsGenerator', () => {
|
||||||
function mockCategoryMetadataFiles(
|
function mockCategoryMetadataFiles(
|
||||||
categoryMetadataFiles: Record<string, Partial<CategoryMetadataFile>>,
|
categoryMetadataFiles: Record<string, Partial<CategoryMetadataFile>>,
|
||||||
) {
|
) {
|
||||||
jest.spyOn(fs, 'pathExists').mockImplementation((metadataFilePath) => {
|
jest
|
||||||
return typeof categoryMetadataFiles[metadataFilePath] !== 'undefined';
|
.spyOn(fs, 'pathExists')
|
||||||
});
|
.mockImplementation(
|
||||||
|
(metadataFilePath) =>
|
||||||
|
typeof categoryMetadataFiles[metadataFilePath] !== 'undefined',
|
||||||
|
);
|
||||||
jest.spyOn(fs, 'readFile').mockImplementation(
|
jest.spyOn(fs, 'readFile').mockImplementation(
|
||||||
// @ts-expect-error: annoying TS error due to overrides
|
// @ts-expect-error: annoying TS error due to overrides
|
||||||
async (metadataFilePath: string) => {
|
async (metadataFilePath: string) =>
|
||||||
return JSON.stringify(categoryMetadataFiles[metadataFilePath]);
|
JSON.stringify(categoryMetadataFiles[metadataFilePath]),
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,7 @@ describe('processSidebars', () => {
|
||||||
];
|
];
|
||||||
|
|
||||||
const StaticSidebarItemsGenerator: SidebarItemsGenerator = jest.fn(
|
const StaticSidebarItemsGenerator: SidebarItemsGenerator = jest.fn(
|
||||||
async () => {
|
async () => StaticGeneratedSidebarSlice,
|
||||||
return StaticGeneratedSidebarSlice;
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
async function testProcessSidebars(unprocessedSidebars: NormalizedSidebars) {
|
async function testProcessSidebars(unprocessedSidebars: NormalizedSidebars) {
|
||||||
|
|
|
@ -73,9 +73,9 @@ export function collectSidebarLinks(sidebar: Sidebar): SidebarItemLink[] {
|
||||||
export function collectSidebarsDocIds(
|
export function collectSidebarsDocIds(
|
||||||
sidebars: Sidebars,
|
sidebars: Sidebars,
|
||||||
): Record<string, string[]> {
|
): Record<string, string[]> {
|
||||||
return mapValues(sidebars, (sidebar) => {
|
return mapValues(sidebars, (sidebar) =>
|
||||||
return collectSidebarDocItems(sidebar).map((docItem) => docItem.id);
|
collectSidebarDocItems(sidebar).map((docItem) => docItem.id),
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createSidebarsUtils(sidebars: Sidebars): {
|
export function createSidebarsUtils(sidebars: Sidebars): {
|
||||||
|
|
|
@ -11,11 +11,9 @@ import {mapValues} from 'lodash';
|
||||||
|
|
||||||
export function getVersionTags(docs: DocMetadata[]): VersionTags {
|
export function getVersionTags(docs: DocMetadata[]): VersionTags {
|
||||||
const groups = groupTaggedItems(docs, (doc) => doc.tags);
|
const groups = groupTaggedItems(docs, (doc) => doc.tags);
|
||||||
return mapValues(groups, (group) => {
|
return mapValues(groups, (group) => ({
|
||||||
return {
|
|
||||||
name: group.tag.label,
|
name: group.tag.label,
|
||||||
docIds: group.items.map((item) => item.id),
|
docIds: group.items.map((item) => item.id),
|
||||||
permalink: group.tag.permalink,
|
permalink: group.tag.permalink,
|
||||||
};
|
}));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,16 +164,16 @@ function translateSidebars(
|
||||||
version: LoadedVersion,
|
version: LoadedVersion,
|
||||||
sidebarsTranslations: TranslationFileContent,
|
sidebarsTranslations: TranslationFileContent,
|
||||||
): Sidebars {
|
): Sidebars {
|
||||||
return mapValues(version.sidebars, (sidebar, sidebarName) => {
|
return mapValues(version.sidebars, (sidebar, sidebarName) =>
|
||||||
return translateSidebar({
|
translateSidebar({
|
||||||
sidebar,
|
sidebar,
|
||||||
sidebarName: getNormalizedSidebarName({
|
sidebarName: getNormalizedSidebarName({
|
||||||
sidebarName,
|
sidebarName,
|
||||||
versionName: version.versionName,
|
versionName: version.versionName,
|
||||||
}),
|
}),
|
||||||
sidebarsTranslations,
|
sidebarsTranslations,
|
||||||
});
|
}),
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVersionTranslationFiles(version: LoadedVersion): TranslationFiles {
|
function getVersionTranslationFiles(version: LoadedVersion): TranslationFiles {
|
||||||
|
|
|
@ -113,7 +113,6 @@ export type LastUpdateData = {
|
||||||
|
|
||||||
export type DocFrontMatter = {
|
export type DocFrontMatter = {
|
||||||
// Front matter uses snake case
|
// Front matter uses snake case
|
||||||
/* eslint-disable camelcase */
|
|
||||||
id?: string;
|
id?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
tags?: FrontMatterTag[];
|
tags?: FrontMatterTag[];
|
||||||
|
@ -133,7 +132,6 @@ export type DocFrontMatter = {
|
||||||
toc_max_heading_level?: number;
|
toc_max_heading_level?: number;
|
||||||
pagination_next?: string | null;
|
pagination_next?: string | null;
|
||||||
pagination_prev?: string | null;
|
pagination_prev?: string | null;
|
||||||
/* eslint-enable camelcase */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DocMetadataBase = LastUpdateData & {
|
export type DocMetadataBase = LastUpdateData & {
|
||||||
|
|
|
@ -18,11 +18,9 @@ declare module '@theme/MDXPage' {
|
||||||
readonly title: string;
|
readonly title: string;
|
||||||
readonly description: string;
|
readonly description: string;
|
||||||
readonly wrapperClassName?: string;
|
readonly wrapperClassName?: string;
|
||||||
/* eslint-disable camelcase */
|
|
||||||
readonly hide_table_of_contents?: string;
|
readonly hide_table_of_contents?: string;
|
||||||
readonly toc_min_heading_level?: number;
|
readonly toc_min_heading_level?: number;
|
||||||
readonly toc_max_heading_level?: number;
|
readonly toc_max_heading_level?: number;
|
||||||
/* eslint-enable camelcase */
|
|
||||||
};
|
};
|
||||||
readonly metadata: {readonly permalink: string};
|
readonly metadata: {readonly permalink: string};
|
||||||
readonly toc: readonly TOCItem[];
|
readonly toc: readonly TOCItem[];
|
||||||
|
|
|
@ -11,26 +11,28 @@ import DebugLayout from '@theme/DebugLayout';
|
||||||
import DebugJsonView from '@theme/DebugJsonView';
|
import DebugJsonView from '@theme/DebugJsonView';
|
||||||
import type {Props} from '@theme/DebugContent';
|
import type {Props} from '@theme/DebugContent';
|
||||||
|
|
||||||
const PluginInstanceContent = ({
|
function PluginInstanceContent({
|
||||||
pluginId,
|
pluginId,
|
||||||
pluginInstanceContent,
|
pluginInstanceContent,
|
||||||
}: {
|
}: {
|
||||||
pluginId: string;
|
pluginId: string;
|
||||||
pluginInstanceContent: unknown;
|
pluginInstanceContent: unknown;
|
||||||
}) => (
|
}) {
|
||||||
|
return (
|
||||||
<section style={{marginBottom: 30}}>
|
<section style={{marginBottom: 30}}>
|
||||||
<code>{pluginId}</code>
|
<code>{pluginId}</code>
|
||||||
<DebugJsonView src={pluginInstanceContent} collapseDepth={2} />
|
<DebugJsonView src={pluginInstanceContent} collapseDepth={2} />
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const PluginContent = ({
|
function PluginContent({
|
||||||
pluginName,
|
pluginName,
|
||||||
pluginContent,
|
pluginContent,
|
||||||
}: {
|
}: {
|
||||||
pluginName: string;
|
pluginName: string;
|
||||||
pluginContent: Record<string, unknown>;
|
pluginContent: Record<string, unknown>;
|
||||||
}) => {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section style={{marginBottom: 60}}>
|
<section style={{marginBottom: 60}}>
|
||||||
<h3>{pluginName}</h3>
|
<h3>{pluginName}</h3>
|
||||||
|
@ -40,19 +42,17 @@ const PluginContent = ({
|
||||||
.filter(
|
.filter(
|
||||||
([_pluginId, pluginInstanceContent]) => !!pluginInstanceContent,
|
([_pluginId, pluginInstanceContent]) => !!pluginInstanceContent,
|
||||||
)
|
)
|
||||||
.map(([pluginId, pluginInstanceContent]) => {
|
.map(([pluginId, pluginInstanceContent]) => (
|
||||||
return (
|
|
||||||
<PluginInstanceContent
|
<PluginInstanceContent
|
||||||
key={pluginId}
|
key={pluginId}
|
||||||
pluginId={pluginId}
|
pluginId={pluginId}
|
||||||
pluginInstanceContent={pluginInstanceContent}
|
pluginInstanceContent={pluginInstanceContent}
|
||||||
/>
|
/>
|
||||||
);
|
))}
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
function DebugContent({allContent}: Props): JSX.Element {
|
function DebugContent({allContent}: Props): JSX.Element {
|
||||||
return (
|
return (
|
||||||
|
@ -66,15 +66,13 @@ function DebugContent({allContent}: Props): JSX.Element {
|
||||||
(instanceContent) => !!instanceContent,
|
(instanceContent) => !!instanceContent,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.map(([pluginName, pluginContent]) => {
|
.map(([pluginName, pluginContent]) => (
|
||||||
return (
|
|
||||||
<PluginContent
|
<PluginContent
|
||||||
key={pluginName}
|
key={pluginName}
|
||||||
pluginName={pluginName}
|
pluginName={pluginName}
|
||||||
pluginContent={pluginContent}
|
pluginContent={pluginContent}
|
||||||
/>
|
/>
|
||||||
);
|
))}
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</DebugLayout>
|
</DebugLayout>
|
||||||
);
|
);
|
||||||
|
|
|
@ -15,7 +15,7 @@ const RootName = null;
|
||||||
|
|
||||||
// Seems ReactJson does not work with SSR
|
// Seems ReactJson does not work with SSR
|
||||||
// https://github.com/mac-s-g/react-json-view/issues/121
|
// https://github.com/mac-s-g/react-json-view/issues/121
|
||||||
const BrowserOnlyReactJson = (props: ReactJsonViewProps) => {
|
function BrowserOnlyReactJson(props: ReactJsonViewProps) {
|
||||||
return (
|
return (
|
||||||
<BrowserOnly>
|
<BrowserOnly>
|
||||||
{() => {
|
{() => {
|
||||||
|
@ -25,13 +25,11 @@ const BrowserOnlyReactJson = (props: ReactJsonViewProps) => {
|
||||||
}}
|
}}
|
||||||
</BrowserOnly>
|
</BrowserOnly>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
function DebugJsonView({src, collapseDepth}: Props): JSX.Element {
|
function DebugJsonView({src, collapseDepth}: Props): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<BrowserOnlyReactJson
|
<BrowserOnlyReactJson
|
||||||
// Prop type defined by react-json-view
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
||||||
src={src as object}
|
src={src as object}
|
||||||
style={{
|
style={{
|
||||||
marginTop: '10px',
|
marginTop: '10px',
|
||||||
|
@ -41,12 +39,12 @@ function DebugJsonView({src, collapseDepth}: Props): JSX.Element {
|
||||||
}}
|
}}
|
||||||
name={RootName}
|
name={RootName}
|
||||||
theme="paraiso"
|
theme="paraiso"
|
||||||
shouldCollapse={(field) => {
|
shouldCollapse={(field) =>
|
||||||
// By default, we collapse the json for performance reasons
|
// By default, we collapse the json for performance reasons
|
||||||
// See https://github.com/mac-s-g/react-json-view/issues/235
|
// See https://github.com/mac-s-g/react-json-view/issues/235
|
||||||
// Non-root elements that are larger than 50 fields are collapsed
|
// Non-root elements that are larger than 50 fields are collapsed
|
||||||
return field.name !== RootName && Object.keys(field.src).length > 50;
|
field.name !== RootName && Object.keys(field.src).length > 50
|
||||||
}}
|
}
|
||||||
collapsed={collapseDepth}
|
collapsed={collapseDepth}
|
||||||
groupArraysAfterLength={5}
|
groupArraysAfterLength={5}
|
||||||
enableClipboard={false}
|
enableClipboard={false}
|
||||||
|
|
|
@ -10,7 +10,8 @@ import Head from '@docusaurus/Head';
|
||||||
import Link from '@docusaurus/Link';
|
import Link from '@docusaurus/Link';
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
const DebugNavLink = ({to, children}: {to: string; children: ReactNode}) => (
|
function DebugNavLink({to, children}: {to: string; children: ReactNode}) {
|
||||||
|
return (
|
||||||
<Link
|
<Link
|
||||||
className={styles.navlink}
|
className={styles.navlink}
|
||||||
isNavLink
|
isNavLink
|
||||||
|
@ -21,7 +22,8 @@ const DebugNavLink = ({to, children}: {to: string; children: ReactNode}) => (
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function DebugLayout({children}: {children: ReactNode}): JSX.Element {
|
function DebugLayout({children}: {children: ReactNode}): JSX.Element {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
/// <reference types="@docusaurus/module-type-aliases" />
|
/// <reference types="@docusaurus/module-type-aliases" />
|
||||||
|
|
||||||
interface Window {
|
interface Window {
|
||||||
/* eslint-disable camelcase */
|
|
||||||
gtag: (
|
gtag: (
|
||||||
command: string,
|
command: string,
|
||||||
fields: string,
|
fields: string,
|
||||||
|
@ -18,5 +17,4 @@ interface Window {
|
||||||
page_path?: string;
|
page_path?: string;
|
||||||
},
|
},
|
||||||
) => void;
|
) => void;
|
||||||
/* eslint-enable camelcase */
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,13 +27,12 @@ describe('createSitemap', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('empty site', () => {
|
test('empty site', () =>
|
||||||
return expect(async () => {
|
expect(async () => {
|
||||||
await createSitemap({} as DocusaurusConfig, [], {});
|
await createSitemap({} as DocusaurusConfig, [], {});
|
||||||
}).rejects.toThrow(
|
}).rejects.toThrow(
|
||||||
'URL in docusaurus.config.js cannot be empty/undefined.',
|
'URL in docusaurus.config.js cannot be empty/undefined.',
|
||||||
);
|
));
|
||||||
});
|
|
||||||
|
|
||||||
test('exclusion of 404 page', async () => {
|
test('exclusion of 404 page', async () => {
|
||||||
const sitemap = await createSitemap(
|
const sitemap = await createSitemap(
|
||||||
|
|
|
@ -27,8 +27,7 @@ const ThemeStorageKey = 'theme';
|
||||||
const noFlashColorMode = ({
|
const noFlashColorMode = ({
|
||||||
defaultMode,
|
defaultMode,
|
||||||
respectPrefersColorScheme,
|
respectPrefersColorScheme,
|
||||||
}: ThemeConfig['colorMode']) => {
|
}: ThemeConfig['colorMode']) => `(function() {
|
||||||
return `(function() {
|
|
||||||
var defaultMode = '${defaultMode}';
|
var defaultMode = '${defaultMode}';
|
||||||
var respectPrefersColorScheme = ${respectPrefersColorScheme};
|
var respectPrefersColorScheme = ${respectPrefersColorScheme};
|
||||||
|
|
||||||
|
@ -63,7 +62,6 @@ const noFlashColorMode = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})();`;
|
})();`;
|
||||||
};
|
|
||||||
|
|
||||||
// Duplicated constant. Unfortunately we can't import it from theme-common, as we need to support older nodejs versions without ESM support
|
// Duplicated constant. Unfortunately we can't import it from theme-common, as we need to support older nodejs versions without ESM support
|
||||||
// TODO: import from theme-common once we only support Node.js with ESM support
|
// TODO: import from theme-common once we only support Node.js with ESM support
|
||||||
|
@ -136,12 +134,11 @@ export default function docusaurusThemeClassic(
|
||||||
getTranslationFiles: async () => getTranslationFiles({themeConfig}),
|
getTranslationFiles: async () => getTranslationFiles({themeConfig}),
|
||||||
translateThemeConfig,
|
translateThemeConfig,
|
||||||
|
|
||||||
getDefaultCodeTranslationMessages: () => {
|
getDefaultCodeTranslationMessages: () =>
|
||||||
return readDefaultCodeTranslationMessages({
|
readDefaultCodeTranslationMessages({
|
||||||
dirPath: path.resolve(__dirname, '..', 'codeTranslations'),
|
dirPath: path.resolve(__dirname, '..', 'codeTranslations'),
|
||||||
locale: currentLocale,
|
locale: currentLocale,
|
||||||
});
|
}),
|
||||||
},
|
|
||||||
|
|
||||||
getClientModules() {
|
getClientModules() {
|
||||||
const modules = [
|
const modules = [
|
||||||
|
|
|
@ -13,10 +13,13 @@ import BlogPostAuthor from '@theme/BlogPostAuthor';
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
// Component responsible for the authors layout
|
// Component responsible for the authors layout
|
||||||
export default function BlogPostAuthors({authors, assets}: Props): JSX.Element {
|
export default function BlogPostAuthors({
|
||||||
|
authors,
|
||||||
|
assets,
|
||||||
|
}: Props): JSX.Element | null {
|
||||||
const authorsCount = authors.length;
|
const authorsCount = authors.length;
|
||||||
if (authorsCount === 0) {
|
if (authorsCount === 0) {
|
||||||
return <></>;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="row margin-top--md margin-bottom--sm">
|
<div className="row margin-top--md margin-bottom--sm">
|
||||||
|
|
|
@ -28,8 +28,7 @@ export default function BlogSidebar({sidebar}: Props): JSX.Element | null {
|
||||||
{sidebar.title}
|
{sidebar.title}
|
||||||
</div>
|
</div>
|
||||||
<ul className={styles.sidebarItemList}>
|
<ul className={styles.sidebarItemList}>
|
||||||
{sidebar.items.map((item) => {
|
{sidebar.items.map((item) => (
|
||||||
return (
|
|
||||||
<li key={item.permalink} className={styles.sidebarItem}>
|
<li key={item.permalink} className={styles.sidebarItem}>
|
||||||
<Link
|
<Link
|
||||||
isNavLink
|
isNavLink
|
||||||
|
@ -39,8 +38,7 @@ export default function BlogSidebar({sidebar}: Props): JSX.Element | null {
|
||||||
{item.title}
|
{item.title}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
);
|
))}
|
||||||
})}
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
|
|
|
@ -59,7 +59,7 @@ function EditMetaRow({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DocItemFooter(props: Props): JSX.Element {
|
export default function DocItemFooter(props: Props): JSX.Element | null {
|
||||||
const {content: DocContent} = props;
|
const {content: DocContent} = props;
|
||||||
const {metadata} = DocContent;
|
const {metadata} = DocContent;
|
||||||
const {editUrl, lastUpdatedAt, formattedLastUpdatedAt, lastUpdatedBy, tags} =
|
const {editUrl, lastUpdatedAt, formattedLastUpdatedAt, lastUpdatedBy, tags} =
|
||||||
|
@ -71,7 +71,7 @@ export default function DocItemFooter(props: Props): JSX.Element {
|
||||||
const canDisplayFooter = canDisplayTagsRow || canDisplayEditMetaRow;
|
const canDisplayFooter = canDisplayTagsRow || canDisplayEditMetaRow;
|
||||||
|
|
||||||
if (!canDisplayFooter) {
|
if (!canDisplayFooter) {
|
||||||
return <></>;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -90,12 +90,12 @@ function DocSidebarDesktop({path, sidebar, onCollapse, isHidden}: Props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/function-component-definition
|
||||||
const DocSidebarMobileSecondaryMenu: MobileSecondaryMenuComponent<Props> = ({
|
const DocSidebarMobileSecondaryMenu: MobileSecondaryMenuComponent<Props> = ({
|
||||||
toggleSidebar,
|
toggleSidebar,
|
||||||
sidebar,
|
sidebar,
|
||||||
path,
|
path,
|
||||||
}) => {
|
}) => (
|
||||||
return (
|
|
||||||
<ul className={clsx(ThemeClassNames.docs.docSidebarMenu, 'menu__list')}>
|
<ul className={clsx(ThemeClassNames.docs.docSidebarMenu, 'menu__list')}>
|
||||||
<DocSidebarItems
|
<DocSidebarItems
|
||||||
items={sidebar}
|
items={sidebar}
|
||||||
|
@ -104,8 +104,7 @@ const DocSidebarMobileSecondaryMenu: MobileSecondaryMenuComponent<Props> = ({
|
||||||
level={1}
|
level={1}
|
||||||
/>
|
/>
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
function DocSidebarMobile(props: Props) {
|
function DocSidebarMobile(props: Props) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -44,11 +44,8 @@ const isActiveSidebarItem = (
|
||||||
// Optimize sidebar at each "level"
|
// Optimize sidebar at each "level"
|
||||||
// TODO this item should probably not receive the "activePath" props
|
// TODO this item should probably not receive the "activePath" props
|
||||||
// TODO this triggers whole sidebar re-renders on navigation
|
// TODO this triggers whole sidebar re-renders on navigation
|
||||||
export const DocSidebarItems = memo(function DocSidebarItems({
|
export const DocSidebarItems = memo(
|
||||||
items,
|
({items, ...props}: DocSidebarItemsProps): JSX.Element => (
|
||||||
...props
|
|
||||||
}: DocSidebarItemsProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<>
|
<>
|
||||||
{items.map((item, index) => (
|
{items.map((item, index) => (
|
||||||
<DocSidebarItem
|
<DocSidebarItem
|
||||||
|
@ -58,8 +55,8 @@ export const DocSidebarItems = memo(function DocSidebarItems({
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
),
|
||||||
});
|
);
|
||||||
|
|
||||||
export default function DocSidebarItem({
|
export default function DocSidebarItem({
|
||||||
item,
|
item,
|
||||||
|
|
|
@ -154,11 +154,11 @@ function DocVersionBannerEnabled({versionMetadata}: Props): JSX.Element {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DocVersionBanner({versionMetadata}: Props): JSX.Element {
|
function DocVersionBanner({versionMetadata}: Props): JSX.Element | null {
|
||||||
if (versionMetadata.banner) {
|
if (versionMetadata.banner) {
|
||||||
return <DocVersionBannerEnabled versionMetadata={versionMetadata} />;
|
return <DocVersionBannerEnabled versionMetadata={versionMetadata} />;
|
||||||
}
|
}
|
||||||
return <></>;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DocVersionBanner;
|
export default DocVersionBanner;
|
||||||
|
|
|
@ -49,12 +49,13 @@ function FooterLink({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const FooterLogo = ({
|
function FooterLogo({
|
||||||
sources,
|
sources,
|
||||||
alt,
|
alt,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
}: Pick<ThemedImageProps, 'sources' | 'alt' | 'width' | 'height'>) => (
|
}: Pick<ThemedImageProps, 'sources' | 'alt' | 'width' | 'height'>) {
|
||||||
|
return (
|
||||||
<ThemedImage
|
<ThemedImage
|
||||||
className="footer__logo"
|
className="footer__logo"
|
||||||
alt={alt}
|
alt={alt}
|
||||||
|
@ -62,7 +63,8 @@ const FooterLogo = ({
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function Footer(): JSX.Element | null {
|
function Footer(): JSX.Element | null {
|
||||||
const {footer} = useThemeConfig();
|
const {footer} = useThemeConfig();
|
||||||
|
|
|
@ -16,8 +16,8 @@ import styles from './styles.module.css';
|
||||||
|
|
||||||
type HeadingComponent = (props: Props) => JSX.Element;
|
type HeadingComponent = (props: Props) => JSX.Element;
|
||||||
|
|
||||||
export const MainHeading: HeadingComponent = function MainHeading({...props}) {
|
// eslint-disable-next-line react/function-component-definition
|
||||||
return (
|
export const MainHeading: HeadingComponent = ({...props}) => (
|
||||||
<header>
|
<header>
|
||||||
<h1
|
<h1
|
||||||
{...props}
|
{...props}
|
||||||
|
@ -26,13 +26,11 @@ export const MainHeading: HeadingComponent = function MainHeading({...props}) {
|
||||||
{props.children}
|
{props.children}
|
||||||
</h1>
|
</h1>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
const createAnchorHeading = (
|
const createAnchorHeading =
|
||||||
Tag: HeadingType,
|
(Tag: HeadingType) =>
|
||||||
): ((props: Props) => JSX.Element) =>
|
({id, ...props}: Props) => {
|
||||||
function TargetComponent({id, ...props}) {
|
|
||||||
const {
|
const {
|
||||||
navbar: {hideOnScroll},
|
navbar: {hideOnScroll},
|
||||||
} = useThemeConfig();
|
} = useThemeConfig();
|
||||||
|
@ -65,8 +63,7 @@ const createAnchorHeading = (
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Heading = (headingType: HeadingType): ((props: Props) => JSX.Element) => {
|
const Heading = (headingType: HeadingType): ((props: Props) => JSX.Element) =>
|
||||||
return headingType === 'h1' ? MainHeading : createAnchorHeading(headingType);
|
headingType === 'h1' ? MainHeading : createAnchorHeading(headingType);
|
||||||
};
|
|
||||||
|
|
||||||
export default Heading;
|
export default Heading;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type {Props} from '@theme/IconArrow';
|
import type {Props} from '@theme/IconArrow';
|
||||||
|
|
||||||
const IconArrow = (props: Props): JSX.Element => {
|
function IconArrow(props: Props): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<svg width="20" height="20" aria-hidden="true" {...props}>
|
<svg width="20" height="20" aria-hidden="true" {...props}>
|
||||||
<g fill="#7a7a7a">
|
<g fill="#7a7a7a">
|
||||||
|
@ -17,6 +17,6 @@ const IconArrow = (props: Props): JSX.Element => {
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default IconArrow;
|
export default IconArrow;
|
||||||
|
|
|
@ -12,7 +12,7 @@ import type {Props} from '@theme/IconEdit';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
const IconEdit = ({className, ...restProps}: Props): JSX.Element => {
|
function IconEdit({className, ...restProps}: Props): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
|
@ -27,6 +27,6 @@ const IconEdit = ({className, ...restProps}: Props): JSX.Element => {
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default IconEdit;
|
export default IconEdit;
|
||||||
|
|
|
@ -10,10 +10,7 @@ import type {Props} from '@theme/IconExternalLink';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
const IconExternalLink = ({
|
function IconExternalLink({width = 13.5, height = 13.5}: Props): JSX.Element {
|
||||||
width = 13.5,
|
|
||||||
height = 13.5,
|
|
||||||
}: Props): JSX.Element => {
|
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width={width}
|
width={width}
|
||||||
|
@ -27,6 +24,6 @@ const IconExternalLink = ({
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default IconExternalLink;
|
export default IconExternalLink;
|
||||||
|
|
|
@ -8,11 +8,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type {Props} from '@theme/IconLanguage';
|
import type {Props} from '@theme/IconLanguage';
|
||||||
|
|
||||||
const IconLanguage = ({
|
function IconLanguage({width = 20, height = 20, ...props}: Props): JSX.Element {
|
||||||
width = 20,
|
|
||||||
height = 20,
|
|
||||||
...props
|
|
||||||
}: Props): JSX.Element => {
|
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
|
@ -26,6 +22,6 @@ const IconLanguage = ({
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default IconLanguage;
|
export default IconLanguage;
|
||||||
|
|
|
@ -8,12 +8,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type {Props} from '@theme/IconMenu';
|
import type {Props} from '@theme/IconMenu';
|
||||||
|
|
||||||
const IconMenu = ({
|
function IconMenu({
|
||||||
width = 30,
|
width = 30,
|
||||||
height = 30,
|
height = 30,
|
||||||
className,
|
className,
|
||||||
...restProps
|
...restProps
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
className={className}
|
className={className}
|
||||||
|
@ -31,6 +31,6 @@ const IconMenu = ({
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default IconMenu;
|
export default IconMenu;
|
||||||
|
|
|
@ -101,7 +101,7 @@ export default function LayoutHead(props: Props): JSX.Element {
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<html lang={htmlLang} dir={htmlDir} />
|
<html lang={htmlLang} dir={htmlDir} />
|
||||||
{favicon && <link rel="shortcut icon" href={faviconUrl} />}
|
{favicon && <link rel="icon" href={faviconUrl} />}
|
||||||
<title>{pageTitle}</title>
|
<title>{pageTitle}</title>
|
||||||
<meta property="og:title" content={pageTitle} />
|
<meta property="og:title" content={pageTitle} />
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
|
|
@ -14,7 +14,7 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
import {useThemeConfig} from '@docusaurus/theme-common';
|
import {useThemeConfig} from '@docusaurus/theme-common';
|
||||||
|
|
||||||
const Logo = (props: Props): JSX.Element => {
|
function Logo(props: Props): JSX.Element {
|
||||||
const {
|
const {
|
||||||
siteConfig: {title},
|
siteConfig: {title},
|
||||||
} = useDocusaurusContext();
|
} = useDocusaurusContext();
|
||||||
|
@ -46,11 +46,11 @@ const Logo = (props: Props): JSX.Element => {
|
||||||
(imageClassName ? (
|
(imageClassName ? (
|
||||||
<div className={imageClassName}>{themedImage}</div>
|
<div className={imageClassName}>{themedImage}</div>
|
||||||
) : (
|
) : (
|
||||||
<>{themedImage}</>
|
themedImage
|
||||||
))}
|
))}
|
||||||
{navbarTitle != null && <b className={titleClassName}>{navbarTitle}</b>}
|
{navbarTitle != null && <b className={titleClassName}>{navbarTitle}</b>}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default Logo;
|
export default Logo;
|
||||||
|
|
|
@ -105,12 +105,13 @@ function useSecondaryMenu({
|
||||||
});
|
});
|
||||||
const previousContent = usePrevious(content);
|
const previousContent = usePrevious(content);
|
||||||
|
|
||||||
const [shown, setShown] = useState<boolean>(() => {
|
const [shown, setShown] = useState<boolean>(
|
||||||
|
() =>
|
||||||
// /!\ content is set with useEffect,
|
// /!\ content is set with useEffect,
|
||||||
// so it's not available on mount anyway
|
// so it's not available on mount anyway
|
||||||
// "return !!content" => always returns false
|
// "return !!content" => always returns false
|
||||||
return false;
|
false,
|
||||||
});
|
);
|
||||||
|
|
||||||
// When content is become available for the first time (set in useEffect)
|
// When content is become available for the first time (set in useEffect)
|
||||||
// we set this content to be shown!
|
// we set this content to be shown!
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React, {useMemo} from 'react';
|
||||||
|
|
||||||
import useTheme from '@theme/hooks/useTheme';
|
import useTheme from '@theme/hooks/useTheme';
|
||||||
import ThemeContext from '@theme/ThemeContext';
|
import ThemeContext from '@theme/ThemeContext';
|
||||||
|
@ -13,9 +13,13 @@ import type {Props} from '@theme/ThemeProvider';
|
||||||
|
|
||||||
function ThemeProvider(props: Props): JSX.Element {
|
function ThemeProvider(props: Props): JSX.Element {
|
||||||
const {isDarkTheme, setLightTheme, setDarkTheme} = useTheme();
|
const {isDarkTheme, setLightTheme, setDarkTheme} = useTheme();
|
||||||
|
const contextValue = useMemo(
|
||||||
|
() => ({isDarkTheme, setLightTheme, setDarkTheme}),
|
||||||
|
[isDarkTheme, setLightTheme, setDarkTheme],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeContext.Provider value={{isDarkTheme, setLightTheme, setDarkTheme}}>
|
<ThemeContext.Provider value={contextValue}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</ThemeContext.Provider>
|
</ThemeContext.Provider>
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,7 +14,7 @@ import type {Props} from '@theme/ThemedImage';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
const ThemedImage = (props: Props): JSX.Element => {
|
function ThemedImage(props: Props): JSX.Element {
|
||||||
const isBrowser = useIsBrowser();
|
const isBrowser = useIsBrowser();
|
||||||
const {isDarkTheme} = useThemeContext();
|
const {isDarkTheme} = useThemeContext();
|
||||||
const {sources, className, alt = '', ...propsRest} = props;
|
const {sources, className, alt = '', ...propsRest} = props;
|
||||||
|
@ -46,6 +46,6 @@ const ThemedImage = (props: Props): JSX.Element => {
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default ThemedImage;
|
export default ThemedImage;
|
||||||
|
|
|
@ -18,16 +18,20 @@ interface IconProps {
|
||||||
style: CSSProperties;
|
style: CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Dark = ({icon, style}: IconProps): JSX.Element => (
|
function Dark({icon, style}: IconProps): JSX.Element {
|
||||||
|
return (
|
||||||
<span className={clsx(styles.toggleIcon, styles.dark)} style={style}>
|
<span className={clsx(styles.toggleIcon, styles.dark)} style={style}>
|
||||||
{icon}
|
{icon}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
const Light = ({icon, style}: IconProps): JSX.Element => (
|
}
|
||||||
|
function Light({icon, style}: IconProps): JSX.Element {
|
||||||
|
return (
|
||||||
<span className={clsx(styles.toggleIcon, styles.light)} style={style}>
|
<span className={clsx(styles.toggleIcon, styles.light)} style={style}>
|
||||||
{icon}
|
{icon}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Based on react-toggle (https://github.com/aaronshaf/react-toggle/).
|
// Based on react-toggle (https://github.com/aaronshaf/react-toggle/).
|
||||||
const Toggle = memo(
|
const Toggle = memo(
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React, {useMemo} from 'react';
|
||||||
|
|
||||||
import useTabGroupChoice from '@theme/hooks/useTabGroupChoice';
|
import useTabGroupChoice from '@theme/hooks/useTabGroupChoice';
|
||||||
import UserPreferencesContext from '@theme/UserPreferencesContext';
|
import UserPreferencesContext from '@theme/UserPreferencesContext';
|
||||||
|
@ -13,12 +13,15 @@ import type {Props} from '@theme/UserPreferencesProvider';
|
||||||
|
|
||||||
function UserPreferencesProvider(props: Props): JSX.Element {
|
function UserPreferencesProvider(props: Props): JSX.Element {
|
||||||
const {tabGroupChoices, setTabGroupChoices} = useTabGroupChoice();
|
const {tabGroupChoices, setTabGroupChoices} = useTabGroupChoice();
|
||||||
return (
|
const contextValue = useMemo(
|
||||||
<UserPreferencesContext.Provider
|
() => ({
|
||||||
value={{
|
|
||||||
tabGroupChoices,
|
tabGroupChoices,
|
||||||
setTabGroupChoices,
|
setTabGroupChoices,
|
||||||
}}>
|
}),
|
||||||
|
[tabGroupChoices, setTabGroupChoices],
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<UserPreferencesContext.Provider value={contextValue}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</UserPreferencesContext.Provider>
|
</UserPreferencesContext.Provider>
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,9 +21,8 @@ const themes = {
|
||||||
type Themes = typeof themes[keyof typeof themes];
|
type Themes = typeof themes[keyof typeof themes];
|
||||||
|
|
||||||
// Ensure to always return a valid theme even if input is invalid
|
// Ensure to always return a valid theme even if input is invalid
|
||||||
const coerceToTheme = (theme?: string | null): Themes => {
|
const coerceToTheme = (theme?: string | null): Themes =>
|
||||||
return theme === themes.dark ? themes.dark : themes.light;
|
theme === themes.dark ? themes.dark : themes.light;
|
||||||
};
|
|
||||||
|
|
||||||
const getInitialTheme = (defaultMode: Themes | undefined): Themes => {
|
const getInitialTheme = (defaultMode: Themes | undefined): Themes => {
|
||||||
if (!ExecutionEnvironment.canUseDOM) {
|
if (!ExecutionEnvironment.canUseDOM) {
|
||||||
|
|
|
@ -80,9 +80,10 @@ ${warning}
|
||||||
});
|
});
|
||||||
|
|
||||||
const translations = filesExtractedTranslations.reduce(
|
const translations = filesExtractedTranslations.reduce(
|
||||||
(acc, extractedTranslations) => {
|
(acc, extractedTranslations) => ({
|
||||||
return {...acc, ...extractedTranslations.translations};
|
...acc,
|
||||||
},
|
...extractedTranslations.translations,
|
||||||
|
}),
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -193,9 +194,7 @@ ${logKeys(unknownMessages)}`),
|
||||||
};
|
};
|
||||||
|
|
||||||
const untranslatedKeys = Object.entries(newLocaleFileMessages)
|
const untranslatedKeys = Object.entries(newLocaleFileMessages)
|
||||||
.filter(([key, value]) => {
|
.filter(([key, value]) => value === baseFileMessages[key])
|
||||||
return value === baseFileMessages[key];
|
|
||||||
})
|
|
||||||
.map(([key]) => key);
|
.map(([key]) => key);
|
||||||
|
|
||||||
if (untranslatedKeys.length) {
|
if (untranslatedKeys.length) {
|
||||||
|
|
|
@ -29,7 +29,7 @@ export type DetailsProps = {
|
||||||
summary?: ReactElement;
|
summary?: ReactElement;
|
||||||
} & ComponentProps<'details'>;
|
} & ComponentProps<'details'>;
|
||||||
|
|
||||||
const Details = ({summary, children, ...props}: DetailsProps): JSX.Element => {
|
function Details({summary, children, ...props}: DetailsProps): JSX.Element {
|
||||||
const isBrowser = useIsBrowser();
|
const isBrowser = useIsBrowser();
|
||||||
const detailsRef = useRef<HTMLDetailsElement>(null);
|
const detailsRef = useRef<HTMLDetailsElement>(null);
|
||||||
|
|
||||||
|
@ -89,6 +89,6 @@ const Details = ({summary, children, ...props}: DetailsProps): JSX.Element => {
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
</details>
|
</details>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default Details;
|
export default Details;
|
||||||
|
|
|
@ -41,13 +41,13 @@ const useAnnouncementBarContextValue = (): AnnouncementBarAPI => {
|
||||||
const {announcementBar} = useThemeConfig();
|
const {announcementBar} = useThemeConfig();
|
||||||
const isBrowser = useIsBrowser();
|
const isBrowser = useIsBrowser();
|
||||||
|
|
||||||
const [isClosed, setClosed] = useState(() => {
|
const [isClosed, setClosed] = useState(() =>
|
||||||
return isBrowser
|
isBrowser
|
||||||
? // On client navigation: init with localstorage value
|
? // On client navigation: init with localstorage value
|
||||||
isDismissedInStorage()
|
isDismissedInStorage()
|
||||||
: // On server/hydration: always visible to prevent layout shifts (will be hidden with css if needed)
|
: // On server/hydration: always visible to prevent layout shifts (will be hidden with css if needed)
|
||||||
false;
|
false,
|
||||||
});
|
);
|
||||||
// Update state after hydration
|
// Update state after hydration
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setClosed(isDismissedInStorage());
|
setClosed(isDismissedInStorage());
|
||||||
|
@ -85,28 +85,29 @@ const useAnnouncementBarContextValue = (): AnnouncementBarAPI => {
|
||||||
}
|
}
|
||||||
}, [announcementBar]);
|
}, [announcementBar]);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(
|
||||||
return {
|
() => ({
|
||||||
isActive: !!announcementBar && !isClosed,
|
isActive: !!announcementBar && !isClosed,
|
||||||
close: handleClose,
|
close: handleClose,
|
||||||
};
|
}),
|
||||||
}, [announcementBar, isClosed, handleClose]);
|
[announcementBar, isClosed, handleClose],
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const AnnouncementBarContext = createContext<AnnouncementBarAPI | null>(null);
|
const AnnouncementBarContext = createContext<AnnouncementBarAPI | null>(null);
|
||||||
|
|
||||||
export const AnnouncementBarProvider = ({
|
export function AnnouncementBarProvider({
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}): JSX.Element => {
|
}): JSX.Element {
|
||||||
const value = useAnnouncementBarContextValue();
|
const value = useAnnouncementBarContextValue();
|
||||||
return (
|
return (
|
||||||
<AnnouncementBarContext.Provider value={value}>
|
<AnnouncementBarContext.Provider value={value}>
|
||||||
{children}
|
{children}
|
||||||
</AnnouncementBarContext.Provider>
|
</AnnouncementBarContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export const useAnnouncementBar = (): AnnouncementBarAPI => {
|
export const useAnnouncementBar = (): AnnouncementBarAPI => {
|
||||||
const api = useContext(AnnouncementBarContext);
|
const api = useContext(AnnouncementBarContext);
|
||||||
|
|
|
@ -132,7 +132,7 @@ const Context = createContext<DocsPreferredVersionContextValue | null>(null);
|
||||||
export function DocsPreferredVersionContextProvider({
|
export function DocsPreferredVersionContextProvider({
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
children: ReactNode;
|
children: JSX.Element;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
if (isDocsPluginEnabled) {
|
if (isDocsPluginEnabled) {
|
||||||
return (
|
return (
|
||||||
|
@ -141,7 +141,7 @@ export function DocsPreferredVersionContextProvider({
|
||||||
</DocsPreferredVersionContextProviderUnsafe>
|
</DocsPreferredVersionContextProviderUnsafe>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return <>{children}</>;
|
return children;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,8 @@ const DocsPreferredVersionStorage = {
|
||||||
read: (
|
read: (
|
||||||
pluginId: string,
|
pluginId: string,
|
||||||
persistence: DocsVersionPersistence,
|
persistence: DocsVersionPersistence,
|
||||||
): string | null => {
|
): string | null =>
|
||||||
return createStorageSlot(storageKey(pluginId), {persistence}).get();
|
createStorageSlot(storageKey(pluginId), {persistence}).get(),
|
||||||
},
|
|
||||||
|
|
||||||
clear: (pluginId: string, persistence: DocsVersionPersistence): void => {
|
clear: (pluginId: string, persistence: DocsVersionPersistence): void => {
|
||||||
createStorageSlot(storageKey(pluginId), {persistence}).del();
|
createStorageSlot(storageKey(pluginId), {persistence}).del();
|
||||||
|
|
|
@ -24,12 +24,12 @@ export function useHistoryActionHandler(handler: HistoryBlockHandler): void {
|
||||||
lastHandlerRef.current = handler;
|
lastHandlerRef.current = handler;
|
||||||
}, [handler]);
|
}, [handler]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(
|
||||||
|
() =>
|
||||||
// See https://github.com/remix-run/history/blob/main/docs/blocking-transitions.md
|
// See https://github.com/remix-run/history/blob/main/docs/blocking-transitions.md
|
||||||
return block((location, action) => {
|
block((location, action) => lastHandlerRef.current(location, action)),
|
||||||
return lastHandlerRef.current(location, action);
|
[block, lastHandlerRef],
|
||||||
});
|
);
|
||||||
}, [block, lastHandlerRef]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -29,7 +29,7 @@ type ExtraProps = {
|
||||||
toggleSidebar: () => void;
|
toggleSidebar: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MobileSecondaryMenuComponent<Props extends unknown> = ComponentType<
|
export type MobileSecondaryMenuComponent<Props> = ComponentType<
|
||||||
Props & ExtraProps
|
Props & ExtraProps
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
@ -108,9 +108,7 @@ export function MobileSecondaryMenuFiller<
|
||||||
setState({component, props: memoizedProps});
|
setState({component, props: memoizedProps});
|
||||||
}, [setState, component, memoizedProps]);
|
}, [setState, component, memoizedProps]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => () => setState(null), [setState]);
|
||||||
return () => setState(null);
|
|
||||||
}, [setState]);
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,7 @@ export const isSamePath = (
|
||||||
path1: string | undefined,
|
path1: string | undefined,
|
||||||
path2: string | undefined,
|
path2: string | undefined,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
const normalize = (pathname: string | undefined) => {
|
const normalize = (pathname: string | undefined) =>
|
||||||
return !pathname || pathname?.endsWith('/') ? pathname : `${pathname}/`;
|
!pathname || pathname?.endsWith('/') ? pathname : `${pathname}/`;
|
||||||
};
|
|
||||||
return normalize(path1) === normalize(path2);
|
return normalize(path1) === normalize(path2);
|
||||||
};
|
};
|
||||||
|
|
|
@ -83,14 +83,13 @@ export function useScrollController(): ScrollController {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getScrollPosition = (): ScrollPosition | null => {
|
const getScrollPosition = (): ScrollPosition | null =>
|
||||||
return ExecutionEnvironment.canUseDOM
|
ExecutionEnvironment.canUseDOM
|
||||||
? {
|
? {
|
||||||
scrollX: window.pageXOffset,
|
scrollX: window.pageXOffset,
|
||||||
scrollY: window.pageYOffset,
|
scrollY: window.pageYOffset,
|
||||||
}
|
}
|
||||||
: null;
|
: null;
|
||||||
};
|
|
||||||
|
|
||||||
type ScrollPosition = {scrollX: number; scrollY: number};
|
type ScrollPosition = {scrollX: number; scrollY: number};
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,8 @@ export function useTOCFilter({
|
||||||
minHeadingLevel,
|
minHeadingLevel,
|
||||||
maxHeadingLevel,
|
maxHeadingLevel,
|
||||||
}: FilterTOCParam): readonly TOCItem[] {
|
}: FilterTOCParam): readonly TOCItem[] {
|
||||||
return useMemo(() => {
|
return useMemo(
|
||||||
return filterTOC({toc, minHeadingLevel, maxHeadingLevel});
|
() => filterTOC({toc, minHeadingLevel, maxHeadingLevel}),
|
||||||
}, [toc, minHeadingLevel, maxHeadingLevel]);
|
[toc, minHeadingLevel, maxHeadingLevel],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,8 +112,7 @@ export function usePluralForm(): {
|
||||||
} {
|
} {
|
||||||
const localePluralForm = useLocalePluralForms();
|
const localePluralForm = useLocalePluralForms();
|
||||||
return {
|
return {
|
||||||
selectMessage: (count: number, pluralMessages: string): string => {
|
selectMessage: (count: number, pluralMessages: string): string =>
|
||||||
return selectPluralMessage(pluralMessages, count, localePluralForm);
|
selectPluralMessage(pluralMessages, count, localePluralForm),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,13 @@ import ReactLiveScope from '@theme/ReactLiveScope';
|
||||||
import CodeBlock from '@theme-init/CodeBlock';
|
import CodeBlock from '@theme-init/CodeBlock';
|
||||||
|
|
||||||
const withLiveEditor = (Component) => {
|
const withLiveEditor = (Component) => {
|
||||||
const WrappedComponent = (props) => {
|
function WrappedComponent(props) {
|
||||||
if (props.live) {
|
if (props.live) {
|
||||||
return <Playground scope={ReactLiveScope} {...props} />;
|
return <Playground scope={ReactLiveScope} {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Component {...props} />;
|
return <Component {...props} />;
|
||||||
};
|
}
|
||||||
|
|
||||||
return WrappedComponent;
|
return WrappedComponent;
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,9 +14,9 @@ import {memoize} from 'lodash';
|
||||||
|
|
||||||
import type {DocusaurusContext, Plugin} from '@docusaurus/types';
|
import type {DocusaurusContext, Plugin} from '@docusaurus/types';
|
||||||
|
|
||||||
const getCompiledOpenSearchTemplate = memoize(() => {
|
const getCompiledOpenSearchTemplate = memoize(() =>
|
||||||
return compile(openSearchTemplate.trim());
|
compile(openSearchTemplate.trim()),
|
||||||
});
|
);
|
||||||
|
|
||||||
function renderOpenSearchTemplate(data: {
|
function renderOpenSearchTemplate(data: {
|
||||||
title: string;
|
title: string;
|
||||||
|
|
|
@ -152,8 +152,8 @@ function DocSearch({
|
||||||
}).current;
|
}).current;
|
||||||
|
|
||||||
const transformItems = useRef<DocSearchModalProps['transformItems']>(
|
const transformItems = useRef<DocSearchModalProps['transformItems']>(
|
||||||
(items) => {
|
(items) =>
|
||||||
return items.map((item) => {
|
items.map((item) => {
|
||||||
// If Algolia contains a external domain, we should navigate without relative URL
|
// If Algolia contains a external domain, we should navigate without relative URL
|
||||||
if (isRegexpStringMatch(externalUrlRegex, item.url)) {
|
if (isRegexpStringMatch(externalUrlRegex, item.url)) {
|
||||||
return item;
|
return item;
|
||||||
|
@ -165,11 +165,11 @@ function DocSearch({
|
||||||
...item,
|
...item,
|
||||||
url: withBaseUrl(`${url.pathname}${url.hash}`),
|
url: withBaseUrl(`${url.pathname}${url.hash}`),
|
||||||
};
|
};
|
||||||
});
|
}),
|
||||||
},
|
|
||||||
).current;
|
).current;
|
||||||
|
|
||||||
const resultsFooterComponent = useMemo(
|
const resultsFooterComponent = useMemo(
|
||||||
|
// eslint-disable-next-line react/no-unstable-nested-components
|
||||||
() => (footerProps: ResultsFooterProps) =>
|
() => (footerProps: ResultsFooterProps) =>
|
||||||
<ResultsFooter {...footerProps} onClose={onClose} />,
|
<ResultsFooter {...footerProps} onClose={onClose} />,
|
||||||
[onClose],
|
[onClose],
|
||||||
|
@ -250,7 +250,7 @@ function DocSearch({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SearchBar() {
|
function SearchBar(): JSX.Element {
|
||||||
const {siteConfig} = useDocusaurusContext();
|
const {siteConfig} = useDocusaurusContext();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return <DocSearch {...siteConfig.themeConfig.algolia} />;
|
return <DocSearch {...siteConfig.themeConfig.algolia} />;
|
||||||
|
|
|
@ -54,14 +54,14 @@ function useDocsSearchVersionsHelpers() {
|
||||||
// State of the version select menus / algolia facet filters
|
// State of the version select menus / algolia facet filters
|
||||||
// docsPluginId -> versionName map
|
// docsPluginId -> versionName map
|
||||||
const [searchVersions, setSearchVersions] = useState<Record<string, string>>(
|
const [searchVersions, setSearchVersions] = useState<Record<string, string>>(
|
||||||
() => {
|
() =>
|
||||||
return Object.entries(allDocsData).reduce(
|
Object.entries(allDocsData).reduce(
|
||||||
(acc, [pluginId, pluginData]) => {
|
(acc, [pluginId, pluginData]) => ({
|
||||||
return {...acc, [pluginId]: pluginData.versions[0].name};
|
...acc,
|
||||||
},
|
[pluginId]: pluginData.versions[0].name,
|
||||||
|
}),
|
||||||
{},
|
{},
|
||||||
);
|
),
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Set the value of a single select menu
|
// Set the value of a single select menu
|
||||||
|
@ -81,11 +81,11 @@ function useDocsSearchVersionsHelpers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We want to display one select per versioned docs plugin instance
|
// We want to display one select per versioned docs plugin instance
|
||||||
const SearchVersionSelectList = ({
|
function SearchVersionSelectList({
|
||||||
docsSearchVersionsHelpers,
|
docsSearchVersionsHelpers,
|
||||||
}: {
|
}: {
|
||||||
docsSearchVersionsHelpers: ReturnType<typeof useDocsSearchVersionsHelpers>;
|
docsSearchVersionsHelpers: ReturnType<typeof useDocsSearchVersionsHelpers>;
|
||||||
}) => {
|
}) {
|
||||||
const versionedPluginEntries = Object.entries(
|
const versionedPluginEntries = Object.entries(
|
||||||
docsSearchVersionsHelpers.allDocsData,
|
docsSearchVersionsHelpers.allDocsData,
|
||||||
)
|
)
|
||||||
|
@ -126,7 +126,7 @@ const SearchVersionSelectList = ({
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
type ResultDispatcherState = {
|
type ResultDispatcherState = {
|
||||||
items: {
|
items: {
|
||||||
|
@ -225,12 +225,11 @@ function SearchPage(): JSX.Element {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sanitizeValue = (value: string) => {
|
const sanitizeValue = (value: string) =>
|
||||||
return value.replace(
|
value.replace(
|
||||||
/algolia-docsearch-suggestion--highlight/g,
|
/algolia-docsearch-suggestion--highlight/g,
|
||||||
'search-result-match',
|
'search-result-match',
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
const items = hits.map(
|
const items = hits.map(
|
||||||
({
|
({
|
||||||
|
@ -239,9 +238,9 @@ function SearchPage(): JSX.Element {
|
||||||
_snippetResult: snippet = {},
|
_snippetResult: snippet = {},
|
||||||
}) => {
|
}) => {
|
||||||
const parsedURL = new URL(url);
|
const parsedURL = new URL(url);
|
||||||
const titles = Object.keys(hierarchy).map((key) => {
|
const titles = Object.keys(hierarchy).map((key) =>
|
||||||
return sanitizeValue(hierarchy[key].value);
|
sanitizeValue(hierarchy[key].value),
|
||||||
});
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: titles.pop()!,
|
title: titles.pop()!,
|
||||||
|
|
|
@ -47,10 +47,9 @@ function useSearchQuery(): SearchQuery {
|
||||||
);
|
);
|
||||||
|
|
||||||
const generateSearchPageLink = useCallback(
|
const generateSearchPageLink = useCallback(
|
||||||
(targetSearchQuery: string) => {
|
(targetSearchQuery: string) =>
|
||||||
// Refer to https://github.com/facebook/docusaurus/pull/2838
|
// Refer to https://github.com/facebook/docusaurus/pull/2838
|
||||||
return `${baseUrl}search?q=${encodeURIComponent(targetSearchQuery)}`;
|
`${baseUrl}search?q=${encodeURIComponent(targetSearchQuery)}`,
|
||||||
},
|
|
||||||
[baseUrl],
|
[baseUrl],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -351,7 +351,9 @@ describe('mergeTranslations', () => {
|
||||||
|
|
||||||
describe('mapAsyncSequencial', () => {
|
describe('mapAsyncSequencial', () => {
|
||||||
function sleep(timeout: number): Promise<void> {
|
function sleep(timeout: number): Promise<void> {
|
||||||
return new Promise((resolve) => setTimeout(resolve, timeout));
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, timeout);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
test('map sequentially', async () => {
|
test('map sequentially', async () => {
|
||||||
|
@ -390,7 +392,9 @@ describe('mapAsyncSequencial', () => {
|
||||||
|
|
||||||
describe('findAsyncSequencial', () => {
|
describe('findAsyncSequencial', () => {
|
||||||
function sleep(timeout: number): Promise<void> {
|
function sleep(timeout: number): Promise<void> {
|
||||||
return new Promise((resolve) => setTimeout(resolve, timeout));
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, timeout);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
test('find sequentially', async () => {
|
test('find sequentially', async () => {
|
||||||
|
|
|
@ -10,7 +10,6 @@ import fs from 'fs-extra';
|
||||||
|
|
||||||
// Return an ordered list of locales we should try
|
// Return an ordered list of locales we should try
|
||||||
export function codeTranslationLocalesToTry(locale: string): string[] {
|
export function codeTranslationLocalesToTry(locale: string): string[] {
|
||||||
// @ts-expect-error: TODO until available in TS, see https://github.com/microsoft/TypeScript/issues/37326
|
|
||||||
const intlLocale = Intl.Locale ? new Intl.Locale(locale) : undefined;
|
const intlLocale = Intl.Locale ? new Intl.Locale(locale) : undefined;
|
||||||
if (!intlLocale) {
|
if (!intlLocale) {
|
||||||
return [locale];
|
return [locale];
|
||||||
|
@ -24,7 +23,7 @@ export function codeTranslationLocalesToTry(locale: string): string[] {
|
||||||
}
|
}
|
||||||
// if locale is like "pt-BR", we want to fallback to "pt"
|
// if locale is like "pt-BR", we want to fallback to "pt"
|
||||||
else {
|
else {
|
||||||
return [locale, intlLocale.language];
|
return [locale, intlLocale.language!];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -259,7 +259,7 @@ export function removePrefix(str: string, prefix: string): string {
|
||||||
return str.startsWith(prefix) ? str.slice(prefix.length) : str;
|
return str.startsWith(prefix) ? str.slice(prefix.length) : str;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getElementsAround<T extends unknown>(
|
export function getElementsAround<T>(
|
||||||
array: T[],
|
array: T[],
|
||||||
aroundIndex: number,
|
aroundIndex: number,
|
||||||
): {
|
): {
|
||||||
|
@ -306,7 +306,7 @@ export function getPluginI18nPath({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function mapAsyncSequencial<T extends unknown, R extends unknown>(
|
export async function mapAsyncSequencial<T, R>(
|
||||||
array: T[],
|
array: T[],
|
||||||
action: (t: T) => Promise<R>,
|
action: (t: T) => Promise<R>,
|
||||||
): Promise<R[]> {
|
): Promise<R[]> {
|
||||||
|
@ -389,9 +389,7 @@ export function reportMessage(
|
||||||
export function mergeTranslations(
|
export function mergeTranslations(
|
||||||
contents: TranslationFileContent[],
|
contents: TranslationFileContent[],
|
||||||
): TranslationFileContent {
|
): TranslationFileContent {
|
||||||
return contents.reduce((acc, content) => {
|
return contents.reduce((acc, content) => ({...acc, ...content}), {});
|
||||||
return {...acc, ...content};
|
|
||||||
}, {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSwizzledComponent(
|
export function getSwizzledComponent(
|
||||||
|
|
|
@ -16,11 +16,10 @@ const SPACE_FOR_APPENDING = 10;
|
||||||
const isMacOs = process.platform === `darwin`;
|
const isMacOs = process.platform === `darwin`;
|
||||||
const isWindows = process.platform === `win32`;
|
const isWindows = process.platform === `win32`;
|
||||||
|
|
||||||
export const isNameTooLong = (str: string): boolean => {
|
export const isNameTooLong = (str: string): boolean =>
|
||||||
return isMacOs || isWindows
|
isMacOs || isWindows
|
||||||
? str.length + SPACE_FOR_APPENDING > MAX_PATH_SEGMENT_CHARS // MacOS (APFS) and Windows (NTFS) filename length limit (255 chars)
|
? str.length + SPACE_FOR_APPENDING > MAX_PATH_SEGMENT_CHARS // MacOS (APFS) and Windows (NTFS) filename length limit (255 chars)
|
||||||
: Buffer.from(str).length + SPACE_FOR_APPENDING > MAX_PATH_SEGMENT_BYTES; // Other (255 bytes)
|
: Buffer.from(str).length + SPACE_FOR_APPENDING > MAX_PATH_SEGMENT_BYTES; // Other (255 bytes)
|
||||||
};
|
|
||||||
|
|
||||||
export const shortName = (str: string): string => {
|
export const shortName = (str: string): string => {
|
||||||
if (isMacOs || isWindows) {
|
if (isMacOs || isWindows) {
|
||||||
|
|
|
@ -91,7 +91,7 @@ export default async function choosePort(
|
||||||
(port) =>
|
(port) =>
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
if (port === defaultPort) {
|
if (port === defaultPort) {
|
||||||
return resolve(port);
|
resolve(port);
|
||||||
}
|
}
|
||||||
const message =
|
const message =
|
||||||
process.platform !== 'win32' && defaultPort < 1024 && !isRoot()
|
process.platform !== 'win32' && defaultPort < 1024 && !isRoot()
|
||||||
|
@ -121,7 +121,6 @@ export default async function choosePort(
|
||||||
console.log(chalk.red(message));
|
console.log(chalk.red(message));
|
||||||
resolve(null);
|
resolve(null);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}),
|
}),
|
||||||
(err) => {
|
(err) => {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
|
@ -22,9 +22,7 @@ export const createStatefulLinksCollector = (): StatefulLinksCollector => {
|
||||||
collectLink: (link: string): void => {
|
collectLink: (link: string): void => {
|
||||||
allLinks.add(link);
|
allLinks.add(link);
|
||||||
},
|
},
|
||||||
getCollectedLinks: (): string[] => {
|
getCollectedLinks: (): string[] => [...allLinks],
|
||||||
return [...allLinks];
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,16 +33,14 @@ const Context = createContext<LinksCollector>({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useLinksCollector = (): LinksCollector => {
|
export const useLinksCollector = (): LinksCollector => useContext(Context);
|
||||||
return useContext(Context);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ProvideLinksCollector = ({
|
export function ProvideLinksCollector({
|
||||||
children,
|
children,
|
||||||
linksCollector,
|
linksCollector,
|
||||||
}: {
|
}: {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
linksCollector: LinksCollector;
|
linksCollector: LinksCollector;
|
||||||
}): JSX.Element => {
|
}): JSX.Element {
|
||||||
return <Context.Provider value={linksCollector}>{children}</Context.Provider>;
|
return <Context.Provider value={linksCollector}>{children}</Context.Provider>;
|
||||||
};
|
}
|
||||||
|
|
|
@ -49,19 +49,18 @@ const flatten = <T>(arrays: T[][]): T[] =>
|
||||||
// output: /blog/2018/12/14/Happy-First-Birthday-Slash
|
// output: /blog/2018/12/14/Happy-First-Birthday-Slash
|
||||||
const removeRouteNameHash = (str: string) => str.replace(/(-[^-]+)$/, '');
|
const removeRouteNameHash = (str: string) => str.replace(/(-[^-]+)$/, '');
|
||||||
|
|
||||||
const getChunkNamesToLoad = (path: string): string[] => {
|
const getChunkNamesToLoad = (path: string): string[] =>
|
||||||
return flatten(
|
flatten(
|
||||||
Object.entries(routesChunkNames)
|
Object.entries(routesChunkNames)
|
||||||
.filter(
|
.filter(
|
||||||
([routeNameWithHash]) =>
|
([routeNameWithHash]) =>
|
||||||
removeRouteNameHash(routeNameWithHash) === path,
|
removeRouteNameHash(routeNameWithHash) === path,
|
||||||
)
|
)
|
||||||
.map(([, routeChunks]) => {
|
.map(([, routeChunks]) =>
|
||||||
// flat() is useful for nested chunk names, it's not like array.flat()
|
// flat() is useful for nested chunk names, it's not like array.flat()
|
||||||
return Object.values(flat(routeChunks));
|
Object.values(flat(routeChunks)),
|
||||||
}),
|
),
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
const docusaurus = {
|
const docusaurus = {
|
||||||
prefetch: (routePath: string): boolean => {
|
prefetch: (routePath: string): boolean => {
|
||||||
|
@ -82,6 +81,7 @@ const docusaurus = {
|
||||||
chunkNamesNeeded.forEach((chunkName) => {
|
chunkNamesNeeded.forEach((chunkName) => {
|
||||||
// "__webpack_require__.gca" is a custom function provided by ChunkAssetPlugin.
|
// "__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.
|
// 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);
|
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.
|
||||||
|
|
|
@ -68,23 +68,24 @@ export function interpolate<Str extends string, Value extends ReactNode>(
|
||||||
else if (elements.every((el) => typeof el === 'string')) {
|
else if (elements.every((el) => typeof el === 'string')) {
|
||||||
return processedText
|
return processedText
|
||||||
.split(ValueFoundMarker)
|
.split(ValueFoundMarker)
|
||||||
.reduce<string>((str, value, index) => {
|
.reduce<string>(
|
||||||
return str.concat(value).concat((elements[index] as string) ?? '');
|
(str, value, index) =>
|
||||||
}, '');
|
str.concat(value).concat((elements[index] as string) ?? ''),
|
||||||
|
'',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// JSX interpolation: returns ReactNode
|
// JSX interpolation: returns ReactNode
|
||||||
else {
|
else {
|
||||||
return processedText
|
return processedText.split(ValueFoundMarker).reduce<ReactNode[]>(
|
||||||
.split(ValueFoundMarker)
|
(array, value, index) => [
|
||||||
.reduce<ReactNode[]>((array, value, index) => {
|
|
||||||
return [
|
|
||||||
...array,
|
...array,
|
||||||
<React.Fragment key={index}>
|
<React.Fragment key={index}>
|
||||||
{value}
|
{value}
|
||||||
{elements[index]}
|
{elements[index]}
|
||||||
</React.Fragment>,
|
</React.Fragment>,
|
||||||
];
|
],
|
||||||
}, []);
|
[],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,9 +45,7 @@ export function useBaseUrlUtils(): BaseUrlUtils {
|
||||||
const {siteConfig: {baseUrl = '/', url: siteUrl} = {}} =
|
const {siteConfig: {baseUrl = '/', url: siteUrl} = {}} =
|
||||||
useDocusaurusContext();
|
useDocusaurusContext();
|
||||||
return {
|
return {
|
||||||
withBaseUrl: (url, options) => {
|
withBaseUrl: (url, options) => addBaseUrl(siteUrl, baseUrl, url, options),
|
||||||
return addBaseUrl(siteUrl, baseUrl, url, options);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,11 +28,11 @@ import chalk from 'chalk';
|
||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import {memoize} from 'lodash';
|
import {memoize} from 'lodash';
|
||||||
|
|
||||||
const getCompiledSSRTemplate = memoize((template) => {
|
const getCompiledSSRTemplate = memoize((template) =>
|
||||||
return eta.compile(template.trim(), {
|
eta.compile(template.trim(), {
|
||||||
rmWhitespace: true,
|
rmWhitespace: true,
|
||||||
});
|
}),
|
||||||
});
|
);
|
||||||
|
|
||||||
function renderSSRTemplate(ssrTemplate, data) {
|
function renderSSRTemplate(ssrTemplate, data) {
|
||||||
const compiled = getCompiledSSRTemplate(ssrTemplate);
|
const compiled = getCompiledSSRTemplate(ssrTemplate);
|
||||||
|
|
|
@ -20,7 +20,7 @@ function Layout(props) {
|
||||||
<>
|
<>
|
||||||
<Head defaultTitle={`${defaultTitle}${tagline ? ` · ${tagline}` : ''}`}>
|
<Head defaultTitle={`${defaultTitle}${tagline ? ` · ${tagline}` : ''}`}>
|
||||||
{title && <title>{`${title} · ${tagline}`}</title>}
|
{title && <title>{`${title} · ${tagline}`}</title>}
|
||||||
{favicon && <link rel="shortcut icon" href={faviconUrl} />}
|
{favicon && <link rel="icon" href={faviconUrl} />}
|
||||||
{description && <meta name="description" content={description} />}
|
{description && <meta name="description" content={description} />}
|
||||||
{description && (
|
{description && (
|
||||||
<meta property="og:description" content={description} />
|
<meta property="og:description" content={description} />
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export default ({error, retry, pastDelay}) => {
|
export default function Loading({error, retry, pastDelay}) {
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -133,4 +133,4 @@ export default ({error, retry, pastDelay}) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
}
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
// Wrapper at the very top of the app, that is applied constantly
|
// Wrapper at the very top of the app, that is applied constantly
|
||||||
// and does not depend on current route (unlike the layout)
|
// and does not depend on current route (unlike the layout)
|
||||||
//
|
//
|
||||||
|
@ -15,7 +13,7 @@ import React from 'react';
|
||||||
//
|
//
|
||||||
// See https://github.com/facebook/docusaurus/issues/3919
|
// See https://github.com/facebook/docusaurus/issues/3919
|
||||||
function Root({children}) {
|
function Root({children}) {
|
||||||
return <>{children}</>;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Root;
|
export default Root;
|
||||||
|
|
|
@ -75,9 +75,9 @@ export function getAllBrokenLinks({
|
||||||
}): Record<string, BrokenLink[]> {
|
}): Record<string, BrokenLink[]> {
|
||||||
const filteredRoutes = filterIntermediateRoutes(routes);
|
const filteredRoutes = filterIntermediateRoutes(routes);
|
||||||
|
|
||||||
const allBrokenLinks = mapValues(allCollectedLinks, (pageLinks, pagePath) => {
|
const allBrokenLinks = mapValues(allCollectedLinks, (pageLinks, pagePath) =>
|
||||||
return getPageBrokenLinks({pageLinks, pagePath, routes: filteredRoutes});
|
getPageBrokenLinks({pageLinks, pagePath, routes: filteredRoutes}),
|
||||||
});
|
);
|
||||||
|
|
||||||
// remove pages without any broken link
|
// remove pages without any broken link
|
||||||
return pickBy(allBrokenLinks, (brokenLinks) => brokenLinks.length > 0);
|
return pickBy(allBrokenLinks, (brokenLinks) => brokenLinks.length > 0);
|
||||||
|
@ -186,9 +186,9 @@ export async function filterExistingFileLinks({
|
||||||
return filePathsToTry.some(isExistingFile);
|
return filePathsToTry.some(isExistingFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapValues(allCollectedLinks, (links) => {
|
return mapValues(allCollectedLinks, (links) =>
|
||||||
return links.filter((link) => !linkFileExists(link));
|
links.filter((link) => !linkFileExists(link)),
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function handleBrokenLinks({
|
export async function handleBrokenLinks({
|
||||||
|
|
|
@ -108,7 +108,7 @@ const I18N_CONFIG_SCHEMA = Joi.object<I18nConfig>({
|
||||||
.optional()
|
.optional()
|
||||||
.default(DEFAULT_I18N_CONFIG);
|
.default(DEFAULT_I18N_CONFIG);
|
||||||
|
|
||||||
const SiteUrlSchema = URISchema.required().custom(function (value, helpers) {
|
const SiteUrlSchema = URISchema.required().custom((value, helpers) => {
|
||||||
try {
|
try {
|
||||||
const {pathname} = new URL(value);
|
const {pathname} = new URL(value);
|
||||||
if (pathname !== '/') {
|
if (pathname !== '/') {
|
||||||
|
@ -124,7 +124,7 @@ const SiteUrlSchema = URISchema.required().custom(function (value, helpers) {
|
||||||
export const ConfigSchema = Joi.object({
|
export const ConfigSchema = Joi.object({
|
||||||
baseUrl: Joi.string()
|
baseUrl: Joi.string()
|
||||||
.required()
|
.required()
|
||||||
.regex(new RegExp('/$', 'm'))
|
.regex(/\/$/m)
|
||||||
.message('{{#label}} must be a string with a trailing slash.'),
|
.message('{{#label}} must be a string with a trailing slash.'),
|
||||||
baseUrlIssueBanner: Joi.boolean().default(DEFAULT_CONFIG.baseUrlIssueBanner),
|
baseUrlIssueBanner: Joi.boolean().default(DEFAULT_CONFIG.baseUrlIssueBanner),
|
||||||
favicon: Joi.string().optional(),
|
favicon: Joi.string().optional(),
|
||||||
|
|
|
@ -14,9 +14,7 @@ import chalk from 'chalk';
|
||||||
function getDefaultLocaleLabel(locale: string) {
|
function getDefaultLocaleLabel(locale: string) {
|
||||||
// Intl.DisplayNames is ES2021 - Node14+
|
// Intl.DisplayNames is ES2021 - Node14+
|
||||||
// https://v8.dev/features/intl-displaynames
|
// https://v8.dev/features/intl-displaynames
|
||||||
// @ts-expect-error: wait for TS support of ES2021 feature
|
|
||||||
if (typeof Intl.DisplayNames !== 'undefined') {
|
if (typeof Intl.DisplayNames !== 'undefined') {
|
||||||
// @ts-expect-error: wait for TS support of ES2021 feature
|
|
||||||
return new Intl.DisplayNames([locale], {type: 'language'}).of(locale);
|
return new Intl.DisplayNames([locale], {type: 'language'}).of(locale);
|
||||||
}
|
}
|
||||||
return locale;
|
return locale;
|
||||||
|
@ -76,9 +74,10 @@ Note: Docusaurus only support running one locale at a time.`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const localeConfigs = locales.reduce((acc, locale) => {
|
const localeConfigs = locales.reduce(
|
||||||
return {...acc, [locale]: getLocaleConfig(locale)};
|
(acc, locale) => ({...acc, [locale]: getLocaleConfig(locale)}),
|
||||||
}, {});
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
defaultLocale: i18nConfig.defaultLocale,
|
defaultLocale: i18nConfig.defaultLocale,
|
||||||
|
|
|
@ -124,12 +124,12 @@ export async function loadPlugins({
|
||||||
|
|
||||||
const allContent: AllContent = chain(loadedPlugins)
|
const allContent: AllContent = chain(loadedPlugins)
|
||||||
.groupBy((item) => item.name)
|
.groupBy((item) => item.name)
|
||||||
.mapValues((nameItems) => {
|
.mapValues((nameItems) =>
|
||||||
return chain(nameItems)
|
chain(nameItems)
|
||||||
.groupBy((item) => item.options.id ?? DEFAULT_PLUGIN_ID)
|
.groupBy((item) => item.options.id ?? DEFAULT_PLUGIN_ID)
|
||||||
.mapValues((idItems) => idItems[0].content)
|
.mapValues((idItems) => idItems[0].content)
|
||||||
.value();
|
.value(),
|
||||||
})
|
)
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
// 3. Plugin Lifecycle - contentLoaded.
|
// 3. Plugin Lifecycle - contentLoaded.
|
||||||
|
|
|
@ -19,9 +19,9 @@ export function sortAliases(aliases: ThemeAliases): ThemeAliases {
|
||||||
// Alphabetical order by default
|
// Alphabetical order by default
|
||||||
const entries = sortBy(Object.entries(aliases), ([alias]) => alias);
|
const entries = sortBy(Object.entries(aliases), ([alias]) => alias);
|
||||||
// @theme/NavbarItem should be after @theme/NavbarItem/LocaleDropdown
|
// @theme/NavbarItem should be after @theme/NavbarItem/LocaleDropdown
|
||||||
entries.sort(([alias1], [alias2]) => {
|
entries.sort(([alias1], [alias2]) =>
|
||||||
return alias1.includes(`${alias2}/`) ? -1 : 0;
|
alias1.includes(`${alias2}/`) ? -1 : 0,
|
||||||
});
|
);
|
||||||
return Object.fromEntries(entries);
|
return Object.fromEntries(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -271,9 +271,10 @@ export async function getPluginsDefaultCodeTranslationMessages(
|
||||||
plugins.map((plugin) => plugin.getDefaultCodeTranslationMessages?.() ?? {}),
|
plugins.map((plugin) => plugin.getDefaultCodeTranslationMessages?.() ?? {}),
|
||||||
);
|
);
|
||||||
|
|
||||||
return pluginsMessages.reduce((allMessages, pluginMessages) => {
|
return pluginsMessages.reduce(
|
||||||
return {...allMessages, ...pluginMessages};
|
(allMessages, pluginMessages) => ({...allMessages, ...pluginMessages}),
|
||||||
}, {});
|
{},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyDefaultCodeTranslations({
|
export function applyDefaultCodeTranslations({
|
||||||
|
@ -298,11 +299,9 @@ Please report this Docusaurus issue.
|
||||||
|
|
||||||
return mapValues(
|
return mapValues(
|
||||||
extractedCodeTranslations,
|
extractedCodeTranslations,
|
||||||
(messageTranslation, messageId) => {
|
(messageTranslation, messageId) => ({
|
||||||
return {
|
|
||||||
...messageTranslation,
|
...messageTranslation,
|
||||||
message: defaultCodeMessages[messageId] ?? messageTranslation.message,
|
message: defaultCodeMessages[messageId] ?? messageTranslation.message,
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,9 +85,10 @@ export async function extractSiteSourceCodeTranslations(
|
||||||
function toTranslationFileContent(
|
function toTranslationFileContent(
|
||||||
sourceCodeFileTranslations: SourceCodeFileTranslations[],
|
sourceCodeFileTranslations: SourceCodeFileTranslations[],
|
||||||
): TranslationFileContent {
|
): TranslationFileContent {
|
||||||
return sourceCodeFileTranslations.reduce((acc, item) => {
|
return sourceCodeFileTranslations.reduce(
|
||||||
return {...acc, ...item.translations};
|
(acc, item) => ({...acc, ...item.translations}),
|
||||||
}, {});
|
{},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sourceCodeFilePaths = await getSourceCodeFilePaths(siteDir, plugins);
|
const sourceCodeFilePaths = await getSourceCodeFilePaths(siteDir, plugins);
|
||||||
|
|
|
@ -250,35 +250,38 @@ describe('extending PostCSS', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run multiple times: ensure last run does not override previous runs
|
// Run multiple times: ensure last run does not override previous runs
|
||||||
webpackConfig = applyConfigurePostCss((postCssOptions) => {
|
webpackConfig = applyConfigurePostCss(
|
||||||
return {
|
(postCssOptions) => ({
|
||||||
...postCssOptions,
|
...postCssOptions,
|
||||||
plugins: [
|
plugins: [
|
||||||
...postCssOptions.plugins,
|
...postCssOptions.plugins,
|
||||||
createFakePlugin('postcss-plugin-1'),
|
createFakePlugin('postcss-plugin-1'),
|
||||||
],
|
],
|
||||||
};
|
}),
|
||||||
}, webpackConfig);
|
webpackConfig,
|
||||||
|
);
|
||||||
|
|
||||||
webpackConfig = applyConfigurePostCss((postCssOptions) => {
|
webpackConfig = applyConfigurePostCss(
|
||||||
return {
|
(postCssOptions) => ({
|
||||||
...postCssOptions,
|
...postCssOptions,
|
||||||
plugins: [
|
plugins: [
|
||||||
createFakePlugin('postcss-plugin-2'),
|
createFakePlugin('postcss-plugin-2'),
|
||||||
...postCssOptions.plugins,
|
...postCssOptions.plugins,
|
||||||
],
|
],
|
||||||
};
|
}),
|
||||||
}, webpackConfig);
|
webpackConfig,
|
||||||
|
);
|
||||||
|
|
||||||
webpackConfig = applyConfigurePostCss((postCssOptions) => {
|
webpackConfig = applyConfigurePostCss(
|
||||||
return {
|
(postCssOptions) => ({
|
||||||
...postCssOptions,
|
...postCssOptions,
|
||||||
plugins: [
|
plugins: [
|
||||||
...postCssOptions.plugins,
|
...postCssOptions.plugins,
|
||||||
createFakePlugin('postcss-plugin-3'),
|
createFakePlugin('postcss-plugin-3'),
|
||||||
],
|
],
|
||||||
};
|
}),
|
||||||
}, webpackConfig);
|
webpackConfig,
|
||||||
|
);
|
||||||
|
|
||||||
// @ts-expect-error: relax type
|
// @ts-expect-error: relax type
|
||||||
const postCssLoader1 = webpackConfig.module?.rules[0].use[2];
|
const postCssLoader1 = webpackConfig.module?.rules[0].use[2];
|
||||||
|
|
|
@ -176,18 +176,16 @@ class CleanWebpackPlugin {
|
||||||
all: false,
|
all: false,
|
||||||
assets: true,
|
assets: true,
|
||||||
}).assets || [];
|
}).assets || [];
|
||||||
const assets = statsAssets.map((asset: {name: string}) => {
|
const assets = statsAssets.map((asset: {name: string}) => asset.name);
|
||||||
return asset.name;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all files that were in the previous build but not the current
|
* Get all files that were in the previous build but not the current
|
||||||
*
|
*
|
||||||
* (relies on del's cwd: outputPath option)
|
* (relies on del's cwd: outputPath option)
|
||||||
*/
|
*/
|
||||||
const staleFiles = this.currentAssets.filter((previousAsset) => {
|
const staleFiles = this.currentAssets.filter(
|
||||||
return assets.includes(previousAsset) === false;
|
(previousAsset) => assets.includes(previousAsset) === false,
|
||||||
});
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save assets for next compilation
|
* Save assets for next compilation
|
||||||
|
|
|
@ -164,7 +164,7 @@ export const getCustomizableJSLoader =
|
||||||
: jsLoader(isServer);
|
: jsLoader(isServer);
|
||||||
|
|
||||||
// TODO remove this before end of 2021?
|
// TODO remove this before end of 2021?
|
||||||
const warnBabelLoaderOnce = memoize(function () {
|
const warnBabelLoaderOnce = memoize(() => {
|
||||||
console.warn(
|
console.warn(
|
||||||
chalk.yellow(
|
chalk.yellow(
|
||||||
'Docusaurus plans to support multiple JS loader strategies (Babel, esbuild...): "getBabelLoader(isServer)" is now deprecated in favor of "getJSLoader({isServer})".',
|
'Docusaurus plans to support multiple JS loader strategies (Babel, esbuild...): "getBabelLoader(isServer)" is now deprecated in favor of "getJSLoader({isServer})".',
|
||||||
|
@ -180,7 +180,7 @@ const getBabelLoaderDeprecated = function getBabelLoaderDeprecated(
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO remove this before end of 2021 ?
|
// TODO remove this before end of 2021 ?
|
||||||
const warnCacheLoaderOnce = memoize(function () {
|
const warnCacheLoaderOnce = memoize(() => {
|
||||||
console.warn(
|
console.warn(
|
||||||
chalk.yellow(
|
chalk.yellow(
|
||||||
'Docusaurus uses Webpack 5 and getCacheLoader() usage is now deprecated.',
|
'Docusaurus uses Webpack 5 and getCacheLoader() usage is now deprecated.',
|
||||||
|
@ -335,24 +335,20 @@ export function getFileLoaderUtils(): FileLoaderUtils {
|
||||||
`${OUTPUT_STATIC_ASSETS_DIR_NAME}/${folder}/[name]-[hash].[ext]`;
|
`${OUTPUT_STATIC_ASSETS_DIR_NAME}/${folder}/[name]-[hash].[ext]`;
|
||||||
|
|
||||||
const loaders: FileLoaderUtils['loaders'] = {
|
const loaders: FileLoaderUtils['loaders'] = {
|
||||||
file: (options: {folder: AssetFolder}) => {
|
file: (options: {folder: AssetFolder}) => ({
|
||||||
return {
|
|
||||||
loader: require.resolve(`file-loader`),
|
loader: require.resolve(`file-loader`),
|
||||||
options: {
|
options: {
|
||||||
name: fileLoaderFileName(options.folder),
|
name: fileLoaderFileName(options.folder),
|
||||||
},
|
},
|
||||||
};
|
}),
|
||||||
},
|
url: (options: {folder: AssetFolder}) => ({
|
||||||
url: (options: {folder: AssetFolder}) => {
|
|
||||||
return {
|
|
||||||
loader: require.resolve(`url-loader`),
|
loader: require.resolve(`url-loader`),
|
||||||
options: {
|
options: {
|
||||||
limit: urlLoaderLimit,
|
limit: urlLoaderLimit,
|
||||||
name: fileLoaderFileName(options.folder),
|
name: fileLoaderFileName(options.folder),
|
||||||
fallback: require.resolve(`file-loader`),
|
fallback: require.resolve(`file-loader`),
|
||||||
},
|
},
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
|
|
||||||
// TODO find a better solution to avoid conflicts with the ideal-image plugin
|
// TODO find a better solution to avoid conflicts with the ideal-image plugin
|
||||||
// TODO this may require a little breaking change for ideal-image users?
|
// TODO this may require a little breaking change for ideal-image users?
|
||||||
|
@ -372,33 +368,26 @@ export function getFileLoaderUtils(): FileLoaderUtils {
|
||||||
* Loads image assets, inlines images via a data URI if they are below
|
* Loads image assets, inlines images via a data URI if they are below
|
||||||
* the size threshold
|
* the size threshold
|
||||||
*/
|
*/
|
||||||
images: () => {
|
images: () => ({
|
||||||
return {
|
|
||||||
use: [loaders.url({folder: 'images'})],
|
use: [loaders.url({folder: 'images'})],
|
||||||
test: /\.(ico|jpg|jpeg|png|gif|webp)(\?.*)?$/,
|
test: /\.(ico|jpg|jpeg|png|gif|webp)(\?.*)?$/,
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
|
|
||||||
fonts: () => {
|
fonts: () => ({
|
||||||
return {
|
|
||||||
use: [loaders.url({folder: 'fonts'})],
|
use: [loaders.url({folder: 'fonts'})],
|
||||||
test: /\.(woff|woff2|eot|ttf|otf)$/,
|
test: /\.(woff|woff2|eot|ttf|otf)$/,
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads audio and video and inlines them via a data URI if they are below
|
* Loads audio and video and inlines them via a data URI if they are below
|
||||||
* the size threshold
|
* the size threshold
|
||||||
*/
|
*/
|
||||||
media: () => {
|
media: () => ({
|
||||||
return {
|
|
||||||
use: [loaders.url({folder: 'medias'})],
|
use: [loaders.url({folder: 'medias'})],
|
||||||
test: /\.(mp4|webm|ogv|wav|mp3|m4a|aac|oga|flac)$/,
|
test: /\.(mp4|webm|ogv|wav|mp3|m4a|aac|oga|flac)$/,
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
|
|
||||||
svg: () => {
|
svg: () => ({
|
||||||
return {
|
|
||||||
test: /\.svg?$/,
|
test: /\.svg?$/,
|
||||||
oneOf: [
|
oneOf: [
|
||||||
{
|
{
|
||||||
|
@ -426,15 +415,12 @@ export function getFileLoaderUtils(): FileLoaderUtils {
|
||||||
use: [loaders.url({folder: 'images'})],
|
use: [loaders.url({folder: 'images'})],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
|
|
||||||
otherAssets: () => {
|
otherAssets: () => ({
|
||||||
return {
|
|
||||||
use: [loaders.file({folder: 'files'})],
|
use: [loaders.file({folder: 'files'})],
|
||||||
test: /\.(pdf|doc|docx|xls|xlsx|zip|rar)$/,
|
test: /\.(pdf|doc|docx|xls|xlsx|zip|rar)$/,
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {loaders, rules};
|
return {loaders, rules};
|
||||||
|
|
|
@ -12,9 +12,8 @@ import type {Palette} from 'node-vibrant/lib/color';
|
||||||
* it returns a Base64 image string with required formatting
|
* it returns a Base64 image string with required formatting
|
||||||
* to work on the web (<img src=".." /> or in CSS url('..'))
|
* to work on the web (<img src=".." /> or in CSS url('..'))
|
||||||
*/
|
*/
|
||||||
const toBase64 = (extMimeType: string, data: Buffer): string => {
|
const toBase64 = (extMimeType: string, data: Buffer): string =>
|
||||||
return `data:${extMimeType};base64,${data.toString('base64')}`;
|
`data:${extMimeType};base64,${data.toString('base64')}`;
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* takes a color swatch object, converts it to an array & returns
|
* takes a color swatch object, converts it to an array & returns
|
||||||
|
|
|
@ -12,8 +12,9 @@ const messages = stylelint.utils.ruleMessages(ruleName, {
|
||||||
rejected: 'Missing copyright in the header comment',
|
rejected: 'Missing copyright in the header comment',
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = stylelint.createPlugin(ruleName, (actual) => {
|
module.exports = stylelint.createPlugin(
|
||||||
return (root, result) => {
|
ruleName,
|
||||||
|
(actual) => (root, result) => {
|
||||||
const validOptions = stylelint.utils.validateOptions(result, ruleName, {
|
const validOptions = stylelint.utils.validateOptions(result, ruleName, {
|
||||||
actual,
|
actual,
|
||||||
});
|
});
|
||||||
|
@ -48,8 +49,8 @@ module.exports = stylelint.createPlugin(ruleName, (actual) => {
|
||||||
ruleName,
|
ruleName,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
});
|
);
|
||||||
|
|
||||||
module.exports.ruleName = ruleName;
|
module.exports.ruleName = ruleName;
|
||||||
module.exports.messages = messages;
|
module.exports.messages = messages;
|
||||||
|
|
|
@ -14,7 +14,9 @@ Used in conjunction with waitForCrowdin.js (which is not enough)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async function delay(ms) {
|
async function delay(ms) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, ms);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
|
|
|
@ -43,8 +43,7 @@ function ShowcaseCardTagIcons({tags}: {tags: TagType[]}) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ShowcaseCard = memo(function ({user}: {user: User}) {
|
const ShowcaseCard = memo(({user}: {user: User}) => (
|
||||||
return (
|
|
||||||
<div key={user.title} className="col col--4 margin-bottom--lg">
|
<div key={user.title} className="col col--4 margin-bottom--lg">
|
||||||
<div className={clsx('card', styles.showcaseCard)}>
|
<div className={clsx('card', styles.showcaseCard)}>
|
||||||
<div className={clsx('card__image', styles.showcaseCardImage)}>
|
<div className={clsx('card__image', styles.showcaseCardImage)}>
|
||||||
|
@ -91,7 +90,6 @@ const ShowcaseCard = memo(function ({user}: {user: User}) {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
));
|
||||||
});
|
|
||||||
|
|
||||||
export default ShowcaseCard;
|
export default ShowcaseCard;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue