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 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 wrapper to each line in a code block, allowing numbering of the code block and highlighting desired lines of code**.
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
- There is one **boolean** option.
138
+ All options are **optional** and have **default values**.
137
139
 
138
140
  ```typescript
139
141
  type HighlightLinesOptions = {
140
- showLineNumbers?: boolean; // the default is "false"
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
@@ -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, classNames) => {
48
+ const createLine = (children, lineNumber, showLineNumbers, linesToBeHighlighted) => {
23
49
  return {
24
50
  type: "element",
25
- tagName: "div",
51
+ tagName: settings.lineContainerTagName,
26
52
  children,
27
53
  properties: {
28
- className: classNames,
29
- ...(lineNumber && { "data-line-number": lineNumber }),
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 starryNightGutter(tree, showLineNumbers, linesToBeHighlighted) {
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
- line.push({
59
- type: "text",
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, and the eol.
91
+ // Add a line
64
92
  lineNumber += 1;
65
- replacement.push(createLine(line, showLineNumbers ? lineNumber : undefined, clsx([
66
- "code-line",
67
- showLineNumbers && "numbered-code-line",
68
- linesToBeHighlighted.includes(lineNumber) && "highlighted-code-line",
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, showLineNumbers ? lineNumber : undefined, clsx([
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 index = code.properties.className.findIndex(testingFunction);
131
- if (index > -1) {
132
- code.properties.className[index] = "language-unknown";
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
- starryNightGutter(code, showLineNumbers, linesToBeHighlighted);
170
+ gutter(code, showLineNumbers, linesToBeHighlighted);
148
171
  });
149
172
  };
150
173
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rehype-highlight-code-lines",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Rehype plugin to add line numbers to code blocks and allow highlighting of desired code lines",
5
5
  "type": "module",
6
6
  "exports": "./dist/esm/index.js",
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({}, DEFAULT_SETTINGS, options);
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 | undefined,
39
- classNames: string[],
82
+ lineNumber: number,
83
+ showLineNumbers: boolean,
84
+ linesToBeHighlighted: number[],
40
85
  ): Element => {
41
86
  return {
42
87
  type: "element",
43
- tagName: "div",
88
+ tagName: settings.lineContainerTagName,
44
89
  children,
45
90
  properties: {
46
- className: classNames,
47
- ...(lineNumber && { "data-line-number": lineNumber }),
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 starryNightGutter(
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
- line.push({
90
- type: "text",
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, and the eol.
139
+ // Add a line
96
140
  lineNumber += 1;
97
- replacement.push(
98
- createLine(
99
- line,
100
- showLineNumbers ? lineNumber : undefined,
101
- clsx([
102
- "code-line",
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 index = code.properties.className.findIndex(testingFunction);
216
+ const idx = code.properties.className.findIndex(testingFunction);
192
217
 
193
- if (index > -1) {
194
- code.properties.className[index] = "language-unknown";
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
- starryNightGutter(code, showLineNumbers, linesToBeHighlighted);
240
+ gutter(code, showLineNumbers, linesToBeHighlighted);
213
241
  });
214
242
  };
215
243
  };