shiki-transformer-fold 0.1.0 → 0.2.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 +21 -21
- package/README.md +74 -74
- package/dist/index.d.mts +13 -0
- package/dist/index.mjs +42 -9
- package/package.json +1 -1
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025-present - Julien Huang
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-present - Julien Huang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
# shiki-transformer-fold
|
|
2
|
-
|
|
3
|
-
<!-- automd:badges color=yellow -->
|
|
4
|
-
|
|
5
|
-
[](https://npmjs.com/package/shiki-transformer-fold)
|
|
6
|
-
[](https://npm.chart.dev/shiki-transformer-fold)
|
|
7
|
-
|
|
8
|
-
<!-- /automd -->
|
|
9
|
-
|
|
10
|
-
Code folding support for shiki.
|
|
11
|
-
|
|
12
|
-
## Usage
|
|
13
|
-
|
|
14
|
-
Install the package:
|
|
15
|
-
|
|
16
|
-
```sh
|
|
17
|
-
# ✨ Auto-detect (supports npm, yarn, pnpm, deno and bun)
|
|
18
|
-
npx nypm install shiki-transformer-fold
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Add the transformer
|
|
22
|
-
|
|
23
|
-
```ts
|
|
24
|
-
import { codeToHtml } from "shiki";
|
|
25
|
-
import {
|
|
26
|
-
attachFoldToggleListener,
|
|
27
|
-
transformerRenderHtmlFold,
|
|
28
|
-
} from "shiki-transformer-fold";
|
|
29
|
-
|
|
30
|
-
const html = await codeToHtml(code, {
|
|
31
|
-
lang: "html",
|
|
32
|
-
theme: "nord",
|
|
33
|
-
transformers: [transformerRenderHtmlFold()],
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
// attach listeners to allow opening and closing the rendered code
|
|
37
|
-
// need to be called only once
|
|
38
|
-
attachFoldToggleListener();
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Development
|
|
42
|
-
|
|
43
|
-
<details>
|
|
44
|
-
|
|
45
|
-
<summary>local development</summary>
|
|
46
|
-
|
|
47
|
-
- Clone this repository
|
|
48
|
-
- Install latest LTS version of [Node.js](https://nodejs.org/en/)
|
|
49
|
-
- Enable [Corepack](https://github.com/nodejs/corepack) using `corepack enable`
|
|
50
|
-
- Install dependencies using `pnpm install`
|
|
51
|
-
- Run interactive tests using `pnpm dev`
|
|
52
|
-
|
|
53
|
-
</details>
|
|
54
|
-
|
|
55
|
-
## License
|
|
56
|
-
|
|
57
|
-
<!-- automd:contributors license=MIT -->
|
|
58
|
-
|
|
59
|
-
Published under the [MIT](https://github.com/huang-julien/shiki-transformer-fold/blob/main/LICENSE) license.
|
|
60
|
-
Made by [community](https://github.com/huang-julien/shiki-transformer-fold/graphs/contributors) 💛
|
|
61
|
-
<br><br>
|
|
62
|
-
<a href="https://github.com/huang-julien/shiki-transformer-fold/graphs/contributors">
|
|
63
|
-
<img src="https://contrib.rocks/image?repo=huang-julien/shiki-transformer-fold" />
|
|
64
|
-
</a>
|
|
65
|
-
|
|
66
|
-
<!-- /automd -->
|
|
67
|
-
|
|
68
|
-
<!-- automd:with-automd -->
|
|
69
|
-
|
|
70
|
-
---
|
|
71
|
-
|
|
72
|
-
_🤖 auto updated with [automd](https://automd.unjs.io)_
|
|
73
|
-
|
|
74
|
-
<!-- /automd -->
|
|
1
|
+
# shiki-transformer-fold
|
|
2
|
+
|
|
3
|
+
<!-- automd:badges color=yellow -->
|
|
4
|
+
|
|
5
|
+
[](https://npmjs.com/package/shiki-transformer-fold)
|
|
6
|
+
[](https://npm.chart.dev/shiki-transformer-fold)
|
|
7
|
+
|
|
8
|
+
<!-- /automd -->
|
|
9
|
+
|
|
10
|
+
Code folding support for shiki.
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
Install the package:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
# ✨ Auto-detect (supports npm, yarn, pnpm, deno and bun)
|
|
18
|
+
npx nypm install shiki-transformer-fold
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Add the transformer
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { codeToHtml } from "shiki";
|
|
25
|
+
import {
|
|
26
|
+
attachFoldToggleListener,
|
|
27
|
+
transformerRenderHtmlFold,
|
|
28
|
+
} from "shiki-transformer-fold";
|
|
29
|
+
|
|
30
|
+
const html = await codeToHtml(code, {
|
|
31
|
+
lang: "html",
|
|
32
|
+
theme: "nord",
|
|
33
|
+
transformers: [transformerRenderHtmlFold()],
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// attach listeners to allow opening and closing the rendered code
|
|
37
|
+
// need to be called only once
|
|
38
|
+
attachFoldToggleListener();
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Development
|
|
42
|
+
|
|
43
|
+
<details>
|
|
44
|
+
|
|
45
|
+
<summary>local development</summary>
|
|
46
|
+
|
|
47
|
+
- Clone this repository
|
|
48
|
+
- Install latest LTS version of [Node.js](https://nodejs.org/en/)
|
|
49
|
+
- Enable [Corepack](https://github.com/nodejs/corepack) using `corepack enable`
|
|
50
|
+
- Install dependencies using `pnpm install`
|
|
51
|
+
- Run interactive tests using `pnpm dev`
|
|
52
|
+
|
|
53
|
+
</details>
|
|
54
|
+
|
|
55
|
+
## License
|
|
56
|
+
|
|
57
|
+
<!-- automd:contributors license=MIT -->
|
|
58
|
+
|
|
59
|
+
Published under the [MIT](https://github.com/huang-julien/shiki-transformer-fold/blob/main/LICENSE) license.
|
|
60
|
+
Made by [community](https://github.com/huang-julien/shiki-transformer-fold/graphs/contributors) 💛
|
|
61
|
+
<br><br>
|
|
62
|
+
<a href="https://github.com/huang-julien/shiki-transformer-fold/graphs/contributors">
|
|
63
|
+
<img src="https://contrib.rocks/image?repo=huang-julien/shiki-transformer-fold" />
|
|
64
|
+
</a>
|
|
65
|
+
|
|
66
|
+
<!-- /automd -->
|
|
67
|
+
|
|
68
|
+
<!-- automd:with-automd -->
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
_🤖 auto updated with [automd](https://automd.unjs.io)_
|
|
73
|
+
|
|
74
|
+
<!-- /automd -->
|
package/dist/index.d.mts
CHANGED
|
@@ -7,10 +7,17 @@ interface TransformerHtmlFoldOptions {
|
|
|
7
7
|
* @default 'shiki-fold'
|
|
8
8
|
*/
|
|
9
9
|
classPrefix?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Fold regions at this specific nesting level by default when rendering?
|
|
12
|
+
* Set to 1 to fold root-level tags, 2 to fold second-level tags
|
|
13
|
+
* @default undefined
|
|
14
|
+
*/
|
|
15
|
+
foldLevel?: number;
|
|
10
16
|
}
|
|
11
17
|
interface FoldRegion {
|
|
12
18
|
startLine: number;
|
|
13
19
|
endLine: number;
|
|
20
|
+
level: number;
|
|
14
21
|
}
|
|
15
22
|
declare module "shiki" {
|
|
16
23
|
interface ShikiTransformerContextMeta {
|
|
@@ -22,6 +29,12 @@ declare module "shiki" {
|
|
|
22
29
|
foldRegions: FoldRegion[];
|
|
23
30
|
/** Map of foldable regions detected in the code */
|
|
24
31
|
foldStartLines: Map<number, FoldRegion>;
|
|
32
|
+
/** Map of lines that are part of folded regions */
|
|
33
|
+
foldedRanges: Map<number, {
|
|
34
|
+
hiddenCount: number;
|
|
35
|
+
}>;
|
|
36
|
+
/** Fold level setting */
|
|
37
|
+
foldLevel: number;
|
|
25
38
|
__lineIdCounter: number;
|
|
26
39
|
};
|
|
27
40
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -30,16 +30,25 @@ const SELF_CLOSING_REGEX = /<([a-zA-Z][a-zA-Z0-9\-_:.]*)[^>]*\/>/g;
|
|
|
30
30
|
* Shiki transformer that adds code folding functionality for HTML-like tags.
|
|
31
31
|
*/
|
|
32
32
|
function transformerRenderHtmlFold(options = {}) {
|
|
33
|
-
const { classPrefix = "shiki-fold" } = options;
|
|
33
|
+
const { classPrefix = "shiki-fold", foldLevel = 0 } = options;
|
|
34
34
|
return {
|
|
35
35
|
name: "shiki-transformer-html-fold",
|
|
36
36
|
tokens(lines) {
|
|
37
37
|
const foldRegions = detectFoldRegions(lines);
|
|
38
38
|
const foldStartLines = new Map(foldRegions.map((r) => [r.startLine, r]));
|
|
39
|
+
const foldedRanges = new Map();
|
|
40
|
+
if (foldLevel > 0) {
|
|
41
|
+
for (const region of foldRegions) if (region.level === foldLevel) {
|
|
42
|
+
foldedRanges.set(region.startLine, { hiddenCount: region.endLine - region.startLine });
|
|
43
|
+
for (let i = region.startLine + 1; i <= region.endLine; i++) foldedRanges.set(i, { hiddenCount: -1 });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
39
46
|
this.meta.transformerHtmlFold = {
|
|
40
47
|
foldRegions,
|
|
41
48
|
foldStartLines,
|
|
42
|
-
|
|
49
|
+
foldedRanges,
|
|
50
|
+
__lineIdCounter: 0,
|
|
51
|
+
foldLevel
|
|
43
52
|
};
|
|
44
53
|
return lines;
|
|
45
54
|
},
|
|
@@ -58,12 +67,28 @@ function transformerRenderHtmlFold(options = {}) {
|
|
|
58
67
|
const { transformerHtmlFold } = this.meta;
|
|
59
68
|
const lineId = `${classPrefix}-${transformerHtmlFold.__lineIdCounter++}`;
|
|
60
69
|
element.properties["data-fold-line"] = lineId;
|
|
70
|
+
const foldedInfo = transformerHtmlFold.foldedRanges.get(lineNumber);
|
|
71
|
+
if (foldedInfo && foldedInfo.hiddenCount === -1) element.properties.class = `${element.properties.class || ""} ${classPrefix}-hidden`.trim();
|
|
61
72
|
const region = transformerHtmlFold.foldStartLines.get(lineNumber);
|
|
62
73
|
if (region) {
|
|
63
74
|
const endLineId = `${classPrefix}-${region.endLine - 1}`;
|
|
75
|
+
const shouldFold = transformerHtmlFold.foldLevel > 0 && region.level === transformerHtmlFold.foldLevel;
|
|
64
76
|
element.properties["data-fold-end-id"] = endLineId;
|
|
65
|
-
element.properties["data-
|
|
77
|
+
element.properties["data-fold-level"] = String(region.level);
|
|
78
|
+
element.properties["data-folded"] = shouldFold ? "true" : "false";
|
|
66
79
|
element.properties.class = `${element.properties.class || ""} ${classPrefix}-foldable`.trim();
|
|
80
|
+
if (shouldFold && foldedInfo && foldedInfo.hiddenCount > 0) {
|
|
81
|
+
const count = foldedInfo.hiddenCount;
|
|
82
|
+
element.children.push({
|
|
83
|
+
type: "element",
|
|
84
|
+
tagName: "span",
|
|
85
|
+
properties: { class: `${classPrefix}-summary` },
|
|
86
|
+
children: [{
|
|
87
|
+
type: "text",
|
|
88
|
+
value: `... ${count} line${count > 1 ? "s" : ""} hidden`
|
|
89
|
+
}]
|
|
90
|
+
});
|
|
91
|
+
}
|
|
67
92
|
}
|
|
68
93
|
return element;
|
|
69
94
|
}
|
|
@@ -83,10 +108,14 @@ function detectFoldRegions(lines) {
|
|
|
83
108
|
if (!tagName) continue;
|
|
84
109
|
if (selfClosingPositions.has(match.index)) continue;
|
|
85
110
|
const tagNameLower = tagName.toLowerCase();
|
|
86
|
-
if (!VOID_TAGS.has(tagNameLower))
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
111
|
+
if (!VOID_TAGS.has(tagNameLower)) {
|
|
112
|
+
const level = stack.length + 1;
|
|
113
|
+
stack.push({
|
|
114
|
+
tagName: tagNameLower,
|
|
115
|
+
line: lineNum,
|
|
116
|
+
level
|
|
117
|
+
});
|
|
118
|
+
}
|
|
90
119
|
}
|
|
91
120
|
for (const match of text.matchAll(CLOSE_TAG_REGEX)) {
|
|
92
121
|
const [_, tagName] = match;
|
|
@@ -94,9 +123,11 @@ function detectFoldRegions(lines) {
|
|
|
94
123
|
const tagNameLower = tagName.toLowerCase();
|
|
95
124
|
for (let i = stack.length - 1; i >= 0; i--) if (stack[i]?.tagName === tagNameLower) {
|
|
96
125
|
const startLine = stack[i].line;
|
|
126
|
+
const level = stack[i].level;
|
|
97
127
|
if (lineNum > startLine) regions.push({
|
|
98
128
|
startLine,
|
|
99
|
-
endLine: lineNum
|
|
129
|
+
endLine: lineNum,
|
|
130
|
+
level
|
|
100
131
|
});
|
|
101
132
|
stack.splice(i, 1);
|
|
102
133
|
break;
|
|
@@ -124,13 +155,15 @@ function getStyles(classPrefix) {
|
|
|
124
155
|
|
|
125
156
|
//#endregion
|
|
126
157
|
//#region src/listeners.ts
|
|
158
|
+
let toggleListenerAttached = false;
|
|
127
159
|
/**
|
|
128
160
|
* Attach a delegated click listener for fold toggles.
|
|
129
161
|
* Uses event delegation so it works with dynamically rendered content.
|
|
130
162
|
* Only needs to be called once.
|
|
131
163
|
*/
|
|
132
164
|
function attachFoldToggleListener(classPrefix = "shiki-fold") {
|
|
133
|
-
if (typeof document === "undefined") return;
|
|
165
|
+
if (typeof document === "undefined" || toggleListenerAttached) return;
|
|
166
|
+
toggleListenerAttached = true;
|
|
134
167
|
const hiddenClass = `${classPrefix}-hidden`;
|
|
135
168
|
const summaryClass = `${classPrefix}-summary`;
|
|
136
169
|
document.addEventListener("click", (event) => {
|