@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 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
@@ -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 };
@@ -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
+ }