From 241d2e897382f3eca59582ddea58d5d638611e51 Mon Sep 17 00:00:00 2001 From: Deep Date: Thu, 3 Oct 2024 19:20:24 -0400 Subject: [PATCH 01/15] feat: wip add codeblock line highlight --- examples/app-router/app/globals.css | 6 + examples/app-router/app/page.tsx | 2 +- .../rehype/rehypeSyntaxHighlighting.ts | 158 +++++++++++------- src/styles/prism.css | 32 +++- 4 files changed, 136 insertions(+), 62 deletions(-) diff --git a/examples/app-router/app/globals.css b/examples/app-router/app/globals.css index b5c61c9..60cf767 100644 --- a/examples/app-router/app/globals.css +++ b/examples/app-router/app/globals.css @@ -1,3 +1,9 @@ @tailwind base; @tailwind components; @tailwind utilities; + +@layer base { + :root { + --codeblock-line-highlight: 166 87% 31%; + } +} diff --git a/examples/app-router/app/page.tsx b/examples/app-router/app/page.tsx index f7d88a5..178952e 100644 --- a/examples/app-router/app/page.tsx +++ b/examples/app-router/app/page.tsx @@ -2,7 +2,7 @@ import { getCompiledServerMdx } from "@mintlify/mdx"; export default async function Home() { const fileContentResponse = await fetch( - "https://raw.githubusercontent.com/mintlify/starter/main/essentials/code.mdx" + "https://gist.githubusercontent.com/deep93333/eb0c54f5b7496c6d17875b0ec4a55edd/raw/bca51b41e940393d50fafc78f14506130fe60b2c/code.mdx" ); const fileContentData = await fileContentResponse.text(); diff --git a/src/plugins/rehype/rehypeSyntaxHighlighting.ts b/src/plugins/rehype/rehypeSyntaxHighlighting.ts index 66a055c..227ee3e 100644 --- a/src/plugins/rehype/rehypeSyntaxHighlighting.ts +++ b/src/plugins/rehype/rehypeSyntaxHighlighting.ts @@ -1,77 +1,92 @@ -import { Node, toString } from "hast-util-to-string"; +import { toString } from "hast-util-to-string"; +import { RefractorElement } from "refractor"; import { refractor } from "refractor/lib/all.js"; -import { visit } from "unist-util-visit"; import { Parent } from "unist"; +import { visit } from "unist-util-visit"; -export const rehypeSyntaxHighlighting = (options: { +export type RehypeSyntaxHighlightingOptions = { ignoreMissing?: boolean; alias?: Record; -}) => { +}; + +export type TreeNode = RefractorElement & { + type: "element" | "text"; + properties: { + className?: string[]; + }; +}; + +export type TreeParent = Parent & { + tagName: string; + properties: { + className?: string[]; + }; +}; + +export const rehypeSyntaxHighlighting = ( + options: RehypeSyntaxHighlightingOptions +) => { if (options.alias) { refractor.alias(options.alias); } return (tree: Parent) => { - visit( - tree, - "element", - ( - node: Node & { - tagName: string; - children: Node[]; - properties: { - className?: string[]; - }; - }, - _index, - parent?: { - tagName: string; - properties: { - className?: string[]; - }; - } - ) => { - if (!parent || parent.tagName !== "pre" || node.tagName !== "code") { - return; - } + visit(tree, "element", (node: TreeNode, _index, parent?: TreeParent) => { + if (!parent || parent.tagName !== "pre" || node.tagName !== "code") { + return; + } - const lang = getLanguage(node); + const lang = getLanguage(node); + const linesToHighlight = getLineHighlight(node); - if (lang === null) { - return; - } + if (lang === null) { + return; + } + + let result; + try { + parent.properties.className = ( + parent.properties.className || [] + ).concat("language-" + lang); + const code = toString(node); - let result; - try { - parent.properties.className = ( - parent.properties.className || [] - ).concat("language-" + lang); - result = refractor.highlight(toString(node), lang); - node.children = result.children; - } catch (err) { - if ( - options.ignoreMissing && - /Unknown language/.test((err as Error).message) - ) { - return; - } - throw err; + const nodes = code + .split("\n") + .reduce((acc: RefractorElement[], line: string, index: number) => { + const node: TreeNode = { + type: "element", + tagName: "span", + properties: { + className: [ + "line", + line.trim() !== "" && linesToHighlight?.includes(index) + ? "line-highlight" + : "", + ], + }, + children: refractor.highlight(line, lang).children, + }; + acc.push(node); + acc.push({ type: "text", tagName: "code", value: "\n" } as any); + return acc; + }, []); + + node.children = nodes; + } catch (err) { + if ( + options.ignoreMissing && + /Unknown language/.test((err as Error).message) + ) { + return; } + throw err; } - ); + }); }; }; -function getLanguage( - node: Node & { - tagName: string; - children: Node[]; - properties: { - className?: string[]; - }; - } -) { - const className = node.properties.className || []; +function getLanguage(node: TreeNode) { + const className = node.properties?.className || []; for (const classListItem of className) { if (classListItem.slice(0, 9) === "language-") { @@ -81,3 +96,34 @@ function getLanguage( return null; } + +function getLineHighlight(node: TreeNode) { + const meta = node.data?.meta as string | undefined; + if (!meta) return null; + + const match = meta.match(/^\{(.+)\}$/); + if (!match) return null; + + const content = match[1]?.trim(); + + const parts = content?.split(","); + const lineNumbers: number[] = []; + + parts?.forEach((part) => { + const range = part.split("-").map((num) => parseInt(num, 10)); + if (!range?.[0] || isNaN(range[0])) return; + lineNumbers.push(Math.max(0, range[0] - 1)); + + if (!range?.[1] || isNaN(range[1])) return; + + const start = Math.min(range[0], range[1]); + const end = Math.max(range[0], range[1]); + for (let i = start; i <= end; i++) { + lineNumbers.push(Math.max(0, i - 1)); + } + }); + + return lineNumbers.length > 0 + ? [...new Set(lineNumbers)].sort((a, b) => a - b) + : null; +} diff --git a/src/styles/prism.css b/src/styles/prism.css index d053306..a4d98b7 100644 --- a/src/styles/prism.css +++ b/src/styles/prism.css @@ -261,17 +261,39 @@ code.language-toml .table { /********************************************************* * Line highlighting */ -pre[class*='language-'] > code[class*='language-'] { - position: relative; - z-index: 1; +pre[class*='language-'] { + padding: 0.5rem; + box-sizing: border-box; + overflow-x: auto; + width: 100%; +} + +pre[class*='language-'] .line { + padding-left: 0.5rem; + padding-right: 0.5rem; + white-space: pre-wrap; + word-break: break-word; } .line-highlight.line-highlight { - background: #f7ebc6; - box-shadow: inset 5px 0 0 #f7d87c; + background: hsl(var(--codeblock-line-highlight) / 0.3); + width: 100%; + /* padding: 0.2rem 0; */ + display: inline-block; + box-shadow: inset 2px 0 0 hsl(var(--codeblock-line-highlight)/1); z-index: 0; } +.line-highlight:not(.line-highlight + .line-highlight) { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} + +.line-highlight:not(:has(+ .line-highlight)) { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; +} + pre[class^='language-diff-'] { display: flex; padding-left: 2.25rem; From 9e98f4b10a6f5e59955d1a714d6cf1eb4f431c7b Mon Sep 17 00:00:00 2001 From: Deep Date: Mon, 7 Oct 2024 20:09:27 -0400 Subject: [PATCH 02/15] feat: add support for codeblock line highlight --- examples/app-router/app/globals.css | 2 +- examples/app-router/app/page.tsx | 8 +- .../app-router/examples/highlight-example.mdx | 48 +++++++ .../examples/highlight-example.mdx | 48 +++++++ examples/pages-router/next.config.js | 2 +- examples/pages-router/pages/index.tsx | 10 +- package.json | 25 +++- .../rehype/rehypeSyntaxHighlighting.ts | 120 ++++++++++-------- src/styles/prism.css | 42 +++--- 9 files changed, 211 insertions(+), 94 deletions(-) create mode 100644 examples/app-router/examples/highlight-example.mdx create mode 100644 examples/pages-router/examples/highlight-example.mdx diff --git a/examples/app-router/app/globals.css b/examples/app-router/app/globals.css index 60cf767..bea798b 100644 --- a/examples/app-router/app/globals.css +++ b/examples/app-router/app/globals.css @@ -4,6 +4,6 @@ @layer base { :root { - --codeblock-line-highlight: 166 87% 31%; + --primary-light: 85 215 153; } } diff --git a/examples/app-router/app/page.tsx b/examples/app-router/app/page.tsx index 178952e..9ba8a52 100644 --- a/examples/app-router/app/page.tsx +++ b/examples/app-router/app/page.tsx @@ -1,15 +1,13 @@ import { getCompiledServerMdx } from "@mintlify/mdx"; +import { promises as fs } from 'fs'; export default async function Home() { - const fileContentResponse = await fetch( - "https://gist.githubusercontent.com/deep93333/eb0c54f5b7496c6d17875b0ec4a55edd/raw/bca51b41e940393d50fafc78f14506130fe60b2c/code.mdx" - ); - const fileContentData = await fileContentResponse.text(); + const data = await fs.readFile(process.cwd() + '/examples/highlight-example.mdx'); const { content, frontmatter } = await getCompiledServerMdx<{ title: string; }>({ - source: fileContentData, + source: data.toString(), }); return ( diff --git a/examples/app-router/examples/highlight-example.mdx b/examples/app-router/examples/highlight-example.mdx new file mode 100644 index 0000000..d77c687 --- /dev/null +++ b/examples/app-router/examples/highlight-example.mdx @@ -0,0 +1,48 @@ +# Syntax Highlighting Example + +This MDX file demonstrates syntax highlighting for various programming languages. + +## JavaScript + +```js index.js {2} +console.log("Hello, world!"); +function add(a, b) { + return a + b; +} + +function subtract(a, b) { + return a - b; +} +``` + +## Python + +```python index.py {1-2,4-5} +def add(a, b): + return a + b + +def subtract(a, b): + return a - b +``` + +## Java + +```java {3} +public class Main { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } +} +``` + +## C# + +```csharp index.cs {1,3-4} +public class Program +{ + public static void Main(string[] args) + { + Console.WriteLine("Hello, World!"); + } +} +``` diff --git a/examples/pages-router/examples/highlight-example.mdx b/examples/pages-router/examples/highlight-example.mdx new file mode 100644 index 0000000..d77c687 --- /dev/null +++ b/examples/pages-router/examples/highlight-example.mdx @@ -0,0 +1,48 @@ +# Syntax Highlighting Example + +This MDX file demonstrates syntax highlighting for various programming languages. + +## JavaScript + +```js index.js {2} +console.log("Hello, world!"); +function add(a, b) { + return a + b; +} + +function subtract(a, b) { + return a - b; +} +``` + +## Python + +```python index.py {1-2,4-5} +def add(a, b): + return a + b + +def subtract(a, b): + return a - b +``` + +## Java + +```java {3} +public class Main { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } +} +``` + +## C# + +```csharp index.cs {1,3-4} +public class Program +{ + public static void Main(string[] args) + { + Console.WriteLine("Hello, World!"); + } +} +``` diff --git a/examples/pages-router/next.config.js b/examples/pages-router/next.config.js index a843cbe..5ec3d6e 100644 --- a/examples/pages-router/next.config.js +++ b/examples/pages-router/next.config.js @@ -1,6 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - reactStrictMode: true, + transpilePackages: ["next-mdx-remote"] } module.exports = nextConfig diff --git a/examples/pages-router/pages/index.tsx b/examples/pages-router/pages/index.tsx index 5783833..6008fbd 100644 --- a/examples/pages-router/pages/index.tsx +++ b/examples/pages-router/pages/index.tsx @@ -1,15 +1,13 @@ -import { MDXComponent, getCompiledMdx } from "@mintlify/mdx"; import type { MDXCompiledResult } from "@mintlify/mdx"; +import { MDXComponent, getCompiledMdx } from "@mintlify/mdx"; +import { promises as fs } from "fs"; import type { GetStaticProps, InferGetStaticPropsType } from "next"; export const getStaticProps = (async () => { - const fileContentResponse = await fetch( - "https://raw.githubusercontent.com/mintlify/starter/main/essentials/code.mdx" - ); - const fileContentData = await fileContentResponse.text(); + const data = await fs.readFile(process.cwd() + '/examples/highlight-example.mdx'); const mdxSource = await getCompiledMdx({ - source: fileContentData, + source: data.toString(), }); return { diff --git a/package.json b/package.json index 9774f7f..54f6257 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mintlify/mdx", - "version": "0.0.46", + "version": "0.0.48", "description": "Markdown parser from Mintlify", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -36,23 +36,36 @@ "@mintlify/eslint-config-typescript": "^1.0.9", "@mintlify/ts-config": "^2.0.2", "@tsconfig/recommended": "1.x", + "@types/react": "^18.3.11", + "@types/react-dom": "^18.3.0", "@typescript-eslint/eslint-plugin": "6.x", "@typescript-eslint/parser": "6.x", "eslint": "8.x", "eslint-config-prettier": "8.x", "eslint-plugin-unused-imports": "^3.x", "prettier": "^3.1.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", "rimraf": "^5.0.1", "typescript": "^5.2.2" }, + "peerDependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, "dependencies": { + "@mdx-js/mdx": "^3.0.1", + "@mdx-js/react": "^3.0.1", + "@types/hast": "^3.0.4", + "@types/unist": "^3.0.3", "hast-util-to-string": "^2.0.0", - "next-mdx-remote": "^4.4.1", + "next-mdx-remote": "^5.0.0", "refractor": "^4.8.0", - "rehype-katex": "^6.0.3", - "remark-gfm": "^3.0.1", - "remark-math": "^5.1.1", - "remark-smartypants": "^2.0.0", + "rehype-katex": "^7.0.1", + "remark-gfm": "^4.0.0", + "remark-math": "^6.0.0", + "remark-smartypants": "^3.0.2", + "unified": "^11.0.5", "unist-util-visit": "^4.1.1" } } diff --git a/src/plugins/rehype/rehypeSyntaxHighlighting.ts b/src/plugins/rehype/rehypeSyntaxHighlighting.ts index 227ee3e..e2ce8fa 100644 --- a/src/plugins/rehype/rehypeSyntaxHighlighting.ts +++ b/src/plugins/rehype/rehypeSyntaxHighlighting.ts @@ -1,6 +1,7 @@ import { toString } from "hast-util-to-string"; import { RefractorElement } from "refractor"; import { refractor } from "refractor/lib/all.js"; +import type { Plugin } from 'unified'; import { Parent } from "unist"; import { visit } from "unist-util-visit"; @@ -23,53 +24,64 @@ export type TreeParent = Parent & { }; }; -export const rehypeSyntaxHighlighting = ( - options: RehypeSyntaxHighlightingOptions -) => { - if (options.alias) { +const lineHighlightPattern = /\{(.*?)\}/; + +export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?], TreeNode> = ( + options = {} +) => { + if (options?.alias) { refractor.alias(options.alias); } - return (tree: Parent) => { + return (tree) => { visit(tree, "element", (node: TreeNode, _index, parent?: TreeParent) => { if (!parent || parent.tagName !== "pre" || node.tagName !== "code") { return; } - const lang = getLanguage(node); - const linesToHighlight = getLineHighlight(node); - - if (lang === null) { - return; - } + const lang = getLanguage(node) || "plaintext"; - let result; try { parent.properties.className = ( parent.properties.className || [] ).concat("language-" + lang); const code = toString(node); - - const nodes = code - .split("\n") - .reduce((acc: RefractorElement[], line: string, index: number) => { - const node: TreeNode = { - type: "element", - tagName: "span", - properties: { - className: [ - "line", - line.trim() !== "" && linesToHighlight?.includes(index) - ? "line-highlight" - : "", - ], - }, - children: refractor.highlight(line, lang).children, - }; - acc.push(node); - acc.push({ type: "text", tagName: "code", value: "\n" } as any); + const lines = code.split("\n"); + const linesToHighlight = getLinesToHighlight(node, lines.length); + + const nodes = lines.reduce( + (acc: RefractorElement[], line: string, index: number) => { + const isNotEmptyLine = line.trim() !== ""; + const isHighlighted = linesToHighlight.includes(index + 1); // Line numbers start from 1 + + if (isNotEmptyLine) { + const node: TreeNode = { + type: "element", + tagName: "span", + properties: { + className: [ + isHighlighted ? "line-highlight" : "", + ], + }, + children: refractor.highlight(line, lang).children, + }; + acc.push(node); + } else { + acc.push({ type: "text", value: line } as any); + } + + if(index < lines.length - 1) { + acc.push({ type: "text", value: "\n" } as any); + } return acc; - }, []); + }, + [] + ); + + if (node.data?.meta) { + // remove line highlight meta + node.data.meta = removeLineHighlightMeta(node.data.meta.toString()); + } node.children = nodes; } catch (err) { @@ -97,33 +109,37 @@ function getLanguage(node: TreeNode) { return null; } -function getLineHighlight(node: TreeNode) { - const meta = node.data?.meta as string | undefined; - if (!meta) return null; - const match = meta.match(/^\{(.+)\}$/); - if (!match) return null; +function getLinesToHighlight(node: TreeNode, maxLines: number): number[] { + const meta = node.data?.meta?.toString(); + if (!meta) return []; + + const content = meta.match(lineHighlightPattern)?.[1]?.trim(); + if (!content) return []; - const content = match[1]?.trim(); + const lineNumbers = new Set(); - const parts = content?.split(","); - const lineNumbers: number[] = []; + content.split(",").forEach((part) => { + const [start, end] = part.split("-").map((num) => { + const trimmed = num.trim(); + if (!/^\d+$/.test(trimmed)) return undefined; + const parsed = parseInt(trimmed, 10); + return parsed > maxLines ? undefined : parsed; + }); - parts?.forEach((part) => { - const range = part.split("-").map((num) => parseInt(num, 10)); - if (!range?.[0] || isNaN(range[0])) return; - lineNumbers.push(Math.max(0, range[0] - 1)); + if (start === undefined) return; + const endLine = end ?? start; - if (!range?.[1] || isNaN(range[1])) return; + if (endLine < start) return; - const start = Math.min(range[0], range[1]); - const end = Math.max(range[0], range[1]); - for (let i = start; i <= end; i++) { - lineNumbers.push(Math.max(0, i - 1)); + for (let i = start; i <= Math.min(endLine, maxLines); i++) { + lineNumbers.add(i); } }); - return lineNumbers.length > 0 - ? [...new Set(lineNumbers)].sort((a, b) => a - b) - : null; + return Array.from(lineNumbers).sort((a, b) => a - b); +} + +function removeLineHighlightMeta(meta: string): string { + return meta.replace(lineHighlightPattern, '').trim(); } diff --git a/src/styles/prism.css b/src/styles/prism.css index a4d98b7..b017f77 100644 --- a/src/styles/prism.css +++ b/src/styles/prism.css @@ -261,37 +261,33 @@ code.language-toml .table { /********************************************************* * Line highlighting */ -pre[class*='language-'] { - padding: 0.5rem; - box-sizing: border-box; - overflow-x: auto; - width: 100%; -} - -pre[class*='language-'] .line { - padding-left: 0.5rem; - padding-right: 0.5rem; - white-space: pre-wrap; - word-break: break-word; -} -.line-highlight.line-highlight { - background: hsl(var(--codeblock-line-highlight) / 0.3); +.line-highlight { + background: rgb(var(--primary-light) / 0.2); width: 100%; - /* padding: 0.2rem 0; */ + padding-top: 0.2rem; + padding-bottom: 0.2rem; display: inline-block; - box-shadow: inset 2px 0 0 hsl(var(--codeblock-line-highlight)/1); + position: relative; z-index: 0; } -.line-highlight:not(.line-highlight + .line-highlight) { - border-top-left-radius: 4px; - border-top-right-radius: 4px; +.line-highlight::before, +.line-highlight::after { + content: ''; + position: absolute; + top: 0; + bottom: 0; + width: 1rem; + background: rgb(var(--primary-light) / 0.2); } -.line-highlight:not(:has(+ .line-highlight)) { - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; +.line-highlight::before { + left: -1rem; + border-left: 4px solid rgb(var(--primary-light)/1); +} +.line-highlight::after { + right: -1rem; } pre[class^='language-diff-'] { From eb12a3ed538413b0305bbbd0629cb71622ba1650 Mon Sep 17 00:00:00 2001 From: Deep Date: Mon, 7 Oct 2024 20:47:59 -0400 Subject: [PATCH 03/15] fix formatting --- examples/app-router/app/globals.css | 6 ++-- .../examples/highlight-example.mdx | 6 ++-- examples/pages-router/pages/_document.tsx | 4 +-- examples/pages-router/pages/api/hello.ts | 10 +++--- examples/pages-router/pages/index.tsx | 4 ++- src/client/default.tsx | 4 +-- src/index.ts | 6 ++-- src/plugins/index.ts | 2 +- src/plugins/rehype/index.ts | 2 +- .../rehype/rehypeSyntaxHighlighting.ts | 22 ++++++------ src/styles/prism.css | 34 +++++++++---------- src/types/index.ts | 4 +-- 12 files changed, 52 insertions(+), 52 deletions(-) diff --git a/examples/app-router/app/globals.css b/examples/app-router/app/globals.css index bea798b..66b454e 100644 --- a/examples/app-router/app/globals.css +++ b/examples/app-router/app/globals.css @@ -3,7 +3,7 @@ @tailwind utilities; @layer base { - :root { - --primary-light: 85 215 153; - } + :root { + --primary-light: 85 215 153; + } } diff --git a/examples/pages-router/examples/highlight-example.mdx b/examples/pages-router/examples/highlight-example.mdx index d77c687..fbae93e 100644 --- a/examples/pages-router/examples/highlight-example.mdx +++ b/examples/pages-router/examples/highlight-example.mdx @@ -15,7 +15,7 @@ function subtract(a, b) { } ``` -## Python +## Python ```python index.py {1-2,4-5} def add(a, b): @@ -25,7 +25,7 @@ def subtract(a, b): return a - b ``` -## Java +## Java ```java {3} public class Main { @@ -35,7 +35,7 @@ public class Main { } ``` -## C# +## C# ```csharp index.cs {1,3-4} public class Program diff --git a/examples/pages-router/pages/_document.tsx b/examples/pages-router/pages/_document.tsx index 54e8bf3..b2fff8b 100644 --- a/examples/pages-router/pages/_document.tsx +++ b/examples/pages-router/pages/_document.tsx @@ -1,4 +1,4 @@ -import { Html, Head, Main, NextScript } from 'next/document' +import { Html, Head, Main, NextScript } from "next/document"; export default function Document() { return ( @@ -9,5 +9,5 @@ export default function Document() { - ) + ); } diff --git a/examples/pages-router/pages/api/hello.ts b/examples/pages-router/pages/api/hello.ts index f8bcc7e..ea77e8f 100644 --- a/examples/pages-router/pages/api/hello.ts +++ b/examples/pages-router/pages/api/hello.ts @@ -1,13 +1,13 @@ // Next.js API route support: https://nextjs.org/docs/api-routes/introduction -import type { NextApiRequest, NextApiResponse } from 'next' +import type { NextApiRequest, NextApiResponse } from "next"; type Data = { - name: string -} + name: string; +}; export default function handler( req: NextApiRequest, - res: NextApiResponse + res: NextApiResponse, ) { - res.status(200).json({ name: 'John Doe' }) + res.status(200).json({ name: "John Doe" }); } diff --git a/examples/pages-router/pages/index.tsx b/examples/pages-router/pages/index.tsx index 6008fbd..96ae9f3 100644 --- a/examples/pages-router/pages/index.tsx +++ b/examples/pages-router/pages/index.tsx @@ -4,7 +4,9 @@ import { promises as fs } from "fs"; import type { GetStaticProps, InferGetStaticPropsType } from "next"; export const getStaticProps = (async () => { - const data = await fs.readFile(process.cwd() + '/examples/highlight-example.mdx'); + const data = await fs.readFile( + process.cwd() + "/examples/highlight-example.mdx", + ); const mdxSource = await getCompiledMdx({ source: data.toString(), diff --git a/src/client/default.tsx b/src/client/default.tsx index 4786b54..f42290b 100644 --- a/src/client/default.tsx +++ b/src/client/default.tsx @@ -1,5 +1,5 @@ -import { MDXRemote } from 'next-mdx-remote'; -import type { MDXRemoteProps } from 'next-mdx-remote'; +import { MDXRemote } from "next-mdx-remote"; +import type { MDXRemoteProps } from "next-mdx-remote"; export const MDXComponent = ({ compiledSource, diff --git a/src/index.ts b/src/index.ts index cd245f6..b50a2f2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ -export * from './client/index.js'; -export * from './server/index.js'; -export * from './types/index.js'; +export * from "./client/index.js"; +export * from "./server/index.js"; +export * from "./types/index.js"; diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 95697e5..4fe92a0 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -1 +1 @@ -export * from './rehype/index.js'; +export * from "./rehype/index.js"; diff --git a/src/plugins/rehype/index.ts b/src/plugins/rehype/index.ts index 865bc66..53317a0 100644 --- a/src/plugins/rehype/index.ts +++ b/src/plugins/rehype/index.ts @@ -1 +1 @@ -export * from './rehypeSyntaxHighlighting.js'; +export * from "./rehypeSyntaxHighlighting.js"; diff --git a/src/plugins/rehype/rehypeSyntaxHighlighting.ts b/src/plugins/rehype/rehypeSyntaxHighlighting.ts index e2ce8fa..e8433c6 100644 --- a/src/plugins/rehype/rehypeSyntaxHighlighting.ts +++ b/src/plugins/rehype/rehypeSyntaxHighlighting.ts @@ -1,7 +1,7 @@ import { toString } from "hast-util-to-string"; import { RefractorElement } from "refractor"; import { refractor } from "refractor/lib/all.js"; -import type { Plugin } from 'unified'; +import type { Plugin } from "unified"; import { Parent } from "unist"; import { visit } from "unist-util-visit"; @@ -26,9 +26,10 @@ export type TreeParent = Parent & { const lineHighlightPattern = /\{(.*?)\}/; -export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?], TreeNode> = ( - options = {} -) => { +export const rehypeSyntaxHighlighting: Plugin< + [RehypeSyntaxHighlightingOptions?], + TreeNode +> = (options = {}) => { if (options?.alias) { refractor.alias(options.alias); } @@ -53,15 +54,13 @@ export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?] (acc: RefractorElement[], line: string, index: number) => { const isNotEmptyLine = line.trim() !== ""; const isHighlighted = linesToHighlight.includes(index + 1); // Line numbers start from 1 - + if (isNotEmptyLine) { const node: TreeNode = { type: "element", tagName: "span", properties: { - className: [ - isHighlighted ? "line-highlight" : "", - ], + className: [isHighlighted ? "line-highlight" : ""], }, children: refractor.highlight(line, lang).children, }; @@ -70,12 +69,12 @@ export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?] acc.push({ type: "text", value: line } as any); } - if(index < lines.length - 1) { + if (index < lines.length - 1) { acc.push({ type: "text", value: "\n" } as any); } return acc; }, - [] + [], ); if (node.data?.meta) { @@ -109,7 +108,6 @@ function getLanguage(node: TreeNode) { return null; } - function getLinesToHighlight(node: TreeNode, maxLines: number): number[] { const meta = node.data?.meta?.toString(); if (!meta) return []; @@ -141,5 +139,5 @@ function getLinesToHighlight(node: TreeNode, maxLines: number): number[] { } function removeLineHighlightMeta(meta: string): string { - return meta.replace(lineHighlightPattern, '').trim(); + return meta.replace(lineHighlightPattern, "").trim(); } diff --git a/src/styles/prism.css b/src/styles/prism.css index b017f77..6c514ec 100644 --- a/src/styles/prism.css +++ b/src/styles/prism.css @@ -192,24 +192,24 @@ * Language Specific */ -pre[class*='language-javascript'], -code[class*='language-javascript'], -pre[class*='language-jsx'], -code[class*='language-jsx'], -pre[class*='language-typescript'], -code[class*='language-typescript'], -pre[class*='language-tsx'], -code[class*='language-tsx'] { +pre[class*="language-javascript"], +code[class*="language-javascript"], +pre[class*="language-jsx"], +code[class*="language-jsx"], +pre[class*="language-typescript"], +code[class*="language-typescript"], +pre[class*="language-tsx"], +code[class*="language-tsx"] { color: #9cdcfe; } -pre[class*='language-css'], -code[class*='language-css'] { +pre[class*="language-css"], +code[class*="language-css"] { color: #ce9178; } -pre[class*='language-html'], -code[class*='language-html'] { +pre[class*="language-html"], +code[class*="language-html"] { color: #d4d4d4; } @@ -274,7 +274,7 @@ code.language-toml .table { .line-highlight::before, .line-highlight::after { - content: ''; + content: ""; position: absolute; top: 0; bottom: 0; @@ -284,24 +284,24 @@ code.language-toml .table { .line-highlight::before { left: -1rem; - border-left: 4px solid rgb(var(--primary-light)/1); + border-left: 4px solid rgb(var(--primary-light) / 1); } .line-highlight::after { right: -1rem; } -pre[class^='language-diff-'] { +pre[class^="language-diff-"] { display: flex; padding-left: 2.25rem; padding-right: 2.25rem; } -pre[class^='language-diff-'] > code { +pre[class^="language-diff-"] > code { flex: none; min-width: 100%; } -pre[class^='language-diff-'] > code { +pre[class^="language-diff-"] > code { flex: none; min-width: 100%; } diff --git a/src/types/index.ts b/src/types/index.ts index f640efe..877d8cb 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,5 @@ -import type { MDXRemoteSerializeResult } from 'next-mdx-remote'; -import type { serialize } from 'next-mdx-remote/serialize'; +import type { MDXRemoteSerializeResult } from "next-mdx-remote"; +import type { serialize } from "next-mdx-remote/serialize"; type SerializeOptions = NonNullable[1]>; From 379d0ae11917832cf2c27d188e8eb4b921bd8777 Mon Sep 17 00:00:00 2001 From: Deep Date: Tue, 8 Oct 2024 22:01:29 -0400 Subject: [PATCH 04/15] fix: update mdx package --- examples/app-router/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/app-router/package.json b/examples/app-router/package.json index c224d2e..c369299 100644 --- a/examples/app-router/package.json +++ b/examples/app-router/package.json @@ -9,7 +9,7 @@ "lint": "next lint" }, "dependencies": { - "@mintlify/mdx": "^0.0.43", + "@mintlify/mdx": "^0.0.44", "next": "14.0.4", "react": "^18", "react-dom": "^18" From 7c10ca6b5fbcbec1bf5c45f5d51142c1e814ec17 Mon Sep 17 00:00:00 2001 From: Deep Date: Tue, 8 Oct 2024 22:03:12 -0400 Subject: [PATCH 05/15] fix: update mdx package in pages router --- examples/pages-router/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pages-router/package.json b/examples/pages-router/package.json index 89d8c55..c824192 100644 --- a/examples/pages-router/package.json +++ b/examples/pages-router/package.json @@ -9,7 +9,7 @@ "lint": "next lint" }, "dependencies": { - "@mintlify/mdx": "^0.0.43", + "@mintlify/mdx": "^0.0.44", "next": "14.0.4", "react": "^18", "react-dom": "^18" From e50bf2965750ad1b73246f10d4b3b07e06ddc017 Mon Sep 17 00:00:00 2001 From: Deep Date: Tue, 8 Oct 2024 22:03:36 -0400 Subject: [PATCH 06/15] revert next config --- examples/pages-router/next.config.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/pages-router/next.config.js b/examples/pages-router/next.config.js index 5ec3d6e..9355ff6 100644 --- a/examples/pages-router/next.config.js +++ b/examples/pages-router/next.config.js @@ -1,6 +1,5 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - transpilePackages: ["next-mdx-remote"] -} +}; -module.exports = nextConfig +module.exports = nextConfig; From 03e7a35745dd5e19427b7270da39c29ef8567eab Mon Sep 17 00:00:00 2001 From: Deep Date: Tue, 8 Oct 2024 23:13:44 -0400 Subject: [PATCH 07/15] chore: apply prettier --- .prettierrc | 1 + examples/app-router/app/layout.tsx | 17 ++-- examples/app-router/app/page.tsx | 2 +- .../app-router/examples/highlight-example.mdx | 13 ++- examples/app-router/next.config.js | 4 +- examples/app-router/postcss.config.js | 2 +- examples/app-router/readme.md | 16 ++- examples/app-router/tailwind.config.ts | 15 ++- .../examples/highlight-example.mdx | 7 +- examples/pages-router/next.config.js | 3 +- examples/pages-router/pages/_app.tsx | 6 +- examples/pages-router/pages/_document.tsx | 2 +- examples/pages-router/pages/api/hello.ts | 9 +- examples/pages-router/pages/index.tsx | 16 ++- examples/pages-router/postcss.config.js | 2 +- examples/pages-router/styles/globals.css | 6 ++ examples/pages-router/tailwind.config.ts | 15 ++- package.json | 3 + src/client/default.tsx | 4 +- src/client/index.ts | 4 +- src/client/rsc.tsx | 5 +- src/index.ts | 6 +- src/plugins/index.ts | 2 +- src/plugins/rehype/index.ts | 2 +- .../rehype/rehypeSyntaxHighlighting.ts | 97 +++++++++---------- src/server/index.ts | 41 ++++---- src/styles/prism.css | 32 +++--- src/types/index.ts | 4 +- 28 files changed, 163 insertions(+), 173 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..374cfd3 --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +"@mintlify/prettier-config/config.js" diff --git a/examples/app-router/app/layout.tsx b/examples/app-router/app/layout.tsx index 1b21819..bec98c5 100644 --- a/examples/app-router/app/layout.tsx +++ b/examples/app-router/app/layout.tsx @@ -1,17 +1,14 @@ -import type { Metadata } from "next"; -import "@/app/globals.css"; -import "@mintlify/mdx/dist/styles.css"; +import '@mintlify/mdx/dist/styles.css'; +import type { Metadata } from 'next'; + +import '@/app/globals.css'; export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: 'Create Next App', + description: 'Generated by create next app', }; -export default function RootLayout({ - children, -}: { - children: React.ReactNode; -}) { +export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} diff --git a/examples/app-router/app/page.tsx b/examples/app-router/app/page.tsx index 9ba8a52..af95dd5 100644 --- a/examples/app-router/app/page.tsx +++ b/examples/app-router/app/page.tsx @@ -1,4 +1,4 @@ -import { getCompiledServerMdx } from "@mintlify/mdx"; +import { getCompiledServerMdx } from '@mintlify/mdx'; import { promises as fs } from 'fs'; export default async function Home() { diff --git a/examples/app-router/examples/highlight-example.mdx b/examples/app-router/examples/highlight-example.mdx index d77c687..c4d8463 100644 --- a/examples/app-router/examples/highlight-example.mdx +++ b/examples/app-router/examples/highlight-example.mdx @@ -1,11 +1,14 @@ -# Syntax Highlighting Example +--- +title: 'Line Highlighting' +description: 'Highlights specific lines and/or line ranges' +--- This MDX file demonstrates syntax highlighting for various programming languages. ## JavaScript ```js index.js {2} -console.log("Hello, world!"); +console.log('Hello, world!'); function add(a, b) { return a + b; } @@ -15,7 +18,7 @@ function subtract(a, b) { } ``` -## Python +## Python ```python index.py {1-2,4-5} def add(a, b): @@ -25,7 +28,7 @@ def subtract(a, b): return a - b ``` -## Java +## Java ```java {3} public class Main { @@ -35,7 +38,7 @@ public class Main { } ``` -## C# +## C# ```csharp index.cs {1,3-4} public class Program diff --git a/examples/app-router/next.config.js b/examples/app-router/next.config.js index 767719f..658404a 100644 --- a/examples/app-router/next.config.js +++ b/examples/app-router/next.config.js @@ -1,4 +1,4 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {} +const nextConfig = {}; -module.exports = nextConfig +module.exports = nextConfig; diff --git a/examples/app-router/postcss.config.js b/examples/app-router/postcss.config.js index 33ad091..12a703d 100644 --- a/examples/app-router/postcss.config.js +++ b/examples/app-router/postcss.config.js @@ -3,4 +3,4 @@ module.exports = { tailwindcss: {}, autoprefixer: {}, }, -} +}; diff --git a/examples/app-router/readme.md b/examples/app-router/readme.md index 24a45c3..13e14fc 100644 --- a/examples/app-router/readme.md +++ b/examples/app-router/readme.md @@ -13,7 +13,7 @@ You can check out the demo of [this page](https://github.com/mintlify/mdx/blob/m 1. Call the `getCompiledServerMdx` function inside your async React Server Component which will give you the `frontmatter` and `content`. ```tsx - import { getCompiledServerMdx } from "@mintlify/mdx"; + import { getCompiledServerMdx } from '@mintlify/mdx'; export default async function Home() { const { content, frontmatter } = await getCompiledServerMdx({ @@ -38,19 +38,15 @@ You can check out the demo of [this page](https://github.com/mintlify/mdx/blob/m 2. Import `@mintlify/mdx/dist/styles.css` inside your `layout.tsx` file. This file contains the styles for the code syntax highlighting. ```tsx - import type { Metadata } from "next"; - import "@mintlify/mdx/dist/styles.css"; + import '@mintlify/mdx/dist/styles.css'; + import type { Metadata } from 'next'; export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: 'Create Next App', + description: 'Generated by create next app', }; - export default function RootLayout({ - children, - }: { - children: React.ReactNode; - }) { + export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} diff --git a/examples/app-router/tailwind.config.ts b/examples/app-router/tailwind.config.ts index cc1084e..e4ff882 100644 --- a/examples/app-router/tailwind.config.ts +++ b/examples/app-router/tailwind.config.ts @@ -1,20 +1,19 @@ -import type { Config } from "tailwindcss"; +import type { Config } from 'tailwindcss'; const config: Config = { content: [ - "./pages/**/*.{js,ts,jsx,tsx,mdx}", - "./components/**/*.{js,ts,jsx,tsx,mdx}", - "./app/**/*.{js,ts,jsx,tsx,mdx}", + './pages/**/*.{js,ts,jsx,tsx,mdx}', + './components/**/*.{js,ts,jsx,tsx,mdx}', + './app/**/*.{js,ts,jsx,tsx,mdx}', ], theme: { extend: { backgroundImage: { - "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", - "gradient-conic": - "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", + 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', + 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', }, }, }, - plugins: [require("@tailwindcss/typography")], + plugins: [require('@tailwindcss/typography')], }; export default config; diff --git a/examples/pages-router/examples/highlight-example.mdx b/examples/pages-router/examples/highlight-example.mdx index fbae93e..c4d8463 100644 --- a/examples/pages-router/examples/highlight-example.mdx +++ b/examples/pages-router/examples/highlight-example.mdx @@ -1,11 +1,14 @@ -# Syntax Highlighting Example +--- +title: 'Line Highlighting' +description: 'Highlights specific lines and/or line ranges' +--- This MDX file demonstrates syntax highlighting for various programming languages. ## JavaScript ```js index.js {2} -console.log("Hello, world!"); +console.log('Hello, world!'); function add(a, b) { return a + b; } diff --git a/examples/pages-router/next.config.js b/examples/pages-router/next.config.js index 9355ff6..658404a 100644 --- a/examples/pages-router/next.config.js +++ b/examples/pages-router/next.config.js @@ -1,5 +1,4 @@ /** @type {import('next').NextConfig} */ -const nextConfig = { -}; +const nextConfig = {}; module.exports = nextConfig; diff --git a/examples/pages-router/pages/_app.tsx b/examples/pages-router/pages/_app.tsx index 3b184f3..e3d15a8 100644 --- a/examples/pages-router/pages/_app.tsx +++ b/examples/pages-router/pages/_app.tsx @@ -1,7 +1,7 @@ -import "@/styles/globals.css"; -import "@mintlify/mdx/dist/styles.css"; +import '@mintlify/mdx/dist/styles.css'; +import { AppProps } from 'next/app'; -import { AppProps } from "next/app"; +import '@/styles/globals.css'; export default function App({ Component, pageProps }: AppProps) { return ; diff --git a/examples/pages-router/pages/_document.tsx b/examples/pages-router/pages/_document.tsx index b2fff8b..e1e9cbb 100644 --- a/examples/pages-router/pages/_document.tsx +++ b/examples/pages-router/pages/_document.tsx @@ -1,4 +1,4 @@ -import { Html, Head, Main, NextScript } from "next/document"; +import { Html, Head, Main, NextScript } from 'next/document'; export default function Document() { return ( diff --git a/examples/pages-router/pages/api/hello.ts b/examples/pages-router/pages/api/hello.ts index ea77e8f..eb4cc66 100644 --- a/examples/pages-router/pages/api/hello.ts +++ b/examples/pages-router/pages/api/hello.ts @@ -1,13 +1,10 @@ // Next.js API route support: https://nextjs.org/docs/api-routes/introduction -import type { NextApiRequest, NextApiResponse } from "next"; +import type { NextApiRequest, NextApiResponse } from 'next'; type Data = { name: string; }; -export default function handler( - req: NextApiRequest, - res: NextApiResponse, -) { - res.status(200).json({ name: "John Doe" }); +export default function handler(req: NextApiRequest, res: NextApiResponse) { + res.status(200).json({ name: 'John Doe' }); } diff --git a/examples/pages-router/pages/index.tsx b/examples/pages-router/pages/index.tsx index 96ae9f3..bdfe849 100644 --- a/examples/pages-router/pages/index.tsx +++ b/examples/pages-router/pages/index.tsx @@ -1,12 +1,10 @@ -import type { MDXCompiledResult } from "@mintlify/mdx"; -import { MDXComponent, getCompiledMdx } from "@mintlify/mdx"; -import { promises as fs } from "fs"; -import type { GetStaticProps, InferGetStaticPropsType } from "next"; +import type { MDXCompiledResult } from '@mintlify/mdx'; +import { MDXComponent, getCompiledMdx } from '@mintlify/mdx'; +import { promises as fs } from 'fs'; +import type { GetStaticProps, InferGetStaticPropsType } from 'next'; export const getStaticProps = (async () => { - const data = await fs.readFile( - process.cwd() + "/examples/highlight-example.mdx", - ); + const data = await fs.readFile(process.cwd() + '/examples/highlight-example.mdx'); const mdxSource = await getCompiledMdx({ source: data.toString(), @@ -21,9 +19,7 @@ export const getStaticProps = (async () => { mdxSource: MDXCompiledResult; }>; -export default function Home({ - mdxSource, -}: InferGetStaticPropsType) { +export default function Home({ mdxSource }: InferGetStaticPropsType) { return (

{String(mdxSource.frontmatter.title)}

diff --git a/examples/pages-router/postcss.config.js b/examples/pages-router/postcss.config.js index 33ad091..12a703d 100644 --- a/examples/pages-router/postcss.config.js +++ b/examples/pages-router/postcss.config.js @@ -3,4 +3,4 @@ module.exports = { tailwindcss: {}, autoprefixer: {}, }, -} +}; diff --git a/examples/pages-router/styles/globals.css b/examples/pages-router/styles/globals.css index b5c61c9..66b454e 100644 --- a/examples/pages-router/styles/globals.css +++ b/examples/pages-router/styles/globals.css @@ -1,3 +1,9 @@ @tailwind base; @tailwind components; @tailwind utilities; + +@layer base { + :root { + --primary-light: 85 215 153; + } +} diff --git a/examples/pages-router/tailwind.config.ts b/examples/pages-router/tailwind.config.ts index cc1084e..e4ff882 100644 --- a/examples/pages-router/tailwind.config.ts +++ b/examples/pages-router/tailwind.config.ts @@ -1,20 +1,19 @@ -import type { Config } from "tailwindcss"; +import type { Config } from 'tailwindcss'; const config: Config = { content: [ - "./pages/**/*.{js,ts,jsx,tsx,mdx}", - "./components/**/*.{js,ts,jsx,tsx,mdx}", - "./app/**/*.{js,ts,jsx,tsx,mdx}", + './pages/**/*.{js,ts,jsx,tsx,mdx}', + './components/**/*.{js,ts,jsx,tsx,mdx}', + './app/**/*.{js,ts,jsx,tsx,mdx}', ], theme: { extend: { backgroundImage: { - "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", - "gradient-conic": - "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", + 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', + 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', }, }, }, - plugins: [require("@tailwindcss/typography")], + plugins: [require('@tailwindcss/typography')], }; export default config; diff --git a/package.json b/package.json index 54f6257..65d7f38 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,9 @@ "devDependencies": { "@mintlify/eslint-config": "^1.0.4", "@mintlify/eslint-config-typescript": "^1.0.9", + "@mintlify/prettier-config": "^1.0.1", "@mintlify/ts-config": "^2.0.2", + "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@tsconfig/recommended": "1.x", "@types/react": "^18.3.11", "@types/react-dom": "^18.3.0", @@ -44,6 +46,7 @@ "eslint-config-prettier": "8.x", "eslint-plugin-unused-imports": "^3.x", "prettier": "^3.1.1", + "prettier-plugin-tailwindcss": "^0.6.8", "react": "^18.3.1", "react-dom": "^18.3.1", "rimraf": "^5.0.1", diff --git a/src/client/default.tsx b/src/client/default.tsx index f42290b..2e75976 100644 --- a/src/client/default.tsx +++ b/src/client/default.tsx @@ -1,5 +1,5 @@ -import { MDXRemote } from "next-mdx-remote"; -import type { MDXRemoteProps } from "next-mdx-remote"; +import type { MDXRemoteProps } from 'next-mdx-remote'; +import { MDXRemote } from 'next-mdx-remote'; export const MDXComponent = ({ compiledSource, diff --git a/src/client/index.ts b/src/client/index.ts index 78f9d9b..170d789 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -1,2 +1,2 @@ -export * from "./default.js"; -export * from "./rsc.js"; +export * from './default.js'; +export * from './rsc.js'; diff --git a/src/client/rsc.tsx b/src/client/rsc.tsx index 8d10793..600d561 100644 --- a/src/client/rsc.tsx +++ b/src/client/rsc.tsx @@ -1,7 +1,6 @@ -import { MDXRemote } from "next-mdx-remote/rsc"; -import type { MDXRemoteProps } from "next-mdx-remote/rsc"; +import type { MDXRemoteProps } from 'next-mdx-remote/rsc'; +import { MDXRemote } from 'next-mdx-remote/rsc'; export const MDXServerComponent = ({ source, components }: MDXRemoteProps) => { - // @ts-expect-error: 'MDXRemote' cannot be used as a JSX component. return ; }; diff --git a/src/index.ts b/src/index.ts index b50a2f2..cd245f6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ -export * from "./client/index.js"; -export * from "./server/index.js"; -export * from "./types/index.js"; +export * from './client/index.js'; +export * from './server/index.js'; +export * from './types/index.js'; diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 4fe92a0..95697e5 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -1 +1 @@ -export * from "./rehype/index.js"; +export * from './rehype/index.js'; diff --git a/src/plugins/rehype/index.ts b/src/plugins/rehype/index.ts index 53317a0..865bc66 100644 --- a/src/plugins/rehype/index.ts +++ b/src/plugins/rehype/index.ts @@ -1 +1 @@ -export * from "./rehypeSyntaxHighlighting.js"; +export * from './rehypeSyntaxHighlighting.js'; diff --git a/src/plugins/rehype/rehypeSyntaxHighlighting.ts b/src/plugins/rehype/rehypeSyntaxHighlighting.ts index e8433c6..fc07454 100644 --- a/src/plugins/rehype/rehypeSyntaxHighlighting.ts +++ b/src/plugins/rehype/rehypeSyntaxHighlighting.ts @@ -1,9 +1,9 @@ -import { toString } from "hast-util-to-string"; -import { RefractorElement } from "refractor"; -import { refractor } from "refractor/lib/all.js"; -import type { Plugin } from "unified"; -import { Parent } from "unist"; -import { visit } from "unist-util-visit"; +import { toString } from 'hast-util-to-string'; +import { RefractorElement } from 'refractor'; +import { refractor } from 'refractor/lib/all.js'; +import type { Plugin } from 'unified'; +import { Parent } from 'unist'; +import { visit } from 'unist-util-visit'; export type RehypeSyntaxHighlightingOptions = { ignoreMissing?: boolean; @@ -11,7 +11,7 @@ export type RehypeSyntaxHighlightingOptions = { }; export type TreeNode = RefractorElement & { - type: "element" | "text"; + type: 'element' | 'text'; properties: { className?: string[]; }; @@ -26,56 +26,52 @@ export type TreeParent = Parent & { const lineHighlightPattern = /\{(.*?)\}/; -export const rehypeSyntaxHighlighting: Plugin< - [RehypeSyntaxHighlightingOptions?], - TreeNode -> = (options = {}) => { +export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?], TreeNode> = ( + options = {} +) => { if (options?.alias) { refractor.alias(options.alias); } return (tree) => { - visit(tree, "element", (node: TreeNode, _index, parent?: TreeParent) => { - if (!parent || parent.tagName !== "pre" || node.tagName !== "code") { + visit(tree, 'element', (node: TreeNode, _index, parent?: TreeParent) => { + if (!parent || parent.tagName !== 'pre' || node.tagName !== 'code') { return; } - const lang = getLanguage(node) || "plaintext"; + const lang = getLanguage(node) || 'plaintext'; try { - parent.properties.className = ( - parent.properties.className || [] - ).concat("language-" + lang); + parent.properties.className = (parent.properties.className || []).concat( + 'language-' + lang + ); const code = toString(node); - const lines = code.split("\n"); + const lines = code.split('\n'); const linesToHighlight = getLinesToHighlight(node, lines.length); - const nodes = lines.reduce( - (acc: RefractorElement[], line: string, index: number) => { - const isNotEmptyLine = line.trim() !== ""; - const isHighlighted = linesToHighlight.includes(index + 1); // Line numbers start from 1 - - if (isNotEmptyLine) { - const node: TreeNode = { - type: "element", - tagName: "span", - properties: { - className: [isHighlighted ? "line-highlight" : ""], - }, - children: refractor.highlight(line, lang).children, - }; - acc.push(node); - } else { - acc.push({ type: "text", value: line } as any); - } - - if (index < lines.length - 1) { - acc.push({ type: "text", value: "\n" } as any); - } - return acc; - }, - [], - ); + const nodes = lines.reduce((acc: RefractorElement[], line: string, index: number) => { + const isNotEmptyLine = line.trim() !== ''; + const isHighlighted = linesToHighlight.includes(index + 1); // Line numbers start from 1 + + if (isNotEmptyLine) { + const node: TreeNode = { + type: 'element', + tagName: 'span', + properties: { + className: [isHighlighted ? 'line-highlight' : ''], + }, + children: refractor.highlight(line, lang).children, + }; + acc.push(node); + } else { + acc.push({ type: 'text', value: line } as any); + } + + if (index < lines.length - 1) { + acc.push({ type: 'text', value: '\n' } as any); + } + return acc; + }, []); if (node.data?.meta) { // remove line highlight meta @@ -84,10 +80,7 @@ export const rehypeSyntaxHighlighting: Plugin< node.children = nodes; } catch (err) { - if ( - options.ignoreMissing && - /Unknown language/.test((err as Error).message) - ) { + if (options.ignoreMissing && /Unknown language/.test((err as Error).message)) { return; } throw err; @@ -100,7 +93,7 @@ function getLanguage(node: TreeNode) { const className = node.properties?.className || []; for (const classListItem of className) { - if (classListItem.slice(0, 9) === "language-") { + if (classListItem.slice(0, 9) === 'language-') { return classListItem.slice(9).toLowerCase(); } } @@ -117,8 +110,8 @@ function getLinesToHighlight(node: TreeNode, maxLines: number): number[] { const lineNumbers = new Set(); - content.split(",").forEach((part) => { - const [start, end] = part.split("-").map((num) => { + content.split(',').forEach((part) => { + const [start, end] = part.split('-').map((num) => { const trimmed = num.trim(); if (!/^\d+$/.test(trimmed)) return undefined; const parsed = parseInt(trimmed, 10); @@ -139,5 +132,5 @@ function getLinesToHighlight(node: TreeNode, maxLines: number): number[] { } function removeLineHighlightMeta(meta: string): string { - return meta.replace(lineHighlightPattern, "").trim(); + return meta.replace(lineHighlightPattern, '').trim(); } diff --git a/src/server/index.ts b/src/server/index.ts index 843e662..4394ca2 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -1,12 +1,13 @@ -import { compileMDX } from "next-mdx-remote/rsc"; -import type { CompileMDXResult, MDXRemoteProps } from "next-mdx-remote/rsc"; -import { serialize } from "next-mdx-remote/serialize"; -import rehypeKatex from "rehype-katex"; -import remarkGfm from "remark-gfm"; -import remarkMath from "remark-math"; -import remarkSmartypants from "remark-smartypants"; -import { rehypeSyntaxHighlighting } from "../plugins/index.js"; -import type { SerializeOptions } from "../types/index.js"; +import type { CompileMDXResult, MDXRemoteProps } from 'next-mdx-remote/rsc'; +import { compileMDX } from 'next-mdx-remote/rsc'; +import { serialize } from 'next-mdx-remote/serialize'; +import rehypeKatex from 'rehype-katex'; +import remarkGfm from 'remark-gfm'; +import remarkMath from 'remark-math'; +import remarkSmartypants from 'remark-smartypants'; + +import { rehypeSyntaxHighlighting } from '../plugins/index.js'; +import type { SerializeOptions } from '../types/index.js'; export const getCompiledMdx = async ({ source, @@ -15,9 +16,9 @@ export const getCompiledMdx = async ({ parseFrontmatter = true, }: { source: string; - mdxOptions?: SerializeOptions["mdxOptions"]; - scope?: SerializeOptions["scope"]; - parseFrontmatter?: SerializeOptions["parseFrontmatter"]; + mdxOptions?: SerializeOptions['mdxOptions']; + scope?: SerializeOptions['scope']; + parseFrontmatter?: SerializeOptions['parseFrontmatter']; }) => { try { const serializedResponse = await serialize(source, { @@ -39,7 +40,7 @@ export const getCompiledMdx = async ({ ], ...(mdxOptions?.rehypePlugins || []), ], - format: mdxOptions?.format || "mdx", + format: mdxOptions?.format || 'mdx', useDynamicImport: mdxOptions?.useDynamicImport || true, }, scope, @@ -54,18 +55,16 @@ export const getCompiledMdx = async ({ } }; -export const getCompiledServerMdx = async < - TFrontmatter = Record, ->({ +export const getCompiledServerMdx = async >({ source, mdxOptions, components, parseFrontmatter = true, }: { - source: MDXRemoteProps["source"]; - mdxOptions?: SerializeOptions["mdxOptions"]; - components?: MDXRemoteProps["components"]; - parseFrontmatter?: SerializeOptions["parseFrontmatter"]; + source: MDXRemoteProps['source']; + mdxOptions?: SerializeOptions['mdxOptions']; + components?: MDXRemoteProps['components']; + parseFrontmatter?: SerializeOptions['parseFrontmatter']; }): Promise> => { return await compileMDX({ source, @@ -87,7 +86,7 @@ export const getCompiledServerMdx = async < ], ...(mdxOptions?.rehypePlugins || []), ], - format: mdxOptions?.format || "mdx", + format: mdxOptions?.format || 'mdx', useDynamicImport: mdxOptions?.useDynamicImport || true, }, parseFrontmatter, diff --git a/src/styles/prism.css b/src/styles/prism.css index 6c514ec..75862ad 100644 --- a/src/styles/prism.css +++ b/src/styles/prism.css @@ -192,24 +192,24 @@ * Language Specific */ -pre[class*="language-javascript"], -code[class*="language-javascript"], -pre[class*="language-jsx"], -code[class*="language-jsx"], -pre[class*="language-typescript"], -code[class*="language-typescript"], -pre[class*="language-tsx"], -code[class*="language-tsx"] { +pre[class*='language-javascript'], +code[class*='language-javascript'], +pre[class*='language-jsx'], +code[class*='language-jsx'], +pre[class*='language-typescript'], +code[class*='language-typescript'], +pre[class*='language-tsx'], +code[class*='language-tsx'] { color: #9cdcfe; } -pre[class*="language-css"], -code[class*="language-css"] { +pre[class*='language-css'], +code[class*='language-css'] { color: #ce9178; } -pre[class*="language-html"], -code[class*="language-html"] { +pre[class*='language-html'], +code[class*='language-html'] { color: #d4d4d4; } @@ -274,7 +274,7 @@ code.language-toml .table { .line-highlight::before, .line-highlight::after { - content: ""; + content: ''; position: absolute; top: 0; bottom: 0; @@ -290,18 +290,18 @@ code.language-toml .table { right: -1rem; } -pre[class^="language-diff-"] { +pre[class^='language-diff-'] { display: flex; padding-left: 2.25rem; padding-right: 2.25rem; } -pre[class^="language-diff-"] > code { +pre[class^='language-diff-'] > code { flex: none; min-width: 100%; } -pre[class^="language-diff-"] > code { +pre[class^='language-diff-'] > code { flex: none; min-width: 100%; } diff --git a/src/types/index.ts b/src/types/index.ts index 877d8cb..f640efe 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,5 @@ -import type { MDXRemoteSerializeResult } from "next-mdx-remote"; -import type { serialize } from "next-mdx-remote/serialize"; +import type { MDXRemoteSerializeResult } from 'next-mdx-remote'; +import type { serialize } from 'next-mdx-remote/serialize'; type SerializeOptions = NonNullable[1]>; From dcbab359bfe01fcd1ac29091751f37cb21220a31 Mon Sep 17 00:00:00 2001 From: Deep Date: Tue, 8 Oct 2024 23:16:04 -0400 Subject: [PATCH 08/15] revert missing style --- src/styles/prism.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/styles/prism.css b/src/styles/prism.css index 75862ad..235d4f8 100644 --- a/src/styles/prism.css +++ b/src/styles/prism.css @@ -262,6 +262,11 @@ code.language-toml .table { * Line highlighting */ +pre[class*='language-'] > code[class*='language-'] { + position: relative; + z-index: 1; +} + .line-highlight { background: rgb(var(--primary-light) / 0.2); width: 100%; From d9e3907c8597297c0bdbe9bb12804d8c2ab48f85 Mon Sep 17 00:00:00 2001 From: Deep Date: Tue, 8 Oct 2024 23:19:21 -0400 Subject: [PATCH 09/15] simplify syntax highlight plugin logic --- src/plugins/rehype/rehypeSyntaxHighlighting.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/plugins/rehype/rehypeSyntaxHighlighting.ts b/src/plugins/rehype/rehypeSyntaxHighlighting.ts index fc07454..98dc63e 100644 --- a/src/plugins/rehype/rehypeSyntaxHighlighting.ts +++ b/src/plugins/rehype/rehypeSyntaxHighlighting.ts @@ -51,7 +51,8 @@ export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?] const nodes = lines.reduce((acc: RefractorElement[], line: string, index: number) => { const isNotEmptyLine = line.trim() !== ''; - const isHighlighted = linesToHighlight.includes(index + 1); // Line numbers start from 1 + // Line numbers start from 1 + const isHighlighted = linesToHighlight.includes(index + 1); if (isNotEmptyLine) { const node: TreeNode = { @@ -75,7 +76,7 @@ export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?] if (node.data?.meta) { // remove line highlight meta - node.data.meta = removeLineHighlightMeta(node.data.meta.toString()); + node.data.meta = (node.data.meta as string).replace(lineHighlightPattern, '').trim(); } node.children = nodes; @@ -118,12 +119,12 @@ function getLinesToHighlight(node: TreeNode, maxLines: number): number[] { return parsed > maxLines ? undefined : parsed; }); - if (start === undefined) return; + if (!start) return; const endLine = end ?? start; if (endLine < start) return; - - for (let i = start; i <= Math.min(endLine, maxLines); i++) { + const max = Math.min(endLine, maxLines); + for (let i = start; i <= max; i++) { lineNumbers.add(i); } }); @@ -131,6 +132,3 @@ function getLinesToHighlight(node: TreeNode, maxLines: number): number[] { return Array.from(lineNumbers).sort((a, b) => a - b); } -function removeLineHighlightMeta(meta: string): string { - return meta.replace(lineHighlightPattern, '').trim(); -} From 1f999c904f47f569603c5263702905227f18a9f9 Mon Sep 17 00:00:00 2001 From: Deep Date: Sat, 19 Oct 2024 10:27:41 -0400 Subject: [PATCH 10/15] fix type issues --- .../rehype/rehypeSyntaxHighlighting.ts | 72 ++++++++++--------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/src/plugins/rehype/rehypeSyntaxHighlighting.ts b/src/plugins/rehype/rehypeSyntaxHighlighting.ts index 98dc63e..9afe35e 100644 --- a/src/plugins/rehype/rehypeSyntaxHighlighting.ts +++ b/src/plugins/rehype/rehypeSyntaxHighlighting.ts @@ -1,7 +1,8 @@ import { toString } from 'hast-util-to-string'; -import { RefractorElement } from 'refractor'; +import { RefractorElement, RefractorRoot } from 'refractor'; import { refractor } from 'refractor/lib/all.js'; import type { Plugin } from 'unified'; +import type { Node } from 'unist'; import { Parent } from 'unist'; import { visit } from 'unist-util-visit'; @@ -10,12 +11,12 @@ export type RehypeSyntaxHighlightingOptions = { alias?: Record; }; -export type TreeNode = RefractorElement & { - type: 'element' | 'text'; - properties: { - className?: string[]; +export type TreeNode = RefractorElement & + Node & { + properties: { + className?: string[]; + }; }; -}; export type TreeParent = Parent & { tagName: string; @@ -49,30 +50,33 @@ export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?] const lines = code.split('\n'); const linesToHighlight = getLinesToHighlight(node, lines.length); - const nodes = lines.reduce((acc: RefractorElement[], line: string, index: number) => { - const isNotEmptyLine = line.trim() !== ''; - // Line numbers start from 1 - const isHighlighted = linesToHighlight.includes(index + 1); - - if (isNotEmptyLine) { - const node: TreeNode = { - type: 'element', - tagName: 'span', - properties: { - className: [isHighlighted ? 'line-highlight' : ''], - }, - children: refractor.highlight(line, lang).children, - }; - acc.push(node); - } else { - acc.push({ type: 'text', value: line } as any); - } - - if (index < lines.length - 1) { - acc.push({ type: 'text', value: '\n' } as any); - } - return acc; - }, []); + const nodes = lines.reduce( + (acc: RefractorRoot['children'], line: string, index: number) => { + const isNotEmptyLine = line.trim() !== ''; + // Line numbers start from 1 + const isHighlighted = linesToHighlight.includes(index + 1); + + if (isNotEmptyLine) { + const node: TreeNode = { + type: 'element', + tagName: 'span', + properties: { + className: [isHighlighted ? 'line-highlight' : ''], + }, + children: refractor.highlight(line, lang).children, + }; + acc.push(node); + } else { + acc.push({ type: 'text', value: line }); + } + + if (index < lines.length - 1) { + acc.push({ type: 'text', value: '\n' }); + } + return acc; + }, + [] + ); if (node.data?.meta) { // remove line highlight meta @@ -91,19 +95,18 @@ export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?] }; function getLanguage(node: TreeNode) { - const className = node.properties?.className || []; + const className = node.properties.className || []; for (const classListItem of className) { if (classListItem.slice(0, 9) === 'language-') { return classListItem.slice(9).toLowerCase(); } } - return null; } function getLinesToHighlight(node: TreeNode, maxLines: number): number[] { - const meta = node.data?.meta?.toString(); + const meta = typeof node.data?.meta === 'string' ? node.data.meta : undefined; if (!meta) return []; const content = meta.match(lineHighlightPattern)?.[1]?.trim(); @@ -116,7 +119,7 @@ function getLinesToHighlight(node: TreeNode, maxLines: number): number[] { const trimmed = num.trim(); if (!/^\d+$/.test(trimmed)) return undefined; const parsed = parseInt(trimmed, 10); - return parsed > maxLines ? undefined : parsed; + return parsed > maxLines ? maxLines : parsed; }); if (!start) return; @@ -131,4 +134,3 @@ function getLinesToHighlight(node: TreeNode, maxLines: number): number[] { return Array.from(lineNumbers).sort((a, b) => a - b); } - From 41ebc98eff5e9b7e77bcce48b037d2e3bb19bed5 Mon Sep 17 00:00:00 2001 From: Deep Date: Sat, 19 Oct 2024 10:27:52 -0400 Subject: [PATCH 11/15] add ts ignore --- src/client/rsc.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/client/rsc.tsx b/src/client/rsc.tsx index 600d561..b53f466 100644 --- a/src/client/rsc.tsx +++ b/src/client/rsc.tsx @@ -2,5 +2,6 @@ import type { MDXRemoteProps } from 'next-mdx-remote/rsc'; import { MDXRemote } from 'next-mdx-remote/rsc'; export const MDXServerComponent = ({ source, components }: MDXRemoteProps) => { + //@ts-ignore return ; }; From 296a1b452168cd2772afebd3ddc48fd972ae6eb1 Mon Sep 17 00:00:00 2001 From: Deep Date: Sat, 19 Oct 2024 10:29:10 -0400 Subject: [PATCH 12/15] add comment --- src/client/rsc.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/rsc.tsx b/src/client/rsc.tsx index b53f466..d1420b3 100644 --- a/src/client/rsc.tsx +++ b/src/client/rsc.tsx @@ -2,6 +2,6 @@ import type { MDXRemoteProps } from 'next-mdx-remote/rsc'; import { MDXRemote } from 'next-mdx-remote/rsc'; export const MDXServerComponent = ({ source, components }: MDXRemoteProps) => { - //@ts-ignore + // @ts-ignore: 'MDXRemote' cannot be used as a JSX component. return ; }; From 4cdfaf31896eb4d42fdfc9e8358e889d04fa1585 Mon Sep 17 00:00:00 2001 From: Deep Date: Sat, 19 Oct 2024 10:31:09 -0400 Subject: [PATCH 13/15] remove optional chain --- src/plugins/rehype/rehypeSyntaxHighlighting.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/rehype/rehypeSyntaxHighlighting.ts b/src/plugins/rehype/rehypeSyntaxHighlighting.ts index 9afe35e..ba1cae1 100644 --- a/src/plugins/rehype/rehypeSyntaxHighlighting.ts +++ b/src/plugins/rehype/rehypeSyntaxHighlighting.ts @@ -30,7 +30,7 @@ const lineHighlightPattern = /\{(.*?)\}/; export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?], TreeNode> = ( options = {} ) => { - if (options?.alias) { + if (options.alias) { refractor.alias(options.alias); } From b6911987d88758a75ace164006c5f24042272249 Mon Sep 17 00:00:00 2001 From: Deep Date: Mon, 21 Oct 2024 00:17:08 -0400 Subject: [PATCH 14/15] cover no lang specify edge case --- src/plugins/rehype/rehypeSyntaxHighlighting.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/plugins/rehype/rehypeSyntaxHighlighting.ts b/src/plugins/rehype/rehypeSyntaxHighlighting.ts index ba1cae1..c5a1f2e 100644 --- a/src/plugins/rehype/rehypeSyntaxHighlighting.ts +++ b/src/plugins/rehype/rehypeSyntaxHighlighting.ts @@ -94,19 +94,28 @@ export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?] }; }; -function getLanguage(node: TreeNode) { +function getLanguage(node: TreeNode): string | null { const className = node.properties.className || []; for (const classListItem of className) { if (classListItem.slice(0, 9) === 'language-') { - return classListItem.slice(9).toLowerCase(); + const lang = classListItem.slice(9).toLowerCase(); + + if (refractor.registered(lang)) { + return lang; + } + return null; } } + return null; } function getLinesToHighlight(node: TreeNode, maxLines: number): number[] { - const meta = typeof node.data?.meta === 'string' ? node.data.meta : undefined; + const meta = + typeof node.data?.meta === 'string' + ? node.data.meta + : node.properties.className?.reduce((acc, item) => acc + ' ' + item, ''); if (!meta) return []; const content = meta.match(lineHighlightPattern)?.[1]?.trim(); From 9994b161939728821f9fbde799f4eadb641aa342 Mon Sep 17 00:00:00 2001 From: "Kaishan (Sam) Ding" Date: Tue, 22 Oct 2024 11:51:41 -0700 Subject: [PATCH 15/15] remove paddings --- src/styles/prism.css | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/styles/prism.css b/src/styles/prism.css index 235d4f8..5895419 100644 --- a/src/styles/prism.css +++ b/src/styles/prism.css @@ -270,8 +270,6 @@ pre[class*='language-'] > code[class*='language-'] { .line-highlight { background: rgb(var(--primary-light) / 0.2); width: 100%; - padding-top: 0.2rem; - padding-bottom: 0.2rem; display: inline-block; position: relative; z-index: 0;