Skip to content

Commit e461194

Browse files
committed
Split mdsx config into multiple files for better readability
1 parent b6fcae4 commit e461194

9 files changed

Lines changed: 318 additions & 265 deletions

File tree

docs/mdsx.config.js

Lines changed: 5 additions & 245 deletions
Original file line numberDiff line numberDiff line change
@@ -3,80 +3,12 @@ import { defineConfig } from 'mdsx';
33
import rehypeSlug from 'rehype-slug';
44
import remarkGfm from 'remark-gfm';
55
import rehypePrettyCode from 'rehype-pretty-code';
6-
import { transformerMetaHighlight } from '@shikijs/transformers';
76

8-
import { readFileSync } from 'node:fs';
9-
import path, { resolve } from 'node:path';
10-
import process from 'node:process';
11-
// import { fileURLToPath } from "node:url";
12-
// import prettier from "@prettier/sync";
13-
// import { getHighlighter } from 'shiki';
14-
// import { createHighlighter } from 'shiki';
15-
import { u } from 'unist-builder';
16-
import { visit } from 'unist-util-visit';
17-
// import { codeBlockPrettierConfig } from "./other/code-block-prettier.js";
18-
// import { rehypeCustomHighlight } from '@mdsx/rehype-custom-highlighter';
19-
20-
/**
21-
* Custom transformer for diff highlighting
22-
* Processes code blocks with 'diff' in the meta string to add/remove line styling
23-
*/
24-
function shikiDiffTransformer() {
25-
return {
26-
name: 'diff-transformer',
27-
code(node) {
28-
// Check if this code block has 'diff' in the meta string
29-
const metaString = this.options.meta?.__raw || '';
30-
if (!metaString.includes('diff')) return;
31-
32-
// Add class to the pre element
33-
this.addClassToHast(this.pre, 'has-diff');
34-
35-
// Get all line elements
36-
const lines = node.children.filter((child) => child.type === 'element');
37-
38-
for (const line of lines) {
39-
// Get all text tokens in this line
40-
const tokens = line.children.filter((child) => child.type === 'element');
41-
42-
if (tokens.length === 0) continue;
43-
44-
// Check the first token's text content
45-
const firstToken = tokens[0];
46-
const textNodes = firstToken.children?.filter((child) => child.type === 'text');
47-
48-
if (!textNodes || textNodes.length === 0) continue;
49-
50-
const firstText = textNodes[0];
51-
const text = firstText.value;
52-
53-
if (text.startsWith('+')) {
54-
this.addClassToHast(line, 'diff-add');
55-
firstText.value = text.slice(1);
56-
} else if (text.startsWith('-')) {
57-
this.addClassToHast(line, 'diff-remove');
58-
firstText.value = text.slice(1);
59-
}
60-
}
61-
}
62-
};
63-
}
64-
65-
/**
66-
* @type {import('rehype-pretty-code').Options}
67-
*/
68-
const prettyCodeOptions = {
69-
theme: {
70-
light: 'github-light',
71-
dark: 'github-dark'
72-
},
73-
keepBackground: false,
74-
defaultLang: {
75-
block: 'plaintext'
76-
// inline: "plaintext",
77-
},
78-
transformers: [shikiDiffTransformer(), transformerMetaHighlight()]
79-
};
7+
import {
8+
prettyCodeOptions,
9+
rehypeCodeBlockTitle,
10+
rehypeHandleCodeBlocks
11+
} from './src/lib/markdown/config/index.js';
8012

8113
export const mdsxConfig = defineConfig({
8214
extensions: ['.md'],
@@ -94,175 +26,3 @@ export const mdsxConfig = defineConfig({
9426
}
9527
}
9628
});
97-
98-
/**
99-
* Adds the source code to component examples.
100-
*
101-
* @returns {HastTransformer} - unified transformer
102-
*/
103-
function rehypeComponentExample() {
104-
return async (tree) => {
105-
const componentRegex = /component="([^"]+)"/;
106-
const nameRegex = /name="([^"]+)"/;
107-
108-
visit(tree, (node, index, parent) => {
109-
if (node?.type === 'raw' && node?.value?.startsWith('<SourceExample')) {
110-
const currNode = node;
111-
112-
const componentMatch = currNode.value.match(componentRegex);
113-
const component = componentMatch ? componentMatch[1] : null;
114-
if (!component) return null;
115-
116-
const nameMatch = currNode.value.match(nameRegex);
117-
const name = nameMatch ? nameMatch[1] : null;
118-
if (!name) return null;
119-
120-
try {
121-
const sourceCode = getComponentSourceFileContent(component, name);
122-
if (!sourceCode)
123-
throw new Error(
124-
`Could not find source code for component: ${component} example: ${name}`
125-
);
126-
127-
const sourceCodeNode = u('element', {
128-
tagName: 'pre',
129-
properties: {
130-
className: ['code']
131-
},
132-
children: [
133-
u('element', {
134-
tagName: 'code',
135-
properties: {
136-
className: [`language-svelte`]
137-
},
138-
attributes: {},
139-
children: [
140-
{
141-
type: 'text',
142-
value: sourceCode
143-
}
144-
]
145-
})
146-
]
147-
});
148-
if (!index) return;
149-
parent.children.splice(index + 1, 0, sourceCodeNode);
150-
} catch (e) {
151-
console.error(e);
152-
}
153-
}
154-
});
155-
};
156-
}
157-
158-
function getComponentSourceFileContent(component, name) {
159-
if (!component || !name) return null;
160-
161-
const filePath = path.join(
162-
process.cwd(),
163-
`./src/examples/components/${component}/${name}.svelte`
164-
);
165-
try {
166-
const fileContents = readFileSync(filePath, 'utf-8');
167-
168-
// return prettier.format(
169-
// transformComponentSourceContent(fileContents),
170-
// codeBlockPrettierConfig
171-
// );
172-
return fileContents;
173-
} catch (e) {
174-
console.error(`Error reading file at ${filePath}:`, e);
175-
return null;
176-
}
177-
}
178-
179-
/**
180-
* Handles metadata attributes for code blocks with titles
181-
* Adds data-metadata attribute when a figcaption (title) is present
182-
*/
183-
function rehypeCodeBlockTitle() {
184-
return (tree) => {
185-
visit(tree, 'element', (node) => {
186-
if (
187-
node.tagName === 'figure' &&
188-
node.properties?.['data-rehype-pretty-code-figure'] !== undefined
189-
) {
190-
const preElement = node.children?.at(-1);
191-
const firstChild = node.children?.at(0);
192-
193-
if (
194-
preElement &&
195-
preElement.type === 'element' &&
196-
preElement.tagName === 'pre' &&
197-
firstChild &&
198-
firstChild.type === 'element' &&
199-
firstChild.tagName === 'figcaption'
200-
) {
201-
node.properties['data-metadata'] = '';
202-
preElement.properties['data-metadata'] = '';
203-
}
204-
}
205-
});
206-
};
207-
}
208-
209-
/**
210-
* Adds custom classes and data attributes to code blocks based on meta string
211-
* Supports syntax like ```js frame title="My Code" showLineNumbers
212-
*/
213-
function rehypeHandleCodeBlocks() {
214-
return (tree) => {
215-
visit(tree, 'element', (node) => {
216-
if (node.tagName === 'pre') {
217-
node.properties.className = node.properties.className || [];
218-
219-
// Check if the code element has a data-meta attribute from rehype-pretty-code
220-
const codeNode = node.children?.find((child) => child.tagName === 'code');
221-
const meta = codeNode?.data?.meta || codeNode?.properties?.metastring;
222-
223-
if (meta) {
224-
// Check for 'frame' keyword
225-
if (meta.includes('frame')) {
226-
node.properties['data-frame'] = '';
227-
}
228-
229-
// Extract title="..." if present
230-
const titleMatch = meta.match(/title="([^"]+)"/);
231-
if (titleMatch) {
232-
node.properties['data-title'] = titleMatch[1];
233-
}
234-
235-
// Check for line numbers flag in meta
236-
if (meta.includes('showLineNumbers') || meta.includes('ln')) {
237-
node.properties['data-line-numbers'] = '';
238-
}
239-
}
240-
241-
// Check recursively for [data-line] attribute in nested children
242-
function hasDataLine(element) {
243-
if (element.properties?.['data-line'] !== undefined) {
244-
return true;
245-
}
246-
if (element.children) {
247-
return element.children.some((child) => hasDataLine(child));
248-
}
249-
return false;
250-
}
251-
252-
if (codeNode && hasDataLine(codeNode)) {
253-
node.properties['data-line-numbers'] = '';
254-
}
255-
256-
// Extract language from code element
257-
const codeClassName = codeNode?.properties?.className;
258-
if (Array.isArray(codeClassName)) {
259-
const langClass = codeClassName.find((cls) => cls.startsWith('language-'));
260-
if (langClass) {
261-
const language = langClass.replace('language-', '');
262-
node.properties['data-language'] = language;
263-
}
264-
}
265-
}
266-
});
267-
};
268-
}

docs/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,13 @@
5858
"@types/d3-scale-chromatic": "^3.1.0",
5959
"@types/d3-shape": "^3.1.7",
6060
"@types/d3-time": "^3.0.4",
61+
"@types/hast": "^3.0.4",
6162
"@types/node": "^24.10.1",
6263
"@types/shapefile": "^0.6.4",
6364
"@types/topojson-client": "^3.1.5",
6465
"@types/topojson-simplify": "^3.0.3",
6566
"@types/topojson-specification": "^1.0.5",
67+
"@types/unist": "^3.0.3",
6668
"@vitest/browser": "^4.0.15",
6769
"@vitest/browser-playwright": "^4.0.15",
6870
"d3-array": "^3.2.4",
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Rehype plugins
2+
export { rehypeCodeBlockTitle } from '../rehype/code-block-title.js';
3+
export { rehypeHandleCodeBlocks } from '../rehype/handle-code-blocks.js';
4+
export { rehypeComponentExample } from '../rehype/component-example.js';
5+
6+
// Transformers
7+
export { shikiDiffTransformer } from '../transformers/shiki-diff.js';
8+
9+
// Configuration
10+
export { prettyCodeOptions } from './pretty-code.js';
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { transformerMetaHighlight } from '@shikijs/transformers';
2+
import { shikiDiffTransformer } from '../transformers/shiki-diff.js';
3+
4+
/**
5+
* @type {import('rehype-pretty-code').Options}
6+
*/
7+
export const prettyCodeOptions = {
8+
theme: {
9+
light: 'github-light',
10+
dark: 'github-dark'
11+
},
12+
keepBackground: false,
13+
defaultLang: {
14+
block: 'plaintext'
15+
// inline: "plaintext",
16+
},
17+
transformers: [shikiDiffTransformer(), transformerMetaHighlight()]
18+
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { visit } from 'unist-util-visit';
2+
3+
/**
4+
* Handles metadata attributes for code blocks with titles
5+
* Adds data-metadata attribute when a figcaption (title) is present
6+
* @returns {(tree: import('hast').Root) => void}
7+
*/
8+
export function rehypeCodeBlockTitle() {
9+
return (tree) => {
10+
visit(tree, 'element', (node) => {
11+
if (
12+
node.tagName === 'figure' &&
13+
node.properties?.['data-rehype-pretty-code-figure'] !== undefined
14+
) {
15+
const preElement = node.children?.at(-1);
16+
const firstChild = node.children?.at(0);
17+
18+
if (
19+
preElement &&
20+
preElement.type === 'element' &&
21+
preElement.tagName === 'pre' &&
22+
firstChild &&
23+
firstChild.type === 'element' &&
24+
firstChild.tagName === 'figcaption'
25+
) {
26+
node.properties['data-metadata'] = '';
27+
preElement.properties['data-metadata'] = '';
28+
}
29+
}
30+
});
31+
};
32+
}

0 commit comments

Comments
 (0)