pantsdown 1.4.5 → 1.5.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/README.md +3 -4
- package/package.json +1 -1
- package/src/css/styles.css +7 -0
- package/src/renderer.ts +45 -40
- package/src/types.ts +2 -0
- package/src/utils.ts +4 -22
package/README.md
CHANGED
|
@@ -5,12 +5,11 @@
|
|
|
5
5
|
<img src="https://raw.githubusercontent.com/wallpants/pantsdown/main/docs/github.svg" height="60px" align="right" />
|
|
6
6
|
<img src="https://raw.githubusercontent.com/wallpants/pantsdown/main/docs/markdown.svg" height="60px" align="right" />
|
|
7
7
|
|
|
8
|
-
Pantsdown is a **Markdown** to **
|
|
9
|
-
Basically this is a non customizable version of Marked, optimized to render markdown similar to how GitHub does it plus
|
|
8
|
+
Pantsdown is a **Markdown** to **HTML** converter. It attempts to render markdown similar to how GitHub does it plus
|
|
10
9
|
some features developed specifically for [github-preview.nvim](https://github.com/wallpants/github-preview.nvim).
|
|
11
10
|
|
|
12
11
|
If you need a feature that's supported by GitHub and is not already listed in the [Roadmap](#roadmap),
|
|
13
|
-
feel free to open an issue.
|
|
12
|
+
feel free to open an issue.
|
|
14
13
|
|
|
15
14
|
### [Demo](https://wallpants.github.io/pantsdown/)
|
|
16
15
|
|
|
@@ -133,5 +132,5 @@ console.log(html);
|
|
|
133
132
|
## Roadmap
|
|
134
133
|
|
|
135
134
|
- [ ] Add button to copy code in fences
|
|
136
|
-
- [ ] [Mermaid](https://mermaid.js.org/)
|
|
135
|
+
- [ ] [Mermaid](https://mermaid.js.org/) usage docs
|
|
137
136
|
- [ ] [Footnote](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#footnotes) support [Base](https://github.com/bent10/marked-extensions/tree/main/packages/footnote)
|
package/package.json
CHANGED
package/src/css/styles.css
CHANGED
package/src/renderer.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import GithubSlugger from "github-slugger";
|
|
2
2
|
import hljs from "highlight.js";
|
|
3
3
|
import { inline } from "./rules/inline.ts";
|
|
4
|
-
import { type PantsdownConfig, type SourceMap } from "./types.ts";
|
|
4
|
+
import { type HTMLAttrs, type PantsdownConfig, type SourceMap } from "./types.ts";
|
|
5
5
|
import {
|
|
6
6
|
cleanUrl,
|
|
7
7
|
escape,
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
fixLocalImageHref,
|
|
10
10
|
getHtmlElementText,
|
|
11
11
|
injectHtmlAttributes,
|
|
12
|
-
renderHtmlClasses,
|
|
13
12
|
} from "./utils.ts";
|
|
14
13
|
|
|
15
14
|
const defaultConfig: NonNullable<PantsdownConfig["renderer"]> = {
|
|
@@ -31,9 +30,16 @@ export class Renderer {
|
|
|
31
30
|
|
|
32
31
|
code(code: string, infostring: string | undefined, sourceMap: SourceMap): string {
|
|
33
32
|
const lang = (infostring ?? "").match(/^\S*/)?.[0];
|
|
34
|
-
|
|
35
33
|
code = code.replace(/\n$/, "") + "\n";
|
|
36
34
|
|
|
35
|
+
if (lang === "mermaid") {
|
|
36
|
+
return injectHtmlAttributes(
|
|
37
|
+
`<section><div class="mermaid">${code}</div></section>`,
|
|
38
|
+
[["class", "mermaid-container"]],
|
|
39
|
+
sourceMap,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
37
43
|
const language = lang && hljs.getLanguage(lang) ? lang : "plaintext";
|
|
38
44
|
const highlightedCode = hljs.highlight(code, { language }).value;
|
|
39
45
|
|
|
@@ -54,7 +60,7 @@ export class Renderer {
|
|
|
54
60
|
html(html: string, _block: boolean, sourceMap?: SourceMap | undefined): string {
|
|
55
61
|
const result = fixHtmlLocalImageHref(html, this.rendererConfig.relativeImageUrlPrefix);
|
|
56
62
|
|
|
57
|
-
const attrs:
|
|
63
|
+
const attrs: HTMLAttrs = [];
|
|
58
64
|
|
|
59
65
|
if (this.rendererConfig.detailsTagDefaultOpen) {
|
|
60
66
|
const tag = inline.tag.exec(html);
|
|
@@ -69,12 +75,12 @@ export class Renderer {
|
|
|
69
75
|
heading(text: string, level: number, sourceMap: SourceMap): string {
|
|
70
76
|
const elementText = getHtmlElementText(text);
|
|
71
77
|
const slug = this.slugger.slug(elementText);
|
|
72
|
-
let result = `<h${level}
|
|
78
|
+
let result = `<h${level}>`;
|
|
73
79
|
// span with negative top to add some offset when scrolling to #slug
|
|
74
80
|
result += `<span style="position: absolute; top: -50px;" id="${slug}"></span>`;
|
|
75
81
|
result += `${text}<a class="anchor octicon-link" href="#${slug}"></a>`;
|
|
76
82
|
result += `</h${level}>\n`;
|
|
77
|
-
return injectHtmlAttributes(result, [], sourceMap);
|
|
83
|
+
return injectHtmlAttributes(result, [["style", "position: relative;"]], sourceMap);
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
hr(sourceMap: SourceMap): string {
|
|
@@ -83,28 +89,35 @@ export class Renderer {
|
|
|
83
89
|
|
|
84
90
|
list(body: string, ordered: boolean, start: number | "", classes: string[] = []): string {
|
|
85
91
|
const type = ordered ? "ol" : "ul";
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
"
|
|
89
|
-
|
|
92
|
+
const attrs: HTMLAttrs = [["class", classes.join(" ")]];
|
|
93
|
+
if (ordered && start && start !== 1) {
|
|
94
|
+
attrs.push(["start", String(start)]);
|
|
95
|
+
}
|
|
96
|
+
const result = `<${type}>\n${body}</${type}>\n`;
|
|
97
|
+
return injectHtmlAttributes(result, attrs);
|
|
90
98
|
}
|
|
91
99
|
|
|
92
100
|
listitem(text: string, task: boolean, _checked: boolean, sourceMap: SourceMap): string {
|
|
93
101
|
const attrs: [string, string][] = [];
|
|
94
102
|
if (task) attrs.push(["class", "task-list-item"]);
|
|
95
|
-
|
|
103
|
+
const result = `<li>${text}</li>\n`;
|
|
104
|
+
return injectHtmlAttributes(result, attrs, sourceMap);
|
|
96
105
|
}
|
|
97
106
|
|
|
98
107
|
checkbox(checked: boolean, classes: string[] = []): string {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
108
|
+
const result = "<input>";
|
|
109
|
+
const attrs: HTMLAttrs = [
|
|
110
|
+
["disabled", ""],
|
|
111
|
+
["type", "checkbox"],
|
|
112
|
+
["class", classes.join(" ")],
|
|
113
|
+
];
|
|
114
|
+
if (checked) attrs.push(["checked", ""]);
|
|
115
|
+
return injectHtmlAttributes(result, attrs);
|
|
104
116
|
}
|
|
105
117
|
|
|
106
118
|
paragraph(text: string, sourceMap: SourceMap): string {
|
|
107
|
-
|
|
119
|
+
const result = `<p>${text}</p>\n`;
|
|
120
|
+
return injectHtmlAttributes(result, [], sourceMap);
|
|
108
121
|
}
|
|
109
122
|
|
|
110
123
|
table(header: string, body: string): string {
|
|
@@ -113,11 +126,8 @@ export class Renderer {
|
|
|
113
126
|
}
|
|
114
127
|
|
|
115
128
|
tablerow(content: string, sourceMapStart: number | undefined): string {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
[],
|
|
119
|
-
sourceMapStart ? [sourceMapStart, sourceMapStart] : undefined,
|
|
120
|
-
);
|
|
129
|
+
const sourceMap: SourceMap = sourceMapStart ? [sourceMapStart, sourceMapStart] : undefined;
|
|
130
|
+
return injectHtmlAttributes(`<tr>\n${content}</tr>\n`, [], sourceMap);
|
|
121
131
|
}
|
|
122
132
|
|
|
123
133
|
tablecell(
|
|
@@ -128,8 +138,10 @@ export class Renderer {
|
|
|
128
138
|
},
|
|
129
139
|
): string {
|
|
130
140
|
const type = flags.header ? "th" : "td";
|
|
131
|
-
const
|
|
132
|
-
|
|
141
|
+
const attrs: HTMLAttrs = [];
|
|
142
|
+
if (flags.align) attrs.push(["align", flags.align]);
|
|
143
|
+
const result = `<${type}>` + content + `</${type}>\n`;
|
|
144
|
+
return injectHtmlAttributes(result, attrs);
|
|
133
145
|
}
|
|
134
146
|
|
|
135
147
|
/**
|
|
@@ -160,13 +172,9 @@ export class Renderer {
|
|
|
160
172
|
if (cleanHref === null) {
|
|
161
173
|
return text;
|
|
162
174
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
out += ' title="' + title + '"';
|
|
167
|
-
}
|
|
168
|
-
out += ">" + text + "</a>";
|
|
169
|
-
return out;
|
|
175
|
+
const attrs: HTMLAttrs = [["href", cleanHref]];
|
|
176
|
+
if (title) attrs.push(["title", title]);
|
|
177
|
+
return injectHtmlAttributes(`<a>${text}</a>`, attrs);
|
|
170
178
|
}
|
|
171
179
|
|
|
172
180
|
image(href: string, title: string | null, text: string): string {
|
|
@@ -174,15 +182,12 @@ export class Renderer {
|
|
|
174
182
|
if (cleanHref === null) {
|
|
175
183
|
return text;
|
|
176
184
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (title)
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
out += ">";
|
|
185
|
-
return out;
|
|
185
|
+
const attrs: HTMLAttrs = [
|
|
186
|
+
["src", fixLocalImageHref(cleanHref, this.rendererConfig.relativeImageUrlPrefix)],
|
|
187
|
+
["alt", text],
|
|
188
|
+
];
|
|
189
|
+
if (title) attrs.push(["title", title]);
|
|
190
|
+
return injectHtmlAttributes("<img>", attrs);
|
|
186
191
|
}
|
|
187
192
|
|
|
188
193
|
text(text: string): string {
|
package/src/types.ts
CHANGED
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Lexer } from "./lexer.ts";
|
|
2
|
-
import { type SourceMap, type Tokens } from "./types.ts";
|
|
2
|
+
import { type HTMLAttrs, type SourceMap, type Tokens } from "./types.ts";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Helpers
|
|
@@ -33,14 +33,6 @@ export function escape(html: string, encode?: boolean) {
|
|
|
33
33
|
return html;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export function renderHtmlClasses(classes: string[]) {
|
|
37
|
-
if (!classes.length) return "";
|
|
38
|
-
let result = ' class="';
|
|
39
|
-
result += classes.join(" ");
|
|
40
|
-
result += '"';
|
|
41
|
-
return result;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
36
|
export function getHtmlElementText(html: string) {
|
|
45
37
|
try {
|
|
46
38
|
const parser = new DOMParser();
|
|
@@ -56,20 +48,10 @@ export function getHtmlElementText(html: string) {
|
|
|
56
48
|
}
|
|
57
49
|
}
|
|
58
50
|
|
|
59
|
-
|
|
60
|
-
* Only works when html element has no other attributes already defined.
|
|
61
|
-
* For the regex to work the element must be clean of attributes:
|
|
62
|
-
*
|
|
63
|
-
* <div>{whatever}</div>
|
|
64
|
-
*/
|
|
65
|
-
export function injectHtmlAttributes(
|
|
66
|
-
html: string,
|
|
67
|
-
attrs: [name: string, value: string | number][],
|
|
68
|
-
sourceMap: SourceMap,
|
|
69
|
-
) {
|
|
51
|
+
export function injectHtmlAttributes(html: string, attrs: HTMLAttrs, sourceMap?: SourceMap) {
|
|
70
52
|
if (sourceMap) {
|
|
71
|
-
attrs.push(["line-start", sourceMap[0]]);
|
|
72
|
-
attrs.push(["line-end", sourceMap[1]]);
|
|
53
|
+
attrs.push(["line-start", String(sourceMap[0])]);
|
|
54
|
+
attrs.push(["line-end", String(sourceMap[1])]);
|
|
73
55
|
}
|
|
74
56
|
|
|
75
57
|
if (!attrs.length) return html;
|