Skip to content

Commit

Permalink
⏩ Extend renderers to allow for kind/subtypes (#460)
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanc1 authored Aug 31, 2024
1 parent 6ca6dbb commit ed5a7f6
Show file tree
Hide file tree
Showing 13 changed files with 357 additions and 90 deletions.
8 changes: 8 additions & 0 deletions .changeset/tidy-candles-develop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'myst-to-react': patch
'@myst-theme/providers': patch
'@myst-theme/jupyter': patch
'@myst-theme/site': patch
---

Allow for specific renderers
2 changes: 1 addition & 1 deletion packages/jupyter/src/figure.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import classNames from 'classnames';
import { OutputDecoration } from './decoration.js';

export function Figure({ node }: { node: GenericNode }) {
const { container: Container } = DEFAULT_RENDERERS;
const { base: Container } = DEFAULT_RENDERERS['container'];
const isFromJupyer = node.source?.kind === SourceFileKind.Notebook;
const output = node.children?.find((child) => child.type === 'output');
if (isFromJupyer && !!output) {
Expand Down
14 changes: 12 additions & 2 deletions packages/myst-to-react/src/MyST.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { matches } from 'unist-util-select';
import type { NodeRenderersValidated } from '@myst-theme/providers';
import { useNodeRenderers } from '@myst-theme/providers';
import type { GenericNode } from 'myst-common';

Expand All @@ -10,17 +12,25 @@ function DefaultComponent({ node }: { node: GenericNode }) {
);
}

export function selectRenderer(renderers: NodeRenderersValidated, node: GenericNode) {
const componentRenderers = renderers[node.type] ?? renderers['DefaultComponent'];
const SpecificComponent = Object.entries(componentRenderers)
.reverse()
.find(([selector]) => selector !== 'base' && matches(selector, node))?.[1];
return SpecificComponent ?? componentRenderers.base ?? DefaultComponent;
}

export function MyST({ ast }: { ast?: GenericNode | GenericNode[] }) {
const renderers = useNodeRenderers();
if (!ast || ast.length === 0) return null;
if (!Array.isArray(ast)) {
const Component = renderers[ast.type] ?? renderers['DefaultComponent'] ?? DefaultComponent;
const Component = selectRenderer(renderers, ast);
return <Component key={ast.key} node={ast} />;
}
return (
<>
{ast?.map((node) => {
const Component = renderers[node.type] ?? DefaultComponent;
const Component = selectRenderer(renderers, node);
return <Component key={node.key} node={node} />;
})}
</>
Expand Down
55 changes: 29 additions & 26 deletions packages/myst-to-react/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { NodeRenderer } from '@myst-theme/providers';
import { mergeRenderers } from '@myst-theme/providers';
import BASIC_RENDERERS from './basic.js';
import ADMONITION_RENDERERS from './admonitions.js';
import DROPDOWN_RENDERERS from './dropdown.js';
Expand Down Expand Up @@ -30,29 +30,32 @@ export { Details } from './dropdown.js';
export { TabSet, TabItem } from './tabs.js';
export { useFetchMdast } from './crossReference.js';

export const DEFAULT_RENDERERS: Record<string, NodeRenderer> = {
...BASIC_RENDERERS,
...UNKNOWN_MYST_RENDERERS,
...IMAGE_RENDERERS,
...LINK_RENDERERS,
...CODE_RENDERERS,
...MATH_RENDERERS,
...CITE_RENDERERS,
...TAB_RENDERERS,
...IFRAME_RENDERERS,
...FOOTNOTE_RENDERERS,
...ADMONITION_RENDERERS,
...REACTIVE_RENDERERS,
...HEADING_RENDERERS,
...CROSS_REFERENCE_RENDERERS,
...DROPDOWN_RENDERERS,
...CARD_RENDERERS,
...GRID_RENDERERS,
...INLINE_EXPRESSION_RENDERERS,
...EXT_RENDERERS,
...PROOF_RENDERERS,
...EXERCISE_RENDERERS,
...ASIDE_RENDERERS,
};
export const DEFAULT_RENDERERS = mergeRenderers(
[
BASIC_RENDERERS,
UNKNOWN_MYST_RENDERERS,
IMAGE_RENDERERS,
LINK_RENDERERS,
CODE_RENDERERS,
MATH_RENDERERS,
CITE_RENDERERS,
TAB_RENDERERS,
IFRAME_RENDERERS,
FOOTNOTE_RENDERERS,
ADMONITION_RENDERERS,
REACTIVE_RENDERERS,
HEADING_RENDERERS,
CROSS_REFERENCE_RENDERERS,
DROPDOWN_RENDERERS,
CARD_RENDERERS,
GRID_RENDERERS,
INLINE_EXPRESSION_RENDERERS,
EXT_RENDERERS,
PROOF_RENDERERS,
EXERCISE_RENDERERS,
ASIDE_RENDERERS,
],
true,
);

export { MyST } from './MyST.js';
export { MyST, selectRenderer } from './MyST.js';
107 changes: 61 additions & 46 deletions packages/myst-to-react/src/links/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
} from '@heroicons/react/24/outline';
import { useLinkProvider, useSiteManifest, useBaseurl, withBaseurl } from '@myst-theme/providers';
import type { SiteManifest } from 'myst-config';
import type { NodeRenderer } from '@myst-theme/providers';
import type { NodeRenderer, NodeRenderers } from '@myst-theme/providers';
import { HoverPopover, LinkCard } from '../components/index.js';
import { WikiLink } from './wiki.js';
import { RRIDLink } from './rrid.js';
Expand Down Expand Up @@ -55,51 +55,54 @@ function InternalLink({ url, children }: { url: string; children: React.ReactNod
);
}

export const link: NodeRenderer<TransformedLink> = ({ node }) => {
const internal = node.internal ?? false;
const protocol = node.protocol;
export const WikiLinkRenderer: NodeRenderer<TransformedLink> = ({ node }) => {
return (
<WikiLink url={node.url} page={node.data?.page as string} wiki={node.data?.wiki as string}>
<MyST ast={node.children} />
</WikiLink>
);
};

switch (protocol) {
case 'wiki':
return (
<WikiLink url={node.url} page={node.data?.page as string} wiki={node.data?.wiki as string}>
<MyST ast={node.children} />
</WikiLink>
);
case 'github':
return (
<GithubLink
kind={node.data?.kind as any}
url={node.url}
org={node.data?.org as string}
repo={node.data?.repo as string}
raw={node.data?.raw as string}
file={node.data?.file as string}
from={node.data?.from as number | undefined}
to={node.data?.to as number | undefined}
issue_number={node.data?.issue_number as number | undefined}
>
<MyST ast={node.children} />
</GithubLink>
);
case 'rrid':
return <RRIDLink rrid={node.data?.rrid as string} />;
case 'ror':
return <RORLink node={node} ror={node.data?.ror as string} />;
default:
if (internal) {
return (
<InternalLink url={node.url}>
<MyST ast={node.children} />
</InternalLink>
);
}
return (
<a target="_blank" href={node.url} rel="noreferrer">
<MyST ast={node.children} />
</a>
);
export const GithubLinkRenderer: NodeRenderer<TransformedLink> = ({ node }) => {
return (
<GithubLink
kind={node.data?.kind as any}
url={node.url}
org={node.data?.org as string}
repo={node.data?.repo as string}
raw={node.data?.raw as string}
file={node.data?.file as string}
from={node.data?.from as number | undefined}
to={node.data?.to as number | undefined}
issue_number={node.data?.issue_number as number | undefined}
>
<MyST ast={node.children} />
</GithubLink>
);
};

export const RRIDLinkRenderer: NodeRenderer<TransformedLink> = ({ node }) => (
<RRIDLink rrid={node.data?.rrid as string} />
);

export const RORLinkRenderer: NodeRenderer<TransformedLink> = ({ node }) => (
<RORLink node={node} ror={node.data?.ror as string} />
);

export const SimpleLink: NodeRenderer<TransformedLink> = ({ node }) => {
const internal = node.internal ?? false;
if (internal) {
return (
<InternalLink url={node.url}>
<MyST ast={node.children} />
</InternalLink>
);
}
return (
<a target="_blank" href={node.url} rel="noreferrer">
<MyST ast={node.children} />
</a>
);
};

export const linkBlock: NodeRenderer<TransformedLink> = ({ node }) => {
Expand Down Expand Up @@ -134,8 +137,20 @@ export const linkBlock: NodeRenderer<TransformedLink> = ({ node }) => {
);
};

const LINK_RENDERERS = {
link,
const LINK_RENDERERS: NodeRenderers = {
link: {
base: SimpleLink,
// Then duplicate the renderers for protocols
'link[protocol=github]': GithubLinkRenderer,
'link[protocol=wiki]': WikiLinkRenderer,
'link[protocol=rrid]': RRIDLinkRenderer,
'link[protocol=ror]': RORLinkRenderer,
// Put the kinds last as they will match first in the future
'link[kind=github]': GithubLinkRenderer,
'link[kind=wiki]': WikiLinkRenderer,
'link[kind=rrid]': RRIDLinkRenderer,
'link[kind=ror]': RORLinkRenderer,
},
linkBlock,
};

Expand Down
4 changes: 3 additions & 1 deletion packages/providers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
"license": "MIT",
"scripts": {
"clean": "rimraf dist",
"lint": "eslint src/**/*.ts*",
"test": "vitest run",
"test:watch": "vitest watch",
"lint": "eslint \"src/**/!(*.spec).ts\" -c ./.eslintrc.cjs",
"lint:format": "prettier --check \"src/**/*.{ts,tsx,md}\"",
"dev": "npm-run-all --parallel \"build:* -- --watch\"",
"build:esm": "tsc",
Expand Down
2 changes: 1 addition & 1 deletion packages/providers/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export * from './ui.js';
export * from './site.js';
export * from './tabs.js';
export * from './xref.js';
export * from './types.js';
export * from './renderers.js';
export * from './project.js';
Loading

0 comments on commit ed5a7f6

Please sign in to comment.