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 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;