rehype-highlight-code-lines 0.0.1 → 0.0.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.
- package/README.md +34 -3
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +51 -28
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +75 -47
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
[![typescript][badge-typescript]][typescript-url]
|
|
9
9
|
[![License][badge-license]][github-license-url]
|
|
10
10
|
|
|
11
|
-
This package is a [unified][unified] ([rehype][rehype]) plugin **to add
|
|
11
|
+
This package is a [unified][unified] ([rehype][rehype]) plugin **to add container to each line in a code block, allowing numbering of the code block and highlighting desired lines of code**.
|
|
12
12
|
|
|
13
13
|
**[unified][unified]** is a project that transforms content with abstract syntax trees (ASTs) using the new parser **[micromark][micromark]**. **[remark][remark]** adds support for markdown to unified. **[mdast][mdast]** is the Markdown Abstract Syntax Tree (AST) which is a specification for representing markdown in a syntax tree. "**[rehype][rehype]**" is a tool that transforms HTML with plugins. "**[hast][hast]**" stands for HTML Abstract Syntax Tree (HAST) that rehype uses.
|
|
14
14
|
|
|
@@ -131,18 +131,49 @@ Without `rehype-highlight-code-lines`, the lines of code wouldn't be in a `div`.
|
|
|
131
131
|
</pre>
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
+
***Note:** `hljs` prefix comes from `rehype-highlight`*.
|
|
135
|
+
|
|
134
136
|
## Options
|
|
135
137
|
|
|
136
|
-
|
|
138
|
+
All options are **optional** and have **default values**.
|
|
137
139
|
|
|
138
140
|
```typescript
|
|
139
141
|
type HighlightLinesOptions = {
|
|
140
|
-
showLineNumbers?: boolean; //
|
|
142
|
+
showLineNumbers?: boolean; // default is "false"
|
|
143
|
+
lineContainerTagName?: "div" | "span"; // default is "span"
|
|
141
144
|
};
|
|
142
145
|
|
|
143
146
|
use(rehypeHighlightLines, HighlightLinesOptions);
|
|
144
147
|
```
|
|
145
148
|
|
|
149
|
+
#### `showLineNumbers`
|
|
150
|
+
|
|
151
|
+
It is a **boolean** option which is for all code blocks will be numbered.
|
|
152
|
+
|
|
153
|
+
By default, it is `false`.
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
use(rehypeHighlightLines, {
|
|
157
|
+
showLineNumbers: true,
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Now, all code blocks will become numbered by line.
|
|
162
|
+
|
|
163
|
+
#### `lineContainerTagName`
|
|
164
|
+
|
|
165
|
+
It is a **union** type option which is **"div" | "span"** for providing custom HTML tag name for code lines.
|
|
166
|
+
|
|
167
|
+
By default, it is `span` which is **inline** level container. If you set it as `div`, the container will be **block** level, in perspective of CSS.
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
use(rehypeHighlightLines, {
|
|
171
|
+
lineContainerTagName: "div",
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Now, the code line container's tag name will be `div`.
|
|
176
|
+
|
|
146
177
|
### Examples:
|
|
147
178
|
|
|
148
179
|
```typescript
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Plugin } from "unified";
|
|
|
2
2
|
import type { Root } from "hast";
|
|
3
3
|
export type HighlightLinesOptions = {
|
|
4
4
|
showLineNumbers?: boolean;
|
|
5
|
+
lineContainerTagName?: "div" | "span";
|
|
5
6
|
};
|
|
6
7
|
export declare function clsx(arr: (string | false | null | undefined | 0)[]): string[];
|
|
7
8
|
/**
|
package/dist/esm/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { visit, CONTINUE } from "unist-util-visit";
|
|
|
2
2
|
import rangeParser from "parse-numeric-range";
|
|
3
3
|
const DEFAULT_SETTINGS = {
|
|
4
4
|
showLineNumbers: false,
|
|
5
|
+
lineContainerTagName: "span",
|
|
5
6
|
};
|
|
6
7
|
// a simple util for our use case, like clsx package
|
|
7
8
|
export function clsx(arr) {
|
|
@@ -14,25 +15,54 @@ export function clsx(arr) {
|
|
|
14
15
|
*/
|
|
15
16
|
const plugin = (options) => {
|
|
16
17
|
const settings = Object.assign({}, DEFAULT_SETTINGS, options);
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* flatten deep nodes
|
|
21
|
+
*
|
|
22
|
+
*/
|
|
23
|
+
function flattenCodeTree(children, className = [], newTree = []) {
|
|
24
|
+
for (let i = 0; i < children.length; i++) {
|
|
25
|
+
const node = children[i];
|
|
26
|
+
console.log(node);
|
|
27
|
+
if (node.type !== "element") {
|
|
28
|
+
newTree = newTree.concat([node]);
|
|
29
|
+
}
|
|
30
|
+
else if (node.children.length === 1 && node.children[0].type !== "element") {
|
|
31
|
+
// @ts-expect-error
|
|
32
|
+
node.properties.className = className.concat(node.properties.className);
|
|
33
|
+
newTree = newTree.concat([node]);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// @ts-expect-error
|
|
37
|
+
const classNames = className.concat(node.properties?.className || []);
|
|
38
|
+
newTree = newTree.concat(flattenCodeTree(node.children, classNames));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return newTree;
|
|
42
|
+
}
|
|
17
43
|
/**
|
|
18
44
|
*
|
|
19
45
|
* constructs the line element
|
|
20
46
|
*
|
|
21
47
|
*/
|
|
22
|
-
const createLine = (children, lineNumber,
|
|
48
|
+
const createLine = (children, lineNumber, showLineNumbers, linesToBeHighlighted) => {
|
|
23
49
|
return {
|
|
24
50
|
type: "element",
|
|
25
|
-
tagName:
|
|
51
|
+
tagName: settings.lineContainerTagName,
|
|
26
52
|
children,
|
|
27
53
|
properties: {
|
|
28
|
-
className:
|
|
29
|
-
|
|
54
|
+
className: clsx([
|
|
55
|
+
"code-line",
|
|
56
|
+
showLineNumbers && "numbered-code-line",
|
|
57
|
+
linesToBeHighlighted.includes(lineNumber) && "highlighted-code-line",
|
|
58
|
+
]),
|
|
59
|
+
dataLineNumber: showLineNumbers ? lineNumber : undefined,
|
|
30
60
|
},
|
|
31
61
|
};
|
|
32
62
|
};
|
|
33
63
|
// match all common types of line breaks
|
|
34
64
|
const RE = /\r?\n|\r/g;
|
|
35
|
-
function
|
|
65
|
+
function gutter(tree, showLineNumbers, linesToBeHighlighted) {
|
|
36
66
|
const replacement = [];
|
|
37
67
|
let index = -1;
|
|
38
68
|
let start = 0;
|
|
@@ -44,7 +74,7 @@ const plugin = (options) => {
|
|
|
44
74
|
let textStart = 0;
|
|
45
75
|
let match = RE.exec(child.value);
|
|
46
76
|
while (match) {
|
|
47
|
-
// Nodes in this line.
|
|
77
|
+
// Nodes in this line. (current child is exclusive)
|
|
48
78
|
const line = tree.children.slice(start, index);
|
|
49
79
|
/* v8 ignore start */
|
|
50
80
|
// Prepend text from a partial matched earlier text.
|
|
@@ -55,21 +85,16 @@ const plugin = (options) => {
|
|
|
55
85
|
/* v8 ignore end */
|
|
56
86
|
// Append text from this text.
|
|
57
87
|
if (match.index > textStart) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
value: child.value.slice(textStart, match.index),
|
|
61
|
-
});
|
|
88
|
+
const value = child.value.slice(textStart, match.index);
|
|
89
|
+
line.push({ type: "text", value });
|
|
62
90
|
}
|
|
63
|
-
// Add a line
|
|
91
|
+
// Add a line
|
|
64
92
|
lineNumber += 1;
|
|
65
|
-
replacement.push(createLine(line,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
type: "text",
|
|
71
|
-
value: match[0],
|
|
72
|
-
});
|
|
93
|
+
replacement.push(createLine(line, lineNumber, showLineNumbers, linesToBeHighlighted));
|
|
94
|
+
// Add eol if the tag name is "span"
|
|
95
|
+
if (settings.lineContainerTagName === "span") {
|
|
96
|
+
replacement.push({ type: "text", value: match[0] });
|
|
97
|
+
}
|
|
73
98
|
start = index + 1;
|
|
74
99
|
textStart = match.index + match[0].length;
|
|
75
100
|
match = RE.exec(child.value);
|
|
@@ -89,11 +114,7 @@ const plugin = (options) => {
|
|
|
89
114
|
}
|
|
90
115
|
if (line.length > 0) {
|
|
91
116
|
lineNumber += 1;
|
|
92
|
-
replacement.push(createLine(line,
|
|
93
|
-
"code-line",
|
|
94
|
-
showLineNumbers && "numbered-code-line",
|
|
95
|
-
linesToBeHighlighted.includes(lineNumber) && "highlighted-code-line",
|
|
96
|
-
])));
|
|
117
|
+
replacement.push(createLine(line, lineNumber, showLineNumbers, linesToBeHighlighted));
|
|
97
118
|
}
|
|
98
119
|
/* v8 ignore end */
|
|
99
120
|
// Replace children with new array.
|
|
@@ -127,9 +148,9 @@ const plugin = (options) => {
|
|
|
127
148
|
const language = className.slice(9).toLowerCase();
|
|
128
149
|
if (language.startsWith("{") || language.startsWith("showlinenumbers")) {
|
|
129
150
|
meta = meta ? language + meta : language;
|
|
130
|
-
const
|
|
131
|
-
if (
|
|
132
|
-
code.properties.className[
|
|
151
|
+
const idx = code.properties.className.findIndex(testingFunction);
|
|
152
|
+
if (idx > -1) {
|
|
153
|
+
code.properties.className[idx] = "language-unknown";
|
|
133
154
|
}
|
|
134
155
|
}
|
|
135
156
|
}
|
|
@@ -143,8 +164,10 @@ const plugin = (options) => {
|
|
|
143
164
|
const linesToBeHighlighted = strLineNumbers ? rangeParser(strLineNumbers) : [];
|
|
144
165
|
if (!showLineNumbers && linesToBeHighlighted.length === 0)
|
|
145
166
|
return;
|
|
167
|
+
// flatten deeper nodes into first level <span>s an texts
|
|
168
|
+
code.children = flattenCodeTree(code.children);
|
|
146
169
|
// add wrapper for each line mutating the code element
|
|
147
|
-
|
|
170
|
+
gutter(code, showLineNumbers, linesToBeHighlighted);
|
|
148
171
|
});
|
|
149
172
|
};
|
|
150
173
|
};
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,WAAW,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -3,14 +3,26 @@ import type { Root, Element, ElementContent, ElementData } from "hast";
|
|
|
3
3
|
import { type VisitorResult, visit, CONTINUE } from "unist-util-visit";
|
|
4
4
|
import rangeParser from "parse-numeric-range";
|
|
5
5
|
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
7
|
+
type Prettify<T> = { [K in keyof T]: T[K] } & {};
|
|
8
|
+
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
10
|
+
type PartiallyRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
|
|
11
|
+
|
|
6
12
|
export type HighlightLinesOptions = {
|
|
7
13
|
showLineNumbers?: boolean;
|
|
14
|
+
lineContainerTagName?: "div" | "span";
|
|
8
15
|
};
|
|
9
16
|
|
|
10
17
|
const DEFAULT_SETTINGS: HighlightLinesOptions = {
|
|
11
18
|
showLineNumbers: false,
|
|
19
|
+
lineContainerTagName: "span",
|
|
12
20
|
};
|
|
13
21
|
|
|
22
|
+
type PartiallyRequiredHighlightLinesOptions = Prettify<
|
|
23
|
+
PartiallyRequired<HighlightLinesOptions, "showLineNumbers" | "lineContainerTagName">
|
|
24
|
+
>;
|
|
25
|
+
|
|
14
26
|
type CodeData = ElementData & {
|
|
15
27
|
meta?: string;
|
|
16
28
|
};
|
|
@@ -26,7 +38,39 @@ export function clsx(arr: (string | false | null | undefined | 0)[]): string[] {
|
|
|
26
38
|
*
|
|
27
39
|
*/
|
|
28
40
|
const plugin: Plugin<[HighlightLinesOptions?], Root> = (options) => {
|
|
29
|
-
const settings = Object.assign(
|
|
41
|
+
const settings = Object.assign(
|
|
42
|
+
{},
|
|
43
|
+
DEFAULT_SETTINGS,
|
|
44
|
+
options,
|
|
45
|
+
) as PartiallyRequiredHighlightLinesOptions;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
*
|
|
49
|
+
* flatten deep nodes
|
|
50
|
+
*
|
|
51
|
+
*/
|
|
52
|
+
function flattenCodeTree(
|
|
53
|
+
children: ElementContent[],
|
|
54
|
+
className: string[] = [],
|
|
55
|
+
newTree: ElementContent[] = [],
|
|
56
|
+
): ElementContent[] {
|
|
57
|
+
for (let i = 0; i < children.length; i++) {
|
|
58
|
+
const node = children[i];
|
|
59
|
+
console.log(node);
|
|
60
|
+
if (node.type !== "element") {
|
|
61
|
+
newTree = newTree.concat([node]);
|
|
62
|
+
} else if (node.children.length === 1 && node.children[0].type !== "element") {
|
|
63
|
+
// @ts-expect-error
|
|
64
|
+
node.properties.className = className.concat(node.properties.className);
|
|
65
|
+
newTree = newTree.concat([node]);
|
|
66
|
+
} else {
|
|
67
|
+
// @ts-expect-error
|
|
68
|
+
const classNames = className.concat(node.properties?.className || []);
|
|
69
|
+
newTree = newTree.concat(flattenCodeTree(node.children, classNames));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return newTree;
|
|
73
|
+
}
|
|
30
74
|
|
|
31
75
|
/**
|
|
32
76
|
*
|
|
@@ -35,16 +79,21 @@ const plugin: Plugin<[HighlightLinesOptions?], Root> = (options) => {
|
|
|
35
79
|
*/
|
|
36
80
|
const createLine = (
|
|
37
81
|
children: ElementContent[],
|
|
38
|
-
lineNumber: number
|
|
39
|
-
|
|
82
|
+
lineNumber: number,
|
|
83
|
+
showLineNumbers: boolean,
|
|
84
|
+
linesToBeHighlighted: number[],
|
|
40
85
|
): Element => {
|
|
41
86
|
return {
|
|
42
87
|
type: "element",
|
|
43
|
-
tagName:
|
|
88
|
+
tagName: settings.lineContainerTagName,
|
|
44
89
|
children,
|
|
45
90
|
properties: {
|
|
46
|
-
className:
|
|
47
|
-
|
|
91
|
+
className: clsx([
|
|
92
|
+
"code-line",
|
|
93
|
+
showLineNumbers && "numbered-code-line",
|
|
94
|
+
linesToBeHighlighted.includes(lineNumber) && "highlighted-code-line",
|
|
95
|
+
]),
|
|
96
|
+
dataLineNumber: showLineNumbers ? lineNumber : undefined,
|
|
48
97
|
},
|
|
49
98
|
};
|
|
50
99
|
};
|
|
@@ -52,12 +101,9 @@ const plugin: Plugin<[HighlightLinesOptions?], Root> = (options) => {
|
|
|
52
101
|
// match all common types of line breaks
|
|
53
102
|
const RE = /\r?\n|\r/g;
|
|
54
103
|
|
|
55
|
-
function
|
|
56
|
-
tree: Element,
|
|
57
|
-
showLineNumbers: boolean,
|
|
58
|
-
linesToBeHighlighted: number[],
|
|
59
|
-
) {
|
|
104
|
+
function gutter(tree: Element, showLineNumbers: boolean, linesToBeHighlighted: number[]) {
|
|
60
105
|
const replacement: Array<ElementContent> = [];
|
|
106
|
+
|
|
61
107
|
let index = -1;
|
|
62
108
|
let start = 0;
|
|
63
109
|
let startTextRemainder = "";
|
|
@@ -71,7 +117,7 @@ const plugin: Plugin<[HighlightLinesOptions?], Root> = (options) => {
|
|
|
71
117
|
let match = RE.exec(child.value);
|
|
72
118
|
|
|
73
119
|
while (match) {
|
|
74
|
-
// Nodes in this line.
|
|
120
|
+
// Nodes in this line. (current child is exclusive)
|
|
75
121
|
const line = tree.children.slice(start, index);
|
|
76
122
|
|
|
77
123
|
/* v8 ignore start */
|
|
@@ -86,29 +132,18 @@ const plugin: Plugin<[HighlightLinesOptions?], Root> = (options) => {
|
|
|
86
132
|
|
|
87
133
|
// Append text from this text.
|
|
88
134
|
if (match.index > textStart) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
value: child.value.slice(textStart, match.index),
|
|
92
|
-
});
|
|
135
|
+
const value = child.value.slice(textStart, match.index);
|
|
136
|
+
line.push({ type: "text", value });
|
|
93
137
|
}
|
|
94
138
|
|
|
95
|
-
// Add a line
|
|
139
|
+
// Add a line
|
|
96
140
|
lineNumber += 1;
|
|
97
|
-
replacement.push(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
showLineNumbers && "numbered-code-line",
|
|
104
|
-
linesToBeHighlighted.includes(lineNumber) && "highlighted-code-line",
|
|
105
|
-
]),
|
|
106
|
-
),
|
|
107
|
-
{
|
|
108
|
-
type: "text",
|
|
109
|
-
value: match[0],
|
|
110
|
-
},
|
|
111
|
-
);
|
|
141
|
+
replacement.push(createLine(line, lineNumber, showLineNumbers, linesToBeHighlighted));
|
|
142
|
+
|
|
143
|
+
// Add eol if the tag name is "span"
|
|
144
|
+
if (settings.lineContainerTagName === "span") {
|
|
145
|
+
replacement.push({ type: "text", value: match[0] });
|
|
146
|
+
}
|
|
112
147
|
|
|
113
148
|
start = index + 1;
|
|
114
149
|
textStart = match.index + match[0].length;
|
|
@@ -134,17 +169,7 @@ const plugin: Plugin<[HighlightLinesOptions?], Root> = (options) => {
|
|
|
134
169
|
|
|
135
170
|
if (line.length > 0) {
|
|
136
171
|
lineNumber += 1;
|
|
137
|
-
replacement.push(
|
|
138
|
-
createLine(
|
|
139
|
-
line,
|
|
140
|
-
showLineNumbers ? lineNumber : undefined,
|
|
141
|
-
clsx([
|
|
142
|
-
"code-line",
|
|
143
|
-
showLineNumbers && "numbered-code-line",
|
|
144
|
-
linesToBeHighlighted.includes(lineNumber) && "highlighted-code-line",
|
|
145
|
-
]),
|
|
146
|
-
),
|
|
147
|
-
);
|
|
172
|
+
replacement.push(createLine(line, lineNumber, showLineNumbers, linesToBeHighlighted));
|
|
148
173
|
}
|
|
149
174
|
|
|
150
175
|
/* v8 ignore end */
|
|
@@ -188,10 +213,10 @@ const plugin: Plugin<[HighlightLinesOptions?], Root> = (options) => {
|
|
|
188
213
|
if (language.startsWith("{") || language.startsWith("showlinenumbers")) {
|
|
189
214
|
meta = meta ? language + meta : language;
|
|
190
215
|
|
|
191
|
-
const
|
|
216
|
+
const idx = code.properties.className.findIndex(testingFunction);
|
|
192
217
|
|
|
193
|
-
if (
|
|
194
|
-
code.properties.className[
|
|
218
|
+
if (idx > -1) {
|
|
219
|
+
code.properties.className[idx] = "language-unknown";
|
|
195
220
|
}
|
|
196
221
|
}
|
|
197
222
|
}
|
|
@@ -208,8 +233,11 @@ const plugin: Plugin<[HighlightLinesOptions?], Root> = (options) => {
|
|
|
208
233
|
|
|
209
234
|
if (!showLineNumbers && linesToBeHighlighted.length === 0) return;
|
|
210
235
|
|
|
236
|
+
// flatten deeper nodes into first level <span>s an texts
|
|
237
|
+
code.children = flattenCodeTree(code.children);
|
|
238
|
+
|
|
211
239
|
// add wrapper for each line mutating the code element
|
|
212
|
-
|
|
240
|
+
gutter(code, showLineNumbers, linesToBeHighlighted);
|
|
213
241
|
});
|
|
214
242
|
};
|
|
215
243
|
};
|