Migrate to djot
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Isaac Mills 2025-07-16 11:45:32 -06:00
parent c0324c0bc2
commit cbfa885984
Signed by: fnmain
GPG key ID: B67D7410F33A0F61
42 changed files with 3701 additions and 547 deletions

View file

@ -0,0 +1,11 @@
root = true
[*]
charset = utf-8
indent_size = 2
indent_style = space
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

4
posthtml-djot/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
node_modules
coverage
dist/
*.log

21
posthtml-djot/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License (MIT)
Copyright (c) TheComputerM
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

230
posthtml-djot/README.md Normal file
View file

@ -0,0 +1,230 @@
<div align="center">
<img width="150" height="150" title="PostHTML" src="https://posthtml.github.io/posthtml/logo.svg">
<h1>posthtml-markdownit</h1>
Transform Markdown to HTML
[![Version][npm-version-shield]][npm]
[![Build][github-ci-shield]][github-ci]
[![License][license-shield]][license]
[![Downloads][npm-stats-shield]][npm-stats]
</div>
## Introduction
This PostHTML plugin compiles Markdown to HTML using [markdown-it](https://github.com/markdown-it/markdown-it).
Before:
```xml
<markdown>
# Heading 1
---
Paragraph with some text
_Italic_
**Bold**
- List item 1
- List item 2
- List item 3
</markdown>
```
After:
```html
<h1>Heading 1</h1>
<hr>
<p>Paragraph with some text</p>
<p>
<em>Italic</em>
<strong>Bold</strong>
</p>
<ul>
<li>List item 1</li>
<li>List item 2</li>
<li>List item 3</li>
</ul>
```
## Install
```
npm i -D posthtml posthtml-markdownit
```
## Usage
```js
import posthtml from 'posthtml'
import markdown from 'posthtml-markdownit'
posthtml([
markdown()
])
.process('<markdown># Test</markdown>')
.then(result => console.log(result.html))
// <h1>Test</h1>
```
### Importing files
You can import and render Markdown files:
Before:
```xml
<markdown src="./README.md">
# Imported
</markdown>
```
After:
```html
<!-- Here be the contents of README.md, as HTML -->
<h1>Imported</h1>
```
## Syntax
### Tags
Both `<markdown>` and `<md>` tags are supported.
Before:
```xml
<md>
# Heading 1
</md>
```
After:
```html
<h1>Heading 1</h1>
```
By default, the custom tags like `<md>` are replaced with the compiled Markdown. See the [tag attribute](#tag) if you need a wrapping tag around your Markdown content.
### Attributes
You can also use the `markdown` or `md` attributes on an HTML element:
Before:
```html
<div md>
# Heading 1
</div>
```
After:
```html
<h1>Heading 1</h1>
```
#### `tag`
You can use a `tag` attribute to wrap the resulting markup in a tag:
Before:
```xml
<md tag="section">
# Heading 1
</md>
```
After:
```html
<section>
<h1>Heading 1</h1>
</section>
```
#### `inline`
You can mark the content to be rendered inline. This is helpful if you're including a file that will be used as an inline element and don't want the enclosing `p` tags.
Before:
```xml
<div class="example">
<markdown src="./example.md" inline>
Imported
</markdown>
</div>
```
After:
```html
<p class="example">Imported</p>
```
Instead of:
```html
<div class="example">
<p>Imported</p>
</div>
```
## Options
### `root`
Type: `string`\
Default: `./`
A path relative to which Markdown files will be [imported](#importing-files).
### `encoding`
Type: `string`\
Default: `utf8`
Encoding for imported Markdown files.
### `markdownit`
Type: `object`\
Default: `{}`
Options passed to the `markdown-it` library. See the [available options](https://github.com/markdown-it/markdown-it#init-with-presets-and-options).
### `plugins`
Type: `array`\
Default: `[]`
Plugins for `markdown-it`.
Example:
```js
import {light as emoji} from 'markdown-it-emoji'
markdown({
plugins: [{
plugin: emoji,
options: {} // Options for markdown-it-emoji
}]
})
```
[npm]: https://www.npmjs.com/package/posthtml-markdownit
[npm-version-shield]: https://img.shields.io/npm/v/posthtml-markdownit.svg
[npm-stats]: http://npm-stat.com/charts.html?package=posthtml-markdownit
[npm-stats-shield]: https://img.shields.io/npm/dt/posthtml-markdownit.svg
[github-ci]: https://github.com/posthtml/posthtml-markdownit/actions/workflows/nodejs.yml
[github-ci-shield]: https://github.com/posthtml/posthtml-markdownit/actions/workflows/nodejs.yml/badge.svg
[license]: ./LICENSE
[license-shield]: https://img.shields.io/npm/l/posthtml-markdownit.svg

View file

@ -0,0 +1,11 @@
import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
rollup: {
emitCJS: true,
},
rootDir: './lib',
outDir: '../dist',
entries: ['index.js'],
declaration: true,
})

32
posthtml-djot/lib/index.d.ts vendored Normal file
View file

@ -0,0 +1,32 @@
import type { ParseOptions } from '@djot/djot/types/parse';
import type { HTMLRenderOptions } from '@djot/djot/types/html';
export type DjotConfig = {
/**
Path relative to which markdown files are imported.
@default './'
*/
root?: string;
/**
Encoding for imported Markdown files.
@default 'utf8'
*/
encoding?: string;
/**
Options to pass to the `djot` library.
@default {}
*/
djot?: ParseOptions;
/**
Options to pass to the `djot` HTML renderer.
@default {}
*/
djot_html?: HTMLRenderOptions;
};

108
posthtml-djot/lib/index.js Normal file
View file

@ -0,0 +1,108 @@
import {render} from 'posthtml-render'
import {parser} from 'posthtml-parser'
import * as djot from '@djot/djot'
import minIndent from 'min-indent'
import {resolve} from 'node:path'
import {readFileSync} from 'node:fs'
import {match} from 'posthtml/lib/api.js'
const normalize = data => {
const indents = minIndent(data)
// Removing indents
if (indents !== 0) {
const replaceRegex = new RegExp(`^[ \\t]{${indents}}`, 'gm')
return data.replace(replaceRegex, '')
}
return data
}
const parse = (data, settings, html_settings, hasTag) => {
// Parsing content
const parsed = djot.renderHTML(djot.parse(data, settings), html_settings)
return hasTag ? `\n${parsed}` : parsed
}
const importDjot = (src, settings) => {
const from = resolve(settings.root, src)
const content = readFileSync(from, settings.encoding)
return normalize(content)
}
const plugin = options => {
const settings = {
root: './',
encoding: 'utf8',
djot: {},
djot_html: {},
...options
}
return tree => {
tree.match = tree.match || match
tree.match([
{tag: 'dj'},
{tag: 'djot'},
{attrs: {dj: ''}},
{attrs: {djot: ''}}
], node => {
let content = ''
if (['dj', 'djot'].includes(node.tag)) {
node.tag = false
}
if (node.attrs) {
// Change tag
if (node.attrs.tag) {
node.tag = node.attrs.tag
delete node.attrs.tag
}
for (const attribute of ['dj', 'djot']) {
if (attribute in node.attrs) {
delete node.attrs[attribute]
}
}
// Import djot file if specified
const src = node.attrs.src || false
if (src) {
content += importDjot(src, settings)
if (tree.messages) {
const from = resolve(settings.root, src)
tree.messages.push({
type: 'dependency',
file: from
})
}
delete node.attrs.src
}
}
// Converting content to html tree
content += normalize(render(node.content))
// Parsing content
const parsed = parse(content, settings.djot, settings.djot_html, node.tag)
// Converting tree to html content
node.content = parser(parsed)
return node
})
return tree
}
}
export default plugin

View file

@ -0,0 +1,54 @@
{
"name": "posthtml-djot",
"description": "A PostHTML plugin to transform Djot",
"version": "3.1.2",
"license": "MIT",
"author": "TheComputerM",
"bugs": "https://github.com/posthtml/posthtml-djot/issues",
"homepage": "https://github.com/posthtml/posthtml-djot",
"repository": "posthtml/posthtml-djot",
"type": "module",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"types": "./dist/index.d.ts",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"files": [
"dist"
],
"engines": {
"node": ">=18"
},
"scripts": {
"dev": "vitest",
"build": "unbuild",
"prepack": "unbuild",
"test": "vitest run --coverage",
"lint": "biome lint ./lib ./test",
"pretest": "npm run lint",
"release": "npx np"
},
"keywords": [
"html",
"posthtml",
"posthtml-plugin",
"djot"
],
"dependencies": {
"min-indent": "^1.0.0",
"posthtml": "^0.16.6",
"posthtml-parser": "^0.12.0",
"posthtml-render": "^3.0.0"
},
"devDependencies": {
"@biomejs/biome": "2.1.1",
"@djot/djot": "^0.3.2",
"@vitest/coverage-v8": "^3.0.5",
"unbuild": "^2.0.0",
"vitest": "^3.0.5"
}
}

View file

@ -0,0 +1,19 @@
<h1>Heading 1</h1>
<p>Paragraph with some text</p>
<div>
<ul>
<li>List item 1</li>
<li>List item 2</li>
<li>List item 3</li>
</ul>
</div>
<section>
<blockquote>
<p>A quote</p>
</blockquote>
</section>

View file

@ -0,0 +1,3 @@
<section>
<h1>A markdown heading</h1>
</section>

View file

@ -0,0 +1,5 @@
<h1>Heading</h1>
<pre><code class="language-js">function () {
console.log(&quot;Hello&quot;)
}
</code></pre>

View file

@ -0,0 +1 @@
Hello <em>there</em>

View file

@ -0,0 +1,2 @@
<h1>Hello there</h1>
<h2>Imported content should be above me</h2>

View file

@ -0,0 +1 @@
<p><a href="https://example.com">https://example.com</a></p>

View file

@ -0,0 +1 @@
<p>You can use emojis 😃</p>

17
posthtml-djot/test/fixtures/basic.html vendored Executable file
View file

@ -0,0 +1,17 @@
<md>
# Heading 1
</md>
<markdown>
Paragraph with some text
</markdown>
<div md>
- List item 1
- List item 2
- List item 3
</div>
<section markdown>
> A quote
</section>

View file

@ -0,0 +1,3 @@
<md tag="section">
# A markdown heading
</md>

9
posthtml-djot/test/fixtures/code.html vendored Normal file
View file

@ -0,0 +1,9 @@
<md>
# Heading
```js
function () {
console.log("Hello")
}
```
</md>

View file

@ -0,0 +1 @@
<md src="test/fixtures/test-inline.md" inline></md>

View file

@ -0,0 +1,3 @@
<md src="test/fixtures/test.md">
## Imported content should be above me
</md>

View file

@ -0,0 +1,3 @@
<md>
https://example.com
</md>

View file

@ -0,0 +1,3 @@
<md>
You can use emojis :)
</md>

View file

@ -0,0 +1 @@
Hello _there_

1
posthtml-djot/test/fixtures/test.md vendored Normal file
View file

@ -0,0 +1 @@
# Hello there

View file

@ -0,0 +1,59 @@
import path from 'node:path'
import {readFileSync} from 'node:fs'
import {fileURLToPath} from 'node:url'
import plugin from '../lib/index.js'
import {test, expect} from 'vitest'
import posthtml from 'posthtml'
import {light as emoji} from 'markdown-it-emoji'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const fixture = file => readFileSync(path.join(__dirname, 'fixtures', `${file}.html`), 'utf8').trim()
const expected = file => readFileSync(path.join(__dirname, 'expected', `${file}.html`), 'utf8').trim()
const clean = html => html.replace(/[^\S\r\n]+$/gm, '').trim()
const process = (_t, name, options, log = false) => {
return posthtml([plugin(options)])
.process(fixture(name))
.then(result => log ? console.log(result.html) : clean(result.html))
.then(html => expect(html).toEqual(expected(name)))
}
test('Basic', t => {
return process(t, 'basic')
})
test('Fenced code block', t => {
return process(t, 'code')
})
test('Custom tag', t => {
return process(t, 'change-tag')
})
test('Render markdown in imported file', t => {
return process(t, 'importing')
})
test('Render markdown inline from imported file', t => {
return process(t, 'importing-inline')
})
test('Uses markdown-it plugins', t => {
return process(t, 'md-plugin', {
plugins: [
{
plugin: emoji
}
]
})
})
test('Uses markdown-it options', t => {
return process(t, 'md-options', {
markdownit: {
linkify: true
}
})
})

View file

@ -0,0 +1,7 @@
import {defineConfig} from 'vitest/config'
export default defineConfig({
test: {
include: ['**/*test.{js,ts}'],
},
})