rehype-pre-language
Advanced tools
Comparing version
@@ -6,3 +6,3 @@ import type { Plugin } from "unified"; | ||
* | ||
* This plugin adds language information as a property to pre element | ||
* adds code language to <pre> element as a property | ||
* | ||
@@ -9,0 +9,0 @@ */ |
@@ -1,6 +0,5 @@ | ||
import { visit } from "unist-util-visit"; | ||
import { hasProperty } from "hast-util-has-property"; | ||
import { visit, CONTINUE } from "unist-util-visit"; | ||
/** | ||
* | ||
* This plugin adds language information as a property to pre element | ||
* adds code language to <pre> element as a property | ||
* | ||
@@ -11,2 +10,20 @@ */ | ||
/** | ||
* | ||
* extract the language from properties | ||
*/ | ||
function getLanguage(properties) { | ||
const { className } = properties; | ||
if (!className) | ||
return; | ||
const filtered = className.filter((name) => name.startsWith("language-")); | ||
/* v8 ignore next */ | ||
if (!filtered.length) | ||
return; | ||
let language = filtered[0].slice(9); | ||
if (language.startsWith("diff-")) { | ||
language = language.slice(5); | ||
} | ||
return language; | ||
} | ||
/** | ||
* Transform. | ||
@@ -21,36 +38,23 @@ * | ||
visit(tree, "element", function (node, index, parent) { | ||
if (!parent) | ||
/* v8 ignore next */ | ||
if (!parent || typeof index === "undefined") | ||
return; | ||
if (node.tagName !== "code") | ||
return; | ||
if (parent.type !== "element" || parent.tagName !== "pre") | ||
return; | ||
if (hasProperty(node, "className")) { | ||
const filtered = node.properties.className.filter((name) => name.startsWith("language-")); | ||
if (filtered.length) { | ||
let language = filtered[0].slice(9); | ||
if (language.startsWith("diff-")) { | ||
language = language.slice(5); | ||
} | ||
if (parent.properties) { | ||
if (property === "className") { | ||
if (hasProperty(parent, "className")) { | ||
parent.properties["className"] = [ | ||
...parent.properties.className, | ||
language, | ||
]; | ||
} | ||
else { | ||
parent.properties["className"] = [language]; | ||
} | ||
} | ||
else { | ||
parent.properties[property] = language; | ||
} | ||
} | ||
else { | ||
parent.properties = { [property]: language }; | ||
} | ||
} | ||
return CONTINUE; | ||
const language = getLanguage(node.properties); | ||
if (!language) | ||
return CONTINUE; | ||
/* v8 ignore next */ | ||
if (parent.type === "root") | ||
return; // just for type narrowing | ||
if (property !== "className") { | ||
parent.properties[property] = language; | ||
} | ||
else { | ||
parent.properties.className = [ | ||
/* v8 ignore next */ // ignore because I couldn't create a test case | ||
...(parent.properties.className ? parent.properties.className : []), | ||
language, | ||
]; | ||
} | ||
}); | ||
@@ -57,0 +61,0 @@ }; |
{ | ||
"name": "rehype-pre-language", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"description": "Rehype plugin to add language information as a property to pre element", | ||
@@ -10,10 +10,12 @@ "type": "module", | ||
"scripts": { | ||
"build": "rimraf dist && tsc", | ||
"type-coverage": "type-coverage --ignoreAsAssertion", | ||
"build": "rimraf dist && tsc --build && type-coverage", | ||
"format": "npm run prettier && npm run lint", | ||
"prettier": "prettier --write .", | ||
"lint": "eslint .", | ||
"prettier": "prettier --write .", | ||
"test": "vitest", | ||
"test:ci": "vitest --watch=false", | ||
"test": "vitest --watch=false", | ||
"test:watch": "vitest", | ||
"test:file": "vitest test.spec.ts", | ||
"prepack": "npm run build", | ||
"prepublishOnly": "npm run test:ci && npm run type-coverage && npm run prettier && npm run lint" | ||
"prepublishOnly": "npm run test && npm run format && npm run test-coverage", | ||
"test-coverage": "vitest run --coverage" | ||
}, | ||
@@ -48,24 +50,24 @@ "files": [ | ||
"@types/dedent": "^0.7.2", | ||
"@types/node": "^20.10.5", | ||
"@typescript-eslint/eslint-plugin": "^6.16.0", | ||
"@typescript-eslint/parser": "^6.16.0", | ||
"dedent": "^0.7.0", | ||
"eslint": "^8.56.0", | ||
"@types/node": "^20.11.30", | ||
"@typescript-eslint/eslint-plugin": "^7.3.1", | ||
"@typescript-eslint/parser": "^7.3.1", | ||
"@vitest/coverage-v8": "^1.4.0", | ||
"dedent": "^1.5.1", | ||
"eslint": "^8.57.0", | ||
"eslint-config-prettier": "^9.1.0", | ||
"eslint-plugin-prettier": "^5.1.2", | ||
"prettier": "^3.1.1", | ||
"eslint-plugin-prettier": "^5.1.3", | ||
"prettier": "^3.2.5", | ||
"rehype-stringify": "^10.0.0", | ||
"remark-gfm": "^4.0.0", | ||
"remark-parse": "^11.0.0", | ||
"remark-rehype": "^11.0.0", | ||
"remark-rehype": "^11.1.0", | ||
"rimraf": "^5.0.5", | ||
"type-coverage": "^2.27.1", | ||
"typescript": "^5.3.3", | ||
"typescript": "^5.4.3", | ||
"unified": "^11.0.4", | ||
"vfile": "^6.0.1", | ||
"vitest": "^1.3.0" | ||
"vitest": "^1.4.0" | ||
}, | ||
"dependencies": { | ||
"@types/hast": "^3.0.4", | ||
"hast-util-has-property": "^3.0.0", | ||
"unist-util-visit": "^5.0.0" | ||
@@ -76,2 +78,3 @@ }, | ||
"detail": true, | ||
"ignoreAsAssertion": true, | ||
"ignoreCatch": true, | ||
@@ -78,0 +81,0 @@ "strict": true |
# rehype-pre-language | ||
[![NPM version][npm-image]][npm-url] | ||
[![Build][github-build]][github-build-url] | ||
![npm-typescript] | ||
[![License][github-license]][github-license-url] | ||
[![NPM version][badge-npm-version]][npm-package-url] | ||
[![NPM downloads][badge-npm-download]][npm-package-url] | ||
[![Build][badge-build]][github-workflow-url] | ||
[](https://codecov.io/gh/ipikuka/rehype-pre-language) | ||
[](https://github.com/ipikuka/rehype-pre-language) | ||
[![typescript][badge-typescript]][typescript-url] | ||
[![License][badge-license]][github-license-url] | ||
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]"). | ||
This package is a [unified][unified] ([rehype][rehype]) plugin **to add language information of `<code>` element into `<pre>` element as a property**. | ||
"**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. | ||
**[unified][unified]** is a project that transforms content with abstract syntax trees (ASTs) using the new parser **[micromark][micromark]**. **[remark][remark]** adds support for markdown to unified. **[mdast][mdast]** is the Markdown Abstract Syntax Tree (AST) which is a specification for representing markdown in a syntax tree. "**[rehype][rehype]**" is a tool that transforms HTML with plugins. "**[hast][hast]**" stands for HTML Abstract Syntax Tree (HAST) that rehype uses. | ||
**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.** | ||
**This plugin finds the `<code>` elements in hast, takes the language information and adds the language into `<pre>` element as "className" property by default or as a property provided in options.** | ||
@@ -69,4 +72,5 @@ ## When should I use this? | ||
```html | ||
<pre class="javascript"><code class="language-javascript">const me = "ipikuka"; | ||
</code></pre> | ||
<pre class="javascript"> | ||
<code class="language-javascript">const me = "ipikuka";</code> | ||
</pre> | ||
``` | ||
@@ -77,4 +81,5 @@ | ||
```html | ||
<pre><code class="language-javascript">const me = "ipikuka"; | ||
</code></pre> | ||
<pre> | ||
<code class="language-javascript">const me = "ipikuka";</code> | ||
</pre> | ||
``` | ||
@@ -111,3 +116,3 @@ | ||
This package is fully typed with [TypeScript][typeScript]. | ||
This package is fully typed with [TypeScript][typescript]. | ||
@@ -118,3 +123,3 @@ The plugin exports the type `PreLanguageOption`. | ||
This plugin works with unified version 6+ and remark version 7+. | ||
This plugin works with `rehype-parse` version 1+, `rehype-stringify` version 1+, `rehype` version 1+, and unified version `4+`. | ||
@@ -127,2 +132,4 @@ ## Security | ||
I like to contribute the Unified / Remark / MDX ecosystem, so I recommend you to have a look my plugins. | ||
### My Remark Plugins | ||
@@ -141,3 +148,3 @@ | ||
- [`remark-flexible-toc`](https://www.npmjs.com/package/remark-flexible-toc) | ||
– Remark plugin to expose the table of contents via Vfile.data or via an option reference | ||
– Remark plugin to expose the table of contents via `vfile.data` or via an option reference | ||
- [`remark-mdx-remove-esm`](https://www.npmjs.com/package/remark-mdx-remove-esm) | ||
@@ -160,26 +167,35 @@ – Remark plugin to remove import and/or export statements (mdxjsEsm) | ||
[MIT][license] © ipikuka | ||
[MIT License](./LICENSE) © ipikuka | ||
### Keywords | ||
[unified][unifiednpm] [rehype][rehypenpm] [rehype-plugin][rehypepluginnpm] [hast][hastnpm] [markdown][markdownnpm] | ||
🟩 [unified][unifiednpm] 🟩 [rehype][rehypenpm] 🟩 [rehype plugin][rehypepluginnpm] 🟩 [hast][hastnpm] 🟩 [markdown][markdownnpm] | ||
[unified]: https://github.com/unifiedjs/unified | ||
[unifiednpm]: https://www.npmjs.com/search?q=keywords:unified | ||
[rehype]: https://github.com/rehypejs/rehype | ||
[rehypenpm]: https://www.npmjs.com/search?q=keywords:rehype | ||
[rehypepluginnpm]: https://www.npmjs.com/search?q=keywords:rehype%20plugin | ||
[hast]: https://github.com/syntax-tree/hast | ||
[hastnpm]: https://www.npmjs.com/search?q=keywords:hast | ||
[markdownnpm]: https://www.npmjs.com/search?q=keywords:markdown | ||
[unified]: https://github.com/unifiedjs/unified | ||
[micromark]: https://github.com/micromark/micromark | ||
[remark]: https://github.com/remarkjs/remark | ||
[remarkplugins]: https://github.com/remarkjs/remark/blob/main/doc/plugins.md | ||
[mdast]: https://github.com/syntax-tree/mdast | ||
[rehype]: https://github.com/rehypejs/rehype | ||
[rehypeplugins]: https://github.com/rehypejs/rehype/blob/main/doc/plugins.md | ||
[hast]: https://github.com/syntax-tree/hast | ||
[typescript]: https://www.typescriptlang.org/ | ||
[license]: https://github.com/ipikuka/rehype-pre-language/blob/main/LICENSE | ||
[markdownnpm]: https://www.npmjs.com/search?q=keywords:markdown | ||
[npm-url]: https://www.npmjs.com/package/rehype-pre-language | ||
[npm-image]: https://img.shields.io/npm/v/rehype-pre-language | ||
[github-license]: https://img.shields.io/github/license/ipikuka/rehype-pre-language | ||
[github-license-url]: https://github.com/ipikuka/rehype-pre-language/blob/master/LICENSE | ||
[github-build]: https://github.com/ipikuka/rehype-pre-language/actions/workflows/publish.yml/badge.svg | ||
[github-build-url]: https://github.com/ipikuka/rehype-pre-language/actions/workflows/publish.yml | ||
[npm-typescript]: https://img.shields.io/npm/types/rehype-pre-language | ||
[badge-npm-version]: https://img.shields.io/npm/v/rehype-pre-language | ||
[badge-npm-download]:https://img.shields.io/npm/dt/rehype-pre-language | ||
[npm-package-url]: https://www.npmjs.com/package/rehype-pre-language | ||
[badge-license]: https://img.shields.io/github/license/ipikuka/rehype-pre-language | ||
[github-license-url]: https://github.com/ipikuka/rehype-pre-language/blob/main/LICENSE | ||
[badge-build]: https://github.com/ipikuka/rehype-pre-language/actions/workflows/publish.yml/badge.svg | ||
[github-workflow-url]: https://github.com/ipikuka/rehype-pre-language/actions/workflows/publish.yml | ||
[badge-typescript]: https://img.shields.io/npm/types/rehype-pre-language | ||
[typescript-url]: https://www.typescriptlang.org/ |
import type { Plugin } from "unified"; | ||
import type { Root } from "hast"; | ||
import { type VisitorResult, visit } from "unist-util-visit"; | ||
import { hasProperty } from "hast-util-has-property"; | ||
import type { Properties, Root } from "hast"; | ||
import { type VisitorResult, visit, CONTINUE } from "unist-util-visit"; | ||
@@ -10,3 +9,3 @@ export type PreLanguageOption = string; | ||
* | ||
* This plugin adds language information as a property to pre element | ||
* adds code language to <pre> element as a property | ||
* | ||
@@ -18,2 +17,25 @@ */ | ||
/** | ||
* | ||
* extract the language from properties | ||
*/ | ||
function getLanguage(properties: Properties): string | undefined { | ||
const { className } = properties; | ||
if (!className) return; | ||
const filtered = (className as string[]).filter((name) => name.startsWith("language-")); | ||
/* v8 ignore next */ | ||
if (!filtered.length) return; | ||
let language = filtered[0].slice(9); | ||
if (language.startsWith("diff-")) { | ||
language = language.slice(5); | ||
} | ||
return language; | ||
} | ||
/** | ||
* Transform. | ||
@@ -28,37 +50,22 @@ * | ||
visit(tree, "element", function (node, index, parent): VisitorResult { | ||
if (!parent) return; | ||
/* v8 ignore next */ | ||
if (!parent || typeof index === "undefined") return; | ||
if (node.tagName !== "code") return; | ||
if (node.tagName !== "code") return CONTINUE; | ||
if (parent.type !== "element" || parent.tagName !== "pre") return; | ||
const language = getLanguage(node.properties); | ||
if (hasProperty(node, "className")) { | ||
const filtered = (node.properties.className as string[]).filter((name) => | ||
name.startsWith("language-"), | ||
); | ||
if (!language) return CONTINUE; | ||
if (filtered.length) { | ||
let language = filtered[0].slice(9); | ||
/* v8 ignore next */ | ||
if (parent.type === "root") return; // just for type narrowing | ||
if (language.startsWith("diff-")) { | ||
language = language.slice(5); | ||
} | ||
if (parent.properties) { | ||
if (property === "className") { | ||
if (hasProperty(parent, "className")) { | ||
parent.properties["className"] = [ | ||
...(parent.properties.className as string[]), | ||
language, | ||
]; | ||
} else { | ||
parent.properties["className"] = [language]; | ||
} | ||
} else { | ||
parent.properties[property] = language; | ||
} | ||
} else { | ||
parent.properties = { [property]: language }; | ||
} | ||
} | ||
if (property !== "className") { | ||
parent.properties[property] = language; | ||
} else { | ||
parent.properties.className = [ | ||
/* v8 ignore next */ // ignore because I couldn't create a test case | ||
...(parent.properties.className ? (parent.properties.className as string[]) : []), | ||
language, | ||
]; | ||
} | ||
@@ -65,0 +72,0 @@ }); |
Sorry, the diff of this file is not supported yet
16998
7.84%2
-33.33%128
4.07%195
8.94%20
5.26%- Removed
- Removed