@scalar/code-highlight 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @scalar/code-highlight
2
2
 
3
+ ## 0.0.8
4
+
5
+ ### Patch Changes
6
+
7
+ - b910fc1: feat: new Markdown helper methods splitContent and getHeadings
8
+
3
9
  ## 0.0.7
4
10
 
5
11
  ### Patch Changes
@@ -1,2 +1,2 @@
1
- export { htmlFromMarkdown } from './markdown.js';
1
+ export * from './markdown.js';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/markdown/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/markdown/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA"}
@@ -1 +1 @@
1
- export { htmlFromMarkdown } from './markdown.js';
1
+ export { getHeadings, htmlFromMarkdown, splitContent } from './markdown.js';
@@ -1,6 +1,21 @@
1
- /** Take a markdown string and generate a raw HTML string */
2
- export declare function htmlFromMarkdown(markdownString: string, options?: {
1
+ /**
2
+ * Take a Markdown string and generate HTML from it
3
+ */
4
+ export declare function htmlFromMarkdown(markdown: string, options?: {
3
5
  removeTags?: string[];
4
6
  allowTags?: string[];
7
+ transform?: (node: Record<string, any>) => Record<string, any>;
8
+ transformType?: string;
5
9
  }): string;
10
+ /**
11
+ * Find all headings of a specific type in a Markdown AST.
12
+ */
13
+ export declare function getHeadings(markdown: string, depth?: number): {
14
+ depth: number;
15
+ value: string;
16
+ }[];
17
+ /**
18
+ * Return multiple Markdown documents. Every heading should be its own document.
19
+ */
20
+ export declare function splitContent(markdown: string): string[];
6
21
  //# sourceMappingURL=markdown.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/markdown/markdown.ts"],"names":[],"mappings":"AAYA,4DAA4D;AAC5D,wBAAgB,gBAAgB,CAC9B,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE;IACR,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;CACrB,UA4CF"}
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/markdown/markdown.ts"],"names":[],"mappings":"AA4CA;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IACR,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,UAkDF;AASD;;GAEG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,MAAU,GAChB;IACD,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd,EAAE,CAiBF;AAuBD;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,YAgC5C"}
@@ -6,12 +6,33 @@ import rehypeStringify from 'rehype-stringify';
6
6
  import remarkGfm from 'remark-gfm';
7
7
  import remarkParse from 'remark-parse';
8
8
  import remarkRehype from 'remark-rehype';
9
+ import remarkStringify from 'remark-stringify';
9
10
  import { unified } from 'unified';
11
+ import { visit, SKIP } from 'unist-util-visit';
10
12
  import { rehypeHighlight } from '../rehype-highlight/rehype-highlight.js';
11
13
  import { standardLanguages } from '../languages/standard.js';
12
14
 
13
- /** Take a markdown string and generate a raw HTML string */
14
- function htmlFromMarkdown(markdownString, options) {
15
+ /**
16
+ * Plugin to transform nodes in a Markdown AST
17
+ */
18
+ const transformNodes = function (options,
19
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
20
+ ..._ignored) {
21
+ return (tree) => {
22
+ if (!options?.transform || !options?.type) {
23
+ return;
24
+ }
25
+ visit(tree, options?.type, (node) => {
26
+ options?.transform ? options?.transform(node) : node;
27
+ return SKIP;
28
+ });
29
+ return;
30
+ };
31
+ };
32
+ /**
33
+ * Take a Markdown string and generate HTML from it
34
+ */
35
+ function htmlFromMarkdown(markdown, options) {
15
36
  // Add permitted tags and remove stripped ones
16
37
  const removeTags = options?.removeTags ?? [];
17
38
  const tagNames = [
@@ -23,6 +44,10 @@ function htmlFromMarkdown(markdownString, options) {
23
44
  .use(remarkParse)
24
45
  // Support autolink literals, footnotes, strikethrough, tables and tasklists
25
46
  .use(remarkGfm)
47
+ .use(transformNodes, {
48
+ transform: options?.transform,
49
+ type: options?.transformType,
50
+ })
26
51
  // Allows any HTML tags
27
52
  .use(remarkRehype, { allowDangerousHtml: true })
28
53
  // Creates a HTML AST
@@ -30,6 +55,8 @@ function htmlFromMarkdown(markdownString, options) {
30
55
  // Removes disallowed tags
31
56
  .use(rehypeSanitize, {
32
57
  ...defaultSchema,
58
+ // Don’t prefix the heading ids
59
+ clobberPrefix: '',
33
60
  // Makes it even more strict
34
61
  tagNames,
35
62
  attributes: {
@@ -50,8 +77,86 @@ function htmlFromMarkdown(markdownString, options) {
50
77
  // Converts the HTML AST to a string
51
78
  .use(rehypeStringify)
52
79
  // Run the pipeline
53
- .processSync(markdownString);
80
+ .processSync(markdown);
54
81
  return html.toString();
55
82
  }
83
+ /**
84
+ * Create a Markdown AST from a string.
85
+ */
86
+ function getMarkdownAst(markdown) {
87
+ return unified().use(remarkParse).use(remarkGfm).parse(markdown);
88
+ }
89
+ /**
90
+ * Find all headings of a specific type in a Markdown AST.
91
+ */
92
+ function getHeadings(markdown, depth = 1) {
93
+ const tree = getMarkdownAst(markdown);
94
+ const nodes = [];
95
+ visit(tree, 'heading', (node) => {
96
+ const text = findTextInHeading(node);
97
+ if (text) {
98
+ nodes.push({ depth: node.depth ?? depth, value: text.value });
99
+ }
100
+ });
101
+ return nodes;
102
+ }
103
+ /**
104
+ * Find the text in a Markdown node (recursively).
105
+ */
106
+ function findTextInHeading(node) {
107
+ if (node.type === 'text') {
108
+ return node;
109
+ }
110
+ if ('children' in node && node.children) {
111
+ for (const child of node.children) {
112
+ const text = findTextInHeading(child);
113
+ if (text) {
114
+ return text;
115
+ }
116
+ }
117
+ }
118
+ return null;
119
+ }
120
+ /**
121
+ * Return multiple Markdown documents. Every heading should be its own document.
122
+ */
123
+ function splitContent(markdown) {
124
+ const tree = getMarkdownAst(markdown);
125
+ /** Sections */
126
+ const sections = [];
127
+ /** Nodes inside a section */
128
+ let nodes = [];
129
+ tree.children?.forEach((node) => {
130
+ // If the node is a heading, start a new section
131
+ if (node.type === 'heading') {
132
+ if (nodes.length) {
133
+ sections.push(nodes);
134
+ }
135
+ sections.push([node]);
136
+ nodes = [];
137
+ }
138
+ // Otherwise, add the node to the current section
139
+ else {
140
+ nodes.push(node);
141
+ }
142
+ });
143
+ // Add any remaining nodes
144
+ if (nodes.length) {
145
+ sections.push(nodes);
146
+ }
147
+ return sections.map((section) => createDocument(section));
148
+ }
149
+ /**
150
+ * Use remark to create a Markdown document from a list of nodes.
151
+ */
152
+ function createDocument(nodes) {
153
+ // Create the Markdown string
154
+ const markdown = unified().use(remarkStringify).use(remarkGfm).stringify({
155
+ type: 'root',
156
+ children: nodes,
157
+ });
158
+ // Remove the whitespace
159
+ return markdown.trim();
160
+ }
56
161
 
57
- export { htmlFromMarkdown };
162
+ export { getHeadings, htmlFromMarkdown, splitContent };
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "lowlight",
17
17
  "highlightjs"
18
18
  ],
19
- "version": "0.0.7",
19
+ "version": "0.0.8",
20
20
  "engines": {
21
21
  "node": ">=18"
22
22
  },
@@ -73,19 +73,21 @@
73
73
  "remark-gfm": "^4.0.0",
74
74
  "remark-parse": "^11.0.0",
75
75
  "remark-rehype": "^11.1.0",
76
+ "remark-stringify": "^11.0.0",
76
77
  "unified": "^11.0.4",
77
78
  "unist-util-visit": "^5.0.0"
78
79
  },
79
80
  "devDependencies": {
80
81
  "@rollup/plugin-typescript": "^11.1.6",
81
82
  "@types/hast": "^3.0.4",
83
+ "@types/mdast": "^4.0.4",
84
+ "@types/unist": "^3.0.2",
82
85
  "rollup": "^4.16.4",
83
- "tsc-alias": "^1.8.8",
84
86
  "vfile": "^6.0.1",
85
87
  "vite": "^5.2.10",
86
- "vue": "^3.4.22",
87
- "@scalar/build-tooling": "0.1.9",
88
- "@scalar/themes": "0.9.13"
88
+ "vue": "^3.4.29",
89
+ "@scalar/build-tooling": "0.1.10",
90
+ "@scalar/themes": "0.9.22"
89
91
  },
90
92
  "scripts": {
91
93
  "build": "scalar-build-rollup",