Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(mdx-loader): streamline typescript usage for remark plugin types #10651

Merged
merged 14 commits into from
Nov 7, 2024
33 changes: 13 additions & 20 deletions packages/docusaurus-mdx-loader/src/remark/admonitions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,11 @@
* LICENSE file in the root directory of this source tree.
*/
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer, Processor} from 'unified';
import type {Transformer, Plugin} from 'unified';

// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {ContainerDirective} from 'mdast-util-directive';
import type {Parent} from 'mdast';

// TODO as of April 2023, no way to import/re-export this ESM type easily :/
// This might change soon, likely after TS 5.2
// See https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1517839391
// import type {Plugin} from 'unified';
type Plugin = any; // TODO fix this asap
import type {Parent, Root} from 'mdast';

export type AdmonitionOptions = {
keywords: string[];
Expand Down Expand Up @@ -85,42 +79,41 @@ function getTextOnlyTitle(directiveLabel: DirectiveLabel): string | undefined {
: undefined;
}

const plugin: Plugin = function plugin(
this: Processor,
optionsInput: Partial<AdmonitionOptions> = {},
): Transformer {
const plugin: Plugin<Partial<AdmonitionOptions>[], Root> = function plugin(
this,
optionsInput = {},
): Transformer<Root> {
const {keywords} = normalizeAdmonitionOptions(optionsInput);

return async (root) => {
const {visit} = await import('unist-util-visit');

visit(root, (node) => {
if (node.type === 'containerDirective') {
const directive = node as ContainerDirective;
const isAdmonition = keywords.includes(directive.name);
const isAdmonition = keywords.includes(node.name);

if (!isAdmonition) {
return;
}

const {directiveLabel, contentNodes} = parseDirective(directive);
const {directiveLabel, contentNodes} = parseDirective(node);

const textOnlyTitle =
directive.attributes?.title ??
node.attributes?.title ??
(directiveLabel ? getTextOnlyTitle(directiveLabel) : undefined);

// Transform the mdast directive node to a hast admonition node
// See https://github.com/syntax-tree/mdast-util-to-hast#fields-on-nodes
// TODO in MDX v2 we should transform the whole directive to
// mdxJsxFlowElement instead of using hast
directive.data = {
node.data = {
hName: 'admonition',
hProperties: {
...(textOnlyTitle && {title: textOnlyTitle}),
type: directive.name,
type: node.name,
},
};
directive.children = contentNodes;
node.children = contentNodes;

// TODO legacy MDX v1 <mdxAdmonitionTitle> workaround
// v1: not possible to inject complex JSX elements as props
Expand All @@ -135,7 +128,7 @@ const plugin: Plugin = function plugin(
children: directiveLabel.children,
};
// @ts-expect-error: invented node type
directive.children.unshift(complexTitleNode);
node.children.unshift(complexTitleNode);
}
}
});
Expand Down
32 changes: 13 additions & 19 deletions packages/docusaurus-mdx-loader/src/remark/contentTitle/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,13 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer} from 'unified';
import type {Heading, Parent} from 'mdast';
import type {Transformer, Plugin} from 'unified';
import type {Heading, Parent, Root} from 'mdast';

// @ts-expect-error: ES support...
import type {MdxJsxFlowElement} from 'mdast-util-mdx';

// TODO as of April 2023, no way to import/re-export this ESM type easily :/
// TODO upgrade to TS 5.3
// See https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1517839391
// import type {Plugin} from 'unified';
type Plugin = any; // TODO fix this asap

interface PluginOptions {
removeContentTitle?: boolean;
}
Expand All @@ -41,35 +34,36 @@ function wrapHeadingInJsxHeader(
* This is exposed as "data.contentTitle" to the processed vfile
* Also gives the ability to strip that content title (used for the blog plugin)
*/
const plugin: Plugin = function plugin(
options: PluginOptions = {},
): Transformer {
const plugin: Plugin<PluginOptions[], Root> = function plugin(
options = {},
): Transformer<Root> {
// content title is
const removeContentTitle = options.removeContentTitle ?? false;

return async (root, vfile) => {
const {toString} = await import('mdast-util-to-string');
const {visit, EXIT} = await import('unist-util-visit');
visit(root, ['heading', 'thematicBreak'], (node, index, parent) => {
if (!parent || index === undefined) {
return undefined;
}
if (node.type === 'heading') {
const headingNode = node as Heading;
// console.log('headingNode:', headingNode);

if (headingNode.depth === 1) {
vfile.data.contentTitle = toString(headingNode);
if (node.depth === 1) {
vfile.data.contentTitle = toString(node);
if (removeContentTitle) {
// @ts-expect-error: TODO how to fix?
parent!.children.splice(index, 1);
parent.children.splice(index, 1);
} else {
// TODO in the future it might be better to export contentTitle as
// as JSX node to keep this logic a theme concern?
// See https://github.com/facebook/docusaurus/pull/10335#issuecomment-2250187371
wrapHeadingInJsxHeader(headingNode, parent, index!);
wrapHeadingInJsxHeader(node, parent, index);
}
return EXIT; // We only handle the very first heading
}
// We only handle contentTitle if it's the very first heading found
if (headingNode.depth >= 1) {
if (node.depth >= 1) {
return EXIT;
}
}
Expand Down
7 changes: 3 additions & 4 deletions packages/docusaurus-mdx-loader/src/remark/details/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer} from 'unified';

// @ts-expect-error: ES support...
import type {MdxJsxFlowElement} from 'mdast-util-mdx';
import type {Root} from 'mdast';

// Transform <details> to <Details>
// MDX 2 doesn't allow to substitute html elements with the provider anymore
export default function plugin(): Transformer {
export default function plugin(): Transformer<Root> {
return async (root) => {
const {visit} = await import('unist-util-visit');
visit(root, 'mdxJsxFlowElement', (node: MdxJsxFlowElement) => {
visit(root, 'mdxJsxFlowElement', (node) => {
if (node.name === 'details') {
node.name = 'Details';
}
Expand Down
7 changes: 3 additions & 4 deletions packages/docusaurus-mdx-loader/src/remark/head/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer} from 'unified';

// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {MdxJsxFlowElement} from 'mdast-util-mdx';
import type {Root} from 'mdast';

// Transform <head> to <Head>
// MDX 2 doesn't allow to substitute html elements with the provider anymore
export default function plugin(): Transformer {
export default function plugin(): Transformer<Root> {
return async (root) => {
const {visit} = await import('unist-util-visit');
visit(root, 'mdxJsxFlowElement', (node: MdxJsxFlowElement) => {
visit(root, 'mdxJsxFlowElement', (node) => {
if (node.name === 'head') {
node.name = 'Head';
}
Expand Down
14 changes: 8 additions & 6 deletions packages/docusaurus-mdx-loader/src/remark/headings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,22 @@

import {parseMarkdownHeadingId, createSlugger} from '@docusaurus/utils';
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer} from 'unified';
import type {Heading, Text} from 'mdast';
import type {Plugin, Transformer} from 'unified';
import type {Root, Text} from 'mdast';

export interface PluginOptions {
anchorsMaintainCase: boolean;
}

export default function plugin({
const plugin: Plugin<PluginOptions[], Root> = function plugin({
anchorsMaintainCase,
}: PluginOptions): Transformer {
}): Transformer<Root> {
return async (root) => {
const {toString} = await import('mdast-util-to-string');
const {visit} = await import('unist-util-visit');

const slugs = createSlugger();
visit(root, 'heading', (headingNode: Heading) => {
visit(root, 'heading', (headingNode) => {
const data = headingNode.data ?? (headingNode.data = {});
const properties = (data.hProperties || (data.hProperties = {})) as {
id: string;
Expand Down Expand Up @@ -77,4 +77,6 @@ export default function plugin({
properties.id = id;
});
};
}
};

export default plugin;
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
*/

// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer, Processor} from 'unified';
import type {Code} from 'mdast';
import type {Transformer, Plugin} from 'unified';
import type {Root} from 'mdast';

// Solution inspired by https://github.com/pomber/docusaurus-mdx-2/blob/main/packages/mdx-loader/src/remark/codeCompat/index.ts
// TODO after MDX 2 we probably don't need this - remove soon?
Expand All @@ -16,11 +16,11 @@ import type {Code} from 'mdast';

// To make theme-classic/src/theme/MDXComponents/Pre work
// we need to fill two properties that mdx v2 doesn't provide anymore
export default function codeCompatPlugin(this: Processor): Transformer {
const plugin: Plugin<unknown[], Root> = function plugin(): Transformer<Root> {
return async (root) => {
const {visit} = await import('unist-util-visit');

visit(root, 'code', (node: Code) => {
visit(root, 'code', (node) => {
node.data = node.data || {};

node.data.hProperties = node.data.hProperties || {};
Expand All @@ -31,4 +31,6 @@ export default function codeCompatPlugin(this: Processor): Transformer {
node.data.hProperties.live = node.meta?.split(' ').includes('live');
});
};
}
};

export default plugin;
6 changes: 3 additions & 3 deletions packages/docusaurus-mdx-loader/src/remark/mermaid/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ import {transformNode} from '../utils';

// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer} from 'unified';
import type {Code} from 'mdast';
import type {Root} from 'mdast';

// TODO: this plugin shouldn't be in the core MDX loader
// After we allow plugins to provide Remark/Rehype plugins (see
// https://github.com/facebook/docusaurus/issues/6370), this should be provided
// by theme-mermaid itself
export default function plugin(): Transformer {
export default function plugin(): Transformer<Root> {
return async (root) => {
const {visit} = await import('unist-util-visit');

visit(root, 'code', (node: Code) => {
visit(root, 'code', (node) => {
if (node.lang === 'mermaid') {
// TODO migrate to mdxJsxFlowElement? cf admonitions
transformNode(node, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import {
} from '@docusaurus/utils';

// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer} from 'unified';
import type {Definition, Link} from 'mdast';
import type {Plugin, Transformer} from 'unified';
import type {Definition, Link, Root} from 'mdast';

type ResolveMarkdownLinkParams = {
/**
Expand All @@ -35,12 +35,6 @@ export interface PluginOptions {
resolveMarkdownLink: ResolveMarkdownLink;
}

// TODO as of April 2023, no way to import/re-export this ESM type easily :/
// TODO upgrade to TS 5.3
// See https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1517839391
// import type {Plugin} from 'unified';
type Plugin = any; // TODO fix this asap

const HAS_MARKDOWN_EXTENSION = /\.mdx?$/i;

function parseMarkdownLinkURLPath(link: string): URLPath | null {
Expand All @@ -64,7 +58,9 @@ function parseMarkdownLinkURLPath(link: string): URLPath | null {
* This is exposed as "data.contentTitle" to the processed vfile
* Also gives the ability to strip that content title (used for the blog plugin)
*/
const plugin: Plugin = function plugin(options: PluginOptions): Transformer {
const plugin: Plugin<PluginOptions[], Root> = function plugin(
options,
): Transformer<Root> {
const {resolveMarkdownLink} = options;
return async (root, file) => {
const {visit} = await import('unist-util-visit');
Expand Down
10 changes: 7 additions & 3 deletions packages/docusaurus-mdx-loader/src/remark/toc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from './utils';
import type {Heading, Root} from 'mdast';
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer} from 'unified';
import type {Plugin, Transformer} from 'unified';
import type {
MdxjsEsm,
MdxJsxFlowElement,
Expand Down Expand Up @@ -155,7 +155,9 @@ async function collectTOCItems({
}
}

export default function plugin(options: PluginOptions = {}): Transformer<Root> {
const plugin: Plugin<PluginOptions[], Root> = function plugin(
options = {},
): Transformer<Root> {
const tocExportName = options.name || 'toc';

return async (root) => {
Expand Down Expand Up @@ -184,4 +186,6 @@ export default function plugin(options: PluginOptions = {}): Transformer<Root> {
}),
);
};
}
};

export default plugin;
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import sizeOf from 'image-size';
import logger from '@docusaurus/logger';
import {assetRequireAttributeValue, transformNode} from '../utils';
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer} from 'unified';
import type {Plugin, Transformer} from 'unified';
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {MdxJsxTextElement} from 'mdast-util-mdx';
import type {Image} from 'mdast';
import type {Image, Root} from 'mdast';
import type {Parent} from 'unist';

type PluginOptions = {
Expand Down Expand Up @@ -186,7 +186,9 @@ async function processImageNode(target: Target, context: Context) {
await toImageRequireNode(target, imagePath, context);
}

export default function plugin(options: PluginOptions): Transformer {
const plugin: Plugin<PluginOptions[], Root> = function plugin(
options,
): Transformer<Root> {
return async (root, vfile) => {
const {visit} = await import('unist-util-visit');

Expand All @@ -201,9 +203,14 @@ export default function plugin(options: PluginOptions): Transformer {
};

const promises: Promise<void>[] = [];
visit(root, 'image', (node: Image, index, parent) => {
visit(root, 'image', (node, index, parent) => {
if (!parent || index === undefined) {
return;
}
promises.push(processImageNode([node, index, parent!], context));
});
await Promise.all(promises);
};
}
};

export default plugin;
Loading