@scalar/code-highlight 0.3.0 → 0.3.2

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/code/highlight.js +58 -38
  3. package/dist/code/index.js +1 -5
  4. package/dist/code/line-numbers.js +76 -51
  5. package/dist/constants.js +10 -12
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +5 -14
  9. package/dist/languages/basic.js +28 -31
  10. package/dist/languages/index.js +3 -9
  11. package/dist/languages/json-yaml.js +7 -10
  12. package/dist/languages/standard.js +115 -100
  13. package/dist/markdown/index.d.ts +1 -1
  14. package/dist/markdown/index.d.ts.map +1 -1
  15. package/dist/markdown/index.js +1 -2
  16. package/dist/markdown/markdown.js +148 -100
  17. package/dist/rehype-alert/index.js +1 -5
  18. package/dist/rehype-alert/rehype-alert.js +99 -91
  19. package/dist/rehype-highlight/index.js +1 -5
  20. package/dist/rehype-highlight/rehype-highlight.js +93 -85
  21. package/package.json +11 -17
  22. package/dist/code/highlight.js.map +0 -7
  23. package/dist/code/index.js.map +0 -7
  24. package/dist/code/line-numbers.js.map +0 -7
  25. package/dist/constants.js.map +0 -7
  26. package/dist/index.js.map +0 -7
  27. package/dist/languages/basic.js.map +0 -7
  28. package/dist/languages/index.js.map +0 -7
  29. package/dist/languages/json-yaml.js.map +0 -7
  30. package/dist/languages/standard.js.map +0 -7
  31. package/dist/markdown/index.js.map +0 -7
  32. package/dist/markdown/markdown.js.map +0 -7
  33. package/dist/rehype-alert/index.js.map +0 -7
  34. package/dist/rehype-alert/rehype-alert.js.map +0 -7
  35. package/dist/rehype-highlight/index.js.map +0 -7
  36. package/dist/rehype-highlight/rehype-highlight.js.map +0 -7
@@ -1,94 +1,102 @@
1
- import { visit } from "unist-util-visit";
2
- const ALERT_TYPES = ["note", "tip", "important", "warning", "caution", "success"];
1
+ import { visit } from 'unist-util-visit';
2
+ const ALERT_TYPES = ['note', 'tip', 'important', 'warning', 'caution', 'success'];
3
+ // Simple whitespace check function
3
4
  function isWhitespace(node) {
4
- return node.type === "text" && typeof node.value === "string" && /^\s*$/.test(node.value);
5
+ return node.type === 'text' && typeof node.value === 'string' && /^\s*$/.test(node.value);
5
6
  }
6
- function rehypeAlert() {
7
- return (tree) => {
8
- visit(tree, "element", (node, index, parent) => {
9
- if (node.tagName !== "blockquote" || typeof index !== "number" || !parent || parent.type !== "root") {
10
- return;
11
- }
12
- const headIndex = node.children.findIndex((child) => !isWhitespace(child));
13
- if (headIndex === -1) {
14
- return;
15
- }
16
- const head = node.children[headIndex];
17
- if (!head || head.type !== "element" || head.tagName !== "p") {
18
- return;
19
- }
20
- const text = head.children[0];
21
- if (!text || text.type !== "text" || !text.value.startsWith("[!")) {
22
- return;
23
- }
24
- const end = text.value.indexOf("]");
25
- if (end === -1) {
26
- return;
27
- }
28
- const alertType = text.value.slice(2, end).toLowerCase();
29
- if (!ALERT_TYPES.includes(alertType)) {
30
- return;
31
- }
32
- if (end + 1 === text.value.length) {
33
- const next = head.children[1];
34
- if (next) {
35
- if (next.type !== "element" || next.tagName !== "br") {
36
- return;
37
- }
38
- if (!head.children[2]) {
39
- return;
40
- }
41
- head.children = head.children.slice(2);
42
- const node2 = head.children[0];
43
- if (node2 && node2.type === "text" && node2.value.charAt(0) === "\n") {
44
- node2.value = node2.value.slice(1);
45
- }
46
- } else {
47
- const skipped = headIndex + 1 < node.children.length && isWhitespace(node.children[headIndex + 1]);
48
- const nextIndex = skipped ? headIndex + 2 : headIndex + 1;
49
- if (nextIndex >= node.children.length || node.children[nextIndex]?.type !== "element") {
50
- return;
51
- }
52
- node.children = node.children.slice(nextIndex);
53
- }
54
- } else if (text.value.charAt(end + 1) === "\n" && // Check if the next character is a newline or a non-whitespace character
55
- (end + 2 === text.value.length || !/^\s*$/.test(text.value.slice(end + 2)))) {
56
- text.value = text.value.slice(end + 2);
57
- } else {
58
- text.value = text.value.replace(/^\s*\[!.*?\]\s*/, "");
59
- }
60
- const contentChildren = [];
61
- for (let i = headIndex; i < node.children.length; i++) {
62
- const child = node.children[i];
63
- if (child?.type === "element" && child.tagName === "p" && child.children) {
64
- contentChildren.push(...child.children);
65
- } else {
66
- contentChildren.push(child);
67
- }
68
- }
69
- parent.children[index] = {
70
- type: "element",
71
- tagName: "div",
72
- properties: { className: ["markdown-alert", `markdown-alert-${alertType}`] },
73
- children: [
74
- {
75
- type: "element",
76
- tagName: "div",
77
- properties: { className: ["markdown-alert-icon"] },
78
- children: []
79
- },
80
- {
81
- type: "element",
82
- tagName: "div",
83
- properties: { className: ["markdown-alert-content"] },
84
- children: [{ type: "text", value: " " }, ...contentChildren]
85
- }
86
- ]
87
- };
88
- });
89
- };
7
+ export function rehypeAlert() {
8
+ return (tree) => {
9
+ visit(tree, 'element', (node, index, parent) => {
10
+ if (node.tagName !== 'blockquote' || typeof index !== 'number' || !parent || parent.type !== 'root') {
11
+ return;
12
+ }
13
+ // Find the first non-whitespace child
14
+ const headIndex = node.children.findIndex((child) => !isWhitespace(child));
15
+ if (headIndex === -1) {
16
+ return;
17
+ }
18
+ const head = node.children[headIndex];
19
+ if (!head || head.type !== 'element' || head.tagName !== 'p') {
20
+ return;
21
+ }
22
+ const text = head.children[0];
23
+ if (!text || text.type !== 'text' || !text.value.startsWith('[!')) {
24
+ return;
25
+ }
26
+ const end = text.value.indexOf(']');
27
+ if (end === -1) {
28
+ return;
29
+ }
30
+ // Extract the alert type
31
+ const alertType = text.value.slice(2, end).toLowerCase();
32
+ if (!ALERT_TYPES.includes(alertType)) {
33
+ return;
34
+ }
35
+ // Remove the blockquote if it's empty
36
+ if (end + 1 === text.value.length) {
37
+ const next = head.children[1];
38
+ if (next) {
39
+ if (next.type !== 'element' || next.tagName !== 'br') {
40
+ return;
41
+ }
42
+ if (!head.children[2]) {
43
+ return;
44
+ }
45
+ head.children = head.children.slice(2);
46
+ const node = head.children[0];
47
+ if (node && node.type === 'text' && node.value.charAt(0) === '\n') {
48
+ node.value = node.value.slice(1);
49
+ }
50
+ }
51
+ else {
52
+ const skipped = headIndex + 1 < node.children.length && isWhitespace(node.children[headIndex + 1]);
53
+ const nextIndex = skipped ? headIndex + 2 : headIndex + 1;
54
+ if (nextIndex >= node.children.length || node.children[nextIndex]?.type !== 'element') {
55
+ return;
56
+ }
57
+ node.children = node.children.slice(nextIndex);
58
+ }
59
+ }
60
+ else if (text.value.charAt(end + 1) === '\n' &&
61
+ // Check if the next character is a newline or a non-whitespace character
62
+ (end + 2 === text.value.length || !/^\s*$/.test(text.value.slice(end + 2)))) {
63
+ text.value = text.value.slice(end + 2);
64
+ }
65
+ else {
66
+ // Remove the alert marker if it's not followed by a newline or a non-whitespace character
67
+ text.value = text.value.replace(/^\s*\[!.*?\]\s*/, '');
68
+ }
69
+ // Extract content from paragraphs to avoid wrapping in <p> tags
70
+ const contentChildren = [];
71
+ for (let i = headIndex; i < node.children.length; i++) {
72
+ const child = node.children[i];
73
+ if (child?.type === 'element' && child.tagName === 'p' && child.children) {
74
+ contentChildren.push(...child.children);
75
+ }
76
+ else {
77
+ contentChildren.push(child);
78
+ }
79
+ }
80
+ // Replace the blockquote with a div containing the alert
81
+ parent.children[index] = {
82
+ type: 'element',
83
+ tagName: 'div',
84
+ properties: { className: ['markdown-alert', `markdown-alert-${alertType}`] },
85
+ children: [
86
+ {
87
+ type: 'element',
88
+ tagName: 'div',
89
+ properties: { className: ['markdown-alert-icon'] },
90
+ children: [],
91
+ },
92
+ {
93
+ type: 'element',
94
+ tagName: 'div',
95
+ properties: { className: ['markdown-alert-content'] },
96
+ children: [{ type: 'text', value: ' ' }, ...contentChildren],
97
+ },
98
+ ],
99
+ };
100
+ });
101
+ };
90
102
  }
91
- export {
92
- rehypeAlert
93
- };
94
- //# sourceMappingURL=rehype-alert.js.map
@@ -1,5 +1 @@
1
- import { rehypeHighlight } from "./rehype-highlight.js";
2
- export {
3
- rehypeHighlight
4
- };
5
- //# sourceMappingURL=index.js.map
1
+ export { rehypeHighlight } from './rehype-highlight.js';
@@ -1,90 +1,98 @@
1
- import { toText } from "hast-util-to-text";
2
- import { createLowlight } from "lowlight";
3
- import { visit } from "unist-util-visit";
4
- import { lowlightLanguageMappings } from "../constants.js";
1
+ import { toText } from 'hast-util-to-text';
2
+ import { createLowlight } from 'lowlight';
3
+ import { visit } from 'unist-util-visit';
4
+ import { lowlightLanguageMappings } from '../constants.js';
5
5
  const emptyOptions = {};
6
- function rehypeHighlight(options) {
7
- const settings = options || emptyOptions;
8
- const aliases = settings.aliases;
9
- const detect = options?.detect ?? false;
10
- const languages = settings.languages;
11
- const plainText = settings.plainText;
12
- const prefix = settings.prefix;
13
- const subset = settings.subset;
14
- let name = "hljs";
15
- const lowlight = options?.lowlight ?? createLowlight(languages);
16
- if (aliases) {
17
- lowlight.registerAlias(aliases);
18
- }
19
- if (prefix) {
20
- const pos = prefix.indexOf("-");
21
- name = pos > -1 ? prefix.slice(0, pos) : prefix;
22
- }
23
- return (tree, file) => {
24
- visit(tree, "element", (node, _, parent) => {
25
- if (node.tagName !== "code" || !parent || parent.type !== "element" || parent.tagName !== "pre") {
26
- return;
27
- }
28
- const lang = language(node);
29
- if (lang === "no-highlight" || !lang && !detect || lang && plainText?.includes(lang)) {
30
- return;
31
- }
32
- if (!Array.isArray(node.properties.className)) {
33
- node.properties.className = [];
34
- }
35
- if (!node.properties.className.includes(name)) {
36
- node.properties.className.unshift(name);
37
- }
38
- let result;
39
- try {
40
- result = lang ? lowlight.highlight(lang, toText(parent), { prefix }) : lowlight.highlightAuto(toText(parent), { prefix, subset });
41
- } catch (error) {
42
- const cause = error;
43
- if (lang && /Unknown language/.test(cause.message)) {
44
- file.message(`Cannot highlight as \`${lang}\`, it's not registered`, {
45
- ancestors: [parent, node],
46
- cause,
47
- place: node.position,
48
- ruleId: "missing-language",
49
- source: "rehype-highlight"
50
- });
51
- return;
52
- }
53
- throw cause;
54
- }
55
- if (!lang && result.data?.language) {
56
- node.properties.className.push("language-" + result.data.language);
57
- }
58
- if (result.children.length > 0) {
59
- node.children = result.children;
60
- }
61
- });
62
- };
63
- }
64
- function language(node) {
65
- const list = node.properties.className;
66
- if (!Array.isArray(list)) {
67
- return "";
68
- }
69
- const name = list.reduce((result, _item) => {
70
- if (result) {
71
- return result;
6
+ /**
7
+ * Lowlight syntax highlighting plugin for rehype pipelines
8
+ *
9
+ * Derived from: @url https://github.com/rehypejs/rehype-highlight/blob/main/lib/index.js
10
+ */
11
+ export function rehypeHighlight(options) {
12
+ const settings = options || emptyOptions;
13
+ const aliases = settings.aliases;
14
+ const detect = options?.detect ?? false;
15
+ const languages = settings.languages;
16
+ const plainText = settings.plainText;
17
+ const prefix = settings.prefix;
18
+ const subset = settings.subset;
19
+ let name = 'hljs';
20
+ // Create a lowlight instance if not provided
21
+ const lowlight = options?.lowlight ?? createLowlight(languages);
22
+ if (aliases) {
23
+ lowlight.registerAlias(aliases);
72
24
  }
73
- const item = String(_item);
74
- if (item === "no-highlight" || item === "nohighlight") {
75
- return "no-highlight";
25
+ if (prefix) {
26
+ const pos = prefix.indexOf('-');
27
+ name = pos > -1 ? prefix.slice(0, pos) : prefix;
76
28
  }
77
- if (item.slice(0, 5) === "lang-") {
78
- return item.slice(5);
79
- }
80
- if (item.slice(0, 9) === "language-") {
81
- return item.slice(9);
29
+ /** Transform.*/
30
+ return (tree, file) => {
31
+ visit(tree, 'element', (node, _, parent) => {
32
+ if (node.tagName !== 'code' || !parent || parent.type !== 'element' || parent.tagName !== 'pre') {
33
+ return;
34
+ }
35
+ const lang = language(node);
36
+ if (lang === 'no-highlight' || (!lang && !detect) || (lang && plainText?.includes(lang))) {
37
+ return;
38
+ }
39
+ if (!Array.isArray(node.properties.className)) {
40
+ node.properties.className = [];
41
+ }
42
+ if (!node.properties.className.includes(name)) {
43
+ node.properties.className.unshift(name);
44
+ }
45
+ let result;
46
+ try {
47
+ result = lang
48
+ ? lowlight.highlight(lang, toText(parent), { prefix })
49
+ : lowlight.highlightAuto(toText(parent), { prefix, subset });
50
+ }
51
+ catch (error) {
52
+ const cause = error;
53
+ if (lang && /Unknown language/.test(cause.message)) {
54
+ file.message(`Cannot highlight as \`${lang}\`, it's not registered`, {
55
+ ancestors: [parent, node],
56
+ cause,
57
+ place: node.position,
58
+ ruleId: 'missing-language',
59
+ source: 'rehype-highlight',
60
+ });
61
+ /* c8 ignore next 5 -- throw arbitrary hljs errors */
62
+ return;
63
+ }
64
+ throw cause;
65
+ }
66
+ if (!lang && result.data?.language) {
67
+ node.properties.className.push('language-' + result.data.language);
68
+ }
69
+ if (result.children.length > 0) {
70
+ node.children = result.children;
71
+ }
72
+ });
73
+ };
74
+ }
75
+ /** Get the programming language of `node` or an empty string */
76
+ function language(node) {
77
+ const list = node.properties.className;
78
+ if (!Array.isArray(list)) {
79
+ return '';
82
80
  }
83
- return result;
84
- }, "");
85
- return lowlightLanguageMappings[name || ""] || name;
81
+ const name = list.reduce((result, _item) => {
82
+ if (result) {
83
+ return result;
84
+ }
85
+ const item = String(_item);
86
+ if (item === 'no-highlight' || item === 'nohighlight') {
87
+ return 'no-highlight';
88
+ }
89
+ if (item.slice(0, 5) === 'lang-') {
90
+ return item.slice(5);
91
+ }
92
+ if (item.slice(0, 9) === 'language-') {
93
+ return item.slice(9);
94
+ }
95
+ return result;
96
+ }, '');
97
+ return lowlightLanguageMappings[name || ''] || name;
86
98
  }
87
- export {
88
- rehypeHighlight
89
- };
90
- //# sourceMappingURL=rehype-highlight.js.map
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "lowlight",
17
17
  "highlightjs"
18
18
  ],
19
- "version": "0.3.0",
19
+ "version": "0.3.2",
20
20
  "engines": {
21
21
  "node": ">=22"
22
22
  },
@@ -80,31 +80,25 @@
80
80
  "rehype-parse": "^9.0.1",
81
81
  "rehype-raw": "^7.0.0",
82
82
  "rehype-sanitize": "^6.0.0",
83
- "rehype-stringify": "^10.0.0",
84
- "remark-gfm": "^4.0.0",
83
+ "rehype-stringify": "^10.0.1",
84
+ "remark-gfm": "^4.0.1",
85
85
  "remark-parse": "^11.0.0",
86
- "remark-rehype": "^11.1.0",
86
+ "remark-rehype": "^11.1.2",
87
87
  "remark-stringify": "^11.0.0",
88
- "unified": "^11.0.4",
89
- "unist-util-visit": "^5.0.0"
88
+ "unified": "^11.0.5",
89
+ "unist-util-visit": "^5.1.0"
90
90
  },
91
91
  "devDependencies": {
92
92
  "@types/hast": "^3.0.4",
93
93
  "@types/mdast": "^4.0.4",
94
- "vfile": "^6.0.1",
95
- "vite": "^7.3.1",
96
- "@scalar/build-tooling": "0.5.0",
97
- "@scalar/themes": "0.15.0"
94
+ "vfile": "^6.0.3",
95
+ "vite": "8.0.0",
96
+ "@scalar/themes": "0.15.2"
98
97
  },
99
98
  "scripts": {
100
- "build": "scalar-build-esbuild",
99
+ "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && cp -r src/css dist/css",
101
100
  "dev": "vite",
102
- "format": "scalar-format",
103
- "format:check": "scalar-format-check",
104
- "lint:check": "scalar-lint-check",
105
- "lint:fix": "scalar-lint-fix",
106
101
  "test": "vitest",
107
- "types:build": "scalar-types-build",
108
- "types:check": "scalar-types-check"
102
+ "types:check": "tsc --noEmit"
109
103
  }
110
104
  }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/code/highlight.ts"],
4
- "sourcesContent": ["import type { Element, Root } from 'hast'\nimport type { LanguageFn } from 'highlight.js'\nimport rehypeParse from 'rehype-parse'\nimport rehypeStringify from 'rehype-stringify'\nimport { type Plugin, unified } from 'unified'\nimport { visit } from 'unist-util-visit'\n\nimport { lowlightLanguageMappings } from '@/constants'\nimport { rehypeHighlight } from '@/rehype-highlight'\n\nimport { codeBlockLinesPlugin } from './line-numbers'\n\n/**\n * Syntax highlights a code string using the `rehype-highlight` library.\n */\nexport function syntaxHighlight(\n codeString: string,\n options: {\n lang: string\n languages: Record<string, LanguageFn>\n lineNumbers?: boolean\n maskCredentials?: string | string[]\n },\n) {\n // Simple restriction on credentials to prevent unexpected behavior\n const credentials = (\n typeof options?.maskCredentials === 'string' ? [options.maskCredentials] : (options?.maskCredentials ?? [])\n ).filter((c) => {\n // Credentials must be at least 3 characters to mask.\n if (c.length < 3) {\n return false\n }\n\n return true\n })\n\n // Classname is used by lowlight to select the language model\n const className = `language-${lowlightLanguageMappings[options.lang] ?? options.lang}`\n\n // biome-ignore lint/suspicious/noEmptyBlockStatements: empty plugin\n const nullPlugin = (() => {}) satisfies Plugin\n\n const html = unified()\n // Parses markdown\n .use(rehypeParse, { fragment: true })\n // Raw code string must be injected after initial hast parsing\n // so that HTML code is not parsed into the hast tree\n .use(injectRawCodeStringPlugin(codeString))\n // Syntax highlighting\n .use(rehypeHighlight, {\n languages: options.languages,\n })\n .use(options?.lineNumbers ? codeBlockLinesPlugin : nullPlugin)\n // Converts the HTML AST to a string\n .use(rehypeStringify)\n // Run the pipeline\n .processSync(`<pre><code class=\"${className}\"></code></pre>`)\n\n const htmlString = html.toString()\n\n // Replace any credentials with a wrapper element\n return credentials.length\n ? credentials.reduce(\n (acc, credential) =>\n acc\n .split(credential)\n .join(`<span class=\"credential\"><span class=\"credential-value\">${credential}</span></span>`),\n htmlString,\n )\n : htmlString\n}\n\n/**\n * To prevent unified from parsing any content of the code string we inject\n * it as a raw text node into the AST tree as a child of the code element\n */\nfunction injectRawCodeStringPlugin(rawCodeString: string) {\n return () => (tree: Root) => {\n visit(tree, 'element', (node: Element) => {\n if (node.tagName === 'code') {\n node.children.push({\n type: 'text',\n value: rawCodeString,\n })\n }\n })\n }\n}\n"],
5
- "mappings": "AAEA,OAAO,iBAAiB;AACxB,OAAO,qBAAqB;AAC5B,SAAsB,eAAe;AACrC,SAAS,aAAa;AAEtB,SAAS,gCAAgC;AACzC,SAAS,uBAAuB;AAEhC,SAAS,4BAA4B;AAK9B,SAAS,gBACd,YACA,SAMA;AAEA,QAAM,eACJ,OAAO,SAAS,oBAAoB,WAAW,CAAC,QAAQ,eAAe,IAAK,SAAS,mBAAmB,CAAC,GACzG,OAAO,CAAC,MAAM;AAEd,QAAI,EAAE,SAAS,GAAG;AAChB,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,CAAC;AAGD,QAAM,YAAY,YAAY,yBAAyB,QAAQ,IAAI,KAAK,QAAQ,IAAI;AAGpF,QAAM,aAAc,MAAM;AAAA,EAAC;AAE3B,QAAM,OAAO,QAAQ,EAElB,IAAI,aAAa,EAAE,UAAU,KAAK,CAAC,EAGnC,IAAI,0BAA0B,UAAU,CAAC,EAEzC,IAAI,iBAAiB;AAAA,IACpB,WAAW,QAAQ;AAAA,EACrB,CAAC,EACA,IAAI,SAAS,cAAc,uBAAuB,UAAU,EAE5D,IAAI,eAAe,EAEnB,YAAY,qBAAqB,SAAS,iBAAiB;AAE9D,QAAM,aAAa,KAAK,SAAS;AAGjC,SAAO,YAAY,SACf,YAAY;AAAA,IACV,CAAC,KAAK,eACJ,IACG,MAAM,UAAU,EAChB,KAAK,2DAA2D,UAAU,gBAAgB;AAAA,IAC/F;AAAA,EACF,IACA;AACN;AAMA,SAAS,0BAA0B,eAAuB;AACxD,SAAO,MAAM,CAAC,SAAe;AAC3B,UAAM,MAAM,WAAW,CAAC,SAAkB;AACxC,UAAI,KAAK,YAAY,QAAQ;AAC3B,aAAK,SAAS,KAAK;AAAA,UACjB,MAAM;AAAA,UACN,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AACF;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/code/index.ts"],
4
- "sourcesContent": ["export { syntaxHighlight } from './highlight'\n"],
5
- "mappings": "AAAA,SAAS,uBAAuB;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/code/line-numbers.ts"],
4
- "sourcesContent": ["import type { Element, ElementContent, Root, Text } from 'hast'\nimport { visit } from 'unist-util-visit'\n\n// ---------------------------------------------------------------------------\n// Line Numbering plugin\n\nfunction isText(element?: ElementContent): element is Text {\n return element?.type === 'text'\n}\n\nfunction isElement(node?: ElementContent): node is Element {\n return node?.type === 'element'\n}\n\nfunction textElement(value: string): Text {\n return { type: 'text', value }\n}\n\nfunction lineBreak(): Text {\n return { type: 'text', value: '\\n' }\n}\n\n/**\n * Adds lines to code blocks\n */\nexport function codeBlockLinesPlugin() {\n return (tree: Root) => {\n visit(tree, 'element', (node: Element, _i, parent: Root | Element | null) => {\n if (parent?.type === 'element' && parent.tagName === 'pre' && node.tagName === 'code') {\n let numLines = 0\n\n // Wraps each line in a span\n node.children = addLines(node)\n\n // Adds a line break to the end of each line\n node.children.forEach((child: ElementContent) => {\n if (child.type === 'element' && child.tagName === 'span') {\n const lastChild: ElementContent | undefined = child.children[child.children.length - 1]\n\n if (lastChild && (!isText(lastChild) || (isText(lastChild) && !hasLineBreak(lastChild)))) {\n child.children.push(lineBreak())\n numLines++\n }\n }\n })\n\n // We need to maintain a count of the total lines to allow space for the labels\n node.properties.style = [`--line-count: ${numLines};`, `--line-digits: ${numLines.toString().length};`]\n }\n })\n\n // console.log('NUMBER OF LINES IS: ', numLines)\n }\n}\n\n/**\n * Adds lines to a node recursively and returns them\n *\n * @param node - The node to add lines to\n * @param lines - The current lines\n * @param copyParent - Whether to copy the parent node to save the original node styles\n */\nfunction addLines(node: Element, lines: Element[] = [], copyParent?: boolean): Element[] {\n const line = () => lines[lines.length - 1] ?? ((lines.push(createLine()) && lines[lines.length - 1]) || undefined)\n\n node.children.forEach((child: ElementContent) => {\n if (isText(child) && hasLineBreak(child)) {\n const split: string[] = child.value.split(/\\n/)\n\n split.forEach((content: string, i: number) => {\n if (copyParent) {\n line()?.children.push({ ...node, children: [textElement(content)] })\n } else {\n line()?.children.push(textElement(content))\n }\n\n i !== split.length - 1 && lines.push(createLine())\n })\n } else if (isElement(child) && child.children.some(hasLineBreak)) {\n addLines(child, lines, true)\n } else {\n line()?.children.push(child)\n }\n })\n\n return lines\n}\n\n/**\n * Creates a new line element\n *\n * @param children - The children the line should have initially\n */\nfunction createLine(...children: ElementContent[]): Element {\n return {\n type: 'element',\n tagName: 'span',\n properties: { class: ['line'] },\n children,\n }\n}\n\n/**\n * Checks if a node has a line break\n *\n * @param node - The node to check\n */\nfunction hasLineBreak(node: ElementContent): boolean {\n return (isText(node) && /\\r?\\n/.test(node.value)) || (isElement(node) && node.children.some(hasLineBreak))\n}\n"],
5
- "mappings": "AACA,SAAS,aAAa;AAKtB,SAAS,OAAO,SAA2C;AACzD,SAAO,SAAS,SAAS;AAC3B;AAEA,SAAS,UAAU,MAAwC;AACzD,SAAO,MAAM,SAAS;AACxB;AAEA,SAAS,YAAY,OAAqB;AACxC,SAAO,EAAE,MAAM,QAAQ,MAAM;AAC/B;AAEA,SAAS,YAAkB;AACzB,SAAO,EAAE,MAAM,QAAQ,OAAO,KAAK;AACrC;AAKO,SAAS,uBAAuB;AACrC,SAAO,CAAC,SAAe;AACrB,UAAM,MAAM,WAAW,CAAC,MAAe,IAAI,WAAkC;AAC3E,UAAI,QAAQ,SAAS,aAAa,OAAO,YAAY,SAAS,KAAK,YAAY,QAAQ;AACrF,YAAI,WAAW;AAGf,aAAK,WAAW,SAAS,IAAI;AAG7B,aAAK,SAAS,QAAQ,CAAC,UAA0B;AAC/C,cAAI,MAAM,SAAS,aAAa,MAAM,YAAY,QAAQ;AACxD,kBAAM,YAAwC,MAAM,SAAS,MAAM,SAAS,SAAS,CAAC;AAEtF,gBAAI,cAAc,CAAC,OAAO,SAAS,KAAM,OAAO,SAAS,KAAK,CAAC,aAAa,SAAS,IAAK;AACxF,oBAAM,SAAS,KAAK,UAAU,CAAC;AAC/B;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AAGD,aAAK,WAAW,QAAQ,CAAC,iBAAiB,QAAQ,KAAK,kBAAkB,SAAS,SAAS,EAAE,MAAM,GAAG;AAAA,MACxG;AAAA,IACF,CAAC;AAAA,EAGH;AACF;AASA,SAAS,SAAS,MAAe,QAAmB,CAAC,GAAG,YAAiC;AACvF,QAAM,OAAO,MAAM,MAAM,MAAM,SAAS,CAAC,MAAO,MAAM,KAAK,WAAW,CAAC,KAAK,MAAM,MAAM,SAAS,CAAC,KAAM;AAExG,OAAK,SAAS,QAAQ,CAAC,UAA0B;AAC/C,QAAI,OAAO,KAAK,KAAK,aAAa,KAAK,GAAG;AACxC,YAAM,QAAkB,MAAM,MAAM,MAAM,IAAI;AAE9C,YAAM,QAAQ,CAAC,SAAiB,MAAc;AAC5C,YAAI,YAAY;AACd,eAAK,GAAG,SAAS,KAAK,EAAE,GAAG,MAAM,UAAU,CAAC,YAAY,OAAO,CAAC,EAAE,CAAC;AAAA,QACrE,OAAO;AACL,eAAK,GAAG,SAAS,KAAK,YAAY,OAAO,CAAC;AAAA,QAC5C;AAEA,cAAM,MAAM,SAAS,KAAK,MAAM,KAAK,WAAW,CAAC;AAAA,MACnD,CAAC;AAAA,IACH,WAAW,UAAU,KAAK,KAAK,MAAM,SAAS,KAAK,YAAY,GAAG;AAChE,eAAS,OAAO,OAAO,IAAI;AAAA,IAC7B,OAAO;AACL,WAAK,GAAG,SAAS,KAAK,KAAK;AAAA,IAC7B;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAOA,SAAS,cAAc,UAAqC;AAC1D,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IACT,YAAY,EAAE,OAAO,CAAC,MAAM,EAAE;AAAA,IAC9B;AAAA,EACF;AACF;AAOA,SAAS,aAAa,MAA+B;AACnD,SAAQ,OAAO,IAAI,KAAK,QAAQ,KAAK,KAAK,KAAK,KAAO,UAAU,IAAI,KAAK,KAAK,SAAS,KAAK,YAAY;AAC1G;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/constants.ts"],
4
- "sourcesContent": ["/** Map common markdown language shortcuts to lowlight languages */\nexport const lowlightLanguageMappings: Record<string, string> = {\n 'ts': 'typescript',\n 'js': 'javascript',\n 'py': 'python',\n 'py3': 'python',\n 'c#': 'csharp',\n 'c++': 'cpp',\n 'node': 'javascript',\n}\n\n//\n"],
5
- "mappings": "AACO,MAAM,2BAAmD;AAAA,EAC9D,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AACV;",
6
- "names": []
7
- }
package/dist/index.js.map DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/index.ts"],
4
- "sourcesContent": ["export { syntaxHighlight } from './code'\nexport { lowlightLanguageMappings } from './constants'\nexport * from './languages'\nexport { type Node, htmlFromMarkdown, isHeading, textFromNode } from './markdown'\nexport { rehypeHighlight } from './rehype-highlight'\n"],
5
- "mappings": "AAAA,SAAS,uBAAuB;AAChC,SAAS,gCAAgC;AACzC,cAAc;AACd,SAAoB,kBAAkB,WAAW,oBAAoB;AACrE,SAAS,uBAAuB;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/languages/basic.ts"],
4
- "sourcesContent": ["import type { LanguageFn } from 'highlight.js'\nimport bash from 'highlight.js/lib/languages/bash'\nimport css from 'highlight.js/lib/languages/css'\nimport javascript from 'highlight.js/lib/languages/javascript'\nimport json from 'highlight.js/lib/languages/json'\nimport less from 'highlight.js/lib/languages/less'\nimport markdown from 'highlight.js/lib/languages/markdown'\nimport plaintext from 'highlight.js/lib/languages/plaintext'\nimport python from 'highlight.js/lib/languages/python'\nimport scss from 'highlight.js/lib/languages/scss'\nimport shell from 'highlight.js/lib/languages/shell'\nimport typescript from 'highlight.js/lib/languages/typescript'\nimport xml from 'highlight.js/lib/languages/xml'\nimport yaml from 'highlight.js/lib/languages/yaml'\n\nconst basicLanguages: Record<string, LanguageFn> = {\n bash,\n css,\n html: xml,\n javascript,\n json,\n less,\n markdown,\n plaintext,\n python,\n scss,\n shell,\n typescript,\n xml,\n yaml,\n}\n\nexport { basicLanguages }\n"],
5
- "mappings": "AACA,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,gBAAgB;AACvB,OAAO,UAAU;AACjB,OAAO,UAAU;AACjB,OAAO,cAAc;AACrB,OAAO,eAAe;AACtB,OAAO,YAAY;AACnB,OAAO,UAAU;AACjB,OAAO,WAAW;AAClB,OAAO,gBAAgB;AACvB,OAAO,SAAS;AAChB,OAAO,UAAU;AAEjB,MAAM,iBAA6C;AAAA,EACjD;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/languages/index.ts"],
4
- "sourcesContent": ["export { basicLanguages } from './basic'\nexport { jsonYamlLanguages } from './json-yaml'\nexport { standardLanguages } from './standard'\n"],
5
- "mappings": "AAAA,SAAS,sBAAsB;AAC/B,SAAS,yBAAyB;AAClC,SAAS,yBAAyB;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/languages/json-yaml.ts"],
4
- "sourcesContent": ["import type { LanguageFn } from 'highlight.js'\nimport json from 'highlight.js/lib/languages/json'\nimport plaintext from 'highlight.js/lib/languages/plaintext'\nimport yaml from 'highlight.js/lib/languages/yaml'\n\nconst jsonYamlLanguages: Record<string, LanguageFn> = {\n json,\n plaintext,\n yaml,\n}\n\nexport { jsonYamlLanguages }\n"],
5
- "mappings": "AACA,OAAO,UAAU;AACjB,OAAO,eAAe;AACtB,OAAO,UAAU;AAEjB,MAAM,oBAAgD;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AACF;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/languages/standard.ts"],
4
- "sourcesContent": ["import type { LanguageFn } from 'highlight.js'\nimport bash from 'highlight.js/lib/languages/bash'\nimport c from 'highlight.js/lib/languages/c'\nimport clojure from 'highlight.js/lib/languages/clojure'\nimport cpp from 'highlight.js/lib/languages/cpp'\nimport csharp from 'highlight.js/lib/languages/csharp'\nimport css from 'highlight.js/lib/languages/css'\nimport dart from 'highlight.js/lib/languages/dart'\nimport diff from 'highlight.js/lib/languages/diff'\nimport dockerfile from 'highlight.js/lib/languages/dockerfile'\nimport elixir from 'highlight.js/lib/languages/elixir'\nimport fsharp from 'highlight.js/lib/languages/fsharp'\nimport go from 'highlight.js/lib/languages/go'\nimport graphql from 'highlight.js/lib/languages/graphql'\nimport haskell from 'highlight.js/lib/languages/haskell'\nimport http from 'highlight.js/lib/languages/http'\nimport ini from 'highlight.js/lib/languages/ini'\nimport java from 'highlight.js/lib/languages/java'\nimport javascript from 'highlight.js/lib/languages/javascript'\nimport json from 'highlight.js/lib/languages/json'\nimport kotlin from 'highlight.js/lib/languages/kotlin'\nimport less from 'highlight.js/lib/languages/less'\nimport lua from 'highlight.js/lib/languages/lua'\nimport makefile from 'highlight.js/lib/languages/makefile'\nimport markdown from 'highlight.js/lib/languages/markdown'\nimport matlab from 'highlight.js/lib/languages/matlab'\nimport nginx from 'highlight.js/lib/languages/nginx'\nimport objectivec from 'highlight.js/lib/languages/objectivec'\nimport ocaml from 'highlight.js/lib/languages/ocaml'\nimport perl from 'highlight.js/lib/languages/perl'\nimport php from 'highlight.js/lib/languages/php'\nimport plaintext from 'highlight.js/lib/languages/plaintext'\nimport powershell from 'highlight.js/lib/languages/powershell'\nimport properties from 'highlight.js/lib/languages/properties'\nimport python from 'highlight.js/lib/languages/python'\nimport r from 'highlight.js/lib/languages/r'\nimport ruby from 'highlight.js/lib/languages/ruby'\nimport rust from 'highlight.js/lib/languages/rust'\nimport scala from 'highlight.js/lib/languages/scala'\nimport scss from 'highlight.js/lib/languages/scss'\nimport shell from 'highlight.js/lib/languages/shell'\nimport sql from 'highlight.js/lib/languages/sql'\nimport swift from 'highlight.js/lib/languages/swift'\nimport typescript from 'highlight.js/lib/languages/typescript'\nimport xml from 'highlight.js/lib/languages/xml'\nimport yaml from 'highlight.js/lib/languages/yaml'\n// @ts-expect-error No types available\nimport curl from 'highlightjs-curl'\n\n/**\n * We group languages into three categories based on their popularity and usage.\n * This helps in optimizing the bundle size by allowing users to include only\n * the languages they need.\n *\n * 1. Standard Languages: These are the most popular languages that cover the\n * majority of use cases.\n * 2. Medium Languages: These languages are not as popular as the ones in\n * `standardLanguages`, but still have a decent amount of users.\n * 3. Specialized Languages: These languages are more specialized and have a\n * smaller user base.\n *\n * Each category is represented as a separate object, making it easy to import\n * only the languages you need.\n */\n\n/**\n * These are the most popular languages that cover the majority of use cases.\n */\nexport const standardLanguages = {\n bash,\n c,\n clojure,\n cpp,\n csharp,\n css,\n curl,\n dart,\n diff,\n docker: dockerfile,\n dockerfile,\n elixir,\n fsharp,\n go,\n graphql,\n haskell,\n html: xml,\n http,\n ini,\n java,\n javascript,\n json,\n kotlin,\n less,\n lua,\n makefile,\n markdown,\n matlab,\n nginx,\n objectivec,\n ocaml,\n perl,\n php,\n plaintext,\n powershell,\n properties,\n python,\n r,\n ruby,\n rust,\n scala,\n scss,\n shell,\n sql,\n swift,\n toml: ini,\n typescript,\n xml,\n yaml,\n} as const satisfies Record<string, LanguageFn>\n"],
5
- "mappings": "AACA,OAAO,UAAU;AACjB,OAAO,OAAO;AACd,OAAO,aAAa;AACpB,OAAO,SAAS;AAChB,OAAO,YAAY;AACnB,OAAO,SAAS;AAChB,OAAO,UAAU;AACjB,OAAO,UAAU;AACjB,OAAO,gBAAgB;AACvB,OAAO,YAAY;AACnB,OAAO,YAAY;AACnB,OAAO,QAAQ;AACf,OAAO,aAAa;AACpB,OAAO,aAAa;AACpB,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,UAAU;AACjB,OAAO,gBAAgB;AACvB,OAAO,UAAU;AACjB,OAAO,YAAY;AACnB,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,cAAc;AACrB,OAAO,cAAc;AACrB,OAAO,YAAY;AACnB,OAAO,WAAW;AAClB,OAAO,gBAAgB;AACvB,OAAO,WAAW;AAClB,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,eAAe;AACtB,OAAO,gBAAgB;AACvB,OAAO,gBAAgB;AACvB,OAAO,YAAY;AACnB,OAAO,OAAO;AACd,OAAO,UAAU;AACjB,OAAO,UAAU;AACjB,OAAO,WAAW;AAClB,OAAO,UAAU;AACjB,OAAO,WAAW;AAClB,OAAO,SAAS;AAChB,OAAO,WAAW;AAClB,OAAO,gBAAgB;AACvB,OAAO,SAAS;AAChB,OAAO,UAAU;AAEjB,OAAO,UAAU;AAqBV,MAAM,oBAAoB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AACF;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/markdown/index.ts"],
4
- "sourcesContent": ["export * from './markdown'\n"],
5
- "mappings": "AAAA,cAAc;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/markdown/markdown.ts"],
4
- "sourcesContent": ["import type { Heading, Node, PhrasingContent, Root, RootContent } from 'mdast'\nimport rehypeExternalLinks from 'rehype-external-links'\nimport rehypeFormat from 'rehype-format'\nimport rehypeRaw from 'rehype-raw'\nimport rehypeSanitize, { defaultSchema } from 'rehype-sanitize'\nimport rehypeStringify from 'rehype-stringify'\nimport remarkGfm from 'remark-gfm'\nimport remarkParse from 'remark-parse'\nimport remarkRehype from 'remark-rehype'\nimport remarkStringify from 'remark-stringify'\nimport { unified } from 'unified'\nimport { SKIP, visit } from 'unist-util-visit'\n\nimport { standardLanguages } from '@/languages'\nimport { rehypeAlert } from '@/rehype-alert'\nimport { rehypeHighlight } from '@/rehype-highlight'\n\ntype Options = {\n transform?: (node: Node) => Node\n type?: string\n}\n\nexport type { Node } from 'mdast'\n\n/**\n * Type-guard to check if a node is a heading.\n */\nexport const isHeading = (node: Node): node is Heading => {\n return node.type === 'heading' && 'depth' in node && 'children' in node\n}\n\n/**\n * Plugin to transform nodes in a Markdown AST\n */\nconst transformNodes =\n (options?: Readonly<Options> | null | undefined, ..._ignored: any[]) =>\n (tree: Node) => {\n if (!options?.transform || !options?.type) {\n return\n }\n\n visit(tree, options?.type, (node) => {\n options?.transform ? options?.transform(node) : node\n\n return SKIP\n })\n\n return\n }\n\n/**\n * Take a Markdown string and generate HTML from it\n */\nexport function htmlFromMarkdown(\n markdown: string,\n options?: {\n removeTags?: string[]\n allowTags?: string[]\n transform?: (node: Node) => Node\n transformType?: string\n },\n) {\n // Add permitted tags and remove stripped ones\n const removeTags = options?.removeTags ?? []\n const tagNames = [...(defaultSchema.tagNames ?? []), ...(options?.allowTags ?? [])].filter(\n (t) => !removeTags.includes(t),\n )\n\n const html = unified()\n // Parses markdown\n .use(remarkParse)\n // Support autolink literals, footnotes, strikethrough, tables and tasklists\n .use(remarkGfm)\n .use(transformNodes, {\n transform: options?.transform,\n type: options?.transformType,\n })\n // Allows any HTML tags\n .use(remarkRehype, { allowDangerousHtml: true })\n // Adds GitHub alerts\n .use(rehypeAlert)\n // Creates a HTML AST\n .use(rehypeRaw)\n // Removes disallowed tags\n .use(rehypeSanitize, {\n ...defaultSchema,\n // Don't prefix the heading ids\n clobberPrefix: '',\n // Makes it even more strict\n tagNames,\n attributes: {\n ...defaultSchema.attributes,\n abbr: ['title'],\n // Allow all class names while preserving the existing default attributes\n '*': [...(defaultSchema.attributes?.['*'] ?? []), 'className'],\n },\n // Strip content of dangerous elements, not just the tags\n strip: ['script', 'style', 'object', 'embed', 'form'],\n })\n // Syntax highlighting\n .use(rehypeHighlight, {\n languages: standardLanguages,\n // Enable auto detection\n detect: true,\n })\n // Adds target=\"_blank\" to external links\n .use(rehypeExternalLinks, { target: '_blank' })\n // Formats the HTML\n .use(rehypeFormat)\n // Converts the HTML AST to a string\n .use(rehypeStringify)\n // Run the pipeline\n .processSync(markdown)\n\n return html.toString()\n}\n\n/**\n * Create a Markdown AST from a string.\n */\nfunction getMarkdownAst(markdown: string): Root {\n return unified().use(remarkParse).use(remarkGfm).parse(markdown)\n}\n\n/**\n * Find all headings of a specific type in a Markdown AST.\n */\nexport function getHeadings(\n markdown: string,\n depth: number = 1,\n): {\n depth: number\n value: string\n}[] {\n const tree = getMarkdownAst(markdown)\n\n const nodes: {\n depth: number\n value: string\n }[] = []\n\n visit(tree, 'heading', (node) => {\n const text = textFromNode(node)\n\n if (text) {\n nodes.push({ depth: node.depth ?? depth, value: text })\n }\n })\n\n return nodes\n}\n\n/**\n * Extract plain text from a Markdown AST node (recursively).\n *\n * Handles headings with nested phrasing content such as links.\n */\nexport function textFromNode(node: Heading | PhrasingContent): string {\n if (node.type === 'text') {\n return node.value ?? ''\n }\n\n if ('children' in node && Array.isArray(node.children)) {\n return node.children.map((child) => textFromNode(child)).join('')\n }\n\n return ''\n}\n\n/**\n * Return multiple Markdown documents. Every heading should be its own document.\n */\nexport function splitContent(markdown: string) {\n const tree = getMarkdownAst(markdown)\n\n /** Sections */\n const sections: RootContent[][] = []\n\n /** Nodes inside a section */\n let nodes: RootContent[] = []\n\n tree.children?.forEach((node) => {\n // If the node is a heading, start a new section\n if (node.type === 'heading') {\n if (nodes.length) {\n sections.push(nodes)\n }\n\n sections.push([node])\n\n nodes = []\n }\n // Otherwise, add the node to the current section\n else {\n nodes.push(node)\n }\n })\n\n // Add any remaining nodes\n if (nodes.length) {\n sections.push(nodes)\n }\n\n return sections.map((section) => createDocument(section))\n}\n\n/**\n * Use remark to create a Markdown document from a list of nodes.\n */\nfunction createDocument(nodes: RootContent[]) {\n // Create the Markdown string\n const markdown = unified().use(remarkStringify).use(remarkGfm).stringify({\n type: 'root',\n children: nodes,\n })\n\n // Remove the whitespace\n return markdown.trim()\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/rehype-alert/index.ts"],
4
- "sourcesContent": ["export { rehypeAlert } from './rehype-alert'\n"],
5
- "mappings": "AAAA,SAAS,mBAAmB;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/rehype-alert/rehype-alert.ts"],
4
- "sourcesContent": ["import type { Element } from 'hast'\nimport type { Root } from 'mdast'\nimport { visit } from 'unist-util-visit'\n\nconst ALERT_TYPES = ['note', 'tip', 'important', 'warning', 'caution', 'success'] as const\n\n// Simple whitespace check function\nfunction isWhitespace(node: any): boolean {\n return node.type === 'text' && typeof node.value === 'string' && /^\\s*$/.test(node.value)\n}\n\nexport function rehypeAlert() {\n return (tree: Root) => {\n visit(tree, 'element', (node: Element, index, parent: any) => {\n if (node.tagName !== 'blockquote' || typeof index !== 'number' || !parent || parent.type !== 'root') {\n return\n }\n\n // Find the first non-whitespace child\n const headIndex = node.children.findIndex((child) => !isWhitespace(child))\n if (headIndex === -1) {\n return\n }\n\n const head = node.children[headIndex]\n\n if (!head || head.type !== 'element' || head.tagName !== 'p') {\n return\n }\n\n const text = head.children[0]\n if (!text || text.type !== 'text' || !text.value.startsWith('[!')) {\n return\n }\n\n const end = text.value.indexOf(']')\n if (end === -1) {\n return\n }\n\n // Extract the alert type\n const alertType = text.value.slice(2, end).toLowerCase() as (typeof ALERT_TYPES)[number]\n if (!ALERT_TYPES.includes(alertType)) {\n return\n }\n\n // Remove the blockquote if it's empty\n if (end + 1 === text.value.length) {\n const next = head.children[1]\n if (next) {\n if (next.type !== 'element' || next.tagName !== 'br') {\n return\n }\n if (!head.children[2]) {\n return\n }\n head.children = head.children.slice(2)\n const node = head.children[0]\n if (node && node.type === 'text' && node.value.charAt(0) === '\\n') {\n node.value = node.value.slice(1)\n }\n } else {\n const skipped = headIndex + 1 < node.children.length && isWhitespace(node.children[headIndex + 1])\n const nextIndex = skipped ? headIndex + 2 : headIndex + 1\n if (nextIndex >= node.children.length || node.children[nextIndex]?.type !== 'element') {\n return\n }\n\n node.children = node.children.slice(nextIndex)\n }\n } else if (\n text.value.charAt(end + 1) === '\\n' &&\n // Check if the next character is a newline or a non-whitespace character\n (end + 2 === text.value.length || !/^\\s*$/.test(text.value.slice(end + 2)))\n ) {\n text.value = text.value.slice(end + 2)\n } else {\n // Remove the alert marker if it's not followed by a newline or a non-whitespace character\n text.value = text.value.replace(/^\\s*\\[!.*?\\]\\s*/, '')\n }\n\n // Extract content from paragraphs to avoid wrapping in <p> tags\n const contentChildren = []\n for (let i = headIndex; i < node.children.length; i++) {\n const child = node.children[i]\n if (child?.type === 'element' && child.tagName === 'p' && child.children) {\n contentChildren.push(...child.children)\n } else {\n contentChildren.push(child)\n }\n }\n\n // Replace the blockquote with a div containing the alert\n parent.children[index] = {\n type: 'element',\n tagName: 'div',\n properties: { className: ['markdown-alert', `markdown-alert-${alertType}`] },\n children: [\n {\n type: 'element',\n tagName: 'div',\n properties: { className: ['markdown-alert-icon'] },\n children: [],\n },\n {\n type: 'element',\n tagName: 'div',\n properties: { className: ['markdown-alert-content'] },\n children: [{ type: 'text', value: ' ' }, ...contentChildren],\n },\n ],\n }\n })\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["node"]
7
- }