rehype-pre-language 1.0.1
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 -0
- package/README.md +178 -0
- package/dist/esm/index.d.ts +10 -0
- package/dist/esm/index.js +58 -0
- package/dist/esm/index.js.map +1 -0
- package/package.json +78 -0
- package/src/index.ts +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 ipikuka
|
|
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
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# rehype-pre-language
|
|
2
|
+
|
|
3
|
+
[![NPM version][npm-image]][npm-url]
|
|
4
|
+
[![Build][github-build]][github-build-url]
|
|
5
|
+
![npm-typescript]
|
|
6
|
+
[![License][github-license]][github-license-url]
|
|
7
|
+
|
|
8
|
+
This package is a [unified][unified] ([rehype][rehype]) plugin **to add language information as a property to pre element** (compatible with new parser "[micromark][micromark]").
|
|
9
|
+
|
|
10
|
+
"**unified**" is a project that transforms content with abstract syntax trees (ASTs). "**rehype**" is a tool that transforms HTML with plugins. "**hast**" stands for HTML Abstract Syntax Tree (HAST) that rehype uses.
|
|
11
|
+
|
|
12
|
+
**This plugin is a rehype plugin that adds language information into "className" of the `<pre />` element that has a `<code />` element inside if the `<code />` element's className contains a language information.**
|
|
13
|
+
|
|
14
|
+
## When should I use this?
|
|
15
|
+
|
|
16
|
+
This plugin `rehype-pre-language` is useful if there is no language information in `<pre>` element but `<code>` element like `<pre><code className="language-typescript"></pre>`, and you need `<pre>` element to have language information.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
This package is suitable for ESM only. In Node.js (version 16+), install with npm:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install rehype-pre-language
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
or
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
yarn add rehype-pre-language
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
Say we have the following markdown file, `example.md`:
|
|
35
|
+
|
|
36
|
+
````markdown
|
|
37
|
+
```javascript
|
|
38
|
+
const me = "ipikuka";
|
|
39
|
+
```
|
|
40
|
+
````
|
|
41
|
+
|
|
42
|
+
And our module, `example.js`, looks as follows:
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
import { read } from "to-vfile";
|
|
46
|
+
import remark from "remark";
|
|
47
|
+
import gfm from "remark-gfm";
|
|
48
|
+
import remarkRehype from "remark-rehype";
|
|
49
|
+
import rehypeStringify from "rehype-stringify";
|
|
50
|
+
import rehypePreLanguage from "rehype-pre-language";
|
|
51
|
+
|
|
52
|
+
main();
|
|
53
|
+
|
|
54
|
+
async function main() {
|
|
55
|
+
const file = await remark()
|
|
56
|
+
.use(gfm)
|
|
57
|
+
.use(remarkRehype)
|
|
58
|
+
.use(rehypePreLanguage)
|
|
59
|
+
.use(rehypeStringify)
|
|
60
|
+
.process(await read("example.md"));
|
|
61
|
+
|
|
62
|
+
console.log(String(file));
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Now, running `node example.js` you see that the `<pre>` element has a "class" with language information:
|
|
67
|
+
|
|
68
|
+
```html
|
|
69
|
+
<pre class="javascript"><code class="language-javascript">const me = "ipikuka";
|
|
70
|
+
</code></pre>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Without `rehype-pre-language`, the `<pre>` element wouldn't have a language information:
|
|
74
|
+
|
|
75
|
+
```html
|
|
76
|
+
<pre><code class="language-javascript">const me = "ipikuka";
|
|
77
|
+
</code></pre>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Options
|
|
81
|
+
|
|
82
|
+
There is one **string** option which is a property of `<pre>` element in which the language information is going to be passed.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
type PreLanguageOption = string; // the default is "className"
|
|
86
|
+
|
|
87
|
+
use(rehypePreLanguage, PreLanguageOption);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Examples:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// adds the language information into "className" of <pre> element as default
|
|
94
|
+
use(rehypePreLanguage);
|
|
95
|
+
|
|
96
|
+
// adds the language information into "className" of <pre> element
|
|
97
|
+
use(rehypePreLanguage, "className");
|
|
98
|
+
|
|
99
|
+
// adds the language information into "data-language" property of <pre> element
|
|
100
|
+
use(rehypePreLanguage, "data-language");
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Syntax tree
|
|
104
|
+
|
|
105
|
+
This plugin modifies the `hast` (HTML abstract syntax tree).
|
|
106
|
+
|
|
107
|
+
## Types
|
|
108
|
+
|
|
109
|
+
This package is fully typed with [TypeScript][typeScript].
|
|
110
|
+
|
|
111
|
+
The plugin exports the type `PreLanguageOption`.
|
|
112
|
+
|
|
113
|
+
## Compatibility
|
|
114
|
+
|
|
115
|
+
This plugin works with unified version 6+ and remark version 7+.
|
|
116
|
+
|
|
117
|
+
## Security
|
|
118
|
+
|
|
119
|
+
Use of `rehype-pre-language` involves rehype (hast), but doesn't lead to cross-site scripting (XSS) attacks.
|
|
120
|
+
|
|
121
|
+
## My Plugins
|
|
122
|
+
|
|
123
|
+
### My Remark Plugins
|
|
124
|
+
|
|
125
|
+
- [`remark-flexible-code-titles`](https://www.npmjs.com/package/remark-flexible-code-titles)
|
|
126
|
+
– Remark plugin to add titles or/and containers for the code blocks with customizable properties
|
|
127
|
+
- [`remark-flexible-containers`](https://www.npmjs.com/package/remark-flexible-containers)
|
|
128
|
+
– Remark plugin to add custom containers with customizable properties in markdown
|
|
129
|
+
- [`remark-ins`](https://www.npmjs.com/package/remark-ins)
|
|
130
|
+
– Remark plugin to add `ins` element in markdown
|
|
131
|
+
- [`remark-flexible-paragraphs`](https://www.npmjs.com/package/remark-flexible-paragraphs)
|
|
132
|
+
– Remark plugin to add custom paragraphs with customizable properties in markdown
|
|
133
|
+
- [`remark-flexible-markers`](https://www.npmjs.com/package/remark-flexible-markers)
|
|
134
|
+
– Remark plugin to add custom `mark` element with customizable properties in markdown
|
|
135
|
+
- [`remark-flexible-toc`](https://www.npmjs.com/package/remark-flexible-toc)
|
|
136
|
+
– Remark plugin to expose the table of contents via Vfile.data or via an option reference
|
|
137
|
+
- [`remark-mdx-remove-esm`](https://www.npmjs.com/package/remark-mdx-remove-esm)
|
|
138
|
+
– Remark plugin to remove import and/or export statements (mdxjsEsm)
|
|
139
|
+
|
|
140
|
+
### My Rehype Plugins
|
|
141
|
+
|
|
142
|
+
- [`rehype-pre-language`](https://www.npmjs.com/package/rehype-pre-language)
|
|
143
|
+
– Rehype plugin to add language information as a property to `pre` element
|
|
144
|
+
|
|
145
|
+
### My Recma Plugins
|
|
146
|
+
|
|
147
|
+
- [`recma-mdx-escape-missing-components`](https://www.npmjs.com/package/recma-mdx-escape-missing-components)
|
|
148
|
+
– Recma plugin to set the default value `() => null` for the Components in MDX in case of missing or not provided so as not to throw an error
|
|
149
|
+
- [`recma-mdx-change-props`](https://www.npmjs.com/package/recma-mdx-change-props)
|
|
150
|
+
– Recma plugin to change the `props` parameter into the `_props` in the `function _createMdxContent(props) {/* */}` in the compiled source in order to be able to use `{props.foo}` like expressions. It is useful for the `next-mdx-remote` or `next-mdx-remote-client` users in `nextjs` applications.
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
[MIT][license] © ipikuka
|
|
155
|
+
|
|
156
|
+
### Keywords
|
|
157
|
+
|
|
158
|
+
[unified][unifiednpm] [rehype][rehypenpm] [rehype-plugin][rehypepluginnpm] [hast][hastnpm] [markdown][markdownnpm]
|
|
159
|
+
|
|
160
|
+
[unified]: https://github.com/unifiedjs/unified
|
|
161
|
+
[unifiednpm]: https://www.npmjs.com/search?q=keywords:unified
|
|
162
|
+
[rehype]: https://github.com/rehypejs/rehype
|
|
163
|
+
[rehypenpm]: https://www.npmjs.com/search?q=keywords:rehype
|
|
164
|
+
[rehypepluginnpm]: https://www.npmjs.com/search?q=keywords:rehype%20plugin
|
|
165
|
+
[hast]: https://github.com/syntax-tree/hast
|
|
166
|
+
[hastnpm]: https://www.npmjs.com/search?q=keywords:hast
|
|
167
|
+
[micromark]: https://github.com/micromark/micromark
|
|
168
|
+
[typescript]: https://www.typescriptlang.org/
|
|
169
|
+
[license]: https://github.com/ipikuka/rehype-pre-language/blob/main/LICENSE
|
|
170
|
+
[markdownnpm]: https://www.npmjs.com/search?q=keywords:markdown
|
|
171
|
+
|
|
172
|
+
[npm-url]: https://www.npmjs.com/package/rehype-pre-language
|
|
173
|
+
[npm-image]: https://img.shields.io/npm/v/rehype-pre-language
|
|
174
|
+
[github-license]: https://img.shields.io/github/license/ipikuka/rehype-pre-language
|
|
175
|
+
[github-license-url]: https://github.com/ipikuka/rehype-pre-language/blob/master/LICENSE
|
|
176
|
+
[github-build]: https://github.com/ipikuka/rehype-pre-language/actions/workflows/publish.yml/badge.svg
|
|
177
|
+
[github-build-url]: https://github.com/ipikuka/rehype-pre-language/actions/workflows/publish.yml
|
|
178
|
+
[npm-typescript]: https://img.shields.io/npm/types/rehype-pre-language
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Plugin } from "unified";
|
|
2
|
+
import type { Root } from "hast";
|
|
3
|
+
export type PreLanguageOption = string;
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* This plugin adds language information as a property to pre element
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
declare const plugin: Plugin<[PreLanguageOption?], Root>;
|
|
10
|
+
export default plugin;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { visit } from "unist-util-visit";
|
|
2
|
+
import { hasProperty } from "hast-util-has-property";
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* This plugin adds language information as a property to pre element
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
const plugin = (option) => {
|
|
9
|
+
const property = option ? (option.startsWith("on") ? option.slice(2) : option) : "className";
|
|
10
|
+
/**
|
|
11
|
+
* Transform.
|
|
12
|
+
*
|
|
13
|
+
* @param {Root} tree
|
|
14
|
+
* Tree.
|
|
15
|
+
* @returns {undefined}
|
|
16
|
+
* Nothing.
|
|
17
|
+
*/
|
|
18
|
+
return (tree) => {
|
|
19
|
+
visit(tree, "element", function (node, index, parent) {
|
|
20
|
+
if (!parent)
|
|
21
|
+
return;
|
|
22
|
+
if (node.tagName !== "code")
|
|
23
|
+
return;
|
|
24
|
+
if (parent.type !== "element" || parent.tagName !== "pre")
|
|
25
|
+
return;
|
|
26
|
+
if (hasProperty(node, "className")) {
|
|
27
|
+
const filtered = node.properties.className.filter((name) => name.startsWith("language-"));
|
|
28
|
+
if (filtered.length) {
|
|
29
|
+
let language = filtered[0].slice(9);
|
|
30
|
+
if (language.startsWith("diff-")) {
|
|
31
|
+
language = language.slice(5);
|
|
32
|
+
}
|
|
33
|
+
if (parent.properties) {
|
|
34
|
+
if (property === "className") {
|
|
35
|
+
if (hasProperty(parent, "className")) {
|
|
36
|
+
parent.properties["className"] = [
|
|
37
|
+
...parent.properties.className,
|
|
38
|
+
language,
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
parent.properties["className"] = [language];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
parent.properties[property] = language;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
parent.properties = { [property]: language };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
export default plugin;
|
|
58
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAIrD;;;;GAIG;AACH,MAAM,MAAM,GAAuC,CAAC,MAAM,EAAE,EAAE;IAC5D,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;IAE7F;;;;;;;OAOG;IACH,OAAO,CAAC,IAAU,EAAa,EAAE;QAC/B,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,IAAI,EAAE,KAAK,EAAE,MAAM;YAClD,IAAI,CAAC,MAAM;gBAAE,OAAO;YAEpB,IAAI,IAAI,CAAC,OAAO,KAAK,MAAM;gBAAE,OAAO;YAEpC,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK;gBAAE,OAAO;YAElE,IAAI,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC;gBACnC,MAAM,QAAQ,GAAI,IAAI,CAAC,UAAU,CAAC,SAAsB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CACvE,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,CAC7B,CAAC;gBAEF,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;oBACpB,IAAI,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;oBAEpC,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;wBACjC,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;oBAC/B,CAAC;oBAED,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;wBACtB,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;4BAC7B,IAAI,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,CAAC;gCACrC,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,GAAG;oCAC/B,GAAI,MAAM,CAAC,UAAU,CAAC,SAAsB;oCAC5C,QAAQ;iCACT,CAAC;4BACJ,CAAC;iCAAM,CAAC;gCACN,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;4BAC9C,CAAC;wBACH,CAAC;6BAAM,CAAC;4BACN,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;wBACzC,CAAC;oBACH,CAAC;yBAAM,CAAC;wBACN,MAAM,CAAC,UAAU,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;oBAC/C,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rehype-pre-language",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Rehype plugin to add language information as a property to pre element",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": "./dist/esm/index.js",
|
|
7
|
+
"main": "./dist/esm/index.js",
|
|
8
|
+
"types": "./dist/esm/index.d.ts",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "rimraf dist && tsc",
|
|
11
|
+
"type-coverage": "type-coverage --ignoreAsAssertion",
|
|
12
|
+
"lint": "eslint .",
|
|
13
|
+
"prettier": "prettier --write .",
|
|
14
|
+
"test": "vitest",
|
|
15
|
+
"test:ci": "vitest --watch=false",
|
|
16
|
+
"prepack": "npm run build",
|
|
17
|
+
"prepublishOnly": "npm run test:ci && npm run type-coverage && npm run prettier && npm run lint"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist/",
|
|
21
|
+
"src/",
|
|
22
|
+
"LICENSE",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/ipikuka/rehype-pre-language.git"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"unified",
|
|
31
|
+
"hast",
|
|
32
|
+
"rehype",
|
|
33
|
+
"markdown",
|
|
34
|
+
"plugin",
|
|
35
|
+
"rehype-plugin",
|
|
36
|
+
"rehype-pre",
|
|
37
|
+
"rehype-pre-language"
|
|
38
|
+
],
|
|
39
|
+
"author": "ipikuka <talatkuyuk@gmail.com>",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"homepage": "https://github.com/ipikuka/rehype-pre-language#readme",
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/ipikuka/rehype-pre-language/issues"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/dedent": "^0.7.2",
|
|
47
|
+
"@types/node": "^20.10.5",
|
|
48
|
+
"@typescript-eslint/eslint-plugin": "^6.16.0",
|
|
49
|
+
"@typescript-eslint/parser": "^6.16.0",
|
|
50
|
+
"dedent": "^0.7.0",
|
|
51
|
+
"eslint": "^8.56.0",
|
|
52
|
+
"eslint-config-prettier": "^9.1.0",
|
|
53
|
+
"eslint-plugin-prettier": "^5.1.2",
|
|
54
|
+
"prettier": "^3.1.1",
|
|
55
|
+
"rehype-stringify": "^10.0.0",
|
|
56
|
+
"remark-gfm": "^4.0.0",
|
|
57
|
+
"remark-parse": "^11.0.0",
|
|
58
|
+
"remark-rehype": "^11.0.0",
|
|
59
|
+
"rimraf": "^5.0.5",
|
|
60
|
+
"type-coverage": "^2.27.1",
|
|
61
|
+
"typescript": "^5.3.3",
|
|
62
|
+
"unified": "^11.0.4",
|
|
63
|
+
"vfile": "^6.0.1",
|
|
64
|
+
"vitest": "^1.3.0"
|
|
65
|
+
},
|
|
66
|
+
"dependencies": {
|
|
67
|
+
"@types/hast": "^3.0.4",
|
|
68
|
+
"hast-util-has-property": "^3.0.0",
|
|
69
|
+
"unist-util-visit": "^5.0.0"
|
|
70
|
+
},
|
|
71
|
+
"typeCoverage": {
|
|
72
|
+
"atLeast": 100,
|
|
73
|
+
"detail": true,
|
|
74
|
+
"ignoreCatch": true,
|
|
75
|
+
"strict": true
|
|
76
|
+
},
|
|
77
|
+
"sideEffects": false
|
|
78
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Plugin } from "unified";
|
|
2
|
+
import type { Root } from "hast";
|
|
3
|
+
import { type VisitorResult, visit } from "unist-util-visit";
|
|
4
|
+
import { hasProperty } from "hast-util-has-property";
|
|
5
|
+
|
|
6
|
+
export type PreLanguageOption = string;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
* This plugin adds language information as a property to pre element
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
const plugin: Plugin<[PreLanguageOption?], Root> = (option) => {
|
|
14
|
+
const property = option ? (option.startsWith("on") ? option.slice(2) : option) : "className";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Transform.
|
|
18
|
+
*
|
|
19
|
+
* @param {Root} tree
|
|
20
|
+
* Tree.
|
|
21
|
+
* @returns {undefined}
|
|
22
|
+
* Nothing.
|
|
23
|
+
*/
|
|
24
|
+
return (tree: Root): undefined => {
|
|
25
|
+
visit(tree, "element", function (node, index, parent): VisitorResult {
|
|
26
|
+
if (!parent) return;
|
|
27
|
+
|
|
28
|
+
if (node.tagName !== "code") return;
|
|
29
|
+
|
|
30
|
+
if (parent.type !== "element" || parent.tagName !== "pre") return;
|
|
31
|
+
|
|
32
|
+
if (hasProperty(node, "className")) {
|
|
33
|
+
const filtered = (node.properties.className as string[]).filter((name) =>
|
|
34
|
+
name.startsWith("language-"),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
if (filtered.length) {
|
|
38
|
+
let language = filtered[0].slice(9);
|
|
39
|
+
|
|
40
|
+
if (language.startsWith("diff-")) {
|
|
41
|
+
language = language.slice(5);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (parent.properties) {
|
|
45
|
+
if (property === "className") {
|
|
46
|
+
if (hasProperty(parent, "className")) {
|
|
47
|
+
parent.properties["className"] = [
|
|
48
|
+
...(parent.properties.className as string[]),
|
|
49
|
+
language,
|
|
50
|
+
];
|
|
51
|
+
} else {
|
|
52
|
+
parent.properties["className"] = [language];
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
parent.properties[property] = language;
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
parent.properties = { [property]: language };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default plugin;
|