@shikijs/transformers 1.0.0-beta.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.
- package/LICENSE +22 -0
- package/README.md +9 -0
- package/dist/index.d.mts +150 -0
- package/dist/index.d.ts +150 -0
- package/dist/index.mjs +394 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Pine Wu
|
|
4
|
+
Copyright (c) 2023 Anthony Fu <https://github.com/antfu>
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# @shikijs/transformers
|
|
2
|
+
|
|
3
|
+
Collective of common transformers for [shiki](https://github.com/shikijs/shiki), inspired by [shiki-processor](https://github.com/innocenzi/shiki-processor).
|
|
4
|
+
|
|
5
|
+
[Documentation](https://shiki.style/packages/transformers)
|
|
6
|
+
|
|
7
|
+
## License
|
|
8
|
+
|
|
9
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { ShikiTransformer, ShikiTransformerContext } from 'shiki';
|
|
2
|
+
import { Element } from 'hast';
|
|
3
|
+
|
|
4
|
+
interface TransformerRenderWhitespaceOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Class for tab
|
|
7
|
+
*
|
|
8
|
+
* @default 'tab'
|
|
9
|
+
*/
|
|
10
|
+
classTab?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Class for space
|
|
13
|
+
*
|
|
14
|
+
* @default 'space'
|
|
15
|
+
*/
|
|
16
|
+
classSpace?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Position of rendered whitespace
|
|
19
|
+
* @default all position
|
|
20
|
+
*/
|
|
21
|
+
position?: 'all' | 'boundary' | 'trailing';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Render whitespaces as separate tokens.
|
|
25
|
+
* Apply with CSS, it can be used to render tabs and spaces visually.
|
|
26
|
+
*/
|
|
27
|
+
declare function transformerRenderWhitespace(options?: TransformerRenderWhitespaceOptions): ShikiTransformer;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Remove line breaks between lines.
|
|
31
|
+
* Useful when you override `display: block` to `.line` in CSS.
|
|
32
|
+
*/
|
|
33
|
+
declare function transformerRemoveLineBreak(): ShikiTransformer;
|
|
34
|
+
|
|
35
|
+
interface TransformerCompactLineOption {
|
|
36
|
+
/**
|
|
37
|
+
* 1-based line number.
|
|
38
|
+
*/
|
|
39
|
+
line: number;
|
|
40
|
+
classes?: string[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Transformer for `shiki`'s legacy `lineOptions`
|
|
44
|
+
*/
|
|
45
|
+
declare function transformerCompactLineOptions(lineOptions?: TransformerCompactLineOption[]): ShikiTransformer;
|
|
46
|
+
|
|
47
|
+
interface TransformerNotationFocusOptions {
|
|
48
|
+
/**
|
|
49
|
+
* Class for focused lines
|
|
50
|
+
*/
|
|
51
|
+
classActiveLine?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Class added to the root element when the code has focused lines
|
|
54
|
+
*/
|
|
55
|
+
classActivePre?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Allow using `[!code focus]` notation in code to mark focused lines.
|
|
59
|
+
*/
|
|
60
|
+
declare function transformerNotationFocus(options?: TransformerNotationFocusOptions): ShikiTransformer;
|
|
61
|
+
|
|
62
|
+
interface TransformerNotationHighlightOptions {
|
|
63
|
+
/**
|
|
64
|
+
* Class for highlighted lines
|
|
65
|
+
*/
|
|
66
|
+
classActiveLine?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Class added to the root element when the code has highlighted lines
|
|
69
|
+
*/
|
|
70
|
+
classActivePre?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Allow using `[!code highlight]` notation in code to mark highlighted lines.
|
|
74
|
+
*/
|
|
75
|
+
declare function transformerNotationHighlight(options?: TransformerNotationHighlightOptions): ShikiTransformer;
|
|
76
|
+
|
|
77
|
+
interface TransformerNotationWordHighlightOptions {
|
|
78
|
+
/**
|
|
79
|
+
* Class for highlighted words
|
|
80
|
+
*/
|
|
81
|
+
classActiveWord?: string;
|
|
82
|
+
/**
|
|
83
|
+
* Class added to the root element when the code has highlighted words
|
|
84
|
+
*/
|
|
85
|
+
classActivePre?: string;
|
|
86
|
+
}
|
|
87
|
+
declare function transformerNotationWordHighlight(options?: TransformerNotationWordHighlightOptions): ShikiTransformer;
|
|
88
|
+
|
|
89
|
+
declare function parseMetaHighlightWords(meta: string): string[];
|
|
90
|
+
interface TransformerMetaWordHighlightOptions {
|
|
91
|
+
/**
|
|
92
|
+
* Class for highlighted words
|
|
93
|
+
*
|
|
94
|
+
* @default 'highlighted-word'
|
|
95
|
+
*/
|
|
96
|
+
className?: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Allow using `/word/` in the code snippet meta to mark highlighted words.
|
|
100
|
+
*/
|
|
101
|
+
declare function transformerMetaWordHighlight(options?: TransformerMetaWordHighlightOptions): ShikiTransformer;
|
|
102
|
+
|
|
103
|
+
interface TransformerNotationDiffOptions {
|
|
104
|
+
/**
|
|
105
|
+
* Class for added lines
|
|
106
|
+
*/
|
|
107
|
+
classLineAdd?: string;
|
|
108
|
+
/**
|
|
109
|
+
* Class for removed lines
|
|
110
|
+
*/
|
|
111
|
+
classLineRemove?: string;
|
|
112
|
+
/**
|
|
113
|
+
* Class added to the <pre> element when the current code has diff
|
|
114
|
+
*/
|
|
115
|
+
classActivePre?: string;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Use `[!code ++]` and `[!code --]` to mark added and removed lines.
|
|
119
|
+
*/
|
|
120
|
+
declare function transformerNotationDiff(options?: TransformerNotationDiffOptions): ShikiTransformer;
|
|
121
|
+
|
|
122
|
+
interface TransformerNotationErrorLevelOptions {
|
|
123
|
+
classMap?: Record<string, string | string[]>;
|
|
124
|
+
/**
|
|
125
|
+
* Class added to the <pre> element when the current code has diff
|
|
126
|
+
*/
|
|
127
|
+
classActivePre?: string;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Allow using `[!code error]` `[!code warning]` notation in code to mark highlighted lines.
|
|
131
|
+
*/
|
|
132
|
+
declare function transformerNotationErrorLevel(options?: TransformerNotationErrorLevelOptions): ShikiTransformer;
|
|
133
|
+
|
|
134
|
+
declare function parseMetaHighlightString(meta: string): number[] | null;
|
|
135
|
+
interface TransformerMetaHighlightOptions {
|
|
136
|
+
/**
|
|
137
|
+
* Class for highlighted lines
|
|
138
|
+
*
|
|
139
|
+
* @default 'highlighted'
|
|
140
|
+
*/
|
|
141
|
+
className?: string;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Allow using `{1,3-5}` in the code snippet meta to mark highlighted lines.
|
|
145
|
+
*/
|
|
146
|
+
declare function transformerMetaHighlight(options?: TransformerMetaHighlightOptions): ShikiTransformer;
|
|
147
|
+
|
|
148
|
+
declare function createCommentNotationTransformer(name: string, regex: RegExp, onMatch: (this: ShikiTransformerContext, match: string[], line: Element, commentNode: Element, lines: Element[], index: number) => boolean, removeEmptyLines?: boolean): ShikiTransformer;
|
|
149
|
+
|
|
150
|
+
export { type TransformerCompactLineOption, type TransformerMetaHighlightOptions, type TransformerMetaWordHighlightOptions, type TransformerNotationDiffOptions, type TransformerNotationErrorLevelOptions, type TransformerNotationFocusOptions, type TransformerNotationHighlightOptions, type TransformerNotationWordHighlightOptions, type TransformerRenderWhitespaceOptions, createCommentNotationTransformer, parseMetaHighlightString, parseMetaHighlightWords, transformerCompactLineOptions, transformerMetaHighlight, transformerMetaWordHighlight, transformerNotationDiff, transformerNotationErrorLevel, transformerNotationFocus, transformerNotationHighlight, transformerNotationWordHighlight, transformerRemoveLineBreak, transformerRenderWhitespace };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { ShikiTransformer, ShikiTransformerContext } from 'shiki';
|
|
2
|
+
import { Element } from 'hast';
|
|
3
|
+
|
|
4
|
+
interface TransformerRenderWhitespaceOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Class for tab
|
|
7
|
+
*
|
|
8
|
+
* @default 'tab'
|
|
9
|
+
*/
|
|
10
|
+
classTab?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Class for space
|
|
13
|
+
*
|
|
14
|
+
* @default 'space'
|
|
15
|
+
*/
|
|
16
|
+
classSpace?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Position of rendered whitespace
|
|
19
|
+
* @default all position
|
|
20
|
+
*/
|
|
21
|
+
position?: 'all' | 'boundary' | 'trailing';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Render whitespaces as separate tokens.
|
|
25
|
+
* Apply with CSS, it can be used to render tabs and spaces visually.
|
|
26
|
+
*/
|
|
27
|
+
declare function transformerRenderWhitespace(options?: TransformerRenderWhitespaceOptions): ShikiTransformer;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Remove line breaks between lines.
|
|
31
|
+
* Useful when you override `display: block` to `.line` in CSS.
|
|
32
|
+
*/
|
|
33
|
+
declare function transformerRemoveLineBreak(): ShikiTransformer;
|
|
34
|
+
|
|
35
|
+
interface TransformerCompactLineOption {
|
|
36
|
+
/**
|
|
37
|
+
* 1-based line number.
|
|
38
|
+
*/
|
|
39
|
+
line: number;
|
|
40
|
+
classes?: string[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Transformer for `shiki`'s legacy `lineOptions`
|
|
44
|
+
*/
|
|
45
|
+
declare function transformerCompactLineOptions(lineOptions?: TransformerCompactLineOption[]): ShikiTransformer;
|
|
46
|
+
|
|
47
|
+
interface TransformerNotationFocusOptions {
|
|
48
|
+
/**
|
|
49
|
+
* Class for focused lines
|
|
50
|
+
*/
|
|
51
|
+
classActiveLine?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Class added to the root element when the code has focused lines
|
|
54
|
+
*/
|
|
55
|
+
classActivePre?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Allow using `[!code focus]` notation in code to mark focused lines.
|
|
59
|
+
*/
|
|
60
|
+
declare function transformerNotationFocus(options?: TransformerNotationFocusOptions): ShikiTransformer;
|
|
61
|
+
|
|
62
|
+
interface TransformerNotationHighlightOptions {
|
|
63
|
+
/**
|
|
64
|
+
* Class for highlighted lines
|
|
65
|
+
*/
|
|
66
|
+
classActiveLine?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Class added to the root element when the code has highlighted lines
|
|
69
|
+
*/
|
|
70
|
+
classActivePre?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Allow using `[!code highlight]` notation in code to mark highlighted lines.
|
|
74
|
+
*/
|
|
75
|
+
declare function transformerNotationHighlight(options?: TransformerNotationHighlightOptions): ShikiTransformer;
|
|
76
|
+
|
|
77
|
+
interface TransformerNotationWordHighlightOptions {
|
|
78
|
+
/**
|
|
79
|
+
* Class for highlighted words
|
|
80
|
+
*/
|
|
81
|
+
classActiveWord?: string;
|
|
82
|
+
/**
|
|
83
|
+
* Class added to the root element when the code has highlighted words
|
|
84
|
+
*/
|
|
85
|
+
classActivePre?: string;
|
|
86
|
+
}
|
|
87
|
+
declare function transformerNotationWordHighlight(options?: TransformerNotationWordHighlightOptions): ShikiTransformer;
|
|
88
|
+
|
|
89
|
+
declare function parseMetaHighlightWords(meta: string): string[];
|
|
90
|
+
interface TransformerMetaWordHighlightOptions {
|
|
91
|
+
/**
|
|
92
|
+
* Class for highlighted words
|
|
93
|
+
*
|
|
94
|
+
* @default 'highlighted-word'
|
|
95
|
+
*/
|
|
96
|
+
className?: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Allow using `/word/` in the code snippet meta to mark highlighted words.
|
|
100
|
+
*/
|
|
101
|
+
declare function transformerMetaWordHighlight(options?: TransformerMetaWordHighlightOptions): ShikiTransformer;
|
|
102
|
+
|
|
103
|
+
interface TransformerNotationDiffOptions {
|
|
104
|
+
/**
|
|
105
|
+
* Class for added lines
|
|
106
|
+
*/
|
|
107
|
+
classLineAdd?: string;
|
|
108
|
+
/**
|
|
109
|
+
* Class for removed lines
|
|
110
|
+
*/
|
|
111
|
+
classLineRemove?: string;
|
|
112
|
+
/**
|
|
113
|
+
* Class added to the <pre> element when the current code has diff
|
|
114
|
+
*/
|
|
115
|
+
classActivePre?: string;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Use `[!code ++]` and `[!code --]` to mark added and removed lines.
|
|
119
|
+
*/
|
|
120
|
+
declare function transformerNotationDiff(options?: TransformerNotationDiffOptions): ShikiTransformer;
|
|
121
|
+
|
|
122
|
+
interface TransformerNotationErrorLevelOptions {
|
|
123
|
+
classMap?: Record<string, string | string[]>;
|
|
124
|
+
/**
|
|
125
|
+
* Class added to the <pre> element when the current code has diff
|
|
126
|
+
*/
|
|
127
|
+
classActivePre?: string;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Allow using `[!code error]` `[!code warning]` notation in code to mark highlighted lines.
|
|
131
|
+
*/
|
|
132
|
+
declare function transformerNotationErrorLevel(options?: TransformerNotationErrorLevelOptions): ShikiTransformer;
|
|
133
|
+
|
|
134
|
+
declare function parseMetaHighlightString(meta: string): number[] | null;
|
|
135
|
+
interface TransformerMetaHighlightOptions {
|
|
136
|
+
/**
|
|
137
|
+
* Class for highlighted lines
|
|
138
|
+
*
|
|
139
|
+
* @default 'highlighted'
|
|
140
|
+
*/
|
|
141
|
+
className?: string;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Allow using `{1,3-5}` in the code snippet meta to mark highlighted lines.
|
|
145
|
+
*/
|
|
146
|
+
declare function transformerMetaHighlight(options?: TransformerMetaHighlightOptions): ShikiTransformer;
|
|
147
|
+
|
|
148
|
+
declare function createCommentNotationTransformer(name: string, regex: RegExp, onMatch: (this: ShikiTransformerContext, match: string[], line: Element, commentNode: Element, lines: Element[], index: number) => boolean, removeEmptyLines?: boolean): ShikiTransformer;
|
|
149
|
+
|
|
150
|
+
export { type TransformerCompactLineOption, type TransformerMetaHighlightOptions, type TransformerMetaWordHighlightOptions, type TransformerNotationDiffOptions, type TransformerNotationErrorLevelOptions, type TransformerNotationFocusOptions, type TransformerNotationHighlightOptions, type TransformerNotationWordHighlightOptions, type TransformerRenderWhitespaceOptions, createCommentNotationTransformer, parseMetaHighlightString, parseMetaHighlightWords, transformerCompactLineOptions, transformerMetaHighlight, transformerMetaWordHighlight, transformerNotationDiff, transformerNotationErrorLevel, transformerNotationFocus, transformerNotationHighlight, transformerNotationWordHighlight, transformerRemoveLineBreak, transformerRenderWhitespace };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import { addClassToHast } from 'shiki';
|
|
2
|
+
import { addClassToHast as addClassToHast$1 } from 'shiki/core';
|
|
3
|
+
|
|
4
|
+
function separateContinuousSpaces(inputs) {
|
|
5
|
+
const result = [];
|
|
6
|
+
let current = "";
|
|
7
|
+
function bump() {
|
|
8
|
+
if (current.length)
|
|
9
|
+
result.push(current);
|
|
10
|
+
current = "";
|
|
11
|
+
}
|
|
12
|
+
inputs.forEach((part, idx) => {
|
|
13
|
+
if (isTab(part)) {
|
|
14
|
+
bump();
|
|
15
|
+
result.push(part);
|
|
16
|
+
} else if (isSpace(part) && (isSpace(inputs[idx - 1]) || isSpace(inputs[idx + 1]))) {
|
|
17
|
+
bump();
|
|
18
|
+
result.push(part);
|
|
19
|
+
} else {
|
|
20
|
+
current += part;
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
bump();
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
function isTab(part) {
|
|
27
|
+
return part === " ";
|
|
28
|
+
}
|
|
29
|
+
function isSpace(part) {
|
|
30
|
+
return part === " " || part === " ";
|
|
31
|
+
}
|
|
32
|
+
function splitSpaces(parts, type, renderContinuousSpaces = true) {
|
|
33
|
+
if (type === "all")
|
|
34
|
+
return parts;
|
|
35
|
+
let leftCount = 0;
|
|
36
|
+
let rightCount = 0;
|
|
37
|
+
if (type === "boundary") {
|
|
38
|
+
for (let i = 0; i < parts.length; i++) {
|
|
39
|
+
if (isSpace(parts[i]))
|
|
40
|
+
leftCount++;
|
|
41
|
+
else
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (type === "boundary" || type === "trailing") {
|
|
46
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
47
|
+
if (isSpace(parts[i]))
|
|
48
|
+
rightCount++;
|
|
49
|
+
else
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const middle = parts.slice(leftCount, parts.length - rightCount);
|
|
54
|
+
return [
|
|
55
|
+
...parts.slice(0, leftCount),
|
|
56
|
+
...renderContinuousSpaces ? separateContinuousSpaces(middle) : [middle.join("")],
|
|
57
|
+
...parts.slice(parts.length - rightCount)
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function transformerRenderWhitespace(options = {}) {
|
|
62
|
+
const classMap = {
|
|
63
|
+
" ": options.classSpace ?? "space",
|
|
64
|
+
" ": options.classTab ?? "tab"
|
|
65
|
+
};
|
|
66
|
+
const position = options.position ?? "all";
|
|
67
|
+
const keys = Object.keys(classMap);
|
|
68
|
+
return {
|
|
69
|
+
name: "@shikijs/transformers:render-whitespace",
|
|
70
|
+
// We use `root` hook here to ensure it runs after all other transformers
|
|
71
|
+
root(root) {
|
|
72
|
+
const pre = root.children[0];
|
|
73
|
+
const code = pre.children[0];
|
|
74
|
+
code.children.forEach(
|
|
75
|
+
(line) => {
|
|
76
|
+
if (line.type !== "element")
|
|
77
|
+
return;
|
|
78
|
+
const elements = line.children.filter((token) => token.type === "element");
|
|
79
|
+
const last = elements.length - 1;
|
|
80
|
+
line.children = line.children.flatMap((token) => {
|
|
81
|
+
if (token.type !== "element")
|
|
82
|
+
return token;
|
|
83
|
+
const index = elements.indexOf(token);
|
|
84
|
+
if (position === "boundary" && index !== 0 && index !== last)
|
|
85
|
+
return token;
|
|
86
|
+
if (position === "trailing" && index !== last)
|
|
87
|
+
return token;
|
|
88
|
+
const node = token.children[0];
|
|
89
|
+
if (node.type !== "text" || !node.value)
|
|
90
|
+
return token;
|
|
91
|
+
const parts = splitSpaces(
|
|
92
|
+
node.value.split(/([ \t])/).filter((i) => i.length),
|
|
93
|
+
position === "boundary" && index === last && last !== 0 ? "trailing" : position,
|
|
94
|
+
position !== "trailing"
|
|
95
|
+
);
|
|
96
|
+
if (parts.length <= 1)
|
|
97
|
+
return token;
|
|
98
|
+
return parts.map((part) => {
|
|
99
|
+
const clone = {
|
|
100
|
+
...token,
|
|
101
|
+
properties: { ...token.properties }
|
|
102
|
+
};
|
|
103
|
+
clone.children = [{ type: "text", value: part }];
|
|
104
|
+
if (keys.includes(part)) {
|
|
105
|
+
addClassToHast(clone, classMap[part]);
|
|
106
|
+
delete clone.properties.style;
|
|
107
|
+
}
|
|
108
|
+
return clone;
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function transformerRemoveLineBreak() {
|
|
118
|
+
return {
|
|
119
|
+
name: "@shikijs/transformers:remove-line-break",
|
|
120
|
+
code(code) {
|
|
121
|
+
code.children = code.children.filter((line) => !(line.type === "text" && line.value === "\n"));
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function transformerCompactLineOptions(lineOptions = []) {
|
|
127
|
+
return {
|
|
128
|
+
name: "@shikijs/transformers:compact-line-options",
|
|
129
|
+
line(node, line) {
|
|
130
|
+
const lineOption = lineOptions.find((o) => o.line === line);
|
|
131
|
+
if (lineOption?.classes)
|
|
132
|
+
addClassToHast(node, lineOption.classes);
|
|
133
|
+
return node;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function createCommentNotationTransformer(name, regex, onMatch, removeEmptyLines = false) {
|
|
139
|
+
return {
|
|
140
|
+
name,
|
|
141
|
+
code(code) {
|
|
142
|
+
const lines = code.children.filter((i) => i.type === "element");
|
|
143
|
+
const linesToRemove = [];
|
|
144
|
+
lines.forEach((line, idx) => {
|
|
145
|
+
let nodeToRemove;
|
|
146
|
+
for (const child of line.children) {
|
|
147
|
+
if (child.type !== "element")
|
|
148
|
+
continue;
|
|
149
|
+
const text = child.children[0];
|
|
150
|
+
if (text.type !== "text")
|
|
151
|
+
continue;
|
|
152
|
+
let replaced = false;
|
|
153
|
+
text.value = text.value.replace(regex, (...match) => {
|
|
154
|
+
if (onMatch.call(this, match, line, child, lines, idx)) {
|
|
155
|
+
replaced = true;
|
|
156
|
+
return "";
|
|
157
|
+
}
|
|
158
|
+
return match[0];
|
|
159
|
+
});
|
|
160
|
+
if (replaced && !text.value.trim())
|
|
161
|
+
nodeToRemove = child;
|
|
162
|
+
}
|
|
163
|
+
if (nodeToRemove) {
|
|
164
|
+
line.children.splice(line.children.indexOf(nodeToRemove), 1);
|
|
165
|
+
if (line.children.length === 0) {
|
|
166
|
+
linesToRemove.push(line);
|
|
167
|
+
if (removeEmptyLines) {
|
|
168
|
+
const next = code.children[code.children.indexOf(line) + 1];
|
|
169
|
+
if (next && next.type === "text" && next.value === "\n")
|
|
170
|
+
linesToRemove.push(next);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
for (const line of linesToRemove)
|
|
176
|
+
code.children.splice(code.children.indexOf(line), 1);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function escapeRegExp(str) {
|
|
182
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
183
|
+
}
|
|
184
|
+
function transformerNotationMap(options = {}, name = "@shikijs/transformers:notation-map") {
|
|
185
|
+
const {
|
|
186
|
+
classMap = {},
|
|
187
|
+
classActivePre = void 0
|
|
188
|
+
} = options;
|
|
189
|
+
return createCommentNotationTransformer(
|
|
190
|
+
name,
|
|
191
|
+
new RegExp(`\\s*(?://|/\\*|<!--|#)\\s+\\[!code (${Object.keys(classMap).map(escapeRegExp).join("|")})(:\\d+)?\\]\\s*(?:\\*/|-->)?`),
|
|
192
|
+
function([_, match, range = ":1"], _line, _comment, lines, index) {
|
|
193
|
+
const lineNum = Number.parseInt(range.slice(1), 10);
|
|
194
|
+
lines.slice(index, index + lineNum).forEach((line) => {
|
|
195
|
+
addClassToHast(line, classMap[match]);
|
|
196
|
+
});
|
|
197
|
+
if (classActivePre)
|
|
198
|
+
addClassToHast(this.pre, classActivePre);
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function transformerNotationFocus(options = {}) {
|
|
205
|
+
const {
|
|
206
|
+
classActiveLine = "focused",
|
|
207
|
+
classActivePre = "has-focused"
|
|
208
|
+
} = options;
|
|
209
|
+
return transformerNotationMap(
|
|
210
|
+
{
|
|
211
|
+
classMap: {
|
|
212
|
+
focus: classActiveLine
|
|
213
|
+
},
|
|
214
|
+
classActivePre
|
|
215
|
+
},
|
|
216
|
+
"@shikijs/transformers:notation-focus"
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function transformerNotationHighlight(options = {}) {
|
|
221
|
+
const {
|
|
222
|
+
classActiveLine = "highlighted",
|
|
223
|
+
classActivePre = "has-highlighted"
|
|
224
|
+
} = options;
|
|
225
|
+
return transformerNotationMap(
|
|
226
|
+
{
|
|
227
|
+
classMap: {
|
|
228
|
+
highlight: classActiveLine,
|
|
229
|
+
hl: classActiveLine
|
|
230
|
+
},
|
|
231
|
+
classActivePre
|
|
232
|
+
},
|
|
233
|
+
"@shikijs/transformers:notation-highlight"
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function highlightWordInLine(line, ignoredElement, word, className) {
|
|
238
|
+
line.children = line.children.flatMap((span) => {
|
|
239
|
+
if (span.type !== "element" || span.tagName !== "span" || span === ignoredElement)
|
|
240
|
+
return span;
|
|
241
|
+
const textNode = span.children[0];
|
|
242
|
+
if (textNode.type !== "text")
|
|
243
|
+
return span;
|
|
244
|
+
return replaceSpan(span, textNode.value, word, className) ?? span;
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
function inheritElement(original, overrides) {
|
|
248
|
+
return {
|
|
249
|
+
...original,
|
|
250
|
+
properties: {
|
|
251
|
+
...original.properties
|
|
252
|
+
},
|
|
253
|
+
...overrides
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
function replaceSpan(span, text, word, className) {
|
|
257
|
+
const index = text.indexOf(word);
|
|
258
|
+
if (index === -1)
|
|
259
|
+
return;
|
|
260
|
+
const createNode = (value) => inheritElement(span, {
|
|
261
|
+
children: [
|
|
262
|
+
{
|
|
263
|
+
type: "text",
|
|
264
|
+
value
|
|
265
|
+
}
|
|
266
|
+
]
|
|
267
|
+
});
|
|
268
|
+
const nodes = [];
|
|
269
|
+
if (index > 0)
|
|
270
|
+
nodes.push(createNode(text.slice(0, index)));
|
|
271
|
+
const highlightedNode = createNode(word);
|
|
272
|
+
addClassToHast$1(highlightedNode, className);
|
|
273
|
+
nodes.push(highlightedNode);
|
|
274
|
+
if (index + word.length < text.length)
|
|
275
|
+
nodes.push(createNode(text.slice(index + word.length)));
|
|
276
|
+
return nodes;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function transformerNotationWordHighlight(options = {}) {
|
|
280
|
+
const {
|
|
281
|
+
classActiveWord = "highlighted-word",
|
|
282
|
+
classActivePre = void 0
|
|
283
|
+
} = options;
|
|
284
|
+
return createCommentNotationTransformer(
|
|
285
|
+
"@shikijs/transformers:notation-highlight-word",
|
|
286
|
+
// comment-start | marker | word | range | comment-end
|
|
287
|
+
/^\s*(?:\/\/|\/\*|<!--|#)\s+\[!code word:((?:\\.|[^:\]])+)(:\d+)?\]\s*(?:\*\/|-->)?/,
|
|
288
|
+
function([_, word, range], _line, comment, lines, index) {
|
|
289
|
+
const lineNum = range ? Number.parseInt(range.slice(1), 10) : lines.length;
|
|
290
|
+
word = word.replace(/\\(.)/g, "$1");
|
|
291
|
+
lines.slice(index + 1, index + 1 + lineNum).forEach((line) => highlightWordInLine(line, comment, word, classActiveWord));
|
|
292
|
+
if (classActivePre)
|
|
293
|
+
addClassToHast(this.pre, classActivePre);
|
|
294
|
+
return true;
|
|
295
|
+
},
|
|
296
|
+
true
|
|
297
|
+
// remove empty lines
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function parseMetaHighlightWords(meta) {
|
|
302
|
+
if (!meta)
|
|
303
|
+
return [];
|
|
304
|
+
const match = Array.from(meta.matchAll(/\/((?:\\.|[^\/])+?)\//ig));
|
|
305
|
+
return match.map((v) => v[1].replace(/\\(.)/g, "$1"));
|
|
306
|
+
}
|
|
307
|
+
function transformerMetaWordHighlight(options = {}) {
|
|
308
|
+
const {
|
|
309
|
+
className = "highlighted-word"
|
|
310
|
+
} = options;
|
|
311
|
+
return {
|
|
312
|
+
name: "@shikijs/transformers:meta-word-highlight",
|
|
313
|
+
line(node) {
|
|
314
|
+
if (!this.options.meta?.__raw)
|
|
315
|
+
return;
|
|
316
|
+
const words = parseMetaHighlightWords(this.options.meta.__raw);
|
|
317
|
+
for (const word of words)
|
|
318
|
+
highlightWordInLine(node, null, word, className);
|
|
319
|
+
return node;
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function transformerNotationDiff(options = {}) {
|
|
325
|
+
const {
|
|
326
|
+
classLineAdd = "diff add",
|
|
327
|
+
classLineRemove = "diff remove",
|
|
328
|
+
classActivePre = "has-diff"
|
|
329
|
+
} = options;
|
|
330
|
+
return transformerNotationMap(
|
|
331
|
+
{
|
|
332
|
+
classMap: {
|
|
333
|
+
"++": classLineAdd,
|
|
334
|
+
"--": classLineRemove
|
|
335
|
+
},
|
|
336
|
+
classActivePre
|
|
337
|
+
},
|
|
338
|
+
"@shikijs/transformers:notation-diff"
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function transformerNotationErrorLevel(options = {}) {
|
|
343
|
+
const {
|
|
344
|
+
classMap = {
|
|
345
|
+
error: ["highlighted", "error"],
|
|
346
|
+
warning: ["highlighted", "warning"]
|
|
347
|
+
},
|
|
348
|
+
classActivePre = "has-highlighted"
|
|
349
|
+
} = options;
|
|
350
|
+
return transformerNotationMap(
|
|
351
|
+
{
|
|
352
|
+
classMap,
|
|
353
|
+
classActivePre
|
|
354
|
+
},
|
|
355
|
+
"@shikijs/transformers:notation-error-level"
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function parseMetaHighlightString(meta) {
|
|
360
|
+
if (!meta)
|
|
361
|
+
return null;
|
|
362
|
+
const match = meta.match(/{([\d,-]+)}/);
|
|
363
|
+
if (!match)
|
|
364
|
+
return null;
|
|
365
|
+
const lines = match[1].split(",").flatMap((v) => {
|
|
366
|
+
const num = v.split("-").map((v2) => Number.parseInt(v2, 10));
|
|
367
|
+
if (num.length === 1)
|
|
368
|
+
return [num[0]];
|
|
369
|
+
else
|
|
370
|
+
return Array.from({ length: num[1] - num[0] + 1 }, (_, i) => i + num[0]);
|
|
371
|
+
});
|
|
372
|
+
return lines;
|
|
373
|
+
}
|
|
374
|
+
const symbol = Symbol("highlighted-lines");
|
|
375
|
+
function transformerMetaHighlight(options = {}) {
|
|
376
|
+
const {
|
|
377
|
+
className = "highlighted"
|
|
378
|
+
} = options;
|
|
379
|
+
return {
|
|
380
|
+
name: "@shikijs/transformers:meta-highlight",
|
|
381
|
+
line(node, line) {
|
|
382
|
+
var _a;
|
|
383
|
+
if (!this.options.meta?.__raw)
|
|
384
|
+
return;
|
|
385
|
+
(_a = this.meta)[symbol] || (_a[symbol] = parseMetaHighlightString(this.options.meta.__raw));
|
|
386
|
+
const lines = this.meta[symbol] || [];
|
|
387
|
+
if (lines.includes(line))
|
|
388
|
+
addClassToHast(node, className);
|
|
389
|
+
return node;
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export { createCommentNotationTransformer, parseMetaHighlightString, parseMetaHighlightWords, transformerCompactLineOptions, transformerMetaHighlight, transformerMetaWordHighlight, transformerNotationDiff, transformerNotationErrorLevel, transformerNotationFocus, transformerNotationHighlight, transformerNotationWordHighlight, transformerRemoveLineBreak, transformerRenderWhitespace };
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shikijs/transformers",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.0.0-beta.0",
|
|
5
|
+
"description": "Collective of common transformers transformers for Shiki",
|
|
6
|
+
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://github.com/shikijs/shiki#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/shikijs/shiki.git",
|
|
12
|
+
"directory": "packages/transformers"
|
|
13
|
+
},
|
|
14
|
+
"bugs": "https://github.com/shikijs/shiki/issues",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"shiki",
|
|
17
|
+
"@shikijs/transformers"
|
|
18
|
+
],
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.mts",
|
|
23
|
+
"default": "./dist/index.mjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"main": "./dist/index.mjs",
|
|
27
|
+
"module": "./dist/index.mjs",
|
|
28
|
+
"types": "./dist/index.d.mts",
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"shiki": "1.0.0-beta.0"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "unbuild",
|
|
37
|
+
"dev": "unbuild --stub"
|
|
38
|
+
}
|
|
39
|
+
}
|