@scalar/code-highlight 0.3.4 → 0.3.6

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 (46) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/chunks/code.js +124 -0
  3. package/dist/chunks/code.js.map +1 -0
  4. package/dist/chunks/languages.js +12762 -0
  5. package/dist/chunks/languages.js.map +1 -0
  6. package/dist/chunks/lib.js +13688 -0
  7. package/dist/chunks/lib.js.map +1 -0
  8. package/dist/chunks/lib2.js +731 -0
  9. package/dist/chunks/lib2.js.map +1 -0
  10. package/dist/chunks/markdown.js +20922 -0
  11. package/dist/chunks/markdown.js.map +1 -0
  12. package/dist/chunks/rehype-alert.js +74 -0
  13. package/dist/chunks/rehype-alert.js.map +1 -0
  14. package/dist/chunks/rehype-highlight.js +2989 -0
  15. package/dist/chunks/rehype-highlight.js.map +1 -0
  16. package/dist/code/index.d.ts +1 -1
  17. package/dist/code/index.js +3 -1
  18. package/dist/css/code.css +0 -4
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.js +6 -5
  21. package/dist/languages/basic.d.ts.map +1 -1
  22. package/dist/languages/index.d.ts +3 -3
  23. package/dist/languages/index.js +2 -3
  24. package/dist/languages/python.d.ts +22 -0
  25. package/dist/languages/python.d.ts.map +1 -0
  26. package/dist/languages/standard.d.ts.map +1 -1
  27. package/dist/markdown/index.d.ts +1 -1
  28. package/dist/markdown/index.js +3 -1
  29. package/dist/markdown/markdown.d.ts.map +1 -1
  30. package/dist/rehype-alert/index.d.ts +1 -1
  31. package/dist/rehype-alert/index.js +2 -1
  32. package/dist/rehype-highlight/index.d.ts +1 -1
  33. package/dist/rehype-highlight/index.js +2 -1
  34. package/dist/rehype-highlight/rehype-highlight.d.ts +2 -0
  35. package/dist/rehype-highlight/rehype-highlight.d.ts.map +1 -1
  36. package/package.json +4 -4
  37. package/dist/code/highlight.js +0 -62
  38. package/dist/code/line-numbers.js +0 -94
  39. package/dist/constants.js +0 -11
  40. package/dist/languages/basic.js +0 -30
  41. package/dist/languages/curl.js +0 -86
  42. package/dist/languages/json-yaml.js +0 -9
  43. package/dist/languages/standard.js +0 -115
  44. package/dist/markdown/markdown.js +0 -228
  45. package/dist/rehype-alert/rehype-alert.js +0 -102
  46. package/dist/rehype-highlight/rehype-highlight.js +0 -98
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @scalar/code-highlight
2
2
 
3
+ ## 0.3.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [#9508](https://github.com/scalar/scalar/pull/9508): Fix Python code samples appearing mostly unstyled by highlighting function and method call sites
8
+ - [#9506](https://github.com/scalar/scalar/pull/9506): Add Scalar scrollbar styling to highlighted Markdown code blocks.
9
+
10
+ ## 0.3.5
11
+
12
+ ### Patch Changes
13
+
14
+ - [#9451](https://github.com/scalar/scalar/pull/9451): Bundle runtime dependencies into the build so CommonJS-only packages (like `extend` and `debug`) no longer leak to consumers and break Vite dev under pnpm
15
+
3
16
  ## 0.3.4
4
17
 
5
18
  ### Patch Changes
@@ -0,0 +1,124 @@
1
+ import { i as lowlightLanguageMappings, t as rehypeHighlight } from "./rehype-highlight.js";
2
+ import { n as rehypeStringify, s as rehypeParse, t as unified } from "./lib.js";
3
+ import { t as visit } from "./lib2.js";
4
+ //#region src/code/line-numbers.ts
5
+ function isText(element) {
6
+ return element?.type === "text";
7
+ }
8
+ function isElement(node) {
9
+ return node?.type === "element";
10
+ }
11
+ function textElement(value) {
12
+ return {
13
+ type: "text",
14
+ value
15
+ };
16
+ }
17
+ function lineBreak() {
18
+ return {
19
+ type: "text",
20
+ value: "\n"
21
+ };
22
+ }
23
+ /**
24
+ * Adds lines to code blocks
25
+ */
26
+ function codeBlockLinesPlugin() {
27
+ return (tree) => {
28
+ visit(tree, "element", (node, _i, parent) => {
29
+ if (parent?.type === "element" && parent.tagName === "pre" && node.tagName === "code") {
30
+ let numLines = 0;
31
+ node.children = addLines(node);
32
+ node.children.forEach((child) => {
33
+ if (child.type === "element" && child.tagName === "span") {
34
+ const lastChild = child.children[child.children.length - 1];
35
+ if (lastChild && (!isText(lastChild) || isText(lastChild) && !hasLineBreak(lastChild))) {
36
+ child.children.push(lineBreak());
37
+ numLines++;
38
+ }
39
+ }
40
+ });
41
+ node.properties.style = [`--line-count: ${numLines};`, `--line-digits: ${numLines.toString().length};`];
42
+ }
43
+ });
44
+ };
45
+ }
46
+ /**
47
+ * Adds lines to a node recursively and returns them
48
+ *
49
+ * @param node - The node to add lines to
50
+ * @param lines - The current lines
51
+ * @param copyParent - Whether to copy the parent node to save the original node styles
52
+ */
53
+ function addLines(node, lines = [], copyParent) {
54
+ const line = () => lines[lines.length - 1] ?? (lines.push(createLine()) && lines[lines.length - 1] || void 0);
55
+ node.children.forEach((child) => {
56
+ if (isText(child) && hasLineBreak(child)) {
57
+ const split = child.value.split(/\n/);
58
+ split.forEach((content, i) => {
59
+ if (copyParent) line()?.children.push({
60
+ ...node,
61
+ children: [textElement(content)]
62
+ });
63
+ else line()?.children.push(textElement(content));
64
+ i !== split.length - 1 && lines.push(createLine());
65
+ });
66
+ } else if (isElement(child) && child.children.some(hasLineBreak)) addLines(child, lines, true);
67
+ else line()?.children.push(child);
68
+ });
69
+ return lines;
70
+ }
71
+ /**
72
+ * Creates a new line element
73
+ *
74
+ * @param children - The children the line should have initially
75
+ */
76
+ function createLine(...children) {
77
+ return {
78
+ type: "element",
79
+ tagName: "span",
80
+ properties: { class: ["line"] },
81
+ children
82
+ };
83
+ }
84
+ /**
85
+ * Checks if a node has a line break
86
+ *
87
+ * @param node - The node to check
88
+ */
89
+ function hasLineBreak(node) {
90
+ return isText(node) && /\r?\n/.test(node.value) || isElement(node) && node.children.some(hasLineBreak);
91
+ }
92
+ //#endregion
93
+ //#region src/code/highlight.ts
94
+ /**
95
+ * Syntax highlights a code string using the `rehype-highlight` library.
96
+ */
97
+ function syntaxHighlight(codeString, options) {
98
+ const credentials = (typeof options?.maskCredentials === "string" ? [options.maskCredentials] : options?.maskCredentials ?? []).filter((c) => {
99
+ if (c.length < 3) return false;
100
+ return true;
101
+ });
102
+ const className = `language-${lowlightLanguageMappings[options.lang] ?? options.lang}`;
103
+ const nullPlugin = (() => {});
104
+ const htmlString = unified().use(rehypeParse, { fragment: true }).use(injectRawCodeStringPlugin(codeString)).use(rehypeHighlight, { languages: options.languages }).use(options?.lineNumbers ? codeBlockLinesPlugin : nullPlugin).use(rehypeStringify).processSync(`<pre><code class="${className}"></code></pre>`).toString();
105
+ return credentials.length ? credentials.reduce((acc, credential) => acc.split(credential).join(`<span class="credential"><span class="credential-value">${credential}</span></span>`), htmlString) : htmlString;
106
+ }
107
+ /**
108
+ * To prevent unified from parsing any content of the code string we inject
109
+ * it as a raw text node into the AST tree as a child of the code element
110
+ */
111
+ function injectRawCodeStringPlugin(rawCodeString) {
112
+ return () => (tree) => {
113
+ visit(tree, "element", (node) => {
114
+ if (node.tagName === "code") node.children.push({
115
+ type: "text",
116
+ value: rawCodeString
117
+ });
118
+ });
119
+ };
120
+ }
121
+ //#endregion
122
+ export { syntaxHighlight as t };
123
+
124
+ //# sourceMappingURL=code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code.js","names":[],"sources":["../../src/code/line-numbers.ts","../../src/code/highlight.ts"],"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","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"],"mappings":"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"}