diff --git a/src/index.ts b/src/index.ts
index d7c3556..a2d2f5c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -5,127 +5,127 @@ import type { LeafDirective } from "mdast-util-directive";
import type { Plugin } from "unified";
import { visit } from "unist-util-visit";
import {
- isContainerDirective,
- isImage,
- isLink,
- isParagraph,
- isText,
+ isContainerDirective,
+ isImage,
+ isLink,
+ isParagraph,
+ isText,
} from "./utils.js";
export interface Config {
- customHTMLTags: {
- enabled: boolean;
- };
- imageContainerClass: string;
- contentContainerClass: string;
+ customHTMLTags: {
+ enabled: boolean;
+ };
+ imageContainerClass: string;
+ contentContainerClass: string;
}
export const defaultConfig: Config = {
- customHTMLTags: {
- enabled: false,
- },
- imageContainerClass: "image-container",
- contentContainerClass: "content-container",
+ customHTMLTags: {
+ enabled: false,
+ },
+ imageContainerClass: "image-container",
+ contentContainerClass: "content-container",
};
const remarkCard: Plugin<[Config?], Root> = (config = defaultConfig) => {
- return (tree) => {
- visit(tree, isContainerDirective, (node) => {
- if (node.name !== "card-grid") return;
- if (node.children.length === 0) return;
-
- node.data = {
- ...node.data,
- hName: config.customHTMLTags.enabled ? "card-grid" : "div",
- hProperties: {
- ...node.attributes,
- },
- };
- });
-
- visit(tree, isContainerDirective, (node) => {
- if (node.name !== "card") return;
- if (node.children.length === 0) return;
-
- const [firstNode, secondNode, ..._restNodes] = node.children;
- if (!isParagraph(firstNode)) return;
- if (firstNode.children.length === 0) return;
-
- let cardImageOrLink: PhrasingContent;
- let cardContent: PhrasingContent[];
- let cardLabel = "";
-
- const imageContainer: LeafDirective = {
- type: "leafDirective",
- name: "image-container",
- data: {
- hName: "div",
- hProperties: {
- className: config.imageContainerClass,
- },
- },
- children: [],
- };
-
- const contentContainer: LeafDirective = {
- type: "leafDirective",
- name: "content-container",
- data: {
- hName: "div",
- hProperties: {
- className: config.contentContainerClass,
- },
- },
- children: [],
- };
-
- if (firstNode.data?.directiveLabel === true) {
- if (!isText(firstNode.children[0])) return;
-
- cardLabel = firstNode.children[0].value;
-
- if (!isParagraph(secondNode)) return;
- const [imageOrLink, ...restContent] = secondNode.children;
- cardImageOrLink = imageOrLink;
- cardContent = restContent;
- } else {
- const [imageOrLink, ...restContent] = firstNode.children;
- cardImageOrLink = imageOrLink;
- cardContent = restContent;
- }
-
- if (isImage(cardImageOrLink)) {
- cardImageOrLink.alt = cardImageOrLink.alt || cardLabel;
- } else if (isLink(cardImageOrLink)) {
- const cardImage = cardImageOrLink.children[0];
- if (!isImage(cardImage)) return;
-
- cardImage.alt = cardImage.alt || cardLabel;
- } else {
- return;
- }
-
- imageContainer.children.push(cardImageOrLink);
-
- for (const contentElem of cardContent) {
- contentContainer.children.push(contentElem);
- }
-
- node.data = {
- ...node.data,
- hName: config.customHTMLTags.enabled ? "card" : "div",
- hProperties: {
- ...node.attributes,
- },
- };
- node.children.splice(
- 0,
- Number.POSITIVE_INFINITY,
- imageContainer,
- contentContainer
- );
- });
- };
+ return (tree) => {
+ visit(tree, isContainerDirective, (node) => {
+ if (node.name !== "card-grid") return;
+ if (node.children.length === 0) return;
+
+ node.data = {
+ ...node.data,
+ hName: config.customHTMLTags.enabled ? "card-grid" : "div",
+ hProperties: {
+ ...node.attributes,
+ },
+ };
+ });
+
+ visit(tree, isContainerDirective, (node) => {
+ if (node.name !== "card") return;
+ if (node.children.length === 0) return;
+
+ const [firstNode, secondNode, ..._restNodes] = node.children;
+ if (!isParagraph(firstNode)) return;
+ if (firstNode.children.length === 0) return;
+
+ let cardImageOrLink: PhrasingContent;
+ let cardContent: PhrasingContent[];
+ let cardLabel = "";
+
+ const imageContainer: LeafDirective = {
+ type: "leafDirective",
+ name: "image-container",
+ data: {
+ hName: "div",
+ hProperties: {
+ className: config.imageContainerClass,
+ },
+ },
+ children: [],
+ };
+
+ const contentContainer: LeafDirective = {
+ type: "leafDirective",
+ name: "content-container",
+ data: {
+ hName: "div",
+ hProperties: {
+ className: config.contentContainerClass,
+ },
+ },
+ children: [],
+ };
+
+ if (firstNode.data?.directiveLabel === true) {
+ if (!isText(firstNode.children[0])) return;
+
+ cardLabel = firstNode.children[0].value;
+
+ if (!isParagraph(secondNode)) return;
+ const [imageOrLink, ...restContent] = secondNode.children;
+ cardImageOrLink = imageOrLink;
+ cardContent = restContent;
+ } else {
+ const [imageOrLink, ...restContent] = firstNode.children;
+ cardImageOrLink = imageOrLink;
+ cardContent = restContent;
+ }
+
+ if (isImage(cardImageOrLink)) {
+ cardImageOrLink.alt = cardImageOrLink.alt || cardLabel;
+ } else if (isLink(cardImageOrLink)) {
+ const cardImage = cardImageOrLink.children[0];
+ if (!isImage(cardImage)) return;
+
+ cardImage.alt = cardImage.alt || cardLabel;
+ } else {
+ return;
+ }
+
+ imageContainer.children.push(cardImageOrLink);
+
+ for (const contentElem of cardContent) {
+ contentContainer.children.push(contentElem);
+ }
+
+ node.data = {
+ ...node.data,
+ hName: config.customHTMLTags.enabled ? "card" : "div",
+ hProperties: {
+ ...node.attributes,
+ },
+ };
+ node.children.splice(
+ 0,
+ Number.POSITIVE_INFINITY,
+ imageContainer,
+ contentContainer,
+ );
+ });
+ };
};
export default remarkCard;
diff --git a/test/index.test.ts b/test/index.test.ts
index 3da31d6..0386ef0 100644
--- a/test/index.test.ts
+++ b/test/index.test.ts
@@ -9,49 +9,49 @@ import { unified } from "unified";
import remarkCard, { type Config, defaultConfig } from "../src/index.js";
const normalizeHtml = (html: string) => {
- return html.replace(/[\n\s]*(<)|>([\n\s]*)/g, (_match, p1, _p2) =>
- p1 ? "<" : ">"
- );
+ return html.replace(/[\n\s]*(<)|>([\n\s]*)/g, (_match, p1, _p2) =>
+ p1 ? "<" : ">",
+ );
};
const parseMarkdown = mock(
- async (markdown: string, options: Config = defaultConfig, debug = false) => {
- const remarkProcessor = unified()
- .use(remarkParse)
- .use(remarkDirective)
- .use(remarkCard, options)
- .use(remarkRehype)
- .use(rehypeStringify);
-
- if (debug) {
- const remarkOutput = await remarkProcessor.run(
- remarkProcessor.parse(markdown)
- );
- console.log("Remark output:", JSON.stringify(remarkOutput, null, 2));
- }
-
- const output = String(await remarkProcessor.process(markdown));
-
- if (debug) {
- console.log(
- `HTML output:
- ${normalizeHtml(output)}`
- );
- }
-
- return output;
- }
+ async (markdown: string, options: Config = defaultConfig, debug = false) => {
+ const remarkProcessor = unified()
+ .use(remarkParse)
+ .use(remarkDirective)
+ .use(remarkCard, options)
+ .use(remarkRehype)
+ .use(rehypeStringify);
+
+ if (debug) {
+ const remarkOutput = await remarkProcessor.run(
+ remarkProcessor.parse(markdown),
+ );
+ console.log("Remark output:", JSON.stringify(remarkOutput, null, 2));
+ }
+
+ const output = String(await remarkProcessor.process(markdown));
+
+ if (debug) {
+ console.log(
+ `HTML output:
+ ${normalizeHtml(output)}`,
+ );
+ }
+
+ return output;
+ },
);
describe("Test the basic usage of card", () => {
- test("Card with single-line text & image", async () => {
- const input = `
+ test("Card with single-line text & image", async () => {
+ const input = `
:::card
![image alt](https://xxxxx.xxx/yyy.jpg)
Single-line text
:::
`;
- const output = `
+ const output = `
@@ -60,19 +60,19 @@ describe("Test the basic usage of card", () => {
`;
- const html = await parseMarkdown(input);
+ const html = await parseMarkdown(input);
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
- test("Card with class attributes and single-line text & image", async () => {
- const input = `
+ test("Card with class attributes and single-line text & image", async () => {
+ const input = `
:::card{.card.solid-border}
![image alt](https://xxxxx.xxx/yyy.jpg)
Single-line text
:::
`;
- const output = `
+ const output = `
@@ -81,19 +81,19 @@ describe("Test the basic usage of card", () => {
`;
- const html = await parseMarkdown(input);
+ const html = await parseMarkdown(input);
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
- test("Card with a directive label and single-line text & image with no alt text", async () => {
- const input = `
+ test("Card with a directive label and single-line text & image with no alt text", async () => {
+ const input = `
:::card[card alt]
![](https://xxxxx.xxx/yyy.jpg)
Single-line text
:::
`;
- const output = `
+ const output = `
@@ -102,19 +102,19 @@ describe("Test the basic usage of card", () => {
`;
- const html = await parseMarkdown(input);
+ const html = await parseMarkdown(input);
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
- test("Card with a directive label and single-line text & image", async () => {
- const input = `
+ test("Card with a directive label and single-line text & image", async () => {
+ const input = `
:::card[card alt]
![image alt](https://xxxxx.xxx/yyy.jpg)
Single-line text
:::
`;
- const output = `
+ const output = `
@@ -123,13 +123,13 @@ describe("Test the basic usage of card", () => {
`;
- const html = await parseMarkdown(input);
+ const html = await parseMarkdown(input);
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
- test("Card with multiple-line text & image", async () => {
- const input = `
+ test("Card with multiple-line text & image", async () => {
+ const input = `
:::card
![image alt](https://xxxxx.xxx/yyy.jpg)
Multiple
@@ -137,7 +137,7 @@ describe("Test the basic usage of card", () => {
Text
:::
`;
- const output = `
+ const output = `
@@ -147,19 +147,19 @@ describe("Test the basic usage of card", () => {
`;
- const html = await parseMarkdown(input);
+ const html = await parseMarkdown(input);
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
- test("Card with strong text & image", async () => {
- const input = `
+ test("Card with strong text & image", async () => {
+ const input = `
:::card
![image alt](https://xxxxx.xxx/yyy.jpg)
**Strong text**
:::
`;
- const output = `
+ const output = `
@@ -170,19 +170,19 @@ describe("Test the basic usage of card", () => {
`;
- const html = await parseMarkdown(input);
+ const html = await parseMarkdown(input);
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
- test("Card with link text & image", async () => {
- const input = `
+ test("Card with link text & image", async () => {
+ const input = `
:::card
![image alt](https://xxxxx.xxx/yyy.jpg)
[Link text](https://xxxxx.xxx/)
:::
`;
- const output = `
+ const output = `
@@ -193,19 +193,19 @@ describe("Test the basic usage of card", () => {
`;
- const html = await parseMarkdown(input);
+ const html = await parseMarkdown(input);
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
- test("Card with emphasized text & image", async () => {
- const input = `
+ test("Card with emphasized text & image", async () => {
+ const input = `
:::card
![image alt](https://xxxxx.xxx/yyy.jpg)
_Emphasized text_
:::
`;
- const output = `
+ const output = `
@@ -216,19 +216,19 @@ describe("Test the basic usage of card", () => {
`;
- const html = await parseMarkdown(input);
+ const html = await parseMarkdown(input);
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
- test("Card with image & image", async () => {
- const input = `
+ test("Card with image & image", async () => {
+ const input = `
:::card
![image alt](https://xxxxx.xxx/yyy.jpg)
![image alt 2](https://xxxxx.xxx/zzz.jpg)
:::
`;
- const output = `
+ const output = `
@@ -239,19 +239,19 @@ describe("Test the basic usage of card", () => {
`;
- const html = await parseMarkdown(input);
+ const html = await parseMarkdown(input);
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
- test("Card with single-line text & image link", async () => {
- const input = `
+ test("Card with single-line text & image link", async () => {
+ const input = `
:::card
[![image alt](https://xxxxx.xxx/yyy.jpg)](https://xxxxx.xxx/)
Single-line text
:::
`;
- const output = `
+ const output = `
`;
- const html = await parseMarkdown(input);
-
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
-
- // test("Nested cards", async () => {
- // const input = `
- // :::::card{.parent}
- // ![parent card](https://xxxxx.xxx/xxx.jpg)
- // Parent
- // ::::card{.child}
- // ![child card](https://xxxxx.xxx/yyy.jpg)
- // Child
- // :::card{.grandchild}
- // ![grandchild card](https://xxxxx.xxx/zzz.jpg)
- // Grandchild
- // :::
- // ::::
- // :::::
- // `;
- // const output = `
- //
- //
- //
- //
- //
- // Parent
- //
- //
- //
- //
- //
- // Child
- //
- //
- //
- //
- //
- // Grandchild
- //
- //
- //
- //
- //
- //
- // `;
-
- // const html = await parseMarkdown(input);
-
- // expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- // });
-
- test("Card with no content", async () => {
- const input = `
+ const html = await parseMarkdown(input);
+
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
+
+ // test("Nested cards", async () => {
+ // const input = `
+ // :::::card{.parent}
+ // ![parent card](https://xxxxx.xxx/xxx.jpg)
+ // Parent
+ // ::::card{.child}
+ // ![child card](https://xxxxx.xxx/yyy.jpg)
+ // Child
+ // :::card{.grandchild}
+ // ![grandchild card](https://xxxxx.xxx/zzz.jpg)
+ // Grandchild
+ // :::
+ // ::::
+ // :::::
+ // `;
+ // const output = `
+ //
+ //
+ //
+ //
+ //
+ // Parent
+ //
+ //
+ //
+ //
+ //
+ // Child
+ //
+ //
+ //
+ //
+ //
+ // Grandchild
+ //
+ //
+ //
+ //
+ //
+ //
+ // `;
+
+ // const html = await parseMarkdown(input);
+
+ // expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ // });
+
+ test("Card with no content", async () => {
+ const input = `
:::card
:::
`;
- const output = `
+ const output = `
`;
- const html = await parseMarkdown(input);
+ const html = await parseMarkdown(input);
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
- test("Custom Tag Card with single-line text & image", async () => {
- const input = `
+ test("Custom Tag Card with single-line text & image", async () => {
+ const input = `
:::card
![image alt](https://xxxxx.xxx/yyy.jpg)
Single-line text
:::
`;
- const output = `
+ const output = `
@@ -344,19 +344,19 @@ describe("Test the basic usage of card", () => {
`;
- const html = await parseMarkdown(input, {
- customHTMLTags: { enabled: true },
- imageContainerClass: "image-container",
- contentContainerClass: "content-container",
- });
+ const html = await parseMarkdown(input, {
+ customHTMLTags: { enabled: true },
+ imageContainerClass: "image-container",
+ contentContainerClass: "content-container",
+ });
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
});
describe("Test the basic usage of card-grid", () => {
- test("Card grid & some cards", async () => {
- const input = `
+ test("Card grid & some cards", async () => {
+ const input = `
::::card-grid
:::card{.card-1}
![card 1](https://xxxxx.xxx/yyy.jpg)
@@ -372,7 +372,7 @@ describe("Test the basic usage of card-grid", () => {
:::
::::
`;
- const output = `
+ const output = `
@@ -395,13 +395,13 @@ describe("Test the basic usage of card-grid", () => {
`;
- const html = await parseMarkdown(input);
+ const html = await parseMarkdown(input);
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
- test("Card grid with class attributes & some cards", async () => {
- const input = `
+ test("Card grid with class attributes & some cards", async () => {
+ const input = `
::::card-grid{.card-grid}
:::card{.card-1}
![card 1](https://xxxxx.xxx/yyy.jpg)
@@ -417,7 +417,7 @@ describe("Test the basic usage of card-grid", () => {
:::
::::
`;
- const output = `
+ const output = `
@@ -440,13 +440,13 @@ describe("Test the basic usage of card-grid", () => {
`;
- const html = await parseMarkdown(input);
+ const html = await parseMarkdown(input);
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
- test("Card grid with a directive label & some cards", async () => {
- const input = `
+ test("Card grid with a directive label & some cards", async () => {
+ const input = `
::::card-grid[Card grid label]
:::card{.card-1}
![card 1](https://xxxxx.xxx/yyy.jpg)
@@ -462,7 +462,7 @@ describe("Test the basic usage of card-grid", () => {
:::
::::
`;
- const output = `
+ const output = `
Card grid label
@@ -486,27 +486,27 @@ describe("Test the basic usage of card-grid", () => {
`;
- const html = await parseMarkdown(input);
+ const html = await parseMarkdown(input);
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
- test("Card grid & no card", async () => {
- const input = `
+ test("Card grid & no card", async () => {
+ const input = `
::::card-grid
::::
`;
- const output = `
+ const output = `
`;
- const html = await parseMarkdown(input);
+ const html = await parseMarkdown(input);
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
- test("Custom Tag Card grid & some cards", async () => {
- const input = `
+ test("Custom Tag Card grid & some cards", async () => {
+ const input = `
::::card-grid
:::card{.card-1}
![card 1](https://xxxxx.xxx/yyy.jpg)
@@ -522,7 +522,7 @@ describe("Test the basic usage of card-grid", () => {
:::
::::
`;
- const output = `
+ const output = `
@@ -545,12 +545,12 @@ describe("Test the basic usage of card-grid", () => {
`;
- const html = await parseMarkdown(input, {
- customHTMLTags: { enabled: true },
- imageContainerClass: "image-container",
- contentContainerClass: "content-container",
- });
+ const html = await parseMarkdown(input, {
+ customHTMLTags: { enabled: true },
+ imageContainerClass: "image-container",
+ contentContainerClass: "content-container",
+ });
- expect(normalizeHtml(html)).toBe(normalizeHtml(output));
- });
+ expect(normalizeHtml(html)).toBe(normalizeHtml(output));
+ });
});