auwla-markdown 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bun.lock DELETED
@@ -1,131 +0,0 @@
1
- {
2
- "lockfileVersion": 1,
3
- "configVersion": 1,
4
- "workspaces": {
5
- "": {
6
- "name": "markdown",
7
- "dependencies": {
8
- "marked": "^18.0.5",
9
- "marked-highlight": "^2.2.4",
10
- },
11
- "devDependencies": {
12
- "@types/bun": "latest",
13
- "@types/prismjs": "^1.26.6",
14
- "prismjs": "^1.30.0",
15
- "shiki": "^4.3.0",
16
- },
17
- "peerDependencies": {
18
- "typescript": "^5",
19
- },
20
- },
21
- },
22
- "packages": {
23
- "@shikijs/core": ["@shikijs/core@4.3.0", "", { "dependencies": { "@shikijs/primitive": "4.3.0", "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-EooU3i9F6IAE8kEu+AnGf9DFZWkQBZ+hJn3tLVbsH+61mtQiva5biai66fAA6nvFPXkLgvrh7BrR7YcJU83xQQ=="],
24
-
25
- "@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.3.0", "", { "dependencies": { "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.6" } }, "sha512-hTv/KiFf2tpiqlACPiztGGurEARWIutB8YUhcrA1pUC7VzzwKO+g5crUocrLztrZ5ro5Z4hbXg7bYclETn3gSQ=="],
26
-
27
- "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.3.0", "", { "dependencies": { "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1vMdN3gHfnKfLYwecUI2ITJI4RhHt96xEaJumVn7Heb0IlJ8WQMIH0Voak+2j22BpSNKdnOfB/pCTPnPm2gq7A=="],
28
-
29
- "@shikijs/langs": ["@shikijs/langs@4.3.0", "", { "dependencies": { "@shikijs/types": "4.3.0" } }, "sha512-rnlqFbBRSys9bT4gl/5rw9RnS0W/I84ZldXPkO7cvlEMoV85TyF/aU01N7/NbSR776RNLjrJKjfFUXJR6wN1Cg=="],
30
-
31
- "@shikijs/primitive": ["@shikijs/primitive@4.3.0", "", { "dependencies": { "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-CPkz64PTa5diRW1ggzMZH9VM/du4RNChYgVtgqrFcgruvIybmCvySv8GkiHSczUHXYuuR8TdKEwFx+UnZMpgdg=="],
32
-
33
- "@shikijs/themes": ["@shikijs/themes@4.3.0", "", { "dependencies": { "@shikijs/types": "4.3.0" } }, "sha512-Avgt05YiT+Y3prjIc9lmQxhJzHBcCfR6cjiFW4OyaMBbt2A6trX5rfjUzx+Vj/mE9qpArYjatnqo9XPjQNW/AQ=="],
34
-
35
- "@shikijs/types": ["@shikijs/types@4.3.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oc8b9U2SYvofKZk8e/737nIX0qwf6eV2vHFATeObAu7r+mUVpLs8Re0BmVkIjAWAYgkmG/CzLNo7rzuBzRu/wQ=="],
36
-
37
- "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
38
-
39
- "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
40
-
41
- "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
42
-
43
- "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
44
-
45
- "@types/node": ["@types/node@26.0.1", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw=="],
46
-
47
- "@types/prismjs": ["@types/prismjs@1.26.6", "", {}, "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw=="],
48
-
49
- "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
50
-
51
- "@ungap/structured-clone": ["@ungap/structured-clone@1.3.2", "", {}, "sha512-5jsZFwgR5rTdKwidH9Qmat75RKwqfpKlWWB1frDkljN127mwqBu8K0PYo7/hFpF03IEJpfVPpCQDY/eDx3iHvA=="],
52
-
53
- "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
54
-
55
- "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
56
-
57
- "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
58
-
59
- "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
60
-
61
- "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
62
-
63
- "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
64
-
65
- "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
66
-
67
- "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
68
-
69
- "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
70
-
71
- "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
72
-
73
- "marked": ["marked@18.0.5", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w=="],
74
-
75
- "marked-highlight": ["marked-highlight@2.2.4", "", { "peerDependencies": { "marked": ">=4 <19" } }, "sha512-PZxisNMJDduSjc0q6uvjsnqqHCXc9s0eyzxDO9sB1eNGJnd/H1/Fu+z6g/liC1dfJdFW4SftMwMlLvsBhUPrqQ=="],
76
-
77
- "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="],
78
-
79
- "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
80
-
81
- "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="],
82
-
83
- "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="],
84
-
85
- "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="],
86
-
87
- "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
88
-
89
- "oniguruma-parser": ["oniguruma-parser@0.12.2", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="],
90
-
91
- "oniguruma-to-es": ["oniguruma-to-es@4.3.6", "", { "dependencies": { "oniguruma-parser": "^0.12.2", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA=="],
92
-
93
- "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="],
94
-
95
- "property-information": ["property-information@7.2.0", "", {}, "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg=="],
96
-
97
- "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="],
98
-
99
- "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="],
100
-
101
- "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="],
102
-
103
- "shiki": ["shiki@4.3.0", "", { "dependencies": { "@shikijs/core": "4.3.0", "@shikijs/engine-javascript": "4.3.0", "@shikijs/engine-oniguruma": "4.3.0", "@shikijs/langs": "4.3.0", "@shikijs/themes": "4.3.0", "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-NKKjWzR6LIGL3sXBrWDw9sDS9cxx42/DkysaNqJEeOWE8Kix5gpak0bc00OfDVEO4oyXSyz8+aRaqKoBD1yo7A=="],
104
-
105
- "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
106
-
107
- "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
108
-
109
- "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
110
-
111
- "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
112
-
113
- "undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="],
114
-
115
- "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="],
116
-
117
- "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="],
118
-
119
- "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
120
-
121
- "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="],
122
-
123
- "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="],
124
-
125
- "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
126
-
127
- "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
128
-
129
- "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
130
- }
131
- }
@@ -1,42 +0,0 @@
1
- import type { HighlighterAdapter } from '../types';
2
- import type { BundledLanguage, BundledTheme, Highlighter } from 'shiki';
3
-
4
- export interface ShikiAdapterOptions {
5
- theme?: BundledTheme | { light: BundledTheme; dark: BundledTheme };
6
- langs?: BundledLanguage[]; // Pre-load specific languages for speed
7
- }
8
-
9
- export function shikiHighlighter(options: ShikiAdapterOptions = {}): HighlighterAdapter {
10
- let shikiInstance: Highlighter | null = null;
11
- const theme = options.theme || 'github-dark';
12
-
13
- return async (code: string, language: string) => {
14
- // 1. Dynamically import Shiki ONLY when this function is actually called
15
- const { createHighlighter } = await import('shiki');
16
-
17
- // 2. Initialize it as a singleton on the first run
18
- if (!shikiInstance) {
19
- shikiInstance = await createHighlighter({
20
- themes: typeof theme === 'string' ? [theme] : [theme.light, theme.dark],
21
- langs: options.langs || [language as BundledLanguage, 'javascript', 'typescript', 'bash', 'html', 'css'],
22
- });
23
- }
24
-
25
- // 3. Load the language if it hasn't been loaded yet (catch-all for unexpected languages)
26
- if (!shikiInstance.getLoadedLanguages().includes(language)) {
27
- await shikiInstance.loadLanguage(language as BundledLanguage).catch(() => {});
28
- }
29
-
30
- const shikiOptions: any = {
31
- lang: language,
32
- };
33
-
34
- if (typeof theme === 'string') {
35
- shikiOptions.theme = theme;
36
- } else {
37
- shikiOptions.themes = theme;
38
- }
39
-
40
- return shikiInstance.codeToHtml(code, shikiOptions);
41
- };
42
- }
package/src/engine.ts DELETED
@@ -1,68 +0,0 @@
1
- import { Marked } from 'marked';
2
- import { markedHighlight } from 'marked-highlight';
3
- import type { MarkdownConfig, MarkdownEngine, ParsedMarkdown } from './types';
4
- import { extractFrontmatter } from './frontmatter';
5
- import { extractHeadings } from './headings';
6
- import {
7
- preprocessComponents,
8
- copyCodeButtonRenderer,
9
- headerAnchorsRenderer,
10
- } from './features';
11
-
12
- /** Configures and returns an isolated instance of the Markdown parsing engine. */
13
- export function createMDConfig(config: MarkdownConfig = {}): MarkdownEngine {
14
- const markedInstance = new Marked({
15
- gfm: true,
16
- });
17
-
18
- if (config.highlighter) {
19
- markedInstance.use(
20
- markedHighlight({
21
- async: true,
22
- highlight: async (code, lang) => {
23
- if (!lang) return code;
24
- return await (config.highlighter as any)(code, lang);
25
- },
26
- })
27
- );
28
- }
29
-
30
- // Register built-in features using custom Marked renderers
31
- const customRenderer: any = {};
32
-
33
- if (config.features?.headerAnchors) {
34
- customRenderer.heading = headerAnchorsRenderer;
35
- }
36
-
37
- if (config.features?.copyCodeButton) {
38
- customRenderer.code = copyCodeButtonRenderer;
39
- }
40
-
41
- if (Object.keys(customRenderer).length > 0) {
42
- markedInstance.use({ renderer: customRenderer });
43
- }
44
-
45
- return {
46
- parse: async (rawString: string): Promise<ParsedMarkdown> => {
47
- let { content, meta } = extractFrontmatter(rawString);
48
- const headings = extractHeadings(content);
49
-
50
- // Preprocess component blocks and custom tags before sending to Marked
51
- content = await preprocessComponents(
52
- content,
53
- async (str) => await markedInstance.parse(str),
54
- async (str) => await markedInstance.parseInline(str),
55
- config.features,
56
- config.components
57
- );
58
-
59
- const html = await markedInstance.parse(content);
60
-
61
- return {
62
- html,
63
- meta,
64
- headings,
65
- };
66
- },
67
- };
68
- }
package/src/features.ts DELETED
@@ -1,412 +0,0 @@
1
- import type { ComponentRenderer, MarkdownFeatures } from './types';
2
-
3
- const inlineWrapperTags = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'span', 'a', 'li']);
4
-
5
- export const defaultComponents: Record<string, ComponentRenderer> = {
6
- Callout: async (props, rawContent, parse, parseInline, features) => {
7
- const type = props.type || 'note';
8
- const title = props.title || type.toUpperCase();
9
- const htmlContent = await parse(rawContent);
10
- const isCollapsible = 'collapsible' in props && props.collapsible !== 'false';
11
- const isCollapsed = props.collapsed === 'true';
12
-
13
- const customClass = props.class ? ` ${props.class}` : '';
14
- const className = `callout callout-${type.toLowerCase()}${customClass}`;
15
-
16
- const otherAttrs = Object.entries(props)
17
- .filter(([k]) => !['value', 'type', 'title', 'collapsible', 'collapsed', 'class'].includes(k))
18
- .map(([k, v]) => `${k}="${v}"`)
19
- .join(' ');
20
- const attrString = otherAttrs ? ` ${otherAttrs}` : '';
21
-
22
- if (isCollapsible) {
23
- const openAttr = isCollapsed ? '' : ' open';
24
- return `<details class="${className}"${openAttr}${attrString}><summary class="callout-title">${title}</summary><div class="callout-content">${htmlContent}</div></details>`;
25
- }
26
-
27
- return `<div class="${className}"${attrString}><div class="callout-title">${title}</div><div class="callout-content">${htmlContent}</div></div>`;
28
- },
29
-
30
- Tabs: async (props, rawContent, parse, parseInline, features) => {
31
- const content = await parse(rawContent);
32
-
33
- const regex = /<div class="tab-panel[^"]*"\s+data-title="([^"]*)"/g;
34
- const titles: string[] = [];
35
- let match;
36
- while ((match = regex.exec(content)) !== null) {
37
- titles.push(match[1]!);
38
- }
39
-
40
- if (titles.length === 0) return content;
41
-
42
- const tabButtons = titles.map((title, idx) => {
43
- const activeClass = idx === 0 ? 'active' : '';
44
- const clickHandler = `const c = this.closest('.tabs-container'); c.querySelectorAll('.tab-btn').forEach((b, i) => { b.classList.toggle('active', b === this); c.querySelectorAll('.tab-panel')[i].style.display = (b === this) ? 'block' : 'none'; })`;
45
- return `<button class="tab-btn ${activeClass}" onclick="${clickHandler}">${title}</button>`;
46
- }).join('');
47
-
48
- let panelIndex = 0;
49
- const adjustedContent = content.replace(/class="tab-panel([^"]*)"/g, (match, customClasses) => {
50
- const activeClass = panelIndex === 0 ? ' active' : '';
51
- const display = panelIndex === 0 ? 'block' : 'none';
52
- panelIndex++;
53
- return `class="tab-panel${customClasses}${activeClass}" style="display: ${display};"`;
54
- });
55
-
56
- const customClass = props.class ? ` ${props.class}` : '';
57
- const className = `tabs-container${customClass}`;
58
- const otherAttrs = Object.entries(props)
59
- .filter(([k]) => !['value', 'class'].includes(k))
60
- .map(([k, v]) => `${k}="${v}"`)
61
- .join(' ');
62
- const attrString = otherAttrs ? ` ${otherAttrs}` : '';
63
-
64
- return `<div class="${className}"${attrString}><div class="tabs-header">${tabButtons}</div><div class="tabs-content">${adjustedContent}</div></div>`;
65
- },
66
-
67
- Tab: async (props, rawContent, parse, parseInline, features) => {
68
- const title = props.title || '';
69
- const content = await parse(rawContent);
70
-
71
- const customClass = props.class ? ` ${props.class}` : '';
72
- const className = `tab-panel${customClass}`;
73
- const otherAttrs = Object.entries(props)
74
- .filter(([k]) => !['value', 'title', 'class'].includes(k))
75
- .map(([k, v]) => `${k}="${v}"`)
76
- .join(' ');
77
- const attrString = otherAttrs ? ` ${otherAttrs}` : '';
78
-
79
- return `<div class="${className}" data-title="${title}"${attrString}>${content}</div>`;
80
- },
81
-
82
- Table: async (props, rawContent, parse, parseInline, features) => {
83
- const content = await parseInline(rawContent);
84
- const defaultClass = 'auwla-table';
85
- const customClass = props.class ? ` ${props.class}` : '';
86
- const mergedClass = `${defaultClass}${customClass}`;
87
- const attrPairs = Object.entries(props)
88
- .filter(([k]) => k !== 'value' && k !== 'class')
89
- .map(([k, v]) => `${k}="${v}"`)
90
- .join(' ');
91
- const attrString = attrPairs ? ` ${attrPairs}` : '';
92
- return `<table class="${mergedClass}"${attrString}>${content}</table>`;
93
- },
94
-
95
- Row: async (props, rawContent, parse, parseInline, features) => {
96
- const content = await parseInline(rawContent);
97
- const attrPairs = Object.entries(props)
98
- .filter(([k]) => k !== 'value')
99
- .map(([k, v]) => `${k}="${v}"`)
100
- .join(' ');
101
- const attrString = attrPairs ? ` ${attrPairs}` : '';
102
- return `<tr${attrString}>${content}</tr>`;
103
- },
104
-
105
- Column: async (props, rawContent, parse, parseInline, features) => {
106
- const content = await parseInline(rawContent);
107
- const attrPairs = Object.entries(props)
108
- .filter(([k]) => k !== 'value')
109
- .map(([k, v]) => `${k}="${v}"`)
110
- .join(' ');
111
- const attrString = attrPairs ? ` ${attrPairs}` : '';
112
- return `<th${attrString}>${content}</th>`;
113
- },
114
-
115
- Cell: async (props, rawContent, parse, parseInline, features) => {
116
- const content = await parseInline(rawContent);
117
- const attrPairs = Object.entries(props)
118
- .filter(([k]) => k !== 'value')
119
- .map(([k, v]) => `${k}="${v}"`)
120
- .join(' ');
121
- const attrString = attrPairs ? ` ${attrPairs}` : '';
122
- return `<td${attrString}>${content}</td>`;
123
- }
124
- };
125
-
126
- function parseAttributes(attrString: string): Record<string, string> {
127
- const attrs: Record<string, string> = {};
128
- const trimmed = attrString.trim();
129
- if (!trimmed) return attrs;
130
-
131
- if (trimmed.startsWith('=')) {
132
- const match = trimmed.match(/^=\s*(?:"(.*?)"|'(.*?)'|([^\s>]+))/);
133
- if (match) {
134
- attrs.value = match[1] ?? match[2] ?? match[3] ?? '';
135
- return attrs;
136
- }
137
- }
138
-
139
- const regex = /(\w+)(?:\s*=\s*(?:"(.*?)"|'(.*?)'|([^\s>]+)))?/g;
140
- let match;
141
- while ((match = regex.exec(attrString)) !== null) {
142
- const key = match[1]!;
143
- const val = match[2] ?? match[3] ?? match[4] ?? 'true';
144
- attrs[key] = val;
145
- }
146
- return attrs;
147
- }
148
-
149
- async function renderComponent(
150
- tagName: string,
151
- props: Record<string, string>,
152
- rawContent: string,
153
- parseFn: (str: string) => Promise<string>,
154
- parseInlineFn: (str: string) => Promise<string>,
155
- features: MarkdownFeatures,
156
- customComponents: Record<string, ComponentRenderer>
157
- ): Promise<string> {
158
- if (customComponents[tagName]) {
159
- return await customComponents[tagName]!(props, rawContent, parseFn, parseInlineFn, features);
160
- }
161
-
162
- if (defaultComponents[tagName]) {
163
- return await defaultComponents[tagName]!(props, rawContent, parseFn, parseInlineFn, features);
164
- }
165
-
166
- const isInline = inlineWrapperTags.has(tagName.toLowerCase());
167
- const compiledContent = isInline ? await parseInlineFn(rawContent) : await parseFn(rawContent);
168
-
169
- // Match custom tag heading (e.g. h1, h2, h3, etc.)
170
- if (isInline && tagName.toLowerCase().startsWith('h') && tagName.length === 2 && !isNaN(Number(tagName.slice(1)))) {
171
- const depth = tagName.toLowerCase().slice(1);
172
- const plainText = compiledContent.replace(/<[^>]*>/g, '');
173
- const id = props.id || plainText
174
- .toLowerCase()
175
- .replace(/[^\w\s-]/g, '')
176
- .replace(/\s+/g, '-');
177
-
178
- const idAttr = `id="${id}"`;
179
- const classAttr = props.class ? `class="${props.class}"` : '';
180
- const otherAttrs = Object.entries(props)
181
- .filter(([k]) => k !== 'value' && k !== 'id' && k !== 'class')
182
- .map(([k, v]) => `${k}="${v}"`)
183
- .join(' ');
184
- const attrString = [idAttr, classAttr, otherAttrs].filter(Boolean).map(s => s.trim()).join(' ');
185
- const finalAttrString = attrString ? ` ${attrString}` : '';
186
-
187
- if (features?.headerAnchors) {
188
- return `<h${depth}${finalAttrString}><a href="#${id}" class="header-anchor" aria-hidden="true">#</a>${compiledContent}</h${depth}>`;
189
- } else {
190
- return `<h${depth}${finalAttrString}>${compiledContent}</h${depth}>`;
191
- }
192
- }
193
-
194
- const attrPairs = Object.entries(props).filter(([k]) => k !== 'value').map(([k, v]) => `${k}="${v}"`).join(' ');
195
- const attrString = attrPairs ? ` ${attrPairs}` : '';
196
-
197
- return `<${tagName}${attrString}>${compiledContent}</${tagName}>`;
198
- }
199
-
200
- /**
201
- * Preprocesses component-like tags (=<TagName>) inside a raw markdown string.
202
- * Looks up default and custom components, compiles, and injects HTML.
203
- */
204
- export async function preprocessComponents(
205
- rawMarkdown: string,
206
- parseFn: (str: string) => Promise<string>,
207
- parseInlineFn: (str: string) => Promise<string>,
208
- features: MarkdownFeatures = {},
209
- customComponents: Record<string, ComponentRenderer> = {}
210
- ): Promise<string> {
211
- const lines = rawMarkdown.split('\n');
212
- const result: string[] = [];
213
-
214
- type OpenBlock = {
215
- tagName: string;
216
- props: Record<string, string>;
217
- contentLines: string[];
218
- };
219
- const stack: OpenBlock[] = [];
220
-
221
- for (const line of lines) {
222
- const trimmed = line.trim();
223
-
224
- // 1. Single-line components (e.g. =<Column>Name=</Column>)
225
- const singleLineMatch = trimmed.match(/^=<(\w+)([^>]*?)>(.*?)=<\/\1>$/);
226
- if (singleLineMatch) {
227
- const tagName = singleLineMatch[1]!;
228
- const attrString = singleLineMatch[2] || '';
229
- const innerContent = singleLineMatch[3] || '';
230
- const props = parseAttributes(attrString);
231
- const html = await renderComponent(tagName, props, innerContent, parseFn, parseInlineFn, features, customComponents);
232
-
233
- if (stack.length > 0) {
234
- stack[stack.length - 1]!.contentLines.push(html);
235
- } else {
236
- result.push(html);
237
- }
238
- continue;
239
- }
240
-
241
- // 2. Self-closing components
242
- if (trimmed.startsWith('=<') && trimmed.endsWith('/>')) {
243
- const match = trimmed.match(/^=<(\w+)([^>]*?)\/>$/);
244
- if (match) {
245
- const tagName = match[1]!;
246
- const attrString = match[2] || '';
247
- const props = parseAttributes(attrString);
248
- const html = await renderComponent(tagName, props, '', parseFn, parseInlineFn, features, customComponents);
249
-
250
- if (stack.length > 0) {
251
- stack[stack.length - 1]!.contentLines.push(html);
252
- } else {
253
- result.push(html);
254
- }
255
- continue;
256
- }
257
- }
258
-
259
- // 3. Opening component tags
260
- if (trimmed.startsWith('=<') && !trimmed.startsWith('=</') && trimmed.endsWith('>')) {
261
- const match = trimmed.match(/^=<(\w+)([^>]*?)>$/);
262
- if (match) {
263
- const tagName = match[1]!;
264
- const attrString = match[2] || '';
265
- const props = parseAttributes(attrString);
266
-
267
- stack.push({
268
- tagName,
269
- props,
270
- contentLines: []
271
- });
272
- continue;
273
- }
274
- }
275
-
276
- // 4. Closing component tags
277
- if (trimmed.startsWith('=</') && trimmed.endsWith('>')) {
278
- const match = trimmed.match(/^=<\/(.*?)>$/);
279
- if (match) {
280
- const tagName = match[1]!.trim();
281
-
282
- if (stack.length > 0 && stack[stack.length - 1]!.tagName === tagName) {
283
- const block = stack.pop()!;
284
- const rawContent = block.contentLines.join('\n');
285
- const html = await renderComponent(block.tagName, block.props, rawContent, parseFn, parseInlineFn, features, customComponents);
286
-
287
- if (stack.length > 0) {
288
- stack[stack.length - 1]!.contentLines.push(html);
289
- } else {
290
- result.push(html);
291
- }
292
- continue;
293
- }
294
- }
295
- }
296
-
297
- // 5. Standard line
298
- if (stack.length > 0) {
299
- stack[stack.length - 1]!.contentLines.push(line);
300
- } else {
301
- result.push(line);
302
- }
303
- }
304
-
305
- while (stack.length > 0) {
306
- const block = stack.pop()!;
307
- result.push(`=<${block.tagName}>`);
308
- result.push(block.contentLines.join('\n'));
309
- result.push(`=</${block.tagName}>`);
310
- }
311
-
312
- return result.join('\n');
313
- }
314
-
315
- function extractRawCode(raw: string): string {
316
- if (raw.startsWith('```') || raw.startsWith('~~~')) {
317
- const lines = raw.split('\n');
318
- return lines.slice(1, -1).join('\n');
319
- }
320
- return raw;
321
- }
322
-
323
- function parseCodeMeta(langString: string): { lang: string; filename?: string; highlightedLines: Set<number> } {
324
- const parts = langString.trim().split(/\s+/);
325
- const lang = parts[0] || '';
326
- let filename: string | undefined;
327
- const highlightedLines = new Set<number>();
328
-
329
- for (const part of parts.slice(1)) {
330
- if (part.startsWith('[') && part.endsWith(']')) {
331
- filename = part.slice(1, -1);
332
- } else if (part.startsWith('{') && part.endsWith('}')) {
333
- const ranges = part.slice(1, -1).split(',');
334
- for (const range of ranges) {
335
- if (range.includes('-')) {
336
- const [startStr, endStr] = range.split('-');
337
- const start = parseInt(startStr || '0', 10);
338
- const end = parseInt(endStr || '0', 10);
339
- if (!isNaN(start) && !isNaN(end)) {
340
- for (let i = start; i <= end; i++) {
341
- highlightedLines.add(i);
342
- }
343
- }
344
- } else {
345
- const val = parseInt(range, 10);
346
- if (!isNaN(val)) {
347
- highlightedLines.add(val);
348
- }
349
- }
350
- }
351
- }
352
- }
353
-
354
- return { lang, filename, highlightedLines };
355
- }
356
-
357
- function injectHighlightedLines(html: string, highlightedLines: Set<number>): string {
358
- if (highlightedLines.size === 0) return html;
359
-
360
- if (html.includes('class="line"')) {
361
- let lineIndex = 0;
362
- return html.replace(/class="line"/g, () => {
363
- lineIndex++;
364
- if (highlightedLines.has(lineIndex)) {
365
- return 'class="line highlighted-line"';
366
- }
367
- return 'class="line"';
368
- });
369
- }
370
-
371
- const lines = html.split('\n');
372
- const processed = lines.map((line, idx) => {
373
- const lineNum = idx + 1;
374
- if (highlightedLines.has(lineNum)) {
375
- return `<div class="highlighted-line">${line}</div>`;
376
- }
377
- return line;
378
- });
379
- return processed.join('\n');
380
- }
381
-
382
- /** Custom Marked renderer for a button that copies code snippets to clipboard. */
383
- export function copyCodeButtonRenderer(info: any): string {
384
- const rawCode = extractRawCode(info.raw);
385
- const escapedCode = encodeURIComponent(rawCode).replace(/'/g, '%27');
386
-
387
- const { lang, filename, highlightedLines } = parseCodeMeta(info.lang ?? '');
388
-
389
- let codeContent = info.text.trim().startsWith('<pre')
390
- ? info.text
391
- : `<pre><code class="language-${lang}">${info.text}</code></pre>`;
392
-
393
- codeContent = injectHighlightedLines(codeContent, highlightedLines);
394
-
395
- const filenameHeader = filename
396
- ? `<div class="code-block-filename">${filename}</div>`
397
- : '';
398
-
399
- return `<div class="code-block-wrapper" style="position: relative;">${filenameHeader}<button onclick="navigator.clipboard.writeText(decodeURIComponent('${escapedCode}')); this.textContent = 'Copied!'; setTimeout(() => this.textContent = 'Copy', 2000)" class="copy-code-btn" style="position: absolute; right: 0.5rem; top: ${filename ? '2.5rem' : '0.5rem'}; z-index: 10;">Copy</button>${codeContent}</div>`;
400
- }
401
-
402
- /** Custom Marked renderer for adding hoverable anchors pointing to header IDs. */
403
- export function headerAnchorsRenderer(this: any, info: any): string {
404
- const text = info.text ?? (this.parser ? this.parser.parseInline(info.tokens) : info.raw);
405
- const depth = info.depth;
406
- const id = text
407
- .toLowerCase()
408
- .replace(/[^\w\s-]/g, '')
409
- .replace(/\s+/g, '-');
410
- return `<h${depth} id="${id}"><a href="#${id}" class="header-anchor" aria-hidden="true">#</a>${text}</h${depth}>`;
411
- }
412
-