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

This commit is contained in:
Balthasar Hofer 2024-11-07 17:42:32 +01:00 committed by GitHub
parent e32aa605ca
commit bdf55eda22
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 92 additions and 92 deletions

View file

@ -5,17 +5,11 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 // @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 // @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {ContainerDirective} from 'mdast-util-directive'; import type {ContainerDirective} from 'mdast-util-directive';
import type {Parent} from 'mdast'; import type {Parent, Root} 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
export type AdmonitionOptions = { export type AdmonitionOptions = {
keywords: string[]; keywords: string[];
@ -85,10 +79,10 @@ function getTextOnlyTitle(directiveLabel: DirectiveLabel): string | undefined {
: undefined; : undefined;
} }
const plugin: Plugin = function plugin( const plugin: Plugin<Partial<AdmonitionOptions>[], Root> = function plugin(
this: Processor, this,
optionsInput: Partial<AdmonitionOptions> = {}, optionsInput = {},
): Transformer { ): Transformer<Root> {
const {keywords} = normalizeAdmonitionOptions(optionsInput); const {keywords} = normalizeAdmonitionOptions(optionsInput);
return async (root) => { return async (root) => {
@ -96,31 +90,30 @@ const plugin: Plugin = function plugin(
visit(root, (node) => { visit(root, (node) => {
if (node.type === 'containerDirective') { if (node.type === 'containerDirective') {
const directive = node as ContainerDirective; const isAdmonition = keywords.includes(node.name);
const isAdmonition = keywords.includes(directive.name);
if (!isAdmonition) { if (!isAdmonition) {
return; return;
} }
const {directiveLabel, contentNodes} = parseDirective(directive); const {directiveLabel, contentNodes} = parseDirective(node);
const textOnlyTitle = const textOnlyTitle =
directive.attributes?.title ?? node.attributes?.title ??
(directiveLabel ? getTextOnlyTitle(directiveLabel) : undefined); (directiveLabel ? getTextOnlyTitle(directiveLabel) : undefined);
// Transform the mdast directive node to a hast admonition node // Transform the mdast directive node to a hast admonition node
// See https://github.com/syntax-tree/mdast-util-to-hast#fields-on-nodes // See https://github.com/syntax-tree/mdast-util-to-hast#fields-on-nodes
// TODO in MDX v2 we should transform the whole directive to // TODO in MDX v2 we should transform the whole directive to
// mdxJsxFlowElement instead of using hast // mdxJsxFlowElement instead of using hast
directive.data = { node.data = {
hName: 'admonition', hName: 'admonition',
hProperties: { hProperties: {
...(textOnlyTitle && {title: textOnlyTitle}), ...(textOnlyTitle && {title: textOnlyTitle}),
type: directive.name, type: node.name,
}, },
}; };
directive.children = contentNodes; node.children = contentNodes;
// TODO legacy MDX v1 <mdxAdmonitionTitle> workaround // TODO legacy MDX v1 <mdxAdmonitionTitle> workaround
// v1: not possible to inject complex JSX elements as props // v1: not possible to inject complex JSX elements as props
@ -135,7 +128,7 @@ const plugin: Plugin = function plugin(
children: directiveLabel.children, children: directiveLabel.children,
}; };
// @ts-expect-error: invented node type // @ts-expect-error: invented node type
directive.children.unshift(complexTitleNode); node.children.unshift(complexTitleNode);
} }
} }
}); });

View file

@ -4,20 +4,13 @@
* This source code is licensed under the MIT license found in the * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 // @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer} from 'unified'; import type {Transformer, Plugin} from 'unified';
import type {Heading, Parent} from 'mdast'; import type {Heading, Parent, Root} from 'mdast';
// @ts-expect-error: ES support... // @ts-expect-error: ES support...
import type {MdxJsxFlowElement} from 'mdast-util-mdx'; 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 { interface PluginOptions {
removeContentTitle?: boolean; removeContentTitle?: boolean;
} }
@ -41,9 +34,9 @@ function wrapHeadingInJsxHeader(
* This is exposed as "data.contentTitle" to the processed vfile * This is exposed as "data.contentTitle" to the processed vfile
* Also gives the ability to strip that content title (used for the blog plugin) * Also gives the ability to strip that content title (used for the blog plugin)
*/ */
const plugin: Plugin = function plugin( const plugin: Plugin<PluginOptions[], Root> = function plugin(
options: PluginOptions = {}, options = {},
): Transformer { ): Transformer<Root> {
// content title is // content title is
const removeContentTitle = options.removeContentTitle ?? false; const removeContentTitle = options.removeContentTitle ?? false;
@ -51,25 +44,26 @@ const plugin: Plugin = function plugin(
const {toString} = await import('mdast-util-to-string'); const {toString} = await import('mdast-util-to-string');
const {visit, EXIT} = await import('unist-util-visit'); const {visit, EXIT} = await import('unist-util-visit');
visit(root, ['heading', 'thematicBreak'], (node, index, parent) => { visit(root, ['heading', 'thematicBreak'], (node, index, parent) => {
if (!parent || index === undefined) {
return undefined;
}
if (node.type === 'heading') { if (node.type === 'heading') {
const headingNode = node as Heading;
// console.log('headingNode:', headingNode); // console.log('headingNode:', headingNode);
if (headingNode.depth === 1) { if (node.depth === 1) {
vfile.data.contentTitle = toString(headingNode); vfile.data.contentTitle = toString(node);
if (removeContentTitle) { if (removeContentTitle) {
// @ts-expect-error: TODO how to fix? parent.children.splice(index, 1);
parent!.children.splice(index, 1);
} else { } else {
// TODO in the future it might be better to export contentTitle as // TODO in the future it might be better to export contentTitle as
// as JSX node to keep this logic a theme concern? // as JSX node to keep this logic a theme concern?
// See https://github.com/facebook/docusaurus/pull/10335#issuecomment-2250187371 // 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 return EXIT; // We only handle the very first heading
} }
// We only handle contentTitle if it's the very first heading found // We only handle contentTitle if it's the very first heading found
if (headingNode.depth >= 1) { if (node.depth >= 1) {
return EXIT; return EXIT;
} }
} }

View file

@ -8,15 +8,14 @@
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 // @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer} from 'unified'; import type {Transformer} from 'unified';
// @ts-expect-error: ES support... import type {Root} from 'mdast';
import type {MdxJsxFlowElement} from 'mdast-util-mdx';
// Transform <details> to <Details> // Transform <details> to <Details>
// MDX 2 doesn't allow to substitute html elements with the provider anymore // 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) => { return async (root) => {
const {visit} = await import('unist-util-visit'); const {visit} = await import('unist-util-visit');
visit(root, 'mdxJsxFlowElement', (node: MdxJsxFlowElement) => { visit(root, 'mdxJsxFlowElement', (node) => {
if (node.name === 'details') { if (node.name === 'details') {
node.name = 'Details'; node.name = 'Details';
} }

View file

@ -8,15 +8,14 @@
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 // @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer} from 'unified'; import type {Transformer} from 'unified';
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 import type {Root} from 'mdast';
import type {MdxJsxFlowElement} from 'mdast-util-mdx';
// Transform <head> to <Head> // Transform <head> to <Head>
// MDX 2 doesn't allow to substitute html elements with the provider anymore // 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) => { return async (root) => {
const {visit} = await import('unist-util-visit'); const {visit} = await import('unist-util-visit');
visit(root, 'mdxJsxFlowElement', (node: MdxJsxFlowElement) => { visit(root, 'mdxJsxFlowElement', (node) => {
if (node.name === 'head') { if (node.name === 'head') {
node.name = 'Head'; node.name = 'Head';
} }

View file

@ -9,22 +9,22 @@
import {parseMarkdownHeadingId, createSlugger} from '@docusaurus/utils'; import {parseMarkdownHeadingId, createSlugger} from '@docusaurus/utils';
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 // @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 {Heading, Text} from 'mdast'; import type {Root, Text} from 'mdast';
export interface PluginOptions { export interface PluginOptions {
anchorsMaintainCase: boolean; anchorsMaintainCase: boolean;
} }
export default function plugin({ const plugin: Plugin<PluginOptions[], Root> = function plugin({
anchorsMaintainCase, anchorsMaintainCase,
}: PluginOptions): Transformer { }): Transformer<Root> {
return async (root) => { return async (root) => {
const {toString} = await import('mdast-util-to-string'); const {toString} = await import('mdast-util-to-string');
const {visit} = await import('unist-util-visit'); const {visit} = await import('unist-util-visit');
const slugs = createSlugger(); const slugs = createSlugger();
visit(root, 'heading', (headingNode: Heading) => { visit(root, 'heading', (headingNode) => {
const data = headingNode.data ?? (headingNode.data = {}); const data = headingNode.data ?? (headingNode.data = {});
const properties = (data.hProperties || (data.hProperties = {})) as { const properties = (data.hProperties || (data.hProperties = {})) as {
id: string; id: string;
@ -77,4 +77,6 @@ export default function plugin({
properties.id = id; properties.id = id;
}); });
}; };
} };
export default plugin;

View file

@ -6,8 +6,8 @@
*/ */
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 // @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer, Processor} from 'unified'; import type {Transformer, Plugin} from 'unified';
import type {Code} from 'mdast'; 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 // 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? // TODO after MDX 2 we probably don't need this - remove soon?
@ -16,11 +16,11 @@ import type {Code} from 'mdast';
// To make theme-classic/src/theme/MDXComponents/Pre work // To make theme-classic/src/theme/MDXComponents/Pre work
// we need to fill two properties that mdx v2 doesn't provide anymore // 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) => { return async (root) => {
const {visit} = await import('unist-util-visit'); const {visit} = await import('unist-util-visit');
visit(root, 'code', (node: Code) => { visit(root, 'code', (node) => {
node.data = node.data || {}; node.data = node.data || {};
node.data.hProperties = node.data.hProperties || {}; node.data.hProperties = node.data.hProperties || {};
@ -31,4 +31,6 @@ export default function codeCompatPlugin(this: Processor): Transformer {
node.data.hProperties.live = node.meta?.split(' ').includes('live'); node.data.hProperties.live = node.meta?.split(' ').includes('live');
}); });
}; };
} };
export default plugin;

View file

@ -9,17 +9,17 @@ import {transformNode} from '../utils';
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 // @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer} from 'unified'; 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 // TODO: this plugin shouldn't be in the core MDX loader
// After we allow plugins to provide Remark/Rehype plugins (see // After we allow plugins to provide Remark/Rehype plugins (see
// https://github.com/facebook/docusaurus/issues/6370), this should be provided // https://github.com/facebook/docusaurus/issues/6370), this should be provided
// by theme-mermaid itself // by theme-mermaid itself
export default function plugin(): Transformer { export default function plugin(): Transformer<Root> {
return async (root) => { return async (root) => {
const {visit} = await import('unist-util-visit'); const {visit} = await import('unist-util-visit');
visit(root, 'code', (node: Code) => { visit(root, 'code', (node) => {
if (node.lang === 'mermaid') { if (node.lang === 'mermaid') {
// TODO migrate to mdxJsxFlowElement? cf admonitions // TODO migrate to mdxJsxFlowElement? cf admonitions
transformNode(node, { transformNode(node, {

View file

@ -12,8 +12,8 @@ import {
} from '@docusaurus/utils'; } from '@docusaurus/utils';
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 // @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 {Definition, Link} from 'mdast'; import type {Definition, Link, Root} from 'mdast';
type ResolveMarkdownLinkParams = { type ResolveMarkdownLinkParams = {
/** /**
@ -35,12 +35,6 @@ export interface PluginOptions {
resolveMarkdownLink: ResolveMarkdownLink; 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; const HAS_MARKDOWN_EXTENSION = /\.mdx?$/i;
function parseMarkdownLinkURLPath(link: string): URLPath | null { function parseMarkdownLinkURLPath(link: string): URLPath | null {
@ -64,7 +58,9 @@ function parseMarkdownLinkURLPath(link: string): URLPath | null {
* This is exposed as "data.contentTitle" to the processed vfile * This is exposed as "data.contentTitle" to the processed vfile
* Also gives the ability to strip that content title (used for the blog plugin) * 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; const {resolveMarkdownLink} = options;
return async (root, file) => { return async (root, file) => {
const {visit} = await import('unist-util-visit'); const {visit} = await import('unist-util-visit');

View file

@ -15,7 +15,7 @@ import {
} from './utils'; } from './utils';
import type {Heading, Root} from 'mdast'; import type {Heading, Root} from 'mdast';
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 // @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 { import type {
MdxjsEsm, MdxjsEsm,
MdxJsxFlowElement, MdxJsxFlowElement,
@ -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'; const tocExportName = options.name || 'toc';
return async (root) => { return async (root) => {
@ -184,4 +186,6 @@ export default function plugin(options: PluginOptions = {}): Transformer<Root> {
}), }),
); );
}; };
} };
export default plugin;

View file

@ -21,10 +21,10 @@ import sizeOf from 'image-size';
import logger from '@docusaurus/logger'; import logger from '@docusaurus/logger';
import {assetRequireAttributeValue, transformNode} from '../utils'; import {assetRequireAttributeValue, transformNode} from '../utils';
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 // @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 // @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {MdxJsxTextElement} from 'mdast-util-mdx'; import type {MdxJsxTextElement} from 'mdast-util-mdx';
import type {Image} from 'mdast'; import type {Image, Root} from 'mdast';
import type {Parent} from 'unist'; import type {Parent} from 'unist';
type PluginOptions = { type PluginOptions = {
@ -186,7 +186,9 @@ async function processImageNode(target: Target, context: Context) {
await toImageRequireNode(target, imagePath, 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) => { return async (root, vfile) => {
const {visit} = await import('unist-util-visit'); const {visit} = await import('unist-util-visit');
@ -201,9 +203,14 @@ export default function plugin(options: PluginOptions): Transformer {
}; };
const promises: Promise<void>[] = []; 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)); promises.push(processImageNode([node, index, parent!], context));
}); });
await Promise.all(promises); await Promise.all(promises);
}; };
} };
export default plugin;

View file

@ -18,11 +18,11 @@ import {
import escapeHtml from 'escape-html'; import escapeHtml from 'escape-html';
import {assetRequireAttributeValue, transformNode} from '../utils'; import {assetRequireAttributeValue, transformNode} from '../utils';
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 // @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 // @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {MdxJsxTextElement} from 'mdast-util-mdx'; import type {MdxJsxTextElement} from 'mdast-util-mdx';
import type {Parent} from 'unist'; import type {Parent} from 'unist';
import type {Link, Literal} from 'mdast'; import type {Link, Literal, Root} from 'mdast';
type PluginOptions = { type PluginOptions = {
staticDirs: string[]; staticDirs: string[];
@ -199,7 +199,9 @@ async function processLinkNode(target: Target, context: Context) {
} }
} }
export default function plugin(options: PluginOptions): Transformer { const plugin: Plugin<PluginOptions[], Root> = function plugin(
options,
): Transformer<Root> {
return async (root, vfile) => { return async (root, vfile) => {
const {visit} = await import('unist-util-visit'); const {visit} = await import('unist-util-visit');
@ -214,9 +216,14 @@ export default function plugin(options: PluginOptions): Transformer {
}; };
const promises: Promise<void>[] = []; const promises: Promise<void>[] = [];
visit(root, 'link', (node: Link, index, parent) => { visit(root, 'link', (node, index, parent) => {
promises.push(processLinkNode([node, index, parent!], context)); if (!parent || index === undefined) {
return;
}
promises.push(processLinkNode([node, index, parent], context));
}); });
await Promise.all(promises); await Promise.all(promises);
}; };
} };
export default plugin;

View file

@ -9,21 +9,16 @@ import process from 'process';
import logger from '@docusaurus/logger'; import logger from '@docusaurus/logger';
import {posixPath} from '@docusaurus/utils'; import {posixPath} from '@docusaurus/utils';
import {transformNode} from '../utils'; import {transformNode} from '../utils';
import type {Root} from 'mdast';
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 // @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer, Processor, Parent} from 'unified'; import type {Transformer, Processor, Parent, Plugin} from 'unified';
import type { import type {
Directives, Directives,
TextDirective, TextDirective,
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 // @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
} from 'mdast-util-directive'; } from 'mdast-util-directive';
// 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
type DirectiveType = Directives['type']; type DirectiveType = Directives['type'];
const directiveTypes: DirectiveType[] = [ const directiveTypes: DirectiveType[] = [
@ -130,7 +125,9 @@ function isUnusedDirective(directive: Directives) {
return !directive.data; return !directive.data;
} }
const plugin: Plugin = function plugin(this: Processor): Transformer { const plugin: Plugin<unknown[], Root> = function plugin(
this: Processor,
): Transformer<Root> {
return async (tree, file) => { return async (tree, file) => {
const {visit} = await import('unist-util-visit'); const {visit} = await import('unist-util-visit');