mirror of
https://github.com/facebook/docusaurus.git
synced 2025-04-30 02:37:59 +02:00
refactor: remove a lot of implicit anys (#7468)
This commit is contained in:
parent
0c8e57de67
commit
3666a2ede5
23 changed files with 148 additions and 163 deletions
|
@ -1,4 +1,5 @@
|
||||||
__fixtures__
|
__fixtures__
|
||||||
|
__mocks__
|
||||||
dist
|
dist
|
||||||
node_modules
|
node_modules
|
||||||
.yarn
|
.yarn
|
||||||
|
|
|
@ -5,19 +5,27 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import shell from 'shelljs';
|
import shell from 'shelljs';
|
||||||
|
|
||||||
const NODE_MAJOR_VERSION = parseInt(process.versions.node.split('.')[0], 10);
|
const NODE_MAJOR_VERSION = parseInt(
|
||||||
|
/** @type {string} */ (process.versions.node.split('.')[0]),
|
||||||
|
10,
|
||||||
|
);
|
||||||
if (NODE_MAJOR_VERSION < 16) {
|
if (NODE_MAJOR_VERSION < 16) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'This generateExamples Docusaurus script requires at least Node.js 16 and npm 7. See why here: https://github.com/facebook/docusaurus/pull/5722#issuecomment-948847891',
|
'This generateExamples Docusaurus script requires at least Node.js 16 and npm 7. See why here: https://github.com/facebook/docusaurus/pull/5722#issuecomment-948847891',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate one example per init template
|
/**
|
||||||
// We use those generated examples as CodeSandbox projects
|
* Generate one example per init template
|
||||||
// See https://github.com/facebook/docusaurus/issues/1699
|
* We use those generated examples as CodeSandbox projects
|
||||||
|
* See https://github.com/facebook/docusaurus/issues/1699
|
||||||
|
* @param {string} template
|
||||||
|
*/
|
||||||
async function generateTemplateExample(template) {
|
async function generateTemplateExample(template) {
|
||||||
try {
|
try {
|
||||||
console.log(
|
console.log(
|
||||||
|
@ -103,6 +111,12 @@ async function generateTemplateExample(template) {
|
||||||
* Button visible here: https://jamstack.org/generators/
|
* Button visible here: https://jamstack.org/generators/
|
||||||
*/
|
*/
|
||||||
function updateStarters() {
|
function updateStarters() {
|
||||||
|
/**
|
||||||
|
* @param {Object} param0
|
||||||
|
* @param {string} param0.subfolder
|
||||||
|
* @param {string} param0.remote
|
||||||
|
* @param {string} param0.remoteBranch
|
||||||
|
*/
|
||||||
function forcePushGitSubtree({subfolder, remote, remoteBranch}) {
|
function forcePushGitSubtree({subfolder, remote, remoteBranch}) {
|
||||||
console.log('');
|
console.log('');
|
||||||
// See https://stackoverflow.com/questions/33172857/how-do-i-force-a-subtree-push-to-overwrite-remote-changes
|
// See https://stackoverflow.com/questions/33172857/how-do-i-force-a-subtree-push-to-overwrite-remote-changes
|
||||||
|
|
|
@ -45,24 +45,12 @@ program
|
||||||
\`custom\`: enter your custom git clone command. We will prompt you for it.`,
|
\`custom\`: enter your custom git clone command. We will prompt you for it.`,
|
||||||
)
|
)
|
||||||
.description('Initialize website.')
|
.description('Initialize website.')
|
||||||
.action(
|
.action((siteName, template, rootDir, options) => {
|
||||||
(
|
|
||||||
siteName,
|
|
||||||
template,
|
|
||||||
rootDir = '.',
|
|
||||||
{packageManager, skipInstall, typescript, gitStrategy} = {},
|
|
||||||
) => {
|
|
||||||
// See https://github.com/facebook/docusaurus/pull/6860
|
// See https://github.com/facebook/docusaurus/pull/6860
|
||||||
import('../lib/index.js').then(({default: init}) => {
|
import('../lib/index.js').then(({default: init}) => {
|
||||||
init(path.resolve(rootDir), siteName, template, {
|
init(path.resolve(rootDir ?? '.'), siteName, template, options);
|
||||||
packageManager,
|
|
||||||
skipInstall,
|
|
||||||
typescript,
|
|
||||||
gitStrategy,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
program.parse(process.argv);
|
program.parse(process.argv);
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import toString from 'mdast-util-to-string';
|
||||||
import visit from 'unist-util-visit';
|
import visit from 'unist-util-visit';
|
||||||
import slug from '../index';
|
import slug from '../index';
|
||||||
import type {Plugin} from 'unified';
|
import type {Plugin} from 'unified';
|
||||||
|
import type {Parent} from 'unist';
|
||||||
|
|
||||||
function process(doc: string, plugins: Plugin[] = []) {
|
function process(doc: string, plugins: Plugin[] = []) {
|
||||||
const processor = remark().use({plugins: [...plugins, slug]});
|
const processor = remark().use({plugins: [...plugins, slug]});
|
||||||
|
@ -58,11 +59,8 @@ describe('headings remark plugin', () => {
|
||||||
|
|
||||||
it('does not overwrite `data` on headings', () => {
|
it('does not overwrite `data` on headings', () => {
|
||||||
const result = process('# Normal\n', [
|
const result = process('# Normal\n', [
|
||||||
() => {
|
() => (root) => {
|
||||||
function transform(tree) {
|
(root as Parent).children[0]!.data = {foo: 'bar'};
|
||||||
tree.children[0].data = {foo: 'bar'};
|
|
||||||
}
|
|
||||||
return transform;
|
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const expected = u('root', [
|
const expected = u('root', [
|
||||||
|
@ -81,11 +79,10 @@ describe('headings remark plugin', () => {
|
||||||
|
|
||||||
it('does not overwrite `data.hProperties` on headings', () => {
|
it('does not overwrite `data.hProperties` on headings', () => {
|
||||||
const result = process('# Normal\n', [
|
const result = process('# Normal\n', [
|
||||||
() => {
|
() => (root) => {
|
||||||
function transform(tree) {
|
(root as Parent).children[0]!.data = {
|
||||||
tree.children[0].data = {hProperties: {className: ['foo']}};
|
hProperties: {className: ['foo']},
|
||||||
}
|
};
|
||||||
return transform;
|
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const expected = u('root', [
|
const expected = u('root', [
|
||||||
|
@ -111,12 +108,9 @@ describe('headings remark plugin', () => {
|
||||||
'## Something also',
|
'## Something also',
|
||||||
].join('\n\n'),
|
].join('\n\n'),
|
||||||
[
|
[
|
||||||
() => {
|
() => (root) => {
|
||||||
function transform(tree) {
|
(root as Parent).children[1]!.data = {hProperties: {id: 'here'}};
|
||||||
tree.children[1].data = {hProperties: {id: 'here'}};
|
(root as Parent).children[3]!.data = {hProperties: {id: 'something'}};
|
||||||
tree.children[3].data = {hProperties: {id: 'something'}};
|
|
||||||
}
|
|
||||||
return transform;
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -270,9 +264,9 @@ describe('headings remark plugin', () => {
|
||||||
# {#text-after} custom ID
|
# {#text-after} custom ID
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const headers = [];
|
const headers: {text: string; id: string}[] = [];
|
||||||
visit(result, 'heading', (node) => {
|
visit(result, 'heading', (node) => {
|
||||||
headers.push({text: toString(node), id: node.data.id});
|
headers.push({text: toString(node), id: node.data!.id as string});
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(headers).toEqual([
|
expect(headers).toEqual([
|
||||||
|
|
|
@ -75,50 +75,6 @@ exports[`toc remark plugin exports even with existing name 1`] = `
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`toc remark plugin exports with custom name 1`] = `
|
|
||||||
"export const customName = [
|
|
||||||
{
|
|
||||||
value: 'Endi',
|
|
||||||
id: 'endi',
|
|
||||||
level: 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Endi',
|
|
||||||
id: 'endi-1',
|
|
||||||
level: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Yangshun',
|
|
||||||
id: 'yangshun',
|
|
||||||
level: 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'I ♥ unicode.',
|
|
||||||
id: 'i--unicode',
|
|
||||||
level: 2
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
### Endi
|
|
||||||
|
|
||||||
\`\`\`md
|
|
||||||
## This is ignored
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## Endi
|
|
||||||
|
|
||||||
Lorem ipsum
|
|
||||||
|
|
||||||
### Yangshun
|
|
||||||
|
|
||||||
Some content here
|
|
||||||
|
|
||||||
## I ♥ unicode.
|
|
||||||
|
|
||||||
export const c = 1;
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`toc remark plugin handles empty headings 1`] = `
|
exports[`toc remark plugin handles empty headings 1`] = `
|
||||||
"export const toc = [];
|
"export const toc = [];
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,13 @@ import vfile from 'to-vfile';
|
||||||
import plugin from '../index';
|
import plugin from '../index';
|
||||||
import headings from '../../headings/index';
|
import headings from '../../headings/index';
|
||||||
|
|
||||||
const processFixture = async (name, options?) => {
|
const processFixture = async (name: string) => {
|
||||||
const filePath = path.join(__dirname, '__fixtures__', `${name}.md`);
|
const filePath = path.join(__dirname, '__fixtures__', `${name}.md`);
|
||||||
const file = await vfile.read(filePath);
|
const file = await vfile.read(filePath);
|
||||||
const result = await remark()
|
const result = await remark()
|
||||||
.use(headings)
|
.use(headings)
|
||||||
.use(mdx)
|
.use(mdx)
|
||||||
.use(plugin, options)
|
.use(plugin)
|
||||||
.process(file);
|
.process(file);
|
||||||
|
|
||||||
return result.toString();
|
return result.toString();
|
||||||
|
@ -45,14 +45,6 @@ describe('toc remark plugin', () => {
|
||||||
expect(result).toMatchSnapshot();
|
expect(result).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('exports with custom name', async () => {
|
|
||||||
const options = {
|
|
||||||
name: 'customName',
|
|
||||||
};
|
|
||||||
const result = await processFixture('just-content', options);
|
|
||||||
expect(result).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('inserts below imports', async () => {
|
it('inserts below imports', async () => {
|
||||||
const result = await processFixture('insert-below-imports');
|
const result = await processFixture('insert-below-imports');
|
||||||
expect(result).toMatchSnapshot();
|
expect(result).toMatchSnapshot();
|
||||||
|
|
|
@ -23,15 +23,13 @@ const parseOptions: ParserOptions = {
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const name = 'toc';
|
||||||
|
|
||||||
const isImport = (child: Node): child is Literal => child.type === 'import';
|
const isImport = (child: Node): child is Literal => child.type === 'import';
|
||||||
const hasImports = (index: number) => index > -1;
|
const hasImports = (index: number) => index > -1;
|
||||||
const isExport = (child: Node): child is Literal => child.type === 'export';
|
const isExport = (child: Node): child is Literal => child.type === 'export';
|
||||||
|
|
||||||
type PluginOptions = {
|
const isTarget = (child: Literal) => {
|
||||||
name?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const isTarget = (child: Literal, name: string) => {
|
|
||||||
let found = false;
|
let found = false;
|
||||||
const ast = parse(child.value, parseOptions);
|
const ast = parse(child.value, parseOptions);
|
||||||
traverse(ast, {
|
traverse(ast, {
|
||||||
|
@ -44,14 +42,14 @@ const isTarget = (child: Literal, name: string) => {
|
||||||
return found;
|
return found;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOrCreateExistingTargetIndex = (children: Node[], name: string) => {
|
const getOrCreateExistingTargetIndex = (children: Node[]) => {
|
||||||
let importsIndex = -1;
|
let importsIndex = -1;
|
||||||
let targetIndex = -1;
|
let targetIndex = -1;
|
||||||
|
|
||||||
children.forEach((child, index) => {
|
children.forEach((child, index) => {
|
||||||
if (isImport(child)) {
|
if (isImport(child)) {
|
||||||
importsIndex = index;
|
importsIndex = index;
|
||||||
} else if (isExport(child) && isTarget(child, name)) {
|
} else if (isExport(child) && isTarget(child)) {
|
||||||
targetIndex = index;
|
targetIndex = index;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -70,9 +68,7 @@ const getOrCreateExistingTargetIndex = (children: Node[], name: string) => {
|
||||||
return targetIndex;
|
return targetIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function plugin(options: PluginOptions = {}): Transformer {
|
export default function plugin(): Transformer {
|
||||||
const name = options.name || 'toc';
|
|
||||||
|
|
||||||
return (root) => {
|
return (root) => {
|
||||||
const headings: TOCItem[] = [];
|
const headings: TOCItem[] = [];
|
||||||
|
|
||||||
|
@ -91,7 +87,7 @@ export default function plugin(options: PluginOptions = {}): Transformer {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const {children} = root as Parent<Literal>;
|
const {children} = root as Parent<Literal>;
|
||||||
const targetIndex = getOrCreateExistingTargetIndex(children, name);
|
const targetIndex = getOrCreateExistingTargetIndex(children);
|
||||||
|
|
||||||
if (headings.length) {
|
if (headings.length) {
|
||||||
children[targetIndex]!.value = `export const ${name} = ${stringifyObject(
|
children[targetIndex]!.value = `export const ${name} = ${stringifyObject(
|
||||||
|
|
|
@ -10,16 +10,19 @@ import path from 'path';
|
||||||
import remark from 'remark';
|
import remark from 'remark';
|
||||||
import mdx from 'remark-mdx';
|
import mdx from 'remark-mdx';
|
||||||
import vfile from 'to-vfile';
|
import vfile from 'to-vfile';
|
||||||
import plugin from '../index';
|
import plugin, {type PluginOptions} from '../index';
|
||||||
import headings from '../../headings/index';
|
import headings from '../../headings/index';
|
||||||
|
|
||||||
const processFixture = async (name, options) => {
|
const processFixture = async (
|
||||||
|
name: string,
|
||||||
|
options: Partial<PluginOptions>,
|
||||||
|
) => {
|
||||||
const filePath = path.join(__dirname, `__fixtures__/${name}.md`);
|
const filePath = path.join(__dirname, `__fixtures__/${name}.md`);
|
||||||
const file = await vfile.read(filePath);
|
const file = await vfile.read(filePath);
|
||||||
const result = await remark()
|
const result = await remark()
|
||||||
.use(headings)
|
.use(headings)
|
||||||
.use(mdx)
|
.use(mdx)
|
||||||
.use(plugin, {...options, filePath})
|
.use(plugin, {siteDir: __dirname, staticDirs: [], ...options})
|
||||||
.process(file);
|
.process(file);
|
||||||
|
|
||||||
return result.toString();
|
return result.toString();
|
||||||
|
|
|
@ -27,7 +27,7 @@ const {
|
||||||
loaders: {inlineMarkdownImageFileLoader},
|
loaders: {inlineMarkdownImageFileLoader},
|
||||||
} = getFileLoaderUtils();
|
} = getFileLoaderUtils();
|
||||||
|
|
||||||
type PluginOptions = {
|
export type PluginOptions = {
|
||||||
staticDirs: string[];
|
staticDirs: string[];
|
||||||
siteDir: string;
|
siteDir: string;
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,9 +10,9 @@ import remark from 'remark';
|
||||||
import mdx from 'remark-mdx';
|
import mdx from 'remark-mdx';
|
||||||
import vfile from 'to-vfile';
|
import vfile from 'to-vfile';
|
||||||
import plugin from '..';
|
import plugin from '..';
|
||||||
import transformImage from '../../transformImage';
|
import transformImage, {type PluginOptions} from '../../transformImage';
|
||||||
|
|
||||||
const processFixture = async (name: string, options?) => {
|
const processFixture = async (name: string, options?: PluginOptions) => {
|
||||||
const filePath = path.join(__dirname, `__fixtures__/${name}.md`);
|
const filePath = path.join(__dirname, `__fixtures__/${name}.md`);
|
||||||
const staticDirs = [
|
const staticDirs = [
|
||||||
path.join(__dirname, '__fixtures__/static'),
|
path.join(__dirname, '__fixtures__/static'),
|
||||||
|
@ -24,7 +24,6 @@ const processFixture = async (name: string, options?) => {
|
||||||
.use(transformImage, {...options, filePath, staticDirs})
|
.use(transformImage, {...options, filePath, staticDirs})
|
||||||
.use(plugin, {
|
.use(plugin, {
|
||||||
...options,
|
...options,
|
||||||
filePath,
|
|
||||||
staticDirs,
|
staticDirs,
|
||||||
siteDir: path.join(__dirname, '__fixtures__'),
|
siteDir: path.join(__dirname, '__fixtures__'),
|
||||||
})
|
})
|
||||||
|
|
|
@ -25,7 +25,7 @@ const {
|
||||||
loaders: {inlineMarkdownLinkFileLoader},
|
loaders: {inlineMarkdownLinkFileLoader},
|
||||||
} = getFileLoaderUtils();
|
} = getFileLoaderUtils();
|
||||||
|
|
||||||
type PluginOptions = {
|
export type PluginOptions = {
|
||||||
staticDirs: string[];
|
staticDirs: string[];
|
||||||
siteDir: string;
|
siteDir: string;
|
||||||
};
|
};
|
||||||
|
|
12
packages/docusaurus-migrate/src/deps.d.ts
vendored
12
packages/docusaurus-migrate/src/deps.d.ts
vendored
|
@ -5,6 +5,14 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
declare module '@mapbox/hast-util-to-jsx';
|
declare module '@mapbox/hast-util-to-jsx' {
|
||||||
|
import type {Node} from 'unist';
|
||||||
|
|
||||||
declare module 'hast-util-to-string';
|
export default function toJsx(node: Node): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'hast-util-to-string' {
|
||||||
|
import type {Node} from 'unist';
|
||||||
|
|
||||||
|
export default function toString(node: Node): string;
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
|
@ -11,6 +11,7 @@ import {normalizeThemeConfig} from '@docusaurus/utils-validation';
|
||||||
import theme from 'prism-react-renderer/themes/github';
|
import theme from 'prism-react-renderer/themes/github';
|
||||||
import darkTheme from 'prism-react-renderer/themes/dracula';
|
import darkTheme from 'prism-react-renderer/themes/dracula';
|
||||||
import {ThemeConfigSchema, DEFAULT_CONFIG} from '../validateThemeConfig';
|
import {ThemeConfigSchema, DEFAULT_CONFIG} from '../validateThemeConfig';
|
||||||
|
import type {ThemeConfig} from '@docusaurus/theme-common';
|
||||||
|
|
||||||
function testValidateThemeConfig(partialThemeConfig: {[key: string]: unknown}) {
|
function testValidateThemeConfig(partialThemeConfig: {[key: string]: unknown}) {
|
||||||
return normalizeThemeConfig(ThemeConfigSchema, {
|
return normalizeThemeConfig(ThemeConfigSchema, {
|
||||||
|
@ -656,7 +657,7 @@ describe('themeConfig', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('color mode config', () => {
|
describe('color mode config', () => {
|
||||||
const withDefaultValues = (colorMode) =>
|
const withDefaultValues = (colorMode: ThemeConfig['colorMode']) =>
|
||||||
_.merge({}, DEFAULT_CONFIG.colorMode, colorMode);
|
_.merge({}, DEFAULT_CONFIG.colorMode, colorMode);
|
||||||
|
|
||||||
it('switch config', () => {
|
it('switch config', () => {
|
||||||
|
|
|
@ -7,12 +7,10 @@
|
||||||
|
|
||||||
import defaultPrismTheme from 'prism-react-renderer/themes/palenight';
|
import defaultPrismTheme from 'prism-react-renderer/themes/palenight';
|
||||||
import {Joi, URISchema} from '@docusaurus/utils-validation';
|
import {Joi, URISchema} from '@docusaurus/utils-validation';
|
||||||
import type {
|
import type {ThemeConfig} from '@docusaurus/theme-common';
|
||||||
ThemeConfig,
|
import type {ThemeConfigValidationContext} from '@docusaurus/types';
|
||||||
ThemeConfigValidationContext,
|
|
||||||
} from '@docusaurus/types';
|
|
||||||
|
|
||||||
const DEFAULT_DOCS_CONFIG = {
|
const DEFAULT_DOCS_CONFIG: ThemeConfig['docs'] = {
|
||||||
versionPersistence: 'localStorage',
|
versionPersistence: 'localStorage',
|
||||||
sidebar: {
|
sidebar: {
|
||||||
hideable: false,
|
hideable: false,
|
||||||
|
@ -31,13 +29,13 @@ const DocsSchema = Joi.object({
|
||||||
}).default(DEFAULT_DOCS_CONFIG.sidebar),
|
}).default(DEFAULT_DOCS_CONFIG.sidebar),
|
||||||
}).default(DEFAULT_DOCS_CONFIG);
|
}).default(DEFAULT_DOCS_CONFIG);
|
||||||
|
|
||||||
const DEFAULT_COLOR_MODE_CONFIG = {
|
const DEFAULT_COLOR_MODE_CONFIG: ThemeConfig['colorMode'] = {
|
||||||
defaultMode: 'light',
|
defaultMode: 'light',
|
||||||
disableSwitch: false,
|
disableSwitch: false,
|
||||||
respectPrefersColorScheme: false,
|
respectPrefersColorScheme: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_CONFIG = {
|
export const DEFAULT_CONFIG: ThemeConfig = {
|
||||||
colorMode: DEFAULT_COLOR_MODE_CONFIG,
|
colorMode: DEFAULT_COLOR_MODE_CONFIG,
|
||||||
docs: DEFAULT_DOCS_CONFIG,
|
docs: DEFAULT_DOCS_CONFIG,
|
||||||
metadata: [],
|
metadata: [],
|
||||||
|
@ -301,8 +299,9 @@ const CustomCssSchema = Joi.alternatives()
|
||||||
.try(Joi.array().items(Joi.string().required()), Joi.string().required())
|
.try(Joi.array().items(Joi.string().required()), Joi.string().required())
|
||||||
.optional();
|
.optional();
|
||||||
|
|
||||||
export const ThemeConfigSchema = Joi.object({
|
export const ThemeConfigSchema = Joi.object<ThemeConfig>({
|
||||||
// TODO temporary (@alpha-58)
|
// TODO temporary (@alpha-58)
|
||||||
|
// @ts-expect-error: forbidden
|
||||||
disableDarkMode: Joi.any().forbidden().messages({
|
disableDarkMode: Joi.any().forbidden().messages({
|
||||||
'any.unknown':
|
'any.unknown':
|
||||||
'disableDarkMode theme config is deprecated. Please use the new colorMode attribute. You likely want: config.themeConfig.colorMode.disableSwitch = true',
|
'disableDarkMode theme config is deprecated. Please use the new colorMode attribute. You likely want: config.themeConfig.colorMode.disableSwitch = true',
|
||||||
|
|
|
@ -32,7 +32,7 @@ export type NavbarLogo = {
|
||||||
|
|
||||||
// TODO improve
|
// TODO improve
|
||||||
export type Navbar = {
|
export type Navbar = {
|
||||||
style: 'dark' | 'primary';
|
style?: 'dark' | 'primary';
|
||||||
hideOnScroll: boolean;
|
hideOnScroll: boolean;
|
||||||
title?: string;
|
title?: string;
|
||||||
items: NavbarItem[];
|
items: NavbarItem[];
|
||||||
|
|
|
@ -6,9 +6,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {validateThemeConfig, DEFAULT_CONFIG} from '../validateThemeConfig';
|
import {validateThemeConfig, DEFAULT_CONFIG} from '../validateThemeConfig';
|
||||||
|
import type {Joi} from '@docusaurus/utils-validation';
|
||||||
|
|
||||||
function testValidateThemeConfig(themeConfig) {
|
function testValidateThemeConfig(themeConfig: {[key: string]: unknown}) {
|
||||||
function validate(schema, cfg) {
|
function validate(
|
||||||
|
schema: Joi.ObjectSchema<{[key: string]: unknown}>,
|
||||||
|
cfg: {[key: string]: unknown},
|
||||||
|
) {
|
||||||
const {value, error} = schema.validate(cfg, {
|
const {value, error} = schema.validate(cfg, {
|
||||||
convert: false,
|
convert: false,
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,7 +9,10 @@ import {validateThemeConfig, DEFAULT_CONFIG} from '../validateThemeConfig';
|
||||||
import type {Joi} from '@docusaurus/utils-validation';
|
import type {Joi} from '@docusaurus/utils-validation';
|
||||||
|
|
||||||
function testValidateThemeConfig(themeConfig: {[key: string]: unknown}) {
|
function testValidateThemeConfig(themeConfig: {[key: string]: unknown}) {
|
||||||
function validate(schema: Joi.Schema, cfg: {[key: string]: unknown}) {
|
function validate(
|
||||||
|
schema: Joi.ObjectSchema<{[key: string]: unknown}>,
|
||||||
|
cfg: {[key: string]: unknown},
|
||||||
|
) {
|
||||||
const {value, error} = schema.validate(cfg, {
|
const {value, error} = schema.validate(cfg, {
|
||||||
convert: false,
|
convert: false,
|
||||||
});
|
});
|
||||||
|
|
|
@ -237,10 +237,14 @@ function SearchPageContent(): JSX.Element {
|
||||||
url,
|
url,
|
||||||
_highlightResult: {hierarchy},
|
_highlightResult: {hierarchy},
|
||||||
_snippetResult: snippet = {},
|
_snippetResult: snippet = {},
|
||||||
|
}: {
|
||||||
|
url: string;
|
||||||
|
_highlightResult: {hierarchy: {[key: string]: {value: string}}};
|
||||||
|
_snippetResult: {content?: {value: string}};
|
||||||
}) => {
|
}) => {
|
||||||
const parsedURL = new URL(url);
|
const parsedURL = new URL(url);
|
||||||
const titles = Object.keys(hierarchy).map((key) =>
|
const titles = Object.keys(hierarchy).map((key) =>
|
||||||
sanitizeValue(hierarchy[key].value),
|
sanitizeValue(hierarchy[key]!.value),
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
20
packages/docusaurus-types/src/index.d.ts
vendored
20
packages/docusaurus-types/src/index.d.ts
vendored
|
@ -480,19 +480,19 @@ export type HtmlTags = string | HtmlTagObject | (string | HtmlTagObject)[];
|
||||||
|
|
||||||
export type ValidationSchema<T> = Joi.ObjectSchema<T>;
|
export type ValidationSchema<T> = Joi.ObjectSchema<T>;
|
||||||
|
|
||||||
export type Validate<T, U> = (
|
export type Validate<In, Out> = (
|
||||||
validationSchema: ValidationSchema<U>,
|
validationSchema: ValidationSchema<Out>,
|
||||||
options: T,
|
options: In,
|
||||||
) => U;
|
) => Out;
|
||||||
|
|
||||||
export type OptionValidationContext<T, U> = {
|
export type OptionValidationContext<In, Out> = {
|
||||||
validate: Validate<T, U>;
|
validate: Validate<In, Out>;
|
||||||
options: T;
|
options: In;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ThemeConfigValidationContext<T> = {
|
export type ThemeConfigValidationContext<In, Out = In> = {
|
||||||
validate: Validate<T, T>;
|
validate: Validate<In, Out>;
|
||||||
themeConfig: Partial<T>;
|
themeConfig: In;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Plugin<Content = unknown> = {
|
export type Plugin<Content = unknown> = {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import registry from '@generated/registry';
|
||||||
import Loading from '@theme/Loading';
|
import Loading from '@theme/Loading';
|
||||||
import flat from '../flat';
|
import flat from '../flat';
|
||||||
import {RouteContextProvider} from '../routeContext';
|
import {RouteContextProvider} from '../routeContext';
|
||||||
|
import type {RouteContext} from '@docusaurus/types';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface NodeRequire {
|
interface NodeRequire {
|
||||||
|
@ -71,14 +72,21 @@ export default function ComponentCreator(
|
||||||
loader,
|
loader,
|
||||||
modules,
|
modules,
|
||||||
webpack: () => optsWebpack,
|
webpack: () => optsWebpack,
|
||||||
render(loaded, props) {
|
render(
|
||||||
|
loaded: {[keyPath: string]: {[exportedName: string]: unknown}},
|
||||||
|
props,
|
||||||
|
) {
|
||||||
// `loaded` will be a map from key path (as returned from the flattened
|
// `loaded` will be a map from key path (as returned from the flattened
|
||||||
// chunk names) to the modules loaded from the loaders. We now have to
|
// chunk names) to the modules loaded from the loaders. We now have to
|
||||||
// restore the chunk names' previous shape from this flat record.
|
// restore the chunk names' previous shape from this flat record.
|
||||||
// We do so by taking advantage of the existing `chunkNames` and replacing
|
// We do so by taking advantage of the existing `chunkNames` and replacing
|
||||||
// each chunk name with its loaded module, so we don't create another
|
// each chunk name with its loaded module, so we don't create another
|
||||||
// object from scratch.
|
// object from scratch.
|
||||||
const loadedModules = JSON.parse(JSON.stringify(chunkNames));
|
const loadedModules = JSON.parse(JSON.stringify(chunkNames)) as {
|
||||||
|
__comp?: React.ComponentType<object>;
|
||||||
|
__context?: RouteContext;
|
||||||
|
[propName: string]: unknown;
|
||||||
|
};
|
||||||
Object.entries(loaded).forEach(([keyPath, loadedModule]) => {
|
Object.entries(loaded).forEach(([keyPath, loadedModule]) => {
|
||||||
// JSON modules are also loaded as `{ default: ... }` (`import()`
|
// JSON modules are also loaded as `{ default: ... }` (`import()`
|
||||||
// semantics) but we just want to pass the actual value to props.
|
// semantics) but we just want to pass the actual value to props.
|
||||||
|
@ -100,7 +108,8 @@ export default function ComponentCreator(
|
||||||
Object.keys(loadedModule)
|
Object.keys(loadedModule)
|
||||||
.filter((k) => k !== 'default')
|
.filter((k) => k !== 'default')
|
||||||
.forEach((nonDefaultKey) => {
|
.forEach((nonDefaultKey) => {
|
||||||
chunk[nonDefaultKey] = loadedModule[nonDefaultKey];
|
(chunk as {[key: string]: unknown})[nonDefaultKey] =
|
||||||
|
loadedModule[nonDefaultKey];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// We now have this chunk prepared. Go down the key path and replace the
|
// We now have this chunk prepared. Go down the key path and replace the
|
||||||
|
@ -108,15 +117,15 @@ export default function ComponentCreator(
|
||||||
let val = loadedModules;
|
let val = loadedModules;
|
||||||
const keyPaths = keyPath.split('.');
|
const keyPaths = keyPath.split('.');
|
||||||
keyPaths.slice(0, -1).forEach((k) => {
|
keyPaths.slice(0, -1).forEach((k) => {
|
||||||
val = val[k];
|
val = val[k] as {[propName: string]: unknown};
|
||||||
});
|
});
|
||||||
val[keyPaths[keyPaths.length - 1]!] = chunk;
|
val[keyPaths[keyPaths.length - 1]!] = chunk;
|
||||||
});
|
});
|
||||||
|
|
||||||
/* eslint-disable no-underscore-dangle */
|
/* eslint-disable no-underscore-dangle */
|
||||||
const Component = loadedModules.__comp;
|
const Component = loadedModules.__comp!;
|
||||||
delete loadedModules.__comp;
|
delete loadedModules.__comp;
|
||||||
const routeContext = loadedModules.__context;
|
const routeContext = loadedModules.__context!;
|
||||||
delete loadedModules.__context;
|
delete loadedModules.__context;
|
||||||
/* eslint-enable no-underscore-dangle */
|
/* eslint-enable no-underscore-dangle */
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,9 @@
|
||||||
/* eslint-disable jest/no-conditional-expect */
|
/* eslint-disable jest/no-conditional-expect */
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import stylelint, {type LinterResult} from 'stylelint';
|
import stylelint from 'stylelint';
|
||||||
import rule from '../index';
|
import rule from '../index';
|
||||||
|
|
||||||
const {ruleName} = rule;
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
namespace jest {
|
namespace jest {
|
||||||
interface Matchers<R> {
|
interface Matchers<R> {
|
||||||
|
@ -20,29 +18,44 @@ declare global {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOutputCss(output: LinterResult) {
|
type TestSuite = {
|
||||||
|
ruleName: string;
|
||||||
|
fix: boolean;
|
||||||
|
accept: TestCase[];
|
||||||
|
reject: TestCase[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type TestCase = {
|
||||||
|
code: string;
|
||||||
|
description?: string;
|
||||||
|
fixed?: string;
|
||||||
|
message?: string;
|
||||||
|
line?: number;
|
||||||
|
column?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getOutputCss(output: stylelint.LinterResult) {
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
const result = output.results[0]._postcssResult;
|
const result = output.results[0]!._postcssResult!;
|
||||||
return result.root.toString(result.opts.syntax);
|
return result.root.toString(result.opts!.syntax);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testStylelintRule(config, tests) {
|
function testStylelintRule(config: stylelint.Config, tests: TestSuite) {
|
||||||
describe(`${tests.ruleName}`, () => {
|
describe(`${tests.ruleName}`, () => {
|
||||||
const checkTestCaseContent = (testCase) =>
|
const checkTestCaseContent = (testCase: TestCase) =>
|
||||||
testCase.description || testCase.code || 'no description';
|
testCase.description || testCase.code;
|
||||||
|
|
||||||
if (tests.accept?.length) {
|
if (tests.accept?.length) {
|
||||||
describe('accept cases', () => {
|
describe('accept cases', () => {
|
||||||
tests.accept.forEach((testCase) => {
|
tests.accept.forEach((testCase) => {
|
||||||
it(`${checkTestCaseContent(testCase)}`, async () => {
|
it(`${checkTestCaseContent(testCase)}`, async () => {
|
||||||
const options = {
|
const options: stylelint.LinterOptions = {
|
||||||
code: testCase.code,
|
code: testCase.code,
|
||||||
config,
|
config,
|
||||||
syntax: tests.syntax,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const output = await stylelint.lint(options);
|
const output = await stylelint.lint(options);
|
||||||
expect(output.results[0].warnings).toEqual([]);
|
expect(output.results[0]!.warnings).toEqual([]);
|
||||||
if (!tests.fix) {
|
if (!tests.fix) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -61,12 +74,11 @@ function testStylelintRule(config, tests) {
|
||||||
const options = {
|
const options = {
|
||||||
code: testCase.code,
|
code: testCase.code,
|
||||||
config,
|
config,
|
||||||
syntax: tests.syntax,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const output = await stylelint.lint(options);
|
const output = await stylelint.lint(options);
|
||||||
const {warnings} = output.results[0];
|
const {warnings} = output.results[0]!;
|
||||||
const warning = warnings[0];
|
const warning = warnings[0]!;
|
||||||
expect(warnings.length).toBeGreaterThanOrEqual(1);
|
expect(warnings.length).toBeGreaterThanOrEqual(1);
|
||||||
expect(testCase).toHaveMessage();
|
expect(testCase).toHaveMessage();
|
||||||
if (testCase.message != null) {
|
if (testCase.message != null) {
|
||||||
|
@ -95,7 +107,7 @@ function testStylelintRule(config, tests) {
|
||||||
}
|
}
|
||||||
|
|
||||||
expect.extend({
|
expect.extend({
|
||||||
toHaveMessage(testCase) {
|
toHaveMessage(testCase: TestCase) {
|
||||||
if (testCase.message == null) {
|
if (testCase.message == null) {
|
||||||
return {
|
return {
|
||||||
message: () =>
|
message: () =>
|
||||||
|
@ -118,11 +130,11 @@ testStylelintRule(
|
||||||
{
|
{
|
||||||
plugins: [path.join(__dirname, '../../lib/index.js')],
|
plugins: [path.join(__dirname, '../../lib/index.js')],
|
||||||
rules: {
|
rules: {
|
||||||
[ruleName]: [true, {header: '*\n * Copyright'}],
|
[rule.ruleName]: [true, {header: '*\n * Copyright'}],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ruleName,
|
ruleName: rule.ruleName,
|
||||||
fix: true,
|
fix: true,
|
||||||
accept: [
|
accept: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -53,9 +53,11 @@
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules",
|
"node_modules",
|
||||||
|
"coverage/**",
|
||||||
"**/lib/**/*",
|
"**/lib/**/*",
|
||||||
"website/**",
|
"website/**",
|
||||||
"**/__mocks__/**/*",
|
"**/__mocks__/**/*",
|
||||||
|
"**/__fixtures__/**/*",
|
||||||
"examples/**",
|
"examples/**",
|
||||||
"packages/create-docusaurus/templates/**"
|
"packages/create-docusaurus/templates/**"
|
||||||
]
|
]
|
||||||
|
|
Loading…
Add table
Reference in a new issue