Skip to content

Commit

Permalink
markdown 内の [alt](path/to/image) から生成されるhtmlタグを改造
Browse files Browse the repository at this point in the history
<figure>
  <img src="path/to/image" alt="alt" />
  <figcaption>alt</figcaption>
</figure>
  • Loading branch information
ousttrue committed Aug 6, 2024
1 parent 1278d88 commit 94e8f5e
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 3 deletions.
5 changes: 5 additions & 0 deletions docusaurus.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { themes as prismThemes } from "prism-react-renderer";
import type { Config } from "@docusaurus/types";
import type * as Preset from "@docusaurus/preset-classic";
import remark_plugin from "./remark_plugin";

const cardImage = "images/vrm/card.png";

Expand Down Expand Up @@ -40,6 +41,10 @@ const config: Config = {
docs: {
sidebarPath: "./sidebars.ts",
routeBasePath: "/",

// pugins
beforeDefaultRemarkPlugins: [remark_plugin],
// rehypePlugins: [rehype_plugin],
},
blog: {
showReadingTime: false,
Expand Down
32 changes: 31 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
"fetch-meta-tags": "^1.0.12",
"react-markdown": "^9.0.1",
"react-popper": "^2.3.0",
"typescript": "~5.5.3"
"typescript": "~5.5.3",
"unist-util-inspect": "^8.1.0",
"unist-util-remove": "^4.0.0"
},
"overrides": {
"@cmfcmf/docusaurus-search-local": {
Expand Down
54 changes: 54 additions & 0 deletions rehype_plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import unified from "unified";
import { Paragraph } from "mdast";
import { Node, Parent } from "unist";
import { VFileCompatible, VFile } from "vfile";
import { inspect } from "unist-util-inspect";
import { visit } from "unist-util-visit";
// ESM port of https://github.com/tkhquang/gridsome-remark-figure-caption
import { whitespace } from "hast-util-whitespace";
import { remove } from "unist-util-remove";
import { fromMarkdown } from "mdast-util-from-markdown";


// https://github.com/josestg/rehype-figure
function rehype_figure(
node: any, // this is mdx node
index: number,
parent: Parent | undefined
) {
if (node.type != 'mdxJsxTextElement') {
return;
}
if (node.name == 'img') {
return;
}
// if(node.tagName!="img"){
// return;
// }
console.log(inspect(node));
}

// [unified を使って Markdown を拡張する](https://zenn.dev/januswel/articles/745787422d425b01e0c1)
const nop: unified.Plugin = () => {
return (tree: Node, file: VFileCompatible) => {
// @ts-ignore
const path = file.path;
if (!path) {
console.log(inspect(file));
return;
}
if (!path.endsWith("univrm_export.md")) {
// debug
console.log(file, inspect(tree));
return;
}

// process
console.log(inspect(tree));
// @type-ignore
visit(tree, rehype_figure);
}
};
export default nop


155 changes: 155 additions & 0 deletions remark_plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// from https://github.com/Microflash/remark-figure-caption
//
// ESM port of https://github.com/tkhquang/gridsome-remark-figure-caption
import { visit } from "unist-util-visit";
import { whitespace } from "hast-util-whitespace";
import { remove } from "unist-util-remove";
import { fromMarkdown } from "mdast-util-from-markdown";

const createNodes = (imageNode, { figureClassName, imageClassName, captionClassName }) => {
let figchildren = null;
try {
figchildren = fromMarkdown(imageNode.alt).children.flatMap(node => node.children);
} catch (e) {
console.log(`figure-caption-plugin: Failed to parse image alt-text as markdown - using raw value as fallback: ${imageNode.alt}`);
figchildren = [
{
type: "text",
value: imageNode.alt,
},
];
}

const figcaption = {
type: "figcaption",
children: figchildren,
data: {
hName: "figcaption",
...getClassProp(captionClassName),
},
};

const figure = {
type: "figure",
children: [getImageNodeWithClasses(imageNode, imageClassName), figcaption],
data: {
hName: "figure",
...getClassProp(figureClassName),
},
};

return figure;
};

const hasOnlyImages = (node) => {
return node.children.every((child) => {
return child.type === "image" || whitespace(child);
});
};

const isImageNodeWithAlt = (node) => {
return node.type === "image" && Boolean(node.alt) && Boolean(node.url);
};

const isHTMLImageNode = (node) => {
return node.type === "html" && Boolean(node.alt) && /^<img\s/.test(node.value);
};

const isImageWithAlt = (node) => {
return isImageNodeWithAlt(node) || isHTMLImageNode(node);
};

const isImageWithCaption = (parent) => {
return (
parent.type === "figure" &&
parent.children.some((child) => child.type === "figcaption")
);
};

const isImageLink = (parent) => {
return (parent.type === "link");
};

const getClassProp = (className) => {
return {
...(className && {
hProperties: {
class: [className],
},
}),
};
};

const classRegex = /\sclass="(.*?)"\s/gi;

const getImageNodeWithClasses = (node, classes) => {
// Is Image type node
if (!isHTMLImageNode(node)) {
return {
...node,
data: {
...getClassProp(classes),
},
};
}

// is HTML Image node
if (!classes) {
return {
...node,
};
}

// Bruteforce adding classes for now
const hasClass = classRegex.exec(node.value);

if (!hasClass) {
return {
...node,
value: node.value.replace(/<img\s/, `<img class="${classes}" `),
};
}

return {
...node,
value: node.value.replace(classRegex, ` class="$1 ${classes}" `),
};
};

/** @type {import("unified").Plugin<[], import("mdast").Root>} */
function remarkFigureCaption(options = {}) {
return (tree) => {
// Unwrap the images inside Paragraphs
visit(tree, "paragraph", (node, index, parent) => {
if (!hasOnlyImages(node)) {
return;
}

remove(node, "text");

parent.children.splice(index, 1, ...node.children);

return index;
});

// Wrap image nodes in figure
visit(
tree,
(node) => isImageWithAlt(node),
(node, index, parent) => {
// console.log(node.type, node.name);
if (isImageWithCaption(parent) || isImageLink(parent)) {
return;
}

const figure = createNodes(node, options);

node.type = figure.type;
node.children = figure.children;
node.data = figure.data;
}
);
};
}

export default remarkFigureCaption;
24 changes: 24 additions & 0 deletions src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,27 @@
height: 28px;
width: 28px;
}

figure {
/* border: thin #c0c0c0 solid; */
border-radius: 6px;
background: #e0e0ff;
display: flex;
flex-flow: column;
padding: 5px;
/* max-width: 220px; */
width: fit-content;
margin: auto;
}

figure img {
}

figcaption {
/* background-color: #222; */
/* color: #fff; */
font: smaller sans-serif;
padding: 6px 3px 0px 3px;
text-align: center;
margin: auto;
}
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// This file is not used in compilation. It is here just for a nice editor experience.
"extends": "@docusaurus/tsconfig",
"compilerOptions": {
"baseUrl": "."
"baseUrl": ".",
"noUnusedParameters": false
}
}

0 comments on commit 94e8f5e

Please sign in to comment.