Skip to content

Commit

Permalink
Merge pull request #240 from jermspeaks/feature/asides
Browse files Browse the repository at this point in the history
Feature/asides
  • Loading branch information
jermspeaks authored Nov 30, 2023
2 parents 0d7e323 + cdeae8d commit bb4375b
Show file tree
Hide file tree
Showing 7 changed files with 1,139 additions and 44 deletions.
20 changes: 13 additions & 7 deletions astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { defineConfig } from "astro/config";
import AutoImport from "astro-auto-import";
import mdx from "@astrojs/mdx";
// import netlify from "@astrojs/netlify/static";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
import rehypeExternalLinks from "rehype-external-links";
import rehypeSlug from "rehype-slug";
import sitemap from "@astrojs/sitemap";
import svelte from "@astrojs/svelte";
import tailwind from "@astrojs/tailwind";
// import netlify from "@astrojs/netlify/static";

import { asideAutoImport, astroAsides } from "./integrations/astro-asides";
import rehypeAutolinkConfig from "./plugins/rehype-autolink-heading-config";
import rehypeExternalLinkConfig from "./plugins/rehype-external-link-config";
import rehypeFigure from "./plugins/rehype-figure";
Expand All @@ -26,6 +28,16 @@ export default defineConfig({
remarkPlugins: [remarkReadingTime],
},
integrations: [
AutoImport({
imports: [asideAutoImport],
}),
sitemap(),
svelte(),
tailwind({
configFile: "./tailwind.config.cjs",
applyBaseStyles: true,
}),
astroAsides(),
mdx({
rehypePlugins: [
rehypeSlug,
Expand All @@ -35,12 +47,6 @@ export default defineConfig({
],
remarkPlugins: [remarkReadingTime],
}),
sitemap(),
svelte(),
tailwind({
configFile: "./tailwind.config.cjs",
applyBaseStyles: true,
}),
],
// NOTE: Once we need redirects, we can use the adaptor
// adapter: netlify(),
Expand Down
91 changes: 91 additions & 0 deletions integrations/astro-asides.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import type { AstroIntegration } from "astro";
import type * as mdast from "mdast";
import remarkDirective from "remark-directive";
import type * as unified from "unified";
import { remove } from "unist-util-remove";
import { visit } from "unist-util-visit";
import { makeComponentNode } from "./utils/makeComponentNode";

const AsideTagname = "AutoImportedAside";

export const asideAutoImport: Record<string, [string, string][]> = {
"../src/components/Aside.astro": [["default", AsideTagname]],
};

/**
* remark plugin that converts blocks delimited with `:::` into instances of
* the `<Aside>` component. Depends on the `remark-directive` module for the
* core parsing logic.
*
* For example, this Markdown
*
* ```md
* :::tip[Did you know?]
* Astro helps you build faster websites with “Islands Architecture”.
* :::
* ```
*
* will produce this output
*
* ```astro
* <Aside type="tip" title="Did you know?">
* <p>Astro helps you build faster websites with “Islands Architecture”.</p>
* </Aside>
* ```
*/
function remarkAsides(): unified.Plugin<[], mdast.Root> {
const variants = new Set(["note", "tip", "caution", "danger"]);

const transformer: unified.Transformer<mdast.Root> = (tree) => {
// @ts-expect-error Possibly infinite type instantiation we can’t do anything about.
visit(tree, (node, index, parent) => {
if (!parent || index === null || node.type !== "containerDirective")
return;
const type = node.name;
if (!variants.has(type)) return;

// remark-directive converts a container’s “label” to a paragraph in
// its children, but we want to pass it as the title prop to <Aside>, so
// we iterate over the children, find a directive label, store it for the
// title prop, and remove the paragraph from children.
let title: string | undefined;
remove(node, (child) => {
if (child.data?.directiveLabel) {
if ("children" in child && "value" in child.children[0]) {
title = child.children[0].value;
}
return true;
}
});

// Replace this node with the aside component it represents.
parent.children[index] = makeComponentNode(
AsideTagname,
{ attributes: { type, title } },
...node.children
);
});
};

return function attacher() {
return transformer;
};
}

/**
* Astro integration that sets up the remark plugin and auto-imports the `<Aside>` component everywhere.
*/
export function astroAsides(): AstroIntegration {
return {
name: "@astrojs/asides",
hooks: {
"astro:config:setup": ({ updateConfig }) => {
updateConfig({
markdown: {
remarkPlugins: [remarkDirective, remarkAsides()],
},
});
},
},
};
}
33 changes: 33 additions & 0 deletions integrations/utils/makeComponentNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { BlockContent } from "mdast";
import type { MdxJsxAttribute, MdxJsxFlowElement } from "mdast-util-mdx-jsx";

interface NodeProps {
attributes?: Record<string, string | boolean | number | undefined | null>;
}

/**
* Create AST node for a custom component injection.
*
* @example
* makeComponentNode('MyComponent', { prop: 'val' }, h('p', 'Paragraph inside component'))
*
*/
export function makeComponentNode(
name: string,
{ attributes = {} }: NodeProps = {},
...children: BlockContent[]
): MdxJsxFlowElement {
return {
type: "mdxJsxFlowElement",
name,
attributes: Object.entries(attributes)
// Filter out non-truthy attributes to avoid empty attrs being parsed as `true`.
.filter(([_k, v]) => v !== false && Boolean(v))
.map(([name, value]) => ({
type: "mdxJsxAttribute",
name,
value: value as MdxJsxAttribute["value"],
})),
children,
};
}
Loading

0 comments on commit bb4375b

Please sign in to comment.