@scalar/code-highlight 0.0.28 → 0.1.0

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 (42) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/code/highlight.js +37 -59
  3. package/dist/code/highlight.js.map +7 -0
  4. package/dist/code/highlight.test.js +64 -0
  5. package/dist/code/highlight.test.js.map +7 -0
  6. package/dist/code/index.js +5 -1
  7. package/dist/code/index.js.map +7 -0
  8. package/dist/code/line-numbers.js +54 -75
  9. package/dist/code/line-numbers.js.map +7 -0
  10. package/dist/constants.js +11 -11
  11. package/dist/constants.js.map +7 -0
  12. package/dist/index.js +12 -7
  13. package/dist/index.js.map +7 -0
  14. package/dist/languages/basic.js +31 -30
  15. package/dist/languages/basic.js.map +7 -0
  16. package/dist/languages/index.js +9 -3
  17. package/dist/languages/index.js.map +7 -0
  18. package/dist/languages/json-yaml.js +10 -9
  19. package/dist/languages/json-yaml.js.map +7 -0
  20. package/dist/languages/standard.js +77 -76
  21. package/dist/languages/standard.js.map +7 -0
  22. package/dist/markdown/getHeadings.test.js +45 -0
  23. package/dist/markdown/getHeadings.test.js.map +7 -0
  24. package/dist/markdown/htmlFromMarkdown.test.js +41 -0
  25. package/dist/markdown/htmlFromMarkdown.test.js.map +7 -0
  26. package/dist/markdown/index.js +2 -1
  27. package/dist/markdown/index.js.map +7 -0
  28. package/dist/markdown/markdown.js +94 -142
  29. package/dist/markdown/markdown.js.map +7 -0
  30. package/dist/markdown/splitContent.test.js +17 -0
  31. package/dist/markdown/splitContent.test.js.map +7 -0
  32. package/dist/rehype-alert/index.js +5 -1
  33. package/dist/rehype-alert/index.js.map +7 -0
  34. package/dist/rehype-alert/rehype-alert.js +84 -95
  35. package/dist/rehype-alert/rehype-alert.js.map +7 -0
  36. package/dist/rehype-alert/rehype-alert.test.js +74 -0
  37. package/dist/rehype-alert/rehype-alert.test.js.map +7 -0
  38. package/dist/rehype-highlight/index.js +5 -1
  39. package/dist/rehype-highlight/index.js.map +7 -0
  40. package/dist/rehype-highlight/rehype-highlight.js +84 -95
  41. package/dist/rehype-highlight/rehype-highlight.js.map +7 -0
  42. package/package.json +6 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @scalar/code-highlight
2
2
 
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 483ca93: chore: require Node 20 (or above)
8
+
9
+ ## 0.0.29
10
+
11
+ ### Patch Changes
12
+
13
+ - 8c7bad8: chore: move build tooling to esbuild
14
+
3
15
  ## 0.0.28
4
16
 
5
17
  ### Patch Changes
@@ -1,64 +1,42 @@
1
- import rehypeParse from 'rehype-parse';
2
- import rehypeStringify from 'rehype-stringify';
3
- import { unified } from 'unified';
4
- import { visit } from 'unist-util-visit';
5
- import { lowlightLanguageMappings } from '../constants.js';
6
- import { codeBlockLinesPlugin } from './line-numbers.js';
7
- import { rehypeHighlight } from '../rehype-highlight/rehype-highlight.js';
8
-
9
- /**
10
- * Syntax highlights a code string using the `rehype-highlight` library.
11
- */
1
+ import rehypeParse from "rehype-parse";
2
+ import rehypeStringify from "rehype-stringify";
3
+ import { unified } from "unified";
4
+ import { visit } from "unist-util-visit";
5
+ import { lowlightLanguageMappings } from "../constants.js";
6
+ import { rehypeHighlight } from "../rehype-highlight/index.js";
7
+ import { codeBlockLinesPlugin } from "./line-numbers.js";
12
8
  function syntaxHighlight(codeString, options) {
13
- // Simple restriction on credentials to prevent unexpected behavior
14
- const credentials = (typeof options?.maskCredentials === 'string' ? [options.maskCredentials] : (options?.maskCredentials ?? [])).filter((c) => {
15
- // Credentials must be at least 3 characters to mask.
16
- if (c.length < 3) {
17
- return false;
18
- }
19
- return true;
20
- });
21
- // Classname is used by lowlight to select the language model
22
- const className = `language-${lowlightLanguageMappings[options.lang] ?? options.lang}`;
23
- const nullPlugin = (() => { });
24
- const html = unified()
25
- // Parses markdown
26
- .use(rehypeParse, { fragment: true })
27
- // Raw code string must be injected after initial hast parsing
28
- // so that HTML code is not parsed into the hast tree
29
- .use(injectRawCodeStringPlugin(codeString))
30
- // Syntax highlighting
31
- .use(rehypeHighlight, {
32
- languages: options.languages,
33
- })
34
- .use(options?.lineNumbers ? codeBlockLinesPlugin : nullPlugin)
35
- // Converts the HTML AST to a string
36
- .use(rehypeStringify)
37
- // Run the pipeline
38
- .processSync(`<pre><code class="${className}"></code></pre>`);
39
- const htmlString = html.toString();
40
- // Replace any credentials with a wrapper element
41
- return credentials.length
42
- ? credentials.reduce((acc, credential) => acc
43
- .split(credential)
44
- .join(`<span class="credential"><span class="credential-value">${credential}</span></span>`), htmlString)
45
- : htmlString;
9
+ const credentials = (typeof options?.maskCredentials === "string" ? [options.maskCredentials] : options?.maskCredentials ?? []).filter((c) => {
10
+ if (c.length < 3) {
11
+ return false;
12
+ }
13
+ return true;
14
+ });
15
+ const className = `language-${lowlightLanguageMappings[options.lang] ?? options.lang}`;
16
+ const nullPlugin = () => {
17
+ };
18
+ const html = unified().use(rehypeParse, { fragment: true }).use(injectRawCodeStringPlugin(codeString)).use(rehypeHighlight, {
19
+ languages: options.languages
20
+ }).use(options?.lineNumbers ? codeBlockLinesPlugin : nullPlugin).use(rehypeStringify).processSync(`<pre><code class="${className}"></code></pre>`);
21
+ const htmlString = html.toString();
22
+ return credentials.length ? credentials.reduce(
23
+ (acc, credential) => acc.split(credential).join(`<span class="credential"><span class="credential-value">${credential}</span></span>`),
24
+ htmlString
25
+ ) : htmlString;
46
26
  }
47
- /**
48
- * To prevent unified from parsing any content of the code string we inject
49
- * it as a raw text node into the AST tree as a child of the code element
50
- */
51
27
  function injectRawCodeStringPlugin(rawCodeString) {
52
- return () => (tree) => {
53
- visit(tree, 'element', (node) => {
54
- if (node.tagName === 'code') {
55
- node.children.push({
56
- type: 'text',
57
- value: rawCodeString,
58
- });
59
- }
28
+ return () => (tree) => {
29
+ visit(tree, "element", (node) => {
30
+ if (node.tagName === "code") {
31
+ node.children.push({
32
+ type: "text",
33
+ value: rawCodeString
60
34
  });
61
- };
35
+ }
36
+ });
37
+ };
62
38
  }
63
-
64
- export { syntaxHighlight };
39
+ export {
40
+ syntaxHighlight
41
+ };
42
+ //# sourceMappingURL=highlight.js.map
@@ -0,0 +1,7 @@
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 { unified, type Plugin } 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 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,SAAS,eAA4B;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;AAEpF,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
+ }
@@ -0,0 +1,64 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { syntaxHighlight } from "./highlight.js";
3
+ describe("syntaxHighlight", () => {
4
+ const mockLanguages = {
5
+ javascript: () => ({
6
+ name: "javascript",
7
+ aliases: ["js"],
8
+ contains: []
9
+ })
10
+ };
11
+ const defaultOptions = {
12
+ lang: "javascript",
13
+ languages: mockLanguages
14
+ };
15
+ const codeExample = `
16
+ fetch('https://galaxy.scalar.com/planets', {
17
+ method: 'POST',
18
+ headers: {
19
+ 'Content-Type': 'application/json'
20
+ }
21
+ })
22
+ `;
23
+ it("should return highlighted HTML for a given code string", () => {
24
+ const result = syntaxHighlight(codeExample, defaultOptions);
25
+ expect(result).toContain('class="hljs language-javascript"');
26
+ });
27
+ it("should mask credentials in the code string", () => {
28
+ const codeWithCredentials = `
29
+ fetch('https://galaxy.scalar.com/planets', {
30
+ method: 'POST',
31
+ headers: {
32
+ 'Content-Type': 'application/json',
33
+ 'Authorization': 'Bearer secret'
34
+ }
35
+ })
36
+ `;
37
+ const options = {
38
+ ...defaultOptions,
39
+ maskCredentials: ["secret"]
40
+ };
41
+ const result = syntaxHighlight(codeWithCredentials, options);
42
+ expect(result).toContain('<span class="credential"><span class="credential-value">secret</span></span>');
43
+ });
44
+ it("should handle line numbers if option is enabled", () => {
45
+ const options = {
46
+ ...defaultOptions,
47
+ lineNumbers: true
48
+ };
49
+ const result = syntaxHighlight(codeExample, options);
50
+ expect(result).toContain('class="line"');
51
+ });
52
+ it("should correctly work with special characters in credentials", () => {
53
+ const codeExampleWithSpecialChar = `
54
+ const secret = '(secret';
55
+ `;
56
+ const options = {
57
+ ...defaultOptions,
58
+ maskCredentials: ["(secret"]
59
+ };
60
+ const result = syntaxHighlight(codeExampleWithSpecialChar, options);
61
+ expect(result).toContain('<span class="credential"><span class="credential-value">(secret</span></span>');
62
+ });
63
+ });
64
+ //# sourceMappingURL=highlight.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/code/highlight.test.ts"],
4
+ "sourcesContent": ["import type { LanguageFn } from 'highlight.js'\nimport { describe, expect, it } from 'vitest'\n\nimport { syntaxHighlight } from './highlight'\n\ndescribe('syntaxHighlight', () => {\n const mockLanguages: Record<string, LanguageFn> = {\n javascript: () => ({\n name: 'javascript',\n aliases: ['js'],\n contains: [],\n }),\n }\n\n const defaultOptions = {\n lang: 'javascript',\n languages: mockLanguages,\n }\n\n const codeExample = `\n fetch('https://galaxy.scalar.com/planets', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n }\n })\n `\n\n it('should return highlighted HTML for a given code string', () => {\n const result = syntaxHighlight(codeExample, defaultOptions)\n expect(result).toContain('class=\"hljs language-javascript\"')\n })\n\n it('should mask credentials in the code string', () => {\n const codeWithCredentials = `\n fetch('https://galaxy.scalar.com/planets', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': 'Bearer secret'\n }\n })\n `\n const options = {\n ...defaultOptions,\n maskCredentials: ['secret'],\n }\n\n const result = syntaxHighlight(codeWithCredentials, options)\n expect(result).toContain('<span class=\"credential\"><span class=\"credential-value\">secret</span></span>')\n })\n\n it('should handle line numbers if option is enabled', () => {\n const options = {\n ...defaultOptions,\n lineNumbers: true,\n }\n\n const result = syntaxHighlight(codeExample, options)\n expect(result).toContain('class=\"line\"')\n })\n\n it('should correctly work with special characters in credentials', () => {\n const codeExampleWithSpecialChar = `\n const secret = '(secret';\n `\n const options = {\n ...defaultOptions,\n maskCredentials: ['(secret'],\n }\n\n const result = syntaxHighlight(codeExampleWithSpecialChar, options)\n expect(result).toContain('<span class=\"credential\"><span class=\"credential-value\">(secret</span></span>')\n })\n})\n"],
5
+ "mappings": "AACA,SAAS,UAAU,QAAQ,UAAU;AAErC,SAAS,uBAAuB;AAEhC,SAAS,mBAAmB,MAAM;AAChC,QAAM,gBAA4C;AAAA,IAChD,YAAY,OAAO;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,CAAC,IAAI;AAAA,MACd,UAAU,CAAC;AAAA,IACb;AAAA,EACF;AAEA,QAAM,iBAAiB;AAAA,IACrB,MAAM;AAAA,IACN,WAAW;AAAA,EACb;AAEA,QAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASpB,KAAG,0DAA0D,MAAM;AACjE,UAAM,SAAS,gBAAgB,aAAa,cAAc;AAC1D,WAAO,MAAM,EAAE,UAAU,kCAAkC;AAAA,EAC7D,CAAC;AAED,KAAG,8CAA8C,MAAM;AACrD,UAAM,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAS5B,UAAM,UAAU;AAAA,MACd,GAAG;AAAA,MACH,iBAAiB,CAAC,QAAQ;AAAA,IAC5B;AAEA,UAAM,SAAS,gBAAgB,qBAAqB,OAAO;AAC3D,WAAO,MAAM,EAAE,UAAU,8EAA8E;AAAA,EACzG,CAAC;AAED,KAAG,mDAAmD,MAAM;AAC1D,UAAM,UAAU;AAAA,MACd,GAAG;AAAA,MACH,aAAa;AAAA,IACf;AAEA,UAAM,SAAS,gBAAgB,aAAa,OAAO;AACnD,WAAO,MAAM,EAAE,UAAU,cAAc;AAAA,EACzC,CAAC;AAED,KAAG,gEAAgE,MAAM;AACvE,UAAM,6BAA6B;AAAA;AAAA;AAGnC,UAAM,UAAU;AAAA,MACd,GAAG;AAAA,MACH,iBAAiB,CAAC,SAAS;AAAA,IAC7B;AAEA,UAAM,SAAS,gBAAgB,4BAA4B,OAAO;AAClE,WAAO,MAAM,EAAE,UAAU,+EAA+E;AAAA,EAC1G,CAAC;AACH,CAAC;",
6
+ "names": []
7
+ }
@@ -1 +1,5 @@
1
- export { syntaxHighlight } from './highlight.js';
1
+ import { syntaxHighlight } from "./highlight.js";
2
+ export {
3
+ syntaxHighlight
4
+ };
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
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,94 +1,73 @@
1
- import { visit } from 'unist-util-visit';
2
-
3
- // ---------------------------------------------------------------------------
4
- // Line Numbering plugin
1
+ import { visit } from "unist-util-visit";
5
2
  function isText(element) {
6
- return element?.type === 'text';
3
+ return element?.type === "text";
7
4
  }
8
5
  function isElement(node) {
9
- return node?.type === 'element';
6
+ return node?.type === "element";
10
7
  }
11
8
  function textElement(value) {
12
- return { type: 'text', value };
9
+ return { type: "text", value };
13
10
  }
14
11
  function lineBreak() {
15
- return { type: 'text', value: '\n' };
12
+ return { type: "text", value: "\n" };
16
13
  }
17
- /**
18
- * Adds lines to code blocks
19
- */
20
14
  function codeBlockLinesPlugin() {
21
- return (tree) => {
22
- visit(tree, 'element', (node, _i, parent) => {
23
- if (parent?.type === 'element' && parent.tagName === 'pre' && node.tagName === 'code') {
24
- let numLines = 0;
25
- // Wraps each line in a span
26
- node.children = addLines(node);
27
- // Adds a line break to the end of each line
28
- node.children.forEach((child) => {
29
- if (child.type === 'element' && child.tagName === 'span') {
30
- const lastChild = child.children[child.children.length - 1];
31
- if (lastChild && (!isText(lastChild) || (isText(lastChild) && !hasLineBreak(lastChild)))) {
32
- child.children.push(lineBreak());
33
- numLines++;
34
- }
35
- }
36
- });
37
- // We need to maintain a count of the total lines to allow space for the labels
38
- node.properties.style = [`--line-count: ${numLines};`, `--line-digits: ${numLines.toString().length};`];
15
+ return (tree) => {
16
+ visit(tree, "element", (node, _i, parent) => {
17
+ if (parent?.type === "element" && parent.tagName === "pre" && node.tagName === "code") {
18
+ let numLines = 0;
19
+ node.children = addLines(node);
20
+ node.children.forEach((child) => {
21
+ if (child.type === "element" && child.tagName === "span") {
22
+ const lastChild = child.children[child.children.length - 1];
23
+ if (lastChild && (!isText(lastChild) || isText(lastChild) && !hasLineBreak(lastChild))) {
24
+ child.children.push(lineBreak());
25
+ numLines++;
39
26
  }
27
+ }
40
28
  });
41
- // console.log('NUMBER OF LINES IS: ', numLines)
42
- };
29
+ node.properties.style = [`--line-count: ${numLines};`, `--line-digits: ${numLines.toString().length};`];
30
+ }
31
+ });
32
+ };
43
33
  }
44
- /**
45
- * Adds lines to a node recursively and returns them
46
- *
47
- * @param node - The node to add lines to
48
- * @param lines - The current lines
49
- * @param copyParent - Whether to copy the parent node to save the original node styles
50
- */
51
34
  function addLines(node, lines = [], copyParent) {
52
- const line = () => lines[lines.length - 1] ?? (lines.push(createLine()) && lines[lines.length - 1]);
53
- node.children.forEach((child) => {
54
- if (isText(child) && hasLineBreak(child)) {
55
- const split = child.value.split(/\n/);
56
- split.forEach((content, i) => {
57
- copyParent
58
- ? line().children.push({ ...node, children: [textElement(content)] })
59
- : line().children.push(textElement(content));
60
- i !== split.length - 1 && lines.push(createLine());
61
- });
62
- }
63
- else if (isElement(child) && child.children.some(hasLineBreak)) {
64
- addLines(child, lines, true);
35
+ const line = () => lines[lines.length - 1] ?? (lines.push(createLine()) && lines[lines.length - 1] || void 0);
36
+ node.children.forEach((child) => {
37
+ if (isText(child) && hasLineBreak(child)) {
38
+ const split = child.value.split(/\n/);
39
+ split.forEach((content, i) => {
40
+ if (copyParent) {
41
+ line()?.children.push({ ...node, children: [textElement(content)] });
42
+ } else {
43
+ line()?.children.push(textElement(content));
65
44
  }
66
- else {
67
- line().children.push(child);
68
- }
69
- });
70
- return lines;
45
+ i !== split.length - 1 && lines.push(createLine());
46
+ });
47
+ } else if (isElement(child) && child.children.some(hasLineBreak)) {
48
+ addLines(child, lines, true);
49
+ } else {
50
+ line()?.children.push(child);
51
+ }
52
+ });
53
+ return lines;
71
54
  }
72
- /**
73
- * Creates a new line element
74
- *
75
- * @param children - The children the line should have initially
76
- */
77
55
  function createLine(...children) {
78
- return {
79
- type: 'element',
80
- tagName: 'span',
81
- properties: { class: ['line'] },
82
- children,
83
- };
56
+ return {
57
+ type: "element",
58
+ tagName: "span",
59
+ properties: { class: ["line"] },
60
+ children
61
+ };
84
62
  }
85
- /**
86
- * Checks if a node has a line break
87
- *
88
- * @param node - The node to check
89
- */
90
63
  function hasLineBreak(node) {
91
- return (isText(node) && /\r?\n/.test(node.value)) || (isElement(node) && node.children.some(hasLineBreak));
64
+ return isText(node) && /\r?\n/.test(node.value) || isElement(node) && node.children.some(hasLineBreak);
92
65
  }
93
-
94
- export { codeBlockLinesPlugin, isElement, isText, lineBreak, textElement };
66
+ export {
67
+ codeBlockLinesPlugin,
68
+ isElement,
69
+ isText,
70
+ lineBreak,
71
+ textElement
72
+ };
73
+ //# sourceMappingURL=line-numbers.js.map
@@ -0,0 +1,7 @@
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\nexport function isText(element?: ElementContent): element is Text {\n return element?.type === 'text'\n}\n\nexport function isElement(node?: ElementContent): node is Element {\n return node?.type === 'element'\n}\n\nexport function textElement(value: string): Text {\n return { type: 'text', value }\n}\n\nexport function 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;AAKf,SAAS,OAAO,SAA2C;AAChE,SAAO,SAAS,SAAS;AAC3B;AAEO,SAAS,UAAU,MAAwC;AAChE,SAAO,MAAM,SAAS;AACxB;AAEO,SAAS,YAAY,OAAqB;AAC/C,SAAO,EAAE,MAAM,QAAQ,MAAM;AAC/B;AAEO,SAAS,YAAkB;AAChC,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
+ }
package/dist/constants.js CHANGED
@@ -1,13 +1,13 @@
1
- /** Map common markdown language shortcuts to lowlight languages */
2
1
  const lowlightLanguageMappings = {
3
- 'ts': 'typescript',
4
- 'js': 'javascript',
5
- 'py': 'python',
6
- 'py3': 'python',
7
- 'c#': 'csharp',
8
- 'c++': 'cpp',
9
- 'node': 'javascript',
2
+ "ts": "typescript",
3
+ "js": "javascript",
4
+ "py": "python",
5
+ "py3": "python",
6
+ "c#": "csharp",
7
+ "c++": "cpp",
8
+ "node": "javascript"
10
9
  };
11
- //
12
-
13
- export { lowlightLanguageMappings };
10
+ export {
11
+ lowlightLanguageMappings
12
+ };
13
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1,7 @@
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 CHANGED
@@ -1,7 +1,12 @@
1
- export { lowlightLanguageMappings } from './constants.js';
2
- export { rehypeHighlight } from './rehype-highlight/rehype-highlight.js';
3
- export { htmlFromMarkdown } from './markdown/markdown.js';
4
- export { syntaxHighlight } from './code/highlight.js';
5
- export { jsonYamlLanguages } from './languages/json-yaml.js';
6
- export { standardLanguages } from './languages/standard.js';
7
- export { basicLanguages } from './languages/basic.js';
1
+ import { rehypeHighlight } from "./rehype-highlight/index.js";
2
+ import { lowlightLanguageMappings } from "./constants.js";
3
+ import { htmlFromMarkdown } from "./markdown/index.js";
4
+ import { syntaxHighlight } from "./code/index.js";
5
+ export * from "./languages/index.js";
6
+ export {
7
+ htmlFromMarkdown,
8
+ lowlightLanguageMappings,
9
+ rehypeHighlight,
10
+ syntaxHighlight
11
+ };
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts"],
4
+ "sourcesContent": ["export { rehypeHighlight } from './rehype-highlight'\nexport { lowlightLanguageMappings } from './constants'\nexport { htmlFromMarkdown } from './markdown'\nexport { syntaxHighlight } from './code'\nexport * from './languages'\n"],
5
+ "mappings": "AAAA,SAAS,uBAAuB;AAChC,SAAS,gCAAgC;AACzC,SAAS,wBAAwB;AACjC,SAAS,uBAAuB;AAChC,cAAc;",
6
+ "names": []
7
+ }
@@ -1,32 +1,33 @@
1
- import bash from 'highlight.js/lib/languages/bash';
2
- import css from 'highlight.js/lib/languages/css';
3
- import javascript from 'highlight.js/lib/languages/javascript';
4
- import json from 'highlight.js/lib/languages/json';
5
- import less from 'highlight.js/lib/languages/less';
6
- import markdown from 'highlight.js/lib/languages/markdown';
7
- import plaintext from 'highlight.js/lib/languages/plaintext';
8
- import python from 'highlight.js/lib/languages/python';
9
- import scss from 'highlight.js/lib/languages/scss';
10
- import shell from 'highlight.js/lib/languages/shell';
11
- import typescript from 'highlight.js/lib/languages/typescript';
12
- import xml from 'highlight.js/lib/languages/xml';
13
- import yaml from 'highlight.js/lib/languages/yaml';
14
-
1
+ import bash from "highlight.js/lib/languages/bash";
2
+ import css from "highlight.js/lib/languages/css";
3
+ import javascript from "highlight.js/lib/languages/javascript";
4
+ import json from "highlight.js/lib/languages/json";
5
+ import less from "highlight.js/lib/languages/less";
6
+ import markdown from "highlight.js/lib/languages/markdown";
7
+ import plaintext from "highlight.js/lib/languages/plaintext";
8
+ import python from "highlight.js/lib/languages/python";
9
+ import scss from "highlight.js/lib/languages/scss";
10
+ import shell from "highlight.js/lib/languages/shell";
11
+ import typescript from "highlight.js/lib/languages/typescript";
12
+ import xml from "highlight.js/lib/languages/xml";
13
+ import yaml from "highlight.js/lib/languages/yaml";
15
14
  const basicLanguages = {
16
- bash,
17
- css,
18
- html: xml,
19
- javascript,
20
- json,
21
- less,
22
- markdown,
23
- plaintext,
24
- python,
25
- scss,
26
- shell,
27
- typescript,
28
- xml,
29
- yaml,
15
+ bash,
16
+ css,
17
+ html: xml,
18
+ javascript,
19
+ json,
20
+ less,
21
+ markdown,
22
+ plaintext,
23
+ python,
24
+ scss,
25
+ shell,
26
+ typescript,
27
+ xml,
28
+ yaml
30
29
  };
31
-
32
- export { basicLanguages };
30
+ export {
31
+ basicLanguages
32
+ };
33
+ //# sourceMappingURL=basic.js.map
@@ -0,0 +1,7 @@
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,3 +1,9 @@
1
- export { jsonYamlLanguages } from './json-yaml.js';
2
- export { standardLanguages } from './standard.js';
3
- export { basicLanguages } from './basic.js';
1
+ import { jsonYamlLanguages } from "./json-yaml.js";
2
+ import { standardLanguages } from "./standard.js";
3
+ import { basicLanguages } from "./basic.js";
4
+ export {
5
+ basicLanguages,
6
+ jsonYamlLanguages,
7
+ standardLanguages
8
+ };
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/languages/index.ts"],
4
+ "sourcesContent": ["export { jsonYamlLanguages } from './json-yaml'\nexport { standardLanguages } from './standard'\nexport { basicLanguages } from './basic'\n"],
5
+ "mappings": "AAAA,SAAS,yBAAyB;AAClC,SAAS,yBAAyB;AAClC,SAAS,sBAAsB;",
6
+ "names": []
7
+ }
@@ -1,11 +1,12 @@
1
- import json from 'highlight.js/lib/languages/json';
2
- import plaintext from 'highlight.js/lib/languages/plaintext';
3
- import yaml from 'highlight.js/lib/languages/yaml';
4
-
1
+ import json from "highlight.js/lib/languages/json";
2
+ import plaintext from "highlight.js/lib/languages/plaintext";
3
+ import yaml from "highlight.js/lib/languages/yaml";
5
4
  const jsonYamlLanguages = {
6
- json,
7
- plaintext,
8
- yaml,
5
+ json,
6
+ plaintext,
7
+ yaml
9
8
  };
10
-
11
- export { jsonYamlLanguages };
9
+ export {
10
+ jsonYamlLanguages
11
+ };
12
+ //# sourceMappingURL=json-yaml.js.map
@@ -0,0 +1,7 @@
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
+ }